mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-26 03:00:05 +00:00
core: Add support for N3DS memory mappings (#5103)
* core: Add support for N3DS memory mappings * Address review comments
This commit is contained in:
parent
ab8cb17ab7
commit
6d3d9f7a8a
@ -174,7 +174,9 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(system_mode.first);
|
ASSERT(system_mode.first);
|
||||||
ResultStatus init_result{Init(emu_window, *system_mode.first)};
|
auto n3ds_mode = app_loader->LoadKernelN3dsMode();
|
||||||
|
ASSERT(n3ds_mode.first);
|
||||||
|
ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first)};
|
||||||
if (init_result != ResultStatus::Success) {
|
if (init_result != ResultStatus::Success) {
|
||||||
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
||||||
static_cast<u32>(init_result));
|
static_cast<u32>(init_result));
|
||||||
@ -246,7 +248,7 @@ void System::Reschedule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode) {
|
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode) {
|
||||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||||
|
|
||||||
std::size_t num_cores = 2;
|
std::size_t num_cores = 2;
|
||||||
@ -259,7 +261,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
|
|||||||
timing = std::make_unique<Timing>(num_cores);
|
timing = std::make_unique<Timing>(num_cores);
|
||||||
|
|
||||||
kernel = std::make_unique<Kernel::KernelSystem>(
|
kernel = std::make_unique<Kernel::KernelSystem>(
|
||||||
*memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores);
|
*memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode);
|
||||||
|
|
||||||
if (Settings::values.use_cpu_jit) {
|
if (Settings::values.use_cpu_jit) {
|
||||||
#ifdef ARCHITECTURE_x86_64
|
#ifdef ARCHITECTURE_x86_64
|
||||||
|
@ -303,7 +303,7 @@ private:
|
|||||||
* @param system_mode The system mode.
|
* @param system_mode The system mode.
|
||||||
* @return ResultStatus code, indicating if the operation succeeded.
|
* @return ResultStatus code, indicating if the operation succeeded.
|
||||||
*/
|
*/
|
||||||
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode);
|
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode);
|
||||||
|
|
||||||
/// Reschedule the core emulation
|
/// Reschedule the core emulation
|
||||||
void Reschedule();
|
void Reschedule();
|
||||||
|
@ -149,7 +149,8 @@ struct ExHeader_StorageInfo {
|
|||||||
struct ExHeader_ARM11_SystemLocalCaps {
|
struct ExHeader_ARM11_SystemLocalCaps {
|
||||||
u64_le program_id;
|
u64_le program_id;
|
||||||
u32_le core_version;
|
u32_le core_version;
|
||||||
u8 reserved_flags[2];
|
u8 reserved_flag;
|
||||||
|
u8 n3ds_mode;
|
||||||
union {
|
union {
|
||||||
u8 flags0;
|
u8 flags0;
|
||||||
BitField<0, 2, u8> ideal_processor;
|
BitField<0, 2, u8> ideal_processor;
|
||||||
|
@ -19,10 +19,10 @@ namespace Kernel {
|
|||||||
/// Initialize the kernel
|
/// Initialize the kernel
|
||||||
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||||
std::function<void()> prepare_reschedule_callback, u32 system_mode,
|
std::function<void()> prepare_reschedule_callback, u32 system_mode,
|
||||||
u32 num_cores)
|
u32 num_cores, u8 n3ds_mode)
|
||||||
: memory(memory), timing(timing),
|
: memory(memory), timing(timing),
|
||||||
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
|
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
|
||||||
MemoryInit(system_mode);
|
MemoryInit(system_mode, n3ds_mode);
|
||||||
|
|
||||||
resource_limits = std::make_unique<ResourceLimitList>(*this);
|
resource_limits = std::make_unique<ResourceLimitList>(*this);
|
||||||
for (u32 core_id = 0; core_id < num_cores; ++core_id) {
|
for (u32 core_id = 0; core_id < num_cores; ++core_id) {
|
||||||
|
@ -86,7 +86,7 @@ class KernelSystem {
|
|||||||
public:
|
public:
|
||||||
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
|
||||||
std::function<void()> prepare_reschedule_callback, u32 system_mode,
|
std::function<void()> prepare_reschedule_callback, u32 system_mode,
|
||||||
u32 num_cores);
|
u32 num_cores, u8 n3ds_mode);
|
||||||
~KernelSystem();
|
~KernelSystem();
|
||||||
|
|
||||||
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
|
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
|
||||||
@ -263,7 +263,7 @@ public:
|
|||||||
Core::Timing& timing;
|
Core::Timing& timing;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MemoryInit(u32 mem_type);
|
void MemoryInit(u32 mem_type, u8 n3ds_mode);
|
||||||
|
|
||||||
std::function<void()> prepare_reschedule_callback;
|
std::function<void()> prepare_reschedule_callback;
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "core/hle/kernel/vm_manager.h"
|
#include "core/hle/kernel/vm_manager.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -40,11 +41,32 @@ static const u32 memory_region_sizes[8][3] = {
|
|||||||
{0x0B200000, 0x02E00000, 0x02000000}, // 7
|
{0x0B200000, 0x02E00000, 0x02000000}, // 7
|
||||||
};
|
};
|
||||||
|
|
||||||
void KernelSystem::MemoryInit(u32 mem_type) {
|
namespace MemoryMode {
|
||||||
// TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
|
enum N3DSMode : u8 {
|
||||||
ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
|
Mode6 = 1,
|
||||||
|
Mode7 = 2,
|
||||||
|
Mode6_2 = 3,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) {
|
||||||
ASSERT(mem_type != 1);
|
ASSERT(mem_type != 1);
|
||||||
|
|
||||||
|
const bool is_new_3ds = Settings::values.is_new_3ds;
|
||||||
|
u32 reported_mem_type = mem_type;
|
||||||
|
if (is_new_3ds) {
|
||||||
|
if (n3ds_mode == MemoryMode::Mode6 || n3ds_mode == MemoryMode::Mode6_2) {
|
||||||
|
mem_type = 6;
|
||||||
|
reported_mem_type = 6;
|
||||||
|
} else if (n3ds_mode == MemoryMode::Mode7) {
|
||||||
|
mem_type = 7;
|
||||||
|
reported_mem_type = 7;
|
||||||
|
} else {
|
||||||
|
// On the N3ds, all O3ds configurations (<=5) are forced to 6 instead.
|
||||||
|
mem_type = 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
|
// The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
|
||||||
// the sizes specified in the memory_region_sizes table.
|
// the sizes specified in the memory_region_sizes table.
|
||||||
VAddr base = 0;
|
VAddr base = 0;
|
||||||
@ -55,14 +77,12 @@ void KernelSystem::MemoryInit(u32 mem_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We must've allocated the entire FCRAM by the end
|
// We must've allocated the entire FCRAM by the end
|
||||||
ASSERT(base == Memory::FCRAM_SIZE);
|
ASSERT(base == (is_new_3ds ? Memory::FCRAM_N3DS_SIZE : Memory::FCRAM_SIZE));
|
||||||
|
|
||||||
config_mem_handler = std::make_unique<ConfigMem::Handler>();
|
config_mem_handler = std::make_unique<ConfigMem::Handler>();
|
||||||
auto& config_mem = config_mem_handler->GetConfigMem();
|
auto& config_mem = config_mem_handler->GetConfigMem();
|
||||||
config_mem.app_mem_type = mem_type;
|
config_mem.app_mem_type = reported_mem_type;
|
||||||
// app_mem_malloc does not always match the configured size for memory_region[0]: in case the
|
config_mem.app_mem_alloc = memory_region_sizes[reported_mem_type][0];
|
||||||
// n3DS type override is in effect it reports the size the game expects, not the real one.
|
|
||||||
config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
|
|
||||||
config_mem.sys_mem_alloc = memory_regions[1].size;
|
config_mem.sys_mem_alloc = memory_regions[1].size;
|
||||||
config_mem.base_mem_alloc = memory_regions[2].size;
|
config_mem.base_mem_alloc = memory_regions[2].size;
|
||||||
|
|
||||||
|
@ -105,13 +105,22 @@ public:
|
|||||||
* Loads the system mode that this application needs.
|
* Loads the system mode that this application needs.
|
||||||
* This function defaults to 2 (96MB allocated to the application) if it can't read the
|
* This function defaults to 2 (96MB allocated to the application) if it can't read the
|
||||||
* information.
|
* information.
|
||||||
* @returns A pair with the optional system mode, and and the status.
|
* @returns A pair with the optional system mode, and the status.
|
||||||
*/
|
*/
|
||||||
virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
|
virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
|
||||||
// 96MB allocated to the application.
|
// 96MB allocated to the application.
|
||||||
return std::make_pair(2, ResultStatus::Success);
|
return std::make_pair(2, ResultStatus::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the N3ds mode that this application uses.
|
||||||
|
* It defaults to 0 (O3DS default) if it can't read the information.
|
||||||
|
* @returns A pair with the optional N3ds mode, and the status.
|
||||||
|
*/
|
||||||
|
virtual std::pair<std::optional<u8>, ResultStatus> LoadKernelN3dsMode() {
|
||||||
|
return std::make_pair(0, ResultStatus::Success);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether this application is executable.
|
* Get whether this application is executable.
|
||||||
* @param out_executable Reference to store the executable flag into.
|
* @param out_executable Reference to store the executable flag into.
|
||||||
|
@ -61,6 +61,19 @@ std::pair<std::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode
|
|||||||
ResultStatus::Success);
|
ResultStatus::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<std::optional<u8>, ResultStatus> AppLoader_NCCH::LoadKernelN3dsMode() {
|
||||||
|
if (!is_loaded) {
|
||||||
|
ResultStatus res = base_ncch.Load();
|
||||||
|
if (res != ResultStatus::Success) {
|
||||||
|
return std::make_pair(std::optional<u8>{}, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the system mode as the one from the exheader.
|
||||||
|
return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.n3ds_mode,
|
||||||
|
ResultStatus::Success);
|
||||||
|
}
|
||||||
|
|
||||||
ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process) {
|
ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process) {
|
||||||
using Kernel::CodeSet;
|
using Kernel::CodeSet;
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() override;
|
std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() override;
|
||||||
|
|
||||||
|
std::pair<std::optional<u8>, ResultStatus> LoadKernelN3dsMode() override;
|
||||||
|
|
||||||
ResultStatus IsExecutable(bool& out_executable) override;
|
ResultStatus IsExecutable(bool& out_executable) override;
|
||||||
|
|
||||||
ResultStatus ReadCode(std::vector<u8>& buffer) override;
|
ResultStatus ReadCode(std::vector<u8>& buffer) override;
|
||||||
|
@ -17,7 +17,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
|
|||||||
|
|
||||||
timing = std::make_unique<Core::Timing>(1);
|
timing = std::make_unique<Core::Timing>(1);
|
||||||
memory = std::make_unique<Memory::MemorySystem>();
|
memory = std::make_unique<Memory::MemorySystem>();
|
||||||
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1);
|
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1, 0);
|
||||||
|
|
||||||
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
|
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
|
||||||
page_table = &kernel->GetCurrentProcess()->vm_manager.page_table;
|
page_table = &kernel->GetCurrentProcess()->vm_manager.page_table;
|
||||||
|
@ -23,7 +23,7 @@ static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) {
|
|||||||
TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
|
TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
|
||||||
Core::Timing timing(1);
|
Core::Timing timing(1);
|
||||||
Memory::MemorySystem memory;
|
Memory::MemorySystem memory;
|
||||||
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1);
|
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0);
|
||||||
auto [server, client] = kernel.CreateSessionPair();
|
auto [server, client] = kernel.CreateSessionPair();
|
||||||
HLERequestContext context(kernel, std::move(server), nullptr);
|
HLERequestContext context(kernel, std::move(server), nullptr);
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
|
|||||||
TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
|
TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
|
||||||
Core::Timing timing(1);
|
Core::Timing timing(1);
|
||||||
Memory::MemorySystem memory;
|
Memory::MemorySystem memory;
|
||||||
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1);
|
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0);
|
||||||
auto [server, client] = kernel.CreateSessionPair();
|
auto [server, client] = kernel.CreateSessionPair();
|
||||||
HLERequestContext context(kernel, std::move(server), nullptr);
|
HLERequestContext context(kernel, std::move(server), nullptr);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
|
TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") {
|
||||||
Core::Timing timing(1);
|
Core::Timing timing(1);
|
||||||
Memory::MemorySystem memory;
|
Memory::MemorySystem memory;
|
||||||
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1);
|
Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0);
|
||||||
SECTION("these regions should not be mapped on an empty process") {
|
SECTION("these regions should not be mapped on an empty process") {
|
||||||
auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
|
auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
|
||||||
CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
|
CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
|
||||||
|
Loading…
Reference in New Issue
Block a user