xous_tools/
xous_arguments.rs1use std::fmt;
2use std::io::{Cursor, Result, Seek, Write};
3pub type XousArgumentCode = u32;
4pub type XousSize = u32;
5use crc::{Hasher16, crc16};
6
7#[macro_export]
8macro_rules! make_type {
9 ($fcc:expr) => {{
10 let mut c: [u8; 4] = Default::default();
11 c.copy_from_slice($fcc.as_bytes());
12 u32::from_le_bytes(c)
13 }};
14}
15
16pub trait XousArgument: fmt::Display {
17 fn code(&self) -> XousArgumentCode;
19
20 fn name(&self) -> String {
21 let tag_name_bytes = self.code().to_le_bytes();
22 String::from_utf8_lossy(&tag_name_bytes).to_string()
23 }
24
25 fn length(&self) -> XousSize;
27
28 fn finalize(&mut self, _offset: usize) -> usize { 0 }
31
32 fn serialize(&self, output: &mut dyn Write) -> Result<usize>;
35
36 fn last_data(&self) -> &[u8] { &[] }
38
39 fn alignment_offset(&self) -> usize { 0 }
40
41 fn load_offset(&self) -> usize { 0 }
42}
43
44pub struct XousArguments {
45 pub ram_start: XousSize,
46 pub ram_length: XousSize,
47 ram_name: u32,
48 pub arguments: Vec<Box<dyn XousArgument>>,
49 pub detached_offset: Option<XousSize>,
50}
51
52impl fmt::Display for XousArguments {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 writeln!(f, "Xous Arguments with {} parameters", self.arguments.len())?;
55
56 let tag_name_bytes = self.ram_name.to_le_bytes();
57 let tag_name = String::from_utf8_lossy(&tag_name_bytes);
58 writeln!(
59 f,
60 " Main RAM \"{}\" ({:08x}): {:08x} - {:08x}",
61 tag_name,
62 self.ram_name,
63 self.ram_start,
64 self.ram_start + self.ram_length
65 )?;
66
67 for (index, arg) in self.arguments.iter().enumerate() {
68 write!(f, "{:2}{}", index + 1, arg)?;
69 }
70 Ok(())
71 }
72}
73
74impl XousArguments {
75 pub fn new(ram_start: XousSize, ram_length: XousSize, ram_name: u32) -> XousArguments {
76 XousArguments { ram_start, ram_length, ram_name, arguments: vec![], detached_offset: None }
77 }
78
79 pub fn set_detached_offset(&mut self, offset: XousSize) { self.detached_offset = Some(offset); }
80
81 pub fn finalize(&mut self) {
82 let mut running_offset =
83 crate::tags::align_size_up(self.len() as usize, 0) + self.detached_offset.unwrap_or(0) as usize;
84 for arg in &mut self.arguments {
86 running_offset = crate::tags::align_size_up(running_offset, arg.alignment_offset());
87 running_offset += arg.finalize(running_offset);
89 }
90 }
91
92 pub fn add<T: 'static>(&mut self, arg: T)
93 where
94 T: XousArgument + Sized,
95 {
96 self.arguments.push(Box::new(arg));
97 }
98
99 pub fn write<T>(&mut self, mut w: T) -> Result<()>
100 where
101 T: Write + Seek,
102 {
103 let total_length = self.len();
104
105 self.finalize();
108
109 let mut tag_data = Cursor::new(Vec::new());
111 tag_data.write_all(&((total_length / 4) as u32).to_le_bytes())?;
112 tag_data.write_all(&1u32.to_le_bytes())?; tag_data.write_all(&(self.ram_start as u32).to_le_bytes())?;
114 tag_data.write_all(&(self.ram_length as u32).to_le_bytes())?;
115 tag_data.write_all(&(self.ram_name as u32).to_le_bytes())?;
116
117 assert!(tag_data.get_ref().len().trailing_zeros() >= 2, "tag data was not a multiple of 4 bytes!");
118
119 let mut digest = crc16::Digest::new(crc16::X25);
120
121 let header_offset = w.stream_position()?;
123
124 w.write_all(&u32::from_le_bytes(*b"XArg").to_le_bytes())?;
126 digest.write(tag_data.get_ref());
127 w.write_all(&digest.sum16().to_le_bytes())?; w.write_all(&((tag_data.get_ref().len() / 4) as u16).to_le_bytes())?; w.write_all(tag_data.get_ref())?;
130
131 for arg in &self.arguments {
133 let mut tag_data = Cursor::new(Vec::new());
134 let advertised_len = arg.length() as u32;
135 let actual_len = arg.serialize(&mut tag_data)? as u32;
136 assert_eq!(
137 advertised_len,
138 actual_len,
139 "argument {} advertised it would write {} bytes, but it wrote {} bytes",
140 arg.name(),
141 advertised_len,
142 actual_len
143 );
144 assert_eq!(
145 tag_data.get_ref().len() as u32,
146 actual_len,
147 "argument {} said it wrote {} bytes, but it actually wrote {} bytes",
148 arg.name(),
149 actual_len,
150 tag_data.get_ref().len()
151 );
152
153 let mut digest = crc16::Digest::new(crc16::X25);
154 w.write_all(&arg.code().to_le_bytes())?;
156 digest.write(tag_data.get_ref());
157 w.write_all(&digest.sum16().to_le_bytes())?; w.write_all(&((tag_data.get_ref().len() / 4) as u16).to_le_bytes())?; w.write_all(tag_data.get_ref())?;
160 }
161
162 for arg in &self.arguments {
164 let pos = w.stream_position()?;
166 let pad_len = crate::tags::align_size_up(pos as usize, arg.alignment_offset()) - pos as usize;
168 let pad = vec![0u8; pad_len];
171 w.write_all(&pad)?;
172
173 if arg.code() == u32::from_le_bytes(*b"IniS") {
178 assert!(
184 header_offset == 0x1000 || header_offset == 0x0,
185 "Header offset assumption for IniS was not met: loader hard-codes this size"
186 );
187 assert!(
200 arg.load_offset() as u64 == w.stream_position()? - header_offset,
201 "IniS alignment assumption not satisfied, did XArgs overflow? \
202 Did the IniS section trigger an alignment edge case with align_size_up()?"
203 );
204 }
205 w.write_all(arg.last_data()).expect("couldn't write extra arg data");
207 }
208
209 Ok(())
210 }
211
212 pub fn len(&self) -> u32 {
213 let mut total_length = 20 + self.header_len() as u32; for arg in &self.arguments {
215 total_length += arg.length() + 8;
216 }
217 total_length
218 }
219
220 pub fn is_empty(&self) -> bool { false }
221
222 pub fn header_len(&self) -> usize { 8 }
223}