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:
Lioncash 2019-11-26 17:39:57 -05:00
parent b05bfc6036
commit e4c381b885
14 changed files with 298 additions and 153 deletions

View File

@ -45,20 +45,21 @@ public:
} }
void MemoryWrite8(u64 vaddr, u8 value) override { void MemoryWrite8(u64 vaddr, u8 value) override {
Memory::Write8(vaddr, value); parent.system.Memory().Write8(vaddr, value);
} }
void MemoryWrite16(u64 vaddr, u16 value) override { void MemoryWrite16(u64 vaddr, u16 value) override {
Memory::Write16(vaddr, value); parent.system.Memory().Write16(vaddr, value);
} }
void MemoryWrite32(u64 vaddr, u32 value) override { void MemoryWrite32(u64 vaddr, u32 value) override {
Memory::Write32(vaddr, value); parent.system.Memory().Write32(vaddr, value);
} }
void MemoryWrite64(u64 vaddr, u64 value) override { void MemoryWrite64(u64 vaddr, u64 value) override {
Memory::Write64(vaddr, value); parent.system.Memory().Write64(vaddr, value);
} }
void MemoryWrite128(u64 vaddr, Vector value) override { void MemoryWrite128(u64 vaddr, Vector value) override {
Memory::Write64(vaddr, value[0]); auto& memory = parent.system.Memory();
Memory::Write64(vaddr + 8, value[1]); memory.Write64(vaddr, value[0]);
memory.Write64(vaddr + 8, value[1]);
} }
void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 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); 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; DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { 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) { bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 1, return monitor.DoExclusiveOperation(core_index, vaddr, 1, [&] { memory.Write8(vaddr, value); });
[&] { Memory::Write8(vaddr, value); });
} }
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 2, 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) { bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 4, 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) { bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 8, 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) { bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] {
Memory::Write64(vaddr + 0, value[0]); memory.Write64(vaddr + 0, value[0]);
Memory::Write64(vaddr + 8, value[1]); memory.Write64(vaddr + 8, value[1]);
}); });
} }

View File

@ -12,6 +12,10 @@
#include "core/arm/exclusive_monitor.h" #include "core/arm/exclusive_monitor.h"
#include "core/arm/unicorn/arm_unicorn.h" #include "core/arm/unicorn/arm_unicorn.h"
namespace Memory {
class Memory;
}
namespace Core { namespace Core {
class ARM_Dynarmic_Callbacks; class ARM_Dynarmic_Callbacks;
@ -63,7 +67,7 @@ private:
class DynarmicExclusiveMonitor final : public ExclusiveMonitor { class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
public: public:
explicit DynarmicExclusiveMonitor(std::size_t core_count); explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count);
~DynarmicExclusiveMonitor() override; ~DynarmicExclusiveMonitor() override;
void SetExclusive(std::size_t core_index, VAddr addr) override; void SetExclusive(std::size_t core_index, VAddr addr) override;
@ -78,6 +82,7 @@ public:
private: private:
friend class ARM_Dynarmic; friend class ARM_Dynarmic;
Dynarmic::A64::ExclusiveMonitor monitor; Dynarmic::A64::ExclusiveMonitor monitor;
Memory::Memory& memory;
}; };
} // namespace Core } // namespace Core

View File

@ -66,9 +66,10 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
Cpu::~Cpu() = default; 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 #ifdef ARCHITECTURE_x86_64
return std::make_unique<DynarmicExclusiveMonitor>(num_cores); return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores);
#else #else
// TODO(merry): Passthrough exclusive monitor // TODO(merry): Passthrough exclusive monitor
return nullptr; return nullptr;

View File

