Skip to content

Commit

Permalink
enhance: use start address from ELF and ihex file
Browse files Browse the repository at this point in the history
Close #43
Close #40
  • Loading branch information
andelf committed Jan 8, 2024
1 parent 6be3156 commit 8ee7510
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 58 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `-o/--out` for `dump` sumcommand, #38
- BREAKING CHANGE: Refactor
- Add Windows native driver support, #39
- Use loaded memory address from ELF file or ihex file

### Changed

Expand Down
152 changes: 106 additions & 46 deletions src/firmware.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Firmware file formats
use std::path::Path;
use std::str;
use std::{borrow::Cow, path::Path};

use anyhow::Result;
use object::{
Expand All @@ -16,25 +16,89 @@ pub enum FirmwareFormat {
Binary,
}

pub fn read_firmware_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
#[derive(Debug, Clone)]
pub struct Section {
/// The start address of the segment, physical address.
pub address: u32,
pub data: Vec<u8>,
}

impl Section {
pub fn end_address(&self) -> u32 {
self.address + self.data.len() as u32
}
}

/// The abstract representation of a firmware image.
#[derive(Debug, Clone)]
pub enum Firmware {
/// A single section, with address unddefined.
Binary(Vec<u8>),
/// Multiple sections, with different addresses.
Sections(Vec<Section>),
}

impl Firmware {
/// Merge sections w/ <= 256 bytes gap
pub fn merge_sections(self) -> Result<Self> {
if let Firmware::Sections(mut sections) = self {
sections.sort_by_key(|s| s.address);
let mut merged = vec![];

let mut it = sections.drain(0..);
let mut last = it
.next()
.expect("firmware must has at least one section; qed");
for sect in it {
if let Some(gap) = sect.address.checked_sub(last.end_address()) {
if gap > 256 {
merged.push(last);
last = sect.clone();
continue;
} else {
last.data.resize(last.data.len() + gap as usize, 0);
last.data.extend_from_slice(&sect.data);
}
} else {
return Err(anyhow::format_err!(
"section address overflow: {:#010x} + {:#x}",
last.address,
last.data.len()
));
}
}
merged.push(last);
Ok(Firmware::Sections(merged))
} else {
Ok(self)
}
}
}

pub fn read_firmware_from_file<P: AsRef<Path>>(path: P) -> Result<Firmware> {
let p = path.as_ref();
let raw = std::fs::read(p)?;

let format = guess_format(p, &raw);
log::info!("Read {} as {:?} format", p.display(), format);
match format {
FirmwareFormat::PlainHex => Ok(hex::decode(
raw.into_iter()
.filter(|&c| c != b'\r' || c != b'\n')
.collect::<Vec<u8>>(),
)?),
FirmwareFormat::IntelHex => Ok(read_ihex(str::from_utf8(&raw)?)?),
FirmwareFormat::ELF => Ok(objcopy_binary(&raw)?),
FirmwareFormat::Binary => Ok(raw),
FirmwareFormat::PlainHex => {
let raw = hex::decode(
raw.into_iter()
.filter(|&c| c != b'\r' || c != b'\n')
.collect::<Vec<u8>>(),
)?;
Ok(Firmware::Binary(raw))
}
FirmwareFormat::Binary => Ok(Firmware::Binary(raw)),
FirmwareFormat::IntelHex => {
read_ihex(str::from_utf8(&raw)?).and_then(|f| f.merge_sections())
}
FirmwareFormat::ELF => read_elf(&raw).and_then(|f| f.merge_sections()),
}
}

pub fn guess_format(path: &Path, raw: &[u8]) -> FirmwareFormat {
fn guess_format(path: &Path, raw: &[u8]) -> FirmwareFormat {
let ext = path
.extension()
.map(|s| s.to_string_lossy())
Expand Down Expand Up @@ -67,36 +131,52 @@ pub fn read_hex(data: &str) -> Result<Vec<u8>> {
Ok(hex::decode(data)?)
}

pub fn read_ihex(data: &str) -> Result<Vec<u8>> {
pub fn read_ihex(data: &str) -> Result<Firmware> {
use ihex::Record::*;

let mut base_address = 0;

let mut records = vec![];
let mut segs: Vec<Section> = vec![];
let mut last_end_address = 0;
for record in ihex::Reader::new(data) {
let record = record?;
match record {
Data { offset, value } => {
let offset = base_address + offset as u32;
let start_address = base_address + offset as u32;

records.push((offset, value.into()));
if let Some(last) = segs.last_mut() {
if start_address == last_end_address {
// merge to last
last_end_address = start_address + value.len() as u32;
last.data.extend_from_slice(&value);

continue;
}
}

last_end_address = start_address + value.len() as u32;
segs.push(Section {
address: start_address,
data: value.to_vec(),
})
}
EndOfFile => (),
ExtendedSegmentAddress(address) => {
base_address = (address as u32) * 16;
}
StartSegmentAddress { .. } => (),
ExtendedLinearAddress(address) => {
base_address = (address as u32) << 16;
}
StartSegmentAddress { .. } => (),
StartLinearAddress(_) => (),
EndOfFile => (),
};
}
merge_sections(records)

Ok(Firmware::Sections(segs))
}

/// Simulates `objcopy -O binary`.
pub fn objcopy_binary(elf_data: &[u8]) -> Result<Vec<u8>> {
/// Simulates `objcopy -O binary`, returns loadable sections
pub fn read_elf(elf_data: &[u8]) -> Result<Firmware> {
let file_kind = object::FileKind::parse(elf_data)?;

match file_kind {
Expand Down Expand Up @@ -157,7 +237,10 @@ pub fn objcopy_binary(elf_data: &[u8]) -> Result<Vec<u8>> {
}
}
let section_data = &elf_data[segment_offset as usize..][..segment_filesize as usize];
sections.push((p_paddr as u32, section_data.into()));
sections.push(Section {
address: p_paddr as u32,
data: section_data.to_vec(),
});
log::debug!("Section names: {:?}", section_names);
}
}
Expand All @@ -166,29 +249,6 @@ pub fn objcopy_binary(elf_data: &[u8]) -> Result<Vec<u8>> {
anyhow::bail!("empty ELF file");
}
log::debug!("found {} sections", sections.len());
merge_sections(sections)
}

fn merge_sections(mut sections: Vec<(u32, Cow<[u8]>)>) -> Result<Vec<u8>> {
sections.sort(); // order by start address

let start_address = sections.first().unwrap().0;
let end_address = sections.last().unwrap().0 + sections.last().unwrap().1.len() as u32;

log::info!(
"Firmware size: {}, start_address: {:#010x}, end_address: {:#010x}",
end_address - start_address,
start_address,
end_address
);
let total_size = end_address - start_address;

let mut binary = vec![0u8; total_size as usize];
// FIXMME: check section overlap?
for (addr, sect) in sections {
let sect_start = (addr - start_address) as usize;
let sect_end = (addr - start_address) as usize + sect.len();
binary[sect_start..sect_end].copy_from_slice(&sect);
}
Ok(binary)
// merge_sections(sections)
Ok(Firmware::Sections(sections))
}
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,16 @@ impl RiscvChip {
}
}

// The same as wch-openocd-riscv
pub fn fix_code_flash_start(&self, start_address: u32) -> u32 {
let addr = self.code_flash_start() + start_address;
if addr >= 0x10000000 {
addr - 0x08000000
} else {
addr
}
}

/// pack size for fastprogram
pub fn write_pack_size(&self) -> u32 {
match self {
Expand Down
45 changes: 33 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ use std::{thread::sleep, time::Duration};

use anyhow::Result;
use wlink::{
commands, dmi::DebugModuleInterface, firmware::read_firmware_from_file,
operations::ProbeSession, probe::WchLink, regs, RiscvChip,
commands,
dmi::DebugModuleInterface,
firmware::{read_firmware_from_file, Firmware},
operations::ProbeSession,
probe::WchLink,
regs, RiscvChip,
};

use clap::{Parser, Subcommand};
Expand Down Expand Up @@ -323,21 +327,38 @@ fn main() -> Result<()> {
} => {
sess.dump_info()?;

let firmware = read_firmware_from_file(path)?;
let start_address =
address.unwrap_or_else(|| sess.chip_family.code_flash_start());
log::info!(
"Flashing {} bytes to 0x{:08x}",
firmware.len(),
start_address
);

if erase {
log::info!("Erase Flash");
sess.erase_flash()?;
}

sess.write_flash(&firmware, start_address)?;
let firmware = read_firmware_from_file(path)?;

match firmware {
Firmware::Binary(data) => {
let start_address =
address.unwrap_or_else(|| sess.chip_family.code_flash_start());
log::info!("Flashing {} bytes to 0x{:08x}", data.len(), start_address);
sess.write_flash(&data, start_address)?;
}
Firmware::Sections(sections) => {
// Flash section by section
if address != None {
log::warn!("--address is ignored when flashing ELF or ihex");
}
for section in sections {
let start_address =
sess.chip_family.fix_code_flash_start(section.address);
log::info!(
"Flashing {} bytes to 0x{:08x}",
section.data.len(),
start_address
);
sess.write_flash(&section.data, start_address)?;
}
}
}

log::info!("Flash done");

sleep(Duration::from_millis(500));
Expand Down

0 comments on commit 8ee7510

Please sign in to comment.