Skip to content

Commit cd19009

Browse files
committed
feat(x86_64): add PVH support
1 parent 977c7aa commit cd19009

17 files changed

Lines changed: 525 additions & 67 deletions

File tree

.vscode/settings.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
"--message-format=json",
1717
"--all-targets",
1818
"-Zbuild-std=core,alloc",
19-
"--target=aarch64-unknown-none-softfloat",
20-
"--target=riscv64gc-unknown-none-elf",
19+
// "--target=aarch64-unknown-none-softfloat",
20+
// "--target=riscv64gc-unknown-none-elf",
2121
"--target=x86_64-unknown-none",
2222
],
2323
"rust-analyzer.check.targets": [
24-
"aarch64-unknown-none-softfloat",
25-
"riscv64gc-unknown-none-elf",
24+
// "aarch64-unknown-none-softfloat",
25+
// "riscv64gc-unknown-none-elf",
2626
"x86_64-unknown-none",
2727
],
2828
}

Cargo.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ mem-barrier = { version = "0.1.0", optional = true, features = ["nightly"] }
310310
num_enum = { version = "0.7", default-features = false }
311311
pci-ids = { version = "0.2", optional = true }
312312
pci_types = { version = "0.10" }
313+
pvh = { path = "../../pvh" }
313314
rand_chacha = { version = "0.10", default-features = false }
314315
shell-words = { version = "1.1", default-features = false }
315316
simple-shell = { version = "0.0.1", optional = true }

src/arch/x86_64/kernel/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use x86_64::registers::control::{Cr0, Cr4};
1212
use crate::arch::x86_64::kernel::core_local::*;
1313
use crate::env::{self, is_uhyve};
1414

15+
pub mod pvh;
1516
#[cfg(feature = "acpi")]
1617
pub mod acpi;
1718
pub mod apic;
@@ -201,7 +202,8 @@ unsafe extern "C" fn pre_init(boot_info: Option<&'static RawBootInfo>, cpu_id: u
201202
}
202203

203204
if cpu_id == 0 {
204-
env::set_boot_info(*boot_info.unwrap());
205+
// env::set_boot_info(*boot_info.unwrap());
206+
println!("Foo");
205207

206208
crate::boot_processor_main()
207209
} else {

src/arch/x86_64/kernel/pvh/gdt.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
2+
3+
const GDT_LEN: usize = 3;
4+
5+
pub(super) static GDT: GlobalDescriptorTable<GDT_LEN> = Gdt::gdt();
6+
7+
pub(super) static GDT_PTR: DescriptorTablePointer<'static, GDT_LEN> =
8+
DescriptorTablePointer::new(&GDT);
9+
10+
pub(super) struct Gdt;
11+
12+
impl Gdt {
13+
const fn create() -> (
14+
GlobalDescriptorTable<GDT_LEN>,
15+
SegmentSelector,
16+
SegmentSelector,
17+
) {
18+
let mut gdt = GlobalDescriptorTable::empty();
19+
let kernel_code_selector = gdt.append(Descriptor::kernel_code_segment());
20+
let kernel_data_selector = gdt.append(Descriptor::kernel_data_segment());
21+
(gdt, kernel_code_selector, kernel_data_selector)
22+
}
23+
24+
pub(super) const fn gdt() -> GlobalDescriptorTable<GDT_LEN> {
25+
Self::create().0
26+
}
27+
28+
pub(super) const fn kernel_code_selector() -> SegmentSelector {
29+
Self::create().1
30+
}
31+
32+
pub(super) const fn kernel_data_selector() -> SegmentSelector {
33+
Self::create().2
34+
}
35+
}
36+
37+
#[derive(Debug, Clone, Copy)]
38+
#[repr(C, packed(2))]
39+
pub(super) struct DescriptorTablePointer<'a, const MAX: usize> {
40+
limit: u16,
41+
base: &'a GlobalDescriptorTable<MAX>,
42+
}
43+
44+
impl<'a, const MAX: usize> DescriptorTablePointer<'a, MAX> {
45+
const fn new(gdt: &'a GlobalDescriptorTable<MAX>) -> Self {
46+
Self {
47+
limit: gdt.limit(),
48+
base: gdt,
49+
}
50+
}
51+
}

