diff --git a/Makefile b/Makefile index 5825871..5a10743 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,8 @@ LDKERNELFLAGS = --script=script.ld endif OBJECTS = kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ - cpu/idt.o cpu/vectors.o lib/mem.o + cpu/idt.o cpu/gdt.o cpu/swtch.o cpu/vectors.o lib/mem.o proc.o lib/string.o \ + fs/fs.o run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio @@ -76,8 +77,8 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -fs.img: kernel.bin tools/mkfs - tools/mkfs $@ $< +fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 + tools/mkfs $@ $< user/false user/greet user/div0 LDFLAGS=-m elf_i386 diff --git a/console.h b/console.h index 2e5c22a..272fad2 100644 --- a/console.h +++ b/console.h @@ -1,4 +1,4 @@ #pragma once void printk(const char* msg); -void panic(const char* msg); +_Noreturn void panic(const char* msg); diff --git a/cpu/gdt.c b/cpu/gdt.c new file mode 100644 index 0000000..f511ef0 --- /dev/null +++ b/cpu/gdt.c @@ -0,0 +1,65 @@ +#include "gdt.h" +#include "../lib/string.h" + +#include + +struct seg_desc_t { + uint16_t lim_15_0; // Low bits of segment limit + uint16_t base_15_0; // Low bits of segment base address + uint8_t base_23_16; // Middle bits of segment base address + uint8_t type : 4; // Segment type (see STA_ constants) + uint8_t s : 1; // 0 = system, 1 = application + uint8_t dpl : 2; // Descriptor Privilege Level + uint8_t p : 1; // Present + uint8_t lim_19_16 : 4; // High bits of segment limit + uint8_t avl : 1; // Unused (available for software use) + uint8_t rsv1 : 1; // Reserved + uint8_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment + uint8_t g : 1; // Granularity: limit scaled by 4K when set + uint8_t base_31_24; // High bits of segment base address +} __attribute__((packed)); + +#define SEG(type, base, lim, dpl) (struct seg_desc_t) \ +{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ + ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } +#define SEG16(type, base, lim, dpl) (struct seg_desc_t) \ +{ (lim) & 0xffff, (uint)(base) & 0xffff, \ + ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 } + +struct seg_desc_t seg_desc[6]; + +void init_seg_desc() { + seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); + seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); + seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); + seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); +} + +struct gdt_desc_t { + uint16_t size; + void* ptr; +} __attribute__((packed)); + +void load_gdt() { + init_seg_desc(); + + struct gdt_desc_t gdt_desc; + gdt_desc.size = sizeof(seg_desc) - 1; + gdt_desc.ptr = seg_desc; + asm("lgdt %0": : "m"(gdt_desc)); +} + +void switchuvm(struct taskstate *tss, void* esp) { + memset(tss, 0, sizeof(*tss)); + seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0); + seg_desc[SEG_TSS].s = 0; + tss->ss0 = SEG_KDATA << 3; + tss->esp0 = (uint)esp; + // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit + // forbids I/O instructions (e.g., inb and outb) from user space + tss->iomb = (ushort) 0xFFFF; + + asm("ltr %0": : "r"((ushort)(SEG_TSS << 3))); +} diff --git a/cpu/idt.c b/cpu/idt.c index 78f41f0..4b4137c 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -1,5 +1,7 @@ #include "isr.h" #include "gdt.h" +#include "../syscall.h" +#include "../proc.h" #include "../drivers/port.h" #include "../console.h" @@ -47,6 +49,7 @@ void init_idt() { for (int i = 0; i < IDT_HANDLERS; i++) { set_idt_gate(i, 0, default_handlers[i], 0); } + set_idt_gate(T_SYSCALL, 1, default_handlers[T_SYSCALL], DPL_USER); } const char * const exception_messages[] = { @@ -82,6 +85,26 @@ void register_interrupt_handler(uint8_t i, isr_t handler) { } void trap(registers_t *r) { + if (r->int_no == T_SYSCALL) { + switch (r->eax) { + case SYS_exit: + if (r->ebx == 0) { + printk("* success\n"); + } else { + printk("* failure\n"); + } + killproc(); + case SYS_greet: + printk("Hello world!\n"); + r->eax = 0; + break; + default: + printk("Unknown syscall\n"); + r->eax = -1; + } + return; + } + if (r->int_no < 32) { const char* msg = "Reserved"; if (r->int_no < ARRLEN(exception_messages)) { @@ -90,6 +113,7 @@ void trap(registers_t *r) { panic(msg); } + /* Handle the interrupt in a more modular way */ if (interrupt_handlers[r->int_no] != 0) { isr_t handler = interrupt_handlers[r->int_no]; handler(r); diff --git a/cpu/swtch.S b/cpu/swtch.S new file mode 100644 index 0000000..db6b56e --- /dev/null +++ b/cpu/swtch.S @@ -0,0 +1,20 @@ + // swtch(void** oldstack, void* newstack) + .global swtch +swtch: + mov 4(%esp), %eax // eax holds "oldstack" + mov 8(%esp), %ecx + + push %ebx + push %ebp + push %esi + push %edi + + mov %esp, (%eax) // save stack ptr to "oldstack" + mov %ecx, %esp // use "newstack" as stack ptr + + pop %edi + pop %esi + pop %ebp + pop %ebx + + ret diff --git a/fs/fs.c b/fs/fs.c new file mode 100644 index 0000000..5f594ce --- /dev/null +++ b/fs/fs.c @@ -0,0 +1,12 @@ +#include "fs.h" +#include "../lib/string.h" +#include "../drivers/ata.h" +#include "../console.h" + +int stat(const char* name, struct stat *buf) { + panic("stat not implemented"); +} + +int read_file(const char* name, void* buf, uint32_t bufsize) { + panic("read_file not implemented"); +} diff --git a/kernel.c b/kernel.c index 481c60b..5e7441f 100644 --- a/kernel.c +++ b/kernel.c @@ -1,19 +1,43 @@ +asm(".asciz \"kernel start\\n\""); + #include "console.h" -#include "drivers/vga.h" -#include "drivers/uart.h" -#include "drivers/keyboard.h" #include "cpu/isr.h" +#include "cpu/gdt.h" +#include "drivers/keyboard.h" +#include "drivers/vga.h" +#include "drivers/ata.h" +#include "drivers/misc.h" +#include "drivers/uart.h" +#include "fs/fs.h" +#include "lib/string.h" +#include "proc.h" void _start() { - uartinit(); + load_gdt(); init_keyboard(); + uartinit(); load_idt(); sti(); vga_clear_screen(); - printk("\nYABLOKO\n> "); + printk("YABLOKO\n"); + + printk("\n> "); while (1) { + if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { + if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { + qemu_shutdown(); + } else if (!strncmp("run ", kbd_buf, 4)) { + kbd_buf[kbd_buf_size-1] = '\0'; + const char* cmd = kbd_buf + 4; + run_elf(cmd); + } else { + printk("unknown command, try: halt | run CMD"); + } + kbd_buf_size = 0; + printk("\n> "); + } asm("hlt"); } } diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000..fab905b --- /dev/null +++ b/lib/string.c @@ -0,0 +1,20 @@ +#include "string.h" + +int strncmp(const char* s1, const char* s2, size_t size) { + while (size && *s1 && *s2 && *s1 == *s2) { + size--; + s1++; + s2++; + } + if (!size) { + return 0; + } + return (unsigned char)(*s1) - (unsigned char)(*s2); +} + +void memset(void* b, char c, size_t len) { + char* p = b; + for (size_t i = 0; i < len; ++i) { + p[i] = c; + } +} diff --git a/lib/string.h b/lib/string.h new file mode 100644 index 0000000..6184c33 --- /dev/null +++ b/lib/string.h @@ -0,0 +1,7 @@ +#pragma once + +typedef unsigned size_t; + +void kmemmove(char* dst, char* src, size_t size); +int strncmp(const char* s1, const char* s2, size_t size); +void memset(void* b, char c, size_t len); diff --git a/proc.c b/proc.c new file mode 100644 index 0000000..6ec0971 --- /dev/null +++ b/proc.c @@ -0,0 +1,75 @@ +#include "elf.h" +#include "proc.h" +#include "fs/fs.h" +#include "cpu/gdt.h" +#include "cpu/isr.h" +#include "lib/mem.h" +#include "lib/string.h" +#include "console.h" + +struct context { + // matches the behavior of swtch() + uint32_t edi, esi, ebp, ebx; + uint32_t eip; // return address for swtch() +}; + +struct kstack { + uint32_t space[400]; + struct context context; + registers_t trapframe; + char bottom[]; +}; + +struct task { + struct taskstate tss; + struct kstack stack; +}; + +struct vm { + void *kernel_thread; + void *user_thread; + struct task *user_task; +} *vm; + +void trapret(); +void swtch(void** oldstack, void* newstack); + +void run_elf(const char* name) { + if (!vm) { + vm = kmalloc(sizeof(struct vm)); + vm->user_task = kmalloc(sizeof(struct task)); + switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom); + } + if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) { + printk(name); + printk(": file not found\n"); + return; + } + Elf32_Ehdr *hdr = (void*)USER_BASE; + + struct kstack *u = &vm->user_task->stack; + memset(u, 0, sizeof(*u)); + u->context.eip = (uint32_t)trapret; + + registers_t *tf = &u->trapframe; + tf->eip = hdr->e_entry; + tf->cs = (SEG_UCODE << 3) | DPL_USER; + tf->ds = (SEG_UDATA << 3) | DPL_USER; + tf->es = tf->ds; + tf->fs = tf->ds; + tf->gs = tf->ds; + tf->ss = tf->ds; + tf->eflags = FL_IF; + tf->useresp = USER_STACK_BASE; + + // initialization done, now switch to the process + swtch(&vm->kernel_thread, &u->context); + + // process has finished +} + +_Noreturn void killproc() { + void* task_stack; + swtch(&task_stack, vm->kernel_thread); + __builtin_unreachable(); +} diff --git a/proc.h b/proc.h new file mode 100644 index 0000000..54ede88 --- /dev/null +++ b/proc.h @@ -0,0 +1,4 @@ +#pragma once + +void run_elf(const char* name); +_Noreturn void killproc(); diff --git a/syscall.h b/syscall.h new file mode 100644 index 0000000..4c9ba03 --- /dev/null +++ b/syscall.h @@ -0,0 +1,9 @@ +#pragma once + +enum { + T_SYSCALL = 0x84, + SYS_exit = 0, + SYS_greet = 1, +}; + +int syscall(int call, int arg); diff --git a/user/crt.c b/user/crt.c new file mode 100644 index 0000000..5032810 --- /dev/null +++ b/user/crt.c @@ -0,0 +1,18 @@ +#include "../syscall.h" + +int main(); + +int syscall(int call, int arg) { + asm("int $0x84": "+a"(call) : "b"(arg)); + return call; +} + +_Noreturn +void _exit(int exit_status) { + syscall(SYS_exit, exit_status); + __builtin_unreachable(); +} + +void _start() { + _exit(main()); +} diff --git a/user/div0.c b/user/div0.c new file mode 100644 index 0000000..76c2038 --- /dev/null +++ b/user/div0.c @@ -0,0 +1,5 @@ +int main(void) { + volatile int x = 1, y = 0; + x /= y; + return 0; +} diff --git a/user/false.c b/user/false.c new file mode 100644 index 0000000..6346e2d --- /dev/null +++ b/user/false.c @@ -0,0 +1,3 @@ +int main() { + return 1; +} diff --git a/user/greet.c b/user/greet.c new file mode 100644 index 0000000..28b539b --- /dev/null +++ b/user/greet.c @@ -0,0 +1,7 @@ +#include "../syscall.h" + +int main(void) { + syscall(SYS_greet, 0); + syscall(SYS_greet, 0); + return 0; +}