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 <algorithm>
19 #include "Common/Serialize/Serializer.h"
20 #include "Common/Serialize/SerializeFuncs.h"
21 #include "Common/Serialize/SerializeMap.h"
22 #include "Core/HLE/HLE.h"
23 #include "Core/MIPS/MIPS.h"
24 #include "Core/CoreTiming.h"
25 #include "Core/MemMapHelpers.h"
26 #include "Core/Reporting.h"
27 #include "Core/HLE/sceKernel.h"
28 #include "Core/HLE/sceKernelThread.h"
29 #include "Core/HLE/sceKernelSemaphore.h"
30 #include "Core/HLE/KernelWaitHelpers.h"
31 #include "Core/HLE/FunctionWrappers.h"
32
33 #define PSP_SEMA_ATTR_FIFO 0
34 #define PSP_SEMA_ATTR_PRIORITY 0x100
35
36 /** Current state of a semaphore.
37 * @see sceKernelReferSemaStatus.
38 */
39
40 struct NativeSemaphore
41 {
42 /** Size of the ::SceKernelSemaInfo structure. */
43 SceSize_le size;
44 /** NUL-terminated name of the semaphore. */
45 char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
46 /** Attributes. */
47 SceUInt_le attr;
48 /** The initial count the semaphore was created with. */
49 s32_le initCount;
50 /** The current count. */
51 s32_le currentCount;
52 /** The maximum count. */
53 s32_le maxCount;
54 /** The number of threads waiting on the semaphore. */
55 s32_le numWaitThreads;
56 };
57
58
59 struct PSPSemaphore : public KernelObject {
GetNamePSPSemaphore60 const char *GetName() override { return ns.name; }
GetTypeNamePSPSemaphore61 const char *GetTypeName() override { return GetStaticTypeName(); }
GetStaticTypeNamePSPSemaphore62 static const char *GetStaticTypeName() { return "Semaphore"; }
63
GetMissingErrorCodePSPSemaphore64 static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_SEMID; }
GetStaticIDTypePSPSemaphore65 static int GetStaticIDType() { return SCE_KERNEL_TMID_Semaphore; }
GetIDTypePSPSemaphore66 int GetIDType() const override { return SCE_KERNEL_TMID_Semaphore; }
67
DoStatePSPSemaphore68 void DoState(PointerWrap &p) override
69 {
70 auto s = p.Section("Semaphore", 1);
71 if (!s)
72 return;
73
74 Do(p, ns);
75 SceUID dv = 0;
76 Do(p, waitingThreads, dv);
77 Do(p, pausedWaits);
78 }
79
80 NativeSemaphore ns;
81 std::vector<SceUID> waitingThreads;
82 // Key is the callback id it was for, or if no callback, the thread id.
83 std::map<SceUID, u64> pausedWaits;
84 };
85
86 static int semaWaitTimer = -1;
87
88 void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId);
89 void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId);
90
__KernelSemaInit()91 void __KernelSemaInit()
92 {
93 semaWaitTimer = CoreTiming::RegisterEvent("SemaphoreTimeout", __KernelSemaTimeout);
94 __KernelRegisterWaitTypeFuncs(WAITTYPE_SEMA, __KernelSemaBeginCallback, __KernelSemaEndCallback);
95 }
96
__KernelSemaDoState(PointerWrap & p)97 void __KernelSemaDoState(PointerWrap &p)
98 {
99 auto s = p.Section("sceKernelSema", 1);
100 if (!s)
101 return;
102
103 Do(p, semaWaitTimer);
104 CoreTiming::RestoreRegisterEvent(semaWaitTimer, "SemaphoreTimeout", __KernelSemaTimeout);
105 }
106
__KernelSemaphoreObject()107 KernelObject *__KernelSemaphoreObject()
108 {
109 return new PSPSemaphore;
110 }
111
112 // Returns whether the thread should be removed.
__KernelUnlockSemaForThread(PSPSemaphore * s,SceUID threadID,u32 & error,int result,bool & wokeThreads)113 static bool __KernelUnlockSemaForThread(PSPSemaphore *s, SceUID threadID, u32 &error, int result, bool &wokeThreads) {
114 if (!HLEKernel::VerifyWait(threadID, WAITTYPE_SEMA, s->GetUID()))
115 return true;
116
117 // If result is an error code, we're just letting it go.
118 if (result == 0)
119 {
120 int wVal = (int) __KernelGetWaitValue(threadID, error);
121 if (wVal > s->ns.currentCount)
122 return false;
123
124 s->ns.currentCount -= wVal;
125 }
126
127 u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
128 if (timeoutPtr != 0 && semaWaitTimer != -1)
129 {
130 // Remove any event for this thread.
131 s64 cyclesLeft = CoreTiming::UnscheduleEvent(semaWaitTimer, threadID);
132 if (cyclesLeft < 0)
133 cyclesLeft = 0;
134 Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
135 }
136
137 __KernelResumeThreadFromWait(threadID, result);
138 wokeThreads = true;
139 return true;
140 }
141
__KernelSemaBeginCallback(SceUID threadID,SceUID prevCallbackId)142 void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId)
143 {
144 auto result = HLEKernel::WaitBeginCallback<PSPSemaphore, WAITTYPE_SEMA, SceUID>(threadID, prevCallbackId, semaWaitTimer);
145 if (result == HLEKernel::WAIT_CB_SUCCESS)
146 DEBUG_LOG(SCEKERNEL, "sceKernelWaitSemaCB: Suspending sema wait for callback");
147 else
148 WARN_LOG_REPORT(SCEKERNEL, "sceKernelWaitSemaCB: beginning callback with bad wait id?");
149 }
150
__KernelSemaEndCallback(SceUID threadID,SceUID prevCallbackId)151 void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId)
152 {
153 auto result = HLEKernel::WaitEndCallback<PSPSemaphore, WAITTYPE_SEMA, SceUID>(threadID, prevCallbackId, semaWaitTimer, __KernelUnlockSemaForThread);
154 if (result == HLEKernel::WAIT_CB_RESUMED_WAIT)
155 DEBUG_LOG(SCEKERNEL, "sceKernelWaitSemaCB: Resuming sema wait for callback");
156 }
157
158 // Resume all waiting threads (for delete / cancel.)
159 // Returns true if it woke any threads.
__KernelClearSemaThreads(PSPSemaphore * s,int reason)160 static bool __KernelClearSemaThreads(PSPSemaphore *s, int reason) {
161 u32 error;
162 bool wokeThreads = false;
163 std::vector<SceUID>::iterator iter, end;
164 for (iter = s->waitingThreads.begin(), end = s->waitingThreads.end(); iter != end; ++iter)
165 __KernelUnlockSemaForThread(s, *iter, error, reason, wokeThreads);
166 s->waitingThreads.clear();
167
168 return wokeThreads;
169 }
170
sceKernelCancelSema(SceUID id,int newCount,u32 numWaitThreadsPtr)171 int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr)
172 {
173 u32 error;
174 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
175 if (s)
176 {
177 if (newCount > s->ns.maxCount)
178 {
179 DEBUG_LOG(SCEKERNEL, "sceKernelCancelSema(%i, %i, %08x): invalid count", id, newCount, numWaitThreadsPtr);
180 return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
181 }
182
183 DEBUG_LOG(SCEKERNEL, "sceKernelCancelSema(%i, %i, %08x)", id, newCount, numWaitThreadsPtr);
184
185 s->ns.numWaitThreads = (int) s->waitingThreads.size();
186 if (Memory::IsValidAddress(numWaitThreadsPtr))
187 Memory::Write_U32(s->ns.numWaitThreads, numWaitThreadsPtr);
188
189 if (newCount < 0)
190 s->ns.currentCount = s->ns.initCount;
191 else
192 s->ns.currentCount = newCount;
193
194 if (__KernelClearSemaThreads(s, SCE_KERNEL_ERROR_WAIT_CANCEL))
195 hleReSchedule("semaphore canceled");
196
197 return 0;
198 }
199 else
200 {
201 DEBUG_LOG(SCEKERNEL, "sceKernelCancelSema(%i, %i, %08x): invalid semaphore", id, newCount, numWaitThreadsPtr);
202 return error;
203 }
204 }
205
sceKernelCreateSema(const char * name,u32 attr,int initVal,int maxVal,u32 optionPtr)206 int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32 optionPtr)
207 {
208 if (!name)
209 {
210 WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateSema(): invalid name", SCE_KERNEL_ERROR_ERROR);
211 return SCE_KERNEL_ERROR_ERROR;
212 }
213 if (attr >= 0x200)
214 {
215 WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateSema(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
216 return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
217 }
218
219 PSPSemaphore *s = new PSPSemaphore();
220 SceUID id = kernelObjects.Create(s);
221
222 s->ns.size = sizeof(NativeSemaphore);
223 strncpy(s->ns.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
224 s->ns.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
225 s->ns.attr = attr;
226 s->ns.initCount = initVal;
227 s->ns.currentCount = s->ns.initCount;
228 s->ns.maxCount = maxVal;
229 s->ns.numWaitThreads = 0;
230
231 DEBUG_LOG(SCEKERNEL, "%i=sceKernelCreateSema(%s, %08x, %i, %i, %08x)", id, s->ns.name, s->ns.attr, s->ns.initCount, s->ns.maxCount, optionPtr);
232
233 if (optionPtr != 0)
234 {
235 u32 size = Memory::Read_U32(optionPtr);
236 if (size > 4)
237 WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateSema(%s) unsupported options parameter, size = %d", name, size);
238 }
239 if ((attr & ~PSP_SEMA_ATTR_PRIORITY) != 0)
240 WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateSema(%s) unsupported attr parameter: %08x", name, attr);
241
242 return id;
243 }
244
sceKernelDeleteSema(SceUID id)245 int sceKernelDeleteSema(SceUID id)
246 {
247 u32 error;
248 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
249 if (s)
250 {
251 DEBUG_LOG(SCEKERNEL, "sceKernelDeleteSema(%i)", id);
252
253 bool wokeThreads = __KernelClearSemaThreads(s, SCE_KERNEL_ERROR_WAIT_DELETE);
254 if (wokeThreads)
255 hleReSchedule("semaphore deleted");
256
257 return kernelObjects.Destroy<PSPSemaphore>(id);
258 }
259 else
260 {
261 DEBUG_LOG(SCEKERNEL, "sceKernelDeleteSema(%i): invalid semaphore", id);
262 return error;
263 }
264 }
265
sceKernelReferSemaStatus(SceUID id,u32 infoPtr)266 int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
267 {
268 u32 error;
269 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
270 if (s)
271 {
272 DEBUG_LOG(SCEKERNEL, "sceKernelReferSemaStatus(%i, %08x)", id, infoPtr);
273
274 if (!Memory::IsValidAddress(infoPtr))
275 return -1;
276
277 HLEKernel::CleanupWaitingThreads(WAITTYPE_SEMA, id, s->waitingThreads);
278
279 s->ns.numWaitThreads = (int) s->waitingThreads.size();
280 if (Memory::Read_U32(infoPtr) != 0)
281 Memory::WriteStruct(infoPtr, &s->ns);
282 return 0;
283 }
284 else
285 {
286 ERROR_LOG(SCEKERNEL, "sceKernelReferSemaStatus: error %08x", error);
287 return error;
288 }
289 }
290
sceKernelSignalSema(SceUID id,int signal)291 int sceKernelSignalSema(SceUID id, int signal)
292 {
293 u32 error;
294 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
295 if (s)
296 {
297 if (s->ns.currentCount + signal - (int) s->waitingThreads.size() > s->ns.maxCount)
298 {
299 VERBOSE_LOG(SCEKERNEL, "sceKernelSignalSema(%i, %i): overflow (at %i)", id, signal, s->ns.currentCount);
300 return SCE_KERNEL_ERROR_SEMA_OVF;
301 }
302
303 int oldval = s->ns.currentCount;
304 s->ns.currentCount += signal;
305 DEBUG_LOG(SCEKERNEL, "sceKernelSignalSema(%i, %i) (count: %i -> %i)", id, signal, oldval, s->ns.currentCount);
306
307 if ((s->ns.attr & PSP_SEMA_ATTR_PRIORITY) != 0)
308 std::stable_sort(s->waitingThreads.begin(), s->waitingThreads.end(), __KernelThreadSortPriority);
309
310 bool wokeThreads = false;
311 retry:
312 for (auto iter = s->waitingThreads.begin(), end = s->waitingThreads.end(); iter != end; ++iter)
313 {
314 if (__KernelUnlockSemaForThread(s, *iter, error, 0, wokeThreads))
315 {
316 s->waitingThreads.erase(iter);
317 goto retry;
318 }
319 }
320
321 if (wokeThreads)
322 hleReSchedule("semaphore signaled");
323
324 hleEatCycles(900);
325 return 0;
326 }
327 else
328 {
329 DEBUG_LOG(SCEKERNEL, "sceKernelSignalSema(%i, %i): invalid semaphore", id, signal);
330 return error;
331 }
332 }
333
__KernelSemaTimeout(u64 userdata,int cycleslate)334 void __KernelSemaTimeout(u64 userdata, int cycleslate)
335 {
336 SceUID threadID = (SceUID)userdata;
337 u32 error;
338 SceUID uid = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error);
339
340 HLEKernel::WaitExecTimeout<PSPSemaphore, WAITTYPE_SEMA>(threadID);
341
342 // If in FIFO mode, that may have cleared another thread to wake up.
343 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(uid, error);
344 if (s && (s->ns.attr & PSP_SEMA_ATTR_PRIORITY) == PSP_SEMA_ATTR_FIFO) {
345 bool wokeThreads;
346 std::vector<SceUID>::iterator iter = s->waitingThreads.begin();
347 // Unlock every waiting thread until the first that must still wait.
348 while (iter != s->waitingThreads.end() && __KernelUnlockSemaForThread(s, *iter, error, 0, wokeThreads)) {
349 s->waitingThreads.erase(iter);
350 iter = s->waitingThreads.begin();
351 }
352 }
353 }
354
__KernelSetSemaTimeout(PSPSemaphore * s,u32 timeoutPtr)355 static void __KernelSetSemaTimeout(PSPSemaphore *s, u32 timeoutPtr) {
356 if (timeoutPtr == 0 || semaWaitTimer == -1)
357 return;
358
359 int micro = (int) Memory::Read_U32(timeoutPtr);
360
361 // This happens to be how the hardware seems to time things.
362 if (micro <= 3)
363 micro = 24;
364 else if (micro <= 249)
365 micro = 245;
366
367 // This should call __KernelSemaTimeout() later, unless we cancel it.
368 CoreTiming::ScheduleEvent(usToCycles(micro), semaWaitTimer, __KernelGetCurThread());
369 }
370
__KernelWaitSema(SceUID id,int wantedCount,u32 timeoutPtr,bool processCallbacks)371 static int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, bool processCallbacks)
372 {
373 hleEatCycles(900);
374
375 if (wantedCount <= 0)
376 return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
377
378 hleEatCycles(500);
379
380 u32 error;
381 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
382 if (s)
383 {
384 if (wantedCount > s->ns.maxCount)
385 return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
386
387 // If there are any callbacks, we always wait, and wake after the callbacks.
388 bool hasCallbacks = processCallbacks && __KernelCurHasReadyCallbacks();
389 if (s->ns.currentCount >= wantedCount && s->waitingThreads.size() == 0 && !hasCallbacks)
390 s->ns.currentCount -= wantedCount;
391 else
392 {
393 SceUID threadID = __KernelGetCurThread();
394 // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates.
395 if (std::find(s->waitingThreads.begin(), s->waitingThreads.end(), threadID) == s->waitingThreads.end())
396 s->waitingThreads.push_back(threadID);
397 __KernelSetSemaTimeout(s, timeoutPtr);
398 __KernelWaitCurThread(WAITTYPE_SEMA, id, wantedCount, timeoutPtr, processCallbacks, "sema waited");
399 }
400
401 return 0;
402 }
403 else
404 return error;
405 }
406
sceKernelWaitSema(SceUID id,int wantedCount,u32 timeoutPtr)407 int sceKernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr)
408 {
409 int result = __KernelWaitSema(id, wantedCount, timeoutPtr, false);
410 if (result == (int)SCE_KERNEL_ERROR_ILLEGAL_COUNT)
411 DEBUG_LOG(SCEKERNEL, "SCE_KERNEL_ERROR_ILLEGAL_COUNT=sceKernelWaitSema(%i, %i, %i)", id, wantedCount, timeoutPtr);
412 else if (result == 0)
413 DEBUG_LOG(SCEKERNEL, "0=sceKernelWaitSema(%i, %i, %i)", id, wantedCount, timeoutPtr);
414 else
415 DEBUG_LOG(SCEKERNEL, "%08x=sceKernelWaitSema(%i, %i, %i)", result, id, wantedCount, timeoutPtr);
416 return result;
417 }
418
sceKernelWaitSemaCB(SceUID id,int wantedCount,u32 timeoutPtr)419 int sceKernelWaitSemaCB(SceUID id, int wantedCount, u32 timeoutPtr)
420 {
421 int result = __KernelWaitSema(id, wantedCount, timeoutPtr, true);
422 if (result == (int)SCE_KERNEL_ERROR_ILLEGAL_COUNT)
423 DEBUG_LOG(SCEKERNEL, "SCE_KERNEL_ERROR_ILLEGAL_COUNT=sceKernelWaitSemaCB(%i, %i, %i)", id, wantedCount, timeoutPtr);
424 else if (result == 0)
425 DEBUG_LOG(SCEKERNEL, "0=sceKernelWaitSemaCB(%i, %i, %i)", id, wantedCount, timeoutPtr);
426 else
427 DEBUG_LOG(SCEKERNEL, "%08x=sceKernelWaitSemaCB(%i, %i, %i)", result, id, wantedCount, timeoutPtr);
428 return result;
429 }
430
431 // Should be same as WaitSema but without the wait, instead returning SCE_KERNEL_ERROR_SEMA_ZERO
sceKernelPollSema(SceUID id,int wantedCount)432 int sceKernelPollSema(SceUID id, int wantedCount)
433 {
434 if (wantedCount <= 0)
435 {
436 DEBUG_LOG(SCEKERNEL, "SCE_KERNEL_ERROR_ILLEGAL_COUNT=sceKernelPollSema(%i, %i)", id, wantedCount);
437 return (int)SCE_KERNEL_ERROR_ILLEGAL_COUNT;
438 }
439
440 u32 error;
441 PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
442 if (s)
443 {
444 if (s->ns.currentCount >= wantedCount && s->waitingThreads.size() == 0)
445 {
446 DEBUG_LOG(SCEKERNEL, "0=sceKernelPollSema(%i, %i)", id, wantedCount);
447 s->ns.currentCount -= wantedCount;
448 return 0;
449 }
450 else
451 {
452 DEBUG_LOG(SCEKERNEL, "SCE_KERNEL_ERROR_SEMA_ZERO=sceKernelPollSema(%i, %i)", id, wantedCount);
453 return SCE_KERNEL_ERROR_SEMA_ZERO;
454 }
455 }
456 else
457 {
458 DEBUG_LOG(SCEKERNEL, "sceKernelPollSema(%i, %i): invalid semaphore", id, wantedCount);
459 return error;
460 }
461 }
462
463 // The below functions don't really belong to sceKernelSemaphore. They are the core crypto functionality,
464 // exposed through the confusingly named "sceUtilsBufferCopyWithRange" name, which Sony placed in the
465 // not-at-all-suspicious "semaphore" library, which has nothing to do with semaphores.
466
sceUtilsBufferCopyWithRange(u32 outAddr,int outSize,u32 inAddr,int inSize,int cmd)467 static u32 sceUtilsBufferCopyWithRange(u32 outAddr, int outSize, u32 inAddr, int inSize, int cmd)
468 {
469 u8 *outAddress = Memory::IsValidRange(outAddr, outSize) ? Memory::GetPointer(outAddr) : nullptr;
470 const u8 *inAddress = Memory::IsValidRange(inAddr, inSize) ? Memory::GetPointer(inAddr) : nullptr;
471 int temp = kirk_sceUtilsBufferCopyWithRange(outAddress, outSize, inAddress, inSize, cmd);
472 if (temp != 0) {
473 ERROR_LOG(SCEKERNEL, "hleUtilsBufferCopyWithRange: Failed with %d", temp);
474 }
475 return 0;
476 }
477
478 // Note sure what difference there is between this and sceUtilsBufferCopyWithRange.
sceUtilsBufferCopyByPollingWithRange(u32 outAddr,int outSize,u32 inAddr,int inSize,int cmd)479 static int sceUtilsBufferCopyByPollingWithRange(u32 outAddr, int outSize, u32 inAddr, int inSize, int cmd)
480 {
481 u8 *outAddress = Memory::IsValidRange(outAddr, outSize) ? Memory::GetPointer(outAddr) : nullptr;
482 const u8 *inAddress = Memory::IsValidRange(inAddr, inSize) ? Memory::GetPointer(inAddr) : nullptr;
483 return kirk_sceUtilsBufferCopyWithRange(outAddress, outSize, inAddress, inSize, cmd);
484 }
485
486 const HLEFunction semaphore[] = {
487 {0x4C537C72, &WrapU_UIUII<sceUtilsBufferCopyWithRange>, "sceUtilsBufferCopyWithRange", 'x', "xixii" },
488 {0x77E97079, &WrapI_UIUII<sceUtilsBufferCopyByPollingWithRange>, "sceUtilsBufferCopyByPollingWithRange", 'i', "xixii" },
489 };
490
Register_semaphore()491 void Register_semaphore() {
492 RegisterModule("semaphore", ARRAY_SIZE(semaphore), semaphore);
493 }
494