diff --git a/nx/include/switch.h b/nx/include/switch.h index 8c7f4988..0019a082 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -16,6 +16,7 @@ extern "C" { #include #include +#include #include #include diff --git a/nx/include/switch/kernel/thread.h b/nx/include/switch/kernel/thread.h new file mode 100644 index 00000000..1b379166 --- /dev/null +++ b/nx/include/switch/kernel/thread.h @@ -0,0 +1,16 @@ +typedef struct { + Handle handle; + ThreadFunc entry; + void* arg; + void* stack_mem; + void* stack_mirror; + size_t stack_sz; +} Thread; + +Result threadCreate( + Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio, + int cpuid); + +Result threadStart(Thread* t); +Result threadWaitForExit(Thread* t); +Result threadClose(Thread* t); diff --git a/nx/include/switch/kernel/virtmem.h b/nx/include/switch/kernel/virtmem.h index b7745e33..60aa722e 100644 --- a/nx/include/switch/kernel/virtmem.h +++ b/nx/include/switch/kernel/virtmem.h @@ -1,2 +1,5 @@ void* virtmemReserve(size_t size); void virtmemFree(void* addr, size_t size); + +void* virtmemReserveMap(size_t size); +void virtmemFreeMap(void* addr, size_t size); diff --git a/nx/include/switch/svc.h b/nx/include/switch/svc.h index a8439775..c2f651c0 100644 --- a/nx/include/switch/svc.h +++ b/nx/include/switch/svc.h @@ -41,8 +41,13 @@ typedef struct { } __attribute__((packed)) SecmonArgs; 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); Result svcQueryMemory(MemInfo* meminfo_ptr, u32 *pageinfo, u64 addr); void NORETURN svcExitProcess(); +Result svcCreateThread(Handle* out, void* entry, void* arg, void* stack_top, int prio, int cpuid); +Result svcStartThread(Handle handle); +void NORETURN svcExitThread(); Result svcSleepThread(u64 nano); Result svcCloseHandle(Handle handle); Result svcCreateTransferMemory(Handle* out, void* addr, size_t size, u32 perm); diff --git a/nx/source/kernel/svc.s b/nx/source/kernel/svc.s index a0bd10e7..1620c1dc 100644 --- a/nx/source/kernel/svc.s +++ b/nx/source/kernel/svc.s @@ -19,6 +19,16 @@ SVC_BEGIN svcSetHeapSize ret SVC_END +SVC_BEGIN svcMapMemory + svc 0x4 + ret +SVC_END + +SVC_BEGIN svcUnmapMemory + svc 0x5 + ret +SVC_END + SVC_BEGIN svcQueryMemory str x1, [sp, #-16]! svc 0x6 @@ -32,6 +42,25 @@ SVC_BEGIN svcExitProcess ret SVC_END +SVC_BEGIN svcCreateThread + str x0, [sp, #-16]! + svc 0x8 + ldr x2, [sp] + str w1, [x2] + add sp, sp, #16 + ret +SVC_END + +SVC_BEGIN svcStartThread + svc 0x9 + ret +SVC_END + +SVC_BEGIN svcExitThread + svc 0xA + ret +SVC_END + SVC_BEGIN svcSleepThread svc 0xB ret diff --git a/nx/source/kernel/thread.c b/nx/source/kernel/thread.c new file mode 100644 index 00000000..a8e33647 --- /dev/null +++ b/nx/source/kernel/thread.c @@ -0,0 +1,76 @@ +// Copyright 2017 plutoo +#include + +static void _EntryWrap(Thread* t) { + t->entry(t->arg); + svcExitThread(); +} + +Result threadCreate( + Thread* t, ThreadFunc entry, void* arg, size_t stack_sz, int prio, + int cpuid) +{ + Result rc = 0; + void* stack = heapAllocPages(stack_sz); + + if (stack == NULL) { + rc = MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); + } + else { + // todo: svcMapMemory returns 0xDC01 + void* stack_mirror = stack;//virtmemReserveMap(stack_sz); + //rc = svcMapMemory(stack_mirror, stack, stack_sz); + + if (R_SUCCEEDED(rc)) + { + u64 stack_top = ((u64)stack_mirror) + t->stack_sz; + Handle handle; + + rc = svcCreateThread( + &handle, (ThreadFunc) &_EntryWrap, (void*) t, (void*) stack_top, + prio, cpuid); + + if (R_SUCCEEDED(rc)) + { + t->handle = handle; + t->entry = entry; + t->arg = arg; + t->stack_mem = stack; + t->stack_mirror = stack_mirror; + t->stack_sz = stack_sz; + } + + if (R_FAILED(rc)) { + svcUnmapMemory(stack_mirror, stack, stack_sz); + } + } + + if (R_FAILED(rc)) { + virtmemFreeMap(stack_mirror, stack_sz); + //heapFree(stack); + } + } + + return rc; +} + +Result threadStart(Thread* t) { + return svcStartThread(t->handle); +} + +Result threadWaitForExit(Thread* t) { + Handle handle = t->handle; + s32 idx = 0; + return svcWaitSynchronization(&idx, &handle, 1, -1); +} + +Result threadClose(Thread* t) { + Result rc; + + rc = svcUnmapMemory(t->stack_mirror, t->stack_mem, t->stack_sz); + virtmemFreeMap(t->stack_mirror, t->stack_sz); + //heapFree(t->stack); + svcCloseHandle(t->handle); + + return rc; +} diff --git a/nx/source/kernel/virtmem.c b/nx/source/kernel/virtmem.c index 4bcc0138..22361c8a 100644 --- a/nx/source/kernel/virtmem.c +++ b/nx/source/kernel/virtmem.c @@ -15,6 +15,7 @@ enum { static VirtualRegion g_AddressSpace; static VirtualRegion g_Region[REGION_MAX]; static u64 g_CurrentAddr; +static u64 g_CurrentMapAddr; static Result _GetRegionFromInfo(VirtualRegion* r, u64 id0_addr, u32 id0_sz) { u64 base; @@ -127,3 +128,53 @@ void virtmemFree(void* addr, size_t size) { IGNORE_ARG(addr); IGNORE_ARG(size); } + +void* virtmemReserveMap(size_t size) +{ + Result rc; + MemInfo meminfo; + u32 pageinfo; + + size = (size + 0xFFF) &~ 0xFFF; + + u64 addr = g_CurrentMapAddr; + while (1) + { + // Add a guard page. + addr += 0x1000; + + // Make sure we stay inside the reserved map region. + if (!_InRegion(&g_Region[REGION_MAP], addr)) { + addr = g_Region[REGION_MAP].start; + } + + // Query information about address. + rc = svcQueryMemory(&meminfo, &pageinfo, addr); + + if (R_FAILED(rc)) { + fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADQUERYMEMORY)); + } + + if (meminfo.memorytype != 0) { + // Address is already taken, let's move past it. + addr = meminfo.base_addr + meminfo.size; + continue; + } + + if (size > meminfo.size) { + // We can't fit in this region, let's move past it. + addr = meminfo.base_addr + meminfo.size; + continue; + } + + break; + } + + g_CurrentMapAddr = addr + size; + return (void*) addr; +} + +void virtmemFreeMap(void* addr, size_t size) { + IGNORE_ARG(addr); + IGNORE_ARG(size); +}