diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index f70d47aee3..47e312a480 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -1863,10 +1863,17 @@ 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); - // Tlspl draws the line at 6, unlike Vpl above: threads/tls/create records 7, 8, 9 and 10 all - // returning ILLEGAL_ARGUMENT on a real PSP, where threads/vpl/create has 8 and 9 falling - // through to ILLEGAL_PERM. Same-looking check, genuinely different range. - if (partition < 1 || partition > 6) + // 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) return hleLogWarning(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, "invalid partition %d", partition); BlockAllocator *allocator = BlockAllocatorFromID(partition); diff --git a/Core/HLE/sceMd5.cpp b/Core/HLE/sceMd5.cpp index 6abae8b5ca..7f014d864b 100644 --- a/Core/HLE/sceMd5.cpp +++ b/Core/HLE/sceMd5.cpp @@ -52,8 +52,8 @@ u32 sceKernelUtilsMt19937UInt(u32 ctx) { // can be in flight at once. Layout confirmed against a real PSP by pspautotests hash/md5ctx: // 96 bytes, and the word at offset 16 is never written by the kernel. // -// sceKernelUtilsSha1Block* below still keeps one global state, for the same reason this used to - -// there's no hardware recording of the SHA-1 context layout yet. +// SHA-1 gets the same treatment further down. Its context is the same size with the same +// bookkeeping, but note it does not stream whole blocks through buf the way MD5 does. struct PSPMd5Context { u32_le h[4]; u32_le pad; // the kernel leaves this one alone @@ -191,7 +191,55 @@ int sceKernelUtilsMd5BlockResult(u32 ctxAddr, u32 digestAddr) { } -static sha1_context sha1_ctx; +// SHA-1's context, confirmed against a real PSP by pspautotests hash/sha1ctx. Same 96 bytes and +// same bookkeeping as MD5, but no pad word - and unlike MD5, a whole-block update leaves buf +// alone rather than copying the block through it, which is what our sha1 does anyway. +struct PSPSha1Context { + u32_le h[5]; + u16_le usRemains; + u16_le usComputed; + u64_le ullTotalLen; + u8 buf[64]; +}; + +static void Sha1ContextRead(const PSPPointer &ctx, sha1_context *out) { + for (int i = 0; i < 5; i++) { + out->state[i] = ctx->h[i]; + } + u64 total = ctx->ullTotalLen; + out->total[0] = (u32)total; + out->total[1] = (u32)(total >> 32); + memcpy(out->buffer, ctx->buf, sizeof(out->buffer)); +} + +static void Sha1ContextWrite(PSPPointer &ctx, const sha1_context *in) { + for (int i = 0; i < 5; i++) { + ctx->h[i] = (u32)in->state[i]; + } + u64 total = (u64)(u32)in->total[0] | ((u64)(u32)in->total[1] << 32); + ctx->ullTotalLen = total; + ctx->usRemains = (u16)(total & 0x3F); + ctx->usComputed = 0; + memcpy(ctx->buf, in->buffer, sizeof(ctx->buf)); + ctx.NotifyWrite("Sha1Context"); +} + +static int Sha1BlockInit(u32 ctxAddr) { + auto ctx = PSPPointer::Create(ctxAddr); + if (!ctx.IsValid()) + return hleLogError(Log::HLE, -1, "bad context address"); + sha1_context fresh; + sha1_starts(&fresh); + for (int i = 0; i < 5; i++) { + ctx->h[i] = (u32)fresh.state[i]; + } + ctx->usRemains = 0; + ctx->usComputed = 0; + ctx->ullTotalLen = 0; + ctx.NotifyWrite("Sha1Context"); + return hleLogDebug(Log::HLE, 0); +} + int sceKernelUtilsSha1Digest(u32 dataAddr, int len, u32 digestAddr) { DEBUG_LOG(Log::HLE, "sceKernelUtilsSha1Digest(%08x, %d, %08x)", dataAddr, len, digestAddr); @@ -204,34 +252,29 @@ int sceKernelUtilsSha1Digest(u32 dataAddr, int len, u32 digestAddr) { } int sceKernelUtilsSha1BlockInit(u32 ctxAddr) { - DEBUG_LOG(Log::HLE, "sceKernelUtilsSha1BlockInit(%08x)", ctxAddr); - if (!Memory::IsValidAddress(ctxAddr)) - return -1; - - // TODO: Until I know how large a context is, we just go all lazy and use a global context, - // which will work just fine unless games do several MD5 concurrently. - - sha1_starts(&sha1_ctx); - - return 0; + return Sha1BlockInit(ctxAddr); } int sceKernelUtilsSha1BlockUpdate(u32 ctxAddr, u32 dataAddr, int len) { - DEBUG_LOG(Log::HLE, "sceKernelUtilsSha1BlockUpdate(%08x, %08x, %d)", ctxAddr, dataAddr, len); - if (!Memory::IsValidAddress(ctxAddr) || !Memory::IsValidAddress(dataAddr)) - return -1; - - sha1_update(&sha1_ctx, Memory::GetPointerWriteUnchecked(dataAddr), (int)len); - return 0; + auto ctx = PSPPointer::Create(ctxAddr); + if (!ctx.IsValid() || !Memory::IsValidRange(dataAddr, len)) + return hleLogError(Log::HLE, -1, "bad address"); + sha1_context work; + Sha1ContextRead(ctx, &work); + sha1_update(&work, Memory::GetPointerWriteUnchecked(dataAddr), (int)len); + Sha1ContextWrite(ctx, &work); + return hleLogDebug(Log::HLE, 0); } int sceKernelUtilsSha1BlockResult(u32 ctxAddr, u32 digestAddr) { - DEBUG_LOG(Log::HLE, "sceKernelUtilsSha1BlockResult(%08x, %08x)", ctxAddr, digestAddr); - if (!Memory::IsValidAddress(ctxAddr) || !Memory::IsValidAddress(digestAddr)) - return -1; - - sha1_finish(&sha1_ctx, Memory::GetPointerWriteUnchecked(digestAddr)); - return 0; + auto ctx = PSPPointer::Create(ctxAddr); + if (!ctx.IsValid() || !Memory::IsValidRange(digestAddr, 20)) + return hleLogError(Log::HLE, -1, "bad address"); + sha1_context work; + Sha1ContextRead(ctx, &work); + sha1_finish(&work, Memory::GetPointerWriteUnchecked(digestAddr)); + Sha1ContextWrite(ctx, &work); + return hleLogDebug(Log::HLE, 0); } diff --git a/pspautotests b/pspautotests index 1c4a78d809..9ace5419a8 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 1c4a78d809888ee6b947e89d4fb14b92c09c5ade +Subproject commit 9ace5419a8cbe11b10bae3d3d3763d15d404f848 diff --git a/test.py b/test.py index c2fd568af2..9177f934b3 100755 --- a/test.py +++ b/test.py @@ -212,6 +212,7 @@ tests_good = [ "hash/hash", "hash/md5ctx", "hash/mt19937ctx", + "hash/sha1ctx", "hle/check_not_used_uids", "intr/intr", "intr/enablesub", @@ -339,6 +340,7 @@ tests_good = [ "threads/threads/threadmanidtype", "threads/threads/threads", "threads/tls/create", + "threads/tls/partition", "threads/tls/delete", "threads/tls/get", "threads/tls/free",