os

Toy OS
git clone git://git.christosmarg.xyz
Log | Files | Refs | README | LICENSE

commit f5bdd46c928f314671dc5e934424bcfb4254a6e1
parent 1d8bcba8689f039b3aaafab83d124d205e2d327f
Author: Christos Margiolis <christos@margiolis.net>
Date:   Sat, 24 Apr 2021 22:51:01 +0300

Set up ISR, IDT, added basic PS/2 keyboard and timer drivers

Diffstat:
MMakefile | 1+
Mboot/Makefile | 8++++++--
Mboot/boot.asm | 1+
Mboot/extern.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Aboot/idt.c | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aboot/idt.h | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aboot/int.asm | 440+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aboot/kbd.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aboot/kbd.h | 3+++
Mboot/kernel.c | 9+++++++++
Aboot/timer.c | 24++++++++++++++++++++++++
Aboot/timer.h | 6++++++
Mboot/tty.c | 6+++---
13 files changed, 880 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile @@ -8,3 +8,4 @@ run: clean: rm -rf ${TGTDIR} + cd boot && make clean && cd .. diff --git a/boot/Makefile b/boot/Makefile @@ -14,7 +14,11 @@ SRC = *.c *.asm OBJ = loader.o \ kernel.o \ string.o \ - tty.o + tty.o \ + idt.o \ + int.o \ + kbd.o \ + timer.o all: options ${BIN} @@ -33,7 +37,7 @@ ${BIN}: ${OBJ} dd if=/dev/zero bs=1000000 count=1 >> ${KERNEL_BIN} cat ${BOOT_BIN} ${KERNEL_BIN} > $@ -c.o: +.c.o: ${CC} -c ${CFLAGS} $< .asm.o: diff --git a/boot/boot.asm b/boot/boot.asm @@ -157,6 +157,7 @@ lm_check: lm_fail: popa call kernel_exec + jmp $ ; Safety hang. ; We'll check for (and switch to) Long Mode here. lm_enter: diff --git a/boot/extern.h b/boot/extern.h @@ -8,4 +8,49 @@ #include "tty.h" #include "string.h" +static inline uint8_t +inb(uint16_t port) +{ + uint8_t res; + + __asm__ __volatile__ ("in %%dx, %%al" : "=a" (res) : "d" (port)); + return res; +} + +static inline void +outb(uint8_t v, uint16_t port) +{ + __asm__ __volatile__ ("out %%al, %%dx" : : "a" (v), "d" (port)); +} + +static inline uint16_t +inw(uint16_t port) +{ + uint16_t res; + + __asm__ __volatile__ ("in %%dx, %%ax" : "=a" (res) : "d" (port)); + return res; +} + +static inline void +outw(uint16_t v, uint16_t port) +{ + __asm__ __volatile__ ("out %%ax, %%dx" : : "a" (v), "d" (port)); +} + +static inline uint32_t +inl(uint16_t port) +{ + uint32_t res; + + __asm__ __volatile__ ("in %%dx, %%eax" : "=a" (res) : "d" (port)); + return res; +} + +static inline void +outl(uint32_t v, uint16_t port) +{ + __asm__ __volatile__ ("out %%eax, %%dx" : : "a" (v), "d" (port)); +} + #endif /* _KERNEL_EXTERN_H */ diff --git a/boot/idt.c b/boot/idt.c @@ -0,0 +1,168 @@ +#include "extern.h" +#include "idt.h" + +static void idt_set_gate(uint8_t, uint32_t); + +static struct idt_gate idt[N_INT]; +static struct idt_reg idtr; +static void *irq_routines[16] = {NULL}; + +/* Exception messages for the first 32 ISRs. */ +static const char *except[] = { + "Division By Zero Exception", + "Debug Exception", + "Non Maskable Interrupt Exception", + "Breakpoint Exception", + "Into Detected Overflow Exception", + "Out of Bounds Exception", + "Invalid Opcode Exception", + "No Coprocessor Exception", + "Double Fault Exception", + "Coprocessor Segment Overrun Exception", + "Bad TSS Exception", + "Segment Not Present Exception", + "Stack Fault Exception", + "General Protection Fault Exception", + "Page Fault Exception", + "Unknown Interrupt Exception", + "Coprocessor Fault Exception", + "Alignment Check Exception (486+)", + "Machine Check Exception (Pentium/586+)", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", +}; + +static void +idt_set_gate(uint8_t n, uint32_t ptr) +{ + idt[n].off_lo = ptr & 0xffff; + idt[n].sel = KERN_CODESEG; + idt[n].zero = 0; + idt[n].attr = 0x8e; /* The gate is present and is running in kernel mode. */ + idt[n].off_hi = (ptr >> 16) & 0xffff; +} + +void +idt_init(void) +{ + /* Set up the ISRs */ + idt_set_gate(0, (uint32_t)isr0); + idt_set_gate(1, (uint32_t)isr1); + idt_set_gate(2, (uint32_t)isr2); + idt_set_gate(3, (uint32_t)isr3); + idt_set_gate(4, (uint32_t)isr4); + idt_set_gate(5, (uint32_t)isr5); + idt_set_gate(6, (uint32_t)isr6); + idt_set_gate(7, (uint32_t)isr7); + idt_set_gate(8, (uint32_t)isr8); + idt_set_gate(9, (uint32_t)isr9); + idt_set_gate(10, (uint32_t)isr10); + idt_set_gate(11, (uint32_t)isr11); + idt_set_gate(12, (uint32_t)isr12); + idt_set_gate(13, (uint32_t)isr13); + idt_set_gate(14, (uint32_t)isr14); + idt_set_gate(15, (uint32_t)isr15); + idt_set_gate(16, (uint32_t)isr16); + idt_set_gate(17, (uint32_t)isr17); + idt_set_gate(18, (uint32_t)isr18); + idt_set_gate(19, (uint32_t)isr19); + idt_set_gate(20, (uint32_t)isr20); + idt_set_gate(21, (uint32_t)isr21); + idt_set_gate(22, (uint32_t)isr22); + idt_set_gate(23, (uint32_t)isr23); + idt_set_gate(24, (uint32_t)isr24); + idt_set_gate(25, (uint32_t)isr25); + idt_set_gate(26, (uint32_t)isr26); + idt_set_gate(27, (uint32_t)isr27); + idt_set_gate(28, (uint32_t)isr28); + idt_set_gate(29, (uint32_t)isr29); + idt_set_gate(30, (uint32_t)isr30); + idt_set_gate(31, (uint32_t)isr31); + + /* Remap the PIC */ + /* TODO: explain.*/ + outb(0x11, 0x20); + outb(0x11, 0xa0); + outb(0x20, 0x21); + outb(0x28, 0xa1); + outb(0x04, 0x21); + outb(0x02, 0xa1); + outb(0x01, 0x21); + outb(0x01, 0xa1); + outb(0x00, 0x21); + outb(0x00, 0xa1); + + /* Set up the IRQs */ + idt_set_gate(32, (uint32_t)irq0); + idt_set_gate(33, (uint32_t)irq1); + idt_set_gate(34, (uint32_t)irq2); + idt_set_gate(35, (uint32_t)irq3); + idt_set_gate(36, (uint32_t)irq4); + idt_set_gate(37, (uint32_t)irq5); + idt_set_gate(38, (uint32_t)irq6); + idt_set_gate(39, (uint32_t)irq7); + idt_set_gate(40, (uint32_t)irq8); + idt_set_gate(41, (uint32_t)irq9); + idt_set_gate(42, (uint32_t)irq10); + idt_set_gate(43, (uint32_t)irq11); + idt_set_gate(44, (uint32_t)irq12); + idt_set_gate(45, (uint32_t)irq13); + idt_set_gate(46, (uint32_t)irq14); + idt_set_gate(47, (uint32_t)irq15); + + /* https://wiki.osdev.org/Interrupt_Descriptor_Table#Location_and_Size */ + idtr.base = (uint32_t)&idt; + idtr.limit = N_INT * sizeof(struct idt_gate) - 1; + /* Load the IDT */ + __asm__ __volatile__ ("lidtl (%0)" : : "r" (&idtr)); +} + +/* TODO: add comments */ +void +isr_handler(struct reg *r) +{ + /* XXX: 31? */ + if (r->intno < 32) { + tty_write(except[r->intno]); + tty_write(". System halted...\n"); + __asm__ __volatile__ ("hlt"); + } +} + +/* TODO: add comments */ +void +irq_handler(struct reg *r) +{ + void (*handler)(struct reg *); + + if ((handler = irq_routines[r->intno - 32]) != 0) + handler(r); + if (r->intno >= 40) + outb(0x20, 0xa0); + outb(0x20, 0x20); +} + +/* TODO: add comments */ +void +irq_add_handler(uint8_t irqno, void (*handler)(struct reg *r)) +{ + irq_routines[irqno] = handler; +} + +/* TODO: add comments */ +void +irq_rm_handler(uint8_t irqno) +{ + irq_routines[irqno] = NULL; +} diff --git a/boot/idt.h b/boot/idt.h @@ -0,0 +1,109 @@ +#ifndef _KERNEL_IDT_H +#define _KERNEL_IDT_H + +#define N_INT 256 /* There are 256 interrupts in total. */ +#define KERN_CODESEG 0x08 /* Kernel code segment. */ + +struct idt_reg { + uint16_t limit; /* Points at &IDT. */ + uint32_t base; /* Points at the end of the IDT. */ +} __attribute__((packed)); + +/* IA-32 */ +struct idt_gate { + uint16_t off_lo; + uint16_t sel; + uint8_t zero; + uint8_t attr; + uint16_t off_hi; +} __attribute__((packed)); + + +/* This will be populated by isr_common_stub. */ +struct reg { + /* Segment registers. Will be poped last. */ + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + /* General purpose registers. Pushed by `pusha`. */ + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + /* ISR info. Pushed by `push byte`. */ + uint32_t intno; + uint32_t err; + /* Pushed by the processor. */ + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t uesp; + uint32_t ss; +}; + +/* `kernel_main` calls this. */ +extern void idt_init(void); + +extern void isr_handler(struct reg *); +extern void irq_handler(struct reg *); +extern void irq_add_handler(uint8_t, void (*)(struct reg *)); +extern void irq_rm_handler(uint8_t); + +/* The first 32 ISRs are reserved for exceptions. */ +extern void isr0(void); +extern void isr1(void); +extern void isr2(void); +extern void isr3(void); +extern void isr4(void); +extern void isr5(void); +extern void isr6(void); +extern void isr7(void); +extern void isr8(void); +extern void isr9(void); +extern void isr10(void); +extern void isr11(void); +extern void isr12(void); +extern void isr13(void); +extern void isr14(void); +extern void isr15(void); +extern void isr16(void); +extern void isr17(void); +extern void isr18(void); +extern void isr19(void); +extern void isr20(void); +extern void isr21(void); +extern void isr22(void); +extern void isr23(void); +extern void isr24(void); +extern void isr25(void); +extern void isr26(void); +extern void isr27(void); +extern void isr28(void); +extern void isr29(void); +extern void isr30(void); +extern void isr31(void); + +/* IRQs */ +extern void irq0(void); +extern void irq1(void); +extern void irq2(void); +extern void irq3(void); +extern void irq4(void); +extern void irq5(void); +extern void irq6(void); +extern void irq7(void); +extern void irq8(void); +extern void irq9(void); +extern void irq10(void); +extern void irq11(void); +extern void irq12(void); +extern void irq13(void); +extern void irq14(void); +extern void irq15(void); + +#endif /* _KERNEL_IDT_H */ diff --git a/boot/int.asm b/boot/int.asm @@ -0,0 +1,440 @@ +[extern isr_handler] +[extern irq_handler] + +; ISRs +global isr0 +global isr1 +global isr2 +global isr3 +global isr4 +global isr5 +global isr6 +global isr7 +global isr8 +global isr9 +global isr10 +global isr11 +global isr12 +global isr13 +global isr14 +global isr15 +global isr16 +global isr17 +global isr18 +global isr19 +global isr20 +global isr21 +global isr22 +global isr23 +global isr24 +global isr25 +global isr26 +global isr27 +global isr28 +global isr29 +global isr30 +global isr31 + +; IRQs +global irq0 +global irq1 +global irq2 +global irq3 +global irq4 +global irq5 +global irq6 +global irq7 +global irq8 +global irq9 +global irq10 +global irq11 +global irq12 +global irq13 +global irq14 +global irq15 + +; ================== ISR ====================== + +; 0 - Division By Zero +isr0: + cli + push byte 0 + push byte 0 + jmp isr_common_stub + +; 1 - Debug +isr1: + cli + push byte 0 + push byte 1 + jmp isr_common_stub + +; 2 - Non Maskable Interrupt +isr2: + cli + push byte 0 + push byte 2 + jmp isr_common_stub + +; 3 - Breakpoint +isr3: + cli + push byte 0 + push byte 3 + jmp isr_common_stub + +; 4 - Into Detected Overflow +isr4: + cli + push byte 0 + push byte 4 + jmp isr_common_stub + +; 5 - Out of Bounds +isr5: + cli + push byte 0 + push byte 5 + jmp isr_common_stub + +; 6 - Invalid Opcode +isr6: + cli + push byte 0 + push byte 6 + jmp isr_common_stub + +; 7 - No Coprocessor +isr7: + cli + push byte 0 + push byte 7 + jmp isr_common_stub + +; 8 - Double Fault (with error code) +isr8: + cli + push byte 8 + jmp isr_common_stub + +; 9 - Coprocessor Segment Overrrun +isr9: + cli + push byte 0 + push byte 9 + jmp isr_common_stub + +; 10 - Bad TSS (with error code) +isr10: + cli + push byte 10 + jmp isr_common_stub + +; 11 - Segment Not Present (with error code) +isr11: + cli + push byte 11 + jmp isr_common_stub + +; 12 - Stack Fault (with error code) +isr12: + cli + push byte 12 + jmp isr_common_stub + +; 13 - General Protection Fault (with error code) +isr13: + cli + push byte 13 + jmp isr_common_stub + +; 14 - Page Fault (with error code) +isr14: + cli + push byte 14 + jmp isr_common_stub + +; 15 - Unkown Interrupt +isr15: + cli + push byte 0 + push byte 15 + jmp isr_common_stub + +; 16 - Coprocessor Fault +isr16: + cli + push byte 0 + push byte 16 + jmp isr_common_stub + +; 17 - Alignment Check (486+) +isr17: + cli + push byte 0 + push byte 17 + jmp isr_common_stub + +; 18 - Machine Check (Pentium/586+) +isr18: + cli + push byte 0 + push byte 18 + jmp isr_common_stub + +; 19 - Reserved +isr19: + cli + push byte 0 + push byte 19 + jmp isr_common_stub + +; 20 - Reserved +isr20: + cli + push byte 0 + push byte 20 + jmp isr_common_stub + +; 21 - Reserved +isr21: + cli + push byte 0 + push byte 21 + jmp isr_common_stub + +; 22 - Reserved +isr22: + cli + push byte 0 + push byte 22 + jmp isr_common_stub + +; 23 - Reserved +isr23: + cli + push byte 0 + push byte 23 + jmp isr_common_stub + +; 24 - Reserved +isr24: + cli + push byte 0 + push byte 24 + jmp isr_common_stub + +; 25 - Reserved +isr25: + cli + push byte 0 + push byte 25 + jmp isr_common_stub + +; 26 - Reserved +isr26: + cli + push byte 0 + push byte 26 + jmp isr_common_stub + +; 27 - Reserved +isr27: + cli + push byte 0 + push byte 27 + jmp isr_common_stub + +; 28 - Reserved +isr28: + cli + push byte 0 + push byte 28 + jmp isr_common_stub + +; 29 - Reserved +isr29: + cli + push byte 0 + push byte 29 + jmp isr_common_stub + +; 30 - Reserved +isr30: + cli + push byte 0 + push byte 30 + jmp isr_common_stub + +; 31 - Reserved +isr31: + cli + push byte 0 + push byte 31 + jmp isr_common_stub + +; ================== IRQ ====================== + +; 32 - Programmable Interrupt Timer +irq0: + cli + push byte 0 + push byte 32 + jmp irq_common_stub + +; 33 - Keyboard +irq1: + cli + push byte 0 + push byte 33 + jmp irq_common_stub + +; 34 Cascade (used internally by the two PICs, never raised) +irq2: + cli + push byte 0 + push byte 34 + jmp irq_common_stub + +; 35 - COM2 (if enabled) +irq3: + cli + push byte 0 + push byte 35 + jmp irq_common_stub + +; 36 - COM1 (if enabled) +irq4: + cli + push byte 0 + push byte 36 + jmp irq_common_stub + +; 37 - LPT2 (if enabled) +irq5: + cli + push byte 0 + push byte 37 + jmp irq_common_stub + +; 38 - Floppy Disk +irq6: + cli + push byte 0 + push byte 38 + jmp irq_common_stub + +; 39 - LPT1 +irq7: + cli + push byte 0 + push byte 39 + jmp irq_common_stub + +; 40 - CMOS real-time clock (if enabled) +irq8: + cli + push byte 0 + push byte 40 + jmp irq_common_stub + +; 41 - Peripherals / Legacy SCSI / NIC +irq9: + cli + push byte 0 + push byte 41 + jmp irq_common_stub + +; 42 - Peripherals / SCSI / NIC +irq10: + cli + push byte 0 + push byte 42 + jmp irq_common_stub + +; 43 - Peripherals / SCSI / NIC +irq11: + cli + push byte 0 + push byte 43 + jmp irq_common_stub + +; 44 - PS2 Mouse +irq12: + cli + push byte 0 + push byte 44 + jmp irq_common_stub + +; 45 - FPU / Coprocessor / Inter-processor +irq13: + cli + push byte 0 + push byte 45 + jmp irq_common_stub + +; 46 - Primary ATA Hard Disk +irq14: + cli + push byte 0 + push byte 46 + jmp irq_common_stub + +; 47 - Secondary ATA Hard Disk +irq15: + cli + push byte 0 + push byte 47 + jmp irq_common_stub + +; Save the processor state, call the C fault handler and restore the stack frame. +isr_common_stub: + pusha + push ds + push es + push fs + push gs + mov ax, 0x10 ; Kernel data segment descriptor. + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov eax, esp ; Push the stack. + push eax + mov eax, isr_handler + call eax + pop eax + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Clean up ISR info. + sti + iret + +; Call the C handler and restore the stack frame. +irq_common_stub: + pusha + push ds + push es + push fs + push gs + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov eax, esp + push eax + mov eax, irq_handler + call eax + pop eax + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 + sti + iret diff --git a/boot/kbd.c b/boot/kbd.c @@ -0,0 +1,65 @@ +#include "extern.h" +#include "idt.h" + +static void kbd_callback(struct reg *); + +static unsigned char kbdus[128] = { + 0, /* Error */ + 27, /* Escape */ + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + '[', ']', '\n', + 0, /* Control */ + 'a', 's', 'd', 'f', 'g', + 'h', 'j', 'k', 'l', ';', '\'', '`', + 0, /* Left Shift */ + '\\', 'z', 'x', + 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + 0, /* Right Shift */ + '*', + 0, /* Alt */ + ' ', + 0, /* Caps Lock */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F1-F10 */ + 0, /* Num Lock */ + 0, /* Scroll Lock */ + 0, /* Home Key */ + 0, /* Up Arrow */ + 0, /* Page Up */ + '-', + 0, /* Left Arrow */ + 0, + 0, /* Right Arrow */ + '+', + 0, /* End Key */ + 0, /* Down Arrow */ + 0, /* Page Down */ + 0, /* Insert Key */ + 0, /* Delete Key */ + 0, 0, 0, + 0, 0, /* F11, F12 */ + 0, /* The rest are undefined */ +}; + +static void +kbd_callback(struct reg *r) +{ + uint8_t sc; + + /* TODO: backspace */ + if ((sc = inb(0x60)) & 0x80) { + } else { + tty_putc(kbdus[sc]); + } + (void)r; +} + +/* + * Send `kbd_callback` to the `irq_routines` array to be called + * for IRQ 1. + */ +void +kbd_init(void) +{ + irq_add_handler(1, kbd_callback); +} diff --git a/boot/kbd.h b/boot/kbd.h @@ -0,0 +1,3 @@ +#include "extern.h" + +extern void kbd_init(void); diff --git a/boot/kernel.c b/boot/kernel.c @@ -1,8 +1,17 @@ #include "extern.h" +#include "idt.h" +#include "kbd.h" +#include "timer.h" void kernel_main(void) { tty_init(); + idt_init(); + timer_init(50); + kbd_init(); + /* Enable interrupts so that the IRQs can work now. */ + __asm__ __volatile__ ("sti"); tty_write("Nothing to see here yet. At least it booted.\n"); + __asm__ __volatile__ ("hlt"); } diff --git a/boot/timer.c b/boot/timer.c @@ -0,0 +1,24 @@ +#include "extern.h" +#include "idt.h" + +static void timer_callback(struct reg *); + +static uint32_t timer_ticks = 0; + +static void +timer_callback(struct reg *r) +{ + timer_ticks++; + (void)r; +} + +void +timer_init(uint32_t hz) +{ + uint32_t div = 1193180 / hz; + + irq_add_handler(0, timer_callback); + outb(0x36, 0x43); + outb((uint8_t)(div & 0xff), 0x40); + outb((uint8_t)((div >> 8) & 0xff), 0x40); +} diff --git a/boot/timer.h b/boot/timer.h @@ -0,0 +1,6 @@ +#ifndef _KERNEL_TIMER_H +#define _KERNEL_TIMER_H + +extern void timer_init(uint32_t); + +#endif /* _KERNEL_TIMER_H */ diff --git a/boot/tty.c b/boot/tty.c @@ -12,7 +12,7 @@ tty_init(void) tty.row = 0; tty.col = 0; - tty.color = _COLOR(VGA_LIGHT_GREY, VGA_BLUE); + tty.color = _COLOR(VGA_GREEN, VGA_BLACK); tty.buf = (uint16_t *)0xb8000; for (x = 0; x < VGA_COLS; x++) for (y = 0; y < VGA_ROWS; y++) @@ -42,12 +42,12 @@ tty_putc(char c) } if (tty.row >= VGA_ROWS) { - tty.row++; + tty.row = 0; tty.col = 0; } if (tty.col >= VGA_COLS) { + tty.row++; tty.col = 0; - tty.row = 0; } }