| /* |
| * |
| * Copyright 2024 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 16 |
| .global exception_stack_end |
| exception_stack_end: |
| .quad 0 |
| .global exception_state |
| exception_state: |
| .quad 0 |
| |
| /* Some temporary variables which are used while saving exception state. */ |
| vector: |
| .quad 0 |
| error_code: |
| .quad 0 |
| old_rax: |
| .quad 0 |
| old_rcx: |
| .quad 0 |
| |
| .align 16 |
| |
| /* |
| * 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: |
| movq $0, error_code |
| movq $\num, vector |
| jmp exception_common |
| .endm |
| |
| .macro stub_err num |
| exception_stub_\num: |
| pop error_code |
| movq $\num, vector |
| jmp exception_common |
| .endm |
| |
| .altmacro |
| .macro user_defined_stubs from, to |
| stub \from |
| .if \to-\from |
| user_defined_stubs %(from+1),\to |
| .endif |
| .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 |
| /* Split the macro so we avoid a stack overflow. */ |
| user_defined_stubs 32, 63 |
| user_defined_stubs 64, 127 |
| user_defined_stubs 128, 191 |
| user_defined_stubs 192, 255 |
| |
| exception_common: |
| /* |
| * At this point, on x86-64, on the stack there is: |
| * 0(%rsp) rip |
| * 8(%rsp) cs |
| * 16(%rsp) rflags |
| * 24(%rsp) rsp |
| * 32(%rsp) ss |
| * |
| * This section sets up the exception stack. |
| * It saves the old stack pointer (rsp) to preserve RIP, CS, RFLAGS and SS. |
| * Then sets up the new stack pointer to point to the exception stack area. |
| */ |
| movq %rax, old_rax |
| movq %rcx, old_rcx |
| |
| mov %rsp, %rax |
| movq exception_stack_end, %rsp |
| /* |
| * The `exception_state` struct is not 16-byte aligned. |
| * Push an extra 8 bytes to ensure the stack pointer |
| * is 16-byte aligned before calling exception_dispatch. |
| */ |
| push $0 |
| |
| /* |
| * Push values onto the top of the exception stack to form an |
| * exception state structure. |
| */ |
| push vector |
| push error_code |
| |
| /* push of the gs, fs, es, ds, ss and cs */ |
| mov %gs, %rcx |
| movl %ecx, -4(%rsp) /* gs */ |
| mov %fs, %rcx |
| movl %ecx, -8(%rsp) /* fs */ |
| movl $0, -12(%rsp) /* es */ |
| movl $0, -16(%rsp) /* ds */ |
| movq 32(%rax), %rcx |
| movl %ecx, -20(%rsp) /* ss */ |
| movq 8(%rax), %rcx |
| movl %ecx, -24(%rsp) /* cs */ |
| sub $24, %rsp |
| |
| push 16(%rax) /* rflags */ |
| push (%rax) /* rip */ |
| push %r15 |
| push %r14 |
| push %r13 |
| push %r12 |
| push %r11 |
| push %r10 |
| push %r9 |
| push %r8 |
| push 24(%rax) /* rsp */ |
| push %rbp |
| push %rdi |
| push %rsi |
| push %rdx |
| push old_rcx /* rcx */ |
| push %rbx |
| push old_rax /* rax */ |
| |
| /* |
| * Call the C exception handler. It will find the exception state |
| * using the exception_state global pointer. Not |
| * passing parameters means we don't have to worry about what ABI |
| * is being used. |
| */ |
| mov %rsp, exception_state |
| call exception_dispatch |
| |
| /* |
| * Restore state from the exception state structure, including any |
| * changes that might have been made. |
| */ |
| pop old_rax |
| pop %rbx |
| pop old_rcx |
| pop %rdx |
| pop %rsi |
| pop %rdi |
| pop %rbp |
| lea exception_stack, %rax |
| pop 24(%rax) /* rsp */ |
| pop %r8 |
| pop %r9 |
| pop %r10 |
| pop %r11 |
| pop %r12 |
| pop %r13 |
| pop %r14 |
| pop %r15 |
| pop (%rax) /* rip */ |
| pop 16(%rax) /* rflags */ |
| |
| /* pop of the gs, fs, es, ds, ss and cs */ |
| movl (%rsp), %ecx |
| movq %rcx, 8(%rax) /* cs */ |
| movl 4(%rsp), %ecx |
| movq %rcx, 32(%rax) /* ss */ |
| movl 16(%rsp), %ecx |
| mov %rcx, %fs /* fs */ |
| movl 20(%rsp), %ecx |
| mov %rcx, %gs /* gs */ |
| |
| mov %rax, %rsp |
| movq old_rax, %rax |
| movq old_rcx, %rcx |
| |
| iretq |
| |
| /* |
| * 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 16 |
| 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, 0x92, 0xcf, 0x00 |
| |
| /* selgdt 0x20, flat x64 code segment */ |
| .word 0xffff, 0x0000 |
| .byte 0x00, 0x9b, 0xaf, 0x00 |
| gdt_end: |
| |
| /* GDT pointer for use with lgdt */ |
| .global gdt_ptr |
| gdt_ptr: |
| .word gdt_end - gdt - 1 |
| .quad 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 |
| .word 0 /* patchable */ |
| .word 0x20 /* Target code segment selector */ |
| .word 0xee00 /* Present, Type 64-bit Interrupt Gate */ |
| .word 0 /* patchable */ |
| .quad \target /* patchable */ |
| .endm |
| |
| .altmacro |
| .macro user_defined_gates from, to |
| interrupt_gate exception_stub_\from |
| .if \to-\from |
| user_defined_gates %(from+1),\to |
| .endif |
| .endm |
| |
| .align 16 |
| .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 |
| user_defined_gates 32, 63 |
| user_defined_gates 64, 127 |
| user_defined_gates 128, 191 |
| user_defined_gates 192, 255 |
| idt_end: |
| |
| /* IDT pointer for use with lidt */ |
| idt_ptr: |
| .word idt_end - idt - 1 |
| .quad idt |
| |
| .section .text.exception_init_asm |
| .globl exception_init_asm |
| .type exception_init_asm, @function |
| |
| exception_init_asm: |
| /* Set up IDT entries */ |
| mov $idt, %rax |
| 1: |
| movq 8(%rax), %rdi |
| movw %di, (%rax) /* procedure entry point offset bits 0..15 */ |
| shr $16, %rdi |
| movw %di, 6(%rax) /* procedure entry point offset bits 16..31 */ |
| shr $16, %rdi |
| movl %edi, 8(%rax) /* procedure entry point offset bits 32..63 */ |
| movl $0, 12(%rax) /* reserved */ |
| add $16, %rax |
| cmp $idt_end, %rax |
| jne 1b |
| |
| /* Load the IDT */ |
| lidt idt_ptr |
| ret |