Skip to content

NVIDIA: add Arm CCA Realm and RME device-assignment support - #40

Open
ianm-nv wants to merge 40 commits into
NVIDIA:nvidia_unstable-11.0from
ianm-nv:nvidia_unstable-11.0+cca/latest
Open

NVIDIA: add Arm CCA Realm and RME device-assignment support#40
ianm-nv wants to merge 40 commits into
NVIDIA:nvidia_unstable-11.0from
ianm-nv:nvidia_unstable-11.0+cca/latest

Conversation

@ianm-nv

@ianm-nv ianm-nv commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

This series adds Arm Confidential Compute Architecture (CCA) Realm support
to the NVIDIA QEMU 11.0 branch. It imports Linaro's QEMU CCA RFC v4, adapts
it to the QEMU 11.0 and NVIDIA kernel interfaces, adds Realm pause/resume,
and integrates Arm RME device assignment (RME-DA).

The NVIDIA follow-up commits validate Realm creation and image loading,
constrain shared-memory DMA, check RME-DA requests, and correct machine
configuration, object teardown, and DMA replay ordering. They also support
NVIDIA kernels whose live KVM_CAP_ARM_RMI number differs from the QEMU
build-time headers.

The series adds the downstream query-cca-capabilities QMP interface used
by NVIDIA libvirt to determine whether host KVM supports Realms.

Series layout

The branch contains 40 linear commits on top of
nvidia_unstable-11.0 (45aa1ef85148):

  1. 24 Linaro QEMU CCA RFC v4 commits.
  2. 2 Realm pause/resume commits by James Hunt.
  3. 8 NVIDIA QEMU 11.0 integration and CPU-Realm correction commits.
  4. 4 RME-DA commits by Arto Merilainen, with NVIDIA-maintained fixes folded
    into their final implementations.
  5. 1 commit serializing guest-memory attribute changes and notifier replay.
  6. 1 Realm DMA synchronization, mapping, and lifetime correction.

Provenance

The imported commits record their corresponding Linaro source hashes.
Commits requiring changes for QEMU 11.0 are identified as backports and
describe the relevant adaptation.

Main NVIDIA adaptations and fixes

  • Discover the live KVM_CAP_ARM_RMI value through the NVIDIA KVM module
    parameter, validate it, cache it, and fall back to the build-time value
    when the parameter is unavailable.
  • Align the Arm VM type encoding with the companion NVIDIA kernel and use
    VM-scoped KVM capability checks.
  • Correct Realm PMU/debug-resource setup and register synchronization.
  • Validate firmware, DTB, and boot-image loading. Snapshot loader data while
    valid, merge page-overlapping images, and bound Realm population to valid
    RAM ranges.
  • Provide a stable Realm IPA layout and route only valid shared-memory DMA
    aliases through the Realm DMA address space.
  • Add assigned-memory attribute handling and IOMMUFD VDEVICE support for
    RME-DA.
  • Treat Realm Host Interface (RHI) and Trusted I/O (TIO) data as untrusted:
    validate lengths, offsets, mappings, residues, routing IDs, and
    device-memory ranges; serialize device access; and fail closed on rollback
    or registration errors.
  • Reject unsupported Realm machine options instead of silently accepting
    them.
  • Make Realm notifier/listener teardown idempotent, prevent deletion of an
    active RME object, and break the RmeGuest/AddressSpace reference cycle.
  • Serialize guestmemfd attribute changes, listener notifications, and replay
    callbacks so DMA mappings cannot become stale during a concurrent memory
    conversion.
  • Expose query-cca-capabilities using the response schema consumed by
    NVIDIA libvirt commit 00fe79fd234291db242a4c72c2d3373f8f3dcdb2
    on nvidia_unstable-11.9.

