Skip to content

RP2350 LOAD_MAP physical to storage translation missing during hash verification (picotool reports incorrect hash) #287

@u6bkep

Description

@u6bkep

Summary:
picotool reports “hash incorrect” for a valid RP2350 packaged image when the LOAD_MAP uses a physical address (as required by bootrom). The bootrom translates physical to storage using the partition base, but picotool appears to hash from the physical address directly.

Repro (minimal):

  1. Use the PoC script: https://gist.github.com/u6bkep/84ea4cdfb20638765c658c0747c517b9
  2. Run the script to generate the UF2.
  3. Flash UF2 to a Pico 2 in BOOTSEL. (drag and drop)
  4. Run: picotool info -a
  5. Observe: Partition 2 shows hash incorrect.

Expected:
picotool should translate LOAD_MAP physical addresses by the partition base (lma_to_storage) and report hash correct.

Actual:
picotool hashes from the physical address (e.g., 0x10000200) and reports incorrect hash/signature.

Why this is a bug:
Bootrom source (RP2350) adds lma_to_storage for non‑relative LOAD_MAP entries before hashing/copying, so the data at partition_base + offset is used. picotool is not applying this translation, leading to a mismatch.

Notes:

  • The PoC defines the hashed region fully via LOAD_MAP and places data only at the translated address.
  • Bootrom behavior: adds partition base offset for LOAD_MAP absolute/physical addresses.

Bootrom translation (adds partition base before hashing/copying):
varm_blocks.c

uint32_t lma_to_storage = parsed_block->enclosing_window.base + parsed_block->slot_roll - XIP_BASE +
                          ((const parsed_image_def_t *)parsed_block)->rolling_window_delta;

...
from_storage_addr = map_storage_address_value;
if (!picobin_load_map_is_relative(load_map)) {
    if (!call_varm_is_sram_or_xip_ram(from_storage_addr)) {
        from_storage_addr += lma_to_storage;
    }
}

picotool hashing (uses entry.storage_address directly, no partition-base translation):
bintool.cpp

for (const auto &entry : load_map->entries) {
    uint32_t current_storage_address = entry.storage_address;
    ...
    auto seg = elf->segment_from_physical_address(current_storage_address);
    ...
    uint32_t offset = current_storage_address - seg->physical_address();
    ...
    DEBUG_LOG("HASH %08x + %08x\n", (int)entry.storage_address, (int)data.size());
}

Parsing of LOAD_MAP absolute entries also leaves storage_address unchanged:
metadata.h

if (storage_address != 0) {
    if (absolute) {
        size -= runtime_address;
    } else {
        storage_address += current_addr;
    }
}

picotool output

 Metadata Block 2
  address:             0x10000000
  next block address:  0x10000000
  block type:          image def
  target chip:         RP2350
  image type:          ARM Secure
  load map entry 0:    Copy 0x10000200->0x10000300 to 0x20000000->0x20000100
  hash:                incorrect
  hash value:          725443BF7AE1F98D6B22C1943F1FDB40A1EF1A806BB996D277F751543DE62C9B

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions