raspberrypi / picotool

BSD 3-Clause "New" or "Revised" License
525 stars 86 forks source link

DRAFT: add serial command that prints the flash unique id #80

Closed Ferdi265 closed 1 year ago

Ferdi265 commented 1 year ago

This PR adds a command to display the flash unique id from boards currently in BOOTSEL mode.

Since the bootrom RPC interface does not have a way to directly issue flash commands such as CMD_RUID, the only way I found to fix this is to copy the fash_get_unique_id function to RAM and use the EXEC command to read out the unique id.

the code blob uploaded to the pico was compiled using the following C and ASM code:

#include "hardware/regs/io_qspi.h"
#include "hardware/structs/ioqspi.h"
#include "hardware/structs/ssi.h"
#include "hardware/flash.h"

asm(
    ".macro static_assert value, msg\n"
        ".if !(\\value)\n"
        ".err \\msg\n"
        ".endif\n"
    ".endm\n"

    ".set FLASH_RUID_CMD, 0x4b\n"
    ".set FLASH_RUID_DUMMY_BYTES, 4\n"
    ".set FLASH_RUID_DATA_BYTES, 8\n"
    ".set FLASH_RUID_TOTAL_BYTES, (1 + FLASH_RUID_DUMMY_BYTES + FLASH_RUID_DATA_BYTES)\n"
);

void flash_do_cmd(const uint8_t * txbuf, uint8_t *rxbuf, size_t count);
void __attribute__((naked)) flash_get_unique_id_raw(void) {
    asm(
        ".Lflash_get_unique_id_raw:\n"
            "adr r0, .Ltxbuf\n"
            "adr r1, .Lrxbuf\n"
            "ldr r2, .Lbuflen\n"
            "b flash_do_cmd\n"
        ".Lbuflen:\n"
            ".word FLASH_RUID_TOTAL_BYTES\n"
        ".Ltxbuf:\n"
            ".byte FLASH_RUID_CMD\n"
            ".zero (FLASH_RUID_TOTAL_BYTES - 1)\n"
            ".zero (16 - FLASH_RUID_TOTAL_BYTES)\n"
        ".Lrxbuf:\n"
            ".zero FLASH_RUID_TOTAL_BYTES\n"
            ".zero (16 - FLASH_RUID_TOTAL_BYTES)\n"
        "static_assert ((.Lrxbuf - flash_get_unique_id_raw) == 28), \"rxbuf offset incorrect\"\n"
    );
}

static inline void __attribute__((always_inline)) flash_cs_force(_Bool high) {
    uint32_t field_val = high ?
        IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH :
        IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
    hw_write_masked(&ioqspi_hw->io[1].ctrl,
        field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
        IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS
    );
}

void flash_do_cmd(const uint8_t * txbuf, uint8_t *rxbuf, size_t count) {
    flash_cs_force(0);
    size_t tx_remaining = count;
    size_t rx_remaining = count;
    // We may be interrupted -- don't want FIFO to overflow if we're distracted.
    const size_t max_in_flight = 16 - 2;
    while (tx_remaining || rx_remaining) {
        uint32_t flags = ssi_hw->sr;
        bool can_put = !!(flags & SSI_SR_TFNF_BITS);
        bool can_get = !!(flags & SSI_SR_RFNE_BITS);
        if (can_put && tx_remaining && rx_remaining - tx_remaining < max_in_flight) {
            ssi_hw->dr0 = *txbuf++;
            --tx_remaining;
        }
        if (can_get && rx_remaining) {
            *rxbuf++ = (uint8_t)ssi_hw->dr0;
            --rx_remaining;
        }
    }
    flash_cs_force(1);
}
Ferdi265 commented 1 year ago

Tagging relevant issues: #12, #54

Ferdi265 commented 1 year ago

Converted into a draft to signify incompleteness. I am open to suggestions on how to best work this into picotool. See this PR as an RFC of sorts on how to implement this.

For devices still in application mode, the serial number could be read directly from the serial device, since the default serial for that is the flash unique id.

Optimally it would be great to be able to select devices not only by --bus B --addr A, but potentially by --serial SERIAL to simplify working with multiple picos at once.

Ferdi265 commented 1 year ago

Seeing as this is executing raw flash commands on any attached RP2040, regardless of the actually attached flash chip, this can of course potentially go badly on custom boards that do flash differently (however, the internal tinyusb code from pico-sdk would be similarly scary if not patched accordingly).

I'm not sure what to make of this; executing (a nontrivial amount of) code on any attached RP2040 at random just to figure out its serial number is a bit scary.

tomas-pecserke commented 1 year ago

Optimally it would be great to be able to select devices not only by --bus B --addr A, but potentially by --serial SERIAL to simplify working with multiple picos at once.

I implemented this in PR #84.

lurch commented 1 year ago

I know this is only a draft, but it should probably be targeting the develop branch?

Ferdi265 commented 1 year ago

Oh yes, I basically made this in a hurry; will rebase this when I get home today :)

Ferdi265 commented 1 year ago

Rebased; since I forgot to create a feature branch before I pushed this into my fork, this now looks like a merge from master into develop, but the master branch in Ferdi265/picotool now actually tracks develop.

Ferdi265 commented 1 year ago

Okay, in fixing my repo I accidently closed this branch since I renamed the master branch in my repo; I will recreate this PR later