Merge pull request #3169 from lioncash/memory

core/memory: Deglobalize memory management code
master
bunnei 2019-11-28 11:43:17 +07:00 committed by GitHub
commit e3ee017e91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1386 additions and 793 deletions

@ -36,9 +36,9 @@ public:
} }
void SetWaveIndex(std::size_t index); void SetWaveIndex(std::size_t index);
std::vector<s16> DequeueSamples(std::size_t sample_count); std::vector<s16> DequeueSamples(std::size_t sample_count, Memory::Memory& memory);
void UpdateState(); void UpdateState();
void RefreshBuffer(); void RefreshBuffer(Memory::Memory& memory);
private: private:
bool is_in_use{}; bool is_in_use{};
@ -66,17 +66,18 @@ public:
return info; return info;
} }
void UpdateState(); void UpdateState(Memory::Memory& memory);
private: private:
EffectOutStatus out_status{}; EffectOutStatus out_status{};
EffectInStatus info{}; EffectInStatus info{};
}; };
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params, AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
AudioRendererParameter params,
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::shared_ptr<Kernel::WritableEvent> buffer_event,
std::size_t instance_number) std::size_t instance_number)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
effects(params.effect_count) { effects(params.effect_count), memory{memory_} {
audio_out = std::make_unique<AudioCore::AudioOut>(); audio_out = std::make_unique<AudioCore::AudioOut>();
stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
@ -162,7 +163,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
} }
for (auto& effect : effects) { for (auto& effect : effects) {
effect.UpdateState(); effect.UpdateState(memory);
} }
// Release previous buffers and queue next ones for playback // Release previous buffers and queue next ones for playback
@ -206,13 +207,14 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
is_refresh_pending = true; is_refresh_pending = true;
} }
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) { std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count,
Memory::Memory& memory) {
if (!IsPlaying()) { if (!IsPlaying()) {
return {}; return {};
} }
if (is_refresh_pending) { if (is_refresh_pending) {
RefreshBuffer(); RefreshBuffer(memory);
} }
const std::size_t max_size{samples.size() - offset}; const std::size_t max_size{samples.size() - offset};
@ -256,10 +258,11 @@ void AudioRenderer::VoiceState::UpdateState() {
is_in_use = info.is_in_use; is_in_use = info.is_in_use;
} }
void AudioRenderer::VoiceState::RefreshBuffer() { void AudioRenderer::VoiceState::RefreshBuffer(Memory::Memory& memory) {
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16)); const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(), const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
info.wave_buffer[wave_index].buffer_sz); std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
switch (static_cast<Codec::PcmFormat>(info.sample_format)) { switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
case Codec::PcmFormat::Int16: { case Codec::PcmFormat::Int16: {
@ -269,7 +272,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
case Codec::PcmFormat::Adpcm: { case Codec::PcmFormat::Adpcm: {
// Decode ADPCM to PCM16 // Decode ADPCM to PCM16
Codec::ADPCM_Coeff coeffs; Codec::ADPCM_Coeff coeffs;
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()), new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
new_samples.size() * sizeof(s16), coeffs, adpcm_state); new_samples.size() * sizeof(s16), coeffs, adpcm_state);
break; break;
@ -307,18 +310,18 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
is_refresh_pending = false; is_refresh_pending = false;
} }
void AudioRenderer::EffectState::UpdateState() { void AudioRenderer::EffectState::UpdateState(Memory::Memory& memory) {
if (info.is_new) { if (info.is_new) {
out_status.state = EffectStatus::New; out_status.state = EffectStatus::New;
} else { } else {
if (info.type == Effect::Aux) { if (info.type == Effect::Aux) {
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0, ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
"Aux buffers tried to update"); "Aux buffers tried to update");
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0, ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
"Aux buffers tried to update"); "Aux buffers tried to update");
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0, ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
"Aux buffers tried to update"); "Aux buffers tried to update");
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0, ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
"Aux buffers tried to update"); "Aux buffers tried to update");
} }
} }
@ -340,7 +343,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
std::size_t offset{}; std::size_t offset{};
s64 samples_remaining{BUFFER_SIZE}; s64 samples_remaining{BUFFER_SIZE};
while (samples_remaining > 0) { while (samples_remaining > 0) {
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)};
if (samples.empty()) { if (samples.empty()) {
break; break;

@ -22,6 +22,10 @@ namespace Kernel {
class WritableEvent; class WritableEvent;
} }
namespace Memory {
class Memory;
}
namespace AudioCore { namespace AudioCore {
class AudioOut; class AudioOut;
@ -217,7 +221,8 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
class AudioRenderer { class AudioRenderer {
public: public:
AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params, AudioRenderer(Core::Timing::CoreTiming& core_timing, Memory::Memory& memory_,
AudioRendererParameter params,
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
~AudioRenderer(); ~AudioRenderer();
@ -238,7 +243,8 @@ private:
std::vector<VoiceState> voices; std::vector<VoiceState> voices;
std::vector<EffectState> effects; std::vector<EffectState> effects;
std::unique_ptr<AudioOut> audio_out; std::unique_ptr<AudioOut> audio_out;
AudioCore::StreamPtr stream; StreamPtr stream;
Memory::Memory& memory;
}; };
} // namespace AudioCore } // namespace AudioCore

@ -509,7 +509,6 @@ add_library(core STATIC
memory/dmnt_cheat_vm.h memory/dmnt_cheat_vm.h
memory.cpp memory.cpp
memory.h memory.h
memory_setup.h
perf_stats.cpp perf_stats.cpp
perf_stats.h perf_stats.h
reporter.cpp reporter.cpp

@ -13,7 +13,6 @@
#include "core/memory.h" #include "core/memory.h"
namespace Core { namespace Core {
namespace { namespace {
constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
@ -61,15 +60,15 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
Symbols GetSymbols(VAddr text_offset) { Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) {
const auto mod_offset = text_offset + Memory::Read32(text_offset + 4); const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
return {}; return {};
} }
const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset; const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
VAddr string_table_offset{}; VAddr string_table_offset{};
VAddr symbol_table_offset{}; VAddr symbol_table_offset{};
@ -77,8 +76,8 @@ Symbols GetSymbols(VAddr text_offset) {
VAddr dynamic_index = dynamic_offset; VAddr dynamic_index = dynamic_offset;
while (true) { while (true) {
const auto tag = Memory::Read64(dynamic_index); const u64 tag = memory.Read64(dynamic_index);
const auto value = Memory::Read64(dynamic_index + 0x8); const u64 value = memory.Read64(dynamic_index + 0x8);
dynamic_index += 0x10; dynamic_index += 0x10;
if (tag == ELF_DYNAMIC_TAG_NULL) { if (tag == ELF_DYNAMIC_TAG_NULL) {
@ -106,11 +105,11 @@ Symbols GetSymbols(VAddr text_offset) {
VAddr symbol_index = symbol_table_address; VAddr symbol_index = symbol_table_address;
while (symbol_index < string_table_address) { while (symbol_index < string_table_address) {
ELFSymbol symbol{}; ELFSymbol symbol{};
Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
VAddr string_offset = string_table_address + symbol.name_index; VAddr string_offset = string_table_address + symbol.name_index;
std::string name; std::string name;
for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) { for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
name += static_cast<char>(c); name += static_cast<char>(c);
} }
@ -142,28 +141,28 @@ constexpr u64 SEGMENT_BASE = 0x7100000000ull;
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
std::vector<BacktraceEntry> out; std::vector<BacktraceEntry> out;
auto& memory = system.Memory();
auto fp = GetReg(29); auto fp = GetReg(29);
auto lr = GetReg(30); auto lr = GetReg(30);
while (true) { while (true) {
out.push_back({"", 0, lr, 0}); out.push_back({"", 0, lr, 0});
if (!fp) { if (!fp) {
break; break;
} }
lr = Memory::Read64(fp + 8) - 4; lr = memory.Read64(fp + 8) - 4;
fp = Memory::Read64(fp); fp = memory.Read64(fp);
} }
std::map<VAddr, std::string> modules; std::map<VAddr, std::string> modules;
auto& loader{System::GetInstance().GetAppLoader()}; auto& loader{system.GetAppLoader()};
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
return {}; return {};
} }
std::map<std::string, Symbols> symbols; std::map<std::string, Symbols> symbols;
for (const auto& module : modules) { for (const auto& module : modules) {
symbols.insert_or_assign(module.second, GetSymbols(module.first)); symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
} }
for (auto& entry : out) { for (auto& entry : out) {

@ -17,11 +17,13 @@ enum class VMAPermission : u8;
} }
namespace Core { namespace Core {
class System;
/// Generic ARMv8 CPU interface /// Generic ARMv8 CPU interface
class ARM_Interface : NonCopyable { class ARM_Interface : NonCopyable {
public: public:
virtual ~ARM_Interface() {} explicit ARM_Interface(System& system_) : system{system_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext { struct ThreadContext {
std::array<u64, 31> cpu_registers; std::array<u64, 31> cpu_registers;
@ -163,6 +165,10 @@ public:
/// fp+0 : pointer to previous frame record /// fp+0 : pointer to previous frame record
/// fp+8 : value of lr for frame /// fp+8 : value of lr for frame
void LogBacktrace() const; void LogBacktrace() const;
protected:
/// System context that this ARM interface is running under.
System& system;
}; };
} // namespace Core } // namespace Core

@ -28,36 +28,38 @@ public:
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
u8 MemoryRead8(u64 vaddr) override { u8 MemoryRead8(u64 vaddr) override {
return Memory::Read8(vaddr); return parent.system.Memory().Read8(vaddr);
} }
u16 MemoryRead16(u64 vaddr) override { u16 MemoryRead16(u64 vaddr) override {
return Memory::Read16(vaddr); return parent.system.Memory().Read16(vaddr);
} }
u32 MemoryRead32(u64 vaddr) override { u32 MemoryRead32(u64 vaddr) override {
return Memory::Read32(vaddr); return parent.system.Memory().Read32(vaddr);
} }
u64 MemoryRead64(u64 vaddr) override { u64 MemoryRead64(u64 vaddr) override {
return Memory::Read64(vaddr); return parent.system.Memory().Read64(vaddr);
} }
Vector MemoryRead128(u64 vaddr) override { Vector MemoryRead128(u64 vaddr) override {
return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)}; auto& memory = parent.system.Memory();
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
} }
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 {
@ -171,9 +173,10 @@ void ARM_Dynarmic::Step() {
ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index) std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, : ARM_Interface{system},
core_index{core_index}, system{system}, cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} core_index{core_index}, exclusive_monitor{
dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
ARM_Dynarmic::~ARM_Dynarmic() = default; ARM_Dynarmic::~ARM_Dynarmic() = default;
@ -264,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) {
@ -277,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]);
}); });
} }

@ -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;
@ -58,13 +62,12 @@ private:
ARM_Unicorn inner_unicorn; ARM_Unicorn inner_unicorn;
std::size_t core_index; std::size_t core_index;
System& system;
DynarmicExclusiveMonitor& exclusive_monitor; DynarmicExclusiveMonitor& exclusive_monitor;
}; };
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;
@ -79,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

@ -60,7 +60,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
return false; return false;
} }
ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { ARM_Unicorn::ARM_Unicorn(System& system) : ARM_Interface{system} {
CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
auto fpv = 3 << 20; auto fpv = 3 << 20;

@ -45,7 +45,6 @@ private:
static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
uc_engine* uc{}; uc_engine* uc{};
System& system;
GDBStub::BreakpointAddress last_bkpt{}; GDBStub::BreakpointAddress last_bkpt{};
bool last_bkpt_hit = false; bool last_bkpt_hit = false;
}; };

@ -39,6 +39,7 @@
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h" #include "core/memory/cheat_engine.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "core/reporter.h" #include "core/reporter.h"
@ -112,8 +113,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
} }
struct System::Impl { struct System::Impl {
explicit Impl(System& system) explicit Impl(System& system)
: kernel{system}, fs_controller{system}, cpu_core_manager{system}, reporter{system}, : kernel{system}, fs_controller{system}, memory{system},
applet_manager{system} {} cpu_core_manager{system}, reporter{system}, applet_manager{system} {}
Cpu& CurrentCpuCore() { Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore(); return cpu_core_manager.GetCurrentCore();
@ -341,7 +342,8 @@ struct System::Impl {
std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core; std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context; std::shared_ptr<Tegra::DebugContext> debug_context;
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
Memory::Memory memory;
CpuCoreManager cpu_core_manager; CpuCoreManager cpu_core_manager;
bool is_powered_on = false; bool is_powered_on = false;
bool exit_lock = false; bool exit_lock = false;
@ -498,6 +500,14 @@ const ExclusiveMonitor& System::Monitor() const {
return impl->cpu_core_manager.GetExclusiveMonitor(); return impl->cpu_core_manager.GetExclusiveMonitor();
} }
Memory::Memory& System::Memory() {
return impl->memory;
}
const Memory::Memory& System::Memory() const {
return impl->memory;
}
Tegra::GPU& System::GPU() { Tegra::GPU& System::GPU() {
return *impl->gpu_core; return *impl->gpu_core;
} }

@ -86,6 +86,10 @@ namespace Core::Hardware {
class InterruptManager; class InterruptManager;
} }
namespace Memory {
class Memory;
}
namespace Core { namespace Core {
class ARM_Interface; class ARM_Interface;
@ -225,6 +229,12 @@ public:
/// Gets a constant reference to the exclusive monitor /// Gets a constant reference to the exclusive monitor
const ExclusiveMonitor& Monitor() const; const ExclusiveMonitor& Monitor() const;
/// Gets a mutable reference to the system memory instance.
Memory::Memory& Memory();
/// Gets a constant reference to the system memory instance.
const Memory::Memory& Memory() const;
/// Gets a mutable reference to the GPU interface /// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU(); Tegra::GPU& GPU();

@ -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;

@ -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();

@ -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);

@ -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);
} }
@ -969,12 +970,13 @@ static void ReadMemory() {
SendReply("E01"); SendReply("E01");
} }
if (!Memory::IsValidVirtualAddress(addr)) { auto& memory = Core::System::GetInstance().Memory();
if (!memory.IsValidVirtualAddress(addr)) {
return SendReply("E00"); return SendReply("E00");
} }
std::vector<u8> data(len); std::vector<u8> data(len);
Memory::ReadBlock(addr, data.data(), len); memory.ReadBlock(addr, data.data(), len);
MemToGdbHex(reply, data.data(), len); MemToGdbHex(reply, data.data(), len);
reply[len * 2] = '\0'; reply[len * 2] = '\0';
@ -984,22 +986,23 @@ static void ReadMemory() {
/// Modify location in memory with data received from the gdb client. /// Modify location in memory with data received from the gdb client.
static void WriteMemory() { static void WriteMemory() {
auto start_offset = command_buffer + 1; auto start_offset = command_buffer + 1;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1; start_offset = addr_pos + 1;
auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); const auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
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));
if (!Memory::IsValidVirtualAddress(addr)) { auto& system = Core::System::GetInstance();
auto& memory = system.Memory();
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);
Core::System::GetInstance().InvalidateCpuInstructionCaches(); system.InvalidateCpuInstructionCaches();
SendReply("OK"); SendReply("OK");
} }
@ -1055,12 +1058,15 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
breakpoint.active = true; breakpoint.active = true;
breakpoint.addr = addr; breakpoint.addr = addr;
breakpoint.len = len; breakpoint.len = len;
Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
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}; 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});

