extend image builder to handle baking in statics

This commit is contained in:
bunnie 2025-09-21 21:50:31 +08:00
parent 8d19be2784
commit 024c8131bc
3 changed files with 55 additions and 34 deletions

View file

@ -26,6 +26,7 @@ pkcs8 = { version = "0.8.0", features = ["pem"] }
base64 = "0.20.0"
rand = "0.8.5"
aes-gcm-siv = "0.11.1"
bao1x-api = { path = "../libs/bao1x-api" }
[[bin]]
name = "copy-object"

View file

@ -1,7 +1,7 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process;
use tools::elf;
@ -10,16 +10,34 @@ fn main() {
env_logger::init();
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: {} input.elf [output.bin]", args.get(0).unwrap_or(&"copy-object".to_owned()));
println!(
"Usage: {} input.elf [output.bin] [--bao1x]",
args.get(0).unwrap_or(&"copy-object".to_owned())
);
return;
}
let input_filename = Path::new(args.get(1).unwrap()).to_path_buf();
let output_filename = args.get(2).map(|x| Path::new(x).to_path_buf()).unwrap_or_else(|| {
// Parse remaining arguments
let mut output_filename: Option<PathBuf> = None;
let mut bao1x_flag = false;
for arg in args.iter().skip(2) {
if arg == "--bao1x" {
bao1x_flag = true;
} else if output_filename.is_none() {
output_filename = Some(Path::new(arg).to_path_buf());
}
}
// Set default output filename if none provided
let output_filename = output_filename.unwrap_or_else(|| {
let mut output_filename = input_filename.clone();
output_filename.set_extension("bin");
output_filename
});
if output_filename == input_filename {
eprintln!("Input and output filename are the same: {}", output_filename.display());
eprintln!("Specify an output path, or change the suffix of your input file from \".bin\"");
@ -33,6 +51,26 @@ fn main() {
eprintln!("Couldn't create output file {}: {}", output_filename.display(), e);
process::exit(1);
});
if bao1x_flag {
let mut statics = bao1x_api::StaticsInRom {
jump_instruction: bao1x_api::JUMP_INSTRUCTION,
version: bao1x_api::STATICS_IN_ROM_VERSION,
valid_pokes: 0,
data_origin: 0,
data_size_bytes: 0,
poke_table: [(0u32, 0u32); 30],
};
statics.data_origin = pd.data_offset;
statics.data_size_bytes = pd.clear_size;
statics.valid_pokes = pd.poke_table.len() as u16;
for (&entry, dest) in pd.poke_table.iter().zip(statics.poke_table.iter_mut()) {
*dest = entry;
}
f.write(statics.as_bytes()).unwrap_or_else(|e| {
eprintln!("Couldn't write data to {}: {}", output_filename.display(), e);
process::exit(1);
});
}
f.write_all(&pd.program).unwrap_or_else(|e| {
eprintln!("Couldn't write data to {}: {}", output_filename.display(), e);
process::exit(1);

View file

@ -36,6 +36,13 @@ pub struct ProgramDescription {
/// Size of .data section
pub data_size: u32,
/// Poke table for data section; in (address, data) tuples. Addresses are in `u32` format
/// because we're packing them for a target that is 32-bits, which may be different from the host.
pub poke_table: Vec<(u32, u32)>,
/// Size of region to be zero-ized by the loader
pub clear_size: u32,
/// Size of the .bss section
pub bss_size: u32,
@ -156,6 +163,7 @@ pub fn process_program(b: &[u8], rom_only: bool) -> Result<ProgramDescription, E
let elf = ElfFile::new(&b).map_err(|x| ElfReadError::ParseElfError(x))?;
let entry_point = elf.header.pt2.entry_point() as u32;
let mut program_data = Cursor::new(Vec::new());
let mut poke_table = Vec::<(u32, u32)>::new();
let mut size = 0;
let mut data_offset = 0;
@ -328,42 +336,12 @@ pub fn process_program(b: &[u8], rom_only: bool) -> Result<ProgramDescription, E
debug!("Program size: {} bytes", observed_size);
if data_offset as usize % size_of::<u32>() == 0 {
const SPACING: &'static str = " ";
let mut non_zero_tuples = Vec::<(usize, u32)>::new();
for (i, chunk) in data_copy.chunks_exact(4).enumerate() {
let word = u32::from_le_bytes(chunk.try_into().unwrap());
if word != 0 {
non_zero_tuples.push((i, word));
poke_table.push((i as u32, word));
}
}
println!("Code for loader/baremetal integration:\n");
println!("// Define the .data region - bootstrap baremetal using these hard-coded parameters.");
println!("const DATA_ORIGIN: usize = 0x{:x};", data_offset);
println!(
"const DATA_SIZE_BYTES: usize = 0x{:x};",
// round up to the nearest u32 word. Includes .data, .bss, .stack, .heap - regions to be zero'd.
((data_size + bss_size) as usize + size_of::<u32>() - 1) & !(size_of::<u32>() - 1)
);
let mut init_str = String::new();
init_str.push_str(&format!("const DATA_INIT: [(usize, u32); {}] = [\n", non_zero_tuples.len()));
for (offset, data) in non_zero_tuples {
init_str.push_str(&format!("{}(0x{:x}, 0x{:x}),\n", SPACING, offset, data));
}
init_str.push_str(&format!("];"));
print!("{}", init_str);
let boilerplate = r#"
// Clear .data, .bss, .stack, .heap regions & setup .data values
unsafe {
let data_ptr = DATA_ORIGIN as *mut u32;
for i in 0..DATA_SIZE_BYTES / size_of::<u32>() {
data_ptr.add(i).write_volatile(0);
}
for (offset, data) in DATA_INIT {
data_ptr.add(offset).write_volatile(data);
}
}
"#;
println!("\n{}", boilerplate);
} else {
println!(
"Data section is not word-aligned, check objdump in detail for how to initialize the section"
@ -377,6 +355,10 @@ unsafe {
text_offset,
text_size,
bss_size,
// round up to the nearest u32 word. Includes .data, .bss, .stack, .heap - regions to be zero'd.
clear_size: (((data_size + bss_size) as usize + size_of::<u32>() - 1) & !(size_of::<u32>() - 1))
as u32,
poke_table,
})
}