// Copyright 2018 plutoo #include #include "runtime/env.h" #include "runtime/hosversion.h" #include "services/sm.h" #include "services/applet.h" #include "services/acc.h" #include "runtime/diag.h" void NORETURN __nx_exit(Result rc, LoaderReturnFn retaddr); static bool g_isNso = false; static const char* g_loaderInfo = NULL; static u64 g_loaderInfoSize = 0; static Handle g_mainThreadHandle = INVALID_HANDLE; static LoaderReturnFn g_loaderRetAddr = NULL; static void* g_overrideHeapAddr = NULL; static u64 g_overrideHeapSize = 0; static void* g_overrideArgv = NULL; static u64 g_syscallHints[3]; static Handle g_processHandle = INVALID_HANDLE; static char* g_nextLoadPath = NULL; static char* g_nextLoadArgv = NULL; static Result g_lastLoadResult = 0; static bool g_hasRandomSeed = false; static u64 g_randomSeed[2] = { 0, 0 }; static AccountUid* g_userIdStorage = NULL; extern __attribute__((weak)) u32 __nx_applet_type; void envSetup(void* ctx, Handle main_thread, LoaderReturnFn saved_lr) { // Detect and set up NSO environment. if (ctx == NULL) { // Under NSO, we use svcExitProcess as the return address and hint all syscalls as available. g_isNso = true; g_mainThreadHandle = main_thread; g_loaderRetAddr = (LoaderReturnFn) &svcExitProcess; g_syscallHints[0] = g_syscallHints[1] = g_syscallHints[2] = UINT64_MAX; return; } // Save the loader return address. g_loaderRetAddr = saved_lr; // Parse homebrew ABI config entries. ConfigEntry* ent = ctx; while (ent->Key != EntryType_EndOfList) { switch (ent->Key) { case EntryType_MainThreadHandle: g_mainThreadHandle = ent->Value[0]; break; case EntryType_NextLoadPath: g_nextLoadPath = (char*) ent->Value[0]; g_nextLoadArgv = (char*) ent->Value[1]; break; case EntryType_OverrideHeap: g_overrideHeapAddr = (void*) ent->Value[0]; g_overrideHeapSize = ent->Value[1]; break; case EntryType_OverrideService: smAddOverrideHandle(smServiceNameFromU64(ent->Value[0]), ent->Value[1]); break; case EntryType_Argv: g_overrideArgv = (void*) ent->Value[1]; break; case EntryType_SyscallAvailableHint: g_syscallHints[0] = ent->Value[0]; g_syscallHints[1] = ent->Value[1]; break; case EntryType_AppletType: __nx_applet_type = ent->Value[0]; if ((ent->Value[1] & EnvAppletFlags_ApplicationOverride) && __nx_applet_type == AppletType_SystemApplication) __nx_applet_type = AppletType_Application; break; case EntryType_ProcessHandle: g_processHandle = ent->Value[0]; break; case EntryType_LastLoadResult: g_lastLoadResult = ent->Value[0]; break; case EntryType_RandomSeed: g_hasRandomSeed = true; g_randomSeed[0] = ent->Value[0]; g_randomSeed[1] = ent->Value[1]; break; case EntryType_UserIdStorage: g_userIdStorage = (AccountUid*)(uintptr_t)ent->Value[0]; break; case EntryType_HosVersion: { u32 version = ent->Value[0]; if (ent->Value[1] == 0x41544d4f53504852UL) { // 'ATMOSPHR' version |= BIT(31); } hosversionSet(version); break; } case EntryType_SyscallAvailableHint2: g_syscallHints[2] = ent->Value[0]; break; default: if (ent->Flags & EntryFlag_IsMandatory) { // Encountered unknown but mandatory key, bail back to loader. __nx_exit(MAKERESULT(Module_HomebrewAbi, 100 + ent->Key), g_loaderRetAddr); } break; } ent++; } g_loaderInfoSize = ent->Value[1]; if (g_loaderInfoSize) { g_loaderInfo = (const char*)(uintptr_t)ent->Value[0]; } } const char* envGetLoaderInfo(void) { return g_loaderInfo; } u64 envGetLoaderInfoSize(void) { return g_loaderInfoSize; } Handle envGetMainThreadHandle(void) { if (g_mainThreadHandle != INVALID_HANDLE) { return g_mainThreadHandle; } diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_HandleTooEarly)); } bool envIsNso(void) { return g_isNso; } bool envHasHeapOverride(void) { return g_overrideHeapAddr != NULL; } void* envGetHeapOverrideAddr(void) { return g_overrideHeapAddr; } u64 envGetHeapOverrideSize(void) { return g_overrideHeapSize; } bool envHasArgv(void) { return g_overrideArgv != NULL; } void* envGetArgv(void) { return g_overrideArgv; } bool envIsSyscallHinted(unsigned svc) { if (svc >= 0xC0) return false; return !!(g_syscallHints[svc/64] & (1ull << (svc%64))); } Handle envGetOwnProcessHandle(void) { return g_processHandle; } LoaderReturnFn envGetExitFuncPtr(void) { return g_loaderRetAddr; } void envSetExitFuncPtr(LoaderReturnFn addr) { g_loaderRetAddr = addr; } Result envSetNextLoad(const char* path, const char* argv) { if (g_nextLoadPath == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); strcpy(g_nextLoadPath, path); if (g_nextLoadArgv != NULL) { if (argv == NULL) g_nextLoadArgv[0] = '\0'; else strcpy(g_nextLoadArgv, argv); } return 0; } bool envHasNextLoad(void) { return g_nextLoadPath != NULL; } Result envGetLastLoadResult(void) { return g_lastLoadResult; } bool envHasRandomSeed(void) { return g_hasRandomSeed; } void envGetRandomSeed(u64 out[2]) { out[0] = g_randomSeed[0]; out[1] = g_randomSeed[1]; } AccountUid* envGetUserIdStorage(void) { return g_userIdStorage; }