@ -67,23 +67,27 @@ ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) { s32 num_to_wake) {
auto& memory = system.Memory();
// Ensure that we can write to the address. // Ensure that we can write to the address.
if (!Memory::IsValidVirtualAddress(address)) { if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE; return ERR_INVALID_ADDRESS_STATE;
} }
if (static_cast<s32>(Memory::Read32(address)) != value) { if (static_cast<s32>(memory.Read32(address)) != value) {
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);
} }
ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
s32 num_to_wake) { s32 num_to_wake) {
auto& memory = system.Memory();
// Ensure that we can write to the address. // Ensure that we can write to the address.
if (!Memory::IsValidVirtualAddress(address)) { if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE; return ERR_INVALID_ADDRESS_STATE;
} }
@ -109,11 +113,11 @@ ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr a
} }
} }
if (static_cast<s32>(Memory::Read32(address)) != value) { if (static_cast<s32>(memory.Read32(address)) != value) {
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;
} }
@ -134,18 +138,20 @@ ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s
ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
bool should_decrement) { bool should_decrement) {
auto& memory = system.Memory();
// Ensure that we can read the address. // Ensure that we can read the address.
if (!Memory::IsValidVirtualAddress(address)) { if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE; return ERR_INVALID_ADDRESS_STATE;
} }
const s32 cur_value = static_cast<s32>(Memory::Read32(address)); const s32 cur_value = static_cast<s32>(memory.Read32(address));
if (cur_value >= value) { if (cur_value >= value) {
return ERR_INVALID_STATE; return ERR_INVALID_STATE;
} }
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.
@ -157,15 +163,19 @@ ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s6
} }
ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) { ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
auto& memory = system.Memory();
// Ensure that we can read the address. // Ensure that we can read the address.
if (!Memory::IsValidVirtualAddress(address)) { if (!memory.IsValidVirtualAddress(address)) {
return ERR_INVALID_ADDRESS_STATE; return ERR_INVALID_ADDRESS_STATE;
} }
// Only wait for the address if equal. // Only wait for the address if equal.
if (static_cast<s32>(Memory::Read32(address)) != value) { if (static_cast<s32>(memory.Read32(address)) != value) {
return ERR_INVALID_STATE; return ERR_INVALID_STATE;
} }
// Short-circuit without rescheduling, if timeout is zero.
// Short-circuit without rescheduling if timeout is zero.
if (timeout == 0) { if (timeout == 0) {
return RESULT_TIMEOUT; return RESULT_TIMEOUT;
} }

@ -21,10 +21,10 @@ ClientSession::~ClientSession() {
} }
} }
ResultCode ClientSession::SendSyncRequest(Thread* thread) { ResultCode ClientSession::SendSyncRequest(Thread* thread, Memory::Memory& memory) {
// Signal the server session that new data is available // Signal the server session that new data is available
if (auto server = parent->server.lock()) { if (auto server = parent->server.lock()) {
return server->HandleSyncRequest(SharedFrom(thread)); return server->HandleSyncRequest(SharedFrom(thread), memory);
} }
return ERR_SESSION_CLOSED_BY_REMOTE; return ERR_SESSION_CLOSED_BY_REMOTE;

@ -10,6 +10,10 @@
union ResultCode; union ResultCode;
namespace Memory {
class Memory;
}
namespace Kernel { namespace Kernel {
class KernelCore; class KernelCore;
@ -37,7 +41,7 @@ public:
return HANDLE_TYPE; return HANDLE_TYPE;
} }
ResultCode SendSyncRequest(Thread* thread); ResultCode SendSyncRequest(Thread* thread, Memory::Memory& memory);
private: private:
/// The parent session, which links to the server endpoint. /// The parent session, which links to the server endpoint.

@ -214,9 +214,10 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
auto& owner_process = *thread.GetOwnerProcess(); auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable(); auto& handle_table = owner_process.GetHandleTable();
auto& memory = Core::System::GetInstance().Memory();
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), memory.ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32)); dst_cmdbuf.size() * sizeof(u32));
// The header was already built in the internal command buffer. Attempt to parse it to verify // The header was already built in the internal command buffer. Attempt to parse it to verify
@ -273,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;
@ -282,15 +283,14 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
std::vector<u8> buffer; std::vector<u8> buffer;
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
auto& memory = Core::System::GetInstance().Memory();
if (is_buffer_a) { if (is_buffer_a) {
buffer.resize(BufferDescriptorA()[buffer_index].Size()); buffer.resize(BufferDescriptorA()[buffer_index].Size());
Memory::ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
buffer.size());
} else { } else {
buffer.resize(BufferDescriptorX()[buffer_index].Size()); buffer.resize(BufferDescriptorX()[buffer_index].Size());
Memory::ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
buffer.size());
} }
return buffer; return buffer;
@ -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;

