/** * main.c * * Main code for HybOS. * * I spent a lot of time cleaning this damned thing up, so if you * are even REMOTELY thinking of modifying it, you better make * sure you follow the same design pattern as you see now. I am sick * of cleaning up c-style comments because some dumbass is too fucking * lazy to use the PROPER c89-style comments. This is C people, not C++. * * Exports: * void printf(const char *fmt, ...); * int main(void); * * Imports: * kstart.asm getvect(); * kstart.asm setvect(); * video.c console_t _vc[]; * video.c blink(); * video.c init_video(); * kbd.c keyboard_irq(); * kbd.c kbd_hw_init(); * kbd.c init_keyboard(); * sched.c schedule(); * sched.c init_tasks(); * debug.c dump_regs(); * * FIXME: * needs to be renamed to kernel.c */ #include <stdarg.h> /* va_list, va_start(), va_end() */ /*#include <string.h>*/ /* NULL */ #include <stdio.h> /* NULL */ #include <x86.h> /* disable() */ #include <_printf.h> /* do_printf() */ #include <_malloc.h> #include <stdio.h> #include <stdint.h> #include <signal.h> #include "_krnl.h" /* regs_t */ #include "bootlog.h" /* klog() */ /** * FIXME * * These externs and declares are a fucking mess and * need to be ported to their own headers for portability */ /** * Imports */ void getvect(vector_t *v, unsigned vect_num); void setvect(vector_t *v, unsigned vect_num); extern console_t _vc[]; void blink(void); void putch(unsigned c); void init_video(void); void keyboard_irq(void); //void kbd_hw_int(void); void init_keyboard(void); void schedule(void); void init_tasks(void); void dump_regs(regs_t *regs); void _mm_physical_init(void); unsigned _mm_physical_alloc(void); void _mm_physical_free(unsigned page); void _mm_page_copy_byte(uint32_t dest, uint32_t src); void _mm_page_copy_word(uint32_t dest, uint32_t src); void _mm_page_copy_dword(uint32_t dest, uint32_t src); /*void init_cpu(void);*/ /** * printf/kprintf helper */ static int kprintf_help(unsigned c, void **ptr) { /** * Leave this for now */ ptr = ptr; putch(c); return 0; } /** * Format output and print it to stdout (vtty0) * Just like on any other operating system */ /*void printf(const char *fmt, ...) { va_list args; va_start(args, fmt); (void)do_printf(fmt, args, kprintf_help, NULL); va_end(args); }*/ void kprintf(const char *fmt, ...) { va_list args; va_start(args, fmt); (void)do_printf(fmt, args, kprintf_help, NULL); va_end(args); } /** * Format output and print it to stdout (vtty0) * Just like on any other operating system */ void printk(const char *fmt, ...) { va_list args; /** * TODO * * Select vtty0 */ va_start(args, fmt); (void)do_printf(fmt, args, kprintf_help, NULL); va_end(args); } /** * Oh yeah, the fun function ;) */ void panic(const char *fmt, ...) { va_list args; disable(); /* interrupts off */ va_start(args, fmt); _vc[0].attrib = 15; printf("\n\npanic: "); (void)do_printf(fmt, args, kprintf_help, NULL); printf("\n\nSystem halted."); __asm__ __volatile__ ("hlt"); while(1) /* freeze */; } /** * Called when a kernel fault is detected. This does not * (normally) get called when something goes awry in * user-space, therefore it is designed for kernel-space */ void fault(regs_t *regs) { struct exception { char *message; int signal; int processor; }; static const struct exception ex[] = { {"Divide error", SIGFPE, 86}, {"Debug exception", SIGTRAP, 86}, {"Nonmaskable interrupt (NMI)", SIGBUS, 86}, {"Breakpoint (INT3)", SIGEMT, 86}, {"Overflow (INTO)", SIGFPE, 186}, {"Bounds check", SIGFPE, 186}, {"Invalid opcode", SIGILL, 186}, {"Coprocessor not available", SIGFPE, 186}, {"Double fault", SIGBUS, 286}, {"Coprocessor segment overrun", SIGSEGV, 286}, {"Invalid TSS", SIGSEGV, 286}, {"Segment not present", SIGSEGV, 286}, {"Stack exception", SIGSEGV, 286}, {"General Protection Fault", SIGSEGV, 286}, {"Page fault", SIGSEGV, 386}, {NULL, SIGILL, 0}, {"Coprocessor error", SIGFPE, 386}, {"Alignment check",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"??",0,0}, {"IRQ0",0,0}, {"IRQ1",0,0}, {"IRQ2",0,0}, {"IRQ3",0,0}, {"IRQ4",0,0}, {"IRQ5",0,0}, {"IRQ6",0,0}, {"IRQ7",0,0}, {"IRQ8",0,0}, {"IRQ9",0,0}, {"IRQ10",0,0}, {"IRQ11",0,0}, {"IRQ12",0,0}, {"IRQ13",0,0}, {"IRQ14",0,0}, {"IRQ15",0,0}, {"syscall",0,0} }; switch(regs->which_int) { /** * this handler installed at compile-time * Keyboard handler is installed at run-time (see below) */ case 0x20: /* timer IRQ 0 */ //blink(); /** * reset hardware interrupt at 8259 chip */ outportb(0x20, 0x20); break; default: _vc[0].attrib = 15; printf("\n\npanic: Exception 0x%08X", regs->which_int); if(regs->which_int <= sizeof(ex) / sizeof(ex[0].message)) printf(" (%s)", ex[regs->which_int].message); printf("\n"); dump_regs(regs); printf("\n\nSystem halted."); __asm__ __volatile__ ("hlt"); break; } } /** * ?? */ static void init_8259s(void) { static const unsigned irq0_int = 0x20, irq8_int = 0x28; /** * Initialization Control Word #1 (ICW1) */ outportb(0x20, 0x11); outportb(0xA0, 0x11); /** * ICW2: * route IRQs 0-7 to INTs 20h-27h */ outportb(0x21, irq0_int); /** * route IRQs 8-15 to INTs 28h-2Fh */ outportb(0xA1, irq8_int); /** * ICW3 */ outportb(0x21, 0x04); outportb(0xA1, 0x02); /** * ICW4 */ outportb(0x21, 0x01); outportb(0xA1, 0x01); /** * enable IRQ0 (timer) and IRQ1 (keyboard) */ outportb(0x21, ~0x03); outportb(0xA1, ~0x00); } /** * MinGW32 */ #ifdef __WIN32__ #if __GNUC__<3 #error Do not use MinGW GCC 2.x with NASM #endif int __main(void) { return 0; } void _alloca(void) { } #endif /** * malloc, realloc, free, etc */ static char *g_heap_bot, *g_kbrk, *g_heap_top; static void dump_heap(void) { unsigned blks_used = 0, blks_free = 0; size_t bytes_used = 0, bytes_free = 0; malloc_t *m; int total; kprintf("===============================================\n"); for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next) { printk("block %5p: %6u bytes %s\n", m, m->size, m->used ? "used" : "free"); if(m->used) { blks_used++; bytes_used += m->size; } else { blks_free++; bytes_free += m->size; } } kprintf("blocks: %6u used, %6u free, %6u total\n", blks_used, blks_free, blks_used + blks_free); kprintf(" bytes: %6u used, %6u free, %6u total\n", bytes_used, bytes_free, bytes_used + bytes_free); kprintf("g_heap_bot=0x%p, g_kbrk=0x%p, g_heap_top=0x%p\n", g_heap_bot, g_kbrk, g_heap_top); total = (bytes_used + bytes_free) + (blks_used + blks_free) * sizeof(malloc_t); if(total != g_kbrk - g_heap_bot) kprintf("*** some heap memory is not accounted for\n"); kprintf("===============================================\n"); } void dumpheapk(void) { dump_heap(); } /** * POSIX sbrk() looks like this * void *sbrk(int incr); * * Mine is a bit different so I can signal the calling function * if more memory than desired was allocated (e.g. in a system with paging) * If your kbrk()/sbrk() always allocates the amount of memory you ask for, * this code can be easily changed. * * int brk( void *sbrk( void *kbrk( * function void *adr); int delta); int *delta); * ---------------------- ------------ ------------ ------------- * POSIX? yes yes NO * return value if error -1 -1 NULL * get break value . sbrk(0) int x=0; kbrk(&x); * set break value to X brk(X) sbrk(X - sbrk(0)) int x=X, y=0; kbrk(&x) - kbrk(&y); * enlarge heap by N bytes . sbrk(+N) int x=N; kbrk(&x); * shrink heap by N bytes . sbrk(-N) int x=-N; kbrk(&x); * can you tell if you're * given more memory * than you wanted? no no yes */ static void *kbrk(int *delta) { static char heap[HEAP_SIZE]; char *new_brk, *old_brk; /** * heap doesn't exist yet */ if(g_heap_bot == NULL) { g_heap_bot = g_kbrk = heap; g_heap_top = g_heap_bot + HEAP_SIZE; } new_brk = g_kbrk + (*delta); /** * too low: return NULL */ if(new_brk < g_heap_bot) return NULL; /** * too high: return NULL */ if(new_brk >= g_heap_top) return NULL; /** * success: adjust brk value... */ old_brk = g_kbrk; g_kbrk = new_brk; /** * ...return actual delta... (for this sbrk(), they are the same) * (*delta) = (*delta); * ...return old brk value */ return old_brk; } /** * malloc() and free() use g_heap_bot, but not g_kbrk nor g_heap_top */ void *kmalloc(size_t size) { unsigned total_size; malloc_t *m, *n; int delta; if(size == 0) return NULL; total_size = size + sizeof(malloc_t); /** * search heap for free block (FIRST FIT) */ m = (malloc_t *)g_heap_bot; /** * g_heap_bot == 0 == NULL if heap does not yet exist */ if(m != NULL) { if(m->magic != MALLOC_MAGIC) { /*printf("*** kernel heap is corrupt in kmalloc()\n");*/ panic("kernel heap is corrupt in malloc()"); return NULL; } for(; m->next != NULL; m = m->next) { if(m->used) continue; /** * size == m->size is a perfect fit */ if(size == m->size) m->used = 1; else { /** * otherwise, we need an extra sizeof(malloc_t) bytes for the header * of a second, free block */ if(total_size > m->size) continue; /** * create a new, smaller free block after this one */ n = (malloc_t *)((char *)m + total_size); n->size = m->size - total_size; n->next = m->next; n->magic = MALLOC_MAGIC; n->used = 0; /** * reduce the size of this block and mark it used */ m->size = size; m->next = n; m->used = 1; } return (char *)m + sizeof(malloc_t); } } /** * use kbrk() to enlarge (or create!) heap */ delta = total_size; n = kbrk(&delta); /** * uh-oh */ if(n == NULL) return NULL; if(m != NULL) m->next = n; n->size = size; n->magic = MALLOC_MAGIC; n->used = 1; /** * did kbrk() return the exact amount of memory we wanted? * cast to make "gcc -Wall -W ..." shut the hell up */ if((int)total_size == delta) n->next = NULL; else { /** * it returned more than we wanted (it will never return less): * create a new, free block */ m = (malloc_t *)((char *)n + total_size); m->size = delta - total_size - sizeof(malloc_t); m->next = NULL; m->magic = MALLOC_MAGIC; m->used = 0; n->next = m; } return (char *)n + sizeof(malloc_t); } void kfree(void *blk) { malloc_t *m, *n; /** * get address of header */ m = (malloc_t *)((char *)blk - sizeof(malloc_t)); if(m->magic != MALLOC_MAGIC) { /*printf("*** attempt to kfree() block at 0x%p with bad magic value\n", blk);*/ panic("attempt to free() block at 0x%p with bad magic value", blk); return; } /** * find this block in the heap */ n = (malloc_t *)g_heap_bot; if(n->magic != MALLOC_MAGIC) { /*printf("*** kernel heap is corrupt in kfree()\n");*/ panic("kernel heap is corrupt in free()"); return; } for(; n != NULL; n = n->next) { if(n == m) break; } /** * not found? bad pointer or no heap or something else? */ if(n == NULL) { /*printf("*** attempt to kfree() block at 0x%p that is not in the heap\n", blk);*/ panic("attempt to free() block at 0x%p that is not in the heap", blk); return; } /** * free the block */ m->used = 0; /** * coalesce adjacent free blocks * Hard to spell, hard to do */ for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next) { while(!m->used && m->next != NULL && !m->next->used) { /** * resize this block */ m->size += sizeof(malloc_t) + m->next->size; /** * merge with next block */ m->next = m->next->next; } } } void testheap(void) { //int i; //char *t; //kprintf("before char *t = kmalloc((size_t *)25):\n"); //dump_heap(); //t = kmalloc(25); //strcpy(t, "123456789012345678901234"); //kprintf("after char *t = kmalloc((size_t *)25):\n"); //dump_heap(); //kfree(t); //kprintf("after kfree(t):\n"); //dump_heap(); //kprintf("before char *t = kmalloc((size_t *)25):\n"); kprintf("Unable to run testheap -- kmalloc() is broken.\n"); } void *krealloc(void *blk, size_t size) { void *new_blk; malloc_t *m; /** * size == 0: free block */ if(size == 0) { if(blk != NULL) kfree(blk); new_blk = NULL; } else { /** * allocate new block */ new_blk = kmalloc(size); /** * if allocation OK, and if old block exists, copy old block to new */ if(new_blk != NULL && blk != NULL) { m = (malloc_t *)((char *)blk - sizeof(malloc_t)); if(m->magic != MALLOC_MAGIC) { /*printf("*** attempt to krealloc() block at 0x%p with bad magic value\n", blk);*/ panic("attempt to realloc() block at 0x%p with bad magic value", blk); return NULL; } /** * copy minimum of old and new block sizes */ if(size > m->size) size = m->size; memcpy(new_blk, blk, size); /** * free the old block */ kfree(blk); } } return new_blk; } void keyboardISR(void); int main(void) { /** * keyboard interrupt init */ vector_t v; unsigned i; init_video(); init_keyboard(); init_8259s(); /** * XXX: * i know this is a very ugly way of doing this, * however it is the only way it can be done for now. * in the future, i will implement a kprintf function * whose sole purpose will be writing boot messages. * * Also, the color codes need to be mapped to constants * in order to make using them a hell of a lot easier. */ klog("init", "Installing keyboard interrupt handler", K_KLOG_PENDING, &_vc[0]); /* we don't save the old vector */ v.eip = (unsigned)keyboard_irq; v.access_byte = 0x8E; /* present, ring 0, '386 interrupt gate */ setvect(&v, 0x21); klog(NULL, NULL, K_KLOG_SUCCESS, &_vc[0]); /*init_tasks();*/ klog("init", "Enabling hardware interrupts", K_KLOG_PENDING, &_vc[0]); enable(); /*for(i = 0; i < 0xFFFFFFF; i++);*/ klog(NULL, NULL, K_KLOG_SUCCESS, &_vc[0]); /** * Initialize memory management */ /*_mm_init();*/ /** * finished init, time for some gooey ;) */ printf(" _ _ _ _ ____ _____ ___ "); printf(" ( )_( )( \\/ )( _ \\( _ )/ __) "); printf(" ) _ ( \\ / ) _ < )(_)( \\__ \\ "); printf(" (_) (_) (__) (____/(_____)(___/ \n"); printf(" Hybrid Operating System (HybOS) \n"); /** * XXX: debug only */ printf("ALT + F1 - F8 for virtual terminals\n"); printf("Three finger salute to restart\n"); printf("More work needs to be done\n"); printf("$ "); /** * fork (kfork()) control over to a shell */ /*init_shell();*/ /** * idle task/thread */ while(1) { schedule(); } return 0; }