diff options
-rw-r--r-- | src/Makefile | 3 | ||||
-rw-r--r-- | src/arch/x86/include/librm.h | 48 | ||||
-rw-r--r-- | src/arch/x86/transitions/librm.S | 450 | ||||
-rw-r--r-- | src/arch/x86/transitions/librm_mgmt.c | 58 |
4 files changed, 498 insertions, 61 deletions
diff --git a/src/Makefile b/src/Makefile index 2a9cc9e8..3ffa1eba 100644 --- a/src/Makefile +++ b/src/Makefile @@ -151,6 +151,9 @@ all : $(ALL) everything : $(Q)$(MAKE) --no-print-directory $(ALL) \ bin/3c509.rom bin/intel.rom bin/intel.mrom \ + bin-x86_64-pcbios/8086100e.mrom bin-x86_64-pcbios/intel.rom \ + bin-x86_64-pcbios/ipxe.usb bin-x86_64-pcbios/ipxe.pxe \ + bin-x86_64-pcbios/undionly.kpxe \ bin-i386-efi/ipxe.efi bin-i386-efi/ipxe.efidrv \ bin-i386-efi/ipxe.efirom \ bin-x86_64-efi/ipxe.efi bin-x86_64-efi/ipxe.efidrv \ diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 8e39e91b..02d3081b 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -14,6 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define REAL_CS 0x28 #define REAL_DS 0x30 #define P2R_DS 0x38 +#define LONG_CS 0x40 /* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS * @@ -286,16 +287,24 @@ extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); /** Number of interrupts */ #define NUM_INT 256 -/** An interrupt descriptor table register */ -struct idtr { +/** A 32-bit interrupt descriptor table register */ +struct idtr32 { /** Limit */ uint16_t limit; /** Base */ uint32_t base; } __attribute__ (( packed )); -/** An interrupt descriptor table entry */ -struct interrupt_descriptor { +/** A 64-bit interrupt descriptor table register */ +struct idtr64 { + /** Limit */ + uint16_t limit; + /** Base */ + uint64_t base; +} __attribute__ (( packed )); + +/** A 32-bit interrupt descriptor table entry */ +struct interrupt32_descriptor { /** Low 16 bits of address */ uint16_t low; /** Code segment */ @@ -308,23 +317,44 @@ struct interrupt_descriptor { uint16_t high; } __attribute__ (( packed )); +/** A 64-bit interrupt descriptor table entry */ +struct interrupt64_descriptor { + /** Low 16 bits of address */ + uint16_t low; + /** Code segment */ + uint16_t segment; + /** Unused */ + uint8_t unused; + /** Type and attributes */ + uint8_t attr; + /** Middle 16 bits of address */ + uint16_t mid; + /** High 32 bits of address */ + uint32_t high; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + /** Interrupt descriptor is present */ #define IDTE_PRESENT 0x80 /** Interrupt descriptor 32-bit interrupt gate type */ #define IDTE_TYPE_IRQ32 0x0e +/** Interrupt descriptor 64-bit interrupt gate type */ +#define IDTE_TYPE_IRQ64 0x0e + /** An interrupt vector * * Each interrupt vector comprises an eight-byte fragment of code: * - * 60 pushal + * 50 pushl %eax (or pushq %rax in long mode) * b0 xx movb $INT, %al * e9 xx xx xx xx jmp interrupt_wrapper */ struct interrupt_vector { - /** "pushal" instruction */ - uint8_t pushal; + /** "push" instruction */ + uint8_t push; /** "movb" instruction */ uint8_t movb; /** Interrupt number */ @@ -337,8 +367,8 @@ struct interrupt_vector { uint8_t next[0]; } __attribute__ (( packed )); -/** "pushal" instruction */ -#define PUSHAL_INSN 0x60 +/** "push %eax" instruction */ +#define PUSH_INSN 0x50 /** "movb" instruction */ #define MOVB_INSN 0xb0 diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S index af22dee9..dbb0b084 100644 --- a/src/arch/x86/transitions/librm.S +++ b/src/arch/x86/transitions/librm.S @@ -19,6 +19,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* CR4: physical address extensions */ #define CR4_PAE ( 1 << 5 ) +/* Extended feature enable MSR (EFER) */ +#define MSR_EFER 0xc0000080 + +/* EFER: long mode enable */ +#define EFER_LME ( 1 << 8 ) + /* Page: present */ #define PG_P 0x01 @@ -49,6 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS ) #define SIZEOF_I386_FLAGS 4 #define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS ) +#define SIZEOF_X86_64_REGS 128 /* Size of an address */ #ifdef __x86_64__ @@ -57,6 +64,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define SIZEOF_ADDR 4 #endif +/* Default code size */ +#ifdef __x86_64__ +#define CODE_DEFAULT code64 +#else +#define CODE_DEFAULT code32 +#endif + /* Selectively assemble code for 32-bit/64-bit builds */ #ifdef __x86_64__ #define if32 if 0 @@ -124,6 +138,11 @@ p2r_ds: /* 16 bit real mode data segment for prot_to_real transition */ .word 0xffff, ( P2R_DS << 4 ) .byte 0, 0x93, 0x00, 0 + .org gdt + LONG_CS, 0 +long_cs: /* 64 bit long mode code segment */ + .word 0, 0 + .byte 0, 0x9a, 0x20, 0 + gdt_end: .equ gdt_length, gdt_end - gdt @@ -256,10 +275,9 @@ init_librm: .if32 ; subl %edi, %eax ; .endif movl %eax, rm_data16 -.if64 ; /* Reset page tables, if applicable */ - xorl %eax, %eax - movl %eax, pml4 -.endif + /* Configure virt_call for protected mode, if applicable */ +.if64 ; movl $VIRTUAL(vc_pmode), %cs:vc_jmp_offset ; .endif + /* Switch to protected mode */ virtcall init_librm_pmode .section ".text.init_librm", "ax", @progbits @@ -276,8 +294,10 @@ init_librm_pmode: rep movsl popw %ds -.if64 ; /* Initialise page tables, if applicable */ +.if64 ; /* Initialise long mode, if applicable */ movl VIRTUAL(virt_offset), %edi + leal VIRTUAL(p2l_ljmp_target)(%edi), %eax + movl %eax, VIRTUAL(p2l_ljmp_offset) call init_pages .endif /* Return to real mode */ @@ -286,6 +306,9 @@ init_librm_pmode: .code16 init_librm_rmode: + /* Configure virt_call for long mode, if applicable */ +.if64 ; movl $VIRTUAL(vc_lmode), %cs:vc_jmp_offset ; .endif + /* Initialise IDT */ virtcall init_idt @@ -361,9 +384,10 @@ real_to_prot: movw %ax, %gs movw %ax, %ss - /* Switch to protected mode */ + /* Switch to protected mode (with paging disabled if applicable) */ cli movl %cr0, %eax +.if64 ; andl $~CR0_PG, %eax ; .endif orb $CR0_PE, %al movl %eax, %cr0 data32 ljmp $VIRTUAL_CS, $VIRTUAL(r2p_pmode) @@ -380,7 +404,7 @@ r2p_pmode: movl VIRTUAL(pm_esp), %esp /* Load protected-mode interrupt descriptor table */ - lidt VIRTUAL(idtr) + lidt VIRTUAL(idtr32) /* Record real-mode %ss:sp (after removal of data) */ movw %bp, VIRTUAL(rm_ss) @@ -640,10 +664,233 @@ intr_to_prot: .equ _intr_to_virt, intr_to_prot /**************************************************************************** + * prot_to_long (protected-mode near call, 32-bit virtual return address) + * + * Switch from 32-bit protected mode with virtual addresses to 64-bit + * long mode. The protected-mode %esp is adjusted to a physical + * address. All other registers are preserved. + * + * The return address for this function should be a 32-bit (sic) + * virtual address. + * + **************************************************************************** + */ + .if64 + + .section ".text.prot_to_long", "ax", @progbits + .code32 +prot_to_long: + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edx + + /* Set up PML4 */ + movl VIRTUAL(pml4), %eax + movl %eax, %cr3 + + /* Enable PAE */ + movl %cr4, %eax + orb $CR4_PAE, %al + movl %eax, %cr4 + + /* Enable long mode */ + movl $MSR_EFER, %ecx + rdmsr + orw $EFER_LME, %ax + wrmsr + + /* Enable paging */ + movl %cr0, %eax + orl $CR0_PG, %eax + movl %eax, %cr0 + + /* Restore registers */ + popl %edx + popl %ecx + popl %eax + + /* Construct 64-bit return address */ + pushl (%esp) + movl $0xffffffff, 4(%esp) +p2l_ljmp: + /* Switch to long mode (using a physical %rip) */ + ljmp $LONG_CS, $0 + .code64 +p2l_lmode: + /* Adjust and zero-extend %esp to a physical address */ + addl virt_offset, %esp + + /* Use long-mode IDT */ + lidt idtr64 + + /* Return to virtual address */ + ret + + /* Long mode jump offset and target. Required since an ljmp + * in protected mode will zero-extend the offset, and so + * cannot reach an address within the negative 2GB as used by + * -mcmodel=kernel. Assigned by the call to init_librm. + */ + .equ p2l_ljmp_offset, ( p2l_ljmp + 1 ) + .equ p2l_ljmp_target, p2l_lmode + + .endif + +/**************************************************************************** + * long_to_prot (long-mode near call, 64-bit virtual return address) + * + * Switch from 64-bit long mode to 32-bit protected mode with virtual + * addresses. The long-mode %rsp is adjusted to a virtual address. + * All other registers are preserved. + * + * The return address for this function should be a 64-bit (sic) + * virtual address. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_to_prot", "ax", @progbits + .code64 +long_to_prot: + /* Switch to protected mode */ + ljmp *l2p_vector + .code32 +l2p_pmode: + /* Adjust %esp to a virtual address */ + subl VIRTUAL(virt_offset), %esp + + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edx + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable PAE (in case external non-PAE-aware code enables paging) */ + movl %cr4, %eax + andb $~CR4_PAE, %al + movl %eax, %cr4 + + /* Disable long mode */ + movl $MSR_EFER, %ecx + rdmsr + andw $~EFER_LME, %ax + wrmsr + + /* Restore registers */ + popl %edx + popl %ecx + popl %eax + + /* Use protected-mode IDT */ + lidt VIRTUAL(idtr32) + + /* Return */ + ret $4 + + /* Long mode jump vector. Required since there is no "ljmp + * immediate" instruction in long mode. + */ + .section ".data.l2p_vector", "aw", @progbits +l2p_vector: + .long VIRTUAL(l2p_pmode), VIRTUAL_CS + + .endif + +/**************************************************************************** + * long_save_regs (long-mode near call, 64-bit virtual return address) + * + * Preserve registers that are accessible only in long mode. This + * includes %r8-%r15 and the upper halves of %rax, %rbx, %rcx, %rdx, + * %rsi, %rdi, and %rbp. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_preserve_regs", "ax", @progbits + .code64 +long_preserve_regs: + /* Preserve registers */ + pushq %rax + pushq %rcx + pushq %rdx + pushq %rbx + pushq %rsp + pushq %rbp + pushq %rsi + pushq %rdi + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + /* Return */ + jmp *SIZEOF_X86_64_REGS(%rsp) + + .endif + +/**************************************************************************** + * long_restore_regs (long-mode near call, 64-bit virtual return address) + * + * Restore registers that are accessible only in long mode. This + * includes %r8-%r15 and the upper halves of %rax, %rbx, %rcx, %rdx, + * %rsi, %rdi, and %rbp. + * + **************************************************************************** + */ + .if64 + + .section ".text.long_restore_regs", "ax", @progbits + .code64 +long_restore_regs: + /* Move return address above register dump */ + popq SIZEOF_X86_64_REGS(%rsp) + + /* Restore registers */ + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %r11 + popq %r10 + popq %r9 + popq %r8 + movl %edi, (%rsp) + popq %rdi + movl %esi, (%rsp) + popq %rsi + movl %ebp, (%rsp) + popq %rbp + leaq 8(%rsp), %rsp /* discard */ + movl %ebx, (%rsp) + popq %rbx + movl %edx, (%rsp) + popq %rdx + movl %ecx, (%rsp) + popq %rcx + movl %eax, (%rsp) + popq %rax + + /* Return */ + ret + + .endif + +/**************************************************************************** * virt_call (real-mode near call, 16-bit real-mode near return address) * - * Call a specific C function in the protected-mode code. The - * prototype of the C function must be + * Call a specific C function in 32-bit protected mode or 64-bit long + * mode (as applicable). The prototype of the C function must be * void function ( struct i386_all_regs *ix86 ); * ix86 will point to a struct containing the real-mode registers * at entry to virt_call(). @@ -662,7 +909,7 @@ intr_to_prot: * critical data to registers before calling main()). * * Parameters: - * function : virtual address of protected-mode function to call + * function : 32-bit virtual address of function to call * * Example usage: * pushl $pxe_api_call @@ -674,6 +921,12 @@ intr_to_prot: .struct 0 VC_OFFSET_GDT: .space 6 VC_OFFSET_IDT: .space 6 +.if64 +VC_OFFSET_PADDING64: .space 4 /* for alignment */ +VC_OFFSET_CR3: .space 4 +VC_OFFSET_CR4: .space 4 +VC_OFFSET_EMER: .space 8 +.endif VC_OFFSET_IX86: .space SIZEOF_I386_ALL_REGS VC_OFFSET_PADDING: .space 2 /* for alignment */ VC_OFFSET_RETADDR: .space 2 @@ -701,22 +954,49 @@ virt_call: sidt VC_OFFSET_IDT(%bp) sgdt VC_OFFSET_GDT(%bp) +.if64 ; /* Preserve control registers, if applicable */ + movl $MSR_EFER, %ecx + rdmsr + movl %eax, (VC_OFFSET_EMER+0)(%bp) + movl %edx, (VC_OFFSET_EMER+4)(%bp) + movl %cr4, %eax + movl %eax, VC_OFFSET_CR4(%bp) + movl %cr3, %eax + movl %eax, VC_OFFSET_CR3(%bp) +.endif /* For sanity's sake, clear the direction flag as soon as possible */ cld /* Switch to protected mode and move register dump to PM stack */ movl $VC_OFFSET_END, %ecx pushl $VIRTUAL(vc_pmode) - jmp real_to_prot +vc_jmp: jmp real_to_prot .section ".text.virt_call", "ax", @progbits .code32 vc_pmode: - /* Call function */ + /* Call function (in protected mode) */ leal VC_OFFSET_IX86(%esp), %eax pushl %eax call *(VC_OFFSET_FUNCTION+4)(%esp) popl %eax /* discard */ +.if64 ; /* Switch to long mode */ + jmp 1f +vc_lmode: + call prot_to_long + .code64 + + /* Call function (in long mode) */ + leaq VC_OFFSET_IX86(%rsp), %rdi + pushq %rdi + movslq (VC_OFFSET_FUNCTION+8)(%rsp), %rax + callq *%rax + popq %rdi /* discard */ + + /* Switch to protected mode */ + call long_to_prot +1: .code32 +.endif /* Switch to real mode and move register dump back to RM stack */ movl $VC_OFFSET_END, %ecx movl %esp, %esi @@ -725,6 +1005,17 @@ vc_pmode: .section ".text16.virt_call", "ax", @progbits .code16 vc_rmode: +.if64 ; /* Restore control registers, if applicable */ + movw %sp, %bp + movl VC_OFFSET_CR3(%bp), %eax + movl %eax, %cr3 + movl VC_OFFSET_CR4(%bp), %eax + movl %eax, %cr4 + movl (VC_OFFSET_EMER+0)(%bp), %eax + movl (VC_OFFSET_EMER+4)(%bp), %edx + movl $MSR_EFER, %ecx + wrmsr +.endif /* Restore registers and flags and return */ addw $( VC_OFFSET_IX86 + 4 /* also skip %cs and %ss */ ), %sp popw %ds @@ -744,18 +1035,23 @@ vc_rmode: /* Return and discard function parameters */ ret $( VC_OFFSET_END - VC_OFFSET_PARAMS ) + + /* Protected-mode jump target */ + .equ vc_jmp_offset, ( vc_jmp - 4 ) + /**************************************************************************** * real_call (protected-mode near call, 32-bit virtual return address) + * real_call (long-mode near call, 64-bit virtual return address) * - * Call a real-mode function from protected-mode code. + * Call a real-mode function from protected-mode or long-mode code. * * The non-segment register values will be passed directly to the * real-mode code. The segment registers will be set as per * prot_to_real. The non-segment register values set by the real-mode - * function will be passed back to the protected-mode caller. A - * result of this is that this routine cannot be called directly from - * C code, since it clobbers registers that the C ABI expects the - * callee to preserve. + * function will be passed back to the protected-mode or long-mode + * caller. A result of this is that this routine cannot be called + * directly from C code, since it clobbers registers that the C ABI + * expects the callee to preserve. * * librm.h defines a convenient macro REAL_CODE() for using real_call. * See librm.h and realmode.h for details and examples. @@ -769,16 +1065,25 @@ vc_rmode: .struct 0 RC_OFFSET_REGS: .space SIZEOF_I386_REGS RC_OFFSET_REGS_END: -RC_OFFSET_RETADDR: .space 4 +.if64 +RC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS +RC_OFFSET_LREG_RETADDR: .space SIZEOF_ADDR +.endif +RC_OFFSET_RETADDR: .space SIZEOF_ADDR RC_OFFSET_PARAMS: -RC_OFFSET_FUNCTION: .space 4 +RC_OFFSET_FUNCTION: .space SIZEOF_ADDR RC_OFFSET_END: .previous .section ".text.real_call", "ax", @progbits - .code32 + .CODE_DEFAULT .globl real_call real_call: +.if64 ; /* Preserve registers and switch to protected mode, if applicable */ + call long_preserve_regs + call long_to_prot + .code32 +.endif /* Create register dump and function pointer copy on PM stack */ pushal pushl RC_OFFSET_FUNCTION(%esp) @@ -810,6 +1115,11 @@ rc_pmode: /* Restore registers */ popal +.if64 ; /* Switch to long mode and restore registers, if applicable */ + call prot_to_long + .code64 + call long_restore_regs +.endif /* Return and discard function parameters */ ret $( RC_OFFSET_END - RC_OFFSET_PARAMS ) @@ -830,6 +1140,7 @@ rm_default_gdtr_idtr: /**************************************************************************** * phys_call (protected-mode near call, 32-bit virtual return address) + * phys_call (long-mode near call, 64-bit virtual return address) * * Call a function with flat 32-bit physical addressing * @@ -846,16 +1157,25 @@ rm_default_gdtr_idtr: **************************************************************************** */ .struct 0 -PHC_OFFSET_RETADDR: .space 4 +.if64 +PHC_OFFSET_LREGS: .space SIZEOF_X86_64_REGS +PHC_OFFSET_LREG_RETADDR:.space SIZEOF_ADDR +.endif +PHC_OFFSET_RETADDR: .space SIZEOF_ADDR PHC_OFFSET_PARAMS: -PHC_OFFSET_FUNCTION: .space 4 +PHC_OFFSET_FUNCTION: .space SIZEOF_ADDR PHC_OFFSET_END: .previous .section ".text.phys_call", "ax", @progbits - .code32 + .CODE_DEFAULT .globl phys_call phys_call: +.if64 ; /* Preserve registers and switch to protected mode, if applicable */ + call long_preserve_regs + call long_to_prot + .code32 +.endif /* Adjust function pointer to a physical address */ pushl %ebp movl VIRTUAL(virt_offset), %ebp @@ -874,6 +1194,11 @@ phys_call: /* Switch to virtual addresses */ call phys_to_prot +.if64 ; /* Switch to long mode and restore registers, if applicable */ + call prot_to_long + .code64 + call long_restore_regs +.endif /* Return and discard function parameters */ ret $( PHC_OFFSET_END - PHC_OFFSET_PARAMS ) @@ -900,15 +1225,15 @@ flatten_real_mode: ret .section ".text.flatten_dummy", "ax", @progbits - .code32 + .CODE_DEFAULT flatten_dummy: ret /**************************************************************************** * Interrupt wrapper * - * Used by the protected-mode interrupt vectors to call the - * interrupt() function. + * Used by the protected-mode and long-mode interrupt vectors to call + * the interrupt() function. * * May be entered with either physical or virtual stack segment. **************************************************************************** @@ -917,6 +1242,24 @@ flatten_dummy: .code32 .globl interrupt_wrapper interrupt_wrapper: + /* Preserve registers (excluding already-saved %eax and + * otherwise unused registers which are callee-save for both + * 32-bit and 64-bit ABIs). + */ + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + + /* Expand IRQ number to whole %eax register */ + movzbl %al, %eax + +.if64 ; /* Skip transition to long mode, if applicable */ + movw %cs, %bx + cmpw $LONG_CS, %bx + je 1f +.endif /* Preserve segment registers and original %esp */ pushl %ds pushl %es @@ -927,14 +1270,39 @@ interrupt_wrapper: /* Switch to virtual addressing */ call intr_to_prot - - /* Expand IRQ number to whole %eax register */ - movzbl %al, %eax - +.if64 + /* Switch to long mode */ + call prot_to_long + .code64 + +1: /* Preserve long-mode caller-save registers */ + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + + /* Expand IRQ number to whole %rdi register */ + movl %eax, %edi +.endif /* Call interrupt handler */ call interrupt +.if64 + /* Restore long-mode caller-save registers */ + popq %r11 + popq %r10 + popq %r9 + popq %r8 + + /* Skip transition back to protected mode, if applicable */ + cmpw $LONG_CS, %bx + je 1f - /* Restore original stack and segment registers */ + /* Switch to protected mode */ + call long_to_prot + .code32 + cmpw $LONG_CS, %bx +.endif + /* Restore segment registers and original %esp */ lss (%esp), %esp popl %ss popl %gs @@ -942,9 +1310,17 @@ interrupt_wrapper: popl %es popl %ds - /* Restore registers and return */ - popal - iret +1: /* Restore registers */ + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + + /* Return from interrupt (with REX prefix if required) */ +.if64 ; jne 1f ; .byte 0x48 ; .endif +1: iret /**************************************************************************** * Page tables @@ -1022,7 +1398,7 @@ pde_low: pte_textdata: /* Allocated by linker script; must be at the end of .textdata */ - .section ".bss16.pml4", "aw", @nobits + .section ".bss.pml4", "aw", @nobits pml4: .long 0 /**************************************************************************** @@ -1080,9 +1456,7 @@ init_pages: /* Record PML4 physical address */ leal VIRTUAL(pml4e)(%edi), %eax - movl VIRTUAL(data16), %ebx - subl %edi, %ebx - movl %eax, pml4(%ebx) + movl %eax, VIRTUAL(pml4) /* Return */ ret 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 ); } /** |