// Copyright 2011 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Core/HW/AddressSpace.h" #include #include "Common/BitUtils.h" #include "Core/ConfigManager.h" #include "Core/HW/DSP.h" #include "Core/HW/Memmap.h" #include "Core/PowerPC/MMU.h" namespace AddressSpace { u16 Accessors::ReadU16(u32 address) const { u32 result = ReadU8(address); result = result << 8 | ReadU8(address + 1); return result; } void Accessors::WriteU16(u32 address, u16 value) { WriteU8(address, value & 0xff); WriteU8(address + 1, (value >> 8) & 0xff); } u32 Accessors::ReadU32(u32 address) const { u32 result = ReadU16(address); result = result << 16 | ReadU16(address + 2); return result; } void Accessors::WriteU32(u32 address, u32 value) { WriteU16(address, value & 0xffff); WriteU16(address + 2, (value >> 16) & 0xffff); } u64 Accessors::ReadU64(u32 address) const { u64 result = ReadU32(address); result = result << 32 | ReadU32(address + 4); return result; } void Accessors::WriteU64(u32 address, u64 value) { WriteU32(address, value & 0xffffffff); WriteU32(address + 4, (value >> 32) & 0xffffffff); } float Accessors::ReadF32(u32 address) const { return Common::BitCast(ReadU32(address)); } Accessors::iterator Accessors::begin() const { return nullptr; } Accessors::iterator Accessors::end() const { return nullptr; } std::optional Accessors::Search(u32 haystack_start, const u8* needle_start, std::size_t needle_size, bool forwards) const { return std::nullopt; } Accessors::~Accessors() { } struct EffectiveAddressSpaceAccessors : Accessors { bool IsValidAddress(u32 address) const override { return PowerPC::HostIsRAMAddress(address); } u8 ReadU8(u32 address) const override { return PowerPC::HostRead_U8(address); } void WriteU8(u32 address, u8 value) override { PowerPC::HostWrite_U8(value, address); } u16 ReadU16(u32 address) const override { return PowerPC::HostRead_U16(address); } void WriteU16(u32 address, u16 value) override { PowerPC::HostWrite_U16(value, address); } u32 ReadU32(u32 address) const override { return PowerPC::HostRead_U32(address); } void WriteU32(u32 address, u32 value) override { PowerPC::HostWrite_U32(value, address); } u64 ReadU64(u32 address) const override { return PowerPC::HostRead_U64(address); } void WriteU64(u32 address, u64 value) override { PowerPC::HostWrite_U64(value, address); } float ReadF32(u32 address) const override { return PowerPC::HostRead_F32(address); }; bool Matches(u32 haystack_start, const u8* needle_start, std::size_t needle_size) const { u32 page_base = haystack_start & 0xfffff000; u32 offset = haystack_start & 0x0000fff; do { if (!PowerPC::HostIsRAMAddress(page_base)) { return false; } auto page_physical_address = PowerPC::GetTranslatedAddress(page_base); if (!page_physical_address.has_value()) { return false; } // For now, limit to only mem1 and mem2 regions // GetPointer can get confused by the locked dcache region that dolphin pins at 0xe0000000 u32 memory_area = (*page_physical_address) >> 24; if ((memory_area != 0x00) && (memory_area != 0x01)) { return false; } u8* page_ptr = Memory::GetPointer(*page_physical_address); if (page_ptr == nullptr) { return false; } std::size_t chunk_size = std::min(0x1000 - offset, needle_size); if (memcmp(needle_start, page_ptr + offset, chunk_size) != 0) { return false; } needle_size -= chunk_size; needle_start += chunk_size; offset = 0; page_base = page_base + 0x1000; } while (needle_size != 0 && page_base != 0); return (needle_size == 0); } std::optional Search(u32 haystack_start, const u8* needle_start, std::size_t needle_size, bool forward) const override { u32 haystack_address = haystack_start; // For forward=true, search incrementally (step +1) until it wraps back to 0x00000000 // For forward=false, search decrementally (step -1) until it wraps back to 0xfffff000 // Any page that doesn't translate is completely skipped. const u32 haystack_page_limit = forward ? 0x00000000 : 0xfffff000; const u32 haystack_offset_limit = forward ? 0x000 : 0xfff; const u32 haystack_page_change = forward ? 0x1000 : -0x1000; const u32 haystack_offset_change = forward ? 1 : -1; do { if (PowerPC::HostIsRAMAddress(haystack_address)) { do { if (Matches(haystack_address, needle_start, needle_size)) { return std::optional(haystack_address); } haystack_address += haystack_offset_change; } while ((haystack_address & 0xfff) != haystack_offset_limit); } else { haystack_address = (haystack_address + haystack_page_change) & 0xfffff000; } } while ((haystack_address & 0xfffff000) != haystack_page_limit); return std::nullopt; } }; struct AuxiliaryAddressSpaceAccessors : Accessors { static constexpr u32 aram_base_address = 0; bool IsValidAddress(u32 address) const override { return !SConfig::GetInstance().bWii && (address - aram_base_address) < GetSize(); } u8 ReadU8(u32 address) const override { const u8* base = DSP::GetARAMPtr(); return base[address]; } void WriteU8(u32 address, u8 value) override { u8* base = DSP::GetARAMPtr(); base[address] = value; } iterator begin() const override { return DSP::GetARAMPtr(); } iterator end() const override { return DSP::GetARAMPtr() + GetSize(); } std::optional Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size, bool forward) const override { if (!IsValidAddress(haystack_offset)) { return std::nullopt; } const u8* result; if (forward) { result = std::search(begin() + haystack_offset, end(), needle_start, needle_start + needle_size); } else { // using reverse iterator will also search the element in reverse auto reverse_end = std::make_reverse_iterator(begin() + needle_size - 1); auto it = std::search(std::make_reverse_iterator(begin() + haystack_offset + needle_size - 1), reverse_end, std::make_reverse_iterator(needle_start + needle_size), std::make_reverse_iterator(needle_start)); result = (it == reverse_end) ? end() : (&(*it) - needle_size + 1); } if (result == end()) { return std::nullopt; } return std::optional(result - begin()); } private: static u32 GetSize() { return 16 * 1024 * 1024; } }; struct AccessorMapping { u32 base; Accessors* accessors; }; struct CompositeAddressSpaceAccessors : Accessors { CompositeAddressSpaceAccessors() = default; CompositeAddressSpaceAccessors(std::initializer_list accessors) : m_accessor_mappings(accessors.begin(), accessors.end()) { } bool IsValidAddress(u32 address) const override { return FindAppropriateAccessor(address) != m_accessor_mappings.end(); } u8 ReadU8(u32 address) const override { auto mapping = FindAppropriateAccessor(address); if (mapping == m_accessor_mappings.end()) { return 0; } return mapping->accessors->ReadU8(address - mapping->base); } void WriteU8(u32 address, u8 value) override { auto mapping = FindAppropriateAccessor(address); if (mapping == m_accessor_mappings.end()) { return; } return mapping->accessors->WriteU8(address - mapping->base, value); } std::optional Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size, bool forward) const override { for (const AccessorMapping& mapping : m_accessor_mappings) { u32 mapping_offset = haystack_offset - mapping.base; if (!mapping.accessors->IsValidAddress(mapping_offset)) { continue; } auto result = mapping.accessors->Search(mapping_offset, needle_start, needle_size, forward); if (result.has_value()) { return std::optional(*result + mapping.base); } } return std::nullopt; } private: std::vector m_accessor_mappings; std::vector::iterator FindAppropriateAccessor(u32 address) { return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(), [address](const AccessorMapping& a) { return a.accessors->IsValidAddress(address - a.base); }); } std::vector::const_iterator FindAppropriateAccessor(u32 address) const { return std::find_if(m_accessor_mappings.begin(), m_accessor_mappings.end(), [address](const AccessorMapping& a) { return a.accessors->IsValidAddress(address - a.base); }); } }; struct SmallBlockAccessors : Accessors { SmallBlockAccessors() = default; SmallBlockAccessors(u8** alloc_base_, u32 size_) : alloc_base{alloc_base_}, size{size_} {} bool IsValidAddress(u32 address) const override { return (*alloc_base != nullptr) && (address < size); } u8 ReadU8(u32 address) const override { return (*alloc_base)[address]; } void WriteU8(u32 address, u8 value) override { (*alloc_base)[address] = value; } iterator begin() const override { return *alloc_base; } iterator end() const override { return (*alloc_base == nullptr) ? nullptr : (*alloc_base + size); } std::optional Search(u32 haystack_offset, const u8* needle_start, std::size_t needle_size, bool forward) const override { if (!IsValidAddress(haystack_offset) || !IsValidAddress(haystack_offset + static_cast(needle_size) - 1)) { return std::nullopt; } const u8* result; if (forward) { result = std::search(begin() + haystack_offset, end(), needle_start, needle_start + needle_size); } else { // using reverse iterator will also search the element in reverse auto reverse_end = std::make_reverse_iterator(begin() + needle_size - 1); auto it = std::search(std::make_reverse_iterator(begin() + haystack_offset + needle_size - 1), reverse_end, std::make_reverse_iterator(needle_start + needle_size), std::make_reverse_iterator(needle_start)); result = (it == reverse_end) ? end() : (&(*it) - needle_size + 1); } if (result == end()) { return std::nullopt; } return std::optional(result - begin()); } private: u8** alloc_base = nullptr; u32 size = 0; }; struct NullAccessors : Accessors { bool IsValidAddress(u32 address) const override { return false; } u8 ReadU8(u32 address) const override { return 0; } void WriteU8(u32 address, u8 value) override {} }; static EffectiveAddressSpaceAccessors s_effective_address_space_accessors; static AuxiliaryAddressSpaceAccessors s_auxiliary_address_space_accessors; static SmallBlockAccessors s_mem1_address_space_accessors; static SmallBlockAccessors s_mem2_address_space_accessors; static SmallBlockAccessors s_fake_address_space_accessors; static CompositeAddressSpaceAccessors s_physical_address_space_accessors_gcn; static CompositeAddressSpaceAccessors s_physical_address_space_accessors_wii; static NullAccessors s_null_accessors; static bool s_initialized = false; Accessors* GetAccessors(Type address_space) { if (!s_initialized) return &s_null_accessors; // default to effective switch (address_space) { case Type::Effective: return &s_effective_address_space_accessors; case Type::Physical: if (SConfig::GetInstance().bWii) { return &s_physical_address_space_accessors_wii; } else { return &s_physical_address_space_accessors_gcn; } case Type::Mem1: return &s_mem1_address_space_accessors; case Type::Mem2: if (SConfig::GetInstance().bWii) { return &s_mem2_address_space_accessors; } break; case Type::Auxiliary: if (!SConfig::GetInstance().bWii) { return &s_auxiliary_address_space_accessors; } break; case Type::Fake: return &s_fake_address_space_accessors; } return &s_null_accessors; } void Init() { s_mem1_address_space_accessors = {&Memory::m_pRAM, Memory::GetRamSizeReal()}; s_mem2_address_space_accessors = {&Memory::m_pEXRAM, Memory::GetExRamSizeReal()}; s_fake_address_space_accessors = {&Memory::m_pFakeVMEM, Memory::GetFakeVMemSize()}; s_physical_address_space_accessors_gcn = {{0x00000000, &s_mem1_address_space_accessors}}; s_physical_address_space_accessors_wii = {{0x00000000, &s_mem1_address_space_accessors}, {0x10000000, &s_mem2_address_space_accessors}}; s_initialized = true; } void Shutdown() { s_initialized = false; } } // namespace AddressSpace