Nucleus
Barry Multitasking support db9a0c1 (3 years, 3 months ago)
diff --git a/task/clone.c b/task/clone.c
new file mode 100644
index 0000000..0c95083
--- /dev/null
+++ b/task/clone.c
@@ -0,0 +1,41 @@
+/*
+ * This file contains the clone() function/system call. It creates a new task,
+ * and copies most of the attributes from the parent task into it.
+ */
+
+#include <sys/types.h>
+#include <nucleus/task.h>
+
+/* Read the EIP */
+static uintptr_t
+read_eip(void)
+{
+ uintptr_t eip;
+ asm volatile("movl 4(%%ebp), %0" : "=r" (eip));
+ return eip;
+}
+
+/* Clone a task */
+pid_t
+clone(int flags)
+{
+ Task *parent = current, *child = new(&taskType), *tmp;
+
+ child->pageDir = clone_dir();
+
+ parent->next = child;
+ child->next = parent;
+
+ /* Split tasks here */
+ uintptr_t esp, ebp, eip;
+ eip = read_eip();
+ if (current == parent) {
+ asm volatile("mov %%esp, %0" : "=r" (esp));
+ asm volatile("mov %%ebp, %0" : "=r" (ebp));
+ child->esp = esp;
+ child->ebp = ebp;
+ child->eip = eip;
+ return child->tid;
+ }
+ return 0;
+}
diff --git a/task/scheduler.c b/task/scheduler.c
new file mode 100644
index 0000000..9ad1405
--- /dev/null
+++ b/task/scheduler.c
@@ -0,0 +1,61 @@
+/*
+ * This file contains the scheduler. It implements a basic task switching
+ * routine, as well as the schedule() function. The scheduler can be called
+ * from anywhere, and will switch to the next task decided by the scheduler
+ * rules. If it cannot find a task to schedule, it just idles until one becomes
+ * available. This avoids the need for an idle task.
+ */
+
+#include <nucleus/panic.h>
+#include <nucleus/task.h>
+
+/* Read the EIP */
+static uintptr_t
+read_eip(void)
+{
+ uintptr_t eip;
+ asm volatile("movl 4(%%ebp), %0" : "=r" (eip));
+ return eip;
+}
+
+/* Switch to a task */
+static void
+switch_to_task(Task *task)
+{
+ uintptr_t esp, ebp, eip;
+ asm volatile("mov %%esp, %0" : "=r" (esp));
+ asm volatile("mov %%ebp, %0" : "=r" (ebp));
+ eip = read_eip();
+ if (eip == 0x10032004) /* Magic number */
+ return;
+
+ current->esp = esp;
+ current->ebp = ebp;
+ current->eip = eip;
+ current = task;
+ esp = current->esp;
+ ebp = current->ebp;
+ eip = current->eip;
+
+ asm volatile (
+ "cli;"
+ "movl %0, %%ecx;"
+ "movl %1, %%esp;"
+ "movl %2, %%ebp;"
+ "movl %3, %%cr3;"
+ "movl $0x10032004, %%eax;"
+ "sti;"
+ "jmp *%%ecx"
+ :: "g" (eip), "g" (esp), "g" (ebp), "g" (current->pageDir)
+ );
+}
+
+/* Schedule the next task */
+void
+schedule(void)
+{
+ Task *task = current;
+ if (!task->next)
+ return;
+ switch_to_task(task->next);
+}
diff --git a/task/task.c b/task/task.c
new file mode 100644
index 0000000..507caae
--- /dev/null
+++ b/task/task.c
@@ -0,0 +1,88 @@
+/*
+ * This file is in control of initialising the tasking subsystem. It also
+ * implements the Task object.
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <nucleus/cpu.h>
+#include <nucleus/task.h>
+#include <nucleus/memory.h>
+#include <nucleus/object.h>
+
+void timer_handler(struct InterruptFrame *frame);
+
+static void *task_new(void);
+static void task_delete(Object *);
+
+/* Task object type */
+ObjectType taskType = {
+ .new = task_new,
+ .delete = task_delete,
+};
+
+Task *currentTask[MAX_CPUS];
+pid_t nextTid = 1;
+
+/* Create a new Task */
+static void *
+task_new(void)
+{
+ Task *task = kmalloc(sizeof(Task));
+ task->tid = nextTid++;
+ task->priority = NORMAL;
+ task->state = READY;
+ return task;
+}
+
+/* Destroy a Task */
+static void
+task_delete(Object *obj)
+{
+ kfree(obj);
+}
+
+extern char stackTop[];
+
+/* Move the stack */
+static void
+move_stack(uintptr_t top, size_t size)
+{
+ size_t offset;
+ uintptr_t oldStack, oldBase;
+ uintptr_t newStack, newBase;
+ top -= sizeof(uintptr_t);
+ asm volatile("mov %%esp, %0" : "=r" (oldStack));
+ asm volatile("mov %%ebp, %0" : "=r" (oldBase));
+ offset = top - (uintptr_t) stackTop;
+ newStack = oldStack + offset;
+ newBase = oldBase + offset;
+
+ memcpy((void *) newStack, (void *) oldStack, (size_t) stackTop - oldStack);
+
+ /* Update pointers on the stack */
+ uintptr_t i, tmp;
+ for (i = top; i > top - size; i -= sizeof(uintptr_t)) {
+ tmp = *(uintptr_t *) i;
+ if (tmp > oldStack && tmp < (uintptr_t) stackTop) {
+ tmp += offset;
+ *(uintptr_t *) i = tmp;
+ }
+ }
+
+ asm volatile("mov %0, %%esp" :: "r" (newStack));
+ asm volatile("mov %0, %%ebp" :: "r" (newBase));
+}
+
+/* Initialise tasking */
+void
+init_tasking(void)
+{
+ move_stack(0xF0800000, 0x2000);
+
+ current = new(&taskType);
+ asm volatile("mov %%cr3, %0" : "=r" (current->pageDir));
+
+ register_interrupt(0, timer_handler);
+}
diff --git a/task/time.c b/task/time.c
new file mode 100644
index 0000000..39675e4
--- /dev/null
+++ b/task/time.c
@@ -0,0 +1,30 @@
+/*
+ * This file controls the system clock and contains the functions related to
+ * getting and setting the time from various sources. It keeps a monotonic
+ * clock internally, but can also make use of the tasks' clocks, and the RTC.
+ */
+
+#include <stdint.h>
+#include <nucleus/cpu.h>
+#include <nucleus/task.h>
+
+uint32_t monotonic = 0;
+uint8_t slice[MAX_CPUS] = {0};
+
+/* Timer interrupt */
+void
+timer_handler(struct InterruptFrame *frame)
+{
+ monotonic++;
+
+ /* Account timeslices */
+ slice[CPUID]++;
+ if (!current)
+ return;
+
+ /* Call scheduler */
+ if (slice[CPUID] < current->priority)
+ return;
+ slice[CPUID] = 0;
+ schedule();
+}