@ -154,6 +154,16 @@ struct KernelCore::Impl {
system.CoreTiming().ScheduleEvent(time_interval, preemption_event); system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
} }
void MakeCurrentProcess(Process* process) {
current_process = process;
if (process == nullptr) {
return;
}
system.Memory().SetCurrentPageTable(*process);
}
std::atomic<u32> next_object_id{0}; std::atomic<u32> next_object_id{0};
std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
@ -208,13 +218,7 @@ void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
} }
void KernelCore::MakeCurrentProcess(Process* process) { void KernelCore::MakeCurrentProcess(Process* process) {
impl->current_process = process; impl->MakeCurrentProcess(process);
if (process == nullptr) {
return;
}
Memory::SetCurrentPageTable(*process);
} }
Process* KernelCore::CurrentProcess() { Process* KernelCore::CurrentProcess() {

@ -79,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
// thread. // thread.
ASSERT(requesting_thread == current_thread); ASSERT(requesting_thread == current_thread);
const u32 addr_value = Memory::Read32(address); const u32 addr_value = system.Memory().Read32(address);
// If the mutex isn't being held, just return success. // If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
@ -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();

@ -19,6 +19,7 @@
#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h" #include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h" #include "core/hle/kernel/thread.h"
#include "core/memory.h"
namespace Kernel { namespace Kernel {
@ -127,12 +128,13 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) { ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
Memory::Memory& memory) {
// The ServerSession received a sync request, this means that there's new data available // The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar. // similar.
Kernel::HLERequestContext context(SharedFrom(this), thread); Kernel::HLERequestContext context(SharedFrom(this), thread);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); u32* cmd_buf = (u32*)memory.GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;

@ -13,6 +13,10 @@
#include "core/hle/kernel/wait_object.h" #include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Memory {
class Memory;
}
namespace Kernel { namespace Kernel {
class ClientPort; class ClientPort;
@ -85,10 +89,13 @@ public:
/** /**
* Handle a sync request from the emulated application. * Handle a sync request from the emulated application.
*
* @param thread Thread that initiated the request. * @param thread Thread that initiated the request.
* @param memory Memory context to handle the sync request under.
*
* @returns ResultCode from the operation. * @returns ResultCode from the operation.
*/ */
ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread); ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory);
bool ShouldWait(const Thread* thread) const override; bool ShouldWait(const Thread* thread) const override;

@ -332,7 +332,9 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
/// Connect to an OS service given the port name, returns the handle to the port to out /// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
VAddr port_name_address) { VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address)) { auto& memory = system.Memory();
if (!memory.IsValidVirtualAddress(port_name_address)) {
LOG_ERROR(Kernel_SVC, LOG_ERROR(Kernel_SVC,
"Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
port_name_address); port_name_address);
@ -341,7 +343,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
static constexpr std::size_t PortNameMaxLength = 11; static constexpr std::size_t PortNameMaxLength = 11;
// Read 1 char beyond the max allowed port name to detect names that are too long. // Read 1 char beyond the max allowed port name to detect names that are too long.
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
if (port_name.size() > PortNameMaxLength) { if (port_name.size() > PortNameMaxLength) {
LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength, LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
port_name.size()); port_name.size());
@ -383,7 +385,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
// TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
// responds and cause a reschedule. // responds and cause a reschedule.
return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread()); return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread(), system.Memory());
} }
/// Get the ID for the specified thread. /// Get the ID for the specified thread.
@ -452,7 +454,8 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
handles_address, handle_count, nano_seconds); handles_address, handle_count, nano_seconds);
if (!Memory::IsValidVirtualAddress(handles_address)) { auto& memory = system.Memory();
if (!memory.IsValidVirtualAddress(handles_address)) {
LOG_ERROR(Kernel_SVC, LOG_ERROR(Kernel_SVC,
"Handle address is not a valid virtual address, handle_address=0x{:016X}", "Handle address is not a valid virtual address, handle_address=0x{:016X}",
handles_address); handles_address);
@ -474,7 +477,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
for (u64 i = 0; i < handle_count; ++i) { for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
const auto object = handle_table.Get<WaitObject>(handle); const auto object = handle_table.Get<WaitObject>(handle);
if (object == nullptr) { if (object == nullptr) {
@ -616,13 +619,15 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
return; return;
} }
auto& memory = system.Memory();
// This typically is an error code so we're going to assume this is the case // This typically is an error code so we're going to assume this is the case
if (sz == sizeof(u32)) { if (sz == sizeof(u32)) {
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
} else { } else {
// We don't know what's in here so we'll hexdump it // We don't know what's in here so we'll hexdump it
debug_buffer.resize(sz); debug_buffer.resize(sz);
Memory::ReadBlock(addr, debug_buffer.data(), sz); memory.ReadBlock(addr, debug_buffer.data(), sz);
std::string hexdump; std::string hexdump;
for (std::size_t i = 0; i < debug_buffer.size(); i++) { for (std::size_t i = 0; i < debug_buffer.size(); i++) {
hexdump += fmt::format("{:02X} ", debug_buffer[i]); hexdump += fmt::format("{:02X} ", debug_buffer[i]);
@ -712,7 +717,7 @@ static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr addre
} }
std::string str(len, '\0'); std::string str(len, '\0');
Memory::ReadBlock(address, str.data(), str.size()); system.Memory().ReadBlock(address, str.data(), str.size());
LOG_DEBUG(Debug_Emulated, "{}", str); LOG_DEBUG(Debug_Emulated, "{}", str);
} }
@ -1115,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;
} }
@ -1275,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;
} }
@ -1672,6 +1678,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
const std::size_t current_core = system.CurrentCoreIndex(); const std::size_t current_core = system.CurrentCoreIndex();
auto& monitor = system.Monitor(); auto& monitor = system.Monitor();
auto& memory = system.Memory();
// Atomically read the value of the mutex. // Atomically read the value of the mutex.
u32 mutex_val = 0; u32 mutex_val = 0;
@ -1681,7 +1688,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
monitor.SetExclusive(current_core, mutex_address); monitor.SetExclusive(current_core, mutex_address);
// If the mutex is not yet acquired, acquire it. // If the mutex is not yet acquired, acquire it.
mutex_val = Memory::Read32(mutex_address); mutex_val = memory.Read32(mutex_address);
if (mutex_val != 0) { if (mutex_val != 0) {
update_val = mutex_val | Mutex::MutexHasWaitersFlag; update_val = mutex_val | Mutex::MutexHasWaitersFlag;
@ -2284,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);
} }
@ -2323,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);
} }

@ -162,13 +162,13 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
return ERR_INVALID_PROCESSOR_ID; return ERR_INVALID_PROCESSOR_ID;
} }
if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { auto& system = Core::System::GetInstance();
if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
// TODO (bunnei): Find the correct error code to use here // TODO (bunnei): Find the correct error code to use here
return RESULT_UNKNOWN; return RESULT_UNKNOWN;
} }
auto& system = Core::System::GetInstance();
std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
thread->thread_id = kernel.CreateNewThreadID(); thread->thread_id = kernel.CreateNewThreadID();

@ -16,7 +16,6 @@
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/memory_setup.h"
namespace Kernel { namespace Kernel {
namespace { namespace {
@ -786,19 +785,21 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
} }
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
auto& memory = system.Memory();
switch (vma.type) { switch (vma.type) {
case VMAType::Free: case VMAType::Free:
Memory::UnmapRegion(page_table, vma.base, vma.size); memory.UnmapRegion(page_table, vma.base, vma.size);
break; break;
case VMAType::AllocatedMemoryBlock: case VMAType::AllocatedMemoryBlock:
Memory::MapMemoryRegion(page_table, vma.base, vma.size, memory.MapMemoryRegion(page_table, vma.base, vma.size,
vma.backing_block->data() + vma.offset); vma.backing_block->data() + vma.offset);
break; break;
case VMAType::BackingMemory: case VMAType::BackingMemory:
Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
break; break;
case VMAType::MMIO: case VMAType::MMIO:
Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
break; break;
} }
} }

