diff --git a/.gitignore b/.gitignore index 796b96d..ed85fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/build +bochsout.txt diff --git a/Makefile b/Makefile index 5f9211b..861ddb1 100644 --- a/Makefile +++ b/Makefile @@ -21,11 +21,13 @@ $(ASFLAGS) KERNEL_OBJS=\ kernel/kernel.o \ kernel/debugging.o \ +kernel/elf.o \ LIB_OBJS=\ $(ARCHDIR)/bootstrap.o \ $(ARCHDIR)/tty.o \ $(ARCHDIR)/strlib.o \ +$(ARCHDIR)/idt.o \ $(ARCHDIR)/interrupt.o \ $(ARCHDIR)/gdt.o \ @@ -48,10 +50,13 @@ clean: rm -f myos.iso rm -f isodir/boot/myos.kernel -.c.o: +$(ARCHDIR)/interrupt.o: CFLAGS += -mgeneral-regs-only +kernel/elf.o: CFLAGS += -masm=intel + +%.o : %.c i686-elf-gcc -c $< -o $@ $(CFLAGS) -.s.o: +%.o : %.s i686-elf-as $< -o $@ $(ASFLAGS) myos.kernel: $(OBJS) @@ -67,7 +72,7 @@ debug: ASFLAGS += -g debug: all run: myos.iso - qemu-system-i386 myos.iso -display curses + qemu-system-i386 myos.iso run-debug: debug - qemu-system-i386 -S -gdb tcp::9000 myos.iso -display curses + qemu-system-i386 -S -gdb tcp::9000 myos.iso diff --git a/arch/i686/bootstrap.s b/arch/i686/bootstrap.s index ab01428..0c85bce 100644 --- a/arch/i686/bootstrap.s +++ b/arch/i686/bootstrap.s @@ -30,6 +30,7 @@ System V ABI standard and de-facto extensions. The compiler will assume the stack is properly aligned and failure to align the stack will result in undefined behavior. */ + .section .bss .align 16 stack_bottom: @@ -69,15 +70,15 @@ _start: call gdt_init - movw %ax, 0x10 - movw %ds, %ax - movw %es, %ax - movw %fs, %ax - movw %gs, %ax - movw %ss, %ax + movw 0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss # jmp kernel ljmp $0x08, $kernel - + kernel: /* This is a good place to initialize crucial processor state before the @@ -99,8 +100,6 @@ kernel: preserved and the call is well defined. */ - mov %esp, %edx - call kernel_main .global keyboard_test diff --git a/arch/i686/gdt.c b/arch/i686/gdt.c index f51b007..fdacbdf 100644 --- a/arch/i686/gdt.c +++ b/arch/i686/gdt.c @@ -3,7 +3,7 @@ #include #include -#define NUM_ENTRIES 3 +#define NUM_ENTRIES 6 struct SegmentDescriptor { uint32_t limit; @@ -17,6 +17,39 @@ struct GDT { uint64_t* base; } __attribute__((packed)); +struct tss_entry_struct { + uint32_t prev_tss; // The previous TSS - with hardware task switching these form a kind of backward linked list. + uint32_t esp0; // The stack pointer to load when changing to kernel mode. + uint32_t ss0; // The stack segment to load when changing to kernel mode. + // Everything below here is unused. + uint32_t esp1; // esp and ss 1 and 2 would be used when switching to rings 1 or 2. + uint32_t ss1; + uint32_t esp2; + uint32_t ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t es; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t fs; + uint32_t gs; + uint32_t ldt; + uint16_t trap; + uint16_t iomap_base; +} __attribute__((packed)); + +struct tss_entry_struct tss; + static uint64_t entries[NUM_ENTRIES]; static size_t current_entry = 0; struct GDT gdt; @@ -71,16 +104,13 @@ static void load_gdt() gdt.size = NUM_ENTRIES * 8 - 1; gdt.base = entries; - for (int i = 0; i < NUM_ENTRIES; i++) { - print_hex_bytes(gdt.base + i, 8); - terminal_putchar('\n'); - } - asm ( "lgdt %0" : : "m"(gdt) ); } void gdt_init() { + // https://wiki.osdev.org/Global_Descriptor_Table + terminal_initialize(); struct SegmentDescriptor null; null.limit = 0; @@ -106,15 +136,47 @@ void gdt_init() terminal_putchar('\n'); struct SegmentDescriptor kernel_data; - kernel_code.limit = 0x000003FF; + kernel_data.limit = 0x000003FF; kernel_data.base = 0x00400000; kernel_data.access_byte = 0b10010010; kernel_data.flags = 0b1100; terminal_writestring("Kernel Data: "); - add_entry(kernel_data); terminal_putchar('\n'); + struct SegmentDescriptor user_code; + user_code.limit = 0x000003FF; + user_code.base = 0x00800000; + user_code.access_byte = 0b11111010; + user_code.flags = 0b1100; + + terminal_writestring("User Code: "); + + add_entry(user_code); + terminal_putchar('\n'); + + struct SegmentDescriptor user_data; + user_data.limit = 0x000FFFFF; + user_data.base = 0x00c00000; + user_data.access_byte = 0b11110010; + user_data.flags = 0b1100; + + terminal_writestring("User Data: "); + + add_entry(user_data); + terminal_putchar('\n'); + + struct SegmentDescriptor tss_segment; + tss_segment.limit = sizeof(tss_segment) - 1; + tss_segment.base = (uint32_t) &tss; + tss_segment.access_byte = 0x89; + tss_segment.flags = 0b0000; + + terminal_writestring("TSS Segment: "); + + add_entry(tss_segment); + terminal_putchar('\n'); + load_gdt(); } diff --git a/arch/i686/idt.c b/arch/i686/idt.c new file mode 100644 index 0000000..53ff948 --- /dev/null +++ b/arch/i686/idt.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include "interrupt.h" +#include +#include + +#define NUM_ENTRIES 0x100 + +struct InterruptDescriptor { + uint32_t offset; + uint32_t selector; + uint8_t gate_type; + uint8_t ring; + bool present; +}; + +struct IDT { + uint16_t size; + uint64_t* base; +} __attribute__((packed)); + +static uint64_t entries[NUM_ENTRIES]; +struct IDT idt; + +enum EntryTypes { + DivisionByZero = 0x00, + _Reserved0 = 0x01, + NMIInterrupt = 0x02, + Breakpoint = 0x03, + Overflow = 0x04, + BoundsExceeded = 0x05, + InvalidOpcode = 0x06, + DeviceNotAvail = 0x07, + DoubleFault = 0x08, + CoprocSegORun = 0x09, + InvalidTSS = 0x0A, + SegNotPresent = 0x0B, + StackSegFault = 0x0C, + GenerProtFault = 0x0D, + PageFault = 0x0E, + _Reserved1 = 0x0F, + x87FPUError = 0x10, + AlignmentCheck = 0x11, + MachineCheck = 0x12, + SIMDFloatExcep = 0x13, +}; + +static void encodeIdtEntry(uint8_t *target, struct InterruptDescriptor source) +{ + // Encode the present bit + if (source.present) { + target[5] |= 0x80; + } else { + // Not present, return + return; + } + + // Encode the offset + target[0] = (source.offset >> 0x00) & 0xFF; + target[1] = (source.offset >> 0x08) & 0xFF; + target[6] = (source.offset >> 0x10) & 0xFF; + target[7] = (source.offset >> 0x18) & 0xFF; + + // Encode the base + target[2] = (source.selector >> 0x00) & 0xFF; + target[3] = (source.selector >> 0x08) & 0xFF; + + // Encode the gate type + target[5] |= source.gate_type; + + // Encode the DPL + target[5] |= (source.ring << 5); +} + +static void add_entry(struct InterruptDescriptor entry, uint8_t index) +{ + uint64_t descriptor = 0; + encodeIdtEntry((uint8_t*) &descriptor, entry); + + entries[index] = descriptor; +} + +static void load_idt() +{ + idt.size = NUM_ENTRIES * 8 - 1; + idt.base = entries; + + asm ( "lidt %0" : : "m"(idt) ); + asm ( "sti" ); +} + +struct InterruptDescriptor generate_entry(bool present, void isr(struct interrupt_frame*), uint8_t gate_type, uint8_t ring, uint8_t selector) +{ + struct InterruptDescriptor entry; + entry.present = present; + entry.offset = (uint32_t)isr; + entry.gate_type = gate_type; + entry.ring = ring; + entry.selector = selector; + return entry; +} + +static inline void outb(uint16_t port, uint8_t val) +{ + __asm__ volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory"); + /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). + * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). + * The outb %al, %dx encoding is the only option for all other cases. + * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ +} + +void idt_init() +{ + outb(0x21,0xfd); + outb(0xa1,0xff); + + struct InterruptDescriptor entry; + + entry = generate_entry(true, exception, 0xF, 0, 0x8); + for (int i = 0; i < 0x20; i++) + { + add_entry(entry, i); + } + + entry = generate_entry(true, irq, 0xF, 0, 0x8); + for (int i = 0x20; i < 0x28; i++) + { + add_entry(entry, i); + } + + entry = generate_entry(true, divide_by_zero, 0xF, 0, 0x8); + add_entry(entry, DivisionByZero); + + entry = generate_entry(true, general_protection_fault, 0xF, 0, 0x8); + add_entry(entry, GenerProtFault); + + entry = generate_entry(true, double_fault, 0xF, 0, 0x8); + add_entry(entry, DoubleFault); + + struct InterruptDescriptor print_entry; + print_entry.present = true; + print_entry.offset = (uint32_t)print; + print_entry.gate_type = 0xE; + print_entry.ring = 3; + print_entry.selector = 0x8; + + add_entry(print_entry, 0x80); + + struct InterruptDescriptor input_entry; + input_entry.present = true; + input_entry.offset = (uint32_t)input; + input_entry.gate_type = 0xE; + input_entry.ring = 0; + input_entry.selector = 0x8; + + add_entry(input_entry, 0x81); + + load_idt(); + + tss.ss0 = 0x10; + asm("mov %%esp, %0" : "=r" (tss.esp0)); + asm("mov $0x28, %ax"); + asm("ltr %ax"); +} diff --git a/arch/i686/interrupt.c b/arch/i686/interrupt.c index 96c7b7a..e2e7c16 100644 --- a/arch/i686/interrupt.c +++ b/arch/i686/interrupt.c @@ -1,88 +1,114 @@ #include -#include -#include #include +#include -#define NUM_ENTRIES 0 - -struct InterruptDescriptor { - uint32_t offset; - uint32_t selector; - uint8_t gate_type; - uint8_t ring; - bool present; +struct interrupt_frame { + uint32_t eflags; + uint32_t cs; + uint32_t eip; }; -struct IDT { - uint16_t size; - uint64_t* base; -} __attribute__((packed)); +uint8_t test = 'a'; -static uint64_t entries[NUM_ENTRIES]; -static size_t current_entry = 0; -struct IDT idt; - -static void encodeIdtEntry(uint8_t *target, struct InterruptDescriptor source) +static void stack_trace() { - // Encode the present bit - if (source.present) { - target[5] |= 0xF0; - } else { - // Not present, return - return; - } - - // Encode the offset - target[0] = (source.offset >> 0x00) & 0xFF; - target[1] = (source.offset >> 0x08) & 0xFF; - target[6] = (source.offset >> 0x10) & 0xFF; - target[7] = (source.offset >> 0x18) & 0xFF; - - // Encode the base - target[2] = (source.selector >> 0x00) & 0xFF; - target[3] = (source.selector >> 0x08) & 0xFF; - - // Encode the gate type - target[5] |= source.gate_type; - - // Encode the DPL - target[5] |= (source.ring >> 5); - - print_hex_bytes(target, 8); -} - -static void add_entry(struct InterruptDescriptor entry) -{ - if (current_entry == NUM_ENTRIES) { - terminal_writestring("WARNING: More IDT entries than NUM_ENTRIES.\n"); - return; - } - - uint64_t descriptor = 0; - encodeIdtEntry((uint8_t*) &descriptor, entry); - - entries[current_entry] = descriptor; - - current_entry++; -} - -static void load_idt() -{ - idt.size = NUM_ENTRIES * 8 - 1; - idt.base = entries; - - for (int i = 0; i < NUM_ENTRIES; i++) { - print_hex_bytes(idt.base + i, 8); + uint32_t* stack = 0; + asm("mov %%esp, %0" : "=r" (stack)); + for (int i = 0; i < 25; i++) { + uint32_t* current_address = stack + (i * 4); + print_hex_bytes(¤t_address, 4); + terminal_writestring(": "); + print_hex_bytes(current_address, 4); terminal_putchar('\n'); } - - asm ( "lidt %0" : : "m"(idt) ); } -void idt_init() +__attribute__((interrupt)) void divide_by_zero(struct interrupt_frame* frame) { - terminal_writestring("TODO: Add interrupts"); - terminal_putchar('\n'); - - // load_idt(); + terminal_initialize(); + stack_trace(); + terminal_writestring("Yo dude u cant divide by zero yao\n"); + while (1) { } } + +__attribute__((interrupt)) void general_protection_fault(struct interrupt_frame* frame) +{ + /* terminal_initialize(); + stack_trace(); + terminal_writestring("General protection fault\n"); + terminal_writestring("Error code:\n"); + print_hex_bytes(frame, sizeof(frame)); + terminal_putchar('\n'); */ + while (1) { } +} + +__attribute__((interrupt)) void double_fault(struct interrupt_frame* frame) +{ + // terminal_writestring("2 Errors in a row, u better behave naughty naughty\n"); + // while (1) { } +} + +__attribute__((interrupt)) void exception(struct interrupt_frame* frame) +{ + /* terminal_initialize(); + stack_trace(); + terminal_writestring("Some weird error code stuff\n"); + terminal_writestring("Error code:\n"); + print_hex_bytes(frame, sizeof(frame)); + terminal_putchar('\n'); */ + while (1) { } +} + +__attribute__((interrupt)) void print(struct interrupt_frame* frame) +{ + /* terminal_initialize(); + terminal_putchar(test); + test++; */ + + char* start; + asm("mov %%edx, %0" : "=r" (start)); + terminal_writestring(start); + + /* uint8_t* stack; + asm("mov %%esp, %0" : "=r" (stack)); + + terminal_writestring("\nSTACK:\n"); + print_hex_bytes(stack - 24, 4); + terminal_putchar('\n'); + print_hex_bytes(stack - 20, 4); + terminal_putchar('\n'); + print_hex_bytes(stack - 16, 4); + terminal_putchar('\n'); + print_hex_bytes(stack - 12, 4); + terminal_putchar('\n'); + print_hex_bytes(stack - 8, 4); + terminal_putchar('\n'); + print_hex_bytes(stack - 4, 4); + terminal_putchar('\n'); */ +} + +__attribute__((interrupt)) void input(struct interrupt_frame* frame) +{ + char* buffer; + asm("mov %%edx, %0" : "=r" (buffer)); + + size_t index = 0; + + char current_character = get_last_key_pressed(); + while (current_character != '\n') { + char tmp = get_last_key_pressed(); + if (current_character != -1 && tmp != current_character) { + buffer[index] = current_character; + terminal_putchar(current_character); + index++; + } + current_character = tmp; + } + buffer[index] = '\0'; +} + +__attribute__((interrupt)) void irq(struct interrupt_frame* frame) +{ + +} + diff --git a/arch/i686/interrupt.h b/arch/i686/interrupt.h new file mode 100644 index 0000000..6c13bb0 --- /dev/null +++ b/arch/i686/interrupt.h @@ -0,0 +1,12 @@ +struct interrupt_frame; + +__attribute__((interrupt)) void divide_by_zero(struct interrupt_frame* frame); +__attribute__((interrupt)) void general_protection_fault(struct interrupt_frame* frame); +__attribute__((interrupt)) void double_fault(struct interrupt_frame* frame); + +__attribute__((interrupt)) void exception(struct interrupt_frame* frame); + +__attribute__((interrupt)) void print(struct interrupt_frame* frame); +__attribute__((interrupt)) void input(struct interrupt_frame* frame); + +__attribute__((interrupt)) void irq(struct interrupt_frame* frame); diff --git a/bochsrc.txt b/bochsrc.txt new file mode 100644 index 0000000..936cd4f --- /dev/null +++ b/bochsrc.txt @@ -0,0 +1,4 @@ +megs: 32 +log: ./bochsout.txt +ata0-master: type=disk, path="myos.iso", +boot: disk diff --git a/include/kernel/elf.h b/include/kernel/elf.h new file mode 100644 index 0000000..e032202 --- /dev/null +++ b/include/kernel/elf.h @@ -0,0 +1,5 @@ +#include +#include + +void run_program(uint8_t* program, size_t length); + diff --git a/include/kernel/gdt.h b/include/kernel/gdt.h new file mode 100644 index 0000000..2b0cf9b --- /dev/null +++ b/include/kernel/gdt.h @@ -0,0 +1,34 @@ +#include + +struct tss_entry_struct { + uint32_t prev_tss; // The previous TSS - with hardware task switching these form a kind of backward linked list. + uint32_t esp0; // The stack pointer to load when changing to kernel mode. + uint32_t ss0; // The stack segment to load when changing to kernel mode. + // Everything below here is unused. + uint32_t esp1; // esp and ss 1 and 2 would be used when switching to rings 1 or 2. + uint32_t ss1; + uint32_t esp2; + uint32_t ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t es; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t fs; + uint32_t gs; + uint32_t ldt; + uint16_t trap; + uint16_t iomap_base; +} __attribute__((packed)); + +extern struct tss_entry_struct tss; diff --git a/include/kernel/interrupt.h b/include/kernel/idt.h similarity index 100% rename from include/kernel/interrupt.h rename to include/kernel/idt.h diff --git a/kernel/elf.c b/kernel/elf.c new file mode 100644 index 0000000..682be8a --- /dev/null +++ b/kernel/elf.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +void run_program(uint8_t* program, size_t length) +{ + uint8_t* userland_code = (uint8_t*) 0x00800004; + + for (size_t i = 0; i < length; i++) { + userland_code[i] = program[i]; + } + + tss.ss0 = 0x10; + + asm("sti"); + asm("int 0x80"); + + asm("mov %0, esp" : "=r" (tss.esp0)); + + // Generates segfault + // asm("mov esp, %0" : "=r" (tss.esp0)); + + asm("mov ax, 0x23 # Load registers with the correct GDT segments values"); + asm("mov ds, ax"); + asm("mov es, ax"); + asm("mov fs, ax"); + asm("mov gs, ax"); + + asm("push 0x23"); + asm("push 0x00c00100"); + + asm("pushfd"); + asm("pop eax"); + asm("or eax, 0x200"); + asm("push eax"); + + asm("push 0x1b"); + asm("push 0x00800000"); + + asm("iret"); + + asm("int 0x21"); +} diff --git a/kernel/heap.c b/kernel/heap.c new file mode 100644 index 0000000..6518014 --- /dev/null +++ b/kernel/heap.c @@ -0,0 +1,3 @@ +#include + +uint8_t heap[2000]; diff --git a/kernel/kernel.c b/kernel/kernel.c index b67d528..f819f7b 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -1,28 +1,58 @@ #include -#include +#include #include +#include /* 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" #endif +uint8_t program[] = { +/* 0xcd, + 0x80, + 0xc6, + 0x05, + 0x00, + 0x80, + 0x0b, + 0x00, + 0x62, + 0xeb, + 0xfe, */ +/* 0x90, + 0xe9, + 0xfa, + 0x7f, + 0x7b, + 0xf8, */ +/* 0x90, + 0xeb, + 0xfd, */ + 0x6a, + 0x69, + 0xeb, + 0xfc, +}; + void kernel_main(void) { /* Initialize terminal interface */ - terminal_initialize(); + // terminal_initialize(); - // TODO: Add interrupts - // idt_init(); + idt_init(); - terminal_putchar(get_last_key_pressed()); + run_program(program, 15); - char current_character = get_last_key_pressed(); - while (current_character != ',') { - char tmp = get_last_key_pressed(); - if (current_character != -1 && tmp != current_character) { - terminal_putchar(current_character); - } - current_character = tmp; + /* + char buf[100]; + + while (true) { + asm("mov %0, %%edx" :: "r" (buf)); + asm("int $0x81"); + terminal_writestring("newline"); + terminal_putchar('\n'); } + + */ } diff --git a/libc/include/stdio.h b/libc/include/stdio.h new file mode 100644 index 0000000..030ab8a --- /dev/null +++ b/libc/include/stdio.h @@ -0,0 +1,2 @@ +void print(char* s); +void input(char* buf); diff --git a/libc/stdio.c b/libc/stdio.c new file mode 100644 index 0000000..3feb9d8 --- /dev/null +++ b/libc/stdio.c @@ -0,0 +1,11 @@ +void print(char* s) +{ + asm("mov %0, %%edx" :: "r" (s)); + asm("int $0x21"); +} + +void input(char* buf) +{ + asm("mov %0, %%edx" :: "r" (buf)); + asm("int $0x22"); +}