diff --git a/README.md b/README.md index 61290be..7e07422 100644 --- a/README.md +++ b/README.md @@ -55,19 +55,6 @@ cd bootloader && pio run -e versapad_bootloader --target upload # einmalig pio run -e versapad_usb --target upload # danach jedes App-Update ``` -`bootloader/` ist ein eigenständiges PlatformIO-Projekt (eigene -`platformio.ini`, kein Arduino-Framework). Für die PlatformIO-IDE-Buttons in -VS Code müsste der Ordner separat als eigener Workspace geöffnet werden; über -die Kommandozeile reicht `cd bootloader && pio run ...` im selben Fenster. - -Sobald der Bootloader installiert ist, **verweigert `versapad --target -upload` den normalen Upload** — dieser Weg schreibt ab `0x0000` und würde den -Bootloader sonst kommentarlos überschreiben (`upload_openocd.py` prüft das -vorher automatisch). Bauen und Debuggen über `versapad` bleiben uneingeschränkt -möglich, nur der Upload ist betroffen. Für App-Updates danach `versapad_usb` -verwenden; bewusst zurück zu reinem SWD-Betrieb geht über -`pio run -e versapad -t erase-bootloader-and-flash`. - Details, Speicherlayout und die Bootloader-Aktivierung (kein physischer Reset-Taster auf dieser Platine) stehen in [10_usb_bootloader.md](doc/10_usb_bootloader.md). diff --git a/bootloader/.gitignore b/bootloader/.gitignore deleted file mode 100644 index 89cc49c..0000000 --- a/bootloader/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch diff --git a/doc/10_usb_bootloader.md b/doc/10_usb_bootloader.md index 5aa5b89..0397fa9 100644 --- a/doc/10_usb_bootloader.md +++ b/doc/10_usb_bootloader.md @@ -56,30 +56,6 @@ erkennt den Schreibzugriff und springt selbständig in die neue App. `upload_uf2.py` sucht das Laufwerk aktuell nur über die Windows-API (`GetVolumeInformationW`) — keine macOS/Linux-Unterstützung. -## Schutz gegen versehentliches Überschreiben (env:versapad) - -`env:versapad` (SWD, `boards/versapad_nobl.json`) schreibt die App-Firmware -ab `0x0000` und überschreibt damit einen installierten Bootloader -kommentarlos — genau das ist am 2026-08-05 während der Entwicklung passiert -(zweimal). `upload_openocd.py` prüft das seither vor jedem `versapad`-Upload: - -- vergleicht per `verify_image` gegen die lokal gebaute - `bootloader/.pio/build/versapad_bootloader/firmware.bin` -- Bootloader erkannt → Upload wird verweigert, mit Hinweis auf - `versapad_usb` oder den expliziten Override -- lässt sich die Prüfung nicht eindeutig durchführen (z. B. `bootloader/` - noch nicht gebaut, oder die SWD-Verbindung antwortet nicht) → wird - sicherheitshalber ebenfalls verweigert, nicht durchgelassen -- gilt nur für `env:versapad` — `env:versapad_bootloader` nutzt dasselbe - Skript (`extra_scripts = ../upload_openocd.py`) und schreibt bewusst immer - auf `0x0000`, ungeprüft - -Bewusstes Überschreiben (zurück zu reinem SWD-Betrieb ohne Bootloader): - -```bash -pio run -e versapad -t erase-bootloader-and-flash -``` - ## Herkunft und Details Der Bootloader ist abgeleitet und stark eingekürzt aus @@ -111,12 +87,6 @@ stehen in `bootloader/README.md` unter "Hardwaretest"; kurz zusammengefasst: immer `.bin` mit expliziter Adresse und getrennte OpenOCD-Aufrufe verwenden — Details in `bootloader/README.md`, "Achtung bei manuellem SWD-Flashen". -- **`env:versapad` überschreibt den Bootloader kommentarlos.** Ein normaler - `pio run -e versapad --target upload` (der alte, gewohnte SWD-Weg für - App-Updates) schreibt ab `0x0000` und hat den Bootloader dabei zweimal - ohne jede Warnung zerstört. Fix: automatischer Presence-Check in - `upload_openocd.py`, siehe "Schutz gegen versehentliches Überschreiben" - oben. ## Bekannte Einschränkungen diff --git a/upload_openocd.py b/upload_openocd.py index ea69879..b311728 100644 --- a/upload_openocd.py +++ b/upload_openocd.py @@ -2,118 +2,23 @@ Import("env") import os import subprocess -# Real, already-built bootloader image used as the reference for the presence -# check below -- a raw synthetic probe blob turned out unreliable with -# verify_image (silent no-op on tiny files), whereas verify_image against a -# real firmware .bin has been solid throughout this project's bring-up. -BOOTLOADER_BIN = os.path.join( - "bootloader", ".pio", "build", "versapad_bootloader", "firmware.bin" -) +def upload_via_openocd(source, target, env): + pkg_dir = env.PioPlatform().get_package_dir("tool-openocd") + openocd = os.path.join(pkg_dir, "bin", "openocd.exe") + scripts = os.path.join(pkg_dir, "scripts") + firmware = str(source[0]) # .elf path - -def _openocd_paths(env): - pkg_dir = env.PioPlatform().get_package_dir("tool-openocd") - return ( - os.path.join(pkg_dir, "bin", "openocd.exe"), - os.path.join(pkg_dir, "scripts"), - ) - - -def _run_openocd(env, extra_cmd, capture=False): - openocd, scripts = _openocd_paths(env) cmd = [ openocd, "-s", scripts, "-f", "interface/cmsis-dap.cfg", "-f", "target/at91samdXX.cfg", - "-c", extra_cmd, + "-c", 'program "{}" verify reset; shutdown'.format(firmware.replace("\\", "/")) ] + print(" ".join(cmd)) - if capture: - return subprocess.run(cmd, capture_output=True, text=True) - return subprocess.run(cmd) - - -def _bootloader_present(env): - bootloader_bin = env.subst( - os.path.join("$PROJECT_DIR", BOOTLOADER_BIN) - ) - if not os.path.isfile(bootloader_bin): - print("WARNING: {} not found (build it with".format(BOOTLOADER_BIN)) - print("'cd bootloader && pio run -e versapad_bootloader') -- can't check") - print("for an installed bootloader, refusing to flash as a precaution.") - print("Use 'pio run -e versapad -t erase-bootloader-and-flash' to override.") - return True - - result = _run_openocd( - env, - 'init; reset halt; verify_image "{}" 0x0; shutdown'.format( - bootloader_bin.replace("\\", "/") - ), - capture=True, - ) - output = result.stdout + result.stderr - if "checksum mismatch" in output or "diff " in output: - return False # something else is at 0x0000 -- not this bootloader - if "halted due to debug-request" in output and "Error" not in output: - return True # verify_image is silent on a match; absence of a - # mismatch/error after a successful connect means it matched - print("WARNING: could not read flash to check for an installed bootloader") - print("(inconclusive) -- refusing to flash as a precaution.") - print("Use 'pio run -e versapad -t erase-bootloader-and-flash' to override.") - return True - - -def _flash(env, firmware): - result = _run_openocd( - env, 'program "{}" verify reset; shutdown'.format(firmware.replace("\\", "/")) - ) + result = subprocess.run(cmd) if result.returncode != 0: env.Exit(1) - -def upload_via_openocd(source, target, env): - firmware = str(source[0]) # .elf path - - # This script is shared with bootloader/platformio.ini's own upload - # (env:versapad_bootloader), which legitimately writes 0x0000 every time - # -- the guard below only makes sense for the app-without-bootloader - # target (env:versapad). - if env["PIOENV"] == "versapad" and _bootloader_present(env): - print("=" * 78) - print("REFUSING TO FLASH: a UF2 bootloader looks like it's installed at 0x0000.") - print("") - print("This target (env:versapad) writes the app starting at 0x0000 and") - print("would silently overwrite it -- the board would lose its USB flashing") - print("path (see doc/10_usb_bootloader.md).") - print("") - print("Use instead:") - print(" pio run -e versapad_usb --target upload # flash over USB, keeps the bootloader") - print("or, if you deliberately want to erase the bootloader and go back to") - print("standalone SWD-only operation:") - print(" pio run -e versapad -t erase-bootloader-and-flash") - print("=" * 78) - env.Exit(1) - - _flash(env, firmware) - - env.Replace(UPLOADCMD=upload_via_openocd) - - -def erase_bootloader_and_flash(*_args, **_kwargs): - firmware = env.subst(os.path.join("$BUILD_DIR", "${PROGNAME}.elf")) - print("Overwriting 0x0000..0x1FAFF -- any installed UF2 bootloader will be erased.") - _flash(env, firmware) - - -env.AddCustomTarget( - name="erase-bootloader-and-flash", - dependencies=["buildprog"], - actions=[erase_bootloader_and_flash], - title="Erase bootloader + flash (standalone SWD)", - description=( - "Unconditionally overwrites 0x0000..0x1FAFF, wiping any installed UF2 " - "bootloader. Use only to return to standalone SWD-only operation." - ), -)