diff --git a/Core/HLE/HLETables.cpp b/Core/HLE/HLETables.cpp index 0439520898..df6bf26f3e 100644 --- a/Core/HLE/HLETables.cpp +++ b/Core/HLE/HLETables.cpp @@ -327,6 +327,10 @@ void RegisterAllModules() { Register_sceReg(); // Not ready to enable this due to apparent softlocks in Patapon 3. // Register_sceNpMatching2(); + Register_sceRtc_driver(); + Register_scePower_driver(); + Register_sceImpose_driver(); + Register_sceHprm_driver(); // add new modules here. } diff --git a/Core/HLE/sceHprm.cpp b/Core/HLE/sceHprm.cpp index 3581fc5646..f6dba1f759 100644 --- a/Core/HLE/sceHprm.cpp +++ b/Core/HLE/sceHprm.cpp @@ -53,6 +53,9 @@ static u32 sceHprmPeekLatch(u32 latchAddr) { } static u32 sceHprmReadLatch(u32 latchAddr) { + // Real hardware/JPCSP: 4 x u32 output struct, dummied out to all-zero (nothing held/pressed). + for (int i = 0; i < 4; i++) + Memory::WriteOrException_U32(0, latchAddr + i * 4); return hleLogDebug(Log::HLE, 0, "latchAddr %08x", latchAddr); } @@ -74,3 +77,16 @@ void Register_sceHprm() { RegisterHLEModule("sceHprm", ARRAY_SIZE(sceHprm), sceHprm); } + +// Kernel-mode variant library used by VSH modules (e.g. vshbridge). NID 0xE9B776BE is the +// firmware 6.60+ alias of sceHprmReadLatch, listed as nid=0xE9B776BE version=660 alongside the +// 0x40D2F9F0 version=150 NID in JPCSP's sceHprm.java - both resolve to the same function there. +const HLEFunction sceHprm_driver[] = +{ + {0XE9B776BE, &WrapU_U, "sceHprmReadLatch", 'x', "x"}, +}; + +void Register_sceHprm_driver() +{ + RegisterHLEModule("sceHprm_driver", ARRAY_SIZE(sceHprm_driver), sceHprm_driver); +} diff --git a/Core/HLE/sceHprm.h b/Core/HLE/sceHprm.h index 24f2beb514..bcdaffb3d1 100644 --- a/Core/HLE/sceHprm.h +++ b/Core/HLE/sceHprm.h @@ -17,4 +17,5 @@ #pragma once -void Register_sceHprm(); \ No newline at end of file +void Register_sceHprm(); +void Register_sceHprm_driver(); \ No newline at end of file diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index 46869208ea..6625dff2e6 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -127,3 +127,24 @@ const HLEFunction sceImpose[] = { void Register_sceImpose() { RegisterHLEModule("sceImpose", ARRAY_SIZE(sceImpose), sceImpose); } + +// Real name/purpose unknown (jpcsp's sceImpose.java doesn't have one either - it's named +// after its own NID, sceImpose_driver_B497314D). jpcsp's implementation just zeroes the +// output pointer and returns 0; matched here rather than leaving it fully unresolved, since +// an unresolved import leaves the caller's output buffer as PPSSPP's uninitialized-memory +// poison instead of a real value. Kernel-only alias module some firmware-660+ code (e.g. the +// VSH's sceVshBridge_Driver) imports from. +static int sceImpose_driver_B497314D(int param, u32 resultAddr) { + auto result = PSPPointer::Create(resultAddr); + if (result.IsValid()) + *result = 0; + return hleLogDebug(Log::sceUtility, 0, "UNTESTED"); +} + +const HLEFunction sceImpose_driver[] = { + {0XB497314D, &WrapI_IU, "sceImpose_driver_B497314D", 'i', "ix"}, +}; + +void Register_sceImpose_driver() { + RegisterHLEModule("sceImpose_driver", ARRAY_SIZE(sceImpose_driver), sceImpose_driver); +} diff --git a/Core/HLE/sceImpose.h b/Core/HLE/sceImpose.h index e77ad16145..9c8213e2f9 100644 --- a/Core/HLE/sceImpose.h +++ b/Core/HLE/sceImpose.h @@ -20,5 +20,6 @@ class PointerWrap; void Register_sceImpose(); +void Register_sceImpose_driver(); void __ImposeInit(); void __ImposeDoState(PointerWrap &p); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 5968e5fe15..6e710a0a21 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -947,6 +947,16 @@ const HLEFunction ThreadManForKernel[] = {0x1D371B8A, &WrapI_IU, "sceKernelCancelVpl", 'i', "ix", HLE_KERNEL_SYSCALL }, {0x39810265, &WrapI_IU, "sceKernelReferVplStatus", 'i', "ip", HLE_KERNEL_SYSCALL }, {0xBC31C1B9, nullptr, "sceKernelExtendKernelStack"}, + // New entries must go here at the end, not inserted earlier in this table - the funcindex + // (this array position) gets baked directly into resolved-import syscall opcodes written + // into guest RAM (see GetSyscallOp() in HLE.cpp), which savestates capture verbatim. Inserting + // a new entry before an existing one shifts every later entry's index, silently corrupting + // any savestate taken after that entry was already resolved (see AGENTS.md). + {0x3AD58B8C, &WrapU_V, "sceKernelSuspendDispatchThread", 'x', "", HLE_NOT_IN_INTERRUPT | HLE_KERNEL_SYSCALL }, + {0x27E22EC2, &WrapU_U, "sceKernelResumeDispatchThread", 'x', "x", HLE_NOT_IN_INTERRUPT | HLE_KERNEL_SYSCALL }, + {0xC11BA8C4, &WrapI_II, "sceKernelNotifyCallback", 'i', "ii", HLE_KERNEL_SYSCALL }, + {0xF6427665, &WrapI_V, "sceKernelGetUserLevel", 'i', "", HLE_KERNEL_SYSCALL }, + {0x85A2A5BF, &WrapI_V, "sceKernelIsUserModeThread", 'i', "", HLE_KERNEL_SYSCALL }, }; void Register_ThreadManForUser() diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 57ddf1daae..c4c65606d2 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2121,6 +2121,23 @@ bool __KernelIsDispatchEnabled() { return dispatchEnabled && __InterruptsEnabled(); } +// PPSSPP doesn't track a real per-module "user level" (set via the owning module's +// PSP_MODULE_INFO attribute on real firmware, e.g. 0 for kernel-mode modules like the VSH's +// bridge driver, 4 for a regular user-mode game). Approximate it from the calling thread's +// kernel/user attr instead - the two levels VSH code actually seems to check for are "kernel" +// vs "not kernel", so this is enough to avoid it treating itself as an ordinary user thread. +int sceKernelGetUserLevel() { + PSPThread *t = __GetCurrentThread(); + int level = (t && (t->nt.attr & PSP_THREAD_ATTR_KERNEL)) ? 0 : 4; + return hleLogDebug(Log::sceKernel, level); +} + +int sceKernelIsUserModeThread() { + PSPThread *t = __GetCurrentThread(); + int isUser = (t && (t->nt.attr & PSP_THREAD_ATTR_KERNEL)) ? 0 : 1; + return hleLogDebug(Log::sceKernel, isUser); +} + int KernelRotateThreadReadyQueue(int priority) { PSPThread *cur = __GetCurrentThread(); diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 40c6c37ddf..146c3bb5f5 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -59,6 +59,8 @@ int __KernelGetThreadExitStatus(SceUID threadID); int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr); u32 sceKernelSuspendDispatchThread(); u32 sceKernelResumeDispatchThread(u32 suspended); +int sceKernelGetUserLevel(); +int sceKernelIsUserModeThread(); int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr); u32 sceKernelReferThreadStatus(u32 uid, u32 statusPtr); u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr); diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index d2c263ea14..748647bd3a 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -635,6 +635,21 @@ void Register_scePower() { RegisterHLEModule("scePower",ARRAY_SIZE(scePower),scePower); } +// Real name unknown (jpcsp's scePower.java doesn't have one either - it's named after its +// own NID, scePower_driver_5F5006D2, and just returns 0 unconditionally). Kernel-only alias +// module some firmware-660+ code (e.g. the VSH's sceVshBridge_Driver) imports from. +static int scePower_driver_5F5006D2() { + return hleLogDebug(Log::HLE, 0, "UNTESTED"); +} + +const HLEFunction scePower_driver[] = { + {0X5F5006D2, &WrapI_V, "scePower_driver_5F5006D2", 'i', "" }, +}; + +void Register_scePower_driver() { + RegisterHLEModule("scePower_driver", ARRAY_SIZE(scePower_driver), scePower_driver); +} + void Register_sceSuspendForUser() { RegisterHLEModule("sceSuspendForUser", ARRAY_SIZE(sceSuspendForUser), sceSuspendForUser); } diff --git a/Core/HLE/scePower.h b/Core/HLE/scePower.h index 2b38415813..f83173ef13 100644 --- a/Core/HLE/scePower.h +++ b/Core/HLE/scePower.h @@ -23,6 +23,7 @@ void __PowerInit(); void __PowerDoState(PointerWrap &p); void Register_scePower(); +void Register_scePower_driver(); void Register_sceSuspendForUser(); int KernelVolatileMemLock(int type, u32 paddr, u32 psize); diff --git a/Core/HLE/sceRtc.cpp b/Core/HLE/sceRtc.cpp index 8a4111a411..e4059dda96 100644 --- a/Core/HLE/sceRtc.cpp +++ b/Core/HLE/sceRtc.cpp @@ -945,7 +945,23 @@ static int sceRtcGetLastReincarnatedTime(u32 tickPtr) static int sceRtcSetAlarmTick(u32 unknown1, u32 unknown2) { ERROR_LOG_REPORT(Log::sceRtc, "UNIMPL sceRtcSetAlarmTick(%x, %x)", unknown1, unknown2); - return 0; + return 0; +} + +// Real signature per uofw (sceRtc_C2DDBEB5, src/kd/rtc/rtc.c): s32 sceRtcGetAlarmTick(u64 *tick). +// PPSSPP doesn't track a real hardware RTC alarm, so there's nothing meaningful to report - +// but leaving this fully unimplemented (nullptr in the function table) meant callers got back +// PPSSPP's uninitialized-memory poison in *tick instead of a real value. On the VSH boot path +// (see docs/VSHBootInvestigation.md) that poisoned tick was later dereferenced as a pointer by +// vsh_module's own code, causing a wild-address SIGSEGV. Write a real (zero) tick instead, same +// as if no alarm were currently set - matches sceRtcSetAlarmTick() above also being a no-op. +static int sceRtcGetAlarmTick(u32 tickPtr) { + auto tick = PSPPointer::Create(tickPtr); + if (!tick.IsValid()) + return hleLogError(Log::sceRtc, 0, "bad address"); + + *tick = 0; + return hleLogDebug(Log::sceRtc, 0); } // Caller must check outPtr and srcTickPtr. @@ -1132,10 +1148,25 @@ const HLEFunction sceRtc[] = {0X81FCDA34, nullptr, "sceRtcIsAlarmed", '?', "" }, {0XFB3B18CD, nullptr, "sceRtcRegisterCallback", '?', "" }, {0X6A676D2D, nullptr, "sceRtcUnregisterCallback", '?', "" }, - {0XC2DDBEB5, nullptr, "sceRtcGetAlarmTick", '?', "" }, + {0XC2DDBEB5, &WrapI_U, "sceRtcGetAlarmTick", 'i', "x" }, }; void Register_sceRtc() { RegisterHLEModule("sceRtc", ARRAY_SIZE(sceRtc), sceRtc); } + +// sceRtc_driver is the kernel-only alias some firmware-660+ modules (e.g. the VSH's +// sceVshBridge_Driver) import from instead of plain sceRtc - same underlying functions, +// just also exported under a second, kernel-suffixed module name. Confirmed by cross- +// checking jpcsp's sceRtc.java, which registers 0xE09880CF as an alternate NID on the exact +// same sceRtcSetAlarmTick() method (JPCSP doesn't distinguish import module names the way +// PPSSPP's HLE dispatch does, but the NID->function mapping is the same either way). +const HLEFunction sceRtc_driver[] = { + {0XE09880CF, &WrapI_UU, "sceRtcSetAlarmTick", 'i', "xx" }, +}; + +void Register_sceRtc_driver() +{ + RegisterHLEModule("sceRtc_driver", ARRAY_SIZE(sceRtc_driver), sceRtc_driver); +} diff --git a/Core/HLE/sceRtc.h b/Core/HLE/sceRtc.h index a6dc77e60a..38978d38cc 100644 --- a/Core/HLE/sceRtc.h +++ b/Core/HLE/sceRtc.h @@ -35,6 +35,7 @@ void RtcSetBaseTime(int32_t seconds, int32_t micro = 0); u64 __RtcGetCurrentTick(); void Register_sceRtc(); +void Register_sceRtc_driver(); void __RtcInit(); void __RtcDoState(PointerWrap &p);