PS2SDK
PS2 Homebrew Libraries
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 
15 IRX_ID("Multi_Thread_Manager", 2, 3);
16 extern struct irx_export_table _exp_thrdman;
17 extern struct irx_export_table _exp_thbase;
18 extern struct irx_export_table _exp_thevent;
19 extern struct irx_export_table _exp_thsemap;
20 extern struct irx_export_table _exp_thmsgbx;
21 extern struct irx_export_table _exp_thfpool;
22 extern struct irx_export_table _exp_thvpool;
23 
24 struct thread_context thctx;
25 
26 struct 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 
41 void 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 
50 void 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 
63 void 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 
79 void 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 
107 unsigned 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 
119 int 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) {
125  CpuDisableIntr();
126  Kprintf("CheckThreadStack()\n");
127  thread_leave(0, 0, 0, 0);
128  }
129 
130  return stack_remaining;
131 }
132 
133 void *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 
144 int heap_free(struct heaptag *tag)
145 {
146  tag->tag = 0;
147  return FreeHeapMemory(thctx.heap, tag);
148 }
149 
159 int 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 
181 int 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 
199 int 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 
220 int post_boot_callback_1(iop_init_entry_t *next, int delayed)
221 {
222  CpuEnableIntr();
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 
227 int post_boot_callback_2(iop_init_entry_t *next, int delayed)
228 {
229  CpuEnableIntr();
230  ChangeThreadPriority(TH_SELF, 126);
231  if (!next->callback) {
232  while (1) {
233  DelayThread(1000000);
234  }
235  }
236 
237  return 0;
238 }
239 
240 int 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
261 static 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 
287 u32 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 
298 void 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 
321 void 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 
338 void 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 
397 struct 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 
463 int 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 
473 void idle_thread()
474 {
475  while (1)
476  ;
477 }
478 
479 int 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 
541 void 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 
597 int _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?
707  CpuEnableIntr();
708 
709  return MODULE_RESIDENT_END;
710 }
CpuDisableIntr
int CpuDisableIntr()
Definition: intrman.c:228
RegisterIntrHandler
int RegisterIntrHandler(int irq, int mode, int(*handler)(void *arg), void *arg)
Definition: intrman.c:115
SetNewCtxCb
void SetNewCtxCb(void *cb)
Definition: intrman.c:703
CpuEnableIntr
int CpuEnableIntr()
Definition: intrman.c:240
sysclib.h
TH_SELF
#define TH_SELF
Definition: kernel.h:68
EA_MULTI
#define EA_MULTI
Definition: thevent.h:35
thread
Definition: thcommon.h:143
CpuSuspendIntr
int CpuSuspendIntr(int *state)
Definition: intrman.c:195
event
Definition: thcommon.h:71
THS_RUN
#define THS_RUN
Definition: kernel.h:218
loadcore.h
timrman.h
alarm
Definition: alarm.c:27
heaplib.h
EnableIntr
int EnableIntr(int irq)
Definition: intrman.c:336
list_head
Definition: list.h:10
heaptag
Definition: thcommon.h:65
irx_export_table
Definition: irx.h:90
stdio.h
iop_init_entry_t
Definition: loadcore.h:100
CpuResumeIntr
int CpuResumeIntr(int state)
Definition: intrman.c:217
sysmem.h
defs.h
thread_context
Definition: thcommon.h:183
intrman.h
_ModuleInfo
Definition: loadcore.h:31
iop_event_t
Definition: thevent.h:37
thevent.h
_iop_sys_clock
Definition: thbase.h:87
kerr.h
regctx
Definition: thcommon.h:53