Add synchornization primitives, `waiting` to task
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)