From 94094f99bffbba75e02bcd0c438c46553a3cedda Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 15 Dec 2012 18:06:55 -0800 Subject: [PATCH 1/7] Cleanup basic Mbx implementation, create func. --- Core/HLE/FunctionWrappers.h | 5 +++++ Core/HLE/sceKernel.cpp | 2 +- Core/HLE/sceKernelMbx.cpp | 42 +++++++++++++++++++++++++++++-------- Core/HLE/sceKernelMbx.h | 3 +-- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index dd684e8007..2d560cc0b7 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -260,6 +260,11 @@ template void WrapI_CU() { RETURN(retval); } +template void WrapI_CUU() { + int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2)); + RETURN(retval); +} + template void WrapI_CUUU() { int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3)); diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index f733834392..2dc0215507 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -435,7 +435,7 @@ const HLEFunction ThreadManForUser[] = {0x2A3D44FF,sceKernelGetCallbackCount,"sceKernelGetCallbackCount"}, {0x730ED8BC,sceKernelReferCallbackStatus,"sceKernelReferCallbackStatus"}, - {0x8125221D,&WrapU_CIUIU,"sceKernelCreateMbx"}, + {0x8125221D,&WrapI_CUU,"sceKernelCreateMbx"}, {0x86255ADA,&WrapI_I,"sceKernelDeleteMbx"}, {0xE9B3061E,&WrapV_IU,"sceKernelSendMbx"}, {0x18260574,&WrapV_IUU,"sceKernelReceiveMbx"}, diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index ee6073e6a2..0bfe1e12c0 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -25,6 +25,8 @@ // TODO: when a thread is being resumed (message received or cancellation), sceKernelReceiveMbx() always returns 0 +typedef std::pair MbxWaitingThread; + struct NativeMbx { SceSize size; @@ -46,7 +48,7 @@ struct Mbx : public KernelObject { if (nmb.attr & SCE_KERNEL_MBA_THPRI) { - for (std::vector >::iterator it = waitingThreads.begin(); it != waitingThreads.end(); it++) + for (std::vector::iterator it = waitingThreads.begin(); it != waitingThreads.end(); it++) { if (__KernelGetThreadPrio(id) >= __KernelGetThreadPrio((*it).first)) { @@ -63,24 +65,41 @@ struct Mbx : public KernelObject NativeMbx nmb; - std::vector > waitingThreads; + std::vector waitingThreads; std::vector messageQueue; }; -SceUID sceKernelCreateMbx(const char *name, int memoryPartition, SceUInt attr, int size, u32 optAddr) +SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) { - DEBUG_LOG(HLE, "sceKernelCreateMbx(%s, %i, %08x, %i, %08x)", name, memoryPartition, attr, size, optAddr); + if (!name) + { + WARN_LOG(HLE, "%08x=%s(): invalid name", SCE_KERNEL_ERROR_ERROR, __FUNCTION__); + return SCE_KERNEL_ERROR_ERROR; + } + if (attr >= 0x300) + { + WARN_LOG(HLE, "%08x=%s(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, __FUNCTION__, attr); + return SCE_KERNEL_ERROR_ILLEGAL_ATTR; + } Mbx *m = new Mbx(); SceUID id = kernelObjects.Create(m); m->nmb.size = sizeof(NativeMbx); - strncpy(m->nmb.name, name, sizeof(m->nmb.name)); + strncpy(m->nmb.name, name, 31); + m->nmb.name[31] = 0; m->nmb.attr = attr; m->nmb.numWaitThreads = 0; m->nmb.numMessages = 0; m->nmb.packetListHead = 0; + DEBUG_LOG(HLE, "%i=sceKernelCreateMbx(%s, %08x, %08x)", id, name, attr, optAddr); + + if (optAddr != 0) + WARN_LOG(HLE, "%s(%s) unsupported options parameter: %08x", __FUNCTION__, name, optAddr); + if ((attr & ~SCE_KERNEL_MBA_THPRI) != 0) + WARN_LOG(HLE, "%s(%s) unsupported attr parameter: %08x", __FUNCTION__, name, attr); + return id; } @@ -94,8 +113,12 @@ int sceKernelDeleteMbx(SceUID id) for (size_t i = 0; i < m->waitingThreads.size(); i++) { Memory::Write_U32(0, m->waitingThreads[i].second); - __KernelResumeThreadFromWait(m->waitingThreads[i].first); + __KernelResumeThreadFromWait(m->waitingThreads[i].first, SCE_KERNEL_ERROR_WAIT_DELETE); } + + if (!m->waitingThreads.empty()) + hleReSchedule("mbx deleted"); + m->waitingThreads.clear(); } else { @@ -184,7 +207,7 @@ void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); RETURN(0); - __KernelWaitCurThread(WAITTYPE_MBX, 0, 0, 0, false); // ? + __KernelWaitCurThread(WAITTYPE_MBX, id, 0, 0, false); } } @@ -211,9 +234,9 @@ void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) else { DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); - m->AddWaitingThread(id, packetAddrPtr); + m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); RETURN(0); - __KernelWaitCurThread(WAITTYPE_MBX, 0, 0, 0, true); // ? + __KernelWaitCurThread(WAITTYPE_MBX, id, 0, 0, true); } } @@ -281,6 +304,7 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) DEBUG_LOG(HLE, "sceKernelReferMbxStatus(%i, %08x)", id, infoAddr); if (info) { + info->size = m->nmb.size; strncpy(info->name, m->nmb.name, 32); info->attr = m->nmb.attr; info->numWaitThreads = m->waitingThreads.size(); diff --git a/Core/HLE/sceKernelMbx.h b/Core/HLE/sceKernelMbx.h index e841dcb6d0..05257b101a 100644 --- a/Core/HLE/sceKernelMbx.h +++ b/Core/HLE/sceKernelMbx.h @@ -34,7 +34,7 @@ struct SceKernelMbxInfo u32 topPacketAddr; }; -SceUID sceKernelCreateMbx(const char *name, int memoryPartition, SceUInt attr, int size, u32 optAddr); +SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr); int sceKernelDeleteMbx(SceUID id); void sceKernelSendMbx(SceUID id, u32 addPacketAddr); void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr); @@ -42,4 +42,3 @@ void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr); int sceKernelPollMbx(SceUID id, u32 packetAddrPtr); int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr); int sceKernelReferMbxStatus(SceUID id, u32 infoAddr); - From 13ed64ff4baf45239e0f6c477cadd8c92c46b0ad Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 15 Dec 2012 18:57:15 -0800 Subject: [PATCH 2/7] Add basic timeout support for Mbx. --- Core/HLE/sceKernelMbx.cpp | 59 +++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 0bfe1e12c0..7ef7b3f2c3 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -19,13 +19,19 @@ #include "sceKernelThread.h" #include "sceKernelMbx.h" #include "HLE.h" +#include "../../Core/CoreTiming.h" #define SCE_KERNEL_MBA_THPRI 0x100 #define SCE_KERNEL_MBA_MSPRI 0x400 +#define SCE_KERNEL_MBA_ATTR_KNOWN (SCE_KERNEL_MBA_THPRI | SCE_KERNEL_MBA_MSPRI) // TODO: when a thread is being resumed (message received or cancellation), sceKernelReceiveMbx() always returns 0 typedef std::pair MbxWaitingThread; +void __KernelMbxTimeout(u64 userdata, int cyclesLate); + +bool mbxInitComplete = false; +int mbxWaitTimer = 0; struct NativeMbx { @@ -69,14 +75,55 @@ struct Mbx : public KernelObject std::vector messageQueue; }; +void __KernelMbxInit() +{ + mbxWaitTimer = CoreTiming::RegisterEvent("MbxTimeout", &__KernelMbxTimeout); + + mbxInitComplete = true; +} + +void __KernelMbxTimeout(u64 userdata, int cyclesLate) +{ + SceUID threadID = (SceUID)userdata; + + u32 error; + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error); + if (timeoutPtr != 0) + Memory::Write_U32(0, timeoutPtr); + + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); + // TODO: waitingThreads (but not here.) +} + +void __KernelWaitMbx(Mbx *m, u32 timeoutPtr) +{ + if (timeoutPtr == 0 || mbxWaitTimer == 0) + return; + + int micro = (int) Memory::Read_U32(timeoutPtr); + + // TODO: test timing. + if (micro <= 3) + micro = 15; + else if (micro <= 249) + micro = 250; + + // This should call __KernelMbxTimeout() later, unless we cancel it. + CoreTiming::ScheduleEvent(usToCycles(micro), mbxWaitTimer, __KernelGetCurThread()); +} + SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) { + if (!mbxInitComplete) + __KernelMbxInit(); + if (!name) { WARN_LOG(HLE, "%08x=%s(): invalid name", SCE_KERNEL_ERROR_ERROR, __FUNCTION__); return SCE_KERNEL_ERROR_ERROR; } - if (attr >= 0x300) + // Accepts 0x000 - 0x0FF, 0x100 - 0x1FF, and 0x400 - 0x4FF. + if (((attr & ~SCE_KERNEL_MBA_ATTR_KNOWN) & ~0xFF) != 0) { WARN_LOG(HLE, "%08x=%s(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, __FUNCTION__, attr); return SCE_KERNEL_ERROR_ILLEGAL_ATTR; @@ -97,7 +144,7 @@ SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) if (optAddr != 0) WARN_LOG(HLE, "%s(%s) unsupported options parameter: %08x", __FUNCTION__, name, optAddr); - if ((attr & ~SCE_KERNEL_MBA_THPRI) != 0) + if ((attr & ~SCE_KERNEL_MBA_ATTR_KNOWN) != 0) WARN_LOG(HLE, "%s(%s) unsupported attr parameter: %08x", __FUNCTION__, name, attr); return id; @@ -207,7 +254,8 @@ void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); RETURN(0); - __KernelWaitCurThread(WAITTYPE_MBX, id, 0, 0, false); + __KernelWaitMbx(m, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, false); } } @@ -236,7 +284,8 @@ void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); RETURN(0); - __KernelWaitCurThread(WAITTYPE_MBX, id, 0, 0, true); + __KernelWaitMbx(m, timeoutPtr); + __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, true); } } @@ -281,7 +330,7 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr) for (size_t i = 0; i < m->waitingThreads.size(); i++) { Memory::Write_U32(0, m->waitingThreads[i].second); - __KernelResumeThreadFromWait(m->waitingThreads[i].first); + __KernelResumeThreadFromWait(m->waitingThreads[i].first, SCE_KERNEL_ERROR_WAIT_CANCEL); } m->waitingThreads.clear(); From 0d07013199cf92465472a7bc645ff235569b71b7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Dec 2012 09:21:22 -0800 Subject: [PATCH 3/7] Cleanup timeouts and refer for Mbx functions. --- Core/HLE/sceKernelMbx.cpp | 133 ++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 42 deletions(-) diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 7ef7b3f2c3..603262433e 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -25,8 +25,6 @@ #define SCE_KERNEL_MBA_MSPRI 0x400 #define SCE_KERNEL_MBA_ATTR_KNOWN (SCE_KERNEL_MBA_THPRI | SCE_KERNEL_MBA_MSPRI) -// TODO: when a thread is being resumed (message received or cancellation), sceKernelReceiveMbx() always returns 0 - typedef std::pair MbxWaitingThread; void __KernelMbxTimeout(u64 userdata, int cyclesLate); @@ -82,6 +80,36 @@ void __KernelMbxInit() mbxInitComplete = true; } +bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int result, bool &wokeThreads) +{ + SceUID waitID = __KernelGetWaitID(th.first, WAITTYPE_MBX, error); + u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.first, error); + + // The waitID may be different after a timeout. + if (waitID != m->GetUID()) + return true; + + if (result == 0) + m->nmb.numWaitThreads--; + else + { + // Null it out since nothing was received. + if (Memory::IsValidAddress(th.second)) + Memory::Write_U32(0, th.second); + } + + if (timeoutPtr != 0 && mbxWaitTimer != 0) + { + // Remove any event for this thread. + u64 cyclesLeft = CoreTiming::UnscheduleEvent(mbxWaitTimer, th.first); + Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr); + } + + __KernelResumeThreadFromWait(th.first, result); + wokeThreads = true; + return true; +} + void __KernelMbxTimeout(u64 userdata, int cyclesLate) { SceUID threadID = (SceUID)userdata; @@ -91,8 +119,18 @@ void __KernelMbxTimeout(u64 userdata, int cyclesLate) if (timeoutPtr != 0) Memory::Write_U32(0, timeoutPtr); + SceUID mbxID = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error); + Mbx *m = kernelObjects.Get(mbxID, error); + if (m) + { + // This thread isn't waiting anymore, but we'll remove it from waitingThreads later. + // The reason is, if it times out, but what it was waiting on is DELETED prior to it + // actually running, it will get a DELETE result instead of a TIMEOUT. + // So, we need to remember it or we won't be able to mark it DELETE instead later. + m->nmb.numWaitThreads--; + } + __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); - // TODO: waitingThreads (but not here.) } void __KernelWaitMbx(Mbx *m, u32 timeoutPtr) @@ -112,6 +150,19 @@ void __KernelWaitMbx(Mbx *m, u32 timeoutPtr) CoreTiming::ScheduleEvent(usToCycles(micro), mbxWaitTimer, __KernelGetCurThread()); } +void __KernelMbxRemoveThread(Mbx *m, SceUID threadID) +{ + for (size_t i = 0; i < m->waitingThreads.size(); i++) + { + MbxWaitingThread *t = &m->waitingThreads[i]; + if (t->first == threadID) + { + m->waitingThreads.erase(m->waitingThreads.begin() + i); + break; + } + } +} + SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) { if (!mbxInitComplete) @@ -157,15 +208,14 @@ int sceKernelDeleteMbx(SceUID id) if (m) { DEBUG_LOG(HLE, "sceKernelDeleteMbx(%i)", id); - for (size_t i = 0; i < m->waitingThreads.size(); i++) - { - Memory::Write_U32(0, m->waitingThreads[i].second); - __KernelResumeThreadFromWait(m->waitingThreads[i].first, SCE_KERNEL_ERROR_WAIT_DELETE); - } - if (!m->waitingThreads.empty()) - hleReSchedule("mbx deleted"); + bool wokeThreads; + for (size_t i = 0; i < m->waitingThreads.size(); i++) + __KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_DELETE, wokeThreads); m->waitingThreads.clear(); + + if (wokeThreads) + hleReSchedule("mbx deleted"); } else { @@ -216,12 +266,13 @@ void sceKernelSendMbx(SceUID id, u32 packetAddr) } else if (m->messageQueue.empty()) { + //__KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_DELETE, wokeThreads); Memory::Write_U32(packetAddr, m->waitingThreads.front().second); - __KernelResumeThreadFromWait(m->waitingThreads.front().first); + __KernelResumeThreadFromWait(m->waitingThreads.front().first, 0); DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, m->waitingThreads.front().first); m->waitingThreads.erase(m->waitingThreads.begin()); RETURN(0); - __KernelReSchedule(); + hleReSchedule("mbx sent"); } else { @@ -252,6 +303,7 @@ void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) else { DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); + __KernelMbxRemoveThread(m, __KernelGetCurThread()); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); RETURN(0); __KernelWaitMbx(m, timeoutPtr); @@ -263,7 +315,6 @@ void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) { u32 error; Mbx *m = kernelObjects.Get(id, error); - __KernelCheckCallbacks(); if (!m) { @@ -277,11 +328,13 @@ void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr); Memory::Write_U32(m->messageQueue.front(), packetAddrPtr); m->messageQueue.erase(m->messageQueue.begin()); + hleCheckCurrentCallbacks(); RETURN(0); } else { DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); + __KernelMbxRemoveThread(m, __KernelGetCurThread()); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); RETURN(0); __KernelWaitMbx(m, timeoutPtr); @@ -327,13 +380,15 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr) u32 count = m->waitingThreads.size(); DEBUG_LOG(HLE, "sceKernelCancelReceiveMbx(%i, %08x): cancelling %d threads", id, numWaitingThreadsAddr, count); + + bool wokeThreads; for (size_t i = 0; i < m->waitingThreads.size(); i++) - { - Memory::Write_U32(0, m->waitingThreads[i].second); - __KernelResumeThreadFromWait(m->waitingThreads[i].first, SCE_KERNEL_ERROR_WAIT_CANCEL); - } + __KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_CANCEL, wokeThreads); m->waitingThreads.clear(); + if (wokeThreads) + hleReSchedule("mbx canceled"); + if (numWaitingThreadsAddr) Memory::Write_U32(count, numWaitingThreadsAddr); return 0; @@ -349,34 +404,28 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) return error; } - SceKernelMbxInfo *info = (SceKernelMbxInfo*)Memory::GetPointer(infoAddr); - DEBUG_LOG(HLE, "sceKernelReferMbxStatus(%i, %08x)", id, infoAddr); - if (info) + // TODO: Is there a correct error code? + if (!Memory::IsValidAddress(infoAddr)) + return -1; + + SceKernelMbxInfo info; + memcpy(&info, &m->nmb, sizeof(SceKernelMbxInfo)); + info.numMessage = m->messageQueue.size(); + info.numWaitThreads = m->waitingThreads.size(); + + if (!m->messageQueue.empty()) { - info->size = m->nmb.size; - strncpy(info->name, m->nmb.name, 32); - info->attr = m->nmb.attr; - info->numWaitThreads = m->waitingThreads.size(); - info->numMessage = m->messageQueue.size(); - // Fill the 'next' parameter of packets which we don't use by default but could be used by a game - if (m->messageQueue.size() != 0) - { - info->topPacketAddr = m->messageQueue[0]; - for (u32 i = 0; i < m->messageQueue.size() - 1; i++) - { - Memory::Write_U32(m->messageQueue[i + 1], Memory::Read_U32(m->messageQueue[i])); - } - Memory::Write_U32(m->messageQueue[m->messageQueue.size() - 1], 0); - } - else - { - info->topPacketAddr = 0; - } + info.topPacketAddr = m->messageQueue[0]; + + // TODO: Do this when sending messages too? + // Fill in the next ptrs in a loop (0 => 1, 1 => 0 for 2.) + for (int dest = 0, src = 1, n = m->messageQueue.size(); dest < n; ++dest, ++src) + Memory::Write_U32(m->messageQueue[src % n], m->messageQueue[dest]); } else - { - return -1; - } + info.topPacketAddr = 0; + + Memory::WriteStruct(infoAddr, &info); return 0; } From 87fcdc9080697d7493ff4dcb79c1c50fb091a6c8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Dec 2012 16:32:18 -0800 Subject: [PATCH 4/7] Use KERNELOBJECT_MAX_NAME_LENGTH for name length. Just avoiding magic numbers, didn't know there was a constant. --- Core/HLE/sceKernelEventFlag.cpp | 6 +++--- Core/HLE/sceKernelMbx.cpp | 6 +++--- Core/HLE/sceKernelMutex.cpp | 12 ++++++------ Core/HLE/sceKernelSemaphore.cpp | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Core/HLE/sceKernelEventFlag.cpp b/Core/HLE/sceKernelEventFlag.cpp index c3b4122166..2ba4f23cf3 100644 --- a/Core/HLE/sceKernelEventFlag.cpp +++ b/Core/HLE/sceKernelEventFlag.cpp @@ -30,7 +30,7 @@ void __KernelEventFlagTimeout(u64 userdata, int cycleslate); struct NativeEventFlag { u32 size; - char name[32]; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; u32 attr; u32 initPattern; u32 currentPattern; @@ -188,8 +188,8 @@ int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPatte SceUID id = kernelObjects.Create(e); e->nef.size = sizeof(NativeEventFlag); - strncpy(e->nef.name, name, 31); - e->nef.name[31] = 0; + strncpy(e->nef.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + e->nef.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; e->nef.attr = flag_attr; e->nef.initPattern = flag_initPattern; e->nef.currentPattern = e->nef.initPattern; diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 603262433e..ade087e12c 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -34,7 +34,7 @@ int mbxWaitTimer = 0; struct NativeMbx { SceSize size; - char name[32]; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; SceUInt attr; int numWaitThreads; int numMessages; @@ -184,8 +184,8 @@ SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) SceUID id = kernelObjects.Create(m); m->nmb.size = sizeof(NativeMbx); - strncpy(m->nmb.name, name, 31); - m->nmb.name[31] = 0; + strncpy(m->nmb.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + m->nmb.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; m->nmb.attr = attr; m->nmb.numWaitThreads = 0; m->nmb.numMessages = 0; diff --git a/Core/HLE/sceKernelMutex.cpp b/Core/HLE/sceKernelMutex.cpp index 4dcea03d7f..77d2a957c6 100644 --- a/Core/HLE/sceKernelMutex.cpp +++ b/Core/HLE/sceKernelMutex.cpp @@ -49,7 +49,7 @@ struct NativeMutex { SceSize size; - char name[32]; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; SceUInt attr; int lockLevel; @@ -70,7 +70,7 @@ struct Mutex : public KernelObject struct NativeLwMutex { SceSize size; - char name[32]; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; SceUInt attr; SceUInt workareaPtr; }; @@ -207,8 +207,8 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio SceUID id = kernelObjects.Create(mutex); mutex->nm.size = sizeof(mutex); - strncpy(mutex->nm.name, name, 31); - mutex->nm.name[31] = 0; + strncpy(mutex->nm.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + mutex->nm.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; mutex->nm.attr = attr; if (initialCount == 0) { @@ -522,8 +522,8 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init LwMutex *mutex = new LwMutex(); SceUID id = kernelObjects.Create(mutex); mutex->nm.size = sizeof(mutex); - strncpy(mutex->nm.name, name, 31); - mutex->nm.name[31] = 0; + strncpy(mutex->nm.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + mutex->nm.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; mutex->nm.attr = attr; mutex->nm.workareaPtr = workareaPtr; diff --git a/Core/HLE/sceKernelSemaphore.cpp b/Core/HLE/sceKernelSemaphore.cpp index dedfa8b8d1..5b9bdd3d7a 100644 --- a/Core/HLE/sceKernelSemaphore.cpp +++ b/Core/HLE/sceKernelSemaphore.cpp @@ -35,7 +35,7 @@ struct NativeSemaphore /** Size of the ::SceKernelSemaInfo structure. */ SceSize size; /** NUL-terminated name of the semaphore. */ - char name[32]; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; /** Attributes. */ SceUInt attr; /** The initial count the semaphore was created with. */ @@ -191,8 +191,8 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32 SceUID id = kernelObjects.Create(s); s->ns.size = sizeof(NativeSemaphore); - strncpy(s->ns.name, name, 31); - s->ns.name[31] = 0; + strncpy(s->ns.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + s->ns.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0; s->ns.attr = attr; s->ns.initCount = initVal; s->ns.currentCount = s->ns.initCount; From 599465ee6e5a0ec2bfb214045871fcbafb4f9013 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Dec 2012 18:40:47 -0800 Subject: [PATCH 5/7] Correct priority handling for Mbx funcs. Also, moved to correctly updating the linked list. It's possible a game might follow it since it's there. --- Core/HLE/sceKernel.cpp | 6 +- Core/HLE/sceKernelMbx.cpp | 245 ++++++++++++++++++++++++-------------- Core/HLE/sceKernelMbx.h | 16 +-- 3 files changed, 160 insertions(+), 107 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 2dc0215507..662cc30d68 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -437,9 +437,9 @@ const HLEFunction ThreadManForUser[] = {0x8125221D,&WrapI_CUU,"sceKernelCreateMbx"}, {0x86255ADA,&WrapI_I,"sceKernelDeleteMbx"}, - {0xE9B3061E,&WrapV_IU,"sceKernelSendMbx"}, - {0x18260574,&WrapV_IUU,"sceKernelReceiveMbx"}, - {0xF3986382,&WrapV_IUU,"sceKernelReceiveMbxCB"}, + {0xE9B3061E,&WrapI_IU,"sceKernelSendMbx"}, + {0x18260574,&WrapI_IUU,"sceKernelReceiveMbx"}, + {0xF3986382,&WrapI_IUU,"sceKernelReceiveMbxCB"}, {0x0D81716A,&WrapI_IU,"sceKernelPollMbx"}, {0x87D4DD36,&WrapI_IU,"sceKernelCancelReceiveMbx"}, {0xA8E8C846,&WrapI_IU,"sceKernelReferMbxStatus"}, diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index ade087e12c..30d5a9d230 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -25,6 +25,8 @@ #define SCE_KERNEL_MBA_MSPRI 0x400 #define SCE_KERNEL_MBA_ATTR_KNOWN (SCE_KERNEL_MBA_THPRI | SCE_KERNEL_MBA_MSPRI) +const int PSP_MBX_ERROR_DUPLICATE_MSG = 0x800201C9; + typedef std::pair MbxWaitingThread; void __KernelMbxTimeout(u64 userdata, int cyclesLate); @@ -50,20 +52,68 @@ struct Mbx : public KernelObject void AddWaitingThread(SceUID id, u32 addr) { + bool inserted = false; if (nmb.attr & SCE_KERNEL_MBA_THPRI) { for (std::vector::iterator it = waitingThreads.begin(); it != waitingThreads.end(); it++) { - if (__KernelGetThreadPrio(id) >= __KernelGetThreadPrio((*it).first)) + if (__KernelGetThreadPrio(id) < __KernelGetThreadPrio((*it).first)) { waitingThreads.insert(it, std::make_pair(id, addr)); + inserted = true; break; } } } + if (!inserted) + waitingThreads.push_back(std::make_pair(id, addr)); + } + + void AddMessage(int insertPos, u32 ptr) + { + int size = messageQueue.size() + 1; + if (insertPos >= 0) + messageQueue.insert(messageQueue.begin() + insertPos, ptr); else { - waitingThreads.push_back(std::make_pair(id, addr)); + messageQueue.push_back(ptr); + insertPos = size - 1; + } + nmb.numMessages++; + + // Link up the linked list. + if (insertPos >= 1) + Memory::Write_U32(ptr, messageQueue[insertPos - 1]); + else + { + nmb.packetListHead = ptr; + if (size > 1) + Memory::Write_U32(ptr, messageQueue[size - 1]); + else + Memory::Write_U32(ptr, ptr); + } + if (insertPos + 1 >= size) + Memory::Write_U32(nmb.packetListHead, ptr); + else + Memory::Write_U32(messageQueue[insertPos + 1], ptr); + } + + void ReceiveMessage(u32 receivePtr) + { + u32 ptr = messageQueue[0]; + messageQueue.erase(messageQueue.begin()); + nmb.numMessages--; + + // Tell the receiver about the message. + Memory::Write_U32(ptr, receivePtr); + + // Clean up the linked list. + if (messageQueue.empty()) + nmb.packetListHead = 0; + else + { + nmb.packetListHead = messageQueue[0]; + Memory::Write_U32(nmb.packetListHead, messageQueue[messageQueue.size() - 1]); } } @@ -89,15 +139,6 @@ bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int re if (waitID != m->GetUID()) return true; - if (result == 0) - m->nmb.numWaitThreads--; - else - { - // Null it out since nothing was received. - if (Memory::IsValidAddress(th.second)) - Memory::Write_U32(0, th.second); - } - if (timeoutPtr != 0 && mbxWaitTimer != 0) { // Remove any event for this thread. @@ -119,7 +160,7 @@ void __KernelMbxTimeout(u64 userdata, int cyclesLate) if (timeoutPtr != 0) Memory::Write_U32(0, timeoutPtr); - SceUID mbxID = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error); + SceUID mbxID = __KernelGetWaitID(threadID, WAITTYPE_MBX, error); Mbx *m = kernelObjects.Get(mbxID, error); if (m) { @@ -127,7 +168,8 @@ void __KernelMbxTimeout(u64 userdata, int cyclesLate) // The reason is, if it times out, but what it was waiting on is DELETED prior to it // actually running, it will get a DELETE result instead of a TIMEOUT. // So, we need to remember it or we won't be able to mark it DELETE instead later. - m->nmb.numWaitThreads--; + + // TODO: Should numWaitThreads be decreased yet? } __KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT); @@ -140,10 +182,10 @@ void __KernelWaitMbx(Mbx *m, u32 timeoutPtr) int micro = (int) Memory::Read_U32(timeoutPtr); - // TODO: test timing. - if (micro <= 3) - micro = 15; - else if (micro <= 249) + // This seems to match the actual timing. + if (micro <= 2) + micro = 10; + else if (micro <= 209) micro = 250; // This should call __KernelMbxTimeout() later, unless we cancel it. @@ -163,6 +205,26 @@ void __KernelMbxRemoveThread(Mbx *m, SceUID threadID) } } +std::vector::iterator __KernelMbxFindPriority(std::vector &waiting) +{ + _dbg_assert_msg_(HLE, !waiting.empty(), "__KernelMutexFindPriority: Trying to find best of no threads."); + + std::vector::iterator iter, end, best = waiting.end(); + u32 best_prio = 0xFFFFFFFF; + for (iter = waiting.begin(), end = waiting.end(); iter != end; ++iter) + { + u32 iter_prio = __KernelGetThreadPrio(iter->first); + if (iter_prio < best_prio) + { + best = iter; + best_prio = iter_prio; + } + } + + _dbg_assert_msg_(HLE, best != waiting.end(), "__KernelMutexFindPriority: Returning invalid best thread."); + return best; +} + SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr) { if (!mbxInitComplete) @@ -209,7 +271,7 @@ int sceKernelDeleteMbx(SceUID id) { DEBUG_LOG(HLE, "sceKernelDeleteMbx(%i)", id); - bool wokeThreads; + bool wokeThreads = false; for (size_t i = 0; i < m->waitingThreads.size(); i++) __KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_DELETE, wokeThreads); m->waitingThreads.clear(); @@ -224,64 +286,83 @@ int sceKernelDeleteMbx(SceUID id) return kernelObjects.Destroy(id); } -void sceKernelSendMbx(SceUID id, u32 packetAddr) +int sceKernelSendMbx(SceUID id, u32 packetAddr) { u32 error; Mbx *m = kernelObjects.Get(id, error); + if (!m) + { + ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): invalid mbx id", id, packetAddr); + return error; + } + NativeMbxPacket *addPacket = (NativeMbxPacket*)Memory::GetPointer(packetAddr); if (addPacket == 0) { ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): invalid packet address", id, packetAddr); - RETURN(-1); - return; + return -1; } - if (!m) + // If the queue is empty, maybe someone is waiting. + // We have to check them first, they might've timed out. + if (m->messageQueue.empty()) { - ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): invalid mbx id", id, packetAddr); - RETURN(error); - return; - } - - if (m->waitingThreads.empty()) - { - DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): no threads currently waiting, adding message to queue", id, packetAddr); - if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI) + bool wokeThreads = false; + std::vector::iterator iter; + while (!wokeThreads && !m->waitingThreads.empty()) { - for (std::vector::iterator it = m->messageQueue.begin(); it != m->messageQueue.end(); it++) + if ((m->nmb.attr & SCE_KERNEL_MBA_THPRI) != 0) + iter = __KernelMbxFindPriority(m->waitingThreads); + else + iter = m->waitingThreads.begin(); + + MbxWaitingThread t = *iter; + __KernelUnlockMbxForThread(m, t, error, 0, wokeThreads); + m->waitingThreads.erase(iter); + + if (wokeThreads) { - NativeMbxPacket *p = (NativeMbxPacket*)Memory::GetPointer(*it); - if (addPacket->priority >= p->priority) - { - m->messageQueue.insert(it, packetAddr); - break; - } + DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, t.first); + Memory::Write_U32(packetAddr, t.second); + hleReSchedule("mbx sent"); + + // We don't need to do anything else, finish here. + return 0; } } - else + } + + std::vector::iterator it, end; + for (it = m->messageQueue.begin(), end = m->messageQueue.end(); it != end; it++) + { + if (*it == packetAddr) + return PSP_MBX_ERROR_DUPLICATE_MSG; + } + + DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): no threads currently waiting, adding message to queue", id, packetAddr); + + bool inserted = false; + if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI) + { + NativeMbxPacket p; + for (int i = 0, n = m->messageQueue.size(); i < n; ++i) { - m->messageQueue.push_back(packetAddr); + Memory::ReadStruct(m->messageQueue[i], &p); + if (addPacket->priority < p.priority) + { + m->AddMessage(i, packetAddr); + inserted = true; + break; + } } - RETURN(0); - } - else if (m->messageQueue.empty()) - { - //__KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_DELETE, wokeThreads); - Memory::Write_U32(packetAddr, m->waitingThreads.front().second); - __KernelResumeThreadFromWait(m->waitingThreads.front().first, 0); - DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, m->waitingThreads.front().first); - m->waitingThreads.erase(m->waitingThreads.begin()); - RETURN(0); - hleReSchedule("mbx sent"); - } - else - { - ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): WTF!? thread waiting while there is a message in the queue?", id, packetAddr); - RETURN(-1); } + if (!inserted) + m->AddMessage(-1, packetAddr); + + return 0; } -void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) +int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) { u32 error; Mbx *m = kernelObjects.Get(id, error); @@ -289,29 +370,27 @@ void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) if (!m) { ERROR_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): invalid mbx id", id, packetAddrPtr, timeoutPtr); - RETURN(error); - return; + return error; } if (!m->messageQueue.empty()) { DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr); - Memory::Write_U32(m->messageQueue.front(), packetAddrPtr); - m->messageQueue.erase(m->messageQueue.begin()); - RETURN(0); + m->ReceiveMessage(packetAddrPtr); + return 0; } else { DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); __KernelMbxRemoveThread(m, __KernelGetCurThread()); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); - RETURN(0); __KernelWaitMbx(m, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, false); + return 0; } } -void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) +int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) { u32 error; Mbx *m = kernelObjects.Get(id, error); @@ -319,26 +398,24 @@ void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) if (!m) { ERROR_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): invalid mbx id", id, packetAddrPtr, timeoutPtr); - RETURN(error); - return; + return error; } if (!m->messageQueue.empty()) { DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr); - Memory::Write_U32(m->messageQueue.front(), packetAddrPtr); - m->messageQueue.erase(m->messageQueue.begin()); + m->ReceiveMessage(packetAddrPtr); hleCheckCurrentCallbacks(); - RETURN(0); + return 0; } else { DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr); __KernelMbxRemoveThread(m, __KernelGetCurThread()); m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr); - RETURN(0); __KernelWaitMbx(m, timeoutPtr); __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, true); + return 0; } } @@ -356,8 +433,7 @@ int sceKernelPollMbx(SceUID id, u32 packetAddrPtr) if (!m->messageQueue.empty()) { DEBUG_LOG(HLE, "sceKernelPollMbx(%i, %08x): sending first queue message", id, packetAddrPtr); - Memory::Write_U32(m->messageQueue.front(), packetAddrPtr); - m->messageQueue.erase(m->messageQueue.begin()); + m->ReceiveMessage(packetAddrPtr); return 0; } else @@ -381,7 +457,7 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr) u32 count = m->waitingThreads.size(); DEBUG_LOG(HLE, "sceKernelCancelReceiveMbx(%i, %08x): cancelling %d threads", id, numWaitingThreadsAddr, count); - bool wokeThreads; + bool wokeThreads = false; for (size_t i = 0; i < m->waitingThreads.size(); i++) __KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_CANCEL, wokeThreads); m->waitingThreads.clear(); @@ -404,29 +480,16 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) return error; } - // TODO: Is there a correct error code? + // Should we crash the thread somehow? if (!Memory::IsValidAddress(infoAddr)) return -1; - SceKernelMbxInfo info; - memcpy(&info, &m->nmb, sizeof(SceKernelMbxInfo)); - info.numMessage = m->messageQueue.size(); - info.numWaitThreads = m->waitingThreads.size(); - - if (!m->messageQueue.empty()) + // For whatever reason, it won't write if the size (first member) is 0. + if (Memory::Read_U32(infoAddr) != 0) { - info.topPacketAddr = m->messageQueue[0]; - - // TODO: Do this when sending messages too? - // Fill in the next ptrs in a loop (0 => 1, 1 => 0 for 2.) - for (int dest = 0, src = 1, n = m->messageQueue.size(); dest < n; ++dest, ++src) - Memory::Write_U32(m->messageQueue[src % n], m->messageQueue[dest]); + m->nmb.numWaitThreads = m->waitingThreads.size(); + Memory::WriteStruct(infoAddr, &m->nmb); } - else - info.topPacketAddr = 0; - - Memory::WriteStruct(infoAddr, &info); return 0; } - diff --git a/Core/HLE/sceKernelMbx.h b/Core/HLE/sceKernelMbx.h index 05257b101a..1470e1119b 100644 --- a/Core/HLE/sceKernelMbx.h +++ b/Core/HLE/sceKernelMbx.h @@ -24,21 +24,11 @@ struct NativeMbxPacket u8 padding[3]; }; -struct SceKernelMbxInfo -{ - SceSize size; - char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; - SceUInt attr; - int numWaitThreads; - int numMessage; - u32 topPacketAddr; -}; - SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr); int sceKernelDeleteMbx(SceUID id); -void sceKernelSendMbx(SceUID id, u32 addPacketAddr); -void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr); -void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr); +int sceKernelSendMbx(SceUID id, u32 addPacketAddr); +int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr); +int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr); int sceKernelPollMbx(SceUID id, u32 packetAddrPtr); int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr); int sceKernelReferMbxStatus(SceUID id, u32 infoAddr); From 17cba6adc3792e5395a7a264062afd74fb9372d2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Dec 2012 21:32:52 -0800 Subject: [PATCH 6/7] Switch Mbx to using a linked list. This approximates the error messages for the hardware much better. More complicated, though. --- Core/HLE/sceKernelMbx.cpp | 184 ++++++++++++++++++++++++-------------- 1 file changed, 118 insertions(+), 66 deletions(-) diff --git a/Core/HLE/sceKernelMbx.cpp b/Core/HLE/sceKernelMbx.cpp index 30d5a9d230..bcd3b74d84 100644 --- a/Core/HLE/sceKernelMbx.cpp +++ b/Core/HLE/sceKernelMbx.cpp @@ -69,58 +69,94 @@ struct Mbx : public KernelObject waitingThreads.push_back(std::make_pair(id, addr)); } - void AddMessage(int insertPos, u32 ptr) + inline void AddInitialMessage(u32 ptr) { - int size = messageQueue.size() + 1; - if (insertPos >= 0) - messageQueue.insert(messageQueue.begin() + insertPos, ptr); - else - { - messageQueue.push_back(ptr); - insertPos = size - 1; - } nmb.numMessages++; - - // Link up the linked list. - if (insertPos >= 1) - Memory::Write_U32(ptr, messageQueue[insertPos - 1]); - else - { - nmb.packetListHead = ptr; - if (size > 1) - Memory::Write_U32(ptr, messageQueue[size - 1]); - else - Memory::Write_U32(ptr, ptr); - } - if (insertPos + 1 >= size) - Memory::Write_U32(nmb.packetListHead, ptr); - else - Memory::Write_U32(messageQueue[insertPos + 1], ptr); + Memory::Write_U32(ptr, ptr); + nmb.packetListHead = ptr; } - void ReceiveMessage(u32 receivePtr) + inline void AddFirstMessage(u32 endPtr, u32 ptr) { - u32 ptr = messageQueue[0]; - messageQueue.erase(messageQueue.begin()); - nmb.numMessages--; + nmb.numMessages++; + Memory::Write_U32(nmb.packetListHead, ptr); + Memory::Write_U32(ptr, endPtr); + nmb.packetListHead = ptr; + } + + inline void AddLastMessage(u32 endPtr, u32 ptr) + { + nmb.numMessages++; + Memory::Write_U32(ptr, endPtr); + Memory::Write_U32(nmb.packetListHead, ptr); + } + + inline void AddMessage(u32 beforePtr, u32 afterPtr, u32 ptr) + { + nmb.numMessages++; + Memory::Write_U32(afterPtr, ptr); + Memory::Write_U32(ptr, beforePtr); + } + + int ReceiveMessage(u32 receivePtr) + { + u32 ptr = nmb.packetListHead; + + if (nmb.numMessages == 991) + { + u32 next = Memory::Read_U32(nmb.packetListHead); + u32 next2 = Memory::Read_U32(next); + if (next2 == ptr && next != ptr) + { + Memory::Write_U32(next, next); + nmb.packetListHead = next; + } + else + nmb.packetListHead = 0; + } + else + { + // Check over the linked list and reset the head. + int c = 0; + while (true) + { + u32 next = Memory::Read_U32(nmb.packetListHead); + if (!Memory::IsValidAddress(next)) + return SCE_KERNEL_ERROR_ILLEGAL_ADDR; + if (next == ptr) + { + if (nmb.packetListHead != ptr) + { + next = Memory::Read_U32(next); + Memory::Write_U32(next, nmb.packetListHead); + nmb.packetListHead = next; + break; + } + else + { + if (c < nmb.numMessages - 1) + return PSP_MBX_ERROR_DUPLICATE_MSG; + + nmb.packetListHead = 0; + break; + } + } + + nmb.packetListHead = next; + c++; + } + } // Tell the receiver about the message. Memory::Write_U32(ptr, receivePtr); + nmb.numMessages--; - // Clean up the linked list. - if (messageQueue.empty()) - nmb.packetListHead = 0; - else - { - nmb.packetListHead = messageQueue[0]; - Memory::Write_U32(nmb.packetListHead, messageQueue[messageQueue.size() - 1]); - } + return 0; } NativeMbx nmb; std::vector waitingThreads; - std::vector messageQueue; }; void __KernelMbxInit() @@ -305,7 +341,7 @@ int sceKernelSendMbx(SceUID id, u32 packetAddr) // If the queue is empty, maybe someone is waiting. // We have to check them first, they might've timed out. - if (m->messageQueue.empty()) + if (m->nmb.numMessages == 0) { bool wokeThreads = false; std::vector::iterator iter; @@ -332,32 +368,48 @@ int sceKernelSendMbx(SceUID id, u32 packetAddr) } } - std::vector::iterator it, end; - for (it = m->messageQueue.begin(), end = m->messageQueue.end(); it != end; it++) - { - if (*it == packetAddr) - return PSP_MBX_ERROR_DUPLICATE_MSG; - } - DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): no threads currently waiting, adding message to queue", id, packetAddr); - bool inserted = false; - if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI) + if (m->nmb.numMessages == 0) + m->AddInitialMessage(packetAddr); + else { - NativeMbxPacket p; - for (int i = 0, n = m->messageQueue.size(); i < n; ++i) + u32 next = m->nmb.packetListHead, prev; + for (int i = 0, n = m->nmb.numMessages; i < n; i++) { - Memory::ReadStruct(m->messageQueue[i], &p); - if (addPacket->priority < p.priority) + if (next == packetAddr) + return PSP_MBX_ERROR_DUPLICATE_MSG; + if (!Memory::IsValidAddress(next)) + return SCE_KERNEL_ERROR_ILLEGAL_ADDR; + + prev = next; + next = Memory::Read_U32(next); + } + + bool inserted = false; + if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI) + { + NativeMbxPacket p; + for (int i = 0, n = m->nmb.numMessages; i < n; i++) { - m->AddMessage(i, packetAddr); - inserted = true; - break; + Memory::ReadStruct(next, &p); + if (addPacket->priority < p.priority) + { + if (i == 0) + m->AddFirstMessage(prev, packetAddr); + else + m->AddMessage(prev, next, packetAddr); + inserted = true; + break; + } + + prev = next; + next = Memory::Read_U32(next); } } + if (!inserted) + m->AddLastMessage(prev, packetAddr); } - if (!inserted) - m->AddMessage(-1, packetAddr); return 0; } @@ -373,11 +425,10 @@ int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) return error; } - if (!m->messageQueue.empty()) + if (m->nmb.numMessages > 0) { DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr); - m->ReceiveMessage(packetAddrPtr); - return 0; + return m->ReceiveMessage(packetAddrPtr); } else { @@ -401,12 +452,11 @@ int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr) return error; } - if (!m->messageQueue.empty()) + if (m->nmb.numMessages > 0) { DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr); - m->ReceiveMessage(packetAddrPtr); hleCheckCurrentCallbacks(); - return 0; + return m->ReceiveMessage(packetAddrPtr); } else { @@ -430,11 +480,10 @@ int sceKernelPollMbx(SceUID id, u32 packetAddrPtr) return error; } - if (!m->messageQueue.empty()) + if (m->nmb.numMessages > 0) { DEBUG_LOG(HLE, "sceKernelPollMbx(%i, %08x): sending first queue message", id, packetAddrPtr); - m->ReceiveMessage(packetAddrPtr); - return 0; + return m->ReceiveMessage(packetAddrPtr); } else { @@ -484,6 +533,9 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr) if (!Memory::IsValidAddress(infoAddr)) return -1; + for (int i = 0, n = m->nmb.numMessages; i < n; ++i) + m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead); + // For whatever reason, it won't write if the size (first member) is 0. if (Memory::Read_U32(infoAddr) != 0) { From 22a77b643f3ee03a688d2aaae63f7b6b9a92ea1c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 16 Dec 2012 23:01:43 -0800 Subject: [PATCH 7/7] Update tests. --- pspautotests | 2 +- test.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pspautotests b/pspautotests index 700e0df918..8c1284c758 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 700e0df918512a0fc4105c23ce2198fbfbefb5ae +Subproject commit 8c1284c758c05811517c043bbc781ba8ab724d5e diff --git a/test.py b/test.py index 5a4a3f8647..4ac98e263e 100755 --- a/test.py +++ b/test.py @@ -75,6 +75,14 @@ tests_good = [ "threads/lwmutex/try600/try600", "threads/lwmutex/unlock/unlock", "threads/mbx/mbx", + "threads/mbx/cancel/cancel", + "threads/mbx/create/create", + "threads/mbx/delete/delete", + "threads/mbx/poll/poll", + "threads/mbx/priority/priority", + "threads/mbx/receive/receive", + "threads/mbx/refer/refer", + "threads/mbx/send/send", "threads/mutex/mutex", "threads/mutex/create/create", "threads/mutex/delete/delete",