summaryrefslogtreecommitdiff
path: root/payloads/libpayload/arch/x86
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/arch/x86')
-rw-r--r--payloads/libpayload/arch/x86/Makefile.inc1
-rw-r--r--payloads/libpayload/arch/x86/exception.c199
-rw-r--r--payloads/libpayload/arch/x86/exception_asm.S291
-rw-r--r--payloads/libpayload/arch/x86/main.c3
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.