Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ dfu-suffix binary
dfu-util binary
upload-reset binary
hid-flash binary
uf2upload binary
uf2upload.exe binary
*.class binary
*.dll binary
*.exe binary
*.jar binary
*.jpg binary
*.pdf binary
*.png binary

1 change: 1 addition & 0 deletions LICENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This files includes licensing information for parts of this repository.
* [busybox-w32](https://frippery.org/busybox/)
* [dfu-util](http://dfu-util.sourceforge.net/) (also available [here](src/dfu-util/COPYING))
* upload-reset
* uf2upload

[GPLv3](#gplv3) is used for:
* [HID-flash](https://github.com/Serasidis/STM32_HID_Bootloader)
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,31 @@
[![GitHub commits](https://img.shields.io/github/commits-since/stm32duino/Arduino_Tools/2.5.0.svg)](https://github.com/stm32duino/Arduino_Tools/compare/2.4.0...main)

Contains upload tools for STM32 based boards and some other usefull scripts.

## UF2 uploader

`uf2upload` is a self-contained host tool for a board with a UF2 mass-storage
bootloader. It first checks whether the target UF2 volume is already mounted;
when it is, it copies the converted image directly. Otherwise it performs a
USB CDC 1200-bps touch, waits for the volume label and copies the result. It
has no Python or shell dependency at upload time:

```
uf2upload --input firmware.bin --port COM7 --volume XIAOC5BOOT \
--address 0x08008000 --family 0x00C5C5C5 --max-size 1015808
```

Use `--skip-touch` to disable the CDC touch when no bootloader volume is
currently mounted. The source is built for the host package with
`make -C src/uf2upload`.

Linux ARM64/AArch64 builds are provided under `linux/aarch64/uf2upload`.
From an x86_64 Linux host, they can be rebuilt with:

```
make -C src/uf2upload aarch64
```

The ARM64 build is cross-compiled; see
[`src/linux-aarch64/LOCAL_VALIDATION.md`](src/linux-aarch64/LOCAL_VALIDATION.md)
for QEMU-based validation and its limitations.
Binary file added linux/aarch64/uf2upload
Binary file not shown.
Binary file added linux/x86_64/uf2upload
Binary file not shown.
11 changes: 11 additions & 0 deletions src/build_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,14 @@ clang \
-Werror \
-o "${ARCH_DIR}/upload_reset" \
src/upload_reset/unix/upload_reset.c

clang++ \
${CC_ARCH_ARG} \
${CC_MIN_OS_VERSION_ARG} \
${BASE_CFLAGS} \
${BASE_LDFLAGS} \
-std=c++17 \
-Wall \
-Werror \
-o "${ARCH_DIR}/uf2upload" \
src/uf2upload/uf2upload.cpp
16 changes: 12 additions & 4 deletions src/linux-aarch64/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Dockerfile for cross-compiling ARM64 Linux binaries
# This builds dfu-util, hid-flash, and upload_reset for aarch64
# This builds the STM32 host tools, including uf2upload, for Linux aarch64.

FROM ubuntu:22.04

Expand Down Expand Up @@ -57,10 +57,13 @@ RUN git clone https://github.com/Serasidis/STM32_HID_Bootloader.git && \
${STRIP} hid-flash && \
cp hid-flash /build/output/bin/

# Build upload_reset from source
# Build upload_reset and UF2 uploader from source
COPY src/upload_reset/unix/upload_reset.c /build/upload_reset.c
COPY src/uf2upload/uf2upload.cpp /build/uf2upload.cpp
RUN ${CC} -o /build/output/bin/upload_reset /build/upload_reset.c && \
${STRIP} /build/output/bin/upload_reset
${STRIP} /build/output/bin/upload_reset && \
${CXX} -O2 -std=c++17 -Wall -Wextra -Werror -o /build/output/bin/uf2upload /build/uf2upload.cpp && \
${STRIP} /build/output/bin/uf2upload

# Create output directory structure
RUN mkdir -p /output
Expand All @@ -70,7 +73,8 @@ RUN cp /build/output/bin/dfu-util /output/ && \
cp /build/output/bin/dfu-prefix /output/ && \
cp /build/output/bin/dfu-suffix /output/ && \
cp /build/output/bin/hid-flash /output/ && \
cp /build/output/bin/upload_reset /output/
cp /build/output/bin/upload_reset /output/ && \
cp /build/output/bin/uf2upload /output/

# Verify binaries are ARM64
RUN file /output/* | grep aarch64
Expand Down Expand Up @@ -129,6 +133,10 @@ RUN echo "========================================" && \
file /usr/local/bin/upload_reset && \
(upload_reset --help 2>&1 | head -5 || echo "(upload_reset executed successfully)") && \
echo "" && \
echo "=== Testing uf2upload ===" && \
file /usr/local/bin/uf2upload && \
uf2upload --help && \
echo "" && \
echo "========================================" && \
echo "✓ All ARM64 binaries are working!" && \
echo "========================================"
Expand Down
122 changes: 122 additions & 0 deletions src/linux-aarch64/LOCAL_VALIDATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Local Linux ARM64 validation for `uf2upload`

## Scope

This document describes how to validate the Linux ARM64 (`aarch64`) host build
from an x86_64 Linux machine, including Ubuntu in WSL2. It is useful when an
ARM64 development board is not available.

The tested executable is `linux/aarch64/uf2upload`. It runs on a Linux ARM64
computer such as a Raspberry Pi 64-bit OS, ARM server, or ARM64 Linux VM; it
does **not** run on the STM32C5.

## What this can prove

- The source cross-compiles with the AArch64 GNU toolchain.
- The output is an AArch64 ELF and declares the expected Linux runtime
dependencies.
- When run through QEMU user-mode emulation, the binary starts and its
command-line, UF2 conversion, directory discovery, and file-copy paths work.

## What this cannot prove

- A physical ARM64 Linux host can enumerate the board's USB CDC and mass-storage
interfaces.
- A real CDC 1200-bps request resets the board into TinyUF2.
- A real `XIAOC5BOOT` filesystem accepts the copied UF2 and programs flash.
- ARM32 (`armhf`) support. This document covers only 64-bit ARM (`aarch64`).

## Prerequisites on Ubuntu x86_64 / WSL2

```bash
sudo apt update
sudo apt install -y g++-aarch64-linux-gnu binutils-aarch64-linux-gnu \
libc6-dev-arm64-cross file qemu-user-static xxd
```

`qemu-user-static` may register the ARM64 interpreter automatically through
`binfmt_misc`. If it does not, use `qemu-aarch64-static` explicitly as shown
below.

## Cross-build and inspect

From the Arduino_Tools repository root:

```bash
make -C src/uf2upload aarch64
file linux/aarch64/uf2upload
aarch64-linux-gnu-readelf -d linux/aarch64/uf2upload | grep NEEDED
```

Expected `file` output includes:

```text
ELF 64-bit ... ARM aarch64 ... interpreter /lib/ld-linux-aarch64.so.1
```

The binary is dynamically linked and normally needs the ARM64 Linux loader,
`libstdc++.so.6`, `libgcc_s.so.1`, and `libc.so.6` supplied by the eventual
ARM64 Linux distribution.

## QEMU execution with an ARM64 sysroot

Use the installed cross sysroot. The exact directory can vary; on Ubuntu it is
usually `/usr/aarch64-linux-gnu`.

```bash
sysroot=/usr/aarch64-linux-gnu
qemu-aarch64-static -L "$sysroot" linux/aarch64/uf2upload --help
```

This must print the uploader usage text and exit successfully.

## Simulated volume and UF2 conversion

`uf2upload` searches only `/Volumes`, `/media`, `/run/media`, and `/mnt` for
the volume label. Create an isolated test path under `/mnt`; do not point this
at a real bootloader volume.

```bash
set -eu

repo_root="$(git rev-parse --show-toplevel)"
work="$(mktemp -d)"
mount_root="/mnt/uf2upload-arm64-test-$$"
trap 'sudo rm -rf "$mount_root"; rm -rf "$work"' EXIT

printf '\\x01\\x02\\x03\\x04' > "$work/firmware.bin"
sudo mkdir -p "$mount_root/XIAOC5BOOT"
sudo chown -R "$(id -u):$(id -g)" "$mount_root"

qemu-aarch64-static -L /usr/aarch64-linux-gnu \
"$repo_root/linux/aarch64/uf2upload" \
--input "$work/firmware.bin" \
--volume XIAOC5BOOT \
--address 0x08008000 \
--family 0x00C5C5C5 \
--max-size 1015808 \
--timeout 1000

test -f "$mount_root/XIAOC5BOOT"/*
stat -c '%n %s bytes' "$mount_root/XIAOC5BOOT"/*
xxd -g 4 -l 32 "$mount_root/XIAOC5BOOT"/*
```

For the 4-byte input, the copied UF2 must be 512 bytes. The first two words are
the UF2 magic values; the family field is `0x00C5C5C5` in little-endian order.

## Optional pseudo-terminal check

`socat` can provide a pseudo-terminal pair to exercise the Linux `termios`
1200-bps setup path. This is a code-path test only, not a USB CDC test. Arrange
for the simulated `XIAOC5BOOT` directory to appear while the uploader is
waiting, then pass one pseudo-terminal path through `--port`.

## Result wording

If every command above succeeds, use wording such as:

> The Linux ARM64 `uf2upload` binary was cross-built on Ubuntu x86_64 and
> executed under QEMU user-mode emulation. Command-line parsing, UF2 generation,
> simulated volume discovery, and file copy were validated. Physical ARM64 USB
> CDC and TinyUF2 hardware upload have not yet been validated.
15 changes: 15 additions & 0 deletions src/linux-aarch64/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ These scripts use Docker to cross-compile the following binaries from x86_64 to
- **dfu-suffix** - DFU file suffix tool
- **hid-flash** - HID bootloader flash utility
- **upload_reset** - STM32 reset utility
- **uf2upload** - CDC 1200-bps touch and UF2 mass-storage uploader

## Prerequisites

Expand Down Expand Up @@ -61,3 +62,17 @@ Removes all built ARM64 binaries from the `linux/aarch64/` directory.
### `Dockerfile`

Multi-stage Docker build with integrated testing.

## Build only `uf2upload`

For a local cross-build of just the UF2 uploader, install an AArch64 GNU C++
cross-compiler and run:

```bash
make -C src/uf2upload aarch64
file linux/aarch64/uf2upload
```

The resulting executable is for a Linux ARM64/AArch64 host, not for the STM32
target. See [LOCAL_VALIDATION.md](LOCAL_VALIDATION.md) for local cross-build and
QEMU validation steps, including their limitations.
4 changes: 4 additions & 0 deletions src/linux-aarch64/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ docker cp "${CONTAINER_ID}:/dfu-prefix" "${OUTPUT_DIR}/"
docker cp "${CONTAINER_ID}:/dfu-suffix" "${OUTPUT_DIR}/"
docker cp "${CONTAINER_ID}:/hid-flash" "${OUTPUT_DIR}/"
docker cp "${CONTAINER_ID}:/upload_reset" "${OUTPUT_DIR}/"
docker cp "${CONTAINER_ID}:/uf2upload" "${OUTPUT_DIR}/"

echo ""
echo "Step 5: Cleaning up container..."
Expand All @@ -70,6 +71,7 @@ chmod +x "${OUTPUT_DIR}/dfu-prefix"
chmod +x "${OUTPUT_DIR}/dfu-suffix"
chmod +x "${OUTPUT_DIR}/hid-flash"
chmod +x "${OUTPUT_DIR}/upload_reset"
chmod +x "${OUTPUT_DIR}/uf2upload"

echo ""
echo "Step 7: Verifying built binaries..."
Expand All @@ -79,6 +81,7 @@ file "${OUTPUT_DIR}/dfu-prefix"
file "${OUTPUT_DIR}/dfu-suffix"
file "${OUTPUT_DIR}/hid-flash"
file "${OUTPUT_DIR}/upload_reset"
file "${OUTPUT_DIR}/uf2upload"
echo "----------------------------------------"

echo ""
Expand All @@ -92,6 +95,7 @@ echo "All required binaries have been built:"
echo " - dfu-util, dfu-prefix, dfu-suffix (DFU programming)"
echo " - hid-flash (HID bootloader)"
echo " - upload_reset (Reset utility)"
echo " - uf2upload (CDC touch and UF2 mass-storage upload)"
echo ""
echo "Files in ${OUTPUT_DIR}:"
ls -lh "${OUTPUT_DIR}"
1 change: 1 addition & 0 deletions src/linux-aarch64/clean.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ BINARIES=(
"dfu-suffix"
"hid-flash"
"upload_reset"
"uf2upload"
)

echo "Output directory: ${OUTPUT_DIR}"
Expand Down
34 changes: 34 additions & 0 deletions src/uf2upload/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
CXX ?= g++
CXXFLAGS ?= -O2 -std=c++17 -Wall -Wextra -Werror
AARCH64_CXX ?= aarch64-linux-gnu-g++
AARCH64_STRIP ?= aarch64-linux-gnu-strip
AARCH64_DEST = ../../linux/aarch64/uf2upload

ifeq ($(OS),Windows_NT)
DEST = ../../win/uf2upload.exe
LDFLAGS ?= -static -static-libgcc -static-libstdc++
else
LDFLAGS ?=
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
DEST = ../../macosx/uf2upload
else
DEST = ../../linux/x86_64/uf2upload
endif
endif

.PHONY: all aarch64 clean

all: $(DEST)

$(DEST): uf2upload.cpp
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $<

aarch64: $(AARCH64_DEST)

$(AARCH64_DEST): uf2upload.cpp
$(AARCH64_CXX) $(CXXFLAGS) -o $@ $<
$(AARCH64_STRIP) $@

clean:
rm -f ../../win/uf2upload.exe ../../macosx/uf2upload ../../linux/x86_64/uf2upload $(AARCH64_DEST)
Loading