Skip to content

Commit 97c5cde

Browse files
josephlrrbradford
authored andcommitted
pvh: Add code to transition from 32-bit to 64-bit
PVH starts in 32-bit mode, so we have to transition to 64-bit mode before we can start running Rust code. As we have not yet initialized the stack, we can only use registers and static memory. This transition does the following: - Sets up page tables to identity map 2 MiB - Loads page tables into CR3 - Sets CR4.PAE, EFER.LME, and CRO.PG - Sets up a 64-bit GDT - Long Jumps to 64-bit code We put the GDT in the .rodata section, and we put the 32-bit code in its own section. This makes it easier to debug and dissassemble the binary. Signed-off-by: Joe Richey <[email protected]>
1 parent c48601d commit 97c5cde

File tree

4 files changed

+81
-0
lines changed

4 files changed

+81
-0
lines changed

layout.ld

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ SECTIONS
2222
data_start = .;
2323
.rodata : { *(.rodata .rodata.*) } :ram
2424
.text : { *(.text .text.*) }
25+
.text32 : { *(.text32) }
2526
.data : { *(.data .data.*) }
2627
data_size = . - data_start;
2728

src/asm/gdt64.s

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.section .rodata, "a"
2+
3+
gdt64_ptr:
4+
.short gdt64_end - gdt64_start - 1 # GDT length is actually (length - 1)
5+
.quad gdt64_start
6+
7+
gdt64_start: # First descriptor is always null
8+
.quad 0
9+
code64_desc: # 64-bit Code-Segments always have: Base = 0, Limit = 4G
10+
# CS.Limit[15:00] = 0 - Ignored
11+
.short 0x0000
12+
# CS.Base[15:00] = 0 - Ignored
13+
.short 0x0000
14+
# CS.Base[23:16] = 0 (bits 0-7) - Ignored
15+
.byte 0x00
16+
# CS.Accessed = 1 (bit 8) - Don't write to segment on first use
17+
# CS.ReadEnable = 1 (bit 9) - Read/Execute Code-Segment
18+
# CS.Conforming = 0 (bit 10) - Nonconforming, no lower-priv access
19+
# CS.Executable = 1 (bit 11) - Code-Segement
20+
# CS.S = 1 (bit 12) - Not a System-Segement
21+
# CS.DPL = 0 (bits 13-14) - We only use this segment in Ring 0
22+
# CS.P = 1 (bit 15) - Segment is present
23+
.byte 0b10011011
24+
# CS.Limit[19:16] = 0 (bits 16-19) - Ignored
25+
# CS.AVL = 0 (bit 20) - Our software doesn't use this bit
26+
# CS.L = 1 (bit 21) - This isn't a 64-bit segment
27+
# CS.D = 0 (bit 22) - This is a 32-bit segment
28+
# CS.G = 0 (bit 23) - Ignored
29+
.byte 0b00100000
30+
# CS.Base[31:24] = 0 (bits 24-31) - Ignored
31+
.byte 0x00
32+
gdt64_end:

src/asm/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
global_asm!(include_str!("ram32.s"));
12
global_asm!(include_str!("ram64.s"));
3+
global_asm!(include_str!("gdt64.s"));

src/asm/ram32.s

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.section .text32, "ax"
2+
.code32
3+
4+
ram32_start:
5+
# Stash the PVH start_info struct in %rdi.
6+
movl %ebx, %edi
7+
# Zero out %rsi, its value is unspecificed in the PVH Boot Protocol.
8+
xorl %esi, %esi
9+
10+
setup_page_tables:
11+
# First L2 entry identity maps [0, 2 MiB)
12+
movl $0b10000011, (L2_TABLES) # huge (bit 7), writable (bit 1), present (bit 0)
13+
# First L3 entry points to L2 table
14+
movl $L2_TABLES, %eax
15+
orb $0b00000011, %al # writable (bit 1), present (bit 0)
16+
movl %eax, (L3_TABLE)
17+
# First L4 entry points to L3 table
18+
movl $L3_TABLE, %eax
19+
orb $0b00000011, %al # writable (bit 1), present (bit 0)
20+
movl %eax, (L4_TABLE)
21+
22+
enable_paging:
23+
# Load page table root into CR3
24+
movl $L4_TABLE, %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+
jump_to_64bit:
42+
# We are now in 32-bit compatibility mode. To enter 64-bit mode, we need to
43+
# load a 64-bit code segment into our GDT.
44+
lgdtl gdt64_ptr
45+
# Set CS to a 64-bit segment and jump to 64-bit code.
46+
ljmpl $(code64_desc - gdt64_start), $ram64_start

0 commit comments

Comments
 (0)