factbird-common 0.2.0

Common files for the Factbird Duo & Bootloader
Documentation
use std::{
    env,
    fs::{self, File},
    io::Write,
    path::PathBuf,
};

fn main() {
    // if feature `firmware` is not enabled
    if let Err(env::VarError::NotPresent) = env::var("CARGO_FEATURE_FIRMWARE") {
        let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
        // TODO: Parse `common.ld`

        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();
    }
}

/// Entry under the `MEMORY` section in a linker script
#[derive(Clone, Copy, Debug, PartialEq)]
struct MemoryEntry {
    line: usize,
    origin: u64,
    length: u64,
}

impl MemoryEntry {
    fn end(&self) -> u64 {
        self.origin + self.length
    }
}

/// Rm `token` from beginning of `line`, else `continue` loop iteration
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);
        // jump over attributes like (xrw) see parse_attributes()
        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
}