diff options
| author | Aleksa Vučković <aleksav013@gmail.com> | 2021-10-11 11:35:59 +0200 |
|---|---|---|
| committer | Aleksa Vučković <aleksav013@gmail.com> | 2021-10-11 11:35:59 +0200 |
| commit | 578d467b80015c52d0c96c8443b4c13936f33365 (patch) | |
| tree | 05525782bc3baf5a01d8b657f01934e1e598a775 | |
| parent | 3a9ccbd8e762477f75d8b164a1d99383a01414ae (diff) | |
(IDT + keyboard) finally working
| -rw-r--r-- | .gdbinit | 2 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 36 | ||||
| -rw-r--r-- | README.md | 18 | ||||
| -rw-r--r-- | src/boot.s | 57 | ||||
| -rw-r--r-- | src/gdt.c | 13 | ||||
| -rw-r--r-- | src/idt.c | 103 | ||||
| -rw-r--r-- | src/kernel.c | 86 | ||||
| -rw-r--r-- | src/keyboard.h | 74 |
9 files changed, 342 insertions, 49 deletions
diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..5bf175c --- /dev/null +++ b/.gdbinit @@ -0,0 +1,2 @@ +target remote localhost:1234 +file myiso.iso @@ -1,3 +1,3 @@ build isodir -myos.iso +*.iso @@ -1,10 +1,13 @@ -CC=i686-elf-gcc -AS=i686-elf-as -CFLAGS=-ffreestanding -O2 -Wall -Wextra +ARCH=i686-elf + +CC=$(ARCH)-gcc +AS=$(ARCH)-as +CFLAGS=-ffreestanding -O2 -Wall -Wextra -ggdb MKDIR=mkdir -p RM=rm -rf CP=cp +QEMU=qemu-system-x86_64 SOURCE_DIR=src BUILD_DIR=build @@ -12,11 +15,16 @@ ISO_DIR=isodir TARGET=myos -OBJ_FILES=boot.o kernel.o gdt.o +OBJ_FILES=boot.o kernel.o gdt.o idt.o CRTBEGIN_OBJ=$(shell $(CC) -print-file-name=crtbegin.o) CRTEND_OBJ=$(shell $(CC) -print-file-name=crtend.o) OBJ=$(BUILD_DIR)/crti.o $(CRTBEGIN_OBJ) $(patsubst %,$(BUILD_DIR)/%,$(OBJ_FILES)) $(CRTEND_OBJ) $(BUILD_DIR)/crtn.o + +# Default action is set to making kernel binary +.PHONY: all +all: $(BUILD_DIR)/$(TARGET).bin + # Creating iso file $(TARGET).iso: $(BUILD_DIR)/$(TARGET).bin grub-file --is-x86-multiboot $(BUILD_DIR)/myos.bin @@ -25,9 +33,8 @@ $(TARGET).iso: $(BUILD_DIR)/$(TARGET).bin $(CP) $(SOURCE_DIR)/grub.cfg $(ISO_DIR)/boot/grub/grub.cfg grub-mkrescue -o $(TARGET).iso $(ISO_DIR) -# Linking object files +# Linking object files into kernel binary $(BUILD_DIR)/$(TARGET).bin: $(OBJ) - $(MKDIR) $(BUILD_DIR) $(CC) -T $(SOURCE_DIR)/linker.ld -o $@ $(CFLAGS) -nostdlib $^ -lgcc # Compiling as sources @@ -40,10 +47,21 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c $(MKDIR) $(BUILD_DIR) $(CC) -c $< -o $@ -std=gnu99 $(CFLAGS) -# Boot kernel in qemu +# Boot kernel binary in qemu .PHONY: run -run: $(TARGET).iso - qemu-system-x86_64 -cdrom $(TARGET).iso +run: $(BUILD_DIR)/$(TARGET).bin + $(QEMU) -kernel $^ + +# Boot iso in qemu +.PHONY: run-iso +run-iso: $(TARGET).iso + $(QEMU) -cdrom $^ + +# Debug kernel binary in gdb +.PHONY: debug +debug: $(TARGET).bin + $(QEMU) -kernel $^ -s -S & + gdb -x .gdbinit # Clean build files .PHONY: clean @@ -1,10 +1,16 @@ # mykernel ## i686-elf cross compiler -First install binutils and gcc from your package manager. -On Arch based distributions they are available in AUR: -- [binutils](https://aur.archlinux.org/packages/i686-elf-binutils/) -- [gcc](https://aur.archlinux.org/packages/i686-elf-gcc/) +- [binutils](https://aur.archlinux.org/packages/i686-elf-binutils/) +- [gcc](https://aur.archlinux.org/packages/i686-elf-gcc/) -## Bare bones kernel in as and C -[osdev](https://wiki.osdev.org/Bare_Bones) +## You will also need: +- grub +- xorriso(libisoburn) +- qemu +- gdb(optional) + +## Useful articles +- [Bare Bones](https://wiki.osdev.org/Bare_Bones) +- [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table) +- [Interrupt Descriptor Table](https://wiki.osdev.org/Interrupt_Descriptor_Table) @@ -12,33 +12,70 @@ .global _start .global load_gdt +.global load_idt +.global enable_interrupts +.global keyboard_handler +.global ioport_in +.global ioport_out + +.extern init_gdt_table +.extern handle_keyboard_interrupt +.extern kernel_main load_gdt: movl 4(%esp), %edx lgdt (%edx) ret +load_idt: + movl 4(%esp), %edx + lidt (%edx) + sti + ret + +keyboard_handler: + pushal + cld + call handle_keyboard_interrupt + popal + iretl + +ioport_in: + movl 4(%esp),%edx + in %dx,%al + ret + +ioport_out: + movl 4(%esp),%edx + movl 8(%esp),%eax + outb %al,%dx + ret + +.set CODE_SEGMENT, 0x08 +.set DATA_SEGMENT, 0x10 + .section .bss .align 16 stack_bottom: -.skip 16384 # 16 KiB +.skip 16384 stack_top: .section .text .type _start, @function _start: call init_gdt_table - mov 0x10, %ax - mov %ds, %ax - mov %es, %ax - mov %fs, %ax - mov %gs, %ax - mov %ss, %ax - mov $stack_top, %esp + ljmp $CODE_SEGMENT, $next + next: + movw $DATA_SEGMENT, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl $stack_top, %esp cli call _init call kernel_main -1: hlt - jmp 1b + hlt .size _start, . - _start @@ -24,13 +24,14 @@ extern void load_gdt(struct gdt_pointer *gdtp); struct gdt_entry gdt[3]; struct gdt_pointer gdtp; -void init_gdt_entry(size_t num, uint32_t limit, uint32_t base1, uint32_t base2, uint8_t access, uint8_t limit_flags, uint8_t base3) +void init_gdt_entry(size_t num, uint32_t limit, uint32_t base, uint8_t access, uint8_t limit_flags) { gdt[num].limit=limit; - gdt[num].base1=base1; + gdt[num].base1=(base & 0xffff); + gdt[num].base2=(base & 0xff0000) >> 16; gdt[num].access=access; gdt[num].limit_flags=limit_flags; - gdt[num].base3=base3; + gdt[num].base3=(base & 0xff000000) >> 24; } void init_gdt_table() @@ -39,11 +40,11 @@ void init_gdt_table() gdtp.offset=(uint32_t)&gdt; //null - init_gdt_entry(0,0,0,0,0,0,0); + init_gdt_entry(0,0,0,0,0); //code - init_gdt_entry(1,0xffff,0x0000,0x00,0b10011010,0b11001111,0x00); + init_gdt_entry(1,0xffffffff,0,0b10011010,0b11001111); //data - init_gdt_entry(2,0xffff,0x0000,0x00,0b10010010,0b11001111,0x00); + init_gdt_entry(2,0xffffffff,0,0b10010010,0b11001111); load_gdt(&gdtp); } diff --git a/src/idt.c b/src/idt.c new file mode 100644 index 0000000..479d397 --- /dev/null +++ b/src/idt.c @@ -0,0 +1,103 @@ +#include<stdbool.h> +#include<stddef.h> +#include<stdint.h> + +#define INTERRUPT_GATE_32 0x8e + +#define KERNEL_CODE 0x08 +#define KERNEL_DATA 0x10 + +#define PIC1_COMMAND_PORT 0x20 +#define PIC1_DATA_PORT 0x21 +#define PIC2_COMMAND_PORT 0xA0 +#define PIC2_DATA_PORT 0xA1 + +struct idt_entry +{ + uint16_t offset1; + uint16_t selector; + uint8_t zero; + uint8_t type_attr; + uint16_t offset2; +} __attribute__((packed)); + +struct idt_pointer +{ + uint16_t size; + uint32_t offset; +} __attribute__((packed)); + +// asm function +extern void load_idt(struct idt_pointer *idtp); +extern void keyboard_handler(); +extern void ioport_out(uint8_t port, char data); + +struct idt_entry idt[256]; +struct idt_pointer idtp; + +void init_idt_entry(size_t num, uint32_t offset, uint16_t selector, uint8_t type_attr) +{ + idt[num].offset1=(offset & 0xffff); + idt[num].selector=selector; + idt[num].zero=0; + idt[num].type_attr=type_attr; + idt[num].offset2=(offset & 0xffff0000)>>16; +} + +void init_idt_table() +{ + // Program the PICs - Programmable Interrupt Controllers + // Background: + // In modern architectures, the PIC is not a separate chip. + // It is emulated in the CPU for backwards compatability. + // The APIC (Advanced Programmable Interrupt Controller) + // is the new version of the PIC that is integrated into the CPU. + // Default vector offset for PIC is 8 + // This maps IRQ0 to interrupt 8, IRQ1 to interrupt 9, etc. + // This is a problem. The CPU reserves the first 32 interrupts for + // CPU exceptions such as divide by 0, etc. + // In programming the PICs, we move this offset to 0x2 (32) so that + // we can handle all interrupts coming to the PICs without overlapping + // with any CPU exceptions. + + // Send ICWs - Initialization Command Words + // PIC1: IO Port 0x20 (command), 0xA0 (data) + // PIC2: IO Port 0x21 (command), 0xA1 (data) + // ICW1: Initialization command + // Send a fixed value of 0x11 to each PIC to tell it to expect ICW2-4 + // Restart PIC1 + ioport_out(PIC1_COMMAND_PORT, 0x11); + ioport_out(PIC2_COMMAND_PORT, 0x11); + // ICW2: Vector Offset (this is what we are fixing) + // Start PIC1 at 32 (0x20 in hex) (IRQ0=0x20, ..., IRQ7=0x27) + // Start PIC2 right after, at 40 (0x28 in hex) + ioport_out(PIC1_DATA_PORT, 0x20); + ioport_out(PIC2_DATA_PORT, 0x28); + // ICW3: Cascading (how master/slave PICs are wired/daisy chained) + // Tell PIC1 there is a slave PIC at IRQ2 (why 4? don't ask me - https://wiki.osdev.org/8259_PIC) + // Tell PIC2 "its cascade identity" - again, I'm shaky on this concept. More resources in notes + ioport_out(PIC1_DATA_PORT, 0x0); + ioport_out(PIC2_DATA_PORT, 0x0); + // ICW4: "Gives additional information about the environemnt" + // See notes for some potential values + // We are using 8086/8088 (MCS-80/85) mode + // Not sure if that's relevant, but there it is. + // Other modes appear to be special slave/master configurations (see wiki) + ioport_out(PIC1_DATA_PORT, 0x1); + ioport_out(PIC2_DATA_PORT, 0x1); + // Voila! PICs are initialized + + // Mask all interrupts (why? not entirely sure) + // 0xff is 16 bits that are all 1. + // This masks each of the 16 interrupts for that PIC. + ioport_out(PIC1_DATA_PORT, 0xff); + ioport_out(PIC2_DATA_PORT, 0xff); + + init_idt_entry(0x21,(uint32_t)keyboard_handler,KERNEL_CODE,INTERRUPT_GATE_32); + + idtp.size=sizeof(struct idt_entry)*256-1; + idtp.offset=(uint32_t)&idt; + + + load_idt(&idtp); +} diff --git a/src/kernel.c b/src/kernel.c index c6d1045..0882580 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -6,6 +6,17 @@ static const size_t VGA_WIDTH = 80; static const size_t VGA_HEIGHT = 25; +#define PIC1_COMMAND_PORT 0x20 +#define PIC1_DATA_PORT 0x21 +#define PIC2_COMMAND_PORT 0xA0 +#define PIC2_DATA_PORT 0xA1 +// IO Ports for Keyboard +#define KEYBOARD_DATA_PORT 0x60 +#define KEYBOARD_STATUS_PORT 0x64 + +extern char ioport_in(uint8_t port); +extern void ioport_out(uint8_t port, char data); + static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { return fg | bg << 4; @@ -23,24 +34,24 @@ uint16_t* terminal_buffer; void terminal_initialize(void) { - terminal_row = 0; - terminal_column = 0; - terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); - terminal_buffer = (uint16_t*) 0xB8000; - for (size_t y = 0; y < VGA_HEIGHT; y++) + terminal_row=0; + terminal_column=0; + terminal_color=vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + terminal_buffer=(uint16_t*) 0xB8000; + for(size_t y=0;y<VGA_HEIGHT;y++) { - for (size_t x = 0; x < VGA_WIDTH; x++) + for(size_t x=0;x<VGA_WIDTH;x++) { - const size_t index = y * VGA_WIDTH + x; - terminal_buffer[index] = vga_entry(' ', terminal_color); + const size_t index=y*VGA_WIDTH+x; + terminal_buffer[index]=vga_entry(' ', terminal_color); } } } void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) { - const size_t index = y * VGA_WIDTH + x; - terminal_buffer[index] = vga_entry(c, color); + const size_t index=y*VGA_WIDTH+x; + terminal_buffer[index]=vga_entry(c, color); } void movescreen() @@ -55,9 +66,9 @@ void terminal_putchar(char c) else { terminal_putentryat(c, terminal_color, terminal_column, terminal_row); - if (++terminal_column == VGA_WIDTH) terminal_column = 0,terminal_row++; + if (++terminal_column==VGA_WIDTH) terminal_column=0,terminal_row++; } - if (terminal_row == VGA_HEIGHT) movescreen(); + if (terminal_row==VGA_HEIGHT) movescreen(); } void terminal_writestring(const char* data) @@ -65,13 +76,54 @@ void terminal_writestring(const char* data) for(int i=0;data[i]!='\0';i++) terminal_putchar(data[i]); } -void kernel_main(void) +void init_keyboard() { - terminal_initialize(); + // 0xFD = 1111 1101 in binary. enables only IRQ1 + // Why IRQ1? Remember, IRQ0 exists, it's 0-based + ioport_out(PIC1_DATA_PORT, 0xFD); +} + +#include"keyboard.h" +void handle_keyboard_interrupt() +{ + // Write end of interrupt (EOI) + ioport_out(PIC1_COMMAND_PORT, 0x20); + unsigned char status = ioport_in(KEYBOARD_STATUS_PORT); + + // Lowest bit of status will be set if buffer not empty + // (thanks mkeykernel) + if (status & 0x1) + { + char keycode = ioport_in(KEYBOARD_DATA_PORT); + if (keycode < 0 || (uint8_t)keycode >= 128) return; + + if(keycode==14) + { + if(terminal_column) terminal_column--; + terminal_putchar(keyboard[keycode]); + terminal_column--; + return; + } - for(size_t i=0;i<50;i++) - { + terminal_putchar(keyboard[keycode]); + + } +} +void print_message() +{ + for(size_t i=0;i<50;i++) + { for(size_t j=0;j<i;j++) terminal_writestring("#"); terminal_writestring("Hello, kernel World!\n"); - } + } +} + +void kernel_main(void) +{ + terminal_initialize(); + print_message(); + init_idt_table(); + init_keyboard(); + terminal_writestring("First part is working\n"); + while(1); } diff --git a/src/keyboard.h b/src/keyboard.h new file mode 100644 index 0000000..53846e6 --- /dev/null +++ b/src/keyboard.h @@ -0,0 +1,74 @@ +unsigned char keyboard[128] = { + // -------- 0 to 9 -------- + ' ', + ' ', // escape key + '1','2','3','4','5','6','7','8', + // -------- 10 to 19 -------- + '9','0','-','=', + ' ', // Backspace + ' ', // Tab + 'q','w','e','r', + // -------- 20 to 29 -------- + 't','y','u','i','o','p','[',']', + '\n', // Enter + ' ', // left Ctrl + // -------- 30 to 39 -------- + 'a','s','d','f','g','h','j','k','l',';', + // -------- 40 to 49 -------- + ' ','`', + ' ', // left Shift + ' ','z','x','c','v','b','n', + // -------- 50 to 59 -------- + 'm',',','.', + '/', // slash, or numpad slash if preceded by keycode 224 + ' ', // right Shift + '*', // numpad asterisk + ' ', // left Alt + ' ', // Spacebar + ' ', + ' ', // F1 + // -------- 60 to 69 -------- + ' ', // F2 + ' ', // F3 + ' ', // F4 + ' ', // F5 + ' ', // F6 + ' ', // F7 + ' ', // F8 + ' ', // F9 + ' ', // F10 + ' ', + // -------- 70 to 79 -------- + ' ', // scroll lock + '7', // numpad 7, HOME key if preceded by keycode 224 + '8', // numpad 8, up arrow if preceded by keycode 224 + '9', // numpad 9, PAGE UP key if preceded by keycode 224 + '-', // numpad hyphen + '4', // numpad 4, left arrow if preceded by keycode 224 + '5', // numpad 5 + '6', // numpad 6, right arrow if preceded by keycode 224 + ' ', + '1', // numpad 1, END key if preceded by keycode 224 + // -------- 80 to 89 -------- + '2', // numpad 2, down arrow if preceded by keycode 224 + '3', // numpad 3, PAGE DOWN key if preceded by keycode 224 + '0', // numpad 0, INSERT key if preceded by keycode 224 + '.', // numpad dot, DELETE key if preceded by keycode 224 + ' ',' ',' ',' ',' ',' ', + // -------- 90 to 99 -------- + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + // -------- 100 to 109 -------- + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + // -------- 110 to 119 -------- + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + // -------- 120-127 -------- + ' ',' ',' ',' ',' ',' ',' ',' ', +}; +// Right control, right alt seem to send +// keycode 224, then the left control/alt keycode +// Arrow keys also send two interrupts, one 224 and then their actual code +// Same for numpad enter +// 197: Num Lock +// 157: Pause|Break (followed by 197?) +// Clicking on screen appears to send keycodes 70, 198 + // Is this the MARK command or something like that? |
