Skip to content

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

  1. Add the device using the following settings:
    • Device Type: fvp
    • Hostname: A unique name (e.g., fvp-01)
  2. Add the device configuration.

    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 network file 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.sh file with the following contents:

    #!/bin/bash
    
    set -ex
    
    /usr/sbin/libvirtd &
    sleep 3
    
    exec "$@"
    

    Use this Dockerfile instead 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