diff --git a/dev_build.py b/dev_build.py index d1ce44941..c58c76ebb 100644 --- a/dev_build.py +++ b/dev_build.py @@ -81,7 +81,7 @@ def main(): if not BUILD_DIR.exists(): BUILD_DIR.mkdir() - tool_rebuild = f"cd {CWD / 'tool/microkit'} && cargo build --release" + tool_rebuild = f"cd {CWD / 'tool/microkit'} && cargo build" r = system(tool_rebuild) assert r == 0 @@ -90,7 +90,7 @@ def main(): make_env["MICROKIT_BOARD"] = args.board make_env["MICROKIT_CONFIG"] = args.config make_env["MICROKIT_SDK"] = str(release) - make_env["MICROKIT_TOOL"] = (CWD / "tool/microkit/target/release/microkit").absolute() + make_env["MICROKIT_TOOL"] = (CWD / "tool/microkit/target/debug/microkit").absolute() make_env["LLVM"] = str(args.llvm) # Choose the makefile based on the `--example-from-sdk` command line flag diff --git a/libmicrokit/src/main.c b/libmicrokit/src/main.c index 9ebad4712..8e942d48b 100644 --- a/libmicrokit/src/main.c +++ b/libmicrokit/src/main.c @@ -18,20 +18,22 @@ #define PD_MASK 0xff #define CHANNEL_MASK 0x3f +#define SECTION(sec) __attribute__((__section__(sec))) + /* All globals are prefixed with microkit_* to avoid clashes with user defined globals. */ -bool microkit_passive; -char microkit_name[MICROKIT_PD_NAME_LENGTH]; +bool microkit_passive SECTION(".data.patched"); +char microkit_name[MICROKIT_PD_NAME_LENGTH] SECTION(".data.patched"); /* We use seL4 typedefs as this variable is exposed to the libmicrokit header * and we do not want to rely on compiler built-in defines. */ seL4_Bool microkit_have_signal = seL4_False; seL4_CPtr microkit_signal_cap; seL4_MessageInfo_t microkit_signal_msg; -seL4_Word microkit_irqs; -seL4_Word microkit_notifications; -seL4_Word microkit_pps; -seL4_Word microkit_ioports; +seL4_Word microkit_irqs SECTION(".data.patched"); +seL4_Word microkit_notifications SECTION(".data.patched"); +seL4_Word microkit_pps SECTION(".data.patched"); +seL4_Word microkit_ioports SECTION(".data.patched"); extern seL4_IPCBuffer __sel4_ipc_buffer_obj; diff --git a/loader/src/cutil.c b/loader/src/cutil.c index e04d49eaa..49c4558ed 100644 --- a/loader/src/cutil.c +++ b/loader/src/cutil.c @@ -18,6 +18,14 @@ void *memcpy(void *dst, const void *src, size_t sz) return dst; } +void memzero(void *dst, size_t sz) +{ + char *dst_ = dst; + for (size_t i = 0; i < sz; i++) { + dst_[i] = 0x0; + } +} + void *memmove(void *restrict dest, const void *restrict src, size_t n) { unsigned char *d = (unsigned char *)dest; @@ -44,3 +52,4 @@ void *memmove(void *restrict dest, const void *restrict src, size_t n) return dest; } + diff --git a/loader/src/cutil.h b/loader/src/cutil.h index 2dc5d49b5..42025e5f2 100644 --- a/loader/src/cutil.h +++ b/loader/src/cutil.h @@ -19,4 +19,6 @@ void *memcpy(void *dst, const void *src, size_t sz); +void memzero(void *dst, size_t sz); + void *memmove(void *restrict dest, const void *restrict src, size_t n); diff --git a/loader/src/loader.c b/loader/src/loader.c index fafdc20db..ee44f79e0 100644 --- a/loader/src/loader.c +++ b/loader/src/loader.c @@ -84,8 +84,10 @@ static void print_loader_data(void) puthex32(i); puts(" addr: "); puthex64(r->load_addr); - puts(" size: "); - puthex64(r->size); + puts(" file size: "); + puthex64(r->file_size); + puts(" memory size: "); + puthex64(r->memory_size); puts(" offset: "); puthex64(r->offset); puts(" type: "); @@ -102,7 +104,10 @@ static void copy_data(void) puts("LDR|INFO: copying region "); puthex32(i); puts("\n"); - memcpy((void *)(uintptr_t)r->load_addr, base + r->offset, r->size); + memcpy((void *)(uintptr_t)r->load_addr, base + r->offset, r->file_size); + + // we are guaranteed by the microkit tool that memory_size >= file_size + memzero((void *)(r->load_addr + r->file_size), r->memory_size - r->file_size); } } diff --git a/loader/src/loader.h b/loader/src/loader.h index cea7c144a..72a3ce1e2 100644 --- a/loader/src/loader.h +++ b/loader/src/loader.h @@ -13,7 +13,10 @@ struct region { uintptr_t load_addr; - uintptr_t size; + // The size of this region in the loader data 'file' + uintptr_t file_size; + // The size of this region in memory, zero-padded + uintptr_t memory_size; uintptr_t offset; uintptr_t type; }; diff --git a/monitor/src/main.c b/monitor/src/main.c index 82609e786..41b622130 100644 --- a/monitor/src/main.c +++ b/monitor/src/main.c @@ -50,16 +50,19 @@ #define BASE_SCHED_CONTEXT_CAP 138 #define BASE_NOTIFICATION_CAP 202 +#define SECTION(sec) __attribute__((__section__(sec))) +#define UNUSED __attribute__((unused)) + extern seL4_IPCBuffer __sel4_ipc_buffer_obj; seL4_IPCBuffer *__sel4_ipc_buffer = &__sel4_ipc_buffer_obj; -char pd_names[MAX_PDS][MAX_NAME_LEN]; -seL4_Word pd_names_len; -char vm_names[MAX_VMS][MAX_NAME_LEN] __attribute__((unused)); -seL4_Word vm_names_len; +char pd_names[MAX_PDS][MAX_NAME_LEN] SECTION(".data.patched"); +seL4_Word pd_names_len SECTION(".data.patched"); +char vm_names[MAX_VMS][MAX_NAME_LEN] SECTION(".data.patched") UNUSED; +seL4_Word vm_names_len SECTION(".data.patched"); /* For reporting potential stack overflows, keep track of the stack regions for each PD. */ -seL4_Word pd_stack_bottom_addrs[MAX_PDS]; +seL4_Word pd_stack_bottom_addrs[MAX_PDS] SECTION(".data.patched"); /* Sanity check that the architecture specific macro have been set. */ #if defined(ARCH_aarch64) diff --git a/tool/microkit/src/capdl/builder.rs b/tool/microkit/src/capdl/builder.rs index be19db732..4742fa449 100644 --- a/tool/microkit/src/capdl/builder.rs +++ b/tool/microkit/src/capdl/builder.rs @@ -176,19 +176,14 @@ impl CapDLSpec { // 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. for (seg_idx, segment) in elf.loadable_segments().iter().enumerate() { - if segment.data().is_empty() { - continue; - } - let seg_base_vaddr = segment.virt_addr; - let seg_mem_size: u64 = segment.mem_size(); let page_size = PageSize::Small; let page_size_bytes = page_size as u64; // Create and map all frames for this segment. let mut cur_vaddr = round_down(seg_base_vaddr, page_size_bytes); - while cur_vaddr < seg_base_vaddr + seg_mem_size { + while cur_vaddr < seg_base_vaddr + segment.memory_size { let mut frame_init_maybe: Option = None; // Now compute the ELF file offset to fill in this page. @@ -203,10 +198,10 @@ impl CapDLSpec { let target_vaddr_start = cur_vaddr + dest_offset; let section_offset = target_vaddr_start - seg_base_vaddr; - if section_offset < seg_mem_size { + if section_offset < segment.memory_size { // We have data to load let len_to_cpy = - min(page_size_bytes - dest_offset, seg_mem_size - section_offset); + min(page_size_bytes - dest_offset, segment.memory_size - section_offset); frame_init_maybe = Some(FrameInit::Fill(Fill { entries: [FillEntry { @@ -581,7 +576,7 @@ pub fn build_capdl_spec( 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); + ..elf_seg.virt_addr + round_up(elf_seg.memory_size, PageSize::Small as u64); if ranges_overlap(&mr_vaddr_range, &elf_seg_vaddr_range) { return Err(format!("ERROR: mapping MR '{}' to PD '{}' with vaddr [0x{:x}..0x{:x}) will overlap with an ELF segment at [0x{:x}..0x{:x})", map.mr, pd.name, mr_vaddr_range.start, mr_vaddr_range.end, elf_seg_vaddr_range.start, elf_seg_vaddr_range.end)); } diff --git a/tool/microkit/src/capdl/initialiser.rs b/tool/microkit/src/capdl/initialiser.rs index c466c5fcf..2b9f0ec81 100644 --- a/tool/microkit/src/capdl/initialiser.rs +++ b/tool/microkit/src/capdl/initialiser.rs @@ -6,7 +6,6 @@ use std::ops::Range; -use crate::elf::ElfSegmentData; use crate::util::round_up; use crate::{elf::ElfFile, sel4::PageSize}; use crate::{serialise_ut, UntypedObject}; @@ -16,7 +15,7 @@ use crate::{serialise_ut, UntypedObject}; pub const DEFAULT_INITIALISER_HEAP_MULTIPLIER: f64 = 2.0; const INITIALISER_HEAP_ADD_ON_CONSTANT: u64 = 64 * 4096; // Page size used for allocating the spec and heap segments. -pub const INITIALISER_GRANULE_SIZE: PageSize = PageSize::Small; +pub const INITIALISER_PAGE_SIZE: PageSize = PageSize::Small; pub struct CapDLInitialiserSpecMetadata { pub spec_size: u64, @@ -41,7 +40,7 @@ impl CapDLInitialiser { } pub fn image_bound(&self) -> Range { - self.elf.lowest_vaddr()..round_up(self.elf.highest_vaddr(), INITIALISER_GRANULE_SIZE as u64) + self.elf.lowest_vaddr()..round_up(self.elf.highest_vaddr(), INITIALISER_PAGE_SIZE as u64) } pub fn add_spec(&mut self, payload: Vec) { @@ -49,14 +48,15 @@ impl CapDLInitialiser { unreachable!("internal bug: CapDLInitialiser::add_spec() called more than once"); } - let spec_vaddr = self.elf.next_vaddr(INITIALISER_GRANULE_SIZE); + let spec_vaddr = self.elf.next_vaddr(INITIALISER_PAGE_SIZE); let spec_size = payload.len() as u64; self.elf.add_segment( true, false, false, spec_vaddr, - ElfSegmentData::RealData(payload), + spec_size, + payload, ); // These symbol names must match rust-sel4/crates/sel4-capdl-initializer/src/main.rs @@ -73,18 +73,18 @@ impl CapDLInitialiser { ) .unwrap(); - // Very important to make the heap the last region in memory so we can optimise the bootable image size later. - let heap_vaddr = self.elf.next_vaddr(INITIALISER_GRANULE_SIZE); + let heap_vaddr = self.elf.next_vaddr(INITIALISER_PAGE_SIZE); let heap_size = round_up( (spec_size as f64 * self.heap_multiplier) as u64 + INITIALISER_HEAP_ADD_ON_CONSTANT, - INITIALISER_GRANULE_SIZE as u64, + INITIALISER_PAGE_SIZE as u64, ); self.elf.add_segment( true, true, false, heap_vaddr, - ElfSegmentData::UninitialisedData(heap_size), + heap_size, + vec![], ); self.elf .write_symbol( diff --git a/tool/microkit/src/capdl/packaging.rs b/tool/microkit/src/capdl/packaging.rs index e0b90ef3d..bf4c3fa7f 100644 --- a/tool/microkit/src/capdl/packaging.rs +++ b/tool/microkit/src/capdl/packaging.rs @@ -56,7 +56,7 @@ fn reserialise_spec( .segments .get(data.elf_seg_idx) .unwrap() - .data()[data.elf_seg_data_range.clone()], + .data[data.elf_seg_data_range.clone()], )), }); diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 2d05e235e..7f23d3f05 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -106,15 +106,10 @@ const PF_X: u32 = 0x1; const PF_W: u32 = 0x2; const PF_R: u32 = 0x4; -#[derive(Eq, PartialEq, Clone)] -pub enum ElfSegmentData { - RealData(Vec), - UninitialisedData(u64), -} - #[derive(Eq, PartialEq, Clone)] pub struct ElfSegment { - pub data: ElfSegmentData, + pub data: Vec, + pub memory_size: u64, pub phys_addr: u64, pub virt_addr: u64, pub loadable: bool, @@ -122,43 +117,8 @@ pub struct ElfSegment { } impl ElfSegment { - pub fn mem_size(&self) -> u64 { - match &self.data { - ElfSegmentData::RealData(bytes) => bytes.len() as u64, - ElfSegmentData::UninitialisedData(size) => *size, - } - } - pub fn file_size(&self) -> u64 { - match &self.data { - ElfSegmentData::RealData(bytes) => bytes.len() as u64, - ElfSegmentData::UninitialisedData(_) => 0, - } - } - - pub fn data(&self) -> &Vec { - match &self.data { - ElfSegmentData::RealData(bytes) => bytes, - ElfSegmentData::UninitialisedData(_) => { - unreachable!("internal bug: data() called on an uninitialised ELF segment.") - } - } - } - - pub fn data_mut(&mut self) -> &mut Vec { - match &mut self.data { - ElfSegmentData::RealData(bytes) => bytes, - ElfSegmentData::UninitialisedData(_) => { - unreachable!("internal bug: data_mut() called on an uninitialised ELF segment.") - } - } - } - - pub fn is_uninitialised(&self) -> bool { - match &self.data { - ElfSegmentData::RealData(_) => false, - ElfSegmentData::UninitialisedData(_) => true, - } + self.data.len() as u64 } pub fn is_writable(&self) -> bool { @@ -256,15 +216,12 @@ impl ElfFile { continue; } - let mut segment_data_bytes = vec![0; phent.memsz as usize]; - segment_data_bytes[..phent.filesz as usize] - .copy_from_slice(&bytes[segment_start..segment_end]); - - let segment_data = ElfSegmentData::RealData(segment_data_bytes); + let segment_data_bytes = Vec::from(&bytes[segment_start..segment_end]); let flags = phent.flags; let segment = ElfSegment { - data: segment_data, + data: segment_data_bytes, + memory_size: phent.memsz, phys_addr: phent.paddr, virt_addr: phent.vaddr, loadable: phent.type_ == PHENT_TYPE_LOADABLE, @@ -372,26 +329,17 @@ impl ElfFile { pub fn write_symbol(&mut self, variable_name: &str, data: &[u8]) -> Result<(), String> { let (vaddr, size) = self.find_symbol(variable_name)?; for seg in &mut self.segments { - if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.mem_size() { + // note: if the symbol is in the memory range, but not the file size, + // we can't really find somewhere to put it... + if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.file_size() { let offset = (vaddr - seg.virt_addr) as usize; assert!(data.len() as u64 <= size); - seg.data_mut()[offset..offset + data.len()].copy_from_slice(data); + seg.data[offset..offset + data.len()].copy_from_slice(data); return Ok(()); } } - Err(format!("No symbol named {variable_name} found")) - } - - pub fn get_data(&self, vaddr: u64, size: u64) -> Option<&[u8]> { - for seg in &self.segments { - if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.mem_size() { - let offset = (vaddr - seg.virt_addr) as usize; - return Some(&seg.data()[offset..offset + size as usize]); - } - } - - None + Err(format!("symbol {variable_name} not backed by data inside the ELF file")) } fn get_string(strtab: &[u8], idx: usize) -> Result<&str, String> { @@ -426,7 +374,7 @@ impl ElfFile { let existing_vaddrs: Vec = self .loadable_segments() .iter() - .map(|segm| segm.virt_addr + segm.mem_size()) + .map(|segm| segm.virt_addr + segm.memory_size) .collect(); *existing_vaddrs.iter().max().unwrap() } @@ -442,7 +390,8 @@ impl ElfFile { write: bool, execute: bool, vaddr: u64, - data: ElfSegmentData, + memory_size: u64, + data: Vec, ) { let r = if read { PF_R } else { 0 }; let w = if write { PF_W } else { 0 }; @@ -450,6 +399,7 @@ impl ElfFile { let elf_segment = ElfSegment { data, + memory_size, phys_addr: vaddr, virt_addr: vaddr, loadable: true, @@ -526,7 +476,7 @@ impl ElfFile { vaddr: seg.virt_addr, paddr: seg.phys_addr, filesz: seg.file_size(), - memsz: seg.mem_size(), + memsz: seg.memory_size, align: 0, }; @@ -547,10 +497,9 @@ impl ElfFile { for (i, seg) in self .loadable_segments() .iter() - .filter(|seg| !seg.is_uninitialised()) .enumerate() { - elf_file.write_all(seg.data()).unwrap_or_else(|_| { + elf_file.write_all(&seg.data).unwrap_or_else(|_| { panic!( "Failed to write ELF segment data #{} for '{}'", i, diff --git a/tool/microkit/src/lib.rs b/tool/microkit/src/lib.rs index 10ba1f258..d7d3ddb4e 100644 --- a/tool/microkit/src/lib.rs +++ b/tool/microkit/src/lib.rs @@ -106,7 +106,7 @@ impl Region { } pub fn data<'a>(&self, elf: &'a elf::ElfFile) -> &'a Vec { - elf.segments[self.segment_idx].data() + &elf.segments[self.segment_idx].data } } diff --git a/tool/microkit/src/loader.rs b/tool/microkit/src/loader.rs index df02dcae3..940f0410f 100644 --- a/tool/microkit/src/loader.rs +++ b/tool/microkit/src/loader.rs @@ -79,10 +79,10 @@ impl Riscv64 { /// Checks that each region in the given list does not overlap with any other region. /// Panics upon finding an overlapping region -fn check_non_overlapping(regions: &Vec<(u64, &[u8])>) { +fn check_non_overlapping(regions: &Vec<(u64, &[u8], u64)>) { let mut checked: Vec<(u64, u64)> = Vec::new(); - for (base, data) in regions { - let end = base + data.len() as u64; + for (base, _, mem_size) in regions { + let end = base + mem_size; // Check that this does not overlap with any checked regions for (b, e) in &checked { if !(end <= *b || *base >= *e) { @@ -97,7 +97,10 @@ fn check_non_overlapping(regions: &Vec<(u64, &[u8])>) { #[repr(C)] struct LoaderRegion64 { load_addr: u64, - size: u64, + // The size of the region data in the loader + loader_size: u64, + // The memory size of the region once expanded by the loader + memory_size: u64, offset: u64, r#type: u64, } @@ -118,7 +121,7 @@ pub struct Loader<'a> { image: Vec, header: LoaderHeader64, region_metadata: Vec, - regions: Vec<(u64, &'a [u8])>, + regions: Vec<(u64, &'a [u8], u64)>, } impl<'a> Loader<'a> { @@ -146,7 +149,7 @@ impl<'a> Loader<'a> { ), }; - let mut regions: Vec<(u64, &[u8])> = Vec::new(); + let mut regions: Vec<(u64, &[u8], u64)> = Vec::new(); let mut kernel_first_vaddr = None; let mut kernel_last_vaddr = None; @@ -160,10 +163,10 @@ impl<'a> Loader<'a> { } if kernel_last_vaddr.is_none() - || segment.virt_addr + segment.mem_size() > kernel_last_vaddr.unwrap() + || segment.virt_addr + segment.memory_size > kernel_last_vaddr.unwrap() { kernel_last_vaddr = - Some(round_up(segment.virt_addr + segment.mem_size(), mb(2))); + Some(round_up(segment.virt_addr + segment.memory_size, mb(2))); } if kernel_first_paddr.is_none() || segment.phys_addr < kernel_first_paddr.unwrap() { @@ -178,31 +181,20 @@ impl<'a> Loader<'a> { kernel_p_v_offset = Some(segment.virt_addr - segment.phys_addr); } - regions.push((segment.phys_addr, segment.data().as_slice())); + regions.push((segment.phys_addr, segment.data.as_slice(), segment.memory_size)); } } assert!(kernel_first_paddr.is_some()); - // We support an initial task ELF with multiple segments. This is implemented by amalgamating all the segments - // into 1 segment, so if your segments are sparse, a lot of memory will be wasted. - let initial_task_segments = initial_task_elf.loadable_segments(); - // Compute an available physical memory segment large enough to house the initial task (CapDL initialiser with spec) // that is after the kernel window. - let inittask_p_v_offset = initial_task_vaddr_range.start - initial_task_phy_base; + let inittask_p_v_offset = initial_task_vaddr_range.start.wrapping_sub(initial_task_phy_base); let inittask_v_entry = initial_task_elf.entry; - - // initialiser.rs will always place the heap as the last region in the initial task's address space. - // So instead of copying a bunch of useless zeroes into the image, we can just leave it uninitialised. - assert!(initial_task_segments.last().unwrap().is_uninitialised()); - // Skip heap segment - for segment in initial_task_segments[..initial_task_segments.len() - 1].iter() { - if segment.mem_size() > 0 { - let segment_paddr = - initial_task_phy_base + (segment.virt_addr - initial_task_vaddr_range.start); - regions.push((segment_paddr, segment.data())); - } + for segment in initial_task_elf.loadable_segments().iter() { + let segment_paddr = + initial_task_phy_base + (segment.virt_addr - initial_task_vaddr_range.start); + regions.push((segment_paddr, segment.data.as_slice(), segment.memory_size)); } // Determine the pagetable variables @@ -232,7 +224,7 @@ impl<'a> Loader<'a> { // We have to clone here as the image executable is part of this function return object, // and the loader ELF is deserialised in this scope, so its lifetime will be shorter than // the return object. - let mut image = image_segment.data().clone(); + let mut image = image_segment.data.clone(); if image_vaddr != loader_elf.entry { panic!("The loader entry point must be the first byte in the image"); @@ -254,17 +246,20 @@ impl<'a> Loader<'a> { let ui_p_reg_end = initial_task_vaddr_range.end - inittask_p_v_offset; assert!(ui_p_reg_end > ui_p_reg_start); - // This clone isn't too bad as it is just a Vec<(u64, &[u8])> + // This clone isn't too bad as it is just a Vec<(u64, &[u8], u64)> let mut all_regions_with_loader = regions.clone(); - all_regions_with_loader.push((image_vaddr, &image)); + all_regions_with_loader.push((image_vaddr, &image, image.len() as u64)); check_non_overlapping(&all_regions_with_loader); let mut region_metadata = Vec::new(); let mut offset: u64 = 0; - for (addr, data) in ®ions { + for &(addr, data, memory_size) in regions.iter() { + assert!(data.len() as u64 <= memory_size); + region_metadata.push(LoaderRegion64 { - load_addr: *addr, - size: data.len() as u64, + load_addr: addr, + loader_size: data.len() as u64, + memory_size, offset, r#type: 1, }); @@ -273,7 +268,7 @@ impl<'a> Loader<'a> { let size = std::mem::size_of::() as u64 + region_metadata.iter().fold(0_u64, |acc, x| { - acc + x.size + std::mem::size_of::() as u64 + acc + x.loader_size + std::mem::size_of::() as u64 }); let header = LoaderHeader64 { @@ -322,7 +317,7 @@ impl<'a> Loader<'a> { } // Now we can write out all the region data - for (_, data) in &self.regions { + for (_, data, _) in self.regions.iter() { loader_buf .write_all(data) .expect("Failed to write region data to loader");