Monorepo: https://github.com/solokeys/solo2
Hardware: https://github.com/solokeys/solo2-hw
If you follow this document, it will reflash your hacker key into a state where you can not reset into the bootloader, and can not reflash.
This document contains many incorrect and outdated commands.
Do not proceed with this guide unless you can recover a key!!!
Feel free to edit or comment on this getting started page directly
Shortlink: https://solo2.dev
This documentation is CC BY-SA licensed.
Other chat rooms of interest:
make run-dev
runners/lpc55
and calls
cargo run --release --features board-lpcxpresso55,develop
arm-none-eabi-gdb
as runner as configured in .cargo/config.The feature develop
activates useful features during development
See runners/lpc55/Cargo.toml for all features.
To just build: make build-dev
Helpful during development: cargo install bacon; cd runners/lpc55; bacon -j check-dev
Very basic test:
pip install 'fido2~=0.9'
python tests/basic.py
Install Rust/Cargo from rustup.rs:
https://www.rust-lang.org/tools/install
rustup target install thumbv8m.main-none-eabi
cargo install flip-link
cargo install cargo-binutils
rustup component add llvm-tools-preview
Then perform OS/distro specific steps listed below.
Both VS Code + neovim (nightly 0.5 preview) work well with rust-analyzer.
For debugging setup, please see the debugging section.
sudo apt-get install git llvm clang libclang-dev gcc-arm-none-eabi gdb-arm-none-eabi libc6-dev-i386
On Debian, you need to use the gdb-multiarch
executable instead of arm-none-eabi-gdb
, so either create a symlink or update .cargo/config
.
yay clang llvm arm-none-eabi-gdb
You can use the following mkShell
expression inside a flake.nix
or shell.nix
, as long as you provide nixpkgs
and nixpkgs-mozilla
.
{ nixpkgs, mozilla }:
let
rust-overlay = import "${mozilla}/rust-overlay.nix";
pkgs = import nixpkgs {
overlays = [ rust-overlay ];
};
rust_stable = pkgs.latest.rustChannels.stable.rust;
rust_thumbv8m = rust_stable.override {
targets = [ "thumbv8m.main-none-eabi" ];
};
pkgs_cross = pkgs.pkgsCross.arm-embedded;
gcc = pkgs_cross.buildPackages.gcc;
libc_include = "${gcc.libc}/${gcc.libc.incdir}";
gcc-unwrapped = pkgs_cross.buildPackages.gcc-unwrapped;
gcc_include = "${gcc-unwrapped}/lib/gcc/${gcc-unwrapped.targetConfig}/${gcc-unwrapped.version}/include";
in pkgs.mkShell {
nativeBuildInputs = [
gcc
rust_thumbv8m
# Is being added to nixpkgs: https://github.com/NixOS/nixpkgs/pull/121184
#pkgs.flip-link
pkgs.llvm
pkgs.wget
];
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang}/lib";
TARGET_CC = "${gcc.targetPrefix}cc";
TARGET_AR = "${gcc.targetPrefix}ar";
TARGET_CFLAGS = "-I${libc_include}";
BINDGEN_EXTRA_CLANG_ARGS = "-I${libc_include} -I${gcc_include}";
}
TODO (doesn't work currently, because of littlefs2 which cannot be easily compiled on macOS)
TODO
There are multiple possibilities for writing firmware to the Solo 2.
lpc55-host
Install the lpc55 utility:
cargo install lpc55
Bring the device into bootloader mode.
It should then show up using the lpc55
tool.
$ lpc55 ls
bootloaders:
Bootloader { vid: 1209, pid: B000, uuid: A2... }
Before writing custom firmware and data onto your Solo's flash, make a backup to ensure that you can go back to a sane state later.
# Read the program memory flash
lpc55 read-memory -vvv 0 524288 -o solo2-fw-backup-program.bin
# Also read the Protected Flash Region pages.
# CFPA scratch 0x9de00
lpc55 read-memory -vvv 646656 512 -o solo2-fw-backup-pfr-cfpa-scratch.bin
# CFPA ping 0x9e000
lpc55 read-memory -vvv 647168 512 -o solo2-fw-backup-pfr-cfpa-ping.bin
# CFPA pong 0x9e200
lpc55 read-memory -vvv 647680 512 -o solo2-fw-backup-pfr-cfpa-pong.bin
# CMPA 0x9e400
lpc55 read-memory -vvv 648192 512 -o solo2-fw-backup-pfr-cmpa.bin
To be able to run custom firmware, we need to disable secure boot.
This is done by disabling the SECURE_BOOT_CFG
/ SEC_BOOT_EN
flag in the CMPA protected flash region page.
First, read the PFR with the lpc55-host
tool:
lpc55 pfr --format yaml > pfr-settings.yaml
cp pfr-settings.yaml pfr-settings-no-secure-boot.yaml
⚠️ IMPORTANT: You now need to modify the copied file. Only keep the factory
top level object and set secure-boot-configuration
→ secure-boot-enabled: false
.
Your file should look something like this:
factory:
boot-configuration:
speed: 48MHz
mode: Usb
usb-id:
vid: 4617
pid: 45056
secure-boot-configuration:
secure-boot-enabled: false # this was changed
dice-computation-disabled: true
rot-fingerprint: ... # omitted, don't change in your file
customer-data: ... # omitted, don't change in your file
After that, we can write the CMPA settings:
lpc55 configure factory-settings -vvv pfr-settings-no-secure-boot.yaml
After secure boot was disabled, you can flash the self-built firmware.
lpc55 write-flash -vvv path/to/provisioner.bin
When you are really sure that the flashing process worked fine, restart the Solo2.
lpc55 reboot
mboot
Install cargo-binutils
:
$ cargo install cargo-binutils
$ rustup component add llvm-tools-preview
Install mboot
, for example using pip
and a virtual environemnt:
$ python3 -m venv mboot
$ source mboot/bin/activate
$ pip install mboot
If you want to perform the following steps without superuser rights, install these UDEV rules:
$ curl https://raw.githubusercontent.com/molejar/pyIMX/master/udev/90-imx-sdp.rules | sudo tee /etc/udev/rules.d/90-imx-sdp.rules
$ sudo udevadm control --reload-rules
Check that the device is connected and in bootloader mode:
$ mboot info
DEVICE: USB COMPOSITE DEVICE (0x1FC9, 0x0021)
...
Compile the firmware and prepare a binary image (see the Overview section for information about the features):
$ cargo objcopy --release --features board-lpcxpresso55,develop -- -O binary firmware.bin
Flash the firmware image to the MCU:
$ mboot erase --mass
$ mboot write firmware.bin
fido-authenticator
fido-authenticator
needs an attestation key and certificate, both with the ID 0. These can be generated using solo2-cli
(requires the dev-pki
feature):
$ solo2-cli dev-pki fido fido.key fido.cert
These files (or any other files in the required format) can then be written to the device using the provisioner app. First, run the provisioner app on the prototype:
$ cd runners/lpc55 && make run-pro
Then use solo2-cli
to write the key and the certificate to the device:
$ solo2-cli app provisioner write-file fido.cert fido/x5c/00
$ solo2-cli app provisioner write-file fido.key fido/sec/00
(Make sure that there is no other pcsc smartcard connected to the device becuase currently, solo2-cli can’t handle multiple available smartcards.)
To debug the firmware on the board, the arm
version of GDB (Debian gdb-multiarch
and Arch arm-none-eabi-gdb
) is required.
If you want to use a J-Link (highly recommended, as it can be used to flash the firmware without bootloader mode), install the J-Link Software and Documentation Pack providing JLinkGDBServer
.
Flash the JLink debugger firmware to the NXP LPC55 devkit (guide – if you figure out how to use probe.rs instead, please share!)
Then you need to connect both the debug port via USB and USB for the LPC55 itself.
For debugging with the Solo 2 Hacker edition, you need a special cable (TC2030-CTX No Legs, 6 PIN, 10 PIN - note the number of pins for the ribbon connector).
This can be used with the Segger JLink EDU mini, which you are allowed to use in private or educational contexts.
Note: The outer metal pins are too long for the spring pins to make contact with the Solo 2. You need to shorten them carefully.
Debug Link
port, run JLinkGDBServer -strict -device LPC55S69 -if SWD -vd
.nc localhost 19021
to connect to the RTT port of the J-Link server. This will show you the delog log output of the firmware via RTT.make build-dev
(see runners/lpc55 for more information on building with logging enabled).arm-none-eabi-gdb -x runners/lpc55/jlink.gdb runners/lpc55/target/thumbv8m.main-none-eabi/release/runner
. This executes the GDB commands from the runners/lpc55/jlink.gdb
file and loads the symbols from the specified runner
binary.If you're not the command-line person but would still like to debug the firmware, the marus25.cortex-debug
VS Code extension will do the heavy-lifting for you.
Note, however, that is does not work as well as plain GDB does.
When setting breakpoints using the VS Code interface, make sure to open the files that are actually compiled in the firmware.
Thus, setting a breakpoint in the fido-authenticator
for example, will require you to open the corresponding file in your .cargo
folder, unless you modified the Cargo.toml
to use a local copy of the source code.
The following is an examplary launch configuration for VS Code:
{
"type": "cortex-debug",
"request": "launch",
"servertype": "jlink",
"executable": "./solo2/runners/lpc55/target/thumbv8m.main-none-eabi/release/runner",
"name": "Debug (J-Link)",
"device": "LPC55S69",
"interface": "swd",
"cwd": "${workspaceRoot}",
"serverArgs": [
"-strict",
"-vd",
],
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"label": "",
"port": 0,
"type": "console"
}
]
}
}