diff --git a/AGENTS.md b/AGENTS.md index 71413fd163..5168058ea3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -177,11 +177,6 @@ small examples to copy from). A module is a `const HLEFunction []` table o build-tested here, so double check them by hand against how an existing neighboring file (e.g. `sceVaudio.cpp`) is listed in each. Note: New files in the unittest project have to be updated in the unittest part in android/jni/Android.mk. -## Adding HLE functions - -Always add new functions at the *end* of an array of const HLEFunction. We store the function index in savestates -(in resolved syscall opcodes), so inserting a new function in the middle of an existing array will break things. - ## WebSocket debugger PPSSPP has a JSON/WebSocket debugger and automation API (connect, read/write memory, search memory for values or byte diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index e36c32b415..85df5f83ef 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -968,92 +968,15 @@ void Register_LoadExecForUser() RegisterHLEModule("LoadExecForUser", ARRAY_SIZE(LoadExecForUser), LoadExecForUser); } -// sceKernelExitVSHVSH and sceKernelExitVSHKernel (two NIDs each, across firmware versions) are -// the VSH's own way of tearing itself down - when called from a regular game/homebrew context -// (which can happen, since these live in a kernel-mode module games can still reach) they have -// the same observable effect as sceKernelExitGame. See SceKernelLoadExecVSHParam in sceKernel.h. -static int sceKernelExitVSH(u32 paramPtr) { - if (Memory::IsValidRange(paramPtr, sizeof(SceKernelLoadExecVSHParam))) { - auto param = PSPPointer::Create(paramPtr); - INFO_LOG(Log::sceKernel, "sceKernelExitVSH: size=%d, args=%08x, argp=%08x, flags=%08x", param->size, param->args, param->argp, param->flags); - } - - INFO_LOG(Log::sceKernel, "sceKernelExitVSH"); - __KernelSwitchOffThread("VSH exited"); - Core_Stop(); - - g_OSD.Show(OSDType::MESSAGE_INFO, "sceKernelExitVSH()", 0.0f, "kernelexit"); - return hleNoLog(0); -} - -// The VSH's own version of sceKernelLoadExec, used for pushing a game to run over USB/WLAN -// (e.g. from a PC) rather than loading it from a file already on the memory stick/UMD. -static int sceKernelLoadExecBufferVSHUsbWlan(int bufferSize, u32 bufferAddr, u32 paramPtr) { - if (!Memory::IsValidRange(bufferAddr, bufferSize)) { - return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid buffer"); - } - - const u8 *data = Memory::GetPointerUnchecked(bufferAddr); - std::string error_string; - if (!__KernelLoadExecFromBuffer(data, (size_t)bufferSize, paramPtr, &error_string)) { - Core_UpdateState(CORE_RUNTIME_ERROR); - return hleLogError(Log::sceKernel, -1, "failed: %s", error_string.c_str()); - } - if (gpu) { - gpu->Reinitialize(); - } - return hleLogInfo(Log::sceKernel, 0); -} - -const HLEFunction LoadExecForKernel[] = { +const HLEFunction LoadExecForKernel[] = +{ {0x4AC57943, &WrapI_I, "sceKernelRegisterExitCallback", 'i', "i", HLE_KERNEL_SYSCALL }, - {0XA3D5E142, &WrapI_U, "sceKernelExitVSHVSH", 'i', "x", HLE_KERNEL_SYSCALL }, + {0XA3D5E142, nullptr, "sceKernelExitVSHVSH", '?', "" }, {0X28D0D249, &WrapI_CU, "sceKernelLoadExecVSHMs2", 'i', "sx" }, - {0x6D302D3D, &WrapI_U, "sceKernelExitVSHKernel", 'i', "x", HLE_KERNEL_SYSCALL },// when called in game mode it will have the same effect that sceKernelExitGame + {0x6D302D3D, &WrapV_V, "sceKernelExitVSHKernel", 'v', "x", HLE_KERNEL_SYSCALL },// when called in game mode it will have the same effect that sceKernelExitGame {0x05572A5F, &WrapV_V, "sceKernelExitGame", 'v', "", HLE_KERNEL_SYSCALL }, - {0X08F7166C, &WrapI_U, "sceKernelExitVSHVSH", 'i', "x", HLE_KERNEL_SYSCALL }, - {0XD940C83C, &WrapI_CU, "sceKernelLoadExecVSHMs2", 'i', "sx" }, - {0XF9CFCF2F, &WrapI_CU, "sceKernelLoadExec_F9CFCF2F", 'i', "sx" }, - {0XD8320A28, &WrapI_CU, "sceKernelLoadExecVSHDisc", 'i', "sx" }, - {0XC3474C2A, &WrapI_U, "sceKernelExitVSHKernel", 'i', "x", HLE_KERNEL_SYSCALL }, - {0XBEF585EC, &WrapI_IUU, "sceKernelLoadExecBufferVSHUsbWlan", 'i', "ixx" }, - // Everything below here is only known by NID - even JPCSP, which is further along in VSH - // support, only knows them by name/NID (or not even that) and stubs them all out. - {0X11412288, nullptr, "sceKernelLoadExec_11412288", '?', "" }, - {0XA5ECA6E3, nullptr, "sceKernelLoadExec_11412288", '?', "" }, - {0X00745486, nullptr, "sceKernelLoadExecVSHMs4", '?', "" }, - {0X4FB44D27, nullptr, "sceKernelLoadExecVSHMs1", '?', "" }, - {0XCC6A47D2, nullptr, "sceKernelLoadExecVSHMs3", '?', "" }, - {0X7CABED9B, nullptr, "sceKernelLoadExecVSHMs5", '?', "" }, - {0X1B305B09, nullptr, "sceKernelLoadExecVSHDiscDebug", '?', "" }, - {0XD4B49C4B, nullptr, "sceKernelLoadExecVSHDiscUpdater", '?', "" }, - {0X2B8813AF, nullptr, "sceKernelLoadExecBufferVSHUsbWlanDebug", '?', "" }, - {0X1F08547A, nullptr, "sceKernelInvokeExitCallback", '?', "" }, - {0X1F88A490, nullptr, "sceKernelRegisterExitCallback", '?', "" }, - {0X24114598, nullptr, "sceKernelUnregisterExitCallback", '?', "" }, - {0XB57D0DEC, nullptr, "sceKernelCheckExitCallback", '?', "" }, - {0X032A7938, nullptr, "LoadExecForKernel_032A7938", '?', "" }, - {0X077BA314, nullptr, "LoadExecForKernel_077BA314", '?', "" }, - {0X16A68007, nullptr, "LoadExecForKernel_16A68007", '?', "" }, - {0X1B8AB02E, nullptr, "LoadExecForKernel_1B8AB02E", '?', "" }, - {0X40564748, nullptr, "LoadExecForKernel_40564748", '?', "" }, - {0X47A5A49C, nullptr, "LoadExecForKernel_47A5A49C", '?', "" }, - {0X7CAFE77F, nullptr, "LoadExecForKernel_7CAFE77F", '?', "" }, - {0X87C3589C, nullptr, "LoadExecForKernel_87C3589C", '?', "" }, - {0X8C4679D3, nullptr, "LoadExecForKernel_8C4679D3", '?', "" }, - {0X9BD32619, nullptr, "LoadExecForKernel_9BD32619", '?', "" }, - {0XA6658F10, nullptr, "LoadExecForKernel_A6658F10", '?', "" }, - {0XB343FDAB, nullptr, "LoadExecForKernel_B343FDAB", '?', "" }, - {0XBC26BEEF, nullptr, "LoadExecForKernel_BC26BEEF", '?', "" }, - {0XC11E6DF1, nullptr, "LoadExecForKernel_C11E6DF1", '?', "" }, - {0XC540E3B3, nullptr, "LoadExecForKernel_C540E3B3", '?', "" }, - {0XC7C83B1E, nullptr, "LoadExecForKernel_C7C83B1E", '?', "" }, - {0XDBD0CF1B, nullptr, "LoadExecForKernel_DBD0CF1B", '?', "" }, - {0XE1972A24, nullptr, "LoadExecForKernel_E1972A24", '?', "" }, - {0XE704ECC3, nullptr, "LoadExecForKernel_E704ECC3", '?', "" }, - {0XAE9EFC0D, nullptr, "LoadExecForKernel_AE9EFC0D", '?', "" }, }; - + void Register_LoadExecForKernel() { RegisterHLEModule("LoadExecForKernel", ARRAY_SIZE(LoadExecForKernel), LoadExecForKernel); diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index c1a16c3ecb..b1d79f9476 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -82,28 +82,6 @@ struct SceKernelLoadExecParam u32_le keyp; // Encryption key? Not yet used }; -// Used by the VSH's own LoadExec variants (sceKernelLoadExecVSHMs2 and friends, see -// LoadExecForKernel in sceKernel.cpp) instead of SceKernelLoadExecParam above. The first four -// fields share the same layout as SceKernelLoadExecParam, which is why the plain -// sceKernelLoadExec happens to work fine even when handed one of these. Fields past "flags" -// are only present if size >= 0x30 - see JPCSP's SceKernelLoadExecVSHParam for the reference -// this was ported from. -struct SceKernelLoadExecVSHParam -{ - SceSize_le size; // Size of the structure - SceSize_le args; // Size of the arg string - u32_le argp; // Pointer to the arg string - u32_le keyp; // Pointer to the encryption key string - SceSize_le vshmainArgsSize; // Size of the vshmain-specific arg buffer - u32_le vshmainArgs; // Pointer to the vshmain-specific arg buffer - u32_le configFilep; // Pointer to a config file path string - u32_le unknownString; - u32_le flags; - u32_le extArgs; - u32_le extArgp; - u32_le opt11; -}; - void __KernelInit(); void __KernelShutdown(); void __KernelDoState(PointerWrap &p); diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 4ad422b684..3b0212eb64 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1776,10 +1776,7 @@ void __KernelLoadReset() { __KernelInit(); } -// Shared by __KernelLoadExec (loading from a file) and __KernelLoadExecFromBuffer (loading -// from a buffer already in RAM, as used by the VSH's USB/WLAN game-push feature) - everything -// past the point where we have the executable's bytes in hand. -bool __KernelLoadExecFromPtr(const u8 *data, size_t size, const char *filename, u32 paramPtr, std::string *error_string) { +bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string) { SceKernelLoadExecParam param{}; auto paramData = PSPPointer::Create(paramPtr); @@ -1804,7 +1801,18 @@ bool __KernelLoadExecFromPtr(const u8 *data, size_t size, const char *filename, __KernelLoadReset(); - PSPModule *module = __KernelLoadModule((u8 *)data, size, 0, filename, error_string); + std::vector fileData; + if (pspFileSystem.ReadEntireFile(filename, fileData) < 0) { + ERROR_LOG(Log::Loader, "Failed to load executable %s - file doesn't exist", filename); + *error_string = StringFromFormat("Could not find executable %s", filename); + delete[] param_argp; + delete[] param_key; + __KernelShutdown(); + return false; + } + + size_t size = fileData.size(); + PSPModule *module = __KernelLoadModule(fileData.data(), size, 0, filename, error_string); if (!module || module->isFake) { if (module) { @@ -1864,22 +1872,6 @@ bool __KernelLoadExecFromPtr(const u8 *data, size_t size, const char *filename, return true; } -bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string) { - std::vector fileData; - if (pspFileSystem.ReadEntireFile(filename, fileData) < 0) { - ERROR_LOG(Log::Loader, "Failed to load executable %s - file doesn't exist", filename); - *error_string = StringFromFormat("Could not find executable %s", filename); - __KernelShutdown(); - return false; - } - - return __KernelLoadExecFromPtr(fileData.data(), fileData.size(), filename, paramPtr, error_string); -} - -bool __KernelLoadExecFromBuffer(const u8 *data, size_t size, u32 paramPtr, std::string *error_string) { - return __KernelLoadExecFromPtr(data, size, "vshbuffer", paramPtr, error_string); -} - bool __KernelLoadGEDump(std::string_view base_filename, std::string *error_string) { __KernelLoadReset(); @@ -2546,7 +2538,7 @@ static u32 sceKernelLoadModuleDNAS(const char *name, u32 flags) return hleNoLog(0); } -// Suspecting this is a badly brute-forced function name... Although, it's not entirely implausible. Buffer makes sense. +// Pretty sure this is a badly brute-forced function name... SceUID sceKernelLoadModuleBufferUsbWlan(u32 size, u32 bufPtr, u32 flags, u32 lmoptionPtr) { if (flags != 0) { diff --git a/Core/HLE/sceKernelModule.h b/Core/HLE/sceKernelModule.h index 0cdcf97f31..2a2d39dd20 100644 --- a/Core/HLE/sceKernelModule.h +++ b/Core/HLE/sceKernelModule.h @@ -235,10 +235,6 @@ u32 __KernelGetModuleGP(SceUID module); bool KernelModuleIsKernelMode(SceUID module); bool __KernelLoadGEDump(std::string_view base_filename, std::string *error_string); bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_string); -// Exposed so sceVshBridge can reuse it directly for vshKernelLoadModuleBufferVSH, matching JPCSP. -SceUID sceKernelLoadModuleBufferUsbWlan(u32 size, u32 bufPtr, u32 flags, u32 lmoptionPtr); -bool __KernelLoadExecFromBuffer(const u8 *data, size_t size, u32 paramPtr, std::string *error_string); -// Exposed for HLE.cpp's "Unknown syscall" diagnostic - see the definition for details. bool KernelFindImportByStubAddr(u32 stubAddr, std::string *importModuleName, u32 *nid, std::string *importingModuleName); // Describes which loaded module (and section within it) an address falls in, e.g. "EBOOT.BIN.text+1234". // Returns an empty string if the address isn't inside any currently loaded module.