#include #include #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 "keyboard.h" #include "mouse.h" #include "pci.h" #include "setjmp.h" #include "kernel.h" // TODO: move away from main! #include "drivers/video/vga.h" #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 ); typedef enum { MODE_TEXT, MODE_GRAPHICS } mode_t; typedef struct { vga_text_t vga_text; driver_manager_t driver_manager; vga_t vga; mode_t mode; mouse_t mouse; desktop_t desktop; window_t window1; window_t window2; window_t window3; text_widget_t widget3; } global_context_t; static global_context_t global_context; static void funcA( void ); static void funcB( void ); static void refresh_screen( void ); // 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 ); // 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 ) { goto TERMINATE; } // TODO: two demo tasks for now, later we should do something useful in a task task_manager_t task_manager; task_manager_init( &task_manager ); task_t taskA; task_init( &taskA, GDT_CODE_SEGMENT_SELECTOR, &funcA ); task_manager_add_task( &task_manager, &taskA ); task_t taskB; task_init( &taskB, GDT_CODE_SEGMENT_SELECTOR, &funcB ); task_manager_add_task( &task_manager, &taskB ); task_t taskRefreshScreen; task_init( &taskRefreshScreen, GDT_CODE_SEGMENT_SELECTOR, &refresh_screen ); task_manager_add_task( &task_manager, &taskRefreshScreen ); puts( "Initializing interrupts" ); interrupt_t interrupt; interrupts_init( &interrupt, GDT_CODE_SEGMENT_SELECTOR, &task_manager ); puts( "Initializing drivers" ); driver_manager_init( &global_context.driver_manager ); // hard-wired drivers keyboard_t keyboard; keyboard_init( &keyboard, &handle_keyboard_event, (void *)&global_context ); interrupt_handler_t keyboard_interrupt_handler; interrupt_handler_init( &keyboard_interrupt_handler, IRQ_BASE + 0x01, &interrupt, keyboard_handle_interrupt, &keyboard ); interrupts_register_interrupt_handler( keyboard_interrupt_handler ); driver_manager_add_driver( &global_context.driver_manager, (driver_t *)&keyboard ); mouse_init( &global_context.mouse, &handle_mouse_event, global_context.vga_text.res_x, global_context.vga_text.res_y, (void *)&global_context ); interrupt_handler_t mouse_interrupt_handler; interrupt_handler_init( &mouse_interrupt_handler, IRQ_BASE + 0x0C, &interrupt, mouse_handle_interrupt, &global_context.mouse ); interrupts_register_interrupt_handler( mouse_interrupt_handler ); driver_manager_add_driver( &global_context.driver_manager, (driver_t *)&global_context.mouse ); // exit point in case of kernel panic, do this as soon as // possible if( setjmp( panic_jmp_buf ) > 0 ) { goto TERMINATE; } // 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 ); // TODO: merge with the text mode vga_text_t object vga_init( &global_context.vga, NULL ); driver_manager_add_driver( &global_context.driver_manager, (driver_t *)&global_context.vga ); 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 ); 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; vga_t *vga = &global_context->vga; mouse_t *mouse = &global_context->mouse; 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( "proc - show process 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 ) { // TODO: print memory usage } else if( strcmp( buf, "proc" ) == 0 ) { // TODO: print scheduler status } 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 ) { // TODO: move mode switches to and from graphics/text mode here, // for now when we enter the graphics mode we remain there switch( global_context->mode ) { case MODE_TEXT: if( vga_set_mode( vga, vga_make_mode( VGA_MODE_TYPE_GRAPHICS, 320, 200, 8 ) ) ) { vga_text_save( vga_text ); desktop_init( &global_context->desktop, 320, 200, VGA_COLOR_BLUE ); window_init( &global_context->window1, (widget_t *)&global_context->desktop, 60, 90, 60, 70, VGA_COLOR_LIGHT_GREY ); // TODO: this looks clunky! ((composite_widget_vtable_t *)global_context->desktop.base.base.vtable)->add_child( &global_context->desktop, (widget_t *)&global_context->window1 ); window_init( &global_context->window2, (widget_t *)&global_context->desktop, 130, 80, 60, 70, VGA_COLOR_GREEN ); ((composite_widget_vtable_t *)global_context->desktop.base.base.vtable)->add_child( &global_context->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 *)&global_context->desktop, 55, 5, vga->mode.x - 65, 100, VGA_COLOR_CYAN ); ((composite_widget_vtable_t *)global_context->desktop.base.base.vtable)->add_child( &global_context->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, VGA_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_set_resolution( mouse, vga->mode.x, vga->mode.y ); // enable Z buffering vga_use_z_buffer( &global_context->vga, true ); global_context->mode = MODE_GRAPHICS; } break; case MODE_GRAPHICS: break; } } 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 ) { vga_text_restore( vga_text ); if( vga_set_mode( vga, vga_make_mode( VGA_MODE_TYPE_TEXT, 640, 480, 4 ) ) ) { vga_text_set_cursor( vga_text, vga_text->cursor_x, vga_text->cursor_y ); mouse_set_resolution( mouse, vga_text->res_x, vga_text->res_y ); global_context->mode = MODE_TEXT; } } } 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 funcA( void ) { while( true ) { //~ printf( "A" ); //~ kernel_panic( "panic in task" ); } } static void funcB( void ) { while( true ) { //~ printf( "B" ); } } static void refresh_screen( void ) { while( true ) { 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.vga ); vga_refresh( &global_context.vga ); interrupts_enable( ); break; } } }