diff options
author | Michael Brown | 2016-02-19 02:50:13 +0100 |
---|---|---|
committer | Michael Brown | 2016-02-19 03:58:09 +0100 |
commit | d1562c38a6882a129998935cd63d09ab18f77add (patch) | |
tree | e834487ca48085ca2f62d200419e6a839301a514 /src/arch/x86/include/librm.h | |
parent | [relocate] Preserve page alignment during relocation (diff) | |
download | ipxe-d1562c38a6882a129998935cd63d09ab18f77add.tar.gz ipxe-d1562c38a6882a129998935cd63d09ab18f77add.tar.xz ipxe-d1562c38a6882a129998935cd63d09ab18f77add.zip |
[librm] Prepare for long-mode memory map
The bulk of the iPXE binary (the .textdata section) is physically
relocated at runtime to the top of the 32-bit address space in order
to allow space for an OS to be loaded. The relocation is achieved
with the assistance of segmentation: we adjust the code and data
segment bases so that the link-time addresses remain valid.
Segmentation is not available (for normal code and data segments) in
long mode. We choose to compile the C code with -mcmodel=kernel and
use a link-time address of 0xffffffffeb000000. This choice allows us
to identity-map the entirety of the 32-bit address space, and to alias
our chosen link-time address to the physical location of our .textdata
section. (This requires the .textdata section to always be aligned to
a page boundary.)
We simultaneously choose to set the 32-bit virtual address segment
bases such that the link-time addresses may simply be truncated to 32
bits in order to generate a valid 32-bit virtual address. This allows
symbols in .textdata to be trivially accessed by both 32-bit and
64-bit code.
There is no (sensible) way in 32-bit assembly code to generate the
required R_X86_64_32S relocation records for these truncated symbols.
However, subtracting the fixed constant 0xffffffff00000000 has the
same effect as truncation, and can be represented in a standard
R_X86_64_32 relocation record. We define the VIRTUAL() macro to
abstract away this truncation operation, and apply it to all
references by 32-bit (or 16-bit) assembly code to any symbols within
the .textdata section.
We define "virt_offset" for a 64-bit build as "the value to be added
to an address within .textdata in order to obtain its physical
address". With this definition, the low 32 bits of "virt_offset" can
be treated by 32-bit code as functionally equivalent to "virt_offset"
in a 32-bit build.
We define "text16" and "data16" for a 64-bit build as the physical
addresses of the .text16 and .data16 sections. Since a physical
address within the 32-bit address space may be used directly as a
64-bit virtual address (thanks to the identity map), this definition
provides the most natural access to variables in .text16 and .data16.
Note that this requires a minor adjustment in prot_to_real(), which
accesses .text16 using 32-bit virtual addresses.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/arch/x86/include/librm.h')
-rw-r--r-- | src/arch/x86/include/librm.h | 39 |
1 files changed, 36 insertions, 3 deletions
diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h index 97461640..fc31c503 100644 --- a/src/arch/x86/include/librm.h +++ b/src/arch/x86/include/librm.h @@ -7,7 +7,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * Don't change these unless you really know what you're doing. */ - #define VIRTUAL_CS 0x08 #define VIRTUAL_DS 0x10 #define PHYSICAL_CS 0x18 @@ -16,6 +15,40 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define REAL_DS 0x30 #define P2R_DS 0x38 +/* Calculate symbol address within VIRTUAL_CS or VIRTUAL_DS + * + * In a 64-bit build, we set the bases of VIRTUAL_CS and VIRTUAL_DS + * such that truncating a .textdata symbol value to 32 bits gives a + * valid 32-bit virtual address. + * + * The C code is compiled with -mcmodel=kernel and so we must place + * all .textdata symbols within the negative 2GB of the 64-bit address + * space. Consequently, all .textdata symbols will have the MSB set + * after truncation to 32 bits. This means that a straightforward + * R_X86_64_32 relocation record for the symbol will fail, since the + * truncated symbol value will not correctly zero-extend to the + * original 64-bit value. + * + * Using an R_X86_64_32S relocation record would work, but there is no + * (sensible) way to generate these relocation records within 32-bit + * or 16-bit code. + * + * The simplest solution is to generate an R_X86_64_32 relocation + * record with an addend of (-0xffffffff00000000). Since all + * .textdata symbols are within the negative 2GB of the 64-bit address + * space, this addend acts to effectively truncate the symbol to 32 + * bits, thereby matching the semantics of the R_X86_64_32 relocation + * records generated for 32-bit and 16-bit code. + * + * In a 32-bit build, this problem does not exist, and we can just use + * the .textdata symbol values directly. + */ +#ifdef __x86_64__ +#define VIRTUAL(address) ( (address) - 0xffffffff00000000 ) +#else +#define VIRTUAL(address) (address) +#endif + #ifdef ASSEMBLY /** @@ -24,7 +57,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v function C function */ .macro virtcall function - pushl $\function + pushl $VIRTUAL(\function) call prot_call .endm @@ -42,7 +75,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v function C function */ #define VIRT_CALL( function ) \ - "pushl $( " #function " )\n\t" \ + "pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \ "call prot_call\n\t" /* Variables in librm.S */ |