Skip to content

Commit f73899f

Browse files
committed
pvh: WIP
Signed-off-by: Joe Richey <[email protected]>
1 parent b5a032c commit f73899f

15 files changed

+624
-247
lines changed

layout.ld

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ 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. */
@@ -26,6 +27,9 @@ SECTIONS
2627

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

30+
/* These sections are not mapped into RAM */
31+
.note : { *(.note) } :note
32+
2933
/* Match edk2's GccBase.lds DISCARD section */
3034
/DISCARD/ : {
3135
*(.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 pvh_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 pvh_start
19+
desc_end:
20+
.align 4

src/asm/ram32.s

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

src/asm/ram64.s

+2
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ ram64_start:
1111
# Setup the stack (at the end of our RAM region)
1212
movq $ram_max, %rsp
1313

14+
# PVH start_info is in %rdi, the first paramter of the System V ABI.
15+
# BootParams are in %rsi, the second paramter of the System V ABI.
1416
jmp rust64_start

src/boot.rs

+207
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
use core::mem;
2+
3+
use crate::{
4+
common,
5+
fat::{Error, Read},
6+
mem::MemoryRegion,
7+
};
8+
9+
// Common data needed for all boot paths
10+
pub trait Info {
11+
fn rsdp_addr(&self) -> u64;
12+
fn cmdline(&self) -> &str;
13+
fn num_entries(&self) -> u8;
14+
fn entry(&self, idx: u8) -> E820Entry;
15+
}
16+
17+
#[derive(Clone, Copy, Debug)]
18+
#[repr(C, packed(4))]
19+
pub struct E820Entry {
20+
pub addr: u64,
21+
pub size: u64,
22+
pub entry_type: u32,
23+
}
24+
25+
impl E820Entry {
26+
pub const RAM_TYPE: u32 = 1;
27+
}
28+
29+
// The so-called "zeropage"
30+
#[derive(Clone, Copy)]
31+
#[repr(C, align(4096))]
32+
pub struct Params {
33+
screen_info: ScreenInfo, // 0x000
34+
apm_bios_info: ApmBiosInfo, // 0x040
35+
_pad2: [u8; 4], // 0x054
36+
tboot_addr: u64, // 0x058
37+
ist_info: IstInfo, // 0x060
38+
pub acpi_rsdp_addr: u64, // 0x070
39+
_pad3: [u8; 8], // 0x078
40+
hd0_info: HdInfo, // 0x080 - obsolete
41+
hd1_info: HdInfo, // 0x090 - obsolete
42+
sys_desc_table: SysDescTable, // 0x0a0 - obsolete
43+
olpc_ofw_header: OlpcOfwHeader, // 0x0b0
44+
ext_ramdisk_image: u32, // 0x0c0
45+
ext_ramdisk_size: u32, // 0x0c4
46+
ext_cmd_line_ptr: u32, // 0x0c8
47+
_pad4: [u8; 0x74], // 0x0cc
48+
edd_info: EdidInfo, // 0x140
49+
efi_info: EfiInfo, // 0x1c0
50+
alt_mem_k: u32, // 0x1e0
51+
scratch: u32, // 0x1e4
52+
e820_entries: u8, // 0x1e8
53+
eddbuf_entries: u8, // 0x1e9
54+
edd_mbr_sig_buf_entries: u8, // 0x1ea
55+
kbd_status: u8, // 0x1eb
56+
secure_boot: u8, // 0x1ec
57+
_pad5: [u8; 2], // 0x1ed
58+
sentinel: u8, // 0x1ef
59+
pub hdr: Header, // 0x1f0
60+
_pad7: [u8; 0x290 - HEADER_END],
61+
edd_mbr_sig_buffer: [u32; 16], // 0x290
62+
e820_table: [E820Entry; 128], // 0x2d0
63+
_pad8: [u8; 0x30], // 0xcd0
64+
eddbuf: [EddInfo; 6], // 0xd00
65+
_pad9: [u8; 0x114], // 0xeec
66+
}
67+
68+
impl Params {
69+
pub fn new() -> Self {
70+
// SAFETY: Struct consists entirely of primitive integral types.
71+
unsafe { mem::zeroed() }
72+
}
73+
pub fn set_entries(&mut self, info: &dyn Info) {
74+
self.e820_entries = info.num_entries();
75+
for i in 0..self.e820_entries {
76+
self.e820_table[i as usize] = info.entry(i);
77+
}
78+
}
79+
}
80+
81+
impl Info for Params {
82+
fn rsdp_addr(&self) -> u64 {
83+
self.acpi_rsdp_addr
84+
}
85+
fn cmdline(&self) -> &str {
86+
unsafe { common::from_cstring(self.hdr.cmd_line_ptr as u64) }
87+
}
88+
fn num_entries(&self) -> u8 {
89+
self.e820_entries
90+
}
91+
fn entry(&self, idx: u8) -> E820Entry {
92+
assert!(idx < self.num_entries());
93+
self.e820_table[idx as usize]
94+
}
95+
}
96+
97+
// The normal Linux setup_header has an offset of 0x1f1 in BootParams and the
98+
// kernel image. We use an additonal padding field, so that the structure is
99+
// properly aligned, meaning this structure uses an offset of 0x1f0.
100+
const HEADER_START: usize = 0x1f0;
101+
const HEADER_END: usize = HEADER_START + mem::size_of::<Header>();
102+
103+
#[derive(Clone, Copy, Debug)]
104+
#[repr(C)]
105+
pub struct Header {
106+
_pad6: u8,
107+
pub setup_sects: u8,
108+
pub root_flags: u16,
109+
pub syssize: u32,
110+
pub ram_size: u16,
111+
pub vid_mode: u16,
112+
pub root_dev: u16,
113+
pub boot_flag: u16,
114+
pub jump: u16,
115+
pub header: [u8; 4],
116+
pub version: u16,
117+
pub realmode_swtch: u32,
118+
pub start_sys_seg: u16,
119+
pub kernel_version: u16,
120+
pub type_of_loader: u8,
121+
pub loadflags: u8,
122+
pub setup_move_size: u16,
123+
pub code32_start: u32,
124+
pub ramdisk_image: u32,
125+
pub ramdisk_size: u32,
126+
pub bootsect_kludge: u32,
127+
pub heap_end_ptr: u16,
128+
pub ext_loader_ver: u8,
129+
pub ext_loader_type: u8,
130+
pub cmd_line_ptr: u32,
131+
pub initrd_addr_max: u32,
132+
pub kernel_alignment: u32,
133+
pub relocatable_kernel: u8,
134+
pub min_alignment: u8,
135+
pub xloadflags: u16,
136+
pub cmdline_size: u32,
137+
pub hardware_subarch: u32,
138+
pub hardware_subarch_data: u64,
139+
pub payload_offset: u32,
140+
pub payload_length: u32,
141+
pub setup_data: u64,
142+
pub pref_address: u64,
143+
pub init_size: u32,
144+
pub handover_offset: u32,
145+
}
146+
147+
impl Header {
148+
pub fn from_file(f: &mut dyn Read) -> Result<Self, Error> {
149+
let mut data: [u8; 1024] = [0; 1024];
150+
let mut region = MemoryRegion::from_bytes(&mut data);
151+
152+
f.seek(0)?;
153+
f.load_file(&mut region)?;
154+
155+
#[repr(C)]
156+
struct HeaderData {
157+
before: [u8; HEADER_START],
158+
hdr: Header,
159+
after: [u8; 1024 - HEADER_END],
160+
}
161+
// SAFETY: Struct consists entirely of primitive integral types.
162+
Ok(unsafe { mem::transmute::<_, HeaderData>(data) }.hdr)
163+
}
164+
}
165+
166+
// Right now the stucts below are unused, so we only need them to be the correct
167+
// size. Update test_size_and_offset if a struct's real definition is added.
168+
#[derive(Clone, Copy)]
169+
#[repr(C, align(4))]
170+
struct ScreenInfo([u8; 0x40]);
171+
#[derive(Clone, Copy)]
172+
#[repr(C, align(4))]
173+
struct ApmBiosInfo([u8; 0x14]);
174+
#[derive(Clone, Copy)]
175+
#[repr(C, align(4))]
176+
struct IstInfo([u8; 0x10]);
177+
#[derive(Clone, Copy)]
178+
#[repr(C, align(16))]
179+
struct HdInfo([u8; 0x10]);
180+
#[derive(Clone, Copy)]
181+
#[repr(C, align(2))]
182+
struct SysDescTable([u8; 0x10]);
183+
#[derive(Clone, Copy)]
184+
#[repr(C, align(4))]
185+
struct OlpcOfwHeader([u8; 0x10]);
186+
#[derive(Clone, Copy)]
187+
#[repr(C, align(4))]
188+
struct EdidInfo([u8; 0x80]);
189+
#[derive(Clone, Copy)]
190+
#[repr(C, align(4))]
191+
struct EfiInfo([u8; 0x20]);
192+
#[derive(Clone, Copy)]
193+
#[repr(C, align(2))]
194+
struct EddInfo([u8; 0x52]);
195+
196+
#[cfg(test)]
197+
mod tests {
198+
use super::*;
199+
#[test]
200+
fn test_size_and_offset() {
201+
assert_eq!(mem::size_of::<Header>(), 120);
202+
assert_eq!(mem::size_of::<E820Entry>(), 20);
203+
assert_eq!(mem::size_of::<Params>(), 4096);
204+
205+
assert_eq!(offset_of!(Params, hdr), HEADER_START);
206+
}
207+
}

0 commit comments

Comments
 (0)