diff options
author | Michael Brown | 2016-02-18 03:44:19 +0100 |
---|---|---|
committer | Michael Brown | 2016-02-24 04:10:12 +0100 |
commit | 614305743031bdfc02cb4ce346e450cd1d476e17 (patch) | |
tree | a353314423efd2eab372bd47bf9a8e2fae12a9e5 /src/arch/x86/transitions/librm_mgmt.c | |
parent | [librm] Rename prot_call() to virt_call() (diff) | |
download | ipxe-614305743031bdfc02cb4ce346e450cd1d476e17.tar.gz ipxe-614305743031bdfc02cb4ce346e450cd1d476e17.tar.xz ipxe-614305743031bdfc02cb4ce346e450cd1d476e17.zip |
[librm] Add support for running in 64-bit long mode
Add support for running the BIOS version of iPXE in 64-bit long mode.
A 64-bit BIOS version of iPXE can be built using e.g.
make bin-x86_64-pcbios/ipxe.usb
make bin-x86_64-pcbios/8086100e.mrom
The 64-bit BIOS version should appear to function identically to the
normal 32-bit BIOS version. The physical memory layout is unaltered:
iPXE is still relocated to the top of the available 32-bit address
space. The code is linked to a virtual address of 0xffffffffeb000000
(in the negative 2GB as required by -mcmodel=kernel), with 4kB pages
created to cover the whole of .textdata. 2MB pages are created to
cover the whole of the 32-bit address space.
The 32-bit portions of the code run with VIRTUAL_CS and VIRTUAL_DS
configured such that truncating a 64-bit virtual address gives a
32-bit virtual address pointing to the same physical location.
The stack pointer remains as a physical address when running in long
mode (although the .stack section is accessible via the negative 2GB
virtual address); this is done in order to simplify the handling of
interrupts occurring while executing a portion of 32-bit code with
flat physical addressing via PHYS_CODE().
Interrupts may be enabled in either 64-bit long mode, 32-bit protected
mode with virtual addresses, 32-bit protected mode with physical
addresses, or 16-bit real mode. Interrupts occurring in any mode
other than real mode will be reflected down to real mode and handled
by whichever ISR is hooked into the BIOS interrupt vector table.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/x86/transitions/librm_mgmt.c')
-rw-r--r-- | src/arch/x86/transitions/librm_mgmt.c | 58 |
1 files changed, 44 insertions, 14 deletions
diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c index 32695ae0..a81270a1 100644 --- a/src/arch/x86/transitions/librm_mgmt.c +++ b/src/arch/x86/transitions/librm_mgmt.c @@ -23,12 +23,22 @@ extern char interrupt_wrapper[]; /** The interrupt vectors */ static struct interrupt_vector intr_vec[NUM_INT]; -/** The interrupt descriptor table */ -struct interrupt_descriptor idt[NUM_INT] __attribute__ (( aligned ( 16 ) )); +/** The 32-bit interrupt descriptor table */ +static struct interrupt32_descriptor +idt32[NUM_INT] __attribute__ (( aligned ( 16 ) )); + +/** The 32-bit interrupt descriptor table register */ +struct idtr32 idtr32 = { + .limit = ( sizeof ( idt32 ) - 1 ), +}; + +/** The 64-bit interrupt descriptor table */ +static struct interrupt64_descriptor +idt64[NUM_INT] __attribute__ (( aligned ( 16 ) )); /** The interrupt descriptor table register */ -struct idtr idtr = { - .limit = ( sizeof ( idt ) - 1 ), +struct idtr64 idtr64 = { + .limit = ( sizeof ( idt64 ) - 1 ), }; /** Timer interrupt profiler */ @@ -75,13 +85,27 @@ void remove_user_from_rm_stack ( userptr_t data, size_t size ) { * @v vector Interrupt vector, or NULL to disable */ void set_interrupt_vector ( unsigned int intr, void *vector ) { - struct interrupt_descriptor *idte; - - idte = &idt[intr]; - idte->segment = VIRTUAL_CS; - idte->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); - idte->low = ( ( ( intptr_t ) vector ) & 0xffff ); - idte->high = ( ( ( intptr_t ) vector ) >> 16 ); + struct interrupt32_descriptor *idte32; + struct interrupt64_descriptor *idte64; + intptr_t addr = ( ( intptr_t ) vector ); + + /* Populate 32-bit interrupt descriptor */ + idte32 = &idt32[intr]; + idte32->segment = VIRTUAL_CS; + idte32->attr = ( vector ? ( IDTE_PRESENT | IDTE_TYPE_IRQ32 ) : 0 ); + idte32->low = ( addr >> 0 ); + idte32->high = ( addr >> 16 ); + + /* Populate 64-bit interrupt descriptor, if applicable */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + idte64 = &idt64[intr]; + idte64->segment = LONG_CS; + idte64->attr = ( vector ? + ( IDTE_PRESENT | IDTE_TYPE_IRQ64 ) : 0 ); + idte64->low = ( addr >> 0 ); + idte64->mid = ( addr >> 16 ); + idte64->high = ( ( ( uint64_t ) addr ) >> 32 ); + } } /** @@ -95,7 +119,7 @@ void init_idt ( void ) { /* Initialise the interrupt descriptor table and interrupt vectors */ for ( intr = 0 ; intr < NUM_INT ; intr++ ) { vec = &intr_vec[intr]; - vec->pushal = PUSHAL_INSN; + vec->push = PUSH_INSN; vec->movb = MOVB_INSN; vec->intr = intr; vec->jmp = JMP_INSN; @@ -107,8 +131,14 @@ void init_idt ( void ) { intr_vec, sizeof ( intr_vec[0] ), virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); - /* Initialise the interrupt descriptor table register */ - idtr.base = virt_to_phys ( idt ); + /* Initialise the 32-bit interrupt descriptor table register */ + idtr32.base = virt_to_phys ( idt32 ); + + /* Initialise the 64-bit interrupt descriptor table register, + * if applicable. + */ + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) + idtr64.base = virt_to_phys ( idt64 ); } /** |