Skip to content

Commit 64ee41c

Browse files
committed
pvh: Support booting via PVH ELFNOTE
This PR adds support for booting via the Xen HVM direct boot ABI. See: https://xenbits.xen.org/docs/4.12-testing/misc/pvh.html This uses a 32-bit unpaged entry point, so we just point it at ram32_start. This allows our firmware to be used with QEMU's -kernel option. Signed-off-by: Joe Richey <[email protected]>
1 parent feb6bfd commit 64ee41c

File tree

4 files changed

+101
-0
lines changed

4 files changed

+101
-0
lines changed

layout.ld

+8
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ ENTRY(ram64_start)
33
PHDRS
44
{
55
program PT_LOAD FILEHDR PHDRS ;
6+
note PT_NOTE ;
67
}
78

89
/* Loaders like to put stuff in low memory (< 1M), so we don't use it. */
910
ram_min = 1M;
1011
ram_max = 2M;
1112
/* Our stack grows down from ram_max. TODO: Add a guard for stack overflows. */
1213
stack_size = 64K;
14+
/* ram32.s needs 3 pages for initial page tables, use the space after ram_max */
15+
pml2 = ram_max;
16+
pml3 = pml2 + 0x1000;
17+
pml4 = pml3 + 0x1000;
1318

1419
SECTIONS
1520
{
@@ -26,6 +31,9 @@ SECTIONS
2631

2732
ASSERT((. <= ram_max - stack_size), "firmware size too big for RAM region")
2833

34+
/* These sections are not mapped into RAM */
35+
.note : { *(.note) } :note
36+
2937
/* Match edk2's GccBase.lds DISCARD section */
3038
/DISCARD/ : {
3139
*(.note.GNU-stack)

src/asm/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
global_asm!(include_str!("note.s"));
2+
global_asm!(include_str!("ram32.s"));
13
global_asm!(include_str!("ram64.s"));

src/asm/note.s

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.section .note, "a"
2+
3+
# From xen/include/public/elfnote.h, "Physical entry point into the kernel."
4+
XEN_ELFNOTE_PHYS32_ENTRY = 18
5+
6+
# We don't bother defining an ELFNOTE macro, as we only have one note.
7+
# This is equialent to the kernel's:
8+
# ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long ram32_start)
9+
.align 4
10+
.long name_end - name_start # namesz
11+
.long desc_end - desc_start # descsz
12+
.long XEN_ELFNOTE_PHYS32_ENTRY # type
13+
name_start:
14+
.asciz "Xen"
15+
name_end:
16+
.align 4
17+
desc_start:
18+
.long ram32_start
19+
desc_end:
20+
.align 4

src/asm/ram32.s

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
.section .text, "ax"
2+
.code32
3+
4+
ram32_start:
5+
# Indicate (via serial) that we are executing out of RAM
6+
movw $0x3f8, %dx
7+
movb $'R', %al
8+
outb %al, %dx
9+
10+
setup_page_tables:
11+
# First PML2 entry identity maps [0, 2 MiB)
12+
movl $0b10000011, (pml2) # huge (bit 7), writable (bit 1), present (bit 0)
13+
# First PML3 entry points to PML2 table
14+
movl $pml2, %eax
15+
orb $0b00000011, %al # writable (bit 1), present (bit 0)
16+
movl %eax, (pml3)
17+
# First PML4 entry points to PML3 table
18+
movl $pml3, %eax
19+
orb $0b00000011, %al # writable (bit 1), present (bit 0)
20+
movl %eax, (pml4)
21+
22+
enable_paging:
23+
# Load page table root into CR3
24+
movl $pml4, %eax
25+
movl %eax, %cr3
26+
27+
# Set CR4.PAE (Physical Address Extension)
28+
movl %cr4, %eax
29+
orb $0b00100000, %al # Set bit 5
30+
movl %eax, %cr4
31+
# Set EFER.LME (Long Mode Enable)
32+
movl $0xC0000080, %ecx
33+
rdmsr
34+
orb $0b00000001, %ah # Set bit 8
35+
wrmsr
36+
# Set CRO.PG (Paging)
37+
movl %cr0, %eax
38+
orl $(1 << 31), %eax
39+
movl %eax, %cr0
40+
41+
# Indicate (via serial) that we have enabled paging
42+
movw $0x3f8, %dx
43+
movb $'P', %al
44+
outb %al, %dx
45+
46+
jump_to_64bit:
47+
# We are now in 32-bit compatibility mode. To enter 64-bit mode, we need to
48+
# load a 64-bit code segment into our GDT.
49+
lgdtl gdt64_ptr
50+
# Set CS to a 64-bit segment and jump to 64-bit code.
51+
ljmpl $(code64_desc - gdt64_start), $ram64_start
52+
53+
gdt64_ptr:
54+
.short gdt64_end - gdt64_start - 1 # GDT length is actually (length - 1)
55+
.long gdt64_start
56+
gdt64_start:
57+
# First descriptor is null
58+
.quad 0
59+
code64_desc:
60+
# For 64-bit code descriptors, all bits except the following are ignored:
61+
# - CS.A=1 (bit 40) segment is accessed, prevents a write on first use.
62+
# - CS.R=1 (bit 41) segment is readable. (this might not be necessary)
63+
# - CS.C=1 (bit 42) segment is conforming. (this might not be necessary)
64+
# - CS.E=1 (bit 43) required, we are a executable code segment.
65+
# - CS.S=1 (bit 44) required, we are not a system segment.
66+
# - CS.DPL=0 (bits 45/46) we are using this segment in Ring 0.
67+
# - CS.P=1 (bit 47) required, the segment is present.
68+
# - CS.L=1 (bit 53) required, we are a 64-bit (long mode) segment.
69+
# - CS.D=0 (bit 54) required, CS.L=1 && CS.D=1 is resevered for future use.
70+
.quad (1<<40) | (1<<41) | (1<<42) | (1<<43) | (1<<44) | (1<<47) | (1<<53)
71+
gdt64_end:

0 commit comments

Comments
 (0)