@ -43,7 +43,8 @@ public:
IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core, IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core,
std::string&& device_name, std::string&& unique_name) std::string&& device_name, std::string&& unique_name)
: ServiceFramework("IAudioOut"), audio_core(audio_core), : ServiceFramework("IAudioOut"), audio_core(audio_core),
device_name(std::move(device_name)), audio_params(audio_params) { device_name(std::move(device_name)),
audio_params(audio_params), main_memory{system.Memory()} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@ -137,7 +138,7 @@ private:
const u64 tag{rp.Pop<u64>()}; const u64 tag{rp.Pop<u64>()};
std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@ -209,6 +210,7 @@ private:
/// This is the event handle used to check if the audio buffer was released /// This is the event handle used to check if the audio buffer was released
Kernel::EventPair buffer_event; Kernel::EventPair buffer_event;
Memory::Memory& main_memory;
}; };
AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} {

@ -49,8 +49,9 @@ public:
system_event = system_event =
Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent"); Kernel::WritableEvent::CreateEventPair(system.Kernel(), "IAudioRenderer:SystemEvent");
renderer = std::make_unique<AudioCore::AudioRenderer>( renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), system.Memory(),
system.CoreTiming(), audren_params, system_event.writable, instance_number); audren_params, system_event.writable,
instance_number);
} }
private: private:

@ -391,13 +391,10 @@ public:
} }
void RenameFile(Kernel::HLERequestContext& ctx) { void RenameFile(Kernel::HLERequestContext& ctx) {
std::vector<u8> buffer; std::vector<u8> buffer = ctx.ReadBuffer(0);
buffer.resize(ctx.BufferDescriptorX()[0].Size());
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
const std::string src_name = Common::StringFromBuffer(buffer); const std::string src_name = Common::StringFromBuffer(buffer);
buffer.resize(ctx.BufferDescriptorX()[1].Size()); buffer = ctx.ReadBuffer(1);
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
const std::string dst_name = Common::StringFromBuffer(buffer); const std::string dst_name = Common::StringFromBuffer(buffer);
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);

@ -140,9 +140,10 @@ public:
rb.Push(ERROR_INVALID_SIZE); rb.Push(ERROR_INVALID_SIZE);
return; return;
} }
// Read NRR data from memory // Read NRR data from memory
std::vector<u8> nrr_data(nrr_size); std::vector<u8> nrr_data(nrr_size);
Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); system.Memory().ReadBlock(nrr_address, nrr_data.data(), nrr_size);
NRRHeader header; NRRHeader header;
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
@ -291,7 +292,7 @@ public:
// Read NRO data from memory // Read NRO data from memory
std::vector<u8> nro_data(nro_size); std::vector<u8> nro_data(nro_size);
Memory::ReadBlock(nro_address, nro_data.data(), nro_size); system.Memory().ReadBlock(nro_address, nro_data.data(), nro_size);
SHA256Hash hash{}; SHA256Hash hash{};
mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0); mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0);

@ -17,7 +17,8 @@ namespace Service::LM {
class ILogger final : public ServiceFramework<ILogger> { class ILogger final : public ServiceFramework<ILogger> {
public: public:
ILogger(Manager& manager) : ServiceFramework("ILogger"), manager(manager) { explicit ILogger(Manager& manager_, Memory::Memory& memory_)
: ServiceFramework("ILogger"), manager{manager_}, memory{memory_} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &ILogger::Log, "Log"}, {0, &ILogger::Log, "Log"},
{1, &ILogger::SetDestination, "SetDestination"}, {1, &ILogger::SetDestination, "SetDestination"},
@ -35,15 +36,15 @@ private:
MessageHeader header{}; MessageHeader header{};
VAddr addr{ctx.BufferDescriptorX()[0].Address()}; VAddr addr{ctx.BufferDescriptorX()[0].Address()};
const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size}; const VAddr end_addr{addr + ctx.BufferDescriptorX()[0].size};
Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); memory.ReadBlock(addr, &header, sizeof(MessageHeader));
addr += sizeof(MessageHeader); addr += sizeof(MessageHeader);
FieldMap fields; FieldMap fields;
while (addr < end_addr) { while (addr < end_addr) {
const auto field = static_cast<Field>(Memory::Read8(addr++)); const auto field = static_cast<Field>(memory.Read8(addr++));
const auto length = Memory::Read8(addr++); const auto length = memory.Read8(addr++);
if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { if (static_cast<Field>(memory.Read8(addr)) == Field::Skip) {
++addr; ++addr;
} }
@ -54,7 +55,7 @@ private:
} }
std::vector<u8> data(length); std::vector<u8> data(length);
Memory::ReadBlock(addr, data.data(), length); memory.ReadBlock(addr, data.data(), length);
fields.emplace(field, std::move(data)); fields.emplace(field, std::move(data));
} }
@ -74,11 +75,13 @@ private:
} }
Manager& manager; Manager& manager;
Memory::Memory& memory;
}; };
class LM final : public ServiceFramework<LM> { class LM final : public ServiceFramework<LM> {
public: public:
explicit LM(Manager& manager) : ServiceFramework{"lm"}, manager(manager) { explicit LM(Manager& manager_, Memory::Memory& memory_)
: ServiceFramework{"lm"}, manager{manager_}, memory{memory_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, &LM::OpenLogger, "OpenLogger"}, {0, &LM::OpenLogger, "OpenLogger"},
@ -94,14 +97,16 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS); rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILogger>(manager); rb.PushIpcInterface<ILogger>(manager, memory);
} }
Manager& manager; Manager& manager;
Memory::Memory& memory;
}; };
void InstallInterfaces(Core::System& system) { void InstallInterfaces(Core::System& system) {
std::make_shared<LM>(system.GetLogManager())->InstallAsService(system.ServiceManager()); std::make_shared<LM>(system.GetLogManager(), system.Memory())
->InstallAsService(system.ServiceManager());
} }
} // namespace Service::LM } // namespace Service::LM

@ -191,7 +191,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
std::memcpy(entries.data(), input2.data(), std::memcpy(entries.data(), input2.data(),
params.num_entries * sizeof(Tegra::CommandListHeader)); params.num_entries * sizeof(Tegra::CommandListHeader));
} else { } else {
Memory::ReadBlock(params.address, entries.data(), system.Memory().ReadBlock(params.address, entries.data(),
params.num_entries * sizeof(Tegra::CommandListHeader)); params.num_entries * sizeof(Tegra::CommandListHeader));
} }
UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);

