Skip to content

Commit

Permalink
pvh: WIP
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr committed Mar 7, 2020
1 parent b5a032c commit f73899f
Show file tree
Hide file tree
Showing 15 changed files with 624 additions and 247 deletions.
4 changes: 4 additions & 0 deletions layout.ld
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ENTRY(ram64_start)
PHDRS
{
program PT_LOAD FILEHDR PHDRS ;
note PT_NOTE ;
}

/* Loaders like to put stuff in low memory (< 1M), so we don't use it. */
Expand All @@ -26,6 +27,9 @@ SECTIONS

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

/* These sections are not mapped into RAM */
.note : { *(.note) } :note

/* Match edk2's GccBase.lds DISCARD section */
/DISCARD/ : {
*(.note.GNU-stack)
Expand Down
2 changes: 2 additions & 0 deletions src/asm/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
global_asm!(include_str!("note.s"));
global_asm!(include_str!("ram32.s"));
global_asm!(include_str!("ram64.s"));
20 changes: 20 additions & 0 deletions src/asm/note.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.section .note, "a"

# From xen/include/public/elfnote.h, "Physical entry point into the kernel."
XEN_ELFNOTE_PHYS32_ENTRY = 18

# We don't bother defining an ELFNOTE macro, as we only have one note.
# This is equialent to the kernel's:
# ELFNOTE(Xen, XEN_ELFNOTE_PHYS32_ENTRY, .long pvh_start)
.align 4
.long name_end - name_start # namesz
.long desc_end - desc_start # descsz
.long XEN_ELFNOTE_PHYS32_ENTRY # type
name_start:
.asciz "Xen"
name_end:
.align 4
desc_start:
.long pvh_start
desc_end:
.align 4
76 changes: 76 additions & 0 deletions src/asm/ram32.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.section .text, "ax"
.code32

pvh_start:
# Stash the PVH start_info struct in %rdi.
movl %ebx, %edi
xor %esi, %esi

ram32_start:
# Indicate (via serial) that we are executing out of RAM
movw $0x3f8, %dx
movb $'R', %al
outb %al, %dx

setup_page_tables:
# First L2 entry identity maps [0, 2 MiB)
movl $0b10000011, (L2_TABLES) # huge (bit 7), writable (bit 1), present (bit 0)
# First L3 entry points to L2 table
movl $L2_TABLES, %eax
orb $0b00000011, %al # writable (bit 1), present (bit 0)
movl %eax, (L3_TABLE)
# First L4 entry points to L3 table
movl $L3_TABLE, %eax
orb $0b00000011, %al # writable (bit 1), present (bit 0)
movl %eax, (L4_TABLE)

enable_paging:
# Load page table root into CR3
movl $L4_TABLE, %eax
movl %eax, %cr3

# Set CR4.PAE (Physical Address Extension)
movl %cr4, %eax
orb $0b00100000, %al # Set bit 5
movl %eax, %cr4
# Set EFER.LME (Long Mode Enable)
movl $0xC0000080, %ecx
rdmsr
orb $0b00000001, %ah # Set bit 8
wrmsr
# Set CRO.PG (Paging)
movl %cr0, %eax
orl $(1 << 31), %eax
movl %eax, %cr0

# Indicate (via serial) that we have enabled paging
movw $0x3f8, %dx
movb $'P', %al
outb %al, %dx

jump_to_64bit:
# We are now in 32-bit compatibility mode. To enter 64-bit mode, we need to
# load a 64-bit code segment into our GDT.
lgdtl gdt64_ptr
# Set CS to a 64-bit segment and jump to 64-bit code.
ljmpl $(code64_desc - gdt64_start), $ram64_start

gdt64_ptr:
.short gdt64_end - gdt64_start - 1 # GDT length is actually (length - 1)
.long gdt64_start
gdt64_start:
# First descriptor is null
.quad 0
code64_desc:
# For 64-bit code descriptors, all bits except the following are ignored:
# - CS.A=1 (bit 40) segment is accessed, prevents a write on first use.
# - CS.R=1 (bit 41) segment is readable. (this might not be necessary)
# - CS.C=1 (bit 42) segment is conforming. (this might not be necessary)
# - CS.E=1 (bit 43) required, we are a executable code segment.
# - CS.S=1 (bit 44) required, we are not a system segment.
# - CS.DPL=0 (bits 45/46) we are using this segment in Ring 0.
# - CS.P=1 (bit 47) required, the segment is present.
# - CS.L=1 (bit 53) required, we are a 64-bit (long mode) segment.
# - CS.D=0 (bit 54) required, CS.L=1 && CS.D=1 is resevered for future use.
.quad (1<<40) | (1<<41) | (1<<42) | (1<<43) | (1<<44) | (1<<47) | (1<<53)
gdt64_end:
2 changes: 2 additions & 0 deletions src/asm/ram64.s
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ ram64_start:
# Setup the stack (at the end of our RAM region)
movq $ram_max, %rsp

# PVH start_info is in %rdi, the first paramter of the System V ABI.
# BootParams are in %rsi, the second paramter of the System V ABI.
jmp rust64_start
207 changes: 207 additions & 0 deletions src/boot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use core::mem;

use crate::{
common,
fat::{Error, Read},
mem::MemoryRegion,
};

// Common data needed for all boot paths
pub trait Info {
fn rsdp_addr(&self) -> u64;
fn cmdline(&self) -> &str;
fn num_entries(&self) -> u8;
fn entry(&self, idx: u8) -> E820Entry;
}

#[derive(Clone, Copy, Debug)]
#[repr(C, packed(4))]
pub struct E820Entry {
pub addr: u64,
pub size: u64,
pub entry_type: u32,
}

impl E820Entry {
pub const RAM_TYPE: u32 = 1;
}

// The so-called "zeropage"
#[derive(Clone, Copy)]
#[repr(C, align(4096))]
pub struct Params {
screen_info: ScreenInfo, // 0x000
apm_bios_info: ApmBiosInfo, // 0x040
_pad2: [u8; 4], // 0x054
tboot_addr: u64, // 0x058
ist_info: IstInfo, // 0x060
pub acpi_rsdp_addr: u64, // 0x070
_pad3: [u8; 8], // 0x078
hd0_info: HdInfo, // 0x080 - obsolete
hd1_info: HdInfo, // 0x090 - obsolete
sys_desc_table: SysDescTable, // 0x0a0 - obsolete
olpc_ofw_header: OlpcOfwHeader, // 0x0b0
ext_ramdisk_image: u32, // 0x0c0
ext_ramdisk_size: u32, // 0x0c4
ext_cmd_line_ptr: u32, // 0x0c8
_pad4: [u8; 0x74], // 0x0cc
edd_info: EdidInfo, // 0x140
efi_info: EfiInfo, // 0x1c0
alt_mem_k: u32, // 0x1e0
scratch: u32, // 0x1e4
e820_entries: u8, // 0x1e8
eddbuf_entries: u8, // 0x1e9
edd_mbr_sig_buf_entries: u8, // 0x1ea
kbd_status: u8, // 0x1eb
secure_boot: u8, // 0x1ec
_pad5: [u8; 2], // 0x1ed
sentinel: u8, // 0x1ef
pub hdr: Header, // 0x1f0
_pad7: [u8; 0x290 - HEADER_END],
edd_mbr_sig_buffer: [u32; 16], // 0x290
e820_table: [E820Entry; 128], // 0x2d0
_pad8: [u8; 0x30], // 0xcd0
eddbuf: [EddInfo; 6], // 0xd00
_pad9: [u8; 0x114], // 0xeec
}

impl Params {
pub fn new() -> Self {
// SAFETY: Struct consists entirely of primitive integral types.
unsafe { mem::zeroed() }
}
pub fn set_entries(&mut self, info: &dyn Info) {
self.e820_entries = info.num_entries();
for i in 0..self.e820_entries {
self.e820_table[i as usize] = info.entry(i);
}
}
}

impl Info for Params {
fn rsdp_addr(&self) -> u64 {
self.acpi_rsdp_addr
}
fn cmdline(&self) -> &str {
unsafe { common::from_cstring(self.hdr.cmd_line_ptr as u64) }
}
fn num_entries(&self) -> u8 {
self.e820_entries
}
fn entry(&self, idx: u8) -> E820Entry {
assert!(idx < self.num_entries());
self.e820_table[idx as usize]
}
}

// The normal Linux setup_header has an offset of 0x1f1 in BootParams and the
// kernel image. We use an additonal padding field, so that the structure is
// properly aligned, meaning this structure uses an offset of 0x1f0.
const HEADER_START: usize = 0x1f0;
const HEADER_END: usize = HEADER_START + mem::size_of::<Header>();

#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Header {
_pad6: u8,
pub setup_sects: u8,
pub root_flags: u16,
pub syssize: u32,
pub ram_size: u16,
pub vid_mode: u16,
pub root_dev: u16,
pub boot_flag: u16,
pub jump: u16,
pub header: [u8; 4],
pub version: u16,
pub realmode_swtch: u32,
pub start_sys_seg: u16,
pub kernel_version: u16,
pub type_of_loader: u8,
pub loadflags: u8,
pub setup_move_size: u16,
pub code32_start: u32,
pub ramdisk_image: u32,
pub ramdisk_size: u32,
pub bootsect_kludge: u32,
pub heap_end_ptr: u16,
pub ext_loader_ver: u8,
pub ext_loader_type: u8,
pub cmd_line_ptr: u32,
pub initrd_addr_max: u32,
pub kernel_alignment: u32,
pub relocatable_kernel: u8,
pub min_alignment: u8,
pub xloadflags: u16,
pub cmdline_size: u32,
pub hardware_subarch: u32,
pub hardware_subarch_data: u64,
pub payload_offset: u32,
pub payload_length: u32,
pub setup_data: u64,
pub pref_address: u64,
pub init_size: u32,
pub handover_offset: u32,
}