@ -24,6 +24,10 @@ namespace Core::Timing {
class CoreTiming; class CoreTiming;
} }
namespace Memory {
class Memory;
}
namespace Core { namespace Core {
class ARM_Interface; class ARM_Interface;
@ -86,7 +90,19 @@ public:
void Shutdown(); 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: private:
void Reschedule(); void Reschedule();

View File

@ -25,7 +25,7 @@ CpuCoreManager::~CpuCoreManager() = default;
void CpuCoreManager::Initialize() { void CpuCoreManager::Initialize() {
barrier = std::make_unique<CpuBarrier>(); 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) { for (std::size_t index = 0; index < cores.size(); ++index) {
cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);

View File

@ -508,8 +508,9 @@ static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
bp->second.len, bp->second.addr, static_cast<int>(type)); bp->second.len, bp->second.addr, static_cast<int>(type));
if (type == BreakpointType::Execute) { if (type == BreakpointType::Execute) {
Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); auto& system = Core::System::GetInstance();
Core::System::GetInstance().InvalidateCpuInstructionCaches(); system.Memory().WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
system.InvalidateCpuInstructionCaches();
} }
p.erase(addr); p.erase(addr);
} }
@ -993,14 +994,14 @@ static void WriteMemory() {
const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset)); const u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
const auto& memory = system.Memory(); auto& memory = system.Memory();
if (!memory.IsValidVirtualAddress(addr)) { if (!memory.IsValidVirtualAddress(addr)) {
return SendReply("E00"); return SendReply("E00");
} }
std::vector<u8> data(len); std::vector<u8> data(len);
GdbHexToMem(data.data(), len_pos + 1, len); GdbHexToMem(data.data(), len_pos + 1, len);
Memory::WriteBlock(addr, data.data(), len); memory.WriteBlock(addr, data.data(), len);
system.InvalidateCpuInstructionCaches(); system.InvalidateCpuInstructionCaches();
SendReply("OK"); SendReply("OK");
} }
@ -1058,13 +1059,14 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
breakpoint.addr = addr; breakpoint.addr = addr;
breakpoint.len = len; 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()); memory.ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4}; static constexpr std::array<u8, 4> btrap{0x00, 0x7d, 0x20, 0xd4};
if (type == BreakpointType::Execute) { if (type == BreakpointType::Execute) {
Memory::WriteBlock(addr, btrap.data(), btrap.size()); memory.WriteBlock(addr, btrap.data(), btrap.size());
Core::System::GetInstance().InvalidateCpuInstructionCaches(); system.InvalidateCpuInstructionCaches();
} }
p.insert({addr, breakpoint}); p.insert({addr, breakpoint});

View File

@ -78,7 +78,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32
return ERR_INVALID_STATE; 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); return SignalToAddressOnly(address, num_to_wake);
} }
@ -117,7 +117,7 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
return ERR_INVALID_STATE; 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); WakeThreads(waiting_threads, num_to_wake);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -151,7 +151,7 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
} }
if (should_decrement) { 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. // Short-circuit without rescheduling, if timeout is zero.

View File

@ -274,7 +274,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
} }
// Copy the translated command buffer back into the thread's command buffer area. // Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), memory.WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32)); dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS; 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 size = buffer_size; // TODO(bunnei): This needs to be HW tested
} }
auto& memory = Core::System::GetInstance().Memory();
if (is_buffer_b) { if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
} else { } else {
Memory::WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
} }
return size; return size;

View File

@ -117,7 +117,7 @@ ResultCode Mutex::Release(VAddr address) {
// There are no more threads waiting for the mutex, release it completely. // There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) { if (thread == nullptr) {
Memory::Write32(address, 0); system.Memory().Write32(address, 0);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -132,7 +132,7 @@ ResultCode Mutex::Release(VAddr address) {
} }
// Grant the mutex to the next waiting thread and resume it. // 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); ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->ResumeFromWait(); thread->ResumeFromWait();

View File

