use std::{
env,
fs::{self, File},
io::Write,
path::PathBuf,
};
fn main() {
if let Err(env::VarError::NotPresent) = env::var("CARGO_FEATURE_FIRMWARE") {
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
let script_contents = fs::read_to_string("./common.ld").unwrap();
let firmware = get_memory_entry(script_contents.as_str(), "FIRMWARE").unwrap();
let fwupdate = get_memory_entry(script_contents.as_str(), "FWUPDATE").unwrap();
let fwswap = get_memory_entry(script_contents.as_str(), "FWSWAP").unwrap();
File::create(out.join("memory_map.rs"))
.unwrap()
.write_all(
format!(
"
#[allow(non_upper_case_globals)]
const __header_start__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __header_end__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __firmware_start__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __firmware_end__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __fwupdate_start__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __fwupdate_end__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __fwswap_start__: u32 = {:#x?};
#[allow(non_upper_case_globals)]
const __fwswap_end__: u32 = {:#x?};",
firmware.origin,
firmware.origin + 0x400,
firmware.origin,
firmware.end(),
fwupdate.origin,
fwupdate.end(),
fwswap.origin,
fwswap.end(),
)
.as_bytes(),
)
.unwrap();
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct MemoryEntry {
line: usize,
origin: u64,
length: u64,
}
impl MemoryEntry {
fn end(&self) -> u64 {
self.origin + self.length
}
}
macro_rules! eat {
($line:expr, $token:expr) => {
if let Some(a) = $line.strip_prefix($token) {
a.trim()
} else {
continue;
}
};
($line:expr, $token:expr, $alt_token:expr) => {
if let Some(a) = $line.strip_prefix($token) {
a.trim()
} else if let Some(a) = $line.strip_prefix($alt_token) {
a.trim()
} else {
continue;
}
};
}
fn get_memory_entry(linker_script: &str, name: &str) -> Option<MemoryEntry> {
macro_rules! tryc {
($expr:expr) => {
if let Ok(x) = $expr {
x
} else {
continue;
}
};
}
for (index, mut line) in linker_script.lines().enumerate() {
line = line.trim();
line = eat!(line, name);
println!("{:?}", line);
if let Some(i) = line.find(':') {
line = line[i..].trim();
}
line = eat!(line, ":");
line = eat!(line, "ORIGIN", "org");
line = eat!(line, "=");
let boundary_pos = tryc!(line.find(|c| c == ',' || c == ' ').ok_or(()));
const HEX: &str = "0x";
let origin = if line.starts_with(HEX) {
tryc!(u64::from_str_radix(&line[HEX.len()..boundary_pos], 16))
} else {
tryc!(line[..boundary_pos].parse())
};
line = &line[boundary_pos..].trim();
line = eat!(line, ",");
line = eat!(line, "LENGTH", "len");
line = eat!(line, "=");
let segments: Vec<&str> = line.split('+').map(|s| s.trim().trim_end()).collect();
let mut total_length = 0;
for segment in segments {
let boundary_pos = segment
.find(|c| c == 'K' || c == 'M' || c == 'k' || c == 'm')
.unwrap_or_else(|| segment.len());
let length: u64 = tryc!(segment[..boundary_pos].parse());
let raw = &segment[boundary_pos..];
let mut chars = raw.chars();
let unit = chars.next();
if unit == Some('K') || unit == Some('k') {
total_length += length * 1024;
} else if unit == Some('M') || unit == Some('m') {
total_length += length * 1024 * 1024;
} else if unit == None {
total_length += length;
}
}
return Some(MemoryEntry {
line: index,
origin,
length: total_length,
});
}
None
}