impl Header {
pub fn from_file(f: &mut dyn Read) -> Result<Self, Error> {
let mut data: [u8; 1024] = [0; 1024];
let mut region = MemoryRegion::from_bytes(&mut data);

f.seek(0)?;
f.load_file(&mut region)?;

#[repr(C)]
struct HeaderData {
before: [u8; HEADER_START],
hdr: Header,
after: [u8; 1024 - HEADER_END],
}
// SAFETY: Struct consists entirely of primitive integral types.
Ok(unsafe { mem::transmute::<_, HeaderData>(data) }.hdr)
}
}

// Right now the stucts below are unused, so we only need them to be the correct
// size. Update test_size_and_offset if a struct's real definition is added.
#[derive(Clone, Copy)]
#[repr(C, align(4))]
struct ScreenInfo([u8; 0x40]);
#[derive(Clone, Copy)]
#[repr(C, align(4))]
struct ApmBiosInfo([u8; 0x14]);
#[derive(Clone, Copy)]
#[repr(C, align(4))]
struct IstInfo([u8; 0x10]);
#[derive(Clone, Copy)]
#[repr(C, align(16))]
struct HdInfo([u8; 0x10]);
#[derive(Clone, Copy)]
#[repr(C, align(2))]
struct SysDescTable([u8; 0x10]);
#[derive(Clone, Copy)]
#[repr(C, align(4))]
struct OlpcOfwHeader([u8; 0x10]);
#[derive(Clone, Copy)]
#[repr(C, align(4))]
struct EdidInfo([u8; 0x80]);
#[derive(Clone, Copy)]
#[repr(C, align(4))]
struct EfiInfo([u8; 0x20]);
#[derive(Clone, Copy)]
#[repr(C, align(2))]
struct EddInfo([u8; 0x52]);

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_size_and_offset() {
assert_eq!(mem::size_of::<Header>(), 120);
assert_eq!(mem::size_of::<E820Entry>(), 20);
assert_eq!(mem::size_of::<Params>(), 4096);

assert_eq!(offset_of!(Params, hdr), HEADER_START);
}
}
Loading

0 comments on commit f73899f

Please sign in to comment.