diff options
Diffstat (limited to '')
48 files changed, 1064 insertions, 351 deletions
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4a4ba1101..8f42f4fdc 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -31,6 +31,11 @@ add_subdirectory(glad) # inih add_subdirectory(inih) +# lz4 +set(LZ4_BUNDLED_MODE ON) +add_subdirectory(lz4/contrib/cmake_unofficial) +target_include_directories(lz4_static INTERFACE ./lz4/lib) + # MicroProfile add_library(microprofile INTERFACE) target_include_directories(microprofile INTERFACE ./microprofile) @@ -44,6 +49,10 @@ add_subdirectory(soundtouch) # The SoundTouch target doesn't export the necessary include paths as properties by default target_include_directories(SoundTouch INTERFACE ./soundtouch/include) +# Unicorn +add_library(unicorn-headers INTERFACE) +target_include_directories(unicorn-headers INTERFACE ./unicorn/include) + # Xbyak if (ARCHITECTURE_x86_64) # Defined before "dynarmic" above diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 8f15e3f70cb96e56705e5de6ba97b5d09423a56 +Subproject 69eccf826d657a6cfb1d731b00629939d230ec5 diff --git a/externals/enet b/externals/enet -Subproject a84c120eff13d2fa3eadb41ef7afe0f7819f4d6 +Subproject 9d9ba122d4818f7ae1aef2197933ac696edb233 diff --git a/externals/lz4 b/externals/lz4 new file mode 160000 +Subproject c10863b98e1503af90616ae99725ecd120265df diff --git a/src/common/common_types.h b/src/common/common_types.h index ee18eac81..844d34965 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -24,6 +24,7 @@ #pragma once +#include <array> #include <cstdint> #ifdef _MSC_VER @@ -47,8 +48,11 @@ typedef double f64; ///< 64-bit floating point // TODO: It would be nice to eventually replace these with strong types that prevent accidental // conversion between each other. -typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. -typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. +typedef u64 VAddr; ///< Represents a pointer in the userspace virtual address space. +typedef u64 PAddr; ///< Represents a pointer in the ARM11 physical address space. + +using u128 = std::array<std::uint64_t, 2>; +static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); // An inheritable class to disallow the copy constructor and operator= functions class NonCopyable { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2618da18c..8b25eaf0a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,6 +6,8 @@ set(SRCS arm/dyncom/arm_dyncom_interpreter.cpp arm/dyncom/arm_dyncom_thumb.cpp arm/dyncom/arm_dyncom_trans.cpp + arm/unicorn/arm_unicorn.cpp + arm/unicorn/unicorn_dynload.c arm/skyeye_common/armstate.cpp arm/skyeye_common/armsupp.cpp arm/skyeye_common/vfp/vfp.cpp @@ -178,8 +180,11 @@ set(SRCS hw/y2r.cpp loader/3dsx.cpp loader/elf.cpp + loader/linker.cpp loader/loader.cpp loader/ncch.cpp + loader/nro.cpp + loader/nso.cpp loader/smdh.cpp tracer/recorder.cpp memory.cpp @@ -199,6 +204,8 @@ set(HEADERS arm/dyncom/arm_dyncom_run.h arm/dyncom/arm_dyncom_thumb.h arm/dyncom/arm_dyncom_trans.h + arm/unicorn/arm_unicorn.h + arm/unicorn/unicorn_dynload.h arm/skyeye_common/arm_regformat.h arm/skyeye_common/armstate.h arm/skyeye_common/armsupp.h @@ -379,8 +386,11 @@ set(HEADERS hw/y2r.h loader/3dsx.h loader/elf.h + loader/linker.h loader/loader.h loader/ncch.h + loader/nro.h + loader/nso.h loader/smdh.h tracer/recorder.h tracer/citrace.h @@ -395,7 +405,7 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(core STATIC ${SRCS} ${HEADERS}) target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) +target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt lz4_static) if (ENABLE_WEB_SERVICE) target_link_libraries(core PUBLIC json-headers web_service) endif() diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index ba528403c..0b3096347 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -5,6 +5,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/kernel/vm_manager.h" #include "core/arm/skyeye_common/arm_regformat.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h" @@ -14,14 +15,18 @@ public: virtual ~ARM_Interface() {} struct ThreadContext { - u32 cpu_registers[13]; - u32 sp; - u32 lr; - u32 pc; - u32 cpsr; - u32 fpu_registers[64]; - u32 fpscr; - u32 fpexc; + u64 cpu_registers[30]; + u64 lr; + u64 sp; + u64 pc; + u64 cpsr; + u128 fpu_registers[32]; + u64 fpscr; + u64 fpexc; + + + // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT + VAddr tls_address; }; /** @@ -38,6 +43,8 @@ public: Run(1); } + virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) {} + /// Clear all instruction cache virtual void ClearInstructionCache() = 0; @@ -48,27 +55,31 @@ public: * Set the Program Counter to an address * @param addr Address to set PC to */ - virtual void SetPC(u32 addr) = 0; + virtual void SetPC(u64 addr) = 0; /* * Get the current Program Counter * @return Returns current PC */ - virtual u32 GetPC() const = 0; + virtual u64 GetPC() const = 0; /** * Get an ARM register - * @param index Register index (0-15) + * @param index Register index * @return Returns the value in the register */ - virtual u32 GetReg(int index) const = 0; + virtual u64 GetReg(int index) const = 0; /** * Set an ARM register - * @param index Register index (0-15) + * @param index Register index * @param value Value to set register to */ - virtual void SetReg(int index, u32 value) = 0; + virtual void SetReg(int index, u64 value) = 0; + + virtual const u128& GetExtReg(int index) const = 0; + + virtual void SetExtReg(int index, u128& value) = 0; /** * Gets the value of a VFP register @@ -124,6 +135,10 @@ public: */ virtual void SetCP15Register(CP15Register reg, u32 value) = 0; + virtual VAddr GetTlsAddress() const = 0; + + virtual void SetTlsAddress(VAddr address) = 0; + /** * Saves the current CPU context * @param ctx Thread context to save diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 2cb56d12f..6dcab5bab 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -14,98 +14,108 @@ #include "core/hle/svc.h" #include "core/memory.h" -static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void* user_arg) { - ARMul_State* state = static_cast<ARMul_State*>(user_arg); +static void InterpreterFallback(u64 pc, Dynarmic::Jit* jit, void* user_arg) { + UNIMPLEMENTED_MSG("InterpreterFallback for ARM64 JIT does not exist!"); +} - state->Reg = jit->Regs(); - state->Cpsr = jit->Cpsr(); - state->Reg[15] = pc; - state->ExtReg = jit->ExtRegs(); - state->VFP[VFP_FPSCR] = jit->Fpscr(); - state->NumInstrsToExecute = 1; +static bool IsReadOnlyMemory(u64 vaddr) { + // TODO(bunnei): ImplementMe + return false; +} - InterpreterMainLoop(state); +u8 MemoryRead8(const u64 addr) { + return Memory::Read8(static_cast<VAddr>(addr)); +} - bool is_thumb = (state->Cpsr & (1 << 5)) != 0; - state->Reg[15] &= (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC); +u16 MemoryRead16(const u64 addr) { + return Memory::Read16(static_cast<VAddr>(addr)); +} - jit->Regs() = state->Reg; - jit->Cpsr() = state->Cpsr; - jit->ExtRegs() = state->ExtReg; - jit->SetFpscr(state->VFP[VFP_FPSCR]); +u32 MemoryRead32(const u64 addr) { + return Memory::Read32(static_cast<VAddr>(addr)); } -static bool IsReadOnlyMemory(u32 vaddr) { - // TODO(bunnei): ImplementMe - return false; +u64 MemoryRead64(const u64 addr) { + return Memory::Read64(static_cast<VAddr>(addr)); +} + +void MemoryWrite8(const u64 addr, const u8 data) { + Memory::Write8(static_cast<VAddr>(addr), data); +} + +void MemoryWrite16(const u64 addr, const u16 data) { + Memory::Write16(static_cast<VAddr>(addr), data); +} + +void MemoryWrite32(const u64 addr, const u32 data) { + Memory::Write32(static_cast<VAddr>(addr), data); +} + +void MemoryWrite64(const u64 addr, const u64 data) { + Memory::Write64(static_cast<VAddr>(addr), data); } -static Dynarmic::UserCallbacks GetUserCallbacks( - const std::shared_ptr<ARMul_State>& interpeter_state, Memory::PageTable* current_page_table) { +static Dynarmic::UserCallbacks GetUserCallbacks(ARM_Dynarmic* this_) { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.InterpreterFallback = &InterpreterFallback; - user_callbacks.user_arg = static_cast<void*>(interpeter_state.get()); + user_callbacks.user_arg = static_cast<void*>(this_); user_callbacks.CallSVC = &SVC::CallSVC; user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory; - user_callbacks.memory.ReadCode = &Memory::Read32; - user_callbacks.memory.Read8 = &Memory::Read8; - user_callbacks.memory.Read16 = &Memory::Read16; - user_callbacks.memory.Read32 = &Memory::Read32; - user_callbacks.memory.Read64 = &Memory::Read64; - user_callbacks.memory.Write8 = &Memory::Write8; - user_callbacks.memory.Write16 = &Memory::Write16; - user_callbacks.memory.Write32 = &Memory::Write32; - user_callbacks.memory.Write64 = &Memory::Write64; - user_callbacks.page_table = ¤t_page_table->pointers; - user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state); + user_callbacks.memory.ReadCode = &MemoryRead32; + user_callbacks.memory.Read8 = &MemoryRead8; + user_callbacks.memory.Read16 = &MemoryRead16; + user_callbacks.memory.Read32 = &MemoryRead32; + user_callbacks.memory.Read64 = &MemoryRead64; + user_callbacks.memory.Write8 = &MemoryWrite8; + user_callbacks.memory.Write16 = &MemoryWrite16; + user_callbacks.memory.Write32 = &MemoryWrite32; + user_callbacks.memory.Write64 = &MemoryWrite64; + //user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); return user_callbacks; } ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { - interpreter_state = std::make_shared<ARMul_State>(initial_mode); - PageTableChanged(); } -void ARM_Dynarmic::SetPC(u32 pc) { - jit->Regs()[15] = pc; +void ARM_Dynarmic::MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) { +} + +void ARM_Dynarmic::SetPC(u64 pc) { + jit->Regs64()[32] = pc; +} + +u64 ARM_Dynarmic::GetPC() const { + return jit->Regs64()[32]; +} + +u64 ARM_Dynarmic::GetReg(int index) const { + return jit->Regs64()[index]; } -u32 ARM_Dynarmic::GetPC() const { - return jit->Regs()[15]; +void ARM_Dynarmic::SetReg(int index, u64 value) { + jit->Regs64()[index] = value; } -u32 ARM_Dynarmic::GetReg(int index) const { - return jit->Regs()[index]; +const u128& ARM_Dynarmic::GetExtReg(int index) const { + return jit->ExtRegs64()[index]; } -void ARM_Dynarmic::SetReg(int index, u32 value) { - jit->Regs()[index] = value; +void ARM_Dynarmic::SetExtReg(int index, u128& value) { + jit->ExtRegs64()[index] = value; } u32 ARM_Dynarmic::GetVFPReg(int index) const { - return jit->ExtRegs()[index]; + return {}; } void ARM_Dynarmic::SetVFPReg(int index, u32 value) { - jit->ExtRegs()[index] = value; } u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const { - if (reg == VFP_FPSCR) { - return jit->Fpscr(); - } - - // Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state - return interpreter_state->VFP[reg]; + return {}; } void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) { - if (reg == VFP_FPSCR) { - jit->SetFpscr(value); - } - - // Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state - interpreter_state->VFP[reg] = value; } u32 ARM_Dynarmic::GetCPSR() const { @@ -117,11 +127,18 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) { } u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) { - return interpreter_state->CP15[reg]; + return {}; } void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { - interpreter_state->CP15[reg] = value; +} + +VAddr ARM_Dynarmic::GetTlsAddress() const { + return jit->TlsAddr(); +} + +void ARM_Dynarmic::SetTlsAddress(VAddr address) { + jit->TlsAddr() = address; } MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); @@ -136,29 +153,29 @@ void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { } void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { - memcpy(ctx.cpu_registers, jit->Regs().data(), sizeof(ctx.cpu_registers)); - memcpy(ctx.fpu_registers, jit->ExtRegs().data(), sizeof(ctx.fpu_registers)); + memcpy(ctx.cpu_registers, jit->Regs64().data(), sizeof(ctx.cpu_registers)); + memcpy(ctx.fpu_registers, jit->ExtRegs64().data(), sizeof(ctx.fpu_registers)); - ctx.sp = jit->Regs()[13]; - ctx.lr = jit->Regs()[14]; - ctx.pc = jit->Regs()[15]; + ctx.lr = jit->Regs64()[30]; + ctx.sp = jit->Regs64()[31]; + ctx.pc = jit->Regs64()[32]; ctx.cpsr = jit->Cpsr(); - ctx.fpscr = jit->Fpscr(); - ctx.fpexc = interpreter_state->VFP[VFP_FPEXC]; + // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT + ctx.tls_address = jit->TlsAddr(); } void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { - memcpy(jit->Regs().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); - memcpy(jit->ExtRegs().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); + memcpy(jit->Regs64().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); + memcpy(jit->ExtRegs64().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); - jit->Regs()[13] = ctx.sp; - jit->Regs()[14] = ctx.lr; - jit->Regs()[15] = ctx.pc; + jit->Regs64()[30] = ctx.lr; + jit->Regs64()[31] = ctx.sp; + jit->Regs64()[32] = ctx.pc; jit->Cpsr() = ctx.cpsr; - jit->SetFpscr(ctx.fpscr); - interpreter_state->VFP[VFP_FPEXC] = ctx.fpexc; + // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT + jit->TlsAddr() = ctx.tls_address; } void ARM_Dynarmic::PrepareReschedule() { @@ -180,6 +197,6 @@ void ARM_Dynarmic::PageTableChanged() { return; } - jit = new Dynarmic::Jit(GetUserCallbacks(interpreter_state, current_page_table)); + jit = new Dynarmic::Jit(GetUserCallbacks(this), Dynarmic::Arch::ARM64); jits.emplace(current_page_table, std::unique_ptr<Dynarmic::Jit>(jit)); } diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 0b00158a5..6567359b0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -19,10 +19,14 @@ class ARM_Dynarmic final : public ARM_Interface { public: ARM_Dynarmic(PrivilegeMode initial_mode); - void SetPC(u32 pc) override; - u32 GetPC() const override; - u32 GetReg(int index) const override; - void SetReg(int index, u32 value) override; + void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) override; + + void SetPC(u64 pc) override; + u64 GetPC() const override; + u64 GetReg(int index) const override; + void SetReg(int index, u64 value) override; + const u128& GetExtReg(int index) const override; + void SetExtReg(int index, u128& value) override; u32 GetVFPReg(int index) const override; void SetVFPReg(int index, u32 value) override; u32 GetVFPSystemReg(VFPSystemRegister reg) const override; @@ -31,6 +35,8 @@ public: void SetCPSR(u32 cpsr) override; u32 GetCP15Register(CP15Register reg) override; void SetCP15Register(CP15Register reg, u32 value) override; + VAddr GetTlsAddress() const override; + void SetTlsAddress(VAddr address) override; void SaveContext(ThreadContext& ctx) override; void LoadContext(const ThreadContext& ctx) override; @@ -45,5 +51,4 @@ private: Dynarmic::Jit* jit = nullptr; Memory::PageTable* current_page_table = nullptr; std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::Jit>> jits; - std::shared_ptr<ARMul_State> interpreter_state; }; diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 4d72aef77..5ebf7a2f1 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -25,26 +25,33 @@ void ARM_DynCom::ClearInstructionCache() { trans_cache_buf_top = 0; } -void ARM_DynCom::PageTableChanged() { - ClearInstructionCache(); +void ARM_DynCom::SetPC(u64 pc) { + state->Reg[15] = pc; } -void ARM_DynCom::SetPC(u32 pc) { - state->Reg[15] = pc; +void ARM_DynCom::PageTableChanged() { + ClearInstructionCache(); } -u32 ARM_DynCom::GetPC() const { +u64 ARM_DynCom::GetPC() const { return state->Reg[15]; } -u32 ARM_DynCom::GetReg(int index) const { +u64 ARM_DynCom::GetReg(int index) const { return state->Reg[index]; } -void ARM_DynCom::SetReg(int index, u32 value) { +void ARM_DynCom::SetReg(int index, u64 value) { state->Reg[index] = value; } +const u128& ARM_DynCom::GetExtReg(int index) const { + return {}; +} + +void ARM_DynCom::SetExtReg(int index, u128& value) { +} + u32 ARM_DynCom::GetVFPReg(int index) const { return state->ExtReg[index]; } @@ -77,6 +84,13 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) { state->CP15[reg] = value; } +VAddr ARM_DynCom::GetTlsAddress() const { + return {}; +} + +void ARM_DynCom::SetTlsAddress(VAddr /*address*/) { +} + void ARM_DynCom::ExecuteInstructions(int num_instructions) { state->NumInstrsToExecute = num_instructions; diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index fc1ffed6a..cc3c0f3da 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -18,10 +18,12 @@ public: void ClearInstructionCache() override; void PageTableChanged() override; - void SetPC(u32 pc) override; - u32 GetPC() const override; - u32 GetReg(int index) const override; - void SetReg(int index, u32 value) override; + void SetPC(u64 pc) override; + u64 GetPC() const override; + u64 GetReg(int index) const override; + void SetReg(int index, u64 value) override; + const u128& GetExtReg(int index) const override; + void SetExtReg(int index, u128& value) override; u32 GetVFPReg(int index) const override; void SetVFPReg(int index, u32 value) override; u32 GetVFPSystemReg(VFPSystemRegister reg) const override; @@ -30,6 +32,8 @@ public: void SetCPSR(u32 cpsr) override; u32 GetCP15Register(CP15Register reg) override; void SetCP15Register(CP15Register reg, u32 value) override; + VAddr GetTlsAddress() const override; + void SetTlsAddress(VAddr address) override; void SaveContext(ThreadContext& ctx) override; void LoadContext(const ThreadContext& ctx) override; diff --git a/src/core/core.cpp b/src/core/core.cpp index 0c7a72987..c5448630f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -9,6 +9,7 @@ #include "core/arm/arm_interface.h" #include "core/arm/dynarmic/arm_dynarmic.h" #include "core/arm/dyncom/arm_dyncom.h" +#include "core/arm/unicorn/arm_unicorn.h" #include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" @@ -115,7 +116,6 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file return ResultStatus::ErrorLoader; } } - Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); status = ResultStatus::Success; return status; } diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index 61f7654f7..67076c73f 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -15,16 +15,19 @@ ArchiveFactory_SaveData::ArchiveFactory_SaveData( : sd_savedata_source(sd_savedata) {} ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { - return sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id); + UNIMPLEMENTED(); + return {}; //sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id); } ResultCode ArchiveFactory_SaveData::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { - return sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info); + UNIMPLEMENTED(); + return RESULT_SUCCESS; //sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info); } ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const { - return sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id); + UNIMPLEMENTED(); + return {}; //sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id); } } // namespace FileSys diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp index a16941c70..3222000cf 100644 --- a/src/core/file_sys/archive_selfncch.cpp +++ b/src/core/file_sys/archive_selfncch.cpp @@ -278,9 +278,10 @@ void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) { } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { - auto archive = std::make_unique<SelfNCCHArchive>( - ncch_data[Kernel::g_current_process->codeset->program_id]); - return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); + //auto archive = std::make_unique<SelfNCCHArchive>( + // ncch_data[Kernel::g_current_process->codeset->program_id]); + //return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); + return {}; } ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) { diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index 38177e32c..8f12c6a1d 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -69,7 +69,7 @@ void HandlePacket(); * @param addr Address to search from. * @param type Type of breakpoint. */ -BreakpointAddress GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType type); +BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); /** * Check if a breakpoint of the specified type exists at the given address. @@ -77,7 +77,7 @@ BreakpointAddress GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType * @param addr Address of breakpoint. * @param type Type of breakpoint. */ -bool CheckBreakpoint(u32 addr, GDBStub::BreakpointType type); +bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); // If set to true, the CPU will halt at the beginning of the next CPU loop. bool GetCpuHaltFlag(); diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index f93439f21..31fda6db3 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -20,23 +20,41 @@ namespace HLE { * HLE a function return from the current ARM11 userland process * @param res Result to return */ -static inline void FuncReturn(u32 res) { +static inline void FuncReturn(u64 res) { Core::CPU().SetReg(0, res); } -/** - * HLE a function return (64-bit) from the current ARM11 userland process - * @param res Result to return (64-bit) - * @todo Verify that this function is correct - */ -static inline void FuncReturn64(u64 res) { - Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF)); - Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF)); -} - //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type ResultCode +template <ResultCode func(u64)> +void Wrap() { + FuncReturn(func(PARAM(0)).raw); +} + +template <ResultCode func(u32, u64, u32)> +void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); +} + +template <ResultCode func(u64, u32)> +void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1)).raw); +} + +template <ResultCode func(u64, u64, u64)> +void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); +} + +template <ResultCode func(u64*, u64, u64, u64)> +void Wrap() { + u64 param_1 = 0; + u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; + Core::CPU().SetReg(1, param_1); + FuncReturn(retval); +} + template <ResultCode func(u32, u32, u32, u32)> void Wrap() { FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw); @@ -84,6 +102,14 @@ void Wrap() { func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw); } +template <ResultCode func(u32, u64*)> +void Wrap() { + u64 param_1 = 0; + u32 retval = func(PARAM(0), ¶m_1).raw; + Core::CPU().SetReg(1, param_1); + FuncReturn(retval); +} + template <ResultCode func(u32*)> void Wrap() { u32 param_1 = 0; @@ -99,16 +125,17 @@ void Wrap() { FuncReturn(retval); } -template <ResultCode func(MemoryInfo*, PageInfo*, u32)> +template <ResultCode func(MemoryInfo*, PageInfo*, u64)> void Wrap() { MemoryInfo memory_info = {}; PageInfo page_info = {}; u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; - Core::CPU().SetReg(1, memory_info.base_address); - Core::CPU().SetReg(2, memory_info.size); - Core::CPU().SetReg(3, memory_info.permission); - Core::CPU().SetReg(4, memory_info.state); - Core::CPU().SetReg(5, page_info.flags); + + Memory::Write64(PARAM(0), memory_info.base_address); + Memory::Write64(PARAM(0) + 8, memory_info.size); + Memory::Write64(PARAM(0) + 16, memory_info.permission); + Memory::Write64(PARAM(0) + 24, memory_info.state); + FuncReturn(retval); } @@ -138,7 +165,7 @@ void Wrap() { FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); } -template <ResultCode func(u32*, u32)> +template <ResultCode func(u32*, u64)> void Wrap() { u32 param_1 = 0; u32 retval = func(¶m_1, PARAM(1)).raw; @@ -226,6 +253,11 @@ void Wrap() { FuncReturn(retval); } +template <ResultCode func(u32, u32, u32)> +void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 @@ -255,9 +287,9 @@ void Wrap() { func(PARAM(0), PARAM(1)); } -template <void func(u8)> +template <void func(u64, u64, u64)> void Wrap() { - func((u8)PARAM(0)); + func(PARAM(0), PARAM(1), PARAM(2)); } #undef PARAM diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 2cbca5e5b..30dade552 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -25,10 +25,11 @@ void ReleaseThreadMutexes(Thread* thread) { Mutex::Mutex() {} Mutex::~Mutex() {} -SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { +SharedPtr<Mutex> Mutex::Create(bool initial_locked, VAddr addr, std::string name) { SharedPtr<Mutex> mutex(new Mutex); mutex->lock_count = 0; + mutex->addr = addr; mutex->name = std::move(name); mutex->holding_thread = nullptr; diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index bacacd690..503d3ee75 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -21,7 +21,7 @@ public: * @param name Optional name of mutex * @return Pointer to new Mutex object */ - static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); + static SharedPtr<Mutex> Create(bool initial_locked, VAddr addr, std::string name = "Unknown"); std::string GetTypeName() const override { return "Mutex"; @@ -39,6 +39,7 @@ public: u32 priority; ///< The priority of the mutex, used for priority inheritance. std::string name; ///< Name of mutex (optional) SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex + VAddr addr; /** * Elevate the mutex priority to the best priority diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index cf3163e0f..9e145866f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -30,10 +30,10 @@ CodeSet::~CodeSet() {} u32 Process::next_process_id; -SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { +SharedPtr<Process> Process::Create(std::string&& name) { SharedPtr<Process> process(new Process); - process->codeset = std::move(code_set); + process->name = std::move(name); process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); @@ -112,25 +112,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { } } -void Process::Run(s32 main_thread_priority, u32 stack_size) { - memory_region = GetMemoryRegion(flags.memory_region); - - auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, - MemoryState memory_state) { - auto vma = vm_manager - .MapMemoryBlock(segment.addr, codeset->memory, segment.offset, segment.size, - memory_state) - .Unwrap(); - vm_manager.Reprotect(vma, permissions); - misc_memory_used += segment.size; - memory_region->used += segment.size; - }; - - // Map CodeSet segments - MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); - MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); - MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); - +void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { // Allocate and map stack vm_manager .MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size, @@ -147,7 +129,28 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { } vm_manager.LogLayout(Log::Level::Debug); - Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this); + + Kernel::SetupMainThread(entry_point, main_thread_priority, this); +} + +void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { + memory_region = GetMemoryRegion(flags.memory_region); + + auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, + MemoryState memory_state) { + auto vma = vm_manager + .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, segment.size, + memory_state) + .Unwrap(); + vm_manager.Reprotect(vma, permissions); + misc_memory_used += segment.size; + memory_region->used += segment.size; + }; + + // Map CodeSet segments + MapSegment(module_->code, VMAPermission::ReadWrite, MemoryState::Private); + MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static); + MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static); } VAddr Process::GetLinearHeapAreaAddress() const { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index b52211d2a..f05f2703e 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -79,7 +79,11 @@ struct CodeSet final : public Object { u32 size = 0; }; - Segment code, rodata, data; + Segment segments[3]; + Segment& code = segments[0]; + Segment& rodata = segments[1]; + Segment& data = segments[2]; + VAddr entrypoint; private: @@ -89,13 +93,13 @@ private: class Process final : public Object { public: - static SharedPtr<Process> Create(SharedPtr<CodeSet> code_set); + static SharedPtr<Process> Create(std::string&& name); std::string GetTypeName() const override { return "Process"; } std::string GetName() const override { - return codeset->name; + return name; } static const HandleType HANDLE_TYPE = HandleType::Process; @@ -105,7 +109,6 @@ public: static u32 next_process_id; - SharedPtr<CodeSet> codeset; /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; @@ -134,7 +137,9 @@ public: /** * Applies address space changes and launches the process main thread. */ - void Run(s32 main_thread_priority, u32 stack_size); + void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); + + void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); /////////////////////////////////////////////////////////////////////////////////////////////// // Memory Management @@ -160,6 +165,8 @@ public: /// This vector will grow as more pages are allocated for new threads. std::vector<std::bitset<8>> tls_slots; + std::string name; + VAddr GetLinearHeapAreaAddress() const; VAddr GetLinearHeapBase() const; VAddr GetLinearHeapLimit() const; diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index fcf586728..2605b2595 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -13,7 +13,7 @@ namespace Kernel { Semaphore::Semaphore() {} Semaphore::~Semaphore() {} -ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, +ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, VAddr address, std::string name) { if (initial_count > max_count) @@ -25,6 +25,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou // and the rest is reserved for the caller thread semaphore->max_count = max_count; semaphore->available_count = initial_count; + semaphore->address = address; semaphore->name = std::move(name); return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index 7b0cacf2e..77c491a24 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -22,7 +22,7 @@ public: * @param name Optional name of semaphore * @return The created semaphore */ - static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, + static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, VAddr address, std::string name = "Unknown"); std::string GetTypeName() const override { @@ -39,6 +39,7 @@ public: s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have s32 available_count; ///< Number of free slots left in the semaphore + VAddr address; std::string name; ///< Name of semaphore (optional) bool ShouldWait(Thread* thread) const override; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0f7970ebe..75df49ac2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -358,8 +358,8 @@ std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& t * @param entry_point Address of entry point for execution * @param arg User argument for thread */ -static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_top, - u32 entry_point, u32 arg) { +static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stack_top, + VAddr entry_point, u64 arg) { memset(&context, 0, sizeof(ARM_Interface::ThreadContext)); context.cpu_registers[0] = arg; @@ -446,7 +446,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // Map the page to the current process' address space. // TODO(Subv): Find the correct MemoryState for this region. vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Private); + linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Static); } // Mark the slot as used @@ -495,6 +495,9 @@ void Thread::BoostPriority(u32 priority) { } SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) { + // Setup page table so we can write to memory + SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, Memory::HEAP_VADDR_END, owner_process); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 314fba81f..fafcab156 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -184,8 +184,8 @@ public: u32 thread_id; u32 status; - u32 entry_point; - u32 stack_top; + VAddr entry_point; + VAddr stack_top; u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application u32 current_priority; ///< Current thread priority, can be temporarily changed @@ -250,13 +250,13 @@ void Reschedule(); * Arbitrate the highest priority thread that is waiting * @param address The address for which waiting threads should be arbitrated */ -Thread* ArbitrateHighestPriorityThread(u32 address); +Thread* ArbitrateHighestPriorityThread(VAddr address); /** * Arbitrate all threads currently waiting. * @param address The address for which waiting threads should be arbitrated */ -void ArbitrateAllThreads(u32 address); +void ArbitrateAllThreads(VAddr address); /** * Gets the current thread diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 7a007c065..9762ef535 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -4,8 +4,10 @@ #include <iterator> #include "common/assert.h" +#include "core/arm/arm_interface.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/vm_manager.h" +#include "core/core.h" #include "core/memory.h" #include "core/memory_setup.h" #include "core/mmio.h" @@ -60,7 +62,7 @@ void VMManager::Reset() { page_table.attributes.fill(Memory::PageType::Unmapped); page_table.cached_res_count.fill(0); - UpdatePageTableForVMA(initial_vma); + //UpdatePageTableForVMA(initial_vma); } VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { @@ -73,7 +75,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, - size_t offset, u32 size, + size_t offset, u64 size, MemoryState state) { ASSERT(block != nullptr); ASSERT(offset + size <= block->size()); @@ -83,6 +85,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, VirtualMemoryArea& final_vma = vma_handle->second; ASSERT(final_vma.size == size); + Core::CPU().MapBackingMemory(target, size, block->data() + offset, VMAPermission::ReadWriteExecute); + final_vma.type = VMAType::AllocatedMemoryBlock; final_vma.permissions = VMAPermission::ReadWrite; final_vma.meminfo_state = state; @@ -93,7 +97,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); } -ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u32 size, +ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state) { ASSERT(memory != nullptr); @@ -102,6 +106,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me VirtualMemoryArea& final_vma = vma_handle->second; ASSERT(final_vma.size == size); + Core::CPU().MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); + final_vma.type = VMAType::BackingMemory; final_vma.permissions = VMAPermission::ReadWrite; final_vma.meminfo_state = state; @@ -111,7 +117,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); } -ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, +ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, Memory::MMIORegionPointer mmio_handler) { // This is the appropriately sized VMA that will turn into our allocation. @@ -145,7 +151,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { return MergeAdjacent(vma_handle); } -ResultCode VMManager::UnmapRange(VAddr target, u32 size) { +ResultCode VMManager::UnmapRange(VAddr target, u64 size) { CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); VAddr target_end = target + size; @@ -170,7 +176,7 @@ VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission ne return MergeAdjacent(iter); } -ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_perms) { +ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) { CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); VAddr target_end = target + size; @@ -213,7 +219,7 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) { return vma_map.erase(iter, iter); // Erases an empty range of elements } -ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { +ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); @@ -229,8 +235,8 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { return ERR_INVALID_ADDRESS_STATE; } - u32 start_in_vma = base - vma.base; - u32 end_in_vma = start_in_vma + size; + u64 start_in_vma = base - vma.base; + u64 end_in_vma = start_in_vma + size; if (end_in_vma > vma.size) { // Requested allocation doesn't fit inside VMA @@ -249,7 +255,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { return MakeResult<VMAIter>(vma_handle); } -ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { +ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target); @@ -278,7 +284,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { return MakeResult<VMAIter>(begin_vma); } -VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { +VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { VirtualMemoryArea& old_vma = vma_handle->second; VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 1302527bb..cb5bb8243 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -64,7 +64,7 @@ struct VirtualMemoryArea { /// Virtual base address of the region. VAddr base = 0; /// Size of the region. - u32 size = 0; + u64 size = 0; VMAType type = VMAType::Free; VMAPermission permissions = VMAPermission::None; @@ -109,7 +109,7 @@ public: * used. * @note This is the limit used by the New 3DS kernel. Old 3DS used 0x20000000. */ - static const u32 MAX_ADDRESS = 0x40000000; + static const VAddr MAX_ADDRESS = 0x8000000000; /** * A map covering the entirety of the managed address space, keyed by the `base` field of each @@ -142,7 +142,7 @@ public: * @param state MemoryState tag to attach to the VMA. */ ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, - size_t offset, u32 size, MemoryState state); + size_t offset, u64 size, MemoryState state); /** * Maps an unmanaged host memory pointer at a given address. @@ -152,7 +152,7 @@ public: * @param size Size of the mapping. * @param state MemoryState tag to attach to the VMA. */ - ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u32 size, MemoryState state); + ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); /** * Maps a memory-mapped IO region at a given address. @@ -163,17 +163,17 @@ public: * @param state MemoryState tag to attach to the VMA. * @param mmio_handler The handler that will implement read and write for this MMIO region. */ - ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, + ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, Memory::MMIORegionPointer mmio_handler); /// Unmaps a range of addresses, splitting VMAs as necessary. - ResultCode UnmapRange(VAddr target, u32 size); + ResultCode UnmapRange(VAddr target, u64 size); /// Changes the permissions of the given VMA. VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); /// Changes the permissions of a range of addresses, splitting VMAs as necessary. - ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms); + ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); /** * Scans all VMAs and updates the page table range of any that use the given vector as backing @@ -201,19 +201,19 @@ private: * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing * the appropriate error checking. */ - ResultVal<VMAIter> CarveVMA(VAddr base, u32 size); + ResultVal<VMAIter> CarveVMA(VAddr base, u64 size); /** * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each * end of the range. */ - ResultVal<VMAIter> CarveVMARange(VAddr base, u32 size); + ResultVal<VMAIter> CarveVMARange(VAddr base, u64 size); /** * Splits a VMA in two, at the specified offset. * @returns the right side of the split, with the original iterator becoming the left side. */ - VMAIter SplitVMA(VMAIter vma, u32 offset_in_vma); + VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); /** * Checks for and merges the specified VMA with adjacent ones if possible. diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 59ea9823d..912ab550d 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -1073,7 +1073,17 @@ void Init() { MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); - lock = Kernel::Mutex::Create(false, "APT_U:Lock"); + if (LoadSharedFont()) { + shared_font_loaded = true; + } else if (LoadLegacySharedFont()) { + LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); + shared_font_loaded = true; + } else { + LOG_WARNING(Service_APT, "Unable to load shared font"); + shared_font_loaded = false; + } + + lock = Kernel::Mutex::Create(false, 0, "APT_U:Lock"); cpu_percent = 0; unknown_ns_state_field = 0; diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 9471ec1ef..aac903ccb 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -47,7 +47,7 @@ static void Initialize(Interface* self) { MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); - mutex = Kernel::Mutex::Create(false, "CSND:mutex"); + mutex = Kernel::Mutex::Create(false, 0, "CSND:mutex"); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = IPC::CopyHandleDesc(2); diff --git a/src/core/hle/service/ldr_ro/cro_helper.cpp b/src/core/hle/service/ldr_ro/cro_helper.cpp index f78545f37..6128f8a6c 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.cpp +++ b/src/core/hle/service/ldr_ro/cro_helper.cpp @@ -274,7 +274,7 @@ ResultVal<VAddr> CROHelper::RebaseSegmentTable(u32 cro_size, VAddr data_segment_ } SetEntry(i, segment); } - return MakeResult<u32>(prev_data_segment + module_address); + return MakeResult<VAddr>(prev_data_segment + module_address); } ResultCode CROHelper::RebaseExportNamedSymbolTable() { diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 5c955cf54..fb873981c 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -62,7 +62,7 @@ void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x2, 0, 0); notification_semaphore = - Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); + Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, 0, "SRV:Notification").Unwrap(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index e8ca419d5..e4b803046 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -31,7 +31,6 @@ #include "core/hle/kernel/timer.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/wait_object.h" -#include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -201,21 +200,17 @@ static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { } /// Connect to an OS service given the port name, returns the handle to the port to out -static ResultCode ConnectToPort(Kernel::Handle* out_handle, VAddr port_name_address) { - if (!Memory::IsValidVirtualAddress(port_name_address)) +static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { + if (port_name == nullptr) return Kernel::ERR_NOT_FOUND; - - static constexpr std::size_t PortNameMaxLength = 11; - // 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); - if (port_name.size() > PortNameMaxLength) + if (std::strlen(port_name) > 11) return Kernel::ERR_PORT_NAME_TOO_LONG; - LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name.c_str()); + LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); auto it = Service::g_kernel_named_ports.find(port_name); if (it == Service::g_kernel_named_ports.end()) { - LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name.c_str()); + LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); return Kernel::ERR_NOT_FOUND; } @@ -275,24 +270,6 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = [](ThreadWakeupReason reason, - Kernel::SharedPtr<Kernel::Thread> thread, - Kernel::SharedPtr<Kernel::WaitObject> object) { - - ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); - return; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - - // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we - // don't have to do anything else here. - }; - Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread @@ -307,11 +284,12 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, +static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { Kernel::Thread* thread = Kernel::GetCurrentThread(); - if (!Memory::IsValidVirtualAddress(handles_address)) + // Check if 'handles' is invalid + if (handles == nullptr) return Kernel::ERR_INVALID_POINTER; // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If @@ -326,8 +304,7 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand std::vector<ObjectPtr> objects(handle_count); for (int i = 0; i < handle_count; ++i) { - Kernel::Handle handle = Memory::Read32(handles_address + i * sizeof(Kernel::Handle)); - auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); + auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; @@ -366,23 +343,6 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = [](ThreadWakeupReason reason, - Kernel::SharedPtr<Kernel::Thread> thread, - Kernel::SharedPtr<Kernel::WaitObject> object) { - - ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); - return; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - // The wait_all case does not update the output index. - }; - Core::System::GetInstance().PrepareReschedule(); // This value gets set to -1 by default in this case, it is not modified after this. @@ -400,7 +360,7 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); object->Acquire(thread); - *out = static_cast<s32>(std::distance(objects.begin(), itr)); + *out = std::distance(objects.begin(), itr); return RESULT_SUCCESS; } @@ -428,37 +388,22 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = [](ThreadWakeupReason reason, - Kernel::SharedPtr<Kernel::Thread> thread, - Kernel::SharedPtr<Kernel::WaitObject> object) { - - ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); - - if (reason == ThreadWakeupReason::Timeout) { - thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); - return; - } - - ASSERT(reason == ThreadWakeupReason::Signal); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); - }; - Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects. // Otherwise we retain the default value of timeout, and -1 in the out parameter + thread->wait_set_output = true; *out = -1; return Kernel::RESULT_TIMEOUT; } } /// In a single operation, sends a IPC reply and waits for a new request. -static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count, +static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count, Kernel::Handle reply_target) { - if (!Memory::IsValidVirtualAddress(handles_address)) + // 'handles' has to be a valid pointer even if 'handle_count' is 0. + if (handles == nullptr) return Kernel::ERR_INVALID_POINTER; // Check if 'handle_count' is invalid @@ -469,8 +414,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ std::vector<ObjectPtr> objects(handle_count); for (int i = 0; i < handle_count; ++i) { - Kernel::Handle handle = Memory::Read32(handles_address + i * sizeof(Kernel::Handle)); - auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); + auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; @@ -524,7 +468,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); object->Acquire(thread); - *index = static_cast<s32>(std::distance(objects.begin(), itr)); + *index = std::distance(objects.begin(), itr); if (object->GetHandleType() == Kernel::HandleType::ServerSession) { auto server_session = static_cast<Kernel::ServerSession*>(object); @@ -538,6 +482,8 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ // No objects were ready to be acquired, prepare to suspend the thread. + // TODO(Subv): Perform IPC translation upon wakeup. + // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH_ANY; @@ -549,24 +495,12 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ thread->wait_objects = std::move(objects); - thread->wakeup_callback = [](ThreadWakeupReason reason, - Kernel::SharedPtr<Kernel::Thread> thread, - Kernel::SharedPtr<Kernel::WaitObject> object) { - - ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); - ASSERT(reason == ThreadWakeupReason::Signal); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); - - // TODO(Subv): Perform IPC translation upon wakeup. - }; - Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. // By default the index is set to -1. + thread->wait_set_output = true; *index = -1; return RESULT_SUCCESS; } @@ -623,10 +557,8 @@ static void Break(u8 break_reason) { } /// Used to output a message on a debug hardware unit - does nothing on a retail unit -static void OutputDebugString(VAddr address, int len) { - std::vector<char> string(len); - Memory::ReadBlock(address, string.data(), len); - LOG_DEBUG(Debug_Emulated, "%.*s", len, string.data()); +static void OutputDebugString(const char* string, int len) { + LOG_DEBUG(Debug_Emulated, "%.*s", len, string); } /// Get resource limit @@ -644,9 +576,9 @@ static ResultCode GetResourceLimit(Kernel::Handle* resource_limit, Kernel::Handl } /// Get resource limit current values -static ResultCode GetResourceLimitCurrentValues(VAddr values, Kernel::Handle resource_limit_handle, - VAddr names, u32 name_count) { - LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%08X, name_count=%d", +static ResultCode GetResourceLimitCurrentValues(s64* values, Kernel::Handle resource_limit_handle, + u32* names, u32 name_count) { + LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", resource_limit_handle, names, name_count); SharedPtr<Kernel::ResourceLimit> resource_limit = @@ -654,19 +586,16 @@ static ResultCode GetResourceLimitCurrentValues(VAddr values, Kernel::Handle res if (resource_limit == nullptr) return ERR_INVALID_HANDLE; - for (unsigned int i = 0; i < name_count; ++i) { - u32 name = Memory::Read32(names + i * sizeof(u32)); - s64 value = resource_limit->GetCurrentResourceValue(name); - Memory::Write64(values + i * sizeof(u64), value); - } + for (unsigned int i = 0; i < name_count; ++i) + values[i] = resource_limit->GetCurrentResourceValue(names[i]); return RESULT_SUCCESS; } /// Get resource limit max values -static ResultCode GetResourceLimitLimitValues(VAddr values, Kernel::Handle resource_limit_handle, - VAddr names, u32 name_count) { - LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%08X, name_count=%d", +static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resource_limit_handle, + u32* names, u32 name_count) { + LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", resource_limit_handle, names, name_count); SharedPtr<Kernel::ResourceLimit> resource_limit = @@ -674,11 +603,8 @@ static ResultCode GetResourceLimitLimitValues(VAddr values, Kernel::Handle resou if (resource_limit == nullptr) return ERR_INVALID_HANDLE; - for (unsigned int i = 0; i < name_count; ++i) { - u32 name = Memory::Read32(names + i * sizeof(u32)); - s64 value = resource_limit->GetMaxResourceValue(names); - Memory::Write64(values + i * sizeof(u64), value); - } + for (unsigned int i = 0; i < name_count; ++i) + values[i] = resource_limit->GetMaxResourceValue(names[i]); return RESULT_SUCCESS; } @@ -729,9 +655,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 ent "Newly created thread must run in the SysCore (Core1), unimplemented."); } - CASCADE_RESULT(SharedPtr<Thread> thread, - Kernel::Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, - Kernel::g_current_process)); + CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, + arg, processor_id, stack_top)); thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 @@ -756,7 +681,7 @@ static void ExitThread() { } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) { +static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -766,7 +691,7 @@ static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) { } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Kernel::Handle handle, u32 priority) { +static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { if (priority > THREADPRIO_LOWEST) { return Kernel::ERR_OUT_OF_RANGE; } @@ -1051,7 +976,7 @@ static void SleepThread(s64 nanoseconds) { static s64 GetSystemTick() { s64 result = CoreTiming::GetTicks(); // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. - CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b + Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b return result; } @@ -1110,9 +1035,9 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si } static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client_port, - VAddr name_address, u32 max_sessions) { + const char* name, u32 max_sessions) { // TODO(Subv): Implement named ports. - ASSERT_MSG(name_address == 0, "Named ports are currently unimplemented"); + ASSERT_MSG(name == nullptr, "Named ports are currently unimplemented"); using Kernel::ServerPort; using Kernel::ClientPort; @@ -1263,7 +1188,7 @@ struct FunctionDef { Func* func; const char* name; }; -} // namespace +} static const FunctionDef SVC_Table[] = { {0x00, nullptr, "Unknown"}, @@ -1407,9 +1332,6 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void CallSVC(u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); - // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); - const FunctionDef* info = GetSVCInfo(immediate); if (info) { if (info->func) { @@ -1420,4 +1342,4 @@ void CallSVC(u32 immediate) { } } -} // namespace SVC +} // namespace diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 83ad9d898..d1bfe51e6 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -515,15 +515,15 @@ template void Write<u8>(u32 addr, const u8 data); /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { - VideoCore::g_renderer->SwapBuffers(); - - // Signal to GSP that GPU interrupt has occurred - // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub - // screen, or if both use the same interrupts and these two instead determine the - // beginning and end of the VBlank period. If needed, split the interrupt firing into - // two different intervals. - Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); - Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); + //VideoCore::g_renderer->SwapBuffers(); + + //// Signal to GSP that GPU interrupt has occurred + //// TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub + //// screen, or if both use the same interrupts and these two instead determine the + //// beginning and end of the VBlank period. If needed, split the interrupt firing into + //// two different intervals. + //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); + //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 918038f1e..7b0342cc9 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -267,15 +267,15 @@ ResultStatus AppLoader_THREEDSX::Load(Kernel::SharedPtr<Kernel::Process>& proces return ResultStatus::Error; codeset->name = filename; - process = Kernel::Process::Create(std::move(codeset)); + process = Kernel::Process::Create("main"); + process->LoadModule(codeset, codeset->entrypoint); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - - process->Run(48, Kernel::DEFAULT_STACK_SIZE); + process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); Service::FS::RegisterSelfNCCH(*this); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index e36e42120..9969a8c39 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -364,12 +364,19 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const namespace Loader { FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { - u32 magic; + static constexpr u16 ELF_MACHINE_ARM{0x28}; + + u32 magic = 0; file.Seek(0, SEEK_SET); if (1 != file.ReadArray<u32>(&magic, 1)) return FileType::Error; - if (MakeMagic('\x7f', 'E', 'L', 'F') == magic) + u16 machine = 0; + file.Seek(18, SEEK_SET); + if (1 != file.ReadArray<u16>(&machine, 1)) + return FileType::Error; + + if (MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) return FileType::ELF; return FileType::Error; @@ -394,7 +401,8 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); codeset->name = filename; - process = Kernel::Process::Create(std::move(codeset)); + process = Kernel::Process::Create("main"); + process->LoadModule(codeset, codeset->entrypoint); process->svc_access_mask.set(); process->address_mappings = default_address_mappings; @@ -402,7 +410,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(48, Kernel::DEFAULT_STACK_SIZE); + process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp new file mode 100644 index 000000000..a265b9315 --- /dev/null +++ b/src/core/loader/linker.cpp @@ -0,0 +1,151 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/common_funcs.h" +#include "common/logging/log.h" +#include "common/swap.h" +#include "core/loader/linker.h" +#include "core/memory.h" + +namespace Loader { + +enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 }; + +enum DynamicType : u32 { + DT_NULL = 0, + DT_PLTRELSZ = 2, + DT_STRTAB = 5, + DT_SYMTAB = 6, + DT_RELA = 7, + DT_RELASZ = 8, + DT_STRSZ = 10, + DT_JMPREL = 23, +}; + +struct Elf64_Rela { + u64_le offset; + RelocationType type; + u32_le symbol; + s64_le addend; +}; +static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size."); + +struct Elf64_Dyn { + u64_le tag; + u64_le value; +}; +static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size."); + +struct Elf64_Sym { + u32_le name; + INSERT_PADDING_BYTES(0x2); + u16_le shndx; + u64_le value; + u64_le size; +}; +static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size."); + +void Linker::WriteRelocations(std::vector<u8>& program_image, + const std::vector<Symbol>& symbols, u64 relocation_offset, + u64 size, bool is_jump_relocation, VAddr load_base) { + for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) { + Elf64_Rela rela; + std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela)); + + const Symbol& symbol = symbols[rela.symbol]; + switch (rela.type) { + case RelocationType::RELATIVE: { + const u64 value = load_base + rela.addend; + if (!symbol.name.empty()) { + exports[symbol.name] = value; + } + std::memcpy(&program_image[rela.offset], &value, sizeof(u64)); + break; + } + case RelocationType::JUMP_SLOT: + case RelocationType::GLOB_DAT: + if (!symbol.value) { + imports[symbol.name] = {rela.offset + load_base, 0}; + } else { + exports[symbol.name] = symbol.value; + std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64)); + } + break; + case RelocationType::ABS64: + if (!symbol.value) { + imports[symbol.name] = {rela.offset + load_base, rela.addend}; + } else { + const u64 value = symbol.value + rela.addend; + exports[symbol.name] = value; + std::memcpy(&program_image[rela.offset], &value, sizeof(u64)); + } + break; + default: + LOG_CRITICAL(Loader, "Unknown relocation type: %d", rela.type); + break; + } + } +} + +void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, + VAddr load_base) { + std::map<u64, u64> dynamic; + while (dynamic_section_offset < program_image.size()) { + Elf64_Dyn dyn; + std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn)); + dynamic_section_offset += sizeof(Elf64_Dyn); + + if (dyn.tag == DT_NULL) { + break; + } + dynamic[dyn.tag] = dyn.value; + } + + u64 offset = dynamic[DT_SYMTAB]; + std::vector<Symbol> symbols; + while (offset < program_image.size()) { + Elf64_Sym sym; + std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym)); + offset += sizeof(Elf64_Sym); + + if (sym.name >= dynamic[DT_STRSZ]) { + break; + } + + std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]); + if (sym.value) { + exports[name] = load_base + sym.value; + symbols.emplace_back(std::move(name), load_base + sym.value); + } else { + symbols.emplace_back(std::move(name), 0); + } + } + + if (dynamic.find(DT_RELA) != dynamic.end()) { + WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], false, + load_base); + } + + if (dynamic.find(DT_JMPREL) != dynamic.end()) { + WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], true, + load_base); + } +} + +void Linker::ResolveImports() { + // Resolve imports + for (const auto& import : imports) { + const auto& search = exports.find(import.first); + if (search != exports.end()) { + Memory::Write64(import.second.ea, search->second + import.second.addend); + } + else { + LOG_ERROR(Loader, "Unresolved import: %s", import.first.c_str()); + } + } +} + +} // namespace Loader diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h new file mode 100644 index 000000000..d18155f0d --- /dev/null +++ b/src/core/loader/linker.h @@ -0,0 +1,37 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include <string> +#include "common/common_types.h" + +namespace Loader { + +class Linker { +protected: + struct Symbol { + Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {} + std::string name; + u64 value; + }; + + struct Import { + VAddr ea; + s64 addend; + }; + + void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols, + u64 relocation_offset, u64 size, bool is_jump_relocation, + VAddr load_base); + void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base); + + void ResolveImports(); + + std::map<std::string, Import> imports; + std::map<std::string, VAddr> exports; +}; + +} // namespace Loader diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index be719d74c..73318c584 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -10,6 +10,8 @@ #include "core/loader/3dsx.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" +#include "core/loader/nro.h" +#include "core/loader/nso.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -32,6 +34,8 @@ FileType IdentifyFile(FileUtil::IOFile& file) { CHECK_TYPE(THREEDSX) CHECK_TYPE(ELF) CHECK_TYPE(NCCH) + CHECK_TYPE(NSO) + CHECK_TYPE(NRO) #undef CHECK_TYPE @@ -115,6 +119,14 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp case FileType::CCI: return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); + // NX NSO file format. + case FileType::NSO: + return std::make_unique<AppLoader_NSO>(std::move(file), filepath); + + // NX NRO file format. + case FileType::NRO: + return std::make_unique<AppLoader_NRO>(std::move(file), filepath); + default: return nullptr; } diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 82b2be6a3..311785d05 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -34,6 +34,8 @@ enum class FileType { CIA, ELF, THREEDSX, // 3DSX + NSO, + NRO, }; /** diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 52686e364..e33a37b2e 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -118,7 +118,8 @@ ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& proces codeset->entrypoint = codeset->code.addr; codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); - process = Kernel::Process::Create(std::move(codeset)); + process = Kernel::Process::Create("main"); + process->LoadModule(codeset, codeset->entrypoint); // Attach a resource limit to the process based on the resource limit category process->resource_limit = @@ -138,7 +139,7 @@ ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& proces s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority; u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size; - process->Run(priority, stack_size); + process->Run(codeset->entrypoint, priority, stack_size); return ResultStatus::Success; } return ResultStatus::Error; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp new file mode 100644 index 000000000..24c2c55a9 --- /dev/null +++ b/src/core/loader/nro.cpp @@ -0,0 +1,162 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/logging/log.h" +#include "common/swap.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/loader/nro.h" +#include "core/memory.h" + +namespace Loader { + +struct NroSegmentHeader { + u32_le offset; + u32_le size; +}; +static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect size."); + +struct NroHeader { + INSERT_PADDING_BYTES(0x4); + u32_le module_header_offset; + INSERT_PADDING_BYTES(0x8); + u32_le magic; + INSERT_PADDING_BYTES(0x4); + u32_le file_size; + INSERT_PADDING_BYTES(0x4); + std::array<NroSegmentHeader, 3> segments; // Text, RoData, Data (in that order) + u32_le bss_size; + INSERT_PADDING_BYTES(0x44); +}; +static_assert(sizeof(NroHeader) == 0x80, "NroHeader has incorrect size."); + +struct ModHeader { + u32_le magic; + u32_le dynamic_offset; + u32_le bss_start_offset; + u32_le bss_end_offset; + u32_le unwind_start_offset; + u32_le unwind_end_offset; + u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base +}; +static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); + +FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file) { + // Read NSO header + NroHeader nro_header{}; + file.Seek(0, SEEK_SET); + if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { + return FileType::Error; + } + if (nro_header.magic == MakeMagic('N', 'R', 'O', '0')) { + return FileType::NRO; + } + return FileType::Error; +} + +static constexpr u32 PageAlignSize(u32 size) { + return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; +} + +static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NroSegmentHeader& header) { + std::vector<u8> data; + data.resize(header.size); + + file.Seek(header.offset + sizeof(NroHeader), SEEK_SET); + size_t bytes_read{file.ReadBytes(data.data(), header.size)}; + if (header.size != PageAlignSize(static_cast<u32>(bytes_read))) { + LOG_CRITICAL(Loader, "Failed to read NRO segment bytes", header.size); + return {}; + } + + return data; +} + +bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { + FileUtil::IOFile file(path, "rb"); + if (!file.IsOpen()) { + return {}; + } + + // Read NSO header + NroHeader nro_header{}; + file.Seek(0, SEEK_SET); + if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { + return {}; + } + if (nro_header.magic != MakeMagic('N', 'R', 'O', '0')) { + return {}; + } + + // Build program image + Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0); + std::vector<u8> program_image; + program_image.resize(PageAlignSize(nro_header.file_size + nro_header.bss_size)); + file.Seek(0, SEEK_SET); + file.ReadBytes(program_image.data(), nro_header.file_size); + + for (int i = 0; i < nro_header.segments.size(); ++i) { + codeset->segments[i].addr = nro_header.segments[i].offset; + codeset->segments[i].offset = nro_header.segments[i].offset; + codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); + } + + // Read MOD header + ModHeader mod_header{}; + u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist + std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, + sizeof(ModHeader)); + const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')}; + if (has_mod_header) { + // Resize program image to include .bss section and page align each section + bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); + codeset->data.size += bss_size; + } + program_image.resize(PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)); + + // Relocate symbols if there was a proper MOD header - This must happen after the image has been + // loaded into memory + if (has_mod_header) { + Relocate(program_image, nro_header.module_header_offset + mod_header.dynamic_offset, + load_base); + } + + // Load codeset for current process + codeset->name = path; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + Kernel::g_current_process->LoadModule(codeset, load_base); + + return true; +} + +ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { + if (is_loaded) { + return ResultStatus::ErrorAlreadyLoaded; + } + if (!file.IsOpen()) { + return ResultStatus::Error; + } + + // Load and relocate "main" and "sdk" NSO + static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; + process = Kernel::Process::Create("main"); + if (!LoadNro(filepath, base_addr)) { + return ResultStatus::ErrorInvalidFormat; + } + + process->svc_access_mask.set(); + process->address_mappings = default_address_mappings; + process->resource_limit = + Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); + process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); + + ResolveImports(); + + is_loaded = true; + return ResultStatus::Success; +} + +} // namespace Loader diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h new file mode 100644 index 000000000..c85768c5b --- /dev/null +++ b/src/core/loader/nro.h @@ -0,0 +1,42 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include <string> +#include "common/common_types.h" +#include "common/file_util.h" +#include "core/hle/kernel/kernel.h" +#include "core/loader/linker.h" +#include "core/loader/loader.h" + +namespace Loader { + +/// Loads an NRO file +class AppLoader_NRO final : public AppLoader, Linker { +public: + AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath) + : AppLoader(std::move(file)), filepath(std::move(filepath)) {} + + /** + * Returns the type of the file + * @param file FileUtil::IOFile open file + * @return FileType found, or FileType::Error if this loader doesn't know it + */ + static FileType IdentifyType(FileUtil::IOFile& file); + + FileType GetFileType() override { + return IdentifyType(file); + } + + ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + +private: + bool LoadNro(const std::string& path, VAddr load_base); + + std::string filepath; +}; + +} // namespace Loader diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp new file mode 100644 index 000000000..b1b57d0c0 --- /dev/null +++ b/src/core/loader/nso.cpp @@ -0,0 +1,185 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> +#include <lz4.h> + +#include "common/logging/log.h" +#include "common/swap.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/loader/nso.h" +#include "core/memory.h" + +namespace Loader { + +struct NsoSegmentHeader { + u32_le offset; + u32_le location; + u32_le size; + u32_le alignment; +}; +static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size."); + +struct NsoHeader { + u32_le magic; + INSERT_PADDING_BYTES(0xc); + std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) + u32_le bss_size; + INSERT_PADDING_BYTES(0x1c); + std::array<u32_le, 3> segments_compressed_size; +}; +static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); + +struct ModHeader { + u32_le magic; + u32_le dynamic_offset; + u32_le bss_start_offset; + u32_le bss_end_offset; + u32_le eh_frame_hdr_start_offset; + u32_le eh_frame_hdr_end_offset; + u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base +}; +static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); + +FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file) { + u32 magic = 0; + file.Seek(0, SEEK_SET); + if (1 != file.ReadArray<u32>(&magic, 1)) { + return FileType::Error; + } + + if (MakeMagic('N', 'S', 'O', '0') == magic) { + return FileType::NSO; + } + + return FileType::Error; +} + +static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header, + int compressed_size) { + std::vector<u8> compressed_data; + compressed_data.resize(compressed_size); + + file.Seek(header.offset, SEEK_SET); + if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) { + LOG_CRITICAL(Loader, "Failed to read %d NSO LZ4 compressed bytes", compressed_size); + return {}; + } + + std::vector<u8> uncompressed_data; + uncompressed_data.resize(header.size); + const int bytes_uncompressed = LZ4_decompress_safe( + reinterpret_cast<const char*>(compressed_data.data()), + reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size); + + ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(), + "%d != %d != %d", bytes_uncompressed, header.size, uncompressed_data.size()); + + return uncompressed_data; +} + +static constexpr u32 PageAlignSize(u32 size) { + return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; +} + +VAddr AppLoader_NSO::LoadNso(const std::string& path, VAddr load_base, bool relocate) { + FileUtil::IOFile file(path, "rb"); + if (!file.IsOpen()) { + return {}; + } + + // Read NSO header + NsoHeader nso_header{}; + file.Seek(0, SEEK_SET); + if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) { + return {}; + } + if (nso_header.magic != MakeMagic('N', 'S', 'O', '0')) { + return {}; + } + + // Build program image + Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0); + std::vector<u8> program_image; + for (int i = 0; i < nso_header.segments.size(); ++i) { + std::vector<u8> data = + ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]); + program_image.resize(nso_header.segments[i].location); + program_image.insert(program_image.end(), data.begin(), data.end()); + codeset->segments[i].addr = nso_header.segments[i].location; + codeset->segments[i].offset = nso_header.segments[i].location; + codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); + } + + // MOD header pointer is at .text offset + 4 + u32 module_offset; + std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); + + // Read MOD header + ModHeader mod_header{}; + u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist + std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); + const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')}; + if (has_mod_header) { + // Resize program image to include .bss section and page align each section + bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); + codeset->data.size += bss_size; + } + const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; + program_image.resize(image_size); + + // Relocate symbols if there was a proper MOD header - This must happen after the image has been + // loaded into memory + if (has_mod_header && relocate) { + Relocate(program_image, module_offset + mod_header.dynamic_offset, load_base); + } + + // Load codeset for current process + codeset->name = path; + codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); + Kernel::g_current_process->LoadModule(codeset, load_base); + + return load_base + image_size; +} + +ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { + if (is_loaded) { + return ResultStatus::ErrorAlreadyLoaded; + } + if (!file.IsOpen()) { + return ResultStatus::Error; + } + + // Load and relocate "rtld" NSO + static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; + process = Kernel::Process::Create("main"); + VAddr next_base_addr{LoadNso(filepath, base_addr)}; + if (!next_base_addr) { + return ResultStatus::ErrorInvalidFormat; + } + + // Load and relocate remaining submodules + for (const auto& module_name : {"main", "sdk", "subsdk0", "subsdk1"}) { + const std::string module_path = + filepath.substr(0, filepath.find_last_of("/\\")) + "/" + module_name; + next_base_addr = LoadNso(module_path, next_base_addr); + if (!next_base_addr) { + LOG_WARNING(Loader, "failed to find load module: %s", module_name); + } + } + + process->svc_access_mask.set(); + process->address_mappings = default_address_mappings; + process->resource_limit = + Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); + process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); + + ResolveImports(); + + is_loaded = true; + return ResultStatus::Success; +} + +} // namespace Loader diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h new file mode 100644 index 000000000..b6b86c209 --- /dev/null +++ b/src/core/loader/nso.h @@ -0,0 +1,43 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include <string> +#include "common/common_types.h" +#include "common/file_util.h" +#include "core/hle/kernel/kernel.h" +#include "core/loader/linker.h" +#include "core/loader/loader.h" + +namespace Loader { + +/// Loads an NSO file +class AppLoader_NSO final : public AppLoader, Linker { +public: + AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath) + : AppLoader(std::move(file)), filepath(std::move(filepath)) { + } + + /** + * Returns the type of the file + * @param file FileUtil::IOFile open file + * @return FileType found, or FileType::Error if this loader doesn't know it + */ + static FileType IdentifyType(FileUtil::IOFile& file); + + FileType GetFileType() override { + return IdentifyType(file); + } + + ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + +private: + VAddr LoadNso(const std::string& path, VAddr load_base, bool relocate = false); + + std::string filepath; +}; + +} // namespace Loader diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 7f58be6de..462d68386 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -37,14 +37,14 @@ PageTable* GetCurrentPageTable() { return current_page_table; } -static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { +static void MapPages(PageTable& page_table, VAddr base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, FlushMode::FlushAndInvalidate); - u32 end = base + size; + VAddr end = base + size; while (base != end) { ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); @@ -303,7 +303,7 @@ u8* GetPhysicalPointer(PAddr address) { return nullptr; } - u32 offset_into_region = address - area->paddr_base; + u64 offset_into_region = address - area->paddr_base; u8* target_pointer = nullptr; switch (area->paddr_base) { @@ -339,7 +339,7 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { return; } - u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; + u64 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; PAddr paddr = start; for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { @@ -443,7 +443,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) { VAddr overlap_end = std::min(end, region_end); PAddr physical_start = TryVirtualToPhysicalAddress(overlap_start).value(); - u32 overlap_size = overlap_end - overlap_start; + u32 overlap_size = static_cast<u32>(overlap_end - overlap_start); auto* rasterizer = VideoCore::g_renderer->Rasterizer(); switch (mode) { diff --git a/src/core/memory.h b/src/core/memory.h index dd599f73e..9a04b9a16 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -6,6 +6,7 @@ #include <array> #include <cstddef> +#include <map> #include <string> #include <vector> #include <boost/optional.hpp> @@ -22,10 +23,10 @@ namespace Memory { * Page size used by the ARM architecture. This is the smallest granularity with which memory can * be mapped. */ -const u32 PAGE_SIZE = 0x1000; -const u32 PAGE_MASK = PAGE_SIZE - 1; const int PAGE_BITS = 12; -const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); +const u64 PAGE_SIZE = 1 << PAGE_BITS; +const u64 PAGE_MASK = PAGE_SIZE - 1; +const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (32 - PAGE_BITS); enum class PageType { /// Page is unmapped and should cause an access error. @@ -124,8 +125,8 @@ enum : PAddr { /// Virtual user-space memory regions enum : VAddr { /// Where the application text, data and bss reside. - PROCESS_IMAGE_VADDR = 0x00100000, - PROCESS_IMAGE_MAX_SIZE = 0x03F00000, + PROCESS_IMAGE_VADDR = 0x08000000, + PROCESS_IMAGE_MAX_SIZE = 0x08000000, PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, /// Area where IPC buffers are mapped onto. diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 484713a92..2339bdfb8 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -15,7 +15,7 @@ static Memory::PageTable* page_table = nullptr; TestEnvironment::TestEnvironment(bool mutable_memory_) : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { - Kernel::g_current_process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + Kernel::g_current_process = Kernel::Process::Create(""); page_table = &Kernel::g_current_process->vm_manager.page_table; page_table->pointers.fill(nullptr); diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index 52336d027..4143a3ab8 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -22,7 +22,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); HLERequestContext context(std::move(session)); - auto process = Process::Create(CodeSet::Create("", 0)); + auto process = Process::Create(""); HandleTable handle_table; SECTION("works with empty cmdbuf") { @@ -142,7 +142,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); HLERequestContext context(std::move(session)); - auto process = Process::Create(CodeSet::Create("", 0)); + auto process = Process::Create(""); HandleTable handle_table; auto* input = context.CommandBuffer(); u32_le output[IPC::COMMAND_BUFFER_LENGTH]; diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index a01b896f7..671afb702 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -9,7 +9,7 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { SECTION("these regions should not be mapped on an empty process") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + auto process = Kernel::Process::Create(""); CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); @@ -20,14 +20,14 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { } SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + auto process = Kernel::Process::Create(""); Kernel::MapSharedPages(process->vm_manager); CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); } SECTION("special regions should be valid after mapping them") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + auto process = Kernel::Process::Create(""); SECTION("VRAM") { Kernel::HandleSpecialMapping(process->vm_manager, {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false}); @@ -48,7 +48,7 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { } SECTION("Unmapping a VAddr should make it invalid") { - auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); + auto process = Kernel::Process::Create(""); Kernel::MapSharedPages(process->vm_manager); process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE); CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); |