#include #include "stdint.h" #include "stdlib.h" #include #include "vgatext.h" #include "serial.h" #include "console.h" #include "stdlib.h" #include "string.h" #include "stdio.h" #include "interrupts.h" #include "driver.h" #include "ps2keyboard.h" #include "ps2mouse.h" #include "video.h" #include "pci.h" #include "memorymanagement.h" #include "network.h" #include "setjmp.h" #include "kernel.h" // TODO: move away from main! #include "graphics_context.h" #include "widget.h" #include "composite_widget.h" #include "desktop.h" #include "text_widget.h" #include "window.h" #include "tasks.h" static jmp_buf panic_jmp_buf; static void handle_keyboard_event( keyboard_event_t *event, void *context ); static void handle_mouse_event( mouse_event_t *event, void *context ); static void handle_network_event( network_event_t *event, void *context ); typedef enum { MODE_TEXT, MODE_GRAPHICS } mode_t; typedef struct { vga_text_t vga_text; driver_manager_t driver_manager; task_manager_t task_manager; memory_manager_t memory_manager; video_t *video; mode_t mode; keyboard_t *keyboard; mouse_t *mouse; desktop_t desktop; window_t window1; window_t window2; window_t window3; text_widget_t widget3; network_t *network; } global_context_t; static global_context_t global_context; static void refresh_screen( void ); static void switch_to_graphics_mode( global_context_t *global_context ); static void switch_to_text_mode( global_context_t *global_context ); static void print_task_status( global_context_t *global_context ); static void print_memory_status( global_context_t *global_context ); // must be first entry in kernel.bin (0x8800) as stage 2 of // the boot loader expects the entry point to be here! void kernel_main( void ) { global_context.mode = MODE_TEXT; serial_t serial; serial_init( &serial ); vga_text_init( &global_context.vga_text ); vga_text_set_color( &global_context.vga_text, VGA_TEXT_COLOR_LIGHT_GREY ); vga_text_set_background_color( &global_context.vga_text, VGA_TEXT_COLOR_BLACK ); console_t console; console_init( &console ); console_add_vga_text_output( &console, &global_context.vga_text ); console_add_serial_output( &console, &serial ); // initialize the early console of the kernel __stdio_set_console( &console ); puts( "Started early kernel console" ); // TODO: get those values somehow from the boot loader printf( "Kernel code and data is at 0x%X, kernel stack at 0x%X\n", 0x8800, 0x90000 ); // initialize memory management first, we may need it // TODO: get memupper from multiboot header (but this needs // a multiboot compliant entry function first) or use BIOS // functions to determine size of memory, for now assume we // have at least, for now use the memory above 1 MB, 128 KB // heap is enough for what we are doing in the kernel plus // eventual z buffering the VGA graphic mode uint32_t heap_offset = 0x01000000; size_t heap_size = 128 * 1024; memory_manager_init( &global_context.memory_manager, heap_offset, heap_size ); __stdlib_set_memory_manager( &global_context.memory_manager ); printf( "Kernel heap at 0x%X, size 0x%X\n", heap_offset, heap_size ); // exit point in case of kernel panic, do this as soon as // possible, as soon we have an early console we can croak on if( setjmp( panic_jmp_buf ) > 0 ) { // reset the panic jump point to the really end to avoid // endless loops when something bad happens while shutting // down if( setjmp( panic_jmp_buf ) > 0 ) { goto ABORT; } goto TERMINATE; } // task manager for VGA graphic refresh task_manager_init( &global_context.task_manager ); task_t taskRefreshScreen; task_init( &taskRefreshScreen, GDT_CODE_SEGMENT_SELECTOR, &refresh_screen ); task_manager_add_task( &global_context.task_manager, &taskRefreshScreen ); puts( "Initializing interrupts" ); interrupt_t interrupt; interrupts_init( &interrupt, GDT_CODE_SEGMENT_SELECTOR, &global_context.task_manager ); puts( "Initializing drivers" ); driver_manager_init( &global_context.driver_manager ); // hard-wired drivers global_context.keyboard = (keyboard_t *)malloc( sizeof( ps2keyboard_t ) ); ps2keyboard_init( (ps2keyboard_t *)global_context.keyboard, &handle_keyboard_event, &interrupt, (void *)&global_context ); driver_manager_add_driver( &global_context.driver_manager, (driver_t *)global_context.keyboard ); global_context.mouse = (mouse_t *)malloc( sizeof( ps2mouse_t ) ); ps2mouse_init( (ps2mouse_t *)global_context.mouse, &handle_mouse_event, &interrupt, (void *)&global_context ); ((mouse_vtable_t *)(global_context.mouse->base.vtable))->set_resolution( global_context.mouse, global_context.vga_text.res_x, global_context.vga_text.res_y ); driver_manager_add_driver( &global_context.driver_manager, (driver_t *)global_context.mouse ); // dynamically detected and registered drivers puts( "Detecting devices via PCI.." ); pci_controller_t pci_controller; pci_controller_init( &pci_controller ); pci_controller_scan_and_register( &pci_controller, &global_context.driver_manager, &interrupt, &global_context ); // TODO: merge with the text mode vga_text_t object // TODO: introduce a graphics driver type, don't cast diretly here // TODO: also, what to do if there is more than one driver? global_context.video = (video_t *)driver_manager_find_driver( &global_context.driver_manager, DRIVER_TYPE_VIDEO ); // network driver testing global_context.network = (network_t *)driver_manager_find_driver( &global_context.driver_manager, DRIVER_TYPE_NETWORK ); if( global_context.network == NULL ) { puts( "No suitable network driver found" ); } else { ((network_vtable_t *)( global_context.network->base.vtable ))->register_handler( global_context.network, handle_network_event ); } puts( "Activating drivers" ); driver_manager_activate_all( &global_context.driver_manager ); // TODO: later, disable VGA text console in stdio and add the // graphical one.. puts( "Enabling interrupt handing now.." ); // enabling tasks, this is our really last execution // in the main kernel thread, we never return (but for // termination longjmp to TERMINATE) interrupts_enable( ); // we get back here on termination and kernel panic via goto after // longjmp // TODO: make sure we run this part of the code with the original // early kernel stack and not with a task stack! TERMINATE: puts( "Deactivating drivers" ); driver_manager_deactivate_all( &global_context.driver_manager ); driver_manager_deinit( &global_context.driver_manager ); ABORT: puts( "Kernel terminated" ); } void kernel_panic( const char *format, ... ) { (void)printf( "\n*** KERNEL PANIC ***\n" ); va_list args; va_start( args, format ); (void)vprintf( format, args ); va_end( args ); puts( "" ); longjmp( panic_jmp_buf, 47 /* TODO: error code */ ); } static void handle_keyboard_event( keyboard_event_t *event, void *context ) { static int pos = 0; static char buf[80]; global_context_t *global_context = (global_context_t *)context; vga_text_t *vga_text = &global_context->vga_text; driver_manager_t *driver_manager = &global_context->driver_manager; desktop_t *desktop = &global_context->desktop; switch( global_context->mode ) { case MODE_TEXT: vga_text_hide_mouse_cursor( vga_text ); if( event->type == KEYBOARD_EVENT_TYPE_KEY_PRESSED ) { if( event->key == KEYBOARD_KEY_ASCII ) { if( event->ascii_key == '\n' ) { buf[pos] = '\0'; pos = 0; puts( "" ); if( strcmp( buf, "help" ) == 0 ) { puts( "halt - terminate os" ); puts( "mem - show memory usage" ); puts( "driver - show status of drivers" ); puts( "task - show task status" ); puts( "clear - clear screen" ); puts( "mode - switch between text/graphics mode" ); } else if( strcmp( buf, "halt" ) == 0 ) { longjmp( panic_jmp_buf, 0 ); } else if( strcmp( buf, "mem" ) == 0 ) { print_memory_status( global_context ); } else if( strcmp( buf, "task" ) == 0 ) { print_task_status( global_context ); } else if( strcmp( buf, "driver" ) == 0 ) { driver_manager_print_info_all( driver_manager ); } else if( strcmp( buf, "clear" ) == 0 ) { vga_text_clear_screen( vga_text ); } else if( strcmp( buf, "mode" ) == 0 ) { switch_to_graphics_mode( global_context ); } else { printf( "ERR: Unknown pre-boot command '%s'\n", buf ); } } else if( event->ascii_key == '\b' ) { if( pos > 0 ) { buf[--pos] = '\0'; printf( "\n%s", buf ); } } else { printf( "%c", event->ascii_key ); buf[pos++] = event->ascii_key; } } } break; case MODE_GRAPHICS: if( event->type == KEYBOARD_EVENT_TYPE_KEY_PRESSED ) { if( event->key == KEYBOARD_KEY_ASCII ) { ((widget_t *)desktop)->vtable->on_key_down( &global_context->desktop, event->ascii_key ); } else if( event->key == KEYBOARD_KEY_ESC ) { switch_to_text_mode( global_context ); } } break; } } static void handle_mouse_event( mouse_event_t *event, void *context ) { global_context_t *global_context = (global_context_t *)context; vga_text_t *vga_text = &global_context->vga_text; desktop_t *desktop = &global_context->desktop; switch( global_context->mode ) { case MODE_TEXT: switch( event->type ) { case MOUSE_EVENT_TYPE_BUTTON_UP: vga_text_hide_mouse_cursor( vga_text ); printf( "MOUSE UP %d AT X:%d, Y:%d\n", event->button, event->cursor_x, event->cursor_y ); vga_text_show_mouse_cursor( vga_text ); break; case MOUSE_EVENT_TYPE_BUTTON_DOWN: vga_text_hide_mouse_cursor( vga_text ); printf( "MOUSE DOWN %d AT X:%d, Y:%d\n", event->button, event->cursor_x, event->cursor_y ); vga_text_show_mouse_cursor( vga_text ); break; case MOUSE_EVENT_TYPE_MOVE: vga_text_move_mouse_cursor( vga_text, event->cursor_x, event->cursor_y ); vga_text_show_mouse_cursor( vga_text ); break; } break; case MODE_GRAPHICS: switch( event->type ) { case MOUSE_EVENT_TYPE_BUTTON_UP: if( event->button == MOUSE_BUTTON_LEFT ) { ((widget_t *)desktop)->vtable->on_mouse_up( desktop, event->cursor_x, event->cursor_y ); } break; case MOUSE_EVENT_TYPE_BUTTON_DOWN: if( event->button == MOUSE_BUTTON_LEFT ) { ((widget_t *)desktop)->vtable->on_mouse_down( desktop, event->cursor_x, event->cursor_y ); } break; case MOUSE_EVENT_TYPE_MOVE: ((widget_t *)desktop)->vtable->on_mouse_move( desktop, event->old_cursor_x, event->old_cursor_y, event->cursor_x, event->cursor_y ); break; } break; } } static void refresh_screen( void ) { while( true ) { kernel_halt( ); switch( global_context.mode ) { case MODE_TEXT: // nothing to draw or refresh in text mode, the // VGA card is doing that for us break; case MODE_GRAPHICS: // for now we must avoid races with the interrupt // events originating especially from the mouse interrupts_disable( ); // as vga_t is equals to the graphical context for now ((widget_t *)&global_context.desktop)->vtable->draw( &global_context.desktop, global_context.video ); ((video_vtable_t *)( global_context.video->base.vtable))->refresh( global_context.video ); interrupts_enable( ); break; } } } static void switch_to_graphics_mode( global_context_t *global_context ) { video_t *video = global_context->video; vga_text_t *vga_text = &global_context->vga_text; mouse_t *mouse = global_context->mouse; desktop_t *desktop = &global_context->desktop; video_mode_t mode = video_make_mode( VIDEO_MODE_TYPE_GRAPHICS, 320, 200, 8 ); if( ((video_vtable_t *)(video->base.vtable))->set_mode( video, mode ) ) { vga_text_save( vga_text ); desktop_init( desktop, 320, 200, VIDEO_RGB_COLOR_BLUE ); window_init( &global_context->window1, (widget_t *)desktop, 60, 90, 60, 70, VIDEO_RGB_COLOR_GRAY ); // TODO: this looks clunky! ((composite_widget_vtable_t *)global_context->desktop.base.base.vtable)->add_child( desktop, (widget_t *)&global_context->window1 ); window_init( &global_context->window2, (widget_t *)desktop, 130, 80, 60, 70, VIDEO_RGB_COLOR_GREEN ); ((composite_widget_vtable_t *)global_context->desktop.base.base.vtable)->add_child( desktop, (widget_t *)&global_context->window2 ); char s[100]; char *p = s; for( char c = ' '; c <= '~'; c++ ) { *p++ = c; } *p = '\0'; window_init( &global_context->window3, (widget_t *)desktop, 55, 5, video->mode->x - 65, 100, VIDEO_RGB_COLOR_CYAN ); ((composite_widget_vtable_t *)global_context->desktop.base.base.vtable)->add_child( desktop, (widget_t *)&global_context->window3 ); text_widget_init( &global_context->widget3, (widget_t *)&global_context->window3, 1, 1, global_context->window3.base.base.w - 2, global_context->window3.base.base.h - 2, VIDEO_RGB_COLOR_RED, s ); ((composite_widget_vtable_t *)global_context->window3.base.base.vtable)->add_child( (composite_widget_t *)&global_context->window3, (widget_t *)&global_context->widget3 ); ((mouse_vtable_t *)(mouse->base.vtable))->set_resolution( mouse, video->mode->x, video->mode->y ); // enable Z buffering ((video_vtable_t *)(global_context->video->base.vtable))->use_z_buffer( global_context->video, true ); global_context->mode = MODE_GRAPHICS; } } static void switch_to_text_mode( global_context_t *global_context ) { video_t *video = global_context->video; vga_text_t *vga_text = &global_context->vga_text; mouse_t *mouse = global_context->mouse; vga_text_restore( vga_text ); video_mode_t mode = video_make_mode( VIDEO_MODE_TYPE_TEXT, 640, 480, 4 ); if( ((video_vtable_t *)(video->base.vtable))->set_mode( video, mode ) ) { vga_text_set_cursor( vga_text, vga_text->cursor_x, vga_text->cursor_y ); ((mouse_vtable_t *)(mouse->base.vtable))->set_resolution( mouse, vga_text->res_x, vga_text->res_y ); global_context->mode = MODE_TEXT; } } static void print_task_status( global_context_t *global_context ) { task_manager_t *task_manager = &global_context->task_manager; for( int i = 0; i < task_manager->nof_tasks; i++ ) { printf( "%d %d\n", i, task_manager->task[i]->nof_switches ); } } static void print_memory_status( global_context_t *global_context ) { memory_manager_t *memory_manager = &global_context->memory_manager; printf( "heap memory size: %d\n", memory_manager->size ); printf( "heap memory offset: 0x%X\n", memory_manager->offset ); printf( "heap memory in use: %d\n", memory_manager_stats_used( memory_manager ) ); printf( "heap memory free: %d\n", memory_manager_stats_free( memory_manager ) ); } uintptr_t __stack_chk_guard = STACK_CHK_GUARD; __attribute__( ( noreturn ) ) void __stack_chk_fail_local( void ) { kernel_panic( "Stack smashing detected!" ); // make gcc happy on noreturn does return for( ;; ); } __attribute__( ( noreturn ) ) void __stack_chk_fail( void ) { kernel_panic( "Stack smashing detected!" ); // make gcc happy on noreturn does return for( ;; ); } void handle_network_event( network_event_t *event, void *context ) { printf( "NETWORK DATA: len: %d\n", event->length ); // we must free if data if we no longer need it free( event->data ); }