src/arch/x86_64/kernel/pvh/mod.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
mod gdt;
2+
mod page_tables;
3+
mod stack;
4+
5+
use core::ptr::NonNull;
6+
use core::sync::atomic::{AtomicU32, Ordering};
7+
8+
use hermit_entry::boot_info::{BootInfo, HardwareInfo, LoadInfo, PlatformInfo, SerialPortBase, TlsInfo};
9+
use pvh::start_info::reader::{IdentityMap, StartInfoReader};
10+
11+
use self::stack::{STACK, Stack};
12+
use crate::env::setboot_info2;
13+
use crate::kernel::pre_init;
14+
15+
/// The PVH entry point.
16+
#[unsafe(naked)]
17+
pub(crate) unsafe extern "C" fn pvh_start32() -> ! {
18+
core::arch::naked_asm!(
19+
".code32",
20+
include_str!("pvh_start32.s"),
21+
".code64",
22+
23+
level_4_table = sym page_tables::LEVEL_4_TABLE,
24+
gdt_ptr = sym gdt::GDT_PTR,
25+
kernel_data_selector = const gdt::Gdt::kernel_data_selector().0,
26+
27+
stack = sym STACK,
28+
stack_size = const size_of::<Stack>(),
29+
kernel_code_selector = const gdt::Gdt::kernel_code_selector().0,
30+
rust_start = sym rust_start,
31+
);
32+
}
33+
34+
pvh::xen_elfnote_phys32_entry!(pvh_start32);
35+
36+
/// The native ELF entry point.
37+
#[unsafe(no_mangle)]
38+
#[unsafe(naked)]
39+
unsafe extern "C" fn _start() -> ! {
40+
core::arch::naked_asm!("2: jmp 2b");
41+
}
42+
43+
static START_INFO_PADDR: AtomicU32 = AtomicU32::new(0);
44+
45+
pub fn start_info<'a>() -> StartInfoReader<'a, IdentityMap> {
46+
let paddr = START_INFO_PADDR.load(Ordering::Relaxed);
47+
unsafe { StartInfoReader::from_paddr_identity(paddr).unwrap() }
48+
}
49+
50+
/// The Rust entry point.
51+
unsafe extern "C" fn rust_start(start_info_paddr: u32) -> ! {
52+
START_INFO_PADDR.store(start_info_paddr, Ordering::Relaxed);
53+
54+
let start_info = start_info();
55+
56+
println!("Start info:\n{start_info:#?}");
57+
58+
dbg!(tdata());
59+
60+
let boot_info = BootInfo {
61+
hardware_info: HardwareInfo {
62+
phys_addr_range: 0..0,
63+
serial_port_base: SerialPortBase::new(0x3f8),
64+
device_tree: None,
65+
},
66+
load_info: LoadInfo {
67+
kernel_image_addr_range: executable_start() as u64..executable_end() as u64,
68+
tls_info: dbg!(find_tls_ranges()),
69+
},
70+
platform_info: PlatformInfo::Fdt,
71+
};
72+
73+
println!("boot_info = {boot_info:#x?}");
74+
75+
setboot_info2(boot_info);
76+
77+
unsafe { pre_init(None, 0) }
78+
}
79+
80+
pub fn executable_start() -> *mut () {
81+
unsafe extern "C" {
82+
static mut __executable_start: u8;
83+
}
84+
85+
(&raw mut __executable_start).cast::<()>()
86+
}
87+
88+
pub fn executable_end() -> *mut () {
89+
unsafe extern "C" {
90+
static mut _end: u8;
91+
}
92+
93+
(&raw mut _end).cast::<()>()
94+
}
95+
96+
pub fn tdata() -> *mut () {
97+
unsafe extern "C" {
98+
static mut __ehdr_start: u8;
99+
}
100+
101+
(&raw mut __ehdr_start).cast::<()>()
102+
}
103+
104+
105+
use core::slice;
106+
107+
const PT_LOAD: u32 = 1;
108+
const PT_TLS: u32 = 7;
109+
110+
#[repr(C)]
111+
struct Elf64Ehdr {
112+
e_ident: [u8; 16],
113+
e_type: u16,
114+
e_machine: u16,
115+
e_version: u32,
116+
e_entry: u64,
117+
e_phoff: u64,
118+
e_shoff: u64,
119+
e_flags: u32,
120+
e_ehsize: u16,
121+
e_phentsize: u16,
122+
e_phnum: u16,
123+
e_shentsize: u16,
124+
e_shnum: u16,
125+
e_shstrndx: u16,
126+
}
127+
128+
#[repr(C)]
129+
struct Elf64Phdr {
130+
p_type: u32,
131+
p_flags: u32,
132+
p_offset: u64,
133+
p_vaddr: u64,
134+
p_paddr: u64,
135+
p_filesz: u64,
136+
p_memsz: u64,
137+
p_align: u64,
138+
}
139+
140+
// Provided by the default ELF linker scripts for executables
141+
unsafe extern "C" {
142+
static __ehdr_start: Elf64Ehdr;
143+
}
144+
145+
#[derive(Debug, Clone, Copy)]
146+
pub struct TlsRanges {
147+
pub tdata_start: *const u8,
148+
pub tdata_end: *const u8,
149+
pub tbss_start: *const u8,
150+
pub tbss_end: *const u8,
151+
}
152+
153+
unsafe fn find_tls_ranges() -> Option<TlsInfo> {
154+
let ehdr = &__ehdr_start as *const Elf64Ehdr;
155+
let ehdr_ref = &*ehdr;
156+
157+
// Optional: check ELF magic
158+
if ehdr_ref.e_ident[0..4] != [0x7f, b'E', b'L', b'F'] {
159+
return None;
160+
}
161+
162+
// Program headers
163+
let phdr_ptr = (ehdr as *const u8).add(ehdr_ref.e_phoff as usize) as *const Elf64Phdr;
164+
let phdrs = slice::from_raw_parts(phdr_ptr, ehdr_ref.e_phnum as usize);
165+
166+
// Find PT_LOAD segment that contains file offset 0 (usually p_offset == 0)
167+
let first_load = phdrs
168+
.iter()
169+
.find(|ph| ph.p_type == PT_LOAD && ph.p_offset == 0)?
170+
;
171+
172+
// Compute load base of the main executable
173+
let ehdr_addr = &__ehdr_start as *const _ as usize;
174+
let base = ehdr_addr.wrapping_sub(first_load.p_vaddr as usize);
175+
176+
// Find PT_TLS segment
177+
let tls_ph = phdrs.iter().find(|ph| ph.p_type == PT_TLS)?;
178+
179+
let tdata_start = (base + tls_ph.p_vaddr as usize) as *const u8;
180+
let tdata_end = tdata_start.add(tls_ph.p_filesz as usize);
181+
let tbss_start = tdata_end;
182+
let tbss_end = tdata_start.add(tls_ph.p_memsz as usize);
183+
184+
Some(TlsInfo { start: tls_ph.p_vaddr, filesz: tls_ph.p_filesz, memsz: tls_ph.p_memsz, align: tls_ph.p_align })
185+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! Page Tables.
2+
//!
3+
//! This module defines the page tables that we switch to by setting `CR3` to
4+
//! `LEVEL_4_TABLE`. Specifically, we map the first GiB of virtual memory using
5+
//! 512 2-MiB pages. 2-MiB pages are supported on every x86-64 CPU.
6+
//!
7+
//! # Current implementation
8+
//!
9+
//! Some page tables need to point to other page tables, but also contain flags.
10+
//! We do this by adding the flags as bytes to the pointer, which is possible in
11+
//! const-eval. The resulting expression is relocatable.
12+
//!
13+
//! Casting pointers to integers is not possible in const-eval. Asserting that
14+
//! all flag bits in the address are 0 is not possible. Using a bitwise OR (`|`)
15+
//! operation cannot be expressed and would not be relocatable.
16+
//!
17+
//! For details, see this discussion: [rust-lang/rust#51910 (comment)].
18+
//!
19+
//! [rust-lang/rust#51910 (comment)]: https://github.com/rust-lang/rust/issues/51910#issuecomment-1013271838
20+
21+
use core::ptr;
22+
23+
use x86_64::structures::paging::{PageSize, PageTableFlags, Size2MiB};
24+
25+
const TABLE_FLAGS: PageTableFlags = PageTableFlags::PRESENT.union(PageTableFlags::WRITABLE);
26+
const PAGE_FLAGS: PageTableFlags = TABLE_FLAGS.union(PageTableFlags::HUGE_PAGE);
27+
28+
pub(super) static mut LEVEL_4_TABLE: PageTable = {
29+
let flags = TABLE_FLAGS.bits() as usize;
30+
31+
let mut page_table = [ptr::null_mut(); _];
32+
33+
page_table[0] = (&raw mut LEVEL_3_TABLE).wrapping_byte_add(flags).cast();
34+
35+
PageTable(page_table)
36+
};
37+
38+
static mut LEVEL_3_TABLE: PageTable = {
39+
let flags = TABLE_FLAGS.bits() as usize;
40+
41+
let mut page_table = [ptr::null_mut(); _];
42+
43+
page_table[0] = (&raw mut LEVEL_2_TABLE).wrapping_byte_add(flags).cast();
44+
45+
PageTable(page_table)
46+
};
47+
48+
static mut LEVEL_2_TABLE: PageTable = {
49+
let flags: usize = PAGE_FLAGS.bits() as usize;
50+
51+
let mut page_table = [ptr::null_mut(); _];
52+
53+
let mut i = 0;
54+
while i < page_table.len() {
55+
let addr = i * Size2MiB::SIZE as usize;
56+
page_table[i] = ptr::with_exposed_provenance_mut(addr + flags);
57+
i += 1;
58+
}
59+
60+
PageTable(page_table)
61+
};
62+
63+
#[repr(align(0x1000))]
64+
#[repr(C)]
65+
pub(super) struct PageTable([*mut (); 512]);

0 commit comments

Comments
 (0)