Init repository
This commit is contained in:
parent
d3ecfe3498
commit
7741f01445
35 changed files with 2067 additions and 90 deletions
1
tools/elf2uf2/.gitignore
vendored
Normal file
1
tools/elf2uf2/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
elf2uf2
|
21
tools/elf2uf2/LICENSE.TXT
Normal file
21
tools/elf2uf2/LICENSE.TXT
Normal file
|
@ -0,0 +1,21 @@
|
|||
Copyright 220 (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
|
||||
disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.0
|
3
tools/elf2uf2/README.md
Normal file
3
tools/elf2uf2/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Raspberry Pi Pico elf2uf2 Utility
|
||||
|
||||
THIS CODE COMES FROM THE FOLLOWING REPOSITORY: https://github.com/rej696/elf2uf2
|
60
tools/elf2uf2/elf.h
Normal file
60
tools/elf2uf2/elf.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _ELF_H
|
||||
#define _ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define ELF_MAGIC 0x464c457fu
|
||||
|
||||
#define EM_ARM 0x28u
|
||||
|
||||
#define EF_ARM_ABI_FLOAT_HARD 0x00000400u
|
||||
|
||||
#define PT_LOAD 0x00000001u
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct elf_header {
|
||||
uint32_t magic;
|
||||
uint8_t arch_class;
|
||||
uint8_t endianness;
|
||||
uint8_t version;
|
||||
uint8_t abi;
|
||||
uint8_t abi_version;
|
||||
uint8_t _pad[7];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version2;
|
||||
};
|
||||
|
||||
struct elf32_header {
|
||||
struct elf_header common;
|
||||
uint32_t entry;
|
||||
uint32_t ph_offset;
|
||||
uint32_t sh_offset;
|
||||
uint32_t flags;
|
||||
uint16_t eh_size;
|
||||
uint16_t ph_entry_size;
|
||||
uint16_t ph_num;
|
||||
uint16_t sh_entry_size;
|
||||
uint16_t sh_num;
|
||||
uint16_t sh_str_index;
|
||||
};
|
||||
|
||||
struct elf32_ph_entry {
|
||||
uint32_t type;
|
||||
uint32_t offset;
|
||||
uint32_t vaddr;
|
||||
uint32_t paddr;
|
||||
uint32_t filez;
|
||||
uint32_t memsz;
|
||||
uint32_t flags;
|
||||
uint32_t align;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
353
tools/elf2uf2/main.cpp
Normal file
353
tools/elf2uf2/main.cpp
Normal file
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
#include "uf2.h"
|
||||
#include "elf.h"
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define ERROR_ARGS -1
|
||||
#define ERROR_FORMAT -2
|
||||
#define ERROR_INCOMPATIBLE -3
|
||||
#define ERROR_READ_FAILED -4
|
||||
#define ERROR_WRITE_FAILED -5
|
||||
|
||||
static char error_msg[512];
|
||||
static bool verbose;
|
||||
|
||||
static int fail(int code, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vsnprintf(error_msg, sizeof(error_msg), format, args);
|
||||
va_end(args);
|
||||
return code;
|
||||
}
|
||||
|
||||
static int fail_read_error() {
|
||||
return fail(ERROR_READ_FAILED, "Failed to read input file");
|
||||
}
|
||||
|
||||
static int fail_write_error() {
|
||||
return fail(ERROR_WRITE_FAILED, "Failed to write output file");
|
||||
}
|
||||
|
||||
// we require 256 (as this is the page size supported by the device)
|
||||
#define LOG2_PAGE_SIZE 8u
|
||||
#define PAGE_SIZE (1u << LOG2_PAGE_SIZE)
|
||||
|
||||
struct address_range {
|
||||
enum type {
|
||||
CONTENTS, // may have contents
|
||||
NO_CONTENTS, // must be uninitialized
|
||||
IGNORE // will be ignored
|
||||
};
|
||||
address_range(uint32_t from, uint32_t to, type type) : from(from), to(to), type(type) {}
|
||||
address_range() : address_range(0, 0, IGNORE) {}
|
||||
type type;
|
||||
uint32_t to;
|
||||
uint32_t from;
|
||||
};
|
||||
|
||||
typedef std::vector<address_range> address_ranges;
|
||||
|
||||
#define MAIN_RAM_START 0x20000000u
|
||||
#define MAIN_RAM_END 0x20042000u
|
||||
#define FLASH_START 0x10000000u
|
||||
#define FLASH_END 0x15000000u
|
||||
#define XIP_SRAM_START 0x15000000u
|
||||
#define XIP_SRAM_END 0x15004000u
|
||||
#define MAIN_RAM_BANKED_START 0x21000000u
|
||||
#define MAIN_RAM_BANKED_END 0x21040000u
|
||||
|
||||
const address_ranges rp2040_address_ranges_flash {
|
||||
address_range(FLASH_START, FLASH_END, address_range::type::CONTENTS),
|
||||
address_range(MAIN_RAM_START, MAIN_RAM_END, address_range::type::NO_CONTENTS),
|
||||
address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS)
|
||||
};
|
||||
|
||||
const address_ranges rp2040_address_ranges_ram {
|
||||
address_range(MAIN_RAM_START, MAIN_RAM_END, address_range::type::CONTENTS),
|
||||
address_range(XIP_SRAM_START, XIP_SRAM_END, address_range::type::CONTENTS),
|
||||
address_range(0x00000000u, 0x00004000u, address_range::type::IGNORE) // for now we ignore the bootrom if present
|
||||
};
|
||||
|
||||
struct page_fragment {
|
||||
page_fragment(uint32_t file_offset, uint32_t page_offset, uint32_t bytes) : file_offset(file_offset), page_offset(page_offset), bytes(bytes) {}
|
||||
uint32_t file_offset;
|
||||
uint32_t page_offset;
|
||||
uint32_t bytes;
|
||||
};
|
||||
|
||||
static int usage() {
|
||||
fprintf(stderr, "Usage: elf2uf2 (-v) <input ELF file> <output UF2 file>\n");
|
||||
return ERROR_ARGS;
|
||||
}
|
||||
|
||||
static int read_and_check_elf32_header(FILE *in, elf32_header& eh_out) {
|
||||
if (1 != fread(&eh_out, sizeof(eh_out), 1, in)) {
|
||||
return fail(ERROR_READ_FAILED, "Unable to read ELF header");
|
||||
}
|
||||
if (eh_out.common.magic != ELF_MAGIC) {
|
||||
return fail(ERROR_FORMAT, "Not an ELF file");
|
||||
}
|
||||
if (eh_out.common.version != 1 || eh_out.common.version2 != 1) {
|
||||
return fail(ERROR_FORMAT, "Unrecognized ELF version");
|
||||
}
|
||||
if (eh_out.common.arch_class != 1 || eh_out.common.endianness != 1) {
|
||||
return fail(ERROR_INCOMPATIBLE, "Require 32 bit little-endian ELF");
|
||||
}
|
||||
if (eh_out.eh_size != sizeof(struct elf32_header)) {
|
||||
return fail(ERROR_FORMAT, "Invalid ELF32 format");
|
||||
}
|
||||
if (eh_out.common.machine != EM_ARM) {
|
||||
return fail(ERROR_FORMAT, "Not an ARM executable");
|
||||
}
|
||||
if (eh_out.common.abi != 0) {
|
||||
return fail(ERROR_INCOMPATIBLE, "Unrecognized ABI");
|
||||
}
|
||||
if (eh_out.flags & EF_ARM_ABI_FLOAT_HARD) {
|
||||
return fail(ERROR_INCOMPATIBLE, "HARD-FLOAT not supported");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_address_range(const address_ranges& valid_ranges, uint32_t addr, uint32_t vaddr, uint32_t size, bool uninitialized, address_range &ar) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to >= addr + size) {
|
||||
if (range.type == address_range::type::NO_CONTENTS && !uninitialized) {
|
||||
return fail(ERROR_INCOMPATIBLE, "ELF contains memory contents for uninitialized memory");
|
||||
}
|
||||
ar = range;
|
||||
if (verbose) {
|
||||
printf("%s segment %08x->%08x (%08x->%08x)\n", uninitialized ? "Uninitialized" : "Mapped", addr,
|
||||
addr + size, vaddr, vaddr+size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return fail(ERROR_INCOMPATIBLE, "Memory segment %08x->%08x is outside of valid address range for device", addr, addr+size);
|
||||
}
|
||||
|
||||
int 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) {
|
||||
if (eh.ph_entry_size != sizeof(elf32_ph_entry)) {
|
||||
return fail(ERROR_FORMAT, "Invalid ELF32 program header");
|
||||
}
|
||||
if (eh.ph_num) {
|
||||
std::vector<elf32_ph_entry> entries(eh.ph_num);
|
||||
if (fseek(in, eh.ph_offset, SEEK_SET)) {
|
||||
return fail_read_error();
|
||||
}
|
||||
if (eh.ph_num != fread(&entries[0], sizeof(struct elf32_ph_entry), eh.ph_num, in)) {
|
||||
return fail_read_error();
|
||||
}
|
||||
for(uint i=0;i<eh.ph_num;i++) {
|
||||
elf32_ph_entry& entry = entries[i];
|
||||
if (entry.type == PT_LOAD && entry.memsz) {
|
||||
address_range ar;
|
||||
int rc;
|
||||
uint mapped_size = std::min(entry.filez, entry.memsz);
|
||||
if (mapped_size) {
|
||||
rc = check_address_range(valid_ranges, entry.paddr, entry.vaddr, mapped_size, false, ar);
|
||||
if (rc) return rc;
|
||||
// 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
|
||||
if (ar.type != address_range::type::CONTENTS) {
|
||||
if (verbose) printf(" ignored\n");
|
||||
continue;
|
||||
}
|
||||
uint addr = entry.paddr;
|
||||
uint remaining = mapped_size;
|
||||
uint file_offset = entry.offset;
|
||||
while (remaining) {
|
||||
uint off = addr & (PAGE_SIZE - 1);
|
||||
uint len = std::min(remaining, PAGE_SIZE - off);
|
||||
auto &fragments = pages[addr - off]; // list of fragments
|
||||
// note if filesz is zero, we want zero init which is handled because the
|
||||
// statement above creates an empty page fragment list
|
||||
// check overlap with any existing fragments
|
||||
for (const auto &fragment : fragments) {
|
||||
if ((off < fragment.page_offset + fragment.bytes) !=
|
||||
((off + len) <= fragment.page_offset)) {
|
||||
fail(ERROR_FORMAT, "In memory segments overlap");
|
||||
}
|
||||
}
|
||||
fragments.push_back(
|
||||
page_fragment{file_offset,off,len});
|
||||
addr += len;
|
||||
file_offset += len;
|
||||
remaining -= len;
|
||||
}
|
||||
}
|
||||
if (entry.memsz > entry.filez) {
|
||||
// we have some uninitialized data too
|
||||
rc = check_address_range(valid_ranges, entry.paddr + entry.filez, entry.vaddr + entry.filez, entry.memsz - entry.filez, true,
|
||||
ar);
|
||||
if (rc) return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int realize_page(FILE *in, const std::vector<page_fragment> &fragments, uint8_t *buf, uint buf_len) {
|
||||
assert(buf_len >= PAGE_SIZE);
|
||||
for(auto& frag : fragments) {
|
||||
assert(frag.page_offset >= 0 && frag.page_offset < PAGE_SIZE && frag.page_offset + frag.bytes <= PAGE_SIZE);
|
||||
if (fseek(in, frag.file_offset, SEEK_SET)) {
|
||||
return fail_read_error();
|
||||
}
|
||||
if (1 != fread(buf + frag.page_offset, frag.bytes, 1, in)) {
|
||||
return fail_read_error();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_address_valid(const address_ranges& valid_ranges, uint32_t addr) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to > addr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_address_initialized(const address_ranges& valid_ranges, uint32_t addr) {
|
||||
for(const auto& range : valid_ranges) {
|
||||
if (range.from <= addr && range.to > addr) {
|
||||
return address_range::type::CONTENTS == range.type;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_address_mapped(const std::map<uint32_t, std::vector<page_fragment>>& pages, uint32_t addr) {
|
||||
uint32_t page = addr & ~(PAGE_SIZE - 1);
|
||||
if (!pages.count(page)) return false;
|
||||
// todo check actual address within page
|
||||
return true;
|
||||
}
|
||||
|
||||
int elf2uf2(FILE *in, FILE *out) {
|
||||
elf32_header eh;
|
||||
std::map<uint32_t, std::vector<page_fragment>> pages;
|
||||
int rc = read_and_check_elf32_header(in, eh);
|
||||
bool ram_style = false;
|
||||
address_ranges valid_ranges = {};
|
||||
if (!rc) {
|
||||
ram_style = is_address_initialized(rp2040_address_ranges_ram, eh.entry);
|
||||
if (verbose) {
|
||||
if (ram_style) {
|
||||
printf("Detected RAM binary\n");
|
||||
} else {
|
||||
printf("Detected FLASH binary\n");
|
||||
}
|
||||
}
|
||||
valid_ranges = ram_style ? rp2040_address_ranges_ram : rp2040_address_ranges_flash;
|
||||
rc = read_and_check_elf32_ph_entries(in, eh, valid_ranges, pages);
|
||||
}
|
||||
if (rc) return rc;
|
||||
if (pages.empty()) {
|
||||
return fail(ERROR_INCOMPATIBLE, "The input file has no memory pages");
|
||||
}
|
||||
uint page_num = 0;
|
||||
if (ram_style) {
|
||||
uint32_t expected_ep_main_ram = UINT32_MAX;
|
||||
uint32_t expected_ep_xip_sram = UINT32_MAX;
|
||||
for(auto& page_entry : pages) {
|
||||
if ( ((page_entry.first >= MAIN_RAM_START) && (page_entry.first < MAIN_RAM_END)) && (page_entry.first < expected_ep_main_ram) ) {
|
||||
expected_ep_main_ram = page_entry.first | 0x1;
|
||||
} else if ( ((page_entry.first >= XIP_SRAM_START) && (page_entry.first < XIP_SRAM_END)) && (page_entry.first < expected_ep_xip_sram) ) {
|
||||
expected_ep_xip_sram = page_entry.first | 0x1;
|
||||
}
|
||||
}
|
||||
uint32_t expected_ep = (UINT32_MAX != expected_ep_main_ram) ? expected_ep_main_ram : expected_ep_xip_sram;
|
||||
if (eh.entry == expected_ep_xip_sram) {
|
||||
return fail(ERROR_INCOMPATIBLE, "B0/B1 Boot ROM does not support direct entry into XIP_SRAM\n");
|
||||
} else if (eh.entry != expected_ep) {
|
||||
return fail(ERROR_INCOMPATIBLE, "A RAM binary should have an entry point at the beginning: %08x (not %08x)\n", expected_ep, eh.entry);
|
||||
}
|
||||
static_assert(0 == (MAIN_RAM_START & (PAGE_SIZE - 1)), "");
|
||||
// currently don't require this as entry point is now at the start, we don't know where reset vector is
|
||||
#if 0
|
||||
uint8_t buf[PAGE_SIZE];
|
||||
rc = realize_page(in, pages[MAIN_RAM_START], buf, sizeof(buf));
|
||||
if (rc) return rc;
|
||||
uint32_t sp = ((uint32_t *)buf)[0];
|
||||
uint32_t ip = ((uint32_t *)buf)[1];
|
||||
if (!is_address_mapped(pages, ip)) {
|
||||
return fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: reset vector %08x is not in mapped memory",
|
||||
MAIN_RAM_START, ip);
|
||||
}
|
||||
if (!is_address_valid(valid_ranges, sp - 4)) {
|
||||
return fail(ERROR_INCOMPATIBLE, "Vector table at %08x is invalid: stack pointer %08x is not in RAM",
|
||||
MAIN_RAM_START, sp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
uf2_block block;
|
||||
block.magic_start0 = UF2_MAGIC_START0;
|
||||
block.magic_start1 = UF2_MAGIC_START1;
|
||||
block.flags = UF2_FLAG_FAMILY_ID_PRESENT;
|
||||
block.payload_size = PAGE_SIZE;
|
||||
block.num_blocks = (uint32_t)pages.size();
|
||||
block.file_size = RP2040_FAMILY_ID;
|
||||
block.magic_end = UF2_MAGIC_END;
|
||||
for(auto& page_entry : pages) {
|
||||
block.target_addr = page_entry.first;
|
||||
block.block_no = page_num++;
|
||||
if (verbose) {
|
||||
printf("Page %d / %d %08x\n", block.block_no, block.num_blocks, block.target_addr);
|
||||
}
|
||||
memset(block.data, 0, sizeof(block.data));
|
||||
rc = realize_page(in, page_entry.second, block.data, sizeof(block.data));
|
||||
if (rc) return rc;
|
||||
if (1 != fwrite(&block, sizeof(uf2_block), 1, out)) {
|
||||
return fail_write_error();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int arg = 1;
|
||||
if (arg < argc && !strcmp(argv[arg], "-v")) {
|
||||
verbose = true;
|
||||
arg++;
|
||||
}
|
||||
if (argc < arg + 2) {
|
||||
return usage();
|
||||
}
|
||||
const char *in_filename = argv[arg++];
|
||||
FILE *in = fopen(in_filename, "rb");
|
||||
if (!in) {
|
||||
fprintf(stderr, "Can't open input file '%s'\n", in_filename);
|
||||
return ERROR_ARGS;
|
||||
}
|
||||
const char *out_filename = argv[arg++];
|
||||
FILE *out = fopen(out_filename, "wb");
|
||||
if (!out) {
|
||||
fprintf(stderr, "Can't open output file '%s'\n", out_filename);
|
||||
return ERROR_ARGS;
|
||||
}
|
||||
|
||||
int rc = elf2uf2(in, out);
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
if (rc) {
|
||||
remove(out_filename);
|
||||
if (error_msg[0]) {
|
||||
fprintf(stderr, "ERROR: %s\n", error_msg);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
4
tools/elf2uf2/makefile
Normal file
4
tools/elf2uf2/makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
all: build
|
||||
|
||||
build: main.cpp
|
||||
g++ -o elf2uf2 main.cpp
|
46
tools/elf2uf2/uf2.h
Normal file
46
tools/elf2uf2/uf2.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _BOOT_UF2_H
|
||||
#define _BOOT_UF2_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/** \file uf2.h
|
||||
* \defgroup boot_uf2 boot_uf2
|
||||
*
|
||||
* Header file for the UF2 format supported by an RP2040 in BOOTSEL mode.
|
||||
*/
|
||||
|
||||
#define UF2_MAGIC_START0 0x0A324655u
|
||||
#define UF2_MAGIC_START1 0x9E5D5157u
|
||||
#define UF2_MAGIC_END 0x0AB16F30u
|
||||
|
||||
#define UF2_FLAG_NOT_MAIN_FLASH 0x00000001u
|
||||
#define UF2_FLAG_FILE_CONTAINER 0x00001000u
|
||||
#define UF2_FLAG_FAMILY_ID_PRESENT 0x00002000u
|
||||
#define UF2_FLAG_MD5_PRESENT 0x00004000u
|
||||
|
||||
#define RP2040_FAMILY_ID 0xe48bff56
|
||||
|
||||
struct uf2_block {
|
||||
// 32 byte header
|
||||
uint32_t magic_start0;
|
||||
uint32_t magic_start1;
|
||||
uint32_t flags;
|
||||
uint32_t target_addr;
|
||||
uint32_t payload_size;
|
||||
uint32_t block_no;
|
||||
uint32_t num_blocks;
|
||||
uint32_t file_size; // or familyID;
|
||||
uint8_t data[476];
|
||||
uint32_t magic_end;
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct uf2_block) == 512, "uf2_block not sector sized");
|
||||
|
||||
#endif
|
55
tools/pad_checksum.py
Executable file
55
tools/pad_checksum.py
Executable file
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import binascii
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
def any_int(x):
|
||||
try:
|
||||
return int(x, 0)
|
||||
except:
|
||||
raise argparse.ArgumentTypeError("expected an integer, not '{!r}'".format(x))
|
||||
|
||||
|
||||
def bitrev(x, width):
|
||||
return int("{:0{w}b}".format(x, w=width)[::-1], 2)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("ifile", help="Input file (binary)")
|
||||
parser.add_argument("ofile", help="Output file (assembly)")
|
||||
parser.add_argument("-p", "--pad", help="Padded size (bytes), including 4-byte checksum, default 256",
|
||||
type=any_int, default=256)
|
||||
parser.add_argument("-s", "--seed", help="Checksum seed value, default 0",
|
||||
type=any_int, default=0)
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
idata = open(args.ifile, "rb").read()
|
||||
except:
|
||||
sys.exit("Could not open input file '{}'".format(args.ifile))
|
||||
|
||||
if len(idata) > args.pad - 4:
|
||||
sys.exit("Input file size ({} bytes) too large for final size ({} bytes)".format(len(idata), args.pad))
|
||||
|
||||
idata_padded = idata + bytes(args.pad - 4 - len(idata))
|
||||
|
||||
# Our bootrom CRC32 is slightly bass-ackward but it's best to work around for now (FIXME)
|
||||
# 100% worth it to save two Thumb instructions
|
||||
checksum = bitrev(
|
||||
(binascii.crc32(bytes(bitrev(b, 8) for b in idata_padded), args.seed ^ 0xffffffff) ^ 0xffffffff) & 0xffffffff, 32)
|
||||
odata = idata_padded + struct.pack("<L", checksum)
|
||||
|
||||
try:
|
||||
with open(args.ofile, "w") as ofile:
|
||||
ofile.write("// Padded and checksummed version of: {}\n\n".format(args.ifile))
|
||||
ofile.write(".cpu cortex-m0plus\n")
|
||||
ofile.write(".thumb\n\n")
|
||||
ofile.write(".section .boot2, \"ax\"\n\n")
|
||||
for offs in range(0, len(odata), 16):
|
||||
chunk = odata[offs:min(offs + 16, len(odata))]
|
||||
ofile.write(".byte {}\n".format(", ".join("0x{:02x}".format(b) for b in chunk)))
|
||||
except:
|
||||
sys.exit("Could not open output file '{}'".format(args.ofile))
|
Loading…
Add table
Add a link
Reference in a new issue