@ -17,59 +17,27 @@
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/vm_manager.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/memory_setup.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
namespace Memory { namespace Memory {
static Common::PageTable* current_page_table = nullptr; // Implementation class used to keep the specifics of the memory subsystem hidden
// from outside classes. This also allows modification to the internals of the memory
// subsystem without needing to rebuild all files that make use of the memory interface.
struct Memory::Impl {
explicit Impl(Core::System& system_) : system{system_} {}
void SetCurrentPageTable(Kernel::Process& process) { void SetCurrentPageTable(Kernel::Process& process) {
current_page_table = &process.VMManager().page_table; current_page_table = &process.VMManager().page_table;
const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth(); const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth();
auto& system = Core::System::GetInstance();
system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);
system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width); system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width); system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
} }
static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
Common::PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (Core::System::GetInstance().IsPoweredOn()) {
auto& gpu = Core::System::GetInstance().GPU();
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
}
}
}
VAddr end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
base + page_table.pointers.size());
std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
if (memory == nullptr) {
std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory);
} else {
while (base != end) {
page_table.pointers[base] = memory;
base += 1;
memory += PAGE_SIZE;
}
}
}
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
@ -80,10 +48,12 @@ void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler) { Common::MemoryHookPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Special); MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr,
Common::PageType::Special);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, std::move(mmio_handler)}; const Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice,
std::move(mmio_handler)};
page_table.special_regions.add( page_table.special_regions.add(
std::make_pair(interval, std::set<Common::SpecialRegion>{region})); std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
} }
@ -91,33 +61,57 @@ void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) { void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, Common::PageType::Unmapped); MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr,
Common::PageType::Unmapped);
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
page_table.special_regions.erase(interval); page_table.special_regions.erase(interval);
} }
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size, void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) { Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.add( page_table.special_regions.add(
std::make_pair(interval, std::set<Common::SpecialRegion>{region})); std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
} }
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size, void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) { Common::MemoryHookPointer hook) {
auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1);
Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)};
page_table.special_regions.subtract( page_table.special_regions.subtract(
std::make_pair(interval, std::set<Common::SpecialRegion>{region})); std::make_pair(interval, std::set<Common::SpecialRegion>{region}));
} }
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
const auto& page_table = process.VMManager().page_table;
const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
if (page_pointer != nullptr) {
return true;
}
if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) {
return true;
}
if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) {
return false;
}
return false;
}
bool IsValidVirtualAddress(VAddr vaddr) const {
return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);
}
/** /**
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
* using a VMA from the current process * using a VMA from the current process
*/ */
static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
const auto& vm_manager = process.VMManager(); const auto& vm_manager = process.VMManager();
const auto it = vm_manager.FindVMA(vaddr); const auto it = vm_manager.FindVMA(vaddr);
@ -145,97 +139,13 @@ static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
* Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
* using a VMA from the current process. * using a VMA from the current process.
*/ */
static u8* GetPointerFromVMA(VAddr vaddr) { u8* GetPointerFromVMA(VAddr vaddr) {
return GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr); return GetPointerFromVMA(*system.CurrentProcess(), vaddr);
}
template <typename T>
T Read(const VAddr vaddr) {
const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
// NOTE: Avoid adding any extra logic to this fast-path block
T value;
std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
return value;
}
Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
return 0;
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
case Common::PageType::RasterizerCachedMemory: {
auto host_ptr{GetPointerFromVMA(vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
T value;
std::memcpy(&value, host_ptr, sizeof(T));
return value;
}
default:
UNREACHABLE();
}
return {};
}
template <typename T>
void Write(const VAddr vaddr, const T data) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) {
// 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: {
auto host_ptr{GetPointerFromVMA(vaddr)};
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), sizeof(T));
std::memcpy(host_ptr, &data, sizeof(T));
break;
}
default:
UNREACHABLE();
}
}
bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
const auto& page_table = process.VMManager().page_table;
const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
if (page_pointer)
return true;
if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory)
return true;
if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special)
return false;
return false;
}
bool IsValidVirtualAddress(const VAddr vaddr) {
return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr);
}
bool IsKernelVirtualAddress(const VAddr vaddr) {
return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
} }
u8* GetPointer(const VAddr vaddr) { u8* GetPointer(const VAddr vaddr) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
if (page_pointer) { if (page_pointer != nullptr) {
return page_pointer + (vaddr & PAGE_MASK); return page_pointer + (vaddr & PAGE_MASK);
} }
@ -248,83 +158,6 @@ u8* GetPointer(const VAddr vaddr) {
return nullptr; return nullptr;
} }
std::string ReadCString(VAddr vaddr, std::size_t max_length) {
std::string string;
string.reserve(max_length);
for (std::size_t i = 0; i < max_length; ++i) {
char c = Read8(vaddr);
if (c == '\0')
break;
string.push_back(c);
++vaddr;
}
string.shrink_to_fit();
return string;
}
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
}
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU address
// space, marking the region as un/cached. The region is marked un/cached at a granularity of
// CPU pages, hence why we iterate on a CPU page basis (note: GPU page size is different). This
// assumes the specified GPU address region is contiguous as well.
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::Memory:
page_type = Common::PageType::RasterizerCachedMemory;
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
break;
case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already marked as cached.
break;
default:
UNREACHABLE();
}
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common that
// this area is already unmarked as cached.
break;
case Common::PageType::RasterizerCachedMemory: {
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
if (pointer == nullptr) {
// It's possible that this function has been called while updating the pagetable
// after unmapping a VMA. In that case the underlying VMA will no longer exist,
// and we should just leave the pagetable entry blank.
page_type = Common::PageType::Unmapped;
} else {
page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
}
break;
}
default:
UNREACHABLE();
}
}
}
}
u8 Read8(const VAddr addr) { u8 Read8(const VAddr addr) {
return Read<u8>(addr); return Read<u8>(addr);
} }
@ -341,55 +174,6 @@ u64 Read64(const VAddr addr) {
return Read<u64_le>(addr); return Read<u64_le>(addr);
} }
void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_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 = src_addr >> PAGE_BITS;
std::size_t page_offset = src_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 ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* src_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)};
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(dest_buffer, host_ptr, copy_amount);
break;
}
default:
UNREACHABLE();
}
page_index++;
page_offset = 0;
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
remaining_size -= copy_amount;
}
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size);
}
void Write8(const VAddr addr, const u8 data) { void Write8(const VAddr addr, const u8 data) {
Write<u8>(addr, data); Write<u8>(addr, data);
} }
@ -406,6 +190,70 @@ void Write64(const VAddr addr, const u64 data) {
Write<u64_le>(addr, data); Write<u64_le>(addr, data);
} }
std::string ReadCString(VAddr vaddr, std::size_t max_length) {
std::string string;
string.reserve(max_length);
for (std::size_t i = 0; i < max_length; ++i) {
const char c = Read8(vaddr);
if (c == '\0') {
break;
}
string.push_back(c);
++vaddr;
}
string.shrink_to_fit();
return string;
}
void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_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 = src_addr >> PAGE_BITS;
std::size_t page_offset = src_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 ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, src_addr, size);
std::memset(dest_buffer, 0, copy_amount);
break;
}
case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]);
const u8* const src_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_buffer, src_ptr, copy_amount);
break;
}
case Common::PageType::RasterizerCachedMemory: {
const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(dest_buffer, host_ptr, copy_amount);
break;
}
default:
UNREACHABLE();
}
page_index++;
page_offset = 0;
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
remaining_size -= copy_amount;
}
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
ReadBlock(*system.CurrentProcess(), src_addr, dest_buffer, size);
}
void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
const std::size_t size) { const std::size_t size) {
const auto& page_table = process.VMManager().page_table; const auto& page_table = process.VMManager().page_table;
@ -416,7 +264,7 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
while (remaining_size > 0) { while (remaining_size > 0) {
const std::size_t copy_amount = const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 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); const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) { switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
@ -428,13 +276,13 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
case Common::PageType::Memory: { case Common::PageType::Memory: {
DEBUG_ASSERT(page_table.pointers[page_index]); DEBUG_ASSERT(page_table.pointers[page_index]);
u8* dest_ptr = page_table.pointers[page_index] + page_offset; u8* const dest_ptr = page_table.pointers[page_index] + page_offset;
std::memcpy(dest_ptr, src_buffer, copy_amount); std::memcpy(dest_ptr, src_buffer, copy_amount);
break; break;
} }
case Common::PageType::RasterizerCachedMemory: { case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
std::memcpy(host_ptr, src_buffer, copy_amount); std::memcpy(host_ptr, src_buffer, copy_amount);
break; break;
} }
@ -450,7 +298,7 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
} }
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { 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); 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) {
@ -462,7 +310,7 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
while (remaining_size > 0) { while (remaining_size > 0) {
const std::size_t copy_amount = const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 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); const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) { switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
@ -479,8 +327,8 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
break; break;
} }
case Common::PageType::RasterizerCachedMemory: { case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
Core::System::GetInstance().GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount); system.GPU().InvalidateRegion(ToCacheAddr(host_ptr), copy_amount);
std::memset(host_ptr, 0, copy_amount); std::memset(host_ptr, 0, copy_amount);
break; break;
} }
@ -494,6 +342,10 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std:
} }
} }
void ZeroBlock(const VAddr dest_addr, const std::size_t size) {
ZeroBlock(*system.CurrentProcess(), dest_addr, size);
}
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
const std::size_t size) { const std::size_t size) {
const auto& page_table = process.VMManager().page_table; const auto& page_table = process.VMManager().page_table;
@ -504,7 +356,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
while (remaining_size > 0) { while (remaining_size > 0) {
const std::size_t copy_amount = const std::size_t copy_amount =
std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); 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); const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
switch (page_table.attributes[page_index]) { switch (page_table.attributes[page_index]) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
@ -521,8 +373,8 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
break; break;
} }
case Common::PageType::RasterizerCachedMemory: { case Common::PageType::RasterizerCachedMemory: {
const auto& host_ptr{GetPointerFromVMA(process, current_vaddr)}; const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr);
Core::System::GetInstance().GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount); system.GPU().FlushRegion(ToCacheAddr(host_ptr), copy_amount);
WriteBlock(process, dest_addr, host_ptr, copy_amount); WriteBlock(process, dest_addr, host_ptr, copy_amount);
break; break;
} }
@ -539,7 +391,325 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
} }
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size); return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
}
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
}
// Iterate over a contiguous CPU address space, which corresponds to the specified GPU
// address space, marking the region as un/cached. The region is marked un/cached at a
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
// is different). This assumes the specified GPU address region is contiguous as well.
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::Memory:
page_type = Common::PageType::RasterizerCachedMemory;
current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
break;
case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common
// that this area is already marked as cached.
break;
default:
UNREACHABLE();
}
} else {
// Switch page type to uncached if now uncached
switch (page_type) {
case Common::PageType::Unmapped:
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common
// that this area is already unmarked as cached.
break;
case Common::PageType::RasterizerCachedMemory: {
u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK);
if (pointer == nullptr) {
// It's possible that this function has been called while updating the
// pagetable after unmapping a VMA. In that case the underlying VMA will no
// longer exist, and we should just leave the pagetable entry blank.
page_type = Common::PageType::Unmapped;
} else {
page_type = Common::PageType::Memory;
current_page_table->pointers[vaddr >> PAGE_BITS] = pointer;
}
break;
}
default:
UNREACHABLE();
}
}
}
}
/**
* Maps a region of pages as a specific type.
*
* @param page_table The page table to use to perform the mapping.
* @param base The base address to begin mapping at.
* @param size The total size of the range in bytes.
* @param memory The memory to map.
* @param type The page type to map the memory as.
*/
void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory,
Common::PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (system.IsPoweredOn()) {
auto& gpu = system.GPU();
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
}
}
}
const VAddr end = base + size;
ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
base + page_table.pointers.size());
std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
if (memory == nullptr) {
std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end,
memory);
} else {
while (base != end) {
page_table.pointers[base] = memory;
base += 1;
memory += PAGE_SIZE;
}
}
}
/**
* Reads a particular data type out of memory at the given virtual address.
*
* @param vaddr The virtual address to read the data type from.
*
* @tparam T The data type to read out of memory. This type *must* be
* trivially copyable, otherwise the behavior of this function
* is undefined.
*
* @returns The instance of T read from the specified virtual address.
*/
template <typename T>
T Read(const VAddr vaddr) {
const 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
T value;
std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T));
return value;
}
const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
switch (type) {
case Common::PageType::Unmapped:
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
return 0;
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);
break;
case Common::PageType::RasterizerCachedMemory: {
const u8* const host_ptr = GetPointerFromVMA(vaddr);
system.GPU().FlushRegion(ToCacheAddr(host_ptr), sizeof(T));
T value;
std::memcpy(&value, host_ptr, sizeof(T));
return value;
}
default:
UNREACHABLE();
}
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();
}
}
Common::PageTable* current_page_table = nullptr;
Core::System& system;
};
Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
Memory::~Memory() = default;
void Memory::SetCurrentPageTable(Kernel::Process& process) {
impl->SetCurrentPageTable(process);
}
void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) {
impl->MapMemoryRegion(page_table, base, size, target);
}
void Memory::MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler) {
impl->MapIoRegion(page_table, base, size, std::move(mmio_handler));
}
void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
impl->UnmapRegion(page_table, base, size);
}
void Memory::AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) {
impl->AddDebugHook(page_table, base, size, std::move(hook));
}
void Memory::RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook) {
impl->RemoveDebugHook(page_table, base, size, std::move(hook));
}
bool Memory::IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
return impl->IsValidVirtualAddress(process, vaddr);
}
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
return impl->IsValidVirtualAddress(vaddr);
}
u8* Memory::GetPointer(VAddr vaddr) {
return impl->GetPointer(vaddr);
}
const u8* Memory::GetPointer(VAddr vaddr) const {
return impl->GetPointer(vaddr);
}
u8 Memory::Read8(const VAddr addr) {
return impl->Read8(addr);
}
u16 Memory::Read16(const VAddr addr) {
return impl->Read16(addr);
}
u32 Memory::Read32(const VAddr addr) {
return impl->Read32(addr);
}
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);
}
void Memory::ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
const std::size_t size) {
impl->ReadBlock(process, src_addr, dest_buffer, size);
}
void Memory::ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t 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);
}
void Memory::ZeroBlock(VAddr dest_addr, std::size_t size) {
impl->ZeroBlock(dest_addr, size);
}
void Memory::CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
const std::size_t size) {
impl->CopyBlock(process, dest_addr, src_addr, size);
}
void Memory::CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
impl->CopyBlock(dest_addr, src_addr, size);
}
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
bool IsKernelVirtualAddress(const VAddr vaddr) {
return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
} }
} // namespace Memory } // namespace Memory