@ -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{}); 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; return RESULT_SUCCESS;
} }
@ -1280,20 +1280,21 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
} }
auto& memory = system.Memory();
const auto& vm_manager = process->VMManager(); const auto& vm_manager = process->VMManager();
const MemoryInfo memory_info = vm_manager.QueryMemory(address); const MemoryInfo memory_info = vm_manager.QueryMemory(address);
Memory::Write64(memory_info_address, memory_info.base_address); memory.Write64(memory_info_address, memory_info.base_address);
Memory::Write64(memory_info_address + 8, memory_info.size); memory.Write64(memory_info_address + 8, memory_info.size);
Memory::Write32(memory_info_address + 16, memory_info.state); memory.Write32(memory_info_address + 16, memory_info.state);
Memory::Write32(memory_info_address + 20, memory_info.attributes); memory.Write32(memory_info_address + 20, memory_info.attributes);
Memory::Write32(memory_info_address + 24, memory_info.permission); 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 + 32, memory_info.ipc_ref_count);
Memory::Write32(memory_info_address + 28, memory_info.device_ref_count); memory.Write32(memory_info_address + 28, memory_info.device_ref_count);
Memory::Write32(memory_info_address + 36, 0); memory.Write32(memory_info_address + 36, 0);
// Page info appears to be currently unused by the kernel and is always set to zero. // 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; return RESULT_SUCCESS;
} }
@ -2290,12 +2291,13 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
return ERR_INVALID_ADDRESS_STATE; return ERR_INVALID_ADDRESS_STATE;
} }
auto& memory = system.Memory();
const auto& process_list = kernel.GetProcessList(); const auto& process_list = kernel.GetProcessList();
const auto num_processes = process_list.size(); const auto num_processes = process_list.size();
const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); 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) { 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); 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; return ERR_INVALID_ADDRESS_STATE;
} }
auto& memory = system.Memory();
const auto& thread_list = current_process->GetThreadList(); const auto& thread_list = current_process->GetThreadList();
const auto num_threads = thread_list.size(); const auto num_threads = thread_list.size();
const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
auto list_iter = thread_list.cbegin(); auto list_iter = thread_list.cbegin();
for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { 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); out_thread_ids += sizeof(u64);
} }

View File

@ -58,35 +58,6 @@ u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
u8* GetPointerFromVMA(VAddr vaddr) { u8* GetPointerFromVMA(VAddr vaddr) {
return ::Memory::GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), 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 } // Anonymous namespace
// Implementation class used to keep the specifics of the memory subsystem hidden // Implementation class used to keep the specifics of the memory subsystem hidden
@ -195,6 +166,22 @@ struct Memory::Impl {
return Read<u64_le>(addr); 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 ReadCString(VAddr vaddr, std::size_t max_length) {
std::string string; std::string string;
string.reserve(max_length); string.reserve(max_length);
@ -259,6 +246,53 @@ struct Memory::Impl {
ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size); 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) { void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
const auto& page_table = process.VMManager().page_table; const auto& page_table = process.VMManager().page_table;
std::size_t remaining_size = size; std::size_t remaining_size = size;
@ -501,6 +535,46 @@ struct Memory::Impl {
return {}; 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; Core::System& system;
}; };
@ -562,6 +636,22 @@ u64 Memory::Read64(const VAddr addr) {
return impl->Read64(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) { std::string Memory::ReadCString(VAddr vaddr, std::size_t max_length) {
return impl->ReadCString(vaddr, 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); 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) { void Memory::ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size) {
impl->ZeroBlock(process, dest_addr, 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; 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 } // namespace Memory

View File

@ -192,6 +192,50 @@ public:
*/ */
u64 Read64(VAddr addr); 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. * Reads a null-terminated string from the given virtual address.
* This function will continually read characters until either: * 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); 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. * 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 /// Determines if the given VAddr is a kernel address
bool IsKernelVirtualAddress(VAddr vaddr); 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 } // namespace Memory

View File

@ -30,7 +30,7 @@ void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
} }
void StandardVmCallbacks::MemoryWrite(VAddr address, const 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() { u64 StandardVmCallbacks::HidKeysDown() {

View File

@ -34,16 +34,16 @@ u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {
void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) { void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
switch (width) { switch (width) {
case 1: case 1:
Memory::Write8(addr, static_cast<u8>(value)); memory.Write8(addr, static_cast<u8>(value));
break; break;
case 2: case 2:
Memory::Write16(addr, static_cast<u16>(value)); memory.Write16(addr, static_cast<u16>(value));
break; break;
case 4: case 4:
Memory::Write32(addr, static_cast<u32>(value)); memory.Write32(addr, static_cast<u32>(value));
break; break;
case 8: case 8:
Memory::Write64(addr, value); memory.Write64(addr, value);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();