// PingPongOS - PingPong Operating System
-void task_init()
+/* PEDRO HENRIQUE FRIEDRICH RAMOS : GRR20243133 */
+
+#include <sys/mman.h>
+#include <stlib.h>
+#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;
+}
+
+/* ------------------------------------------------------------------------- */