blob: 9b3edee3420532a8130e00b85e46dba0c2d2bab9 [file] [log] [blame] [edit]
/*
* 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 <algorithm>
#include <memory>
#include <set>
#include <string>
#include <utility>
#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 = {}) : _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<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_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;
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 = {}) : 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<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