From: Duje Mihanović Date: Wed, 22 Sep 2021 20:15:40 +0000 (+0200) Subject: Handle interrupts X-Git-Tag: 0.1.0~16 X-Git-Url: http://git.dujemihanovic.xyz/img/static/%7B%7B?a=commitdiff_plain;h=9fc180435dd7b476130851c48e1ac28009daa864;p=nameless-os.git Handle interrupts Currently handles exception 8 and IRQ 1. EXC8 is double fault, and IRQ1 is keyboard. EXC8 handler puts out a ':(' on the top left corner of the screen and halts the machine, while IRQ1 puts out a string on the screen. However, it will only do so once, and I have yet to figure out why. --- diff --git a/Makefile b/Makefile index 5d98229..c8f15f3 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,9 @@ AS = yasm LD = i686-elf-ld CC = i686-elf-gcc -KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/kernel.o +GIT_REV = $(shell git rev-parse --short HEAD) + +KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/kernel.o all: boot.img kernel/kernel.elf @@ -20,10 +22,19 @@ kernel/entry.o: kernel/entry.s $(AS) -f elf kernel/entry.s -o $@ kernel/arch/x86/tty/tty.o: kernel/arch/x86/tty/tty.c - $(CC) -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/arch/x86/tty/tty.c + $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/arch/x86/tty/tty.c + +kernel/drivers/irq/i8259a.o: kernel/drivers/irq/i8259a.c + $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/drivers/irq/i8259a.c + +kernel/arch/x86/irq/idt.o: kernel/arch/x86/irq/idt.c + $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/arch/x86/irq/idt.c + +kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c + $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -mgeneral-regs-only -c kernel/arch/x86/irq/sample_handler.c kernel/kernel.o: kernel/kernel.c - $(CC) -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/kernel.c + $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\" -c kernel/kernel.c kernel/kernel.elf: kernel/kernel.bin $(LD) -o $@ -T kernel/linker.ld ${KERNEL_OBJ} --oformat=elf32-i386 diff --git a/include/arch/x86/io.h b/include/arch/x86/io.h index 586eb74..09f6fa7 100644 --- a/include/arch/x86/io.h +++ b/include/arch/x86/io.h @@ -17,4 +17,9 @@ static inline uint8_t inb(uint16_t port) return ret; } +static inline void io_wait(void) +{ + inb(0x80); +} + #endif diff --git a/include/arch/x86/irq/i8259a.h b/include/arch/x86/irq/i8259a.h new file mode 100644 index 0000000..31448e4 --- /dev/null +++ b/include/arch/x86/irq/i8259a.h @@ -0,0 +1,23 @@ +#ifndef X86_I8259A_H +#define X86_I8259A_H + +#define PIC1_COMMAND 0x20 +#define PIC1_DATA 0x21 +#define PIC2_COMMAND 0xA0 +#define PIC2_DATA 0xA1 + +#define PIC_ICW1_INIT 0x10 +#define PIC_ICW1_ICW4 0x1 + +#define PIC_ICW4_8086 0x1 + +#define PIC_EOI 0x20 + +extern void pic_init(int offset1, int offset2); +extern void pic_mask(uint8_t irq); +extern void pic_unmask(uint8_t irq); +extern void pic_mask_all(void); +extern void pic_unmask_all(void); +extern void pic_send_eoi(uint8_t irq); + +#endif diff --git a/include/arch/x86/irq/idt.h b/include/arch/x86/irq/idt.h new file mode 100644 index 0000000..8b2efa1 --- /dev/null +++ b/include/arch/x86/irq/idt.h @@ -0,0 +1,28 @@ +#ifndef X86_IDT_H +#define X86_IDT_H + +#include + +#define IDT_VECTOR_COUNT 256 /* our IDT will have 256 vectors */ +#define IDT_DESCRIPTOR_SIZE 8 /* each IDT descriptor is 8 bytes long */ + +#define IDT_TRAP_GATE 0x1F +#define IDT_INTERRUPT_GATE 0x0E + +struct idt_descriptor { + uint16_t offset_1; /* bits 0-15 of offset */ + uint16_t segsel; /* segment selector */ + unsigned unused : 8, type : 5, dpl : 2, present : 1; + uint16_t offset_2; /* bits 16-31 of offset */ +} __attribute__((packed)); + +struct idtr { + uint16_t limit; /* size of IDT minus 1 */ + uint32_t base; /* starting address of IDT */ +} __attribute__((packed)); + +extern void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl); +extern inline void load_idt(struct idtr idtr); +extern inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt); + +#endif diff --git a/kernel/arch/x86/irq/idt.c b/kernel/arch/x86/irq/idt.c new file mode 100644 index 0000000..54e8612 --- /dev/null +++ b/kernel/arch/x86/irq/idt.c @@ -0,0 +1,27 @@ +#include +#include + +/* Note to self: passing a pointer to an asm constraint as "m" will give a pointer to pointer */ +inline void load_idt(struct idtr idtr) +{ + asm volatile ("lidt %0": : "m" (idtr)); +} + +void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl) +{ + struct idt_descriptor *descriptor = &idt[vector]; + + descriptor->offset_1 = offset & 0xFFFF; + descriptor->segsel = segment; + descriptor->unused = 0; + descriptor->type = type; + descriptor->dpl = dpl; + descriptor->present = 1; + descriptor->offset_2 = offset >> 16; +} + +inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt) +{ + idtr->limit = ((IDT_VECTOR_COUNT * IDT_DESCRIPTOR_SIZE) - 1); /* limit must be size - 1 */ + idtr->base = (uint32_t) idt; +} diff --git a/kernel/arch/x86/irq/sample_handler.c b/kernel/arch/x86/irq/sample_handler.c new file mode 100644 index 0000000..c8bddfe --- /dev/null +++ b/kernel/arch/x86/irq/sample_handler.c @@ -0,0 +1,30 @@ +#include +#include +#include + +typedef uint32_t uword_t; + +struct interrupt_frame { + uword_t ip; + uword_t cs; + uword_t flags; +}; + +struct abort_frame; + +__attribute__((interrupt)) +void keyb_handler(struct interrupt_frame *frame) +{ + pic_send_eoi(1); + kprint("Got a keyboard interrupt!\n", 0); +} + + +__attribute__((interrupt)) +void double_fault(struct abort_frame *frame) +{ + *(volatile char *) (0xb8000) = ":"; + *(volatile char *) (0xb8002) = "("; + asm volatile ("cli; hlt"); +} + diff --git a/kernel/drivers/irq/i8259a.c b/kernel/drivers/irq/i8259a.c new file mode 100644 index 0000000..ed1e6ac --- /dev/null +++ b/kernel/drivers/irq/i8259a.c @@ -0,0 +1,80 @@ +#include +#include +#include + +void pic_init(int offset1, int offset2) +{ + uint8_t a1, a2; + + a1 = inb(PIC1_DATA); + a2 = inb(PIC2_DATA); + + outb(PIC1_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4); + io_wait(); + outb(PIC2_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4); + io_wait(); + outb(PIC1_DATA, offset1); + io_wait(); + outb(PIC2_DATA, offset2); + io_wait(); + outb(PIC1_DATA, 4); + io_wait(); + outb(PIC2_DATA, 2); + io_wait(); + + outb(PIC1_DATA, PIC_ICW4_8086); + io_wait(); + outb(PIC2_DATA, PIC_ICW4_8086); + io_wait(); + + outb(PIC1_DATA, a1); + outb(PIC2_DATA, a2); +} + +void pic_mask(uint8_t irq) +{ + uint16_t port; + uint8_t value; + + if (irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + } + + value = inb(port) | (1 << irq); + outb(port, value); +} + +void pic_unmask(uint8_t irq) +{ + uint16_t port; + uint8_t value; + + if (irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + } + + value = inb(port) & ~(1 << irq); + outb(port, value); +} + +void pic_mask_all(void) +{ + outb(PIC1_DATA, 0xFF); +} + +void pic_unmask_all(void) +{ + outb(PIC1_DATA, 0); +} + +void pic_send_eoi(uint8_t irq) +{ + if (irq >= 8) + outb(PIC2_COMMAND, PIC_EOI); + + outb(PIC1_COMMAND, PIC_EOI); +} diff --git a/kernel/kernel.c b/kernel/kernel.c index 7b6d354..e1a0df5 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -1,39 +1,31 @@ #include #include +#include +#include #include -const char *string = "Hello there!\n\n\ -Hopefully your machine manages to print this text.\n\ -If it did, that's great news because I managed to write a partial VGA driver.\n\n\ -Right now, the short-term roadmap is as follows:\n\n\ -* Complete the text-mode part of the VGA driver.\n\ -* Enable interrupts using the PIC.\n\ -* Write a driver for the Intel 8042 PS/2 controller so the OS can receive keystrokes.\n\ -* Once all that is done, it would be desirable to start the switch to userspace so the OS can actually be remotely usable.\n\n\ -Feel free to mess around with the code, although I doubt it will be very interesting at the moment.\n"; +extern void double_fault(struct abort_frame *frame); +extern void keyb_handler(struct interrupt_frame *frame); +struct idt_descriptor idt[256] __attribute__((aligned(0x10))); +struct idtr idtr __attribute__((aligned(0x10))); void _start(void) { screen_clear(); - /*kprint(string, 0x07);*/ - kprint("Color 0x01\n", VGA_COLOR_BLUE); - kprint("Color 0x02\n", VGA_COLOR_GREEN); - kprint("Color 0x03\n", VGA_COLOR_TEAL); - kprint("Color 0x04\n", VGA_COLOR_DARK_RED); - kprint("Color 0x05\n", VGA_COLOR_MAGENTA); - kprint("Color 0x06\n", VGA_COLOR_BROWN); - kprint("Color 0x07\n", VGA_COLOR_LIGHT_GRAY); - kprint("Color 0x08\n", VGA_COLOR_DARK_GRAY); - kprint("Color 0x09\n", VGA_COLOR_PURPLE); - kprint("Color 0x0A\n", VGA_COLOR_LIME); - kprint("Color 0x0B\n", VGA_COLOR_CYAN); - kprint("Color 0x0C\n", VGA_COLOR_BRIGHT_RED); - kprint("Color 0x0D\n", VGA_COLOR_PINK); - kprint("Color 0x0E\n", VGA_COLOR_YELLOW); - kprint("Color 0x0F\n", VGA_COLOR_WHITE); - kprintb(0xAE); + kprint("Welcome to Nameless OS!\nRunning revision: ", 0); + kprint(GIT_COMMIT, 0); kprint("\n", 0); - kprintw(0xDEAD); - kprint("\n", 0); - kprintd(0x89ABCDEF); + kprint("Preparing IDT...\n", 0); + idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_GATE, 0x0); + idt_set_descriptor(idt, 0x21, 0x8, (uint32_t) keyb_handler, IDT_INTERRUPT_GATE, 0x0); + kprint("IDT prepared, loading...\n", 0); + populate_idtr(&idtr, idt); + load_idt(idtr); + kprint("IDT loaded, enabling interrupts...\n", 0); + pic_init(0x20, 0x28); + pic_mask_all(); + pic_unmask(1); + asm volatile ("sti"); + kprint("All done\n", 0); + while(1); }