Running RoboRIO firmware inside DockerContainerized native ARMv7l emulation in 20 minutes
It has now been 11 weeks since the last time I have had access to a RoboRIO to use for debugging code, and there are limits to my simulation software. So, I really only have one choice: emulate my entire robot.
My goal is to eventually have every bit of hardware on 5024's Darth Raider emulated, and running on my docker swarm. Conveniently, everything uses (mostly) the same CPU architecture. In this post, I will go over how to build a RoboRIO docker container.
Host system requirements
This process requires a host computer with:
- An x86_64 CPU
- A decent amount of RAM
- Ubuntu 18.04 or later
- Docker CE installed
- docker-compose installed
Getting a system image
This is the hardest step. To get a RoboRIO docker container running, you will need:
- A copy of the latest RoboRIO firmware package
- A copy of
To acquire a copy of the latest RoboRIO Firmware package, you will need to install the FRC Game Tools on a Windows machine (not wine).
After installing the toolsuite, and activating it with your FRC team's activation key (provided in Kit of Parts), you can grab the latest
FRC_roboRIO_XXXX_vXX.zip file from the installation directory of the FRC Imaging Tool (This will vary depending on how, and where the Game Tools are installed).
After unzipping this file, you will find another ZIP file, and a LabVIEW FPGA file. Unzip the ZIP, and look for a file called
systemimage.tar.gz. This is the RoboRIO system image. Copy it to your Ubuntu computer.
The bootstrap process is made up of a few parts:
- Enabling support for ARM-based docker containers
- Converting the RoboRIO system image to a Docker base image
- Building a Dockerfile with hacked auth
Enabling Docker-ARM support
Since the RoboRIO system image and libraries are compiled to run on ARMv7l hardware, they will refuse to run on an x86_64 system. This is where QEMU comes in to play. We can use QEMU as an emulation layer between out docker containers and our CPU. To get QEMU set up, we must first install support for ARM->x86 emulation by running:
sudo apt install qemu binfmt-support qemu-user-static -y
Once QEMU has been installed, we must run the registration scripts with:
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
Converting the system image to a Docker base
We have a system image filesystem, but need Docker to view it as a Docker image.
Using my pre-built image
Feel free to skip the following step, and just use my pre-built RoboRIO base image. It is already set up with hacked auth, and is (at the time of writing) based on firmware version
To use it, replace
ewpratten/roborio:2020_v10 in the
docker-compose.yml config below.
Building your own image
Make a folder, and put both the system image, and
libfakearmv7l.so files in it. This will be your "working directory". Now, import the system image into docker with:
docker import ./systemimage.tar.gz roborio:tmp
This will build a docker base image out of the system image, and name it
roborio:tmp. You can use this on it's own, but if you want to deploy code to the container with GradleRIO, or SSH into the container, you will need to strip the NI Auth.
Stripping National Instruments Auth
By default, the RoboRIO system image comes fairly locked down. To fix this, we can "extend" our imported docker image with some configuration to allow us to remove some unknown passwords.
In the working directory, we must first create a file called
common_auth. This will store our modified authentication configuration. Add the following to the file:
# # /etc/pam.d/common-auth - authentication settings common to all services # # This file is included from other service-specific PAM config files, # and should contain a list of the authentication modules that define # the central authentication scheme for use on the system # (e.g., /etc/shadow, LDAP, Kerberos, etc.). The default is to use the # traditional Unix authentication mechanisms. # ~~~ This file is modified for use with Docker ~~~ # here are the per-package modules (the "Primary" block) # auth [success=2 auth_err=1 default=ignore] pam_niauth.so nullok # auth [success=1 default=ignore] pam_unix.so nullok # here's the fallback if no module succeeds # auth requisite pam_deny.so # prime the stack with a positive return value if there isn't one already; # this avoids us returning an error just because nothing sets a success code # since the modules above will each just jump around auth required pam_permit.so # and here are more per-package modules (the "Additional" block)
Now, we must create a
Dockerfile in the same directory with the following contents:
FROM roborio:tmp # Fixes issues with the original RoboRIO image RUN mkdir -p /var/volatile/tmp && \ mkdir -p /var/volatile/cache && \ mkdir -p /var/volatile/log && \ mkdir -p /var/run/sshd RUN opkg update && \ opkg install binutils-symlinks gcc-symlinks g++-symlinks libgcc-s-dev make libstdc++-dev # Overwrite auth COPY system/common_auth /etc/pam.d/common-auth RUN useradd admin -ou 0 -g 0 -s /bin/bash -m RUN usermod -aG sudo admin # Fixes for WPILib RUN mkdir -p /usr/local/frc/third-party/lib RUN chmod 777 /usr/local/frc/third-party/lib # This forces uname to report armv7l COPY system/libfakearmv7l.so /usr/local/lib/libfakearmv7l.so RUN chmod +x /usr/local/lib/libfakearmv7l.so && \ mkdir -p /home/admin/.ssh && \ echo "LD_PRELOAD=/usr/local/lib/libfakearmv7l.so" >> /home/admin/.ssh/environment && \ echo "PermitUserEnvironment yes" >> /etc/ssh/sshd_config && \ echo "PasswordAuthentication no">> /etc/ssh/sshd_config # Put the CPU into 32bit mode, and start an SSH server ENTRYPOINT ["setarch", "linux32", "&&", "/usr/sbin/sshd", "-D" ]
This file will cause the container to:
- Install needed tools
- Configure an "admin" user with full permissions
- Set r/w permissions for all FRC libraries
- Overwrite the system architecture with a custom string to allow programs like
pipto run properly
- Enables password-less SSH login
- Sets the CPU to 32bit mode
We can now build the final image with these commands:
docker build -f ./Dockerfile -t roborio:local . docker rmi roborio:tmp docker tag roborio:local roborio:latest
Running the RoboRIO container locally
We can now use
docker-compose to start a fake robot network locally, and run our RoboRIO container. First, we need to make a
docker-compose.yml file. In this file, add:
version: "3" services: roborio: image: roborio:latest # Change this to "ewpratten/roborio:2020_v10" if using my pre-built image networks: robo_net: ipv4_address: 10.50.24.2 networks: robo_net: ipam: driver: default config: - subnet: 10.50.24.0/24
We can now start the RoboRIO container by running
You should now be able to SSH into the RoboRIO container with:
Or even deploy code to the container! (Just make sure to set your FRC team number to