Skip to content

Commit

Permalink
[librm] Add support for running in 64-bit long mode
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mcb30 committed Feb 24, 2016
1 parent e2cf313 commit 6143057
Show file tree
Hide file tree
Showing 4 changed files with 498 additions and 61 deletions.
3 changes: 3 additions & 0 deletions src/Makefile
Expand Up @@ -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 \
Expand Down
48 changes: 39 additions & 9 deletions src/arch/x86/include/librm.h
Expand Up @@ -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
*
Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand All @@ -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
Expand Down

0 comments on commit 6143057

Please sign in to comment.