SDV on QNX

We internally test SDV on hardware. We use QVM on QNX for virtualization. We share all targets, documentation, and configurations as samples.

Prerequisites

This page assumes a preconfigured QNX, and doesn't include steps to build, configure, or flash QNX on hardware.

This page assumes that you're running QNX on arm64 hardware, and building and using the _arm64 targets of SDV.

Required components

  • A device tree blob (DTB) to define the virtual hardware
  • A Linux kernel to use
  • A combined ramdisk that includes all ramdisk boot properties
  • A handoff of a DICE chain for SDV VM authentication
  • All disk partitions of the system
  • A QVM configuration of the VM

Device tree blob (DTB)

QVM generates most of the device tree blob (DTB) based on its configuration. Therefore, the DTB can be minimal:

/*
 * Copyright (C) 2026 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/dts-v1/;

/ {
    model = "Virtual Machine";
    #address-cells = <2>;
    #size-cells = <2>;

    // This reserved-memory node specifies for the open-dice driver which memory region(s) to
    // expose as /dev/open-dice<n> device(s)
    reserved-memory {
        // The number of u32 cells to represent the address of a memory region
        #address-cells = <2>;
        // The number of u32 cells to represent the size of a memory region
        #size-cells = <2>;
        ranges;
        // The unit address (after the @) must match the address in the reg property
        dice@D1C30000 {
            // The open-dice driver receives this device tree node based on this
            // property.
            compatible = "google,open-dice";
            no-map;
            // The address and the size of the memory region that is passed to the
            // open-dice driver.
            // First two hex numbers (cells) represent the address of the memory region,
            // the last two represent its size (4K, in this case).
            reg = <0x0 0xD1C30000 0x0 0x1000>;
        };
    };
};

Kernel

SDV uses the Generic Kernel Image (GKI), and you can use the output from a build directly.

Ramdisk

The ramdisk information should include the different ramdisks that Android generates and the boot properties. Because QNX doesn't support a bootloader by default, you must generate a ramdisk with the expected data format.

You can do this with the following Python script, which concatenates multiple ramdisks and adds the bootconfig (which is the vendor-bootconfig.img file) in the expected format:

def create_qnx_ramdisk(output: io.BytesIO,
                       ramdisks: Sequence[io.BytesIO],
                       bootconfig: Optional[io.BytesIO]):
  BUFFER_SIZE = 4096

  for ramdisk in ramdisks:
    while buf := ramdisk.read(BUFFER_SIZE):
      output.write(buf)

  # Kernel looks for bootconfig at the end of ramdisk.

  if bootconfig:
    bootconfig_checksum = 0
    bootconfig_size = 0
    while buf := bootconfig.read(BUFFER_SIZE):
      bootconfig_checksum = (bootconfig_checksum + sum(buf)) & 0xFFFFFFFF
      bootconfig_size += len(buf)
      output.write(buf)

    output.write(struct.pack('II', bootconfig_size, bootconfig_checksum))
    output.write(b'#BOOTCONFIG\n')

DICE chain

In a production system, the host system and bootloader construct the DICE chain dynamically during the boot process and provide a DICE chain to the guest to verify that the image can be trusted. Because this requirement can make testing and development flows complicated, you load a DICE chain snapshot instead.

To generate the dice_handover_instance* files, use any SDV Cuttlefish build. For example:

lunch sdv_core_cf-trunk_staging-userdebug
m
cp $OUT/product/etc/dice_handover_instance* <GUEST>/test_dice_handover

Disk

QVM supports multiple formats for a virtual disk; our reference configuration uses a disk image file that contains all partitions. bpttool can generate those disks, but isn't actively developed within Android.

QVM configuration

The QVM configuration sets the configuration, and points to the different files. We provide the following sample configurations:

Frequently asked questions

Q: adb can't connect to my VM. What can I do?

A: In this setup, adb uses Ethernet to connect to your VM. You need a port forwarding configuration on QNX that forwards requests to guests on port 5555. You need to connect to the host system. As an alternative, you can also configure a bridge to the VM, and connect to it directly.

Q: The system log shows that IServiceRegistrationAgent is missing. Why?

A: Service discovery relies on a fully working trust and binder setup. Verify that OpenDICE was loaded successfully, and HwBinder isn't showing errors during startup. If those aren't working, you need to fix them first.

Q: init_open_dice fails. What happened?

A: The startup scripts load the trust information during startup, and wipe the memory location. Verify in your QVM config that the cmdline configuration and shared data in the memory location fit together. Also verify that the memory location in the driver, QVM config, and DTB match, and the memory location is writable.