// // Created by rick on 22-02-21. // #include #include #include #include #include #include #include #include #include #include #define stack_end(task) ((task)->stack + ((task)->stack_page_count * PAGE_SIZE)) #define TASK_STATE_UNKNOWN (0) #define TASK_STATE_RUNNABLE (1 << 0) #define TASK_STATE_RUNNING (1 << 1) #define TASK_STATE_WAIT_IRQ (1 << 2) #define TASK_STATE_WAIT_SIGNAL (1 << 3) #define TASK_STATE_STOPPED (1 << 6) #define TASK_STATE_ERROR (1 << 7) const char *task_state_str(uint8_t state) { switch (state) { case TASK_STATE_RUNNABLE: return "RUNNABLE "; case TASK_STATE_RUNNING: return "RUNNING "; case TASK_STATE_WAIT_IRQ: return "WAIT_IRQ "; case TASK_STATE_WAIT_SIGNAL: return "WAIT_SIGNAL"; case TASK_STATE_STOPPED: return "STOPPED "; case TASK_STATE_ERROR: return "ERROR "; case TASK_STATE_UNKNOWN: default: return "UNKNOWN "; } } int errno = 0; typedef struct task { // state bool present: 1; uint8_t state; uint16_t wait_irq; // identity pid_t tid; char *name; // linked list struct task *next; // persistence/memory int errno; void *stack; uint32_t stack_page_count; task_registers_t *task_registers; } task_t; typedef struct { task_registers_t task_registers; isr_registers_t isr_registers; task_entrypoint entrypoint; void *entry_data; char alignment[4]; } att_packed task_stack_start; volatile uint32_t task_locked = 0; task_t *idle_task = NULL; task_t *first_task = NULL; task_t *last_task = NULL; task_t *current_task = NULL; pid_t last_tid = 0; pid_t reaper_pid = 0; extern void switch_task(task_registers_t **, task_registers_t *); extern att_cdecl att_noreturn void __task_entry_point(); extern att_noreturn void __task_entry_point_inner(); // internal api void task_free(task_t *task); // explicit cdecl calling convention void att_unused att_cdecl att_noreturn task_entry_point(task_entrypoint entrypoint, void *entry_data) { entrypoint(entry_data); syscall_kill_self(); while (true); // halt } void task_ensure_enabled() { if (current_task == NULL) { k_panics("Tasking should be enabled\n"); } } void task_lock_acquire() { if (__sync_add_and_fetch(&task_locked, 1) == UINT32_MAX) { k_panics("To many task locks"); }; } void task_lock_free() { if (__sync_sub_and_fetch(&task_locked, 1) == UINT32_MAX) { k_panics("To many task free's"); } } pid_t task_get_current_tid() { return current_task->tid; } void task_signal(pid_t tid) { task_t *t = first_task; while (t != NULL) { if (t->tid == tid) { if (t->state != TASK_STATE_WAIT_SIGNAL) { // todo } t->state = TASK_STATE_RUNNABLE; break; } t = t->next; } } void task_suspend() { current_task->state = TASK_STATE_WAIT_SIGNAL; task_switch_next(); } // internal tasks void att_noreturn task_idle(void *data) { while (true) __asm__("hlt"); } void att_noreturn task_reaper(void *data) { while (true) { bool did_reap = false; task_lock_acquire(); task_t *t = first_task; task_t *p = first_task; task_t *n = NULL; while (t != NULL) { n = t->next; if (t->state == TASK_STATE_STOPPED) { did_reap = true; if (t == first_task) { if (n == NULL) { k_panics("No more tasks"); } first_task = n; task_free(t); t = NULL; } else { p->next = n; task_free(t); t = p; } } p = t; t = n; } task_lock_free(); if (did_reap) { syscall_yield_job(); } else { syscall_job_suspend(); } } } void task_notify_irq(uint8_t irq_no) { uint16_t irq__bit = 1 << irq_no; task_t *t = first_task; while (t != NULL) { if (t->state == TASK_STATE_WAIT_IRQ && (t->wait_irq & irq__bit) != 0) { t->wait_irq = 0; t->state = TASK_STATE_RUNNABLE; } t = t->next; } } void task_wait_irq(uint16_t irq_bits) { current_task->wait_irq = irq_bits; current_task->state = TASK_STATE_WAIT_IRQ; task_switch_next(); } void task_switch_next_inner(task_t *next_task) { if (task_locked != 0) { return; // don't switch while the task is locked } if (next_task == current_task) { return; } task_t *previous_task = current_task; current_task = next_task; if (previous_task != NULL) { previous_task->errno = errno; if (previous_task->state == TASK_STATE_RUNNING) { previous_task->state = TASK_STATE_RUNNABLE; } } current_task->state = TASK_STATE_RUNNING; errno = current_task->errno; // switch task switch_task(previous_task == NULL ? NULL : &previous_task->task_registers, current_task->task_registers); } void task_start_first() { if (task_locked > 0) { k_panics("Tasking locked before start\n"); } task_switch_next_inner(first_task); } task_t *task_first_runnable() { task_t *next_task = current_task; bool looped = false; if (next_task == idle_task || next_task->next == NULL) { looped = true; next_task = first_task; } else { next_task = current_task->next; } do { if (!looped && next_task == NULL) { looped = true; next_task = first_task; } if (next_task == NULL) { break; } if (next_task->state == TASK_STATE_RUNNABLE) { return next_task; } next_task = next_task->next; } while (next_task != current_task); if (current_task->state == TASK_STATE_RUNNING) return current_task; return idle_task; } void task_switch_next() { if (current_task == NULL) { return; } task_switch_next_inner(task_first_runnable()); } task_t *task_create(task_entrypoint entrypoint, void *entry_data, char *name) { task_t *new_task = malloc(sizeof(task_t)); memset((uint8_t *) new_task, 0, sizeof(task_t)); new_task->tid = last_tid++; new_task->name = strdup(name); new_task->state = TASK_STATE_RUNNABLE; new_task->stack = pmm_get_pages(16); new_task->stack_page_count = 16; // todo check for null // prepare stack // cdecl format task_stack_start *stack = (task_stack_start *) (stack_end(new_task) - sizeof(task_stack_start)); memset((uint8_t *) stack, 0, sizeof(task_stack_start)); // setup isr return frame stack->isr_registers.cs = 0x08; // todo proper gdt setup stack->isr_registers.ds = 0x10; // todo // stack->isr_registers.ss = 0x10; // todo stack->isr_registers.eip = (uint32_t) __task_entry_point_inner; stack->isr_registers.eflags = 0x200; // setup task switch frame stack->task_registers.eip = (uint32_t) __task_entry_point; stack->task_registers.ebp = (uint32_t) stack_end(new_task); // setup entrypoint stack->entrypoint = entrypoint; stack->entry_data = entry_data; new_task->task_registers = &stack->task_registers; new_task->present = true; return new_task; } void task_free(task_t *task) { pmm_free_pages(task->stack, 16); free(task); } void task_init() { idle_task = task_create(task_idle, NULL, "idle"); reaper_pid = task_spawn(task_reaper, NULL, "reaper"); } pid_t task_spawn(task_entrypoint entrypoint, void *entry_data, char *name) { task_t *new_task = task_create(entrypoint, entry_data, name); if (first_task == NULL) { // first task first_task = new_task; } else { last_task->next = new_task; } last_task = new_task; return new_task->tid; } void task_end(pid_t tid) { task_t *t = first_task; while (t != NULL) { if (t->tid == tid) { t->state = TASK_STATE_STOPPED; task_signal(reaper_pid); break; } t = t->next; } } void task_print_all() { // acquiring task lock as reference to current task may disappear // might overrun kprint buffer task_lock_acquire(); printf("tasks:\n"); task_t *c = first_task; while (c != NULL) { printf("%d - %s %s\n", c->tid, task_state_str(c->state), c->name); c = c->next; } task_lock_free(); }