Merge pull request #3169 from lioncash/memory
core/memory: Deglobalize memory management code
This commit is contained in:
commit
e3ee017e91
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;
|
||||||
|
|
Loading…
Reference in a new issue