@ -5,8 +5,18 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <memory>
#include <string> #include <string>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/memory_hook.h"
namespace Common {
struct PageTable;
}
namespace Core {
class System;
}
namespace Kernel { namespace Kernel {
class Process; class Process;
@ -36,41 +46,369 @@ enum : VAddr {
KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE, KERNEL_REGION_END = KERNEL_REGION_VADDR + KERNEL_REGION_SIZE,
}; };
/// Changes the currently active page table to that of /// Central class that handles all memory operations and state.
/// the given process instance. class Memory {
public:
explicit Memory(Core::System& system);
~Memory();
Memory(const Memory&) = delete;
Memory& operator=(const Memory&) = delete;
Memory(Memory&&) = default;
Memory& operator=(Memory&&) = default;
/**
* Changes the currently active page table to that of the given process instance.
*
* @param process The process to use the page table of.
*/
void SetCurrentPageTable(Kernel::Process& process); void SetCurrentPageTable(Kernel::Process& process);
/// Determines if the given VAddr is valid for the specified process. /**
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); * Maps an allocated buffer onto a region of the emulated process address space.
bool IsValidVirtualAddress(VAddr vaddr); *
/// Determines if the given VAddr is a kernel address * @param page_table The page table of the emulated process.
bool IsKernelVirtualAddress(VAddr vaddr); * @param base The address to start mapping at. Must be page-aligned.
* @param size The amount of bytes to map. Must be page-aligned.
* @param target Buffer with the memory backing the mapping. Must be of length at least
* `size`.
*/
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
u8 Read8(VAddr addr); /**
u16 Read16(VAddr addr); * Maps a region of the emulated process address space as a IO region.
u32 Read32(VAddr addr); *
u64 Read64(VAddr addr); * @param page_table The page table of the emulated process.
* @param base The address to start mapping at. Must be page-aligned.
* @param size The amount of bytes to map. Must be page-aligned.
* @param mmio_handler The handler that backs the mapping.
*/
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler);
void Write8(VAddr addr, u8 data); /**
void Write16(VAddr addr, u16 data); * Unmaps a region of the emulated process address space.
void Write32(VAddr addr, u32 data); *
void Write64(VAddr addr, u64 data); * @param page_table The page table of the emulated process.
* @param base The address to begin unmapping at.
* @param size The amount of bytes to unmap.
*/
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size); /**
void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); * Adds a memory hook to intercept reads and writes to given region of memory.
void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, *
std::size_t size); * @param page_table The page table of the emulated process
void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); * @param base The starting address to apply the hook to.
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); * @param size The size of the memory region to apply the hook to, in bytes.
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); * @param hook The hook to apply to the region of memory.
*/
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook);
/**
* Removes a memory hook from a given range of memory.
*
* @param page_table The page table of the emulated process.
* @param base The starting address to remove the hook from.
* @param size The size of the memory region to remove the hook from, in bytes.
* @param hook The hook to remove from the specified region of memory.
*/
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook);
/**
* Checks whether or not the supplied address is a valid virtual
* address for the given process.
*
* @param process The emulated process to check the address against.
* @param vaddr The virtual address to check the validity of.
*
* @returns True if the given virtual address is valid, false otherwise.
*/
bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr) const;
/**
* Checks whether or not the supplied address is a valid virtual
* address for the current process.
*
* @param vaddr The virtual address to check the validity of.
*
* @returns True if the given virtual address is valid, false otherwise.
*/
bool IsValidVirtualAddress(VAddr vaddr) const;
/**
* Gets a pointer to the given address.
*
* @param vaddr Virtual address to retrieve a pointer to.
*
* @returns The pointer to the given address, if the address is valid.
* If the address is not valid, nullptr will be returned.
*/
u8* GetPointer(VAddr vaddr); u8* GetPointer(VAddr vaddr);
/**
* Gets a pointer to the given address.
*
* @param vaddr Virtual address to retrieve a pointer to.
*
* @returns The pointer to the given address, if the address is valid.
* If the address is not valid, nullptr will be returned.
*/
const u8* GetPointer(VAddr vaddr) const;
/**
* Reads an 8-bit unsigned value from the current process' address space
* at the given virtual address.
*
* @param addr The virtual address to read the 8-bit value from.
*
* @returns the read 8-bit unsigned value.
*/
u8 Read8(VAddr addr);
/**
* Reads a 16-bit unsigned value from the current process' address space
* at the given virtual address.
*
* @param addr The virtual address to read the 16-bit value from.
*
* @returns the read 16-bit unsigned value.
*/
u16 Read16(VAddr addr);
/**
* Reads a 32-bit unsigned value from the current process' address space
* at the given virtual address.
*
* @param addr The virtual address to read the 32-bit value from.
*
* @returns the read 32-bit unsigned value.
*/
u32 Read32(VAddr addr);
/**
* Reads a 64-bit unsigned value from the current process' address space
* at the given virtual address.
*
* @param addr The virtual address to read the 64-bit value from.
*
* @returns the read 64-bit value.
*/
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:
*
* - A null character ('\0') is reached.
* - max_length characters have been read.
*
* @note The final null-terminating character (if found) is not included
* in the returned string.
*
* @param vaddr The address to begin reading the string from.
* @param max_length The maximum length of the string to read in characters.
*
* @returns The read string.
*/
std::string ReadCString(VAddr vaddr, std::size_t max_length); std::string ReadCString(VAddr vaddr, std::size_t max_length);
/** /**
* Mark each page touching the region as cached. * Reads a contiguous block of bytes from a specified process' address space.
*
* @param process The process to read the data from.
* @param src_addr The virtual address to begin reading from.
* @param dest_buffer The buffer to place the read bytes into.
* @param size The amount of data to read, in bytes.
*
* @note If a size of 0 is specified, then this function reads nothing and
* no attempts to access memory are made at all.
*
* @pre dest_buffer must be at least size bytes in length, otherwise a
* buffer overrun will occur.
*
* @post The range [dest_buffer, size) contains the read bytes from the
* process' address space.
*/
void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer,
std::size_t size);
/**
* Reads a contiguous block of bytes from the current process' address space.
*
* @param src_addr The virtual address to begin reading from.
* @param dest_buffer The buffer to place the read bytes into.
* @param size The amount of data to read, in bytes.
*
* @note If a size of 0 is specified, then this function reads nothing and
* no attempts to access memory are made at all.
*
* @pre dest_buffer must be at least size bytes in length, otherwise a
* buffer overrun will occur.
*
* @post The range [dest_buffer, size) contains the read bytes from the
* current process' address space.
*/
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.
*
* @param process The process that will have a portion of its memory zeroed out.
* @param dest_addr The starting virtual address of the range to zero out.
* @param size The size of the address range to zero out, in bytes.
*
* @post The range [dest_addr, size) within the process' address space is
* filled with zeroes.
*/
void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
/**
* Fills the specified address range within the current process' address space with zeroes.
*
* @param dest_addr The starting virtual address of the range to zero out.
* @param size The size of the address range to zero out, in bytes.
*
* @post The range [dest_addr, size) within the current process' address space is
* filled with zeroes.
*/
void ZeroBlock(VAddr dest_addr, std::size_t size);
/**
* Copies data within a process' address space to another location within the
* same address space.
*
* @param process The process that will have data copied within its address space.
* @param dest_addr The destination virtual address to begin copying the data into.
* @param src_addr The source virtual address to begin copying the data from.
* @param size The size of the data to copy, in bytes.
*
* @post The range [dest_addr, size) within the process' address space contains the
* same data within the range [src_addr, size).
*/
void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
std::size_t size);
/**
* Copies data within the current process' address space to another location within the
* same address space.
*
* @param dest_addr The destination virtual address to begin copying the data into.
* @param src_addr The source virtual address to begin copying the data from.
* @param size The size of the data to copy, in bytes.
*
* @post The range [dest_addr, size) within the current process' address space
* contains the same data within the range [src_addr, size).
*/
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
/**
* Marks each page within the specified address range as cached or uncached.
*
* @param vaddr The virtual address indicating the start of the address range.
* @param size The size of the address range in bytes.
* @param cached Whether or not any pages within the address range should be
* marked as cached or uncached.
*/ */
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached); void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
/// Determines if the given VAddr is a kernel address
bool IsKernelVirtualAddress(VAddr vaddr);
} // namespace Memory } // namespace Memory

