sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
osal.c
1/*
2# _____ ___ ____ ___ ____
3# ____| | ____| | | |____|
4# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
5#-----------------------------------------------------------------------
6# Copyright 2001-2004, ps2dev - http://www.ps2dev.org
7# Licenced under Academic Free License version 2.0
8# Review ps2sdk README & LICENSE files for further details.
9*/
10
11#include <ps2_osal.h>
12#include <time.h>
13#include <kernel_util.h>
14#include <delaythread.h>
15
16#define MAX_PS2_UID 2048 // SWAG
17#define DEFAULT_STACK_SIZE_BYTES 4096
18#define PS2_MAX_TLS 32
19#define MAX_THREADS 256
20
21#if 0
22#define PS2_DEBUG(x) printf(x)
23#else
24#define PS2_DEBUG(x)
25#endif
26
27#define POLLING_DELAY_IN_us 100
28
29struct OsalThreadInfo __threadInfo[MAX_THREADS];
30
31/* TLS key used to access ps2ThreadData struct for reach thread. */
32unsigned int __threadDataKey;
33
34extern void *__globalTls;
35
36/*
37 * Data stored on a per-thread basis - allocated in pte_osThreadCreate
38 * and freed in pte_osThreadDelete.
39 */
40typedef struct ps2ThreadData
41 {
42 /* Entry point and parameters to thread's main function */
43 pte_osThreadEntryPoint entryPoint;
44 void * argv;
45
46 /* Semaphore used for cancellation. Posted to by pte_osThreadCancel,
47 polled in pte_osSemaphoreCancellablePend */
48 s32 cancelSem;
49
51
52
53/****************************************************************************
54 *
55 * Helper functions
56 *
57 ***************************************************************************/
58ps2ThreadData *__getThreadData(s32 threadHandle)
59{
60 ps2ThreadData *pThreadData;
61 void *pTls;
62
63 pTls = __getTlsStructFromThread(threadHandle);
64 pThreadData = (ps2ThreadData *) pteTlsGetValue(pTls, __threadDataKey);
65
66 return pThreadData;
67}
68
69static inline int32_t SemWaitTimeout(s32 semHandle, uint32_t timeout)
70{
71 u64 timeoutUsec;
72 u64 *timeoutPtr;
73
74 if (timeout == 0) {
75 return PollSema(semHandle);
76 }
77
78 timeoutPtr = NULL;
79
80 if (timeout > 0 && timeout != UINT32_MAX) {
81 timeoutUsec = timeout;
82 timeoutPtr = &timeoutUsec;
83 return WaitSemaEx(semHandle, 1, timeoutPtr);
84 }
85
86 return WaitSema(semHandle);
87}
88
89/* A new thread's stub entry point. It retrieves the real entry point from the per thread control
90 * data as well as any parameters to this function, and then calls the entry point.
91 */
92int __ps2StubThreadEntry(void *argv)
93{
94 int result;
95 ps2ThreadData *pThreadData;
96
97 pThreadData = __getThreadData(GetThreadId());
98 result = (*(pThreadData->entryPoint))(pThreadData->argv);
99
100 return result;
101}
102
103/****************************************************************************
104 *
105 * Initialization
106 *
107 ***************************************************************************/
108pte_osResult pte_osInit(void)
109{
110 ee_sema_t sema;
111 pte_osResult result;
112 ps2ThreadData *pThreadData;
113
114 /* Allocate and initialize TLS support */
115 result = pteTlsGlobalInit(PS2_MAX_TLS);
116
117 if (result == PTE_OS_OK) {
118 /* Allocate a key that we use to store control information (e.g. cancellation semaphore) per thread */
119 result = __pteTlsAlloc(&__threadDataKey);
120
121 if (result == PTE_OS_OK) {
122 /* Initialize the structure used to emulate TLS for
123 * non-POSIX threads
124 */
125 __globalTls = pteTlsThreadInit();
126
127 /* Also create a "thread data" structure for a single non-POSIX thread. */
128
129 /* Allocate some memory for our per-thread control data. We use this for:
130 * 1. Entry point and parameters for the user thread's main function.
131 * 2. Semaphore used for thread cancellation.
132 */
133 pThreadData = (ps2ThreadData *) malloc(sizeof(ps2ThreadData));
134
135 if (pThreadData == NULL) {
136 result = PTE_OS_NO_RESOURCES;
137 } else {
138 /* Save a pointer to our per-thread control data as a TLS value */
139 __pteTlsSetValue(__globalTls, __threadDataKey, pThreadData);
140
141 sema.init_count = 0;
142 sema.max_count = 255;
143 sema.option = 0;
144 pThreadData->cancelSem = CreateSema(&sema);
145
146 result = PTE_OS_OK;
147 }
148 }
149 }
150
151 return result;
152}
153
154pte_osResult pte_osTerminate(void) {
155 pteTlsGlobalDestroy();
156 return PTE_OS_OK;
157}
158
159/****************************************************************************
160 *
161 * Threads
162 *
163 ***************************************************************************/
164
165static inline int invert_priority(int priority)
166{
167 return (pte_osThreadGetMinPriority() - priority) + pte_osThreadGetMaxPriority();
168}
169
170pte_osResult pte_osThreadCreate(pte_osThreadEntryPoint entryPoint,
171 int stackSize,
172 int initialPriority,
173 void *argv,
174 pte_osThreadHandle* ppte_osThreadHandle)
175{
176 ee_thread_t eethread;
177 ee_sema_t sema;
178 struct OsalThreadInfo *threadInfo;
179 void *stack;
180 static int threadNum;
181 void *pTls;
182 s32 threadId;
183 ps2ThreadData *pThreadData;
184
185 if (++threadNum > MAX_PS2_UID) {
186 threadNum = 0;
187 }
188
189 /* Make sure that the stack we're going to allocate is big enough */
190 if (stackSize < DEFAULT_STACK_SIZE_BYTES) {
191 stackSize = DEFAULT_STACK_SIZE_BYTES;
192 }
193
194 /* Allocate TLS structure for this thread. */
195 pTls = pteTlsThreadInit();
196 if (pTls == NULL) {
197 PS2_DEBUG("pteTlsThreadInit: PTE_OS_NO_RESOURCES\n");
198 return PTE_OS_NO_RESOURCES;
199 }
200
201 /* Allocate some memory for our per-thread control data. We use this for:
202 * 1. Entry point and parameters for the user thread's main function.
203 * 2. Semaphore used for thread cancellation.
204 */
205 pThreadData = (ps2ThreadData *) malloc(sizeof(ps2ThreadData));
206
207 if (pThreadData == NULL) {
208 pteTlsThreadDestroy(pTls);
209 PS2_DEBUG("malloc(ps2ThreadData): PTE_OS_NO_RESOURCES\n");
210 return PTE_OS_NO_RESOURCES;
211 }
212
213 /* Save a pointer to our per-thread control data as a TLS value */
214 __pteTlsSetValue(pTls, __threadDataKey, pThreadData);
215
216 pThreadData->entryPoint = entryPoint;
217 pThreadData->argv = argv;
218
219 sema.init_count = 0;
220 sema.max_count = 255;
221 sema.option = 0;
222 pThreadData->cancelSem = CreateSema(&sema);
223 if (pThreadData->cancelSem < 0) {
224 free(pThreadData);
225 pteTlsThreadDestroy(pTls);
226 PS2_DEBUG("CreateSema(cancelSem): PTE_OS_NO_RESOURCES\n");
227 return PTE_OS_NO_RESOURCES;
228 }
229
230 /* Allocate the thread stack before calling CreateThread — passing a
231 * NULL stack to the EE kernel is undefined behaviour and the original
232 * check below ran *after* the syscall.
233 */
234 stack = malloc(stackSize);
235 if (!stack) {
236 DeleteSema(pThreadData->cancelSem);
237 free(pThreadData);
238 pteTlsThreadDestroy(pTls);
239 PS2_DEBUG("malloc(stack): PTE_OS_NO_RESOURCES\n");
240 return PTE_OS_NO_RESOURCES;
241 }
242
243 eethread.attr = 0;
244 eethread.option = 0;
245 eethread.func = &__ps2StubThreadEntry;
246 eethread.stack = stack;
247 eethread.stack_size = stackSize;
248 eethread.gp_reg = &_gp;
249 eethread.initial_priority = invert_priority(initialPriority);
250 threadId = CreateThread(&eethread);
251
252 if (threadId < 0) {
253 /* Must check threadId *before* indexing __threadInfo — a negative
254 * id is an OOB write.
255 */
256 free(stack);
257 DeleteSema(pThreadData->cancelSem);
258 free(pThreadData);
259 pteTlsThreadDestroy(pTls);
260 PS2_DEBUG("CreateThread: PTE_OS_GENERAL_FAILURE\n");
261 return PTE_OS_GENERAL_FAILURE;
262 }
263
264 /* In order to emulate TLS functionality, we append the address of the TLS structure that we
265 * allocated above to an additional struct. To set or get TLS values for this thread, the user
266 * needs to get the threadId from the OS and then extract
267 * a pointer to the TLS structure.
268 */
269 threadInfo = &__threadInfo[threadId];
270 threadInfo->threadNumber = threadNum;
271 threadInfo->tlsPtr = pTls;
272
273 *ppte_osThreadHandle = threadId;
274 return PTE_OS_OK;
275}
276
277pte_osResult pte_osThreadStart(pte_osThreadHandle osThreadHandle)
278{
279 StartThread(osThreadHandle, 0);
280
281 return PTE_OS_OK;
282}
283
284pte_osResult pte_osThreadDelete(pte_osThreadHandle handle)
285{
286 ps2ThreadData *pThreadData;
287 void *pTls;
288 ee_thread_status_t info;
289 struct OsalThreadInfo *threadInfo;
290 int res;
291
292 res = ReferThreadStatus(handle, &info);
293
294 pTls = __getTlsStructFromThread(handle);
295 pThreadData = __getThreadData(handle);
296 DeleteSema(pThreadData->cancelSem);
297 free(pThreadData);
298 pteTlsThreadDestroy(pTls);
299 TerminateThread(handle);
300 DeleteThread(handle);
301
302 if (res > 0 && info.stack) {
303 free(info.stack);
304 }
305
306 threadInfo = &__threadInfo[handle];
307 threadInfo->threadNumber = 0;
308 threadInfo->tlsPtr = NULL;
309
310 return PTE_OS_OK;
311}
312
313pte_osResult pte_osThreadExitAndDelete(pte_osThreadHandle handle)
314{
315 pte_osThreadDelete(handle);
316 ExitDeleteThread();
317
318 return PTE_OS_OK;
319}
320
321void pte_osThreadExit()
322{
323 ExitThread();
324}
325
326/*
327 * This has to be cancellable, so we can't just call sceKernelWaitThreadEnd.
328 * Instead, poll on this in a loop, like we do for a cancellable semaphore.
329 */
330pte_osResult pte_osThreadWaitForEnd(pte_osThreadHandle threadHandle)
331{
332 pte_osResult result;
333 ps2ThreadData *pThreadData;
334
335 pThreadData = __getThreadData(GetThreadId());
336
337 while (1) {
338 ee_thread_status_t info;
339
340 /* Poll task to see if it has ended */
341 ReferThreadStatus(threadHandle, &info);
342
343 if (info.status == THS_DORMANT) {
344 /* Thread has ended */
345 result = PTE_OS_OK;
346 break;
347 } else {
348 ee_sema_t semInfo;
349
350 if (pThreadData != NULL) {
351 s32 osResult;
352
353 osResult = ReferSemaStatus(pThreadData->cancelSem, &semInfo);
354 if (osResult == pThreadData->cancelSem) {
355 if (semInfo.count > 0) {
356 result = PTE_OS_INTERRUPTED;
357 break;
358 } else {
359 /* Nothing found and not timed out yet; let's yield so we're not
360 * in busy loop.
361 */
362 DelayThread(POLLING_DELAY_IN_us);
363 }
364 } else {
365 result = PTE_OS_GENERAL_FAILURE;
366 break;
367 }
368 }
369 }
370 }
371
372 return result;
373}
374
375pte_osThreadHandle pte_osThreadGetHandle(void)
376{
377 return GetThreadId();
378}
379
380int pte_osThreadGetPriority(pte_osThreadHandle threadHandle)
381{
382 ee_thread_status_t thinfo;
383
384 ReferThreadStatus(threadHandle, &thinfo);
385
386 return invert_priority(thinfo.current_priority);
387}
388
389pte_osResult pte_osThreadSetPriority(pte_osThreadHandle threadHandle, int newPriority)
390{
391 ChangeThreadPriority(threadHandle, invert_priority(newPriority));
392 return PTE_OS_OK;
393}
394
395pte_osResult pte_osThreadCancel(pte_osThreadHandle threadHandle)
396{
397 s32 osResult;
398 pte_osResult result;
399 ps2ThreadData *pThreadData;
400
401 pThreadData = __getThreadData(threadHandle);
402 osResult = SignalSema(pThreadData->cancelSem);
403
404 if (osResult == pThreadData->cancelSem) {
405 result = PTE_OS_OK;
406 } else {
407 result = PTE_OS_GENERAL_FAILURE;
408 }
409
410 return result;
411}
412
413pte_osResult pte_osThreadCheckCancel(pte_osThreadHandle threadHandle)
414{
415 ps2ThreadData *pThreadData;
416 ee_sema_t semInfo;
417 s32 osResult;
418 pte_osResult result;
419
420 pThreadData = __getThreadData(threadHandle);
421 if (pThreadData != NULL) {
422 osResult = ReferSemaStatus(pThreadData->cancelSem, &semInfo);
423
424 if (osResult == pThreadData->cancelSem) {
425 if (semInfo.count > 0) {
426 result = PTE_OS_INTERRUPTED;
427 } else {
428 result = PTE_OS_OK;
429 }
430 } else {
431 /* sceKernelReferSemaStatus returned an error */
432 result = PTE_OS_GENERAL_FAILURE;
433 }
434 } else {
435 /* For some reason, we couldn't get thread data */
436 result = PTE_OS_GENERAL_FAILURE;
437 }
438
439 return result;
440}
441
442void pte_osThreadSleep(unsigned int msecs)
443{
444 DelayThread(msecs * 1000);
445}
446
447int pte_osThreadGetMinPriority()
448{
449 return pte_osThreadGetDefaultPriority() - 32;
450}
451
452int pte_osThreadGetMaxPriority()
453{
454 return pte_osThreadGetDefaultPriority() + 32;
455}
456
457int pte_osThreadGetDefaultPriority()
458{
459 return 60;
460}
461
462int pthread_num_processors_np(void)
463{
464 return 1;
465}
466
467/****************************************************************************
468 *
469 * Mutexes
470 *
471 ****************************************************************************/
472pte_osResult pte_osMutexCreate(pte_osMutexHandle *pHandle)
473{
474 static int mutexCtr = 0;
475 ee_sema_t sema;
476 pte_osMutexHandle handle;
477
478 if (mutexCtr++ > MAX_PS2_UID) {
479 mutexCtr = 0;
480 }
481
482 sema.init_count = 1;
483 sema.max_count = 1;
484 sema.option = 0;
485 handle = CreateSema(&sema);
486
487 if (handle < 0) {
488 return PTE_OS_NO_RESOURCES;
489 }
490
491 *pHandle = handle;
492 return PTE_OS_OK;
493}
494
495pte_osResult pte_osMutexDelete(pte_osMutexHandle handle)
496{
497 DeleteSema(handle);
498
499 return PTE_OS_OK;
500}
501
502pte_osResult pte_osMutexLock(pte_osMutexHandle handle)
503{
504 SemWaitTimeout(handle, UINT32_MAX);
505
506 return PTE_OS_OK;
507}
508
509pte_osResult pte_osMutexTimedLock(pte_osMutexHandle handle, unsigned int timeoutMsecs)
510{
511 pte_osResult result;
512 int32_t timeout = timeoutMsecs * 1000;
513 int status = SemWaitTimeout(handle, timeout);
514 if (status > 0) {
515 result = PTE_OS_OK;
516 } else if (status == -1) {
517 result = PTE_OS_TIMEOUT;
518 } else {
519 result = PTE_OS_GENERAL_FAILURE;
520 }
521
522 return result;
523}
524
525pte_osResult pte_osMutexUnlock(pte_osMutexHandle handle)
526{
527 SignalSema(handle);
528 return PTE_OS_OK;
529}
530
531/****************************************************************************
532 *
533 * Semaphores
534 *
535 ***************************************************************************/
536pte_osResult pte_osSemaphoreCreate(int initialValue, pte_osSemaphoreHandle *pHandle)
537{
538 pte_osSemaphoreHandle handle;
539 ee_sema_t sema;
540 static int semCtr = 0;
541
542 if (semCtr++ > MAX_PS2_UID) {
543 semCtr = 0;
544 }
545
546 sema.init_count = initialValue;
547 sema.max_count = 32767;
548 sema.option = 0;
549 handle = CreateSema(&sema);
550
551 if (handle < 0) {
552 return PTE_OS_NO_RESOURCES;
553 }
554
555 *pHandle = handle;
556 return PTE_OS_OK;
557}
558
559pte_osResult pte_osSemaphoreDelete(pte_osSemaphoreHandle handle)
560{
561 DeleteSema(handle);
562 return PTE_OS_OK;
563}
564
565pte_osResult pte_osSemaphorePost(pte_osSemaphoreHandle handle, int count)
566{
567 int i;
568 for (i = 0; i < count; i++)
569 SignalSema(handle);
570
571 return PTE_OS_OK;
572}
573
574pte_osResult pte_osSemaphorePend(pte_osSemaphoreHandle handle, unsigned int *pTimeoutMsecs)
575{
576 uint32_t timeout;
577 int32_t result;
578 pte_osResult osResult;
579
580 if (pTimeoutMsecs == NULL) {
581 timeout = UINT32_MAX;
582 } else {
583 timeout = *pTimeoutMsecs * 1000;
584 }
585
586 result = SemWaitTimeout(handle, timeout);
587 if (result > 0) {
588 osResult = PTE_OS_OK;
589 } else if (result == -1) {
590 osResult = PTE_OS_TIMEOUT;
591 } else {
592 osResult = PTE_OS_GENERAL_FAILURE;
593 }
594
595 return osResult;
596}
597
598
599/*
600 * Pend on a semaphore- and allow the pend to be cancelled.
601 *
602 * PS2 OS provides no functionality to asynchronously interrupt a blocked call. We simulte
603 * this by polling on the main semaphore and the cancellation semaphore and sleeping in a loop.
604 */
605pte_osResult pte_osSemaphoreCancellablePend(pte_osSemaphoreHandle semHandle, unsigned int *pTimeout)
606{
607 ps2ThreadData *pThreadData;
608
609 pThreadData = __getThreadData(GetThreadId());
610
611 clock_t start_time;
612 pte_osResult result = PTE_OS_OK;
613 uint32_t timeout;
614
615 start_time = clock();
616
617 // clock() is in microseconds, timeout as passed in was in milliseconds
618 if (pTimeout == NULL) {
619 timeout = 0;
620 } else {
621 timeout = *pTimeout * 1000;
622 }
623
624 while (1) {
625 int32_t status;
626
627 /* Poll semaphore */
628 status = SemWaitTimeout(semHandle, 0);
629 if (status > 0) {
630 /* User semaphore posted to */
631 result = PTE_OS_OK;
632 break;
633 } else if ((pTimeout != NULL) && ((clock() - start_time) > timeout)) {
634 /* Timeout expired */
635 result = PTE_OS_TIMEOUT;
636 break;
637 } else {
638 ee_sema_t semInfo;
639
640 if (pThreadData != NULL) {
641 s32 osResult;
642
643 osResult = ReferSemaStatus(pThreadData->cancelSem, &semInfo);
644 if (osResult == pThreadData->cancelSem) {
645 if (semInfo.count > 0) {
646 result = PTE_OS_INTERRUPTED;
647 break;
648 } else {
649 /* Nothing found and not timed out yet; let's yield so we're not
650 * in busy loop.
651 */
652 DelayThread(POLLING_DELAY_IN_us);
653 }
654 } else {
655 result = PTE_OS_GENERAL_FAILURE;
656 break;
657 }
658 }
659 }
660 }
661
662 return result;
663}
664
665
666/****************************************************************************
667 *
668 * Atomic Operations
669 *
670 ***************************************************************************/
671int pte_osAtomicExchange(int *ptarg, int val)
672{
673 int intc = DIntr();
674 int origVal;
675
676 origVal = *ptarg;
677 *ptarg = val;
678
679 if(intc) { EIntr(); }
680 return origVal;
681}
682
683int pte_osAtomicCompareExchange(int *pdest, int exchange, int comp)
684{
685 int intc = DIntr();
686 int origVal;
687
688 origVal = *pdest;
689 if (*pdest == comp){
690 *pdest = exchange;
691 }
692
693 if(intc) { EIntr(); }
694 return origVal;
695}
696
697int pte_osAtomicExchangeAdd(int volatile* pAddend, int value)
698{
699 int origVal;
700 int intc = DIntr();
701
702 origVal = *pAddend;
703 *pAddend += value;
704
705 if(intc) { EIntr(); }
706 return origVal;
707}
708
709int pte_osAtomicDecrement(int *pdest)
710{
711 int val;
712 int intc = DIntr();
713
714 (*pdest)--;
715 val = *pdest;
716
717 if(intc) { EIntr(); }
718 return val;
719}
720
721int pte_osAtomicIncrement(int *pdest)
722{
723 int val;
724 int intc = DIntr();
725
726 (*pdest)++;
727 val = *pdest;
728
729 if(intc) { EIntr(); }
730 return val;
731}
#define MAX_THREADS
Definition kernel.h:72
s32 WaitSemaEx(s32 semaid, int signal, u64 *timeout)
Definition kernel_util.c:26
void * _gp
u32 count
start sector of fragmented bd/file