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(&current_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