// PingPongOS - PingPong Operating System /* PEDRO HENRIQUE FRIEDRICH RAMOS : GRR20243133 */ #include #include #include "tcb.h" #include "ctx.h" /* ------------------------------------------------------------------------- */ /* Constants -------------------------------------------------------------- */ /* 8 pages = 32 KiB */ #define STACK_SIZE (8 * 4096) /* ------------------------------------------------------------------------- */ /* Static variables ------------------------------------------------------- */ static struct task_t *task_current; static struct task_t task_kernel; /* Is task_kernel like init for ppos? */ /* ------------------------------------------------------------------------- */ /* Internal functions ----------------------------------------------------- */ /* Allocate memory for a task's stack. * * The size parameter must be page aligned! */ static inline void * alloc_stack (size_t size) { /* Using mmap () instead of malloc () makes a little bit more sense, since * when a task ends the mapping can be immediately deleted. If we use malloc, * we can't know for sure if the brk will be lowered. * * Also, it (probably) makes the stack allocations farther apart, so it's less * likely that a task can corrupt another task's stack by accident, which is * possible since ppos doesn't have virtual memory nor memory protection. */ return mmap (/* Let the kernel choose the address for the mapping. */ NULL, /* Size of the mapping (must be page-aligned). */ size, /* RW stack. */ PROT_READ | PROT_WRITE, /* Not shared between (linux) processes. * Not backed by a file. */ MAP_PRIVATE | MAP_ANONYMOUS, /* No file descriptor. */ -1, /* No offset. */ 0); } /* ------------------------------------------------------------------------- */ /* Free allocated memory for a task's stack. */ static inline int free_stack (void *stack, size_t size) { return munmap (stack, size); } /* ------------------------------------------------------------------------- */ /* Get the next free task id. * * Stupid simple implementation, not thread safe! */ static int get_next_id (void) { static int id = 0; return ++id; /* id=0 is reserved for task_kernel */ } /* ------------------------------------------------------------------------- */ /* Exported functions ----------------------------------------------------- */ void task_init (void) { /* TODO */ } /* ------------------------------------------------------------------------- */ struct task_t * task_create (char *name, void (*entry) (void *), void *arg) { struct task_t *task; unsigned char *stack; if (!(task = malloc (sizeof *task))) goto err_malloc_task; if (!(stack = alloc_stack (STACK_SIZE))) goto err_malloc_stack; task->id = get_next_id (); task->name = name; task->status = STATUS_READY; /* Probably not the best way to do this but with this interface and the * current implementation it's the best we can do right now. * * We need it for task_switch (). * * In a multiprocessor system this doesn't hold up. */ task->parent = task_current; if (ctx_create (&task->context, entry, arg, stack, STACK_SIZE)) goto err_ctx_create; return task; err_ctx_create: free_stack (stack, STACK_SIZE); err_malloc_stack: free (task); err_malloc_task: return NULL; } /* ------------------------------------------------------------------------- */ int task_destroy (struct task_t *task) { if (task->status != STATUS_TERMINATED) return ERROR; if (free_stack (task->context.stack, task->context.size)) return ERROR; /* Probably EINVAL. */ free (task); return NOERROR; } /* ------------------------------------------------------------------------- */ int task_switch (struct task_t *task) { if (!task) return task_switch (task_current->parent); /* Instructions not clear... */ switch (task->status) { case STATUS_TERMINATED: /* Switch to task_kernel or to parent? */ return task_switch (task_kernel); case STATUS_RUNNING: /* Return ERROR or switch to another task? */ return ERROR; } task_current->status = STATUS_SUSPENDED; task_current = task; task_current->status = STATUS_RUNNING; /* task_current and task are not the same, because if they task->status would * be STATUS_RUNNING, which is handled earlier. * * Since that's the only error case for ctx_swap (), we're fine ... */ return ctx_swap (&task_current->context, &task->context); } /* ------------------------------------------------------------------------- */ int task_id (struct task_t *task) { if (!task) return task_id (task->current); return task->id; } /* ------------------------------------------------------------------------- */ char * task_name (struct task_t *task) { if (!task) return task_id (task->current); return task->name; } /* ------------------------------------------------------------------------- */