diff options
-rw-r--r-- | src/kernel/arch/amd64/32/32 | 130 | ||||
-rw-r--r-- | src/kernel/arch/amd64/32/boot.s | 1 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/irq.c | 9 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/irq.h | 1 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/isr.c | 6 | ||||
-rw-r--r-- | src/kernel/arch/amd64/time.c | 50 | ||||
-rw-r--r-- | src/kernel/arch/amd64/time.h | 4 | ||||
-rw-r--r-- | src/kernel/arch/generic.h | 6 | ||||
-rw-r--r-- | src/kernel/proc.c | 17 | ||||
-rw-r--r-- | src/kernel/proc.h | 6 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 8 | ||||
-rw-r--r-- | src/shared/include/camellia/syscalls.h | 4 | ||||
-rw-r--r-- | src/user/app/shell/builtins.c | 9 | ||||
-rw-r--r-- | src/user/app/tests/tests.c | 31 | ||||
-rw-r--r-- | src/user/lib/syscall.c | 4 |
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); } |