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