From a9555bb5b541ebc64839065ed0a9cae6774e52c1 Mon Sep 17 00:00:00 2001 From: plutoo Date: Sun, 8 Oct 2017 15:11:59 +0200 Subject: [PATCH] Implement virtual memory allocator --- nx/include/switch/result.h | 2 + nx/source/kernel/virtmem.c | 122 ++++++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index ea7b475f..daaaedf6 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -22,3 +22,5 @@ #define LIBNX_BADRELOC 1 #define LIBNX_OUTOFMEM 2 #define LIBNX_ALREADYMAPPED 3 +#define LIBNX_BADGETINFO 4 +#define LIBNX_BADQUERYMEMORY 5 diff --git a/nx/source/kernel/virtmem.c b/nx/source/kernel/virtmem.c index 426eef03..4bcc0138 100644 --- a/nx/source/kernel/virtmem.c +++ b/nx/source/kernel/virtmem.c @@ -1,21 +1,129 @@ #include -static u8* g_VirtBase; +typedef struct { + u64 start; + u64 end; +} VirtualRegion; + +enum { + REGION_MAP =0, + REGION_HEAP=1, + REGION_UNK =2, + REGION_MAX +}; + +static VirtualRegion g_AddressSpace; +static VirtualRegion g_Region[REGION_MAX]; +static u64 g_CurrentAddr; + +static Result _GetRegionFromInfo(VirtualRegion* r, u64 id0_addr, u32 id0_sz) { + u64 base; + Result rc = svcGetInfo(&base, id0_addr, CUR_PROCESS_HANDLE, 0); + + if (R_SUCCEEDED(rc)) { + u64 size; + rc = svcGetInfo(&size, id0_sz, CUR_PROCESS_HANDLE, 0); + + if (R_SUCCEEDED(rc)) { + r->start = base; + r->end = base + size; + } + } + + return rc; +} + +static inline bool _InRegion(VirtualRegion* r, u64 addr) { + return (addr >= r->start) && (addr < (r->end-1)); +} void virtmemSetup() { - // TODO: Implement this. + if (R_FAILED(_GetRegionFromInfo(&g_AddressSpace, 12, 13))) { + // Default values in case we're running on 1.0.0 + // Assumes 36-bit address space + g_AddressSpace.start = 0x8000000ull; + g_AddressSpace.end = 0x1000000000ull; + } + + if (R_FAILED(_GetRegionFromInfo(&g_Region[REGION_MAP], 2, 3))) { + fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADGETINFO)); + } + + if (R_FAILED(_GetRegionFromInfo(&g_Region[REGION_HEAP], 4, 5))) { + fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADGETINFO)); + } + + // Failure is OK, happens on 1.0.0 + // In that case, g_UnkRegion will default to (0, 0). + _GetRegionFromInfo(&g_Region[REGION_UNK], 14, 15); } void* virtmemReserve(size_t size) { - void* ret = g_VirtBase; + Result rc; + MemInfo meminfo; + u32 pageinfo; + size_t i; size = (size + 0xFFF) &~ 0xFFF; - g_VirtBase += size + 0x1000; - return ret; + u64 addr = g_CurrentAddr; + while (1) + { + // Add a guard page. + addr += 0x1000; + + // If we go outside address space, let's go back to start. + if (!_InRegion(&g_AddressSpace, addr)) { + addr = g_AddressSpace.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; + } + + // Check if we end up in a reserved region. + for(i=0; i