diff --git a/Cargo.lock b/Cargo.lock index 3a52fba2d..76ca523af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,6 +249,7 @@ dependencies = [ "sel4-capdl-initializer-types", "serde", "serde_json", + "zerocopy", ] [[package]] @@ -963,3 +964,23 @@ checksum = "cbc04313cab124e498ab1724e739720807b6dc405b9ed0edc5860164d2e4ff70" dependencies = [ "xml", ] + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/build_sdk.py b/build_sdk.py index 5ff324b1f..71a5db12f 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -443,7 +443,8 @@ class KernelPath: kernel_options={ "KernelDebugBuild": True, "KernelPrinting": True, - "KernelVerificationBuild": False + "KernelVerificationBuild": False, + "HardwareDebugAPI": True, }, kernel_options_arch={}, ), diff --git a/libmicrokit/include/microkit.h b/libmicrokit/include/microkit.h index 90155d21e..7e8ae79eb 100644 --- a/libmicrokit/include/microkit.h +++ b/libmicrokit/include/microkit.h @@ -16,6 +16,7 @@ typedef unsigned int microkit_child; typedef unsigned int microkit_ioport; typedef seL4_MessageInfo_t microkit_msginfo; +#define VSPACE_CAP 3 #define MONITOR_EP 5 /* Only valid in the 'benchmark' configuration */ #define TCB_CAP 6 @@ -25,9 +26,11 @@ typedef seL4_MessageInfo_t microkit_msginfo; #define BASE_ENDPOINT_CAP 74 #define BASE_IRQ_CAP 138 #define BASE_TCB_CAP 202 -#define BASE_VM_TCB_CAP 266 -#define BASE_VCPU_CAP 330 -#define BASE_IOPORT_CAP 394 +#define BASE_VSPACE_CAP 266 +#define BASE_VM_TCB_CAP 330 +#define BASE_VCPU_CAP 394 +#define BASE_IOPORT_CAP 458 +#define BASE_FRAME_CAP 522 #define MICROKIT_MAX_CHANNELS 62 #define MICROKIT_MAX_CHANNEL_ID (MICROKIT_MAX_CHANNELS - 1) diff --git a/tool/microkit/Cargo.toml b/tool/microkit/Cargo.toml index 2e89b1eef..fcbb9ad5e 100644 --- a/tool/microkit/Cargo.toml +++ b/tool/microkit/Cargo.toml @@ -20,3 +20,4 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.117" rkyv = { version = "0.8.12", default-features = false, features = ["alloc", "bytecheck", "pointer_width_32"] } sel4-capdl-initializer-types = { workspace = true, features = ["serde", "deflate", "transform"] } +zerocopy = {version="0.8.25",features=["derive"]} diff --git a/tool/microkit/src/capdl/builder.rs b/tool/microkit/src/capdl/builder.rs index 44754e8e6..9019183de 100644 --- a/tool/microkit/src/capdl/builder.rs +++ b/tool/microkit/src/capdl/builder.rs @@ -29,8 +29,11 @@ use crate::{ }, sel4::{Arch, Config, PageSize}, util::{ranges_overlap, round_down, round_up}, + {TableMetadata, TopLevelPageTable, PGD, PUD}, }; +use zerocopy::IntoBytes; + // Corresponds to the IPC buffer symbol in libmicrokit and the monitor const SYMBOL_IPC_BUFFER: &str = "__sel4_ipc_buffer_obj"; @@ -92,11 +95,13 @@ const PD_BASE_OUTPUT_NOTIFICATION_CAP: u64 = 10; const PD_BASE_OUTPUT_ENDPOINT_CAP: u64 = PD_BASE_OUTPUT_NOTIFICATION_CAP + 64; const PD_BASE_IRQ_CAP: u64 = PD_BASE_OUTPUT_ENDPOINT_CAP + 64; const PD_BASE_PD_TCB_CAP: u64 = PD_BASE_IRQ_CAP + 64; -const PD_BASE_VM_TCB_CAP: u64 = PD_BASE_PD_TCB_CAP + 64; +const PD_BASE_PD_VSPACE_CAP: u64 = PD_BASE_PD_TCB_CAP + 64; +const PD_BASE_VM_TCB_CAP: u64 = PD_BASE_PD_VSPACE_CAP + 64; const PD_BASE_VCPU_CAP: u64 = PD_BASE_VM_TCB_CAP + 64; const PD_BASE_IOPORT_CAP: u64 = PD_BASE_VCPU_CAP + 64; +const BASE_FRAME_CAP: u64 = PD_BASE_IOPORT_CAP + 64; -pub const PD_CAP_SIZE: u32 = 512; +pub const PD_CAP_SIZE: u32 = 4096; const PD_CAP_BITS: u8 = PD_CAP_SIZE.ilog2() as u8; const PD_SCHEDCONTEXT_EXTRA_SIZE: u64 = 256; const PD_SCHEDCONTEXT_EXTRA_SIZE_BITS: u64 = PD_SCHEDCONTEXT_EXTRA_SIZE.ilog2() as u64; @@ -118,6 +123,12 @@ pub struct CapDLSpecContainer { pub expected_allocations: HashMap, } +pub struct FrameMetadata { + pub frame_id: ObjectId, + pub vaddr: u64, + pub frame_sz: u64, +} + impl Default for CapDLSpecContainer { fn default() -> Self { Self::new() @@ -183,10 +194,11 @@ impl CapDLSpecContainer { pd_cpu: CpuCore, elf_id: usize, elf: &ElfFile, - ) -> Result { + ) -> Result<(ObjectId, Vec), String> { // We assumes that ELFs and PDs have a one-to-one relationship. So for each ELF we create a VSpace. let vspace_obj_id = create_vspace(self, sel4_config, pd_name); let vspace_cap = capdl_util_make_page_table_cap(vspace_obj_id); + let mut elf_frame_metadata: Vec = Vec::new(); // For each loadable segment in the ELF, map it into the address space of this PD. let mut frame_sequence = 0; // For object naming purpose only. @@ -247,6 +259,14 @@ impl CapDLSpecContainer { None, PageSize::Small.fixed_size_bits(sel4_config) as u8, ); + + // Record metadata of this frame + elf_frame_metadata.push(FrameMetadata { + frame_id: frame_obj_id, + vaddr: cur_vaddr, + frame_sz: page_size_bytes, + }); + let frame_cap = capdl_util_make_frame_cap( frame_obj_id, segment.is_readable(), @@ -343,7 +363,7 @@ impl CapDLSpecContainer { object: Object::Tcb(tcb_inner_obj), }; - Ok(self.add_root_object(tcb_obj)) + Ok((self.add_root_object(tcb_obj), elf_frame_metadata)) } } @@ -357,12 +377,13 @@ fn map_memory_region( page_sz: u64, target_vspace: ObjectId, frames: &[ObjectId], -) { +) -> Vec { let mut cur_vaddr = map.vaddr; let read = map.perms & SysMapPerms::Read as u8 != 0; let write = map.perms & SysMapPerms::Write as u8 != 0; let execute = map.perms & SysMapPerms::Execute as u8 != 0; let cached = map.cached; + let mut frame_metadata: Vec = Vec::new(); for frame_obj_id in frames.iter() { // Make a cap for this frame. let frame_cap = capdl_util_make_frame_cap(*frame_obj_id, read, write, execute, cached); @@ -377,8 +398,14 @@ fn map_memory_region( cur_vaddr, ) .unwrap(); + frame_metadata.push(FrameMetadata { + frame_id: *frame_obj_id, + vaddr: cur_vaddr, + frame_sz: page_sz, + }); cur_vaddr += page_sz; } + frame_metadata } /// Build a CapDL Spec according to the System Description File. @@ -396,7 +423,7 @@ pub fn build_capdl_spec( // We expect the PD ELFs to be first and the monitor ELF last in the list of ELFs. let mon_elf_id = elfs.len() - 1; assert!(elfs.len() == system.protection_domains.len() + 1); - let monitor_tcb_obj_id = { + let (monitor_tcb_obj_id, _) = { let monitor_elf = &elfs[mon_elf_id]; spec_container .add_elf_to_spec( @@ -572,6 +599,9 @@ pub fn build_capdl_spec( let mut pd_id_to_cspace_id: HashMap = HashMap::new(); let mut pd_id_to_ntfn_id: HashMap = HashMap::new(); let mut pd_id_to_ep_id: HashMap = HashMap::new(); + let mut pd_id_to_frame_metadata: HashMap> = HashMap::new(); + let mut pd_id_to_vspace_id: HashMap = HashMap::new(); + let mut pd_name_to_id: HashMap = HashMap::new(); // Keep track of the global count of vCPU objects so we can bind them to the monitor for setting TCB name in debug config. // Only used on ARM and RISC-V as on x86-64 VMs share the same TCB as PD's which will have their TCB name set separately. @@ -583,14 +613,20 @@ pub fn build_capdl_spec( for (pd_global_idx, pd) in system.protection_domains.iter().enumerate() { let elf_obj = &elfs[pd_global_idx]; + // Allows for easy lookup later + pd_name_to_id.insert(pd.name.clone(), pd_global_idx); + let mut caps_to_bind_to_tcb: Vec = Vec::new(); let mut caps_to_insert_to_pd_cspace: Vec = Vec::new(); + let mut pd_frame_metadata: Vec = Vec::new(); // Step 3-1: Create TCB and VSpace with all ELF loadable frames mapped in. - let pd_tcb_obj_id = spec_container + let (pd_tcb_obj_id, mut elf_frame_metadata) = spec_container .add_elf_to_spec(kernel_config, &pd.name, pd.cpu, pd_global_idx, elf_obj) .unwrap(); let pd_vspace_obj_id = capdl_util_get_vspace_id_from_tcb_id(&spec_container, pd_tcb_obj_id); + pd_id_to_vspace_id.insert(pd_global_idx, pd_vspace_obj_id); + pd_frame_metadata.append(&mut elf_frame_metadata); // In the benchmark configuration, we allow PDs to access their own TCB. // This is necessary for accessing kernel's benchmark API. @@ -633,7 +669,7 @@ pub fn build_capdl_spec( } } - map_memory_region( + let mut mr_frames_metadata = map_memory_region( &mut spec_container, kernel_config, &pd.name, @@ -642,6 +678,7 @@ pub fn build_capdl_spec( pd_vspace_obj_id, frames, ); + pd_frame_metadata.append(&mut mr_frames_metadata); } // Step 3-3: Create and map in the stack (bottom up) @@ -670,9 +707,19 @@ pub fn build_capdl_spec( cur_stack_vaddr, ) .unwrap(); + + pd_frame_metadata.push(FrameMetadata { + frame_id: stack_frame_obj_id, + vaddr: cur_stack_vaddr, + frame_sz: PageSize::Small as u64, + }); + cur_stack_vaddr += PageSize::Small as u64; } + // We now have all the frame metadata for this PD + pd_id_to_frame_metadata.insert(pd_global_idx, pd_frame_metadata); + // Step 3-4 Create Scheduling Context let pd_sc_obj_id = capdl_util_make_sc_obj( &mut spec_container, @@ -705,6 +752,14 @@ pub fn build_capdl_spec( capdl_util_make_tcb_cap(pd_tcb_obj_id), ); + // Allow the parent PD to access the child's Vspace: + capdl_util_insert_cap_into_cspace( + &mut spec_container, + *parent_cspace_obj_id, + (PD_BASE_PD_VSPACE_CAP + pd.id.unwrap()) as u32, + capdl_util_make_page_table_cap(pd_vspace_obj_id), + ); + fault_ep_cap } else { // badge = pd_global_idx + 1 because seL4 considers badge = 0 as no badge. @@ -1043,6 +1098,219 @@ pub fn build_capdl_spec( } } + for (pd_global_idx, pd) in system.protection_domains.iter().enumerate() { + let frame_cap_idx = BASE_FRAME_CAP; + let mut frame_cap_counter = 0; + + if pd.page_table_copies.is_some() { + let mut table_metadata = TableMetadata { + base_addr: 0, + pgd: [0; 64], + }; + let mut table_data = Vec::::new(); + let mut offset = 0; + let mut page_table_size = 0; + let pd_cspace_id = *pd_id_to_cspace_id.get(&pd_global_idx).unwrap(); + + for pt_copy in pd.page_table_copies.clone().unwrap().entries.iter() { + let source_pd_index = *pd_name_to_id.get(&pt_copy.source_pd).unwrap(); + + // This pd is a child of the parent. We are going to create a copy of its page tables + let mut top_level_page_table = match kernel_config.arch { + Arch::Aarch64 => TopLevelPageTable::Aarch64 { + top_level: PGD::new(), + }, + Arch::Riscv64 => TopLevelPageTable::Riscv64 { + top_level: PUD::new(), + }, + Arch::X86_64 => { + panic!("Child page table mappings are not supported on x86-64") + } + }; + + let pd_frame_metadata = pd_id_to_frame_metadata.get(&source_pd_index).unwrap(); + for frame_metadata in pd_frame_metadata { + capdl_util_insert_cap_into_cspace( + &mut spec_container, + pd_cspace_id, + (frame_cap_idx + frame_cap_counter) as u32, + capdl_util_make_frame_cap(frame_metadata.frame_id, true, true, false, true), + ); + + match top_level_page_table { + TopLevelPageTable::Aarch64 { ref mut top_level } => { + top_level.add_page_at_vaddr( + frame_metadata.vaddr, + frame_cap_idx + frame_cap_counter, + frame_metadata.frame_sz, + ); + } + TopLevelPageTable::Riscv64 { ref mut top_level } => { + top_level.add_page_at_vaddr( + frame_metadata.vaddr, + frame_cap_idx + frame_cap_counter, + frame_metadata.frame_sz, + ); + } + }; + frame_cap_counter += 1; + } + // Now that we have finished constructing the page tables, + // add to the PD's metadata + offset = match top_level_page_table { + TopLevelPageTable::Aarch64 { ref mut top_level } => { + top_level.recurse(offset, &mut table_data) + } + TopLevelPageTable::Riscv64 { ref mut top_level } => { + top_level.recurse(offset, &mut table_data) + } + }; + table_metadata.pgd[pt_copy.table_index] = offset - (512 * 8); + page_table_size = match top_level_page_table { + TopLevelPageTable::Aarch64 { top_level } => top_level.get_size(), + TopLevelPageTable::Riscv64 { top_level } => top_level.get_size(), + }; + } + // Now create a filled frame with this this PD's child's page tables + + let num_frames = page_table_size / PageSize::Small as u64; + let mut dest_offset: usize = 0; + + // @kwinter: Figure out a better start vaddr + align it + // Leave a pages between the stack and the start of our search + let mut cur_vaddr: u64 = round_down( + kernel_config.pd_stack_bottom(pd.stack_size) - PageSize::Small as u64, + PageSize::Small as u64, + ); + + // Work downwards now, and find a contiguous memory range that does not overlap + // with any elf loadable segment or user defined MR + + let mut found_valid_region = false; + let elf_obj = &elfs[pd_global_idx]; + + while (cur_vaddr - (num_frames * PageSize::Small as u64)) > 0 + && !found_valid_region + { + // Pick a range and make sure it doesn't overlap with any MR vaddr's + // and make it page aligned + let table_range = + (cur_vaddr - (num_frames * PageSize::Small as u64))..cur_vaddr; + + // Don't assume that we have any maps + if !pd.maps.is_empty() { + for map in pd.maps.iter() { + let frames = mr_name_to_frames.get(&map.mr).unwrap(); + // MRs have frames of equal size so just use the first frame's page size. + let page_size_bytes = 1 + << capdl_util_get_frame_size_bits( + &spec_container, + *frames.first().unwrap(), + ); + let mr_vaddr_range = + map.vaddr..(map.vaddr + (page_size_bytes * frames.len() as u64)); + if ranges_overlap(&mr_vaddr_range, &table_range) { + found_valid_region = false; + cur_vaddr = round_down(map.vaddr, PageSize::Small as u64); + break; + } else { + found_valid_region = true + } + } + + if !found_valid_region { + continue; + } + } + + // We can assume that our PD will have at least one loadable segment + assert!(!elf_obj.loadable_segments().is_empty()); + for elf_seg in elf_obj.loadable_segments().iter() { + let elf_seg_vaddr_range = elf_seg.virt_addr + ..elf_seg.virt_addr + round_up(elf_seg.mem_size(), PageSize::Small as u64); + + if ranges_overlap(&table_range, &elf_seg_vaddr_range) { + found_valid_region = false; + cur_vaddr = round_down(elf_seg.virt_addr, PageSize::Small as u64); + } else { + found_valid_region = true; + } + } + } + + if !found_valid_region { + panic!("Could not find valid memory region for page table data!\n"); + } + + let mut table_base_addr = cur_vaddr - (num_frames * PageSize::Small as u64); + + table_metadata.base_addr = table_base_addr; + + let pd_vspace_obj_id = *pd_id_to_vspace_id.get(&pd_global_idx).unwrap(); + + for i in 0..num_frames { + let mut frame_fill = Fill { + entries: [].to_vec(), + }; + + #[allow(unused)] + let mut len_to_cpy = 0; + + if (table_data.len() - dest_offset) < (PageSize::Small as usize).try_into().unwrap() + { + len_to_cpy = table_data.len() - dest_offset; + } else { + len_to_cpy = PageSize::Small as usize; + } + + frame_fill.entries.push(FillEntry { + range: Range { + start: 0, + end: len_to_cpy as u64, + }, + content: FillEntryContent::Data(FillContent::BytesContent(BytesContent { + bytes: table_data[dest_offset..(dest_offset + len_to_cpy)].to_vec(), + })), + }); + let frame_obj_id = capdl_util_make_frame_obj( + &mut spec_container, + frame_fill, + &format!("elf_{}_child_pts_{}", pd.name, i), + None, + PageSize::Small.fixed_size_bits(kernel_config) as u8, + ); + let frame_cap = capdl_util_make_frame_cap(frame_obj_id, true, true, true, true); + match map_page( + &mut spec_container, + kernel_config, + &pd.name, + pd_vspace_obj_id, + frame_cap, + PageSize::Small as u64, + table_base_addr, + ) { + Ok(_) => { + table_base_addr += len_to_cpy as u64; + dest_offset += len_to_cpy; + } + Err(map_err_reason) => { + return Err(format!( + "Failed to map frame for page table data range at vaddr: {:x} because: {:?}", + cur_vaddr, map_err_reason + )) + } + }; + } + // Finally, patch the table_metadata into the elf + #[allow(unused_mut)] + let mut elf_obj = &mut elfs[pd_global_idx]; + elf_obj.write_symbol( + pd.page_table_copies.as_ref().unwrap().setvar.as_ref(), + table_metadata.as_bytes(), + )?; + } + } + // ********************************* // Step 4. Create channels // ********************************* diff --git a/tool/microkit/src/lib.rs b/tool/microkit/src/lib.rs index ceb1d22fa..da7e4864e 100644 --- a/tool/microkit/src/lib.rs +++ b/tool/microkit/src/lib.rs @@ -11,6 +11,8 @@ use crate::{ util::struct_to_bytes, }; +use zerocopy::{Immutable, IntoBytes}; + pub mod capdl; pub mod crc32; pub mod elf; @@ -32,6 +34,282 @@ pub const MAX_VMS: usize = 63; pub const PD_MAX_NAME_LENGTH: usize = 64; pub const VM_MAX_NAME_LENGTH: usize = 64; +// Note that these constants align with the only architectures that we are +// supporting at the moment +pub const PAGE_TABLE_ENTRIES: u64 = 512; +pub const PAGE_TABLE_MASK: u64 = 0x1ff; +pub enum PageTableMaskShift { + PGD = 39, + PUD = 30, + PD = 21, + PT = 12, +} + +#[derive(IntoBytes, Immutable)] +#[repr(C)] +pub struct TableMetadata { + pub base_addr: u64, + pub pgd: [u64; 64], +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PGD { + puds: Vec>, +} + +impl Default for PGD { + fn default() -> Self { + Self::new() + } +} + +impl PGD { + pub fn new() -> Self { + PGD { + puds: vec![None; PAGE_TABLE_ENTRIES as usize], + } + } + + pub fn recurse(&mut self, mut curr_offset: u64, buffer: &mut Vec) -> u64 { + let mut offset_table: [u64; PAGE_TABLE_ENTRIES as usize] = + [u64::MAX; PAGE_TABLE_ENTRIES as usize]; + for (i, entry) in offset_table.iter_mut().enumerate() { + if let Some(pud) = &mut self.puds[i] { + curr_offset = pud.recurse(curr_offset, buffer); + *entry = curr_offset - (PAGE_TABLE_ENTRIES * 8); + } + } + + for value in &mut offset_table { + buffer.append(&mut value.to_le_bytes().to_vec()); + } + curr_offset + (PAGE_TABLE_ENTRIES * 8) + } + + pub fn add_page_at_vaddr(&mut self, vaddr: u64, frame: u64, size: u64) { + let pgd_index = ((vaddr & (PAGE_TABLE_MASK << PageTableMaskShift::PGD as u64)) + >> PageTableMaskShift::PGD as u64) as usize; + if self.puds[pgd_index].is_none() { + self.puds[pgd_index] = Some(PUD::new()); + } + self.puds[pgd_index] + .as_mut() + .unwrap() + .add_page_at_vaddr(vaddr, frame, size); + } + + pub fn add_page_at_vaddr_range( + &mut self, + mut vaddr: u64, + mut data_len: i64, + frame: u64, + size: u64, + ) { + while data_len > 0 { + self.add_page_at_vaddr(vaddr, frame, size); + data_len -= size as i64; + vaddr += size; + } + } + + pub fn get_size(&self) -> u64 { + let mut child_size = 0; + for pud in &self.puds { + if pud.is_some() { + child_size += pud.as_ref().unwrap().get_size(); + } + } + (PAGE_TABLE_ENTRIES * 8) + child_size + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PUD { + dirs: Vec>, +} + +impl Default for PUD { + fn default() -> Self { + Self::new() + } +} + +impl PUD { + pub fn new() -> Self { + PUD { + dirs: vec![None; PAGE_TABLE_ENTRIES as usize], + } + } + + pub fn recurse(&mut self, mut curr_offset: u64, buffer: &mut Vec) -> u64 { + let mut offset_table: [u64; PAGE_TABLE_ENTRIES as usize] = + [u64::MAX; PAGE_TABLE_ENTRIES as usize]; + for (i, entry) in offset_table.iter_mut().enumerate() { + if let Some(dir) = &mut self.dirs[i] { + curr_offset = dir.recurse(curr_offset, buffer); + *entry = curr_offset - (PAGE_TABLE_ENTRIES * 8); + } + } + + for value in &mut offset_table { + buffer.append(&mut value.to_le_bytes().to_vec()); + } + curr_offset + (PAGE_TABLE_ENTRIES * 8) + } + + pub fn add_page_at_vaddr(&mut self, vaddr: u64, frame: u64, size: u64) { + let pud_index = ((vaddr & (PAGE_TABLE_MASK << PageTableMaskShift::PUD as u64)) + >> PageTableMaskShift::PUD as u64) as usize; + if self.dirs[pud_index].is_none() { + self.dirs[pud_index] = Some(DIR::new()); + } + self.dirs[pud_index] + .as_mut() + .unwrap() + .add_page_at_vaddr(vaddr, frame, size); + } + + pub fn add_page_at_vaddr_range( + &mut self, + mut vaddr: u64, + mut data_len: i64, + frame: u64, + size: u64, + ) { + while data_len > 0 { + self.add_page_at_vaddr(vaddr, frame, size); + data_len -= size as i64; + vaddr += size; + } + } + + pub fn get_size(&self) -> u64 { + let mut child_size = 0; + for dir in &self.dirs { + if dir.is_some() { + child_size += dir.as_ref().unwrap().get_size(); + } + } + (PAGE_TABLE_ENTRIES * 8) + child_size + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum DirEntry { + PageTable(PT), + LargePage(u64), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct DIR { + entries: Vec>, +} + +impl DIR { + fn new() -> Self { + DIR { + entries: vec![None; PAGE_TABLE_ENTRIES as usize], + } + } + + fn recurse(&mut self, mut curr_offset: u64, buffer: &mut Vec) -> u64 { + let mut offset_table: [u64; PAGE_TABLE_ENTRIES as usize] = + [u64::MAX; PAGE_TABLE_ENTRIES as usize]; + for (i, dir_entry) in offset_table.iter_mut().enumerate() { + if let Some(entry) = &mut self.entries[i] { + match entry { + DirEntry::PageTable(x) => { + curr_offset = x.recurse(curr_offset, buffer); + *dir_entry = curr_offset - (PAGE_TABLE_ENTRIES * 8); + } + DirEntry::LargePage(x) => { + // we mark the top bit to signal to the pd that this is a large page + *dir_entry = *x | (1 << 63); + } + } + } + } + + for value in &mut offset_table { + buffer.append(&mut value.to_le_bytes().to_vec()); + } + curr_offset + (PAGE_TABLE_ENTRIES * 8) + } + + fn add_page_at_vaddr(&mut self, vaddr: u64, frame: u64, size: u64) { + let dir_index = ((vaddr & (PAGE_TABLE_MASK << PageTableMaskShift::PD as u64)) + >> PageTableMaskShift::PD as u64) as usize; + if size == PageSize::Small as u64 { + if self.entries[dir_index].is_none() { + self.entries[dir_index] = Some(DirEntry::PageTable(PT::new())); + } + match &mut self.entries[dir_index] { + Some(DirEntry::PageTable(x)) => { + x.add_page_at_vaddr(vaddr, frame, size); + } + _ => { + panic!("Trying to add small page where a large page already exists!"); + } + } + } else if size == PageSize::Large as u64 { + if let Some(DirEntry::PageTable(_)) = self.entries[dir_index] { + panic!("Attempting to insert a large page where a page table already exists!"); + } + self.entries[dir_index] = Some(DirEntry::LargePage(frame)); + } + } + + fn get_size(&self) -> u64 { + let mut child_size = 0; + for pt in &self.entries { + if let Some(DirEntry::PageTable(x)) = pt { + child_size += x.get_size(); + } + } + (PAGE_TABLE_ENTRIES * 8) + child_size + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PT { + large_page: u64, + pages: Vec, +} + +impl PT { + fn new() -> Self { + PT { + pages: vec![u64::MAX; PAGE_TABLE_ENTRIES as usize], + large_page: u64::MAX, + } + } + + fn recurse(&mut self, curr_offset: u64, buffer: &mut Vec) -> u64 { + for value in &mut self.pages { + buffer.append(&mut value.to_le_bytes().to_vec()); + } + curr_offset + (PAGE_TABLE_ENTRIES * 8) + } + + fn add_page_at_vaddr(&mut self, vaddr: u64, frame: u64, size: u64) { + let pt_index = ((vaddr & (PAGE_TABLE_MASK << PageTableMaskShift::PT as u64)) + >> PageTableMaskShift::PT as u64) as usize; + // Unconditionally overwrite. + assert!(size == PageSize::Small as u64); + self.pages[pt_index] = frame; + } + + fn get_size(&self) -> u64 { + PAGE_TABLE_ENTRIES * 8 + } +} + +#[derive(Debug, Clone)] +pub enum TopLevelPageTable { + Riscv64 { top_level: PUD }, + Aarch64 { top_level: PGD }, +} + #[derive(Debug, Clone, PartialEq)] pub struct UntypedObject { pub cap: u64, diff --git a/tool/microkit/src/sdf.rs b/tool/microkit/src/sdf.rs index ceb582886..3f1bc3a37 100644 --- a/tool/microkit/src/sdf.rs +++ b/tool/microkit/src/sdf.rs @@ -238,6 +238,18 @@ pub struct Channel { pub end_b: ChannelEnd, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PageTablesEntries { + pub source_pd: String, + pub table_index: usize, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PageTableCopies { + pub setvar: String, + pub entries: Vec, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct CpuCore(pub u8); @@ -273,6 +285,7 @@ pub struct ProtectionDomain { /// Index into the total list of protection domains if a parent /// protection domain exists pub parent: Option, + pub page_table_copies: Option, /// Value of the setvar_id attribute, if a parent protection domain exists pub setvar_id: Option, /// Location in the parsed SDF file @@ -416,6 +429,46 @@ impl SysMap { } } +impl PageTableCopies { + fn from_xml( + xml_sdf: &XmlSystemDescription, + node: &roxmltree::Node, + ) -> Result { + check_attributes(xml_sdf, node, &["setvar"])?; + let setvar = checked_lookup(xml_sdf, node, "setvar")?.to_string(); + let mut pds = Vec::new(); + for child in node.children() { + if !child.is_element() { + continue; + } + match child.tag_name().name() { + "pd" => { + check_attributes(xml_sdf, &child, &["name", "index"])?; + let source_pd = checked_lookup(xml_sdf, &child, "name")?.to_string(); + let index = + sdf_parse_number(checked_lookup(xml_sdf, &child, "index")?, &child)?; + let page_table_copy = PageTablesEntries { + source_pd, + table_index: index as usize, + }; + pds.push(page_table_copy); + } + _ => { + return Err(format!( + "Invalid XML element '{}' in page_table", + child.tag_name().name(), + )); + } + } + } + + Ok(PageTableCopies { + setvar, + entries: pds, + }) + } +} + impl ProtectionDomain { pub fn needs_ep(&self, self_id: usize, channels: &[Channel]) -> bool { self.has_children @@ -461,6 +514,7 @@ impl ProtectionDomain { // but we do the error-checking further down. "smc", "cpu", + "child_pts", ]; if is_child { attrs.push("id"); @@ -594,6 +648,7 @@ impl ProtectionDomain { let mut setvars: Vec = Vec::new(); let mut child_pds = Vec::new(); + let mut page_tables = None; let mut program_image = None; let mut program_image_for_symbols = None; let mut virtual_machine = None; @@ -1048,6 +1103,9 @@ impl ProtectionDomain { virtual_machine = Some(vm); } + "page_tables" => { + page_tables = Some(PageTableCopies::from_xml(xml_sdf, &child)?); + } _ => { let pos = xml_sdf.doc.text_pos_at(child.range().start); return Err(format!( @@ -1089,6 +1147,7 @@ impl ProtectionDomain { virtual_machine, has_children, parent: None, + page_table_copies: page_tables, setvar_id, text_pos: Some(xml_sdf.doc.text_pos_at(node.range().start)), })