1use std::{
2 collections::HashSet,
3 io::{Cursor, Write},
4};
5
6use ::elf::{ElfBytes, ParseError, endian::AnyEndian};
7use assert_into::AssertInto;
8use log::debug;
9use thiserror::Error;
10use zerocopy::IntoBytes;
11
12use crate::{
13 address_range::AddressRangesFromElfError,
14 boards::BoardInfo,
15 elf::{get_page_fragments, realize_page},
16 uf2::{
17 UF2_FLAG_FAMILY_ID_PRESENT, UF2_MAGIC_END, UF2_MAGIC_START0, UF2_MAGIC_START1,
18 Uf2BlockData, Uf2BlockFooter, Uf2BlockHeader,
19 },
20};
21
22pub mod address_range;
23pub mod boards;
24pub mod elf;
25pub mod uf2;
26
27pub trait ProgressReporter {
28 fn start(&mut self, total_bytes: usize);
29 fn advance(&mut self, bytes: usize);
30 fn finish(&mut self);
31}
32
33pub struct NoProgress;
34impl ProgressReporter for NoProgress {
35 fn start(&mut self, _total_bytes: usize) {}
36 fn advance(&mut self, _bytes: usize) {}
37 fn finish(&mut self) {}
38}
39
40#[derive(Error, Debug)]
41pub enum Elf2Uf2Error {
42 #[error("Failed to get address ranges from elf")]
43 AddressRangesError(#[from] AddressRangesFromElfError),
44 #[error("Failed to parse elf file")]
45 ElfParseError(#[from] ParseError),
46 #[error("Failed to realize pages")]
47 RealizePageError(#[from] std::io::Error),
48 #[error("The input file has no memory pages")]
49 InputFileNoMemoryPagesError,
50}
51
52pub fn elf2uf2(
68 input: impl AsRef<[u8]>,
69 mut output: impl Write,
70 board: &dyn BoardInfo,
71 mut reporter: impl ProgressReporter,
72) -> Result<(), Elf2Uf2Error> {
73 let input = input.as_ref();
74 let file = ElfBytes::<AnyEndian>::minimal_parse(input)?;
75
76 let page_size = board.page_size();
77 let flash_sector_erase_size = board.flash_sector_erase_size();
78 let family_id = board.family_id();
79
80 let mut pages = get_page_fragments(&file, page_size)?;
81
82 if pages.is_empty() {
83 return Err(Elf2Uf2Error::InputFileNoMemoryPagesError);
84 }
85
86 let touched_sectors: HashSet<u64> = pages
87 .keys()
88 .map(|addr| addr / flash_sector_erase_size)
89 .collect();
90
91 let last_page_addr = *pages
92 .last_key_value()
93 .expect("Impossible error occurred since pages is garunteed to have a last page")
94 .0;
95 for sector in touched_sectors {
96 let mut page = sector * flash_sector_erase_size;
97
98 while page < (sector + 1) * flash_sector_erase_size {
99 if page < last_page_addr && !pages.contains_key(&page) {
100 pages.insert(page, Vec::new());
101 }
102 page += page_size as u64;
103 }
104 }
105
106 let mut block_header = Uf2BlockHeader {
107 magic_start0: UF2_MAGIC_START0,
108 magic_start1: UF2_MAGIC_START1,
109 flags: UF2_FLAG_FAMILY_ID_PRESENT,
110 target_addr: 0,
111 payload_size: page_size,
112 block_no: 0,
113 num_blocks: pages.len() as u32,
114 file_size: family_id,
115 };
116
117 let mut block_data: Uf2BlockData = [0; 476];
118
119 let block_footer = Uf2BlockFooter {
120 magic_end: UF2_MAGIC_END,
121 };
122
123 log::debug!("Writing program");
124
125 reporter.start(pages.len() * 512);
126
127 let last_page_num = pages.len() - 1;
128
129 for (page_num, (target_addr, fragments)) in pages.into_iter().enumerate() {
130 block_header.target_addr = target_addr as u32;
131 block_header.block_no = page_num.assert_into();
132
133 debug!(
134 "Page {} / {} {:#08x}",
135 block_header.block_no as u32,
136 block_header.num_blocks as u32,
137 block_header.target_addr as u32
138 );
139
140 block_data.iter_mut().for_each(|v| *v = 0);
141
142 realize_page(
143 &mut Cursor::new(input),
144 &fragments,
145 &mut block_data,
146 page_size,
147 )?;
148
149 output.write_all(block_header.as_bytes())?;
150 output.write_all(block_data.as_bytes())?;
151 output.write_all(block_footer.as_bytes())?;
152
153 if page_num != last_page_num {
154 reporter.advance(512);
155 }
156 }
157
158 drop(output);
160
161 reporter.advance(512);
162
163 reporter.finish();
164
165 Ok(())
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 pub fn hello_usb() {
174 log::set_max_level(log::LevelFilter::Debug);
175 let bytes_in = &include_bytes!("../tests/rp2040/hello_usb.elf")[..];
176 let mut bytes_out = Vec::new();
177 let board = boards::RP2040::default();
178 elf2uf2(bytes_in, &mut bytes_out, &board, NoProgress).unwrap();
179
180 assert_eq!(bytes_out, include_bytes!("../tests/rp2040/hello_usb.uf2"));
181 }
182
183 #[test]
184 pub fn hello_serial() {
185 log::set_max_level(log::LevelFilter::Debug);
186 let bytes_in = &include_bytes!("../tests/rp2040/hello_serial.elf")[..];
187 let mut bytes_out = Vec::new();
188 let board = boards::RP2040::default();
189 elf2uf2(bytes_in, &mut bytes_out, &board, NoProgress).unwrap();
190
191 assert_eq!(
192 bytes_out,
193 include_bytes!("../tests/rp2040/hello_serial.uf2")
194 );
195 }
196}