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