core/memory: Migrate over Write{8, 16, 32, 64, Block} to the Memory class
The Write functions are used slightly less than the Read functions, which make these a bit nicer to move over. The only adjustments we really need to make here are to Dynarmic's exclusive monitor instance. We need to keep a reference to the currently active memory instance to perform exclusive read/write operations.
This commit is contained in:
		| @@ -45,20 +45,21 @@ public: | ||||
|     } | ||||
|  | ||||
|     void MemoryWrite8(u64 vaddr, u8 value) override { | ||||
|         Memory::Write8(vaddr, value); | ||||
|         parent.system.Memory().Write8(vaddr, value); | ||||
|     } | ||||
|     void MemoryWrite16(u64 vaddr, u16 value) override { | ||||
|         Memory::Write16(vaddr, value); | ||||
|         parent.system.Memory().Write16(vaddr, value); | ||||
|     } | ||||
|     void MemoryWrite32(u64 vaddr, u32 value) override { | ||||
|         Memory::Write32(vaddr, value); | ||||
|         parent.system.Memory().Write32(vaddr, value); | ||||
|     } | ||||
|     void MemoryWrite64(u64 vaddr, u64 value) override { | ||||
|         Memory::Write64(vaddr, value); | ||||
|         parent.system.Memory().Write64(vaddr, value); | ||||
|     } | ||||
|     void MemoryWrite128(u64 vaddr, Vector value) override { | ||||
|         Memory::Write64(vaddr, value[0]); | ||||
|         Memory::Write64(vaddr + 8, value[1]); | ||||
|         auto& memory = parent.system.Memory(); | ||||
|         memory.Write64(vaddr, value[0]); | ||||
|         memory.Write64(vaddr + 8, value[1]); | ||||
|     } | ||||
|  | ||||
|     void InterpreterFallback(u64 pc, std::size_t num_instructions) override { | ||||
| @@ -266,7 +267,9 @@ void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table, | ||||
|     jit = MakeJit(page_table, new_address_space_size_in_bits); | ||||
| } | ||||
|  | ||||
| DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} | ||||
| DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count) | ||||
|     : monitor(core_count), memory{memory_} {} | ||||
|  | ||||
| DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | ||||
|  | ||||
| void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { | ||||
| @@ -279,29 +282,28 @@ void DynarmicExclusiveMonitor::ClearExclusive() { | ||||
| } | ||||
|  | ||||
| bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { | ||||
|     return monitor.DoExclusiveOperation(core_index, vaddr, 1, | ||||
|                                         [&] { Memory::Write8(vaddr, value); }); | ||||
|     return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); }); | ||||
| } | ||||
|  | ||||
| bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { | ||||
|     return monitor.DoExclusiveOperation(core_index, vaddr, 2, | ||||
|                                         [&] { Memory::Write16(vaddr, value); }); | ||||
|                                         [&] { memory.Write16(vaddr, value); }); | ||||
| } | ||||
|  | ||||
| bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { | ||||
|     return monitor.DoExclusiveOperation(core_index, vaddr, 4, | ||||
|                                         [&] { Memory::Write32(vaddr, value); }); | ||||
|                                         [&] { memory.Write32(vaddr, value); }); | ||||
| } | ||||
|  | ||||
| bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { | ||||
|     return monitor.DoExclusiveOperation(core_index, vaddr, 8, | ||||
|                                         [&] { Memory::Write64(vaddr, value); }); | ||||
|                                         [&] { memory.Write64(vaddr, value); }); | ||||
| } | ||||
|  | ||||
| bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { | ||||
|     return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { | ||||
|         Memory::Write64(vaddr + 0, value[0]); | ||||
|         Memory::Write64(vaddr + 8, value[1]); | ||||
|         memory.Write64(vaddr + 0, value[0]); | ||||
|         memory.Write64(vaddr + 8, value[1]); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,10 @@ | ||||
| #include "core/arm/exclusive_monitor.h" | ||||
| #include "core/arm/unicorn/arm_unicorn.h" | ||||
|  | ||||
| namespace Memory { | ||||
| class Memory; | ||||
| } | ||||
|  | ||||
| namespace Core { | ||||
|  | ||||
| class ARM_Dynarmic_Callbacks; | ||||
| @@ -63,7 +67,7 @@ private: | ||||
|  | ||||
| class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | ||||
| public: | ||||
|     explicit DynarmicExclusiveMonitor(std::size_t core_count); | ||||
|     explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count); | ||||
|     ~DynarmicExclusiveMonitor() override; | ||||
|  | ||||
|     void SetExclusive(std::size_t core_index, VAddr addr) override; | ||||
| @@ -78,6 +82,7 @@ public: | ||||
| private: | ||||
|     friend class ARM_Dynarmic; | ||||
|     Dynarmic::A64::ExclusiveMonitor monitor; | ||||
|     Memory::Memory& memory; | ||||
| }; | ||||
|  | ||||
| } // namespace Core | ||||
|   | ||||
| @@ -66,9 +66,10 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba | ||||
|  | ||||
| Cpu::~Cpu() = default; | ||||
|  | ||||
| std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { | ||||
| std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor( | ||||
|     [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
|     return std::make_unique<DynarmicExclusiveMonitor>(num_cores); | ||||
|     return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores); | ||||
| #else | ||||
|     // TODO(merry): Passthrough exclusive monitor | ||||
|     return nullptr; | ||||
|   | ||||
| @@ -24,6 +24,10 @@ namespace Core::Timing { | ||||
| class CoreTiming; | ||||
| } | ||||
|  | ||||
| namespace Memory { | ||||
| class Memory; | ||||
| } | ||||
|  | ||||
| namespace Core { | ||||
|  | ||||
| class ARM_Interface; | ||||
| @@ -86,7 +90,19 @@ public: | ||||
|  | ||||
|     void Shutdown(); | ||||
|  | ||||
|     static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); | ||||
|     /** | ||||
|      * Creates an exclusive monitor to handle exclusive reads/writes. | ||||
|      * | ||||
|      * @param memory The current memory subsystem that the monitor may wish | ||||
|      *               to keep track of. | ||||
|      * | ||||
|      * @param num_cores The number of cores to assume about the CPU. | ||||
|      * | ||||
|      * @returns The constructed exclusive monitor instance, or nullptr if the current | ||||
|      *          CPU backend is unable to use an exclusive monitor. | ||||
|      */ | ||||
|     static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||||
|                                                                   std::size_t num_cores); | ||||
|  | ||||
| private: | ||||
|     void Reschedule(); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ CpuCoreManager::~CpuCoreManager() = default; | ||||
|  | ||||
| void CpuCoreManager::Initialize() { | ||||
|     barrier = std::make_unique<CpuBarrier>(); | ||||
|     exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); | ||||
|     exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size()); | ||||
|  | ||||
|     for (std::size_t index = 0; index < cores.size(); ++index) { | ||||
|         cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); | ||||
|   | ||||
| @@ -508,8 +508,9 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) { | ||||
|               bp->second.len, bp->second.addr, static_cast<int>(type)); | ||||
|  | ||||
|     if (type == BreakpointType::Execute) { | ||||
|         Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); | ||||
|         Core::System::GetInstance().InvalidateCpuInstructionCaches(); | ||||
|         auto& system = Core::System::GetInstance(); | ||||
|         system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); | ||||
|         system.InvalidateCpuInstructionCaches(); | ||||
|     } | ||||
|     p.erase(addr); | ||||
| } | ||||
| @@ -993,14 +994,14 @@ static void WriteMemory() { | ||||
|     const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset)); | ||||
|  | ||||
|     auto& system = Core::System::GetInstance(); | ||||
|     const auto& memory = system.Memory(); | ||||
|     auto& memory = system.Memory(); | ||||
|     if (!memory.IsValidVirtualAddress(addr)) { | ||||
|         return SendReply("E00"); | ||||
|     } | ||||
|  | ||||
|     std::vector<u8> data(len); | ||||
|     GdbHexToMem(data.data(), len_pos + 1, len); | ||||
|     Memory::WriteBlock(addr, data.data(), len); | ||||
|     memory.WriteBlock(addr, data.data(), len); | ||||
|     system.InvalidateCpuInstructionCaches(); | ||||
|     SendReply("OK"); | ||||
| } | ||||
| @@ -1058,13 +1059,14 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { | ||||
|     breakpoint.addr = addr; | ||||
|     breakpoint.len = len; | ||||
|  | ||||
|     auto& memory = Core::System::GetInstance().Memory(); | ||||
|     auto& system = Core::System::GetInstance(); | ||||
|     auto& memory = system.Memory(); | ||||
|     memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | ||||
|  | ||||
|     static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; | ||||
|     if (type == BreakpointType::Execute) { | ||||
|         Memory::WriteBlock(addr, btrap.data(), btrap.size()); | ||||
|         Core::System::GetInstance().InvalidateCpuInstructionCaches(); | ||||
|         memory.WriteBlock(addr, btrap.data(), btrap.size()); | ||||
|         system.InvalidateCpuInstructionCaches(); | ||||
|     } | ||||
|     p.insert({addr, breakpoint}); | ||||
|  | ||||
|   | ||||
| @@ -78,7 +78,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
|  | ||||
|     Memory::Write32(address, static_cast<u32>(value + 1)); | ||||
|     memory.Write32(address, static_cast<u32>(value + 1)); | ||||
|     return SignalToAddressOnly(address, num_to_wake); | ||||
| } | ||||
|  | ||||
| @@ -117,7 +117,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
|  | ||||
|     Memory::Write32(address, static_cast<u32>(updated_value)); | ||||
|     memory.Write32(address, static_cast<u32>(updated_value)); | ||||
|     WakeThreads(waiting_threads, num_to_wake); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| @@ -151,7 +151,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6 | ||||
|     } | ||||
|  | ||||
|     if (should_decrement) { | ||||
|         Memory::Write32(address, static_cast<u32>(cur_value - 1)); | ||||
|         memory.Write32(address, static_cast<u32>(cur_value - 1)); | ||||
|     } | ||||
|  | ||||
|     // Short-circuit without rescheduling, if timeout is zero. | ||||
|   | ||||
| @@ -274,8 +274,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | ||||
|     } | ||||
|  | ||||
|     // Copy the translated command buffer back into the thread's command buffer area. | ||||
|     Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), | ||||
|                        dst_cmdbuf.size() * sizeof(u32)); | ||||
|     memory.WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), | ||||
|                       dst_cmdbuf.size() * sizeof(u32)); | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| @@ -311,10 +311,11 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | ||||
|         size = buffer_size; // TODO(bunnei): This needs to be HW tested | ||||
|     } | ||||
|  | ||||
|     auto& memory = Core::System::GetInstance().Memory(); | ||||
|     if (is_buffer_b) { | ||||
|         Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | ||||
|         memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | ||||
|     } else { | ||||
|         Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | ||||
|         memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | ||||
|     } | ||||
|  | ||||
|     return size; | ||||
|   | ||||
| @@ -117,7 +117,7 @@ ResultCode Mutex::Release(VAddr address) { | ||||
|  | ||||
|     // There are no more threads waiting for the mutex, release it completely. | ||||
|     if (thread == nullptr) { | ||||
|         Memory::Write32(address, 0); | ||||
|         system.Memory().Write32(address, 0); | ||||
|         return RESULT_SUCCESS; | ||||
|     } | ||||
|  | ||||
| @@ -132,7 +132,7 @@ ResultCode Mutex::Release(VAddr address) { | ||||
|     } | ||||
|  | ||||
|     // Grant the mutex to the next waiting thread and resume it. | ||||
|     Memory::Write32(address, mutex_value); | ||||
|     system.Memory().Write32(address, mutex_value); | ||||
|  | ||||
|     ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); | ||||
|     thread->ResumeFromWait(); | ||||
|   | ||||
| @@ -1120,7 +1120,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H | ||||
|         std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{}); | ||||
|     } | ||||
|  | ||||
|     Memory::WriteBlock(thread_context, &ctx, sizeof(ctx)); | ||||
|     system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx)); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| @@ -1280,20 +1280,21 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
|  | ||||
|     auto& memory = system.Memory(); | ||||
|     const auto& vm_manager = process->VMManager(); | ||||
|     const MemoryInfo memory_info = vm_manager.QueryMemory(address); | ||||
|  | ||||
|     Memory::Write64(memory_info_address, memory_info.base_address); | ||||
|     Memory::Write64(memory_info_address + 8, memory_info.size); | ||||
|     Memory::Write32(memory_info_address + 16, memory_info.state); | ||||
|     Memory::Write32(memory_info_address + 20, memory_info.attributes); | ||||
|     Memory::Write32(memory_info_address + 24, memory_info.permission); | ||||
|     Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count); | ||||
|     Memory::Write32(memory_info_address + 28, memory_info.device_ref_count); | ||||
|     Memory::Write32(memory_info_address + 36, 0); | ||||
|     memory.Write64(memory_info_address, memory_info.base_address); | ||||
|     memory.Write64(memory_info_address + 8, memory_info.size); | ||||
|     memory.Write32(memory_info_address + 16, memory_info.state); | ||||
|     memory.Write32(memory_info_address + 20, memory_info.attributes); | ||||
|     memory.Write32(memory_info_address + 24, memory_info.permission); | ||||
|     memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count); | ||||
|     memory.Write32(memory_info_address + 28, memory_info.device_ref_count); | ||||
|     memory.Write32(memory_info_address + 36, 0); | ||||
|  | ||||
|     // Page info appears to be currently unused by the kernel and is always set to zero. | ||||
|     Memory::Write32(page_info_address, 0); | ||||
|     memory.Write32(page_info_address, 0); | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| @@ -2290,12 +2291,13 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     auto& memory = system.Memory(); | ||||
|     const auto& process_list = kernel.GetProcessList(); | ||||
|     const auto num_processes = process_list.size(); | ||||
|     const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); | ||||
|  | ||||
|     for (std::size_t i = 0; i < copy_amount; ++i) { | ||||
|         Memory::Write64(out_process_ids, process_list[i]->GetProcessID()); | ||||
|         memory.Write64(out_process_ids, process_list[i]->GetProcessID()); | ||||
|         out_process_ids += sizeof(u64); | ||||
|     } | ||||
|  | ||||
| @@ -2329,13 +2331,14 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd | ||||
|         return ERR_INVALID_ADDRESS_STATE; | ||||
|     } | ||||
|  | ||||
|     auto& memory = system.Memory(); | ||||
|     const auto& thread_list = current_process->GetThreadList(); | ||||
|     const auto num_threads = thread_list.size(); | ||||
|     const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); | ||||
|  | ||||
|     auto list_iter = thread_list.cbegin(); | ||||
|     for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { | ||||
|         Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID()); | ||||
|         memory.Write64(out_thread_ids, (*list_iter)->GetThreadID()); | ||||
|         out_thread_ids += sizeof(u64); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -58,35 +58,6 @@ u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { | ||||
| u8* GetPointerFromVMA(VAddr vaddr) { | ||||
|     return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| void Write(const VAddr vaddr, const T data) { | ||||
|     u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||
|     if (page_pointer != nullptr) { | ||||
|         // NOTE: Avoid adding any extra logic to this fast-path block | ||||
|         std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||||
|     switch (type) { | ||||
|     case Common::PageType::Unmapped: | ||||
|         LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | ||||
|                   static_cast<u32>(data), vaddr); | ||||
|         return; | ||||
|     case Common::PageType::Memory: | ||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||||
|         break; | ||||
|     case Common::PageType::RasterizerCachedMemory: { | ||||
|         u8* const host_ptr{GetPointerFromVMA(vaddr)}; | ||||
|         Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); | ||||
|         std::memcpy(host_ptr, &data, sizeof(T)); | ||||
|         break; | ||||
|     } | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| // Implementation class used to keep the specifics of the memory subsystem hidden | ||||
| @@ -195,6 +166,22 @@ struct Memory::Impl { | ||||
|         return Read<u64_le>(addr); | ||||
|     } | ||||
|  | ||||
|     void Write8(const VAddr addr, const u8 data) { | ||||
|         Write<u8>(addr, data); | ||||
|     } | ||||
|  | ||||
|     void Write16(const VAddr addr, const u16 data) { | ||||
|         Write<u16_le>(addr, data); | ||||
|     } | ||||
|  | ||||
|     void Write32(const VAddr addr, const u32 data) { | ||||
|         Write<u32_le>(addr, data); | ||||
|     } | ||||
|  | ||||
|     void Write64(const VAddr addr, const u64 data) { | ||||
|         Write<u64_le>(addr, data); | ||||
|     } | ||||
|  | ||||
|     std::string ReadCString(VAddr vaddr, std::size_t max_length) { | ||||
|         std::string string; | ||||
|         string.reserve(max_length); | ||||
| @@ -259,6 +246,53 @@ struct Memory::Impl { | ||||
|         ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); | ||||
|     } | ||||
|  | ||||
|     void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | ||||
|                     const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         std::size_t remaining_size = size; | ||||
|         std::size_t page_index = dest_addr >> PAGE_BITS; | ||||
|         std::size_t page_offset = dest_addr & PAGE_MASK; | ||||
|  | ||||
|         while (remaining_size > 0) { | ||||
|             const std::size_t copy_amount = | ||||
|                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | ||||
|             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | ||||
|  | ||||
|             switch (page_table.attributes[page_index]) { | ||||
|             case Common::PageType::Unmapped: { | ||||
|                 LOG_ERROR(HW_Memory, | ||||
|                           "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | ||||
|                           current_vaddr, dest_addr, size); | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::Memory: { | ||||
|                 DEBUG_ASSERT(page_table.pointers[page_index]); | ||||
|  | ||||
|                 u8* const dest_ptr = page_table.pointers[page_index] + page_offset; | ||||
|                 std::memcpy(dest_ptr, src_buffer, copy_amount); | ||||
|                 break; | ||||
|             } | ||||
|             case Common::PageType::RasterizerCachedMemory: { | ||||
|                 u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); | ||||
|                 system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); | ||||
|                 std::memcpy(host_ptr, src_buffer, copy_amount); | ||||
|                 break; | ||||
|             } | ||||
|             default: | ||||
|                 UNREACHABLE(); | ||||
|             } | ||||
|  | ||||
|             page_index++; | ||||
|             page_offset = 0; | ||||
|             src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | ||||
|             remaining_size -= copy_amount; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { | ||||
|         WriteBlock(*system.CurrentProcess(), dest_addr, src_buffer, size); | ||||
|     } | ||||
|  | ||||
|     void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { | ||||
|         const auto& page_table = process.VMManager().page_table; | ||||
|         std::size_t remaining_size = size; | ||||
| @@ -501,6 +535,46 @@ struct Memory::Impl { | ||||
|         return {}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Writes a particular data type to memory at the given virtual address. | ||||
|      * | ||||
|      * @param vaddr The virtual address to write the data type to. | ||||
|      * | ||||
|      * @tparam T The data type to write to memory. This type *must* be | ||||
|      *           trivially copyable, otherwise the behavior of this function | ||||
|      *           is undefined. | ||||
|      * | ||||
|      * @returns The instance of T write to the specified virtual address. | ||||
|      */ | ||||
|     template <typename T> | ||||
|     void Write(const VAddr vaddr, const T data) { | ||||
|         u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||
|         if (page_pointer != nullptr) { | ||||
|             // NOTE: Avoid adding any extra logic to this fast-path block | ||||
|             std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; | ||||
|         switch (type) { | ||||
|         case Common::PageType::Unmapped: | ||||
|             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8, | ||||
|                       static_cast<u32>(data), vaddr); | ||||
|             return; | ||||
|         case Common::PageType::Memory: | ||||
|             ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr); | ||||
|             break; | ||||
|         case Common::PageType::RasterizerCachedMemory: { | ||||
|             u8* const host_ptr{GetPointerFromVMA(vaddr)}; | ||||
|             system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T)); | ||||
|             std::memcpy(host_ptr, &data, sizeof(T)); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Core::System& system; | ||||
| }; | ||||
|  | ||||
| @@ -562,6 +636,22 @@ u64 Memory::Read64(const VAddr addr) { | ||||
|     return impl->Read64(addr); | ||||
| } | ||||
|  | ||||
| void Memory::Write8(VAddr addr, u8 data) { | ||||
|     impl->Write8(addr, data); | ||||
| } | ||||
|  | ||||
| void Memory::Write16(VAddr addr, u16 data) { | ||||
|     impl->Write16(addr, data); | ||||
| } | ||||
|  | ||||
| void Memory::Write32(VAddr addr, u32 data) { | ||||
|     impl->Write32(addr, data); | ||||
| } | ||||
|  | ||||
| void Memory::Write64(VAddr addr, u64 data) { | ||||
|     impl->Write64(addr, data); | ||||
| } | ||||
|  | ||||
| std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) { | ||||
|     return impl->ReadCString(vaddr, max_length); | ||||
| } | ||||
| @@ -575,6 +665,15 @@ void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_ | ||||
|     impl->ReadBlock(src_addr, dest_buffer, size); | ||||
| } | ||||
|  | ||||
| void Memory::WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | ||||
|                         std::size_t size) { | ||||
|     impl->WriteBlock(process, dest_addr, src_buffer, size); | ||||
| } | ||||
|  | ||||
| void Memory::WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { | ||||
|     impl->WriteBlock(dest_addr, src_buffer, size); | ||||
| } | ||||
|  | ||||
| void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) { | ||||
|     impl->ZeroBlock(process, dest_addr, size); | ||||
| } | ||||
| @@ -612,67 +711,4 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { | ||||
|     return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; | ||||
| } | ||||
|  | ||||
| void Write8(const VAddr addr, const u8 data) { | ||||
|     Write<u8>(addr, data); | ||||
| } | ||||
|  | ||||
| void Write16(const VAddr addr, const u16 data) { | ||||
|     Write<u16_le>(addr, data); | ||||
| } | ||||
|  | ||||
| void Write32(const VAddr addr, const u32 data) { | ||||
|     Write<u32_le>(addr, data); | ||||
| } | ||||
|  | ||||
| void Write64(const VAddr addr, const u64 data) { | ||||
|     Write<u64_le>(addr, data); | ||||
| } | ||||
|  | ||||
| void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | ||||
|                 const std::size_t size) { | ||||
|     const auto& page_table = process.VMManager().page_table; | ||||
|     std::size_t remaining_size = size; | ||||
|     std::size_t page_index = dest_addr >> PAGE_BITS; | ||||
|     std::size_t page_offset = dest_addr & PAGE_MASK; | ||||
|  | ||||
|     while (remaining_size > 0) { | ||||
|         const std::size_t copy_amount = | ||||
|             std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); | ||||
|         const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | ||||
|  | ||||
|         switch (page_table.attributes[page_index]) { | ||||
|         case Common::PageType::Unmapped: { | ||||
|             LOG_ERROR(HW_Memory, | ||||
|                       "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})", | ||||
|                       current_vaddr, dest_addr, size); | ||||
|             break; | ||||
|         } | ||||
|         case Common::PageType::Memory: { | ||||
|             DEBUG_ASSERT(page_table.pointers[page_index]); | ||||
|  | ||||
|             u8* dest_ptr = page_table.pointers[page_index] + page_offset; | ||||
|             std::memcpy(dest_ptr, src_buffer, copy_amount); | ||||
|             break; | ||||
|         } | ||||
|         case Common::PageType::RasterizerCachedMemory: { | ||||
|             const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; | ||||
|             Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); | ||||
|             std::memcpy(host_ptr, src_buffer, copy_amount); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|  | ||||
|         page_index++; | ||||
|         page_offset = 0; | ||||
|         src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | ||||
|         remaining_size -= copy_amount; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { | ||||
|     WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size); | ||||
| } | ||||
|  | ||||
| } // namespace Memory | ||||
|   | ||||
| @@ -192,6 +192,50 @@ public: | ||||
|      */ | ||||
|     u64 Read64(VAddr addr); | ||||
|  | ||||
|     /** | ||||
|      * Writes an 8-bit unsigned integer to the given virtual address in | ||||
|      * the current process' address space. | ||||
|      * | ||||
|      * @param addr The virtual address to write the 8-bit unsigned integer to. | ||||
|      * @param data The 8-bit unsigned integer to write to the given virtual address. | ||||
|      * | ||||
|      * @post The memory at the given virtual address contains the specified data value. | ||||
|      */ | ||||
|     void Write8(VAddr addr, u8 data); | ||||
|  | ||||
|     /** | ||||
|      * Writes a 16-bit unsigned integer to the given virtual address in | ||||
|      * the current process' address space. | ||||
|      * | ||||
|      * @param addr The virtual address to write the 16-bit unsigned integer to. | ||||
|      * @param data The 16-bit unsigned integer to write to the given virtual address. | ||||
|      * | ||||
|      * @post The memory range [addr, sizeof(data)) contains the given data value. | ||||
|      */ | ||||
|     void Write16(VAddr addr, u16 data); | ||||
|  | ||||
|     /** | ||||
|      * Writes a 32-bit unsigned integer to the given virtual address in | ||||
|      * the current process' address space. | ||||
|      * | ||||
|      * @param addr The virtual address to write the 32-bit unsigned integer to. | ||||
|      * @param data The 32-bit unsigned integer to write to the given virtual address. | ||||
|      * | ||||
|      * @post The memory range [addr, sizeof(data)) contains the given data value. | ||||
|      */ | ||||
|     void Write32(VAddr addr, u32 data); | ||||
|  | ||||
|     /** | ||||
|      * Writes a 64-bit unsigned integer to the given virtual address in | ||||
|      * the current process' address space. | ||||
|      * | ||||
|      * @param addr The virtual address to write the 64-bit unsigned integer to. | ||||
|      * @param data The 64-bit unsigned integer to write to the given virtual address. | ||||
|      * | ||||
|      * @post The memory range [addr, sizeof(data)) contains the given data value. | ||||
|      */ | ||||
|     void Write64(VAddr addr, u64 data); | ||||
|  | ||||
|     /** | ||||
|      * Reads a null-terminated string from the given virtual address. | ||||
|      * This function will continually read characters until either: | ||||
| @@ -247,6 +291,50 @@ public: | ||||
|      */ | ||||
|     void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); | ||||
|  | ||||
|     /** | ||||
|      * Writes a range of bytes into a given process' address space at the specified | ||||
|      * virtual address. | ||||
|      * | ||||
|      * @param process    The process to write data into the address space of. | ||||
|      * @param dest_addr  The destination virtual address to begin writing the data at. | ||||
|      * @param src_buffer The data to write into the process' address space. | ||||
|      * @param size       The size of the data to write, in bytes. | ||||
|      * | ||||
|      * @post The address range [dest_addr, size) in the process' address space | ||||
|      *       contains the data that was within src_buffer. | ||||
|      * | ||||
|      * @post If an attempt is made to write into an unmapped region of memory, the writes | ||||
|      *       will be ignored and an error will be logged. | ||||
|      * | ||||
|      * @post If a write is performed into a region of memory that is considered cached | ||||
|      *       rasterizer memory, will cause the currently active rasterizer to be notified | ||||
|      *       and will mark that region as invalidated to caches that the active | ||||
|      *       graphics backend may be maintaining over the course of execution. | ||||
|      */ | ||||
|     void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | ||||
|                     std::size_t size); | ||||
|  | ||||
|     /** | ||||
|      * Writes a range of bytes into the current process' address space at the specified | ||||
|      * virtual address. | ||||
|      * | ||||
|      * @param dest_addr  The destination virtual address to begin writing the data at. | ||||
|      * @param src_buffer The data to write into the current process' address space. | ||||
|      * @param size       The size of the data to write, in bytes. | ||||
|      * | ||||
|      * @post The address range [dest_addr, size) in the current process' address space | ||||
|      *       contains the data that was within src_buffer. | ||||
|      * | ||||
|      * @post If an attempt is made to write into an unmapped region of memory, the writes | ||||
|      *       will be ignored and an error will be logged. | ||||
|      * | ||||
|      * @post If a write is performed into a region of memory that is considered cached | ||||
|      *       rasterizer memory, will cause the currently active rasterizer to be notified | ||||
|      *       and will mark that region as invalidated to caches that the active | ||||
|      *       graphics backend may be maintaining over the course of execution. | ||||
|      */ | ||||
|     void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); | ||||
|  | ||||
|     /** | ||||
|      * Fills the specified address range within a process' address space with zeroes. | ||||
|      * | ||||
| @@ -320,13 +408,4 @@ void SetCurrentPageTable(Kernel::Process& process); | ||||
| /// Determines if the given VAddr is a kernel address | ||||
| bool IsKernelVirtualAddress(VAddr vaddr); | ||||
|  | ||||
| void Write8(VAddr addr, u8 data); | ||||
| void Write16(VAddr addr, u16 data); | ||||
| void Write32(VAddr addr, u32 data); | ||||
| void Write64(VAddr addr, u64 data); | ||||
|  | ||||
| void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | ||||
|                 std::size_t size); | ||||
| void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); | ||||
|  | ||||
| } // namespace Memory | ||||
|   | ||||
| @@ -30,7 +30,7 @@ void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { | ||||
| } | ||||
|  | ||||
| void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { | ||||
|     WriteBlock(SanitizeAddress(address), data, size); | ||||
|     system.Memory().WriteBlock(SanitizeAddress(address), data, size); | ||||
| } | ||||
|  | ||||
| u64 StandardVmCallbacks::HidKeysDown() { | ||||
|   | ||||
| @@ -34,16 +34,16 @@ u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { | ||||
| void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) { | ||||
|     switch (width) { | ||||
|     case 1: | ||||
|         Memory::Write8(addr, static_cast<u8>(value)); | ||||
|         memory.Write8(addr, static_cast<u8>(value)); | ||||
|         break; | ||||
|     case 2: | ||||
|         Memory::Write16(addr, static_cast<u16>(value)); | ||||
|         memory.Write16(addr, static_cast<u16>(value)); | ||||
|         break; | ||||
|     case 4: | ||||
|         Memory::Write32(addr, static_cast<u32>(value)); | ||||
|         memory.Write32(addr, static_cast<u32>(value)); | ||||
|         break; | ||||
|     case 8: | ||||
|         Memory::Write64(addr, value); | ||||
|         memory.Write64(addr, value); | ||||
|         break; | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user