diff --git a/nx/include/switch.h b/nx/include/switch.h index cb00e0b2..7825f5d5 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -20,6 +20,7 @@ extern "C" { #include #include #include +#include #include #include diff --git a/nx/include/switch/kernel/jit.h b/nx/include/switch/kernel/jit.h new file mode 100644 index 00000000..879206a0 --- /dev/null +++ b/nx/include/switch/kernel/jit.h @@ -0,0 +1,23 @@ +// Copyright 2018 plutoo + +typedef enum { + JitType_CodeMemory, + JitType_JitMemory +} JitType; + +typedef struct { + JitType type; + size_t size; + void* src_addr; + void* rx_addr; + void* rw_addr; + Handle handle; +} Jit; + +Result jitCreate(Jit* j, size_t size); +Result jitTransitionToWritable(Jit* j); +Result jitTransitionToExecutable(Jit* j); +Result jitClose(Jit* j); + +void* jitGetRwAddr(Jit* j); +void* jitGetRxAddr(Jit* j); diff --git a/nx/include/switch/kernel/svc.h b/nx/include/switch/kernel/svc.h index de93af0c..93ee74a1 100644 --- a/nx/include/switch/kernel/svc.h +++ b/nx/include/switch/kernel/svc.h @@ -27,6 +27,13 @@ typedef struct { u64 X[8]; } __attribute__((packed)) SecmonArgs; +typedef enum { + JitMapOperation_MapOwner=0, + JitMapOperation_MapSlave=1, + JitMapOperation_UnmapOwner=2, + JitMapOperation_UnmapSlave=3 +} JitMapOperation; + Result svcSetHeapSize(void** out_addr, u64 size); Result svcMapMemory(void* dst_addr, void* src_addr, u64 size); Result svcUnmapMemory(void* dst_addr, void* src_addr, u64 size); @@ -57,6 +64,8 @@ Result svcSetThreadActivity(Handle thread, bool paused); Result svcCreateSession(Handle *server_handle, Handle *client_handle, u32 unk0, u64 unk1);//unk* are normally 0? Result svcAcceptSession(Handle *session_handle, Handle port_handle); Result svcReplyAndReceive(s32* index, const Handle* handles, s32 handleCount, Handle replyTarget, u64 timeout); +Result svcCreateJitMemory(Handle* jit_handle, void* src_addr, u64 size); +Result svcMapJitMemory(Handle jit_handle, JitMapOperation op, void* dst_addr, u64 size, u64 perm); Result svcCreateSharedMemory(Handle* out, size_t size, u32 local_perm, u32 other_perm); Result svcMapTransferMemory(Handle tmem_handle, void* addr, size_t size, u32 perm); Result svcUnmapTransferMemory(Handle tmem_handle, void* addr, size_t size); diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index 1c66669e..25ad146d 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -50,4 +50,6 @@ enum { LIBNX_INITFAIL_HID, LIBNX_INITFAIL_FS, LIBNX_BADGETINFO_RNG, + LIBNX_JITUNAVAILABLE, + LIBNX_WEIRDKERNEL }; diff --git a/nx/include/switch/runtime/env.h b/nx/include/switch/runtime/env.h index 742e1911..fedb8b6a 100644 --- a/nx/include/switch/runtime/env.h +++ b/nx/include/switch/runtime/env.h @@ -42,4 +42,6 @@ void* envGetArgv(void); bool envIsSyscallHinted(u8 svc); +Handle envGetOwnProcessHandle(void); + LoaderReturnFn envGetExitFuncPtr(void); diff --git a/nx/include/switch/types.h b/nx/include/switch/types.h index b23758e0..b9e14833 100644 --- a/nx/include/switch/types.h +++ b/nx/include/switch/types.h @@ -50,6 +50,7 @@ typedef enum { PERM_W = 2, PERM_X = 4, PERM_RW = PERM_R | PERM_W, + PERM_RX = PERM_R | PERM_X, PERM_DONTCARE = 0x10000000 } Permission; diff --git a/nx/source/kernel/jit.c b/nx/source/kernel/jit.c new file mode 100644 index 00000000..8a15e1cb --- /dev/null +++ b/nx/source/kernel/jit.c @@ -0,0 +1,161 @@ +// Copyright 2018 plutoo +#include +#include + +Result jitCreate(Jit* j, size_t size) +{ + JitType type; + + // Use new jit primitive introduced in 4.0.0, if available. + if (kernelAbove400() && envIsSyscallHinted(0x4B) && envIsSyscallHinted(0x4C)) { + type = JitType_JitMemory; + } + // Fall back to MapProcessCodeMemory if available. + else if (envIsSyscallHinted(0x73) && envIsSyscallHinted(0x77) && envIsSyscallHinted(0x78)) { + type = JitType_CodeMemory; + } + else { + // Jit is unavailable. :( + return MAKERESULT(MODULE_LIBNX, LIBNX_JITUNAVAILABLE); + } + + size = (size + 0xFFF) &~ 0xFFF; + + void* src_addr = memalign(size, 0x1000); + + if (src_addr == NULL) + return MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); + + j->type = type; + j->size = size; + j->src_addr = src_addr; + j->rx_addr = virtmemReserve(j->size); + j->handle = INVALID_HANDLE; + + Result rc = 0; + + switch (j->type) + { + case JitType_CodeMemory: + j->rw_addr = j->src_addr; + break; + + case JitType_JitMemory: + j->rw_addr = virtmemReserve(j->size); + + rc = svcCreateJitMemory(&j->handle, j->src_addr, j->size); + if (R_SUCCEEDED(rc)) + { + rc = svcMapJitMemory(j->handle, JitMapOperation_MapOwner, j->rw_addr, j->size, PERM_RW); + if (R_SUCCEEDED(rc)) + { + rc = svcMapJitMemory(j->handle, JitMapOperation_MapSlave, j->rx_addr, j->size, PERM_RX); + + if (R_FAILED(rc)) { + svcMapJitMemory(j->handle, JitMapOperation_UnmapOwner, j->rw_addr, j->size, 0); + } + } + + if (R_FAILED(rc)) { + svcCloseHandle(j->handle); + j->handle = INVALID_HANDLE; + } + } + + if (R_FAILED(rc)) { + virtmemFree(j->rw_addr, j->size); + j->rw_addr = NULL; + } + + break; + } + + if (R_FAILED(rc)) { + virtmemFree(j->rx_addr, j->size); + free(j->src_addr); + j->src_addr = NULL; + } + + return rc; +} + +Result jitTransitionToWritable(Jit* j) +{ + Result rc = 0; + + switch (j->type) { + case JitType_CodeMemory: + rc = svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64) j->rx_addr, (u64) j->src_addr, j->size); + break; + + case JitType_JitMemory: + // No need to do anything. + break; + } + + return rc; +} + +Result jitTransitionToExecutable(Jit* j) +{ + Result rc = 0; + + switch (j->type) { + case JitType_CodeMemory: + rc = svcMapProcessCodeMemory(envGetOwnProcessHandle(), (u64) j->rx_addr, (u64) j->src_addr, j->size); + break; + + case JitType_JitMemory: + // todo: Clean dcache, invalidate icache. + break; + } + + return rc; +} + +Result jitClose(Jit* j) +{ + Result rc = 0; + + switch (j->type) + { + case JitType_CodeMemory: + rc = jitTransitionToWritable(j); + + if (R_SUCCEEDED(rc)) { + virtmemFree(j->rx_addr, j->size); + } + break; + + case JitType_JitMemory: + rc = svcMapJitMemory(j->handle, JitMapOperation_UnmapOwner, j->rw_addr, j->size, 0); + + if (R_SUCCEEDED(rc)) { + virtmemFree(j->rw_addr, j->size); + + rc = svcMapJitMemory(j->handle, JitMapOperation_UnmapSlave, j->rx_addr, j->size, 0); + + if (R_SUCCEEDED(rc)) { + virtmemFree(j->rw_addr, j->size); + svcCloseHandle(j->handle); + } + } + break; + } + + if (R_SUCCEEDED(rc)) { + if (j->src_addr != NULL) { + free(j->src_addr); + j->src_addr = NULL; + } + } + return 0; +} + +void* jitGetRwAddr(Jit* j) { + return j->rw_addr; +} + +void* jitGetRxAddr(Jit* j) { + return j->rw_addr; +} diff --git a/nx/source/kernel/shmem.c b/nx/source/kernel/shmem.c index 8145b411..db3da6d0 100644 --- a/nx/source/kernel/shmem.c +++ b/nx/source/kernel/shmem.c @@ -55,6 +55,7 @@ Result shmemUnmap(SharedMemory* s) rc = svcUnmapSharedMemory(s->handle, s->map_addr, s->size); if (R_SUCCEEDED(rc)) { + virtmemFree(s->map_addr, s->size); s->map_addr = NULL; } diff --git a/nx/source/kernel/svc.s b/nx/source/kernel/svc.s index 33c9abf6..86a6f781 100644 --- a/nx/source/kernel/svc.s +++ b/nx/source/kernel/svc.s @@ -197,6 +197,20 @@ SVC_BEGIN svcReplyAndReceive ret SVC_END +SVC_BEGIN svcCreateJitMemory + str x0, [sp, #-16]! + svc 0x4B + ldr x2, [sp] + str w1, [x2] + add sp, sp, #16 + ret +SVC_END + +SVC_BEGIN svcMapJitMemory + svc 0x4C + ret +SVC_END + SVC_BEGIN svcCreateSharedMemory str x0, [sp, #-16]! svc 0x50 diff --git a/nx/source/kernel/tmem.c b/nx/source/kernel/tmem.c index d9db1f44..c844cc2f 100644 --- a/nx/source/kernel/tmem.c +++ b/nx/source/kernel/tmem.c @@ -63,6 +63,7 @@ Result tmemUnmap(TransferMemory* t) rc = svcUnmapTransferMemory(t->handle, t->map_addr, t->size); if (R_SUCCEEDED(rc)) { + virtmemFree(t->map_addr, t->size); t->map_addr = NULL; } diff --git a/nx/source/kernel/virtmem.c b/nx/source/kernel/virtmem.c index 03dbbda9..d397d5a8 100644 --- a/nx/source/kernel/virtmem.c +++ b/nx/source/kernel/virtmem.c @@ -41,10 +41,27 @@ static inline bool _InRegion(VirtualRegion* r, u64 addr) { void virtmemSetup(void) { if (R_FAILED(_GetRegionFromInfo(&g_AddressSpace, 12, 13))) { - // Default values in case we're running on 1.0.0 - // Assumes 32-bit address space - g_AddressSpace.start = 0ull; - g_AddressSpace.end = 0x100000000ull; + // 1.0.0 doesn't expose address space size so we have to do this dirty hack to detect it. + // Forgive me. + + Result rc = svcUnmapMemory((void*) 0xFFFFFFFFFFFFE000ULL, (void*) 0xFFFFFE000ull, 0x1000); + + if (rc == 0xD401) { + // Invalid src-address error means that a valid 36-bit address was rejected. + // Thus we are 32-bit. + g_AddressSpace.start = 0x200000ull; + g_AddressSpace.end = 0x100000000ull; + } + else if (rc == 0xDC01) { + // Invalid dst-address error means our 36-bit src-address was valid. + // Thus we are 36-bit. + g_AddressSpace.start = 0x8000000ull; + g_AddressSpace.end = 0x1000000000ull; + } + else { + // Wat. + fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_WEIRDKERNEL)); + } } if (R_FAILED(_GetRegionFromInfo(&g_Region[REGION_STACK], 2, 3))) {