blob: 306f1f29a1118f6448cb1b22408765a12722ae9f [file] [log] [blame] [edit]
#pragma once
#include <memory>
#include <iterator>
#include "boot/picobin.h"
#include <map>
#include <cassert>
#include "elf_file.h"
#if ENABLE_DEBUG_LOG
#define DEBUG_LOG(...) printf(__VA_ARGS__)
#else
#define DEBUG_LOG(...) ((void)0)
#endif
// Support for SDK 2.1.0 & SDK 2.1.1 -----
#ifndef PICOBIN_IMAGE_TYPE_EXE_EXTRA_SECURITY_BITS
#define PICOBIN_IMAGE_TYPE_EXE_EXTRA_SECURITY_BITS _u(0x0800)
#endif
// ------
struct item;
template<typename InputIterator> std::vector<uint32_t> lsb_bytes_to_words(InputIterator begin, InputIterator end) {
using InputType = typename std::iterator_traits<InputIterator>::value_type;
static_assert(sizeof(InputType) == 1, "");
std::vector<uint32_t> rc;
size_t size = end - begin;
assert(!(size & 3));
if (size) {
rc.reserve(size / 4);
for(auto it = begin; it < end; ) {
uint32_t word = *it++;
word |= (*it++) << 8;
word |= (*it++) << 16;
word |= (*it++) << 24;
rc.push_back(word);
}
}
return rc;
}
template<typename InputIterator, typename OutputType = std::uint8_t> std::vector<OutputType> words_to_lsb_bytes(InputIterator begin, InputIterator end) {
static_assert(sizeof(OutputType) == 1, "");
std::vector<OutputType> rc;
size_t size = end - begin;
if (size) {
rc.reserve(size * 4);
for(auto it = begin; it < end; ) {
uint32_t word = *it++;
rc.push_back(word & 0xff);
rc.push_back((word >> 8) & 0xff);
rc.push_back((word >> 16) & 0xff);
rc.push_back((word >> 24) & 0xff);
}
}
return rc;
}
struct item_writer_context {
item_writer_context(uint32_t base_addr) : base_addr(base_addr), word_offset(0) {}
std::map<std::shared_ptr<item>, uint32_t> item_word_offsets;
uint32_t base_addr;
uint32_t word_offset;
};
struct item {
explicit item() {};
virtual ~item() = default;
virtual uint8_t type() const = 0;
static uint32_t decode_size(uint32_t item_header) {
uint32_t size;
if (item_header & 0x80) {
size = (uint16_t)(item_header >> 8);
} else {
size = (uint8_t)(item_header >> 8);
}
return size;
}
virtual std::vector<uint32_t> to_words(item_writer_context &ctx) const = 0;
virtual uint32_t encode_type_and_size(unsigned int size) const {
assert(size < PICOBIN_MAX_BLOCK_SIZE);
if (size < 256) {
return (size << 8u) | type();
} else {
// todo byte order
return (size << 8u) | 0x80 | type();
}
}
};
// always use single byte size
struct single_byte_size_item : public item {
virtual uint32_t encode_type_and_size(unsigned int size) const override {
assert(size < PICOBIN_MAX_BLOCK_SIZE);
assert(size < 128);
assert(!(type() & 128));
return (size << 8u) | type();
}
};
// always use double byte size
struct double_byte_size_item : public item {
virtual uint32_t encode_type_and_size(unsigned int size) const override {
assert(size < PICOBIN_MAX_BLOCK_SIZE);
assert(type() & 128);
// todo byte order
return (size << 8u) | 0x80 | type();
}
};
struct ignored_item : public double_byte_size_item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_2BS_IGNORED; }
ignored_item() = default;
explicit ignored_item(uint32_t size, std::vector<uint32_t> data) : size(size), data(data) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
uint32_t size = item::decode_size(header);
std::vector<uint32_t> data;
for (unsigned int i=1; i < size; i++) {
data.push_back(*it++);
}
return std::make_shared<ignored_item>(size, data);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
std::vector<uint32_t> ret;
ret.push_back(encode_type_and_size(size));
ret.insert(ret.end(), data.begin(), data.end());
return ret;
}
uint32_t size;
std::vector<uint32_t> data;
};
enum image_type_image_type {
type_invalid=PICOBIN_IMAGE_TYPE_IMAGE_TYPE_INVALID,
type_exe=PICOBIN_IMAGE_TYPE_IMAGE_TYPE_EXE,
type_data=PICOBIN_IMAGE_TYPE_IMAGE_TYPE_DATA
};
enum image_type_exe_security {
sec_unspecified=PICOBIN_IMAGE_TYPE_EXE_SECURITY_UNSPECIFIED,
sec_ns=PICOBIN_IMAGE_TYPE_EXE_SECURITY_NS,
sec_s=PICOBIN_IMAGE_TYPE_EXE_SECURITY_S
};
enum image_type_exe_cpu {
cpu_arm=PICOBIN_IMAGE_TYPE_EXE_CPU_ARM,
cpu_riscv=PICOBIN_IMAGE_TYPE_EXE_CPU_RISCV,
cpu_varmulet=PICOBIN_IMAGE_TYPE_EXE_CPU_VARMULET
};
enum image_type_exe_chip {
chip_rp2040=PICOBIN_IMAGE_TYPE_EXE_CHIP_RP2040,
chip_rp2350=PICOBIN_IMAGE_TYPE_EXE_CHIP_RP2350
};
struct image_type_item : public single_byte_size_item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE; }
image_type_item() = default;
explicit image_type_item(uint16_t flags) : flags(flags) {}
template <typename I> static std::shared_ptr<item> parse(I it, I end, uint32_t header) {
return std::make_shared<image_type_item>(header >> 16);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
return {encode_type_and_size(1) | (flags << 16)};
}
image_type_image_type image_type() const { return static_cast<image_type_image_type>((flags & PICOBIN_IMAGE_TYPE_IMAGE_TYPE_BITS) >> PICOBIN_IMAGE_TYPE_IMAGE_TYPE_LSB); }
image_type_exe_security security() const { return static_cast<image_type_exe_security>((flags & PICOBIN_IMAGE_TYPE_EXE_SECURITY_BITS) >> PICOBIN_IMAGE_TYPE_EXE_SECURITY_LSB); }
image_type_exe_cpu cpu() const { return static_cast<image_type_exe_cpu>((flags & PICOBIN_IMAGE_TYPE_EXE_CPU_BITS) >> PICOBIN_IMAGE_TYPE_EXE_CPU_LSB); }
image_type_exe_chip chip() const { return static_cast<image_type_exe_chip>((flags & PICOBIN_IMAGE_TYPE_EXE_CHIP_BITS) >> PICOBIN_IMAGE_TYPE_EXE_CHIP_LSB); }
bool tbyb() const { return flags & PICOBIN_IMAGE_TYPE_EXE_TBYB_BITS; }
bool extra_security() const { return flags & PICOBIN_IMAGE_TYPE_EXE_EXTRA_SECURITY_BITS; }
uint16_t flags;
};
struct partition_table_item : public single_byte_size_item {
struct partition {
uint8_t permissions = 0;
uint16_t first_sector = 0;
uint16_t last_sector = 0;
uint32_t flags = 0;
uint64_t id;
std::string name;
std::vector<uint32_t> extra_families;
std::vector<uint32_t> to_words() const {
std::vector<uint32_t> ret = {
((permissions << PICOBIN_PARTITION_PERMISSIONS_LSB) & PICOBIN_PARTITION_PERMISSIONS_BITS)
| ((first_sector << PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS)
| ((last_sector << PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS),
((permissions << PICOBIN_PARTITION_PERMISSIONS_LSB) & PICOBIN_PARTITION_PERMISSIONS_BITS)
| flags,
};
if (flags & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS) {
ret.push_back((uint32_t)id);
ret.push_back((uint32_t)(id >> 32));
}
if (extra_families.size() > 0) {
assert(extra_families.size() == (flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS) >> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB);
ret.insert(ret.end(), extra_families.begin(), extra_families.end());
}
if (name.size() > 0) {
char size = name.size();
assert((size & 0x7f) == size);
std::vector<char> name_vec = {size};
for (char c : name) {
name_vec.push_back(c);
}
while (name_vec.size() % 4 != 0) name_vec.push_back(0);
while (name_vec.size() > 0) {
ret.push_back(*(uint32_t*)name_vec.data());
name_vec.erase(name_vec.begin(), name_vec.begin() + 4);
}
}
return ret;
}
};
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_PARTITION_TABLE; }
partition_table_item() = default;
explicit partition_table_item(uint32_t unpartitioned_flags, bool singleton) : unpartitioned_flags(unpartitioned_flags), singleton(singleton) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
uint32_t size = decode_size(header);
uint8_t singleton_count = header >> 24;
bool singleton = singleton_count & 0x80;
uint8_t partition_count = singleton_count & 0x0f;
uint32_t unpartitioned_flags = *it++;
auto pt = std::make_shared<partition_table_item>(unpartitioned_flags, singleton);
std::vector<uint32_t> data;
for (unsigned int i=2; i < size; i++) {
data.push_back(*it++);
}
size_t i=0;
while (i < data.size()) {
partition new_p;
uint32_t permissions_locations = data[i++];
new_p.permissions = (permissions_locations & PICOBIN_PARTITION_PERMISSIONS_BITS) >> PICOBIN_PARTITION_PERMISSIONS_LSB;
new_p.first_sector = (permissions_locations & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB;
new_p.last_sector = (permissions_locations & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB;
uint32_t permissions_flags = data[i++];
uint8_t permissions2 = (permissions_flags & PICOBIN_PARTITION_PERMISSIONS_BITS) >> PICOBIN_PARTITION_PERMISSIONS_LSB;
if (new_p.permissions != permissions2) {
printf("Permissions mismatch %02x %02x\n", new_p.permissions, permissions2);
assert(false);
}
new_p.flags = permissions_flags & (~PICOBIN_PARTITION_PERMISSIONS_BITS);
if (new_p.flags & PICOBIN_PARTITION_FLAGS_HAS_ID_BITS) {
uint32_t low = data[i++];
uint32_t high = data[i++];
new_p.id = (uint64_t)low | ((uint64_t)high << 32);
}
uint8_t num_extra_families = (new_p.flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_BITS) >> PICOBIN_PARTITION_FLAGS_ACCEPTS_NUM_EXTRA_FAMILIES_LSB;
for (int fam=0; fam < num_extra_families; fam++) {
new_p.extra_families.push_back(data[i++]);
}
if (new_p.flags & PICOBIN_PARTITION_FLAGS_HAS_NAME_BITS) {
auto bytes = words_to_lsb_bytes(data.begin() + i++, data.end());
int name_size = bytes[0];
// This works neatly - accounts for the size byte at the start
i += name_size / 4;
new_p.name = std::string((char*)(bytes.data() + 1), name_size);
}
pt->partitions.push_back(new_p);
}
return pt;
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
std::vector<uint32_t> partition_words;
for (auto p : partitions) {
auto words = p.to_words();
partition_words.insert(partition_words.end(), words.begin(), words.end());
}
std::vector<uint32_t> ret = {
encode_type_and_size(2 + partition_words.size()) | (singleton << 31) | ((uint8_t)partitions.size() << 24),
unpartitioned_flags
};
ret.insert(ret.end(), partition_words.begin(), partition_words.end());
return ret;
}
uint32_t unpartitioned_flags;
bool singleton;
std::vector<partition> partitions;
};
struct vector_table_item : public single_byte_size_item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_VECTOR_TABLE; }
vector_table_item() = default;
explicit vector_table_item(uint32_t addr) : addr(addr) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
if (decode_size(header) != 2) {
printf("Bad block size %d for vector table item\n", decode_size(header));
assert(false);
}
return std::make_shared<vector_table_item>(*it++);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
return {encode_type_and_size(2), addr};
}
uint32_t addr;
};
struct rolling_window_delta_item : public single_byte_size_item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_ROLLING_WINDOW_DELTA; }
rolling_window_delta_item() = default;
explicit rolling_window_delta_item(uint32_t addr) : addr(addr) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
if (decode_size(header) != 2) {
printf("Bad block size %d for rolling window delta item\n", decode_size(header));
assert(false);
}
return std::make_shared<rolling_window_delta_item>(*it++);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
return {encode_type_and_size(2), (uint32_t)addr};
}
int32_t addr;
};
struct entry_point_item : public single_byte_size_item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_ENTRY_POINT; }
entry_point_item() = default;
explicit entry_point_item(uint32_t ep, uint32_t sp) : ep(ep), sp(sp) {}
explicit entry_point_item(uint32_t ep, uint32_t sp, uint32_t splim) : ep(ep), sp(sp), splim(splim), splim_set(true) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
uint32_t size = decode_size(header);
uint32_t ep = *it++;
uint32_t sp = *it++;
if (size == 3) {
return std::make_shared<entry_point_item>(ep, sp);
} else if (size == 4) {
uint32_t splim = *it++;
return std::make_shared<entry_point_item>(ep, sp, splim);
} else {
printf("Bad block size %d for entry point item\n", size);
assert(false);
return nullptr;
}
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
std::vector<uint32_t> ret = {encode_type_and_size(splim_set ? 4 : 3), ep, sp};
if (splim_set) ret.push_back(splim);
return ret;
}
uint32_t ep;
uint32_t sp;
uint32_t splim;
bool splim_set = false;
};
struct load_map_item : public item {
struct entry {
uint32_t storage_address;
uint32_t runtime_address;
uint32_t size;
};
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_LOAD_MAP; }
load_map_item() = default;
explicit load_map_item(bool absolute, std::vector<entry> entries) : absolute(absolute), entries(entries) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header, uint32_t current_addr) {
uint8_t num_entries = (header >> 24) & 0x7f;
bool absolute = (header >> 24) & 0x80;
std::vector<entry> entries;
for (int i=0; i < num_entries; i++) {
uint32_t storage_address = (uint32_t)*it++;
uint32_t runtime_address = (uint32_t)*it++;
uint32_t size = (uint32_t)*it++;
if (storage_address != 0) {
if (absolute) {
size -= runtime_address;
} else {
storage_address += current_addr;
}
}
entries.push_back({
storage_address,
runtime_address,
size
});
}
return std::make_shared<load_map_item>(absolute, entries);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
assert(entries.size() < 256);
std::vector<uint32_t> rc = {
encode_type_and_size(1 + 3 * entries.size()) | (((uint8_t)entries.size())<<24) | (absolute ? 1 << 31 : 0)
};
for(const auto &entry : entries) {
// todo byte order
if (absolute) {
rc.push_back(entry.storage_address);
rc.push_back(entry.runtime_address);
if (entry.storage_address != 0) {
rc.push_back(entry.runtime_address + entry.size);
} else {
rc.push_back(entry.size);
}
} else {
if (entry.storage_address != 0) {
rc.push_back(entry.storage_address - ctx.base_addr - ctx.word_offset * 4);
} else {
rc.push_back(entry.storage_address);
}
rc.push_back(entry.runtime_address);
rc.push_back(entry.size);
}
}
return rc;
}
bool absolute;
std::vector<entry> entries;
};
struct version_item : public item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_VERSION; }
version_item() = default;
explicit version_item(uint16_t major, uint16_t minor) : version_item(major, minor, 0, {}) {}
version_item(uint16_t major, uint16_t minor, uint16_t rollback, std::vector<uint16_t> otp_rows) :
rollback(rollback), otp_rows(std::move(otp_rows)) {
// NOTE: A linux sysroot might define `major` and `minor` as macros so we
// can't use regular initialization in the form `major(major)` since we woudn't
// be able to tell if we're calling a macro or not.
this->major = major;
this->minor = minor;
}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
// todo validate
uint32_t major_minor = *it++;
uint16_t major = major_minor >> 16;
uint16_t minor = major_minor & 0xffff;
unsigned int otp_row_count = header >> 24;
uint16_t rollback = 0;
std::vector<uint16_t> otp_rows;
if (otp_row_count) {
unsigned int pair = *it++;
// todo endian
rollback = (uint16_t) pair;
for(unsigned int i=0;i<otp_row_count;i++) {
if (i&1) pair = *it++;
otp_rows.push_back((uint16_t)(pair >> ((1^(i&1))*16)));
}
}
return std::make_shared<version_item>(major, minor, rollback, otp_rows);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
assert(otp_rows.size() < 256);
unsigned int size = 2 + (!otp_rows.empty() + otp_rows.size() + 1) / 2;
std::vector<uint32_t> rc = {
(uint32_t)(encode_type_and_size(size) | (otp_rows.size() << 24))
};
uint32_t major_minor = (uint16_t)major << 16 | (uint16_t)minor;
rc.push_back(major_minor);
if (!otp_rows.empty()) {
rc.push_back(rollback);
for(unsigned int i=0;i<otp_rows.size();i++) {
if (i&1) {
rc.push_back(otp_rows[i]);
} else {
rc[rc.size()-1] |= otp_rows[i] << 16;
}
}
}
return rc;
}
uint16_t major;
uint16_t minor;
uint16_t rollback;
std::vector<uint16_t> otp_rows;
};
struct hash_def_item : public single_byte_size_item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_1BS_HASH_DEF; }
hash_def_item() = default;
explicit hash_def_item(uint8_t hash_type) : hash_type(hash_type) {}
explicit hash_def_item(uint8_t hash_type, uint16_t block_words_to_hash) : hash_type(hash_type), block_words_to_hash(block_words_to_hash) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
uint8_t hash_type = (header & 0xff000000) >> 24;
uint16_t block_words_to_hash = *it++;
return std::make_shared<hash_def_item>(hash_type, block_words_to_hash);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
return {
encode_type_and_size(2) | (hash_type << 24),
block_words_to_hash == 0 ? (ctx.word_offset + 2) : block_words_to_hash
};
}
uint8_t hash_type;
uint16_t block_words_to_hash = 0;
};
struct signature_item : public item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_SIGNATURE; }
signature_item() = default;
explicit signature_item(uint8_t sig_type) : sig_type(sig_type) {}
explicit signature_item(uint8_t sig_type, std::vector<uint8_t> signature_bytes, std::vector<uint8_t> public_key_bytes) : sig_type(sig_type), signature_bytes(signature_bytes), public_key_bytes(public_key_bytes) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
auto sig_block_size = decode_size(header);
uint8_t sig_type = (header & 0xff000000) >> 24;
assert(sig_block_size == 0x21);
std::vector<uint8_t> signature_bytes;
std::vector<uint8_t> public_key_bytes;
{
std::vector<uint32_t> words;
for (int i=0; i < 16; i++) {
words.push_back(*it++);
}
auto bytes = words_to_lsb_bytes(words.begin(), words.end());
std::copy(bytes.begin(), bytes.end(), std::back_inserter(public_key_bytes));
}
{
std::vector<uint32_t> words;
for (int i=0; i < 16; i++) {
words.push_back(*it++);
}
auto bytes = words_to_lsb_bytes(words.begin(), words.end());
std::copy(bytes.begin(), bytes.end(), std::back_inserter(signature_bytes));
}
return std::make_shared<signature_item>(sig_type, signature_bytes, public_key_bytes);
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
assert(signature_bytes.size() % 4 == 0);
assert(public_key_bytes.size() % 4 == 0);
std::vector<uint32_t> rc = {
encode_type_and_size(1 + public_key_bytes.size()/4 + signature_bytes.size()/4) | (sig_type << 24),
};
auto words = lsb_bytes_to_words(public_key_bytes.begin(), public_key_bytes.end());
std::copy(words.begin(), words.end(), std::back_inserter(rc));
words = lsb_bytes_to_words(signature_bytes.begin(), signature_bytes.end());
std::copy(words.begin(), words.end(), std::back_inserter(rc));
return rc;
}
uint8_t sig_type;
std::vector<uint8_t> signature_bytes;
std::vector<uint8_t> public_key_bytes;
};
struct hash_value_item : public item {
uint8_t type() const override { return PICOBIN_BLOCK_ITEM_HASH_VALUE; }
hash_value_item() = default;
explicit hash_value_item(std::vector<uint8_t> hash_bytes) : hash_bytes(hash_bytes) {}
template <typename I> static std::shared_ptr<item> parse(I& it, I end, uint32_t header) {
auto hash_size = decode_size(header) - 1;
std::vector<uint32_t> hash_words;
for (unsigned int i=0; i < hash_size; i++) {
hash_words.push_back(*it++);
}
return std::make_shared<hash_value_item>(words_to_lsb_bytes(hash_words.begin(), hash_words.end()));
}
std::vector<uint32_t> to_words(item_writer_context& ctx) const override {
assert(hash_bytes.size() % 4 == 0);
std::vector<uint32_t> rc = {
encode_type_and_size(1 + hash_bytes.size()/4),
};
auto words = lsb_bytes_to_words(hash_bytes.begin(), hash_bytes.end());
std::copy(words.begin(), words.end(), std::back_inserter(rc));
return rc;
}
std::vector<uint8_t> hash_bytes;
};
struct block {
explicit block(uint32_t physical_addr, uint32_t next_block_rel=0, uint32_t next_block_rel_index=0, std::vector<std::shared_ptr<item>> items = {})
: physical_addr(physical_addr),
next_block_rel(next_block_rel),
next_block_rel_index(next_block_rel_index),
items( std::move(items)) {}
template <typename I> static std::unique_ptr<block> parse(uint32_t physical_addr, I next_block_rel_loc, I it, I end) {
I block_base = it;
uint32_t current_addr = physical_addr + 4; // for the ffffded3
std::vector<std::shared_ptr<item>> items;
while (it < end) {
auto item_base = it;
uint32_t header = *it++;
uint32_t size = item::decode_size(header);
std::shared_ptr<item> i;
switch ((uint8_t)header) {
case PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE:
i = image_type_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_PARTITION_TABLE:
i = partition_table_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_1BS_VECTOR_TABLE:
i = vector_table_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_1BS_ROLLING_WINDOW_DELTA:
i = rolling_window_delta_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_1BS_VERSION:
i = version_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_1BS_ENTRY_POINT:
i = entry_point_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_LOAD_MAP:
i = load_map_item::parse(it, end, header, current_addr);
break;
case PICOBIN_BLOCK_ITEM_1BS_HASH_DEF:
i = hash_def_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_HASH_VALUE:
i = hash_value_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_SIGNATURE:
i = signature_item::parse(it, end, header);
break;
case PICOBIN_BLOCK_ITEM_2BS_IGNORED:
i = ignored_item::parse(it, end, header);
break;
default:
DEBUG_LOG("Ignoring block type: %02x\n", (uint8_t)header);
i = ignored_item::parse(it, end, header);
break;
}
if (i) {
items.push_back(i);
}
current_addr += (size*4);
if (it != item_base + size) {
printf("WARNING: item at %08x had wrong size %x (expected %x)\n", (unsigned int)(physical_addr + (item_base - block_base) * 4), (int)(it - item_base), (int)size);
it = item_base + size;
assert(false);
}
}
uint32_t next_block_rel = *next_block_rel_loc;
uint32_t next_block_rel_index = next_block_rel_loc - block_base + 1;
return std::make_unique<block>(physical_addr, next_block_rel, next_block_rel_index, items);
}
std::vector<uint32_t> to_words() {
std::vector<uint32_t> words;
words.push_back(PICOBIN_BLOCK_MARKER_START);
item_writer_context ctx(physical_addr);
for(const auto &item : items) {
ctx.word_offset = words.size();
ctx.item_word_offsets[item] = ctx.word_offset;
const auto item_words = item->to_words(ctx);
std::copy(item_words.begin(), item_words.end(), std::back_inserter(words));
}
// todo should use a real item struct
assert(words.size() <= PICOBIN_MAX_BLOCK_SIZE - 3);
// todo byte order
words.push_back(PICOBIN_BLOCK_ITEM_2BS_LAST | (words.size() - 1) << 8);
words.push_back(next_block_rel);
words.push_back(PICOBIN_BLOCK_MARKER_END);
return words;
}
template <typename I> std::shared_ptr<I> get_item() {
I tmp = I();
uint8_t type = tmp.type();
auto it = std::find_if(items.begin(), items.end(), [type](std::shared_ptr<item> i) { return i->type() == type; });
if (it != std::end(items)) {
return std::dynamic_pointer_cast<I>(*it);
} else {
return nullptr;
}
}
uint32_t physical_addr;
uint32_t next_block_rel;
uint32_t next_block_rel_index;
std::vector<std::shared_ptr<item>> items;
};