FVP¶
The FVP device type in LAVA refers to running Fixed Virtual Platforms.
LAVA FVP worker setup¶
LAVA executes FVP devices inside Docker containers. Therefore, like any other
LAVA worker that can run docker device types, Docker needs to be
installed.
Creating device type¶
Create the device type using the name fvp.
Creating device¶
- Add the device using the following settings:
- Device Type:
fvp - Hostname: A unique name (e.g.,
fvp-01)
- Device Type:
-
For a minimal configuration, simply extend the base template:
{% extends "fvp.jinja2" %}
FVP Docker images¶
LAVA does not handle the download of FVP binaries. These are assumed to be in the Docker image defined in the LAVA job.
You can use Docker images published by the
shrinkwraptool project. The image
docker.io/shrinkwraptool/base-slim:2025.12.0 is known to work with LAVA.
Building FVP Docker images¶
Here is a sample Dockerfile for building your own Docker images for running FVPs in LAVA.
FROM ubuntu:22.04
# Install telnet package
RUN apt-get update && \
apt-get install --no-install-recommends --yes bc libatomic1 telnet libdbus-1-3 && \
rm -rf /var/cache/apt
# Add FVP Binaries
RUN mkdir /opt/fvp
ADD Foundation_Platform_11.30_27_Linux64.tgz /opt/fvp
This example is for the free Foundation model. Download this by going to
FVP Downloads
and downloading the Foundation_Platform_*_Linux64.tgz archive. Put the
Dockerfile and the archive in the same directory, then build the image from
there:
docker build -t fvp_foundation_platform:11.30 .
Run the following command to test the image. Adjust the versions and binary
paths as needed. You should see Hello, 64-bit world! printed.
docker run --rm \
fvp_foundation_platform:11.30 \
/opt/fvp/Foundation_Platformpkg/models/Linux64_GCC-9.3/Foundation_Platform \
--image /opt/fvp/Foundation_Platformpkg/examples/hello.axf
Networking inside models¶
Optionally, if you require networking in the model, here is a way to enable this.
-
Create the
networkfile with the following contents:#!/bin/bash # If the change occurs to the "default" libvirt managed network if [ "${1}" = "default" ] ; then # If the network is started if [ "${2}" = "started" ] ; then ip tuntap add mode tap tap01 ip link set tap01 promisc on ip link set tap01 up ip link set tap01 master virbr0 fi fi -
Create the
entrypoint.shfile with the following contents:#!/bin/bash set -ex /usr/sbin/libvirtd & sleep 3 exec "$@"Use this
Dockerfileinstead for installing the additional packages and adding the scripts:FROM ubuntu:22.04 # Install packages RUN apt-get update && \ apt-get install --no-install-recommends --yes bc libatomic1 telnet libdbus-1-3 \ libvirt-daemon-system iproute2 dnsmasq&& \ rm -rf /var/cache/apt # Add FVP Binaries RUN mkdir /opt/fvp ADD Foundation_Platform_11.30_27_Linux64.tgz /opt/fvp COPY network /etc/libvirt/hooks/network RUN chmod +x /etc/libvirt/hooks/network COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] -
Configure your FVP device type to run with privileged mode for creating the interfaces.
{% extends "fvp.jinja2" %} {% set fvp_docker_privileged = True %}Warning
Access to devices running with privileged mode should be strictly controlled via user groups to mitigate security concerns.
-
In your LAVA job, add the following arguments to your foundation model (other models will differ):
arguments: - ... - "--network=bridged" - "--network-bridge=tap01"
This will be required if you require the use of transfer_overlay. This could
be useful in the event you want to pass binaries to the model that contains the
filesystem but stored in a way LAVA cannot currently put the overlay into.
transfer_overlay:
# It may be required to suppress some kernel messages
download_command: echo 3 > /proc/sys/kernel/printk ; wget
unpack_command: tar -C / -xzf
Sample job definition¶
device_type: fvp
job_name: sample fvp job
timeouts:
job:
minutes: 60
priority: medium
visibility: public
actions:
- deploy:
to: fvp
timeout:
minutes: 15
uniquify: false
images:
startup:
# Create the 'startup.nsh' file with the below content, host it locally
# or on a http file server, and then update the url below.
# Image dtb=fvp-base-revc.dtb systemd.log_level=warning console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda ip=dhcp
url: file:///<path>/startup.nsh
uefi:
url: https://storage.tuxboot.com/buildroot/fvp-aemva/FVP_AARCH64_EFI.fd
bl1:
url: https://storage.tuxboot.com/buildroot/fvp-aemva/bl1.bin
fip:
url: https://storage.tuxboot.com/buildroot/fvp-aemva/fip.bin
dtb:
url: https://storage.tuxboot.com/buildroot/fvp-aemva/fvp-base-revc.dtb
kernel:
url: https://storage.tuxboot.com/buildroot/fvp-aemva/Image
rootfs:
url:
https://storage.tuxboot.com/buildroot/fvp-aemva/rootfs.ext4.zst
compression: zstd
format: ext4
overlays:
lava: true
- boot:
method: fvp
docker:
name: docker.io/shrinkwraptool/base-slim:2025.12.0
local: true
image: /tools/Base_RevC_AEMvA_pkg/models/Linux64_GCC-9.3/FVP_Base_RevC-2xAEMvA
version_string: Fast Models [^\n]+
timeout:
minutes: 10
console_string: 'terminal_0: Listening for serial connection on port (?P<PORT>\d+)'
feedbacks:
- '(?P<NAME>terminal_1): Listening for serial connection on port (?P<PORT>\d+)'
- '(?P<NAME>terminal_2): Listening for serial connection on port (?P<PORT>\d+)'
- '(?P<NAME>terminal_3): Listening for serial connection on port (?P<PORT>\d+)'
arguments:
- --stat
- -C bp.dram_size=4
- -C bp.flashloader0.fname='{FIP}'
- -C bp.flashloader1.fname='{UEFI}'
- -C bp.hostbridge.userNetPorts=8022=22
- -C bp.hostbridge.userNetworking=1
- -C bp.refcounter.non_arch_start_at_default=1
- -C bp.secure_memory=1
- -C bp.secureflashloader.fname='{BL1}'
- -C bp.smsc_91c111.enabled=1
- -C bp.terminal_0.mode=telnet
- -C bp.terminal_0.start_telnet=0
- -C bp.terminal_1.mode=raw
- -C bp.terminal_1.start_telnet=0
- -C bp.terminal_2.mode=raw
- -C bp.terminal_2.start_telnet=0
- -C bp.terminal_3.mode=raw
- -C bp.terminal_3.start_telnet=0
- -C bp.ve_sysregs.exit_on_shutdown=1
- -C bp.virtio_rng.enabled=1
- -C bp.virtioblockdevice.image_path='{ROOTFS}'
- -C bp.virtiop9device.root_path=
- -C bp.vis.disable_visualisation=1
- -C cache_state_modelled=0
- -C cluster0.NUM_CORES=4
- -C cluster0.PA_SIZE=48
- -C cluster0.check_memory_attributes=0
- -C cluster0.clear_reg_top_eret=2
- -C cluster0.cpu0.semihosting-cwd={ARTIFACT_DIR}
- -C cluster0.ecv_support_level=2
- -C cluster0.enhanced_pac2_level=3
- -C cluster0.gicv3.cpuintf-mmap-access-level=2
- -C cluster0.gicv3.without-DS-support=1
- -C cluster0.gicv4.mask-virtual-interrupt=1
- -C cluster0.has_16k_granule=1
- -C cluster0.has_amu=1
- -C cluster0.has_arm_v8-1=1
- -C cluster0.has_arm_v8-2=1
- -C cluster0.has_arm_v8-3=1
- -C cluster0.has_arm_v8-4=1
- -C cluster0.has_arm_v8-5=1
- -C cluster0.has_arm_v8-6=1
- -C cluster0.has_arm_v8-7=1
- -C cluster0.has_arm_v8-8=1
- -C cluster0.has_arm_v8-9=1
- -C cluster0.has_arm_v9-0=1
- -C cluster0.has_arm_v9-1=1
- -C cluster0.has_arm_v9-2=1
- -C cluster0.has_arm_v9-3=1
- -C cluster0.has_arm_v9-4=1
- -C cluster0.has_arm_v9-5=1
- -C cluster0.has_branch_target_exception=1
- -C cluster0.has_brbe=1
- -C cluster0.has_brbe_v1p1=1
- -C cluster0.has_const_pac=1
- -C cluster0.has_gcs=1
- -C cluster0.has_hpmn0=1
- -C cluster0.has_large_system_ext=1
- -C cluster0.has_large_va=1
- -C cluster0.has_permission_indirection_s1=1
- -C cluster0.has_permission_indirection_s2=1
- -C cluster0.has_permission_overlay_s1=1
- -C cluster0.has_permission_overlay_s2=1
- -C cluster0.has_rndr=1
- -C cluster0.has_sve=1
- -C cluster0.max_32bit_el=0
- -C cluster0.pmb_idr_external_abort=1
- -C cluster0.stage12_tlb_size=1024
- -C cluster0.sve.has_sme2=1
- -C cluster0.sve.has_sme=1
- -C cluster0.sve.has_sve2=1
- -C cluster1.NUM_CORES=4
- -C cluster1.PA_SIZE=48
- -C cluster1.check_memory_attributes=0
- -C cluster1.clear_reg_top_eret=2
- -C cluster1.ecv_support_level=2
- -C cluster1.enhanced_pac2_level=3
- -C cluster1.gicv3.cpuintf-mmap-access-level=2
- -C cluster1.gicv3.without-DS-support=1
- -C cluster1.gicv4.mask-virtual-interrupt=1
- -C cluster1.has_16k_granule=1
- -C cluster1.has_amu=1
- -C cluster1.has_arm_v8-1=1
- -C cluster1.has_arm_v8-2=1
- -C cluster1.has_arm_v8-3=1
- -C cluster1.has_arm_v8-4=1
- -C cluster1.has_arm_v8-5=1
- -C cluster1.has_arm_v8-6=1
- -C cluster1.has_arm_v8-7=1
- -C cluster1.has_arm_v8-8=1
- -C cluster1.has_arm_v8-9=1
- -C cluster1.has_arm_v9-0=1
- -C cluster1.has_arm_v9-1=1
- -C cluster1.has_arm_v9-2=1
- -C cluster1.has_arm_v9-3=1
- -C cluster1.has_arm_v9-4=1
- -C cluster1.has_arm_v9-5=1
- -C cluster1.has_branch_target_exception=1
- -C cluster1.has_brbe=1
- -C cluster1.has_brbe_v1p1=1
- -C cluster1.has_const_pac=1
- -C cluster1.has_gcs=1
- -C cluster1.has_hpmn0=1
- -C cluster1.has_large_system_ext=1
- -C cluster1.has_large_va=1
- -C cluster1.has_permission_indirection_s1=1
- -C cluster1.has_permission_indirection_s2=1
- -C cluster1.has_permission_overlay_s1=1
- -C cluster1.has_permission_overlay_s2=1
- -C cluster1.has_rndr=1
- -C cluster1.has_sve=1
- -C cluster1.max_32bit_el=0
- -C cluster1.pmb_idr_external_abort=1
- -C cluster1.stage12_tlb_size=1024
- -C cluster1.sve.has_sme2=1
- -C cluster1.sve.has_sme=1
- -C cluster1.sve.has_sve2=1
- -C gic_distributor.has_nmi=1
- -C pci.pci_smmuv3.mmu.SMMU_AIDR=2
- -C pci.pci_smmuv3.mmu.SMMU_IDR0=135263935
- -C pci.pci_smmuv3.mmu.SMMU_IDR1=216481056
- -C pci.pci_smmuv3.mmu.SMMU_IDR3=5908
- -C pci.pci_smmuv3.mmu.SMMU_IDR5=4294902901
- -C pci.pci_smmuv3.mmu.SMMU_S_IDR1=2684354562
- -C pci.pci_smmuv3.mmu.SMMU_S_IDR2=0
- -C pci.pci_smmuv3.mmu.SMMU_S_IDR3=0
- -C pctl.startup=0.0.0.0
auto_login:
login_prompt: 'login:'
username: root
prompts:
- root@(.*):[/~]#
- test:
timeout:
minutes: 10
definitions:
- from: inline
repository:
metadata:
format: Lava-Test Test Definition 1.0
name: health checks
run:
steps:
- lava-test-case kernel-info --shell uname -a
- lava-test-case network-info --shell ip a
name: health-checks
path: inline/health-checks.yaml