*
* Don't change these unless you really know what you're doing.
*/
-
#define VIRTUAL_CS 0x08
#define VIRTUAL_DS 0x10
#define PHYSICAL_CS 0x18
#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
/**
* @v function C function
*/
.macro virtcall function
- pushl $\function
+ pushl $VIRTUAL(\function)
call prot_call
.endm
* @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 */
#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 )
-
- .arch i386
+
+/* Size of an address */
+#ifdef __x86_64__
+#define SIZEOF_ADDR 8
+#else
+#define SIZEOF_ADDR 4
+#endif
+
+/* Selectively assemble code for 32-bit/64-bit builds */
+#ifdef __x86_64__
+#define if32 if 0
+#define if64 if 1
+#else
+#define if32 if 1
+#define if64 if 0
+#endif
/****************************************************************************
* Global descriptor table
rm_ss: .word 0
.section ".data.pm_esp", "aw", @progbits
-pm_esp: .long _estack
+pm_esp: .long VIRTUAL(_estack)
/****************************************************************************
* Virtual address offsets
****************************************************************************
*/
.struct 0
-VA_VIRT_OFFSET: .space 4
-VA_TEXT16: .space 4
-VA_DATA16: .space 4
+VA_VIRT_OFFSET: .space SIZEOF_ADDR
+VA_TEXT16: .space SIZEOF_ADDR
+VA_DATA16: .space SIZEOF_ADDR
VA_SIZE:
.previous
* Parameters:
* %cs : .text16 segment
* %ds : .data16 segment
- * %edi : Physical base of protected-mode code (virt_offset)
+ * %edi : Physical base of protected-mode code
****************************************************************************
*/
.section ".text16.init_librm", "ax", @progbits
pushl %edi
/* Store rm_virt_offset and set up virtual_cs and virtual_ds segments */
+ subl $VIRTUAL(_textdata), %edi
movl %edi, rm_virt_offset
+.if64 ; setae (rm_virt_offset+4) ; .endif
movl %edi, %eax
movw $virtual_cs, %bx
call set_seg_base
shll $4, %eax
movw $real_cs, %bx
call set_seg_base
- subl %edi, %eax
+.if32 ; subl %edi, %eax ; .endif
movl %eax, rm_text16
/* Store rm_ds and rm_data16, set up real_ds segment and GDT base */
call set_seg_base
movl %eax, gdt_base
addl $gdt, gdt_base
- subl %edi, %eax
+.if32 ; subl %edi, %eax ; .endif
movl %eax, rm_data16
/* Switch to protected mode */
movw $REAL_DS, %ax
movw %ax, %ds
movl $rm_virt_addrs, %esi
- movl $virt_addrs, %edi
+ movl $VIRTUAL(virt_addrs), %edi
movl $( VA_SIZE / 4 ), %ecx
rep movsl
popw %ds
movl %cr0, %eax
orb $CR0_PE, %al
movl %eax, %cr0
- data32 ljmp $VIRTUAL_CS, $r2p_pmode
+ data32 ljmp $VIRTUAL_CS, $VIRTUAL(r2p_pmode)
.section ".text.real_to_prot", "ax", @progbits
.code32
r2p_pmode:
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
- movl pm_esp, %esp
+ movl VIRTUAL(pm_esp), %esp
/* Load protected-mode interrupt descriptor table */
- lidt idtr
+ lidt VIRTUAL(idtr)
/* Record real-mode %ss:sp (after removal of data) */
- movw %bp, rm_ss
+ movw %bp, VIRTUAL(rm_ss)
addl %ecx, %edx
- movw %dx, rm_sp
+ movw %dx, VIRTUAL(rm_sp)
/* Move data from RM stack to PM stack */
subl %ecx, %esp
.code32
prot_to_real:
/* Copy real-mode global descriptor table register to RM code segment */
- movl text16, %edi
+ movl VIRTUAL(text16), %edi
+.if64 ; subl VIRTUAL(virt_offset), %edi ; .endif
leal rm_gdtr(%edi), %edi
movsw
movsl
addl $4, %ecx
/* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
- movzwl rm_ss, %ebp
- movzwl rm_sp, %edx
+ movzwl VIRTUAL(rm_ss), %ebp
+ movzwl VIRTUAL(rm_sp), %edx
subl %ecx, %edx
movl %ebp, %eax
shll $4, %eax
leal (%eax,%edx), %edi
- subl virt_offset, %edi
+ subl VIRTUAL(virt_offset), %edi
/* Move data from PM stack to RM stack */
movl %esp, %esi
rep movsb
/* Record protected-mode %esp (after removal of data) */
- movl %esi, pm_esp
+ movl %esi, VIRTUAL(pm_esp)
/* Load real-mode segment limits */
movw $P2R_DS, %ax
/* Switch to protected mode and move register dump to PM stack */
movl $PC_OFFSET_END, %ecx
- pushl $pc_pmode
+ pushl $VIRTUAL(pc_pmode)
jmp real_to_prot
.section ".text.prot_call", "ax", @progbits
.code32
/* Switch to real mode and move register dump to RM stack */
movl $( RC_OFFSET_REGS_END + 4 /* function pointer copy */ ), %ecx
pushl $rc_rmode
- movl $rm_default_gdtr_idtr, %esi
+ movl $VIRTUAL(rm_default_gdtr_idtr), %esi
jmp prot_to_real
.section ".text16.real_call", "ax", @progbits
.code16
/* Switch to protected mode and move register dump back to PM stack */
movl $RC_OFFSET_REGS_END, %ecx
- pushl $rc_pmode
+ pushl $VIRTUAL(rc_pmode)
jmp real_to_prot
.section ".text.real_call", "ax", @progbits
.code32
* May be entered with either physical or virtual stack segment.
****************************************************************************
*/
+ .section ".text.interrupt_wrapper", "ax", @progbits
+ .code32
.globl interrupt_wrapper
interrupt_wrapper:
/* Preserve segment registers and original %esp */