@ -20,18 +20,17 @@ namespace Memory {
constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12); constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12);
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
StandardVmCallbacks::StandardVmCallbacks(const Core::System& system, StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
const CheatProcessMetadata& metadata)
: metadata(metadata), system(system) {} : metadata(metadata), system(system) {}
StandardVmCallbacks::~StandardVmCallbacks() = default; StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
ReadBlock(SanitizeAddress(address), data, size); system.Memory().ReadBlock(SanitizeAddress(address), data, 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() {

@ -24,7 +24,7 @@ namespace Memory {
class StandardVmCallbacks : public DmntCheatVm::Callbacks { class StandardVmCallbacks : public DmntCheatVm::Callbacks {
public: public:
StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata); StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata);
~StandardVmCallbacks() override; ~StandardVmCallbacks() override;
void MemoryRead(VAddr address, void* data, u64 size) override; void MemoryRead(VAddr address, void* data, u64 size) override;
@ -37,7 +37,7 @@ private:
VAddr SanitizeAddress(VAddr address) const; VAddr SanitizeAddress(VAddr address) const;
const CheatProcessMetadata& metadata; const CheatProcessMetadata& metadata;
const Core::System& system; Core::System& system;
}; };
// Intermediary class that parses a text file or other disk format for storing cheats into a // Intermediary class that parses a text file or other disk format for storing cheats into a

@ -1,43 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "common/memory_hook.h"
namespace Common {
struct PageTable;
}
namespace Memory {
/**
* Maps an allocated buffer onto a region of the emulated process address space.
*
* @param page_table The page table of the emulated process.
* @param base The address to start mapping at. Must be page-aligned.
* @param size The amount of bytes to map. Must be page-aligned.
* @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
*/
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target);
/**
* Maps a region of the emulated process address space as a IO region.
* @param page_table The page table of the emulated process.
* @param base The address to start mapping at. Must be page-aligned.
* @param size The amount of bytes to map. Must be page-aligned.
* @param mmio_handler The handler that backs the mapping.
*/
void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer mmio_handler);
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size);
void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook);
void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,
Common::MemoryHookPointer hook);
} // namespace Memory

@ -147,7 +147,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
} }
template <bool read_value, typename DescriptorType> template <bool read_value, typename DescriptorType>
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) { json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memory::Memory& memory) {
auto buffer_out = json::array(); auto buffer_out = json::array();
for (const auto& desc : buffer) { for (const auto& desc : buffer) {
auto entry = json{ auto entry = json{
@ -157,7 +157,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
if constexpr (read_value) { if constexpr (read_value) {
std::vector<u8> data(desc.Size()); std::vector<u8> data(desc.Size());
Memory::ReadBlock(desc.Address(), data.data(), desc.Size()); memory.ReadBlock(desc.Address(), data.data(), desc.Size());
entry["data"] = Common::HexToString(data); entry["data"] = Common::HexToString(data);
} }
@ -167,7 +167,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
return buffer_out; return buffer_out;
} }
json GetHLERequestContextData(Kernel::HLERequestContext& ctx) { json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Memory::Memory& memory) {
json out; json out;
auto cmd_buf = json::array(); auto cmd_buf = json::array();
@ -177,10 +177,10 @@ json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
out["command_buffer"] = std::move(cmd_buf); out["command_buffer"] = std::move(cmd_buf);
out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA()); out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA(), memory);
out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB()); out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB(), memory);
out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC()); out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC(), memory);
out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX()); out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX(), memory);
return out; return out;
} }
@ -259,7 +259,7 @@ void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u
const auto title_id = system.CurrentProcess()->GetTitleID(); const auto title_id = system.CurrentProcess()->GetTitleID();
auto out = GetFullDataAuto(timestamp, title_id, system); auto out = GetFullDataAuto(timestamp, title_id, system);
auto function_out = GetHLERequestContextData(ctx); auto function_out = GetHLERequestContextData(ctx, system.Memory());
function_out["command_id"] = command_id; function_out["command_id"] = command_id;
function_out["function_name"] = name; function_out["function_name"] = name;
function_out["service_name"] = service_name; function_out["service_name"] = service_name;

