elfconvert: add support for kelvin workloads - make the pad field a file type (one of: application, springbok, kelvin) - add -f kelvin to signal a kelvin workload - add -f springbok and treat -f model as springbok for backwards compat - add -f application Newly formatted files are backward compatible. Change-Id: I80d05ac7d9926f659126a4a901a50ddd715b4d63
diff --git a/misc/elfconvert/src/convert.rs b/misc/elfconvert/src/convert.rs index df79e5d..8603d3a 100644 --- a/misc/elfconvert/src/convert.rs +++ b/misc/elfconvert/src/convert.rs
@@ -25,7 +25,7 @@ * fsize: u32 Length of data that follows (bytes) * msize: u32 Size of memory region (bytes) * align: u32 Section data alignment (bytes) - * pad: u32 <ignore, reserved for future use> + * ftype: u32 File type (see below) * crc32: u32 CRC32 of the data that follows * * Section header flags (mostly from ELF program section): @@ -68,9 +68,6 @@ } } -/// Maximum memory size for TCM, used for model loading -const TCM_SIZE: usize = 0x1000000; - // TODO(sleffler): use runtime defs const MAGIC: u64 = 0x0405_1957_1014_1955; @@ -79,6 +76,10 @@ const SECTION_EXEC: u32 = 0x4; // Data are executable const SECTION_ENTRY: u32 = 0x8; // Entry point valid +const FTYPE_APPLICATION: u32 = 0x0405_1957; // CantripOS application +const FTYPE_SPRINGBOK: u32 = 0x1014_1955; // Springbok model +const FTYPE_KELVIN: u32 = 0x0124_1998; // Kelvin model + /// Given a set of ELF flags, convert into a set of flags for a section /// recognizable by the CantripOS ProcessManager's loader. fn to_section_flags(elf_flags: xmas_elf::program::Flags) -> u32 { @@ -107,12 +108,13 @@ fsize: u32, msize: u32, align: u32, // Section data alignment (bytes) - pad: u32, + ftype: u32, // File type crc32: u32, } impl SectionHeader { fn new( + ftype: u32, vaddr: u64, flags: u32, entry: u64, @@ -133,7 +135,7 @@ fsize: (fsize as u32).to_be(), msize: (msize as u32).to_be(), align: (align as u32).to_be(), - pad: 0, + ftype: ftype.to_be(), crc32: crc.to_be(), } } @@ -152,31 +154,6 @@ } } -/// The virtualized address of each loadable WMMU section (go/shodan-vc-memory). -const TEXT_VADDR: u64 = 0x80000000; -const CONST_DATA_VADDR: u64 = 0x81000000; -const MODEL_OUTPUT_VADDR: u64 = 0x82000000; -const STATIC_DATA_VADDR: u64 = 0x83000000; -const TEMP_DATA_VADDR: u64 = 0x85000000; - -/// Helpful conversion from a ModelSection to a string name of that address -fn vaddr_as_str(vaddr: u64) -> &'static str { - match vaddr { - TEXT_VADDR => ".text", - CONST_DATA_VADDR => ".data", - MODEL_OUTPUT_VADDR => ".model_output", - STATIC_DATA_VADDR => ".static", - TEMP_DATA_VADDR => ".bss", - _ => "<unknown>", - } -} - -/// Predicate to determine if a given vaddr is loadable in the TCM -fn is_vaddr_in_tcm(vaddr: u64) -> bool { - matches!(vaddr, TEXT_VADDR | CONST_DATA_VADDR | MODEL_OUTPUT_VADDR | STATIC_DATA_VADDR - | TEMP_DATA_VADDR) -} - #[derive(Debug)] pub enum ConversionError { SegmentOutsideTCM(u64), @@ -211,14 +188,47 @@ } } -/// Converts an ELF-format ML model into CantripOS' loadable format. +pub mod springbok { +/// Springbok support. + +use super::*; + +/// Maximum memory size for TCM, used for model loading +const TCM_SIZE: usize = 0x1000000; + +/// The virtualized address of each loadable WMMU section (go/shodan-vc-memory). +const TEXT_VADDR: u64 = 0x80000000; +const CONST_DATA_VADDR: u64 = 0x81000000; +const MODEL_OUTPUT_VADDR: u64 = 0x82000000; +const STATIC_DATA_VADDR: u64 = 0x83000000; +const TEMP_DATA_VADDR: u64 = 0x85000000; + +/// Helpful conversion from a ModelSection to a string name of that address +fn vaddr_as_str(vaddr: u64) -> &'static str { + match vaddr { + TEXT_VADDR => ".text", + CONST_DATA_VADDR => ".data", + MODEL_OUTPUT_VADDR => ".model_output", + STATIC_DATA_VADDR => ".static", + TEMP_DATA_VADDR => ".bss", + _ => "<unknown>", + } +} + +/// Predicate to determine if a given vaddr is loadable in the TCM +fn is_vaddr_in_tcm(vaddr: u64) -> bool { + matches!(vaddr, TEXT_VADDR | CONST_DATA_VADDR | MODEL_OUTPUT_VADDR | STATIC_DATA_VADDR + | TEMP_DATA_VADDR) +} + +/// Converts an ELF-format ML (Springbok) model into CantripOS' loadable format. /// /// Returns the number of bytes written. pub fn model(elf: &ElfFile, output_file: &mut File) -> Result<u64, ConversionError> { let entry = elf.header.pt2.entry_point(); info!("ELF entry point is {:#x}", entry); - for seg in elf.program_iter().filter(is_load_type) { + for seg in elf.program_iter().filter(super::is_load_type) { let fsize = seg.file_size() as usize; let msize = seg.mem_size() as usize; let align = seg.align() as usize; @@ -256,6 +266,7 @@ let mut digest = crc32::Digest::new(crc32::IEEE); digest.write(bytes); let section = SectionHeader::new( + FTYPE_SPRINGBOK, seg.virtual_addr(), flags, entry, @@ -279,6 +290,84 @@ Ok(output_file.stream_position()?) } +} // springbok + +pub mod kelvin { +/// Kelvin support. + +use super::*; + +/// Maximum memory size for TCM, used for model loading +const TCM_SIZE: usize = 0x400000; + +/// Converts an ELF-format Kelvin workload into CantripOS' loadable format. +/// +/// Returns the number of bytes written. +pub fn model(elf: &ElfFile, output_file: &mut File) -> Result<u64, ConversionError> { + let entry = elf.header.pt2.entry_point() as usize; + info!("ELF entry point is {:#x}", entry); + + for seg in elf.program_iter().filter(super::is_load_type) { + let fsize = seg.file_size() as usize; + let msize = seg.mem_size() as usize; + let align = seg.align() as usize; + let mut flags = to_section_flags(seg.flags()); + let vaddr = seg.virtual_addr() as usize; + let segment_name = "LOAD"; // XXX + + debug!("Processing new section [vaddr={:#x}, fsize={:#x}, msize={:#x}, align={:#x}, flags={:#b}]", + vaddr, fsize, msize, align, flags); + + if let SegmentData::Undefined(bytes) = seg.get_data(elf)? { + if vaddr + msize >= TCM_SIZE { + return Err(ConversionError::SegmentOutsideTCM(vaddr as u64)); + } + + if vaddr <= entry && entry < vaddr + msize { + debug!( + "Marking segment {} as entrypoint [vaddr={:#x}, entry={:#x}]", + segment_name, vaddr, entry + ); + flags |= SECTION_ENTRY; + } + + debug!( + "Processing {} segment [len={}, addr={:#x}, msize={}]", + segment_name, + bytes.len(), + vaddr, + msize + ); + + let mut digest = crc32::Digest::new(crc32::IEEE); + digest.write(bytes); + let section = SectionHeader::new( + FTYPE_KELVIN, + seg.virtual_addr(), + flags, + entry as u64, + align, + digest.sum32(), + fsize, + msize, + ); + + section.write(output_file, bytes)?; + info!( + "Wrote {} segment of {} bytes at {:#x} msize {}", + segment_name, + bytes.len(), + seg.virtual_addr(), + msize + ); + } + } + + Ok(output_file.stream_position()?) +} + +} // kelvin + /// Converts an ELF-format application binary into CantripOS' loadable format. /// /// Returns the number of bytes written. @@ -309,6 +398,7 @@ let mut digest = crc32::Digest::new(crc32::IEEE); digest.write(bytes); let header = SectionHeader::new( + FTYPE_APPLICATION, seg.virtual_addr(), flags, entry,
diff --git a/misc/elfconvert/src/main.rs b/misc/elfconvert/src/main.rs index a30a1b8..3e46e30 100644 --- a/misc/elfconvert/src/main.rs +++ b/misc/elfconvert/src/main.rs
@@ -28,7 +28,9 @@ #[derive(clap::ValueEnum, Clone)] enum FileType { App = 0, - Model, + Model, // Springbok for backwards compat + Springbok, // Springbok IREE output + Kelvin, // Kelvin workload } #[derive(Parser)] @@ -84,8 +86,12 @@ let count = convert::application(&elf, &mut output_file)?; info!("Wrote {} bytes.", count); } - FileType::Model => { - let count = convert::model(&elf, &mut output_file)?; + FileType::Model | FileType::Springbok => { + let count = convert::springbok::model(&elf, &mut output_file)?; + info!("Wrote {} bytes.", count); + } + FileType::Kelvin => { + let count = convert::kelvin::model(&elf, &mut output_file)?; info!("Wrote {} bytes.", count); } }