├── TODO.md ├── lib └── libgcc.a ├── include ├── isr.h ├── pit.h ├── i8259.h ├── string.h ├── print.h ├── kheap.h ├── kerror.h ├── driver.h ├── irq.h ├── assembly.h ├── vga.h ├── idt.h ├── list.h ├── memory.h ├── common.h ├── gdt.h └── boot │ └── multiboot.h ├── .gitignore ├── src ├── Makefile ├── isr.c ├── string.c ├── drivers │ ├── keyboard.c │ └── driver.c ├── assembly.c ├── list.c ├── irq.c ├── pit.c ├── gdt.c ├── i8259.c ├── idt.c ├── kernel.c ├── kheap.c ├── kerror.c ├── vga.c ├── memory.c └── print.c ├── run.sh ├── asm ├── gdt_x86.s ├── idt_x86.s └── loader.s ├── debug.sh ├── linker.ld ├── README.md └── Makefile /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ====== 3 | -------------------------------------------------------------------------------- /lib/libgcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grant-h/uOS/HEAD/lib/libgcc.a -------------------------------------------------------------------------------- /include/isr.h: -------------------------------------------------------------------------------- 1 | #ifndef ISR_H 2 | #define ISR_H 3 | 4 | void init_desc_tables(); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/pit.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint32 get_tick_count(); 4 | void init_pit(uint32 freq); 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary directories 2 | build/ 3 | boot/ 4 | 5 | # ELF map files 6 | *.map 7 | 8 | # Vim 9 | *.swo 10 | *.swp 11 | 12 | # GDB History 13 | .gdb_history 14 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile glue 2 | # TODO: how can I easily keep my phonys and rules synced with the main 3 | # Makefile? 4 | .PHONY: all clean run debug 5 | all clean run debug: 6 | $(MAKE) -C .. $@ 7 | -------------------------------------------------------------------------------- /include/i8259.h: -------------------------------------------------------------------------------- 1 | #ifndef I8259_H 2 | #define I8259_H 3 | 4 | void pic_send_eoi(unsigned char irq); 5 | void pic_init(); 6 | void pic_mask_irq(unsigned char irq); 7 | void pic_unmask_irq(unsigned char irq); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | QEMU=qemu-system-i386 3 | TARGET="boot/kernel.bin" 4 | 5 | # all commands now relative to script location 6 | cd $(dirname $0) 7 | 8 | # start qemu! 9 | ${QEMU} $@ -kernel $TARGET 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | void * memcpy(void * dst, void * src, unsigned int size); 5 | void * memset(void * ptr, int value, unsigned int size); 6 | int strlen(const char * string); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/print.h: -------------------------------------------------------------------------------- 1 | #ifndef PRINT_H 2 | #define PRINT_H 3 | 4 | #include 5 | 6 | int printf(const char * fmt, ...); 7 | int vprintf(const char * fmt, va_list args); 8 | 9 | int sprintf(char * buf, const char * fmt, ...); 10 | int vsprintf(char * buf, const char * fmt, va_list args); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/kheap.h: -------------------------------------------------------------------------------- 1 | #ifndef KHEAP_H 2 | #define KHEAP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | void kmalloc_early_init(uint32 base, uint32 max); 9 | int kmalloc_early_init_grub(struct multiboot_info * mbi); 10 | uint32 kmalloc_early(uint32 size); 11 | uint32 kmalloc_early_align(uint32 size); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/kerror.h: -------------------------------------------------------------------------------- 1 | #ifndef KERROR_H 2 | #define KERROR_H 3 | 4 | #include 5 | 6 | #define ASSERT(cond) \ 7 | if(!(cond)) { \ 8 | panic("Assertion failed in %s:%s() at line %d", __FILE__, __func__, __LINE__); \ 9 | } 10 | 11 | #define BREAKPOINT asm volatile("int $0x3") 12 | 13 | void panic(char * reason, ...); 14 | void handle_exception(struct registers * reg); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/driver.h: -------------------------------------------------------------------------------- 1 | #ifndef DRIVER_H 2 | #define DRIVER_H 3 | 4 | #include 5 | 6 | typedef int (*driver_init_t)(); 7 | typedef int (*driver_fini_t)(); 8 | 9 | struct driver_interface_table { 10 | driver_init_t initialize; 11 | driver_fini_t finalize; 12 | }; 13 | 14 | struct driver_interface { 15 | const char * name; 16 | struct driver_interface_table functions; 17 | }; 18 | 19 | void register_driver(struct driver_interface * drv); 20 | void unregister_driver(struct driver_interface * drv); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/irq.h: -------------------------------------------------------------------------------- 1 | #ifndef IRQ_H 2 | #define IRQ_H 3 | 4 | #include 5 | #include 6 | 7 | #define IRQ_BASE 32 8 | #define IRQ_AMOUNT 16 9 | 10 | enum irq_vectors 11 | { 12 | IRQ0 = 0, 13 | IRQ1, IRQ2, IRQ3, IRQ4, 14 | IRQ5, IRQ6, IRQ7, IRQ8, 15 | IRQ9, IRQ10, IRQ11,IRQ12, 16 | IRQ13, IRQ14, IRQ15 17 | }; 18 | 19 | void irq_handler(struct registers * regs); 20 | void register_irq_handler(unsigned int irq, void (*handler)(struct registers *)); 21 | void unregister_irq_handler(unsigned int irq); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /asm/gdt_x86.s: -------------------------------------------------------------------------------- 1 | [GLOBAL gdt_flush] ; Allows the C code to call gdt_flush(). 2 | 3 | gdt_flush: 4 | mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. 5 | lgdt [eax] ; Load the new GDT pointer 6 | 7 | mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment 8 | mov ds, ax ; Load all data segment selectors 9 | mov es, ax 10 | mov fs, ax 11 | mov gs, ax 12 | mov ss, ax 13 | jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump! 14 | .flush: 15 | ret 16 | -------------------------------------------------------------------------------- /src/isr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | void isr_handler(struct registers * regs) 10 | { 11 | int int_no = regs->int_no; 12 | 13 | if(int_no <= 19) //an exception has occured, panic! 14 | handle_exception(regs); 15 | else if(int_no >= IRQ_BASE) 16 | irq_handler(regs); 17 | else 18 | printf("[INFO] Unhandled interrupt %d\n", int_no); 19 | } 20 | 21 | void init_desc_tables() 22 | { 23 | init_gdt(); 24 | init_idt(); 25 | } 26 | -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | QEMUPID="/tmp/uosqemu.pid" 3 | TARGET="boot/kernel.bin" 4 | 5 | exithook() { 6 | # kill qemu once GDB exits 7 | kill `cat $QEMUPID` > /dev/null 2>&1 8 | } 9 | 10 | # all commands now relative to script location 11 | cd $(dirname $0) 12 | 13 | # start qemu in the background with the CPU halted and waiting for GDB on TCP 1234 14 | qemu-system-i386 $@ -daemonize -pidfile $QEMUPID -s -S -kernel $TARGET 15 | trap exithook EXIT 16 | 17 | # run GDB and attach to the qemu instance 18 | gdb $TARGET -nx -ex 'target remote 127.0.0.1:1234' 19 | 20 | 21 | exit 0 22 | -------------------------------------------------------------------------------- /src/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void * memcpy(void * dst, void * src, unsigned int size) 4 | { 5 | unsigned int i; 6 | 7 | for(i = 0; i < size; i++) 8 | { 9 | *((unsigned char *)dst+i) = *((unsigned char *)src+i); 10 | } 11 | 12 | return dst; 13 | } 14 | 15 | void * memset(void * ptr, int value, unsigned int size) 16 | { 17 | unsigned int i; 18 | for(i = 0; i < size; i++) 19 | *((unsigned char *)ptr+i) = (unsigned char)value; 20 | 21 | return ptr; 22 | } 23 | 24 | int strlen(const char * string) 25 | { 26 | int len = 0; 27 | while(*string++) 28 | len++; 29 | 30 | return len; 31 | } 32 | -------------------------------------------------------------------------------- /src/drivers/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static void keyboard_irq(struct registers * regs); 6 | 7 | int keyboard_initialize() 8 | { 9 | printf("Keyboard initialized\n"); 10 | register_irq_handler(IRQ1, keyboard_irq); 11 | return 0; 12 | } 13 | 14 | static void keyboard_irq(struct registers * regs) 15 | { 16 | printf("IRQ1\n"); 17 | } 18 | 19 | int keyboard_finalize() 20 | { 21 | printf("Keyboard unloading\n"); 22 | return 0; 23 | } 24 | 25 | struct driver_interface keyboard_driver = { 26 | .name = "keyboard", 27 | .functions = { 28 | .initialize = keyboard_initialize, 29 | .finalize = keyboard_finalize 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/assembly.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void outb(unsigned short port, unsigned char value) 4 | { 5 | asm volatile ("outb %1, %0" : : "dN" (port), "a" (value)); 6 | } 7 | 8 | unsigned char inb(unsigned short port) 9 | { 10 | unsigned char ret; 11 | asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port)); 12 | return ret; 13 | } 14 | 15 | unsigned short inw(unsigned short port) 16 | { 17 | unsigned short ret; 18 | asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port)); 19 | return ret; 20 | } 21 | 22 | void disable_interupts() 23 | { 24 | asm volatile("cli"); 25 | } 26 | 27 | void enable_interupts() 28 | { 29 | asm volatile("sti"); 30 | } 31 | 32 | void halt() 33 | { 34 | asm volatile("hlt"); 35 | } 36 | -------------------------------------------------------------------------------- /src/drivers/driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct loaded_driver { 6 | struct driver_interface * drv; 7 | struct ll_node list; 8 | }; 9 | 10 | static LIST_DEFINE(loaded_drivers); 11 | 12 | void register_driver(struct driver_interface * drv) 13 | { 14 | printf("%s: loading\n", drv->name); 15 | drv->functions.initialize(); 16 | 17 | /*struct loaded_driver * driverRecord = kmalloc(sizeof(struct loaded_driver)); 18 | 19 | driverRecord->drv = drv; 20 | list_append(&driverRecord->list, &loaded_drivers);*/ 21 | } 22 | 23 | void unregister_driver(struct driver_interface * drv) 24 | { 25 | printf("%s: unloading\n", drv->name); 26 | drv->functions.finalize(); 27 | } 28 | -------------------------------------------------------------------------------- /include/assembly.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSEMBLY_H 2 | #define ASSEMBLY_H 3 | 4 | struct registers 5 | { 6 | unsigned int ds; // Data segment selector 7 | unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha. 8 | unsigned int int_no, err_code; // Interrupt number and error code (if applicable) 9 | unsigned int eip, cs, eflags, useresp, ss; // Pushed by the processor automatically. 10 | }; 11 | 12 | #define FORCEINLINE __attribute__((always_inline)) 13 | 14 | void outb(unsigned short port, unsigned char value); 15 | unsigned char inb(unsigned short port); 16 | unsigned short inw(unsigned short port); 17 | void disable_interupts(); 18 | void enable_interupts(); 19 | void halt(); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void list_append(struct ll_node * entry, struct ll_node * head) 4 | { 5 | // Previous of HEAD is the last node 6 | struct ll_node * last = head->prev; 7 | 8 | entry->next = head; 9 | entry->prev = last; 10 | last->next = entry; 11 | head->prev = entry; 12 | } 13 | 14 | void list_insert(struct ll_node * entry, struct ll_node * head) 15 | { 16 | entry->next = head->next; 17 | entry->prev = head; 18 | head->next->prev = entry; 19 | head->next = entry; 20 | } 21 | 22 | void list_delete(struct ll_node * entry) 23 | { 24 | struct ll_node * left = entry->prev, *right = entry->next; 25 | 26 | left->next = right; 27 | right->prev = left; 28 | 29 | entry->next = LIST_POISON; 30 | entry->prev = LIST_POISON; 31 | } 32 | -------------------------------------------------------------------------------- /include/vga.h: -------------------------------------------------------------------------------- 1 | #ifndef VGA_H 2 | #define VGA_H 3 | 4 | enum vga_color 5 | { 6 | COLOR_BLACK = 0, 7 | COLOR_BLUE, 8 | COLOR_GREEN, 9 | COLOR_CYAN, 10 | COLOR_RED, 11 | COLOR_MAGENTA, 12 | COLOR_BROWN, 13 | COLOR_LGREY, 14 | COLOR_DGREY, 15 | COLOR_LBLUE, 16 | COLOR_LGREEN, 17 | COLOR_LCYAN, 18 | COLOR_LRED, 19 | COLOR_LMAGENTA, 20 | COLOR_YELLOW, 21 | COLOR_WHITE, 22 | COLOR_DONTCARE 23 | }; 24 | typedef enum vga_color vga_color_t; 25 | 26 | //TODO-WANT: add an attribute system where old colors can be pushed and poped 27 | //just like MS Visual Studio's #pragma push/pop 28 | 29 | void vga_init(); 30 | void vga_set_color(vga_color_t fg, vga_color_t bg); 31 | void vga_get_color(vga_color_t * fg, vga_color_t * bg); 32 | void vga_kputc(unsigned char c); 33 | void random_screen(); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | void init_idt(); 5 | 6 | // A struct describing an interrupt gate. 7 | struct idt_entry_struct 8 | { 9 | unsigned short base_lo; // The lower 16 bits of the address to jump to when this interrupt fires. 10 | unsigned short sel; // Kernel segment selector. 11 | unsigned char always0; // This must always be zero. 12 | unsigned char flags; // More flags. See documentation. 13 | unsigned short base_hi; // The upper 16 bits of the address to jump to. 14 | } __attribute__((packed)); 15 | typedef struct idt_entry_struct idt_entry_t; 16 | 17 | // A struct describing a pointer to an array of interrupt handlers. 18 | // This is in a format suitable for giving to 'lidt'. 19 | struct idt_ptr_struct 20 | { 21 | unsigned short limit; 22 | unsigned int base; // The address of the first element in our idt_entry_t array. 23 | } __attribute__((packed)); 24 | typedef struct idt_ptr_struct idt_ptr_t; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/irq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void * irq_handlers[IRQ_AMOUNT] = {NULL}; 7 | 8 | // This gets called from our ASM interrupt handler stub. 9 | void irq_handler(struct registers * regs) 10 | { 11 | ASSERT(regs->int_no >= IRQ_BASE); 12 | 13 | //IRQ_BASE is the start vector of the IRQs 14 | unsigned char irq_num = regs->int_no - IRQ_BASE; 15 | 16 | void (* handler)(struct registers *) = irq_handlers[irq_num]; 17 | 18 | if(handler) 19 | handler(regs); 20 | else 21 | printf("[INFO] Unhandled IRQ %d\n", irq_num); 22 | 23 | pic_send_eoi(irq_num); 24 | } 25 | 26 | void register_irq_handler(unsigned int irq, void (*handler)(struct registers *)) 27 | { 28 | ASSERT(irq < IRQ_AMOUNT); 29 | 30 | printf("IRQ%d: registered\n", irq); 31 | 32 | irq_handlers[irq] = handler; 33 | } 34 | 35 | void unregister_irq_handler(unsigned int irq) 36 | { 37 | ASSERT(irq < IRQ_AMOUNT); 38 | ASSERT(irq_handlers[irq] != NULL); //prevent strange bugs 39 | 40 | irq_handlers[irq] = NULL; 41 | } 42 | -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | 4 | #include 5 | 6 | /* A simple doubly-linked list implementation 7 | * Inspired by generic list usage in the Linux kernel 8 | * 9 | * Instead of having the list node containing the data, 10 | * we merely attach a linkage structure to a user defined 11 | * struct. Allows for a single list implementation while still 12 | * being expressive enough for any list node data. 13 | */ 14 | 15 | #define LIST_POISON ((struct ll_node *)0xdead0000) 16 | 17 | struct ll_node { 18 | struct ll_node * next; 19 | struct ll_node * prev; 20 | }; 21 | 22 | // This is a nice trick to initialize the list circularly to itself 23 | // i.e. struct ll_node list = { &list, &list } 24 | #define LIST_INIT(name) { &(name), &(name) } 25 | #define LIST_DEFINE(name) struct ll_node name = LIST_INIT(name) 26 | 27 | #define list_entry(ptr, type, member) \ 28 | container_of(ptr, type, member) 29 | 30 | #define list_for_each(pos, head) \ 31 | for(pos = (head)->next; pos != head; pos = pos->next) 32 | 33 | #define list_for_each_safe(pos, nxt, head) \ 34 | for(pos = (head)->next, nxt = pos->next; pos != head; pos = nxt, nxt = pos->next) 35 | 36 | void list_append(struct ll_node * entry, struct ll_node * head); 37 | void list_insert(struct ll_node * entry, struct ll_node * head); 38 | void list_delete(struct ll_node * entry); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | 4 | // helper constants 5 | #define PAGE_SIZE 0x1000 6 | #define PAGE_ALIGN 0xfffff000 7 | #define NOT_ALIGNED ~(PAGE_ALIGN) 8 | #define PAGE_ALIGN_UP(addr) (((addr) & NOT_ALIGNED) ? (((addr) & PAGE_ALIGN) + PAGE_SIZE) \ 9 | : ((addr))) 10 | // 1024 DWORD entries per PD and PT 11 | #define PAGE_ENTRIES 1024 12 | #define PAGE_TABLE_SIZE (sizeof(uint32)*PAGE_ENTRIES) 13 | 14 | // SI units to number of pages 15 | #define PAGES_PER_KB(kb) (PAGE_ALIGN_UP((kb)*1024) / PAGE_SIZE) 16 | #define PAGES_PER_MB(mb) (PAGE_ALIGN_UP((mb)*1024*1024) / PAGE_SIZE) 17 | #define PAGES_PER_GB(gb) (PAGE_ALIGN_UP((gb)*1024*1024*1024) / PAGE_SIZE) 18 | 19 | // page flags 20 | #define PAGE_ENTRY_PRESENT 0x1 21 | #define PAGE_ENTRY_RW 0x2 22 | #define PAGE_ENTRY_ACCESS 0x20 23 | 24 | // Information about the Kernel from the linker 25 | extern uint32 _KERNEL_START; 26 | extern uint32 _KERNEL_END; 27 | extern uint32 _EARLY_KMALLOC_START; 28 | extern uint32 _EARLY_KMALLOC_END; 29 | 30 | // Simplified storage varables (see memory.c) 31 | extern uint32 KERNEL_START; 32 | extern uint32 KERNEL_END; 33 | extern uint32 EARLY_KMALLOC_START; 34 | extern uint32 EARLY_KMALLOC_END; 35 | 36 | void paging_init(); 37 | uint32 page_ident_map(uint32 addr, uint32 perm); 38 | uint32 page_map(uint32 virt, uint32 phys, uint32 perm); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/pit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | uint64 tick_count = 0; 9 | uint32 tick_hz = 0; 10 | 11 | void timer_tick(struct registers * reg) 12 | { 13 | tick_count++; 14 | 15 | if(tick_count % tick_hz == 0) 16 | { 17 | int secondsElapsed = tick_count / tick_hz; 18 | 19 | if(secondsElapsed % 10 == 0) 20 | printf("%d", secondsElapsed); 21 | else 22 | printf("."); 23 | 24 | if(secondsElapsed == 20) 25 | { 26 | //int die = 10 / 0; 27 | /*uint32 * pageFault = (uint32 *)0xc0000000; 28 | *pageFault = 0xdeadbeefU; 29 | uint32 heh = *pageFault; 30 | 31 | printf("%p\n", heh);*/ 32 | 33 | //BREAKPOINT; 34 | } 35 | else if(secondsElapsed == 7) 36 | { 37 | //printf("%p", &secondsElapsed); 38 | } 39 | } 40 | } 41 | 42 | uint32 get_tick_count() 43 | { 44 | return tick_count; 45 | } 46 | 47 | void init_pit(uint32 freq) 48 | { 49 | uint32 divisor = 1193180 / freq; 50 | 51 | tick_hz = freq; 52 | 53 | // Send the command byte. 54 | outb(0x43, 0x36); 55 | 56 | // Divisor has to be sent byte-wise, so split here into upper/lower bytes. 57 | uint8 l = (uint8)(divisor & 0xFF); 58 | uint8 h = (uint8)( (divisor>>8) & 0xFF ); 59 | 60 | // Send the frequency divisor. 61 | outb(0x40, l); 62 | outb(0x40, h); 63 | 64 | register_irq_handler(IRQ0, timer_tick); 65 | } 66 | 67 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #ifndef NULL 5 | #define NULL 0 6 | #endif 7 | #define FALSE 0 8 | #define TRUE 1 9 | 10 | /* 11 | * Minimal vararg (stdarg.h) support. 12 | * Copied from M$ support code 13 | */ 14 | 15 | typedef char * va_list; 16 | #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 17 | #define va_start(ap,v) (ap = (va_list)&v + _INTSIZEOF(v)) 18 | #define va_arg(ap,t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) 19 | #define va_end(ap) (ap = (va_list)0) 20 | 21 | /* 22 | * Bitwise Helpers 23 | */ 24 | 25 | #define ISSET(field, var) ((var) & (field)) 26 | #define ISSET_BIT(bit, var) ((var) & (1 << (bit))) 27 | 28 | /* 29 | * Minimal stdint.h for "portable" ints 30 | */ 31 | 32 | typedef unsigned long long uint64; 33 | typedef long long int64; 34 | typedef unsigned int uint32; 35 | typedef int int32; 36 | typedef unsigned short uint16; 37 | typedef short int16; 38 | typedef unsigned char uint8; 39 | typedef char int8; 40 | typedef uint32 size_t; 41 | 42 | // Calculate the offset in bytes of a struct member 43 | #define offsetof(type, member) ((size_t) &((type *)0)->member) 44 | 45 | // Retrieves the container object from a pointer to a struct member 46 | // __memb is not strictly required, but it allows the compiler to typecheck 47 | // It must be converted to a character pointer as offsetof() is in bytes 48 | #define container_of(ptr, type, member) ({ \ 49 | const typeof(((type *)0)->member) * __memb = (ptr); \ 50 | (type *)((char *)__memb - offsetof(type, member)); }) 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/gdt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Lets us access our ASM functions from our C code. 6 | extern void gdt_flush(uint32 ptr); 7 | static void gdt_set_gate(uint32 num, uint32 base, uint32 limit, uint16 flags); 8 | 9 | gdt_entry_t gdt_entries[5]; 10 | gdt_ptr_t gdt_ptr; 11 | 12 | void init_gdt() 13 | { 14 | gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1; 15 | gdt_ptr.base = (uint32)&gdt_entries; 16 | 17 | gdt_set_gate(0, 0, 0, 0); // Null segment 18 | gdt_set_gate(1, 0, 0x000FFFFF, GDT_CODE_PL0); // Kernel code segment 19 | gdt_set_gate(2, 0, 0x000FFFFF, GDT_DATA_PL0); // Kernel data segment 20 | gdt_set_gate(3, 0, 0x000FFFFF, GDT_CODE_PL3); // User mode code segment 21 | gdt_set_gate(4, 0, 0x000FFFFF, GDT_DATA_PL3); // User mode data segment 22 | 23 | gdt_flush((uint32)&gdt_ptr); 24 | } 25 | 26 | // Set the value of one GDT entry. 27 | // FROM_BIT_VAR:TO_BIT 28 | static void gdt_set_gate(uint32 num, uint32 base, uint32 limit, uint16 flags) 29 | { 30 | uint64 descriptor = 0; 31 | 32 | //start with the top 33 | descriptor = base & 0xFF000000; //base direct map 34 | descriptor |= (base >> 16) & 0x000000FF; //base 23-16:7-0 35 | descriptor |= (flags << 8) & 0x00F0FF00; //flags 16-11:24-19 7-0:15-8 36 | descriptor |= limit & 0x000F0000; //limit direct map 37 | 38 | //end with the bottom 39 | descriptor <<= 32; 40 | 41 | descriptor |= (base << 16) & 0xFFFF0000; //base 15-0:31-16 42 | descriptor |= limit & 0x0000FFFF; //limit direct map 43 | 44 | //gdt_entries[num].limit_low = limit & 0x0000FFFF; 45 | //gdt_entries[num].base_low = base & 0x0000FFFF; 46 | //gdt_entries[num].base_middle = base & 0x00FF0000; 47 | 48 | memcpy(&gdt_entries[num], &descriptor, sizeof(uint64)); 49 | } 50 | -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY (_start) 2 | 3 | SECTIONS 4 | { 5 | /* Base address starting at 1MB */ 6 | . = 0x00100000; 7 | _EARLY_KERNEL_START = .; 8 | 9 | /* Special multiboot compliant header. 10 | * Used by GRUB for image detection. 11 | */ 12 | .__mbHeader : { *(.__mbHeader) } 13 | .early_text ALIGN (0x1000) : { *(.early_text) } 14 | .early_data ALIGN (0x1000) : { *(.early_data) } 15 | .early_bss ALIGN (0x1000) : 16 | { 17 | _EARLY_BSS_START = .; 18 | *(.early_bss) 19 | _EARLY_BSS_END = .; 20 | } 21 | 22 | _EARLY_KERNEL_END = .; 23 | 24 | /* Higher half (3 GB) */ 25 | . += 0xC0000000; 26 | 27 | /* Let the source know about the kernel start */ 28 | _KERNEL_START = .; 29 | 30 | /* make sure the sections are page aligned */ 31 | .text ALIGN (0x1000) : AT(ADDR(.text) - 0xC0000000) { *(.text) } 32 | .rodata ALIGN (0x1000) : AT(ADDR(.rodata) - 0xC0000000) { *(.rodata*) } 33 | .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) { *(.data) } 34 | .bss ALIGN (0x1000) : AT(ADDR(.bss) - 0xC0000000) 35 | { 36 | _BSS_START = .; 37 | *(.bss) 38 | *(COMMON) 39 | _BSS_END = .; 40 | } 41 | .early_kmalloc ALIGN (0x1000) : AT(ADDR(.early_kmalloc) - 0xC0000000) 42 | { 43 | _EARLY_KMALLOC_START = .; 44 | . += 0x100000; /* 1MB of heap space */ 45 | _EARLY_KMALLOC_END = .; 46 | } 47 | 48 | /* Let the source know about the kernel end */ 49 | _KERNEL_END = .; 50 | _EARLY_BSS_SIZE = _EARLY_BSS_END - _EARLY_BSS_START; 51 | _BSS_SIZE = _BSS_END - _BSS_START; 52 | _KERNEL_SIZE = _KERNEL_END - _KERNEL_START; 53 | } 54 | 55 | ASSERT(_KERNEL_SIZE < 0x300000, "Kernel exceeds the 3 MB limit. asm/loader.s must be updated to work with a >3MB kernel"); 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # µOS (Micro OS or uOS) 2 | An in progress, learning kernel for x86 created by Grant Hernandez. 3 | 4 | This project started as a hobby in early 2012 in an effort to learn more about the x86 architecture and kernels in general. All of my knowledge on this subject has been from the OSDev wiki and the Intel reference manuals. All code included is my own, including the lower-level library functions, such as `printf()`. 5 | 6 | Here is a snapshot from the beginning of 2013 of µOS running on my bare-metal Thinkpad T420. 7 | 8 | 9 | 10 | While the kernel currently just a toy, it serves as my testbed for kernel programming and fundamental Operating System concepts. 11 | 12 | ## Building and Environment Setup 13 | µOS currently only supports the 32-bit x86 architecture, but there are future plans to support x86\_64 and ARM. Testing is done using QEMU for x86 system emulation, but the kernel should be able to run on bare metal at *any* time. 14 | 15 | The build tools required are GNU Make, NASM, and GCC. 16 | I opted to use NASM instead of GNU AS as I prefer its syntax. This may change in the future as AS has better support within the GCC toolchain. 17 | 18 | * **Distribution:** Ubuntu 16.04.3 LTS 19 | * **Operating System:** Linux 4.10.0-33 20 | * **Compiler:** GCC 5.4.0 (clang NOT supported) 21 | * **Emulator:** QEMU emulator version 2.5.0 22 | 23 | ``` 24 | ~/code/uOS $ make 25 | mkdir -p build boot 26 | gcc -m32 -I/home/digital/code/uOS/include/ -ggdb -Wall -Wextra -nostdlib -nostdinc -fno-builtin -nostartfiles -nodefaultlibs -c src/kernel.c -o build/kernel.o 27 | ... 28 | nasm -f elf asm/gdt_x86.s -o build/gdt_x86.o 29 | nasm -f elf asm/idt_x86.s -o build/idt_x86.o 30 | ld -m elf_i386 -g -T linker.ld ... -o boot/kernel.bin -lgcc 31 | ``` 32 | 33 | Once the kernel has built succesfully it will output an ELF executable which can be loaded 34 | into QEMU for testing: 35 | ``` 36 | ~/code/uOS $ ./run.sh 37 | ``` 38 | 39 | ## Current Features 40 | * Multiboot compliant 41 | * Descriptor table setup (GDT and IDT) 42 | * Interrupt and exception handling via PIC 43 | * Very basic IRQ framework (callbacks) 44 | * Basic VGA driver (characters only) 45 | * Timing using the Programmable Interval Timer (PIT) 46 | * Paging (virtual memory) 47 | * Higher-half kernel (kernel base `0xC0000000`) 48 | * Early kmalloc (temporary bootstrap heap) 49 | 50 | ## Features Needed (Next in the pipeline) 51 | * Physical and virtual memory managers (page allocators) 52 | * Task Scheduling and tasking framework 53 | * Atomic operations for mutual exclusion 54 | * User-mode (ring 3 transition) 55 | 56 | ## Long Term Features Wanted 57 | * Rudimentary driver framework 58 | * Keyboard and mouse support 59 | * More hardware detection 60 | * Virtual File System (VFS) design 61 | * Multiplatform support (x86\_64, ARM) 62 | -------------------------------------------------------------------------------- /src/i8259.c: -------------------------------------------------------------------------------- 1 | /* i8259.c 2 | * Control code for the interrupt controller 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* PIC0: master, PIC1: slave */ 11 | #define PIC_MASTER_CMD 0x20 12 | #define PIC_MASTER_DATA 0x21 13 | #define PIC_SLAVE_CMD 0xA0 14 | #define PIC_SLAVE_DATA 0xA1 15 | 16 | #define PIC_EOI 0x20 17 | 18 | #define ICW1_ICW4 0x01 /* ICW4 (not) needed */ 19 | #define ICW1_SINGLE 0x02 /* Single (cascade) mode */ 20 | #define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ 21 | #define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ 22 | #define ICW1_INIT 0x10 /* Initialization - required! */ 23 | 24 | #define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ 25 | #define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 26 | #define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ 27 | #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ 28 | #define ICW4_SFNM 0x10 /* Special fully nested (not) */ 29 | 30 | void pic_send_eoi(unsigned char irq) 31 | { 32 | if(irq >= 8) 33 | outb(PIC_SLAVE_CMD, PIC_EOI); 34 | 35 | outb(PIC_MASTER_CMD, PIC_EOI); 36 | } 37 | 38 | void pic_init() 39 | { 40 | unsigned char a1, a2; 41 | 42 | //a1 = inb(PIC_MASTER_DATA); // save masks 43 | //a2 = inb(PIC_SLAVE_DATA); 44 | 45 | outb(PIC_MASTER_CMD, ICW1_INIT+ICW1_ICW4); // starts the initialization sequence (in cascade mode) 46 | outb(PIC_SLAVE_CMD, ICW1_INIT+ICW1_ICW4); 47 | outb(PIC_MASTER_DATA, 0x20); // ICW2: Master PIC vector offset 48 | outb(PIC_SLAVE_DATA, 0x28); // ICW2: Slave PIC vector offset 49 | outb(PIC_MASTER_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) 50 | outb(PIC_SLAVE_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) 51 | 52 | outb(PIC_MASTER_DATA, ICW4_8086); // ICW4: 8086 extra hint 53 | outb(PIC_SLAVE_DATA, ICW4_8086); 54 | 55 | outb(PIC_MASTER_DATA, 0); 56 | outb(PIC_SLAVE_DATA, 0); 57 | } 58 | 59 | void pic_mask_irq(unsigned char irq) 60 | { 61 | ASSERT(irq <= 15); 62 | 63 | unsigned char mask; 64 | unsigned char port; 65 | 66 | if(irq > 7) 67 | { 68 | port = PIC_SLAVE_DATA; 69 | irq -= 8; 70 | } 71 | else 72 | { 73 | port = PIC_MASTER_DATA; 74 | } 75 | 76 | mask = inb(port); 77 | mask |= 1 << irq; 78 | 79 | outb(port, mask); 80 | } 81 | 82 | void pic_unmask_irq(unsigned char irq) 83 | { 84 | ASSERT(irq <= 15); 85 | 86 | unsigned char mask; 87 | unsigned char port; 88 | 89 | if(irq > 7) 90 | { 91 | port = PIC_SLAVE_DATA; 92 | irq -= 8; 93 | } 94 | else 95 | { 96 | port = PIC_MASTER_DATA; 97 | } 98 | 99 | mask = inb(port); 100 | 101 | mask &= ~(1 << irq); 102 | 103 | outb(port, mask); 104 | } 105 | -------------------------------------------------------------------------------- /src/idt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern void idt_flush(unsigned int); 6 | 7 | static void idt_set_gate(unsigned char,unsigned int,unsigned short,unsigned char); 8 | 9 | idt_entry_t idt_entries[256]; 10 | idt_ptr_t idt_ptr; 11 | 12 | extern void isr0 (); 13 | extern void isr1 (); 14 | extern void isr2 (); 15 | extern void isr3 (); 16 | extern void isr4 (); 17 | extern void isr5 (); 18 | extern void isr6 (); 19 | extern void isr7 (); 20 | extern void isr8 (); 21 | extern void isr9 (); 22 | extern void isr10(); 23 | extern void isr11(); 24 | extern void isr12(); 25 | extern void isr13(); 26 | extern void isr14(); 27 | extern void isr15(); 28 | extern void isr16(); 29 | extern void isr17(); 30 | extern void isr18(); 31 | extern void isr19(); 32 | extern void isr20(); 33 | extern void isr21(); 34 | extern void isr22(); 35 | extern void isr23(); 36 | extern void isr24(); 37 | extern void isr25(); 38 | extern void isr26(); 39 | extern void isr27(); 40 | extern void isr28(); 41 | extern void isr29(); 42 | extern void isr30(); 43 | extern void isr31(); 44 | extern void irq0(); 45 | extern void irq1(); 46 | extern void irq2(); 47 | extern void irq3(); 48 | extern void irq4(); 49 | extern void irq5(); 50 | extern void irq6(); 51 | extern void irq7(); 52 | extern void irq8(); 53 | extern void irq9(); 54 | extern void irq10(); 55 | extern void irq11(); 56 | extern void irq12(); 57 | extern void irq13(); 58 | extern void irq14(); 59 | extern void irq15(); 60 | 61 | void (* INT_LIST[])(void) = {isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, 62 | isr8, isr9, isr10, isr11, isr12, isr13, isr14, 63 | isr15, isr16, isr17, isr18, isr19,isr20, isr21, 64 | isr22, isr23, isr24, isr25, isr26, isr27, isr28, 65 | isr29, isr30, isr31, irq0, irq1, irq2, irq3, irq4, 66 | irq6, irq6, irq7, irq8, irq9, irq10, irq11, irq12, 67 | irq13, irq14, irq15}; 68 | 69 | #define INT_LIST_LEN (sizeof(INT_LIST)/sizeof(INT_LIST[0])) 70 | 71 | void init_idt() 72 | { 73 | unsigned int isr; 74 | 75 | idt_ptr.limit = sizeof(idt_entry_t) * 256 -1; 76 | idt_ptr.base = (unsigned int)&idt_entries; 77 | 78 | memset(&idt_entries, 0, sizeof(idt_entry_t)*256); 79 | 80 | for(isr = 0; isr < INT_LIST_LEN; isr++) 81 | idt_set_gate(isr, (unsigned int)INT_LIST[isr], 0x08, 0x8E); 82 | 83 | idt_flush((unsigned int)&idt_ptr); 84 | } 85 | 86 | static void idt_set_gate(unsigned char num, unsigned int base, unsigned short sel, unsigned char flags) 87 | { 88 | idt_entries[num].base_lo = base & 0xFFFF; 89 | idt_entries[num].base_hi = (base >> 16) & 0xFFFF; 90 | 91 | idt_entries[num].sel = sel; 92 | idt_entries[num].always0 = 0; 93 | // We must uncomment the OR below when we get to using user-mode. 94 | // It sets the interrupt gate's privilege level to 3. 95 | idt_entries[num].flags = flags /* | 0x60 */; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # uOS Primary Makefile 2 | BUILD_MODE="DEBUG" 3 | 4 | CC=$(PREFIX)gcc 5 | LD=$(PREFIX)gcc 6 | ASM=nasm 7 | QEMUOPT=-name uOS -m 32 -no-reboot -d all 8 | 9 | VPATH=src asm 10 | BUILD_DIR=$(PWD)/build 11 | 12 | LINKING_INFO=linker.ld 13 | ASMFLAGS=-f elf 14 | GCC_KERNEL=-nostdlib \ 15 | -nostdinc \ 16 | -fno-builtin \ 17 | -nostartfiles \ 18 | -nodefaultlibs 19 | 20 | ifeq ($(BUILD_MODE), "DEBUG") 21 | LDFLAGS=-Wl,-m,elf_i386 \ 22 | -Wl,-Map,kernel.map \ 23 | -ggdb \ 24 | -T $(LINKING_INFO) \ 25 | -L$(CURDIR)/lib/ \ 26 | $(GCC_KERNEL) 27 | 28 | CFLAGS=-m32 \ 29 | -I$(CURDIR)/include/ \ 30 | -ggdb \ 31 | -Wall \ 32 | -Wextra \ 33 | $(GCC_KERNEL) 34 | else ifeq ($(BUILD_MODE), "QUICK") 35 | LDFLAGS=-O1 \ 36 | -Wl,-m,elf_i386 \ 37 | -T $(LINKING_INFO) \ 38 | -L$(CURDIR)/lib/ \ 39 | $(GCC_KERNEL) 40 | 41 | CFLAGS=-O1 \ 42 | -m32 \ 43 | -I$(CURDIR)/include/ \ 44 | -Wall \ 45 | -Wextra \ 46 | $(GCC_KERNEL) 47 | else ifeq ($(BUILD_MODE), "RELEASE_LTO") 48 | LDFLAGS=-O2 \ 49 | -flto \ 50 | -fwhole-program \ 51 | -Wl,-m,elf_i386 \ 52 | -Wl,-Map,kernel.map \ 53 | -T $(LINKING_INFO) \ 54 | -L$(CURDIR)/lib/ \ 55 | $(GCC_KERNEL) 56 | CFLAGS=-O2 \ 57 | -flto \ 58 | -fwhole-program \ 59 | -m32 \ 60 | -I$(CURDIR)/include/ \ 61 | -Wall \ 62 | -Wextra \ 63 | -Werror \ 64 | -nostdlib -nostdinc -fno-builtin -nostartfiles -nodefaultlibs 65 | else 66 | $(error "Unknown build mode") 67 | endif 68 | 69 | export CFLAGS LDFLAGS 70 | 71 | CSRC=kernel.o \ 72 | vga.o \ 73 | assembly.o \ 74 | string.o \ 75 | gdt.o \ 76 | idt.o \ 77 | isr.o \ 78 | kerror.o \ 79 | print.o \ 80 | irq.o \ 81 | i8259.o \ 82 | pit.o \ 83 | kheap.o \ 84 | memory.o \ 85 | list.o \ 86 | drivers/driver.o \ 87 | drivers/keyboard.o 88 | 89 | ASRC=loader.o gdt_x86.o idt_x86.o 90 | SOURCES=$(CSRC) $(ASRC) 91 | OBJECTS=$(addprefix $(BUILD_DIR)/, $(SOURCES)) 92 | EXECUTABLE=boot/kernel.bin 93 | 94 | .PHONY: all clean run debug 95 | all: $(EXECUTABLE) 96 | 97 | #linking stage. All objects required to be compiled. 98 | #Linking info required as well 99 | 100 | $(EXECUTABLE) : $(BUILD_DIR) $(OBJECTS) $(LINKING_INFO) 101 | $(LD) $(LDFLAGS) $(OBJECTS) -o $(EXECUTABLE) -lgcc 102 | 103 | #Make sure our build and output directories are created 104 | $(BUILD_DIR): 105 | mkdir -p $(BUILD_DIR) boot 106 | 107 | #Compiling C and Assembly files 108 | #All C/Asm files required to be present 109 | #A check for headers can be added later 110 | 111 | $(BUILD_DIR)/%.o : %.c 112 | if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi 113 | $(CC) $(CFLAGS) -c $< -o $@ 114 | 115 | $(BUILD_DIR)/%.o: %.s 116 | $(ASM) $(ASMFLAGS) $< -o $@ 117 | 118 | #BE CAREFUL! 119 | clean: 120 | -rm -f $(OBJECTS) $(EXECUTABLE) 121 | -rmdir $(BUILD_DIR)/ 122 | -rm -f kernel.map 123 | -rm -f boot/kernel.img 124 | 125 | # make sure everything is built first 126 | run : all 127 | ./run.sh $(QEMUOPT) 128 | 129 | debug : all 130 | ./debug.sh $(QEMUOPT) 131 | -------------------------------------------------------------------------------- /src/kernel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | void banner(); 12 | void print_multiboot(struct multiboot_info * mbi); 13 | 14 | void kmain(struct multiboot_info * mbi, uint32 magic) 15 | { 16 | vga_init(); 17 | banner(); 18 | 19 | if(magic != MULTIBOOT_BOOTLOADER_MAGIC) 20 | panic("Kernel was not loaded using a multiboot compliant bootloader. Cannot continue" \ 21 | " reliably without multiboot information. Halting...\n"); 22 | 23 | //////////////////// 24 | 25 | // Initializes our IDT and GDT to get exception handling up ASAP 26 | init_desc_tables(); 27 | 28 | // print some diagnostic info on grub's memory map 29 | print_multiboot(mbi); 30 | printf(" [%p-%p KERNL]\n", KERNEL_START, KERNEL_END); 31 | 32 | // initialize our early kmalloc 33 | kmalloc_early_init(EARLY_KMALLOC_START, EARLY_KMALLOC_END); 34 | 35 | // transition from early page tables to kernel tables 36 | paging_init(); 37 | 38 | // Initialize the interrupt controller 39 | pic_init(); 40 | // interface with the Programmable Interval Timer 41 | init_pit(100); // 100 Hz 42 | enable_interupts(); // hardware interrupts are now enabled 43 | 44 | extern struct driver_interface keyboard_driver; 45 | register_driver(&keyboard_driver); 46 | 47 | // become interrupt driven 48 | for(;;) halt(); 49 | } 50 | 51 | void banner() 52 | { 53 | // make the header stand out 54 | vga_set_color(COLOR_WHITE, COLOR_DONTCARE); 55 | printf(" ____ _____\n"); 56 | printf(" __ __/ __ \\/ ___/\n"); 57 | printf(" / / / / / / /\\___ \\\n"); 58 | printf(" / /_/ / /_/ /____/ /\n"); 59 | printf(" / ____/\\____/______/\n"); 60 | printf(" / /\n"); 61 | printf(" /_/ Micro Operating System\n"); 62 | printf(" By Grant Hernandez\n"); 63 | 64 | printf("\nBuilt on %s at %s\n\n", __DATE__, __TIME__); 65 | vga_set_color(COLOR_LGREY, COLOR_DONTCARE); 66 | } 67 | 68 | void print_multiboot(struct multiboot_info * mbi) 69 | { 70 | if(mbi == NULL) 71 | { 72 | printf("No multiboot information!\n"); 73 | return; 74 | } 75 | 76 | if(mbi->flags & MULTIBOOT_INFO_MEMORY) 77 | { 78 | unsigned int total_mem = mbi->mem_lower + mbi->mem_upper; 79 | printf("Total Memory %d KiB: mem_lower = %d KiB, mem_upper = %d KiB\n", 80 | total_mem, 81 | mbi->mem_lower, 82 | mbi->mem_upper); 83 | } 84 | 85 | if(mbi->flags & MULTIBOOT_INFO_MEM_MAP) 86 | { 87 | unsigned int * mem_info_ptr = (unsigned int *)mbi->mmap_addr; 88 | 89 | printf("Tentative Memory Map\n"); 90 | 91 | while(mem_info_ptr < (unsigned int *)mbi->mmap_addr+mbi->mmap_length) 92 | { 93 | multiboot_memory_map_t * cur = (multiboot_memory_map_t *)mem_info_ptr; 94 | 95 | if(cur->len > 0) 96 | printf(" [%p-%p %5s]\n", (uint32)cur->addr, 97 | (uint32)(cur->addr+cur->len), 98 | cur->type == MULTIBOOT_MEMORY_AVAILABLE ? "AVAIL" : "RESVD"); 99 | 100 | mem_info_ptr += cur->size + sizeof(cur->size); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/kheap.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | uint8 initialized = 0; 7 | uint32 initialBase = 0x0; 8 | uint32 currentBase = 0x0; 9 | uint32 maxBase = 0x0; 10 | 11 | /* kmalloc_early_xxx 12 | * At this point, all we have is a location of free memory 13 | * but no paging. No paging means that there is literally no point 14 | * in handing out memory addresses when we want to enforce memory 15 | * boundaries through paging. 16 | * 17 | * These early functions are just used to bootstrap early code until 18 | * the point when paging and the real heap can be enabled. 19 | */ 20 | 21 | // TODO: reliably choose a free spot in memory 22 | void kmalloc_early_init(uint32 base, uint32 max) 23 | { 24 | ASSERT(base < max); 25 | 26 | printf("kmalloc_early [%p, %p]\n", base, max); 27 | initialBase = currentBase = base; 28 | maxBase = max; 29 | initialized = 1; 30 | } 31 | 32 | /* Returns true on a finding a free memory region. 33 | * Uses this to initialize kmalloc_early 34 | * TODO: depreciate this and just make a function to get a list 35 | * of memory regions by availability 36 | */ 37 | int kmalloc_early_init_grub(struct multiboot_info * mbi) 38 | { 39 | if(mbi == NULL) 40 | return 0; 41 | 42 | if(mbi->flags & MULTIBOOT_INFO_MEM_MAP) 43 | { 44 | uint32 * mem_info_ptr = (uint32 *)mbi->mmap_addr; 45 | 46 | while(mem_info_ptr < (uint32 *)mbi->mmap_addr+mbi->mmap_length) 47 | { 48 | multiboot_memory_map_t * cur = (multiboot_memory_map_t *)mem_info_ptr; 49 | 50 | // choose the first memory location we find. don't worry about the size yet 51 | if(cur->type == MULTIBOOT_MEMORY_AVAILABLE && cur->len > 0) 52 | { 53 | if(cur->addr == 0x0 && cur->len > PAGE_SIZE) 54 | { 55 | cur->addr += PAGE_SIZE; 56 | cur->len -= PAGE_SIZE; 57 | } 58 | 59 | kmalloc_early_init(cur->addr, cur->addr+cur->len); 60 | //kmalloc_early_init(0x0 KERNEL_END, 0xC0000000 + KERNEL_END+0x10000); 61 | return 1; 62 | } 63 | 64 | // move to the next structure 65 | mem_info_ptr += cur->size + sizeof(cur->size); 66 | } 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | uint32 kmalloc_early(uint32 size) 73 | { 74 | if(!initialized) 75 | panic("kmalloc_early: no base address initialized\n"); 76 | 77 | if(!size) 78 | return NULL; 79 | 80 | if(size+currentBase > maxBase) 81 | panic("kmalloc_early: %u byte allocation caused us to run out of memory (%u bytes left)\n", size, maxBase - currentBase); 82 | 83 | 84 | uint32 addr = currentBase; 85 | 86 | currentBase += size; 87 | 88 | return addr; 89 | } 90 | 91 | uint32 kmalloc_early_align(uint32 size) 92 | { 93 | if(!initialized) 94 | panic("kmalloc_early_align: no base address initialized\n"); 95 | 96 | // prevent page alignment if the allocation size is zero 97 | if(!size) 98 | return NULL; 99 | 100 | 101 | // is our placement address not page aligned? 102 | if(currentBase & ~PAGE_ALIGN) 103 | { 104 | currentBase &= PAGE_ALIGN; 105 | currentBase += PAGE_SIZE; 106 | } 107 | 108 | // just call the regular early kmalloc, which will act on the page 109 | // aligned placement address 110 | return kmalloc_early(size); 111 | } 112 | -------------------------------------------------------------------------------- /include/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | 4 | void init_gdt(); 5 | 6 | // This structure contains the value of one GDT entry. 7 | // We use the attribute 'packed' to tell GCC not to change 8 | // any of the alignment in the structure. 9 | struct gdt_entry_struct 10 | { 11 | unsigned short limit_low; // The lower 16 bits of the limit. 12 | unsigned short base_low; // The lower 16 bits of the base. 13 | unsigned char base_middle; // The next 8 bits of the base. 14 | unsigned char access; // Access flags, determine what ring this segment can be used in. 15 | unsigned char granularity; 16 | unsigned char base_high; // The last 8 bits of the base. 17 | } __attribute__((packed)); //DO NOT ALIGN 18 | typedef struct gdt_entry_struct gdt_entry_t; 19 | 20 | struct gdt_ptr_struct 21 | { 22 | unsigned short limit; // The upper 16 bits of all selector limits. 23 | unsigned int base; // The address of the first gdt_entry_t struct. 24 | } __attribute__((packed)); 25 | typedef struct gdt_ptr_struct gdt_ptr_t; 26 | 27 | 28 | /* 29 | * Thanks to this website for the barebones. I had this working before (592550) 30 | * but it wasn't as elegant of a solution. 31 | * http://wiki.osdev.org/GDT_Tutorial 32 | */ 33 | 34 | #define SEG_TYPE(x) ((x) << 0x04) // Descriptor type (0 for system, 1 for code/data) 35 | #define SEG_PRES(x) ((x) << 0x07) // Present 36 | #define SEG_SAVL(x) ((x) << 0x0C) // Available for system use 37 | #define SEG_LONG(x) ((x) << 0x0D) // Long mode 38 | #define SEG_SIZE(x) ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32) 39 | #define SEG_GRAN(x) ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB) 40 | #define SEG_PRIV(x) (((x) & 0x03) << 0x05) // Set privilege level (0 - 3) 41 | 42 | #define SEG_DATA_RD 0x00 // Read-Only 43 | #define SEG_DATA_RDA 0x01 // Read-Only, accessed 44 | #define SEG_DATA_RDWR 0x02 // Read/Write 45 | #define SEG_DATA_RDWRA 0x03 // Read/Write, accessed 46 | #define SEG_DATA_RDEXPD 0x04 // Read-Only, expand-down 47 | #define SEG_DATA_RDEXPDA 0x05 // Read-Only, expand-down, accessed 48 | #define SEG_DATA_RDWREXPD 0x06 // Read/Write, expand-down 49 | #define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed 50 | #define SEG_CODE_EX 0x08 // Execute-Only 51 | #define SEG_CODE_EXA 0x09 // Execute-Only, accessed 52 | #define SEG_CODE_EXRD 0x0A // Execute/Read 53 | #define SEG_CODE_EXRDA 0x0B // Execute/Read, accessed 54 | #define SEG_CODE_EXC 0x0C // Execute-Only, conforming 55 | #define SEG_CODE_EXCA 0x0D // Execute-Only, conforming, accessed 56 | #define SEG_CODE_EXRDC 0x0E // Execute/Read, conforming 57 | #define SEG_CODE_EXRDCA 0x0F // Execute/Read, conforming, accessed 58 | 59 | #define GDT_CODE_PL0 SEG_TYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ 60 | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ 61 | SEG_PRIV(0) | SEG_CODE_EXRD 62 | 63 | #define GDT_DATA_PL0 SEG_TYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ 64 | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ 65 | SEG_PRIV(0) | SEG_DATA_RDWR 66 | 67 | #define GDT_CODE_PL3 SEG_TYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ 68 | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ 69 | SEG_PRIV(3) | SEG_CODE_EXRD 70 | 71 | #define GDT_DATA_PL3 SEG_TYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | \ 72 | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | \ 73 | SEG_PRIV(3) | SEG_DATA_RDWR 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /asm/idt_x86.s: -------------------------------------------------------------------------------- 1 | [GLOBAL idt_flush] ; Allows the C code to call idt_flush(). 2 | 3 | idt_flush: 4 | mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. 5 | lidt [eax] ; Load the IDT pointer. 6 | ret 7 | 8 | 9 | %macro ISR_NOERRCODE 1 ; define a macro, taking one parameter 10 | [GLOBAL isr%1] ; %1 accesses the first parameter. 11 | isr%1: 12 | cli 13 | push byte 0 14 | push byte %1 15 | jmp isr_common_stub 16 | %endmacro 17 | 18 | %macro ISR_ERRCODE 1 19 | [GLOBAL isr%1] 20 | isr%1: 21 | cli 22 | push byte %1 23 | jmp isr_common_stub 24 | %endmacro 25 | 26 | %macro IRQ 2 27 | [GLOBAL irq%1] 28 | irq%1: 29 | cli 30 | push byte 0 31 | push byte %2 32 | jmp irq_common_stub 33 | %endmacro 34 | 35 | [EXTERN isr_handler] 36 | 37 | ; This is our common ISR stub. It saves the processor state, sets 38 | ; up for kernel mode segments, calls the C-level fault handler, 39 | ; and finally restores the stack frame. 40 | isr_common_stub: 41 | pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax 42 | 43 | mov ax, ds ; Lower 16-bits of eax = ds. 44 | push eax ; save the data segment descriptor 45 | push esp ; Push a pointer to the registers 46 | 47 | mov ax, 0x10 ; load the kernel data segment descriptor 48 | mov ds, ax 49 | mov es, ax 50 | mov fs, ax 51 | mov gs, ax 52 | 53 | call isr_handler 54 | 55 | add esp, 4 ; ignore the pointer to the registers 56 | pop eax ; reload the original data segment descriptor 57 | mov ds, ax 58 | mov es, ax 59 | mov fs, ax 60 | mov gs, ax 61 | 62 | popa ; Pops edi,esi,ebp... 63 | add esp, 8 ; Cleans up the pushed error code and pushed ISR number 64 | sti 65 | iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP 66 | 67 | [EXTERN irq_handler] 68 | 69 | ; This is our common IRQ stub. It saves the processor state, sets 70 | ; up for kernel mode segments, calls the C-level fault handler, 71 | ; and finally restores the stack frame. 72 | irq_common_stub: 73 | pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax 74 | 75 | mov ax, ds ; Lower 16-bits of eax = ds. 76 | push eax ; save the data segment descriptor 77 | push esp ; Push a pointer to the registers 78 | 79 | mov ax, 0x10 ; load the kernel data segment descriptor 80 | mov ds, ax 81 | mov es, ax 82 | mov fs, ax 83 | mov gs, ax 84 | 85 | call irq_handler 86 | 87 | add esp, 4 ; ignore the pointer to the registers 88 | pop ebx ; reload the original data segment descriptor 89 | mov ds, bx 90 | mov es, bx 91 | mov fs, bx 92 | mov gs, bx 93 | 94 | popa ; Pops edi,esi,ebp... 95 | add esp, 8 ; Cleans up the pushed error code and pushed ISR number 96 | sti 97 | iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP 98 | 99 | 100 | ISR_NOERRCODE 0 101 | ISR_NOERRCODE 1 102 | ISR_NOERRCODE 2 103 | ISR_NOERRCODE 3 104 | ISR_NOERRCODE 4 105 | ISR_NOERRCODE 5 106 | ISR_NOERRCODE 6 107 | ISR_NOERRCODE 7 108 | ISR_ERRCODE 8 109 | ISR_NOERRCODE 9 110 | ISR_ERRCODE 10 111 | ISR_ERRCODE 11 112 | ISR_ERRCODE 12 113 | ISR_ERRCODE 13 114 | ISR_ERRCODE 14 115 | ISR_NOERRCODE 15 116 | ISR_NOERRCODE 16 117 | ISR_NOERRCODE 17 118 | ISR_NOERRCODE 18 119 | ISR_NOERRCODE 19 120 | ISR_NOERRCODE 20 121 | ISR_NOERRCODE 21 122 | ISR_NOERRCODE 22 123 | ISR_NOERRCODE 23 124 | ISR_NOERRCODE 24 125 | ISR_NOERRCODE 25 126 | ISR_NOERRCODE 26 127 | ISR_NOERRCODE 27 128 | ISR_NOERRCODE 28 129 | ISR_NOERRCODE 29 130 | ISR_NOERRCODE 30 131 | ISR_NOERRCODE 31 132 | 133 | IRQ 0, 32 134 | IRQ 1, 33 135 | IRQ 2, 34 136 | IRQ 3, 35 137 | IRQ 4, 36 138 | IRQ 5, 37 139 | IRQ 6, 38 140 | IRQ 7, 39 141 | IRQ 8, 40 142 | IRQ 9, 41 143 | IRQ 10, 42 144 | IRQ 11, 43 145 | IRQ 12, 44 146 | IRQ 13, 45 147 | IRQ 14, 46 148 | IRQ 15, 47 149 | -------------------------------------------------------------------------------- /src/kerror.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | enum exception_codes 5 | { 6 | EXCEPT_DIV = 0, 7 | EXCEPT_RESVD, 8 | EXCEPT_NMI, 9 | EXCEPT_BP, 10 | EXCEPT_OVERFLOW, 11 | EXCEPT_BOUND, 12 | EXCEPT_INVOP, 13 | EXCEPT_DEVNA, 14 | EXCEPT_DOUBLE, 15 | EXCEPT_COSEG, 16 | EXCEPT_INVTSS, 17 | EXCEPT_SEGNP, 18 | EXCEPT_SSFAULT, 19 | EXCEPT_GFP, 20 | EXCEPT_PAGEFAULT, 21 | EXCEPT_RESVD2, 22 | EXCEPT_X87FPU, 23 | EXCEPT_ALIGN, 24 | EXCEPT_MACHINECK, 25 | EXCEPT_SIMD 26 | }; 27 | 28 | char * exception_strings[] = { "Divide Error", 29 | "Reserved", 30 | "NMI Interrupt", 31 | "Breakpoint", 32 | "Overflow", 33 | "BOUND Range Exceeded", 34 | "Invalid Opcode", 35 | "Device Not Available", 36 | "Double Fault", 37 | "Coprocessor Segment Overrun", 38 | "Invalid TSS", 39 | "Segment Not Present", 40 | "Stack-Segment Fault", 41 | "General Protection Fault (GPF)", 42 | "Page Fault", 43 | "Reserved", 44 | "x87 FPU Error", 45 | "Alignment Check", 46 | "Machine Check", 47 | "SIMD Floating-Point Exception" }; 48 | 49 | static void panic_exception(struct registers * reg); 50 | 51 | void panic(char * reason, ...) 52 | { 53 | //we will never leave this function... 54 | disable_interupts(); 55 | 56 | va_list args; 57 | 58 | va_start(args, reason); 59 | 60 | printf("\n\n[PANIC] Halt and catch fire\n"); 61 | printf("Reason: "); 62 | 63 | vprintf(reason, args); 64 | 65 | printf("\n"); 66 | 67 | for(;;) 68 | halt(); //goodbye 69 | } 70 | 71 | void handle_exception(struct registers * reg) 72 | { 73 | int exCode = reg->int_no; 74 | 75 | ASSERT(exCode >= EXCEPT_DIV && exCode <= EXCEPT_SIMD); 76 | 77 | switch (exCode) 78 | { 79 | case EXCEPT_DIV: 80 | break; 81 | case EXCEPT_RESVD: 82 | break; 83 | case EXCEPT_NMI: 84 | break; 85 | case EXCEPT_BP: 86 | break; 87 | case EXCEPT_OVERFLOW: 88 | break; 89 | case EXCEPT_BOUND: 90 | break; 91 | case EXCEPT_INVOP: 92 | break; 93 | case EXCEPT_DEVNA: 94 | break; 95 | case EXCEPT_DOUBLE: 96 | break; 97 | case EXCEPT_COSEG: 98 | break; 99 | case EXCEPT_INVTSS: 100 | break; 101 | case EXCEPT_SEGNP: 102 | break; 103 | case EXCEPT_SSFAULT: 104 | break; 105 | case EXCEPT_GFP: 106 | break; 107 | case EXCEPT_PAGEFAULT: 108 | break; 109 | case EXCEPT_RESVD2: 110 | break; 111 | case EXCEPT_X87FPU: 112 | break; 113 | case EXCEPT_ALIGN: 114 | break; 115 | case EXCEPT_MACHINECK: 116 | break; 117 | case EXCEPT_SIMD: 118 | break; 119 | } 120 | 121 | panic_exception(reg); 122 | } 123 | 124 | 125 | void panic_exception(struct registers * reg) 126 | { 127 | disable_interupts(); 128 | 129 | printf("\n\n[PANIC] Exception '%s' (%d)\n\n", 130 | exception_strings[reg->int_no], reg->int_no); 131 | 132 | printf("Exception details:\n"); 133 | printf("Panic called from ISR%d with error code %d\n", reg->int_no, reg->err_code); 134 | printf("Fault occured at EIP %p, CS 0x%04X\n\n", reg->eip, reg->cs); 135 | 136 | uint32 cr2; 137 | asm volatile("mov %%cr2, %0": "=b" (cr2)); 138 | 139 | 140 | printf("===== Register Dump =====\n" 141 | "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" 142 | "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" 143 | "EIP=%08x CR2=%08x\n", 144 | reg->eax, reg->ebx, reg->ecx, reg->edx, 145 | reg->esi, reg->edi, reg->ebp, reg->esp, 146 | reg->eip, cr2); 147 | 148 | printf("Halting..."); 149 | 150 | for(;;) halt(); 151 | } 152 | -------------------------------------------------------------------------------- /src/vga.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Defines basic VGA text manipulation routines. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define VID_MEM 0xb8000 13 | #define COLUMNS 80 14 | #define BYTES_PER_ROW COLUMNS*2 15 | #define ROWS 25 16 | #define TOTAL_BYTES ROWS*BYTES_PER_ROW 17 | 18 | #define VGA_COMMAND_PORT 0x3D4 19 | #define VGA_DATA_PORT 0x3D5 20 | #define VGA_HIGH_INDEX_REG 0xE 21 | #define VGA_LOW_INDEX_REG 0xF 22 | 23 | static unsigned int vga_row = 0, vga_col = 0; 24 | static uint8 attr_fg = COLOR_LGREY, attr_bg = COLOR_BLUE; 25 | 26 | static void update_cursor(); 27 | static void clear_row(int row); 28 | static void clear_screen(); 29 | static void scroll_up(); 30 | 31 | void vga_init() 32 | { 33 | //TODO: check for monochrome monitor (just for fun) 34 | 35 | clear_screen(); //screen is now in a known state 36 | printf("VGA Init: Mode %dx%d, %s, attr fg:%#x bg:%#x\n", COLUMNS, ROWS, "color", attr_fg, attr_bg); 37 | } 38 | 39 | /* TODO: This color setting interface is cumbersome 40 | * for my application, it would be better if 41 | * color codes were inline with the text, such 42 | * as traditional terminal coloring schemes 43 | * Also, do I even care about the kernel having colors? 44 | */ 45 | 46 | void vga_set_color(vga_color_t fg, vga_color_t bg) 47 | { 48 | if(fg < COLOR_DONTCARE) 49 | attr_fg = fg; 50 | if(bg < COLOR_DONTCARE) 51 | attr_bg = bg; 52 | } 53 | 54 | void vga_get_color(vga_color_t * fg, vga_color_t * bg) 55 | { 56 | if(fg) 57 | *fg = attr_fg; 58 | if(bg) 59 | *bg = attr_bg; 60 | } 61 | 62 | void vga_kputc(unsigned char c) 63 | { 64 | //special character handling 65 | if(c == '\n') 66 | { 67 | vga_row++; 68 | vga_col = 0; 69 | 70 | if(vga_row == ROWS) 71 | scroll_up(); 72 | update_cursor(); 73 | } 74 | else if(c == '\r') 75 | { 76 | vga_col = 0; 77 | update_cursor(); 78 | } 79 | else 80 | { 81 | unsigned char *vidmem = (unsigned char *)VID_MEM; 82 | unsigned char *writePosition = vidmem + (vga_row*COLUMNS + vga_col)*2; //times 2 to skip attributes 83 | writePosition[0] = c; 84 | writePosition[1] = (attr_bg << 4) | attr_fg; 85 | 86 | vga_col++; 87 | if(vga_col == COLUMNS) 88 | vga_kputc('\n'); 89 | } 90 | } 91 | 92 | void update_cursor() 93 | { 94 | uint8 pos = vga_row*COLUMNS + vga_col; 95 | 96 | outb(VGA_COMMAND_PORT, VGA_HIGH_INDEX_REG); //send the HIGH byte 97 | outb(VGA_DATA_PORT, (pos >> 4) & 0xFF); 98 | 99 | outb(VGA_COMMAND_PORT, VGA_LOW_INDEX_REG); //send the LOW byte 100 | outb(VGA_DATA_PORT, pos & 0xFF); 101 | } 102 | 103 | void clear_row(int row) 104 | { 105 | unsigned char *vidmem = (unsigned char *)VID_MEM + BYTES_PER_ROW*row; 106 | 107 | int i; 108 | for(i = 0; i < BYTES_PER_ROW; i++) 109 | { 110 | if(i % 2 == 0) //character 111 | vidmem[i] = '\0'; 112 | else //attribute 113 | vidmem[i] = (attr_bg << 4) | attr_fg; 114 | } 115 | } 116 | 117 | void clear_screen() 118 | { 119 | int i; 120 | for(i = 0; i < ROWS; i++) 121 | clear_row(i); 122 | 123 | vga_row = vga_col = 0; 124 | update_cursor(); 125 | } 126 | 127 | void scroll_up() 128 | { 129 | int row; 130 | //start reading from the second row and overwrite the first one 131 | unsigned char * readPos = (unsigned char *)VID_MEM + BYTES_PER_ROW; 132 | unsigned char * writePos = (unsigned char *)VID_MEM; 133 | 134 | //where row is the row to written to 135 | //stop before the last row because there is nothing after it to be read 136 | for(row = 0; row < ROWS-1; row++) //row 0 becomes row 1, row 1 becomes row 2 137 | { 138 | memcpy(writePos, readPos, BYTES_PER_ROW); 139 | readPos += BYTES_PER_ROW; 140 | writePos += BYTES_PER_ROW; 141 | } 142 | 143 | clear_row(ROWS-1); //clear last row 144 | 145 | if(vga_row > 0) 146 | { 147 | vga_row--; 148 | update_cursor(); 149 | } 150 | } 151 | 152 | uint32 randChar() 153 | { 154 | static unsigned int seed = 120939; 155 | seed *= 3; 156 | seed ^= get_tick_count(); 157 | 158 | return seed % 256; 159 | } 160 | 161 | void random_screen() 162 | { 163 | unsigned char * writePos = (unsigned char *)VID_MEM; 164 | 165 | for(;;) 166 | { 167 | int row, i; 168 | 169 | for(row = 0; row < ROWS; row++) 170 | { 171 | for(i = 0; i < BYTES_PER_ROW; i++) 172 | { 173 | writePos[i+row*BYTES_PER_ROW] = randChar(); 174 | } 175 | } 176 | 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /asm/loader.s: -------------------------------------------------------------------------------- 1 | global _start ; make the entry point visible to linker 2 | 3 | extern kmain ; kmain is defined in kernel.cpp 4 | 5 | ; used to relocate the kernel (virtually) and zero the BSS 6 | extern _KERNEL_START 7 | extern _KERNEL_END 8 | extern _BSS_START 9 | extern _BSS_SIZE 10 | 11 | extern _EARLY_KERNEL_START 12 | extern _EARLY_KERNEL_END 13 | extern _EARLY_BSS_START 14 | extern _EARLY_BSS_SIZE 15 | 16 | KERNEL_BASE equ 0xC0000000 17 | LOWMEM_END equ _EARLY_KERNEL_END ; lowmem ends at the 1st MB 18 | PAGE_SIZE equ 4096 19 | PAGE_SHIFT equ 12 ; 2^12 = 4096 = PAGE_SIZE 20 | PAGE_PERM equ 3 ; default page permissions: present, read/write 21 | STACK_SIZE equ 4*PAGE_SIZE ; initial kernel stack space size of 16k 22 | 23 | ; constants for setting up the Multiboot header 24 | MODULEALIGN equ 1<<0 ; align loaded modules on page boundaries 25 | MEMINFO equ 1<<1 ; provide memory map (BIOS e820) 26 | FLAGS equ MODULEALIGN | MEMINFO ; this is the Multiboot 'flag' field 27 | MAGIC equ 0x1BADB002 ; magic number lets bootloader find the header 28 | CHECKSUM equ -(MAGIC + FLAGS) ; checksum required 29 | 30 | ; explicitly give multiboot header a section 31 | [section .__mbHeader] 32 | 33 | align 4 34 | dd MAGIC 35 | dd FLAGS 36 | dd CHECKSUM 37 | 38 | ;########################################## 39 | 40 | [section .early_text progbits] 41 | 42 | _start: 43 | ; save our multiboot information from grub before messing with registers 44 | ; these can't be saved on the stack as we are about to zero it 45 | mov [multiboot_magic], eax 46 | mov [multiboot_info], ebx 47 | 48 | ; zero the early BSS to start things off well 49 | mov ecx, _EARLY_BSS_SIZE 50 | xor al, al 51 | mov edi, _EARLY_BSS_START 52 | rep stosb 53 | 54 | ; identity map from 0x00000000 -> EARLY_KERNEL_END 55 | ; WARNING: code assumes that the kernel won't be greater than 3MB 56 | mov eax, lowmem_pt 57 | mov [page_directory], eax 58 | or dword [page_directory], PAGE_PERM ; mark the PT as present 59 | 60 | xor eax, eax ; eax is our starting physical address (0x00000000) 61 | .lowmem: 62 | mov ecx, eax 63 | shr ecx, PAGE_SHIFT ; divide our current address by PAGE_SIZE 64 | and ecx, 0x3ff ; mask of higher bits to create an index (mod 1024) 65 | mov [lowmem_pt+ecx*4], eax ; copy our physical address to the page entry 66 | or dword [lowmem_pt+ecx*4], PAGE_PERM ; write our permissions (present, etc) 67 | 68 | add eax, PAGE_SIZE ; move on to the next page 69 | cmp eax, LOWMEM_END ; are we done with lowmem? 70 | jl .lowmem 71 | 72 | ; create virtual mappings for the kernel in the higher-half 73 | mov edx, KERNEL_BASE 74 | shr edx, 22 ; find which page table we need to use 75 | 76 | mov eax, kernel_pt 77 | mov [page_directory+edx*4], eax 78 | or dword [page_directory+edx*4], PAGE_PERM ; mark the PT as present 79 | 80 | mov eax, _KERNEL_START ; the kernel's current virtual start 81 | .higher: 82 | mov ecx, eax 83 | shr ecx, PAGE_SHIFT 84 | and ecx, 0x3ff ; generate kernel PTE index 85 | 86 | mov ebx, eax 87 | sub ebx, KERNEL_BASE ; convert virt->physical 88 | mov [kernel_pt+ecx*4], ebx 89 | or dword [kernel_pt+ecx*4], PAGE_PERM 90 | 91 | add eax, PAGE_SIZE ; move on to the next page 92 | cmp eax, _KERNEL_END ; are we done mapping in the kernel? 93 | jl .higher 94 | 95 | mov eax, page_directory 96 | mov cr3, eax ; load CR3 with our page directory 97 | 98 | mov eax, cr0 99 | or eax, 0x80000000 100 | mov cr0, eax ; enable paging! make sure the next instruction fetch doesnt page fault 101 | 102 | ; zero the kernel BSS 103 | mov ecx, _BSS_SIZE 104 | xor al, al 105 | mov edi, _BSS_START 106 | rep stosb 107 | 108 | ; adjust the stack in to the virtual area 109 | ; setup and adjust the stack 110 | mov esp, stack + STACK_SIZE 111 | 112 | push dword [multiboot_magic] ; Multiboot magic number 113 | push dword [multiboot_info] ; Multiboot info structure 114 | call kmain ; call virtual kernel entry point 115 | 116 | .catchfire: 117 | hlt ; halt machine if the kernel returns (it won't) 118 | jmp short .catchfire 119 | 120 | ;############################################### 121 | 122 | [section .early_bss nobits] 123 | 124 | ; NOTE THE USAGE OF ALIGNB (MUST BE USED IN BSS) 125 | alignb 4096 ; page align the page directory and tables 126 | page_directory: ; should also be page aligned (hopefully) 127 | resd 1024 ; reserve 1024 DWORDs for our page table pointers 128 | lowmem_pt: 129 | resd 1024 ; lowmem identity mappings 130 | kernel_pt: 131 | resd 1024 ; our kernel page table mappings 132 | 133 | [section .early_data] 134 | multiboot_magic: 135 | dd 0 136 | multiboot_info: 137 | dd 0 138 | 139 | ;############################################### 140 | ; This bss section is in the higher-half 141 | [section .bss] 142 | align 4 143 | stack: 144 | resb STACK_SIZE ; reserve 16k stack on a DWORD boundary 145 | -------------------------------------------------------------------------------- /src/memory.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // assert 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | // initialize the KERNEL_* convience variables 10 | // allows us forget about using & all the time 11 | uint32 KERNEL_START = (uint32)&_KERNEL_START; 12 | uint32 KERNEL_END = (uint32)&_KERNEL_END; 13 | uint32 EARLY_KMALLOC_START = (uint32)&_EARLY_KMALLOC_START; 14 | uint32 EARLY_KMALLOC_END = (uint32)&_EARLY_KMALLOC_END; 15 | 16 | // stored Page Directory 17 | static uint32 * pageDirectory = NULL; 18 | // A reverse mapping from physical to virtual 19 | static uint32 ** ptPhysToVirt = NULL; 20 | // Guard against multiple enables of paging and for 21 | // virtual to physical translation 22 | static int kernelPagingEnabled = 0; 23 | 24 | static void page_map_pte(uint32 * pt, uint32 index, uint32 phys, uint32 perm); 25 | static uint32 * _native_get_page_directory(); 26 | static void _native_set_page_directory(uint32 * phyDir); 27 | static void _native_paging_enable(); 28 | static void _native_paging_disable(); 29 | 30 | uint32 virt_to_phys(uint32 virt_addr) 31 | { 32 | uint32 * pd = NULL; 33 | uint32 * pt = NULL; 34 | uint32 pde = 0; 35 | 36 | uint32 index = virt_addr / PAGE_SIZE; 37 | uint32 pageDirI = (index / 1024) % 1024; 38 | uint32 pageTableI = index % 1024; 39 | 40 | // we are referencing our virtual memory alloc'd page directory 41 | if(kernelPagingEnabled) { 42 | pd = pageDirectory; // virtual address 43 | pde = (uint32)ptPhysToVirt[pageDirI] | 44 | (uint32)pd[pageDirI] & PAGE_ENTRY_PRESENT; // virtual address with flags 45 | pt = (uint32*)(pde & PAGE_ALIGN); 46 | } else { 47 | pd = _native_get_page_directory(); // identity mapped physical address 48 | pde = (uint32)pd[pageDirI]; // identity mapped physical address 49 | pt = (uint32*)(pde & PAGE_ALIGN); 50 | } 51 | 52 | // TODO: make this more robust 53 | ASSERT(pde & PAGE_ENTRY_PRESENT); 54 | ASSERT(pt[pageTableI] & PAGE_ENTRY_PRESENT); 55 | 56 | // return just physical address without flags 57 | return pt[pageTableI] & PAGE_ALIGN; 58 | } 59 | 60 | void paging_init() 61 | { 62 | uint32 i; 63 | uint32 addr = 0; 64 | 65 | // 1024 page table entries (pointers) 66 | pageDirectory = (uint32 *)kmalloc_early_align(PAGE_TABLE_SIZE); 67 | ptPhysToVirt = (uint32 **)kmalloc_early_align(PAGE_TABLE_SIZE); 68 | 69 | // initialize all the page tables to not present, rw, supervisor 70 | memset(pageDirectory, 0, PAGE_TABLE_SIZE); 71 | // initialize reverse mappings 72 | memset(ptPhysToVirt, 0, PAGE_TABLE_SIZE); 73 | 74 | uint32 * oldDir = _native_get_page_directory(); 75 | printf("paging: PTD at %p (old %p)\n", 76 | virt_to_phys((uint32)pageDirectory), oldDir); 77 | 78 | // Identity map the 1 megabyte 79 | for(i = 0; i < PAGES_PER_MB(1); i++) { 80 | page_ident_map(addr, PAGE_ENTRY_RW); 81 | addr += PAGE_SIZE; 82 | } 83 | 84 | // move our kernel to the higher half 85 | uint32 virtBase = 0xC0000000; 86 | uint32 virtStart = KERNEL_START; 87 | uint32 virtEnd = PAGE_ALIGN_UP(KERNEL_END); 88 | 89 | for(addr = virtStart-virtBase; virtStart < virtEnd; virtStart += PAGE_SIZE, addr += PAGE_SIZE) { 90 | page_map(virtStart, addr, PAGE_ENTRY_RW); 91 | } 92 | 93 | // set our directory and enable paging 94 | _native_set_page_directory(virt_to_phys(pageDirectory)); 95 | _native_paging_enable(); 96 | kernelPagingEnabled = 1; 97 | } 98 | 99 | /* A direct mapping between the virtual and physical realm 100 | */ 101 | uint32 page_ident_map(uint32 addr, uint32 perm) 102 | { 103 | return page_map(addr, addr, perm); 104 | } 105 | 106 | void page_ident_map_range(uint32 start, uint32 end, uint32 perm) 107 | { 108 | ASSERT(start < end); 109 | 110 | end = PAGE_ALIGN_UP(end); 111 | 112 | for(; start < end; start += PAGE_SIZE) 113 | page_map(start, start, perm); 114 | } 115 | 116 | /* Allocates a mapping between the requested virtual address 117 | * and the physical address, using the requested permissions. 118 | * Returns the address of the PTE 119 | */ 120 | uint32 page_map(uint32 virt, uint32 phys, uint32 perm) 121 | { 122 | ASSERT(!(virt & NOT_ALIGNED || phys & NOT_ALIGNED)); 123 | ASSERT(pageDirectory); 124 | 125 | //printf("page_map: %p -> %p\n", virt, phys); 126 | 127 | uint32 index = virt / PAGE_SIZE; 128 | uint32 pageDirI = (index / 1024) % 1024; 129 | uint32 pageTableI = index % 1024; 130 | 131 | // if the page table isn't present, create it 132 | if(!(pageDirectory[pageDirI] & PAGE_ENTRY_PRESENT)) 133 | { 134 | uint32 * pageTable = (uint32 *)kmalloc_early_align(PAGE_TABLE_SIZE); 135 | 136 | // Clear all physical addresses and flags 137 | memset(pageTable, 0, PAGE_TABLE_SIZE); 138 | 139 | // Add the page table to the directory and mark it as present 140 | // NOTE: PDE's MUST have a physical address 141 | pageDirectory[pageDirI] = virt_to_phys((uint32)pageTable) 142 | | PAGE_ENTRY_PRESENT | PAGE_ENTRY_RW; 143 | 144 | printf("page_map: table %u created at %p (%p)\n", 145 | pageDirI, pageTable, pageDirectory[pageDirI]); 146 | 147 | // store the virtual address of the PTE 148 | ptPhysToVirt[pageDirI] = pageTable; 149 | } 150 | 151 | // load our virtual PT address from our reverse mapping 152 | uint32 * pageTable = ptPhysToVirt[pageDirI]; 153 | page_map_pte(pageTable, pageTableI, phys, perm); 154 | 155 | return (uint32)&pageTable[pageTableI]; 156 | } 157 | 158 | static void page_map_pte(uint32 * pt, uint32 index, uint32 phys, uint32 perm) 159 | { 160 | ASSERT(pt); 161 | ASSERT(index < PAGE_ENTRIES); 162 | 163 | // clear out the page table entry, but keep the flags 164 | pt[index] &= ~PAGE_ALIGN; 165 | 166 | // OR on our physical address 167 | pt[index] |= phys; 168 | 169 | // Add permissions 170 | pt[index] |= perm; 171 | 172 | // mark the entry as present 173 | pt[index] |= PAGE_ENTRY_PRESENT; 174 | } 175 | 176 | static inline void _native_set_page_directory(uint32 * phyDir) 177 | { 178 | asm volatile("mov %0, %%cr3":: "b"(phyDir)); 179 | } 180 | 181 | static inline uint32 * _native_get_page_directory() 182 | { 183 | uint32 ret; 184 | asm volatile("mov %%cr3, %0": "=b" (ret)); 185 | 186 | return (uint32 *)ret; 187 | } 188 | 189 | static inline void _native_paging_enable() 190 | { 191 | uint32 cr0; 192 | 193 | asm volatile("mov %%cr0, %0": "=b"(cr0)); 194 | cr0 |= 0x80000000; 195 | asm volatile("mov %0, %%cr0":: "b"(cr0)); // brace yourself 196 | } 197 | 198 | static inline void _native_paging_disable() 199 | { 200 | uint32 cr0; 201 | 202 | asm volatile("mov %%cr0, %0": "=b"(cr0)); 203 | cr0 &= ~(0x80000000U); 204 | asm volatile("mov %0, %%cr0":: "b"(cr0)); 205 | } 206 | -------------------------------------------------------------------------------- /include/boot/multiboot.h: -------------------------------------------------------------------------------- 1 | /* multiboot.h - Multiboot header file. */ 2 | /* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY 17 | * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 19 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef MULTIBOOT_HEADER 23 | #define MULTIBOOT_HEADER 1 24 | 25 | /* How many bytes from the start of the file we search for the header. */ 26 | #define MULTIBOOT_SEARCH 8192 27 | 28 | /* The magic field should contain this. */ 29 | #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 30 | 31 | /* This should be in %eax. */ 32 | #define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 33 | 34 | /* The bits in the required part of flags field we don't support. */ 35 | #define MULTIBOOT_UNSUPPORTED 0x0000fffc 36 | 37 | /* Alignment of multiboot modules. */ 38 | #define MULTIBOOT_MOD_ALIGN 0x00001000 39 | 40 | /* Alignment of the multiboot info structure. */ 41 | #define MULTIBOOT_INFO_ALIGN 0x00000004 42 | 43 | /* Flags set in the 'flags' member of the multiboot header. */ 44 | 45 | /* Align all boot modules on i386 page (4KB) boundaries. */ 46 | #define MULTIBOOT_PAGE_ALIGN 0x00000001 47 | 48 | /* Must pass memory information to OS. */ 49 | #define MULTIBOOT_MEMORY_INFO 0x00000002 50 | 51 | /* Must pass video information to OS. */ 52 | #define MULTIBOOT_VIDEO_MODE 0x00000004 53 | 54 | /* This flag indicates the use of the address fields in the header. */ 55 | #define MULTIBOOT_AOUT_KLUDGE 0x00010000 56 | 57 | /* Flags to be set in the 'flags' member of the multiboot info structure. */ 58 | 59 | /* is there basic lower/upper memory information? */ 60 | #define MULTIBOOT_INFO_MEMORY 0x00000001 61 | /* is there a boot device set? */ 62 | #define MULTIBOOT_INFO_BOOTDEV 0x00000002 63 | /* is the command-line defined? */ 64 | #define MULTIBOOT_INFO_CMDLINE 0x00000004 65 | /* are there modules to do something with? */ 66 | #define MULTIBOOT_INFO_MODS 0x00000008 67 | 68 | /* These next two are mutually exclusive */ 69 | 70 | /* is there a symbol table loaded? */ 71 | #define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 72 | /* is there an ELF section header table? */ 73 | #define MULTIBOOT_INFO_ELF_SHDR 0X00000020 74 | 75 | /* is there a full memory map? */ 76 | #define MULTIBOOT_INFO_MEM_MAP 0x00000040 77 | 78 | /* Is there drive info? */ 79 | #define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 80 | 81 | /* Is there a config table? */ 82 | #define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 83 | 84 | /* Is there a boot loader name? */ 85 | #define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 86 | 87 | /* Is there a APM table? */ 88 | #define MULTIBOOT_INFO_APM_TABLE 0x00000400 89 | 90 | /* Is there video information? */ 91 | #define MULTIBOOT_INFO_VIDEO_INFO 0x00000800 92 | 93 | #ifndef ASM_FILE 94 | 95 | typedef unsigned short multiboot_uint16_t; 96 | typedef unsigned int multiboot_uint32_t; 97 | typedef unsigned long long multiboot_uint64_t; 98 | 99 | struct multiboot_header 100 | { 101 | /* Must be MULTIBOOT_MAGIC - see above. */ 102 | multiboot_uint32_t magic; 103 | 104 | /* Feature flags. */ 105 | multiboot_uint32_t flags; 106 | 107 | /* The above fields plus this one must equal 0 mod 2^32. */ 108 | multiboot_uint32_t checksum; 109 | 110 | /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ 111 | multiboot_uint32_t header_addr; 112 | multiboot_uint32_t load_addr; 113 | multiboot_uint32_t load_end_addr; 114 | multiboot_uint32_t bss_end_addr; 115 | multiboot_uint32_t entry_addr; 116 | 117 | /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ 118 | multiboot_uint32_t mode_type; 119 | multiboot_uint32_t width; 120 | multiboot_uint32_t height; 121 | multiboot_uint32_t depth; 122 | }; 123 | 124 | /* The symbol table for a.out. */ 125 | struct multiboot_aout_symbol_table 126 | { 127 | multiboot_uint32_t tabsize; 128 | multiboot_uint32_t strsize; 129 | multiboot_uint32_t addr; 130 | multiboot_uint32_t reserved; 131 | }; 132 | typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; 133 | 134 | /* The section header table for ELF. */ 135 | struct multiboot_elf_section_header_table 136 | { 137 | multiboot_uint32_t num; 138 | multiboot_uint32_t size; 139 | multiboot_uint32_t addr; 140 | multiboot_uint32_t shndx; 141 | }; 142 | typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; 143 | 144 | struct multiboot_info 145 | { 146 | /* Multiboot info version number */ 147 | multiboot_uint32_t flags; 148 | 149 | /* Available memory from BIOS */ 150 | multiboot_uint32_t mem_lower; 151 | multiboot_uint32_t mem_upper; 152 | 153 | /* "root" partition */ 154 | multiboot_uint32_t boot_device; 155 | 156 | /* Kernel command line */ 157 | multiboot_uint32_t cmdline; 158 | 159 | /* Boot-Module list */ 160 | multiboot_uint32_t mods_count; 161 | multiboot_uint32_t mods_addr; 162 | 163 | union 164 | { 165 | multiboot_aout_symbol_table_t aout_sym; 166 | multiboot_elf_section_header_table_t elf_sec; 167 | } u; 168 | 169 | /* Memory Mapping buffer */ 170 | multiboot_uint32_t mmap_length; 171 | multiboot_uint32_t mmap_addr; 172 | 173 | /* Drive Info buffer */ 174 | multiboot_uint32_t drives_length; 175 | multiboot_uint32_t drives_addr; 176 | 177 | /* ROM configuration table */ 178 | multiboot_uint32_t config_table; 179 | 180 | /* Boot Loader Name */ 181 | multiboot_uint32_t boot_loader_name; 182 | 183 | /* APM table */ 184 | multiboot_uint32_t apm_table; 185 | 186 | /* Video */ 187 | multiboot_uint32_t vbe_control_info; 188 | multiboot_uint32_t vbe_mode_info; 189 | multiboot_uint16_t vbe_mode; 190 | multiboot_uint16_t vbe_interface_seg; 191 | multiboot_uint16_t vbe_interface_off; 192 | multiboot_uint16_t vbe_interface_len; 193 | }; 194 | typedef struct multiboot_info multiboot_info_t; 195 | 196 | struct multiboot_mmap_entry 197 | { 198 | multiboot_uint32_t size; 199 | multiboot_uint64_t addr; 200 | multiboot_uint64_t len; 201 | #define MULTIBOOT_MEMORY_AVAILABLE 1 202 | #define MULTIBOOT_MEMORY_RESERVED 2 203 | multiboot_uint32_t type; 204 | } __attribute__((packed)); 205 | typedef struct multiboot_mmap_entry multiboot_memory_map_t; 206 | 207 | struct multiboot_mod_list 208 | { 209 | /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ 210 | multiboot_uint32_t mod_start; 211 | multiboot_uint32_t mod_end; 212 | 213 | /* Module command line */ 214 | multiboot_uint32_t cmdline; 215 | 216 | /* padding to take it to 16 bytes (must be zero) */ 217 | multiboot_uint32_t pad; 218 | }; 219 | typedef struct multiboot_mod_list multiboot_module_t; 220 | 221 | #endif /* ! ASM_FILE */ 222 | 223 | #endif /* ! MULTIBOOT_HEADER */ 224 | -------------------------------------------------------------------------------- /src/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | * Internal definitions 6 | */ 7 | 8 | #define ISDIGIT(x) ((x) >= '0' && (x) <= '9') 9 | #define PRINT_BUF_LEN 65 10 | 11 | //Flags 12 | #define LEFT_JUST 0x1 13 | #define SHOW_SIGN 0x2 14 | #define HIDE_SIGN 0x4 15 | #define DISP_HEXD 0x8 16 | #define PADW_ZERO 0x10 17 | 18 | //Internal flags 19 | #define DATA_LONG 0x1 20 | #define DATA_LLGN 0x2 21 | #define DATA_SHRT 0x4 22 | 23 | /* 24 | * Internal Function Prototypes 25 | */ 26 | static int outchar(char ** out, char c); 27 | static int print(char ** out, const char * fmt, va_list args); 28 | static int prints(char ** out, const char * string, int32 width, int32 flags); 29 | static int printi(char ** out, int64 num, int32 base, int32 sign, int32 width, int32 flags, char convbase); 30 | 31 | /* 32 | * Function Definitions 33 | */ 34 | 35 | int printf(const char * fmt, ...) 36 | { 37 | if (!fmt) 38 | return 0; 39 | 40 | va_list list; 41 | va_start(list, fmt); 42 | 43 | int32 cw = print(NULL, fmt, list); 44 | 45 | va_end(list); 46 | 47 | return cw; 48 | } 49 | 50 | int vprintf(const char * fmt, va_list args) 51 | { 52 | if (!fmt || !args) 53 | return 0; 54 | 55 | va_list list = args; 56 | 57 | int32 cw = print(NULL, fmt, list); 58 | 59 | va_end(list); 60 | 61 | return cw; 62 | } 63 | 64 | int sprintf(char * buf, const char * fmt, ...) 65 | { 66 | if (!fmt || !buf) 67 | return 0; 68 | 69 | va_list list; 70 | va_start(list, fmt); 71 | 72 | int32 cw = print(&buf, fmt, list); 73 | 74 | va_end(list); 75 | 76 | return cw; 77 | } 78 | 79 | int vsprintf(char * buf, const char * fmt, va_list args) 80 | { 81 | if (!fmt || !buf || !args) 82 | return 0; 83 | 84 | va_list list = args; 85 | 86 | int32 cw = print(&buf, fmt, list); 87 | 88 | va_end(list); 89 | 90 | return cw; 91 | } 92 | 93 | /* 94 | * Internal function definitions 95 | */ 96 | 97 | static int outchar(char ** out, char c) 98 | { 99 | extern void vga_kputc(unsigned char); 100 | 101 | if (out) 102 | { 103 | **out = c; 104 | (*out)++; 105 | } 106 | else 107 | vga_kputc(c); 108 | 109 | return 1; 110 | } 111 | 112 | static int print(char ** out, const char * fmt, va_list args) 113 | { 114 | int32 cw = 0; 115 | 116 | for (; *fmt; ++fmt) 117 | { 118 | int32 parsing = FALSE, 119 | flags = 0, 120 | internal_flags = 0, 121 | width = 0; 122 | 123 | if (*fmt != '%') 124 | { 125 | cw += outchar(out, *fmt); 126 | continue; 127 | } 128 | 129 | ++fmt; 130 | 131 | if (*fmt == '\0') 132 | break; 133 | if (*fmt == '%') 134 | { 135 | cw += outchar(out, *fmt); 136 | continue; 137 | } 138 | 139 | /* FLAGS STAGE 140 | * '-' left, '+' show sign, ' ' no sign 141 | * '#' 0x, '0' pad using 0s 142 | */ 143 | 144 | parsing = TRUE; 145 | while (parsing) 146 | { 147 | switch (*fmt++) 148 | { 149 | case '-': 150 | flags |= LEFT_JUST; 151 | break; 152 | case '+': 153 | flags |= SHOW_SIGN; 154 | break; 155 | case ' ': 156 | flags |= HIDE_SIGN; 157 | break; 158 | case '#': 159 | flags |= DISP_HEXD; 160 | break; 161 | case '0': 162 | flags |= PADW_ZERO; 163 | break; 164 | default: 165 | parsing = FALSE; 166 | fmt--; 167 | break; 168 | } 169 | } 170 | 171 | /* WIDTH STAGE 172 | * '0'-'9' 173 | */ 174 | 175 | while (ISDIGIT(*fmt)) 176 | { 177 | width += *fmt - '0'; 178 | ++fmt; 179 | 180 | if (ISDIGIT(*fmt)) 181 | width *= 10; 182 | } 183 | 184 | /* BIT LENGTH STAGE 185 | * 'h' short, 'l' long 186 | * two longs ("ll") is long long 187 | */ 188 | 189 | parsing = TRUE; 190 | while (parsing) 191 | { 192 | switch (*fmt++) 193 | { 194 | case 'h': 195 | internal_flags |= DATA_SHRT; 196 | break; 197 | case 'l': 198 | if(internal_flags & DATA_LONG) 199 | internal_flags |= DATA_LLGN; 200 | else 201 | internal_flags |= DATA_LONG; 202 | break; 203 | default: 204 | parsing = FALSE; 205 | fmt--; 206 | break; 207 | } 208 | } 209 | 210 | /* Sanity check the flags for mutually exclusive ones 211 | * If there is conflict, resolve it by mistrusting the user 212 | * and disable all the conflicts 213 | */ 214 | 215 | if (flags & HIDE_SIGN && flags & SHOW_SIGN) 216 | { 217 | flags &= ~HIDE_SIGN; 218 | flags &= ~SHOW_SIGN; 219 | } 220 | 221 | if (internal_flags & DATA_SHRT && (internal_flags & DATA_LONG || internal_flags & DATA_LLGN)) 222 | internal_flags = 0; 223 | 224 | if (flags & DISP_HEXD && flags & SHOW_SIGN) 225 | { 226 | flags &= ~DISP_HEXD; 227 | flags &= ~SHOW_SIGN; 228 | } 229 | 230 | /* FORMAT SPECIFIER STAGE 231 | * 'c' print char, 'd' or 'i' print signed int type 232 | * 's' print null terminated string or '(null)' if pointer NULL 233 | * 'u' print unsigned int type 234 | * 'x' prints unsigned in as hex 'X' capitalized 235 | * 'p' print using the pointer type 236 | */ 237 | 238 | char buf[2] = {0}; 239 | char * str = 0; 240 | 241 | switch (*fmt) 242 | { 243 | case 's': 244 | flags &= LEFT_JUST; //the only flag used 245 | str = (char *)va_arg(args, int32); 246 | cw += prints(out, str ? str : "(null)", width, flags); 247 | break; 248 | case 'c': 249 | flags &= LEFT_JUST; 250 | buf[0] = va_arg(args, int32); 251 | buf[1] = '\0'; 252 | cw += prints(out, buf, width, flags); 253 | break; 254 | case 'd': 255 | case 'i': 256 | if (internal_flags & DATA_LLGN) 257 | cw += printi(out, va_arg(args, int64), 10, TRUE, width, flags, 'a'); 258 | else if (internal_flags & DATA_SHRT) 259 | cw += printi(out, va_arg(args, int16), 10, TRUE, width, flags, 'a'); 260 | else 261 | cw += printi(out, va_arg(args, int32), 10, TRUE, width, flags, 'a'); 262 | break; 263 | case 'u': 264 | if (internal_flags & DATA_LLGN) 265 | cw += printi(out, va_arg(args, uint64), 10, FALSE, width, flags, 'a'); 266 | else if (internal_flags & DATA_SHRT) 267 | cw += printi(out, va_arg(args, uint16), 10, FALSE, width, flags, 'a'); 268 | else 269 | cw += printi(out, va_arg(args, uint32), 10, FALSE, width, flags, 'a'); 270 | break; 271 | case 'x': 272 | if (internal_flags & DATA_LLGN) 273 | cw += printi(out, va_arg(args, uint64), 16, FALSE, width, flags, 'a'); 274 | else if (internal_flags & DATA_SHRT) 275 | cw += printi(out, va_arg(args, uint16), 16, FALSE, width, flags, 'a'); 276 | else 277 | cw += printi(out, va_arg(args, uint32), 16, FALSE, width, flags, 'a'); 278 | break; 279 | case 'p': 280 | flags = 0, width = 0; //reset flags 281 | flags = DISP_HEXD | PADW_ZERO; 282 | 283 | if (internal_flags & DATA_LLGN) 284 | width = sizeof(int64)*2; 285 | else if (internal_flags & DATA_SHRT) 286 | width = sizeof(int16)*2; 287 | else 288 | width = sizeof(int32)*2; 289 | case 'X': //fall through 290 | if (internal_flags & DATA_LLGN) 291 | cw += printi(out, va_arg(args, uint64), 16, FALSE, width, flags, 'A'); 292 | else if (internal_flags & DATA_SHRT) 293 | cw += printi(out, va_arg(args, uint16), 16, FALSE, width, flags, 'A'); 294 | else 295 | cw += printi(out, va_arg(args, uint32), 16, FALSE, width, flags, 'A'); 296 | break; 297 | default: 298 | cw += outchar(out, *fmt); 299 | break; 300 | } 301 | 302 | } 303 | 304 | return cw; 305 | } 306 | 307 | static int prints(char ** out, const char * string, int32 width, int32 flags) 308 | { 309 | int32 cw = 0, len = 0; 310 | char padc = ' '; 311 | char * hexd = "0x"; 312 | const char * ptr = string; 313 | 314 | while(*ptr++) len++; //string length 315 | 316 | if(len >= width) //dont care 317 | width = 0; 318 | else 319 | { 320 | if(flags & PADW_ZERO) 321 | padc = '0'; 322 | width -= len; //how much padding we need 323 | } 324 | 325 | if (flags & LEFT_JUST) 326 | { 327 | if (flags & DISP_HEXD) 328 | while (*hexd) 329 | cw += outchar(out, *hexd++); 330 | 331 | if (width) 332 | { 333 | if (flags & PADW_ZERO) //padding comes first 334 | { 335 | while (width--) 336 | cw += outchar(out, padc); 337 | 338 | while (*string) 339 | cw += outchar(out, *string++); 340 | 341 | } 342 | else //padding comes after everything 343 | { 344 | while (*string) 345 | cw += outchar(out, *string++); 346 | 347 | while (width--) 348 | cw += outchar(out, padc); 349 | } 350 | } 351 | else //no padding 352 | while (*string) 353 | cw += outchar(out, *string++); 354 | } 355 | else //right justified 356 | { 357 | if (width && !(flags & PADW_ZERO)) 358 | { 359 | while (width--) 360 | cw += outchar(out, padc); 361 | 362 | if (flags & DISP_HEXD) 363 | while (*hexd) 364 | cw += outchar(out, *hexd++); 365 | } 366 | else if(width && flags & PADW_ZERO) 367 | { 368 | if(flags & DISP_HEXD) 369 | while (*hexd) 370 | cw += outchar(out, *hexd++); 371 | 372 | while (width--) 373 | cw += outchar(out, padc); 374 | } 375 | else //no padding 376 | if(flags & DISP_HEXD) 377 | while (*hexd) 378 | cw += outchar(out, *hexd++); 379 | 380 | while (*string) 381 | cw += outchar(out, *string++); 382 | } 383 | 384 | return cw; 385 | } 386 | 387 | static int printi(char ** out, int64 num, int32 base, int32 sign, int32 width, int32 flags, char convbase) 388 | { 389 | int32 cw = 0, neg = FALSE; 390 | uint64 u; 391 | char buffer[PRINT_BUF_LEN] = {0}; //longest representation (base 2, 64 bits) 392 | char * iter; 393 | 394 | u = num; 395 | 396 | if(base < 2) //error check 397 | return 0; 398 | 399 | if (base != 16) //show hexd only for base 16 400 | flags &= ~DISP_HEXD; 401 | 402 | if (num == 0) //short circuit and prevents errors 403 | { 404 | buffer[0] = '0'; 405 | buffer[1] = '\0'; 406 | return prints(out, buffer, width, flags); 407 | } 408 | 409 | if (sign && base == 10 && num < 0) 410 | { 411 | neg = TRUE; 412 | u = -num; //restore sanity 413 | } 414 | 415 | 416 | iter = buffer + PRINT_BUF_LEN-1; 417 | *iter-- = '\0'; 418 | 419 | while (u) //everything is going in reverse 420 | { 421 | int32 rem = u % base; 422 | char c; 423 | 424 | if (rem >= 10) 425 | c = rem - 10 + convbase; 426 | else 427 | c = rem + '0'; 428 | 429 | *iter-- = c; 430 | 431 | u /= base; 432 | } 433 | 434 | if (!(flags & HIDE_SIGN) && base == 10) 435 | { 436 | if (flags & PADW_ZERO) //sign always first 437 | { 438 | if (flags & SHOW_SIGN && !neg) 439 | { 440 | cw += outchar(out, '+'); 441 | width--; 442 | } 443 | 444 | if (neg) 445 | { 446 | cw += outchar(out, '-'); 447 | width--; 448 | } 449 | } 450 | else 451 | { 452 | if (flags & SHOW_SIGN && !neg) 453 | *iter-- = '+'; 454 | 455 | if (neg) 456 | *iter-- = '-'; 457 | } 458 | } 459 | 460 | return cw + prints(out, ++iter, width, flags); 461 | } 462 | --------------------------------------------------------------------------------