Bootloader EEPROM on a Raspberry Pi 5: step 6, read it back¶
You are upgrading a locked Raspberry Pi 5, and the two minutes at the end of step 5 are over: now you read it back and find your result.
Do the three reads again, then find your result in the chart at the end of this step.
Open the web site of the place the Pi is at (for the Welland fleet,
https://welland.fpgas.online), find this Pi in its list of boards and open
the Pi’s page. The web terminal on that page logs you in as user pi. (With
ssh instead: ssh pi@ followed by the address on that page; the same page
shows the password.) Run:
$ vcgencmd bootloader_version
2026/09/25 15:59:09
version 1accd6659983613cd104973ce2432da14f1a7682 (release)
timestamp 1790351949
update-time 1791022751
capabilities 0x0000007f
$ vcgencmd bootloader_config
[all]
BOOT_UART=1
WAKE_ON_GPIO=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf2
NET_INSTALL_AT_POWER_ON=0
That is what a Pi that is done prints. Read 1 is the first line of the first
command. Read 2 is the BOOT_ORDER line of the second.
Read 3 needs a small script on the Pi (a fleet Pi forgets it at every restart,
so put it there each time). In the terminal on the Pi, type
cat > read_flash_status.py, press Enter, paste the text below, press Enter,
and then press Ctrl-D on that empty line. Nothing is printed when it worked.
# SPDX-License-Identifier: Apache-2.0
"""Print the part and the status registers of a Pi 5's bootloader flash.
sudo python3 read_flash_status.py
Read commands only (9Fh JEDEC id, 05h SR1, 35h SR2, 15h SR3):
nothing on the flash changes.
"""
import array
import ctypes
import fcntl
import os
import struct
def xfer(fd, tx):
txb = ctypes.create_string_buffer(bytes(tx), len(tx))
rxb = ctypes.create_string_buffer(len(tx))
msg = struct.pack("QQIIHBBBBBB",
ctypes.addressof(txb), ctypes.addressof(rxb),
len(tx), 1000000, 0, 8, 0, 0, 0, 0, 0)
# SPI_IOC_MESSAGE(1)
fcntl.ioctl(fd, 0x40206B00, array.array("B", msg), True)
return rxb.raw
fd = os.open("/dev/spidev10.0", os.O_RDWR) # the Pi 5's bootloader flash
print("jedec", xfer(fd, [0x9F, 0, 0, 0])[1:].hex())
for name, cmd in (("SR1", 0x05), ("SR2", 0x35), ("SR3", 0x15)):
print(name, hex(xfer(fd, [cmd, 0])[1]))
Then run it. It only reads. (With ssh from a computer that has the script as
a file, downloaded here,
one command does both: ssh pi@ADDRESS sudo python3 - < read_flash_status.py.)
$ sudo python3 read_flash_status.py
jedec ef4015
SR1 0xbc
SR2 0x2
SR3 0x60
Read 3 is the SR1 line: 0xbc is locked, 0x0 is not locked. If the jedec
line is anything other than ef4015, this is a flash chip this page does not
cover: stop, and write nothing to that Pi.
Do not use sudo rpi-eeprom-update for this check: it prints “up to date” on
Pis that are not on the fleet’s bootloader.
Your result:
The steps the chart sends you back to: step 1, make the card, and step 2, network cable out.