diff options
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) | |||
| 31 | # inih | 31 | # inih |
| 32 | add_subdirectory(inih) | 32 | add_subdirectory(inih) |
| 33 | 33 | ||
| 34 | # lz4 | ||
| 35 | set(LZ4_BUNDLED_MODE ON) | ||
| 36 | add_subdirectory(lz4/contrib/cmake_unofficial) | ||
| 37 | target_include_directories(lz4_static INTERFACE ./lz4/lib) | ||
| 38 | |||
| 34 | # MicroProfile | 39 | # MicroProfile |
| 35 | add_library(microprofile INTERFACE) | 40 | add_library(microprofile INTERFACE) |
| 36 | target_include_directories(microprofile INTERFACE ./microprofile) | 41 | target_include_directories(microprofile INTERFACE ./microprofile) |
| @@ -44,6 +49,10 @@ add_subdirectory(soundtouch) | |||
| 44 | # The SoundTouch target doesn't export the necessary include paths as properties by default | 49 | # The SoundTouch target doesn't export the necessary include paths as properties by default |
| 45 | target_include_directories(SoundTouch INTERFACE ./soundtouch/include) | 50 | target_include_directories(SoundTouch INTERFACE ./soundtouch/include) |
| 46 | 51 | ||
| 52 | # Unicorn | ||
| 53 | add_library(unicorn-headers INTERFACE) | ||
| 54 | target_include_directories(unicorn-headers INTERFACE ./unicorn/include) | ||
| 55 | |||
| 47 | # Xbyak | 56 | # Xbyak |
| 48 | if (ARCHITECTURE_x86_64) | 57 | if (ARCHITECTURE_x86_64) |
| 49 | # Defined before "dynarmic" above | 58 | # 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 @@ | |||
| 24 | 24 | ||
| 25 | #pragma once | 25 | #pragma once |
| 26 | 26 | ||
| 27 | #include <array> | ||
| 27 | #include <cstdint> | 28 | #include <cstdint> |
| 28 | 29 | ||
| 29 | #ifdef _MSC_VER | 30 | #ifdef _MSC_VER |
| @@ -47,8 +48,11 @@ typedef double f64; ///< 64-bit floating point | |||
| 47 | 48 | ||
| 48 | // TODO: It would be nice to eventually replace these with strong types that prevent accidental | 49 | // TODO: It would be nice to eventually replace these with strong types that prevent accidental |
| 49 | // conversion between each other. | 50 | // conversion between each other. |
| 50 | typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. | 51 | typedef u64 VAddr; ///< Represents a pointer in the userspace virtual address space. |
| 51 | typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. | 52 | typedef u64 PAddr; ///< Represents a pointer in the ARM11 physical address space. |
| 53 | |||
| 54 | using u128 = std::array<std::uint64_t, 2>; | ||
| 55 | static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); | ||
| 52 | 56 | ||
| 53 | // An inheritable class to disallow the copy constructor and operator= functions | 57 | // An inheritable class to disallow the copy constructor and operator= functions |
| 54 | class NonCopyable { | 58 | 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 | |||
| 6 | arm/dyncom/arm_dyncom_interpreter.cpp | 6 | arm/dyncom/arm_dyncom_interpreter.cpp |
| 7 | arm/dyncom/arm_dyncom_thumb.cpp | 7 | arm/dyncom/arm_dyncom_thumb.cpp |
| 8 | arm/dyncom/arm_dyncom_trans.cpp | 8 | arm/dyncom/arm_dyncom_trans.cpp |
| 9 | arm/unicorn/arm_unicorn.cpp | ||
| 10 | arm/unicorn/unicorn_dynload.c | ||
| 9 | arm/skyeye_common/armstate.cpp | 11 | arm/skyeye_common/armstate.cpp |
| 10 | arm/skyeye_common/armsupp.cpp | 12 | arm/skyeye_common/armsupp.cpp |
| 11 | arm/skyeye_common/vfp/vfp.cpp | 13 | arm/skyeye_common/vfp/vfp.cpp |
| @@ -178,8 +180,11 @@ set(SRCS | |||
| 178 | hw/y2r.cpp | 180 | hw/y2r.cpp |
| 179 | loader/3dsx.cpp | 181 | loader/3dsx.cpp |
| 180 | loader/elf.cpp | 182 | loader/elf.cpp |
| 183 | loader/linker.cpp | ||
| 181 | loader/loader.cpp | 184 | loader/loader.cpp |
| 182 | loader/ncch.cpp | 185 | loader/ncch.cpp |
| 186 | loader/nro.cpp | ||
| 187 | loader/nso.cpp | ||
| 183 | loader/smdh.cpp | 188 | loader/smdh.cpp |
| 184 | tracer/recorder.cpp | 189 | tracer/recorder.cpp |
| 185 | memory.cpp | 190 | memory.cpp |
| @@ -199,6 +204,8 @@ set(HEADERS | |||
| 199 | arm/dyncom/arm_dyncom_run.h | 204 | arm/dyncom/arm_dyncom_run.h |
| 200 | arm/dyncom/arm_dyncom_thumb.h | 205 | arm/dyncom/arm_dyncom_thumb.h |
| 201 | arm/dyncom/arm_dyncom_trans.h | 206 | arm/dyncom/arm_dyncom_trans.h |
| 207 | arm/unicorn/arm_unicorn.h | ||
| 208 | arm/unicorn/unicorn_dynload.h | ||
| 202 | arm/skyeye_common/arm_regformat.h | 209 | arm/skyeye_common/arm_regformat.h |
| 203 | arm/skyeye_common/armstate.h | 210 | arm/skyeye_common/armstate.h |
| 204 | arm/skyeye_common/armsupp.h | 211 | arm/skyeye_common/armsupp.h |
| @@ -379,8 +386,11 @@ set(HEADERS | |||
| 379 | hw/y2r.h | 386 | hw/y2r.h |
| 380 | loader/3dsx.h | 387 | loader/3dsx.h |
| 381 | loader/elf.h | 388 | loader/elf.h |
| 389 | loader/linker.h | ||
| 382 | loader/loader.h | 390 | loader/loader.h |
| 383 | loader/ncch.h | 391 | loader/ncch.h |
| 392 | loader/nro.h | ||
| 393 | loader/nso.h | ||
| 384 | loader/smdh.h | 394 | loader/smdh.h |
| 385 | tracer/recorder.h | 395 | tracer/recorder.h |
| 386 | tracer/citrace.h | 396 | tracer/citrace.h |
| @@ -395,7 +405,7 @@ set(HEADERS | |||
| 395 | create_directory_groups(${SRCS} ${HEADERS}) | 405 | create_directory_groups(${SRCS} ${HEADERS}) |
| 396 | add_library(core STATIC ${SRCS} ${HEADERS}) | 406 | add_library(core STATIC ${SRCS} ${HEADERS}) |
| 397 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) | 407 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) |
| 398 | target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) | 408 | target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt lz4_static) |
| 399 | if (ENABLE_WEB_SERVICE) | 409 | if (ENABLE_WEB_SERVICE) |
| 400 | target_link_libraries(core PUBLIC json-headers web_service) | 410 | target_link_libraries(core PUBLIC json-headers web_service) |
| 401 | endif() | 411 | 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 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/kernel/vm_manager.h" | ||
| 8 | #include "core/arm/skyeye_common/arm_regformat.h" | 9 | #include "core/arm/skyeye_common/arm_regformat.h" |
| 9 | #include "core/arm/skyeye_common/vfp/asm_vfp.h" | 10 | #include "core/arm/skyeye_common/vfp/asm_vfp.h" |
| 10 | 11 | ||
| @@ -14,14 +15,18 @@ public: | |||
| 14 | virtual ~ARM_Interface() {} | 15 | virtual ~ARM_Interface() {} |
| 15 | 16 | ||
| 16 | struct ThreadContext { | 17 | struct ThreadContext { |
| 17 | u32 cpu_registers[13]; | 18 | u64 cpu_registers[30]; |
| 18 | u32 sp; | 19 | u64 lr; |
| 19 | u32 lr; | 20 | u64 sp; |
| 20 | u32 pc; | 21 | u64 pc; |
| 21 | u32 cpsr; | 22 | u64 cpsr; |
| 22 | u32 fpu_registers[64]; | 23 | u128 fpu_registers[32]; |
| 23 | u32 fpscr; | 24 | u64 fpscr; |
| 24 | u32 fpexc; | 25 | u64 fpexc; |
| 26 | |||
| 27 | |||
| 28 | // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT | ||
| 29 | VAddr tls_address; | ||
| 25 | }; | 30 | }; |
| 26 | 31 | ||
| 27 | /** | 32 | /** |
| @@ -38,6 +43,8 @@ public: | |||
| 38 | Run(1); | 43 | Run(1); |
| 39 | } | 44 | } |
| 40 | 45 | ||
| 46 | virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) {} | ||
| 47 | |||
| 41 | /// Clear all instruction cache | 48 | /// Clear all instruction cache |
| 42 | virtual void ClearInstructionCache() = 0; | 49 | virtual void ClearInstructionCache() = 0; |
| 43 | 50 | ||
| @@ -48,27 +55,31 @@ public: | |||
| 48 | * Set the Program Counter to an address | 55 | * Set the Program Counter to an address |
| 49 | * @param addr Address to set PC to | 56 | * @param addr Address to set PC to |
| 50 | */ | 57 | */ |
| 51 | virtual void SetPC(u32 addr) = 0; | 58 | virtual void SetPC(u64 addr) = 0; |
| 52 | 59 | ||
| 53 | /* | 60 | /* |
| 54 | * Get the current Program Counter | 61 | * Get the current Program Counter |
| 55 | * @return Returns current PC | 62 | * @return Returns current PC |
| 56 | */ | 63 | */ |
| 57 | virtual u32 GetPC() const = 0; | 64 | virtual u64 GetPC() const = 0; |
| 58 | 65 | ||
| 59 | /** | 66 | /** |
| 60 | * Get an ARM register | 67 | * Get an ARM register |
| 61 | * @param index Register index (0-15) | 68 | * @param index Register index |
| 62 | * @return Returns the value in the register | 69 | * @return Returns the value in the register |
| 63 | */ | 70 | */ |
| 64 | virtual u32 GetReg(int index) const = 0; | 71 | virtual u64 GetReg(int index) const = 0; |
| 65 | 72 | ||
| 66 | /** | 73 | /** |
| 67 | * Set an ARM register | 74 | * Set an ARM register |
| 68 | * @param index Register index (0-15) | 75 | * @param index Register index |
| 69 | * @param value Value to set register to | 76 | * @param value Value to set register to |
| 70 | */ | 77 | */ |
| 71 | virtual void SetReg(int index, u32 value) = 0; | 78 | virtual void SetReg(int index, u64 value) = 0; |
| 79 | |||
| 80 | virtual const u128& GetExtReg(int index) const = 0; | ||
| 81 | |||
| 82 | virtual void SetExtReg(int index, u128& value) = 0; | ||
| 72 | 83 | ||
| 73 | /** | 84 | /** |
| 74 | * Gets the value of a VFP register | 85 | * Gets the value of a VFP register |
| @@ -124,6 +135,10 @@ public: | |||
| 124 | */ | 135 | */ |
| 125 | virtual void SetCP15Register(CP15Register reg, u32 value) = 0; | 136 | virtual void SetCP15Register(CP15Register reg, u32 value) = 0; |
| 126 | 137 | ||
| 138 | virtual VAddr GetTlsAddress() const = 0; | ||
| 139 | |||
| 140 | virtual void SetTlsAddress(VAddr address) = 0; | ||
| 141 | |||
| 127 | /** | 142 | /** |
| 128 | * Saves the current CPU context | 143 | * Saves the current CPU context |
| 129 | * @param ctx Thread context to save | 144 | * @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 @@ | |||
| 14 | #include "core/hle/svc.h" | 14 | #include "core/hle/svc.h" |
| 15 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 16 | 16 | ||
| 17 | static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void* user_arg) { | 17 | static void InterpreterFallback(u64 pc, Dynarmic::Jit* jit, void* user_arg) { |
| 18 | ARMul_State* state = static_cast<ARMul_State*>(user_arg); | 18 | UNIMPLEMENTED_MSG("InterpreterFallback for ARM64 JIT does not exist!"); |
| 19 | } | ||
| 19 | 20 | ||
| 20 | state->Reg = jit->Regs(); | 21 | static bool IsReadOnlyMemory(u64 vaddr) { |
| 21 | state->Cpsr = jit->Cpsr(); | 22 | // TODO(bunnei): ImplementMe |
| 22 | state->Reg[15] = pc; | 23 | return false; |
| 23 | state->ExtReg = jit->ExtRegs(); | 24 | } |
| 24 | state->VFP[VFP_FPSCR] = jit->Fpscr(); | ||
| 25 | state->NumInstrsToExecute = 1; | ||
| 26 | 25 | ||
| 27 | InterpreterMainLoop(state); | 26 | u8 MemoryRead8(const u64 addr) { |
| 27 | return Memory::Read8(static_cast<VAddr>(addr)); | ||
| 28 | } | ||
| 28 | 29 | ||
| 29 | bool is_thumb = (state->Cpsr & (1 << 5)) != 0; | 30 | u16 MemoryRead16(const u64 addr) { |
| 30 | state->Reg[15] &= (is_thumb ? 0xFFFFFFFE : 0xFFFFFFFC); | 31 | return Memory::Read16(static_cast<VAddr>(addr)); |
| 32 | } | ||
| 31 | 33 | ||
| 32 | jit->Regs() = state->Reg; | 34 | u32 MemoryRead32(const u64 addr) { |
| 33 | jit->Cpsr() = state->Cpsr; | 35 | return Memory::Read32(static_cast<VAddr>(addr)); |
| 34 | jit->ExtRegs() = state->ExtReg; | ||
| 35 | jit->SetFpscr(state->VFP[VFP_FPSCR]); | ||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | static bool IsReadOnlyMemory(u32 vaddr) { | 38 | u64 MemoryRead64(const u64 addr) { |
| 39 | // TODO(bunnei): ImplementMe | 39 | return Memory::Read64(static_cast<VAddr>(addr)); |
| 40 | return false; | 40 | } |
| 41 | |||
| 42 | void MemoryWrite8(const u64 addr, const u8 data) { | ||
| 43 | Memory::Write8(static_cast<VAddr>(addr), data); | ||
| 44 | } | ||
| 45 | |||
| 46 | void MemoryWrite16(const u64 addr, const u16 data) { | ||
| 47 | Memory::Write16(static_cast<VAddr>(addr), data); | ||
| 48 | } | ||
| 49 | |||
| 50 | void MemoryWrite32(const u64 addr, const u32 data) { | ||
| 51 | Memory::Write32(static_cast<VAddr>(addr), data); | ||
| 52 | } | ||
| 53 | |||
| 54 | void MemoryWrite64(const u64 addr, const u64 data) { | ||
| 55 | Memory::Write64(static_cast<VAddr>(addr), data); | ||
| 41 | } | 56 | } |
| 42 | 57 | ||
| 43 | static Dynarmic::UserCallbacks GetUserCallbacks( | 58 | static Dynarmic::UserCallbacks GetUserCallbacks(ARM_Dynarmic* this_) { |
| 44 | const std::shared_ptr<ARMul_State>& interpeter_state, Memory::PageTable* current_page_table) { | ||
| 45 | Dynarmic::UserCallbacks user_callbacks{}; | 59 | Dynarmic::UserCallbacks user_callbacks{}; |
| 46 | user_callbacks.InterpreterFallback = &InterpreterFallback; | 60 | user_callbacks.InterpreterFallback = &InterpreterFallback; |
| 47 | user_callbacks.user_arg = static_cast<void*>(interpeter_state.get()); | 61 | user_callbacks.user_arg = static_cast<void*>(this_); |
| 48 | user_callbacks.CallSVC = &SVC::CallSVC; | 62 | user_callbacks.CallSVC = &SVC::CallSVC; |
| 49 | user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory; | 63 | user_callbacks.memory.IsReadOnlyMemory = &IsReadOnlyMemory; |
| 50 | user_callbacks.memory.ReadCode = &Memory::Read32; | 64 | user_callbacks.memory.ReadCode = &MemoryRead32; |
| 51 | user_callbacks.memory.Read8 = &Memory::Read8; | 65 | user_callbacks.memory.Read8 = &MemoryRead8; |
| 52 | user_callbacks.memory.Read16 = &Memory::Read16; | 66 | user_callbacks.memory.Read16 = &MemoryRead16; |
| 53 | user_callbacks.memory.Read32 = &Memory::Read32; | 67 | user_callbacks.memory.Read32 = &MemoryRead32; |
| 54 | user_callbacks.memory.Read64 = &Memory::Read64; | 68 | user_callbacks.memory.Read64 = &MemoryRead64; |
| 55 | user_callbacks.memory.Write8 = &Memory::Write8; | 69 | user_callbacks.memory.Write8 = &MemoryWrite8; |
| 56 | user_callbacks.memory.Write16 = &Memory::Write16; | 70 | user_callbacks.memory.Write16 = &MemoryWrite16; |
| 57 | user_callbacks.memory.Write32 = &Memory::Write32; | 71 | user_callbacks.memory.Write32 = &MemoryWrite32; |
| 58 | user_callbacks.memory.Write64 = &Memory::Write64; | 72 | user_callbacks.memory.Write64 = &MemoryWrite64; |
| 59 | user_callbacks.page_table = ¤t_page_table->pointers; | 73 | //user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); |
| 60 | user_callbacks.coprocessors[15] = std::make_shared<DynarmicCP15>(interpeter_state); | ||
| 61 | return user_callbacks; | 74 | return user_callbacks; |
| 62 | } | 75 | } |
| 63 | 76 | ||
| 64 | ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { | 77 | ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { |
| 65 | interpreter_state = std::make_shared<ARMul_State>(initial_mode); | ||
| 66 | PageTableChanged(); | ||
| 67 | } | 78 | } |
| 68 | 79 | ||
| 69 | void ARM_Dynarmic::SetPC(u32 pc) { | 80 | void ARM_Dynarmic::MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) { |
| 70 | jit->Regs()[15] = pc; | 81 | } |
| 82 | |||
| 83 | void ARM_Dynarmic::SetPC(u64 pc) { | ||
| 84 | jit->Regs64()[32] = pc; | ||
| 85 | } | ||
| 86 | |||
| 87 | u64 ARM_Dynarmic::GetPC() const { | ||
| 88 | return jit->Regs64()[32]; | ||
| 89 | } | ||
| 90 | |||
| 91 | u64 ARM_Dynarmic::GetReg(int index) const { | ||
| 92 | return jit->Regs64()[index]; | ||
| 71 | } | 93 | } |
| 72 | 94 | ||
| 73 | u32 ARM_Dynarmic::GetPC() const { | 95 | void ARM_Dynarmic::SetReg(int index, u64 value) { |
| 74 | return jit->Regs()[15]; | 96 | jit->Regs64()[index] = value; |
| 75 | } | 97 | } |
| 76 | 98 | ||
| 77 | u32 ARM_Dynarmic::GetReg(int index) const { | 99 | const u128& ARM_Dynarmic::GetExtReg(int index) const { |
| 78 | return jit->Regs()[index]; | 100 | return jit->ExtRegs64()[index]; |
| 79 | } | 101 | } |
| 80 | 102 | ||
| 81 | void ARM_Dynarmic::SetReg(int index, u32 value) { | 103 | void ARM_Dynarmic::SetExtReg(int index, u128& value) { |
| 82 | jit->Regs()[index] = value; | 104 | jit->ExtRegs64()[index] = value; |
| 83 | } | 105 | } |
| 84 | 106 | ||
| 85 | u32 ARM_Dynarmic::GetVFPReg(int index) const { | 107 | u32 ARM_Dynarmic::GetVFPReg(int index) const { |
| 86 | return jit->ExtRegs()[index]; | 108 | return {}; |
| 87 | } | 109 | } |
| 88 | 110 | ||
| 89 | void ARM_Dynarmic::SetVFPReg(int index, u32 value) { | 111 | void ARM_Dynarmic::SetVFPReg(int index, u32 value) { |
| 90 | jit->ExtRegs()[index] = value; | ||
| 91 | } | 112 | } |
| 92 | 113 | ||
| 93 | u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const { | 114 | u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const { |
| 94 | if (reg == VFP_FPSCR) { | 115 | return {}; |
| 95 | return jit->Fpscr(); | ||
| 96 | } | ||
| 97 | |||
| 98 | // Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state | ||
| 99 | return interpreter_state->VFP[reg]; | ||
| 100 | } | 116 | } |
| 101 | 117 | ||
| 102 | void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) { | 118 | void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) { |
| 103 | if (reg == VFP_FPSCR) { | ||
| 104 | jit->SetFpscr(value); | ||
| 105 | } | ||
| 106 | |||
| 107 | // Dynarmic does not implement and/or expose other VFP registers, fallback to interpreter state | ||
| 108 | interpreter_state->VFP[reg] = value; | ||
| 109 | } | 119 | } |
| 110 | 120 | ||
| 111 | u32 ARM_Dynarmic::GetCPSR() const { | 121 | u32 ARM_Dynarmic::GetCPSR() const { |
| @@ -117,11 +127,18 @@ void ARM_Dynarmic::SetCPSR(u32 cpsr) { | |||
| 117 | } | 127 | } |
| 118 | 128 | ||
| 119 | u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) { | 129 | u32 ARM_Dynarmic::GetCP15Register(CP15Register reg) { |
| 120 | return interpreter_state->CP15[reg]; | 130 | return {}; |
| 121 | } | 131 | } |
| 122 | 132 | ||
| 123 | void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { | 133 | void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { |
| 124 | interpreter_state->CP15[reg] = value; | 134 | } |
| 135 | |||
| 136 | VAddr ARM_Dynarmic::GetTlsAddress() const { | ||
| 137 | return jit->TlsAddr(); | ||
| 138 | } | ||
| 139 | |||
| 140 | void ARM_Dynarmic::SetTlsAddress(VAddr address) { | ||
| 141 | jit->TlsAddr() = address; | ||
| 125 | } | 142 | } |
| 126 | 143 | ||
| 127 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); | 144 | MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); |
| @@ -136,29 +153,29 @@ void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { | |||
| 136 | } | 153 | } |
| 137 | 154 | ||
| 138 | void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { | 155 | void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { |
| 139 | memcpy(ctx.cpu_registers, jit->Regs().data(), sizeof(ctx.cpu_registers)); | 156 | memcpy(ctx.cpu_registers, jit->Regs64().data(), sizeof(ctx.cpu_registers)); |
| 140 | memcpy(ctx.fpu_registers, jit->ExtRegs().data(), sizeof(ctx.fpu_registers)); | 157 | memcpy(ctx.fpu_registers, jit->ExtRegs64().data(), sizeof(ctx.fpu_registers)); |
| 141 | 158 | ||
| 142 | ctx.sp = jit->Regs()[13]; | 159 | ctx.lr = jit->Regs64()[30]; |
| 143 | ctx.lr = jit->Regs()[14]; | 160 | ctx.sp = jit->Regs64()[31]; |
| 144 | ctx.pc = jit->Regs()[15]; | 161 | ctx.pc = jit->Regs64()[32]; |
| 145 | ctx.cpsr = jit->Cpsr(); | 162 | ctx.cpsr = jit->Cpsr(); |
| 146 | 163 | ||
| 147 | ctx.fpscr = jit->Fpscr(); | 164 | // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT |
| 148 | ctx.fpexc = interpreter_state->VFP[VFP_FPEXC]; | 165 | ctx.tls_address = jit->TlsAddr(); |
| 149 | } | 166 | } |
| 150 | 167 | ||
| 151 | void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { | 168 | void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { |
| 152 | memcpy(jit->Regs().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); | 169 | memcpy(jit->Regs64().data(), ctx.cpu_registers, sizeof(ctx.cpu_registers)); |
| 153 | memcpy(jit->ExtRegs().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); | 170 | memcpy(jit->ExtRegs64().data(), ctx.fpu_registers, sizeof(ctx.fpu_registers)); |
| 154 | 171 | ||
| 155 | jit->Regs()[13] = ctx.sp; | 172 | jit->Regs64()[30] = ctx.lr; |
| 156 | jit->Regs()[14] = ctx.lr; | 173 | jit->Regs64()[31] = ctx.sp; |
| 157 | jit->Regs()[15] = ctx.pc; | 174 | jit->Regs64()[32] = ctx.pc; |
| 158 | jit->Cpsr() = ctx.cpsr; | 175 | jit->Cpsr() = ctx.cpsr; |
| 159 | 176 | ||
| 160 | jit->SetFpscr(ctx.fpscr); | 177 | // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT |
| 161 | interpreter_state->VFP[VFP_FPEXC] = ctx.fpexc; | 178 | jit->TlsAddr() = ctx.tls_address; |
| 162 | } | 179 | } |
| 163 | 180 | ||
| 164 | void ARM_Dynarmic::PrepareReschedule() { | 181 | void ARM_Dynarmic::PrepareReschedule() { |
| @@ -180,6 +197,6 @@ void ARM_Dynarmic::PageTableChanged() { | |||
| 180 | return; | 197 | return; |
| 181 | } | 198 | } |
| 182 | 199 | ||
| 183 | jit = new Dynarmic::Jit(GetUserCallbacks(interpreter_state, current_page_table)); | 200 | jit = new Dynarmic::Jit(GetUserCallbacks(this), Dynarmic::Arch::ARM64); |
| 184 | jits.emplace(current_page_table, std::unique_ptr<Dynarmic::Jit>(jit)); | 201 | jits.emplace(current_page_table, std::unique_ptr<Dynarmic::Jit>(jit)); |
| 185 | } | 202 | } |
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 { | |||
| 19 | public: | 19 | public: |
| 20 | ARM_Dynarmic(PrivilegeMode initial_mode); | 20 | ARM_Dynarmic(PrivilegeMode initial_mode); |
| 21 | 21 | ||
| 22 | void SetPC(u32 pc) override; | 22 | void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) override; |
| 23 | u32 GetPC() const override; | 23 | |
| 24 | u32 GetReg(int index) const override; | 24 | void SetPC(u64 pc) override; |
| 25 | void SetReg(int index, u32 value) override; | 25 | u64 GetPC() const override; |
| 26 | u64 GetReg(int index) const override; | ||
| 27 | void SetReg(int index, u64 value) override; | ||
| 28 | const u128& GetExtReg(int index) const override; | ||
| 29 | void SetExtReg(int index, u128& value) override; | ||
| 26 | u32 GetVFPReg(int index) const override; | 30 | u32 GetVFPReg(int index) const override; |
| 27 | void SetVFPReg(int index, u32 value) override; | 31 | void SetVFPReg(int index, u32 value) override; |
| 28 | u32 GetVFPSystemReg(VFPSystemRegister reg) const override; | 32 | u32 GetVFPSystemReg(VFPSystemRegister reg) const override; |
| @@ -31,6 +35,8 @@ public: | |||
| 31 | void SetCPSR(u32 cpsr) override; | 35 | void SetCPSR(u32 cpsr) override; |
| 32 | u32 GetCP15Register(CP15Register reg) override; | 36 | u32 GetCP15Register(CP15Register reg) override; |
| 33 | void SetCP15Register(CP15Register reg, u32 value) override; | 37 | void SetCP15Register(CP15Register reg, u32 value) override; |
| 38 | VAddr GetTlsAddress() const override; | ||
| 39 | void SetTlsAddress(VAddr address) override; | ||
| 34 | 40 | ||
| 35 | void SaveContext(ThreadContext& ctx) override; | 41 | void SaveContext(ThreadContext& ctx) override; |
| 36 | void LoadContext(const ThreadContext& ctx) override; | 42 | void LoadContext(const ThreadContext& ctx) override; |
| @@ -45,5 +51,4 @@ private: | |||
| 45 | Dynarmic::Jit* jit = nullptr; | 51 | Dynarmic::Jit* jit = nullptr; |
| 46 | Memory::PageTable* current_page_table = nullptr; | 52 | Memory::PageTable* current_page_table = nullptr; |
| 47 | std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::Jit>> jits; | 53 | std::map<Memory::PageTable*, std::unique_ptr<Dynarmic::Jit>> jits; |
| 48 | std::shared_ptr<ARMul_State> interpreter_state; | ||
| 49 | }; | 54 | }; |
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() { | |||
| 25 | trans_cache_buf_top = 0; | 25 | trans_cache_buf_top = 0; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | void ARM_DynCom::PageTableChanged() { | 28 | void ARM_DynCom::SetPC(u64 pc) { |
| 29 | ClearInstructionCache(); | 29 | state->Reg[15] = pc; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void ARM_DynCom::SetPC(u32 pc) { | 32 | void ARM_DynCom::PageTableChanged() { |
| 33 | state->Reg[15] = pc; | 33 | ClearInstructionCache(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | u32 ARM_DynCom::GetPC() const { | 36 | u64 ARM_DynCom::GetPC() const { |
| 37 | return state->Reg[15]; | 37 | return state->Reg[15]; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | u32 ARM_DynCom::GetReg(int index) const { | 40 | u64 ARM_DynCom::GetReg(int index) const { |
| 41 | return state->Reg[index]; | 41 | return state->Reg[index]; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | void ARM_DynCom::SetReg(int index, u32 value) { | 44 | void ARM_DynCom::SetReg(int index, u64 value) { |
| 45 | state->Reg[index] = value; | 45 | state->Reg[index] = value; |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | const u128& ARM_DynCom::GetExtReg(int index) const { | ||
| 49 | return {}; | ||
| 50 | } | ||
| 51 | |||
| 52 | void ARM_DynCom::SetExtReg(int index, u128& value) { | ||
| 53 | } | ||
| 54 | |||
| 48 | u32 ARM_DynCom::GetVFPReg(int index) const { | 55 | u32 ARM_DynCom::GetVFPReg(int index) const { |
| 49 | return state->ExtReg[index]; | 56 | return state->ExtReg[index]; |
| 50 | } | 57 | } |
| @@ -77,6 +84,13 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) { | |||
| 77 | state->CP15[reg] = value; | 84 | state->CP15[reg] = value; |
| 78 | } | 85 | } |
| 79 | 86 | ||
| 87 | VAddr ARM_DynCom::GetTlsAddress() const { | ||
| 88 | return {}; | ||
| 89 | } | ||
| 90 | |||
| 91 | void ARM_DynCom::SetTlsAddress(VAddr /*address*/) { | ||
| 92 | } | ||
| 93 | |||
| 80 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { | 94 | void ARM_DynCom::ExecuteInstructions(int num_instructions) { |
| 81 | state->NumInstrsToExecute = num_instructions; | 95 | state->NumInstrsToExecute = num_instructions; |
| 82 | 96 | ||
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: | |||
| 18 | void ClearInstructionCache() override; | 18 | void ClearInstructionCache() override; |
| 19 | void PageTableChanged() override; | 19 | void PageTableChanged() override; |
| 20 | 20 | ||
| 21 | void SetPC(u32 pc) override; | 21 | void SetPC(u64 pc) override; |
| 22 | u32 GetPC() const override; | 22 | u64 GetPC() const override; |
| 23 | u32 GetReg(int index) const override; | 23 | u64 GetReg(int index) const override; |
| 24 | void SetReg(int index, u32 value) override; | 24 | void SetReg(int index, u64 value) override; |
| 25 | const u128& GetExtReg(int index) const override; | ||
| 26 | void SetExtReg(int index, u128& value) override; | ||
| 25 | u32 GetVFPReg(int index) const override; | 27 | u32 GetVFPReg(int index) const override; |
| 26 | void SetVFPReg(int index, u32 value) override; | 28 | void SetVFPReg(int index, u32 value) override; |
| 27 | u32 GetVFPSystemReg(VFPSystemRegister reg) const override; | 29 | u32 GetVFPSystemReg(VFPSystemRegister reg) const override; |
| @@ -30,6 +32,8 @@ public: | |||
| 30 | void SetCPSR(u32 cpsr) override; | 32 | void SetCPSR(u32 cpsr) override; |
| 31 | u32 GetCP15Register(CP15Register reg) override; | 33 | u32 GetCP15Register(CP15Register reg) override; |
| 32 | void SetCP15Register(CP15Register reg, u32 value) override; | 34 | void SetCP15Register(CP15Register reg, u32 value) override; |
| 35 | VAddr GetTlsAddress() const override; | ||
| 36 | void SetTlsAddress(VAddr address) override; | ||
| 33 | 37 | ||
| 34 | void SaveContext(ThreadContext& ctx) override; | 38 | void SaveContext(ThreadContext& ctx) override; |
| 35 | void LoadContext(const ThreadContext& ctx) override; | 39 | 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 @@ | |||
| 9 | #include "core/arm/arm_interface.h" | 9 | #include "core/arm/arm_interface.h" |
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | 10 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 11 | #include "core/arm/dyncom/arm_dyncom.h" | 11 | #include "core/arm/dyncom/arm_dyncom.h" |
| 12 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 12 | #include "core/core.h" | 13 | #include "core/core.h" |
| 13 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 14 | #include "core/gdbstub/gdbstub.h" | 15 | #include "core/gdbstub/gdbstub.h" |
| @@ -115,7 +116,6 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file | |||
| 115 | return ResultStatus::ErrorLoader; | 116 | return ResultStatus::ErrorLoader; |
| 116 | } | 117 | } |
| 117 | } | 118 | } |
| 118 | Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); | ||
| 119 | status = ResultStatus::Success; | 119 | status = ResultStatus::Success; |
| 120 | return status; | 120 | return status; |
| 121 | } | 121 | } |
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( | |||
| 15 | : sd_savedata_source(sd_savedata) {} | 15 | : sd_savedata_source(sd_savedata) {} |
| 16 | 16 | ||
| 17 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { | 17 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) { |
| 18 | return sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id); | 18 | UNIMPLEMENTED(); |
| 19 | return {}; //sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id); | ||
| 19 | } | 20 | } |
| 20 | 21 | ||
| 21 | ResultCode ArchiveFactory_SaveData::Format(const Path& path, | 22 | ResultCode ArchiveFactory_SaveData::Format(const Path& path, |
| 22 | const FileSys::ArchiveFormatInfo& format_info) { | 23 | const FileSys::ArchiveFormatInfo& format_info) { |
| 23 | return sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info); | 24 | UNIMPLEMENTED(); |
| 25 | return RESULT_SUCCESS; //sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info); | ||
| 24 | } | 26 | } |
| 25 | 27 | ||
| 26 | ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const { | 28 | ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const { |
| 27 | return sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id); | 29 | UNIMPLEMENTED(); |
| 30 | return {}; //sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id); | ||
| 28 | } | 31 | } |
| 29 | 32 | ||
| 30 | } // namespace FileSys | 33 | } // 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) { | |||
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { | 280 | ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) { |
| 281 | auto archive = std::make_unique<SelfNCCHArchive>( | 281 | //auto archive = std::make_unique<SelfNCCHArchive>( |
| 282 | ncch_data[Kernel::g_current_process->codeset->program_id]); | 282 | // ncch_data[Kernel::g_current_process->codeset->program_id]); |
| 283 | return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | 283 | //return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); |
| 284 | return {}; | ||
| 284 | } | 285 | } |
| 285 | 286 | ||
| 286 | ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) { | 287 | 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(); | |||
| 69 | * @param addr Address to search from. | 69 | * @param addr Address to search from. |
| 70 | * @param type Type of breakpoint. | 70 | * @param type Type of breakpoint. |
| 71 | */ | 71 | */ |
| 72 | BreakpointAddress GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType type); | 72 | BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); |
| 73 | 73 | ||
| 74 | /** | 74 | /** |
| 75 | * Check if a breakpoint of the specified type exists at the given address. | 75 | * Check if a breakpoint of the specified type exists at the given address. |
| @@ -77,7 +77,7 @@ BreakpointAddress GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType | |||
| 77 | * @param addr Address of breakpoint. | 77 | * @param addr Address of breakpoint. |
| 78 | * @param type Type of breakpoint. | 78 | * @param type Type of breakpoint. |
| 79 | */ | 79 | */ |
| 80 | bool CheckBreakpoint(u32 addr, GDBStub::BreakpointType type); | 80 | bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); |
| 81 | 81 | ||
| 82 | // If set to true, the CPU will halt at the beginning of the next CPU loop. | 82 | // If set to true, the CPU will halt at the beginning of the next CPU loop. |
| 83 | bool GetCpuHaltFlag(); | 83 | 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 { | |||
| 20 | * HLE a function return from the current ARM11 userland process | 20 | * HLE a function return from the current ARM11 userland process |
| 21 | * @param res Result to return | 21 | * @param res Result to return |
| 22 | */ | 22 | */ |
| 23 | static inline void FuncReturn(u32 res) { | 23 | static inline void FuncReturn(u64 res) { |
| 24 | Core::CPU().SetReg(0, res); | 24 | Core::CPU().SetReg(0, res); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | /** | ||
| 28 | * HLE a function return (64-bit) from the current ARM11 userland process | ||
| 29 | * @param res Result to return (64-bit) | ||
| 30 | * @todo Verify that this function is correct | ||
| 31 | */ | ||
| 32 | static inline void FuncReturn64(u64 res) { | ||
| 33 | Core::CPU().SetReg(0, (u32)(res & 0xFFFFFFFF)); | ||
| 34 | Core::CPU().SetReg(1, (u32)((res >> 32) & 0xFFFFFFFF)); | ||
| 35 | } | ||
| 36 | |||
| 37 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 27 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 38 | // Function wrappers that return type ResultCode | 28 | // Function wrappers that return type ResultCode |
| 39 | 29 | ||
| 30 | template <ResultCode func(u64)> | ||
| 31 | void Wrap() { | ||
| 32 | FuncReturn(func(PARAM(0)).raw); | ||
| 33 | } | ||
| 34 | |||
| 35 | template <ResultCode func(u32, u64, u32)> | ||
| 36 | void Wrap() { | ||
| 37 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 38 | } | ||
| 39 | |||
| 40 | template <ResultCode func(u64, u32)> | ||
| 41 | void Wrap() { | ||
| 42 | FuncReturn(func(PARAM(0), PARAM(1)).raw); | ||
| 43 | } | ||
| 44 | |||
| 45 | template <ResultCode func(u64, u64, u64)> | ||
| 46 | void Wrap() { | ||
| 47 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 48 | } | ||
| 49 | |||
| 50 | template <ResultCode func(u64*, u64, u64, u64)> | ||
| 51 | void Wrap() { | ||
| 52 | u64 param_1 = 0; | ||
| 53 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; | ||
| 54 | Core::CPU().SetReg(1, param_1); | ||
| 55 | FuncReturn(retval); | ||
| 56 | } | ||
| 57 | |||
| 40 | template <ResultCode func(u32, u32, u32, u32)> | 58 | template <ResultCode func(u32, u32, u32, u32)> |
| 41 | void Wrap() { | 59 | void Wrap() { |
| 42 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw); | 60 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw); |
| @@ -84,6 +102,14 @@ void Wrap() { | |||
| 84 | func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw); | 102 | func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw); |
| 85 | } | 103 | } |
| 86 | 104 | ||
| 105 | template <ResultCode func(u32, u64*)> | ||
| 106 | void Wrap() { | ||
| 107 | u64 param_1 = 0; | ||
| 108 | u32 retval = func(PARAM(0), ¶m_1).raw; | ||
| 109 | Core::CPU().SetReg(1, param_1); | ||
| 110 | FuncReturn(retval); | ||
| 111 | } | ||
| 112 | |||
| 87 | template <ResultCode func(u32*)> | 113 | template <ResultCode func(u32*)> |
| 88 | void Wrap() { | 114 | void Wrap() { |
| 89 | u32 param_1 = 0; | 115 | u32 param_1 = 0; |
| @@ -99,16 +125,17 @@ void Wrap() { | |||
| 99 | FuncReturn(retval); | 125 | FuncReturn(retval); |
| 100 | } | 126 | } |
| 101 | 127 | ||
| 102 | template <ResultCode func(MemoryInfo*, PageInfo*, u32)> | 128 | template <ResultCode func(MemoryInfo*, PageInfo*, u64)> |
| 103 | void Wrap() { | 129 | void Wrap() { |
| 104 | MemoryInfo memory_info = {}; | 130 | MemoryInfo memory_info = {}; |
| 105 | PageInfo page_info = {}; | 131 | PageInfo page_info = {}; |
| 106 | u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; | 132 | u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; |
| 107 | Core::CPU().SetReg(1, memory_info.base_address); | 133 | |
| 108 | Core::CPU().SetReg(2, memory_info.size); | 134 | Memory::Write64(PARAM(0), memory_info.base_address); |
| 109 | Core::CPU().SetReg(3, memory_info.permission); | 135 | Memory::Write64(PARAM(0) + 8, memory_info.size); |
| 110 | Core::CPU().SetReg(4, memory_info.state); | 136 | Memory::Write64(PARAM(0) + 16, memory_info.permission); |
| 111 | Core::CPU().SetReg(5, page_info.flags); | 137 | Memory::Write64(PARAM(0) + 24, memory_info.state); |
| 138 | |||
| 112 | FuncReturn(retval); | 139 | FuncReturn(retval); |
| 113 | } | 140 | } |
| 114 | 141 | ||
| @@ -138,7 +165,7 @@ void Wrap() { | |||
| 138 | FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); | 165 | FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); |
| 139 | } | 166 | } |
| 140 | 167 | ||
| 141 | template <ResultCode func(u32*, u32)> | 168 | template <ResultCode func(u32*, u64)> |
| 142 | void Wrap() { | 169 | void Wrap() { |
| 143 | u32 param_1 = 0; | 170 | u32 param_1 = 0; |
| 144 | u32 retval = func(¶m_1, PARAM(1)).raw; | 171 | u32 retval = func(¶m_1, PARAM(1)).raw; |
| @@ -226,6 +253,11 @@ void Wrap() { | |||
| 226 | FuncReturn(retval); | 253 | FuncReturn(retval); |
| 227 | } | 254 | } |
| 228 | 255 | ||
| 256 | template <ResultCode func(u32, u32, u32)> | ||
| 257 | void Wrap() { | ||
| 258 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | ||
| 259 | } | ||
| 260 | |||
| 229 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 261 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 230 | // Function wrappers that return type u32 | 262 | // Function wrappers that return type u32 |
| 231 | 263 | ||
| @@ -255,9 +287,9 @@ void Wrap() { | |||
| 255 | func(PARAM(0), PARAM(1)); | 287 | func(PARAM(0), PARAM(1)); |
| 256 | } | 288 | } |
| 257 | 289 | ||
| 258 | template <void func(u8)> | 290 | template <void func(u64, u64, u64)> |
| 259 | void Wrap() { | 291 | void Wrap() { |
| 260 | func((u8)PARAM(0)); | 292 | func(PARAM(0), PARAM(1), PARAM(2)); |
| 261 | } | 293 | } |
| 262 | 294 | ||
| 263 | #undef PARAM | 295 | #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) { | |||
| 25 | Mutex::Mutex() {} | 25 | Mutex::Mutex() {} |
| 26 | Mutex::~Mutex() {} | 26 | Mutex::~Mutex() {} |
| 27 | 27 | ||
| 28 | SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { | 28 | SharedPtr<Mutex> Mutex::Create(bool initial_locked, VAddr addr, std::string name) { |
| 29 | SharedPtr<Mutex> mutex(new Mutex); | 29 | SharedPtr<Mutex> mutex(new Mutex); |
| 30 | 30 | ||
| 31 | mutex->lock_count = 0; | 31 | mutex->lock_count = 0; |
| 32 | mutex->addr = addr; | ||
| 32 | mutex->name = std::move(name); | 33 | mutex->name = std::move(name); |
| 33 | mutex->holding_thread = nullptr; | 34 | mutex->holding_thread = nullptr; |
| 34 | 35 | ||
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: | |||
| 21 | * @param name Optional name of mutex | 21 | * @param name Optional name of mutex |
| 22 | * @return Pointer to new Mutex object | 22 | * @return Pointer to new Mutex object |
| 23 | */ | 23 | */ |
| 24 | static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); | 24 | static SharedPtr<Mutex> Create(bool initial_locked, VAddr addr, std::string name = "Unknown"); |
| 25 | 25 | ||
| 26 | std::string GetTypeName() const override { | 26 | std::string GetTypeName() const override { |
| 27 | return "Mutex"; | 27 | return "Mutex"; |
| @@ -39,6 +39,7 @@ public: | |||
| 39 | u32 priority; ///< The priority of the mutex, used for priority inheritance. | 39 | u32 priority; ///< The priority of the mutex, used for priority inheritance. |
| 40 | std::string name; ///< Name of mutex (optional) | 40 | std::string name; ///< Name of mutex (optional) |
| 41 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex | 41 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex |
| 42 | VAddr addr; | ||
| 42 | 43 | ||
| 43 | /** | 44 | /** |
| 44 | * Elevate the mutex priority to the best priority | 45 | * 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() {} | |||
| 30 | 30 | ||
| 31 | u32 Process::next_process_id; | 31 | u32 Process::next_process_id; |
| 32 | 32 | ||
| 33 | SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { | 33 | SharedPtr<Process> Process::Create(std::string&& name) { |
| 34 | SharedPtr<Process> process(new Process); | 34 | SharedPtr<Process> process(new Process); |
| 35 | 35 | ||
| 36 | process->codeset = std::move(code_set); | 36 | process->name = std::move(name); |
| 37 | process->flags.raw = 0; | 37 | process->flags.raw = 0; |
| 38 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | 38 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); |
| 39 | 39 | ||
| @@ -112,25 +112,7 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | |||
| 112 | } | 112 | } |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | void Process::Run(s32 main_thread_priority, u32 stack_size) { | 115 | void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { |
| 116 | memory_region = GetMemoryRegion(flags.memory_region); | ||
| 117 | |||
| 118 | auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, | ||
| 119 | MemoryState memory_state) { | ||
| 120 | auto vma = vm_manager | ||
| 121 | .MapMemoryBlock(segment.addr, codeset->memory, segment.offset, segment.size, | ||
| 122 | memory_state) | ||
| 123 | .Unwrap(); | ||
| 124 | vm_manager.Reprotect(vma, permissions); | ||
| 125 | misc_memory_used += segment.size; | ||
| 126 | memory_region->used += segment.size; | ||
| 127 | }; | ||
| 128 | |||
| 129 | // Map CodeSet segments | ||
| 130 | MapSegment(codeset->code, VMAPermission::ReadExecute, MemoryState::Code); | ||
| 131 | MapSegment(codeset->rodata, VMAPermission::Read, MemoryState::Code); | ||
| 132 | MapSegment(codeset->data, VMAPermission::ReadWrite, MemoryState::Private); | ||
| 133 | |||
| 134 | // Allocate and map stack | 116 | // Allocate and map stack |
| 135 | vm_manager | 117 | vm_manager |
| 136 | .MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size, | 118 | .MapMemoryBlock(Memory::HEAP_VADDR_END - stack_size, |
| @@ -147,7 +129,28 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | |||
| 147 | } | 129 | } |
| 148 | 130 | ||
| 149 | vm_manager.LogLayout(Log::Level::Debug); | 131 | vm_manager.LogLayout(Log::Level::Debug); |
| 150 | Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this); | 132 | |
| 133 | Kernel::SetupMainThread(entry_point, main_thread_priority, this); | ||
| 134 | } | ||
| 135 | |||
| 136 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | ||
| 137 | memory_region = GetMemoryRegion(flags.memory_region); | ||
| 138 | |||
| 139 | auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, | ||
| 140 | MemoryState memory_state) { | ||
| 141 | auto vma = vm_manager | ||
| 142 | .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, segment.size, | ||
| 143 | memory_state) | ||
| 144 | .Unwrap(); | ||
| 145 | vm_manager.Reprotect(vma, permissions); | ||
| 146 | misc_memory_used += segment.size; | ||
| 147 | memory_region->used += segment.size; | ||
| 148 | }; | ||
| 149 | |||
| 150 | // Map CodeSet segments | ||
| 151 | MapSegment(module_->code, VMAPermission::ReadWrite, MemoryState::Private); | ||
| 152 | MapSegment(module_->rodata, VMAPermission::Read, MemoryState::Static); | ||
| 153 | MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::Static); | ||
| 151 | } | 154 | } |
| 152 | 155 | ||
| 153 | VAddr Process::GetLinearHeapAreaAddress() const { | 156 | 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 { | |||
| 79 | u32 size = 0; | 79 | u32 size = 0; |
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | Segment code, rodata, data; | 82 | Segment segments[3]; |
| 83 | Segment& code = segments[0]; | ||
| 84 | Segment& rodata = segments[1]; | ||
| 85 | Segment& data = segments[2]; | ||
| 86 | |||
| 83 | VAddr entrypoint; | 87 | VAddr entrypoint; |
| 84 | 88 | ||
| 85 | private: | 89 | private: |
| @@ -89,13 +93,13 @@ private: | |||
| 89 | 93 | ||
| 90 | class Process final : public Object { | 94 | class Process final : public Object { |
| 91 | public: | 95 | public: |
| 92 | static SharedPtr<Process> Create(SharedPtr<CodeSet> code_set); | 96 | static SharedPtr<Process> Create(std::string&& name); |
| 93 | 97 | ||
| 94 | std::string GetTypeName() const override { | 98 | std::string GetTypeName() const override { |
| 95 | return "Process"; | 99 | return "Process"; |
| 96 | } | 100 | } |
| 97 | std::string GetName() const override { | 101 | std::string GetName() const override { |
| 98 | return codeset->name; | 102 | return name; |
| 99 | } | 103 | } |
| 100 | 104 | ||
| 101 | static const HandleType HANDLE_TYPE = HandleType::Process; | 105 | static const HandleType HANDLE_TYPE = HandleType::Process; |
| @@ -105,7 +109,6 @@ public: | |||
| 105 | 109 | ||
| 106 | static u32 next_process_id; | 110 | static u32 next_process_id; |
| 107 | 111 | ||
| 108 | SharedPtr<CodeSet> codeset; | ||
| 109 | /// Resource limit descriptor for this process | 112 | /// Resource limit descriptor for this process |
| 110 | SharedPtr<ResourceLimit> resource_limit; | 113 | SharedPtr<ResourceLimit> resource_limit; |
| 111 | 114 | ||
| @@ -134,7 +137,9 @@ public: | |||
| 134 | /** | 137 | /** |
| 135 | * Applies address space changes and launches the process main thread. | 138 | * Applies address space changes and launches the process main thread. |
| 136 | */ | 139 | */ |
| 137 | void Run(s32 main_thread_priority, u32 stack_size); | 140 | void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); |
| 141 | |||
| 142 | void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); | ||
| 138 | 143 | ||
| 139 | /////////////////////////////////////////////////////////////////////////////////////////////// | 144 | /////////////////////////////////////////////////////////////////////////////////////////////// |
| 140 | // Memory Management | 145 | // Memory Management |
| @@ -160,6 +165,8 @@ public: | |||
| 160 | /// This vector will grow as more pages are allocated for new threads. | 165 | /// This vector will grow as more pages are allocated for new threads. |
| 161 | std::vector<std::bitset<8>> tls_slots; | 166 | std::vector<std::bitset<8>> tls_slots; |
| 162 | 167 | ||
| 168 | std::string name; | ||
| 169 | |||
| 163 | VAddr GetLinearHeapAreaAddress() const; | 170 | VAddr GetLinearHeapAreaAddress() const; |
| 164 | VAddr GetLinearHeapBase() const; | 171 | VAddr GetLinearHeapBase() const; |
| 165 | VAddr GetLinearHeapLimit() const; | 172 | 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 { | |||
| 13 | Semaphore::Semaphore() {} | 13 | Semaphore::Semaphore() {} |
| 14 | Semaphore::~Semaphore() {} | 14 | Semaphore::~Semaphore() {} |
| 15 | 15 | ||
| 16 | ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, | 16 | ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, VAddr address, |
| 17 | std::string name) { | 17 | std::string name) { |
| 18 | 18 | ||
| 19 | if (initial_count > max_count) | 19 | if (initial_count > max_count) |
| @@ -25,6 +25,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou | |||
| 25 | // and the rest is reserved for the caller thread | 25 | // and the rest is reserved for the caller thread |
| 26 | semaphore->max_count = max_count; | 26 | semaphore->max_count = max_count; |
| 27 | semaphore->available_count = initial_count; | 27 | semaphore->available_count = initial_count; |
| 28 | semaphore->address = address; | ||
| 28 | semaphore->name = std::move(name); | 29 | semaphore->name = std::move(name); |
| 29 | 30 | ||
| 30 | return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); | 31 | 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: | |||
| 22 | * @param name Optional name of semaphore | 22 | * @param name Optional name of semaphore |
| 23 | * @return The created semaphore | 23 | * @return The created semaphore |
| 24 | */ | 24 | */ |
| 25 | static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, | 25 | static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, VAddr address, |
| 26 | std::string name = "Unknown"); | 26 | std::string name = "Unknown"); |
| 27 | 27 | ||
| 28 | std::string GetTypeName() const override { | 28 | std::string GetTypeName() const override { |
| @@ -39,6 +39,7 @@ public: | |||
| 39 | 39 | ||
| 40 | s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have | 40 | s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have |
| 41 | s32 available_count; ///< Number of free slots left in the semaphore | 41 | s32 available_count; ///< Number of free slots left in the semaphore |
| 42 | VAddr address; | ||
| 42 | std::string name; ///< Name of semaphore (optional) | 43 | std::string name; ///< Name of semaphore (optional) |
| 43 | 44 | ||
| 44 | bool ShouldWait(Thread* thread) const override; | 45 | 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 | |||
| 358 | * @param entry_point Address of entry point for execution | 358 | * @param entry_point Address of entry point for execution |
| 359 | * @param arg User argument for thread | 359 | * @param arg User argument for thread |
| 360 | */ | 360 | */ |
| 361 | static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_top, | 361 | static void ResetThreadContext(ARM_Interface::ThreadContext& context, VAddr stack_top, |
| 362 | u32 entry_point, u32 arg) { | 362 | VAddr entry_point, u64 arg) { |
| 363 | memset(&context, 0, sizeof(ARM_Interface::ThreadContext)); | 363 | memset(&context, 0, sizeof(ARM_Interface::ThreadContext)); |
| 364 | 364 | ||
| 365 | context.cpu_registers[0] = arg; | 365 | context.cpu_registers[0] = arg; |
| @@ -446,7 +446,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 446 | // Map the page to the current process' address space. | 446 | // Map the page to the current process' address space. |
| 447 | // TODO(Subv): Find the correct MemoryState for this region. | 447 | // TODO(Subv): Find the correct MemoryState for this region. |
| 448 | vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, | 448 | vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, |
| 449 | linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Private); | 449 | linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::Static); |
| 450 | } | 450 | } |
| 451 | 451 | ||
| 452 | // Mark the slot as used | 452 | // Mark the slot as used |
| @@ -495,6 +495,9 @@ void Thread::BoostPriority(u32 priority) { | |||
| 495 | } | 495 | } |
| 496 | 496 | ||
| 497 | SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) { | 497 | SharedPtr<Thread> SetupMainThread(u32 entry_point, u32 priority, SharedPtr<Process> owner_process) { |
| 498 | // Setup page table so we can write to memory | ||
| 499 | SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); | ||
| 500 | |||
| 498 | // Initialize new "main" thread | 501 | // Initialize new "main" thread |
| 499 | auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, | 502 | auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, |
| 500 | Memory::HEAP_VADDR_END, owner_process); | 503 | 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: | |||
| 184 | u32 thread_id; | 184 | u32 thread_id; |
| 185 | 185 | ||
| 186 | u32 status; | 186 | u32 status; |
| 187 | u32 entry_point; | 187 | VAddr entry_point; |
| 188 | u32 stack_top; | 188 | VAddr stack_top; |
| 189 | 189 | ||
| 190 | u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application | 190 | u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application |
| 191 | u32 current_priority; ///< Current thread priority, can be temporarily changed | 191 | u32 current_priority; ///< Current thread priority, can be temporarily changed |
| @@ -250,13 +250,13 @@ void Reschedule(); | |||
| 250 | * Arbitrate the highest priority thread that is waiting | 250 | * Arbitrate the highest priority thread that is waiting |
| 251 | * @param address The address for which waiting threads should be arbitrated | 251 | * @param address The address for which waiting threads should be arbitrated |
| 252 | */ | 252 | */ |
| 253 | Thread* ArbitrateHighestPriorityThread(u32 address); | 253 | Thread* ArbitrateHighestPriorityThread(VAddr address); |
| 254 | 254 | ||
| 255 | /** | 255 | /** |
| 256 | * Arbitrate all threads currently waiting. | 256 | * Arbitrate all threads currently waiting. |
| 257 | * @param address The address for which waiting threads should be arbitrated | 257 | * @param address The address for which waiting threads should be arbitrated |
| 258 | */ | 258 | */ |
| 259 | void ArbitrateAllThreads(u32 address); | 259 | void ArbitrateAllThreads(VAddr address); |
| 260 | 260 | ||
| 261 | /** | 261 | /** |
| 262 | * Gets the current thread | 262 | * 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 @@ | |||
| 4 | 4 | ||
| 5 | #include <iterator> | 5 | #include <iterator> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "core/arm/arm_interface.h" | ||
| 7 | #include "core/hle/kernel/errors.h" | 8 | #include "core/hle/kernel/errors.h" |
| 8 | #include "core/hle/kernel/vm_manager.h" | 9 | #include "core/hle/kernel/vm_manager.h" |
| 10 | #include "core/core.h" | ||
| 9 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 10 | #include "core/memory_setup.h" | 12 | #include "core/memory_setup.h" |
| 11 | #include "core/mmio.h" | 13 | #include "core/mmio.h" |
| @@ -60,7 +62,7 @@ void VMManager::Reset() { | |||
| 60 | page_table.attributes.fill(Memory::PageType::Unmapped); | 62 | page_table.attributes.fill(Memory::PageType::Unmapped); |
| 61 | page_table.cached_res_count.fill(0); | 63 | page_table.cached_res_count.fill(0); |
| 62 | 64 | ||
| 63 | UpdatePageTableForVMA(initial_vma); | 65 | //UpdatePageTableForVMA(initial_vma); |
| 64 | } | 66 | } |
| 65 | 67 | ||
| 66 | VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | 68 | VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { |
| @@ -73,7 +75,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | |||
| 73 | 75 | ||
| 74 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | 76 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, |
| 75 | std::shared_ptr<std::vector<u8>> block, | 77 | std::shared_ptr<std::vector<u8>> block, |
| 76 | size_t offset, u32 size, | 78 | size_t offset, u64 size, |
| 77 | MemoryState state) { | 79 | MemoryState state) { |
| 78 | ASSERT(block != nullptr); | 80 | ASSERT(block != nullptr); |
| 79 | ASSERT(offset + size <= block->size()); | 81 | ASSERT(offset + size <= block->size()); |
| @@ -83,6 +85,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | |||
| 83 | VirtualMemoryArea& final_vma = vma_handle->second; | 85 | VirtualMemoryArea& final_vma = vma_handle->second; |
| 84 | ASSERT(final_vma.size == size); | 86 | ASSERT(final_vma.size == size); |
| 85 | 87 | ||
| 88 | Core::CPU().MapBackingMemory(target, size, block->data() + offset, VMAPermission::ReadWriteExecute); | ||
| 89 | |||
| 86 | final_vma.type = VMAType::AllocatedMemoryBlock; | 90 | final_vma.type = VMAType::AllocatedMemoryBlock; |
| 87 | final_vma.permissions = VMAPermission::ReadWrite; | 91 | final_vma.permissions = VMAPermission::ReadWrite; |
| 88 | final_vma.meminfo_state = state; | 92 | final_vma.meminfo_state = state; |
| @@ -93,7 +97,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | |||
| 93 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 97 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| 94 | } | 98 | } |
| 95 | 99 | ||
| 96 | ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u32 size, | 100 | ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u64 size, |
| 97 | MemoryState state) { | 101 | MemoryState state) { |
| 98 | ASSERT(memory != nullptr); | 102 | ASSERT(memory != nullptr); |
| 99 | 103 | ||
| @@ -102,6 +106,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 102 | VirtualMemoryArea& final_vma = vma_handle->second; | 106 | VirtualMemoryArea& final_vma = vma_handle->second; |
| 103 | ASSERT(final_vma.size == size); | 107 | ASSERT(final_vma.size == size); |
| 104 | 108 | ||
| 109 | Core::CPU().MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | ||
| 110 | |||
| 105 | final_vma.type = VMAType::BackingMemory; | 111 | final_vma.type = VMAType::BackingMemory; |
| 106 | final_vma.permissions = VMAPermission::ReadWrite; | 112 | final_vma.permissions = VMAPermission::ReadWrite; |
| 107 | final_vma.meminfo_state = state; | 113 | final_vma.meminfo_state = state; |
| @@ -111,7 +117,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 111 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 117 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| 112 | } | 118 | } |
| 113 | 119 | ||
| 114 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, | 120 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, |
| 115 | MemoryState state, | 121 | MemoryState state, |
| 116 | Memory::MMIORegionPointer mmio_handler) { | 122 | Memory::MMIORegionPointer mmio_handler) { |
| 117 | // This is the appropriately sized VMA that will turn into our allocation. | 123 | // This is the appropriately sized VMA that will turn into our allocation. |
| @@ -145,7 +151,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { | |||
| 145 | return MergeAdjacent(vma_handle); | 151 | return MergeAdjacent(vma_handle); |
| 146 | } | 152 | } |
| 147 | 153 | ||
| 148 | ResultCode VMManager::UnmapRange(VAddr target, u32 size) { | 154 | ResultCode VMManager::UnmapRange(VAddr target, u64 size) { |
| 149 | CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); | 155 | CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); |
| 150 | VAddr target_end = target + size; | 156 | VAddr target_end = target + size; |
| 151 | 157 | ||
| @@ -170,7 +176,7 @@ VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission ne | |||
| 170 | return MergeAdjacent(iter); | 176 | return MergeAdjacent(iter); |
| 171 | } | 177 | } |
| 172 | 178 | ||
| 173 | ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_perms) { | 179 | ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) { |
| 174 | CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); | 180 | CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); |
| 175 | VAddr target_end = target + size; | 181 | VAddr target_end = target + size; |
| 176 | 182 | ||
| @@ -213,7 +219,7 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) { | |||
| 213 | return vma_map.erase(iter, iter); // Erases an empty range of elements | 219 | return vma_map.erase(iter, iter); // Erases an empty range of elements |
| 214 | } | 220 | } |
| 215 | 221 | ||
| 216 | ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | 222 | ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { |
| 217 | ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); | 223 | ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); |
| 218 | ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); | 224 | ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); |
| 219 | 225 | ||
| @@ -229,8 +235,8 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | |||
| 229 | return ERR_INVALID_ADDRESS_STATE; | 235 | return ERR_INVALID_ADDRESS_STATE; |
| 230 | } | 236 | } |
| 231 | 237 | ||
| 232 | u32 start_in_vma = base - vma.base; | 238 | u64 start_in_vma = base - vma.base; |
| 233 | u32 end_in_vma = start_in_vma + size; | 239 | u64 end_in_vma = start_in_vma + size; |
| 234 | 240 | ||
| 235 | if (end_in_vma > vma.size) { | 241 | if (end_in_vma > vma.size) { |
| 236 | // Requested allocation doesn't fit inside VMA | 242 | // Requested allocation doesn't fit inside VMA |
| @@ -249,7 +255,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u32 size) { | |||
| 249 | return MakeResult<VMAIter>(vma_handle); | 255 | return MakeResult<VMAIter>(vma_handle); |
| 250 | } | 256 | } |
| 251 | 257 | ||
| 252 | ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { | 258 | ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { |
| 253 | ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); | 259 | ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); |
| 254 | ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target); | 260 | ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target); |
| 255 | 261 | ||
| @@ -278,7 +284,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u32 size) { | |||
| 278 | return MakeResult<VMAIter>(begin_vma); | 284 | return MakeResult<VMAIter>(begin_vma); |
| 279 | } | 285 | } |
| 280 | 286 | ||
| 281 | VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { | 287 | VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { |
| 282 | VirtualMemoryArea& old_vma = vma_handle->second; | 288 | VirtualMemoryArea& old_vma = vma_handle->second; |
| 283 | VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA | 289 | VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA |
| 284 | 290 | ||
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 { | |||
| 64 | /// Virtual base address of the region. | 64 | /// Virtual base address of the region. |
| 65 | VAddr base = 0; | 65 | VAddr base = 0; |
| 66 | /// Size of the region. | 66 | /// Size of the region. |
| 67 | u32 size = 0; | 67 | u64 size = 0; |
| 68 | 68 | ||
| 69 | VMAType type = VMAType::Free; | 69 | VMAType type = VMAType::Free; |
| 70 | VMAPermission permissions = VMAPermission::None; | 70 | VMAPermission permissions = VMAPermission::None; |
| @@ -109,7 +109,7 @@ public: | |||
| 109 | * used. | 109 | * used. |
| 110 | * @note This is the limit used by the New 3DS kernel. Old 3DS used 0x20000000. | 110 | * @note This is the limit used by the New 3DS kernel. Old 3DS used 0x20000000. |
| 111 | */ | 111 | */ |
| 112 | static const u32 MAX_ADDRESS = 0x40000000; | 112 | static const VAddr MAX_ADDRESS = 0x8000000000; |
| 113 | 113 | ||
| 114 | /** | 114 | /** |
| 115 | * A map covering the entirety of the managed address space, keyed by the `base` field of each | 115 | * A map covering the entirety of the managed address space, keyed by the `base` field of each |
| @@ -142,7 +142,7 @@ public: | |||
| 142 | * @param state MemoryState tag to attach to the VMA. | 142 | * @param state MemoryState tag to attach to the VMA. |
| 143 | */ | 143 | */ |
| 144 | ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, | 144 | ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, |
| 145 | size_t offset, u32 size, MemoryState state); | 145 | size_t offset, u64 size, MemoryState state); |
| 146 | 146 | ||
| 147 | /** | 147 | /** |
| 148 | * Maps an unmanaged host memory pointer at a given address. | 148 | * Maps an unmanaged host memory pointer at a given address. |
| @@ -152,7 +152,7 @@ public: | |||
| 152 | * @param size Size of the mapping. | 152 | * @param size Size of the mapping. |
| 153 | * @param state MemoryState tag to attach to the VMA. | 153 | * @param state MemoryState tag to attach to the VMA. |
| 154 | */ | 154 | */ |
| 155 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u32 size, MemoryState state); | 155 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); |
| 156 | 156 | ||
| 157 | /** | 157 | /** |
| 158 | * Maps a memory-mapped IO region at a given address. | 158 | * Maps a memory-mapped IO region at a given address. |
| @@ -163,17 +163,17 @@ public: | |||
| 163 | * @param state MemoryState tag to attach to the VMA. | 163 | * @param state MemoryState tag to attach to the VMA. |
| 164 | * @param mmio_handler The handler that will implement read and write for this MMIO region. | 164 | * @param mmio_handler The handler that will implement read and write for this MMIO region. |
| 165 | */ | 165 | */ |
| 166 | ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, | 166 | ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, |
| 167 | Memory::MMIORegionPointer mmio_handler); | 167 | Memory::MMIORegionPointer mmio_handler); |
| 168 | 168 | ||
| 169 | /// Unmaps a range of addresses, splitting VMAs as necessary. | 169 | /// Unmaps a range of addresses, splitting VMAs as necessary. |
| 170 | ResultCode UnmapRange(VAddr target, u32 size); | 170 | ResultCode UnmapRange(VAddr target, u64 size); |
| 171 | 171 | ||
| 172 | /// Changes the permissions of the given VMA. | 172 | /// Changes the permissions of the given VMA. |
| 173 | VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); | 173 | VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); |
| 174 | 174 | ||
| 175 | /// Changes the permissions of a range of addresses, splitting VMAs as necessary. | 175 | /// Changes the permissions of a range of addresses, splitting VMAs as necessary. |
| 176 | ResultCode ReprotectRange(VAddr target, u32 size, VMAPermission new_perms); | 176 | ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); |
| 177 | 177 | ||
| 178 | /** | 178 | /** |
| 179 | * Scans all VMAs and updates the page table range of any that use the given vector as backing | 179 | * Scans all VMAs and updates the page table range of any that use the given vector as backing |
| @@ -201,19 +201,19 @@ private: | |||
| 201 | * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing | 201 | * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing |
| 202 | * the appropriate error checking. | 202 | * the appropriate error checking. |
| 203 | */ | 203 | */ |
| 204 | ResultVal<VMAIter> CarveVMA(VAddr base, u32 size); | 204 | ResultVal<VMAIter> CarveVMA(VAddr base, u64 size); |
| 205 | 205 | ||
| 206 | /** | 206 | /** |
| 207 | * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each | 207 | * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each |
| 208 | * end of the range. | 208 | * end of the range. |
| 209 | */ | 209 | */ |
| 210 | ResultVal<VMAIter> CarveVMARange(VAddr base, u32 size); | 210 | ResultVal<VMAIter> CarveVMARange(VAddr base, u64 size); |
| 211 | 211 | ||
| 212 | /** | 212 | /** |
| 213 | * Splits a VMA in two, at the specified offset. | 213 | * Splits a VMA in two, at the specified offset. |
| 214 | * @returns the right side of the split, with the original iterator becoming the left side. | 214 | * @returns the right side of the split, with the original iterator becoming the left side. |
| 215 | */ | 215 | */ |
| 216 | VMAIter SplitVMA(VMAIter vma, u32 offset_in_vma); | 216 | VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); |
| 217 | 217 | ||
| 218 | /** | 218 | /** |
| 219 | * Checks for and merges the specified VMA with adjacent ones if possible. | 219 | * 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() { | |||
| 1073 | MemoryPermission::ReadWrite, MemoryPermission::Read, 0, | 1073 | MemoryPermission::ReadWrite, MemoryPermission::Read, 0, |
| 1074 | Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); | 1074 | Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); |
| 1075 | 1075 | ||
| 1076 | lock = Kernel::Mutex::Create(false, "APT_U:Lock"); | 1076 | if (LoadSharedFont()) { |
| 1077 | shared_font_loaded = true; | ||
| 1078 | } else if (LoadLegacySharedFont()) { | ||
| 1079 | LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); | ||
| 1080 | shared_font_loaded = true; | ||
| 1081 | } else { | ||
| 1082 | LOG_WARNING(Service_APT, "Unable to load shared font"); | ||
| 1083 | shared_font_loaded = false; | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | lock = Kernel::Mutex::Create(false, 0, "APT_U:Lock"); | ||
| 1077 | 1087 | ||
| 1078 | cpu_percent = 0; | 1088 | cpu_percent = 0; |
| 1079 | unknown_ns_state_field = 0; | 1089 | 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) { | |||
| 47 | MemoryPermission::ReadWrite, 0, | 47 | MemoryPermission::ReadWrite, 0, |
| 48 | Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); | 48 | Kernel::MemoryRegion::BASE, "CSND:SharedMemory"); |
| 49 | 49 | ||
| 50 | mutex = Kernel::Mutex::Create(false, "CSND:mutex"); | 50 | mutex = Kernel::Mutex::Create(false, 0, "CSND:mutex"); |
| 51 | 51 | ||
| 52 | cmd_buff[1] = RESULT_SUCCESS.raw; | 52 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 53 | cmd_buff[2] = IPC::CopyHandleDesc(2); | 53 | 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_ | |||
| 274 | } | 274 | } |
| 275 | SetEntry(i, segment); | 275 | SetEntry(i, segment); |
| 276 | } | 276 | } |
| 277 | return MakeResult<u32>(prev_data_segment + module_address); | 277 | return MakeResult<VAddr>(prev_data_segment + module_address); |
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | ResultCode CROHelper::RebaseExportNamedSymbolTable() { | 280 | 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) { | |||
| 62 | IPC::RequestParser rp(ctx, 0x2, 0, 0); | 62 | IPC::RequestParser rp(ctx, 0x2, 0, 0); |
| 63 | 63 | ||
| 64 | notification_semaphore = | 64 | notification_semaphore = |
| 65 | Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); | 65 | Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, 0, "SRV:Notification").Unwrap(); |
| 66 | 66 | ||
| 67 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | 67 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); |
| 68 | rb.Push(RESULT_SUCCESS); | 68 | 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 @@ | |||
| 31 | #include "core/hle/kernel/timer.h" | 31 | #include "core/hle/kernel/timer.h" |
| 32 | #include "core/hle/kernel/vm_manager.h" | 32 | #include "core/hle/kernel/vm_manager.h" |
| 33 | #include "core/hle/kernel/wait_object.h" | 33 | #include "core/hle/kernel/wait_object.h" |
| 34 | #include "core/hle/lock.h" | ||
| 35 | #include "core/hle/result.h" | 34 | #include "core/hle/result.h" |
| 36 | #include "core/hle/service/service.h" | 35 | #include "core/hle/service/service.h" |
| 37 | 36 | ||
| @@ -201,21 +200,17 @@ static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { | |||
| 201 | } | 200 | } |
| 202 | 201 | ||
| 203 | /// Connect to an OS service given the port name, returns the handle to the port to out | 202 | /// Connect to an OS service given the port name, returns the handle to the port to out |
| 204 | static ResultCode ConnectToPort(Kernel::Handle* out_handle, VAddr port_name_address) { | 203 | static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { |
| 205 | if (!Memory::IsValidVirtualAddress(port_name_address)) | 204 | if (port_name == nullptr) |
| 206 | return Kernel::ERR_NOT_FOUND; | 205 | return Kernel::ERR_NOT_FOUND; |
| 207 | 206 | if (std::strlen(port_name) > 11) | |
| 208 | static constexpr std::size_t PortNameMaxLength = 11; | ||
| 209 | // Read 1 char beyond the max allowed port name to detect names that are too long. | ||
| 210 | std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); | ||
| 211 | if (port_name.size() > PortNameMaxLength) | ||
| 212 | return Kernel::ERR_PORT_NAME_TOO_LONG; | 207 | return Kernel::ERR_PORT_NAME_TOO_LONG; |
| 213 | 208 | ||
| 214 | LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name.c_str()); | 209 | LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); |
| 215 | 210 | ||
| 216 | auto it = Service::g_kernel_named_ports.find(port_name); | 211 | auto it = Service::g_kernel_named_ports.find(port_name); |
| 217 | if (it == Service::g_kernel_named_ports.end()) { | 212 | if (it == Service::g_kernel_named_ports.end()) { |
| 218 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name.c_str()); | 213 | LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); |
| 219 | return Kernel::ERR_NOT_FOUND; | 214 | return Kernel::ERR_NOT_FOUND; |
| 220 | } | 215 | } |
| 221 | 216 | ||
| @@ -275,24 +270,6 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) | |||
| 275 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 270 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 276 | thread->WakeAfterDelay(nano_seconds); | 271 | thread->WakeAfterDelay(nano_seconds); |
| 277 | 272 | ||
| 278 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 279 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 280 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 281 | |||
| 282 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 283 | |||
| 284 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 285 | thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); | ||
| 286 | return; | ||
| 287 | } | ||
| 288 | |||
| 289 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 290 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 291 | |||
| 292 | // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we | ||
| 293 | // don't have to do anything else here. | ||
| 294 | }; | ||
| 295 | |||
| 296 | Core::System::GetInstance().PrepareReschedule(); | 273 | Core::System::GetInstance().PrepareReschedule(); |
| 297 | 274 | ||
| 298 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread | 275 | // 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) | |||
| 307 | } | 284 | } |
| 308 | 285 | ||
| 309 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 286 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 310 | static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, | 287 | static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 handle_count, |
| 311 | bool wait_all, s64 nano_seconds) { | 288 | bool wait_all, s64 nano_seconds) { |
| 312 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 289 | Kernel::Thread* thread = Kernel::GetCurrentThread(); |
| 313 | 290 | ||
| 314 | if (!Memory::IsValidVirtualAddress(handles_address)) | 291 | // Check if 'handles' is invalid |
| 292 | if (handles == nullptr) | ||
| 315 | return Kernel::ERR_INVALID_POINTER; | 293 | return Kernel::ERR_INVALID_POINTER; |
| 316 | 294 | ||
| 317 | // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If | 295 | // 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 | |||
| 326 | std::vector<ObjectPtr> objects(handle_count); | 304 | std::vector<ObjectPtr> objects(handle_count); |
| 327 | 305 | ||
| 328 | for (int i = 0; i < handle_count; ++i) { | 306 | for (int i = 0; i < handle_count; ++i) { |
| 329 | Kernel::Handle handle = Memory::Read32(handles_address + i * sizeof(Kernel::Handle)); | 307 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); |
| 330 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); | ||
| 331 | if (object == nullptr) | 308 | if (object == nullptr) |
| 332 | return ERR_INVALID_HANDLE; | 309 | return ERR_INVALID_HANDLE; |
| 333 | objects[i] = object; | 310 | objects[i] = object; |
| @@ -366,23 +343,6 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand | |||
| 366 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 343 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 367 | thread->WakeAfterDelay(nano_seconds); | 344 | thread->WakeAfterDelay(nano_seconds); |
| 368 | 345 | ||
| 369 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 370 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 371 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 372 | |||
| 373 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL); | ||
| 374 | |||
| 375 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 376 | thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); | ||
| 377 | return; | ||
| 378 | } | ||
| 379 | |||
| 380 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 381 | |||
| 382 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 383 | // The wait_all case does not update the output index. | ||
| 384 | }; | ||
| 385 | |||
| 386 | Core::System::GetInstance().PrepareReschedule(); | 346 | Core::System::GetInstance().PrepareReschedule(); |
| 387 | 347 | ||
| 388 | // This value gets set to -1 by default in this case, it is not modified after this. | 348 | // 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 | |||
| 400 | // We found a ready object, acquire it and set the result value | 360 | // We found a ready object, acquire it and set the result value |
| 401 | Kernel::WaitObject* object = itr->get(); | 361 | Kernel::WaitObject* object = itr->get(); |
| 402 | object->Acquire(thread); | 362 | object->Acquire(thread); |
| 403 | *out = static_cast<s32>(std::distance(objects.begin(), itr)); | 363 | *out = std::distance(objects.begin(), itr); |
| 404 | return RESULT_SUCCESS; | 364 | return RESULT_SUCCESS; |
| 405 | } | 365 | } |
| 406 | 366 | ||
| @@ -428,37 +388,22 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand | |||
| 428 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 388 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 429 | thread->WakeAfterDelay(nano_seconds); | 389 | thread->WakeAfterDelay(nano_seconds); |
| 430 | 390 | ||
| 431 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 432 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 433 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 434 | |||
| 435 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 436 | |||
| 437 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 438 | thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); | ||
| 439 | return; | ||
| 440 | } | ||
| 441 | |||
| 442 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 443 | |||
| 444 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 445 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); | ||
| 446 | }; | ||
| 447 | |||
| 448 | Core::System::GetInstance().PrepareReschedule(); | 391 | Core::System::GetInstance().PrepareReschedule(); |
| 449 | 392 | ||
| 450 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 393 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 451 | // signal in one of its wait objects. | 394 | // signal in one of its wait objects. |
| 452 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | 395 | // Otherwise we retain the default value of timeout, and -1 in the out parameter |
| 396 | thread->wait_set_output = true; | ||
| 453 | *out = -1; | 397 | *out = -1; |
| 454 | return Kernel::RESULT_TIMEOUT; | 398 | return Kernel::RESULT_TIMEOUT; |
| 455 | } | 399 | } |
| 456 | } | 400 | } |
| 457 | 401 | ||
| 458 | /// In a single operation, sends a IPC reply and waits for a new request. | 402 | /// In a single operation, sends a IPC reply and waits for a new request. |
| 459 | static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count, | 403 | static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count, |
| 460 | Kernel::Handle reply_target) { | 404 | Kernel::Handle reply_target) { |
| 461 | if (!Memory::IsValidVirtualAddress(handles_address)) | 405 | // 'handles' has to be a valid pointer even if 'handle_count' is 0. |
| 406 | if (handles == nullptr) | ||
| 462 | return Kernel::ERR_INVALID_POINTER; | 407 | return Kernel::ERR_INVALID_POINTER; |
| 463 | 408 | ||
| 464 | // Check if 'handle_count' is invalid | 409 | // Check if 'handle_count' is invalid |
| @@ -469,8 +414,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ | |||
| 469 | std::vector<ObjectPtr> objects(handle_count); | 414 | std::vector<ObjectPtr> objects(handle_count); |
| 470 | 415 | ||
| 471 | for (int i = 0; i < handle_count; ++i) { | 416 | for (int i = 0; i < handle_count; ++i) { |
| 472 | Kernel::Handle handle = Memory::Read32(handles_address + i * sizeof(Kernel::Handle)); | 417 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); |
| 473 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); | ||
| 474 | if (object == nullptr) | 418 | if (object == nullptr) |
| 475 | return ERR_INVALID_HANDLE; | 419 | return ERR_INVALID_HANDLE; |
| 476 | objects[i] = object; | 420 | objects[i] = object; |
| @@ -524,7 +468,7 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ | |||
| 524 | // We found a ready object, acquire it and set the result value | 468 | // We found a ready object, acquire it and set the result value |
| 525 | Kernel::WaitObject* object = itr->get(); | 469 | Kernel::WaitObject* object = itr->get(); |
| 526 | object->Acquire(thread); | 470 | object->Acquire(thread); |
| 527 | *index = static_cast<s32>(std::distance(objects.begin(), itr)); | 471 | *index = std::distance(objects.begin(), itr); |
| 528 | 472 | ||
| 529 | if (object->GetHandleType() == Kernel::HandleType::ServerSession) { | 473 | if (object->GetHandleType() == Kernel::HandleType::ServerSession) { |
| 530 | auto server_session = static_cast<Kernel::ServerSession*>(object); | 474 | auto server_session = static_cast<Kernel::ServerSession*>(object); |
| @@ -538,6 +482,8 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ | |||
| 538 | 482 | ||
| 539 | // No objects were ready to be acquired, prepare to suspend the thread. | 483 | // No objects were ready to be acquired, prepare to suspend the thread. |
| 540 | 484 | ||
| 485 | // TODO(Subv): Perform IPC translation upon wakeup. | ||
| 486 | |||
| 541 | // Put the thread to sleep | 487 | // Put the thread to sleep |
| 542 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | 488 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 543 | 489 | ||
| @@ -549,24 +495,12 @@ static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_ | |||
| 549 | 495 | ||
| 550 | thread->wait_objects = std::move(objects); | 496 | thread->wait_objects = std::move(objects); |
| 551 | 497 | ||
| 552 | thread->wakeup_callback = [](ThreadWakeupReason reason, | ||
| 553 | Kernel::SharedPtr<Kernel::Thread> thread, | ||
| 554 | Kernel::SharedPtr<Kernel::WaitObject> object) { | ||
| 555 | |||
| 556 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 557 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 558 | |||
| 559 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 560 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); | ||
| 561 | |||
| 562 | // TODO(Subv): Perform IPC translation upon wakeup. | ||
| 563 | }; | ||
| 564 | |||
| 565 | Core::System::GetInstance().PrepareReschedule(); | 498 | Core::System::GetInstance().PrepareReschedule(); |
| 566 | 499 | ||
| 567 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 500 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 568 | // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. | 501 | // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. |
| 569 | // By default the index is set to -1. | 502 | // By default the index is set to -1. |
| 503 | thread->wait_set_output = true; | ||
| 570 | *index = -1; | 504 | *index = -1; |
| 571 | return RESULT_SUCCESS; | 505 | return RESULT_SUCCESS; |
| 572 | } | 506 | } |
| @@ -623,10 +557,8 @@ static void Break(u8 break_reason) { | |||
| 623 | } | 557 | } |
| 624 | 558 | ||
| 625 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit | 559 | /// Used to output a message on a debug hardware unit - does nothing on a retail unit |
| 626 | static void OutputDebugString(VAddr address, int len) { | 560 | static void OutputDebugString(const char* string, int len) { |
| 627 | std::vector<char> string(len); | 561 | LOG_DEBUG(Debug_Emulated, "%.*s", len, string); |
| 628 | Memory::ReadBlock(address, string.data(), len); | ||
| 629 | LOG_DEBUG(Debug_Emulated, "%.*s", len, string.data()); | ||
| 630 | } | 562 | } |
| 631 | 563 | ||
| 632 | /// Get resource limit | 564 | /// Get resource limit |
| @@ -644,9 +576,9 @@ static ResultCode GetResourceLimit(Kernel::Handle* resource_limit, Kernel::Handl | |||
| 644 | } | 576 | } |
| 645 | 577 | ||
| 646 | /// Get resource limit current values | 578 | /// Get resource limit current values |
| 647 | static ResultCode GetResourceLimitCurrentValues(VAddr values, Kernel::Handle resource_limit_handle, | 579 | static ResultCode GetResourceLimitCurrentValues(s64* values, Kernel::Handle resource_limit_handle, |
| 648 | VAddr names, u32 name_count) { | 580 | u32* names, u32 name_count) { |
| 649 | LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%08X, name_count=%d", | 581 | LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", |
| 650 | resource_limit_handle, names, name_count); | 582 | resource_limit_handle, names, name_count); |
| 651 | 583 | ||
| 652 | SharedPtr<Kernel::ResourceLimit> resource_limit = | 584 | SharedPtr<Kernel::ResourceLimit> resource_limit = |
| @@ -654,19 +586,16 @@ static ResultCode GetResourceLimitCurrentValues(VAddr values, Kernel::Handle res | |||
| 654 | if (resource_limit == nullptr) | 586 | if (resource_limit == nullptr) |
| 655 | return ERR_INVALID_HANDLE; | 587 | return ERR_INVALID_HANDLE; |
| 656 | 588 | ||
| 657 | for (unsigned int i = 0; i < name_count; ++i) { | 589 | for (unsigned int i = 0; i < name_count; ++i) |
| 658 | u32 name = Memory::Read32(names + i * sizeof(u32)); | 590 | values[i] = resource_limit->GetCurrentResourceValue(names[i]); |
| 659 | s64 value = resource_limit->GetCurrentResourceValue(name); | ||
| 660 | Memory::Write64(values + i * sizeof(u64), value); | ||
| 661 | } | ||
| 662 | 591 | ||
| 663 | return RESULT_SUCCESS; | 592 | return RESULT_SUCCESS; |
| 664 | } | 593 | } |
| 665 | 594 | ||
| 666 | /// Get resource limit max values | 595 | /// Get resource limit max values |
| 667 | static ResultCode GetResourceLimitLimitValues(VAddr values, Kernel::Handle resource_limit_handle, | 596 | static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resource_limit_handle, |
| 668 | VAddr names, u32 name_count) { | 597 | u32* names, u32 name_count) { |
| 669 | LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%08X, name_count=%d", | 598 | LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", |
| 670 | resource_limit_handle, names, name_count); | 599 | resource_limit_handle, names, name_count); |
| 671 | 600 | ||
| 672 | SharedPtr<Kernel::ResourceLimit> resource_limit = | 601 | SharedPtr<Kernel::ResourceLimit> resource_limit = |
| @@ -674,11 +603,8 @@ static ResultCode GetResourceLimitLimitValues(VAddr values, Kernel::Handle resou | |||
| 674 | if (resource_limit == nullptr) | 603 | if (resource_limit == nullptr) |
| 675 | return ERR_INVALID_HANDLE; | 604 | return ERR_INVALID_HANDLE; |
| 676 | 605 | ||
| 677 | for (unsigned int i = 0; i < name_count; ++i) { | 606 | for (unsigned int i = 0; i < name_count; ++i) |
| 678 | u32 name = Memory::Read32(names + i * sizeof(u32)); | 607 | values[i] = resource_limit->GetMaxResourceValue(names[i]); |
| 679 | s64 value = resource_limit->GetMaxResourceValue(names); | ||
| 680 | Memory::Write64(values + i * sizeof(u64), value); | ||
| 681 | } | ||
| 682 | 608 | ||
| 683 | return RESULT_SUCCESS; | 609 | return RESULT_SUCCESS; |
| 684 | } | 610 | } |
| @@ -729,9 +655,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 ent | |||
| 729 | "Newly created thread must run in the SysCore (Core1), unimplemented."); | 655 | "Newly created thread must run in the SysCore (Core1), unimplemented."); |
| 730 | } | 656 | } |
| 731 | 657 | ||
| 732 | CASCADE_RESULT(SharedPtr<Thread> thread, | 658 | CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, |
| 733 | Kernel::Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, | 659 | arg, processor_id, stack_top)); |
| 734 | Kernel::g_current_process)); | ||
| 735 | 660 | ||
| 736 | thread->context.fpscr = | 661 | thread->context.fpscr = |
| 737 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 | 662 | FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 |
| @@ -756,7 +681,7 @@ static void ExitThread() { | |||
| 756 | } | 681 | } |
| 757 | 682 | ||
| 758 | /// Gets the priority for the specified thread | 683 | /// Gets the priority for the specified thread |
| 759 | static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) { | 684 | static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { |
| 760 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); | 685 | const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); |
| 761 | if (thread == nullptr) | 686 | if (thread == nullptr) |
| 762 | return ERR_INVALID_HANDLE; | 687 | return ERR_INVALID_HANDLE; |
| @@ -766,7 +691,7 @@ static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) { | |||
| 766 | } | 691 | } |
| 767 | 692 | ||
| 768 | /// Sets the priority for the specified thread | 693 | /// Sets the priority for the specified thread |
| 769 | static ResultCode SetThreadPriority(Kernel::Handle handle, u32 priority) { | 694 | static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { |
| 770 | if (priority > THREADPRIO_LOWEST) { | 695 | if (priority > THREADPRIO_LOWEST) { |
| 771 | return Kernel::ERR_OUT_OF_RANGE; | 696 | return Kernel::ERR_OUT_OF_RANGE; |
| 772 | } | 697 | } |
| @@ -1051,7 +976,7 @@ static void SleepThread(s64 nanoseconds) { | |||
| 1051 | static s64 GetSystemTick() { | 976 | static s64 GetSystemTick() { |
| 1052 | s64 result = CoreTiming::GetTicks(); | 977 | s64 result = CoreTiming::GetTicks(); |
| 1053 | // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. | 978 | // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. |
| 1054 | CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b | 979 | Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b |
| 1055 | return result; | 980 | return result; |
| 1056 | } | 981 | } |
| 1057 | 982 | ||
| @@ -1110,9 +1035,9 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si | |||
| 1110 | } | 1035 | } |
| 1111 | 1036 | ||
| 1112 | static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client_port, | 1037 | static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client_port, |
| 1113 | VAddr name_address, u32 max_sessions) { | 1038 | const char* name, u32 max_sessions) { |
| 1114 | // TODO(Subv): Implement named ports. | 1039 | // TODO(Subv): Implement named ports. |
| 1115 | ASSERT_MSG(name_address == 0, "Named ports are currently unimplemented"); | 1040 | ASSERT_MSG(name == nullptr, "Named ports are currently unimplemented"); |
| 1116 | 1041 | ||
| 1117 | using Kernel::ServerPort; | 1042 | using Kernel::ServerPort; |
| 1118 | using Kernel::ClientPort; | 1043 | using Kernel::ClientPort; |
| @@ -1263,7 +1188,7 @@ struct FunctionDef { | |||
| 1263 | Func* func; | 1188 | Func* func; |
| 1264 | const char* name; | 1189 | const char* name; |
| 1265 | }; | 1190 | }; |
| 1266 | } // namespace | 1191 | } |
| 1267 | 1192 | ||
| 1268 | static const FunctionDef SVC_Table[] = { | 1193 | static const FunctionDef SVC_Table[] = { |
| 1269 | {0x00, nullptr, "Unknown"}, | 1194 | {0x00, nullptr, "Unknown"}, |
| @@ -1407,9 +1332,6 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | |||
| 1407 | void CallSVC(u32 immediate) { | 1332 | void CallSVC(u32 immediate) { |
| 1408 | MICROPROFILE_SCOPE(Kernel_SVC); | 1333 | MICROPROFILE_SCOPE(Kernel_SVC); |
| 1409 | 1334 | ||
| 1410 | // Lock the global kernel mutex when we enter the kernel HLE. | ||
| 1411 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 1412 | |||
| 1413 | const FunctionDef* info = GetSVCInfo(immediate); | 1335 | const FunctionDef* info = GetSVCInfo(immediate); |
| 1414 | if (info) { | 1336 | if (info) { |
| 1415 | if (info->func) { | 1337 | if (info->func) { |
| @@ -1420,4 +1342,4 @@ void CallSVC(u32 immediate) { | |||
| 1420 | } | 1342 | } |
| 1421 | } | 1343 | } |
| 1422 | 1344 | ||
| 1423 | } // namespace SVC | 1345 | } // 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); | |||
| 515 | 515 | ||
| 516 | /// Update hardware | 516 | /// Update hardware |
| 517 | static void VBlankCallback(u64 userdata, int cycles_late) { | 517 | static void VBlankCallback(u64 userdata, int cycles_late) { |
| 518 | VideoCore::g_renderer->SwapBuffers(); | 518 | //VideoCore::g_renderer->SwapBuffers(); |
| 519 | 519 | ||
| 520 | // Signal to GSP that GPU interrupt has occurred | 520 | //// Signal to GSP that GPU interrupt has occurred |
| 521 | // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub | 521 | //// TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub |
| 522 | // screen, or if both use the same interrupts and these two instead determine the | 522 | //// screen, or if both use the same interrupts and these two instead determine the |
| 523 | // beginning and end of the VBlank period. If needed, split the interrupt firing into | 523 | //// beginning and end of the VBlank period. If needed, split the interrupt firing into |
| 524 | // two different intervals. | 524 | //// two different intervals. |
| 525 | Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); | 525 | //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); |
| 526 | Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); | 526 | //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); |
| 527 | 527 | ||
| 528 | // Reschedule recurrent event | 528 | // Reschedule recurrent event |
| 529 | CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); | 529 | 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 | |||
| 267 | return ResultStatus::Error; | 267 | return ResultStatus::Error; |
| 268 | codeset->name = filename; | 268 | codeset->name = filename; |
| 269 | 269 | ||
| 270 | process = Kernel::Process::Create(std::move(codeset)); | 270 | process = Kernel::Process::Create("main"); |
| 271 | process->LoadModule(codeset, codeset->entrypoint); | ||
| 271 | process->svc_access_mask.set(); | 272 | process->svc_access_mask.set(); |
| 272 | process->address_mappings = default_address_mappings; | 273 | process->address_mappings = default_address_mappings; |
| 273 | 274 | ||
| 274 | // Attach the default resource limit (APPLICATION) to the process | 275 | // Attach the default resource limit (APPLICATION) to the process |
| 275 | process->resource_limit = | 276 | process->resource_limit = |
| 276 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 277 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 277 | 278 | process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); | |
| 278 | process->Run(48, Kernel::DEFAULT_STACK_SIZE); | ||
| 279 | 279 | ||
| 280 | Service::FS::RegisterSelfNCCH(*this); | 280 | Service::FS::RegisterSelfNCCH(*this); |
| 281 | 281 | ||
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 | |||
| 364 | namespace Loader { | 364 | namespace Loader { |
| 365 | 365 | ||
| 366 | FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { | 366 | FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { |
| 367 | u32 magic; | 367 | static constexpr u16 ELF_MACHINE_ARM{0x28}; |
| 368 | |||
| 369 | u32 magic = 0; | ||
| 368 | file.Seek(0, SEEK_SET); | 370 | file.Seek(0, SEEK_SET); |
| 369 | if (1 != file.ReadArray<u32>(&magic, 1)) | 371 | if (1 != file.ReadArray<u32>(&magic, 1)) |
| 370 | return FileType::Error; | 372 | return FileType::Error; |
| 371 | 373 | ||
| 372 | if (MakeMagic('\x7f', 'E', 'L', 'F') == magic) | 374 | u16 machine = 0; |
| 375 | file.Seek(18, SEEK_SET); | ||
| 376 | if (1 != file.ReadArray<u16>(&machine, 1)) | ||
| 377 | return FileType::Error; | ||
| 378 | |||
| 379 | if (MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) | ||
| 373 | return FileType::ELF; | 380 | return FileType::ELF; |
| 374 | 381 | ||
| 375 | return FileType::Error; | 382 | return FileType::Error; |
| @@ -394,7 +401,8 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 394 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | 401 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); |
| 395 | codeset->name = filename; | 402 | codeset->name = filename; |
| 396 | 403 | ||
| 397 | process = Kernel::Process::Create(std::move(codeset)); | 404 | process = Kernel::Process::Create("main"); |
| 405 | process->LoadModule(codeset, codeset->entrypoint); | ||
| 398 | process->svc_access_mask.set(); | 406 | process->svc_access_mask.set(); |
| 399 | process->address_mappings = default_address_mappings; | 407 | process->address_mappings = default_address_mappings; |
| 400 | 408 | ||
| @@ -402,7 +410,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 402 | process->resource_limit = | 410 | process->resource_limit = |
| 403 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 411 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 404 | 412 | ||
| 405 | process->Run(48, Kernel::DEFAULT_STACK_SIZE); | 413 | process->Run(codeset->entrypoint, 48, Kernel::DEFAULT_STACK_SIZE); |
| 406 | 414 | ||
| 407 | is_loaded = true; | 415 | is_loaded = true; |
| 408 | return ResultStatus::Success; | 416 | 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <vector> | ||
| 6 | |||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/loader/linker.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | |||
| 13 | namespace Loader { | ||
| 14 | |||
| 15 | enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 }; | ||
| 16 | |||
| 17 | enum DynamicType : u32 { | ||
| 18 | DT_NULL = 0, | ||
| 19 | DT_PLTRELSZ = 2, | ||
| 20 | DT_STRTAB = 5, | ||
| 21 | DT_SYMTAB = 6, | ||
| 22 | DT_RELA = 7, | ||
| 23 | DT_RELASZ = 8, | ||
| 24 | DT_STRSZ = 10, | ||
| 25 | DT_JMPREL = 23, | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct Elf64_Rela { | ||
| 29 | u64_le offset; | ||
| 30 | RelocationType type; | ||
| 31 | u32_le symbol; | ||
| 32 | s64_le addend; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size."); | ||
| 35 | |||
| 36 | struct Elf64_Dyn { | ||
| 37 | u64_le tag; | ||
| 38 | u64_le value; | ||
| 39 | }; | ||
| 40 | static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size."); | ||
| 41 | |||
| 42 | struct Elf64_Sym { | ||
| 43 | u32_le name; | ||
| 44 | INSERT_PADDING_BYTES(0x2); | ||
| 45 | u16_le shndx; | ||
| 46 | u64_le value; | ||
| 47 | u64_le size; | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size."); | ||
| 50 | |||
| 51 | void Linker::WriteRelocations(std::vector<u8>& program_image, | ||
| 52 | const std::vector<Symbol>& symbols, u64 relocation_offset, | ||
| 53 | u64 size, bool is_jump_relocation, VAddr load_base) { | ||
| 54 | for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) { | ||
| 55 | Elf64_Rela rela; | ||
| 56 | std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela)); | ||
| 57 | |||
| 58 | const Symbol& symbol = symbols[rela.symbol]; | ||
| 59 | switch (rela.type) { | ||
| 60 | case RelocationType::RELATIVE: { | ||
| 61 | const u64 value = load_base + rela.addend; | ||
| 62 | if (!symbol.name.empty()) { | ||
| 63 | exports[symbol.name] = value; | ||
| 64 | } | ||
| 65 | std::memcpy(&program_image[rela.offset], &value, sizeof(u64)); | ||
| 66 | break; | ||
| 67 | } | ||
| 68 | case RelocationType::JUMP_SLOT: | ||
| 69 | case RelocationType::GLOB_DAT: | ||
| 70 | if (!symbol.value) { | ||
| 71 | imports[symbol.name] = {rela.offset + load_base, 0}; | ||
| 72 | } else { | ||
| 73 | exports[symbol.name] = symbol.value; | ||
| 74 | std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64)); | ||
| 75 | } | ||
| 76 | break; | ||
| 77 | case RelocationType::ABS64: | ||
| 78 | if (!symbol.value) { | ||
| 79 | imports[symbol.name] = {rela.offset + load_base, rela.addend}; | ||
| 80 | } else { | ||
| 81 | const u64 value = symbol.value + rela.addend; | ||
| 82 | exports[symbol.name] = value; | ||
| 83 | std::memcpy(&program_image[rela.offset], &value, sizeof(u64)); | ||
| 84 | } | ||
| 85 | break; | ||
| 86 | default: | ||
| 87 | LOG_CRITICAL(Loader, "Unknown relocation type: %d", rela.type); | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, | ||
| 94 | VAddr load_base) { | ||
| 95 | std::map<u64, u64> dynamic; | ||
| 96 | while (dynamic_section_offset < program_image.size()) { | ||
| 97 | Elf64_Dyn dyn; | ||
| 98 | std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn)); | ||
| 99 | dynamic_section_offset += sizeof(Elf64_Dyn); | ||
| 100 | |||
| 101 | if (dyn.tag == DT_NULL) { | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | dynamic[dyn.tag] = dyn.value; | ||
| 105 | } | ||
| 106 | |||
| 107 | u64 offset = dynamic[DT_SYMTAB]; | ||
| 108 | std::vector<Symbol> symbols; | ||
| 109 | while (offset < program_image.size()) { | ||
| 110 | Elf64_Sym sym; | ||
| 111 | std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym)); | ||
| 112 | offset += sizeof(Elf64_Sym); | ||
| 113 | |||
| 114 | if (sym.name >= dynamic[DT_STRSZ]) { | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | |||
| 118 | std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]); | ||
| 119 | if (sym.value) { | ||
| 120 | exports[name] = load_base + sym.value; | ||
| 121 | symbols.emplace_back(std::move(name), load_base + sym.value); | ||
| 122 | } else { | ||
| 123 | symbols.emplace_back(std::move(name), 0); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | if (dynamic.find(DT_RELA) != dynamic.end()) { | ||
| 128 | WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], false, | ||
| 129 | load_base); | ||
| 130 | } | ||
| 131 | |||
| 132 | if (dynamic.find(DT_JMPREL) != dynamic.end()) { | ||
| 133 | WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ], true, | ||
| 134 | load_base); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | void Linker::ResolveImports() { | ||
| 139 | // Resolve imports | ||
| 140 | for (const auto& import : imports) { | ||
| 141 | const auto& search = exports.find(import.first); | ||
| 142 | if (search != exports.end()) { | ||
| 143 | Memory::Write64(import.second.ea, search->second + import.second.addend); | ||
| 144 | } | ||
| 145 | else { | ||
| 146 | LOG_ERROR(Loader, "Unresolved import: %s", import.first.c_str()); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Loader { | ||
| 12 | |||
| 13 | class Linker { | ||
| 14 | protected: | ||
| 15 | struct Symbol { | ||
| 16 | Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {} | ||
| 17 | std::string name; | ||
| 18 | u64 value; | ||
| 19 | }; | ||
| 20 | |||
| 21 | struct Import { | ||
| 22 | VAddr ea; | ||
| 23 | s64 addend; | ||
| 24 | }; | ||
| 25 | |||
| 26 | void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols, | ||
| 27 | u64 relocation_offset, u64 size, bool is_jump_relocation, | ||
| 28 | VAddr load_base); | ||
| 29 | void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base); | ||
| 30 | |||
| 31 | void ResolveImports(); | ||
| 32 | |||
| 33 | std::map<std::string, Import> imports; | ||
| 34 | std::map<std::string, VAddr> exports; | ||
| 35 | }; | ||
| 36 | |||
| 37 | } // 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 @@ | |||
| 10 | #include "core/loader/3dsx.h" | 10 | #include "core/loader/3dsx.h" |
| 11 | #include "core/loader/elf.h" | 11 | #include "core/loader/elf.h" |
| 12 | #include "core/loader/ncch.h" | 12 | #include "core/loader/ncch.h" |
| 13 | #include "core/loader/nro.h" | ||
| 14 | #include "core/loader/nso.h" | ||
| 13 | 15 | ||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 15 | 17 | ||
| @@ -32,6 +34,8 @@ FileType IdentifyFile(FileUtil::IOFile& file) { | |||
| 32 | CHECK_TYPE(THREEDSX) | 34 | CHECK_TYPE(THREEDSX) |
| 33 | CHECK_TYPE(ELF) | 35 | CHECK_TYPE(ELF) |
| 34 | CHECK_TYPE(NCCH) | 36 | CHECK_TYPE(NCCH) |
| 37 | CHECK_TYPE(NSO) | ||
| 38 | CHECK_TYPE(NRO) | ||
| 35 | 39 | ||
| 36 | #undef CHECK_TYPE | 40 | #undef CHECK_TYPE |
| 37 | 41 | ||
| @@ -115,6 +119,14 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileTyp | |||
| 115 | case FileType::CCI: | 119 | case FileType::CCI: |
| 116 | return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); | 120 | return std::make_unique<AppLoader_NCCH>(std::move(file), filepath); |
| 117 | 121 | ||
| 122 | // NX NSO file format. | ||
| 123 | case FileType::NSO: | ||
| 124 | return std::make_unique<AppLoader_NSO>(std::move(file), filepath); | ||
| 125 | |||
| 126 | // NX NRO file format. | ||
| 127 | case FileType::NRO: | ||
| 128 | return std::make_unique<AppLoader_NRO>(std::move(file), filepath); | ||
| 129 | |||
| 118 | default: | 130 | default: |
| 119 | return nullptr; | 131 | return nullptr; |
| 120 | } | 132 | } |
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 { | |||
| 34 | CIA, | 34 | CIA, |
| 35 | ELF, | 35 | ELF, |
| 36 | THREEDSX, // 3DSX | 36 | THREEDSX, // 3DSX |
| 37 | NSO, | ||
| 38 | NRO, | ||
| 37 | }; | 39 | }; |
| 38 | 40 | ||
| 39 | /** | 41 | /** |
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 | |||
| 118 | codeset->entrypoint = codeset->code.addr; | 118 | codeset->entrypoint = codeset->code.addr; |
| 119 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); | 119 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); |
| 120 | 120 | ||
| 121 | process = Kernel::Process::Create(std::move(codeset)); | 121 | process = Kernel::Process::Create("main"); |
| 122 | process->LoadModule(codeset, codeset->entrypoint); | ||
| 122 | 123 | ||
| 123 | // Attach a resource limit to the process based on the resource limit category | 124 | // Attach a resource limit to the process based on the resource limit category |
| 124 | process->resource_limit = | 125 | process->resource_limit = |
| @@ -138,7 +139,7 @@ ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& proces | |||
| 138 | 139 | ||
| 139 | s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority; | 140 | s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority; |
| 140 | u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size; | 141 | u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size; |
| 141 | process->Run(priority, stack_size); | 142 | process->Run(codeset->entrypoint, priority, stack_size); |
| 142 | return ResultStatus::Success; | 143 | return ResultStatus::Success; |
| 143 | } | 144 | } |
| 144 | return ResultStatus::Error; | 145 | 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <vector> | ||
| 6 | |||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/swap.h" | ||
| 9 | #include "core/hle/kernel/process.h" | ||
| 10 | #include "core/hle/kernel/resource_limit.h" | ||
| 11 | #include "core/loader/nro.h" | ||
| 12 | #include "core/memory.h" | ||
| 13 | |||
| 14 | namespace Loader { | ||
| 15 | |||
| 16 | struct NroSegmentHeader { | ||
| 17 | u32_le offset; | ||
| 18 | u32_le size; | ||
| 19 | }; | ||
| 20 | static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect size."); | ||
| 21 | |||
| 22 | struct NroHeader { | ||
| 23 | INSERT_PADDING_BYTES(0x4); | ||
| 24 | u32_le module_header_offset; | ||
| 25 | INSERT_PADDING_BYTES(0x8); | ||
| 26 | u32_le magic; | ||
| 27 | INSERT_PADDING_BYTES(0x4); | ||
| 28 | u32_le file_size; | ||
| 29 | INSERT_PADDING_BYTES(0x4); | ||
| 30 | std::array<NroSegmentHeader, 3> segments; // Text, RoData, Data (in that order) | ||
| 31 | u32_le bss_size; | ||
| 32 | INSERT_PADDING_BYTES(0x44); | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(NroHeader) == 0x80, "NroHeader has incorrect size."); | ||
| 35 | |||
| 36 | struct ModHeader { | ||
| 37 | u32_le magic; | ||
| 38 | u32_le dynamic_offset; | ||
| 39 | u32_le bss_start_offset; | ||
| 40 | u32_le bss_end_offset; | ||
| 41 | u32_le unwind_start_offset; | ||
| 42 | u32_le unwind_end_offset; | ||
| 43 | u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base | ||
| 44 | }; | ||
| 45 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | ||
| 46 | |||
| 47 | FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file) { | ||
| 48 | // Read NSO header | ||
| 49 | NroHeader nro_header{}; | ||
| 50 | file.Seek(0, SEEK_SET); | ||
| 51 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 52 | return FileType::Error; | ||
| 53 | } | ||
| 54 | if (nro_header.magic == MakeMagic('N', 'R', 'O', '0')) { | ||
| 55 | return FileType::NRO; | ||
| 56 | } | ||
| 57 | return FileType::Error; | ||
| 58 | } | ||
| 59 | |||
| 60 | static constexpr u32 PageAlignSize(u32 size) { | ||
| 61 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | ||
| 62 | } | ||
| 63 | |||
| 64 | static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NroSegmentHeader& header) { | ||
| 65 | std::vector<u8> data; | ||
| 66 | data.resize(header.size); | ||
| 67 | |||
| 68 | file.Seek(header.offset + sizeof(NroHeader), SEEK_SET); | ||
| 69 | size_t bytes_read{file.ReadBytes(data.data(), header.size)}; | ||
| 70 | if (header.size != PageAlignSize(static_cast<u32>(bytes_read))) { | ||
| 71 | LOG_CRITICAL(Loader, "Failed to read NRO segment bytes", header.size); | ||
| 72 | return {}; | ||
| 73 | } | ||
| 74 | |||
| 75 | return data; | ||
| 76 | } | ||
| 77 | |||
| 78 | bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | ||
| 79 | FileUtil::IOFile file(path, "rb"); | ||
| 80 | if (!file.IsOpen()) { | ||
| 81 | return {}; | ||
| 82 | } | ||
| 83 | |||
| 84 | // Read NSO header | ||
| 85 | NroHeader nro_header{}; | ||
| 86 | file.Seek(0, SEEK_SET); | ||
| 87 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 88 | return {}; | ||
| 89 | } | ||
| 90 | if (nro_header.magic != MakeMagic('N', 'R', 'O', '0')) { | ||
| 91 | return {}; | ||
| 92 | } | ||
| 93 | |||
| 94 | // Build program image | ||
| 95 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0); | ||
| 96 | std::vector<u8> program_image; | ||
| 97 | program_image.resize(PageAlignSize(nro_header.file_size + nro_header.bss_size)); | ||
| 98 | file.Seek(0, SEEK_SET); | ||
| 99 | file.ReadBytes(program_image.data(), nro_header.file_size); | ||
| 100 | |||
| 101 | for (int i = 0; i < nro_header.segments.size(); ++i) { | ||
| 102 | codeset->segments[i].addr = nro_header.segments[i].offset; | ||
| 103 | codeset->segments[i].offset = nro_header.segments[i].offset; | ||
| 104 | codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); | ||
| 105 | } | ||
| 106 | |||
| 107 | // Read MOD header | ||
| 108 | ModHeader mod_header{}; | ||
| 109 | u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist | ||
| 110 | std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset, | ||
| 111 | sizeof(ModHeader)); | ||
| 112 | const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')}; | ||
| 113 | if (has_mod_header) { | ||
| 114 | // Resize program image to include .bss section and page align each section | ||
| 115 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | ||
| 116 | codeset->data.size += bss_size; | ||
| 117 | } | ||
| 118 | program_image.resize(PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)); | ||
| 119 | |||
| 120 | // Relocate symbols if there was a proper MOD header - This must happen after the image has been | ||
| 121 | // loaded into memory | ||
| 122 | if (has_mod_header) { | ||
| 123 | Relocate(program_image, nro_header.module_header_offset + mod_header.dynamic_offset, | ||
| 124 | load_base); | ||
| 125 | } | ||
| 126 | |||
| 127 | // Load codeset for current process | ||
| 128 | codeset->name = path; | ||
| 129 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 130 | Kernel::g_current_process->LoadModule(codeset, load_base); | ||
| 131 | |||
| 132 | return true; | ||
| 133 | } | ||
| 134 | |||
| 135 | ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | ||
| 136 | if (is_loaded) { | ||
| 137 | return ResultStatus::ErrorAlreadyLoaded; | ||
| 138 | } | ||
| 139 | if (!file.IsOpen()) { | ||
| 140 | return ResultStatus::Error; | ||
| 141 | } | ||
| 142 | |||
| 143 | // Load and relocate "main" and "sdk" NSO | ||
| 144 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | ||
| 145 | process = Kernel::Process::Create("main"); | ||
| 146 | if (!LoadNro(filepath, base_addr)) { | ||
| 147 | return ResultStatus::ErrorInvalidFormat; | ||
| 148 | } | ||
| 149 | |||
| 150 | process->svc_access_mask.set(); | ||
| 151 | process->address_mappings = default_address_mappings; | ||
| 152 | process->resource_limit = | ||
| 153 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||
| 154 | process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); | ||
| 155 | |||
| 156 | ResolveImports(); | ||
| 157 | |||
| 158 | is_loaded = true; | ||
| 159 | return ResultStatus::Success; | ||
| 160 | } | ||
| 161 | |||
| 162 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/file_util.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/loader/linker.h" | ||
| 13 | #include "core/loader/loader.h" | ||
| 14 | |||
| 15 | namespace Loader { | ||
| 16 | |||
| 17 | /// Loads an NRO file | ||
| 18 | class AppLoader_NRO final : public AppLoader, Linker { | ||
| 19 | public: | ||
| 20 | AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath) | ||
| 21 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Returns the type of the file | ||
| 25 | * @param file FileUtil::IOFile open file | ||
| 26 | * @return FileType found, or FileType::Error if this loader doesn't know it | ||
| 27 | */ | ||
| 28 | static FileType IdentifyType(FileUtil::IOFile& file); | ||
| 29 | |||
| 30 | FileType GetFileType() override { | ||
| 31 | return IdentifyType(file); | ||
| 32 | } | ||
| 33 | |||
| 34 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||
| 35 | |||
| 36 | private: | ||
| 37 | bool LoadNro(const std::string& path, VAddr load_base); | ||
| 38 | |||
| 39 | std::string filepath; | ||
| 40 | }; | ||
| 41 | |||
| 42 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <vector> | ||
| 6 | #include <lz4.h> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/hle/kernel/process.h" | ||
| 11 | #include "core/hle/kernel/resource_limit.h" | ||
| 12 | #include "core/loader/nso.h" | ||
| 13 | #include "core/memory.h" | ||
| 14 | |||
| 15 | namespace Loader { | ||
| 16 | |||
| 17 | struct NsoSegmentHeader { | ||
| 18 | u32_le offset; | ||
| 19 | u32_le location; | ||
| 20 | u32_le size; | ||
| 21 | u32_le alignment; | ||
| 22 | }; | ||
| 23 | static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size."); | ||
| 24 | |||
| 25 | struct NsoHeader { | ||
| 26 | u32_le magic; | ||
| 27 | INSERT_PADDING_BYTES(0xc); | ||
| 28 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) | ||
| 29 | u32_le bss_size; | ||
| 30 | INSERT_PADDING_BYTES(0x1c); | ||
| 31 | std::array<u32_le, 3> segments_compressed_size; | ||
| 32 | }; | ||
| 33 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); | ||
| 34 | |||
| 35 | struct ModHeader { | ||
| 36 | u32_le magic; | ||
| 37 | u32_le dynamic_offset; | ||
| 38 | u32_le bss_start_offset; | ||
| 39 | u32_le bss_end_offset; | ||
| 40 | u32_le eh_frame_hdr_start_offset; | ||
| 41 | u32_le eh_frame_hdr_end_offset; | ||
| 42 | u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base | ||
| 43 | }; | ||
| 44 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | ||
| 45 | |||
| 46 | FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file) { | ||
| 47 | u32 magic = 0; | ||
| 48 | file.Seek(0, SEEK_SET); | ||
| 49 | if (1 != file.ReadArray<u32>(&magic, 1)) { | ||
| 50 | return FileType::Error; | ||
| 51 | } | ||
| 52 | |||
| 53 | if (MakeMagic('N', 'S', 'O', '0') == magic) { | ||
| 54 | return FileType::NSO; | ||
| 55 | } | ||
| 56 | |||
| 57 | return FileType::Error; | ||
| 58 | } | ||
| 59 | |||
| 60 | static std::vector<u8> ReadSegment(FileUtil::IOFile& file, const NsoSegmentHeader& header, | ||
| 61 | int compressed_size) { | ||
| 62 | std::vector<u8> compressed_data; | ||
| 63 | compressed_data.resize(compressed_size); | ||
| 64 | |||
| 65 | file.Seek(header.offset, SEEK_SET); | ||
| 66 | if (compressed_size != file.ReadBytes(compressed_data.data(), compressed_size)) { | ||
| 67 | LOG_CRITICAL(Loader, "Failed to read %d NSO LZ4 compressed bytes", compressed_size); | ||
| 68 | return {}; | ||
| 69 | } | ||
| 70 | |||
| 71 | std::vector<u8> uncompressed_data; | ||
| 72 | uncompressed_data.resize(header.size); | ||
| 73 | const int bytes_uncompressed = LZ4_decompress_safe( | ||
| 74 | reinterpret_cast<const char*>(compressed_data.data()), | ||
| 75 | reinterpret_cast<char*>(uncompressed_data.data()), compressed_size, header.size); | ||
| 76 | |||
| 77 | ASSERT_MSG(bytes_uncompressed == header.size && bytes_uncompressed == uncompressed_data.size(), | ||
| 78 | "%d != %d != %d", bytes_uncompressed, header.size, uncompressed_data.size()); | ||
| 79 | |||
| 80 | return uncompressed_data; | ||
| 81 | } | ||
| 82 | |||
| 83 | static constexpr u32 PageAlignSize(u32 size) { | ||
| 84 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | ||
| 85 | } | ||
| 86 | |||
| 87 | VAddr AppLoader_NSO::LoadNso(const std::string& path, VAddr load_base, bool relocate) { | ||
| 88 | FileUtil::IOFile file(path, "rb"); | ||
| 89 | if (!file.IsOpen()) { | ||
| 90 | return {}; | ||
| 91 | } | ||
| 92 | |||
| 93 | // Read NSO header | ||
| 94 | NsoHeader nso_header{}; | ||
| 95 | file.Seek(0, SEEK_SET); | ||
| 96 | if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) { | ||
| 97 | return {}; | ||
| 98 | } | ||
| 99 | if (nso_header.magic != MakeMagic('N', 'S', 'O', '0')) { | ||
| 100 | return {}; | ||
| 101 | } | ||
| 102 | |||
| 103 | // Build program image | ||
| 104 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("", 0); | ||
| 105 | std::vector<u8> program_image; | ||
| 106 | for (int i = 0; i < nso_header.segments.size(); ++i) { | ||
| 107 | std::vector<u8> data = | ||
| 108 | ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]); | ||
| 109 | program_image.resize(nso_header.segments[i].location); | ||
| 110 | program_image.insert(program_image.end(), data.begin(), data.end()); | ||
| 111 | codeset->segments[i].addr = nso_header.segments[i].location; | ||
| 112 | codeset->segments[i].offset = nso_header.segments[i].location; | ||
| 113 | codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | ||
| 114 | } | ||
| 115 | |||
| 116 | // MOD header pointer is at .text offset + 4 | ||
| 117 | u32 module_offset; | ||
| 118 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | ||
| 119 | |||
| 120 | // Read MOD header | ||
| 121 | ModHeader mod_header{}; | ||
| 122 | u32 bss_size{Memory::PAGE_SIZE}; // Default .bss to page size if MOD0 section doesn't exist | ||
| 123 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); | ||
| 124 | const bool has_mod_header{mod_header.magic == MakeMagic('M', 'O', 'D', '0')}; | ||
| 125 | if (has_mod_header) { | ||
| 126 | // Resize program image to include .bss section and page align each section | ||
| 127 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | ||
| 128 | codeset->data.size += bss_size; | ||
| 129 | } | ||
| 130 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | ||
| 131 | program_image.resize(image_size); | ||
| 132 | |||
| 133 | // Relocate symbols if there was a proper MOD header - This must happen after the image has been | ||
| 134 | // loaded into memory | ||
| 135 | if (has_mod_header && relocate) { | ||
| 136 | Relocate(program_image, module_offset + mod_header.dynamic_offset, load_base); | ||
| 137 | } | ||
| 138 | |||
| 139 | // Load codeset for current process | ||
| 140 | codeset->name = path; | ||
| 141 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 142 | Kernel::g_current_process->LoadModule(codeset, load_base); | ||
| 143 | |||
| 144 | return load_base + image_size; | ||
| 145 | } | ||
| 146 | |||
| 147 | ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | ||
| 148 | if (is_loaded) { | ||
| 149 | return ResultStatus::ErrorAlreadyLoaded; | ||
| 150 | } | ||
| 151 | if (!file.IsOpen()) { | ||
| 152 | return ResultStatus::Error; | ||
| 153 | } | ||
| 154 | |||
| 155 | // Load and relocate "rtld" NSO | ||
| 156 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | ||
| 157 | process = Kernel::Process::Create("main"); | ||
| 158 | VAddr next_base_addr{LoadNso(filepath, base_addr)}; | ||
| 159 | if (!next_base_addr) { | ||
| 160 | return ResultStatus::ErrorInvalidFormat; | ||
| 161 | } | ||
| 162 | |||
| 163 | // Load and relocate remaining submodules | ||
| 164 | for (const auto& module_name : {"main", "sdk", "subsdk0", "subsdk1"}) { | ||
| 165 | const std::string module_path = | ||
| 166 | filepath.substr(0, filepath.find_last_of("/\\")) + "/" + module_name; | ||
| 167 | next_base_addr = LoadNso(module_path, next_base_addr); | ||
| 168 | if (!next_base_addr) { | ||
| 169 | LOG_WARNING(Loader, "failed to find load module: %s", module_name); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | process->svc_access_mask.set(); | ||
| 174 | process->address_mappings = default_address_mappings; | ||
| 175 | process->resource_limit = | ||
| 176 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||
| 177 | process->Run(base_addr, 48, Kernel::DEFAULT_STACK_SIZE); | ||
| 178 | |||
| 179 | ResolveImports(); | ||
| 180 | |||
| 181 | is_loaded = true; | ||
| 182 | return ResultStatus::Success; | ||
| 183 | } | ||
| 184 | |||
| 185 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <map> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/file_util.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | #include "core/loader/linker.h" | ||
| 13 | #include "core/loader/loader.h" | ||
| 14 | |||
| 15 | namespace Loader { | ||
| 16 | |||
| 17 | /// Loads an NSO file | ||
| 18 | class AppLoader_NSO final : public AppLoader, Linker { | ||
| 19 | public: | ||
| 20 | AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath) | ||
| 21 | : AppLoader(std::move(file)), filepath(std::move(filepath)) { | ||
| 22 | } | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Returns the type of the file | ||
| 26 | * @param file FileUtil::IOFile open file | ||
| 27 | * @return FileType found, or FileType::Error if this loader doesn't know it | ||
| 28 | */ | ||
| 29 | static FileType IdentifyType(FileUtil::IOFile& file); | ||
| 30 | |||
| 31 | FileType GetFileType() override { | ||
| 32 | return IdentifyType(file); | ||
| 33 | } | ||
| 34 | |||
| 35 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||
| 36 | |||
| 37 | private: | ||
| 38 | VAddr LoadNso(const std::string& path, VAddr load_base, bool relocate = false); | ||
| 39 | |||
| 40 | std::string filepath; | ||
| 41 | }; | ||
| 42 | |||
| 43 | } // 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() { | |||
| 37 | return current_page_table; | 37 | return current_page_table; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { | 40 | static void MapPages(PageTable& page_table, VAddr base, u32 size, u8* memory, PageType type) { |
| 41 | LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, | 41 | LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, |
| 42 | (base + size) * PAGE_SIZE); | 42 | (base + size) * PAGE_SIZE); |
| 43 | 43 | ||
| 44 | RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, | 44 | RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, |
| 45 | FlushMode::FlushAndInvalidate); | 45 | FlushMode::FlushAndInvalidate); |
| 46 | 46 | ||
| 47 | u32 end = base + size; | 47 | VAddr end = base + size; |
| 48 | while (base != end) { | 48 | while (base != end) { |
| 49 | ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); | 49 | ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); |
| 50 | 50 | ||
| @@ -303,7 +303,7 @@ u8* GetPhysicalPointer(PAddr address) { | |||
| 303 | return nullptr; | 303 | return nullptr; |
| 304 | } | 304 | } |
| 305 | 305 | ||
| 306 | u32 offset_into_region = address - area->paddr_base; | 306 | u64 offset_into_region = address - area->paddr_base; |
| 307 | 307 | ||
| 308 | u8* target_pointer = nullptr; | 308 | u8* target_pointer = nullptr; |
| 309 | switch (area->paddr_base) { | 309 | switch (area->paddr_base) { |
| @@ -339,7 +339,7 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { | |||
| 339 | return; | 339 | return; |
| 340 | } | 340 | } |
| 341 | 341 | ||
| 342 | u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; | 342 | u64 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; |
| 343 | PAddr paddr = start; | 343 | PAddr paddr = start; |
| 344 | 344 | ||
| 345 | for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { | 345 | for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { |
| @@ -443,7 +443,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode) { | |||
| 443 | VAddr overlap_end = std::min(end, region_end); | 443 | VAddr overlap_end = std::min(end, region_end); |
| 444 | 444 | ||
| 445 | PAddr physical_start = TryVirtualToPhysicalAddress(overlap_start).value(); | 445 | PAddr physical_start = TryVirtualToPhysicalAddress(overlap_start).value(); |
| 446 | u32 overlap_size = overlap_end - overlap_start; | 446 | u32 overlap_size = static_cast<u32>(overlap_end - overlap_start); |
| 447 | 447 | ||
| 448 | auto* rasterizer = VideoCore::g_renderer->Rasterizer(); | 448 | auto* rasterizer = VideoCore::g_renderer->Rasterizer(); |
| 449 | switch (mode) { | 449 | 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 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <map> | ||
| 9 | #include <string> | 10 | #include <string> |
| 10 | #include <vector> | 11 | #include <vector> |
| 11 | #include <boost/optional.hpp> | 12 | #include <boost/optional.hpp> |
| @@ -22,10 +23,10 @@ namespace Memory { | |||
| 22 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can | 23 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can |
| 23 | * be mapped. | 24 | * be mapped. |
| 24 | */ | 25 | */ |
| 25 | const u32 PAGE_SIZE = 0x1000; | ||
| 26 | const u32 PAGE_MASK = PAGE_SIZE - 1; | ||
| 27 | const int PAGE_BITS = 12; | 26 | const int PAGE_BITS = 12; |
| 28 | const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); | 27 | const u64 PAGE_SIZE = 1 << PAGE_BITS; |
| 28 | const u64 PAGE_MASK = PAGE_SIZE - 1; | ||
| 29 | const size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (32 - PAGE_BITS); | ||
| 29 | 30 | ||
| 30 | enum class PageType { | 31 | enum class PageType { |
| 31 | /// Page is unmapped and should cause an access error. | 32 | /// Page is unmapped and should cause an access error. |
| @@ -124,8 +125,8 @@ enum : PAddr { | |||
| 124 | /// Virtual user-space memory regions | 125 | /// Virtual user-space memory regions |
| 125 | enum : VAddr { | 126 | enum : VAddr { |
| 126 | /// Where the application text, data and bss reside. | 127 | /// Where the application text, data and bss reside. |
| 127 | PROCESS_IMAGE_VADDR = 0x00100000, | 128 | PROCESS_IMAGE_VADDR = 0x08000000, |
| 128 | PROCESS_IMAGE_MAX_SIZE = 0x03F00000, | 129 | PROCESS_IMAGE_MAX_SIZE = 0x08000000, |
| 129 | PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, | 130 | PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, |
| 130 | 131 | ||
| 131 | /// Area where IPC buffers are mapped onto. | 132 | /// 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; | |||
| 15 | TestEnvironment::TestEnvironment(bool mutable_memory_) | 15 | TestEnvironment::TestEnvironment(bool mutable_memory_) |
| 16 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { | 16 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { |
| 17 | 17 | ||
| 18 | Kernel::g_current_process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); | 18 | Kernel::g_current_process = Kernel::Process::Create(""); |
| 19 | page_table = &Kernel::g_current_process->vm_manager.page_table; | 19 | page_table = &Kernel::g_current_process->vm_manager.page_table; |
| 20 | 20 | ||
| 21 | page_table->pointers.fill(nullptr); | 21 | 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 | |||
| 22 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | 22 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); |
| 23 | HLERequestContext context(std::move(session)); | 23 | HLERequestContext context(std::move(session)); |
| 24 | 24 | ||
| 25 | auto process = Process::Create(CodeSet::Create("", 0)); | 25 | auto process = Process::Create(""); |
| 26 | HandleTable handle_table; | 26 | HandleTable handle_table; |
| 27 | 27 | ||
| 28 | SECTION("works with empty cmdbuf") { | 28 | SECTION("works with empty cmdbuf") { |
| @@ -142,7 +142,7 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { | |||
| 142 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | 142 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); |
| 143 | HLERequestContext context(std::move(session)); | 143 | HLERequestContext context(std::move(session)); |
| 144 | 144 | ||
| 145 | auto process = Process::Create(CodeSet::Create("", 0)); | 145 | auto process = Process::Create(""); |
| 146 | HandleTable handle_table; | 146 | HandleTable handle_table; |
| 147 | auto* input = context.CommandBuffer(); | 147 | auto* input = context.CommandBuffer(); |
| 148 | u32_le output[IPC::COMMAND_BUFFER_LENGTH]; | 148 | 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 @@ | |||
| 9 | 9 | ||
| 10 | TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { | 10 | TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { |
| 11 | SECTION("these regions should not be mapped on an empty process") { | 11 | SECTION("these regions should not be mapped on an empty process") { |
| 12 | auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); | 12 | auto process = Kernel::Process::Create(""); |
| 13 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); | 13 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false); |
| 14 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); | 14 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false); |
| 15 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); | 15 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false); |
| @@ -20,14 +20,14 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { | |||
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") { | 22 | SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") { |
| 23 | auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); | 23 | auto process = Kernel::Process::Create(""); |
| 24 | Kernel::MapSharedPages(process->vm_manager); | 24 | Kernel::MapSharedPages(process->vm_manager); |
| 25 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); | 25 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true); |
| 26 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); | 26 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | SECTION("special regions should be valid after mapping them") { | 29 | SECTION("special regions should be valid after mapping them") { |
| 30 | auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); | 30 | auto process = Kernel::Process::Create(""); |
| 31 | SECTION("VRAM") { | 31 | SECTION("VRAM") { |
| 32 | Kernel::HandleSpecialMapping(process->vm_manager, | 32 | Kernel::HandleSpecialMapping(process->vm_manager, |
| 33 | {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false}); | 33 | {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false}); |
| @@ -48,7 +48,7 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | SECTION("Unmapping a VAddr should make it invalid") { | 50 | SECTION("Unmapping a VAddr should make it invalid") { |
| 51 | auto process = Kernel::Process::Create(Kernel::CodeSet::Create("", 0)); | 51 | auto process = Kernel::Process::Create(""); |
| 52 | Kernel::MapSharedPages(process->vm_manager); | 52 | Kernel::MapSharedPages(process->vm_manager); |
| 53 | process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE); | 53 | process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE); |
| 54 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); | 54 | CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false); |