diff --git a/BUILD.bazel b/BUILD.bazel index 5944db8e..31b2df7d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -4,12 +4,6 @@ package(default_visibility = ["//visibility:public"]) PICOTOOL_SDK_VERSION_STRING = module_version() if module_version() != None else "0.0.1-WORKSPACE" -picotool_binary_data_header( - name = "rp2350_rom", - src = "bootrom.end.bin", - out = "rp2350.rom.h", -) - # TODO: Make it possible to build the prebuilt from source. picotool_binary_data_header( name = "xip_ram_perms_elf", @@ -38,6 +32,24 @@ picotool_binary_data_header( out = "flash_id_bin.h", ) +picotool_binary_data_header( + name = "rp2350_a2_rom_end", + src = "//model:rp2350_a2_rom_end_bin", + out = "rp2350_a2_rom_end.h", +) + +picotool_binary_data_header( + name = "rp2350_a3_rom_end", + src = "//model:rp2350_a3_rom_end_bin", + out = "rp2350_a3_rom_end.h", +) + +picotool_binary_data_header( + name = "rp2350_a4_rom_end", + src = "//model:rp2350_a4_rom_end_bin", + out = "rp2350_a4_rom_end.h", +) + cc_library( name = "xip_ram_perms", srcs = ["get_xip_ram_perms.cpp"], @@ -88,7 +100,6 @@ cc_binary( "main.cpp", "otp.cpp", "otp.h", - "rp2350.rom.h", "get_xip_ram_perms.cpp", "get_enc_bootloader.cpp", ] + select({ diff --git a/CMakeLists.txt b/CMakeLists.txt index 691605b8..2c229927 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,16 +217,8 @@ else() endif() add_custom_target(embedded_data DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h) -add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350.rom.h - COMMAND ${CMAKE_COMMAND} - -D BINARY_FILE=${CMAKE_CURRENT_LIST_DIR}/bootrom.end.bin - -D OUTPUT_NAME=rp2350.rom - -P ${CMAKE_CURRENT_LIST_DIR}/cmake/binh.cmake - COMMENT "Configuring rp2350.rom.h" - VERBATIM) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xip_ram_perms_elf.h COMMAND ${CMAKE_COMMAND} -D BINARY_FILE=${XIP_RAM_PERMS_ELF} @@ -260,6 +252,7 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_id_bin.h COMMENT "Configuring flash_id_bin.h" VERBATIM) +add_subdirectory(model) add_subdirectory(errors) add_subdirectory(picoboot_connection) @@ -305,7 +298,7 @@ target_compile_definitions(picotool PRIVATE PICOTOOL_VERSION="${PICOTOOL_VERSION}" SYSTEM_VERSION="${SYSTEM_VERSION}" COMPILER_INFO="${COMPILER_INFO}" - SUPPORT_A2=1 + SUPPORT_RP2350_A2=1 CODE_OTP=${PICOTOOL_CODE_OTP} ) # for OTP info @@ -321,6 +314,7 @@ target_link_libraries(picotool pico_platform_headers pico_usb_reset_interface_headers regs_headers + model bintool elf2uf2 errors diff --git a/README.md b/README.md index 6a73de81..572586cc 100644 --- a/README.md +++ b/README.md @@ -821,8 +821,8 @@ UF2 CONVERT: Convert ELF/BIN to UF2. SYNOPSIS: - picotool uf2 convert [--quiet] [--verbose] [-t ] [-t ] [-o ] [--family ] - [[--abs-block] []] + picotool uf2 convert [--quiet] [--verbose] [-t ] [-t ] [-o ] [--family ] [--platform + ] [[--abs-block] []] OPTIONS: --quiet @@ -847,6 +847,9 @@ OPTIONS: UF2 Family options family ID for UF2 + Platform options + + optional platform for memory verification (eg rp2040, rp2350) Errata RP2350-E10 Fix --abs-block Add an absolute block diff --git a/elf/BUILD.bazel b/elf/BUILD.bazel index 46ed0494..0fd1aef0 100644 --- a/elf/BUILD.bazel +++ b/elf/BUILD.bazel @@ -4,7 +4,6 @@ cc_library( name = "elf", srcs = ["elf_file.cpp"], hdrs = [ - "addresses.h", "elf.h", "elf_file.h", "portable_endian.h", @@ -16,5 +15,5 @@ cc_library( ], }), includes = ["."], - deps = ["//errors"], + deps = ["//model", "//errors"], ) diff --git a/elf/CMakeLists.txt b/elf/CMakeLists.txt index f7833764..d2181900 100644 --- a/elf/CMakeLists.txt +++ b/elf/CMakeLists.txt @@ -3,4 +3,4 @@ add_library(elf STATIC target_include_directories(elf PUBLIC ${CMAKE_CURRENT_LIST_DIR}) -target_link_libraries(elf PRIVATE errors) +target_link_libraries(elf PUBLIC model errors) diff --git a/elf2uf2/CMakeLists.txt b/elf2uf2/CMakeLists.txt index 7ced994a..066c1d54 100644 --- a/elf2uf2/CMakeLists.txt +++ b/elf2uf2/CMakeLists.txt @@ -3,4 +3,4 @@ add_library(elf2uf2 STATIC target_include_directories(elf2uf2 PUBLIC ${CMAKE_CURRENT_LIST_DIR}) -target_link_libraries(elf2uf2 PRIVATE boot_uf2_headers elf errors) +target_link_libraries(elf2uf2 PUBLIC elf errors) diff --git a/elf2uf2/elf2uf2.cpp b/elf2uf2/elf2uf2.cpp index dae7da1b..24d41f3e 100644 --- a/elf2uf2/elf2uf2.cpp +++ b/elf2uf2/elf2uf2.cpp @@ -17,6 +17,7 @@ #include "elf2uf2.h" #include "errors.h" +#include "model.h" #define FLASH_SECTOR_ERASE_SIZE 4096u @@ -156,11 +157,11 @@ bool check_abs_block(uf2_block block) { !(block.flags & UF2_FLAG_EXTENSION_FLAGS_PRESENT && *(uint32_t*)&(block.data[UF2_PAGE_SIZE]) != UF2_EXTENSION_RP2_IGNORE_BLOCK); } -int pages2uf2(std::map>& pages, std::shared_ptr in, std::shared_ptr out, uint32_t family_id, uint32_t abs_block_loc=0) { +int pages2uf2(std::map>& pages, std::shared_ptr in, std::shared_ptr out, uint32_t family_id, model_t model, uint32_t abs_block_loc=0) { // RP2350-E10: add absolute block to start of flash UF2s, targeting end of flash by default - if (family_id != ABSOLUTE_FAMILY_ID && family_id != RP2040_FAMILY_ID && abs_block_loc) { + if (family_id != ABSOLUTE_FAMILY_ID && model->chip() == rp2350 && abs_block_loc) { uint32_t base_addr = pages.begin()->first; - address_ranges flash_range = rp2350_address_ranges_flash; + address_ranges flash_range = address_ranges_flash(model); if (is_address_initialized(flash_range, base_addr)) { uf2_block block = gen_abs_block(abs_block_loc); out->write((char*)&block, sizeof(uf2_block)); @@ -196,7 +197,7 @@ int pages2uf2(std::map>& pages, std::shared return 0; } -int bin2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t address, uint32_t family_id, uint32_t abs_block_loc, bool verbose) { +int bin2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t address, uint32_t family_id, model_t model, uint32_t abs_block_loc, bool verbose) { g_verbose = verbose; std::map> pages; @@ -232,10 +233,10 @@ int bin2uf2(std::shared_ptr in, std::shared_ptr ou remaining -= len; } - return pages2uf2(pages, in, out, family_id, abs_block_loc); + return pages2uf2(pages, in, out, family_id, model, abs_block_loc); } -int elf2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t family_id, uint32_t package_addr, uint32_t abs_block_loc, bool verbose) { +int elf2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t family_id, model_t model, uint32_t package_addr, uint32_t abs_block_loc, bool verbose) { elf_file source_file(verbose); g_verbose = verbose; elf_file *elf = &source_file; @@ -244,14 +245,8 @@ int elf2uf2(std::shared_ptr in, std::shared_ptr ou int rc = elf->read_file(in); bool ram_style = false; address_ranges valid_ranges = {}; - address_ranges flash_range; address_ranges ram_range; - if (family_id == RP2040_FAMILY_ID) { - flash_range = rp2040_address_ranges_flash; - ram_range = rp2040_address_ranges_ram; - } else { - flash_range = rp2350_address_ranges_flash; - ram_range = rp2350_address_ranges_ram; - } + address_ranges flash_range = address_ranges_flash(model); + address_ranges ram_range = address_ranges_ram(model); if (!rc) { rc = rp_determine_binary_type(elf->header(), elf->segments(), flash_range, ram_range, &ram_style); if (!rc) { @@ -284,9 +279,9 @@ int elf2uf2(std::shared_ptr in, std::shared_ptr ou } } 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 && family_id == RP2040_FAMILY_ID) { + if (eh.entry == expected_ep_xip_sram && model->chip() == rp2040) { fail(ERROR_INCOMPATIBLE, "RP2040 B0/B1/B2 Boot ROM does not support direct entry into XIP_SRAM\n"); - } else if (eh.entry != expected_ep && family_id == RP2040_FAMILY_ID) { + } else if (eh.entry != expected_ep && model->chip() == rp2040) { fail(ERROR_INCOMPATIBLE, "A RP2040 RAM binary should have an entry point at the beginning: %08x (not %08x)\n", expected_ep, eh.entry); } static_assert(0 == (SRAM_START & (UF2_PAGE_SIZE - 1)), ""); @@ -344,5 +339,5 @@ int elf2uf2(std::shared_ptr in, std::shared_ptr ou } } - return pages2uf2(pages, in, out, family_id, abs_block_loc); + return pages2uf2(pages, in, out, family_id, model, abs_block_loc); } diff --git a/elf2uf2/elf2uf2.h b/elf2uf2/elf2uf2.h index 02ae6cde..8239a0b1 100644 --- a/elf2uf2/elf2uf2.h +++ b/elf2uf2/elf2uf2.h @@ -14,6 +14,7 @@ #include "boot/uf2.h" #include "elf_file.h" +#include "model.h" // we require 256 (as this is the page size supported by the device) #define LOG2_PAGE_SIZE 8u @@ -21,8 +22,8 @@ bool check_abs_block(uf2_block block); -int bin2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t address, uint32_t family_id, uint32_t abs_block_loc=0, bool verbose=false); -int elf2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t family_id, uint32_t package_addr=0, uint32_t abs_block_loc=0, bool verbose=false); +int bin2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t address, uint32_t family_id, model_t model, uint32_t abs_block_loc=0, bool verbose=false); +int elf2uf2(std::shared_ptr in, std::shared_ptr out, uint32_t family_id, model_t model, uint32_t package_addr=0, uint32_t abs_block_loc=0, bool verbose=false); #endif \ No newline at end of file diff --git a/errors/errors.cpp b/errors/errors.cpp index 3f971b7b..c89f6002 100644 --- a/errors/errors.cpp +++ b/errors/errors.cpp @@ -3,7 +3,7 @@ #include "errors.h" void fail(int code, std::string msg) { - throw command_failure(code, std::move(msg)); + throw failure_error(code, std::move(msg)); } void fail(int code, const char *format, ...) { diff --git a/errors/errors.h b/errors/errors.h index 597df646..869f9a69 100644 --- a/errors/errors.h +++ b/errors/errors.h @@ -21,8 +21,8 @@ #define ERROR_UNKNOWN (-99) -struct command_failure : std::exception { - command_failure(int code, std::string s) : c(code), s(std::move(s)) {} +struct failure_error : std::exception { + failure_error(int code, std::string s) : c(code), s(std::move(s)) {} const char *what() const noexcept override { return s.c_str(); diff --git a/main.cpp b/main.cpp index 0aef1b97..917c4158 100644 --- a/main.cpp +++ b/main.cpp @@ -37,7 +37,6 @@ #include "get_enc_bootloader.h" #if HAS_LIBUSB #include "picoboot_connection_cxx.h" - #include "rp2350.rom.h" #include "get_xip_ram_perms.h" #else #include "picoboot_connection.h" @@ -49,6 +48,7 @@ #include "pico/stdio_usb/reset_interface.h" #include "elf.h" #include "otp.h" +#include "model.h" #include "errors.h" #include "hardware/regs/otp_data.h" @@ -105,9 +105,9 @@ using std::ios; using json = nlohmann::json; #if HAS_LIBUSB -typedef map>> device_map; +typedef map>> device_map; #else -typedef map>> device_map; +typedef map>> device_map; #endif auto memory_names = map{ @@ -171,15 +171,9 @@ std::array, 12> pin_functions_rp2350{{ std::map otp_regs; #if HAS_LIBUSB -auto bus_device_string = [](struct libusb_device *device, model_t model) { +auto bus_device_string = [](struct libusb_device *device, chip_t chip) { string bus_device; - if (model == rp2040) { - bus_device = string("RP2040 device at bus "); - } else if (model == rp2350) { - bus_device = string("RP2350 device at bus "); - } else { - bus_device = string("Device at bus "); - } + bus_device = chip_name(chip) + string(" device at bus "); return bus_device + std::to_string(libusb_get_bus_number(device)) + ", address " + std::to_string(libusb_get_device_address(device)); }; #endif @@ -426,6 +420,28 @@ string family_name(unsigned int family_id) { return hex_string(family_id); } +struct platform_model : public cli::value_base { + explicit platform_model(string name) : value_base(std::move(name)) {} + + template + platform_model &set(T &t) { + string nm = "<" + name() + ">"; + // note we cannot capture "this" + on_action([&t, nm](string value) { + auto ovalue = value; + if (value == "rp2040") { + t = std::make_shared(); + } else if (value == "rp2350") { + t = std::make_shared(); + } else { + return value + " is not a valid platform"; + } + return string(""); + }); + return *this; + } +}; + struct cmd { explicit cmd(string name) : _name(std::move(name)) {} virtual ~cmd() = default; @@ -478,6 +494,7 @@ struct _settings { bool force_no_reboot = false; string switch_cpu; uint32_t family_id = 0; + model_t model = nullptr; bool quiet = false; bool verbose = false; bool use_flash_cache = false; @@ -573,7 +590,7 @@ struct _settings { struct { bool abs_block = false; - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 uint32_t abs_block_loc = 0x11000000 - UF2_PAGE_SIZE; #else uint32_t abs_block_loc = 0; @@ -582,7 +599,7 @@ struct _settings { }; _settings settings; std::shared_ptr selected_cmd; -model_t selected_model = unknown; +chip_t selected_chip = unknown; auto device_selection = ( @@ -1034,7 +1051,7 @@ struct partition_create_command : public cmd { #endif (option("--singleton").set(settings.partition.singleton) % "Singleton partition table") ).min(0).force_expand_help(true) % "Partition Table Options" - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 + ( option("--abs-block").set(settings.uf2.abs_block) % "Enforce support for an absolute block" + hex("abs_block_loc").set(settings.uf2.abs_block_loc).min(0) % "absolute block location (default to 0x10ffff00)" @@ -1330,8 +1347,11 @@ struct uf2_convert_command : public cmd { ).force_expand_help(true) % "Packaging Options" + ( option("--family") & family_id("family_id").set(settings.family_id) % "family ID for UF2" - ).force_expand_help(true) % "UF2 Family options" - #if SUPPORT_A2 + ).force_expand_help(true) % "UF2 Family options" + + ( + option("--platform") & platform_model("platform").set(settings.model) % "optional platform for memory verification (eg rp2040, rp2350)" + ).force_expand_help(true) % "Platform options" + #if SUPPORT_RP2350_A2 + ( option("--abs-block").set(settings.uf2.abs_block) % "Add an absolute block" + hex("abs_block_loc").set(settings.uf2.abs_block_loc).min(0) % "absolute block location (default to 0x10ffff00)" @@ -1873,19 +1893,15 @@ struct memory_access { read(addr, (uint8_t *)v.data(), count * sizeof(typename raw_type_mapping::access_type), zero_fill); } } -}; -static model_t get_model(memory_access &raw_access) { - auto magic = raw_access.read_int(BOOTROM_MAGIC_ADDR); - magic &= 0xffffff; // ignore bootrom version - if (magic == BOOTROM_MAGIC_RP2040) { - return rp2040; - } else if (magic == BOOTROM_MAGIC_RP2350) { - return rp2350; - } else { - return unknown; + model_t get_model() { + assert(model); + return model; } -} + +protected: + model_t model = models::unknown; // something we can read from the start of ROM with +}; template bool get_int(const std::string& s, T& out) { @@ -1915,10 +1931,10 @@ bool get_json_int(json value, T& out) { } } -uint32_t bootrom_func_lookup(memory_access& access, uint16_t tag) { - model_t model = get_model(access); +uint32_t bootrom_func_lookup_rp2040(memory_access& access, uint16_t tag) { + model_t model = access.get_model(); // we are only used on RP2040 - if (model != rp2040) { + if (model->chip() != rp2040) { fail(ERROR_INCOMPATIBLE, "RP2040 BOOT ROM not found"); } @@ -1937,11 +1953,11 @@ uint32_t bootrom_func_lookup(memory_access& access, uint16_t tag) { return 0; } -uint32_t bootrom_table_lookup_rp2350(memory_access& access, uint16_t tag, uint16_t flags) { - model_t model = get_model(access); +uint32_t bootrom_table_lookup_v2(memory_access& access, uint16_t tag, uint16_t flags) { + model_t model = access.get_model(); // we are only used on RP2350 - if (model != rp2350) { - fail(ERROR_INCOMPATIBLE, "RP2350 BOOT ROM not found"); + if (model->rom_table_version() != 2) { + fail(ERROR_INCOMPATIBLE, "BOOT ROM TABLE (v2) not found"); } // dereference the table pointer @@ -1983,24 +1999,81 @@ uint32_t bootrom_table_lookup_rp2350(memory_access& access, uint16_t tag, uint16 return 0; } -static uint32_t get_rom_git_revision(memory_access &raw_access) { - unsigned int addr = bootrom_table_lookup_rp2350(raw_access, rom_table_code('G','R'), RT_FLAG_DATA); +static uint32_t get_rom_git_revision_v2(memory_access &raw_access) { + unsigned int addr = bootrom_table_lookup_v2(raw_access, rom_table_code('G','R'), RT_FLAG_DATA); return raw_access.read_int(addr); } -static rp2350_version_t get_rp2350_version(memory_access &raw_access) { - switch (get_rom_git_revision(raw_access)) { - case 0x312e22fa: - return rp2350_a2; - default: - return rp2350_unknown; +static model_t determine_model(memory_access &raw_access) { + auto raw = raw_access.read_int(BOOTROM_MAGIC_ADDR); + auto magic = raw & 0xf0ffffff; // ignoring bootrom version + if (magic == BOOTROM_MAGIC_RP2040) { + return std::make_shared(); + } else if (magic == BOOTROM_MAGIC_RP2350) { + uint32_t table_entry = raw_access.read_short(BOOTROM_MAGIC_ADDR + 4); + static_assert(ROM_END_RP2350 == 0x8000, ""); + if (table_entry < 0x8000) { + return std::make_shared(); + } else { + return std::make_shared(0x10000); + } + } + return models::unknown; +} + +static inline bool is_transfer_aligned(uint32_t addr, const model_t& model) { + enum memory_type t = get_memory_type(addr, model); + return t != invalid && !(t == flash && addr & (PAGE_SIZE-1)); +} + +// this must be called after the right model is set on raw_access which is why it isn't +// part of init_model +static chip_revision_t determine_chip_revision(memory_access &raw_access) { + chip_revision_t chip_revision = unknown_revision; + model_t model = raw_access.get_model(); + uint8_t rom_version; + raw_access.read_raw(0x13, rom_version); + if (model->chip() == rp2040) { + switch (rom_version) { + case 1: + chip_revision = rp2040_b0; + break; + case 2: + chip_revision = rp2040_b1; + break; + case 3: + chip_revision = rp2040_b2; + break; + default: + break; + }; + } else if (model->chip() == rp2350) { + // switch (get_rom_git_revision_v2(raw_access)) { + // default: + // break; + // } + switch (rom_version) { + case 2: + chip_revision = rp2350_a2; + break; + case 3: + chip_revision = rp2350_a3; + break; + case 4: + chip_revision = rp2350_a4; + break; + default: + break; + }; } + return chip_revision; } #if HAS_LIBUSB struct picoboot_memory_access : public memory_access { - model_t model = unknown; // must be initialized to something up front as it is referenced before it is set to its final value explicit picoboot_memory_access(picoboot::connection &connection) : connection(connection) { - model = get_model(*this); + model = determine_model(*this); + if (model->chip() != unknown) + model->set_chip_revision(determine_chip_revision(*this)); } bool is_device() override { @@ -2078,7 +2151,7 @@ struct picoboot_memory_access : public memory_access { if (flash == get_memory_type(address, model)) { connection.exit_xip(); } - if (model == rp2040 && rom == get_memory_type(address, model) && (address+size) >= 0x2000) { + if (model->chip() == rp2040 && rom == get_memory_type(address, model) && (address+size) >= 0x2000) { // read by memcpy instead unsigned int program_base = SRAM_START + 0x4000; // program is "return memcpy(SRAM_BASE, 0, 0x4000);" @@ -2086,22 +2159,26 @@ struct picoboot_memory_access : public memory_access { 0x07482101, // movs r1, #1; lsls r0, r1, #29 0x2100038a, // lsls r2, r1, #14; movs r1, #0 0x47184b00, // ldr r3, [pc, #0]; bx r3 - bootrom_func_lookup(*this, rom_table_code('M','C')) + bootrom_func_lookup_rp2040(*this, rom_table_code('M','C')) }; write_vector(program_base, program); connection.exec(program_base); // 4k is copied into the start of RAM connection.read(SRAM_START + address, (uint8_t *) buffer, size); - } else if (model == rp2350 && rom == get_memory_type(address, model) && (address+size) > 0x7e00) { - // Cannot read end section of rom from device - uint16_t unreadable_start = MAX(address, 0x7e00); - uint16_t unreadable_end = MIN(address+size, 0x8000); + } else if (contains_unreadable_rom(address, size, model)) { + // Cannot read end section of rom from device, so use the saved header for the chip + const unsigned char *unreadable_data = model->unreadable_rom_data(); + if (unreadable_data == nullptr) { + fail(ERROR_INCOMPATIBLE, "Cannot read unreadable ROM data for %s revision %s", model->name().c_str(), model->revision_name().c_str()); + } + uint16_t unreadable_start = MAX(address, model->unreadable_rom_start()); + uint16_t unreadable_end = MIN(address+size, model->unreadable_rom_end()); uint16_t idx = 0; if (address < unreadable_start) { connection.read(address, (uint8_t *) buffer, unreadable_start - address); idx += unreadable_start - address; } - memcpy(buffer+idx, rp2350_rom+(unreadable_start-0x7e00), unreadable_end - unreadable_start); + memcpy(buffer+idx, unreadable_data+(unreadable_start-(model->unreadable_rom_start())), unreadable_end - unreadable_start); idx += unreadable_end - unreadable_start; if (address + size > unreadable_end) { connection.read(unreadable_end, (uint8_t *) buffer+idx, size - idx); @@ -2198,10 +2275,10 @@ struct iostream_memory_access : public memory_access { void read(uint32_t address, uint8_t *buffer, uint32_t size, bool zero_fill) override { if (address == BOOTROM_MAGIC_ADDR && size == 4) { // return the memory model - if (model == rp2040) { + if (model->chip()== rp2040) { *(uint32_t*)buffer = BOOTROM_MAGIC_RP2040; return; - } else if (model == rp2350) { + } else if (model->chip() == rp2350) { *(uint32_t*)buffer = BOOTROM_MAGIC_RP2350; return; } else { @@ -2257,7 +2334,6 @@ struct iostream_memory_access : public memory_access { std::shared_ptrfile; range_map rmap; uint32_t binary_start; - model_t model = unknown; }; @@ -2323,7 +2399,7 @@ struct remapped_memory_access : public memory_access { struct partition_memory_access : public memory_access { partition_memory_access(memory_access &wrap, uint32_t partition_start) : wrap(wrap), partition_start(partition_start) { - model = get_model(wrap); + model = wrap.get_model(); } void read(uint32_t address, uint8_t *buffer, unsigned int size, bool zero_fill) override { @@ -2350,7 +2426,6 @@ struct partition_memory_access : public memory_access { private: memory_access& wrap; uint32_t partition_start; - model_t model; }; static void read_and_check_elf32_header(std::shared_ptrin, elf32_header& eh_out) { @@ -2360,7 +2435,7 @@ static void read_and_check_elf32_header(std::shared_ptrin, elf32_ } try { rp_check_elf_header(eh_out); - } catch (command_failure &e) { + } catch (failure_error &e) { fail(e.code(), "'" + settings.filenames[0] +"' failed validation - " + e.what()); } } @@ -2380,12 +2455,12 @@ struct binary_info_header { bool find_binary_info(memory_access& access, binary_info_header &hdr) { uint32_t base = access.get_binary_start(); - model_t model = get_model(access); + model_t model = access.get_model(); if (!base) { fail(ERROR_FORMAT, "UF2 file does not contain a valid RP2 executable image"); } uint32_t max_dist = 256; - if (model == rp2040) { + if (model->chip() == rp2040) { max_dist = 64; if (base == FLASH_START) base += 0x100; // skip the boot2 } @@ -2470,9 +2545,9 @@ struct bi_visitor_base { void visit(memory_access& access, const binary_info_header& hdr) { try { - model = get_model(access); + chip = access.get_model()->chip(); } catch (not_mapped_exception&) { - model = rp2040; + chip = rp2040; } for (const auto &a : hdr.bi_addr) { visit(access, a); @@ -2580,7 +2655,7 @@ struct bi_visitor_base { } virtual void pins(uint64_t pin_mask, int func, string name) { - if (model == rp2350) { + if (chip == rp2350) { pins(pin_mask, func, name, pin_functions_rp2350); } else { pins(pin_mask, func, name, pin_functions_rp2040); @@ -2632,7 +2707,7 @@ struct bi_visitor_base { } - model_t model = model_t::unknown; + chip_t chip = chip_t::unknown; }; struct bi_visitor : public bi_visitor_base { @@ -2906,7 +2981,7 @@ uint32_t build_rmap_uf2(std::shared_ptrfile, range_map& r if (block.flags & UF2_FLAG_FAMILY_ID_PRESENT && !(block.flags & UF2_FLAG_NOT_MAIN_FLASH) && block.payload_size == PAGE_SIZE && (!family_id || block.file_size == family_id)) { - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 // ignore the absolute block, but save the address if (check_abs_block(block)) { DEBUG_LOG("Ignoring RP2350-E10 absolute block\n"); @@ -2922,11 +2997,11 @@ uint32_t build_rmap_uf2(std::shared_ptrfile, range_map& r next_family_id = 0; #endif } else if (block.file_size != family_id && family_id && !next_family_id) { - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 if (!check_abs_block(block)) { #endif next_family_id = block.file_size; - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 } #endif } @@ -2943,7 +3018,7 @@ void build_rmap_load_map(std::shared_ptrload_map, range_map& rmap) { } } } - if (get_memory_type(binary_start, rp2350) == invalid) { // pick biggest (rp2350) here for now + if (get_memory_type(binary_start, models::largest) == invalid) { // pick biggest (rp2350) here for now return 0; } return binary_start; @@ -3190,6 +3265,12 @@ void insert_default_families(uint32_t flags_and_permissions, vector if (flags_and_permissions & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_DATA_BITS) family_ids.emplace_back(family_name(DATA_FAMILY_ID)); } +static chip_t image_type_exe_chip_to_chip(uint image_type_exe_chip) { + static_assert((int)chip_rp2040 == (int)rp2040, ""); + static_assert((int)chip_rp2350 == (int)rp2350, ""); + return (chip_t)image_type_exe_chip; +} + #if HAS_LIBUSB void info_guts(memory_access &raw_access, picoboot::connection *con) { #else @@ -3253,31 +3334,25 @@ void info_guts(memory_access &raw_access, void *con) { if (image_def != nullptr) { if (verbose_metadata) info_pair("block type", "image def"); if (image_def->image_type() == type_exe) { - switch (image_def->chip()) { - case chip_rp2040: - info_pair("target chip", "RP2040"); - break; - case chip_rp2350: - info_pair("target chip", "RP2350"); - switch (image_def->cpu()) { - case cpu_riscv: - info_pair("image type", "RISC-V"); - break; - case cpu_varmulet: - info_pair("image type", "Varmulet"); - break; - case cpu_arm: - if (image_def->security() == sec_s) { - info_pair("image type", "ARM Secure"); - } else if (image_def->security() == sec_ns) { - info_pair("image type", "ARM Non-Secure"); - } else if (image_def->security() == sec_unspecified) { - info_pair("image type", "ARM"); - } - } - break; - default: - break; + info_pair("target chip", chip_name(image_type_exe_chip_to_chip(image_def->chip()))); + if (image_def->chip() != chip_rp2040) { + switch (image_def->cpu()) { + case cpu_riscv: + info_pair("image type", "RISC-V"); + break; + case cpu_varmulet: + info_pair("image type", "Varmulet"); + break; + case cpu_arm: + if (image_def->security() == sec_s) { + info_pair("image type", "ARM Secure"); + } else if (image_def->security() == sec_ns) { + info_pair("image type", "ARM Non-Secure"); + } else if (image_def->security() == sec_unspecified) { + info_pair("image type", "ARM"); + } + break; + } } } else if (image_def->image_type() == type_data) { info_pair("image type", "data"); @@ -3368,7 +3443,8 @@ void info_guts(memory_access &raw_access, void *con) { ss << "Clear 0x" << std::hex << e.runtime_address; ss << "->0x" << std::hex << e.runtime_address + e.size; } else if (e.storage_address != e.runtime_address) { - if (is_address_initialized(rp2350_address_ranges_flash, e.runtime_address)) { + address_ranges ranges_flash = address_ranges_flash(raw_access.get_model()); + if (is_address_initialized(ranges_flash, e.runtime_address)) { ss << "ERROR: COPY TO FLASH NOT PERMITTED "; } ss << "Copy 0x" << std::hex << e.storage_address; @@ -3640,8 +3716,8 @@ void info_guts(memory_access &raw_access, void *con) { if (best_block && (settings.info.show_basic || settings.info.all)) { select_group(program_info); info_metadata(best_block.get()); - } else if (!best_block && has_binary_info && get_model(raw_access) == rp2350) { - fos << "WARNING: Binary on RP2350 device does not contain a block loop - this binary will not boot\n"; + } else if (!best_block && has_binary_info && raw_access.get_model()->requires_block_loop()) { + fos << "WARNING: Binary on " << raw_access.get_model()->name() << " device does not contain a block loop - this binary will not boot\n"; } } catch (std::invalid_argument &e) { fos << "Error reading binary info\n"; @@ -3655,29 +3731,14 @@ void info_guts(memory_access &raw_access, void *con) { std::vector> device_state_pairs; if ((settings.info.show_device || settings.info.all) && raw_access.is_device()) { select_group(device_info); - model_t model = get_model(raw_access); + model_t model = raw_access.get_model(); uint8_t rom_version; raw_access.read_raw(0x13, rom_version); - if (model == rp2040) { - info_pair("type", "RP2040"); - if (settings.info.show_debug || settings.info.all) { - switch (rom_version) { - case 1: - info_pair("revision", "B0"); - break; - case 2: - info_pair("revision", "B1"); - break; - case 3: - info_pair("revision", "B2"); - break; - default: - info_pair("revision", "Unknown"); - break; - } - } - } else if (model == rp2350) { - info_pair("type", "RP2350"); + info_pair("type", model->name()); + if (settings.info.show_debug || settings.info.all) { + info_pair("revision", model->revision_name()); + } + if (model->supports_picoboot_cmd(PC_GET_INFO)) { assert(con); struct picoboot_get_info_cmd info_cmd; info_cmd.bType = PICOBOOT_GET_INFO_SYS, @@ -3688,38 +3749,30 @@ void info_guts(memory_access &raw_access, void *con) { SYS_INFO_CHIP_INFO | SYS_INFO_CRITICAL | SYS_INFO_CPU_INFO | SYS_INFO_FLASH_DEV_INFO); uint32_t word_buf[64]; - auto version = get_rp2350_version(raw_access); - if (settings.info.show_debug || settings.info.all) { - switch (version) { - case rp2350_a2: - info_pair("revision", "A2"); - break; - default: - info_pair("revision", "Unknown"); - break; - } - } con->get_info(&info_cmd, (uint8_t *) word_buf, sizeof(word_buf)); uint32_t *data = word_buf; unsigned int word_count = *data++; unsigned int included = *data++; if (included & SYS_INFO_CHIP_INFO) { // package_id, device_id, wafer_id - struct picoboot_otp_cmd otp_cmd; - uint16_t num_gpios = 0; - otp_cmd.wRow = OTP_DATA_NUM_GPIOS_ROW; - otp_cmd.wRowCount = 1; - otp_cmd.bEcc = 1; - con->otp_read(&otp_cmd, (uint8_t *)&num_gpios, sizeof(num_gpios)); - if (num_gpios == 30) { - info_pair("package", "QFN60"); - } else if (num_gpios == 48) { - info_pair("package", "QFN80"); + if (model->chip_revision() == rp2350_a2) { + // On A2, package_id is incorrect, so we use num_gpios instead + struct picoboot_otp_cmd otp_cmd; + uint16_t num_gpios = 0; + otp_cmd.wRow = OTP_DATA_NUM_GPIOS_ROW; + otp_cmd.wRowCount = 1; + otp_cmd.bEcc = 1; + con->otp_read(&otp_cmd, (uint8_t *)&num_gpios, sizeof(num_gpios)); + if (num_gpios == 30) { + info_pair("package", "QFN60"); + } else if (num_gpios == 48) { + info_pair("package", "QFN80"); + } else { + info_pair("package", "unknown"); + } } else { - info_pair("package", "unknown"); + info_pair("package", data[0] ? "QFN60" : "QFN80"); } - // Not correct on A2 - // info_pair("package", data[0] ? "QFN60" : "QFN80"); info_pair("chipid", hex_string(data[1] | (uint64_t)data[2] << 32, 16)); data += 3; } @@ -3792,7 +3845,7 @@ void info_guts(memory_access &raw_access, void *con) { } } if (settings.info.show_debug || settings.info.all) { - info_pair("rom gitrev", hex_string(get_rom_git_revision(raw_access))); + info_pair("rom gitrev", hex_string(get_rom_git_revision_v2(raw_access))); } select_group(device_info); } @@ -3801,7 +3854,7 @@ void info_guts(memory_access &raw_access, void *con) { int32_t size_guess = guess_flash_size(raw_access); if (size_guess > 0) { info_pair("flash size", std::to_string(size_guess/1024) + "K"); - if (model == rp2040) { + if (model->chip() == rp2040) { uint64_t flash_id = 0; con->flash_id(flash_id); info_pair("flash id", hex_string(flash_id, 16, true, true)); @@ -4016,8 +4069,7 @@ bool help_command::execute(device_map &devices) { return false; } -uint32_t get_access_family_id(memory_access &file_access) { - uint32_t family_id = 0; +model_t get_access_model(memory_access &file_access) { vector bin; std::unique_ptr best_block = find_best_block(file_access, bin); if (best_block == NULL) { @@ -4029,36 +4081,40 @@ uint32_t get_access_family_id(memory_access &file_access) { if (checksum == calc_checksum(checksum_data)) { // Checksum is correct, so RP2040 DEBUG_LOG("Detected family ID %s due to boot2 checksum\n", family_name(RP2040_FAMILY_ID).c_str()); - return RP2040_FAMILY_ID; + return std::make_shared(); } else { // Checksum incorrect, so absolute DEBUG_LOG("Assumed family ID %s\n", family_name(ABSOLUTE_FAMILY_ID).c_str()); - return ABSOLUTE_FAMILY_ID; + model_t ret = std::make_shared(); + ret->set_family_id(ABSOLUTE_FAMILY_ID); + return ret; } } else { // no_flash RP2040 binaries have no checksum DEBUG_LOG("Assumed family ID %s\n", family_name(RP2040_FAMILY_ID).c_str()); - return RP2040_FAMILY_ID; + return std::make_shared(); } } auto first_item = best_block->items[0].get(); if (first_item->type() != PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE) { // This will apply for partition tables DEBUG_LOG("Assumed family ID %s due to block with no IMAGE_DEF\n", family_name(ABSOLUTE_FAMILY_ID).c_str()); - return ABSOLUTE_FAMILY_ID; + model_t ret = std::make_shared(); + ret->set_family_id(ABSOLUTE_FAMILY_ID); + return ret; } auto image_def = dynamic_cast(first_item); if (image_def->image_type() == type_exe) { if (image_def->chip() == chip_rp2040) { - family_id = RP2040_FAMILY_ID; + return std::make_shared(); } else if (image_def->chip() == chip_rp2350) { if (image_def->cpu() == cpu_riscv) { - family_id = RP2350_RISCV_FAMILY_ID; + return std::make_shared(); } else if (image_def->cpu() == cpu_arm) { if (image_def->security() == sec_s) { - family_id = RP2350_ARM_S_FAMILY_ID; + return std::make_shared(); } else if (image_def->security() == sec_ns) { - family_id = RP2350_ARM_NS_FAMILY_ID; + return std::make_shared(); } else { fail(ERROR_INCOMPATIBLE, "Cannot autodetect UF2 family: Unsupported security level %x\n", image_def->security()); } @@ -4069,12 +4125,14 @@ uint32_t get_access_family_id(memory_access &file_access) { fail(ERROR_INCOMPATIBLE, "Cannot autodetect UF2 family: Unsupported chip %x\n", image_def->chip()); } } else if (image_def->image_type() == type_data) { - family_id = DATA_FAMILY_ID; + model_t ret = std::make_shared(); + ret->set_family_id(DATA_FAMILY_ID); + return ret; } else { fail(ERROR_INCOMPATIBLE, "Cannot autodetect UF2 family: Unsupported image type %x\n", image_def->image_type()); } - return family_id; + return models::unknown; } uint32_t get_family_id(uint8_t file_idx) { @@ -4083,12 +4141,12 @@ uint32_t get_family_id(uint8_t file_idx) { family_id = settings.family_id; } else if (get_file_type_idx(file_idx) == filetype::elf || get_file_type_idx(file_idx) == filetype::bin) { auto file_access = get_file_memory_access(file_idx); - family_id = get_access_family_id(file_access); + family_id = get_access_model(file_access)->family_id(); } else if (get_file_type_idx(file_idx) == filetype::uf2) { auto file = get_file_idx(ios::in|ios::binary, file_idx); uf2_block block; file->read((char*)&block, sizeof(block)); - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 // ignore the absolute block if (check_abs_block(block)) { DEBUG_LOG("Ignoring RP2350-E10 absolute block\n"); @@ -4104,17 +4162,29 @@ uint32_t get_family_id(uint8_t file_idx) { return family_id; } +model_t get_model(uint8_t file_idx) { + model_t model; + if (settings.model) { + model = settings.model; + } else { + auto file_access = get_file_memory_access(file_idx); + model = get_access_model(file_access); + } + // Clear the family ID, as get_family_id should be used for that, to allow command line override + model->set_family_id(0); + return model; +} + #if HAS_LIBUSB std::shared_ptr>> get_partitions(picoboot::connection &con) { picoboot_memory_access raw_access(con); - if (get_model(raw_access) != rp2350) { - // Not an rp2350, so no partitions + auto model = raw_access.get_model(); + if (!model->supports_partition_table()) { + // no partition table return nullptr; } -#if SUPPORT_A2 - con.exit_xip(); -#endif + if (model->chip_revision() == rp2350_a2) con.exit_xip(); uint8_t loc_flags_id_buf[256]; uint8_t family_id_name_buf[256]; @@ -4142,7 +4212,6 @@ std::shared_ptr>> get_partitions(picoboot::conn } if (has_pt) { - auto rp2350_version = get_rp2350_version(raw_access); for (unsigned int i = 0; i < partition_count; i++) { uint32_t location_and_permissions = loc_flags_id_buf_32[lfi_pos++]; uint32_t flags_and_permissions = loc_flags_id_buf_32[lfi_pos++]; @@ -4183,7 +4252,7 @@ bool config_command::execute(device_map &devices) { fos << "Multiple RP-series devices in BOOTSEL mode found:\n"; } for (auto handles : devices[dr_vidpid_bootrom_ok]) { - selected_model = std::get<0>(handles); + selected_chip = std::get<0>(handles); fos.first_column(0); fos.hanging_indent(0); if (size > 1) { auto s = bus_device_string(std::get<1>(handles), std::get<0>(handles)); @@ -4220,18 +4289,18 @@ bool config_command::execute(device_map &devices) { return false; } +void set_model_from_metadata(file_memory_access& access) { + auto model = get_access_model(access); + access.set_model(model); +} + bool info_command::execute(device_map &devices) { fos.first_column(0); fos.hanging_indent(0); if (!settings.filenames[0].empty()) { uint32_t next_id = 0; auto access = get_file_memory_access(0, false, &next_id); - uint32_t id = 0; - id = get_family_id(0); - if (id == RP2040_FAMILY_ID) { - access.set_model(rp2040); - } else if (id >= RP2350_ARM_S_FAMILY_ID && id <= RP2350_ARM_NS_FAMILY_ID) { - access.set_model(rp2350); - } + set_model_from_metadata(access); + uint32_t id = get_family_id(0); if (next_id) { next_id = id; while (next_id) { @@ -4245,6 +4314,7 @@ bool info_command::execute(device_map &devices) { } fos << s.str() << "\n\n"; auto tmp_access = get_file_memory_access(0, false, &next_id); + set_model_from_metadata(tmp_access); info_guts(tmp_access, nullptr); } } else { @@ -4264,7 +4334,7 @@ bool info_command::execute(device_map &devices) { fos << "Multiple RP-series devices in BOOTSEL mode found:\n"; } for (auto handles : devices[dr_vidpid_bootrom_ok]) { - selected_model = std::get<0>(handles); + selected_chip = std::get<0>(handles); fos.first_column(0); fos.hanging_indent(0); if (size > 1) { auto s = bus_device_string(std::get<1>(handles), std::get<0>(handles)); @@ -4341,18 +4411,19 @@ bool info_command::execute(device_map &devices) { static picoboot::connection get_single_bootsel_device_connection(device_map& devices, bool exclusive = true) { assert(devices[dr_vidpid_bootrom_ok].size() == 1); auto device = devices[dr_vidpid_bootrom_ok][0]; - selected_model = std::get<0>(device); + selected_chip = std::get<0>(device); libusb_device_handle *rc = std::get<2>(device); if (!rc) fail(ERROR_USB, "Unable to connect to device"); - return picoboot::connection(rc, std::get<0>(device), exclusive); + return picoboot::connection(rc, exclusive); } -static picoboot::connection get_single_rp2350_bootsel_device_connection(device_map& devices, bool exclusive = true) { +static picoboot::connection get_single_picoboot_cmd_compatible_device_connection(const std::string& cmd_name, device_map& devices, std::set picoboot_cmds, bool exclusive = true) { auto con = get_single_bootsel_device_connection(devices, exclusive); // todo amy we may have a different VID PID? picoboot_memory_access raw_access(con); - if (get_model(raw_access) != rp2350) { - fail(ERROR_INCOMPATIBLE, "RP2350 command cannot be used with a non RP2350 device"); + std::string failed_device_name; + if (!raw_access.get_model()->supports_picoboot_cmds(picoboot_cmds, failed_device_name)) { + fail(ERROR_INCOMPATIBLE, "'%s' command cannot be used with a %s device", cmd_name.c_str(), failed_device_name.c_str()); } return con; } @@ -4475,7 +4546,7 @@ bool save_command::execute(device_map &devices) { } } - model_t model = get_model(raw_access); + model_t model = raw_access.get_model(); enum memory_type t1 = get_memory_type(start , model); enum memory_type t2 = get_memory_type(end, model); if (t1 != t2 || t1 == invalid || t1 == sram_unstriped) { @@ -4508,7 +4579,7 @@ bool save_command::execute(device_map &devices) { block.flags = UF2_FLAG_FAMILY_ID_PRESENT; block.payload_size = PAGE_SIZE; block.num_blocks = (size + PAGE_SIZE - 1)/PAGE_SIZE; - block.file_size = settings.family_id ? settings.family_id : get_access_family_id(raw_access); + block.file_size = settings.family_id ? settings.family_id : get_access_model(raw_access)->family_id(); block.magic_end = UF2_MAGIC_END; writer256 = [&](FILE *out, const uint8_t *buffer, unsigned int size, unsigned int offset) { static_assert(512 == sizeof(block), ""); @@ -4523,7 +4594,7 @@ bool save_command::execute(device_map &devices) { }; break; default: - throw command_failure(-1, "Unsupported output file type"); + throw failure_error(-1, "Unsupported output file type"); } FILE *out = fopen(settings.filenames[0].c_str(), "wb"); if (out) { @@ -4556,7 +4627,7 @@ bool save_command::execute(device_map &devices) { if (settings.save.verify) { raw_access.clear_cache(); auto file_access = get_file_memory_access(0); - model_t model = get_model(raw_access); + model_t model = raw_access.get_model(); auto ranges = get_coalesced_ranges(file_access, model); for (auto mem_range : ranges) { enum memory_type type = get_memory_type(mem_range.from, model); @@ -4647,7 +4718,7 @@ bool erase_command::execute(device_map &devices) { } } - model_t model = get_model(raw_access); + model_t model = raw_access.get_model(); enum memory_type t1 = get_memory_type(start , model); enum memory_type t2 = get_memory_type(end, model); if (t1 != flash || t1 != t2) { @@ -4670,9 +4741,9 @@ bool erase_command::execute(device_map &devices) { #if HAS_LIBUSB bool get_target_partition(picoboot::connection &con, uint32_t* start = nullptr, uint32_t* end = nullptr) { -#if SUPPORT_A2 - con.exit_xip(); -#endif + picoboot_memory_access raw_access(con); + auto model = raw_access.get_model(); + if (model->chip_revision() == rp2350_a2) con.exit_xip(); uint8_t loc_flags_id_buf[256]; uint32_t *loc_flags_id_buf_32 = (uint32_t *)loc_flags_id_buf; @@ -4721,7 +4792,7 @@ bool load_guts(picoboot::connection con, iostream_memory_access &file_access) { visitor.visit(access, hdr); } } - model_t model = get_model(raw_access); + model_t model = raw_access.get_model(); auto ranges = get_coalesced_ranges(file_access, model); bool uses_flash = false; uint32_t flash_min = std::numeric_limits::max(); @@ -4860,7 +4931,7 @@ bool load_guts(picoboot::connection con, iostream_memory_access &file_access) { if (!start) { fail(ERROR_FORMAT, "Cannot execute as file does not contain a valid RP2 executable image"); } - if (get_model(raw_access) == rp2350) { + if (raw_access.get_model()->supports_picoboot_cmd(PC_REBOOT2)) { struct picoboot_reboot2_cmd cmd; auto mt = get_memory_type(start, model); if (mt == flash) { @@ -4872,13 +4943,13 @@ bool load_guts(picoboot::connection con, iostream_memory_access &file_access) { unsigned int end; switch (mt) { case sram: - end = SRAM_END_RP2350; + end = model->sram_end(); break; case xip_sram: - end = XIP_SRAM_END_RP2350; + end = model->xip_sram_end(); break; default: - end = SRAM_END_RP2350; + end = model->sram_end(); } cmd.dParam1 = end - start; cmd.dFlags = REBOOT2_FLAG_REBOOT_TYPE_RAM_IMAGE; @@ -4888,7 +4959,7 @@ bool load_guts(picoboot::connection con, iostream_memory_access &file_access) { con.reboot2(&cmd); } else { con.reboot(flash == get_memory_type(start, model) ? 0 : start, - model == rp2040 ? SRAM_END_RP2040 : SRAM_END_RP2350, 500); + model->sram_end(), 500); } std::cout << "\nThe device was rebooted to start the application.\n"; return true; @@ -4920,7 +4991,7 @@ bool load_command::execute(device_map &devices) { settings.family_id = family_id; uint32_t start; uint32_t end; - if (get_model(raw_access) != rp2040) { + if (raw_access.get_model()->supports_partition_table()) { if (get_target_partition(con, &start, &end)) { settings.offset = start + FLASH_START; settings.offset_set = true; @@ -4937,7 +5008,7 @@ bool load_command::execute(device_map &devices) { } } auto file_access = get_file_memory_access(0); - if (settings.offset_set && get_file_type() != filetype::bin && get_model(raw_access) == rp2040) { + if (settings.offset_set && get_file_type() != filetype::bin && raw_access.get_model()->chip() == rp2040) { fail(ERROR_ARGS, "Offset only valid for BIN files"); } bool ret = load_guts(con, file_access); @@ -5327,7 +5398,8 @@ bool encrypt_command::execute(device_map &devices) { *tmp << file->rdbuf(); auto program = get_iostream_memory_access(tmp, filetype::elf, true); - program.set_model(rp2350); + // todo should be determined from image_def + program.set_model(std::make_shared()); // data_start_addr settings.config.key = "data_start_addr"; @@ -5675,7 +5747,7 @@ bool seal_command::execute(device_map &devices) { auto tmp = std::make_shared(); tmp->write(reinterpret_cast(sig_data.data()), sig_data.size()); auto out = get_file_idx(ios::out|ios::binary, 1); - bin2uf2(tmp, out, bin_start, family_id, settings.uf2.abs_block_loc); + bin2uf2(tmp, out, bin_start, family_id, access.get_model(), settings.uf2.abs_block_loc); out->close(); } else { fail(ERROR_ARGS, "Must be ELF or BIN"); @@ -5719,13 +5791,7 @@ bool seal_command::execute(device_map &devices) { if (!settings.quiet) { auto access = get_file_memory_access(1); - uint32_t id = 0; - id = get_family_id(1); - if (id == RP2040_FAMILY_ID) { - access.set_model(rp2040); - } else if (id >= RP2350_ARM_S_FAMILY_ID && id <= RP2350_ARM_NS_FAMILY_ID) { - access.set_model(rp2350); - } + set_model_from_metadata(access); fos << "Output File " << settings.filenames[1] << ":\n\n"; settings.info.show_basic = true; info_guts(access, nullptr); @@ -5851,8 +5917,8 @@ bool verify_command::execute(device_map &devices) { auto file_access = get_file_memory_access(0); auto con = get_single_bootsel_device_connection(devices); picoboot_memory_access raw_access(con); - model_t model = get_model(raw_access); - if (settings.offset_set && get_file_type() != filetype::bin && get_model(raw_access) == rp2040) { + model_t model = raw_access.get_model(); + if (settings.offset_set && get_file_type() != filetype::bin && model->chip() == rp2040) { fail(ERROR_ARGS, "Offset only valid for BIN files"); } auto ranges = get_coalesced_ranges(file_access, model); @@ -6226,11 +6292,11 @@ std::map, otp_match> filter_otp(std::vector #if HAS_LIBUSB bool partition_info_command::execute(device_map &devices) { - auto con = get_single_rp2350_bootsel_device_connection(devices, false); + auto con = get_single_picoboot_cmd_compatible_device_connection("partition info", devices, {PC_GET_INFO}, false); -#if SUPPORT_A2 - con.exit_xip(); -#endif + picoboot_memory_access raw_access(con); + model_t model = raw_access.get_model(); + if (model->chip_revision() == rp2350_a2) con.exit_xip(); uint8_t loc_flags_id_buf[256]; uint8_t family_id_name_buf[256]; @@ -6262,8 +6328,6 @@ bool partition_info_command::execute(device_map &devices) { printf(", uf2 { %s }\n", cli::join(family_ids, ", ").c_str()); if (has_pt) { - picoboot_memory_access raw_access(con); - auto rp2350_version = get_rp2350_version(raw_access); printf("partitions:\n"); for (unsigned int i = 0; i < partition_count; i++) { uint32_t location_and_permissions = loc_flags_id_buf_32[lfi_pos++]; @@ -6418,7 +6482,8 @@ bool partition_create_command::execute(device_map &devices) { uint32_t unpartitioned_flags = permissions_to_flags(pt_json["unpartitioned"]["permissions"]) | families_to_flags(pt_json["unpartitioned"]["families"]); partition_table_item pt(unpartitioned_flags, settings.partition.singleton); -#if SUPPORT_A2 +#if SUPPORT_RP2350_A2 + // todo fix test if (!(unpartitioned_flags & PICOBIN_PARTITION_FLAGS_ACCEPTS_DEFAULT_FAMILY_ABSOLUTE_BITS)) { fail(ERROR_INCOMPATIBLE, "Unpartitioned space must accept the absolute family, for the RP2350-E10 fix to work"); } @@ -6442,7 +6507,7 @@ bool partition_create_command::execute(device_map &devices) { } cur_pos = start + size; - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 if (start <= (settings.uf2.abs_block_loc - FLASH_START)/0x1000 && start + size > (settings.uf2.abs_block_loc - FLASH_START)/0x1000) { fail(ERROR_INCOMPATIBLE, "The address %" PRIx32 " cannot be in a partition for the RP2350-E10 fix to work", settings.uf2.abs_block_loc); } @@ -6542,7 +6607,7 @@ bool partition_create_command::execute(device_map &devices) { uint32_t address = settings.offset_set ? settings.offset : FLASH_START; auto tmp = std::make_shared(); tmp->write(reinterpret_cast(data.data()), data.size()); - bin2uf2(tmp, out, address, family_id); + bin2uf2(tmp, out, address, family_id, models::largest); } else { out->write(reinterpret_cast(data.data()), data.size()); } @@ -6569,7 +6634,7 @@ bool partition_create_command::execute(device_map &devices) { #if HAS_LIBUSB bool uf2_info_command::execute(device_map &devices) { - auto con = get_single_rp2350_bootsel_device_connection(devices, false); + auto con = get_single_picoboot_cmd_compatible_device_connection("uf2 info", devices, {PC_GET_INFO}, false); uint32_t buf[5]; picoboot_get_info_cmd cmd; cmd.bType = PICOBOOT_GET_INFO_UF2_STATUS; @@ -6626,10 +6691,11 @@ bool uf2_convert_command::execute(device_map &devices) { } uint32_t family_id = get_family_id(0); + model_t model = get_model(0); auto in = get_file(ios::in|ios::binary); auto out = get_file_idx(ios::out|ios::binary, 1); - #if SUPPORT_A2 + #if SUPPORT_RP2350_A2 // RP2350-E10 : add absolute block if (settings.uf2.abs_block) { fos << "RP2350-E10: Adding absolute block to UF2 targeting " << hex_string(settings.uf2.abs_block_loc) << "\n"; @@ -6639,10 +6705,10 @@ bool uf2_convert_command::execute(device_map &devices) { #endif if (get_file_type() == filetype::elf) { uint32_t package_address = settings.offset_set ? settings.offset : 0; - elf2uf2(in, out, family_id, package_address, settings.uf2.abs_block_loc, settings.verbose); + elf2uf2(in, out, family_id, model, package_address, settings.uf2.abs_block_loc, settings.verbose); } else if (get_file_type() == filetype::bin) { uint32_t address = settings.offset_set ? settings.offset : FLASH_START; - bin2uf2(in, out, address, family_id, settings.uf2.abs_block_loc, settings.verbose); + bin2uf2(in, out, address, family_id, model, settings.uf2.abs_block_loc, settings.verbose); } else { fail(ERROR_ARGS, "Convert currently only from ELF/BIN to UF2\n"); } @@ -7393,9 +7459,18 @@ static void check_otp_write_error(picoboot::command_failure &e, bool ecc) { } } +bool settings_select_ecc(void) { + return settings.otp.ecc && !settings.otp.raw; +} + +uint8_t otp_cmd_max_bits(void) { + return settings_select_ecc() ? 16 : 24; +} + typedef std::function otp_read_func_t; typedef std::function otp_write_func_t; -void process_otp_json(json &otp_json, otp_read_func_t read_func, otp_write_func_t write_func) { +void process_otp_json(json &otp_json, model_t model, otp_read_func_t read_func, otp_write_func_t write_func) { + int raw_max_bits = 24; for (auto row : otp_json.items()) { fos.first_column(0); string row_key = row.key(); @@ -7404,13 +7479,13 @@ void process_otp_json(json &otp_json, otp_read_func_t read_func, otp_write_func_ // Find matching OTP row bool is_sequence = false; - auto row_matches = filter_otp({row_key}, 24, true); - if (row_matches.size() == 0) { + auto row_matches = filter_otp({row_key}, raw_max_bits, true); + if (row_matches.empty()) { fail(ERROR_INCOMPATIBLE, "%s does not match an otp row", row_key.c_str()); } else if (row_matches.size() != 1) { // Check if it is a sequence - auto row_seq0_matches = filter_otp({row_key + "0"}, 24, false); - auto row_seq_0_matches = filter_otp({row_key + "_0"}, 24, false); + auto row_seq0_matches = filter_otp({row_key + "0"}, raw_max_bits, false); + auto row_seq_0_matches = filter_otp({row_key + "_0"}, raw_max_bits, false); if (row_seq0_matches.size() == 1) { row_matches = row_seq0_matches; } else if (row_seq_0_matches.size() == 1) { @@ -7458,7 +7533,7 @@ void process_otp_json(json &otp_json, otp_read_func_t read_func, otp_write_func_ } // Find matching OTP field - auto field_matches = filter_otp({row_key + "." + key}, 24, false); + auto field_matches = filter_otp({row_key + "." + key}, raw_max_bits, false); if (field_matches.size() != 1) { fail(ERROR_INCOMPATIBLE, "%s is not a single otp field", key.c_str()); } @@ -7557,9 +7632,11 @@ static void hack_init_otp_regs() { bool otp_get_command::execute(device_map &devices) { - auto con = get_single_rp2350_bootsel_device_connection(devices); + auto con = get_single_picoboot_cmd_compatible_device_connection("otp get", devices, {PC_OTP_READ}, false); hack_init_otp_regs(); - auto matches = filter_otp(settings.otp.selectors, settings.otp.ecc ? 16 : 24, settings.otp.fuzzy); + picoboot_memory_access raw_access(con); + auto model = raw_access.get_model(); + auto matches = filter_otp(settings.otp.selectors, otp_cmd_max_bits(), settings.otp.fuzzy); uint32_t last_reg_row = 1; // invalid bool first = true; char buf[512]; @@ -7567,7 +7644,6 @@ bool otp_get_command::execute(device_map &devices) { memset(raw_buffer, 0xaa, sizeof(raw_buffer)); int indent0 = settings.otp.list_pages ? 18 : 8; uint32_t last_page = -1; - picoboot_memory_access raw_access(con); for (const auto& e : matches) { const auto &m = e.second; bool do_ecc = settings.otp.ecc; @@ -7650,7 +7726,7 @@ bool otp_get_command::execute(device_map &devices) { corrected_val = otp_calculate_ecc(raw_value &0xffff); snprintf(buf, sizeof(buf), "\nVALUE 0x%06x\n", corrected_val); fos << buf; - // todo more clarity over ECC settigns + // todo more clarity over ECC settings // todo recovery if (corrected_val != raw_value) { fos << raw_buf; @@ -7743,7 +7819,9 @@ bool otp_dump_command::execute(device_map &devices) { // Prevent outputting to the console fos_ptr = fos_null_ptr; - process_otp_json(otp_json, + auto model = models::largest; + + process_otp_json(otp_json, model, [&](uint8_t *buffer, uint32_t len, picoboot_otp_cmd &otp_cmd) { memset(buffer, 0, len); }, [&](uint8_t *buffer, uint32_t len, picoboot_otp_cmd &otp_cmd) { @@ -7767,7 +7845,7 @@ bool otp_dump_command::execute(device_map &devices) { ); fos_ptr = fos_base_ptr; } else { - auto con = get_single_rp2350_bootsel_device_connection(devices, false); + auto con = get_single_picoboot_cmd_compatible_device_connection("otp dump", devices, {PC_OTP_READ}, false); struct picoboot_otp_cmd otp_cmd; otp_cmd.bEcc = do_ecc; // Read most pages by page, as permissions are per page @@ -7836,7 +7914,9 @@ bool otp_dump_command::execute(device_map &devices) { } bool otp_load_command::execute(device_map &devices) { - auto con = get_single_rp2350_bootsel_device_connection(devices, false); + auto con = get_single_picoboot_cmd_compatible_device_connection("otp load", devices, {PC_OTP_READ, PC_OTP_WRITE}); + picoboot_memory_access raw_access(con); + auto model = raw_access.get_model(); // todo pre-check page lock struct picoboot_otp_cmd otp_cmd; std::shared_ptr file = get_file(ios::in|ios::binary); @@ -7844,7 +7924,7 @@ bool otp_load_command::execute(device_map &devices) { hack_init_otp_regs(); json otp_json = json::parse(*file); // todo validation on json - process_otp_json(otp_json, + process_otp_json(otp_json, model, [&](uint8_t *buffer, uint32_t len, picoboot_otp_cmd &otp_cmd) { picoboot_memory_access raw_access(con); con.otp_read(&otp_cmd, buffer, len); @@ -7861,7 +7941,7 @@ bool otp_load_command::execute(device_map &devices) { return false; } otp_cmd.wRow = settings.otp.row; - otp_cmd.bEcc = settings.otp.ecc && !settings.otp.raw; + otp_cmd.bEcc = settings_select_ecc(); unsigned int row_size = otp_cmd.bEcc ? 2 : 4; file->seekg(0, ios::end); uint32_t file_size = file->tellg(); @@ -7887,7 +7967,6 @@ bool otp_load_command::execute(device_map &devices) { std::unique_ptr unique_verify_buffer(new uint8_t[file_size]()); uint8_t* verify_buffer = unique_verify_buffer.get(); - picoboot_memory_access raw_access(con); con.otp_read(&otp_cmd, (uint8_t *)verify_buffer, file_size); unsigned int i; for(i=0;i unique_rows; std::transform(matches.begin(), matches.end(), std::inserter(unique_rows, unique_rows.begin()), [](const auto&e) { return e.first.first; }); @@ -8016,7 +8096,6 @@ bool otp_set_command::execute(device_map &devices) { otp_cmd.wRowCount = 1; otp_cmd.bEcc = 0; uint32_t old_raw_value; - picoboot_memory_access raw_access(con); con.otp_read(&otp_cmd, (uint8_t *)&old_raw_value, sizeof(old_raw_value)); fos.first_column(0); fos.hanging_indent(7); @@ -8102,15 +8181,14 @@ bool otp_set_command::execute(device_map &devices) { // OR with current value, to ignore any already-set bits settings.otp.value |= old_raw_value; } - // todo check for clearing bits - if (old_raw_value && settings.otp.ecc) { - fail(ERROR_NOT_POSSIBLE, "Cannot modify OTP ECC row(s)\n"); - } if (~settings.otp.value & old_raw_value) { fail(ERROR_NOT_POSSIBLE, "Cannot clear bits in OTP row(s): current value %06x, new value %06x\n", old_raw_value, settings.otp.value); } - // todo this is currently crappy, incorrect and generally evil - otp_cmd.bEcc = settings.otp.ecc; + otp_cmd.bEcc = settings_select_ecc(); + // todo check for clearing bits instead + if (old_raw_value && otp_cmd.bEcc) { + fail(ERROR_NOT_POSSIBLE, "Cannot modify OTP ECC row(s)\n"); + } try { if (otp_cmd.bEcc) { uint16_t write_value = settings.otp.value; @@ -8133,7 +8211,7 @@ bool otp_set_command::execute(device_map &devices) { } bool otp_permissions_command::execute(device_map &devices) { - auto con = get_single_rp2350_bootsel_device_connection(devices, false); + auto con = get_single_picoboot_cmd_compatible_device_connection("otp permissions", devices, {PC_OTP_READ, PC_OTP_WRITE}); json perms_json = json::parse(*get_file(ios::in|ios::binary)); @@ -8142,7 +8220,8 @@ bool otp_permissions_command::execute(device_map &devices) { *tmp << file->rdbuf(); auto program = get_iostream_memory_access(tmp, filetype::elf, true); - program.set_model(rp2350); + // todo what do we need from this model - if it is generic, we should make a generic one + program.set_model(std::make_unique()); settings.config.group = "otp_page_permissions"; for (auto it = perms_json.begin(); it != perms_json.end(); ++it) { @@ -8277,7 +8356,7 @@ void wl_do_field(json json_data, vector& data, uint32_t& flags, const } bool otp_white_label_command::execute(device_map &devices) { - auto con = get_single_rp2350_bootsel_device_connection(devices, false); + auto con = get_single_picoboot_cmd_compatible_device_connection("otp white-label", devices, {PC_OTP_READ, PC_OTP_WRITE}); hack_init_otp_regs(); const otp_reg* flags_reg; const otp_reg* addr_reg; @@ -8442,7 +8521,7 @@ bool reboot_command::execute(device_map &devices) { if (!settings.switch_cpu.empty()) { fail(ERROR_ARGS, "--cpu may not be specified for forced reboot"); } - selected_model = std::get<0>(devices[dr_vidpid_stdio_usb][0]); + selected_chip = std::get<0>(devices[dr_vidpid_stdio_usb][0]); reboot_device(std::get<1>(devices[dr_vidpid_stdio_usb][0]), std::get<2>(devices[dr_vidpid_stdio_usb][0]), settings.reboot_usb); if (!quiet) { if (settings.reboot_usb) { @@ -8456,8 +8535,8 @@ bool reboot_command::execute(device_map &devices) { // care what else is happening. auto con = get_single_bootsel_device_connection(devices, false); picoboot_memory_access raw_access(con); - model_t model = get_model(raw_access); - if (model == rp2350) { + model_t model = raw_access.get_model(); + if (model->supports_picoboot_cmd(PC_REBOOT2)) { struct picoboot_reboot2_cmd cmd = { .dFlags = (uint8_t)(settings.reboot_usb ? REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL : REBOOT2_FLAG_REBOOT_TYPE_NORMAL), .dDelayMS = 500, @@ -8489,7 +8568,7 @@ bool reboot_command::execute(device_map &devices) { std::vector program = { 0x20002100, // movs r0, #0; movs r1, #0 0x47104a00, // ldr r2, [pc, #0]; bx r2 - bootrom_func_lookup(raw_access, rom_table_code('U', 'B')) + bootrom_func_lookup_rp2040(raw_access, rom_table_code('U', 'B')) }; raw_access.write_vector(program_base, program); @@ -8616,13 +8695,13 @@ int main(int argc, char **argv) { if (settings.bus != -1 && settings.bus != libusb_get_bus_number(*dev)) continue; if (settings.address != -1 && settings.address != libusb_get_device_address(*dev)) continue; libusb_device_handle *handle = nullptr; - model_t model = unknown; - auto result = picoboot_open_device(*dev, &handle, &model, settings.vid, settings.pid, settings.ser.c_str()); + chip_t chip = unknown; + auto result = picoboot_open_device(*dev, &handle, &chip, settings.vid, settings.pid, settings.ser.c_str()); if (handle) { to_close.push_back(handle); } if (result != dr_error) { - devices[result].emplace_back(std::make_tuple(model, *dev, handle)); + devices[result].emplace_back(std::make_tuple(chip, *dev, handle)); } } } @@ -8783,28 +8862,14 @@ int main(int argc, char **argv) { break; } } - } catch (command_failure &e) { + } catch (failure_error &e) { std::cout << "ERROR: " << e.what() << "\n"; rc = e.code(); } catch (picoboot::command_failure& e) { - // todo rp2350/rp2040 - string device = "RP-series"; - if (selected_model == rp2040) { - device = "RP2040"; - } else if (selected_model == rp2350) { - device = "RP2350"; - } - std::cout << "ERROR: The " << device << " device returned an error: " << e.what() << "\n"; + std::cout << "ERROR: The " << chip_name(selected_chip) << " device returned an error: " << e.what() << "\n"; rc = ERROR_UNKNOWN; } catch (picoboot::connection_error&) { - // todo rp2350/rp2040 - string device = "RP-series"; - if (selected_model == rp2040) { - device = "RP2040"; - } else if (selected_model == rp2350) { - device = "RP2350"; - } - std::cout << "ERROR: Communication with " << device << " device failed\n"; + std::cout << "ERROR: Communication with " << chip_name(selected_chip) << " device failed\n"; rc = ERROR_CONNECTION; } catch (cancelled_exception&) { rc = ERROR_CANCELLED; @@ -8827,7 +8892,7 @@ int main(int argc, char **argv) { } try { rc = selected_cmd->execute(devices); - } catch (command_failure &e) { + } catch (failure_error &e) { std::cout << "ERROR: " << e.what() << "\n"; rc = e.code(); } catch (cancelled_exception&) { diff --git a/model/BUILD.bazel b/model/BUILD.bazel new file mode 100644 index 00000000..7b86451b --- /dev/null +++ b/model/BUILD.bazel @@ -0,0 +1,36 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "model", + srcs = [ + "model.cpp", + ], + hdrs = [ + "addresses.h", + "model.h", + "//:rp2350_a2_rom_end.h", + "//:rp2350_a3_rom_end.h", + "//:rp2350_a4_rom_end.h", + ], + includes = ["."], + deps = [ + "//errors", + "@pico-sdk//src/common/boot_uf2_headers", + "@pico-sdk//src/common/boot_picoboot_headers", + ], +) + +filegroup( + name = "rp2350_a2_rom_end_bin", + srcs = ["rp2350_a2_rom_end.bin"], +) + +filegroup( + name = "rp2350_a3_rom_end_bin", + srcs = ["rp2350_a3_rom_end.bin"], +) + +filegroup( + name = "rp2350_a4_rom_end_bin", + srcs = ["rp2350_a4_rom_end.bin"], +) diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt new file mode 100644 index 00000000..cd9e775a --- /dev/null +++ b/model/CMakeLists.txt @@ -0,0 +1,35 @@ +add_library(model STATIC + model.cpp) + +target_include_directories(model PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +target_link_libraries(model PUBLIC boot_uf2_headers boot_picoboot_headers pico_platform_headers errors) + +add_custom_target(unreadable_rom_data DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/rp2350_a2_rom_end.h + ${CMAKE_CURRENT_BINARY_DIR}/rp2350_a3_rom_end.h + ${CMAKE_CURRENT_BINARY_DIR}/rp2350_a4_rom_end.h) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350_a2_rom_end.h + COMMAND ${CMAKE_COMMAND} + -D BINARY_FILE=${CMAKE_CURRENT_LIST_DIR}/rp2350_a2_rom_end.bin + -D OUTPUT_NAME=rp2350_a2_rom_end + -P ${CMAKE_CURRENT_LIST_DIR}/../cmake/binh.cmake + COMMENT "Configuring rp2350_a2_rom_end.h" + VERBATIM) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350_a3_rom_end.h + COMMAND ${CMAKE_COMMAND} + -D BINARY_FILE=${CMAKE_CURRENT_LIST_DIR}/rp2350_a3_rom_end.bin + -D OUTPUT_NAME=rp2350_a3_rom_end + -P ${CMAKE_CURRENT_LIST_DIR}/../cmake/binh.cmake + COMMENT "Configuring rp2350_a3_rom_end.h" + VERBATIM) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rp2350_a4_rom_end.h + COMMAND ${CMAKE_COMMAND} + -D BINARY_FILE=${CMAKE_CURRENT_LIST_DIR}/rp2350_a4_rom_end.bin + -D OUTPUT_NAME=rp2350_a4_rom_end + -P ${CMAKE_CURRENT_LIST_DIR}/../cmake/binh.cmake + COMMENT "Configuring rp2350_a4_rom_end.h" + VERBATIM) + +add_dependencies(model unreadable_rom_data) diff --git a/elf/addresses.h b/model/addresses.h similarity index 62% rename from elf/addresses.h rename to model/addresses.h index d8926f06..a87ee09b 100644 --- a/elf/addresses.h +++ b/model/addresses.h @@ -21,7 +21,6 @@ #define MAIN_RAM_BANKED_START 0x21000000 #define MAIN_RAM_BANKED_END 0x21040000 - #ifdef __cplusplus #include @@ -47,31 +46,6 @@ struct address_range { typedef std::vector address_ranges; - -const address_ranges rp2040_address_ranges_flash { - address_range(FLASH_START, FLASH_END_RP2040, address_range::type::CONTENTS), - address_range(SRAM_START, SRAM_END_RP2040, 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(SRAM_START, SRAM_END_RP2040, address_range::type::CONTENTS), - address_range(XIP_SRAM_START_RP2040, XIP_SRAM_END_RP2040, address_range::type::CONTENTS), - address_range(ROM_START, ROM_END_RP2040, address_range::type::IGNORE) // for now we ignore the bootrom if present -}; - -const address_ranges rp2350_address_ranges_flash { - address_range(FLASH_START, FLASH_END_RP2350, address_range::type::CONTENTS), - address_range(SRAM_START, SRAM_END_RP2350, address_range::type::NO_CONTENTS), - address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS) -}; - -const address_ranges rp2350_address_ranges_ram { - address_range(SRAM_START, SRAM_END_RP2350, address_range::type::CONTENTS), - address_range(XIP_SRAM_START_RP2350, XIP_SRAM_END_RP2350, address_range::type::CONTENTS), - address_range(ROM_START, ROM_END_RP2350, address_range::type::IGNORE) // for now we ignore the bootrom if present -}; - 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) { diff --git a/model/model.cpp b/model/model.cpp new file mode 100644 index 00000000..e9925f36 --- /dev/null +++ b/model/model.cpp @@ -0,0 +1,4 @@ +#include "model.h" + +std::shared_ptr models::unknown = std::make_shared(); +std::shared_ptr models::largest = std::make_shared(); diff --git a/model/model.h b/model/model.h new file mode 100644 index 00000000..f942ec87 --- /dev/null +++ b/model/model.h @@ -0,0 +1,412 @@ +/* +* Copyright (c) 2025 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#pragma once +#include "boot/uf2.h" +#include "boot/picoboot.h" + +// Unreadable ROM data +#include "rp2350_a2_rom_end.h" +#include "rp2350_a3_rom_end.h" +#include "rp2350_a4_rom_end.h" + +enum memory_type { + rom, + flash, + sram, + sram_unstriped, + xip_sram, + invalid, +}; + +typedef enum { + rp2040, + rp2350, + unknown +} chip_t; + +typedef enum { + rp2040_b0, + rp2040_b1, + rp2040_b2, + rp2350_a2, + rp2350_a3, + rp2350_a4, + unknown_revision +} chip_revision_t; + +#ifdef __cplusplus + +#include +#include +#include +#include +#include +#include "addresses.h" +#include "errors.h" + +static std::string chip_name(chip_t chip) { + std::string device = "RP-series"; + if (chip == rp2040) { + device = "RP2040"; + } else if (chip == rp2350) { + device = "RP2350"; + } + return device; + +} + +// details of a specific chip/version. the most fleshed out version is derived from an actual device connection (usually by +// looking at the bootrom), however "stock" versions can be created from family IDs for example +class model_info { +public: + model_info(chip_t chip, std::string name, uint32_t rom_end, std::set picoboot_cmds = {}) : _chip(chip), _name(std::move(name)), + _rom_end(rom_end), _picoboot_cmds(std::move(picoboot_cmds)) {} + chip_t chip() const { return _chip; } + chip_revision_t chip_revision() const { return _chip_revision; } + void set_chip_revision(chip_revision_t revision) { _chip_revision = revision; } + uint32_t family_id() const { return _family_id; } + void set_family_id(uint32_t family_id) { _family_id = family_id; } + uint32_t rom_start() const { return ROM_START; } + uint32_t rom_end() const { return _rom_end; } + virtual enum memory_type get_memory_type(uint32_t addr) { + if (addr >= rom_start() && addr <= rom_end()) { + return rom; + } + return invalid; + } + + virtual bool supports_picoboot_cmd(picoboot_cmd_id cmd) const { + return _picoboot_cmds.find(cmd) != _picoboot_cmds.end(); + } + + virtual bool supports_picoboot_cmd(picoboot_cmd_id cmd, std::string& failed_device_name) const { + if (!supports_picoboot_cmd(cmd)) { + failed_device_name = name(); + return false; + } + return true; + } + bool supports_picoboot_cmds(std::set cmds, std::string& failed_device_name) const { + for (auto cmd : cmds) { + if (!supports_picoboot_cmd(cmd, failed_device_name)) { + return false; + } + } + return true; + } + std::string name() const { return _name; } + virtual std::string revision_name() const { + return "Unknown"; + } + + virtual bool supports_partition_table() { return false; } + virtual bool supports_otp_v2() { return false; } + virtual bool requires_block_loop() { return false; } + virtual int rom_table_version() { fail(ERROR_NOT_POSSIBLE, "unknown rom table version"); return 0; } + virtual uint32_t flash_start() { fail(ERROR_NOT_POSSIBLE, "unknown flash start"); return 0; } + virtual uint32_t flash_end() { fail(ERROR_NOT_POSSIBLE, "unknown flash end"); return 0; } + virtual uint32_t sram_start() { fail(ERROR_NOT_POSSIBLE, "unknown sram start"); return 0; } + virtual uint32_t sram_end() { fail(ERROR_NOT_POSSIBLE, "unknown sram end"); return 0; } + virtual uint32_t xip_sram_start() { fail(ERROR_NOT_POSSIBLE, "unknown xip sram start"); return 0; } + virtual uint32_t xip_sram_end() { fail(ERROR_NOT_POSSIBLE, "unknown xip sram end"); return 0; } + virtual uint32_t unreadable_rom_start() { return 0xffffffff; } + virtual uint32_t unreadable_rom_end() { return 0xffffffff; } + virtual const unsigned char *unreadable_rom_data() { return nullptr; } +private: + std::string _name; + chip_revision_t _chip_revision; + uint32_t _rom_end; + std::set _picoboot_cmds; + chip_t _chip; + uint32_t _family_id; +}; + +class model_unknown : public model_info { +public: + // note we allow a small amount of ROM so that we can read the id bytes + model_unknown() : model_info(unknown, chip_name(unknown), 0x100) { + + } +}; + +class model_rp : public model_info { +protected: + model_rp(chip_t chip, std::string name, uint32_t rom_end, std::set picoboot_cmds = {}) : model_info(chip, std::move(name), rom_end, std::move(picoboot_cmds)) {} + +public: + enum memory_type get_memory_type(uint32_t addr) override { + if (addr >= flash_start() && addr <= flash_end()) { + return flash; + } + if (addr >= sram_start() && addr <= sram_end()) { + return sram; + } + if (addr >= xip_sram_start() && addr <= xip_sram_end()) { + return xip_sram; + } + return model_info::get_memory_type(addr); + } + + uint32_t sram_start() override { + return SRAM_START; + } + + uint32_t flash_start() override { + return FLASH_START; + } + +}; + +class model_rp_generic : public model_rp { +public: + // allow large memory regions for generic model + model_rp_generic() : model_rp(unknown, chip_name(unknown), 0x100, {}) {} + + uint32_t xip_sram_start() override { + return std::min({XIP_SRAM_START_RP2040, XIP_SRAM_START_RP2350}); + } + + uint32_t xip_sram_end() override { + return std::max({XIP_SRAM_END_RP2040, XIP_SRAM_END_RP2350}); + } + + uint32_t sram_end() override { + return std::max({SRAM_END_RP2040, SRAM_END_RP2350}); + } + + uint32_t flash_end() override { + return std::max({FLASH_END_RP2040, FLASH_END_RP2350}); + } +}; + +class model_rp2040 : public model_rp { +public: + model_rp2040() : model_rp(rp2040, chip_name(rp2040), ROM_END_RP2040, { + PC_EXCLUSIVE_ACCESS, + PC_REBOOT, + PC_FLASH_ERASE, + PC_READ, + PC_WRITE, + PC_EXIT_XIP, + PC_ENTER_CMD_XIP, + PC_EXEC, + PC_VECTORIZE_FLASH, + }) { + set_family_id(RP2040_FAMILY_ID); + } + + enum memory_type get_memory_type(uint32_t addr) override { + if (addr >= MAIN_RAM_BANKED_START && addr <= MAIN_RAM_BANKED_END) { + return sram_unstriped; + } + return model_rp::get_memory_type(addr); + } + + int rom_table_version() override { + return 1; + } + + uint32_t xip_sram_start() override { + return XIP_SRAM_START_RP2040; + } + + uint32_t xip_sram_end() override { + return XIP_SRAM_END_RP2040; + } + + uint32_t sram_end() override { + return SRAM_END_RP2040; + } + + uint32_t flash_end() override { + return FLASH_END_RP2040; + } + + virtual std::string revision_name() const override{ + switch (chip_revision()) { + case rp2040_b0: + return "B0"; + case rp2040_b1: + return "B1"; + case rp2040_b2: + return "B2"; + default: + return model_rp::revision_name(); + } + } + +}; + +class model_rp2350 : public model_rp { +public: + model_rp2350() : model_rp2350(ROM_END_RP2350) {} + explicit model_rp2350(uint32_t rom_end) : model_rp(rp2350, rom_end > 0x8000 ? "RP2350(64k)" : "RP2350", rom_end, { + PC_EXCLUSIVE_ACCESS, + PC_REBOOT, + PC_FLASH_ERASE, + PC_READ, + PC_WRITE, + PC_EXIT_XIP, + PC_ENTER_CMD_XIP, + PC_EXEC, + PC_REBOOT2, + PC_GET_INFO, + PC_OTP_READ, + PC_OTP_WRITE, + }) {} + + bool supports_partition_table() override { return true; } + bool requires_block_loop() override { return true; } + + int rom_table_version() override { + return 2; + } + + uint32_t xip_sram_start() override { + return XIP_SRAM_START_RP2350; + } + + uint32_t xip_sram_end() override { + return XIP_SRAM_END_RP2350; + } + + uint32_t sram_end() override { + return SRAM_END_RP2350; + } + + uint32_t flash_end() override { + return FLASH_END_RP2350; + } + + uint32_t unreadable_rom_start() override { + return rom_end() - 0x200; + } + + uint32_t unreadable_rom_end() override { + return rom_end(); + } + + const unsigned char *unreadable_rom_data() override { + switch (chip_revision()) { + case rp2350_a2: + return rp2350_a2_rom_end; + case rp2350_a3: + return rp2350_a3_rom_end; + case rp2350_a4: + return rp2350_a4_rom_end; + default: + return nullptr; + } + } + + virtual std::string revision_name() const override{ + switch (chip_revision()) { + case rp2350_a2: + return "A2"; + case rp2350_a3: + return "A3"; + case rp2350_a4: + return "A4"; + default: + return model_rp::revision_name(); + } + } +}; + +class model_rp2350_arm_s : public model_rp2350 { +public: + model_rp2350_arm_s() : model_rp2350() { + set_family_id(RP2350_ARM_S_FAMILY_ID); + } +}; + +class model_rp2350_arm_ns : public model_rp2350 { +public: + model_rp2350_arm_ns() : model_rp2350() { + set_family_id(RP2350_ARM_NS_FAMILY_ID); + } +}; + +class model_rp2350_riscv : public model_rp2350 { +public: + model_rp2350_riscv() : model_rp2350() { + set_family_id(RP2350_RISCV_FAMILY_ID); + } +}; + +struct models { + static std::shared_ptr unknown; + static std::shared_ptr largest; +}; + +typedef std::shared_ptr model_t; + +// inclusive of ends +static inline enum memory_type get_memory_type(uint32_t addr, const model_t& model) { + return model->get_memory_type(addr); +} + +static model_t model_from_family(uint32_t family_id) { + if (family_id == RP2040_FAMILY_ID) { + return std::make_shared(); + } else if (family_id >= RP2350_ARM_S_FAMILY_ID && family_id <= RP2350_ARM_NS_FAMILY_ID) { + return std::make_shared(); + } + return models::unknown; +} + +// const address_ranges rp2040_address_ranges_flash { +// address_range(FLASH_START, FLASH_END_RP2040, address_range::type::CONTENTS), +// address_range(SRAM_START, SRAM_END_RP2040, address_range::type::NO_CONTENTS), +// address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS) +// }; +// +// const address_ranges rp2350_address_ranges_flash { +// address_range(FLASH_START, FLASH_END_RP2350, address_range::type::CONTENTS), +// address_range(SRAM_START, SRAM_END_RP2350, address_range::type::NO_CONTENTS), +// address_range(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS) +// }; + + +static address_ranges address_ranges_flash(const model_t& model) { + address_ranges ranges; + ranges.emplace_back(model->flash_start(), model->flash_end(), address_range::type::CONTENTS); + ranges.emplace_back(model->sram_start(), model->sram_end(), address_range::type::NO_CONTENTS); + if (model->chip() == rp2040) { + ranges.emplace_back(MAIN_RAM_BANKED_START, MAIN_RAM_BANKED_END, address_range::type::NO_CONTENTS); + } + return ranges; +} + + +// +// const address_ranges rp2040_address_ranges_ram { +// address_range(SRAM_START, SRAM_END_RP2040, address_range::type::CONTENTS), +// address_range(XIP_SRAM_START_RP2040, XIP_SRAM_END_RP2040, address_range::type::CONTENTS), +// address_range(ROM_START, ROM_END_RP2040, address_range::type::IGNORE) // for now we ignore the bootrom if present +// }; +// +// const address_ranges rp2350_address_ranges_ram { +// address_range(SRAM_START, SRAM_END_RP2350, address_range::type::CONTENTS), +// address_range(XIP_SRAM_START_RP2350, XIP_SRAM_END_RP2350, address_range::type::CONTENTS), +// address_range(ROM_START, ROM_END_RP2350, address_range::type::IGNORE) // for now we ignore the bootrom if present +// }; + +static address_ranges address_ranges_ram(const model_t& model) { + address_ranges ranges; + ranges.emplace_back(model->sram_start(), model->sram_end(), address_range::type::CONTENTS); + ranges.emplace_back(model->xip_sram_start(), model->xip_sram_end(), address_range::type::CONTENTS); + ranges.emplace_back(model->rom_start(), model->rom_end(), address_range::type::IGNORE); + return ranges; +} + +static bool contains_unreadable_rom(uint32_t addr, uint32_t size, const model_t& model) { + return (addr >= model->unreadable_rom_start() && addr < model->unreadable_rom_end()) || + (addr + size > model->unreadable_rom_start() && addr + size <= model->unreadable_rom_end()) || + (addr < model->unreadable_rom_start() && addr + size > model->unreadable_rom_end()); +} + +#endif \ No newline at end of file diff --git a/bootrom.end.bin b/model/rp2350_a2_rom_end.bin similarity index 100% rename from bootrom.end.bin rename to model/rp2350_a2_rom_end.bin diff --git a/model/rp2350_a3_rom_end.bin b/model/rp2350_a3_rom_end.bin new file mode 100644 index 00000000..ef28ff76 Binary files /dev/null and b/model/rp2350_a3_rom_end.bin differ diff --git a/model/rp2350_a4_rom_end.bin b/model/rp2350_a4_rom_end.bin new file mode 100644 index 00000000..8d26f670 Binary files /dev/null and b/model/rp2350_a4_rom_end.bin differ diff --git a/picoboot_connection/picoboot_connection.c b/picoboot_connection/picoboot_connection.c index 265608c7..9ee797e8 100644 --- a/picoboot_connection/picoboot_connection.c +++ b/picoboot_connection/picoboot_connection.c @@ -59,13 +59,13 @@ unsigned int interface; unsigned int out_ep; unsigned int in_ep; -enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, model_t *model, int vid, int pid, const char* ser) { +enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, chip_t *chip, int vid, int pid, const char* ser) { struct libusb_device_descriptor desc; struct libusb_config_descriptor *config; definitely_exclusive = false; *dev_handle = NULL; - *model = unknown; + *chip = unknown; int ret = libusb_get_device_descriptor(device, &desc); enum picoboot_device_result res = dr_vidpid_unknown; if (ret && verbose) { @@ -88,18 +88,18 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d case PRODUCT_ID_PICOPROBE: return dr_vidpid_picoprobe; case PRODUCT_ID_RP2040_STDIO_USB: - *model = rp2040; + *chip = rp2040; res = dr_vidpid_stdio_usb; break; case PRODUCT_ID_STDIO_USB: - *model = rp2350; + *chip = rp2350; res = dr_vidpid_stdio_usb; break; case PRODUCT_ID_RP2040_USBBOOT: - *model = rp2040; + *chip = rp2040; break; case PRODUCT_ID_RP2350_USBBOOT: - *model = rp2350; + *chip = rp2350; break; default: return dr_vidpid_unknown; @@ -179,7 +179,7 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d } if (!ret) { - if (*model == unknown) { + if (*chip == unknown) { struct picoboot_get_info_cmd info_cmd; info_cmd.bType = PICOBOOT_GET_INFO_SYS, info_cmd.dParams[0] = (uint32_t) (SYS_INFO_CHIP_INFO); @@ -187,13 +187,13 @@ enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_d // RP2040 doesn't have this function, so returns non-zero int info_ret = picoboot_get_info(*dev_handle, &info_cmd, (uint8_t*)word_buf, sizeof(word_buf)); if (info_ret) { - *model = rp2040; + *chip = rp2040; } else { - *model = rp2350; + *chip = rp2350; } } if (strlen(ser) != 0) { - if (*model == rp2040) { + if (*chip == rp2040) { // Check flash ID, as USB serial number is not unique uint64_t ser_num = strtoull(ser, NULL, 16); uint64_t id = 0; diff --git a/picoboot_connection/picoboot_connection.h b/picoboot_connection/picoboot_connection.h index 8c2ca22a..2009b778 100644 --- a/picoboot_connection/picoboot_connection.h +++ b/picoboot_connection/picoboot_connection.h @@ -14,8 +14,7 @@ #include #endif #include "boot/picoboot.h" - -#include "addresses.h" +#include "model.h" #define VENDOR_ID_RASPBERRY_PI 0x2e8au #define PRODUCT_ID_RP2040_USBBOOT 0x0003u @@ -41,20 +40,10 @@ enum picoboot_device_result { dr_vidpid_stdio_usb_cant_connect, }; -typedef enum { - rp2040, - rp2350, - unknown -} model_t; - -typedef enum { - rp2350_a2, - rp2350_unknown -} rp2350_version_t; #if HAS_LIBUSB // note that vid and pid are filters, unless both are specified in which case a device with that VID and PID is allowed for RP2350 -enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, model_t *model, int vid, int pid, const char* ser); +enum picoboot_device_result picoboot_open_device(libusb_device *device, libusb_device_handle **dev_handle, chip_t *chip, int vid, int pid, const char* ser); int picoboot_reset(libusb_device_handle *usb_device); int picoboot_cmd_status_verbose(libusb_device_handle *usb_device, struct picoboot_cmd_status *status, @@ -87,57 +76,6 @@ int picoboot_flash_id(libusb_device_handle *usb_device, uint64_t *data); #define PAGE_SIZE (1u << LOG2_PAGE_SIZE) #define FLASH_SECTOR_ERASE_SIZE 4096u -enum memory_type { - rom, - flash, - sram, - sram_unstriped, - xip_sram, - invalid, -}; - -// inclusive of ends -static inline enum memory_type get_memory_type(uint32_t addr, model_t model) { - if (addr >= FLASH_START && addr <= FLASH_END_RP2040) { - return flash; - } - if (addr >= ROM_START && addr <= ROM_END_RP2040) { - return rom; - } - if (addr >= SRAM_START && addr <= SRAM_END_RP2040) { - return sram; - } - if (model == rp2350) { - if (addr >= FLASH_START && addr <= FLASH_END_RP2350) { - return flash; - } - if (addr >= ROM_START && addr <= ROM_END_RP2350) { - return rom; - } - if (addr >= SRAM_START && addr <= SRAM_END_RP2350) { - return sram; - } - } - if (addr >= MAIN_RAM_BANKED_START && addr <= MAIN_RAM_BANKED_END) { - return sram_unstriped; - } - if (model == rp2040) { - if (addr >= XIP_SRAM_START_RP2040 && addr <= XIP_SRAM_END_RP2040) { - return xip_sram; - } - } else if (model == rp2350) { - if (addr >= XIP_SRAM_START_RP2350 && addr <= XIP_SRAM_END_RP2350) { - return xip_sram; - } - } - return invalid; -} - -static inline bool is_transfer_aligned(uint32_t addr, model_t model) { - enum memory_type t = get_memory_type(addr, model); - return t != invalid && !(t == flash && addr & (PAGE_SIZE-1)); -} - static inline bool is_size_aligned(uint32_t addr, int size) { #ifndef _MSC_VER assert(__builtin_popcount(size)==1); diff --git a/picoboot_connection/picoboot_connection_cxx.h b/picoboot_connection/picoboot_connection_cxx.h index 479dc465..6b2feab5 100644 --- a/picoboot_connection/picoboot_connection_cxx.h +++ b/picoboot_connection/picoboot_connection_cxx.h @@ -11,7 +11,6 @@ #include namespace picoboot { - struct command_failure : public std::exception { explicit command_failure(int code) : code(code) {} @@ -28,7 +27,7 @@ namespace picoboot { }; struct connection { - explicit connection(libusb_device_handle *device, model_t model, bool exclusive = true) : device(device), model(model), exclusive(exclusive) { + explicit connection(libusb_device_handle *device, bool exclusive = true) : device(device), exclusive(exclusive) { // do a device reset in case it was left in a bad state reset(); if (exclusive) exclusive_access(EXCLUSIVE); @@ -61,7 +60,6 @@ namespace picoboot { void otp_read(struct picoboot_otp_cmd *otp_cmd, uint8_t *buffer, uint32_t len); void flash_id(uint64_t &data); - model_t get_model() const { return model; } std::vector read_bytes(uint32_t addr, uint32_t len) { std::vector bytes(len); read(addr, bytes.data(), len); @@ -70,7 +68,6 @@ namespace picoboot { private: template void wrap_call(F&& func); libusb_device_handle *device; - model_t model; bool exclusive; }; diff --git a/xip_ram_perms/xip_ram_perms.elf b/xip_ram_perms/xip_ram_perms.elf index 75a111fa..952f0058 100644 Binary files a/xip_ram_perms/xip_ram_perms.elf and b/xip_ram_perms/xip_ram_perms.elf differ