Skip to content

Latest commit

 

History

History

usbarmory

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

usbarmory.rs iqlusion

Crate Docs Build Status Apache 2.0 + MIT Licensed MSRV Gitter Chat

Board support package for USB armory Mk II devices from F-Secure.

USB armory mkII

Minimum Supported Rust Version

  • Rust 1.42

Status

This project is an incomplete work-in-progress in an early developmental stage and will not be ready to use for some time.

Build dependencies

  • flip-lld, linker wrapper that adds zero-cost stack overflow protection. cargo install --git https://github.com/japaric/flip-lld.

  • armv7a-none-eabi compiler support. Install with rustup target add armv7a-none-eabi.

  • if using the fs API, which is gated behind the fs Cargo feature: llvm-config, libclang and 32-bit libc headers. On Arch you can install those with sudo pacman -S clang lib32-glibc. Check that the llvm-config binary is in your $PATH (if you have something like llvm-config-9 then you'll need a symlink) and that the file /usr/include/gnu/stubs-32.h exists (path may be slightly different on other *nix OSes)

Development dependencies

  • arm-none-eabi-binutils OR (cargo-binutils + llvm-tools-preview), if you need to inspect ELF files. sudo pacman -S arm-none-eabi-binutils (Arch Linux) for the former; cargo install cargo-binutils (run it outside the firmware directory) and rustup component add llvm-tools-preview for the latter.

  • libusb-1.0-dev & libudev-dev are required to use the Cargo runner which loads Rust programs on the Armory. Install them with sudo apt-get install libudev-dev libusb-1.0-dev (Ubuntu).

  • usd-runner, the Cargo runner mentioned in the previous bullet. Install it with cd host/usd && cargo install --path .. This will also install the usd-load tool.

  • arm-none-eabi-gcc, required to load programs into the eMMC. Also needed when modifying assembly (.s) files (these need to be re-assembled).

  • qemu-system-arm v4.x, to run firmware on the host and for some unit testing. Install it with sudo pacman -S qemu-arch-extra on Arch Linux.

Building examples

$ # run this from the parent directory (`firmware`)
$ # the source code of examples is at `firmware/examples`
$ cargo build --example $example_name

Running on QEMU

NOTE: QEMU examples are currently broken (they need a different pre-main initialization routine)

The examples whose name is prefixed with qemu- are meant to be run on QEMU and not on hardware. To run these example use the following QEMU command:

(more details about QEMU & Rust, including debugging QEMU programs, can be found in the Embedded Rust book)

$ # working directory: `firmware`
$ qemu-system-arm \
  -cpu cortex-a7 \
  -machine mcimx6ul-evk  \
  -nographic \
  -semihosting-config enable=on,target=native \
  -kernel target/armv7a-none-eabi/release/examples/qemu-hello

If a example doesn't explicitly terminate itself, press C-a + c to bring up the QEMU console then enter the quit command to terminate QEMU.

Good to know

ELF images

Some basic info about ELF images; feel free to skip this section if you now what information an ELF file encodes.

When compiling a crate, rustc compiles Rust code down to a single ELF file (at least when the target is not Windows or mac). This ELF file contains the data that makes up the program (that is machine code, strings, initial values for static variables, etc.) and metadata to indicates where this data should be loaded when the programs starts executing.

Because of metadata the size of ELF files on disk does not accurately reflect the size of the program once it's loaded in memory. To see the real size of the program you need to run the size program on the ELF file.

$ # run these commands from the parent directory (`firmware`)
$ cargo build --example hello --release

$ stat --printf="%s\n" target/armv7a-none-eabi/release/examples/hello
40104

$ # if you installed `arm-none-eabi-binutils`
$ arm-none-eabi-size -Ax target/armv7a-none-eabi/release/examples/hello
target/armv7a-none-eabi/release/examples/hello  :
section             size       addr
.text              0x564   0x91f820
.rodata            0x27b   0x91fd84
.data                0x0   0x91ffff
.bss                 0x1   0x91ffff

$ # if you installed `cargo-binutils`
$ cargo size --release --example hello -- -A
hello  :
section              size      addr
.text                1380  0x91f820
.rodata               635  0x91fd84
.data                   0  0x91ffff
.bss                    1  0x91ffff

The ELF file is 40 KB on disk but only uses around 2 KB of memory when loaded in RAM.

Using the USB Armory debug accessory

You can omit this section if you are not going to use the debug accessory.

The debug accessory lets you receive data sent by the Armory through its serial interface (UART). To visualize the data and interact with the Armory via the serial interface you'll need to install and configure a terminal emulator like minicom. Furthermore, on Linux (and other POSIX OSes) you'll need to tweak some permissions to open the serial device as a non-root user. This section covers how to do those two.

minicom

Install minicom using the following command (Arch Linux):

$ sudo pacman -S minicom

Create the following configuration file:

IMPORTANT this file must contain at least one newline (\n) at the end or minicom will fail to start, parse the config file or hang.

$ cat ~/.minirc.dfl
pu addcarreturn Yes
pu baudrate 115200
pu bits 8
pu parity N
pu rtscts No
pu stopbits 1
pu xonxoff No

Non-root permissions

If you connect the debug accessory to your PC using a micro USB cable you'll see the following USB device in lsusb (or equivalent):

$ lsusb
(..)
Bus 001 Device 029: ID 0403:6011 Future Technology Devices International (..)

On Linux, to use this USB device as a non-root user create the following file:

$ cat /etc/udev/rules.d/50-usbarmory.rules
ATTRS{idVendor}=="15a2", ATTRS{idProduct}=="0080", TAG+="uaccess"

Then run the following command to update udev rules:

$ sudo udevadm control --reload-rules

Re-connecting the debug accessory to your PC should show the following permissions:

$ lsusb
(..)
Bus 001 Device 030: ID 0403:6011 Future Technology Devices International (..)

$ # the '+' is the important part
$ ls -l /dev/bus/usb/001/030
crw-rw----+ 1 root root 189, 28 Mar 24 12:32 /dev/bus/usb/001/029

If you don't get a similar output try logging out and logging in again.

Running on hardware (development mode)

Required hardware

  • USB Armory debug accessory
  • micro USB cable (for the debug accessory)

One time setup

To be able to use the Cargo runner to quickly load and run Rust programs you'll need to zero the program image currently stored in the internal eMMC. Follow the steps of "Setting up an eMMC Boot" up to before the dd invocation. Instead of flashing a image proceed to zero the 4 KB that follow the 1 KB padding (reserved for the partition table).

WARNING the following command will corrupt existing data. Do a backup if there's anything on the eMMC that you'll like to keep.

$ sudo dd if=/dev/zero of=/dev/sda bs=512 seek=2 count=8 conv=fsync
$ sync

Now terminate the USB Mass Store Device emulation by pressing Ctrl-C in the minicom terminal / u-boot console. You can now disconnect the Armory.

Hardware configuration (boot mode)

  • Select the microSD as the Armory's boot mode: there's a DIP switch on the back of the Armory; put it in the "eMMC" position.

Now connect the Armory to the PC (USB-C port). If you run lsusb (or equivalent) you should see the following Vendor ID - Product ID pair:

$ lsusb
(..)
Bus 001 Device 022: ID 15a2:0080 Freescale Semiconductor, Inc.
(..)

Loading an ELF image

Navigate to the firmware directory and use cargo run to load a program into memory.

IMPORTANT Set the COLD_BOOT environment variable when loading a program for the first time after power cycling the board (unplugging it and plugging it again). Omit the env var in consecutive invocations.

$ # use COLD_BOOT the first time

$ COLD_BOOT=1 cargo run --example hello
Hello, world!
(device has reset)

$ # then omit COLD_BOOT

$ cargo run --example hello
Hello, world!
(device has reset)

Programs loaded using the Cargo runner are loaded into RAM. These programs will be lost when power is removed from the board.

Setting up a uSD Boot

Required hardware

  • uSD card
  • (USB) uSD card reader (host side)

Creating a program image

The ROM bootloader doesn't understand ELF files; it expects a different program image format. A tool, named elf2image, is provided, in host/image, to convert ELF files to the image format expected by the ROM bootloader.

Use the following command, from the host/image directory, to convert an ELF file:

$ # adjust this to your needs
$ path2elf=../../firmware/target/armv7a-none-eabi/debug/examples/blinky

$ cargo run --bin elf2image -- $path2elf

$ stat --printf="%s\n" blinky.bin
9840

$ hexyl -n16 blinky.bin
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ d1 00 20 40 00 08 00 80 ┊ 00 00 00 00 2c 04 00 80 │×0 @0•0×┊0000,•0×│
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

Flashing the image

Insert the uSD card into your PC's card reader and identify its device file.

$ lsblk
NAME          MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
mmcblk0       179:0    0  14.5G  0 disk

Run the following command to flash the image into the uSD card.

WARNING the following command will corrupt existing data. Do a backup if there's anything on the uSD that you'll like to keep.

NOTE The ROM bootloader expects the boot image to be located at a 1024-byte offset. Hence the seek=2 argument in the previous command. This also means that you can keep a partition table in those first 1024 bytes (e.g. MBR). You can partition the uSD before running the dd command -- ensure any partition you create doesn't collide with the image you are about to flash.

$ sudo dd if=blinky.bin of=/dev/mmcblk0 bs=512 seek=2 conv=fsync
$ sync

Hardware configuration (boot mode)

  • Select the microSD as the Armory's boot mode: there's a DIP switch on the back of the Armory; put it in the "uSD" position.

  • Insert the uSD card into the Armory's slot.

Now you can plug the Armory into a USB-C port and it will run the program you just flashed.

Setting up an eMMC Boot

As the Armory HAL currently doesn't provide functionality to receive images from a PC and flash them into the internal eMMC we'll use u-boot to flash images into the eMMC.

Required hardware

  • USB Armory debug accessory
  • micro USB cable (for the debug accessory)

NOTE if you haven't used the debug accessory before check out the "Running on hardware (development mode)" section for details on how to configure your PC to use it.

Creating a program image

Same steps as in the "Setting up a uSD Boot" version.

Building U-Boot

Clone U-Boot from https://gitlab.denx.de/u-boot/u-boot.git and check out the v2019.07 tag, and obtain the following patches to add support for the USB Armory Mk II:

Now apply both of them by running git am < file.patch while in the checked-out repository.

Run make usbarmory-mark-two_config to use the default USB Armory configuration.

Then run make, as shown below, to build U-Boot:

$ # NOTE this depends on arm-none-eabi-gcc and other C build tools
$ ARCH=arm CROSS_COMPILE=arm-none-eabi- make

This should result in a u-boot-dtb.imx file, which contains the built U-Boot binary.

Flashing the image

For this step you'll need to put the Armory in uSD boot mode (dip switch on the back set to "uSD") and remove any uSD card from the Armory's card slot.

Now connect the debug accessory to the USB Armory, connect the USB Armory into one of your PC's USB-C ports and connect the debug accessory to your PC (micro USB cable). You should see the following USB devices appear in lsusb (or equivalent):

$ lsusb
(..)
Bus 001 Device 025: ID 15a2:0080 Freescale Semiconductor, Inc.
(..)
Bus 001 Device 029: ID 0403:6011 Future Technology Devices International (..)

Now load u-boot into the device's RAM using the following command:

$ # usd-load command comes from the `host/usd` crate
$ usd-load /path/to/u-boot-dtb.imx
Ivt {
    header: Header {
        tag: 209,
        length: 32,
        version: 64,
    },
    self: 0x877ff400,
    boot: 0x877ff420,
    dcd: 0x877ff42c,
    csf: 0x87881000,
    entry: 0x87800000,
}
dcd_write(address=0x00910000, count=480)
.
DCD_WRITE ACK-ed
clearing DCD pointer (0x877ff42c)
write_file(address=0x877ff400, count=531456)
........................................
WRITE_FILE ACK-ed
jump_address(address=0x877ff400)
JUMP_ADDRESS ACK-ed
DONE

Now access the u-boot console using minicom:

$ minicom -b 115200 -D /dev/ttyUSB2

=> version
U-Boot 2019.07-00002-gba935971cd (Mar 24 2020 - 12:23:32 +0100)

arm-none-eabi-gcc (Arch Repository) 9.2.0
GNU ld (GNU Binutils) 2.34

Run the following command in the u-boot console to expose the eMMC as a USB Mass Storage Device:

=> ums 0 mmc 1

On a new host terminal identify the USB device:

$ lsblk
NAME          MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda             8:0    1  14.6G  0 disk

Flash the program image (the output of elf2image) using the following command:

WARNING the following command will corrupt existing data. Do a backup if there's anything on the eMMC that you'll like to keep.

NOTE The ROM bootloader expects the boot image to be located at a 1024-byte offset. Hence the seek=2 argument in the previous command. This also means that you can keep a partition table in those first 1024 bytes (e.g. MBR). You can partition the uSD before running the dd command -- ensure any partition you create doesn't collide with the image you are about to flash.

$ sudo dd if=blinky.bin of=/dev/sda bs=512 seek=2 conv=fsync
$ sync

Now terminate the USB Mass Store Device emulation by pressing Ctrl-C in the minicom terminal / u-boot console. You can now disconnect the Armory.

Hardware configuration (boot mode)

  • Select the eMMC as the Armory's boot mode: there's a DIP switch on the back of the Armory; put it in the "eMMC" position.

Now you can plug the Armory into a USB-C port and it will run the program you just flashed.

Contributing

If you are interested in contributing to this repository, please make sure to read the CONTRIBUTING.md and CODE_OF_CONDUCT.md files first.

License

Copyright © 2020 iqlusion

Licensed under either of:

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.