@ -11,40 +11,39 @@
#include "core/tools/freezer.h" #include "core/tools/freezer.h"
namespace Tools { namespace Tools {
namespace { namespace {
constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
u64 MemoryReadWidth(u32 width, VAddr addr) { u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {
switch (width) { switch (width) {
case 1: case 1:
return Memory::Read8(addr); return memory.Read8(addr);
case 2: case 2:
return Memory::Read16(addr); return memory.Read16(addr);
case 4: case 4:
return Memory::Read32(addr); return memory.Read32(addr);
case 8: case 8:
return Memory::Read64(addr); return memory.Read64(addr);
default: default:
UNREACHABLE(); UNREACHABLE();
return 0; return 0;
} }
} }
void MemoryWriteWidth(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();
@ -53,7 +52,8 @@ void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
} // Anonymous namespace } // Anonymous namespace
Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) { Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_)
: core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent( event = Core::Timing::CreateEvent(
"MemoryFreezer::FrameCallback", "MemoryFreezer::FrameCallback",
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
@ -89,7 +89,7 @@ void Freezer::Clear() {
u64 Freezer::Freeze(VAddr address, u32 width) { u64 Freezer::Freeze(VAddr address, u32 width) {
std::lock_guard lock{entries_mutex}; std::lock_guard lock{entries_mutex};
const auto current_value = MemoryReadWidth(width, address); const auto current_value = MemoryReadWidth(memory, width, address);
entries.push_back({address, width, current_value}); entries.push_back({address, width, current_value});
LOG_DEBUG(Common_Memory, LOG_DEBUG(Common_Memory,
@ -169,7 +169,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
LOG_DEBUG(Common_Memory, LOG_DEBUG(Common_Memory,
"Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}", "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
entry.address, entry.value, entry.width); entry.address, entry.value, entry.width);
MemoryWriteWidth(entry.width, entry.address, entry.value); MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
} }
core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event);
@ -181,7 +181,7 @@ void Freezer::FillEntryReads() {
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
for (auto& entry : entries) { for (auto& entry : entries) {
entry.value = MemoryReadWidth(entry.width, entry.address); entry.value = MemoryReadWidth(memory, entry.width, entry.address);
} }
} }

@ -16,6 +16,10 @@ class CoreTiming;
struct EventType; struct EventType;
} // namespace Core::Timing } // namespace Core::Timing
namespace Memory {
class Memory;
}
namespace Tools { namespace Tools {
/** /**
@ -34,7 +38,7 @@ public:
u64 value; u64 value;
}; };
explicit Freezer(Core::Timing::CoreTiming& core_timing); explicit Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_);
~Freezer(); ~Freezer();
// Enables or disables the entire memory freezer. // Enables or disables the entire memory freezer.
@ -78,6 +82,7 @@ private:
std::shared_ptr<Core::Timing::EventType> event; std::shared_ptr<Core::Timing::EventType> event;
Core::Timing::CoreTiming& core_timing; Core::Timing::CoreTiming& core_timing;
Memory::Memory& memory;
}; };
} // namespace Tools } // namespace Tools

@ -8,7 +8,6 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/memory_setup.h"
#include "tests/core/arm/arm_test_common.h" #include "tests/core/arm/arm_test_common.h"
namespace ArmTests { namespace ArmTests {
@ -16,8 +15,9 @@ namespace ArmTests {
TestEnvironment::TestEnvironment(bool mutable_memory_) TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_), : mutable_memory(mutable_memory_),
test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
auto process = Kernel::Process::Create(Core::System::GetInstance(), "", auto& system = Core::System::GetInstance();
Kernel::Process::ProcessType::Userland);
auto process = Kernel::Process::Create(system, "", Kernel::Process::ProcessType::Userland);
page_table = &process->VMManager().page_table; page_table = &process->VMManager().page_table;
std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
@ -25,15 +25,16 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
std::fill(page_table->attributes.begin(), page_table->attributes.end(), std::fill(page_table->attributes.begin(), page_table->attributes.end(),
Common::PageType::Unmapped); Common::PageType::Unmapped);
Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); system.Memory().MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); system.Memory().MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
kernel.MakeCurrentProcess(process.get()); kernel.MakeCurrentProcess(process.get());
} }
TestEnvironment::~TestEnvironment() { TestEnvironment::~TestEnvironment() {
Memory::UnmapRegion(*page_table, 0x80000000, 0x80000000); auto& system = Core::System::GetInstance();
Memory::UnmapRegion(*page_table, 0x00000000, 0x80000000); system.Memory().UnmapRegion(*page_table, 0x80000000, 0x80000000);
system.Memory().UnmapRegion(*page_table, 0x00000000, 0x80000000);
} }
void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {

@ -52,7 +52,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
const u64 aligned_size{Common::AlignUp(size, page_size)}; const u64 aligned_size{Common::AlignUp(size, page_size)};
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
ASSERT(system.CurrentProcess() ASSERT(system.CurrentProcess()
->VMManager() ->VMManager()
.SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
@ -67,7 +67,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
const u64 aligned_size{Common::AlignUp(size, page_size)}; const u64 aligned_size{Common::AlignUp(size, page_size)};
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); MapBackingMemory(gpu_addr, system.Memory().GetPointer(cpu_addr), aligned_size, cpu_addr);
ASSERT(system.CurrentProcess() ASSERT(system.CurrentProcess()
->VMManager() ->VMManager()
.SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,

@ -22,7 +22,8 @@ constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
} // Anonymous namespace } // Anonymous namespace
RasterizerAccelerated::RasterizerAccelerated() = default; RasterizerAccelerated::RasterizerAccelerated(Memory::Memory& cpu_memory_)
: cpu_memory{cpu_memory_} {}
RasterizerAccelerated::~RasterizerAccelerated() = default; RasterizerAccelerated::~RasterizerAccelerated() = default;
@ -47,9 +48,9 @@ void RasterizerAccelerated::UpdatePagesCachedCount(VAddr addr, u64 size, int del
const u64 interval_size = interval_end_addr - interval_start_addr; const u64 interval_size = interval_end_addr - interval_start_addr;
if (delta > 0 && count == delta) { if (delta > 0 && count == delta) {
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true); cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
} else if (delta < 0 && count == -delta) { } else if (delta < 0 && count == -delta) {
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false); cpu_memory.RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
} else { } else {
ASSERT(count >= 0); ASSERT(count >= 0);
} }

@ -11,12 +11,16 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
namespace Memory {
class Memory;
}
namespace VideoCore { namespace VideoCore {
/// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface. /// Implements the shared part in GPU accelerated rasterizers in RasterizerInterface.
class RasterizerAccelerated : public RasterizerInterface { class RasterizerAccelerated : public RasterizerInterface {
public: public:
explicit RasterizerAccelerated(); explicit RasterizerAccelerated(Memory::Memory& cpu_memory_);
~RasterizerAccelerated() override; ~RasterizerAccelerated() override;
void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
@ -24,8 +28,9 @@ public:
private: private:
using CachedPageMap = boost::icl::interval_map<u64, int>; using CachedPageMap = boost::icl::interval_map<u64, int>;
CachedPageMap cached_pages; CachedPageMap cached_pages;
std::mutex pages_mutex; std::mutex pages_mutex;
Memory::Memory& cpu_memory;
}; };
} // namespace VideoCore } // namespace VideoCore

@ -19,6 +19,7 @@
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/memory.h"
#include "core/settings.h" #include "core/settings.h"
#include "video_core/engines/kepler_compute.h" #include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_3d.h"
@ -86,8 +87,9 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
ScreenInfo& info) ScreenInfo& info)
: texture_cache{system, *this, device}, shader_cache{*this, system, emu_window, device}, : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device},
system{system}, screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { shader_cache{*this, system, emu_window, device}, system{system}, screen_info{info},
buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
shader_program_manager = std::make_unique<GLShader::ProgramManager>(); shader_program_manager = std::make_unique<GLShader::ProgramManager>();
state.draw.shader_program = 0; state.draw.shader_program = 0;
state.Apply(); state.Apply();
@ -837,7 +839,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
MICROPROFILE_SCOPE(OpenGL_CacheManagement); MICROPROFILE_SCOPE(OpenGL_CacheManagement);
const auto surface{ const auto surface{
texture_cache.TryFindFramebufferSurface(Memory::GetPointer(framebuffer_addr))}; texture_cache.TryFindFramebufferSurface(system.Memory().GetPointer(framebuffer_addr))};
if (!surface) { if (!surface) {
return {}; return {};
} }

@ -158,7 +158,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)}; const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel}; const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
const auto host_ptr{Memory::GetPointer(framebuffer_addr)}; u8* const host_ptr{system.Memory().GetPointer(framebuffer_addr)};
rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes); rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes);
// TODO(Rodrigo): Read this from HLE // TODO(Rodrigo): Read this from HLE

@ -24,9 +24,11 @@ CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, std::size_t size, u64 offse
alignment{alignment} {} alignment{alignment} {}
VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, VKBufferCache::VKBufferCache(Tegra::MemoryManager& tegra_memory_manager,
Memory::Memory& cpu_memory_,
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size) VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size)
: RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager} { : RasterizerCache{rasterizer}, tegra_memory_manager{tegra_memory_manager}, cpu_memory{
cpu_memory_} {
const auto usage = vk::BufferUsageFlagBits::eVertexBuffer | const auto usage = vk::BufferUsageFlagBits::eVertexBuffer |
vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
vk::BufferUsageFlagBits::eUniformBuffer; vk::BufferUsageFlagBits::eUniformBuffer;
@ -48,9 +50,9 @@ u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignme
// TODO: Figure out which size is the best for given games. // TODO: Figure out which size is the best for given games.
cache &= size >= 2048; cache &= size >= 2048;
const auto& host_ptr{Memory::GetPointer(*cpu_addr)}; u8* const host_ptr{cpu_memory.GetPointer(*cpu_addr)};
if (cache) { if (cache) {
auto entry = TryGet(host_ptr); const auto entry = TryGet(host_ptr);
if (entry) { if (entry) {
if (entry->GetSize() >= size && entry->GetAlignment() == alignment) { if (entry->GetSize() >= size && entry->GetAlignment() == alignment) {
return entry->GetOffset(); return entry->GetOffset();
@ -62,7 +64,7 @@ u64 VKBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, u64 alignme
AlignBuffer(alignment); AlignBuffer(alignment);
const u64 uploaded_offset = buffer_offset; const u64 uploaded_offset = buffer_offset;
if (!host_ptr) { if (host_ptr == nullptr) {
return uploaded_offset; return uploaded_offset;
} }

@ -13,6 +13,10 @@
#include "video_core/renderer_vulkan/declarations.h" #include "video_core/renderer_vulkan/declarations.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
namespace Memory {
class Memory;
}
namespace Tegra { namespace Tegra {
class MemoryManager; class MemoryManager;
} }
@ -58,7 +62,7 @@ private:
class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { class VKBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
public: public:
explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, explicit VKBufferCache(Tegra::MemoryManager& tegra_memory_manager, Memory::Memory& cpu_memory_,
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device, VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size); VKMemoryManager& memory_manager, VKScheduler& scheduler, u64 size);
~VKBufferCache(); ~VKBufferCache();
@ -92,6 +96,7 @@ private:
void AlignBuffer(std::size_t alignment); void AlignBuffer(std::size_t alignment);
Tegra::MemoryManager& tegra_memory_manager; Tegra::MemoryManager& tegra_memory_manager;
Memory::Memory& cpu_memory;
std::unique_ptr<VKStreamBuffer> stream_buffer; std::unique_ptr<VKStreamBuffer> stream_buffer;
vk::Buffer buffer_handle; vk::Buffer buffer_handle;

@ -80,7 +80,7 @@ QString WaitTreeText::GetText() const {
WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table)
: mutex_address(mutex_address) { : mutex_address(mutex_address) {
mutex_value = Memory::Read32(mutex_address); mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address);
owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
owner = handle_table.Get<Kernel::Thread>(owner_handle); owner = handle_table.Get<Kernel::Thread>(owner_handle);
} }
@ -115,10 +115,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
std::vector<std::unique_ptr<WaitTreeItem>> list; std::vector<std::unique_ptr<WaitTreeItem>> list;
constexpr std::size_t BaseRegister = 29; constexpr std::size_t BaseRegister = 29;
auto& memory = Core::System::GetInstance().Memory();
u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister];
while (base_pointer != 0) { while (base_pointer != 0) {
const u64 lr = Memory::Read64(base_pointer + sizeof(u64)); const u64 lr = memory.Read64(base_pointer + sizeof(u64));
if (lr == 0) { if (lr == 0) {
break; break;
} }
@ -126,7 +127,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
list.push_back(std::make_unique<WaitTreeText>( list.push_back(std::make_unique<WaitTreeText>(
tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'}))); tr("0x%1").arg(lr - sizeof(u32), 16, 16, QLatin1Char{'0'})));
base_pointer = Memory::Read64(base_pointer); base_pointer = memory.Read64(base_pointer);
} }
return list; return list;