Add synchornization primitives, `waiting` to task
diff --git a/include/kernel/sync.h b/include/kernel/sync.h
new file mode 100644
index 0000000..3fbe209
--- /dev/null
+++ b/include/kernel/sync.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <kint.h>
+
+// Synchronization primitives
+
+/// Spinlock
+typedef volatile int spinlock_t;
+
+void sl_acquire(spinlock_t *sl);
+void sl_release(spinlock_t *sl);
+spinlock_t sl_new();
+
+/// Semaphore
+typedef volatile int semaphore_t;
+
+void sm_wait(semaphore_t sm);
+void sm_signal(semaphore_t sm);
+semaphore_t sm_new();
+
+/// Initialize synchronization primitives, only call this once after the
+/// scheduler has been initialized.
+void init_sync();
diff --git a/include/kernel/task.h b/include/kernel/task.h
index b0d0f97..83e7fd0 100644
--- a/include/kernel/task.h
+++ b/include/kernel/task.h
@@ -29,6 +29,8 @@
struct task
{
int id;
+ /// Is this task waiting for I/O?
+ bool waiting;
struct process *proc;
/// Physical address of the top of the stack.
uint stack_top_p;
@@ -60,6 +62,8 @@
void spawn_thread(task_function_t function, void *data);
+void set_waiting(int tid, bool waiting);
+
/**
* Halt the current thread.
*/
diff --git a/include/sys.h b/include/sys.h
new file mode 100644
index 0000000..0a7044e
--- /dev/null
+++ b/include/sys.h
@@ -0,0 +1,16 @@
+#pragma once
+
+// Bluejay System Calls
+
+#define SYS_GIVEUP 0x100
+
+/// Bluejay interrupt number
+#define SYS_INT 0x80
+
+/**
+ * @brief Gives up the current threads remaining CPU time. Unless you have set
+ * the thread to waiting, it will resume next time the scheduler decides it's
+ * its turn. The behaviour is identical to if the thread had just run out of
+ * time naturally.
+ */
+extern void sys_giveup();
diff --git a/share/jmk/jmk.m4 b/share/jmk/jmk.m4
index 0b2c96c..e251a8b 100644
--- a/share/jmk/jmk.m4
+++ b/share/jmk/jmk.m4
@@ -20,6 +20,7 @@
CC ?= gcc
LD ?= ld
CFLAGS += -I$(ROOT)/include
+ASMFLAGS += -felf
jmk_clean_libs =
MAKEFILE_DEPTH ?= 1
@@ -35,7 +36,8 @@
`ifelse($1, `freestanding',
`CFLAGS += -nostdlib -nostdinc -fno-builtin -fno-stack-protector -ffreestanding',
$1, `optimize', `CFLAGS += -O2',
- $1, `debug', `CFLAGS += -g',
+ $1, `debug', `CFLAGS += -g
+ASMFLAGS += -Fdwarf',
$1, `32', `CFLAGS += -m32',
$1, `warn', `CFLAGS += -Wall -Wno-unused-function -Wno-unused-variable -Wno-incompatible-pointer-types',
$1, `nasm', `ASM = nasm')')
diff --git a/src/kernel/Jmk b/src/kernel/Jmk
index eefaa34..0b1eac6 100644
--- a/src/kernel/Jmk
+++ b/src/kernel/Jmk
@@ -11,6 +11,7 @@
archetype(c)
archetype(asm)
+depends(sys, $(ROOT)/src/libsys, libsys.a)
depends(initrd, $(ROOT)/boot/initrd, initrd.img)
depends(ata_pio, dri/ata_pio, ata_pio.a)
depends(pci, dri/pci, pci.a)
@@ -53,10 +54,12 @@
task.o \
task_api.o \
faults.o \
+ sync.o \
lib(ext2) \
lib(ide) \
lib(ata_pio) \
- lib(pci)
+ lib(pci) \
+ lib(sys)
type(custom_link)
diff --git a/src/kernel/dri/ata_pio/ata_pio.c b/src/kernel/dri/ata_pio/ata_pio.c
index 5696925..1554702 100644
--- a/src/kernel/dri/ata_pio/ata_pio.c
+++ b/src/kernel/dri/ata_pio/ata_pio.c
@@ -149,36 +149,4 @@
void init_ata_pio()
{
add_interrupt_handler(46, ata_pio_handle_irq);
-
-/*
- // TODO: Consider adding this back.
-
- // 0xA0 for master, 0xB0 for slave
- outb(ATA_PORT_DRIVE_SEL, 0xA0);
-
- outb(ATA_PORT_LBA_LOW, 0);
- outb(ATA_PORT_LBA_MID, 0);
- outb(ATA_PORT_LBA_HIGH, 0);
-
- outb(ATA_PORT_CMD, ATA_CMD_IDENTIFY);
-
- if (inb(ATA_PORT_STATUS))
- {
- kprintf(OKAY "ATA drive exists\n");
-
- // Wait until either DRQ or ERR is set
- uchar status;
- while ((status = inb(ATA_PORT_STATUS)) & (ATA_DRQ | ATA_ERR))
- {
- if (status & ATA_ERR)
- {
-
- }
- }
- }
- else
- {
- kprintf(ERROR "ATA drive does not exist\n");
- }
-*/
}
diff --git a/src/kernel/sync.c b/src/kernel/sync.c
new file mode 100644
index 0000000..2bac9e4
--- /dev/null
+++ b/src/kernel/sync.c
@@ -0,0 +1,142 @@
+#include <sync.h>
+#include <alloc.h>
+#include <task.h>
+#include <sys.h>
+#include "syscall.h"
+
+#define SM_PER_LIST 1024
+#define SM_MAX_WAITING 64
+
+struct sm_task
+{
+ uint tid;
+ struct sm_task *next;
+};
+
+struct semaphore
+{
+ spinlock_t task_lock;
+ struct sm_task *first, *last;
+ _Atomic uint sm;
+};
+
+struct sm_list
+{
+ struct semaphore semaphores[SM_PER_LIST];
+ struct sm_list *next;
+};
+
+static struct sm_list *sm_first = NULL;
+static uint sm_num = 0;
+
+
+void sl_acquire(spinlock_t *sl)
+{
+ // This is a GCC intrinsic
+ while (!__sync_bool_compare_and_swap(sl, -2, 1))
+ {
+ // Kindly inform the CPU that this is a spin lock
+ asm("pause");
+ }
+}
+
+void sl_release(spinlock_t *sl)
+{
+ *sl = 0;
+}
+
+spinlock_t sl_new()
+{
+ return 0;
+}
+
+void sm_unsafe_wait(struct semaphore *sm)
+{
+ sm->sm--;
+
+ if (sm->sm < 0)
+ {
+ // Add this task to the waiting list
+ // This will be quick, so just use a spinlock
+ sl_acquire(sm->task_lock);
+
+ struct sm_task *task = malloc(sizeof(struct sm_task));
+
+ task->next = NULL;
+ task->tid = task_id();
+
+ if (sm->last)
+ {
+ sm->last->next = task;
+ sm->last = task;
+ }
+ else
+ {
+ sm->last = sm->first = task;
+ }
+
+ sl_release(sm->task_lock);
+
+ set_waiting(task_id(), true);
+ sys_giveup();
+ }
+
+ // Otherwise there's nobody else waiting, just go ahead
+}
+
+void sm_unsafe_signal(struct semaphore *sm)
+{
+ sm->sm++;
+
+ if (sm->sm <= 0)
+ {
+ sl_acquire(sm->task_lock);
+
+ if (sm->first)
+ {
+ struct sm_task *f = sm->first;
+ sm->first = sm->first->next;
+
+ set_waiting(f->tid, false);
+
+ free(f);
+ }
+
+ sl_release(sm->task_lock);
+ }
+}
+
+semaphore_t sm_new()
+{
+ if (sm_first == NULL)
+ {
+ sm_first = malloc(sizeof(struct sm_list));
+ }
+
+ uint num = sm_num++;
+
+ struct sm_list *l = sm_first;
+
+ while (num >= SM_PER_LIST)
+ {
+ if (!l->next)
+ {
+ l->next = malloc(sizeof(struct sm_list));
+ }
+
+ l = l->next;
+
+ num -= SM_PER_LIST;
+ }
+
+ l->semaphores[num].first = NULL;
+ l->semaphores[num].last = NULL;
+ l->semaphores[num].sm = 1;
+ l->semaphores[num].task_lock = sl_new();
+
+ return num;
+}
+
+void init_sync()
+{
+}
diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c
index 5a48b30..1f31296 100644
--- a/src/kernel/syscall.c
+++ b/src/kernel/syscall.c
@@ -1,10 +1,22 @@
#include "syscall.h"
#include "log.h"
#include "pic.h"
+#include "task.h"
+
+#include <sys.h>
void do_syscall(struct registers *regs)
{
- kprintf(INFO "Syscall executed: %d\n", regs->eax);
+ switch (regs->eax)
+ {
+ case SYS_GIVEUP:
+ // easy, just switch tasks
+ switch_task();
+ break;
+
+ default:
+ kprintf(INFO "Syscall executed: %d\n", regs->eax);
+ }
}
void init_syscall()
diff --git a/src/kernel/task.c b/src/kernel/task.c
index c8068c4..116e4a2 100644
--- a/src/kernel/task.c
+++ b/src/kernel/task.c
@@ -53,6 +53,7 @@
.ebp = kernel_ebp,
.eip = kernel_eip,
.id = next_task_id++,
+ .waiting = false,
};
tasks_initialized = true;
@@ -105,6 +106,7 @@
task->id = next_task_id++;
task->ebp = task->esp = new_stack_base_v;
task->eip = (uint)function;
+ task->waiting = false;
last_task->next = ll_task;
ll_task->prev = last_task;
@@ -172,15 +174,42 @@
current_task->task.esp = esp;
current_task->task.eip = eip;
- if (current_task->next == NULL)
+ struct ll_task_i *original = current_task;
+
+ do
{
- current_task = first_task;
+ if (current_task->next == NULL)
+ {
+ current_task = first_task;
+ }
+ else
+ {
+ // Continue the next task
+ current_task = current_task->next;
+ }
}
- else
+ while (current_task->task.waiting);
+
+ if (current_task == original && original->task.waiting)
{
- // Continue the next task
- current_task = current_task->next;
+ kpanic("All tasks are waiting for I/O. There must be at least 1 task using CPU at all times.");
}
switch_to_task(¤t_task->task);
}
+
+void set_waiting(int tid, bool waiting)
+{
+ asm("cli");
+
+ for (struct ll_task_i *t = first_task; t != NULL; t = t->next)
+ {
+ if (t->task.id == tid)
+ {
+ t->task.waiting = waiting;
+ break;
+ }
+ }
+
+ asm("sti");
+}
diff --git a/src/kernel/task_api.s b/src/kernel/task_api.s
index 01f3ca4..c862c21 100644
--- a/src/kernel/task_api.s
+++ b/src/kernel/task_api.s
@@ -11,9 +11,7 @@
;; add esp, 12 ; Clear the arguments
popa ; Reset everything
xor eax, eax ; Return 0
-
- pop ebx ; This is just to make debugging easy
- jmp ebx
+ ret
[global _switch_to_task]
;; _switch_to_task(uint page_directory, uint eip, uint ebp, uint esp)
diff --git a/src/libsys/Jmk b/src/libsys/Jmk
new file mode 100644
index 0000000..b233578
--- /dev/null
+++ b/src/libsys/Jmk
@@ -0,0 +1,21 @@
+init(libsys, libsys.a)
+
+preset(optimize)
+preset(warn)
+preset(freestanding)
+preset(32)
+preset(debug)
+preset(nasm)
+
+archetype(asm)
+
+OBJECTS = sys.o
+
+sys.s: sys.inc
+
+sys.inc: $(ROOT)/include/sys.h
+ @cat $< | grep '#define' | sed 's/^#/%/' > $@
+
+type(static_lib)
+
+finish
diff --git a/src/libsys/sys.s b/src/libsys/sys.s
new file mode 100644
index 0000000..aa2212e
--- /dev/null
+++ b/src/libsys/sys.s
@@ -0,0 +1,11 @@
+;;;; Bluejay system call client library implementation.
+
+ ;; System call numbers
+ %include "sys.inc"
+
+ [bits 32]
+ [global sys_giveup]
+sys_giveup:
+ mov eax, SYS_GIVEUP
+ int SYS_INT
+ ret ; Will return here after the task is re-scheduled