Jean-Philippe Brucker and others added 30 commits September 9, 2026 22:45
This is a temporary patch intended for testing purposes only. It provides
definitions related to supporting Realms by the QEMU-VMM.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(backported from commit 46fba3f60f1d5e5ddea835bd5cfb6dced1ddcf47 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
…h_init()

Returning an error to kvm_init() is fatal anyway, no need to continue
the initialization.

Leave the `ret` variable in the function scope because it will be reused
when adding RME support.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit c9e0538a3e56bddc6ccbef2595a90c0f01948359 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Add a new RmeGuest object, inheriting from ConfidentialGuestSupport, to
support the Arm Realm Management Extension (RME). It is instantiated by
passing on the command-line:

  -M virt,confidential-guest-support=<id>
  -object rme-guest,id=<id>

This is only the skeleton. Support will be added in following patches.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(backported from commit 84460f046c56f50595e59870ae590d2dc7523e3d https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
[ianm: omitted the ConfidentialGuestSupportProperties QAPI base, which is
not available in QEMU 11.0]
Signed-off-by: Ian May <ianm@nvidia.com>
Initialise an @rme_guest object and install a handler to get notified
when the Realm is ready to start.  That way we can finish reading the
Realm once we know everything is in place.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(backported from commit 1d293ef90047ad3c5de4784b90537f5f7d701851 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
[ianm: omitted allow_convert_in_place, which is not available in QEMU 11.0]
Signed-off-by: Ian May <ianm@nvidia.com>
For a Realm, KVM is much more restrictive on the registers a VMM can
initialize.  To prepare for Realm VM initialization, split the get/put
register functions for a non-secure VM in their own function.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 065b0e57d6a574b852edd7564b5cfdc1b46d606c https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
The target code calls kvm_arm_vcpu_init() to mark the vCPU as part of a
Realm. For a Realm vCPU, only x0-x7 can be set at runtime. Before boot,
the PC can also be set, and is ignored at runtime. KVM also accepts a
few system register changes during initial configuration, as returned by
KVM_GET_REG_LIST.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 7b99148ebe2fc60711b059c99e1ca02fa4b587fa https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
When a Realm is requested, create a scratch Realm VM by setting bit 8
of the argument given to the KVM_CREATE_VM ioctl().  Bit 7-0 are reserved
for the guest PA size.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 2ce3ab9f6a3bfbac2fca359cbc224fb788d5b25e https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
…essary

The Arm KVM code can return different values from KVM_CHECK_EXTENSION
depending on the VM type. Use kvm_vm_check_extension() where necessary
to ensure we get the right response from KVM.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 0edc61af82210beacbf7ff22ee1c80e27b40d7ec https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Add a function to register a notifier that is invoked when ROMs get
loaded into guest memory.

It will be used by Arm confidential guest support, in order to register
all blobs loaded into memory with KVM, so that their content is moved
into Realm state and measured into the initial VM state.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 5a1c5c57b07cf9501fa548c78454214bb68221da https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
…lm memory

Add a Rom notifier to keep track of binary blobs loaded in Realm memory.
That way we can deterministically calculate the Realm Initial Measurement (RIM).

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 0288a9c77fcb2db3c5ecab25558d63edd1f5657b https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Once the Realm descriptor has been created, tell KVM to transfer runtime
images (kernel, DT, and rootfs) from guest memory to Realm memory.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 12afb2f135c59600cb84dc14c4375ea1204acb2b https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
…nts in KVM

Add "num-breakpoints" and "num-watchpoints" CPU parameters to configure
the debug features that KVM presents to the guest. The KVM vCPU
configuration is modified by calling SET_ONE_REG on the ID register.

This is needed for Realm VMs, whose parameters include breakpoints and
watchpoints, and influence the Realm Initial Measurement.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 6fd014297760be023d8a7060ecab8d0e51c51a2c https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Add a "num-pmu-counters" CPU parameter to configure the number of
counters that KVM presents to the guest. This is needed for Realm VMs,
whose parameters include the number of PMU counters and influence the
Realm Initial Measurement.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit cfc3a5faad3c75f71d9b6ed4b489c31400ccf18a https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
The host cannot access registers of a Realm. Instead of showing all
registers as zero in "info registers", display a message about this
restriction.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 7568b34e186c5fa9acf17a093143d630cb47ae73 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
The HVC conduit for PSCI is not supported for Realms, so default
to SMC.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 1ff3e53b25a2fdde95a042b4f52ebff2b5cebbdd https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Set the Ream VM bit in the IPA address space size when needed.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit beea72e5f3c30ac36cbe23e8ee758cd57bf4a5b4 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
… for RME

When RME is enabled, the upper GPA bit is used to distinguish protected
from unprotected addresses. Reserve it when setting up the guest memory
map.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 06b813b51fcbfdcdc5cea8938d3162ced0503238 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
The dtb-randomness feature, which adds random seeds to the DTB, isn't
really compatible with confidential VMs since it randomizes the Realm
Initial Measurement. Enabling it is not an error, but it prevents
attestation. It also isn't useful to a Realm, which doesn't trust host
input.

Currently the feature is automatically enabled, unless the user disables
it on the command-line. Change it to OnOffAuto, and automatically
disable it for confidential VMs, unless the user explicitly enables it.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit efde425360513e48627474e34942961d60d0407a https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
For confidential VMs we'll want to skip flash device creation.
Unfortunately, in virt_instance_init() the machine->cgs member has not
yet been initialized, so we cannot check whether confidential guest is
enabled. Move virt_flash_create() to machvirt_init(), where we can
access the machine->cgs member.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 2da577b703c893c2017b7594946a334cfbf0fa88 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
… guest firmware

Use RAM instead of flash to store firmware image and boot information,
including UEFI variables.  That way the firmware can be measured and
included in the Realm's initial measurements.  It also prevents
variable stored in flash and modified as part of the boot process to
be leaked outside the Realm.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 07c798336af663bff4672e187a0e17e9ba101398 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
…emory region

In Arm CCA, the guest-physical address space is split in half. The top
half represents memory shared between guest and host, and the bottom
half is private to the guest. From QEMU's point of view, the two halves
are merged into a single region, and pages within this region are either
shared or private.

Virtual devices implemented by the host are only allowed to access the
top half. For emulated MMIO, KVM strips the GPA before returning to
QEMU, so the GPA already belongs to QEMU's merged view of guest memory.
However DMA addresses cannot be stripped this way and need special
handling by the VMM.

When emulating DMA the VMM needs to translate the addresses into its
merged view. Add an IOMMU memory region on the top half, that
retargets DMA accesses to the merged sysmem.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit cb1e21fdd28b25fda8ead6503ece8289e43fb524 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Some distributions provide packages continaing firmware to be run under
QEMU, such as "qemu-efi-aarch64" or "edk2-aarch64". Those packages also
contain descriptors in /usr/share/qemu/firmware/*.json listing the
firmware features, so that environments like libvirt can figure out
which firmware they can load.

Define an optional feature for arm64 firmware to indicate that a
firmware supports running in a Realm. Firmware implementations need
extra support for running in a Realm, in particular to distinguish
shared from private guest memory.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Acked-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 627a544c5f7f85d4b5effdcebc09bd96e434fdb6 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Load DTB without modifications when provided.  That way it is possible
for a verification entity to reconstruct the Realm's initial measurement.

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit 2bb4ca207d08ad80aaac8bc0c4cbe1e1b845b4d9 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
An independent verifier needs to reconstruct the content of guest memory
in order to attest that it is running trusted code. To avoid having to
reconstruct the bootloader generated by QEMU, skip this step and jump
directly to the kernel, with the DTB address in x0 as specified by the
Linux boot protocol [1].

[1] https://docs.kernel.org/arch/arm64/booting.html

Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
(cherry picked from commit d69e5bf476210877995f17f7d83a095d206234f0 https://gitlab.com/Linaro/cca-public/qemu/-/tree/upstream-v4)
Signed-off-by: Ian May <ianm@nvidia.com>
Add a convenience function that pairs with
kvm_mark_guest_state_protected().

Signed-off-by: James O. D. Hunt <jameshunt@nvidia.com>
Signed-off-by: Ian May <ianm@nvidia.com>
Track successful Realm population and activation so later VM state
changes do not re-enter the one-time setup path. Once KVM guest state
is protected, also skip the normal Arm virtual-time synchronization
that Realm KVM does not support.

This preserves QMP stop/cont operation for Realm guests.

Signed-off-by: James O. D. Hunt <jameshunt@nvidia.com>
Signed-off-by: Ian May <ianm@nvidia.com>
The RFC series defines KVM_VM_TYPE_ARM_REALM as a standalone flag bit
(1UL << 30). Our kernel instead carves a 4-bit VM-type field out of
bits[11:8] and defines REALM as KVM_VM_TYPE_ARM(1) == 0x100.

With the upstream encoding, KVM_CREATE_VM passes bit 30, which our
kernel reads as VM type 0 (normal) with a stray reserved bit, so the
realm is silently created as a normal VM and every subsequent RMI call
fails.

Realign the header with include/uapi/linux/kvm.h. Preserve the kernel's
KVM_VM_TYPE_ARM_MASK name for the VM-type field; the IPA-size and protected
VM fields remain separate. Only REALM and NORMAL are referenced by QEMU, so
no code change is needed.

Signed-off-by: Ian May <ianm@nvidia.com>
Bring the RFC KVM setup in line with deployed Realm kernels and QEMU reset
behavior.

Discover the live KVM_ARM_RMI capability when the NVIDIA kernel exports
it, validate the module parameter, and report fallback to the compile-time
value.
Preserve the Arm VM type field and use VM-scoped capability probes. Add
targeted diagnostics when KVM rejects main or scratch Realm VM creation with
an IPA size that may exceed the RMM S2SZ limit.

Avoid normal-VM register synchronization that Realms do not support.
Configure the measured PMU counter count through the PMU device attribute,
including zero counters, and skip virtual-time adjustment after activation.

Initialize the PMU override for every CPU model and apply debug and PMU
overrides only on the first vCPU reset because KVM rejects them after the
VM has run or the PMU has been created. Reject later property writes rather
than reporting a value that the one-shot KVM configuration cannot apply.

Signed-off-by: Ian May <ianm@nvidia.com>
Make confidential boot use the resolved firmware filename and fail cleanly
when the requested image is missing. Bound DTB copies to their allocated
loader region so an oversized tree cannot overwrite adjacent guest memory.

Keep the dtb-randomness QOM property compatible with ordinary virt machines
while suppressing effective randomness for confidential guests, whose DTB
is part of the measured initial state.

Signed-off-by: Ian May <ianm@nvidia.com>
Use the selected confidential-guest object when deriving the Realm VM type.
Define the VM-type values in QEMU's Arm KVM interface so the machine model
does not depend on a host Linux header when built for TCG or HVF. Verify the
local Realm value against the imported KVM UAPI in KVM-specific code so any
encoding drift fails the build. Preserve requested dtb-randomness
independently of its effective measured-boot behavior.

Create pflash devices early enough to retain the pflash0 and pflash1 machine
properties, but leave them unmapped for Realms. Reject non-KVM launches and
pflash-backed Realm firmware before guest initialization. Cover the pflash
property ABI with qtests.

Signed-off-by: Ian May <ianm@nvidia.com>
ianm-nv and others added 10 commits September 9, 2026 22:45
Bind Realm state to the confidential-guest object selected by the machine.
Remove its notifiers when the object is finalized. Install the Realm DMA
address space before PCI and platform devices obtain DMA views, and route
virtio-mmio through it.

Mirror guestmemfd shared-page transitions into the Realm IOMMU so DMA is
exposed only for shared memory. Accept both the shared-bit spelling and the
canonical firmware spelling for pages already shared with the RMM.

Signed-off-by: Ian May <ianm@nvidia.com>
memory_region_find() truncates a section at a MemoryRegion boundary.
Continuing with the original size can update KVM attributes across the
full range and then fail to update the first RAMBlock attribute bitmap,
leaving the two views inconsistent.

Reject requests that do not resolve to one complete section before
changing any KVM state. This is a generic guestmemfd conversion check
and applies equally to RME, TDX, and SEV-SNP.

Signed-off-by: Ian May <ianm@nvidia.com>
RFC v3 keeps pointers supplied by the ROM loader until Realm activation.
ROM-only data is freed before the notification, and even persistent data has
no lifetime guarantee across that interval.

Snapshot each complete image while the notification data is valid. Include
zero-filled tails, align and merge page-overlapping images, and place
byte-granular contents at the correct offset in page-aligned buffers.
Replace an earlier snapshot when a reset reports the same image again before
activation. Avoid constructing the zero-filled notification copy when no
listener is present. Stop accepting loader notifications after activation
and unregister the notifier at that boundary.

Convert populated ranges through kvm_convert_memory() so KVM attributes,
QEMU's RamDiscardManager bitmap, DMA notifications, and the discarded shared
mapping remain synchronized. The population source is the private snapshot,
so discarding the guest's shared alias cannot destroy the input data.

Validate range arithmetic and buffer alignment, detect KVM ioctls that make
no progress, and terminate before vCPUs run if population fails.

Signed-off-by: Ian May <ianm@nvidia.com>
Libvirt detects the rme-guest object and then uses
query-cca-capabilities to determine whether host KVM supports Realms.
Without that command, libvirt clears its CCA capability and rejects an
otherwise usable QEMU binary.

Implement query-cca-capabilities using the same runtime
KVM_CAP_ARM_RMI lookup and VM-scoped capability probe as Realm creation.

The response schema matches NVIDIA libvirt commit
00fe79fd234291db242a4c72c2d3373f8f3dcdb2 on nvidia_unstable-11.9.
That code consumes return.sections[].measurement-algo.

Signed-off-by: Ian May <ianm@nvidia.com>
Import the kernel definitions required for Arm RME device assignment
(RME-DA):

  - linux/iommufd.h: VIOMMU/VDEVICE allocation and VDEVICE TSM
    operations.
  - linux/kvm.h: KVM_EXIT_ARM64_TIO and its kvm_run CCA payload.
  - asm-arm64/kvm.h: the RMI_EXIT_VDEV_MAP TIO reason.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Ian May <ianm@nvidia.com>
kvm_convert_memory() can run from multiple vCPU threads without the
BQL. Concurrent conversions can race while updating the
RamBlockAttributes bitmap, and listener registration or replay can
observe an inconsistent transition.

Add a mutex covering bitmap access, the listener list, notifications,
and replay callbacks. Keeping callbacks inside the critical section
orders their side effects with the state they report.

This is common guestmemfd synchronization. Listener callbacks,
including VFIO DMA map and unmap ioctls, run while the mutex is held;
conversions are deliberately serialized to preserve ordering.

Signed-off-by: Ian May <ianm@nvidia.com>
A Realm marks device MMIO ranges private when a device is bound to it
and shared again when the mapping is released. Honour those requests on
the KVM memory-attribute path.

Allow RME to convert ram_device regions such as VFIO BARs to shared,
clearing PRIVATE so the kernel tears down stale assigned-device stage-2
mappings. Clear VDEV-installed PRIVATE attributes when a device memory
slot is removed, and fail closed when that rollback cannot be completed.

Advertise assigned-device memory through ConfidentialGuestSupport so
the RME-specific paths can be selected. Add the corresponding non-KVM
stubs.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Ian May <ianm@nvidia.com>
Add the IOMMUFD plumbing for assigning a VFIO PCI device to an Arm CCA
Realm.

Keep the RHI function IDs, status values, and request structures consumed
by QEMU in include/hw/arm/rme-da.h. These definitions are not generated
Linux UAPI and would otherwise be lost when linux-headers is refreshed.
Assert the request structure sizes so ABI layout changes fail the build.

Build a stage-2 nesting-parent HWPT, VIOMMU, and stage-1 bypass HWPT
without changing the legacy single-HWPT path. Keep Realm devices out of
ordinary reusable HWPTs and fully unwind failed topology construction.
Retry interrupted detach operations and terminate if an attached parent
HWPT cannot be detached safely during rollback.

Register an opted-in PCI device with an IOMMUFD VDEVICE after its virtual
bus number is assigned. Validate the backend, device type, parent HWPT,
VIOMMU, nested HWPT, and machine device-assignment support. Track the RID
used for registration and reject accesses after bridge renumbering changes
the device identity. Propagate synchronous failures and stop the VM on
deferred failures. Track run state, clear the failure latch when the VM
stops, and retry registration on resume. Reject unsupported hotplug and
compile the path only when IOMMUFD is linked.

Implement bind, unbind, guest request, measurement, TDI state, and VDEV_MAP
operations. Validate request residues and every page of device-memory
ranges before changing KVM attributes, and require host-page alignment.
If the host rejects a request after PRIVATE attributes are installed, stop
the VM rather than guessing at the previous state. Provide stubs for builds
without IOMMUFD.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Ian May <ianm@nvidia.com>
Forward the RHI device-assignment hypercalls in the SMCCC Standard
Hypervisor range to userspace through a KVM SMCCC filter. Dispatch object,
interface-report, measurement, and TDI-state requests to the IOMMUFD TSM
helpers, and handle KVM_EXIT_ARM64_TIO for VDEV_MAP validation.

Treat the exit data as an untrusted guest interface. Validate routing IDs,
object lengths and offsets, response residues, memory-map ranges, and
complete guest buffer mappings. Initialize all request and response state,
reject unknown exits, preserve KVM register errors, and serialize VFIO
lookup and TSM operations with the BQL.

Fetch cached objects at offset zero into bounded QEMU-owned buffers before
copying the requested slice to Realm memory. This avoids exposing the target
kernel's unchecked source-pointer offset to guest input. Reject out-of-range
offsets with the corresponding RHI status.

Allow CPU-only Realms when the KVM SMCCC filter is unavailable, but disable
the assigned-device-memory capability. An opted-in VFIO device then fails
during realize instead of starting with unusable RHI forwarding.

Confine GP-register synchronization to forwarded hypercalls and release all
guest mappings after use.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Ian May <ianm@nvidia.com>
Use the maximum supported Realm IPA width so the shared bit remains stable
when the PCI layout changes. Keep the Realm DMA AddressSpace alive while the
machine uses it. Release it from the RmeGuest unparent hook to break its
reference cycle through the root MemoryRegion owner. Reject deletion of an
active Realm object and make partial-initialization cleanup idempotent.

Serialize the RAM-discard listener cache so translations cannot race
listener teardown. Emit replay MAP notifications from the protected
RamBlockAttributes callback so bitmap changes and MAP or UNMAP notifications
remain ordered. Document the required nesting between ram_discard_lock and
the RamBlockAttributes lock.

Limit notifier mappings to the shared-bit DMA alias. Accept canonical and
shared-bit RAM addresses only while the RAM discard manager records the page
as shared. For addresses outside tracked RAM, classify the target through
address_space_memory: permit non-RAM MMIO such as GIC ITS MSI writes, while
untracked RAM, including VFIO BARs, continues to fail closed.

Track and remove the Realm migration blocker along with the ROM notifier,
VM-state handler, memory listener, and DMA AddressSpace during teardown.

Signed-off-by: Ian May <ianm@nvidia.com>
@nirmoy

nirmoy commented Sep 10, 2026

Copy link
Copy Markdown

BaseOS Kernel Review

Note

🔄 Review in progress

Boro is reviewing this pull request. Results will appear here when ready.

🔍 Review artifacts

  • 🟡 Boro review: in progress
Review metadata
  • Reviewed head: 70e7658badd4
  • Overall status: in progress

This comment is maintained by BaseOS Reviewer and updated when the GitHub watcher publishes a newer review.

@nvmochs

nvmochs commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

@ianm-nv I reviewed this PR and confirmed that the issues I had mentioned previously in my cursory review a few weeks ago are now resolved. There are some new findings and a few "informational notes" (findings, but classified a bit differently due to recognition that this is beta code). I'm not sure how actionable these are at this point in time as none of them appear to functionally affect the "good path". Will let you review and then we can discuss how best to proceed.


Findings

1. [P1] Realm DMA conversion can succeed after a VFIO map/unmap failure

When a Realm page changes between private and shared, target/arm/kvm-rme.c:560 publishes an IOMMU MAP or UNMAP event. Through the registered notifier chain, this reaches hw/vfio/listener.c:156, which performs the actual VFIO/IOMMUFD mapping operation.

If that operation fails, VFIO reports the error but cannot return it because the generic IOMMU notifier interface returns void. Control eventually returns to target/arm/kvm-rme.c:598, which unconditionally returns zero. As a result,
kvm_convert_memory() finishes as though the device mapping update succeeded.

For a private-to-shared transition, the CPU-visible page can become shared while the assigned device has no corresponding DMA mapping. For a shared-to-private transition, an old device mapping can remain after QEMU considers device access revoked. This breaks the invariant that KVM attributes, QEMU’s RAM-discard bitmap, and IOMMUFD mappings describe the same sharing state.

Replay of existing shared pages uses the same notification mechanism, so a failed replay MAP can also be logged without preventing VFIO device setup from continuing.

2. [P2] Realm boot images cannot cross RAM MemoryRegion boundaries

QEMU can construct contiguous guest RAM from multiple NUMA memory backends:

Guest-visible RAM

  +-----------------------+-----------------------+
  | NUMA memdev 0         | NUMA memdev 1         |
  | MemoryRegion A        | MemoryRegion B        |
  +-----------------------+-----------------------+
                          ^
                    backend boundary

There is no address-space hole at this boundary, so a kernel, initrd, or ELF segment spanning it is still entirely within valid guest RAM. QEMU creates these adjacent backend regions in hw/core/numa.c:627, and Realm machine validation does not reject such a configuration.

The Realm loader records and page-aligns complete boot-image ranges. It can also coalesce overlapping or adjacent loader entries without considering the underlying RAM backend boundaries:

target/arm/kvm-rme.c:200

At VM activation, target/arm/kvm-rme.c:130 passes each complete range to kvm_convert_memory(). That function uses memory_region_find(), which returns only the portion contained in the first leaf MemoryRegion, and then explicitly rejects the range when the returned section is smaller than requested:

accel/kvm/kvm-all.c:3393

The resulting error propagates to the Realm run-state handler, which terminates QEMU during startup. The failure depends on image placement: the same image and memory backends can work or fail depending on whether an image happens to straddle one of the otherwise transparent NUMA boundaries.

3. [P2] Large zero-filled ELF segments create two full-size staging buffers

For an ELF PT_LOAD segment, QEMU distinguishes:

p_filesz: bytes physically present in the ELF file
p_memsz: complete segment size after loading

When p_memsz > p_filesz, the remainder is normally an implicit zero-filled BSS tail. rom_add_elf_program() deliberately retains only the file-backed portion and records the larger in-memory size, avoiding a separate p_memsz allocation:

hw/core/loader.c:1211

The Realm loader notification path materializes that sparse representation into a contiguous buffer. hw/core/loader.c:1300 first allocates and zeroes p_memsz bytes, then copies the p_filesz data into it. While that temporary buffer remains
live, target/arm/kvm-rme.c:324 allocates another p_memsz buffer and copies the complete temporary image into its retained Realm snapshot.

For a segment with 16 MiB of file data and a 1 GiB in-memory size, the notifier path temporarily holds two additional 1 GiB buffers. This is in addition to guest RAM and the mapped ELF file. Under a process or cgroup memory limit, an
otherwise valid ELF can therefore fail during Realm startup.

This applies only to direct ELF loading with a substantial p_memsz - p_filesz difference. Raw Arm Linux Image files and ELF segments without a meaningful zero-filled tail do not experience the duplicate full-size allocation.

Informational notes

4. The beta implementation does not provide RHI_DA_VERSION

The RHI-DA SMCCC filter starts at RHI_DA_FEATURES (0x4b), leaving RHI_DA_VERSION (0x4a) outside the range forwarded to QEMU:

target/arm/kvm.c:610

The RHI dispatcher likewise handles FEATURES, OBJECT_SIZE, OBJECT_READ, and the VDEV operations, but has no VERSION case:

target/arm/kvm.c:2139

Published DEN0148 beta1 describes RHI_DA_VERSION as the protocol-discovery operation. A guest following that sequence would receive SMCCC_RET_NOT_SUPPORTED and conclude that RHI-DA is unavailable.

The RFC v4 Linux guest used with this beta stack does not follow that sequence. It calls RHI_DA_FEATURES directly, which is forwarded and handled successfully. Consequently, the missing version call does not create a current functional problem and is noted only as relevant to future ABI alignment. See the Arm DEN0148 RHI specification (https://documentation-service.arm.com/static/6980b4be9135fd4e42bbf2ed?token=).

5. RHI_DA_OBJECT_READ uses source-offset semantics

The object-read handler maps the guest’s AArch64 argument registers as follows:

x2 / call->in[2] = object type
x3 / call->in[3] = guest buffer IPA
x4 / call->in[4] = maximum length
x5 / call->in[5] = offset

See target/arm/kvm.c:1987.

QEMU interprets x5 as an offset into the source object:

copy_len = MIN(max_len, object_size - offset);
memcpy(hva, object_data + offset, copy_len);

Under that interpretation, an offset of 128 means “skip the first 128 bytes of the object and place the remaining data at the beginning of the guest buffer.”

Published DEN0148 beta1 describes the field as an offset within the destination buffer. Under that interpretation, the complete object would be copied to guest_ipa + offset.

The NVIDIA kernel branch inspected implements the source-offset convention, and its current guest callers always set gprs[5] to zero. With a zero offset, the two interpretations produce exactly the same result. The difference is therefore latent and does not affect the currently inspected beta guest.

@NathanChenNVIDIA

Copy link
Copy Markdown
Collaborator

Hi @ianm-nv ,
Commit 701c144's description may need some details about why the 'backported from' tag was used, i.e. backporting the defines for KVM_VM_TYPE_ARM_PROTECTED and KVM_VM_TYPE_ARM_MASK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants