sdk-core v2026.11.05
PS2 SDK - core tier (build-critical EE/IOP modules)
Loading...
Searching...
No Matches
thcommon.c
1#include "thcommon.h"
2#include "kerr.h"
3#include "thevent.h"
4#include "timrman.h"
5#include "sysmem.h"
6#include "heaplib.h"
7#include "intrman.h"
8#include "loadcore.h"
9#include "sysclib.h"
10#include "stdio.h"
11#include <defs.h>
12
13#include <limits.h>
14
15IRX_ID("Multi_Thread_Manager", 2, 3);
16extern struct irx_export_table _exp_thrdman;
17extern struct irx_export_table _exp_thbase;
18extern struct irx_export_table _exp_thevent;
19extern struct irx_export_table _exp_thsemap;
20extern struct irx_export_table _exp_thmsgbx;
21extern struct irx_export_table _exp_thfpool;
22extern struct irx_export_table _exp_thvpool;
23
24struct thread_context thctx;
25
26struct alarm *alarm_alloc()
27{
28 struct alarm *alarm;
29 if (list_empty(&thctx.alarm_pool)) {
30 alarm = heap_alloc(0, sizeof(*alarm));
31 thctx.alarm_id++;
32 alarm->tag.id = thctx.alarm_id;
33 } else {
34 alarm = list_first_entry(&thctx.alarm_pool, struct alarm, alarm_list);
35 list_remove(&alarm->alarm_list);
36 }
37
38 return alarm;
39}
40
41void alarm_free(struct alarm *alarm)
42{
43 if (alarm->tag.id >= 33) {
44 heap_free(&alarm->tag);
45 } else {
46 list_insert(&thctx.alarm_pool, &alarm->alarm_list);
47 }
48}
49
50void alarm_insert(struct list_head *list, struct alarm *alarm)
51{
52 struct alarm *i;
53
54 list_for_each (i, list, alarm_list) {
55 if (alarm->target < i->target) {
56 break;
57 }
58 }
59
60 list_insert(&i->alarm_list, &alarm->alarm_list);
61}
62
63void waitlist_insert(struct thread *thread, struct event *event, s32 priority)
64{
65 struct thread *weaker;
66
67 list_remove(&thread->queue);
68
69 weaker = list_first_entry(&event->waiters, struct thread, queue);
70 list_for_each (weaker, &event->waiters, queue) {
71 if (priority < weaker->priority) {
72 break;
73 }
74 }
75
76 list_insert(&weaker->queue, &thread->queue);
77}
78
79void update_timer_compare(int timid, u64 time, struct list_head *alarm_list)
80{
81 struct alarm *prev, *i;
82 u32 counter, new_compare = 0;
83
84 // what if list is empty? (luckily its not but....)
85 prev = list_first_entry(alarm_list, struct alarm, alarm_list);
86
87 if (!list_empty(alarm_list)) {
88 list_for_each (i, alarm_list, alarm_list) {
89 if (i->target >= prev->target + thctx.unk4c8) {
90 break;
91 }
92
93 prev = i;
94 }
95 }
96
97 if (prev->target - time >= thctx.unk4c8) {
98 new_compare = prev->target;
99 } else {
100 counter = GetTimerCounter(timid);
101 new_compare = counter + thctx.unk4c8;
102 }
103
104 SetTimerCompare(timid, new_compare);
105}
106
107unsigned int thread_delay_cb(void *user)
108{
109 struct thread *thread = user;
110
111 list_remove(&thread->queue);
112 thread->status = THS_READY;
113 readyq_insert_back(thread);
114 thctx.run_next = NULL;
115
116 return 0;
117}
118
119int check_thread_stack()
120{
121 int stack_remaining;
122 stack_remaining = (u32)&stack_remaining - (u32)thctx.current_thread->stack_top;
123
124 if (stack_remaining < 0xa8) {
126 Kprintf("CheckThreadStack()\n");
127 thread_leave(0, 0, 0, 0);
128 }
129
130 return stack_remaining;
131}
132
133void *heap_alloc(u16 tag, u32 bytes)
134{
135 struct heaptag *ptr = AllocHeapMemory(thctx.heap, bytes);
136 if (ptr) {
137 memset(ptr, 0, bytes);
138 ptr->tag = tag;
139 }
140
141 return ptr;
142}
143
144int heap_free(struct heaptag *tag)
145{
146 tag->tag = 0;
147 return FreeHeapMemory(thctx.heap, tag);
148}
149
159int thread_leave(int ret1, int ret2, int intr_state, int release)
160{
161 register u32 a0 __asm__("a0") = ret1;
162 register u32 a1 __asm__("a1") = ret2;
163 register u32 a2 __asm__("a2") = intr_state;
164 register s32 result __asm__("v0");
165
166 if (!release) {
167 thctx.current_thread->reason_counter = &thctx.current_thread->thread_preemption_count;
168 } else {
169 thctx.current_thread->reason_counter = &thctx.current_thread->release_count;
170 }
171
172 __asm__ __volatile__("li $v0, 0x20\n"
173 "syscall\n"
174 : "=r"(result)
175 : "r"(a0), "r"(a1), "r"(a2)
176 : "memory");
177
178 return result;
179}
180
181int thread_start(struct thread *thread, int intr_state)
182{
183 if (thread->priority < thctx.current_thread->priority) {
184 thctx.current_thread->status = THS_READY;
185 readyq_insert_front(thctx.current_thread);
186 thread->status = THS_RUN;
187 thctx.run_next = thread;
188
189 return thread_leave(KE_OK, 0, intr_state, 0);
190 } else {
191 thread->status = THS_READY;
192 readyq_insert_back(thread);
193
194 CpuResumeIntr(intr_state);
195 return KE_OK;
196 }
197}
198
199int thread_init_and_start(struct thread *thread, int intr_state)
200{
201 thread->wait_type = 0;
202 thread->wait_usecs = 0;
203 thread->wakeup_count = 0;
204 thread->priority = thread->init_priority;
205 thread->saved_regs->unk = -2;
206 thread->saved_regs->sp = (u32)&thread->saved_regs[1];
207 thread->saved_regs->fp = thread->saved_regs->sp;
208 thread->saved_regs->ra = (u32)ExitThread;
209 thread->saved_regs->gp = (u32)thread->gp;
210 thread->saved_regs->sr = 0x404;
211 thread->saved_regs->sr |= thread->attr & 8;
212 thread->saved_regs->pc = (u32)thread->entry;
213 thread->saved_regs->I_CTRL = 1;
214
215 list_remove(&thread->queue);
216
217 return thread_start(thread, intr_state);
218}
219
220int post_boot_callback_1(iop_init_entry_t *next, int delayed)
221{
223 printf("\r\nIOP Realtime Kernel Ver. 2.2\r\n Copyright 1999-2002 (C) Sony Computer Entertainment Inc. \r\n");
224 return 0;
225}
226
227int post_boot_callback_2(iop_init_entry_t *next, int delayed)
228{
230 ChangeThreadPriority(TH_SELF, 126);
231 if (!next->callback) {
232 while (1) {
233 DelayThread(1000000);
234 }
235 }
236
237 return 0;
238}
239
240int read_sys_time(iop_sys_clock_t *clock)
241{
242 u32 hi = thctx.time_hi;
243 u32 counter = GetTimerCounter(thctx.timer_id);
244
245 if (counter >= thctx.time_lo) {
246 thctx.time_lo = counter;
247 } else {
248 hi++;
249 }
250
251 if (clock) {
252 clock->hi = hi;
253 clock->lo = counter;
254 }
255
256 return 0;
257}
258
259// didn't try to figure out the original algo
260// just grabbed this one
261static u32 ntz(u32 x)
262{
263 u32 n;
264
265 if (x == 0)
266 return (32);
267 n = 1;
268 if ((x & 0x0000FFFF) == 0) {
269 n = n + 16;
270 x = x >> 16;
271 }
272 if ((x & 0x000000FF) == 0) {
273 n = n + 8;
274 x = x >> 8;
275 }
276 if ((x & 0x0000000F) == 0) {
277 n = n + 4;
278 x = x >> 4;
279 }
280 if ((x & 0x00000003) == 0) {
281 n = n + 2;
282 x = x >> 2;
283 }
284 return n - (x & 1);
285}
286
287u32 readyq_highest()
288{
289 for (int i = 0; i < 4; i++) {
290 if (thctx.queue_map[i]) {
291 return ntz(thctx.queue_map[i]) + 32 * i;
292 }
293 }
294
295 return 128;
296}
297
298void report_stack_overflow(struct thread *thread)
299{
300 ModuleInfo_t *img_info;
301 char *name;
302
303 Kprintf("\nThread (thid=%x, #%d) stack overflow\n Stack = %x, Stack size = %x, SP=%x\n",
304 MAKE_HANDLE(thread),
305 thread->tag.id,
306 thread->stack_top,
307 thread->stack_size,
308 thread->saved_regs);
309
310 img_info = FindImageInfo(thread->entry);
311 if (img_info) {
312 name = img_info->name;
313 if (name) {
314 Kprintf(" Module Name = %s\n", name);
315 }
316 }
317
318 __builtin_trap();
319}
320
321void do_delete_thread()
322{
323 struct thread *thread;
324
325 while (!list_empty(&thctx.delete_queue)) {
326 thread = list_first_entry(&thctx.delete_queue, struct thread, queue);
327 if (thread->attr & TH_CLEAR_STACK) {
328 memset(thread->stack_top, 0, thread->stack_size);
329 }
330
331 FreeSysMemory(thread->stack_top);
332 list_remove(&thread->queue);
333 list_remove(&thread->thread_list);
334 heap_free(&thread->tag);
335 }
336}
337
338void schedule_next()
339{
340 struct thread *cur, *new_;
341 u32 prio;
342
343 cur = thctx.current_thread;
344 thctx.run_next = thctx.current_thread;
345
346 prio = readyq_highest();
347
348 // originally would fall down and hit the bottom kprintf
349 // but i don't want the nesting
350 if (prio >= 128) {
351 Kprintf("Panic: not found ready Thread\n");
352 return;
353 }
354
355 new_ = list_first_entry(&thctx.ready_queue[prio], struct thread, queue);
356
357 if (thctx.current_thread->status == THS_RUN) {
358 if (thctx.debug_flags & 4) {
359 Kprintf(" THS_RUN cp=%d : hp=%d ", cur->priority, prio);
360 }
361
362 if (prio < cur->priority) {
363 if (thctx.debug_flags & 4) {
364 Kprintf(" readyq = %x, newrun = %x:%d, prio = %d",
365 &thctx.ready_queue[prio],
366 new_,
367 new_->tag.id,
368 prio);
369 }
370
371 readyq_remove(new_, prio);
372 new_->status = THS_RUN;
373 thctx.run_next = new_;
374 cur->status = THS_READY;
375 readyq_insert_front(cur);
376 }
377 } else {
378 if (thctx.debug_flags & 4) {
379 Kprintf(" not THS_RUN ");
380
381 Kprintf(" readyq = %x, newrun = %x:%d, prio = %d",
382 &thctx.ready_queue[prio],
383 new_,
384 new_->tag.id,
385 prio);
386 }
387
388 readyq_remove(new_, prio);
389 new_->status = THS_RUN;
390 thctx.run_next = new_;
391 }
392
393 if ((thctx.debug_flags & 4) != 0)
394 Kprintf("\n");
395}
396
397struct regctx *new_context_cb(struct regctx *ctx)
398{
399 u64 new_time;
400 u32 timer;
401
402 if ((thctx.debug_flags & 3) != 0) {
403 if ((thctx.debug_flags & 3) == 1)
404 Kprintf("[%3d->", thctx.current_thread->tag.id);
405 if ((thctx.debug_flags & 3) == 2)
406 Kprintf("switch_context(%x:%x,pc=%x,ei=%x =>%x:%d)\n",
407 ctx,
408 ctx->unk,
409 ctx->pc,
410 ctx->I_CTRL,
411 thctx.current_thread,
412 thctx.current_thread->tag.id);
413 }
414
415 thctx.current_thread->saved_regs = ctx;
416 if ((u32)ctx < (u32)thctx.current_thread->stack_top) {
417 report_stack_overflow(thctx.current_thread);
418 }
419
420 if (!thctx.run_next) {
421 if (!list_empty(&thctx.delete_queue)) {
422 do_delete_thread();
423 }
424
425 schedule_next();
426 }
427
428 if (thctx.current_thread == thctx.run_next) {
429 thctx.thread_resume_count++;
430 } else {
431 timer = thctx.timer_func();
432 new_time = add64(0, timer - thctx.last_timer, thctx.current_thread->run_clocks_hi, thctx.current_thread->run_clocks_lo);
433
434 thctx.current_thread->run_clocks_lo = (u32)new_time;
435 thctx.current_thread->run_clocks_hi = (u32)(new_time >> 32);
436 thctx.thread_switch_count++;
437 (*thctx.current_thread->reason_counter)++;
438 }
439
440 thctx.current_thread = thctx.run_next;
441
442 if ((thctx.debug_flags & 3) != 0) {
443 if ((thctx.debug_flags & 3) == 1)
444 Kprintf("%3d]", thctx.run_next->tag.id);
445 if ((thctx.debug_flags & 3) == 2)
446 Kprintf(" switch_context --> %x:%x,pc=%x,ei=%x =>%x:%d\n",
447 thctx.run_next->saved_regs,
448 thctx.run_next->saved_regs->unk,
449 thctx.run_next->saved_regs->pc,
450 thctx.run_next->saved_regs->I_CTRL,
451 thctx.run_next,
452 thctx.run_next->tag.id);
453 }
454
455 // some sort of debug display?
456 if (thctx.debug_flags & 0x20) {
457 _sw(~(1 << ((thctx.run_next->tag.id - 1) & 7)), 0xbf802070);
458 }
459
460 return thctx.run_next->saved_regs;
461}
462
463int preempt_cb(int unk)
464{
465 if (thctx.run_next != thctx.current_thread) {
466 thctx.current_thread->reason_counter = &thctx.current_thread->irq_preemption_count;
467 return 1;
468 }
469
470 return 0;
471}
472
473void idle_thread()
474{
475 while (1)
476 ;
477}
478
479int timer_handler(void *user)
480{
481 struct thread_context *thctx = user;
482 struct alarm *alarm;
483 u32 status, counter, ret;
484 u64 time = 0;
485
486 status = GetTimerStatus(thctx->timer_id);
487 counter = GetTimerCounter(thctx->timer_id);
488
489 // overflow
490 if (status & 0x1000) {
491 thctx->time_hi++;
492 thctx->time_lo = counter;
493 }
494
495 // compare
496 if (status & 0x800) {
497 list_for_each_safe (alarm, &thctx->alarm, alarm_list) {
498 counter = GetTimerCounter(thctx->timer_id);
499 status = GetTimerStatus(thctx->timer_id);
500 if (counter < thctx->time_lo && (status & 0x1000)) {
501 thctx->time_hi++;
502 thctx->time_lo = counter;
503 }
504
505 time = as_u64(thctx->time_hi, counter);
506 if (time < alarm->target) {
507 break;
508 }
509
510 // alarm has fired, remove, update, and reschedule
511 list_remove(&alarm->alarm_list);
512
513 if (alarm->tag.id == 1) {
514 alarm->target += 0x100000000;
515 } else {
516 ret = alarm->cb(alarm->userptr);
517 if (!ret) {
518 alarm_free(alarm);
519 thctx->alarm_count--;
520 continue;
521 }
522
523 if (ret < thctx->min_wait) {
524 ret = thctx->min_wait;
525 }
526
527 alarm->target += ret;
528 }
529
530 alarm_insert(&thctx->alarm, alarm);
531 }
532
533 update_timer_compare(thctx->timer_id, time, &thctx->alarm);
534 }
535
536
537 return 1;
538}
539
540
541void init_timer()
542{
543 iop_sys_clock_t compare;
544 s32 timer_id, timer_irq;
545 struct alarm *alarm;
546 int *bootmode;
547 int state;
548
549 thctx.unk_clock_mult = 0x1200;
550 thctx.unk_clock_div = 125;
551
552 bootmode = QueryBootMode(7);
553 if (bootmode && *bootmode == 200) {
554 thctx.unk_clock_mult = 25;
555 thctx.unk_clock_div = 1;
556 }
557
558 USec2SysClock(100, &compare);
559
560 thctx.min_wait = compare.lo;
561 thctx.unk4c8 = 2 * compare.lo;
562
563 timer_id = AllocHardTimer(1, 32, 1);
564 thctx.timer_id = timer_id;
565 thctx.timer_func = GetTimerReadFunc(timer_id);
566 timer_irq = GetHardTimerIntrCode(timer_id);
567 RegisterIntrHandler(timer_irq, 1, timer_handler, &thctx);
568
569 list_init(&thctx.alarm);
570 list_init(&thctx.alarm_pool);
571 thctx.alarm_id = 0;
572 CpuSuspendIntr(&state);
573 alarm = alarm_alloc();
574 list_insert(&thctx.alarm, &alarm->alarm_list);
575 USec2SysClock(2000, &compare);
576 // hmm
577 alarm->target = 0x100000000LL - compare.lo;
578
579 thctx.alarm_count = 1;
580
581 for (int i = 0; i < 32; i++) {
582 alarm = heap_alloc(0, sizeof(*alarm));
583 thctx.alarm_id++;
584 alarm->tag.id = thctx.alarm_id;
585 alarm_free(alarm);
586 }
587
588 SetTimerMode(timer_id, 0);
589 SetTimerCompare(timer_id, compare.lo);
590 SetTimerCounter(timer_id, 0);
591 SetTimerMode(timer_id, 0x70);
592 EnableIntr(GetHardTimerIntrCode(timer_id));
593 CpuResumeIntr(state);
594}
595
596
597int _start(int argc, char **argv)
598{
599 struct thread *idle, *current;
600 iop_event_t flag;
601 int *BootMode;
602 int state;
603 int i;
604
605 if (RegisterNonAutoLinkEntries(&_exp_thrdman)) {
606 return MODULE_NO_RESIDENT_END;
607 }
608
609 if (RegisterLibraryEntries(&_exp_thbase)) {
610 return MODULE_NO_RESIDENT_END;
611 }
612
613 CpuSuspendIntr(&state);
614 RegisterLibraryEntries(&_exp_thevent);
615 RegisterLibraryEntries(&_exp_thsemap);
616 RegisterLibraryEntries(&_exp_thmsgbx);
617 RegisterLibraryEntries(&_exp_thfpool);
618 RegisterLibraryEntries(&_exp_thvpool);
619
620 memset(&thctx, 0, sizeof(thctx));
621 thctx.debug_flags = DEBUG_FLAGS;
622
623 list_init(&thctx.semaphore);
624 list_init(&thctx.event_flag);
625 list_init(&thctx.mbox);
626 list_init(&thctx.vpool);
627 list_init(&thctx.fpool);
628 list_init(&thctx.sleep_queue);
629 list_init(&thctx.delay_queue);
630 // list_init(&thctx.unused_list1);
631 // list_init(&thctx.unused_list2);
632 list_init(&thctx.dormant_queue);
633 list_init(&thctx.delete_queue);
634 list_init(&thctx.thread_list);
635
636 for (int i = 0; i < 128; i++) {
637 list_init(&thctx.ready_queue[i]);
638 }
639
640 thctx.heap = CreateHeap(2048, 1);
641
642 // Create the idle thread
643 idle = heap_alloc(TAG_THREAD, sizeof(*idle));
644 idle->tag.id = ++thctx.thread_id;
645 idle->stack_size = 512;
646 idle->stack_top = AllocSysMemory(1, 512, 0);
647 idle->init_priority = 127;
648 idle->priority = 127;
649 idle->attr = TH_C;
650 idle->status = THS_READY;
651 idle->entry = idle_thread;
652 idle->saved_regs = idle->stack_top + (((idle->stack_size << 2) >> 2) - RESERVED_REGCTX_SIZE);
653 memset(idle->saved_regs, 0, RESERVED_REGCTX_SIZE);
654 idle->gp = GetGP();
655
656 idle->saved_regs->unk = -2;
657 idle->saved_regs->sp = (u32)&idle->saved_regs[1];
658 idle->saved_regs->gp = (u32)idle->gp;
659 idle->saved_regs->fp = idle->saved_regs->sp;
660 idle->saved_regs->ra = (u32)ExitThread;
661 idle->saved_regs->sr = (idle->attr & 0xF0000000) | 0x404;
662 idle->saved_regs->sr |= idle->attr & 8;
663 idle->saved_regs->pc = (u32)idle->entry;
664 idle->saved_regs->I_CTRL = 1;
665
666 list_insert(&thctx.thread_list, &idle->thread_list);
667 thctx.idle_thread = idle;
668 readyq_insert_back(idle);
669
670 // Create a thread entry for our current state
671 current = heap_alloc(TAG_THREAD, sizeof(*current));
672 current->tag.id = ++thctx.thread_id;
673 // Taking the address of a stack variable to get
674 // the allocated stack and size. Cute.
675 current->stack_size = QueryBlockSize(&i);
676 current->stack_top = QueryBlockTopAddress(&i);
677 current->init_priority = 8;
678 current->priority = 1;
679 current->attr = TH_C;
680 current->status = THS_RUN;
681 current->gp = GetGP();
682
683 list_insert(&thctx.thread_list, &current->thread_list);
684 thctx.current_thread = current;
685 thctx.run_next = current;
686 current->queue.next = NULL;
687 current->queue.prev = NULL;
688
689 SetNewCtxCb(new_context_cb);
690 SetShouldPreemptCb(preempt_cb);
691 init_timer();
692
693 flag.attr = EA_MULTI;
694 flag.bits = 0;
695 flag.option = 0;
696 thctx.sytem_status_flag = CreateEventFlag(&flag);
697
698 BootMode = QueryBootMode(4);
699 if (BootMode) {
700 SetEventFlag(thctx.sytem_status_flag, 1 << (*BootMode & 3));
701 }
702
703 RegisterPostBootCallback(post_boot_callback_1, 2, 0);
704 RegisterPostBootCallback(post_boot_callback_2, 3, 0);
705
706 // mismatched with suspend?
708
709 return MODULE_RESIDENT_END;
710}
int CpuEnableIntr()
Definition intrman.c:240
int CpuResumeIntr(int state)
Definition intrman.c:217
void SetNewCtxCb(void *cb)
Definition intrman.c:703
int CpuDisableIntr()
Definition intrman.c:228
int CpuSuspendIntr(int *state)
Definition intrman.c:195
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition intrman.c:115
int EnableIntr(int irq)
Definition intrman.c:336
#define THS_RUN
Definition kernel.h:219
#define TH_SELF
Definition kernel.h:69
struct _ModuleInfo ModuleInfo_t
Definition loadcore.h:101
int thread_leave(int unk, int unk2, int intr_state, int release)
Definition thcommon.c:159
#define EA_MULTI
Definition thevent.h:36