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