diff --git a/src/system/boot/platform/bios_ia32/Jamfile b/src/system/boot/platform/bios_ia32/Jamfile index 2372272467..9219e5b392 100644 --- a/src/system/boot/platform/bios_ia32/Jamfile +++ b/src/system/boot/platform/bios_ia32/Jamfile @@ -21,7 +21,8 @@ KernelMergeObject boot_platform_bios_ia32.o : shell.S start.cpp debug.cpp - bios.S + bios.cpp + bios_asm.S console.cpp serial.cpp devices.cpp @@ -37,6 +38,8 @@ KernelMergeObject boot_platform_bios_ia32.o : video.cpp apm.cpp hpet.cpp + interrupts.cpp + interrupts_asm.S # VESA/DDC EDID decode_edid.c diff --git a/src/system/boot/platform/bios_ia32/bios.cpp b/src/system/boot/platform/bios_ia32/bios.cpp new file mode 100644 index 0000000000..5d848eade5 --- /dev/null +++ b/src/system/boot/platform/bios_ia32/bios.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "bios.h" + +#include "interrupts.h" + + +extern "C" void call_bios_internal(uint8 num, struct bios_regs* regs); + + +void +call_bios(uint8 num, struct bios_regs* regs) +{ + restore_bios_idt(); + call_bios_internal(num, regs); + set_debug_idt(); + +} diff --git a/src/system/boot/platform/bios_ia32/bios.S b/src/system/boot/platform/bios_ia32/bios_asm.S similarity index 98% rename from src/system/boot/platform/bios_ia32/bios.S rename to src/system/boot/platform/bios_ia32/bios_asm.S index 84451e6680..5a6f0575f5 100644 --- a/src/system/boot/platform/bios_ia32/bios.S +++ b/src/system/boot/platform/bios_ia32/bios_asm.S @@ -158,11 +158,11 @@ _real_code_segment: //-------------------------------------------------------------- -/*! void call_bios(uint8 num, struct bios_regs *regs) +/*! void call_bios_internal(uint8 num, struct bios_regs *regs) Does a BIOS call by triggering a software interrupt in real mode. */ -FUNCTION(call_bios) +FUNCTION(call_bios_internal) pushal pushfl diff --git a/src/system/boot/platform/bios_ia32/debug.cpp b/src/system/boot/platform/bios_ia32/debug.cpp index 130b1e6866..acd56a6ff2 100644 --- a/src/system/boot/platform/bios_ia32/debug.cpp +++ b/src/system/boot/platform/bios_ia32/debug.cpp @@ -6,7 +6,6 @@ #include "debug.h" -#include #include #include @@ -26,6 +25,33 @@ static char sBuffer[16384]; static uint32 sBufferPosition; static ring_buffer* sDebugSyslogBuffer = NULL; +static bool sPostCleanup = false; + + +static void +dprintf_args(const char *format, va_list args) +{ + char buffer[512]; + int length = vsnprintf(buffer, sizeof(buffer), format, args); + + if (length >= (int)sizeof(buffer)) + length = sizeof(buffer) - 1; + + if (sPostCleanup && sDebugSyslogBuffer != NULL) { + ring_buffer_write(sDebugSyslogBuffer, (const uint8*)buffer, length); + } else if (sBufferPosition + length < sizeof(sBuffer)) { + memcpy(sBuffer + sBufferPosition, buffer, length); + sBufferPosition += length; + } + + serial_puts(buffer, length); + + if (platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) + fprintf(stderr, "%s", buffer); +} + + +// #pragma mark - /*! This works only after console_init() was called. @@ -54,26 +80,29 @@ panic(const char *format, ...) void dprintf(const char *format, ...) { - char buffer[512]; - va_list list; - int length; + va_list args; - va_start(list, format); - length = vsnprintf(buffer, sizeof(buffer), format, list); - va_end(list); + va_start(args, format); + dprintf_args(format, args); + va_end(args); +} - if (length >= (int)sizeof(buffer)) - length = sizeof(buffer) - 1; - if (sBufferPosition + length < sizeof(sBuffer)) { - memcpy(sBuffer + sBufferPosition, buffer, length); - sBufferPosition += length; - } +void +kprintf(const char *format, ...) +{ + va_list args; - serial_puts(buffer, length); + va_start(args, format); - if (platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) - fprintf(stderr, "%s", buffer); + // print to console, if available + if (stdout != NULL) + vfprintf(stdout, format, args); + + // always print to serial line + dprintf_args(format, args); + + va_end(args); } @@ -120,7 +149,8 @@ debug_cleanup(void) if (gKernelArgs.keep_debug_output_buffer) { // copy the output gathered so far into the ring buffer ring_buffer_clear(sDebugSyslogBuffer); - ring_buffer_write(sDebugSyslogBuffer, (uint8*)sBuffer, sBufferPosition); + ring_buffer_write(sDebugSyslogBuffer, (uint8*)sBuffer, + sBufferPosition); memcpy(buffer, kDebugSyslogSignature, signatureLength); } else { @@ -137,4 +167,6 @@ debug_cleanup(void) gKernelArgs.debug_size = sBufferPosition; } } + + sPostCleanup = true; } diff --git a/src/system/boot/platform/bios_ia32/debug.h b/src/system/boot/platform/bios_ia32/debug.h index 08ec6a471b..bad459122c 100644 --- a/src/system/boot/platform/bios_ia32/debug.h +++ b/src/system/boot/platform/bios_ia32/debug.h @@ -6,6 +6,8 @@ #define DEBUG_H +#include + #include diff --git a/src/system/boot/platform/bios_ia32/interrupts.cpp b/src/system/boot/platform/bios_ia32/interrupts.cpp new file mode 100644 index 0000000000..3b4505c0a3 --- /dev/null +++ b/src/system/boot/platform/bios_ia32/interrupts.cpp @@ -0,0 +1,249 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "interrupts.h" + +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +#include "debug.h" +#include "keyboard.h" + + +//#define TRACE_INTERRUPTS +#ifdef TRACE_INTERRUPTS +# define TRACE(x...) dprintf(x) +#else +# define TRACE(x...) ; +#endif + + +// interrupt function prototypes +#define INTERRUPT_FUNCTION(vector) \ + extern "C" void interrupt_function##vector(); +INTERRUPT_FUNCTIONS() +#undef INTERRUPT_FUNCTION + +#define INTERRUPT_FUNCTION(vector) \ + extern "C" void exception_interrupt_function##vector(); +INTERRUPT_FUNCTIONS() +#undef INTERRUPT_FUNCTION + + +static const char *kInterruptNames[DEBUG_IDT_SLOT_COUNT] = { + /* 0 */ "Divide Error Exception", + /* 1 */ "Debug Exception", + /* 2 */ "NMI Interrupt", + /* 3 */ "Breakpoint Exception", + /* 4 */ "Overflow Exception", + /* 5 */ "BOUND Range Exceeded Exception", + /* 6 */ "Invalid Opcode Exception", + /* 7 */ "Device Not Available Exception", + /* 8 */ "Double Fault Exception", + /* 9 */ "Coprocessor Segment Overrun", + /* 10 */ "Invalid TSS Exception", + /* 11 */ "Segment Not Present", + /* 12 */ "Stack Fault Exception", + /* 13 */ "General Protection Exception", + /* 14 */ "Page-Fault Exception", + /* 15 */ "-", + /* 16 */ "x87 FPU Floating-Point Error", + /* 17 */ "Alignment Check Exception", + /* 18 */ "Machine-Check Exception", + /* 19 */ "SIMD Floating-Point Exception", +}; + + +struct interrupt_frame { + uint32 vector; + uint32 edi; + uint32 esi; + uint32 ebp; + uint32 esp; + uint32 ebx; + uint32 edx; + uint32 ecx; + uint32 eax; + uint32 error_code; + uint32 eip; + uint32 cs; + uint32 eflags; +}; + +struct interrupt_descriptor { + uint32 a; + uint32 b; +}; + + +static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT]; + +static gdt_idt_descr sBIOSIDTDescriptor; +static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT }; + + +static void +set_interrupt_gate(int n, void (*function)()) +{ + if (n >= DEBUG_IDT_SLOT_COUNT) + return; + + sDebugIDT[n].a = (KERNEL_CODE_SEG << 16) | (0x0000ffff & (addr_t)function); + sDebugIDT[n].b = (0xffff0000 & (addr_t)function) | 0x8e00; +} + + +static void +set_idt(const gdt_idt_descr& idtDescriptor) +{ + asm("lidt %0;" : : "m" (idtDescriptor)); +} + + +extern "C" void +handle_exception_exception() +{ + while (true); +} + + +extern "C" void +handle_exception(interrupt_frame frame) +{ + // Before doing anything else, set the interrupt gates so that + // handle_exception_exception() is called. This way we may avoid + // triple-faulting, if e.g. the stack is messed up and the user has at + // least the already printed output. + #define INTERRUPT_FUNCTION(vector) \ + set_interrupt_gate(vector, &exception_interrupt_function##vector); + INTERRUPT_FUNCTIONS() + #undef INTERRUPT_FUNCTION + + // If the console is already/still initialized, clear the screen and prepare + // for output. + if (stdout != NULL) { + console_set_color(RED, BLACK); + console_clear_screen(); + console_set_cursor(0, 0); + console_hide_cursor(); + } + + // print exception name + if (frame.vector < DEBUG_IDT_SLOT_COUNT) + kprintf("%s", kInterruptNames[frame.vector]); + else + kprintf("Unknown exception %" B_PRIu32, frame.vector); + + // additional info for page fault + if (frame.vector == 14) { + uint32 cr2; + asm("movl %%cr2, %0" : "=r"(cr2)); + kprintf(": %s fault at address: %#" B_PRIx32, + (frame.error_code & 0x2) != 0 ? "write" : "read", cr2); + } + + kprintf("\n"); + + // print greeting and registers + kprintf("Welcome to Boot Loader Death Land!\n"); + kprintf("\n"); + + #define REG(name) " " #name " %-#10" B_PRIx32 + + kprintf(REG(eax) " " REG(ebx) " " REG(ecx) " " REG(edx) "\n", + frame.eax, frame.ebx, frame.ecx, frame.edx); + kprintf(REG(esi) " " REG(edi) " " REG(ebp) " " REG(esp) "\n", + frame.esi, frame.edi, frame.ebp, frame.esp); + kprintf(REG(eip) REG(eflags) "\n", frame.eip, frame.eflags); + + #undef REG + + // print stack trace (max 10 frames) + kprintf("\n frame return address\n"); + + struct x86_stack_frame { + x86_stack_frame* next; + void* return_address; + }; + + x86_stack_frame* stackFrame = (x86_stack_frame*)frame.ebp; + void* instructionPointer = (void*)frame.eip; + + for (int32 i = 0; i < 10 && stackFrame != NULL; i++) { + kprintf(" %p %p\n", stackFrame, instructionPointer); + + instructionPointer = stackFrame->return_address; + stackFrame = stackFrame->next; + } + + if (stdout != NULL) { + kprintf("\nPress a key to reboot.\n"); + clear_key_buffer(); + wait_for_key(); + platform_exit(); + } + + while (true); +} + + +void +interrupts_init() +{ + // get the IDT + asm("sidt %0;" : : "m" (sBIOSIDTDescriptor)); + + TRACE("IDT: base: %p, limit: %u\n", sBIOSIDTDescriptor.base, + sBIOSIDTDescriptor.limit); + + // set interrupt gates + #define INTERRUPT_FUNCTION(vector) \ + set_interrupt_gate(vector, &interrupt_function##vector); + INTERRUPT_FUNCTIONS() + #undef INTERRUPT_FUNCTION + + // set the debug IDT + set_debug_idt(); +} + + +void +restore_bios_idt() +{ + set_idt(sBIOSIDTDescriptor); +} + + +void +set_debug_idt() +{ + set_idt(sDebugIDTDescriptor); +} + + +void +interrupts_init_kernel_idt(void* idt, size_t idtSize) +{ + // clear it but copy the descriptors we've set up for the exceptions + memset(idt, 0, idtSize); + memcpy(idt, sDebugIDT, sizeof(sDebugIDT)); + + // load the idt + gdt_idt_descr idtDescriptor; + idtDescriptor.limit = idtSize - 1; + idtDescriptor.base = idt; + set_idt(idtDescriptor); +} diff --git a/src/system/boot/platform/bios_ia32/interrupts.h b/src/system/boot/platform/bios_ia32/interrupts.h new file mode 100644 index 0000000000..43cec99653 --- /dev/null +++ b/src/system/boot/platform/bios_ia32/interrupts.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef INTERRUPTS_H +#define INTERRUPTS_H + + +#ifndef _ASSEMBLER + + +#include + +#include + + +struct gdt_idt_descr { + uint16 limit; + void* base; +} _PACKED; + + +__BEGIN_DECLS + + +void interrupts_init(); +void set_debug_idt(); +void restore_bios_idt(); +void interrupts_init_kernel_idt(void* idt, size_t idtSize); + + +__END_DECLS + + +#endif // _ASSEMBLER + +#define INTERRUPT_FUNCTION_ERROR(vector) INTERRUPT_FUNCTION(vector) + +#define INTERRUPT_FUNCTIONS5(vector1, vector2, vector3, vector4, vector5) \ + INTERRUPT_FUNCTION(vector1) \ + INTERRUPT_FUNCTION(vector2) \ + INTERRUPT_FUNCTION(vector3) \ + INTERRUPT_FUNCTION(vector4) \ + INTERRUPT_FUNCTION(vector5) + +#define INTERRUPT_FUNCTIONS() \ + INTERRUPT_FUNCTIONS5(0, 1, 2, 3, 4) \ + INTERRUPT_FUNCTIONS5(5, 6, 7, 8, 9) \ + INTERRUPT_FUNCTION_ERROR(10) \ + INTERRUPT_FUNCTION_ERROR(11) \ + INTERRUPT_FUNCTION_ERROR(12) \ + INTERRUPT_FUNCTION_ERROR(13) \ + INTERRUPT_FUNCTION_ERROR(14) \ + INTERRUPT_FUNCTION(15) \ + INTERRUPT_FUNCTION(16) \ + INTERRUPT_FUNCTION_ERROR(17) \ + INTERRUPT_FUNCTION(18) \ + INTERRUPT_FUNCTION(19) + +#define DEBUG_IDT_SLOT_COUNT 20 + + +#endif // INTERRUPTS_H diff --git a/src/system/boot/platform/bios_ia32/interrupts_asm.S b/src/system/boot/platform/bios_ia32/interrupts_asm.S new file mode 100644 index 0000000000..a49ed49861 --- /dev/null +++ b/src/system/boot/platform/bios_ia32/interrupts_asm.S @@ -0,0 +1,46 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include "interrupts.h" + + +// #pragma mark - interrupt functions + + +#define INTERRUPT_FUNCTION(vector) \ +.align 8; \ +FUNCTION(interrupt_function##vector): \ + pushl $0; \ + pusha; \ + pushl $vector; \ + call handle_exception; + +#undef INTERRUPT_FUNCTION_ERROR +#define INTERRUPT_FUNCTION_ERROR(vector) \ +.align 8; \ +FUNCTION(interrupt_function##vector): \ + pusha; \ + pushl $vector; \ + call handle_exception; + +INTERRUPT_FUNCTIONS() + + +// #pragma mark - interrupt functions during exception + + +#undef INTERRUPT_FUNCTION +#define INTERRUPT_FUNCTION(vector) \ +.align 8; \ +FUNCTION(exception_interrupt_function##vector): \ + call handle_exception_exception; + +#undef INTERRUPT_FUNCTION_ERROR +#define INTERRUPT_FUNCTION_ERROR(vector) INTERRUPT_FUNCTION(vector) + +INTERRUPT_FUNCTIONS() diff --git a/src/system/boot/platform/bios_ia32/mmu.cpp b/src/system/boot/platform/bios_ia32/mmu.cpp index b80f4c4420..f08854973e 100644 --- a/src/system/boot/platform/bios_ia32/mmu.cpp +++ b/src/system/boot/platform/bios_ia32/mmu.cpp @@ -7,19 +7,21 @@ #include "mmu.h" -#include "bios.h" +#include + +#include + +#include +#include #include #include #include #include -#include -#include #include -#include - -#include +#include "bios.h" +#include "interrupts.h" /*! The (physical) memory layout of the boot loader is currently as follows: @@ -58,11 +60,6 @@ // for output to work. -struct gdt_idt_descr { - uint16 limit; - uint32 *base; -} _PACKED; - // memory structure returned by int 0x15, ax 0xe820 struct extended_memory { uint64 base_addr; @@ -491,7 +488,6 @@ mmu_init_for_kernel(void) TRACE("mmu_init_for_kernel\n"); // set up a new idt { - struct gdt_idt_descr idtDescriptor; uint32 *idt; // find a new idt @@ -504,18 +500,9 @@ mmu_init_for_kernel(void) gKernelArgs.arch_args.vir_idt = (uint32)get_next_virtual_page(); map_page(gKernelArgs.arch_args.vir_idt, (uint32)idt, kDefaultPageFlags); - // clear it out - uint32* virtualIDT = (uint32*)gKernelArgs.arch_args.vir_idt; - for (int32 i = 0; i < IDT_LIMIT / 4; i++) { - virtualIDT[i] = 0; - } - - // load the idt - idtDescriptor.limit = IDT_LIMIT - 1; - idtDescriptor.base = (uint32 *)gKernelArgs.arch_args.vir_idt; - - asm("lidt %0;" - : : "m" (idtDescriptor)); + // initialize it + interrupts_init_kernel_idt((void*)gKernelArgs.arch_args.vir_idt, + IDT_LIMIT); TRACE("idt at virtual address 0x%lx\n", gKernelArgs.arch_args.vir_idt); } @@ -561,7 +548,7 @@ mmu_init_for_kernel(void) // load the GDT gdtDescriptor.limit = GDT_LIMIT - 1; - gdtDescriptor.base = (uint32 *)gKernelArgs.arch_args.vir_gdt; + gdtDescriptor.base = (void*)gKernelArgs.arch_args.vir_gdt; asm("lgdt %0;" : : "m" (gdtDescriptor)); diff --git a/src/system/boot/platform/bios_ia32/shell.S b/src/system/boot/platform/bios_ia32/shell.S index 405be7987d..2aad4e51d9 100644 --- a/src/system/boot/platform/bios_ia32/shell.S +++ b/src/system/boot/platform/bios_ia32/shell.S @@ -103,7 +103,7 @@ start_loader: /** Loads %di sectors from floppy disk, starting at head %dh, sector %cx. - * The data is loaded to %es:%bx. On exit, %es:%bx will point immediately + * The data is loaded to %es:%bx. On exit, %es:%bx will point immediately * behind the loaded data, so that you can continue to read in data. * %ax, %cx, %dx, %bp, %di and %si will be clobbered. */ @@ -127,7 +127,7 @@ load_sectors: mov %cx, %si // and remember it pop %cx pop %bx - + load_track: mov %di, %ax // limit the sector count to track boundaries add %cl, %al @@ -292,8 +292,10 @@ _protected_code_segment: mov %ax, %gs mov %ax, %ss - mov $0x10000, %ebp // setup new stack - mov %ebp, %esp + movl $0x10000, %esp // setup new stack + pushl $0 // terminate stack frame chain (next frame and + pushl $0 // return address) + mov %esp, %ebp call _start diff --git a/src/system/boot/platform/bios_ia32/start.cpp b/src/system/boot/platform/bios_ia32/start.cpp index 9f259578d8..6af13dd6da 100644 --- a/src/system/boot/platform/bios_ia32/start.cpp +++ b/src/system/boot/platform/bios_ia32/start.cpp @@ -20,6 +20,7 @@ #include "cpu.h" #include "debug.h" #include "hpet.h" +#include "interrupts.h" #include "keyboard.h" #include "mmu.h" #include "multiboot.h" @@ -84,6 +85,10 @@ platform_start_kernel(void) smp_init_other_cpus(); debug_cleanup(); mmu_init_for_kernel(); + + // We're about to enter the kernel -- disable console output. + stdout = NULL; + smp_boot_other_cpus(); dprintf("kernel entry at %lx\n", @@ -128,6 +133,7 @@ _start(void) serial_init(); serial_enable(); + interrupts_init(); console_init(); cpu_init(); mmu_init(); diff --git a/src/system/boot/platform/pxe_ia32/Jamfile b/src/system/boot/platform/pxe_ia32/Jamfile index 95eda43995..73c75ebe35 100644 --- a/src/system/boot/platform/pxe_ia32/Jamfile +++ b/src/system/boot/platform/pxe_ia32/Jamfile @@ -20,7 +20,8 @@ UsePrivateHeaders [ FDirName storage ] ; local bios_ia32_src = start.cpp debug.cpp - bios.S + bios.cpp + bios_asm.S console.cpp serial.cpp keyboard.cpp @@ -34,6 +35,8 @@ local bios_ia32_src = video.cpp hpet.cpp apm.cpp + interrupts.cpp + interrupts_asm.S ; local bios_ia32_edid_src =