SHA-256 hash verification of flash slots, pico_hash_binary, flash:: namespace, ring_buffer iterator, non-destructive log
This commit is contained in:
@@ -12,6 +12,7 @@ pico_sdk_init()
|
|||||||
set(LIB_SOURCES
|
set(LIB_SOURCES
|
||||||
lib/arp.cpp
|
lib/arp.cpp
|
||||||
lib/dispatch.cpp
|
lib/dispatch.cpp
|
||||||
|
lib/flash.cpp
|
||||||
lib/handlers.cpp
|
lib/handlers.cpp
|
||||||
lib/icmp.cpp
|
lib/icmp.cpp
|
||||||
lib/igmp.cpp
|
lib/igmp.cpp
|
||||||
@@ -21,7 +22,7 @@ set(LIB_SOURCES
|
|||||||
w6300/w6300.cpp
|
w6300/w6300.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIB_DEPS pico_stdlib tinyusb_device tinyusb_board hardware_pio hardware_spi hardware_dma hardware_clocks)
|
set(LIB_DEPS pico_stdlib pico_sha256 tinyusb_device tinyusb_board hardware_pio hardware_spi hardware_dma hardware_clocks)
|
||||||
|
|
||||||
add_executable(picomap firmware.cpp ${LIB_SOURCES})
|
add_executable(picomap firmware.cpp ${LIB_SOURCES})
|
||||||
target_include_directories(picomap PRIVATE include w6300)
|
target_include_directories(picomap PRIVATE include w6300)
|
||||||
@@ -35,6 +36,7 @@ math(EXPR VERSION_MAJOR "${BUILD_EPOCH} >> 16")
|
|||||||
math(EXPR VERSION_MINOR "${BUILD_EPOCH} & 65535")
|
math(EXPR VERSION_MINOR "${BUILD_EPOCH} & 65535")
|
||||||
pico_set_binary_version(picomap MAJOR ${VERSION_MAJOR} MINOR ${VERSION_MINOR})
|
pico_set_binary_version(picomap MAJOR ${VERSION_MAJOR} MINOR ${VERSION_MINOR})
|
||||||
target_compile_definitions(picomap PRIVATE BUILD_EPOCH=${BUILD_EPOCH})
|
target_compile_definitions(picomap PRIVATE BUILD_EPOCH=${BUILD_EPOCH})
|
||||||
|
pico_hash_binary(picomap)
|
||||||
pico_embed_pt_in_binary(picomap ${CMAKE_CURRENT_LIST_DIR}/partition_table.json)
|
pico_embed_pt_in_binary(picomap ${CMAKE_CURRENT_LIST_DIR}/partition_table.json)
|
||||||
pico_add_extra_outputs(picomap)
|
pico_add_extra_outputs(picomap)
|
||||||
target_link_libraries(picomap ${LIB_DEPS})
|
target_link_libraries(picomap ${LIB_DEPS})
|
||||||
@@ -48,6 +50,7 @@ pico_enable_stdio_uart(picomap_test 0)
|
|||||||
pico_set_binary_type(picomap_test copy_to_ram)
|
pico_set_binary_type(picomap_test copy_to_ram)
|
||||||
pico_set_binary_version(picomap_test MAJOR ${VERSION_MAJOR} MINOR ${VERSION_MINOR})
|
pico_set_binary_version(picomap_test MAJOR ${VERSION_MAJOR} MINOR ${VERSION_MINOR})
|
||||||
target_compile_definitions(picomap_test PRIVATE BUILD_EPOCH=${BUILD_EPOCH})
|
target_compile_definitions(picomap_test PRIVATE BUILD_EPOCH=${BUILD_EPOCH})
|
||||||
|
pico_hash_binary(picomap_test)
|
||||||
pico_embed_pt_in_binary(picomap_test ${CMAKE_CURRENT_LIST_DIR}/partition_table.json)
|
pico_embed_pt_in_binary(picomap_test ${CMAKE_CURRENT_LIST_DIR}/partition_table.json)
|
||||||
pico_add_extra_outputs(picomap_test)
|
pico_add_extra_outputs(picomap_test)
|
||||||
target_link_libraries(picomap_test ${LIB_DEPS})
|
target_link_libraries(picomap_test ${LIB_DEPS})
|
||||||
|
|||||||
@@ -37,15 +37,3 @@ inline void dlog_if_slow(std::string_view label, uint32_t threshold_us, std::fun
|
|||||||
dlogf("%.*s %luus", static_cast<int>(label.size()), label.data(), static_cast<unsigned long>(elapsed));
|
dlogf("%.*s %luus", static_cast<int>(label.size()), label.data(), static_cast<unsigned long>(elapsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<log_entry> dlog_drain() {
|
|
||||||
std::vector<log_entry> result;
|
|
||||||
uint16_t n = g_debug_log.used();
|
|
||||||
result.reserve(n);
|
|
||||||
for (uint16_t i = 0; i < n; i++) {
|
|
||||||
log_entry e;
|
|
||||||
g_debug_log.peek(std::span{&e, 1});
|
|
||||||
result.push_back(std::move(e));
|
|
||||||
g_debug_log.consume(1);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
20
firmware/include/flash.h
Normal file
20
firmware/include/flash.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace flash {
|
||||||
|
|
||||||
|
constexpr uint32_t FLASH_BASE = 0x10000000;
|
||||||
|
constexpr uint32_t FLASH_SIZE = 2 * 1024 * 1024;
|
||||||
|
|
||||||
|
struct slot {
|
||||||
|
bool valid;
|
||||||
|
uint32_t version;
|
||||||
|
bool hash_ok;
|
||||||
|
auto as_tuple() const { return std::tie(valid, version, hash_ok); }
|
||||||
|
auto as_tuple() { return std::tie(valid, version, hash_ok); }
|
||||||
|
};
|
||||||
|
|
||||||
|
slot scan(uint32_t flash_offset);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -53,4 +53,15 @@ struct ring_buffer {
|
|||||||
uint16_t len = pending < contig ? pending : contig;
|
uint16_t len = pending < contig ? pending : contig;
|
||||||
return {data.data() + offset, len};
|
return {data.data() + offset, len};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
const ring_buffer* rb;
|
||||||
|
uint16_t index;
|
||||||
|
const T& operator*() const { return rb->data[(rb->head + index) % N]; }
|
||||||
|
iterator& operator++() { index++; return *this; }
|
||||||
|
bool operator!=(const iterator& o) const { return index != o.index; }
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const { return {this, 0}; }
|
||||||
|
iterator end() const { return {this, used()}; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "msgpack.h"
|
#include "msgpack.h"
|
||||||
#include "halfsiphash.h"
|
#include "halfsiphash.h"
|
||||||
#include "static_vector.h"
|
#include "static_vector.h"
|
||||||
|
#include "flash.h"
|
||||||
|
|
||||||
struct Envelope {
|
struct Envelope {
|
||||||
static constexpr int8_t ext_id = 0;
|
static constexpr int8_t ext_id = 0;
|
||||||
@@ -128,19 +129,11 @@ struct RequestFlashStatus {
|
|||||||
auto as_tuple() { return std::tie(); }
|
auto as_tuple() { return std::tie(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SlotInfo {
|
|
||||||
bool valid;
|
|
||||||
uint32_t version;
|
|
||||||
bool hash_ok;
|
|
||||||
auto as_tuple() const { return std::tie(valid, version, hash_ok); }
|
|
||||||
auto as_tuple() { return std::tie(valid, version, hash_ok); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ResponseFlashStatus {
|
struct ResponseFlashStatus {
|
||||||
static constexpr int8_t ext_id = 15;
|
static constexpr int8_t ext_id = 15;
|
||||||
int8_t boot_partition;
|
int8_t boot_partition;
|
||||||
SlotInfo slot_a;
|
flash::slot slot_a;
|
||||||
SlotInfo slot_b;
|
flash::slot slot_b;
|
||||||
auto as_tuple() const { return std::tie(boot_partition, slot_a, slot_b); }
|
auto as_tuple() const { return std::tie(boot_partition, slot_a, slot_b); }
|
||||||
auto as_tuple() { return std::tie(boot_partition, slot_a, slot_b); }
|
auto as_tuple() { return std::tie(boot_partition, slot_a, slot_b); }
|
||||||
};
|
};
|
||||||
|
|||||||
163
firmware/lib/flash.cpp
Normal file
163
firmware/lib/flash.cpp
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#include "flash.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include "pico/sha256.h"
|
||||||
|
#include "boot/picobin.h"
|
||||||
|
|
||||||
|
namespace flash {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr uint32_t PICOBIN_MARKER_END = 0xab123579;
|
||||||
|
|
||||||
|
struct __attribute__((packed)) last_item {
|
||||||
|
uint8_t type;
|
||||||
|
uint16_t block_item_words;
|
||||||
|
uint8_t pad;
|
||||||
|
int32_t next_block_offset;
|
||||||
|
uint32_t marker_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __attribute__((packed)) hash_def_header {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t size_words;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t hash_type;
|
||||||
|
uint16_t block_words_to_hash;
|
||||||
|
uint16_t pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __attribute__((packed)) load_map_header {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t size_words;
|
||||||
|
uint8_t reserved;
|
||||||
|
uint8_t flags_and_count;
|
||||||
|
|
||||||
|
uint8_t count() const { return flags_and_count & 0x7f; }
|
||||||
|
bool absolute() const { return flags_and_count & 0x80; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct load_map_entry {
|
||||||
|
uint32_t storage_addr;
|
||||||
|
uint32_t runtime_addr;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct parsed_item {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t size_words;
|
||||||
|
const uint32_t* words;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct parsed_block {
|
||||||
|
const uint32_t* base;
|
||||||
|
parsed_item items[16];
|
||||||
|
uint8_t item_count;
|
||||||
|
int32_t next_block_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool parse_block(const uint32_t* start, const uint32_t* limit, parsed_block& out) {
|
||||||
|
if (start >= limit || *start != PICOBIN_BLOCK_MARKER_START) return false;
|
||||||
|
out.base = start;
|
||||||
|
out.item_count = 0;
|
||||||
|
out.next_block_offset = 0;
|
||||||
|
|
||||||
|
auto* w = start + 1;
|
||||||
|
while (w + 3 < limit && out.item_count < 16) {
|
||||||
|
uint8_t type = *w & 0xff;
|
||||||
|
if (type == PICOBIN_BLOCK_ITEM_2BS_LAST) {
|
||||||
|
auto* last = reinterpret_cast<const last_item*>(w);
|
||||||
|
out.next_block_offset = last->next_block_offset;
|
||||||
|
return last->marker_end == PICOBIN_MARKER_END;
|
||||||
|
}
|
||||||
|
uint8_t size_words = (*w >> 8) & 0xff;
|
||||||
|
if (w + size_words > limit) return false;
|
||||||
|
out.items[out.item_count++] = {type, size_words, w};
|
||||||
|
w += size_words;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed_item* find_item(const parsed_block& blk, uint8_t type) {
|
||||||
|
for (uint8_t i = 0; i < blk.item_count; i++)
|
||||||
|
if (blk.items[i].type == type) return &blk.items[i];
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_hash(const parsed_block& last) {
|
||||||
|
auto* lm_item = find_item(last, PICOBIN_BLOCK_ITEM_LOAD_MAP);
|
||||||
|
auto* hd_item = find_item(last, PICOBIN_BLOCK_ITEM_1BS_HASH_DEF);
|
||||||
|
auto* hv_item = find_item(last, PICOBIN_BLOCK_ITEM_HASH_VALUE);
|
||||||
|
if (!lm_item || !hd_item || !hv_item) return false;
|
||||||
|
if (hd_item->size_words < 2 || hv_item->size_words < 9) return false;
|
||||||
|
|
||||||
|
auto* hd = reinterpret_cast<const hash_def_header*>(hd_item->words);
|
||||||
|
if (hd->hash_type != PICOBIN_HASH_SHA256) return false;
|
||||||
|
|
||||||
|
auto* lm = reinterpret_cast<const load_map_header*>(lm_item->words);
|
||||||
|
auto* entries = reinterpret_cast<const load_map_entry*>(lm_item->words + 1);
|
||||||
|
uint32_t lm_xip_addr = reinterpret_cast<uint32_t>(lm_item->words);
|
||||||
|
|
||||||
|
pico_sha256_state_t sha;
|
||||||
|
if (pico_sha256_try_start(&sha, SHA256_BIG_ENDIAN, false) != PICO_OK) return false;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < lm->count(); i++) {
|
||||||
|
uint32_t storage_addr = entries[i].storage_addr;
|
||||||
|
uint32_t size = entries[i].size;
|
||||||
|
if (lm->absolute()) size -= entries[i].runtime_addr;
|
||||||
|
if (storage_addr == 0) {
|
||||||
|
pico_sha256_update_blocking(&sha, reinterpret_cast<const uint8_t*>(&size), 4);
|
||||||
|
} else {
|
||||||
|
if (!lm->absolute()) storage_addr += lm_xip_addr;
|
||||||
|
pico_sha256_update_blocking(&sha, reinterpret_cast<const uint8_t*>(storage_addr), size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pico_sha256_update_blocking(&sha,
|
||||||
|
reinterpret_cast<const uint8_t*>(last.base),
|
||||||
|
static_cast<size_t>(hd->block_words_to_hash) * 4);
|
||||||
|
|
||||||
|
sha256_result_t result;
|
||||||
|
pico_sha256_finish(&sha, &result);
|
||||||
|
|
||||||
|
auto* expected = reinterpret_cast<const uint8_t*>(hv_item->words + 1);
|
||||||
|
return memcmp(result.bytes, expected, 32) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
slot scan(uint32_t flash_offset) {
|
||||||
|
slot info{};
|
||||||
|
constexpr uint32_t scan_limit = 4096;
|
||||||
|
constexpr uint32_t slot_size = 512 * 1024;
|
||||||
|
auto* slot_base = reinterpret_cast<const uint32_t*>(FLASH_BASE + flash_offset);
|
||||||
|
auto* slot_end = reinterpret_cast<const uint32_t*>(FLASH_BASE + flash_offset + slot_size);
|
||||||
|
|
||||||
|
auto* s = slot_base;
|
||||||
|
auto* s_end = slot_base + scan_limit / 4;
|
||||||
|
while (s < s_end && *s != PICOBIN_BLOCK_MARKER_START) s++;
|
||||||
|
if (s >= s_end) return info;
|
||||||
|
|
||||||
|
parsed_block start;
|
||||||
|
if (!parse_block(s, slot_end, start)) return info;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < start.item_count; i++) {
|
||||||
|
if (start.items[i].type == PICOBIN_BLOCK_ITEM_1BS_VERSION && start.items[i].size_words >= 2)
|
||||||
|
info.version = start.items[i].words[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* cur = &start;
|
||||||
|
parsed_block next;
|
||||||
|
while (cur->next_block_offset != 0) {
|
||||||
|
auto* np = reinterpret_cast<const uint32_t*>(
|
||||||
|
reinterpret_cast<const uint8_t*>(cur->base) + cur->next_block_offset);
|
||||||
|
if (np <= slot_base || np >= slot_end) return info;
|
||||||
|
if (np == start.base) break;
|
||||||
|
if (!parse_block(np, slot_end, next)) return info;
|
||||||
|
cur = &next;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.valid = true;
|
||||||
|
info.hash_ok = verify_hash(*cur);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,15 +3,11 @@
|
|||||||
#include "pico/bootrom.h"
|
#include "pico/bootrom.h"
|
||||||
#include "hardware/flash.h"
|
#include "hardware/flash.h"
|
||||||
#include "hardware/watchdog.h"
|
#include "hardware/watchdog.h"
|
||||||
#include "boot/picobin.h"
|
#include "flash.h"
|
||||||
#include "dispatch.h"
|
#include "dispatch.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "debug_log.h"
|
#include "debug_log.h"
|
||||||
|
|
||||||
static constexpr uint32_t XIP_BASE_ADDR = 0x10000000;
|
|
||||||
static constexpr uint32_t FLASH_SIZE = 2 * 1024 * 1024;
|
|
||||||
static constexpr uint32_t SLOT_A_OFFSET = 0x00000;
|
|
||||||
static constexpr uint32_t SLOT_B_OFFSET = 0x80000;
|
|
||||||
static boot_reason detected_boot_reason;
|
static boot_reason detected_boot_reason;
|
||||||
|
|
||||||
static void poke_watchdog() {
|
static void poke_watchdog() {
|
||||||
@@ -54,18 +50,18 @@ std::optional<ResponseInfo> handle_info(const responder&, const RequestInfo&) {
|
|||||||
|
|
||||||
std::optional<ResponseLog> handle_log(const responder&, const RequestLog&) {
|
std::optional<ResponseLog> handle_log(const responder&, const RequestLog&) {
|
||||||
ResponseLog resp;
|
ResponseLog resp;
|
||||||
for (auto& e : dlog_drain())
|
for (auto& e : g_debug_log)
|
||||||
resp.entries.push_back(LogEntry{e.timestamp_us, std::move(e.message)});
|
resp.entries.push_back(LogEntry{e.timestamp_us, e.message});
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ResponseFlashErase> handle_flash_erase(const responder&, const RequestFlashErase& req) {
|
std::optional<ResponseFlashErase> handle_flash_erase(const responder&, const RequestFlashErase& req) {
|
||||||
if (req.addr < XIP_BASE_ADDR || req.addr + req.len > XIP_BASE_ADDR + FLASH_SIZE) {
|
if (req.addr < flash::FLASH_BASE || req.addr + req.len > flash::FLASH_BASE + flash::FLASH_SIZE) {
|
||||||
dlogf("flash erase: out of range %08lx+%lu",
|
dlogf("flash erase: out of range %08lx+%lu",
|
||||||
static_cast<unsigned long>(req.addr), static_cast<unsigned long>(req.len));
|
static_cast<unsigned long>(req.addr), static_cast<unsigned long>(req.len));
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
uint32_t offset = req.addr - XIP_BASE_ADDR;
|
uint32_t offset = req.addr - flash::FLASH_BASE;
|
||||||
if (offset % FLASH_SECTOR_SIZE != 0 || req.len % FLASH_SECTOR_SIZE != 0 || req.len == 0) {
|
if (offset % FLASH_SECTOR_SIZE != 0 || req.len % FLASH_SECTOR_SIZE != 0 || req.len == 0) {
|
||||||
dlogf("flash erase: bad alignment %08lx+%lu",
|
dlogf("flash erase: bad alignment %08lx+%lu",
|
||||||
static_cast<unsigned long>(req.addr), static_cast<unsigned long>(req.len));
|
static_cast<unsigned long>(req.addr), static_cast<unsigned long>(req.len));
|
||||||
@@ -76,12 +72,12 @@ std::optional<ResponseFlashErase> handle_flash_erase(const responder&, const Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ResponseFlashWrite> handle_flash_write(const responder&, const RequestFlashWrite& req) {
|
std::optional<ResponseFlashWrite> handle_flash_write(const responder&, const RequestFlashWrite& req) {
|
||||||
if (req.addr < XIP_BASE_ADDR || req.addr + req.data.size() > XIP_BASE_ADDR + FLASH_SIZE) {
|
if (req.addr < flash::FLASH_BASE || req.addr + req.data.size() > flash::FLASH_BASE + flash::FLASH_SIZE) {
|
||||||
dlogf("flash write: out of range %08lx+%zu",
|
dlogf("flash write: out of range %08lx+%zu",
|
||||||
static_cast<unsigned long>(req.addr), req.data.size());
|
static_cast<unsigned long>(req.addr), req.data.size());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
uint32_t offset = req.addr - XIP_BASE_ADDR;
|
uint32_t offset = req.addr - flash::FLASH_BASE;
|
||||||
if (offset % FLASH_PAGE_SIZE != 0 || req.data.size() % FLASH_PAGE_SIZE != 0 || req.data.empty()) {
|
if (offset % FLASH_PAGE_SIZE != 0 || req.data.size() % FLASH_PAGE_SIZE != 0 || req.data.empty()) {
|
||||||
dlogf("flash write: bad alignment %08lx+%zu",
|
dlogf("flash write: bad alignment %08lx+%zu",
|
||||||
static_cast<unsigned long>(req.addr), req.data.size());
|
static_cast<unsigned long>(req.addr), req.data.size());
|
||||||
@@ -91,34 +87,6 @@ std::optional<ResponseFlashWrite> handle_flash_write(const responder&, const Req
|
|||||||
return ResponseFlashWrite{};
|
return ResponseFlashWrite{};
|
||||||
}
|
}
|
||||||
|
|
||||||
static SlotInfo scan_slot(uint32_t flash_offset) {
|
|
||||||
SlotInfo info{};
|
|
||||||
constexpr uint32_t scan_limit = 4096;
|
|
||||||
auto* scan = reinterpret_cast<const uint32_t*>(XIP_BASE_ADDR + flash_offset);
|
|
||||||
auto* scan_end = scan + scan_limit / 4;
|
|
||||||
while (scan < scan_end && *scan != PICOBIN_BLOCK_MARKER_START) scan++;
|
|
||||||
if (scan >= scan_end) return info;
|
|
||||||
info.valid = true;
|
|
||||||
|
|
||||||
auto* p = reinterpret_cast<const uint8_t*>(scan + 1);
|
|
||||||
auto* end = reinterpret_cast<const uint8_t*>(scan + 64);
|
|
||||||
while (p + 2 <= end) {
|
|
||||||
uint8_t type = p[0];
|
|
||||||
uint8_t size_words = p[1];
|
|
||||||
if (type == PICOBIN_BLOCK_ITEM_2BS_LAST) break;
|
|
||||||
uint32_t item_bytes = static_cast<uint32_t>(size_words) * 4;
|
|
||||||
if (p + item_bytes > end) break;
|
|
||||||
if (type == PICOBIN_BLOCK_ITEM_1BS_VERSION && size_words >= 2) {
|
|
||||||
auto* words = reinterpret_cast<const uint16_t*>(p + 4);
|
|
||||||
uint16_t minor = words[0];
|
|
||||||
uint16_t major = words[1];
|
|
||||||
info.version = (static_cast<uint32_t>(major) << 16) | minor;
|
|
||||||
}
|
|
||||||
p += item_bytes;
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<ResponseFlashStatus> handle_flash_status(const responder&, const RequestFlashStatus&) {
|
std::optional<ResponseFlashStatus> handle_flash_status(const responder&, const RequestFlashStatus&) {
|
||||||
ResponseFlashStatus resp;
|
ResponseFlashStatus resp;
|
||||||
boot_info_t bi;
|
boot_info_t bi;
|
||||||
@@ -126,8 +94,8 @@ std::optional<ResponseFlashStatus> handle_flash_status(const responder&, const R
|
|||||||
resp.boot_partition = bi.partition;
|
resp.boot_partition = bi.partition;
|
||||||
else
|
else
|
||||||
resp.boot_partition = -1;
|
resp.boot_partition = -1;
|
||||||
resp.slot_a = scan_slot(SLOT_A_OFFSET);
|
resp.slot_a = flash::scan(0x00000);
|
||||||
resp.slot_b = scan_slot(SLOT_B_OFFSET);
|
resp.slot_b = flash::scan(0x80000);
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user