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);
}
}