292 lines
6.5 KiB
ArmAsm
292 lines
6.5 KiB
ArmAsm
/*
|
|
* 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
|