#include "types.h" #include "result.h" #include "services/fatal.h" #include "kernel/mutex.h" #include "kernel/svc.h" #include "kernel/virtmem.h" typedef struct { u64 start; u64 end; } VirtualRegion; enum { REGION_STACK=0, REGION_HEAP=1, REGION_LEGACY_ALIAS=2, REGION_MAX }; static VirtualRegion g_AddressSpace; static VirtualRegion g_Region[REGION_MAX]; static u64 g_CurrentAddr; static u64 g_CurrentMapAddr; static Mutex g_VirtMemMutex; 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); } void virtmemSetup(void) { if (R_FAILED(_GetRegionFromInfo(&g_AddressSpace, InfoType_AslrRegionAddress, InfoType_AslrRegionSize))) { // [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; g_Region[REGION_STACK].start = 0x200000ull; g_Region[REGION_STACK].end = 0x40000000ull; } 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; g_Region[REGION_STACK].start = 0x8000000ull; g_Region[REGION_STACK].end = 0x80000000ull; } else { // Wat. fatalThrow(MAKERESULT(Module_Libnx, LibnxError_WeirdKernel)); } } else { if (R_FAILED(_GetRegionFromInfo(&g_Region[REGION_STACK], InfoType_StackRegionAddress, InfoType_StackRegionSize))) { fatalThrow(MAKERESULT(Module_Libnx, LibnxError_BadGetInfo_Stack)); } } if (R_FAILED(_GetRegionFromInfo(&g_Region[REGION_HEAP], InfoType_HeapRegionAddress, InfoType_HeapRegionSize))) { fatalThrow(MAKERESULT(Module_Libnx, LibnxError_BadGetInfo_Heap)); } _GetRegionFromInfo(&g_Region[REGION_LEGACY_ALIAS], InfoType_AliasRegionAddress, InfoType_AliasRegionSize); } void* virtmemReserve(size_t size) { Result rc; MemoryInfo meminfo; u32 pageinfo; size_t i; size = (size + 0xFFF) &~ 0xFFF; mutexLock(&g_VirtMemMutex); 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)) { fatalThrow(MAKERESULT(Module_Libnx, LibnxError_BadQueryMemory)); } if (meminfo.type != 0) { // Address is already taken, let's move past it. addr = meminfo.addr + meminfo.size; continue; } if (addr + size > meminfo.addr + meminfo.size) { // We can't fit in this region, let's move past it. addr = meminfo.addr + meminfo.size; continue; } // Check if we end up in a reserved region. for(i=0; i meminfo.addr + meminfo.size) { // We can't fit in this region, let's move past it. addr = meminfo.addr + meminfo.size; continue; } break; } g_CurrentMapAddr = addr + size; mutexUnlock(&g_VirtMemMutex); return (void*) addr; } void virtmemFreeStack(void* addr, size_t size) { IGNORE_ARG(addr); IGNORE_ARG(size); }