1use std::env;
2use std::num::ParseIntError;
12use std::path::Path;
13use std::result::Result;
14
15pub struct Memory {
16 sections: Vec<MemorySection>,
17}
18
19impl Memory {
20 pub fn new() -> Memory {
21 Memory {
22 sections: Vec::new(),
23 }
24 }
25
26 pub fn add_section(self, section: MemorySection) -> Memory {
27 let mut sections = self.sections;
28 sections.push(section);
29 Memory { sections }
30 }
31
32 pub fn to_string(&self) -> String {
33 let mut out = String::new();
34
35 for section in &self.sections {
37 out.push_str(&format!(
38 "_{}_start = {:#X};\n",
39 section.name, section.origin
40 ));
41 out.push_str(&format!(
42 "_{}_length = {:#X};\n",
43 section.name, section.length
44 ));
45 }
46
47 if !&self.sections.is_empty() {
50 out.push_str("\n");
51 }
52
53 out.push_str("MEMORY\n{\n");
54 for section in &self.sections {
55 out.push_str(§ion.to_string());
56 }
57 out.push_str("}\n");
58 out
59 }
60
61 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
62 std::fs::write(path, self.to_string())
63 }
64
65 #[cfg(feature = "build-rs")]
66 pub fn to_cargo_outdir(&self, filename: &str) -> std::io::Result<()> {
67 use std::path::PathBuf;
68
69 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
70 self.to_file(out.join(filename))?;
71
72 println!("cargo:rustc-link-search={}", out.display());
73 Ok(())
74 }
75}
76
77pub struct MemorySection {
78 name: String,
79 attrs: Option<String>,
80 origin: u64,
81 length: u64,
82 pagesize: u64,
83}
84
85impl MemorySection {
86 pub fn new(name: &str, origin: u64, length: u64) -> MemorySection {
87 Self {
88 name: name.into(),
89 origin,
90 length,
91 attrs: None,
92 pagesize: 1,
93 }
94 }
95
96 pub fn offset(self, offset: u64) -> MemorySection {
97 Self {
98 name: self.name,
99 origin: self.origin + offset,
100 length: self.length - offset,
101 attrs: self.attrs,
102 pagesize: self.pagesize,
103 }
104 }
105
106 pub fn pagesize(self, pagesize: u64) -> MemorySection {
107 Self {
108 name: self.name,
109 origin: self.origin,
110 length: self.length,
111 attrs: self.attrs,
112 pagesize,
113 }
114 }
115
116 pub fn slot(self, slot: usize, num_slots: usize) -> MemorySection {
123 assert!(slot < num_slots);
124
125 fn align_add(val: u64, alignment: u64) -> u64 {
126 if val % alignment != 0 {
127 (val + alignment) - val % alignment
128 } else {
129 val
130 }
131 }
132
133 fn align_sub(mut val: u64, alignment: u64) -> u64 {
134 val -= val % alignment;
135 val
136 }
137
138 let origin = align_add(self.origin, self.pagesize);
140 let end = align_sub(self.origin + self.length, self.pagesize);
141
142 let slot_length = align_sub((end - origin) / num_slots as u64, self.pagesize);
143 let slot_origin = origin + (slot as u64 * slot_length);
144
145 Self {
146 name: self.name,
147 origin: slot_origin,
148 length: slot_length,
149 attrs: self.attrs,
150 pagesize: self.pagesize,
151 }
152 }
153
154 pub fn from_env(self) -> MemorySection {
181 self.from_env_with_prefix("LDMEMORY")
182 }
183
184 pub fn from_env_with_prefix(self, prefix: &str) -> MemorySection {
188 use std::env::var;
189 let offset_env = &[prefix, "OFFSET"].join("_");
190 let num_slots_env = &[prefix, "NUM_SLOTS"].join("_");
191 let slot_env = &[prefix, "SLOT"].join("_");
192 let pagesize_env = &[prefix, "PAGESIZE"].join("_");
193 let slot_offset_env = &[prefix, "SLOT_OFFSET"].join("_");
194
195 let mut res = self;
196 if let Ok(offset) = var(offset_env) {
197 let offset = offset
198 .parse_dec_or_hex()
199 .expect(&format!("parsing {}", &offset_env));
200 res = res.offset(offset);
201 }
202
203 if let Ok(pagesize) = var(pagesize_env) {
204 let pagesize = pagesize
205 .parse_dec_or_hex()
206 .expect(&format!("parsing {}", &pagesize_env));
207 res = res.pagesize(pagesize);
208 }
209
210 if let Ok(slot) = var(slot_env) {
211 let slot: usize = slot
212 .parse::<usize>()
213 .expect(&format!("parsing {}", slot_env));
214 let num_slots: usize = var(num_slots_env)
215 .unwrap_or("2".into())
216 .parse()
217 .expect(&format!("parsing {}", &num_slots_env));
218 let slot_offset = var(slot_offset_env)
219 .unwrap_or("0".into())
220 .parse_dec_or_hex()
221 .expect(&format!("parsing {}", &slot_offset_env));
222
223 res = res.slot(slot, num_slots);
224
225 if slot_offset > 0 {
226 res = res.offset(slot_offset);
227 }
228 }
229
230 if env::var("CARGO").is_ok() && env::var("OUT_DIR").is_ok() {
235 for var in [
236 offset_env,
237 num_slots_env,
238 slot_env,
239 slot_offset_env,
240 pagesize_env,
241 ]
242 .iter()
243 {
244 println!("cargo:rerun-if-env-changed={}", var);
245 }
246 }
247 res
248 }
249
250 pub fn attrs(self, attrs: &str) -> MemorySection {
251 Self {
252 name: self.name,
253 origin: self.origin,
254 length: self.length,
255 attrs: Some(attrs.into()),
256 pagesize: self.pagesize,
257 }
258 }
259
260 pub fn to_string(&self) -> String {
261 format!(
262 " {} {}: ORIGIN = {:#X}, LENGTH = {:#X}\n",
263 self.name,
264 self.attrs
265 .as_ref()
266 .map_or_else(|| "".to_string(), |attrs| format!("({})", attrs)),
267 self.origin,
268 self.length
269 )
270 }
271}
272
273pub trait ParseDecOrHex {
275 fn parse_dec_or_hex(&self) -> Result<u64, ParseIntError>;
276}
277
278impl ParseDecOrHex for str {
279 fn parse_dec_or_hex(&self) -> Result<u64, ParseIntError> {
280 if self.starts_with("0x") {
281 u64::from_str_radix(&self[2..], 16)
282 } else {
283 u64::from_str_radix(self, 10)
284 }
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::{Memory, MemorySection};
291 #[test]
292 fn basic_memory() {
293 let memory = Memory::new();
294 assert_eq!(memory.to_string(), "MEMORY\n{\n}\n");
295 }
296
297 #[test]
298 fn basic_section() {
299 let section = MemorySection::new("SectionName", 0, 0xFFFF);
300 assert_eq!(
301 section.to_string(),
302 " SectionName : ORIGIN = 0x0, LENGTH = 0xFFFF\n"
303 );
304 }
305
306 #[test]
307 fn section_offset() {
308 let section = MemorySection::new("SectionName", 0, 0x10000).offset(0x1000);
309 assert_eq!(
310 section.to_string(),
311 " SectionName : ORIGIN = 0x1000, LENGTH = 0xF000\n"
312 );
313 }
314
315 #[test]
316 fn section_attrs() {
317 let section = MemorySection::new("SectionName", 0, 0x10000).attrs("r!w!x");
318 assert_eq!(
319 section.to_string(),
320 " SectionName (r!w!x): ORIGIN = 0x0, LENGTH = 0x10000\n"
321 );
322 }
323
324 #[test]
325 fn complex() {
326 let memory = Memory::new().add_section(
327 MemorySection::new("SectionName", 0, 0x10000)
328 .offset(0x1000)
329 .attrs("rw!x"),
330 );
331
332 assert_eq!(
333 memory.to_string(),
334 concat!(
335 "_SectionName_start = 0x1000;\n",
336 "_SectionName_length = 0xF000;\n",
337 "\n",
338 "MEMORY\n{\n",
339 " SectionName (rw!x): ORIGIN = 0x1000, LENGTH = 0xF000\n",
340 "}\n"
341 )
342 );
343 }
344}