diff options
Diffstat (limited to 'src/kernel')
-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 |
9 files changed, 195 insertions, 60 deletions
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: |