├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── grub.cfg ├── linker.ld └── src ├── boot.asm ├── enable_paging.asm ├── gdt.c ├── gdt.h ├── idt.c ├── idt.h ├── interrupt.asm ├── isr.c ├── isr.h ├── load_gdt.asm ├── load_idt.asm ├── main.c ├── memory.c ├── memory.h ├── paging.c ├── paging.h ├── ports.c ├── ports.h ├── string.c ├── string.h ├── terminal.c ├── terminal.h ├── timer.c └── timer.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | os.iso 4 | iso/ 5 | *.DS_Store 6 | *.save 7 | serial.log 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 programmeruser2 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = i686-elf-gcc 2 | C_SRC = $(wildcard src/*.c) 3 | ASM = $(wildcard src/*.asm) 4 | OBJS = $(patsubst src/%.c,%.o,$(C_SRC)) $(patsubst src/%.asm,%.o,$(ASM)) 5 | .PHONY: all test 6 | all: os.iso clean 7 | test: os.iso clean 8 | qemu-system-i386 -cdrom os.iso 9 | os.iso: kernel.bin 10 | mkdir -p iso/boot/grub 11 | cp kernel.bin iso/boot/kernel.bin 12 | cp grub.cfg iso/boot/grub/grub.cfg 13 | grub-mkrescue -o os.iso iso 14 | kernel.bin: $(OBJS) 15 | i686-elf-gcc -T linker.ld -o $@ -ffreestanding -O2 -nostdlib $(OBJS) 16 | %.o: src/%.asm 17 | nasm -f elf32 $< -o $@ 18 | %.o: src/%.c 19 | $(CC) -c $^ -nostdlib -ffreestanding -O2 -lgcc -g # debug symbols 20 | clean: 21 | rm *.o 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > # NOTE: I am not actively developing this OS. 2 | > However, feel free to use anything from here if you want to. (I'm not sure if you want to use my horrible code from 2 years ago though) 3 | 4 | # os 5 | A simple OS 6 | -------------------------------------------------------------------------------- /grub.cfg: -------------------------------------------------------------------------------- 1 | menuentry "OS" { 2 | multiboot /boot/kernel.bin 3 | } 4 | -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | /* The bootloader will look at this image and start execution at the symbol 2 | designated as the entry point. */ 3 | ENTRY(_start) 4 | 5 | /* Tell where the various sections of the object files will be put in the final 6 | kernel image. */ 7 | SECTIONS 8 | { 9 | /* Begin putting sections at 1 MiB, a conventional place for kernels to be 10 | loaded at by the bootloader. */ 11 | . = 1M; 12 | 13 | /* First put the multiboot header, as it is required to be put very early 14 | early in the image or the bootloader won't recognize the file format. 15 | Next we'll put the .text section. */ 16 | .text BLOCK(4K) : ALIGN(4K) 17 | { 18 | *(.multiboot) 19 | *(.text) 20 | } 21 | 22 | /* Read-only data. */ 23 | .rodata BLOCK(4K) : ALIGN(4K) 24 | { 25 | *(.rodata) 26 | } 27 | 28 | /* Read-write data (initialized) */ 29 | .data BLOCK(4K) : ALIGN(4K) 30 | { 31 | *(.data) 32 | } 33 | 34 | /* Read-write data (uninitialized) and stack */ 35 | .bss BLOCK(4K) : ALIGN(4K) 36 | { 37 | *(COMMON) 38 | *(.bss) 39 | } 40 | 41 | /* The compiler may produce other sections, by default it will put them in 42 | a segment with the same name. Simply add stuff here as needed. */ 43 | } -------------------------------------------------------------------------------- /src/boot.asm: -------------------------------------------------------------------------------- 1 | MBALIGN equ 1<<0 2 | MEMINFO equ 1<<1 3 | FLAGS equ MBALIGN | MEMINFO 4 | MAGIC equ 0x1BADB002 5 | CHECKSUM equ -(MAGIC + FLAGS) 6 | 7 | section .multiboot 8 | align 4 9 | dd MAGIC 10 | dd FLAGS 11 | dd CHECKSUM 12 | section .bss 13 | align 16 14 | stack_bottom: 15 | resb 16386 16 | stack_top: 17 | 18 | section .text 19 | global _start:function (_start.end - _start) 20 | _start: 21 | mov esp, stack_top 22 | extern kernel_main 23 | call kernel_main 24 | cli 25 | .hang: 26 | hlt 27 | jmp .hang 28 | .end: 29 | -------------------------------------------------------------------------------- /src/enable_paging.asm: -------------------------------------------------------------------------------- 1 | global load_page_directory 2 | load_page_directory: 3 | push ebp 4 | mov ebp, esp 5 | mov eax, [esp + 8] 6 | mov cr3, eax 7 | mov esp, ebp 8 | pop ebp 9 | ret 10 | global enable_paging 11 | enable_paging: 12 | push ebp 13 | mov ebp, esp 14 | mov eax, cr0 15 | or eax, 0x80000000 16 | mov cr0, eax 17 | mov esp, ebp 18 | pop ebp 19 | ret 20 | -------------------------------------------------------------------------------- /src/gdt.c: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "terminal.h" 3 | #include 4 | extern void load_gdt(uint32_t gdt); 5 | struct gdt_entry_t gdt_entries[5]; 6 | struct gdt_ptr_t gdt_ptr; 7 | void create_gdt_entry(struct gdt_entry_t* gdt_entry_ptr, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) { 8 | gdt_entry_ptr->base_low = (base & 0xFFFF); 9 | gdt_entry_ptr->base_middle = (base >> 16) & 0xFF; 10 | gdt_entry_ptr->base_high = (base >> 24) & 0xFF; 11 | 12 | gdt_entry_ptr->limit_low = (limit & 0xFFFF); 13 | gdt_entry_ptr->granularity = (limit >> 16) & 0x0F; 14 | 15 | gdt_entry_ptr->granularity = granularity | 0xF0; 16 | gdt_entry_ptr->access = access; 17 | } 18 | void gdt_initialize(void) { 19 | gdt_ptr.limit = (sizeof(struct gdt_entry_t) * 5) - 1; 20 | gdt_ptr.base = (uint32_t) &gdt_entries; 21 | create_gdt_entry(&gdt_entries[0], 0, 0, 0, 0); // null segment 22 | create_gdt_entry(&gdt_entries[1], 0, 0xFFFFFFFF, 0x9A, 0xCF); // code segment 23 | create_gdt_entry(&gdt_entries[2], 0, 0xFFFFFFFF, 0x92, 0xCF); // data segment 24 | create_gdt_entry(&gdt_entries[3], 0, 0xFFFFFFFF, 0xFA, 0xCF ); // user mode code segment 25 | create_gdt_entry(&gdt_entries[4], 0, 0xFFFFFFFF, 0xF2, 0xCF); // user mode data segment 26 | load_gdt((uint32_t) &gdt_ptr); 27 | } 28 | -------------------------------------------------------------------------------- /src/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | #include 4 | struct gdt_entry_t { 5 | uint16_t limit_low; 6 | uint16_t base_low; 7 | uint8_t base_middle; 8 | uint8_t access; 9 | uint8_t granularity; 10 | uint8_t base_high; 11 | } __attribute__((packed)); 12 | struct gdt_ptr_t { 13 | uint16_t limit; 14 | uint32_t base; 15 | } __attribute__((packed)); 16 | void gdt_initialize(void); 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /src/idt.c: -------------------------------------------------------------------------------- 1 | #include "idt.h" 2 | #include "ports.h" 3 | #include 4 | 5 | #define PIC_MASTER_COMMAND_PORT 0x20 6 | #define PIC_MASTER_DATA_PORT 0x21 7 | #define PIC_SLAVE_COMMAND_PORT 0xA0 8 | #define PIC_SLAVE_DATA_PORT 0xA1 9 | 10 | struct idt_entry_t idt_entries[256] = { 0 }; //fill with zeroes 11 | struct idt_ptr_t idt_ptr; 12 | 13 | extern void load_idt(uint32_t idt); 14 | 15 | void create_idt_entry(struct idt_entry_t* idt_entry_ptr, uint32_t base, uint16_t selector, uint8_t flags) { 16 | idt_entry_ptr->base_low = base & 0xFFFF; 17 | idt_entry_ptr->base_high = (base >> 16) & 0xFFFF; 18 | idt_entry_ptr->selector = selector; 19 | idt_entry_ptr->always_zero = 0; 20 | //uncomment below when getting to user mode to set privilege level to 3 21 | idt_entry_ptr->flags = flags /* | 0x60 */; 22 | } 23 | void idt_initialize(void) { 24 | #define DEFINE_IDT_ENTRY(n) create_idt_entry(&idt_entries[n], (uint32_t) isr##n, 0x08, 0x8E) 25 | #define DEFINE_IRQ_ENTRY(n, i) create_idt_entry(&idt_entries[i], (uint32_t) irq##n, 0x08, 0x8E) 26 | idt_ptr.limit = sizeof(struct idt_entry_t) * 256 - 1; 27 | idt_ptr.base = (uint32_t) &idt_entries; 28 | extern void isr0(void); 29 | extern void isr1(void); 30 | extern void isr2(void); 31 | extern void isr3(void); 32 | extern void isr4(void); 33 | extern void isr5(void); 34 | extern void isr6(void); 35 | extern void isr7(void); 36 | extern void isr8(void); 37 | extern void isr9(void); 38 | extern void isr10(void); 39 | extern void isr11(void); 40 | extern void isr12(void); 41 | extern void isr13(void); 42 | extern void isr14(void); 43 | extern void isr15(void); 44 | extern void isr16(void); 45 | extern void isr17(void); 46 | extern void isr18(void); 47 | extern void isr19(void); 48 | extern void isr20(void); 49 | extern void isr21(void); 50 | extern void isr22(void); 51 | extern void isr23(void); 52 | extern void isr24(void); 53 | extern void isr25(void); 54 | extern void isr26(void); 55 | extern void isr27(void); 56 | extern void isr28(void); 57 | extern void isr29(void); 58 | extern void isr30(void); 59 | extern void isr31(void); 60 | 61 | extern void irq0(void); 62 | extern void irq1(void); 63 | extern void irq2(void); 64 | extern void irq3(void); 65 | extern void irq4(void); 66 | extern void irq5(void); 67 | extern void irq6(void); 68 | extern void irq7(void); 69 | extern void irq8(void); 70 | extern void irq9(void); 71 | extern void irq10(void); 72 | extern void irq11(void); 73 | extern void irq12(void); 74 | extern void irq13(void); 75 | extern void irq14(void); 76 | extern void irq15(void); 77 | 78 | //remapping the PIC 79 | outb(PIC_MASTER_COMMAND_PORT, 0x11); 80 | outb(PIC_SLAVE_COMMAND_PORT, 0x11); 81 | outb(PIC_MASTER_DATA_PORT, 0x20); 82 | outb(PIC_MASTER_DATA_PORT, 0x04); 83 | outb(PIC_MASTER_DATA_PORT, 0x01); 84 | outb(PIC_MASTER_DATA_PORT, 0x0); 85 | outb(PIC_SLAVE_DATA_PORT, 0x28); 86 | outb(PIC_SLAVE_DATA_PORT, 0x02); 87 | outb(PIC_SLAVE_DATA_PORT, 0x01); 88 | outb(PIC_SLAVE_DATA_PORT, 0x0); 89 | 90 | DEFINE_IDT_ENTRY(0); 91 | DEFINE_IDT_ENTRY(1); 92 | DEFINE_IDT_ENTRY(2); 93 | DEFINE_IDT_ENTRY(3); 94 | DEFINE_IDT_ENTRY(4); 95 | DEFINE_IDT_ENTRY(5); 96 | DEFINE_IDT_ENTRY(6); 97 | DEFINE_IDT_ENTRY(7); 98 | DEFINE_IDT_ENTRY(8); 99 | DEFINE_IDT_ENTRY(9); 100 | DEFINE_IDT_ENTRY(10); 101 | DEFINE_IDT_ENTRY(11); 102 | DEFINE_IDT_ENTRY(12); 103 | DEFINE_IDT_ENTRY(13); 104 | DEFINE_IDT_ENTRY(14); 105 | DEFINE_IDT_ENTRY(15); 106 | DEFINE_IDT_ENTRY(16); 107 | DEFINE_IDT_ENTRY(17); 108 | DEFINE_IDT_ENTRY(18); 109 | DEFINE_IDT_ENTRY(19); 110 | DEFINE_IDT_ENTRY(20); 111 | DEFINE_IDT_ENTRY(21); 112 | DEFINE_IDT_ENTRY(22); 113 | DEFINE_IDT_ENTRY(23); 114 | DEFINE_IDT_ENTRY(24); 115 | DEFINE_IDT_ENTRY(25); 116 | DEFINE_IDT_ENTRY(26); 117 | DEFINE_IDT_ENTRY(27); 118 | DEFINE_IDT_ENTRY(28); 119 | DEFINE_IDT_ENTRY(29); 120 | DEFINE_IDT_ENTRY(30); 121 | DEFINE_IDT_ENTRY(31); 122 | 123 | DEFINE_IRQ_ENTRY(0, 32); 124 | DEFINE_IRQ_ENTRY(1, 33); 125 | DEFINE_IRQ_ENTRY(2, 34); 126 | DEFINE_IRQ_ENTRY(3, 35); 127 | DEFINE_IRQ_ENTRY(4, 36); 128 | DEFINE_IRQ_ENTRY(5, 37); 129 | DEFINE_IRQ_ENTRY(6, 38); 130 | DEFINE_IRQ_ENTRY(7, 39); 131 | DEFINE_IRQ_ENTRY(8, 40); 132 | DEFINE_IRQ_ENTRY(9, 41); 133 | DEFINE_IRQ_ENTRY(10, 42); 134 | DEFINE_IRQ_ENTRY(11, 43); 135 | DEFINE_IRQ_ENTRY(12, 44); 136 | DEFINE_IRQ_ENTRY(13, 45); 137 | DEFINE_IRQ_ENTRY(14, 46); 138 | DEFINE_IRQ_ENTRY(15, 47); 139 | load_idt((uint32_t) &idt_ptr); 140 | 141 | #undef DEFINE_IDT_ENTRY 142 | #undef DEFINE_IRQ_ENTRY 143 | } 144 | 145 | -------------------------------------------------------------------------------- /src/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | #include 4 | struct idt_entry_t { 5 | uint16_t base_low; 6 | uint16_t selector; 7 | uint8_t always_zero; 8 | uint8_t flags; 9 | uint16_t base_high; 10 | } __attribute__((packed)); 11 | struct idt_ptr_t { 12 | uint16_t limit; 13 | uint32_t base; 14 | } __attribute__((packed)); 15 | void idt_initialize(void); 16 | #endif 17 | -------------------------------------------------------------------------------- /src/interrupt.asm: -------------------------------------------------------------------------------- 1 | %macro ISR_NOERRCODE 1 2 | global isr%1 3 | isr%1: 4 | cli 5 | push byte 0 6 | push byte %1 7 | jmp isr_common_stub 8 | %endmacro 9 | 10 | %macro ISR_ERRCODE 1 11 | global isr%1 12 | isr%1: 13 | cli 14 | push byte %1 15 | jmp isr_common_stub 16 | %endmacro 17 | 18 | ISR_NOERRCODE 0 19 | ISR_NOERRCODE 1 20 | ISR_NOERRCODE 2 21 | ISR_NOERRCODE 3 22 | ISR_NOERRCODE 4 23 | ISR_NOERRCODE 5 24 | ISR_NOERRCODE 6 25 | ISR_NOERRCODE 7 26 | ISR_ERRCODE 8 27 | ISR_NOERRCODE 9 28 | ISR_ERRCODE 10 29 | ISR_ERRCODE 11 30 | ISR_ERRCODE 12 31 | ISR_ERRCODE 13 32 | ISR_ERRCODE 14 33 | ISR_NOERRCODE 15 34 | ISR_NOERRCODE 16 35 | ISR_ERRCODE 17 36 | ISR_NOERRCODE 18 37 | ISR_NOERRCODE 19 38 | ISR_NOERRCODE 20 39 | ISR_ERRCODE 21 40 | ISR_NOERRCODE 22 41 | ISR_NOERRCODE 23 42 | ISR_NOERRCODE 24 43 | ISR_NOERRCODE 25 44 | ISR_NOERRCODE 26 45 | ISR_NOERRCODE 27 46 | ISR_NOERRCODE 28 47 | ISR_NOERRCODE 29 48 | ISR_NOERRCODE 30 49 | ISR_NOERRCODE 31 50 | 51 | extern isr_handler 52 | 53 | isr_common_stub: 54 | pusha 55 | mov ax, ds ;ax is lower 16 bits of eax 56 | push eax ; save data segment descriptor 57 | 58 | mov ax, 0x10 ; load kernel data segment descriptor 59 | mov ds, ax 60 | mov es, ax 61 | mov fs, ax 62 | mov gs, ax 63 | 64 | push esp ; esp is now a pointer the processor state struct, pass as pointer 65 | call isr_handler 66 | pop ebx ; remove saved esp 67 | 68 | pop eax; reload data segment descriptor 69 | mov ds, ax 70 | mov es, ax 71 | mov fs, ax 72 | mov gs, ax 73 | 74 | popa ; pop registers 75 | add esp, 8 ; clean up stack 76 | sti ; turn interrupts back on 77 | iret ; pops 5 things, remove the automatic processor pushed data 78 | 79 | %macro IRQ 2 80 | global irq%1 81 | irq%1: 82 | cli 83 | push byte 0 84 | push byte %2 85 | jmp irq_common_stub 86 | %endmacro 87 | 88 | IRQ 0, 32 89 | IRQ 1, 33 90 | IRQ 2, 34 91 | IRQ 3, 35 92 | IRQ 4, 36 93 | IRQ 5, 37 94 | IRQ 6, 38 95 | IRQ 7, 39 96 | IRQ 8, 40 97 | IRQ 9, 41 98 | IRQ 10, 42 99 | IRQ 11, 43 100 | IRQ 12, 44 101 | IRQ 13, 45 102 | IRQ 14, 46 103 | IRQ 15, 47 104 | 105 | extern irq_handler 106 | 107 | irq_common_stub: 108 | pusha 109 | 110 | ; save data segment descriptor 111 | mov ax, ds 112 | push eax 113 | 114 | mov ax, 0x10 115 | mov ds, ax 116 | mov es, ax 117 | mov fs, ax 118 | mov gs, ax 119 | 120 | ; esp is now a pointer to processor state, pass it as an argument 121 | push esp 122 | call irq_handler 123 | ; remove saved esp 124 | pop ebx 125 | 126 | ; reload original data segment descriptor 127 | pop ebx 128 | mov ds, bx 129 | mov es, bx 130 | mov fs, bx 131 | mov gs, bx 132 | 133 | popa ; pop registers 134 | add esp, 8 ; clean up stack 135 | sti ; turn interrupts back on 136 | iret ; return and pop processor pushed state 137 | -------------------------------------------------------------------------------- /src/isr.c: -------------------------------------------------------------------------------- 1 | #include "isr.h" 2 | #include "terminal.h" 3 | #include "ports.h" 4 | 5 | char* exception_messages[] = { 6 | "Division By Zero", 7 | "Debug", 8 | "Non Maskable Interrupt", 9 | "Breakpoint", 10 | "Into Detected Overflow", 11 | "Out of Bounds", 12 | "Invalid Opcode", 13 | "No Coprocessor", 14 | 15 | "Double Fault", 16 | "Coprocessor Segment Overrun", 17 | "Bad TSS", 18 | "Segment Not Present", 19 | "Stack Fault", 20 | "General Protection Fault", 21 | "Page Fault", 22 | "Unknown Interrupt", 23 | 24 | "Coprocessor Fault", 25 | "Alignment Check", 26 | "Machine Check", 27 | "Reserved", 28 | "Reserved", 29 | "Reserved", 30 | "Reserved", 31 | "Reserved", 32 | 33 | "Reserved", 34 | "Reserved", 35 | "Reserved", 36 | "Reserved", 37 | "Reserved", 38 | "Reserved", 39 | "Reserved", 40 | "Reserved" 41 | }; 42 | isr_t interrupt_handlers[256]; 43 | void isr_handler(struct registers_t* regs) { 44 | terminal_write_string("Recieved interrupt: "); 45 | terminal_write_int(regs->int_number); 46 | terminal_write_line(""); 47 | if (interrupt_handlers[regs->int_number] != NULL) { 48 | isr_t handler = interrupt_handlers[regs->int_number]; 49 | handler(regs); 50 | } else { 51 | terminal_write_line(exception_messages[regs->int_number]); 52 | terminal_write_line("Exception recieved. System halted."); 53 | //halt the processor 54 | for(;;) asm volatile("hlt"); 55 | } 56 | } 57 | 58 | void register_interrupt_handler(uint8_t n, isr_t handler) { 59 | interrupt_handlers[n] = handler; 60 | } 61 | 62 | void irq_handler(struct registers_t* regs) { 63 | //send end of interrupt (EOI) signal to PICs 64 | //if interrupt involved the slave 65 | if (regs->int_number >= 40) 66 | outb(PIC_SLAVE_COMMAND_PORT, 0x20); 67 | //send reset signal to master 68 | outb(PIC_MASTER_COMMAND_PORT, 0x20); 69 | if (interrupt_handlers[regs->int_number] != 0) { 70 | isr_t handler = interrupt_handlers[regs->int_number]; 71 | handler(regs); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/isr.h: -------------------------------------------------------------------------------- 1 | #ifndef ISR_H 2 | #define ISR_H 3 | #include 4 | 5 | #define IRQ0 32 6 | #define IRQ1 33 7 | #define IRQ2 34 8 | #define IRQ3 35 9 | #define IRQ4 36 10 | #define IRQ5 37 11 | #define IRQ6 38 12 | #define IRQ7 39 13 | #define IRQ8 40 14 | #define IRQ9 41 15 | #define IRQ10 42 16 | #define IRQ11 43 17 | #define IRQ12 44 18 | #define IRQ13 45 19 | #define IRQ14 46 20 | #define IRQ15 47 21 | 22 | #define PIC_MASTER_COMMAND_PORT 0x20 23 | #define PIC_MASTER_DATA_PORT 0x21 24 | #define PIC_SLAVE_COMMAND_PORT 0xA0 25 | #define PIC_SLAVE_DATA_PORT 0xA1 26 | 27 | struct registers_t { 28 | uint32_t ds; //data segment selector 29 | uint32_t edi, esi, ebp, useless_value, ebx, edx, ecx, eax; // registers pushed by pusha 30 | uint32_t int_number, err_code; //interrupt number and error code 31 | uint32_t eip, cs, eflags, esp, ss; //automatically pushed by processor 32 | }; 33 | typedef void (*isr_t)(struct registers_t*); 34 | void register_interrupt_handler(uint8_t n, isr_t handler); 35 | #endif 36 | -------------------------------------------------------------------------------- /src/load_gdt.asm: -------------------------------------------------------------------------------- 1 | global load_gdt 2 | load_gdt: 3 | mov eax, [esp + 4] ; get the pointer (the struct's size is 4) 4 | lgdt [eax] ; load the pointer 5 | 6 | ; new data segment offset 7 | mov ax, 0x10 8 | mov ds, ax 9 | mov es, ax 10 | mov fs, ax 11 | mov gs, ax 12 | mov ss, ax 13 | 14 | ; jump to flush the code segment register 15 | jmp 0x08:.flush 16 | .flush: 17 | ret 18 | -------------------------------------------------------------------------------- /src/load_idt.asm: -------------------------------------------------------------------------------- 1 | global load_idt 2 | load_idt: 3 | mov eax, [esp + 4] ; get pointer to idt 4 | lidt [eax] 5 | ret 6 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "terminal.h" 2 | #include "gdt.h" 3 | #include "idt.h" 4 | #include "timer.h" 5 | #include "paging.h" 6 | #include 7 | void timer_callback(size_t tick) { 8 | //terminal_write_int((int) tick); 9 | //terminal_write_line(""); 10 | } 11 | void kernel_main(void) { 12 | terminal_initialize(); 13 | gdt_initialize(); 14 | idt_initialize(); 15 | terminal_write_line("Hello World"); 16 | //asm volatile("int $0x3"); 17 | //asm volatile("int $0x4"); 18 | //enable interrupts 19 | asm volatile("sti"); 20 | //terminal_write_line("Starting timer..."); 21 | //timer_initialize(50, timer_callback); //initialize timer to 50 Hz 22 | paging_initialize(); 23 | terminal_write_line("Hello, paging world!"); 24 | for(;;) asm volatile("hlt"); 25 | } 26 | -------------------------------------------------------------------------------- /src/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | #include 3 | #include 4 | void memory_copy(char* source, char* dest, size_t nbytes) { 5 | for (int i = 0; i < nbytes; ++i) { 6 | *(dest + i) = *(source + i); 7 | } 8 | } 9 | void memory_set(uint8_t* dest, uint8_t value, size_t len) { 10 | uint8_t* ptr = (uint8_t*) dest; 11 | for(; len != 0; --len) *(ptr++) = value; 12 | } 13 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | #include 4 | #include 5 | void memory_copy(char* source, char* dest, size_t nbytes); 6 | void memory_set(uint8_t* dest, uint8_t value, size_t len); 7 | #endif 8 | -------------------------------------------------------------------------------- /src/paging.c: -------------------------------------------------------------------------------- 1 | #include "paging.h" 2 | #include "isr.h" 3 | #include "terminal.h" 4 | #include 5 | #include 6 | //TODO: write a page frame allocator 7 | extern void load_page_directory(uint32_t* directory); 8 | extern void enable_paging(void); 9 | uint32_t page_directory[1024] __attribute__((aligned(4096))); //define a page directory aligned at 4096 10 | uint32_t first_page_table[1024] __attribute__((aligned(4096))); 11 | void page_fault_handler(struct registers_t* regs) { 12 | uint32_t fault_address; 13 | __asm__("mov %%cr0, %0" : "=r" (fault_address)); 14 | int present = !(regs->err_code & 0x1); 15 | int rw = regs->err_code & 0x2; 16 | int user_mode = regs->err_code & 0x4; 17 | int reserved = regs->err_code & 0x8; 18 | int id = regs->err_code & 0x10; 19 | terminal_write_string("Page fault: "); 20 | if (present) terminal_write_string("present "); 21 | if (rw) terminal_write_string("read-only "); 22 | if (user_mode) terminal_write_string("user-mode "); 23 | if (reserved) terminal_write_string("reserved "); 24 | terminal_write_string("at "); 25 | terminal_write_int(fault_address); //TODO: print hex instead of decimal 26 | terminal_write_line(""); 27 | for(;;) __asm__("hlt"); 28 | } 29 | void paging_initialize(void) { 30 | for (size_t i = 0; i < 1024; ++i) { 31 | //flags: supervisor (kernel only), write enabled, not present 32 | page_directory[i] = 0x00000002; 33 | } 34 | for (uint32_t i = 0; i < 1024; ++i) { 35 | //attributes: supervisor level, read/write, present 36 | first_page_table[i] = (i * 0x1000) | 3; 37 | } 38 | //attributes: supervisor level, read/write, present 39 | page_directory[0] = ((uint32_t) first_page_table) | 3; 40 | load_page_directory(page_directory); 41 | enable_paging(); 42 | //register page fault handler 43 | register_interrupt_handler(14, page_fault_handler); 44 | } 45 | -------------------------------------------------------------------------------- /src/paging.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGING_H 2 | #define PAGING_H 3 | void paging_initialize(void); 4 | #endif 5 | -------------------------------------------------------------------------------- /src/ports.c: -------------------------------------------------------------------------------- 1 | #include "ports.h" 2 | #include 3 | uint8_t inb(uint16_t port) { 4 | uint8_t result; 5 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 6 | return result; 7 | } 8 | void outb(uint16_t port, uint8_t data) { 9 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 10 | } 11 | uint16_t inw(uint16_t port) { 12 | uint16_t result; 13 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 14 | return result; 15 | } 16 | void outw(uint16_t port, uint16_t data) { 17 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/ports.h: -------------------------------------------------------------------------------- 1 | #ifndef PORTS_H 2 | #define PORTS_H 3 | #include 4 | 5 | #define COM1 0x3F8 6 | #define COM2 0x2F8 7 | #define COM3 0x3E8 8 | #define COM4 0x2E8 9 | 10 | uint8_t inb(uint16_t port); 11 | void outb(uint16_t port, uint8_t data); 12 | uint16_t inw(uint16_t port); 13 | void outw(uint16_t port, uint16_t data); 14 | #endif 15 | -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include 3 | size_t strlen(char* str) { 4 | size_t len = 0; 5 | while (str[len]) ++len; 6 | return len; 7 | } 8 | char* strcat(char* str1, char* str2) { 9 | const size_t base = strlen(str1); 10 | for (int i = 0; i < strlen(str2); ++i) { 11 | str1[base + i] = str2[i]; 12 | } 13 | return str1; 14 | } 15 | char* string_reverse(char* str) { 16 | size_t len = strlen(str); 17 | for (int i = 0; i < len / 2; ++i) { 18 | char tmp = str[i]; 19 | str[i] = str[len - i - 1]; 20 | str[len - i - 1] = tmp; 21 | } 22 | return str; 23 | } 24 | char* itoa(int num, char* str) { 25 | //K&R implementation 26 | int i, sign; 27 | if ((sign = num) < 0) num = -num; //check sign and change if needed 28 | i = 0; 29 | do { 30 | str[i++] = num % 10 + '0'; 31 | } while ((num /= 10) > 0); //keep going while above zero 32 | if (sign < 0) str[i++] = '-'; //add sign 33 | str[i] = '\0'; //add null terminator 34 | string_reverse(str); //reverse string 35 | return str; 36 | } 37 | -------------------------------------------------------------------------------- /src/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | #include 4 | size_t strlen(char* str); 5 | char* strcat(char* str1, char* str2); 6 | char* string_reverse(char* str); 7 | char* itoa(int num, char* str); 8 | #endif 9 | -------------------------------------------------------------------------------- /src/terminal.c: -------------------------------------------------------------------------------- 1 | #include "terminal.h" 2 | #include "string.h" 3 | #include "ports.h" 4 | #include "memory.h" 5 | #include 6 | #include 7 | #define VGA_COMMAND_PORT 0x3d4 8 | #define VGA_DATA_PORT 0x3d5 9 | #define VGA_BUFFER_BASE 0xb8000 10 | static uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { 11 | return fg | bg << 4; 12 | } 13 | static uint16_t vga_entry(unsigned char uc, uint8_t color) { 14 | return (uint16_t) uc | (uint16_t) color << 8; 15 | } 16 | size_t terminal_row; 17 | size_t terminal_column; 18 | uint8_t terminal_color; 19 | uint16_t* terminal_buffer; 20 | size_t terminal_get_offset(void) { 21 | // request register is 0x3d4, output register is 0x3d5 22 | outb(VGA_COMMAND_PORT, 0x0E); 23 | size_t position = inb(VGA_DATA_PORT); 24 | position = position << 8; 25 | outb(VGA_COMMAND_PORT, 0x0F); 26 | position += inb(VGA_DATA_PORT); 27 | return position; 28 | } 29 | void terminal_set_cursor_pos(size_t x, size_t y) { 30 | uint16_t pos = y * VGA_WIDTH + x; 31 | outb(VGA_COMMAND_PORT, 0x0E); 32 | outb(VGA_DATA_PORT, (uint8_t) ((pos >> 8) & 0xFF)); 33 | outb(VGA_COMMAND_PORT, 0x0F); 34 | outb(VGA_DATA_PORT, (uint8_t) (pos & 0xFF)); 35 | } 36 | void terminal_initialize(void) { 37 | terminal_row = 0; 38 | terminal_column = 0; 39 | terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 40 | terminal_buffer = (uint16_t*) VGA_BUFFER_BASE; 41 | for (size_t y = 0; y < VGA_HEIGHT; ++y) { 42 | for (size_t x = 0; x < VGA_WIDTH; ++x) { 43 | const size_t index = y * VGA_WIDTH + x; 44 | terminal_buffer[index] = vga_entry(' ', terminal_color); 45 | } 46 | } 47 | terminal_set_cursor_pos(terminal_column, terminal_row); 48 | } 49 | void terminal_setcolor(uint8_t color) { 50 | terminal_color = color; 51 | } 52 | 53 | void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) { 54 | const size_t index = y * VGA_WIDTH + x; 55 | terminal_buffer[index] = vga_entry(c, color); 56 | } 57 | void terminal_scroll(void) { 58 | for (size_t y = 1; y < VGA_HEIGHT; ++y) { 59 | char* source = (char*) (y * VGA_WIDTH * 2 + VGA_BUFFER_BASE); 60 | char* dest = (char*) ((y - 1) * VGA_WIDTH * 2 + VGA_BUFFER_BASE); 61 | memory_copy(source, dest, VGA_WIDTH * 2); //remember, char is 8 bits not 16 bits 62 | } 63 | //blank last line 64 | for (size_t x = 0; x < VGA_WIDTH; ++x) 65 | terminal_putentryat(' ', terminal_color, x, VGA_HEIGHT - 1); 66 | terminal_row -= 1; 67 | terminal_set_cursor_pos(terminal_column, terminal_row); 68 | } 69 | void terminal_putchar(char c) { 70 | if (c == '\r') { 71 | terminal_column = 0; 72 | } else if (c == '\n') { 73 | terminal_row += 1; 74 | if (terminal_row == VGA_HEIGHT) { 75 | terminal_scroll(); 76 | } 77 | } else { 78 | terminal_putentryat(c, terminal_color, terminal_column, terminal_row); 79 | if (++terminal_column == VGA_WIDTH) { 80 | terminal_column = 0; 81 | if (++terminal_row == VGA_HEIGHT) { 82 | terminal_scroll(); 83 | } 84 | } 85 | } 86 | } 87 | void terminal_write(char* data, size_t size) { 88 | for (size_t i = 0; i < size; ++i) 89 | terminal_putchar(data[i]); 90 | terminal_set_cursor_pos(terminal_column, terminal_row); 91 | } 92 | void terminal_write_string(char* str) { 93 | terminal_write(str, strlen(str)); 94 | } 95 | void terminal_write_int(int num) { 96 | char buffer[10]; 97 | terminal_write_string(itoa(num, buffer)); 98 | } 99 | void terminal_write_line(char* str) { 100 | terminal_write_string(str); 101 | terminal_write_string("\r\n"); 102 | } 103 | 104 | -------------------------------------------------------------------------------- /src/terminal.h: -------------------------------------------------------------------------------- 1 | #ifndef VGA_H 2 | #define VGA_H 3 | #include 4 | #include 5 | #define VGA_WIDTH 80 6 | #define VGA_HEIGHT 25 7 | enum vga_color { 8 | VGA_COLOR_BLACK = 0, 9 | VGA_COLOR_BLUE = 1, 10 | VGA_COLOR_GREEN = 2, 11 | VGA_COLOR_CYAN = 3, 12 | VGA_COLOR_RED = 4, 13 | VGA_COLOR_MAGENTA = 5, 14 | VGA_COLOR_BROWN = 6, 15 | VGA_COLOR_LIGHT_GREY = 7, 16 | VGA_COLOR_DARK_GREY = 8, 17 | VGA_COLOR_LIGHT_BLUE = 9, 18 | VGA_COLOR_LIGHT_GREEN = 10, 19 | VGA_COLOR_LIGHT_CYAN = 11, 20 | VGA_COLOR_LIGHT_RED = 12, 21 | VGA_COLOR_LIGHT_MAGENTA = 13, 22 | VGA_COLOR_LIGHT_BROWN = 14, 23 | VGA_COLOR_WHITE = 15, 24 | }; 25 | static uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg); 26 | static uint16_t vga_entry(unsigned char uc, uint8_t color); 27 | size_t terminal_get_offset(void); 28 | void terminal_set_cursor_pos(size_t x, size_t y); 29 | void terminal_initialize(void); 30 | void terminal_setcolor(uint8_t color); 31 | void terminal_scroll(void); 32 | void terminal_putentry_at(char c, uint8_t color, size_t x, size_t y); 33 | void terminal_putchar(char c); 34 | void terminal_write(char* str, size_t size); 35 | void terminal_write_string(char* str); 36 | void terminal_write_int(int num); 37 | void terminal_write_line(char* str); 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include "isr.h" 3 | #include "ports.h" 4 | #include 5 | #include 6 | 7 | size_t timer_tick = 0; 8 | void (*timer_set_callback)(size_t); 9 | void timer_irq_callback(struct registers_t* regs) { 10 | ++timer_tick; 11 | timer_set_callback(timer_tick); 12 | } 13 | void timer_initialize(size_t frequency, void (*callback)(size_t)) { 14 | register_interrupt_handler(IRQ0, timer_irq_callback); 15 | 16 | //set callback 17 | timer_set_callback = callback; 18 | 19 | //sent value is the value that the input clock (1193180 Hz) by 20 | size_t divisor = 1193180 / frequency; 21 | 22 | //send command byte 23 | outb(0x43, 0x36); 24 | 25 | //divisor has to be sent bytewise 26 | outb(0x40, (uint8_t) (divisor & 0xFF)); 27 | outb(0x40, (uint8_t) ((divisor >> 8) & 0xFF)); 28 | } 29 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | #include 4 | #include 5 | void timer_initialize(size_t frequency, void (*callback)(size_t)); 6 | #endif 7 | --------------------------------------------------------------------------------