add emmc writer
This commit is contained in:
3
rpi/mass-storage-gadget64/.gitignore
vendored
Normal file
3
rpi/mass-storage-gadget64/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.h
|
||||
boot.sig
|
||||
bootfiles.original.bin
|
58
rpi/mass-storage-gadget64/README.md
Normal file
58
rpi/mass-storage-gadget64/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
# USB mass-storage gadget for BCM2711 and BCM2712
|
||||
|
||||
This directory provides a bootloader image that loads a Linux
|
||||
initramfs that exports common block devices (EMMC, NVMe) as
|
||||
USB mass storage devices using the Linux gadget-fs drivers.
|
||||
|
||||
This allows Raspberry Pi Imager to be run on the host computer
|
||||
and write OS images to the Raspberry Pi or Compute Module block devices.
|
||||
|
||||
## Running
|
||||
To run load the USB MSD device drivers via RPIBOOT run
|
||||
```bash
|
||||
rpiboot -d mass-storage-gadget64
|
||||
|
||||
```
|
||||
|
||||
### Debug
|
||||
The mass-storage-gadget image automatically enables a UART console for debugging (user `root` empty password).
|
||||
|
||||
## Secure boot
|
||||
Once secure-boot has been enable the OS `boot.img` file must be signed with the customer private key.
|
||||
On Pi5 firmware must also be counter-signed with this key.
|
||||
|
||||
The `sign.sh` script wraps the command do this on Pi4 and Pi5.
|
||||
```bash
|
||||
KEY_FILE=$HOME/private.pem
|
||||
./sign.sh ${KEY_FILE}
|
||||
```
|
||||
or as follows if using a HSM wrapper script.
|
||||
```bash
|
||||
./sign.sh -H hsm-wrapper public.pem
|
||||
```
|
||||
|
||||
WARNING: The signed images will not be bootable on a Pi5 without secure-boot enabled. Run `./reset.sh` to reset the signed images to the default unsigned state.
|
||||
|
||||
## Source code
|
||||
The buildroot configuration and supporting patches is available on
|
||||
the [mass-storage-gadget64](https://github.com/raspberrypi/buildroot/tree/mass-storage-gadget64)
|
||||
branch of the Raspberry Pi [buildroot](https://github.com/raspberrypi/buildroot) repo.
|
||||
|
||||
### Building
|
||||
|
||||
In order to build directly on a Linux host that has the needed dependencies, run:
|
||||
```bash
|
||||
git clone --branch mass-storage-gadget64 git@github.com:raspberrypi/buildroot.git
|
||||
cd buildroot
|
||||
make raspberrypi64-mass-storage-gadget_defconfig
|
||||
make
|
||||
```
|
||||
|
||||
The output is written to `output/images/sdcard.img` and can be copied to `boot.img`
|
||||
|
||||
Alternatively, if you have docker installed and would like to use the upstream buildroot CI docker image for a build environment, use its `utils/docker-run` helper script:
|
||||
```bash
|
||||
$ git clone --branch mass-storage-gadget64 git@github.com:raspberrypi/buildroot.git
|
||||
$ cd buildroot
|
||||
$ ./utils/docker-run /bin/bash -c "make raspberrypi64-mass-storage-gadget_defconfig && make"
|
||||
```
|
BIN
rpi/mass-storage-gadget64/boot.img
Normal file
BIN
rpi/mass-storage-gadget64/boot.img
Normal file
Binary file not shown.
BIN
rpi/mass-storage-gadget64/bootfiles.bin
Normal file
BIN
rpi/mass-storage-gadget64/bootfiles.bin
Normal file
Binary file not shown.
5
rpi/mass-storage-gadget64/config.txt
Normal file
5
rpi/mass-storage-gadget64/config.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# Load boot.img containing the Linux initramfs for the mass-storage-gadget
|
||||
# In signed-boot or secure-boot mode the bootloader checks the
|
||||
# RSA signature of the ramdisk. The signature is located in boot.sig
|
||||
boot_ramdisk=1
|
||||
uart_2ndstage=1
|
4
rpi/mass-storage-gadget64/reset.sh
Executable file
4
rpi/mass-storage-gadget64/reset.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -f boot.sig bootfiles.bin bootfiles.original.bin
|
||||
ln -sf ../firmware/bootfiles.bin .
|
71
rpi/mass-storage-gadget64/sign.sh
Executable file
71
rpi/mass-storage-gadget64/sign.sh
Executable file
@ -0,0 +1,71 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
set -u
|
||||
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
TMP_DIR=""
|
||||
SIGN_ARGS=""
|
||||
PUBLIC_KEY=""
|
||||
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [ -d "${TMP_DIR}" ]; then rm -rf "${TMP_DIR}"; fi
|
||||
}
|
||||
|
||||
sign_firmware_blob() {
|
||||
echo "Signing firmware in ${1} using $(which rpi-sign-bootcode)"
|
||||
rpi-sign-bootcode \
|
||||
-c 2712 \
|
||||
-i "${1}" \
|
||||
-o "${2}" \
|
||||
-n 16 \
|
||||
-v 0 \
|
||||
${SIGN_ARGS} ${PUBLIC_KEY}
|
||||
}
|
||||
|
||||
sign_bootfiles() {
|
||||
echo "Signing OS image ${1}"
|
||||
input="${1}"
|
||||
output="${2}"
|
||||
(
|
||||
cd "${TMP_DIR}"
|
||||
tar -xf "${input}"
|
||||
echo "Signing 2712/bootcode5.bin"
|
||||
sign_firmware_blob 2712/bootcode5.bin 2712/bootcode5.bin.signed || die "Failed to sign bootcode5.bin"
|
||||
mv -f "2712/bootcode5.bin.signed" "2712/bootcode5.bin"
|
||||
tar -cf "${output}" *
|
||||
find .
|
||||
)
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
if [ "${1}" = "-H" ]; then
|
||||
HSM_WRAPPER="${2}"
|
||||
PUBLIC_KEY="${3}"
|
||||
[ -f "${PUBLIC_KEY}" ] || die "HSM requires a public key file in PEM format. Public key \"${PUBLIC_KEY}\" not found."
|
||||
PUBLIC_KEY="-p ${3}"
|
||||
if ! command -v "${HSM_WRAPPER}"; then
|
||||
die "HSM wrapper script \"${HSM_WRAPPER}\" not found"
|
||||
fi
|
||||
SIGN_ARGS="-H ${HSM_WRAPPER}"
|
||||
else
|
||||
KEY_FILE="${1}"
|
||||
[ -f "${KEY_FILE}" ] || die "KEY_FILE: ${KEY_FILE} not found"
|
||||
SIGN_ARGS="-k ${KEY_FILE}"
|
||||
fi
|
||||
|
||||
PATH="${script_dir}/../tools:${PATH}"
|
||||
KEY_FILE="${1}"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
rm -f bootfiles.bin
|
||||
ln -sf ../firmware/bootfiles.bin bootfiles.original.bin
|
||||
sign_bootfiles "$(pwd)/bootfiles.original.bin" "$(pwd)/bootfiles.bin"
|
||||
|
||||
echo "Signing boot.img with ${SIGN_ARGS}"
|
||||
rpi-eeprom-digest -i boot.img -o boot.sig ${SIGN_ARGS}
|
BIN
rpi/rpiboot
Executable file
BIN
rpi/rpiboot
Executable file
Binary file not shown.
148
rpi/write_from_rpi
Executable file
148
rpi/write_from_rpi
Executable file
@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
from select import select
|
||||
from glob import glob
|
||||
from subprocess import check_output, STDOUT, PIPE, Popen, TimeoutExpired
|
||||
|
||||
IMAGES_HOST = 'l_samenv@linse-c'
|
||||
IMAGES_DIR = 'boxes/rpi_images'
|
||||
IMAGES_PAT = '*.lz4'
|
||||
|
||||
extdisks = {}
|
||||
|
||||
|
||||
def prt(text):
|
||||
print(time.strftime('%H:%M:%S'), text)
|
||||
|
||||
|
||||
def cmd(command, verbose=False):
|
||||
if verbose:
|
||||
print(f'> {command}')
|
||||
return check_output(command.split(), stderr=STDOUT).decode('latin-1')
|
||||
|
||||
|
||||
def get_disks():
|
||||
return [line for line in cmd('lsblk -d').split('\n') if line]
|
||||
|
||||
|
||||
def rpiboot():
|
||||
lines = []
|
||||
with Popen(['sudo', './rpiboot', '-d', 'mass-storage-gadget64'], stdout=PIPE) as p:
|
||||
tmo = 1
|
||||
t = 0
|
||||
while select([p.stdout], [], [], tmo)[0]:
|
||||
rawline = p.stdout.readline()
|
||||
if not rawline:
|
||||
return tmo != 1
|
||||
line = rawline.decode('latin-1').strip()
|
||||
print(line)
|
||||
if lines is None:
|
||||
pass # print(line)
|
||||
elif 'Waiting' in line:
|
||||
tmo = 10
|
||||
lines.append(line)
|
||||
t = time.time()
|
||||
elif tmo == 1:
|
||||
lines.append(line)
|
||||
else:
|
||||
print('\n'.join(lines))
|
||||
print(line)
|
||||
lines = None
|
||||
else:
|
||||
p.terminate()
|
||||
return False
|
||||
|
||||
|
||||
def list_external():
|
||||
print('--- external disks:')
|
||||
for disk, info in extdisks.items():
|
||||
print(disk, info)
|
||||
|
||||
|
||||
rescue = """
|
||||
Remark: not all cables might work.
|
||||
Example on Markus Mac:
|
||||
- does not work: black USB C to USB C cable
|
||||
- works: white USB A to USB C cable plugged into docking station.
|
||||
|
||||
Please unpower the box with compute module,
|
||||
start this script again and put power immediately."""
|
||||
|
||||
|
||||
def wait_disks(prevset, delay):
|
||||
for i in range(int(delay)):
|
||||
disks = get_disks()
|
||||
diskset = set(disks)
|
||||
if diskset != prevset:
|
||||
print('\n'.join(disks))
|
||||
if i:
|
||||
print('')
|
||||
return diskset - prevset
|
||||
print('.', end='')
|
||||
time.sleep(1)
|
||||
return None
|
||||
|
||||
|
||||
def find_raspi(kind='emmc'):
|
||||
input('make sure device is not connected [confirm]')
|
||||
prevset = set(get_disks())
|
||||
input('now connect [confirm]')
|
||||
disks = wait_disks(prevset, 5)
|
||||
if not disks:
|
||||
print('can not yet detect disk, try to run rpiboot')
|
||||
print('if is blocked at "Waiting for BCM...", turn off and on power of target rpi')
|
||||
# cmd('sudo ./rpiboot -d mass-storage-gadget64')
|
||||
rpiboot()
|
||||
disks = wait_disks(prevset, 15)
|
||||
if not disks:
|
||||
raise RuntimeError('raspi disk does not appear')
|
||||
return disks
|
||||
|
||||
|
||||
def write_image(kind):
|
||||
proposed = [v.split()[0] for v in find_raspi(kind)]
|
||||
list_external()
|
||||
if len(proposed) == 1:
|
||||
dev = proposed[0]
|
||||
elif len(proposed) > 1:
|
||||
if kind == 'emmc':
|
||||
print('several potential devices:', proposed)
|
||||
return
|
||||
dev = input('select device to write to {" ".join(proposed)}: ')
|
||||
else:
|
||||
print('not found')
|
||||
return
|
||||
images = check_output(['sudo', '-u', 'l_samenv', 'ssh', IMAGES_HOST, f'cd {IMAGES_DIR} ; ls {IMAGES_PAT}']).decode('latin-1').split('\n')
|
||||
for file in sorted(images):
|
||||
print(file)
|
||||
print('')
|
||||
image = input(f'select image to write to {dev} [{file}]: ') or file
|
||||
prt(f'unmount {dev} ...')
|
||||
print('you may be prompted for your account password for sudo')
|
||||
os.system(f'sudo umount /dev/{dev}')
|
||||
print('')
|
||||
prt('get, uncompress and write... (takes about 15 min)')
|
||||
os.system(f'ssh {IMAGES_HOST} "dd if={IMAGES_DIR}/{image} bs=512k" | lz4 -d | sudo dd of=/dev/{dev} bs=512k')
|
||||
prt('unmount bootfs')
|
||||
for _ in range(3):
|
||||
if os.path.exists('/Volumes/bootfs'):
|
||||
prt('unmount bootfs')
|
||||
os.system('sudo umount bootfs')
|
||||
break
|
||||
time.sleep(1.0)
|
||||
prt('done')
|
||||
os.system(f'sudo umount /dev/{dev}')
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
write_image(sys.argv[1])
|
||||
else:
|
||||
print("""
|
||||
Usage:
|
||||
|
||||
> write_to emmc # write to compute module using PoE board"
|
||||
> write_to sd # write to sd card
|
||||
|
||||
""")
|
Reference in New Issue
Block a user