diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 47e312a480..b9c875fd71 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -1863,17 +1863,12 @@ SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 block return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_NO_MEMORY, "invalid name"); if ((attr & ~PSP_TLSPL_ATTR_KNOWN) >= 0x100) return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid attr parameter: %08x", attr); - // From user mode only 1-6 exist: threads/tls/partition records 7 and up all returning - // ILLEGAL_ARGUMENT on a real PSP, where 1, 3 and 4 fall through to ILLEGAL_PERM below. Note - // this differs from sceKernelCreateVpl above, which does let 8 and 9 reach the permission - // check - the two used to share a range that was only ever right for Vpl. - // - // The kernel-mode range is left as it was, since 8 and 10 map to the user partition for a - // kernel caller and there's no hardware recording from kernel mode to check it against - a - // test PRX built on this suite's common code doesn't fit in the kernel partition alongside - // PSPLink. - const u32 highestPartition = hleIsKernelMode() ? 9 : 6; - if (partition < 1 || partition == 7 || partition > highestPartition) + // Only 1-6 exist, in either privilege level: threads/tls/partition and its kernel-mode twin + // both record 7 and up returning ILLEGAL_ARGUMENT on a real PSP. What the privilege changes + // is the permission check below - 1, 3 and 4 are ILLEGAL_PERM from user mode and fine from + // kernel mode. Note this differs from sceKernelCreateVpl above, which lets 8 and 9 reach the + // permission check; the two used to share a range that was only ever right for Vpl. + if (partition < 1 || partition > 6) return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %d", partition); BlockAllocator *allocator = BlockAllocatorFromID(partition); diff --git a/docs/pspautotests-hardware.md b/docs/pspautotests-hardware.md index 7597c2480b..453c9e306d 100644 --- a/docs/pspautotests-hardware.md +++ b/docs/pspautotests-hardware.md @@ -174,6 +174,53 @@ python3 test.py --graphics=software io/shortname/shortname - **The module must be named `TESTMODULE`** (`common.c` does this) or `gentest.py` prints the load line as an unexpected result. +## Kernel-mode tests + +Some behaviour differs by privilege - `sceKernelCreateTlspl` accepts partitions 1, 3 and 4 from a +kernel module and rejects them with `ILLEGAL_PERM` from user mode - so occasionally a test has to +run as a kernel module. `tests/threads/tls/kernel` is the working example. Set `COMMON_KERNEL = 1` +in the Makefile before including `common.mk` and you get a kernel build: + +```make +TARGETS = partition +EXTRA_OBJS = tlspl-imports.o +COMMON_KERNEL = 1 + +COMMON_DIR = ../../../../common +include $(COMMON_DIR)/common.mk +``` + +Three things make this work, and all three took a while to find, so don't undo them casually: + +- **`USE_KERNEL_LIBS`, set automatically by `common.mk`.** The stock `crt0_prx.o` references + `__libcglue_init`, which pulls in the whole of libcglue, which imports `sceNetInet`, + `sceUtility` and the `ForUser` IO libraries. A kernel module that imports any of those fails to + load with `8002013C` (library not found), and no amount of trimming `LIBS` helps because the + reference comes from the startup object itself. `USE_KERNEL_LIBS` selects a different startup - + and brings `-nostdlib`, so `common/kernelglue.c` supplies the newlib support hooks that go + missing: `_sbrk` over a fixed heap, the `_read`/`_write`/`_close` stubs, no-op retargetable + locks, and `module_start`. +- **Nothing may import a `ForUser` library.** `ThreadManForUser` is fine, but `SysMemUserForUser` + is not, which is why `common.c` compiles the `sceKernelSetCompiledSdkVersion` helpers out of + kernel builds - a function pointer table referencing them is enough to pull the stubs in. + Watch for this: `psp-strings yourtest.prx | grep For` lists what you're importing. Beware also + that `ThreadManForKernel` does not export the Tlspl calls - importing them from there links + cleanly and then returns `8002013A`, library not yet linked, from every call. +- **Size.** The kernel partition has about 285 KB free with PSPLink resident, 242 KB of it + contiguous, and loading over the cable needs room for the file *and* the loaded image at once. + A kernel build therefore gets a 4 KB `schedfBuffer` and a 4 KB heap instead of the user build's + 64 KB and 21 MB. `psp-size yourtest.elf` plus the `.prx` file size against `meminfo`'s MAXFREE + tells you whether it will fit; `800200D9` (memblock alloc failed) means it didn't. + +Output works normally - `printf`, `checkpoint` and `schedf` all behave - but a kernel build writes +to `host0:` with `sceIo` directly rather than through newlib's `FILE`, since that's what dragged +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. + ## Worked example: FAT short names `tests/io/shortname` was written this way and is a decent template. It creates a scratch directory diff --git a/pspautotests b/pspautotests index 9ace5419a8..926360d556 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 9ace5419a8cbe11b10bae3d3d3763d15d404f848 +Subproject commit 926360d5567fbf94c1605cd5ff7e91ef86f9f4cf