1 // Copyright (c) 2012- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include <map>
19 #include <vector>
20 #include "Common/Serialize/Serializer.h"
21 #include "Common/Serialize/SerializeFuncs.h"
22 #include "Common/Serialize/SerializeMap.h"
23 #include "Core/HLE/sceKernel.h"
24 #include "Core/HLE/sceKernelThread.h"
25 #include "Core/HLE/sceKernelMbx.h"
26 #include "Core/HLE/HLE.h"
27 #include "Core/CoreTiming.h"
28 #include "Core/MemMapHelpers.h"
29 #include "Core/Reporting.h"
30 #include "Core/HLE/KernelWaitHelpers.h"
31
32 #define SCE_KERNEL_MBA_THPRI 0x100
33 #define SCE_KERNEL_MBA_MSPRI 0x400
34 #define SCE_KERNEL_MBA_ATTR_KNOWN (SCE_KERNEL_MBA_THPRI | SCE_KERNEL_MBA_MSPRI)
35
36 const int PSP_MBX_ERROR_DUPLICATE_MSG = 0x800201C9;
37
38 struct MbxWaitingThread
39 {
40 SceUID threadID;
41 u32 packetAddr;
42 u64 pausedTimeout;
43
operator ==MbxWaitingThread44 bool operator ==(const SceUID &otherThreadID) const
45 {
46 return threadID == otherThreadID;
47 }
48 };
49 void __KernelMbxTimeout(u64 userdata, int cyclesLate);
50
51 static int mbxWaitTimer = -1;
52
53 struct NativeMbx
54 {
55 SceSize_le size;
56 char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
57 SceUInt_le attr;
58 s32_le numWaitThreads;
59 s32_le numMessages;
60 u32_le packetListHead;
61 };
62
63 struct Mbx : public KernelObject
64 {
GetNameMbx65 const char *GetName() override { return nmb.name; }
GetTypeNameMbx66 const char *GetTypeName() override { return GetStaticTypeName(); }
GetStaticTypeNameMbx67 static const char *GetStaticTypeName() { return "Mbx"; }
GetMissingErrorCodeMbx68 static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MBXID; }
GetStaticIDTypeMbx69 static int GetStaticIDType() { return SCE_KERNEL_TMID_Mbox; }
GetIDTypeMbx70 int GetIDType() const override { return SCE_KERNEL_TMID_Mbox; }
71
AddWaitingThreadMbx72 void AddWaitingThread(SceUID id, u32 addr)
73 {
74 bool inserted = false;
75 if (nmb.attr & SCE_KERNEL_MBA_THPRI)
76 {
77 for (auto it = waitingThreads.begin(); it != waitingThreads.end(); ++it)
78 {
79 if (__KernelGetThreadPrio(id) < __KernelGetThreadPrio(it->threadID))
80 {
81 MbxWaitingThread waiting = {id, addr};
82 waitingThreads.insert(it, waiting);
83 inserted = true;
84 break;
85 }
86 }
87 }
88 if (!inserted)
89 {
90 MbxWaitingThread waiting = {id, addr};
91 waitingThreads.push_back(waiting);
92 }
93 }
94
AddInitialMessageMbx95 inline void AddInitialMessage(u32 ptr)
96 {
97 nmb.numMessages++;
98 Memory::Write_U32(ptr, ptr);
99 nmb.packetListHead = ptr;
100 }
101
AddFirstMessageMbx102 inline void AddFirstMessage(u32 endPtr, u32 ptr)
103 {
104 nmb.numMessages++;
105 Memory::Write_U32(nmb.packetListHead, ptr);
106 Memory::Write_U32(ptr, endPtr);
107 nmb.packetListHead = ptr;
108 }
109
AddLastMessageMbx110 inline void AddLastMessage(u32 endPtr, u32 ptr)
111 {
112 nmb.numMessages++;
113 Memory::Write_U32(ptr, endPtr);
114 Memory::Write_U32(nmb.packetListHead, ptr);
115 }
116
AddMessageMbx117 inline void AddMessage(u32 beforePtr, u32 afterPtr, u32 ptr)
118 {
119 nmb.numMessages++;
120 Memory::Write_U32(afterPtr, ptr);
121 Memory::Write_U32(ptr, beforePtr);
122 }
123
ReceiveMessageMbx124 int ReceiveMessage(u32 receivePtr)
125 {
126 u32 ptr = nmb.packetListHead;
127
128 // Check over the linked list and reset the head.
129 int c = 0;
130 while (true)
131 {
132 u32 next = Memory::Read_U32(nmb.packetListHead);
133 if (!Memory::IsValidAddress(next))
134 return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
135 if (next == ptr)
136 {
137 if (nmb.packetListHead != ptr)
138 {
139 next = Memory::Read_U32(next);
140 Memory::Write_U32(next, nmb.packetListHead);
141 nmb.packetListHead = next;
142 break;
143 }
144 else
145 {
146 if (c < nmb.numMessages - 1)
147 return PSP_MBX_ERROR_DUPLICATE_MSG;
148
149 nmb.packetListHead = 0;
150 break;
151 }
152 }
153
154 nmb.packetListHead = next;
155 c++;
156 }
157
158 // Tell the receiver about the message.
159 Memory::Write_U32(ptr, receivePtr);
160 nmb.numMessages--;
161
162 return 0;
163 }
164
DoStateMbx165 void DoState(PointerWrap &p) override
166 {
167 auto s = p.Section("Mbx", 1);
168 if (!s)
169 return;
170
171 Do(p, nmb);
172 MbxWaitingThread mwt = {0};
173 Do(p, waitingThreads, mwt);
174 Do(p, pausedWaits);
175 }
176
177 NativeMbx nmb;
178
179 std::vector<MbxWaitingThread> waitingThreads;
180 // Key is the callback id it was for, or if no callback, the thread id.
181 std::map<SceUID, MbxWaitingThread> pausedWaits;
182 };
183
184 void __KernelMbxBeginCallback(SceUID threadID, SceUID prevCallbackId);
185 void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId);
186
__KernelMbxInit()187 void __KernelMbxInit()
188 {
189 mbxWaitTimer = CoreTiming::RegisterEvent("MbxTimeout", __KernelMbxTimeout);
190 __KernelRegisterWaitTypeFuncs(WAITTYPE_MBX, __KernelMbxBeginCallback, __KernelMbxEndCallback);
191 }
192
__KernelMbxDoState(PointerWrap & p)193 void __KernelMbxDoState(PointerWrap &p)
194 {
195 auto s = p.Section("sceKernelMbx", 1);
196 if (!s)
197 return;
198
199 Do(p, mbxWaitTimer);
200 CoreTiming::RestoreRegisterEvent(mbxWaitTimer, "MbxTimeout", __KernelMbxTimeout);
201 }
202
__KernelMbxObject()203 KernelObject *__KernelMbxObject()
204 {
205 return new Mbx;
206 }
207
__KernelUnlockMbxForThread(Mbx * m,MbxWaitingThread & th,u32 & error,int result,bool & wokeThreads)208 static bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int result, bool &wokeThreads)
209 {
210 if (!HLEKernel::VerifyWait(th.threadID, WAITTYPE_MBX, m->GetUID()))
211 return true;
212
213 u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.threadID, error);
214 if (timeoutPtr != 0 && mbxWaitTimer != -1)
215 {
216 // Remove any event for this thread.
217 s64 cyclesLeft = CoreTiming::UnscheduleEvent(mbxWaitTimer, th.threadID);
218 Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
219 }
220
221 __KernelResumeThreadFromWait(th.threadID, result);
222 wokeThreads = true;
223 return true;
224 }
225
__KernelUnlockMbxForThreadCheck(Mbx * m,MbxWaitingThread & waitData,u32 & error,int result,bool & wokeThreads)226 static bool __KernelUnlockMbxForThreadCheck(Mbx *m, MbxWaitingThread &waitData, u32 &error, int result, bool &wokeThreads)
227 {
228 if (m->nmb.numMessages > 0 && __KernelUnlockMbxForThread(m, waitData, error, 0, wokeThreads))
229 {
230 m->ReceiveMessage(waitData.packetAddr);
231 return true;
232 }
233 return false;
234 }
235
__KernelMbxBeginCallback(SceUID threadID,SceUID prevCallbackId)236 void __KernelMbxBeginCallback(SceUID threadID, SceUID prevCallbackId)
237 {
238 auto result = HLEKernel::WaitBeginCallback<Mbx, WAITTYPE_MBX, MbxWaitingThread>(threadID, prevCallbackId, mbxWaitTimer);
239 if (result == HLEKernel::WAIT_CB_SUCCESS)
240 DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB: Suspending mbx wait for callback");
241 else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA)
242 ERROR_LOG_REPORT(SCEKERNEL, "sceKernelReceiveMbxCB: wait not found to pause for callback");
243 else
244 WARN_LOG_REPORT(SCEKERNEL, "sceKernelReceiveMbxCB: beginning callback with bad wait id?");
245 }
246
__KernelMbxEndCallback(SceUID threadID,SceUID prevCallbackId)247 void __KernelMbxEndCallback(SceUID threadID, SceUID prevCallbackId)
248 {
249 auto result = HLEKernel::WaitEndCallback<Mbx, WAITTYPE_MBX, MbxWaitingThread>(threadID, prevCallbackId, mbxWaitTimer, __KernelUnlockMbxForThreadCheck);
250 if (result == HLEKernel::WAIT_CB_RESUMED_WAIT)
251 DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB: Resuming mbx wait from callback");
252 }
253
__KernelMbxTimeout(u64 userdata,int cyclesLate)254 void __KernelMbxTimeout(u64 userdata, int cyclesLate)
255 {
256 SceUID threadID = (SceUID)userdata;
257 HLEKernel::WaitExecTimeout<Mbx, WAITTYPE_MBX>(threadID);
258 }
259
__KernelWaitMbx(Mbx * m,u32 timeoutPtr)260 static void __KernelWaitMbx(Mbx *m, u32 timeoutPtr)
261 {
262 if (timeoutPtr == 0 || mbxWaitTimer == -1)
263 return;
264
265 int micro = (int) Memory::Read_U32(timeoutPtr);
266
267 // This seems to match the actual timing.
268 if (micro <= 2)
269 micro = 20;
270 else if (micro <= 209)
271 micro = 250;
272
273 // This should call __KernelMbxTimeout() later, unless we cancel it.
274 CoreTiming::ScheduleEvent(usToCycles(micro), mbxWaitTimer, __KernelGetCurThread());
275 }
276
__KernelMbxFindPriority(std::vector<MbxWaitingThread> & waiting)277 static std::vector<MbxWaitingThread>::iterator __KernelMbxFindPriority(std::vector<MbxWaitingThread> &waiting)
278 {
279 _dbg_assert_msg_(!waiting.empty(), "__KernelMutexFindPriority: Trying to find best of no threads.");
280
281 std::vector<MbxWaitingThread>::iterator iter, end, best = waiting.end();
282 u32 best_prio = 0xFFFFFFFF;
283 for (iter = waiting.begin(), end = waiting.end(); iter != end; ++iter)
284 {
285 u32 iter_prio = __KernelGetThreadPrio(iter->threadID);
286 if (iter_prio < best_prio)
287 {
288 best = iter;
289 best_prio = iter_prio;
290 }
291 }
292
293 _dbg_assert_msg_(best != waiting.end(), "__KernelMutexFindPriority: Returning invalid best thread.");
294 return best;
295 }
296
sceKernelCreateMbx(const char * name,u32 attr,u32 optAddr)297 SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr)
298 {
299 if (!name)
300 {
301 WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateMbx(): invalid name", SCE_KERNEL_ERROR_ERROR);
302 return SCE_KERNEL_ERROR_ERROR;
303 }
304 // Accepts 0x000 - 0x0FF, 0x100 - 0x1FF, and 0x400 - 0x4FF.
305 if (((attr & ~SCE_KERNEL_MBA_ATTR_KNOWN) & ~0xFF) != 0)
306 {
307 WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateMbx(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
308 return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
309 }
310
311 Mbx *m = new Mbx();
312 SceUID id = kernelObjects.Create(m);
313
314 m->nmb.size = sizeof(NativeMbx);
315 strncpy(m->nmb.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
316 m->nmb.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
317 m->nmb.attr = attr;
318 m->nmb.numWaitThreads = 0;
319 m->nmb.numMessages = 0;
320 m->nmb.packetListHead = 0;
321
322 DEBUG_LOG(SCEKERNEL, "%i=sceKernelCreateMbx(%s, %08x, %08x)", id, name, attr, optAddr);
323
324 if (optAddr != 0)
325 {
326 u32 size = Memory::Read_U32(optAddr);
327 if (size > 4)
328 WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateMbx(%s) unsupported options parameter, size = %d", name, size);
329 }
330 if ((attr & ~SCE_KERNEL_MBA_ATTR_KNOWN) != 0)
331 WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateMbx(%s) unsupported attr parameter: %08x", name, attr);
332
333 return id;
334 }
335
sceKernelDeleteMbx(SceUID id)336 int sceKernelDeleteMbx(SceUID id)
337 {
338 u32 error;
339 Mbx *m = kernelObjects.Get<Mbx>(id, error);
340 if (m)
341 {
342 DEBUG_LOG(SCEKERNEL, "sceKernelDeleteMbx(%i)", id);
343
344 bool wokeThreads = false;
345 for (size_t i = 0; i < m->waitingThreads.size(); i++)
346 __KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_DELETE, wokeThreads);
347 m->waitingThreads.clear();
348
349 if (wokeThreads)
350 hleReSchedule("mbx deleted");
351 }
352 else
353 {
354 ERROR_LOG(SCEKERNEL, "sceKernelDeleteMbx(%i): invalid mbx id", id);
355 }
356 return kernelObjects.Destroy<Mbx>(id);
357 }
358
sceKernelSendMbx(SceUID id,u32 packetAddr)359 int sceKernelSendMbx(SceUID id, u32 packetAddr)
360 {
361 u32 error;
362 Mbx *m = kernelObjects.Get<Mbx>(id, error);
363 if (!m)
364 {
365 ERROR_LOG(SCEKERNEL, "sceKernelSendMbx(%i, %08x): invalid mbx id", id, packetAddr);
366 return error;
367 }
368
369 NativeMbxPacket *addPacket = (NativeMbxPacket*)Memory::GetPointer(packetAddr);
370 if (addPacket == 0)
371 {
372 ERROR_LOG(SCEKERNEL, "sceKernelSendMbx(%i, %08x): invalid packet address", id, packetAddr);
373 return -1;
374 }
375
376 // If the queue is empty, maybe someone is waiting.
377 // We have to check them first, they might've timed out.
378 if (m->nmb.numMessages == 0)
379 {
380 bool wokeThreads = false;
381 std::vector<MbxWaitingThread>::iterator iter;
382 while (!wokeThreads && !m->waitingThreads.empty())
383 {
384 if ((m->nmb.attr & SCE_KERNEL_MBA_THPRI) != 0)
385 iter = __KernelMbxFindPriority(m->waitingThreads);
386 else
387 iter = m->waitingThreads.begin();
388
389 MbxWaitingThread t = *iter;
390 __KernelUnlockMbxForThread(m, t, error, 0, wokeThreads);
391 m->waitingThreads.erase(iter);
392
393 if (wokeThreads)
394 {
395 DEBUG_LOG(SCEKERNEL, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, t.threadID);
396 Memory::Write_U32(packetAddr, t.packetAddr);
397 hleReSchedule("mbx sent");
398
399 // We don't need to do anything else, finish here.
400 return 0;
401 }
402 }
403 }
404
405 DEBUG_LOG(SCEKERNEL, "sceKernelSendMbx(%i, %08x): no threads currently waiting, adding message to queue", id, packetAddr);
406
407 if (m->nmb.numMessages == 0)
408 m->AddInitialMessage(packetAddr);
409 else
410 {
411 u32 next = m->nmb.packetListHead, prev = 0;
412 for (int i = 0, n = m->nmb.numMessages; i < n; i++)
413 {
414 if (next == packetAddr)
415 return PSP_MBX_ERROR_DUPLICATE_MSG;
416 if (!Memory::IsValidAddress(next))
417 return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
418
419 prev = next;
420 next = Memory::Read_U32(next);
421 }
422
423 bool inserted = false;
424 if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI)
425 {
426 for (int i = 0, n = m->nmb.numMessages; i < n; i++)
427 {
428 auto p = PSPPointer<NativeMbxPacket>::Create(next);
429 if (addPacket->priority < p->priority)
430 {
431 if (i == 0)
432 m->AddFirstMessage(prev, packetAddr);
433 else
434 m->AddMessage(prev, next, packetAddr);
435 inserted = true;
436 break;
437 }
438
439 prev = next;
440 next = p->next;
441 }
442 }
443 if (!inserted)
444 m->AddLastMessage(prev, packetAddr);
445 }
446
447 return 0;
448 }
449
sceKernelReceiveMbx(SceUID id,u32 packetAddrPtr,u32 timeoutPtr)450 int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
451 {
452 u32 error;
453 Mbx *m = kernelObjects.Get<Mbx>(id, error);
454
455 if (!m)
456 {
457 ERROR_LOG(SCEKERNEL, "sceKernelReceiveMbx(%i, %08x, %08x): invalid mbx id", id, packetAddrPtr, timeoutPtr);
458 return error;
459 }
460
461 if (m->nmb.numMessages > 0)
462 {
463 DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbx(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr);
464 return m->ReceiveMessage(packetAddrPtr);
465 }
466 else
467 {
468 DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr);
469 HLEKernel::RemoveWaitingThread(m->waitingThreads, __KernelGetCurThread());
470 m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr);
471 __KernelWaitMbx(m, timeoutPtr);
472 __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, false, "mbx waited");
473 return 0;
474 }
475 }
476
sceKernelReceiveMbxCB(SceUID id,u32 packetAddrPtr,u32 timeoutPtr)477 int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
478 {
479 u32 error;
480 Mbx *m = kernelObjects.Get<Mbx>(id, error);
481
482 if (!m)
483 {
484 ERROR_LOG(SCEKERNEL, "sceKernelReceiveMbxCB(%i, %08x, %08x): invalid mbx id", id, packetAddrPtr, timeoutPtr);
485 return error;
486 }
487
488 if (m->nmb.numMessages > 0)
489 {
490 DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr);
491 hleCheckCurrentCallbacks();
492 return m->ReceiveMessage(packetAddrPtr);
493 }
494 else
495 {
496 DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr);
497 HLEKernel::RemoveWaitingThread(m->waitingThreads, __KernelGetCurThread());
498 m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr);
499 __KernelWaitMbx(m, timeoutPtr);
500 __KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, true, "mbx waited");
501 return 0;
502 }
503 }
504
sceKernelPollMbx(SceUID id,u32 packetAddrPtr)505 int sceKernelPollMbx(SceUID id, u32 packetAddrPtr)
506 {
507 u32 error;
508 Mbx *m = kernelObjects.Get<Mbx>(id, error);
509
510 if (!m)
511 {
512 ERROR_LOG(SCEKERNEL, "sceKernelPollMbx(%i, %08x): invalid mbx id", id, packetAddrPtr);
513 return error;
514 }
515
516 if (m->nmb.numMessages > 0)
517 {
518 DEBUG_LOG(SCEKERNEL, "sceKernelPollMbx(%i, %08x): sending first queue message", id, packetAddrPtr);
519 return m->ReceiveMessage(packetAddrPtr);
520 }
521 else
522 {
523 DEBUG_LOG(SCEKERNEL, "SCE_KERNEL_ERROR_MBOX_NOMSG=sceKernelPollMbx(%i, %08x): no message in queue", id, packetAddrPtr);
524 return SCE_KERNEL_ERROR_MBOX_NOMSG;
525 }
526 }
527
sceKernelCancelReceiveMbx(SceUID id,u32 numWaitingThreadsAddr)528 int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr)
529 {
530 u32 error;
531 Mbx *m = kernelObjects.Get<Mbx>(id, error);
532
533 if (!m)
534 {
535 ERROR_LOG(SCEKERNEL, "sceKernelCancelReceiveMbx(%i, %08x): invalid mbx id", id, numWaitingThreadsAddr);
536 return error;
537 }
538
539 u32 count = (u32) m->waitingThreads.size();
540 DEBUG_LOG(SCEKERNEL, "sceKernelCancelReceiveMbx(%i, %08x): cancelling %d threads", id, numWaitingThreadsAddr, count);
541
542 bool wokeThreads = false;
543 for (size_t i = 0; i < m->waitingThreads.size(); i++)
544 __KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_CANCEL, wokeThreads);
545 m->waitingThreads.clear();
546
547 if (wokeThreads)
548 hleReSchedule("mbx canceled");
549
550 if (numWaitingThreadsAddr)
551 Memory::Write_U32(count, numWaitingThreadsAddr);
552 return 0;
553 }
554
sceKernelReferMbxStatus(SceUID id,u32 infoAddr)555 int sceKernelReferMbxStatus(SceUID id, u32 infoAddr)
556 {
557 u32 error;
558 Mbx *m = kernelObjects.Get<Mbx>(id, error);
559 if (!m)
560 {
561 ERROR_LOG(SCEKERNEL, "sceKernelReferMbxStatus(%i, %08x): invalid mbx id", id, infoAddr);
562 return error;
563 }
564
565 // Should we crash the thread somehow?
566 if (!Memory::IsValidAddress(infoAddr))
567 return -1;
568
569 for (int i = 0, n = m->nmb.numMessages; i < n; ++i)
570 m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead);
571
572 HLEKernel::CleanupWaitingThreads(WAITTYPE_MBX, id, m->waitingThreads);
573
574 // For whatever reason, it won't write if the size (first member) is 0.
575 if (Memory::Read_U32(infoAddr) != 0)
576 {
577 m->nmb.numWaitThreads = (int) m->waitingThreads.size();
578 Memory::WriteStruct<NativeMbx>(infoAddr, &m->nmb);
579 }
580
581 return 0;
582 }
583