blob: 77e67c7b5ab263f038218afe9d19830d3074400a [file] [log] [blame]
Austin Schuh208337d2022-01-01 14:29:11 -08001/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <cstdio>
8#include <map>
Ravago Jonesd208ae72023-02-13 02:24:07 -08009#include <set>
Austin Schuh208337d2022-01-01 14:29:11 -080010#include <vector>
11#include <cstring>
12#include <cstdarg>
13#include <algorithm>
14#include "boot/uf2.h"
15#include "elf.h"
16
17typedef unsigned int uint;
18
19#define ERROR_ARGS -1
20#define ERROR_FORMAT -2
21#define ERROR_INCOMPATIBLE -3
22#define ERROR_READ_FAILED -4
23#define ERROR_WRITE_FAILED -5
24
Ravago Jonesd208ae72023-02-13 02:24:07 -080025#define FLASH_SECTOR_ERASE_SIZE 4096u
26
Austin Schuh208337d2022-01-01 14:29:11 -080027static char error_msg[512];
28static bool verbose;
29
30static int fail(int code, const char *format, ...) {
31 va_list args;
32 va_start(args, format);
33 vsnprintf(error_msg, sizeof(error_msg), format, args);
34 va_end(args);
35 return code;
36}
37
38static int fail_read_error() {
39 return fail(ERROR_READ_FAILED, "Failed to read input file");
40}
41
42static int fail_write_error() {
43 return fail(ERROR_WRITE_FAILED, "Failed to write output file");
44}
45
46// we require 256 (as this is the page size supported by the device)
47#define LOG2_PAGE_SIZE 8u
48#define PAGE_SIZE (1u << LOG2_PAGE_SIZE)
49
50struct address_range {
51 enum type {
52 CONTENTS, // may have contents
53 NO_CONTENTS, // must be uninitialized
54 IGNORE // will be ignored
55 };
56 address_range(uint32_t from, uint32_t to, type type) : from(from), to(to), type(type) {}
57 address_range() : address_range(0, 0, IGNORE) {}
58 type type;
59 uint32_t to;
60 uint32_t from;
61};
62
63typedef std::vector<address_range> address_ranges;
64
Ravago Jonesd208ae72023-02-13 02:24:07 -080065#define MAIN_RAM_START 0x20000000u // same as SRAM_BASE in addressmap.h
66#define MAIN_RAM_END 0x20042000u // same as SRAM_END in addressmap.h
67#define FLASH_START 0x10000000u // same as XIP_MAIN_BASE in addressmap.h
Austin Schuh208337d2022-01-01 14:29:11 -080068#define FLASH_END 0x15000000u
Ravago Jonesd208ae72023-02-13 02:24:07 -080069#define XIP_SRAM_START 0x15000000u // same as XIP_SRAM_BASE in addressmap.h
70#define XIP_SRAM_END 0x15004000u // same as XIP_SRAM_END in addressmap.h
71#define MAIN_RAM_BANKED_START 0x21000000u // same as SRAM0_BASE in addressmap.h
Austin Schuh208337d2022-01-01 14:29:11 -080072#define MAIN_RAM_BANKED_END 0x21040000u
Ravago Jonesd208ae72023-02-13 02:24:07 -080073#define ROM_START 0x00000000u // same as ROM_BASE in addressmap.h
74#define ROM_END 0x00004000u
Austin Schuh208337d2022-01-01 14:29:11 -080075
76const address_ranges rp2040_address_ranges_flash {
77 address_range(FLASH_START, FLASH_END, address_range::type::CONTENTS),
78 address_range(MAIN_RAM_START, MAIN_RAM_END, address_range::type::NO_CONTENTS),
79 address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
80};
81
82const address_ranges rp2040_address_ranges_ram {
83 address_range(MAIN_RAM_START, MAIN_RAM_END, address_range::type::CONTENTS),
84 address_range(XIP_SRAM_START, XIP_SRAM_END, address_range::type::CONTENTS),
Ravago Jonesd208ae72023-02-13 02:24:07 -080085 address_range(ROM_START, ROM_END, address_range::type::IGNORE) // for now we ignore the bootrom if present
Austin Schuh208337d2022-01-01 14:29:11 -080086};
87
88struct page_fragment {
89 page_fragment(uint32_t file_offset, uint32_t page_offset, uint32_t bytes) : file_offset(file_offset), page_offset(page_offset), bytes(bytes) {}
90 uint32_t file_offset;
91 uint32_t page_offset;
92 uint32_t bytes;
93};
94
95static int usage() {
96 fprintf(stderr, "Usage: elf2uf2 (-v) <input ELF file> <output UF2 file>\n");
97 return ERROR_ARGS;
98}
99
100static int read_and_check_elf32_header(FILE *in, elf32_header& eh_out) {
101 if (1 != fread(&eh_out, sizeof(eh_out), 1, in)) {
102 return fail(ERROR_READ_FAILED, "Unable to read ELF header");
103 }
104 if (eh_out.common.magic != ELF_MAGIC) {
105 return fail(ERROR_FORMAT, "Not an ELF file");
106 }
107 if (eh_out.common.version != 1 || eh_out.common.version2 != 1) {
108 return fail(ERROR_FORMAT, "Unrecognized ELF version");
109 }
110 if (eh_out.common.arch_class != 1 || eh_out.common.endianness != 1) {
111 return fail(ERROR_INCOMPATIBLE, "Require 32 bit little-endian ELF");
112 }
113 if (eh_out.eh_size != sizeof(struct elf32_header)) {
114 return fail(ERROR_FORMAT, "Invalid ELF32 format");
115 }
116 if (eh_out.common.machine != EM_ARM) {
117 return fail(ERROR_FORMAT, "Not an ARM executable");
118 }
119 if (eh_out.common.abi != 0) {
120 return fail(ERROR_INCOMPATIBLE, "Unrecognized ABI");
121 }
122 if (eh_out.flags & EF_ARM_ABI_FLOAT_HARD) {
123 return fail(ERROR_INCOMPATIBLE, "HARD-FLOAT not supported");
124 }
125 return 0;
126}
127
128int check_address_range(const address_ranges& valid_ranges, uint32_t addr, uint32_t vaddr, uint32_t size, bool uninitialized, address_range &ar) {
129 for(const auto& range : valid_ranges) {
130 if (range.from <= addr && range.to >= addr + size) {
131 if (range.type == address_range::type::NO_CONTENTS && !uninitialized) {
132 return fail(ERROR_INCOMPATIBLE, "ELF contains memory contents for uninitialized memory");
133 }
134 ar = range;
135 if (verbose) {
136 printf("%s segment %08x->%08x (%08x->%08x)\n", uninitialized ? "Uninitialized" : "Mapped", addr,
137 addr + size, vaddr, vaddr+size);
138 }
139 return 0;
140 }
141 }
142 return fail(ERROR_INCOMPATIBLE, "Memory segment %08x->%08x is outside of valid address range for device", addr, addr+size);
143}
144
145int read_and_check_elf32_ph_entries(FILE *in, const elf32_header &eh, const address_ranges& valid_ranges, std::map<uint32_t, std::vector<page_fragment>>& pages) {
146 if (eh.ph_entry_size != sizeof(elf32_ph_entry)) {
147 return fail(ERROR_FORMAT, "Invalid ELF32 program header");
148 }
149 if (eh.ph_num) {
150 std::vector<elf32_ph_entry> entries(eh.ph_num);
151 if (fseek(in, eh.ph_offset, SEEK_SET)) {
152 return fail_read_error();
153 }
154 if (eh.ph_num != fread(&entries[0], sizeof(struct elf32_ph_entry), eh.ph_num, in)) {
155 return fail_read_error();
156 }
157 for(uint i=0;i<eh.ph_num;i++) {
158 elf32_ph_entry& entry = entries[i];
159 if (entry.type == PT_LOAD && entry.memsz) {
160 address_range ar;
161 int rc;
162 uint mapped_size = std::min(entry.filez, entry.memsz);
163 if (mapped_size) {
164 rc = check_address_range(valid_ranges, entry.paddr, entry.vaddr, mapped_size, false, ar);
165 if (rc) return rc;
166 // we don't download uninitialized, generally it is BSS and should be zero-ed by crt0.S, or it may be COPY areas which are undefined
167 if (ar.type != address_range::type::CONTENTS) {
168 if (verbose) printf(" ignored\n");
169 continue;
170 }
171 uint addr = entry.paddr;
172 uint remaining = mapped_size;
173 uint file_offset = entry.offset;
174 while (remaining) {
175 uint off = addr & (PAGE_SIZE - 1);
176 uint len = std::min(remaining, PAGE_SIZE - off);
177 auto &fragments = pages[addr - off]; // list of fragments
178 // note if filesz is zero, we want zero init which is handled because the
179 // statement above creates an empty page fragment list
180 // check overlap with any existing fragments
181 for (const auto &fragment : fragments) {
182 if ((off < fragment.page_offset + fragment.bytes) !=
183 ((off + len) <= fragment.page_offset)) {
184 fail(ERROR_FORMAT, "In memory segments overlap");
185 }
186 }
187 fragments.push_back(
188 page_fragment{file_offset,off,len});
189 addr += len;
190 file_offset += len;
191 remaining -= len;
192 }
193 }
194 if (entry.memsz > entry.filez) {
195 // we have some uninitialized data too
196 rc = check_address_range(valid_ranges, entry.paddr + entry.filez, entry.vaddr + entry.filez, entry.memsz - entry.filez, true,
197 ar);
198 if (rc) return rc;
199 }
200 }
201 }
202 }
203 return 0;
204}
205
206int realize_page(FILE *in, const std::vector<page_fragment> &fragments, uint8_t *buf, uint buf_len) {
207 assert(buf_len >= PAGE_SIZE);
208 for(auto& frag : fragments) {
209 assert(frag.page_offset >= 0 && frag.page_offset < PAGE_SIZE && frag.page_offset + frag.bytes <= PAGE_SIZE);
210 if (fseek(in, frag.file_offset, SEEK_SET)) {
211 return fail_read_error();
212 }
213 if (1 != fread(buf + frag.page_offset, frag.bytes, 1, in)) {
214 return fail_read_error();
215 }
216 }
217 return 0;
218}
219
220static bool is_address_valid(const address_ranges& valid_ranges, uint32_t addr) {
221 for(const auto& range : valid_ranges) {
222 if (range.from <= addr && range.to > addr) {
223 return true;
224 }
225 }
226 return false;
227}
228
229static bool is_address_initialized(const address_ranges& valid_ranges, uint32_t addr) {
230 for(const auto& range : valid_ranges) {
231 if (range.from <= addr && range.to > addr) {
232 return address_range::type::CONTENTS == range.type;
233 }
234 }
235 return false;
236}
237
238static bool is_address_mapped(const std::map<uint32_t, std::vector<page_fragment>>& pages, uint32_t addr) {
239 uint32_t page = addr & ~(PAGE_SIZE - 1);
240 if (!pages.count(page)) return false;
241 // todo check actual address within page
242 return true;
243}
244
245int elf2uf2(FILE *in, FILE *out) {
246 elf32_header eh;
247 std::map<uint32_t, std::vector<page_fragment>> pages;
248 int rc = read_and_check_elf32_header(in, eh);
249 bool ram_style = false;
250 address_ranges valid_ranges = {};
251 if (!rc) {
252 ram_style = is_address_initialized(rp2040_address_ranges_ram, eh.entry);
253 if (verbose) {
254 if (ram_style) {
255 printf("Detected RAM binary\n");
256 } else {
257 printf("Detected FLASH binary\n");
258 }
259 }
260 valid_ranges = ram_style ? rp2040_address_ranges_ram : rp2040_address_ranges_flash;
261 rc = read_and_check_elf32_ph_entries(in, eh, valid_ranges, pages);
262 }
263 if (rc) return rc;
264 if (pages.empty()) {
265 return fail(ERROR_INCOMPATIBLE, "The input file has no memory pages");
266 }
267 uint page_num = 0;
268 if (ram_style) {
269 uint32_t expected_ep_main_ram = UINT32_MAX;
270 uint32_t expected_ep_xip_sram = UINT32_MAX;
271 for(auto& page_entry : pages) {
272 if ( ((page_entry.first >= MAIN_RAM_START) && (page_entry.first < MAIN_RAM_END)) && (page_entry.first < expected_ep_main_ram) ) {
273 expected_ep_main_ram = page_entry.first | 0x1;
274 } else if ( ((page_entry.first >= XIP_SRAM_START) && (page_entry.first < XIP_SRAM_END)) && (page_entry.first < expected_ep_xip_sram) ) {
275 expected_ep_xip_sram = page_entry.first | 0x1;
276 }
277 }
278 uint32_t expected_ep = (UINT32_MAX != expected_ep_main_ram) ? expected_ep_main_ram : expected_ep_xip_sram;
279 if (eh.entry == expected_ep_xip_sram) {
280 return fail(ERROR_INCOMPATIBLE, "B0/B1 Boot ROM does not support direct entry into XIP_SRAM\n");
281 } else if (eh.entry != expected_ep) {
282 return fail(ERROR_INCOMPATIBLE, "A RAM binary should have an entry point at the beginning: %08x (not %08x)\n", expected_ep, eh.entry);
283 }
284 static_assert(0 == (MAIN_RAM_START & (PAGE_SIZE - 1)), "");
285 // currently don't require this as entry point is now at the start, we don't know where reset vector is
286#if 0
287 uint8_t buf[PAGE_SIZE];
288 rc = realize_page(in, pages[MAIN_RAM_START], buf, sizeof(buf));
289 if (rc) return rc;
290 uint32_t sp = ((uint32_t *)buf)[0];
291 uint32_t ip = ((uint32_t *)buf)[1];
292 if (!is_address_mapped(pages, ip)) {
293 return fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: reset vector %08x is not in mapped memory",
294 MAIN_RAM_START, ip);
295 }
296 if (!is_address_valid(valid_ranges, sp - 4)) {
297 return fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: stack pointer %08x is not in RAM",
298 MAIN_RAM_START, sp);
299 }
300#endif
Ravago Jonesd208ae72023-02-13 02:24:07 -0800301 } else {
302 // Fill in empty dummy uf2 pages to align the binary to flash sectors (except for the last sector which we don't
303 // need to pad, and choose not to to avoid making all SDK UF2s bigger)
304 // That workaround is required because the bootrom uses the block number for erase sector calculations:
305 // https://github.com/raspberrypi/pico-bootrom/blob/c09c7f08550e8a36fc38dc74f8873b9576de99eb/bootrom/virtual_disk.c#L205
306
307 std::set<uint32_t> touched_sectors;
308 for (auto& page_entry : pages) {
309 uint32_t sector = page_entry.first / FLASH_SECTOR_ERASE_SIZE;
310 touched_sectors.insert(sector);
311 }
312
313 uint32_t last_page = pages.rbegin()->first;
314 for (uint32_t sector : touched_sectors) {
315 for (uint32_t page = sector * FLASH_SECTOR_ERASE_SIZE; page < (sector + 1) * FLASH_SECTOR_ERASE_SIZE; page += PAGE_SIZE) {
316 if (page < last_page) {
317 // Create a dummy page, if it does not exist yet. note that all present pages are first
318 // zeroed before they are filled with any contents, so a dummy page will be all zeros.
319 auto &dummy = pages[page];
320 }
321 }
322 }
Austin Schuh208337d2022-01-01 14:29:11 -0800323 }
324 uf2_block block;
325 block.magic_start0 = UF2_MAGIC_START0;
326 block.magic_start1 = UF2_MAGIC_START1;
327 block.flags = UF2_FLAG_FAMILY_ID_PRESENT;
328 block.payload_size = PAGE_SIZE;
329 block.num_blocks = (uint32_t)pages.size();
330 block.file_size = RP2040_FAMILY_ID;
331 block.magic_end = UF2_MAGIC_END;
332 for(auto& page_entry : pages) {
333 block.target_addr = page_entry.first;
334 block.block_no = page_num++;
335 if (verbose) {
Ravago Jonesd208ae72023-02-13 02:24:07 -0800336 printf("Page %d / %d %08x%s\n", block.block_no, block.num_blocks, block.target_addr,
337 page_entry.second.empty() ? " (padding)": "");
Austin Schuh208337d2022-01-01 14:29:11 -0800338 }
339 memset(block.data, 0, sizeof(block.data));
340 rc = realize_page(in, page_entry.second, block.data, sizeof(block.data));
341 if (rc) return rc;
342 if (1 != fwrite(&block, sizeof(uf2_block), 1, out)) {
343 return fail_write_error();
344 }
345 }
346 return 0;
347}
348
349int main(int argc, char **argv) {
350 int arg = 1;
351 if (arg < argc && !strcmp(argv[arg], "-v")) {
352 verbose = true;
353 arg++;
354 }
355 if (argc < arg + 2) {
356 return usage();
357 }
358 const char *in_filename = argv[arg++];
359 FILE *in = fopen(in_filename, "rb");
360 if (!in) {
361 fprintf(stderr, "Can't open input file '%s'\n", in_filename);
362 return ERROR_ARGS;
363 }
364 const char *out_filename = argv[arg++];
365 FILE *out = fopen(out_filename, "wb");
366 if (!out) {
367 fprintf(stderr, "Can't open output file '%s'\n", out_filename);
368 return ERROR_ARGS;
369 }
370
371 int rc = elf2uf2(in, out);
372 fclose(in);
373 fclose(out);
374 if (rc) {
375 remove(out_filename);
376 if (error_msg[0]) {
377 fprintf(stderr, "ERROR: %s\n", error_msg);
378 }
379 }
380 return rc;
381}