diff --git a/build_sdk.py b/build_sdk.py index d1b768237..6cae4cd5a 100644 --- a/build_sdk.py +++ b/build_sdk.py @@ -334,7 +334,8 @@ class ConfigInfo: kernel_options={ "KernelDebugBuild": True, "KernelPrinting": True, - "KernelVerificationBuild": False + "KernelVerificationBuild": False, + "HardwareDebugAPI": True } ), ConfigInfo( diff --git a/libmicrokit/include/microkit.h b/libmicrokit/include/microkit.h index 75b72196d..63c22b7bf 100644 --- a/libmicrokit/include/microkit.h +++ b/libmicrokit/include/microkit.h @@ -15,6 +15,7 @@ typedef unsigned int microkit_channel; typedef unsigned int microkit_child; typedef seL4_MessageInfo_t microkit_msginfo; +#define VSPACE_CAP 3 #define MONITOR_EP 5 /* Only valid in the 'benchmark' configuration */ #define TCB_CAP 6 @@ -24,8 +25,10 @@ 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_VSPACE_CAP 266 +#define BASE_VM_TCB_CAP 330 +#define BASE_VCPU_CAP 394 +#define BASE_FRAME_CAP 458 #define MICROKIT_MAX_CHANNELS 62 #define MICROKIT_MAX_CHANNEL_ID (MICROKIT_MAX_CHANNELS - 1) diff --git a/tool/microkit/Cargo.lock b/tool/microkit/Cargo.lock index 9073ccf28..3fe3b25ed 100644 --- a/tool/microkit/Cargo.lock +++ b/tool/microkit/Cargo.lock @@ -15,6 +15,7 @@ dependencies = [ "roxmltree", "serde", "serde_json", + "zerocopy", ] [[package]] @@ -94,3 +95,23 @@ name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/tool/microkit/Cargo.toml b/tool/microkit/Cargo.toml index f58496557..db8bb105b 100644 --- a/tool/microkit/Cargo.toml +++ b/tool/microkit/Cargo.toml @@ -18,6 +18,7 @@ path = "src/main.rs" roxmltree = "0.19.0" serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" +zerocopy = {version="0.8.25",features=["derive"]} [profile.release] strip = true diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 1b97195a0..b9f781e28 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -8,6 +8,8 @@ use crate::util::bytes_to_struct; use std::collections::HashMap; use std::fs; use std::path::Path; +use zerocopy::{Immutable, IntoBytes}; +use crate::sel4::PageSize; #[repr(C, packed)] struct ElfHeader32 { @@ -97,6 +99,7 @@ struct ElfHeader64 { const ELF_MAGIC: &[u8; 4] = b"\x7FELF"; pub struct ElfSegment { + pub name: Option, pub data: Vec, pub phys_addr: u64, pub virt_addr: u64, @@ -104,6 +107,13 @@ pub struct ElfSegment { attrs: u32, } +#[derive(IntoBytes, Immutable)] +#[repr(C)] +pub struct TableMetadata { + pub base_addr: u64, + pub pgd: [u64; 64], +} + impl ElfSegment { pub fn mem_size(&self) -> u64 { self.data.len() as u64 @@ -211,6 +221,7 @@ impl ElfFile { .copy_from_slice(&bytes[segment_start..segment_end]); let segment = ElfSegment { + name: None, data: segment_data, phys_addr: phent.paddr, virt_addr: phent.vaddr, @@ -363,4 +374,42 @@ impl ElfFile { pub fn loadable_segments(&self) -> Vec<&ElfSegment> { self.segments.iter().filter(|s| s.loadable).collect() } + + pub fn create_segment(&mut self, segment_name: &str, size: u64) -> ElfSegment { + let mut last_addr = 0; + for segment in self.segments.iter() { + if segment.virt_addr > last_addr { + last_addr = segment.virt_addr + segment.data.len() as u64; + } + } + + // Align the last address we found to a page boundary + last_addr = last_addr + (PageSize::Small as u64 - (last_addr % PageSize::Small as u64)); + + ElfSegment { + name: Some(segment_name.to_string()), + data: vec![0; size as usize], + phys_addr: last_addr, + virt_addr: last_addr, + loadable: true, + attrs: ElfSegmentAttributes::Read as u32, + } + } + + pub fn get_segment(&mut self, segment_name: &str) -> Option<&mut ElfSegment> { + for segment in self.segments.iter_mut() { + if let Some(name) = &segment.name { + if name == segment_name { + return Some(segment); + } + } + } + None + } + + pub fn populate_segment(&mut self, segment_name: &str, data: &[u8]) { + let mut segment = self.get_segment(segment_name).unwrap(); + assert!(data.len() <= segment.data.len()); + segment.data = data.to_vec(); + } } diff --git a/tool/microkit/src/lib.rs b/tool/microkit/src/lib.rs index dfa642566..0deedb42e 100644 --- a/tool/microkit/src/lib.rs +++ b/tool/microkit/src/lib.rs @@ -14,6 +14,8 @@ use sel4::Config; use std::cmp::min; use std::fmt; +use crate::sel4::PageSize; + // Note that these values are used in the monitor so should also be changed there // if any of these were to change. pub const MAX_PDS: usize = 63; @@ -24,6 +26,266 @@ 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(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: PageSize) { + 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: PageSize, + ) { + while data_len > 0 { + self.add_page_at_vaddr(vaddr, frame, size); + data_len -= size as i64; + vaddr += size as u64; + } + } + + 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 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: PageSize) { + 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: PageSize, + ) { + while data_len > 0 { + self.add_page_at_vaddr(vaddr, frame, size); + data_len -= size as i64; + vaddr += size as u64; + } + } + + 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: PageSize) { + let dir_index = ((vaddr & (PAGE_TABLE_MASK << PageTableMaskShift::PD as u64)) >> PageTableMaskShift::PD as u64) as usize; + match size { + PageSize::Small => { + 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!"); + } + } + } + PageSize::Large => { + 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: PageSize) { + let pt_index = ((vaddr & (PAGE_TABLE_MASK << PageTableMaskShift::PT as u64)) >> PageTableMaskShift::PT as u64) as usize; + // Unconditionally overwrite. + assert!(size == PageSize::Small); + 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, Copy, Clone, PartialEq)] pub struct UntypedObject { pub cap: u64, diff --git a/tool/microkit/src/main.rs b/tool/microkit/src/main.rs index 985bba8b3..bd1ffae92 100644 --- a/tool/microkit/src/main.rs +++ b/tool/microkit/src/main.rs @@ -7,12 +7,12 @@ // we want our asserts, even if the compiler figures out they hold true already during compile-time #![allow(clippy::assertions_on_constants)] -use elf::ElfFile; +use elf::{ElfFile, TableMetadata}; use loader::Loader; use microkit_tool::{ elf, loader, sdf, sel4, util, DisjointMemoryRegion, FindFixedError, MemoryRegion, - ObjectAllocator, Region, UntypedObject, MAX_PDS, MAX_VMS, PD_MAX_NAME_LENGTH, - VM_MAX_NAME_LENGTH, + ObjectAllocator, Region, UntypedObject, MAX_PDS, MAX_VMS, PD_MAX_NAME_LENGTH, PGD, + PUD,TopLevelPageTable, VM_MAX_NAME_LENGTH, }; use sdf::{ parse, Channel, ProtectionDomain, SysMap, SysMapPerms, SysMemoryRegion, SysMemoryRegionKind, @@ -20,8 +20,8 @@ use sdf::{ }; use sel4::{ default_vm_attr, Aarch64Regs, Arch, ArmVmAttributes, BootInfo, Config, Invocation, - InvocationArgs, Object, ObjectType, PageSize, PlatformConfig, Rights, Riscv64Regs, - RiscvVirtualMemory, RiscvVmAttributes, + InvocationArgs, MicrokitConfig, Object, ObjectType, PageSize, PlatformConfig, Rights, + Riscv64Regs, RiscvVirtualMemory, RiscvVmAttributes, }; use std::cmp::{max, min}; use std::collections::{HashMap, HashSet}; @@ -34,6 +34,7 @@ use util::{ comma_sep_u64, comma_sep_usize, human_size_strict, json_str, json_str_as_bool, json_str_as_u64, monitor_serialise_names, monitor_serialise_u64_vec, struct_to_bytes, }; +use zerocopy::IntoBytes; // Corresponds to the IPC buffer symbol in libmicrokit and the monitor const SYMBOL_IPC_BUFFER: &str = "__sel4_ipc_buffer_obj"; @@ -54,12 +55,14 @@ const BASE_OUTPUT_NOTIFICATION_CAP: u64 = 10; const BASE_OUTPUT_ENDPOINT_CAP: u64 = BASE_OUTPUT_NOTIFICATION_CAP + 64; const BASE_IRQ_CAP: u64 = BASE_OUTPUT_ENDPOINT_CAP + 64; const BASE_PD_TCB_CAP: u64 = BASE_IRQ_CAP + 64; -const BASE_VM_TCB_CAP: u64 = BASE_PD_TCB_CAP + 64; +const BASE_PD_VSPACE_CAP: u64 = BASE_PD_TCB_CAP + 64; +const BASE_VM_TCB_CAP: u64 = BASE_PD_VSPACE_CAP + 64; const BASE_VCPU_CAP: u64 = BASE_VM_TCB_CAP + 64; +const BASE_FRAME_CAP: u64 = BASE_VCPU_CAP + 64; const MAX_SYSTEM_INVOCATION_SIZE: u64 = util::mb(128); -const PD_CAP_SIZE: u64 = 512; +const PD_CAP_SIZE: u64 = 4096; const PD_CAP_BITS: u64 = PD_CAP_SIZE.ilog2() as u64; const PD_SCHEDCONTEXT_SIZE: u64 = 1 << 8; @@ -732,12 +735,13 @@ fn emulate_kernel_boot( fn build_system( config: &Config, - pd_elf_files: &Vec, + pd_elf_files: &mut Vec, kernel_elf: &ElfFile, monitor_elf: &ElfFile, system: &SystemDescription, invocation_table_size: u64, system_cnode_size: u64, + has_built: bool, ) -> Result { assert!(util::is_power_of_two(system_cnode_size)); assert!(invocation_table_size % config.minimum_page_size == 0); @@ -759,6 +763,123 @@ fn build_system( // Determine physical memory region used by the monitor let initial_task_size = phys_mem_region_from_elf(monitor_elf, config.minimum_page_size).size(); + // Create a vector of optional vectors, which we will populate as needed. This maintains the pd_idx mapping. + let mut all_child_page_tables: Vec>> = vec![None; system.protection_domains.len()]; + + // If we are mapping the child page tables into the parent, we will create the paging structures + // now, so that the pd_elf_size can be properly calculated. + for (parent_idx, pd) in system.protection_domains.iter().enumerate() { + let mut pd_extra_elf_size = 0; + // Flag to enable the patching of child page tables + if pd.child_pts { + // Initialise the page table structure for this parent pd. + all_child_page_tables[parent_idx] = match config.arch { + Arch::Aarch64 => {Some(vec![TopLevelPageTable::Aarch64{top_level: PGD::new()}; 64])}, + Arch::Riscv64 => {Some(vec![TopLevelPageTable::Riscv64{top_level: PUD::new()}; 64])}, + }; + + // Find all child page tables and find size of loadable segments. + for (child_idx, child_pd) in system.protection_domains.iter().enumerate() { + if child_idx <= parent_idx { + continue; + } else if child_idx >= parent_idx && child_pd.parent != Some(parent_idx) { + break; + } + + let child_top_table: &mut TopLevelPageTable = &mut all_child_page_tables[parent_idx].as_mut().unwrap() + [child_pd.id.unwrap() as usize]; + let child_elf = &pd_elf_files[child_idx]; + for loadable_segment in child_elf.loadable_segments() { + // Adding placeholder cap "1", we will populate the actual caps once they have been allocated + match child_top_table { + TopLevelPageTable::Aarch64 { top_level } => { + top_level.add_page_at_vaddr_range( + loadable_segment.virt_addr, + loadable_segment.data.len() as i64, + 1, + PageSize::Small, + ); + }, + TopLevelPageTable::Riscv64 { top_level } => { + top_level.add_page_at_vaddr_range( + loadable_segment.virt_addr, + loadable_segment.data.len() as i64, + 1, + PageSize::Small, + ); + } + }; + + } + // Find all associated memory regions and their size. + for child_map in &child_pd.maps { + // Find the memory region associated with this map + for mr in &system.memory_regions { + if mr.name == child_map.mr { + // Adding placeholder cap "1", we will populate the actual caps once they have been allocated + match child_top_table { + TopLevelPageTable::Aarch64 { top_level } => { + top_level.add_page_at_vaddr_range( + child_map.vaddr, + mr.size as i64, + 1, + mr.page_size, + ); + }, + TopLevelPageTable::Riscv64 { top_level } => { + top_level.add_page_at_vaddr_range( + child_map.vaddr, + mr.size as i64, + 1, + mr.page_size, + ); + } + }; + } + } + } + + // Account for the stack of each child pd + match child_top_table { + TopLevelPageTable::Aarch64 { top_level } => { + top_level.add_page_at_vaddr_range( + config.pd_stack_bottom(child_pd.stack_size), + child_pd.stack_size as i64, + 1, + PageSize::Small, + ); + }, + TopLevelPageTable::Riscv64 { top_level } => { + top_level.add_page_at_vaddr_range( + config.pd_stack_bottom(child_pd.stack_size), + child_pd.stack_size as i64, + 1, + PageSize::Small, + ); + } + }; + // We have now constructed the dummy page tables, calculate how much size we + // need to add to the pd_elf_sizes + pd_extra_elf_size += match child_top_table { + TopLevelPageTable::Aarch64 { top_level } => { + top_level.get_size() + }, + TopLevelPageTable::Riscv64 { top_level } => { + top_level.get_size() + } + }; + } + // We only want to add these new elf regions ONCE. As the microkit tool can run multiple times, + // we keep track of we have already built these. + if !has_built { + // Push this new region to the parent's elf + let parent_elf = &mut pd_elf_files[parent_idx]; + let new_elf_seg = parent_elf.create_segment(".table_data", pd_extra_elf_size); + parent_elf.segments.push(new_elf_seg); + } + } + } + // Determine physical memory region for 'reserved' memory. // // The 'reserved' memory region will not be touched by seL4 during boot @@ -766,7 +887,7 @@ fn build_system( // from this area, which can then be made available to the appropriate // protection domains let mut pd_elf_size = 0; - for pd_elf in pd_elf_files { + for pd_elf in pd_elf_files.iter_mut() { for r in phys_mem_regions_from_elf(pd_elf, config.minimum_page_size) { pd_elf_size += r.size(); } @@ -1971,6 +2092,119 @@ fn build_system( } } + let mut frame_cap = BASE_FRAME_CAP; + + for (pd_idx, pd) in system.protection_domains.iter().enumerate() { + if pd.child_pts { + for maybe_child_pd in system.protection_domains.iter() { + if maybe_child_pd.parent.is_some_and(|x| x == pd_idx) { + for map_set in [&maybe_child_pd.maps, &pd_extra_maps[maybe_child_pd]] { + for mp in map_set { + let mr = all_mr_by_name[mp.mr.as_str()]; + let mut invocation = Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[pd_idx].cap_addr, + dest_index: frame_cap, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: mr_pages[mr][0].cap_addr, + src_depth: config.cap_address_bits, + rights: (Rights::Read as u64 | Rights::Write as u64), + badge: 0, + }, + ); + + invocation.repeat( + mr_pages[mr].len() as u32, + InvocationArgs::CnodeMint { + cnode: 0, + dest_index: 1, + dest_depth: 0, + src_root: 0, + src_obj: 1, + src_depth: 0, + rights: 0, + badge: 0, + }, + ); + + for mr_idx in 0..mr_pages[mr].len() { + let vaddr = mp.vaddr + mr.page_size_bytes() * mr_idx as u64; + let minted_cap = frame_cap + mr_idx as u64; + + let mut child_top = &mut all_child_page_tables[pd_idx].as_mut().unwrap() + [maybe_child_pd.id.unwrap() as usize]; + + match child_top { + TopLevelPageTable::Aarch64 { top_level } => { + top_level.add_page_at_vaddr(vaddr, minted_cap, mr.page_size); + }, + TopLevelPageTable::Riscv64 { top_level } => { + top_level.add_page_at_vaddr(vaddr, minted_cap, mr.page_size); + } + }; + + + } + + frame_cap += mr_pages[mr].len() as u64; + system_invocations.push(invocation); + } + } + } + } + } + } + + for (pd_idx, pd) in system.protection_domains.iter().enumerate() { + if pd.child_pts { + let mut child_pds: Vec = vec![]; + + for maybe_child_pd in system.protection_domains.iter() { + if let Some(parent_idx) = maybe_child_pd.parent { + if parent_idx == pd_idx { + let id = maybe_child_pd.id.unwrap() as usize; + child_pds.push(id); + } + } + } + + if child_pds.is_empty() { + continue; + } + + let mut table_metadata = TableMetadata { + base_addr: 0, + pgd: [0; 64], + }; + let mut table_data = Vec::::new(); + let mut offset = 0; + + for i in child_pds { + let mut child_top = &mut all_child_page_tables[pd_idx].as_mut().unwrap()[i]; + offset = match child_top { + TopLevelPageTable::Aarch64 { top_level } => { + top_level.recurse(offset, &mut table_data) + }, + TopLevelPageTable::Riscv64 { top_level } => { + top_level.recurse(offset, &mut table_data) + } + }; + + table_metadata.pgd[i] = offset - (512 * 8); + } + + // patch the data in + let elf = &mut pd_elf_files[pd_idx]; + elf.populate_segment(".table_data", &table_data); + let new_elf_seg = elf.get_segment(".table_data").unwrap(); + + table_metadata.base_addr = new_elf_seg.virt_addr; + elf.write_symbol("table_metadata", table_metadata.as_bytes())?; + } + } + let mut badged_irq_caps: HashMap<&ProtectionDomain, Vec> = HashMap::new(); for (notification_obj, pd) in zip(¬ification_objs, &system.protection_domains) { badged_irq_caps.insert(pd, vec![]); @@ -2211,6 +2445,33 @@ fn build_system( } } + // Mint access to the child VSpace in the CSpace of root PDs + for (pd_idx, _) in system.protection_domains.iter().enumerate() { + for (maybe_child_idx, maybe_child_pd) in system.protection_domains.iter().enumerate() { + // Check if we are dealing with a child PD + if let Some(parent_idx) = maybe_child_pd.parent { + // Check that the current PD is the parent of the child + if parent_idx == pd_idx { + let cap_idx = BASE_PD_VSPACE_CAP + maybe_child_pd.id.unwrap(); + assert!(cap_idx < PD_CAP_SIZE); + system_invocations.push(Invocation::new( + config, + InvocationArgs::CnodeMint { + cnode: cnode_objs[pd_idx].cap_addr, + dest_index: cap_idx, + dest_depth: PD_CAP_BITS, + src_root: root_cnode_cap, + src_obj: vspace_objs[maybe_child_idx].cap_addr, + src_depth: config.cap_address_bits, + rights: Rights::All as u64, + badge: 0, + }, + )) + } + } + } + } + // Mint access to virtual machine TCBs in the CSpace of parent PDs for (pd_idx, pd) in system.protection_domains.iter().enumerate() { if let Some(vm) = &pd.virtual_machine { @@ -2559,7 +2820,7 @@ fn build_system( // In the benchmark configuration, we allow PDs to access their own TCB. // This is necessary for accessing kernel's benchmark API. - if config.benchmark { + if config.microkit_config == MicrokitConfig::Benchmark { let mut tcb_cap_copy_invocation = Invocation::new( config, InvocationArgs::CnodeCopy { @@ -3274,7 +3535,7 @@ fn main() -> Result<(), String> { cap_address_bits: 64, fan_out_limit: json_str_as_u64(&kernel_config_json, "RETYPE_FAN_OUT_LIMIT")?, hypervisor, - benchmark: args.config == "benchmark", + microkit_config: MicrokitConfig::config_from_str(args.config), fpu: json_str_as_bool(&kernel_config_json, "HAVE_FPU")?, arm_pa_size_bits, arm_smc, @@ -3349,19 +3610,23 @@ fn main() -> Result<(), String> { let mut system_cnode_size = 2; let mut built_system; + let mut has_built = false; loop { built_system = build_system( &kernel_config, - &pd_elf_files, + &mut pd_elf_files, &kernel_elf, &monitor_elf, &system, invocation_table_size, system_cnode_size, + has_built, )?; println!("BUILT: system_cnode_size={} built_system.number_of_system_caps={} invocation_table_size={} built_system.invocation_data_size={}", system_cnode_size, built_system.number_of_system_caps, invocation_table_size, built_system.invocation_data_size); + has_built = true; + if built_system.number_of_system_caps <= system_cnode_size && built_system.invocation_data_size <= invocation_table_size { diff --git a/tool/microkit/src/sdf.rs b/tool/microkit/src/sdf.rs index c41c46e43..eb5015ca5 100644 --- a/tool/microkit/src/sdf.rs +++ b/tool/microkit/src/sdf.rs @@ -19,8 +19,8 @@ use crate::sel4::{Config, IrqTrigger, PageSize}; use crate::util::str_to_bool; use crate::MAX_PDS; +use crate::PGD; use std::path::{Path, PathBuf}; - /// Events that come through entry points (e.g notified or protected) are given an /// identifier that is used as the badge at runtime. /// On 64-bit platforms, this badge has a limit of 64-bits which means that we are @@ -189,8 +189,13 @@ pub struct ProtectionDomain { /// Index into the total list of protection domains if a parent /// protection domain exists pub parent: Option, + pub child_pts: bool, /// Location in the parsed SDF file text_pos: roxmltree::TextPos, + // Create an outline of the page table mappings for each pd. We can + //later populate this outline with the corresponding frame caps should + //any pd have a parent + pub child_page_tables: Option>, } #[derive(Debug, PartialEq, Eq, Hash)] @@ -362,6 +367,7 @@ impl ProtectionDomain { // The SMC field is only available in certain configurations // but we do the error-checking further down. "smc", + "child_pts", ]; if is_child { attrs.push("id"); @@ -656,6 +662,25 @@ impl ProtectionDomain { let has_children = !child_pds.is_empty(); + let child_pts = if has_children { + if let Some(xml_child_pts) = node.attribute("child_pts") { + match str_to_bool(xml_child_pts) { + Some(val) => val, + None => { + return Err(value_error( + xml_sdf, + node, + "child_pts must be 'true' or 'false'".to_string(), + )) + } + } + } else { + false + } + } else { + false + }; + Ok(ProtectionDomain { id, name, @@ -675,7 +700,9 @@ impl ProtectionDomain { virtual_machine, has_children, parent: None, + child_pts, text_pos: xml_sdf.doc.text_pos_at(node.range().start), + child_page_tables: None, }) } } diff --git a/tool/microkit/src/sel4.rs b/tool/microkit/src/sel4.rs index 8184c2c23..8eb4cffe4 100644 --- a/tool/microkit/src/sel4.rs +++ b/tool/microkit/src/sel4.rs @@ -31,6 +31,24 @@ pub struct PlatformConfig { pub memory: Vec, } +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum MicrokitConfig { + Debug, + Benchmark, + Release, +} + +impl MicrokitConfig { + pub fn config_from_str(string: &str) -> Self { + match string { + "debug" => MicrokitConfig::Debug, + "release" => MicrokitConfig::Release, + "benchmark" => MicrokitConfig::Benchmark, + _ => panic!("Invalid microkit configuration provided!"), + } + } +} + /// Represents an allocated kernel object. /// /// Kernel objects can have multiple caps (and caps can have multiple addresses). @@ -59,7 +77,7 @@ pub struct Config { pub cap_address_bits: u64, pub fan_out_limit: u64, pub hypervisor: bool, - pub benchmark: bool, + pub microkit_config: MicrokitConfig, pub fpu: bool, /// ARM-specific, number of physical address bits pub arm_pa_size_bits: Option, @@ -194,7 +212,10 @@ impl ObjectType { pub fn fixed_size_bits(self, config: &Config) -> Option { match self { ObjectType::Tcb => match config.arch { - Arch::Aarch64 => Some(11), + Arch::Aarch64 => match config.microkit_config { + MicrokitConfig::Debug => Some(12), + _ => Some(11), + }, Arch::Riscv64 => match config.fpu { true => Some(11), false => Some(10), diff --git a/tool/microkit/tests/test.rs b/tool/microkit/tests/test.rs index 24be4fb17..be728dd59 100644 --- a/tool/microkit/tests/test.rs +++ b/tool/microkit/tests/test.rs @@ -17,7 +17,7 @@ const DEFAULT_KERNEL_CONFIG: sel4::Config = sel4::Config { cap_address_bits: 64, fan_out_limit: 256, hypervisor: true, - benchmark: false, + microkit_config: sel4::MicrokitConfig::Debug, fpu: true, arm_pa_size_bits: Some(40), arm_smc: None,