#!/bin/sh # som-program-f7 -- program an Emcraft STM32F7 SOM's eNVM over JTAG, and say # exactly what was written from where. # # The JTAG half of the VF6 station's load step (projects-internal, vf6-mfg, # manufacturing/expect/lib.exp, som_stm32f7_program_uboot -> program_uboot_to_envm), # with the verify the station did not do. The kernel half -- NOR, written from # U-Boot over the board's serial console -- is not here; that console is # som-console. # # Every run prints one `image:` line naming the file it used and its sha256, and # ends with a single PASS or FAIL line. That output is the record of what a # board was programmed with. set -u PROG=som-program-f7 # Overridable so the same script can drive another SOM's config, and so it can # be exercised off-target against a checkout of the configs. SCRIPTS="${SOM_OPENOCD_SCRIPTS:-/usr/share/openocd/scripts}" BOARD_CFG="${SOM_BOARD_CFG:-$SCRIPTS/board/emcraft-som-stm32f7.cfg}" ENVM_ADDR=0x08000000 ENVM_SIZE=0x100000 BUILTIN=/usr/share/brintos-som/stm32f7/u-boot.bin # The mount a production station would present, checked first so that the same # script serves both: built-in images for the prototype, operator media later. # See docs/brintos-som-programming-station.md section 9. MEDIA=/mnt/media/stm32f7/u-boot.bin image="" speed="" adapter="" do_backup=1 do_readback=1 dry_run=0 usage() { cat <&2; usage >&2; exit 2 ;; esac done fail() { echo "$PROG: FAIL -- $*"; exit 1; } # --- resolve the image -------------------------------------------------------- source_kind="" if [ -n "$image" ]; then source_kind="argument" elif [ -r "$MEDIA" ]; then image=$MEDIA; source_kind="media" elif [ -r "$BUILTIN" ]; then image=$BUILTIN; source_kind="built-in" else fail "no image: neither $MEDIA nor $BUILTIN is readable, and no --image given" fi [ -r "$image" ] || fail "cannot read $image" size=$(stat -c %s "$image" 2>/dev/null) || fail "cannot stat $image" [ "$size" -gt 0 ] 2>/dev/null || fail "$image is empty" sum=$(sha256sum "$image" | cut -d' ' -f1) echo "$PROG: image: $image ($source_kind, $size bytes)" echo "$PROG: sha256: $sum" prov=$(dirname "$image")/PROVENANCE if [ -r "$prov" ]; then echo "$PROG: provenance: $prov" sed 's/^/ | /' "$prov" else echo "$PROG: provenance: none beside the image" fi echo "$PROG: target: eNVM $ENVM_ADDR on $BOARD_CFG" [ -r "$BOARD_CFG" ] || fail "no board config at $BOARD_CFG" command -v openocd >/dev/null 2>&1 || fail "openocd is not installed" # The speed override goes in BEFORE -f, because the board config reads the # variable while it is being sourced. set -- openocd if [ -n "$speed" ]; then set -- "$@" -c "set BRINTOS_JTAG_SPEED $speed" echo "$PROG: speed: ${speed} kHz (override)" else echo "$PROG: speed: the board config's default after reset" fi if [ -n "$adapter" ]; then case "$adapter" in */*) iface=$adapter ;; *.cfg) iface=interface/ftdi/$adapter ;; *) iface=interface/ftdi/$adapter.cfg ;; esac [ -r "$SCRIPTS/$iface" ] || fail "no such adapter config: $SCRIPTS/$iface" set -- "$@" -c "set BRINTOS_JTAG_IFACE $iface" echo "$PROG: adapter: $iface (override)" else echo "$PROG: adapter: the seam's default in $SCRIPTS/mfg-jtag.cfg" fi set -- "$@" -f "$BOARD_CFG" # /var/tmp links into /var/volatile, which nothing populates in a brintOS guest, # so the backup directory is found rather than assumed -- and found before any # hardware is touched. writable_dir() { [ -d "$1" ] && [ -w "$1" ] && ( : >"$1/.som-w.$$" ) 2>/dev/null && rm -f "$1/.som-w.$$" } if [ "$do_backup" -eq 0 ] && [ "$do_readback" -eq 0 ]; then backup_dir= elif [ -n "${SOM_BACKUP_DIR:-}" ]; then backup_dir=$SOM_BACKUP_DIR writable_dir "$backup_dir" || fail "SOM_BACKUP_DIR=$backup_dir is not a writable directory; nothing was written" else backup_dir= for d in /var/tmp /var/backups /tmp; do if writable_dir "$d"; then backup_dir=$d; break; fi done [ -n "$backup_dir" ] || fail "no writable directory for the backup (/var/tmp, /var/backups, /tmp); nothing was written" fi if [ -z "$backup_dir" ]; then echo "$PROG: workdir: none (--no-backup --no-readback)" elif [ "$backup_dir" = /tmp ]; then echo "$PROG: workdir: /tmp (a backup here lasts only as long as this session)" else echo "$PROG: workdir: $backup_dir" fi if [ "$dry_run" -eq 1 ]; then echo "$PROG: dry-run, nothing written" echo "$PROG: PASS -- resolved $image" exit 0 fi # --- 1. read the current eNVM out --------------------------------------------- # Before anything is erased, because this is the only copy of whatever the board # arrived with, and 1 MB costs a few seconds. if [ "$do_backup" -eq 1 ]; then stamp=$(date -u +%Y%m%dT%H%M%SZ) backup="$backup_dir/envm-$stamp.bin" echo "$PROG: backup: $backup" if ! "$@" -c init -c "reset halt" \ -c "dump_image $backup $ENVM_ADDR $ENVM_SIZE" \ -c "reset run" -c shutdown; then fail "could not read the eNVM out; nothing was written" fi [ -s "$backup" ] || fail "the backup is empty; nothing was written" echo "$PROG: backup sha256: $(sha256sum "$backup" | cut -d' ' -f1)" fi # --- 2. program and verify ---------------------------------------------------- # openocd's own `program` is init, `reset init`, `flash write_image erase`, # `verify_image` and `reset run` -- the station's sequence plus the verify it # skipped. echo "$PROG: programming ..." if ! "$@" -c "program $image $ENVM_ADDR verify reset exit"; then fail "program/verify failed -- the flash is in an unknown state" \ "${backup:+(the board's previous contents are in $backup)}" fi # --- 3. read it back ---------------------------------------------------------- # openocd's verify is a CRC the same tool computed over the same transport. A # readback compared on this side is the independent check, and it costs about a # second at 2 MHz for 120 KB. if [ "$do_readback" -eq 1 ]; then rb="$backup_dir/.som-readback.$$" if ! "$@" -c init -c halt \ -c "dump_image $rb $ENVM_ADDR $size" \ -c "reset run" -c shutdown; then rm -f "$rb" fail "programmed, but the readback could not be taken" fi rbsum=$(sha256sum "$rb" | cut -d' ' -f1) rm -f "$rb" if [ "$rbsum" != "$sum" ]; then fail "readback differs: flash $rbsum, image $sum" fi echo "$PROG: readback: $rbsum matches the image" fi echo "$PROG: the board was reset and is running what was just written." echo "$PROG: to see it boot, open the SOM's console (som-console) and reset the board." echo "$PROG: PASS -- wrote $image to $ENVM_ADDR"