logicaffeine_compile/codegen_sva/hart/
image.rs1use super::soc::{IMAGE_HEADER_SIZE, IMAGE_MAGIC, IMAGE_VERSION, SECTION_ARGS, SECTION_BUNDLE, SECTION_FIRMWARE, SECTION_SHIM};
7use crate::crypto_accel::sha256;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct BootImage {
12 pub rtl_md5: [u8; 16],
14 pub shim: Vec<u8>,
16 pub firmware: Vec<u8>,
18 pub bundle: Vec<(String, Vec<u8>)>,
20 pub args: Vec<String>,
22}
23
24pub fn bundle_bytes(files: &[(String, Vec<u8>)]) -> Vec<u8> {
26 let mut out = (files.len() as u32).to_le_bytes().to_vec();
27 for (name, data) in files {
28 out.extend_from_slice(&(name.len() as u32).to_le_bytes());
29 out.extend_from_slice(name.as_bytes());
30 out.extend_from_slice(&(data.len() as u64).to_le_bytes());
31 out.extend_from_slice(data);
32 }
33 out
34}
35
36fn args_bytes(args: &[String]) -> Vec<u8> {
38 let mut out = Vec::new();
39 for a in args {
40 out.extend_from_slice(a.as_bytes());
41 out.push(0);
42 }
43 out
44}
45
46pub fn body(image: &BootImage) -> Vec<u8> {
49 let mut sections: Vec<(u32, Vec<u8>)> = vec![
50 (SECTION_SHIM, image.shim.clone()),
51 (SECTION_FIRMWARE, image.firmware.clone()),
52 (SECTION_BUNDLE, bundle_bytes(&image.bundle)),
53 ];
54 if !image.args.is_empty() {
55 sections.push((SECTION_ARGS, args_bytes(&image.args)));
56 }
57 let table_end = 8 + 24 * sections.len();
58 let mut offset = (table_end + 7) & !7;
59 let mut table = Vec::new();
60 let mut placed = Vec::new();
61 for (kind, data) in §ions {
62 table.extend_from_slice(&kind.to_le_bytes());
63 table.extend_from_slice(&0u32.to_le_bytes());
64 table.extend_from_slice(&(offset as u64).to_le_bytes());
65 table.extend_from_slice(&(data.len() as u64).to_le_bytes());
66 placed.push(offset);
67 offset = (offset + data.len() + 7) & !7;
68 }
69 let mut out = vec![0u8; offset];
70 out[0..4].copy_from_slice(&(sections.len() as u32).to_le_bytes());
71 out[8..table_end].copy_from_slice(&table);
72 for ((_, data), at) in sections.iter().zip(placed) {
73 out[at..at + data.len()].copy_from_slice(data);
74 }
75 out
76}
77
78pub fn pack(bytes: &[u8]) -> Vec<u8> {
80 miniz_oxide::deflate::compress_to_vec(bytes, 10)
81}
82
83pub fn build(image: &BootImage) -> Vec<u8> {
85 let body = body(image);
86 let packed = pack(&body);
87 let mut header = vec![0u8; IMAGE_HEADER_SIZE as usize];
88 header[0..8].copy_from_slice(&IMAGE_MAGIC.to_le_bytes());
89 header[8..12].copy_from_slice(&IMAGE_VERSION.to_le_bytes());
90 header[12..16].copy_from_slice(&(IMAGE_HEADER_SIZE as u32).to_le_bytes());
91 header[0x10..0x20].copy_from_slice(&image.rtl_md5);
92 header[0x20..0x28].copy_from_slice(&(body.len() as u64).to_le_bytes());
93 header[0x28..0x30].copy_from_slice(&(packed.len() as u64).to_le_bytes());
94 header[0x30..0x50].copy_from_slice(&sha256(&packed));
95 header.extend_from_slice(&packed);
96 header
97}
98
99pub fn open(flash_image: &[u8]) -> Result<([u8; 16], Vec<u8>), String> {
101 let h = flash_image.get(..IMAGE_HEADER_SIZE as usize).ok_or("the image is shorter than its header")?;
102 let word = |o: usize| u64::from_le_bytes(h[o..o + 8].try_into().unwrap());
103 if word(0) != IMAGE_MAGIC {
104 return Err("no boot image here".into());
105 }
106 if u32::from_le_bytes(h[8..12].try_into().unwrap()) != IMAGE_VERSION {
107 return Err("the boot image's version is not this one".into());
108 }
109 let (body_len, packed_len) = (word(0x20) as usize, word(0x28) as usize);
110 let packed = flash_image
111 .get(IMAGE_HEADER_SIZE as usize..IMAGE_HEADER_SIZE as usize + packed_len)
112 .ok_or("the image is shorter than its body")?;
113 if sha256(packed)[..] != h[0x30..0x50] {
114 return Err("the boot image's digest does not match".into());
115 }
116 let body = miniz_oxide::inflate::decompress_to_vec(packed).map_err(|e| format!("the body does not inflate: {e:?}"))?;
117 if body.len() != body_len {
118 return Err(format!("the body inflates to {} bytes, not {body_len}", body.len()));
119 }
120 Ok((h[0x10..0x20].try_into().unwrap(), body))
121}