diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index b9c875fd71..ac0ec377ff 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -390,11 +390,17 @@ void __KernelMemoryShutdown() } BlockAllocator *BlockAllocatorFromID(int id) { + // A kernel module gets the privileged partitions whichever entry point it came in through - + // threads/tls/kernel/partition records sceKernelCreateTlspl accepting 1, 3 and 4 from a + // kernel module and refusing them from user mode, and it reaches the kernel through the + // ordinary ThreadManForUser NID either way. hleIsKernelMode() alone only catches the case + // where the export itself is kernel-only. + const bool kernelMode = hleIsKernelMode() || __KernelCurThreadIsKernelMode(); switch (id) { case KERNEL_PARTITION_ID: case 3: case 4: - if (hleIsKernelMode()) + if (kernelMode) return &kernelMemory; return nullptr; @@ -404,7 +410,7 @@ BlockAllocator *BlockAllocatorFromID(int id) { case 8: case 10: - if (hleIsKernelMode()) + if (kernelMode) return &userMemory; return nullptr; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index bd139eede5..260c349f61 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2290,6 +2290,17 @@ SceUID __KernelGetCurThreadModuleId() { return 0; } +bool __KernelCurThreadIsKernelMode() { + PSPThread *thread = __GetCurrentThread(); + if (thread && (thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0) { + return true; + } + // A kernel module's own main thread isn't necessarily flagged kernel - the attribute comes + // from PSP_MAIN_THREAD_ATTR in the module info, which needn't set it - so fall back to what + // module the thread belongs to, the same way sceKernelCreateThread decides allowKernel. + return KernelModuleIsKernelMode(__KernelGetCurThreadModuleId()); +} + u32 __KernelGetCurThreadStack() { PSPThread *t = __GetCurrentThread(); if (t) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 146c3bb5f5..2edcff09fe 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -341,6 +341,10 @@ KernelObject *__KernelCallbackObject(); SceUID __KernelGetCurThread(); int KernelCurThreadPriority(); bool KernelChangeThreadPriority(SceUID threadID, int priority); +// Whether the running thread belongs to a kernel module. Privilege on the PSP is a property of +// the caller, not of the syscall - hleIsKernelMode() only says the entry point itself is a +// kernel-only export, which is a different question. +bool __KernelCurThreadIsKernelMode(); u32 __KernelGetCurThreadStack(); u32 __KernelGetCurThreadStackStart(); const char *__KernelGetThreadName(SceUID threadID); diff --git a/docs/pspautotests-hardware.md b/docs/pspautotests-hardware.md index 453c9e306d..67329f07a4 100644 --- a/docs/pspautotests-hardware.md +++ b/docs/pspautotests-hardware.md @@ -217,9 +217,16 @@ to `host0:` with `sceIo` directly rather than through newlib's `FILE`, since tha libcglue in. Formatting still goes through newlib's `vsnprintf`, so the usual format specifiers are all available. -**PPSSPP can't run these yet.** `PPSSPPHeadless` times out on a kernel PRX built this way, so a -kernel-mode test can't go in `test.py` - keep it as a hardware reference and diff it by hand -against its user-mode twin. +Output reaches an emulator the same way it reaches the cable: a kernel build feeds +`sceIoDevctl("emulator:", SEND_OUTPUT)` as well as writing the file, and calls +`sceKernelExitGame` at the end so headless stops rather than spinning to its timeout. Both are +easy to forget when writing a new harness - without the first the test appears to produce nothing, +and without the second it always reports TIMEOUT even though it ran. + +One emulator-side note, since it took a while to pin down: privilege on the PSP belongs to the +*caller*, not to the syscall. PPSSPP's `hleIsKernelMode()` only reports whether the entry point +itself is a kernel-only export, so a kernel module calling an ordinary `ForUser` NID used to look +like user mode. `__KernelCurThreadIsKernelMode()` answers the question this test needs. ## Worked example: FAT short names diff --git a/pspautotests b/pspautotests index 926360d556..7c5d150d40 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 926360d5567fbf94c1605cd5ff7e91ef86f9f4cf +Subproject commit 7c5d150d40ab585d8bc1b5f60f5673eb56620ac9 diff --git a/test.py b/test.py index 9177f934b3..5caaed33a2 100755 --- a/test.py +++ b/test.py @@ -341,6 +341,7 @@ tests_good = [ "threads/threads/threads", "threads/tls/create", "threads/tls/partition", + "threads/tls/kernel/partition", "threads/tls/delete", "threads/tls/get", "threads/tls/free",