diff options
Diffstat (limited to 'payloads/libpayload/arch/x86')
-rw-r--r-- | payloads/libpayload/arch/x86/Makefile.inc | 1 | ||||
-rw-r--r-- | payloads/libpayload/arch/x86/exception.c | 199 | ||||
-rw-r--r-- | payloads/libpayload/arch/x86/exception_asm.S | 291 | ||||
-rw-r--r-- | payloads/libpayload/arch/x86/main.c | 3 |
4 files changed, 494 insertions, 0 deletions
diff --git a/payloads/libpayload/arch/x86/Makefile.inc b/payloads/libpayload/arch/x86/Makefile.inc index 8efbc12831..549a6303e7 100644 --- a/payloads/libpayload/arch/x86/Makefile.inc +++ b/payloads/libpayload/arch/x86/Makefile.inc @@ -32,6 +32,7 @@ libc-y += main.c sysinfo.c libc-y += timer.c coreboot.c util.S libc-y += exec.S virtual.c libc-y += string.c +libc-y += exception_asm.S exception.c libcbfs-$(CONFIG_LP_CBFS) += rom_media.c diff --git a/payloads/libpayload/arch/x86/exception.c b/payloads/libpayload/arch/x86/exception.c new file mode 100644 index 0000000000..a9a65ca5a2 --- /dev/null +++ b/payloads/libpayload/arch/x86/exception.c @@ -0,0 +1,199 @@ +/* + * This file is part of the libpayload project. + * + * Copyright 2013 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <exception.h> +#include <libpayload.h> +#include <stdint.h> + +uint8_t exception_stack[0x1000] __attribute__((aligned(8))); +extern void *exception_stack_end; + +struct exception_state +{ + u32 eax; + u32 ecx; + u32 edx; + u32 ebx; + u32 esp; + u32 ebp; + u32 esi; + u32 edi; + u32 eip; + u32 eflags; + u32 cs; + u32 ss; + u32 ds; + u32 es; + u32 fs; + u32 gs; + u32 error_code; + u32 vector; +} __attribute__((packed)); + +struct exception_info +{ + const char *name; + void (*error_code_printer)(u32 code); +}; + +static void print_segment_error_code(u32 code) +{ + printf("%#x - descriptor %#x in the ", code, (code >> 3) & 0x1f); + if (code & (0x1 << 1)) { + printf("IDT"); + } else { + if (code & 0x04) + printf("LDT"); + else + printf("GDT"); + } + if (code & (0x1 << 0)) + printf(", external to the CPU"); + else + printf(", internal to the CPU"); +} + +static void print_page_fault_error_code(u32 code) +{ + printf("%#x -", code); + if (code & (0x1 << 0)) + printf(" page protection"); + else + printf(" page not present"); + if (code & (0x1 << 1)) + printf(", write"); + else + printf(", read"); + if (code & (0x1 << 2)) + printf(", user"); + else + printf(", supervisor"); + if (code & (0x1 << 3)) + printf(", reserved bits set"); + if (code & (0x1 << 4)) + printf(", instruction fetch"); +} + +static void print_raw_error_code(u32 code) +{ + printf("%#x", code); +} + +static struct exception_info exceptions[] = { + [0] = { .name = "divide by zero" }, + [1] = { .name = "debug" }, + [2] = { .name = "non-maskable-interrupt" }, + [3] = { .name = "breakpoint" }, + [4] = { .name = "overflow" }, + [5] = { .name = "bound range" }, + [6] = { .name = "invalid opcode" }, + [7] = { .name = "device not available" }, + [8] = { .name = "double fault", + .error_code_printer = &print_raw_error_code }, + [10] = { .name = "invalid tss", + .error_code_printer = &print_segment_error_code }, + [11] = { .name = "segment not present", + .error_code_printer = &print_segment_error_code }, + [12] = { .name = "stack", + .error_code_printer = &print_segment_error_code }, + [13] = { .name = "general protection", + .error_code_printer = &print_segment_error_code }, + [14] = { .name = "page fault", + .error_code_printer = &print_page_fault_error_code }, + [16] = { .name = "x87 floating point" }, + [17] = { .name = "alignment check", + .error_code_printer = &print_raw_error_code }, + [18] = { .name = "machine check" }, + [19] = { .name = "SIMD floating point" }, + [30] = { .name = "security", + .error_code_printer = &print_raw_error_code }, +}; + +static void dump_stack(uintptr_t addr, size_t bytes) +{ + int i, j; + const int line = 8; + uint32_t *ptr = (uint32_t *)(addr & ~(line * sizeof(*ptr) - 1)); + + printf("Dumping stack:\n"); + for (i = bytes / sizeof(*ptr); i >= 0; i -= line) { + printf("%p: ", ptr + i); + for (j = i; j < i + line; j++) + printf("%08x ", *(ptr + j)); + printf("\n"); + } +} + +void exception_handler(void); +void exception_handler(void) +{ + struct exception_state *state = + (void *)((u8 *)exception_stack_end - sizeof(*state)); + + struct exception_info *info = NULL; + if (state->vector < ARRAY_SIZE(exceptions)) + info = &exceptions[state->vector]; + + if (info) + printf("Exception %d (%s)\n", state->vector, info->name); + else + printf("Unrecognized exception %d\n", state->vector); + if (info->error_code_printer) { + printf("Error code: "); + info->error_code_printer(state->error_code); + printf("\n"); + } + printf("EIP: 0x%08x\n", state->eip); + printf("CS: 0x%04x\n", state->cs); + printf("EFLAGS: 0x%08x\n", state->eflags); + printf("EAX: 0x%08x\n", state->eax); + printf("ECX: 0x%08x\n", state->ecx); + printf("EDX: 0x%08x\n", state->edx); + printf("EBX: 0x%08x\n", state->ebx); + printf("ESP: 0x%08x\n", state->esp); + printf("EBP: 0x%08x\n", state->ebp); + printf("ESI: 0x%08x\n", state->esi); + printf("EDI: 0x%08x\n", state->edi); + printf("DS: 0x%04x\n", state->ds); + printf("ES: 0x%04x\n", state->es); + printf("SS: 0x%04x\n", state->ss); + printf("FS: 0x%04x\n", state->fs); + printf("GS: 0x%04x\n", state->gs); + + dump_stack(state->esp, 512); + + halt(); +} + +void exception_init_asm(void); +void exception_init(void) +{ + exception_stack_end = exception_stack + sizeof(exception_stack); + exception_init_asm(); +} diff --git a/payloads/libpayload/arch/x86/exception_asm.S b/payloads/libpayload/arch/x86/exception_asm.S new file mode 100644 index 0000000000..c56a7a0bab --- /dev/null +++ b/payloads/libpayload/arch/x86/exception_asm.S @@ -0,0 +1,291 @@ +/* + * This file is part of the libpayload project. + * + * Copyright 2013 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + .align 4 + .global exception_stack_end +exception_stack_end: + .long 0 + +/* Some temporary variables which are used while saving exception state. */ +vector: + .long 0 +error_code: + .long 0 +old_esp: + .long 0 +old_eax: + .long 0 + + .align 8 + +/* + * Each exception vector has a small stub associated with it which sets aside + * the error code, if any, records which vector we entered from, and calls + * the common exception entry point. Some exceptions have error codes and some + * don't, so we have a macro for each type. + */ + + .macro stub num +exception_stub_\num: + movl $0, error_code + movl $\num, vector + jmp exception_common + .endm + + .macro stub_err num +exception_stub_\num: + popl error_code + movl $\num, vector + jmp exception_common + .endm + + stub 0 + stub 1 + stub 2 + stub 3 + stub 4 + stub 5 + stub 6 + stub 7 + stub_err 8 + stub 9 + stub_err 10 + stub_err 11 + stub_err 12 + stub_err 13 + stub_err 14 + stub 15 + stub 16 + stub_err 17 + stub 18 + stub 19 + stub 20 + stub 21 + stub 22 + stub 23 + stub 24 + stub 25 + stub 26 + stub 27 + stub 28 + stub 29 + stub_err 30 + stub 31 + +exception_common: + /* + * Save off the stack pointer and old eax value and install the + * exception stack. eax points to the old stack which has the + * exception ip, cs, and flags. + */ + mov %esp, old_esp + addl $12, old_esp + mov %eax, old_eax + mov %esp, %eax + mov exception_stack_end, %esp + + /* + * Push values onto the top of the exception stack to form an + * exception state structure. + */ + pushl vector + pushl error_code + pushl %gs + pushl %fs + pushl %es + pushl %ds + pushl %ss + pushl 4(%eax) + pushl 8(%eax) + pushl (%eax) + pushl %edi + pushl %esi + pushl %ebp + pushl old_esp + pushl %ebx + pushl %edx + pushl %ecx + pushl old_eax + + /* + * Call the C exception handler. It will find the exception state on + * the exception stack. Not passing parameters means we don't have to + * worry about what ABI is being used. + */ + call exception_handler + + /* + * Restore state from the exception state structure, including any + * changes that might have been made. + */ + popl old_eax + popl %ecx + popl %edx + popl %ebx + popl old_esp + + mov old_esp, %eax + subl $12, %eax + + popl %ebp + popl %esi + popl %edi + popl (%eax) + popl 8(%eax) + popl 4(%eax) + popl %ss + popl %ds + popl %es + popl %fs + popl %gs + + mov %eax, %esp + mov old_eax, %eax + + /* Return from the exception. */ + iretl + +/* + * We need segment selectors for the IDT, so we need to know where things are + * in the GDT. We set one up here which is pretty standard and largely copied + * from coreboot. + */ + .align 8 +gdt: + /* selgdt 0, unused */ + .word 0x0000, 0x0000 + .byte 0x00, 0x00, 0x00, 0x00 + + /* selgdt 8, unused */ + .word 0x0000, 0x0000 + .byte 0x00, 0x00, 0x00, 0x00 + + /* selgdt 0x10, flat 4GB code segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0xcf, 0x00 + + /* selgdt 0x18, flat 4GB data segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x93, 0xcf, 0x00 +gdt_end: + +/* GDT pointer for use with lgdt */ +gdt_ptr: + .word gdt_end - gdt - 1 + .long gdt + + /* + * Record the target and construct the actual entry at init time. This + * is necessary because the linker doesn't want to construct the entry + * for us. + */ + .macro interrupt_gate target + .long \target + .long \target + .endm + + .align 8 + .global idt +idt: + interrupt_gate exception_stub_0 + interrupt_gate exception_stub_1 + interrupt_gate exception_stub_2 + interrupt_gate exception_stub_3 + interrupt_gate exception_stub_4 + interrupt_gate exception_stub_5 + interrupt_gate exception_stub_6 + interrupt_gate exception_stub_7 + interrupt_gate exception_stub_8 + interrupt_gate exception_stub_9 + interrupt_gate exception_stub_10 + interrupt_gate exception_stub_11 + interrupt_gate exception_stub_12 + interrupt_gate exception_stub_13 + interrupt_gate exception_stub_14 + interrupt_gate exception_stub_15 + interrupt_gate exception_stub_16 + interrupt_gate exception_stub_17 + interrupt_gate exception_stub_18 + interrupt_gate exception_stub_19 + interrupt_gate exception_stub_20 + interrupt_gate exception_stub_21 + interrupt_gate exception_stub_22 + interrupt_gate exception_stub_23 + interrupt_gate exception_stub_24 + interrupt_gate exception_stub_25 + interrupt_gate exception_stub_26 + interrupt_gate exception_stub_27 + interrupt_gate exception_stub_28 + interrupt_gate exception_stub_29 + interrupt_gate exception_stub_30 + interrupt_gate exception_stub_31 +idt_end: + +/* IDT pointer for use with lidt */ +idt_ptr: + .word idt_end - idt - 1 + .long idt + + .global exception_init_asm +exception_init_asm: + /* Save eax so we can use it as a temporary variable. */ + pushl %eax + + /* Install the GDT. */ + lgdt gdt_ptr + /* Load the segment registers from it. */ + ljmp $0x10, $1f +1: movl $0x18, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* + * Loop over the entries which start out as two copies of the target + * address. We can turn them into real interrupt gates by selectively + * replacing certain bit fields. + */ + movl $idt, %eax +1: + andl $0x0000ffff, (%eax) + orl $0x00100000, (%eax) + andl $0xffff0000, 4(%eax) + orl $0x0000ee00, 4(%eax) + addl $8, %eax + cmp $idt_end, %eax + jne 1b + + /* Install the IDT. */ + lidt idt_ptr + + /* Restore eax and return to the caller. */ + popl %eax + ret diff --git a/payloads/libpayload/arch/x86/main.c b/payloads/libpayload/arch/x86/main.c index c788f0f670..ee52a937e6 100644 --- a/payloads/libpayload/arch/x86/main.c +++ b/payloads/libpayload/arch/x86/main.c @@ -27,6 +27,7 @@ * SUCH DAMAGE. */ +#include <exception.h> #include <libpayload.h> unsigned long loader_eax; /**< The value of EAX passed from the loader */ @@ -54,6 +55,8 @@ void start_main(void) console_init(); #endif + exception_init(); + /* * Any other system init that has to happen before the * user gets control goes here. |