summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordzwdz2022-08-04 15:58:54 +0200
committerdzwdz2022-08-04 15:58:54 +0200
commit81a58004d51547d074b4218f906b0b95f2b2c5dc (patch)
tree4301047fc342e165bc5a043a04b5fed44b8e8084 /src
parent4a844820866094ff7d57435a16b7c23a9126814d (diff)
syscalls: add _syscall_sleep()
Diffstat (limited to 'src')
-rw-r--r--src/kernel/arch/amd64/32/32130
-rw-r--r--src/kernel/arch/amd64/32/boot.s1
-rw-r--r--src/kernel/arch/amd64/interrupts/irq.c9
-rw-r--r--src/kernel/arch/amd64/interrupts/irq.h1
-rw-r--r--src/kernel/arch/amd64/interrupts/isr.c6
-rw-r--r--src/kernel/arch/amd64/time.c50
-rw-r--r--src/kernel/arch/amd64/time.h4
-rw-r--r--src/kernel/arch/generic.h6
-rw-r--r--src/kernel/proc.c17
-rw-r--r--src/kernel/proc.h6
-rw-r--r--src/kernel/syscalls.c8
-rw-r--r--src/shared/include/camellia/syscalls.h4
-rw-r--r--src/user/app/shell/builtins.c9
-rw-r--r--src/user/app/tests/tests.c31
-rw-r--r--src/user/lib/syscall.c4
15 files changed, 150 insertions, 136 deletions
diff --git a/src/kernel/arch/amd64/32/32 b/src/kernel/arch/amd64/32/32
deleted file mode 100644
index 9bd97e6..0000000
--- a/src/kernel/arch/amd64/32/32
+++ /dev/null
@@ -1,130 +0,0 @@
-#include <kernel/arch/generic.h>
-#include <kernel/arch/amd64/gdt.h>
-#include <shared/mem.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-extern char _isr_mini_stack;
-
-struct gdt_entry {
- uint64_t limit_low : 16;
- uint64_t base_low : 24;
- uint64_t accessed : 1; // set by the processor
- // CODE | DATA
- uint64_t rw : 1; // readable? | writeable?
- uint64_t conforming : 1; // conforming? | expands down?
- uint64_t code : 1; // 1 | 0
-
- uint64_t codeordata : 1; // 1 for everything other than TSS and LDT
- uint64_t ring : 2;
- uint64_t present : 1; // always 1
- uint64_t limit_high : 4;
- uint64_t available : 1; // ???
- uint64_t long_mode : 1;
- uint64_t x32 : 1;
- uint64_t gran : 1; // 1 - 4kb, 0 - 1b
- uint64_t base_high : 8;
-} __attribute__((packed));
-
-struct tss_entry {
- uint32_t _unused0;
- uint32_t esp0; // kernel mode stack pointer
- uint32_t ss0; // kernel mode stack segment
- uint8_t _unused1[0x5c];
-} __attribute__((packed));
-
-struct lgdt_arg {
- uint16_t limit;
- uint32_t base;
-} __attribute__((packed));
-
-__attribute__((section(".shared")))
-static struct gdt_entry GDT[SEG_end];
-__attribute__((section(".shared")))
-static struct tss_entry TSS;
-static struct lgdt_arg lgdt_arg; // probably doesn't need to be global
-
-static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code);
-static void gdt_prepare(void);
-static void gdt_load(void);
-
-
-static void gdt_fillout(struct gdt_entry* entry, uint8_t ring, bool code) {
- *entry = (struct gdt_entry) {
- // set up the identity mapping
- .limit_low = 0xFFFF,
- .limit_high = 0xF,
- .gran = 1, // 4KB * 0xFFFFF = (almost) 4GB
- .base_low = 0,
- .base_high = 0,
-
- .ring = ring,
- .code = code,
-
- .accessed = 0,
- .rw = 1,
- .conforming = 0,
- .codeordata = 1,
- .present = 1,
- .long_mode = 0, // ???
- .available = 1, // ???
- .x32 = 1,
- };
-}
-
-static void gdt_prepare(void) {
- GDT[SEG_null].present = 0;
-
- gdt_fillout(&GDT[SEG_r0code], 0, true);
- gdt_fillout(&GDT[SEG_r0data], 0, false);
- gdt_fillout(&GDT[SEG_r3code], 3, true);
- gdt_fillout(&GDT[SEG_r3data], 3, false);
-
- // tss
- memset(&TSS, 0, sizeof(TSS));
- TSS.ss0 = SEG_r0data << 3; // kernel data segment
- TSS.esp0 = (uintptr_t) &_isr_mini_stack;
-
- GDT[SEG_TSS] = (struct gdt_entry) {
- .limit_low = sizeof(TSS),
- .limit_high = sizeof(TSS) >> 16,
- .gran = 0,
- .base_low = (uintptr_t) &TSS,
- .base_high = ((uintptr_t) &TSS) >> 24,
-
- .accessed = 1, // 1 for TSS
- .rw = 0, // 1 busy / 0 not busy
- .conforming = 0, // 0 for TSS
- .code = 1, // 32bit
- .codeordata = 0, // is a system entry
- .ring = 3,
- .present = 1,
- .available = 0, // 0 for TSS
- .long_mode = 0,
- .x32 = 0, // idk
- };
-}
-
-static void gdt_load(void) {
- lgdt_arg.limit = sizeof(GDT) - 1;
- lgdt_arg.base = (uintptr_t) &GDT;
- asm("lgdt (%0)"
- : : "r" (&lgdt_arg) : "memory");
-
- asm("ltr %%ax"
- : : "a" (SEG_TSS << 3 | 3) : "memory");
-
- // update all segment registers
- gdt_farjump(SEG_r0code << 3);
- asm("mov %0, %%ds;"
- "mov %0, %%ss;"
- "mov %0, %%es;"
- "mov %0, %%fs;"
- "mov %0, %%gs;"
- : : "r" (SEG_r0data << 3) : "memory");
-}
-
-void gdt_init(void) {
- gdt_prepare();
- gdt_load();
-}
diff --git a/src/kernel/arch/amd64/32/boot.s b/src/kernel/arch/amd64/32/boot.s
index 0e21036..0621038 100644
--- a/src/kernel/arch/amd64/32/boot.s
+++ b/src/kernel/arch/amd64/32/boot.s
@@ -2,6 +2,7 @@
.global _start
.type _start, @function
_start:
+ cli
mov $_stack_top, %esp
push %ebx // save the address of the multiboot struct
diff --git a/src/kernel/arch/amd64/interrupts/irq.c b/src/kernel/arch/amd64/interrupts/irq.c
index b5e9255..9872ec7 100644
--- a/src/kernel/arch/amd64/interrupts/irq.c
+++ b/src/kernel/arch/amd64/interrupts/irq.c
@@ -5,6 +5,12 @@
static const int PIC1 = 0x20;
static const int PIC2 = 0xA0;
+static void pit_init(void) {
+ uint16_t divisor = 1193;
+ port_out8(0x40, divisor & 0xFF);
+ port_out8(0x40, divisor >> 8);
+}
+
void irq_init(void) {
port_out8(PIC1, 0x11); /* start init sequence */
port_out8(PIC2, 0x11);
@@ -18,11 +24,14 @@ void irq_init(void) {
port_out8(PIC2+1, 0x1);
uint8_t mask = 0xff;
+ mask &= ~(1 << IRQ_PIT);
mask &= ~(1 << IRQ_PS2);
mask &= ~(1 << IRQ_COM1);
port_out8(PIC1+1, mask);
port_out8(PIC2+1, 0xff);
+
+ pit_init();
}
void irq_eoi(uint8_t line) {
diff --git a/src/kernel/arch/amd64/interrupts/irq.h b/src/kernel/arch/amd64/interrupts/irq.h
index 3340101..2a4b12d 100644
--- a/src/kernel/arch/amd64/interrupts/irq.h
+++ b/src/kernel/arch/amd64/interrupts/irq.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#define IRQ_IBASE 0x20
+#define IRQ_PIT 0
#define IRQ_PS2 1
#define IRQ_COM1 4
diff --git a/src/kernel/arch/amd64/interrupts/isr.c b/src/kernel/arch/amd64/interrupts/isr.c
index 71da27b..0bfd865 100644
--- a/src/kernel/arch/amd64/interrupts/isr.c
+++ b/src/kernel/arch/amd64/interrupts/isr.c
@@ -3,6 +3,7 @@
#include <kernel/arch/amd64/interrupts/irq.h>
#include <kernel/arch/amd64/interrupts/isr.h>
#include <kernel/arch/amd64/port_io.h>
+#include <kernel/arch/amd64/time.h>
#include <kernel/arch/generic.h>
#include <kernel/panic.h>
#include <kernel/proc.h>
@@ -28,6 +29,11 @@ void isr_stage3(int interrupt, uint64_t *stackframe) {
isr_test_interrupt_called = true;
return;
+ case IRQ_IBASE + IRQ_PIT:
+ pit_irq();
+ irq_eoi(IRQ_PIT);
+ return;
+
case IRQ_IBASE + IRQ_PS2:
ps2_irq();
irq_eoi(IRQ_PS2);
diff --git a/src/kernel/arch/amd64/time.c b/src/kernel/arch/amd64/time.c
new file mode 100644
index 0000000..d6e53dd
--- /dev/null
+++ b/src/kernel/arch/amd64/time.c
@@ -0,0 +1,50 @@
+#include <kernel/arch/amd64/time.h>
+#include <kernel/panic.h>
+#include <kernel/proc.h>
+
+static uint64_t uptime = 0, goal = ~0;
+static struct process *scheduled = NULL;
+
+uint64_t uptime_ms(void) { return uptime; }
+
+static void update_goal(void) {
+ goal = scheduled ? scheduled->waits4timer.goal : ~(uint64_t)0;
+}
+
+void pit_irq(void) {
+ // TODO inefficient - getting here executes a lot of code which could just be a few lines of asm
+ uptime++;
+ if (uptime < goal) return;
+
+ struct process *p = scheduled;
+ assert(p);
+ scheduled = p->waits4timer.next;
+ process_transition(p, PS_RUNNING);
+ update_goal();
+}
+
+void timer_schedule(struct process *p, uint64_t time) {
+ process_transition(p, PS_WAITS4TIMER);
+ p->waits4timer.goal = time;
+
+ struct process **slot = &scheduled;
+ while (*slot && (*slot)->waits4timer.goal <= time) {
+ assert((*slot)->state == PS_WAITS4TIMER);
+ slot = &(*slot)->waits4timer.next;
+ }
+ p->waits4timer.next = *slot;
+ *slot = p;
+ update_goal();
+}
+
+void timer_deschedule(struct process *p) {
+ assert(p->state == PS_WAITS4TIMER);
+
+ struct process **slot = &scheduled;
+ while (*slot && *slot != p)
+ slot = &(*slot)->waits4timer.next;
+ assert(*slot);
+ *slot = p->waits4timer.next;
+
+ update_goal();
+}
diff --git a/src/kernel/arch/amd64/time.h b/src/kernel/arch/amd64/time.h
new file mode 100644
index 0000000..04ba9b0
--- /dev/null
+++ b/src/kernel/arch/amd64/time.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <kernel/arch/generic.h>
+
+void pit_irq(void);
diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h
index 4a4f632..0b7b2f2 100644
--- a/src/kernel/arch/generic.h
+++ b/src/kernel/arch/generic.h
@@ -6,6 +6,8 @@
#include <stdbool.h>
#include <stddef.h>
+struct process;
+
// i have no idea where else to put it
// some code assumes that it's a power of 2
#define PAGE_SIZE 4096
@@ -22,6 +24,10 @@ void cpu_shutdown(void);
/** on x86: waits for an IRQ */
void cpu_pause(void);
+uint64_t uptime_ms(void);
+void timer_schedule(struct process *p, uint64_t time);
+void timer_deschedule(struct process *p);
+
// src/arch/i386/sysenter.s
_Noreturn void sysexit(struct registers);
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index e7a5ed6..3bf2b0d 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -132,6 +132,9 @@ void process_kill(struct process *p, int ret) {
*iter = p->waits4pipe.next;
}
+ if (p->state == PS_WAITS4TIMER)
+ timer_deschedule(p);
+
for (handle_t h = 0; h < HANDLE_MAX; h++)
handle_close(p->handles[h]);
@@ -217,14 +220,15 @@ static _Noreturn void process_switch(struct process *proc) {
}
_Noreturn void process_switch_any(void) {
- if (process_current && process_current->state == PS_RUNNING)
- process_switch(process_current);
+ for (;;) {
+ if (process_current && process_current->state == PS_RUNNING)
+ process_switch(process_current);
- struct process *found = process_find(PS_RUNNING);
- if (found) process_switch(found);
+ struct process *found = process_find(PS_RUNNING);
+ if (found) process_switch(found);
- cpu_pause();
- process_switch_any();
+ cpu_pause();
+ }
}
struct process *process_next(struct process *p) {
@@ -284,6 +288,7 @@ void process_transition(struct process *p, enum process_state state) {
case PS_WAITS4FS:
case PS_WAITS4REQUEST:
case PS_WAITS4PIPE:
+ case PS_WAITS4TIMER:
assert(last == PS_RUNNING);
break;
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index d6f84bd..fb14e35 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -15,6 +15,7 @@ enum process_state {
PS_WAITS4FS,
PS_WAITS4REQUEST,
PS_WAITS4PIPE,
+ PS_WAITS4TIMER,
PS_LAST,
};
@@ -46,6 +47,11 @@ struct process {
size_t len;
struct process *next;
} waits4pipe;
+ struct {
+ /* managed by timer_schedule */
+ uint64_t goal;
+ struct process *next;
+ } waits4timer;
};
struct vfs_request *handled_req;
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index 4537ff5..ceba528 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -363,6 +363,11 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags) {
SYSCALL_RETURN(0);
}
+void _syscall_sleep(long ms) {
+ // TODO no overflow check - can leak current uptime
+ timer_schedule(process_current, uptime_ms() + ms);
+}
+
long _syscall_execbuf(void __user *ubuf, size_t len) {
if (len == 0) SYSCALL_RETURN(0);
if (len > sizeof(uint64_t) * 6 * 4) // TODO specify max size somewhere
@@ -434,6 +439,9 @@ long _syscall(long num, long a, long b, long c, long d, long e) {
case _SYSCALL_PIPE:
_syscall_pipe((userptr_t)a, b);
break;
+ case _SYSCALL_SLEEP:
+ _syscall_sleep(a);
+ break;
case _SYSCALL_EXECBUF:
_syscall_execbuf((userptr_t)a, b);
break;
diff --git a/src/shared/include/camellia/syscalls.h b/src/shared/include/camellia/syscalls.h
index 84af276..40cb68d 100644
--- a/src/shared/include/camellia/syscalls.h
+++ b/src/shared/include/camellia/syscalls.h
@@ -29,6 +29,8 @@ enum {
_SYSCALL_MEMFLAG,
_SYSCALL_PIPE,
+ _SYSCALL_SLEEP,
+
_SYSCALL_EXECBUF = 100,
_SYSCALL_DEBUG_KLOG,
@@ -88,6 +90,8 @@ long _syscall_fs_respond(void __user *buf, long ret, int flags);
void __user *_syscall_memflag(void __user *addr, size_t len, int flags);
long _syscall_pipe(handle_t __user user_ends[2], int flags);
+void _syscall_sleep(long ms);
+
/* see shared/execbuf.h */
long _syscall_execbuf(void __user *buf, size_t len);
diff --git a/src/user/app/shell/builtins.c b/src/user/app/shell/builtins.c
index 1a46003..d4de354 100644
--- a/src/user/app/shell/builtins.c
+++ b/src/user/app/shell/builtins.c
@@ -117,6 +117,14 @@ static void cmd_ls(int argc, char **argv) {
}
}
+static void cmd_sleep(int argc, char **argv) {
+ if (argc < 2) {
+ eprintf("no arguments");
+ return;
+ }
+ _syscall_sleep(strtol(argv[1], NULL, 0) * 1000);
+}
+
static void cmd_touch(int argc, char **argv) {
if (argc <= 1) {
eprintf("no arguments");
@@ -135,6 +143,7 @@ struct builtin builtins[] = {
{"echo", cmd_echo},
{"hexdump", cmd_hexdump},
{"ls", cmd_ls},
+ {"sleep", cmd_sleep},
{"touch", cmd_touch},
{NULL, NULL},
};
diff --git a/src/user/app/tests/tests.c b/src/user/app/tests/tests.c
index d655ab9..5c40bcd 100644
--- a/src/user/app/tests/tests.c
+++ b/src/user/app/tests/tests.c
@@ -254,7 +254,37 @@ static void test_strtol(void) {
assert(!strcmp("hello", end));
assert(01234 == strtol(" 01234hello", &end, 0));
assert(!strcmp("hello", end));
+}
+
+static void test_sleep(void) {
+ // TODO yet another of those fake tests that you have to verify by hand
+ if (!fork()) {
+ if (!fork()) {
+ _syscall_sleep(100);
+ printf("1\n");
+ _syscall_sleep(200);
+ printf("3\n");
+ _syscall_sleep(200);
+ printf("5\n");
+ _syscall_exit(0);
+ }
+ if (!fork()) {
+ printf("0\n");
+ _syscall_sleep(200);
+ printf("2\n");
+ _syscall_sleep(200);
+ printf("4\n");
+ /* get killed while asleep
+ * a peaceful death, i suppose. */
+ for (;;) _syscall_sleep(1000000000);
+ }
+ _syscall_await();
+ _syscall_exit(0);
+ }
+ /* this part checks if, after killing an asleep process, other processes can still wake up */
+ _syscall_sleep(600);
+ printf("6\n");
}
static void test_misc(void) {
@@ -276,6 +306,7 @@ int main(void) {
run_forked(test_execbuf);
run_forked(test_printf);
run_forked(test_strtol);
+ run_forked(test_sleep);
run_forked(test_misc);
return 1;
}
diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c
index 842b7b9..ffda454 100644
--- a/src/user/lib/syscall.c
+++ b/src/user/lib/syscall.c
@@ -58,6 +58,10 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags) {
return _syscall(_SYSCALL_PIPE, (long)user_ends, (long)flags, 0, 0, 0);
}
+void _syscall_sleep(long ms) {
+ return (void)_syscall(_SYSCALL_SLEEP, ms, 0, 0, 0, 0);
+}
+
long _syscall_execbuf(void __user *buf, size_t len) {
return _syscall(_SYSCALL_EXECBUF, (long)buf, (long)len, 0, 0, 0);
}