diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | src/init/main.c | 50 | ||||
-rw-r--r-- | src/init/stdlib.c | 53 | ||||
-rw-r--r-- | src/init/stdlib.h | 4 | ||||
-rw-r--r-- | src/init/syscalls.c | 8 | ||||
-rw-r--r-- | src/kernel/arch/i386/ata.c | 131 | ||||
-rw-r--r-- | src/kernel/arch/i386/ata.h | 4 | ||||
-rw-r--r-- | src/kernel/arch/i386/boot.c | 7 | ||||
-rw-r--r-- | src/kernel/arch/i386/interrupts/isr.c | 21 | ||||
-rw-r--r-- | src/kernel/arch/i386/port_io.h | 14 | ||||
-rw-r--r-- | src/kernel/arch/i386/tty/serial.c | 28 | ||||
-rw-r--r-- | src/kernel/proc.c | 19 | ||||
-rw-r--r-- | src/kernel/proc.h | 7 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 24 | ||||
-rw-r--r-- | src/shared/syscalls.h | 8 |
15 files changed, 295 insertions, 92 deletions
@@ -18,8 +18,10 @@ endef .PHONY: all boot debug lint check clean all: out/boot.iso check -boot: all - qemu-system-i386 -cdrom out/boot.iso $(QFLAGS) -no-shutdown +boot: all out/hdd + qemu-system-i386 -cdrom out/boot.iso $(QFLAGS) -no-shutdown \ + -drive file=out/hdd,format=raw,media=disk + debug: all qemu-system-i386 -cdrom out/boot.iso $(QFLAGS) -s -S & @@ -59,6 +61,9 @@ out/fs/boot/grub/grub.cfg: grub.cfg @mkdir -p $(@D) @cp $< $@ +out/hdd: + @dd if=/dev/zero of=$@ bs=512 count=70000 + out/obj/%.s.o: src/%.s @mkdir -p $(@D) diff --git a/src/init/main.c b/src/init/main.c index 5c798d2..401e4a0 100644 --- a/src/init/main.c +++ b/src/init/main.c @@ -1,10 +1,10 @@ +#include <init/stdlib.h> #include <init/tar.h> #include <shared/flags.h> #include <shared/syscalls.h> #include <stdint.h> #define argify(str) str, sizeof(str) - 1 -#define log(str) _syscall_write(tty_fd, argify(str), 0) extern char _bss_start; // provided by the linker extern char _bss_end; @@ -23,7 +23,7 @@ int main(void) { tty_fd = _syscall_open("/tty", sizeof("/tty") - 1); if (tty_fd < 0) - _syscall_exit(argify("couldn't open tty")); + _syscall_exit(1); fs_test(); test_await(); @@ -32,7 +32,7 @@ int main(void) { while (_syscall_read(tty_fd, &c, 1, 0)) _syscall_write(tty_fd, &c, 1, 0); - _syscall_exit(argify("my job here is done.")); + _syscall_exit(0); } void read_file(const char *path, size_t len) { @@ -41,9 +41,9 @@ void read_file(const char *path, size_t len) { int buf_len = 64; _syscall_write(tty_fd, path, len, 0); - log(": "); + printf(": "); if (fd < 0) { - log("couldn't open.\n"); + printf("couldn't open.\n"); return; } @@ -64,7 +64,7 @@ void fs_test(void) { } // parent: accesses the fs - log("\n\n"); + printf("\n\n"); // the trailing slash should be ignored by mount() _syscall_mount(front, argify("/init/")); read_file(argify("/init/fake.txt")); @@ -72,30 +72,36 @@ void fs_test(void) { read_file(argify("/init/2.txt")); read_file(argify("/init/dir/3.txt")); - log("\nshadowing /init/dir...\n"); + printf("\nshadowing /init/dir...\n"); _syscall_mount(-1, argify("/init/dir")); read_file(argify("/init/fake.txt")); read_file(argify("/init/1.txt")); read_file(argify("/init/2.txt")); read_file(argify("/init/dir/3.txt")); - log("\n"); + printf("\n"); } void test_await(void) { - char buf[16]; - int len; - - // the child immediately dies - if (!_syscall_fork()) - _syscall_exit(argify("i'm dead")); - if (!_syscall_fork()) - _syscall_exit(argify("i'm also dead")); - - while ((len = _syscall_await(buf, 16)) >= 0) { - log("await returned: "); - _syscall_write(tty_fd, buf, len, 0); - log("\n"); + int ret; + + // regular exit()s + if (!_syscall_fork()) _syscall_exit(69); + if (!_syscall_fork()) _syscall_exit(420); + + // faults + if (!_syscall_fork()) { // invalid memory access + asm volatile("movb $69, 0" ::: "memory"); + printf("this shouldn't happen"); + _syscall_exit(-1); } - log("await: negative len\n"); + if (!_syscall_fork()) { // #GP + asm volatile("hlt" ::: "memory"); + printf("this shouldn't happen"); + _syscall_exit(-1); + } + + while ((ret = _syscall_await()) != ~0) + printf("await returned: %x\n", ret); + printf("await: no more children\n"); } diff --git a/src/init/stdlib.c b/src/init/stdlib.c index 6ed5a0a..a60854b 100644 --- a/src/init/stdlib.c +++ b/src/init/stdlib.c @@ -1,4 +1,6 @@ #include <init/stdlib.h> +#include <shared/syscalls.h> +#include <stdarg.h> int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *c1 = s1, *c2 = s2; @@ -10,3 +12,54 @@ int memcmp(const void *s1, const void *s2, size_t n) { } return 0; } + +size_t strlen(const char *s) { + size_t c = 0; + while (*s++) c++; + return c; +} + +int printf(const char *fmt, ...) { + const char *seg = fmt; // beginning of the current segment + int total = 0; + va_list argp; + va_start(argp, fmt); + for (;;) { + char c = *fmt++; + switch (c) { + case '\0': + // TODO don't assume that stdout is @ fd 0 + _syscall_write(0, seg, fmt - seg - 1, 0); + return total + (fmt - seg - 1); + + case '%': + _syscall_write(0, seg, fmt - seg - 1, 0); + total += fmt - seg - 1; + c = *fmt++; + switch (c) { + case 's': + const char *s = va_arg(argp, char*); + _syscall_write(0, s, strlen(s), 0); + total += strlen(s); + break; + + case 'x': + unsigned int n = va_arg(argp, int); + size_t i = 4; // nibbles * 4 + while (n >> i && i < (sizeof(int) * 8)) + i += 4; + + while (i > 0) { + i -= 4; + char h = '0' + ((n >> i) & 0xf); + if (h > '9') h += 'a' - '9' - 1; + _syscall_write(0, &h, 1, 0); + total++; + } + break; + } + seg = fmt; + break; + } + } +} diff --git a/src/init/stdlib.h b/src/init/stdlib.h index 1cabc7a..3e56beb 100644 --- a/src/init/stdlib.h +++ b/src/init/stdlib.h @@ -5,3 +5,7 @@ // stb-style header file int memcmp(const void *s1, const void *s2, size_t n); + +size_t strlen(const char *s); + +int printf(const char *fmt, ...); diff --git a/src/init/syscalls.c b/src/init/syscalls.c index f32594e..57a7344 100644 --- a/src/init/syscalls.c +++ b/src/init/syscalls.c @@ -1,8 +1,8 @@ // this file could probably just get generated by a script #include <shared/syscalls.h> -_Noreturn void _syscall_exit(const char __user *msg, size_t len) { - _syscall(_SYSCALL_EXIT, (int)msg, len, 0, 0); +_Noreturn void _syscall_exit(int ret) { + _syscall(_SYSCALL_EXIT, ret, 0, 0, 0); __builtin_unreachable(); } @@ -10,8 +10,8 @@ int _syscall_fork(void) { return _syscall(_SYSCALL_FORK, 0, 0, 0, 0); } -int _syscall_await(char __user *buf, int len) { - return _syscall(_SYSCALL_AWAIT, (int)buf, (int)len, 0, 0); +int _syscall_await(void) { + return _syscall(_SYSCALL_AWAIT, 0, 0, 0, 0); } handle_t _syscall_open(const char __user *path, int len) { diff --git a/src/kernel/arch/i386/ata.c b/src/kernel/arch/i386/ata.c new file mode 100644 index 0000000..9224ed6 --- /dev/null +++ b/src/kernel/arch/i386/ata.c @@ -0,0 +1,131 @@ +#include <kernel/arch/i386/ata.h> +#include <kernel/arch/i386/port_io.h> +#include <kernel/panic.h> +#include <stdbool.h> + +#include <kernel/arch/io.h> + +static struct { + enum { + DEV_UNKNOWN, + DEV_PATA, + DEV_PATAPI, + } type; + uint32_t sectors; +} ata_drives[4]; + +enum { + LBAlo = 3, + LBAmid = 4, + LBAhi = 5, + DRV = 6, + CMD = 7, + STATUS = 7, + + /* note: the OSDev wiki uses a different base port for the control port + * however i can just use this offset and stuff will just work tm */ + CTRL = 0x206, +}; // offsets + +// get I/O port base for drive +static uint16_t ata_iobase(int drive) { + bool secondary = drive&2; + return secondary ? 0x170 : 0x1F0; +} + +static void ata_400ns(void) { + uint16_t base = ata_iobase(0); // doesn't matter + for (int i = 0; i < 4; i++) + port_in8(base + STATUS); +} + +static void ata_driveselect(int drive, int block) { + uint8_t v = 0xE0; + if (drive&1) // slave? + v |= 0x10; // set drive number bit + // TODO account for block + port_out8(ata_iobase(drive) + DRV, v); +} + +static void ata_softreset(int drive) { + uint16_t iobase = ata_iobase(drive); + port_out8(iobase + CTRL, 4); + port_out8(iobase + CTRL, 0); + ata_400ns(); + + uint16_t timeout = 10000; + while (--timeout) { // TODO separate polling function + uint8_t v = port_in8(iobase + STATUS); + if (v & 0x80) continue; // still BSY, continue + if (v & 0x40) break; // RDY, break + // TODO check for ERR + } +} + +static void ata_detecttype(int drive) { + ata_softreset(drive); + ata_driveselect(drive, 0); + ata_400ns(); + switch (port_in8(ata_iobase(drive) + LBAmid)) { + case 0: + ata_drives[drive].type = DEV_PATA; + break; + case 0x14: + ata_drives[drive].type = DEV_PATAPI; + break; + default: + ata_drives[drive].type = DEV_UNKNOWN; + break; + } +} + +static bool ata_identify(int drive) { + uint16_t iobase = ata_iobase(drive); + uint16_t data[256]; + uint8_t v; + + ata_driveselect(drive, 0); + for (int i = 2; i < 6; i++) + port_out8(iobase + i, 0); + switch (ata_drives[drive].type) { + case DEV_PATA: + port_out8(iobase + CMD, 0xEC); // IDENTIFY + break; + case DEV_PATAPI: + port_out8(iobase + CMD, 0xA1); // IDENTIFY PACKET DEVICE + break; + default: panic_invalid_state(); + } + + v = port_in8(iobase + STATUS); + if (v == 0) return false; // nonexistent drive + while (port_in8(iobase + STATUS) & 0x80); + + /* pool until bit 3 (DRQ) or 0 (ERR) is set */ + while (!((v = port_in8(iobase + STATUS) & 0x9))); + if (v & 1) return false; /* ERR was set, bail */ + + for (int i = 0; i < 256; i++) + data[i] = port_in16(iobase); + ata_drives[drive].sectors = data[60] | (data[61] << 16); + return true; +} + +void ata_init(void) { + tty_const("\n"); + for (int i = 0; i < 4; i++) { + tty_const("probing drive "); + _tty_var(i); + ata_detecttype(i); + if (ata_drives[i].type != DEV_UNKNOWN) { + if (ata_identify(i)) { + tty_const(" - "); + _tty_var(ata_drives[i].sectors); + tty_const(" sectors (512b)"); + } else { + tty_const(" identify failed"); + } + } + tty_const("\n"); + } +} diff --git a/src/kernel/arch/i386/ata.h b/src/kernel/arch/i386/ata.h new file mode 100644 index 0000000..9137cdb --- /dev/null +++ b/src/kernel/arch/i386/ata.h @@ -0,0 +1,4 @@ +#pragma once +#include <stdint.h> + +void ata_init(void); diff --git a/src/kernel/arch/i386/boot.c b/src/kernel/arch/i386/boot.c index bd2a00b..94e9e2f 100644 --- a/src/kernel/arch/i386/boot.c +++ b/src/kernel/arch/i386/boot.c @@ -1,4 +1,5 @@ #include <kernel/arch/generic.h> +#include <kernel/arch/i386/ata.h> #include <kernel/arch/i386/boot.h> #include <kernel/arch/i386/gdt.h> #include <kernel/arch/i386/interrupts/idt.h> @@ -15,7 +16,9 @@ void kmain_early(struct multiboot_info *multiboot) { gdt_init(); tty_const("idt..."); idt_init(); - + tty_const("ata..."); + ata_init(); + { // find the init module struct multiboot_mod *module = &multiboot->mods[0]; if (multiboot->mods_count < 1) { @@ -25,6 +28,6 @@ void kmain_early(struct multiboot_info *multiboot) { info.init.at = module->start; info.init.size = module->end - module->start; } - + kmain(info); } diff --git a/src/kernel/arch/i386/interrupts/isr.c b/src/kernel/arch/i386/interrupts/isr.c index dacecba..9d8bb6c 100644 --- a/src/kernel/arch/i386/interrupts/isr.c +++ b/src/kernel/arch/i386/interrupts/isr.c @@ -1,29 +1,24 @@ #include <kernel/arch/i386/interrupts/isr.h> #include <kernel/arch/io.h> #include <kernel/panic.h> +#include <kernel/proc.h> #include <stdbool.h> #include <stdint.h> -#define log_n_panic(x) {tty_const(x); panic_unimplemented();} // TODO kill the current process instead of panicking - bool isr_test_interrupt_called = false; void isr_stage3(int interrupt) { switch (interrupt) { - case 0x08: log_n_panic("#DF"); // double fault - case 0x0D: log_n_panic("#GP"); // general protection fault - case 0x0E: { // page fault - int cr2; - tty_const("#PF at "); - asm ("mov %%cr2, %0;" : "=r"(cr2) ::); - _tty_var(cr2); - panic_unimplemented(); - } - + case 0x08: // double fault + tty_const("#DF"); + panic_invalid_state(); case 0x34: isr_test_interrupt_called = true; return; - default: log_n_panic("unknown interrupt"); + default: + // TODO check if the exception was in the kernel + process_kill(process_current, interrupt); + process_switch_any(); } } diff --git a/src/kernel/arch/i386/port_io.h b/src/kernel/arch/i386/port_io.h index a4d640f..eac9331 100644 --- a/src/kernel/arch/i386/port_io.h +++ b/src/kernel/arch/i386/port_io.h @@ -1,12 +1,22 @@ #include <stdint.h> -static inline void port_outb(uint16_t port, uint8_t val) { +static inline void port_out8(uint16_t port, uint8_t val) { asm volatile("outb %0, %1" : : "a" (val), "Nd" (port)); } -static inline uint8_t port_inb(uint16_t port) { +static inline void port_out16(uint16_t port, uint16_t val) { + asm volatile("outw %0, %1" : : "a" (val), "Nd" (port)); +} + +static inline uint8_t port_in8(uint16_t port) { uint8_t val; asm volatile("inb %1, %0" : "=a" (val) : "Nd" (port)); return val; } +static inline uint16_t port_in16(uint16_t port) { + uint16_t val; + asm volatile("inw %1, %0" : "=a" (val) : "Nd" (port)); + return val; +} + diff --git a/src/kernel/arch/i386/tty/serial.c b/src/kernel/arch/i386/tty/serial.c index 2b89ecf..f9bb252 100644 --- a/src/kernel/arch/i386/tty/serial.c +++ b/src/kernel/arch/i386/tty/serial.c @@ -7,37 +7,37 @@ static const int COM1 = 0x3f8; static void serial_selftest(void) { char b = 0x69; - port_outb(COM1 + 4, 0b00011110); // enable loopback mode - port_outb(COM1, b); - assert(port_inb(COM1) == b); + port_out8(COM1 + 4, 0b00011110); // enable loopback mode + port_out8(COM1, b); + assert(port_in8(COM1) == b); } void serial_init(void) { // see https://www.sci.muni.cz/docs/pc/serport.txt - port_outb(COM1 + 1, 0x00); // disable interrupts, we won't be using them + port_out8(COM1 + 1, 0x00); // disable interrupts, we won't be using them // set baud rate divisor - port_outb(COM1 + 3, 0b10000000); // enable DLAB - port_outb(COM1 + 0, 0x01); // divisor = 1 (low byte) - port_outb(COM1 + 1, 0x00); // (high byte) + port_out8(COM1 + 3, 0b10000000); // enable DLAB + port_out8(COM1 + 0, 0x01); // divisor = 1 (low byte) + port_out8(COM1 + 1, 0x00); // (high byte) - port_outb(COM1 + 3, 0b00000011); // 8 bits, no parity, one stop bit - port_outb(COM1 + 2, 0b11000111); // enable FIFO with 14-bit trigger level (???) + port_out8(COM1 + 3, 0b00000011); // 8 bits, no parity, one stop bit + port_out8(COM1 + 2, 0b11000111); // enable FIFO with 14-bit trigger level (???) serial_selftest(); - port_outb(COM1 + 4, 0b00001111); // enable everything in the MCR + port_out8(COM1 + 4, 0b00001111); // enable everything in the MCR } static void serial_putchar(char c) { - while ((port_inb(COM1 + 5) & 0x20) == 0); // wait for THRE - port_outb(COM1, c); + while ((port_in8(COM1 + 5) & 0x20) == 0); // wait for THRE + port_out8(COM1, c); } char serial_read(void) { - while ((port_inb(COM1 + 5) & 0x01) == 0); // wait for DR - return port_inb(COM1); + while ((port_in8(COM1 + 5) & 0x01) == 0); // wait for DR + return port_in8(COM1); } void serial_write(const char *buf, size_t len) { diff --git a/src/kernel/proc.c b/src/kernel/proc.c index b9f96c8..17af0cd 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -103,10 +103,15 @@ handle_t process_find_handle(struct process *proc) { return handle; } +void process_kill(struct process *proc, int ret) { + proc->state = PS_DEAD; + proc->death_msg = ret; + process_try2collect(proc); +} + int process_try2collect(struct process *dead) { struct process *parent = dead->parent; - int len, ret; - bool res; + int ret; assert(dead->state == PS_DEAD); @@ -115,16 +120,12 @@ int process_try2collect(struct process *dead) { dead->state = PS_DEADER; parent->state = PS_RUNNING; - len = min(parent->death_msg.len, dead->death_msg.len); - res = virt_cpy( - parent->pages, parent->death_msg.buf, - dead->pages, dead->death_msg.buf, len); - - ret = res ? len : 0; + ret = dead->death_msg; regs_savereturn(&parent->regs, ret); return ret; default: - return -1; + return -1; // this return value isn't used anywhere + // TODO enforce that, somehow? idk } } diff --git a/src/kernel/proc.h b/src/kernel/proc.h index 82bb59a..7c338bb 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -26,10 +26,7 @@ struct process { // saved value, meaning depends on .state union { - struct { // PS_DEAD, PS_WAITS4CHILDDEATH - char __user *buf; - size_t len; - } death_msg; + int death_msg; // PS_DEAD struct vfs_request pending_req; // PS_WAITS4FS struct { char __user *buf; @@ -56,6 +53,8 @@ _Noreturn void process_switch_any(void); // switches to any running process struct process *process_find(enum process_state); handle_t process_find_handle(struct process *proc); // finds the first free handle +void process_kill(struct process *proc, int ret); + /** Tries to transistion from PS_DEAD to PS_DEADER. * @return a nonnegative length of the quit message if successful, a negative val otherwise*/ int process_try2collect(struct process *dead); diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 1b91eb7..22bf1f6 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -8,28 +8,20 @@ #include <shared/syscalls.h> #include <stdint.h> -_Noreturn void _syscall_exit(const char __user *msg, size_t len) { - process_current->state = PS_DEAD; - process_current->death_msg.buf = (userptr_t) msg; // discarding const - process_current->death_msg.len = len; - process_try2collect(process_current); +_Noreturn void _syscall_exit(int ret) { + process_kill(process_current, ret); process_switch_any(); } -int _syscall_await(char __user *buf, int len) { +int _syscall_await(void) { bool has_children = false; process_current->state = PS_WAITS4CHILDDEATH; - process_current->death_msg.buf = buf; - process_current->death_msg.len = len; // find any already dead children for (struct process *iter = process_current->child; iter; iter = iter->sibling) { - if (iter->state == PS_DEAD) { - int ret = process_try2collect(iter); - assert(ret >= 0); - return ret; - } + if (iter->state == PS_DEAD) + return process_try2collect(iter); if (iter->state != PS_DEADER) has_children = true; } @@ -38,7 +30,7 @@ int _syscall_await(char __user *buf, int len) { process_switch_any(); // wait until a child dies else { process_current->state = PS_RUNNING; - return -1; // error + return ~0; // TODO errno } } @@ -276,9 +268,9 @@ int _syscall_memflag(void __user *addr, size_t len, int flags) { int _syscall(int num, int a, int b, int c, int d) { switch (num) { case _SYSCALL_EXIT: - _syscall_exit((userptr_t)a, b); + _syscall_exit(a); case _SYSCALL_AWAIT: - return _syscall_await((userptr_t)a, b); + return _syscall_await(); case _SYSCALL_FORK: return _syscall_fork(); case _SYSCALL_OPEN: diff --git a/src/shared/syscalls.h b/src/shared/syscalls.h index 44a2f5d..3a21b66 100644 --- a/src/shared/syscalls.h +++ b/src/shared/syscalls.h @@ -27,12 +27,12 @@ int _syscall(int, int, int, int, int); /** Kills the current process. * TODO: what happens to the children? */ -_Noreturn void _syscall_exit(const char __user *msg, size_t len); +_Noreturn void _syscall_exit(int ret); -/** Waits for a child to exit, putting its exit message into *buf. - * @return the length of the message +/** Waits for a child to exit. + * @return the value the child passed to exit() */ -int _syscall_await(char __user *buf, int len); +int _syscall_await(void); /** Creates a copy of the current process, and executes it. * All user memory pages get copied too. |