From 1069a04ca39e33f60656ba2668e2fdab9f5d6e58 Mon Sep 17 00:00:00 2001 From: Robbe Van Herck Date: Wed, 8 Jan 2020 22:36:49 +0100 Subject: [PATCH] so close yet so far --- .gdbinit | 2 + Makefile | 10 +++- bootloader/main.asm | 3 -- kernel/inline_asm.c | 44 ++++++++++++++++++ kernel/interrupts.c | 108 ++++++++++++++++++++++++++++++++++++++++++++ kernel/kernel.c | 43 +++++++++++------- kernel/memory.c | 8 +++- kernel/terminal.c | 31 ++++++++++++- kernel/wrapper.asm | 5 ++ 9 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 .gdbinit create mode 100644 kernel/inline_asm.c create mode 100644 kernel/interrupts.c diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..4f6fe09 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,2 @@ +target remote localhost:1234 +file target/kernel/kernel.bin diff --git a/Makefile b/Makefile index 30344fe..3bcd457 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,12 @@ run: bin qemu-system-i386 -drive format=raw,file=target/boot.bin -monitor stdio +run_kernelonly: compile_kernel + qemu-system-i386 -kernel target/kernel/kernel.bin -monitor stdio + +debug_kernel: compile_kernel + qemu-system-i386 -s -S -kernel target/kernel/kernel.bin + clean: rm -r ./target/ mkdir -p ./target/helpers @@ -19,5 +25,5 @@ compile_bootloader: compile_kernel: nasm -felf32 kernel/wrapper.asm -o target/kernel/wrapper.o - i686-elf-gcc -c kernel/kernel.c -o target/kernel/kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra - i686-elf-gcc -T kernel/linker.ld -o target/kernel/kernel.bin -ffreestanding -O2 -nostdlib target/kernel/wrapper.o target/kernel/kernel.o -lgcc + i686-elf-gcc -g -c kernel/kernel.c -o target/kernel/kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra -mno-80387 -mgeneral-regs-only -mno-red-zone + i686-elf-gcc -g -T kernel/linker.ld -o target/kernel/kernel.bin -ffreestanding -O2 -nostdlib target/kernel/wrapper.o target/kernel/kernel.o -lgcc diff --git a/bootloader/main.asm b/bootloader/main.asm index 384f3e6..aea9181 100644 --- a/bootloader/main.asm +++ b/bootloader/main.asm @@ -136,9 +136,6 @@ mov ch, 0b00100000 mov cl, 0b00000000 int 0x10 -mov bl, dl -call puthex - ; reset disk system mov ah, 0x00 ; mov dl, DISK_ID diff --git a/kernel/inline_asm.c b/kernel/inline_asm.c new file mode 100644 index 0000000..db3de1b --- /dev/null +++ b/kernel/inline_asm.c @@ -0,0 +1,44 @@ +#ifndef INLINE_ASM_C +#define INLINE_ASM_C + +#include + +static inline void outb(uint16_t port, uint8_t val) { + asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t ret; + asm volatile ( "inb %1, %0" + : "=a"(ret) + : "Nd"(port) ); + return ret; +} + +static inline void lidt(void* base, uint16_t size) +{ // This function works in 32 and 64bit mode + struct { + uint16_t length; + void* base; + } __attribute__((packed)) IDTR = { size, base }; + + asm ( "lidt %0" : : "m"(IDTR) ); // let the compiler choose an addressing mode +} + +/** issue a single request to CPUID. Fits 'intel features', for instance + * note that even if only "eax" and "edx" are of interest, other registers + * will be modified by the operation, so we need to tell the compiler about it. + */ +static inline void cpuid(int code, uint32_t *a, uint32_t *d) { + asm volatile("cpuid":"=a"(*a),"=d"(*d):"a"(code):"ecx","ebx"); +} + +/** issue a complete request, storing general registers output as a string + */ +static inline int cpuid_string(int code, uint32_t where[4]) { + asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)), + "=c"(*(where+2)),"=d"(*(where+3)):"a"(code)); + return (int)where[0]; +} + +#endif //INLINE ASM_C \ No newline at end of file diff --git a/kernel/interrupts.c b/kernel/interrupts.c new file mode 100644 index 0000000..31506f4 --- /dev/null +++ b/kernel/interrupts.c @@ -0,0 +1,108 @@ +#ifndef INTERRUPTS_C +#define INTERRUPTS_C + +#include +#include + +#include "memory.c" +#include "terminal.c" +#include "inline_asm.c" + +typedef struct idt_entry_struct { + uint16_t offset_1; // offset bits 0..15 + uint16_t selector; // a code segment selector in GDT or LDT + uint8_t zero; // unused, set to 0 + uint8_t type_attr; // type and attributes, see below + uint16_t offset_2; // offset bits 16..31 +} idt_entry; + +struct interrupt_frame { + uint32_t esp; + uint32_t ss; + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t ebp; + uint32_t edi; + uint32_t esi; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +}; + +idt_entry IDT[256]; +int count = 1; + +void interrupt_new_handler(int intnum, void (*handler)(struct interrupt_frame*)) { + uint32_t handler_address = (uint32_t) handler; + IDT[intnum].offset_1 = (uint16_t) (handler_address & 0xffff); + IDT[intnum].selector = 0x08; + IDT[intnum].zero = 0; + IDT[intnum].type_attr = 0b10001110; // Active, privilege level 00, storage segment = 0; 32 bit interrupt gate + IDT[intnum].offset_2 = (uint16_t) ((handler_address & 0xffff0000) >> 16); +} + +void kb_init() { + uint8_t mask = inb(0x21); + + mask = mask & ~(1 << 1); + + outb(0x21 , mask); +} + +__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame* frame) { + outb(0x20, 0x20); + + char* result = "XXXXXX"; + itoa(frame->eip, result, 16); + terminal_writestring(result); +} + +void interrupt_init() { + /* ICW1 - begin initialization */ + outb(0x20, 0x11); + outb(0xA0, 0x11); + + /* ICW2 - remap offset address of IDT */ + outb(0x21, 0x20); + outb(0xA1, 0x82); + + /* ICW3 - setup cascading */ + outb(0x21, 0x00); + outb(0xA1, 0x00); + + /* ICW4 - environment info */ + outb(0x21, 0x01); + outb(0xA1, 0x01); + + /* mask interrupts */ + outb(0x21 , 0xff); + outb(0xA1 , 0xff); + + interrupt_new_handler(0x21, keyboard_handler); + + uint16_t size = (sizeof(idt_entry) * 256); + + terminal_writestring("Size = 0x"); + char* result = "XXXXX"; + itoa(size, result, 16); + terminal_writestring(result); + + lidt(IDT, size); + + kb_init(); + + uint8_t mask = inb(0x21); + + terminal_writestring(" mask = 0b"); + result = "XXXXXXXX"; + itoa(mask, result, 2); + terminal_writestring(result); +} + +#endif //INTERRUPTS_C \ No newline at end of file diff --git a/kernel/kernel.c b/kernel/kernel.c index 2722b95..e15dc11 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -1,10 +1,3 @@ -#include -#include -#include - -#include "terminal.c" -#include "memory.c" - /* Check if the compiler thinks you are targeting the wrong operating system. */ #if defined(__linux__) #error "You are not using a cross-compiler, you will most certainly run into trouble" @@ -12,15 +5,25 @@ /* This tutorial will only work for the 32-bit ix86 targets. */ #if !defined(__i386__) -#error "This tutorial needs to be compiled with a ix86-elf compiler" +#error "This kernel needs to be compiled with a ix86-elf compiler" #endif -struct person { - long id; - int age; - char gender; -}; - +#include +#include +#include + +#include "terminal.c" +#include "memory.c" +#include "interrupts.c" + +static inline bool are_interrupts_enabled() { + unsigned long flags; + asm volatile ( "pushf\n\t" + "pop %0" + : "=g"(flags) ); + return flags & (1 << 9); +} + void kernel_main(void) { /* Initialize terminal interface */ @@ -45,11 +48,17 @@ void kernel_main(void) memory_str[6] = 0; char* management_str = alloc(sizeof(char) * 13); - for (int i = 0; i < 12; i++) { - management_str[i] = " management!"[i]; + for (int i = 0; i < 13; i++) { + management_str[i] = " management!\n"[i]; } - management_str[12] = 0; + management_str[13] = 0; terminal_writestring(memory_str); terminal_writestring(management_str); + + terminal_writestring((are_interrupts_enabled())? "Interrupts!\n": "No interrupts :(\n"); + + interrupt_init(); + + while(true) {} } \ No newline at end of file diff --git a/kernel/memory.c b/kernel/memory.c index ad060e9..a228222 100644 --- a/kernel/memory.c +++ b/kernel/memory.c @@ -1,9 +1,13 @@ +#ifndef MEMORY_C +#define MEMORY_C + #include -void* _curr_end = (void*) 0x10000; +void* _curr_end = (void*) 0x200000; void* alloc(size_t size) { void* thanks_jp = _curr_end; _curr_end += size; return thanks_jp; -} \ No newline at end of file +} +#endif // MEMORY_C \ No newline at end of file diff --git a/kernel/terminal.c b/kernel/terminal.c index b6d1d34..1299194 100644 --- a/kernel/terminal.c +++ b/kernel/terminal.c @@ -1,3 +1,6 @@ +#ifndef TERMINAL_C +#define TERMINAL_C + #include #include #include @@ -89,4 +92,30 @@ void terminal_write(const char* data, size_t size) { void terminal_writestring(const char* data) { terminal_write(data, strlen(data)); -} \ No newline at end of file +} + +char* itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + +#endif //TERMINAL_C \ No newline at end of file diff --git a/kernel/wrapper.asm b/kernel/wrapper.asm index 3e2dad7..4659f69 100644 --- a/kernel/wrapper.asm +++ b/kernel/wrapper.asm @@ -54,6 +54,10 @@ _start: ; stack (as it grows downwards on x86 systems). This is necessarily done ; in assembly as languages such as C cannot function without a stack. mov esp, stack_top + + mov al, 0xff + out 0xa1, al + out 0x21, al ; This is a good place to initialize crucial processor state before the ; high-level kernel is entered. It's best to minimize the early @@ -71,6 +75,7 @@ _start: ; stack since (pushed 0 bytes so far) and the alignment is thus ; preserved and the call is well defined. ; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main + sti extern kernel_main call kernel_main