Skip to content

Getting Started

r0r0x-xx edited this page Jul 30, 2026 · 1 revision

Getting Started — build, flash, and connect to a board from zero

This page walks through the entire path: cloning the repo, compiling firmware, flashing a real board, and getting a working console — no prior Rockchip, cross-compiling, or RT-Thread knowledge assumed.

0. What you need

Requirement Detail
Build host Any machine that can run Docker (macOS, Linux, Windows+WSL2)
Flashing host A Linux x86-64 machine or VM with real USB passthrough to the board (native Linux, or Linux running in a VM with USB passthrough — see the note below)
Disk ~40 GB free for build objects
Cable A USB-C data cable between the board and whichever machine is flashing it
Board An RV1106 SDK board (SPI NAND, 256 MB)

Why does flashing need a separate Linux machine? Rockchip's official flashing tool, upgrade_tool, is a Linux x86-64 ELF binary — it does not run on macOS, and it needs direct USB access to the board that a container runtime without real USB passthrough (e.g. Docker Desktop on macOS) cannot provide. Building does not have this restriction — any Docker host works for that step. If your build machine is already Linux x86-64 with USB access, you can build and flash on the same machine.

1. Get the source

git clone <this-repo-url> PWNCUBE-FIRMWARE
cd PWNCUBE-FIRMWARE/Firmware

This repository excludes two large, third-party pieces to stay clone-friendly — you need both before building:

  • Toolchains (ARM arm-rockchip830-linux-uclibcgnueabihf + RISC-V xpack-riscv-none-embed-gcc-10.2.0, ~2.6 GB) — extract into toolchain/ at the Firmware/ root.
  • rkbin (Rockchip's proprietary boot-stage binaries) — place at src/rkbin/.

See Firmware/docs/getting-started.md for exact download sources for your build of this SDK.

2. Build (in a Docker container)

The build needs a native Docker volume, not a bind-mount to the host filesystem — the rootfs assembly step (mke2fs) needs extended attributes that bind-mounts on macOS (virtiofs) don't support.

# One-time setup
docker volume create pwncube-work
docker run -d --name pwncube-build --platform linux/amd64 \
  -v "$(pwd):/sdk-src:ro" \
  -v pwncube-work:/work \
  -w /work ubuntu:22.04 sleep infinity
docker exec pwncube-build bash -c "apt-get update -qq && apt-get install -y -qq rsync"

# Copy the source into the container's native volume
docker exec pwncube-build bash -c "rsync -rlptD --exclude=output /sdk-src/ /work/"

# Install build dependencies (once per container)
docker exec pwncube-build bash -c "cd /work && ./build.sh deps"
docker exec pwncube-build bash -c "apt-get install -y python-is-python3 file"

# Full build: mcu -> uboot -> kernel -> packages -> rootfs -> pack
docker exec pwncube-build bash -c "cd /work && source scripts/00-setup-toolchain.sh && ./build.sh"

This produces /work/output/images/update.img — the single image you flash. Copy it out of the container:

docker cp pwncube-build:/work/output/images/update.img ./output/images/update.img

If you edit anything in the source tree afterward, re-sync before rebuilding:

docker exec pwncube-build bash -c "rsync -rlptD --exclude=output /sdk-src/ /work/"

Rebuilding a single component (faster than a full rebuild — e.g. after touching just the rootfs):

docker exec pwncube-build bash -c "cd /work && source scripts/00-setup-toolchain.sh && bash scripts/03-build-rootfs.sh"
docker exec pwncube-build bash -c "cd /work && source scripts/00-setup-toolchain.sh && bash scripts/04-pack-image.sh"

⚠️ Order matters if you touch the MCU firmware: uboot embeds the MCU's rtthread.bin inside uboot.img. Always run mcu before uboot, or the board boots with stale MCU firmware.

3. Get the image to your flashing machine

If building and flashing on the same Linux x86-64 machine, skip this step. Otherwise, copy update.img to your Linux flashing host (scp, a shared folder, etc.) along with the tools/upgrade_tool binary from this repo's Firmware/tools/ directory.

4. Enter maskrom mode and flash

On the physical board:

  1. Hold BOOT.
  2. Without releasing BOOT, press and release RST.
  3. Keep holding BOOT for ~5 seconds, then release.

Confirm the flashing host sees it:

sudo ./upgrade_tool LD
# expected: DevNo=1  Vid=0x2207,Pid=...,...  Mode=Maskrom

If nothing shows up, repeat the button sequence — Mode=Maskrom is the only reliable confirmation. (With the board already booted into Linux, you can also enter maskrom without touching buttons: reboot loader from a shell.)

Flash — always UF (full image); DI -b reports success but does not actually write to this board's SPI-NAND:

sudo ./upgrade_tool UF update.img

Expected output: Download Firmware Success / Upgrade firmware ok.

Quick sanity check that it booted, without a shell yet:

lsusb; dmesg | tail -20

A healthy post-flash boot briefly shows a device idVendor=2207 idProduct=110c (USB-MSC, the U-Boot/rockusb handoff) that disconnects on its own — that's normal, not an error. If the gadget console is enabled in your build (it is, by default, in this repo), a second device then appears and stays connected: idVendor=2207 idProduct=0011 — that's your console, ready for the next step.

5. Connect to the console (no UART needed)

The board has no external UART/Ethernet by default. Command and debug access goes through a USB gadget console instead — a vendor-specific bulk interface (VID:PID 2207:0011) that lands on a menu-driven shell (pwnsat-console) once Linux has booted.

Option A — from a Linux host (the same one you used to flash, or any other):

modprobe usbserial vendor=0x2207 product=0x0011   # creates /dev/ttyUSB0
screen /dev/ttyUSB0 115200                         # interactive session

Option B — directly from macOS (or any host with pyusb), no VM needed: the gadget is a raw vendor class (0xff), so macOS won't assign it a /dev/cu.* device automatically — but you can talk to it directly over libusb:

pip3 install pyusb   # once
python3 Firmware/pwncube_console.py "uname -a"
python3 Firmware/pwncube_console.py --interactive

This is the simpler path for day-to-day use: the Linux/VM step is only needed for flashing (upgrade_tool is Linux x86-64 only) — talking to an already-flashed, already-booted board works directly from any host with pyusb.

From the console menu:

PWNSAT CONSOLE: Press the number to select.
  1) Sensor / Telemetry Dashboard
  2) SpaceCAN Bus Console
  3) Debug Shell
  4) Exit console (quiet mode)
Press M anytime to return here.

Option 3 (Debug Shell) is where radio_test, sensor_test, and everything the attack scripts drive actually run.

Troubleshooting

Symptom Cause
upgrade_tool LD shows nothing Board not actually in maskrom yet — repeat the button sequence, don't retry blindly
Download Boot Fail / please check ddr Maskrom state goes stale after failed attempts — re-enter maskrom fresh and flash on the first try
Board only shows idVendor=2207 idProduct=110c and nothing else after flashing The gadget console isn't coming up — confirm your build includes the pwnsat-console package and the rootfs init changes (enabled by default in this repo)
Toolchain not configured during build Run source scripts/00-setup-toolchain.sh in the current terminal (it doesn't persist across shells)
Docker Desktop on macOS can't flash Expected — it has no real /dev/bus/usb passthrough. Use it only for building; flash from a real Linux x86-64 machine or a VM with genuine USB passthrough (VMware Fusion/Workstation with passthrough enabled, not Docker Desktop)

Clone this wiki locally