| /* | |
| * Copyright (c) 2025 Raspberry Pi (Trading) Ltd. | |
| * | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| */ | |
| #pragma once | |
| #ifdef __cplusplus | |
| #include <algorithm> | |
| #include <memory> | |
| #include <set> | |
| #include <string> | |
| #include <utility> | |
| #endif | |
| #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" | |
| // tsk namespace is polluted on windows | |
| #ifdef _WIN32 | |
| #undef min | |
| #undef max | |
| #define _CRT_SECURE_NO_WARNINGS | |
| #endif | |
| 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 "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_cmd_id> picoboot_cmds = {}, std::set<uint32_t> supported_family_ids = {}) : _chip(chip), _name(std::move(name)), | |
| _rom_end(rom_end), _picoboot_cmds(std::move(picoboot_cmds)), _supported_family_ids(std::move(supported_family_ids)) {} | |
| 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_family_id(uint32_t family_id) const { | |
| return _supported_family_ids.find(family_id) != _supported_family_ids.end(); | |
| } | |
| 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<picoboot_cmd_id> 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_striped_end() { fail(ERROR_NOT_POSSIBLE, "unknown striped sram end"); 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_cmd_id> _picoboot_cmds; | |
| std::set<uint32_t> _supported_family_ids; | |
| 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_cmd_id> picoboot_cmds = {}, std::set<uint32_t> supported_family_ids = {}) : model_info(chip, std::move(name), rom_end, std::move(picoboot_cmds), std::move(supported_family_ids)) {} | |
| 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_striped_end() override { | |
| return std::max({SRAM_STRIPED_END_RP2040, SRAM_STRIPED_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, | |
| }, { | |
| RP2040_FAMILY_ID, | |
| }) { | |
| 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_striped_end() override { | |
| return SRAM_STRIPED_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, | |
| }, { | |
| ABSOLUTE_FAMILY_ID, | |
| DATA_FAMILY_ID, | |
| RP2350_ARM_S_FAMILY_ID, | |
| RP2350_RISCV_FAMILY_ID, | |
| RP2350_ARM_NS_FAMILY_ID, | |
| }) {} | |
| 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_striped_end() override { | |
| return SRAM_STRIPED_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<model_info> unknown; | |
| static std::shared_ptr<model_info> largest; | |
| }; | |
| typedef std::shared_ptr<model_info> 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<model_rp2040>(); | |
| } else if (family_id >= RP2350_ARM_S_FAMILY_ID && family_id <= RP2350_ARM_NS_FAMILY_ID) { | |
| return std::make_shared<model_rp2350>(); | |
| } | |
| 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); | |
| ranges.emplace_back(model->xip_sram_start(), model->xip_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 |