Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0ac5408
NVIDIA: SAUCE: linux-headers: Add RME related definitions
Aug 24, 2026
3bb2036
NVIDIA: SAUCE: target/arm/kvm: Return immediately on error in kvm_arc…
Feb 21, 2024
ce04135
NVIDIA: SAUCE: target/arm: Add confidential guest support
Jun 16, 2022
4e0578e
NVIDIA: SAUCE: target/arm/kvm-rme: Add mechanic to initialize realms
mathieupoirier May 14, 2026
2f3d762
NVIDIA: SAUCE: target/arm/kvm: Split kvm_arch_get/put_registers
Jun 21, 2022
e98829e
NVIDIA: SAUCE: target/arm/kvm-rme: Initialize vCPU
Jan 9, 2023
09a6806
NVIDIA: SAUCE: target/arm/kvm: Create scratch Realm VM when requested
Dec 4, 2023
5f36350
NVIDIA: SAUCE: target/arm/kvm: Use kvm_vm_check_extension() where nec…
Dec 4, 2024
25e234b
NVIDIA: SAUCE: hw/core/loader: Add a ROM loader notifier
Jun 13, 2023
c425e54
NVIDIA: SAUCE: target/arm/kvm-rme: Keep track of images loaded in Rea…
Jun 14, 2023
464d18f
NVIDIA: SAUCE: target/arm/kvm-rme: Populate Realm with runtime images
Jun 8, 2026
0fdf517
NVIDIA: SAUCE: target/arm/cpu: Set number of breakpoints and watchpoi…
Feb 19, 2026
f20e586
NVIDIA: SAUCE: target/arm/cpu: Set number of PMU counters in KVM
Feb 19, 2026
5d4d9bc
NVIDIA: SAUCE: target/arm/cpu: Don't read Realm registers
Feb 7, 2023
4ecd8ef
NVIDIA: SAUCE: hw/arm/virt: Set proper conduit method for Realms
Feb 6, 2023
594426e
NVIDIA: SAUCE: hw/arm/virt: Embed Realm VM type with IPA address space
May 20, 2026
71afe1b
NVIDIA: SAUCE: hw/arm/virt: Reserve one bit of guest physical address…
Feb 6, 2023
ab6aed2
NVIDIA: SAUCE: hw/arm/virt: Disable DTB randomness for confidential VMs
Feb 6, 2023
896eae7
NVIDIA: SAUCE: hw/arm/virt: Move virt_flash_create() to machvirt_init()
Aug 12, 2022
ad06de2
NVIDIA: SAUCE: hw/arm/virt: Use RAM instead of flash for confidential…
Aug 12, 2022
7b3a9f3
NVIDIA: SAUCE: target/arm/kvm-rme: Add DMA remapping for the shared m…
Jan 8, 2025
fcceb67
NVIDIA: SAUCE: docs/interop/firmware.json: Add arm-rme firmware feature
Apr 16, 2025
0b8453e
NVIDIA: SAUCE: hw/arm/boot: Load DTB as is for confidential VMs
Feb 21, 2024
7edd474
NVIDIA: SAUCE: hw/arm/boot: Skip bootloader for confidential guests
Apr 26, 2024
c241098
NVIDIA: SAUCE: accel/kvm: Add kvm_guest_state_protected() helper
jodh-nvidia May 1, 2026
de29a7d
NVIDIA: SAUCE: target/arm/kvm-rme: only activate a Realm once
jodh-nvidia May 1, 2026
fb38e94
NVIDIA: SAUCE: linux-headers: encode ARM VM type in bits[11:8]
ianm-nv Aug 6, 2026
a031cec
NVIDIA: SAUCE: arm/kvm: align Realm capability and vCPU setup with KVM
ianm-nv Aug 25, 2026
f517ec4
NVIDIA: SAUCE: hw/arm: validate Realm firmware and DTB loading
ianm-nv Aug 25, 2026
dd8766d
NVIDIA: SAUCE: hw/arm/virt: validate Realm machine configuration
ianm-nv Aug 25, 2026
5379e9f
NVIDIA: SAUCE: arm: fix Realm DMA address-space setup
ianm-nv Aug 25, 2026
25a2a71
NVIDIA: SAUCE: accel/kvm: reject partial memory conversion
ianm-nv Sep 4, 2026
835c4a1
NVIDIA: SAUCE: target/arm/kvm-rme: snapshot Realm images safely
ianm-nv Aug 26, 2026
346dc1d
NVIDIA: SAUCE: qapi: add CCA capability query for libvirt
ianm-nv Sep 2, 2026
c3ae3c8
NVIDIA: SAUCE: linux-headers: add Arm CCA device assignment UAPI
amerilainen Jun 22, 2026
164b38d
NVIDIA: SAUCE: system/ram-block-attributes: serialize updates
ianm-nv Sep 4, 2026
8b1d90c
NVIDIA: SAUCE: accel/kvm: set assigned-memory attributes
amerilainen Sep 4, 2026
c3b9fdc
NVIDIA: SAUCE: vfio/iommufd: support Arm RME device assignment
amerilainen Jun 22, 2026
ef5ab89
NVIDIA: SAUCE: target/arm/kvm: handle RME-DA exits
amerilainen Jun 22, 2026
477cf9e
NVIDIA: SAUCE: arm/kvm-rme: fix Realm DMA lifetime and mappings
ianm-nv Aug 25, 2026
a32539f
NVIDIA: SAUCE: vfio: fail closed on Realm DMA map errors
ianm-nv Sep 10, 2026
ba20cef
NVIDIA: SAUCE: target/arm/kvm-rme: split population at RAM boundaries
ianm-nv Sep 10, 2026
65630bc
NVIDIA: SAUCE: hw/core/loader: preserve sparse ROM notifier data
ianm-nv Sep 10, 2026
b987c80
NVIDIA: SAUCE: target/arm/kvm: align RHI-DA calls with beta1
ianm-nv Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,9 @@ ARM KVM CPUs
M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Supported
F: include/hw/arm/rme-da.h
F: target/arm/kvm.c
F: target/arm/kvm-rme.c
F: tests/functional/aarch64/test_kvm.py

MIPS KVM CPUs
Expand Down Expand Up @@ -1115,6 +1117,7 @@ S: Supported
F: hw/arm/virt*
F: include/hw/arm/virt.h
F: docs/system/arm/virt.rst
F: tests/qtest/arm-virt-machine-test.c
F: tests/functional/aarch64/test_*virt*.py
F: tests/functional/aarch64/test_tuxrun.py
F: tests/functional/arm/test_tuxrun.py
Expand Down
79 changes: 74 additions & 5 deletions accel/kvm/kvm-all.c
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,12 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
ram_start_offset = memory_region_get_ram_addr(mr) + mr_offset;

if (!add) {
bool clear_attrs =
machine_has_assigned_device_memory(current_machine) &&
memory_region_is_ram_device(mr) &&
(kvm_supported_memory_attributes &
KVM_MEMORY_ATTRIBUTE_PRIVATE);

do {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
Expand Down Expand Up @@ -1708,6 +1714,23 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
__func__, strerror(-err));
abort();
}
/*
* Drop any VDEV-installed PRIVATE attributes for this slot. The
* mem_attr_array is per-VM and persists across slot lifecycle,
* so without this clear a re-mapped or recycled gfn range could
* inherit stale VDEV-locked state. The attribute-clear also
* tears down any leftover ASSIGNED-DEV S2 entries via
* kvm_arch_post_set_memory_attributes() / kvm_unmap_gfn_range().
*/
if (clear_attrs) {
if (kvm_set_memory_attributes_shared(start_addr, slot_size)) {
error_report("failed to clear memory attributes while "
"removing slot [0x%" HWADDR_PRIx
", 0x%" HWADDR_PRIx ")",
start_addr, start_addr + slot_size);
exit(EXIT_FAILURE);
}
}
start_addr += slot_size;
size -= slot_size;
kml->nr_slots_used--;
Expand Down Expand Up @@ -2701,6 +2724,14 @@ static int do_kvm_create_vm(KVMState *s, int type)
error_printf("PPC KVM module is not loaded. Try modprobe kvm_%s.\n",
(type == 2) ? "pr" : "hv");
}
#elif defined(TARGET_AARCH64)
if (ret == -EINVAL &&
(type & KVM_VM_TYPE_ARM_MASK) == KVM_VM_TYPE_ARM_REALM &&
KVM_VM_TYPE_ARM_IPA_SIZE(type)) {
error_printf("The requested Realm IPA size (%u bits) may exceed "
"the RMM S2SZ limit.\n",
(unsigned int)KVM_VM_TYPE_ARM_IPA_SIZE(type));
}
#endif
}

Expand Down Expand Up @@ -3377,18 +3408,51 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
return ret;
}

if (!int128_eq(section.size, int128_make64(size))) {
error_report("Convert memory range (0x%" HWADDR_PRIx
" + 0x%" HWADDR_PRIx ") crosses a memory-region "
"boundary", start, size);
goto out_unref;
}

if (!memory_region_has_guest_memfd(mr)) {
/*
* Because vMMIO region must be shared, guest TD may convert vMMIO
* region to shared explicitly. Don't complain such case. See
* Because vMMIO region may be shared, guest TD may convert vMMIO
* region to shared explicitly. Don't complain such case. See
* memory_region_type() for checking if the region is MMIO region.
*/
if (!to_private &&
!memory_region_is_ram(mr) &&
!memory_region_is_ram_device(mr) &&
(!memory_region_is_ram(mr) ||
(machine_has_assigned_device_memory(current_machine) &&
memory_region_is_ram_device(mr))) &&
!memory_region_is_rom(mr) &&
!memory_region_is_romd(mr)) {
ret = 0;
/*
* For ram_device regions (e.g. VFIO BARs), the realm may
* have marked individual gfns PRIVATE through
* iommufd_tsm_dev_memmap_exit() when validating a VDEV
* mapping. When the realm later asks to release the
* mapping with RSI_IPA_STATE_SET(EMPTY), the kernel
* surfaces it here as a !to_private convert. A silent
* ret=0 would leave PRIVATE set with no DEV mapping
* behind it; honour the release by actually clearing the
* attribute. The kvm_arch_post_set_memory_attributes()
* side-effect then tears down any leftover ASSIGNED-DEV
* S2 via kvm_unmap_gfn_range(KVM_FILTER_PRIVATE), keeping
* userspace's view in sync with the kernel's.
*
* For the non-ram_device legs of this branch (e.g. TDX
* vMMIO traps) no PRIVATE attribute was ever installed,
* so preserve the original ret=0 fast path.
*/
if (machine_has_assigned_device_memory(current_machine) &&
memory_region_is_ram_device(mr) &&
(kvm_supported_memory_attributes &
KVM_MEMORY_ATTRIBUTE_PRIVATE)) {
ret = kvm_set_memory_attributes_shared(start, size);
} else {
ret = 0;
}
} else {
error_report("Convert non guest_memfd backed memory region "
"(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s",
Expand Down Expand Up @@ -4763,6 +4827,11 @@ void kvm_mark_guest_state_protected(void)
kvm_state->guest_state_protected = true;
}

bool kvm_guest_state_protected(void)
{
return kvm_state && kvm_state->guest_state_protected;
}

int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp)
{
int fd;
Expand Down
10 changes: 10 additions & 0 deletions accel/stubs/kvm-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,13 @@ int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp)
{
return -ENOSYS;
}

int kvm_set_memory_attributes_private(hwaddr start, uint64_t size)
{
return -ENOSYS;
}

int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size)
{
return -ENOSYS;
}
5 changes: 4 additions & 1 deletion docs/interop/firmware.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@
# options related to this feature are documented in
# "docs/system/i386/amd-memory-encryption.rst".
#
# @arm-rme: The firmware supports running in a Realm, under the Arm Realm
# Management Extension (RME).
#
# @intel-tdx: The firmware supports running under Intel Trust Domain
# Extensions (TDX).
#
Expand Down Expand Up @@ -229,7 +232,7 @@
{ 'enum' : 'FirmwareFeature',
'data' : [ 'acpi-s3', 'acpi-s4',
'amd-sev', 'amd-sev-es', 'amd-sev-snp',
'intel-tdx',
'arm-rme', 'intel-tdx',
'enrolled-keys', 'requires-smm',
'secure-boot', 'host-uefi-vars',
'verbose-dynamic', 'verbose-static' ] }
Expand Down
9 changes: 5 additions & 4 deletions docs/system/arm/virt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ dtb-randomness
rng-seed and kaslr-seed nodes (in both "/chosen" and
"/secure-chosen") to use for features like the random number
generator and address space randomisation. The default is
``on``. You will want to disable it if your trusted boot chain
will verify the DTB it is passed, since this option causes the
DTB to be non-deterministic. It would be the responsibility of
the firmware to come up with a seed and pass it on if it wants to.
``off`` for confidential VMs, and ``on`` otherwise. You will want
to disable it if your trusted boot chain will verify the DTB it is
passed, since this option causes the DTB to be non-deterministic.
It would be the responsibility of the firmware to come up with a
seed and pass it on if it wants to.

dtb-kaslr-seed
A deprecated synonym for dtb-randomness.
Expand Down
1 change: 1 addition & 0 deletions docs/system/confidential-guest-support.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,6 @@ Currently supported confidential guest mechanisms are:
* POWER Protected Execution Facility (PEF) (see :ref:`power-papr-protected-execution-facility-pef`)
* s390x Protected Virtualization (PV) (see :doc:`s390x/protvirt`)
* AWS Nitro Enclaves (see :doc:`nitro`)
* Arm Realm Management Extension (RME)

Other mechanisms may be supported in future.
103 changes: 94 additions & 9 deletions hw/arm/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,42 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
g_autoptr(MemoryDeviceInfoList) md_list = NULL;
Error *err = NULL;

if (binfo->dtb_filename) {
if (binfo->dtb_filename && binfo->confidential) {
g_autofree char *filename = NULL;
int64_t file_size;

/*
* If the user is providing a DTB for a confidential VM, it is already
* tailored to this configuration and measured. Load it as is, without
* any modification.
*/
filename = qemu_find_file(QEMU_FILE_TYPE_DTB, binfo->dtb_filename);
if (!filename) {
fprintf(stderr, "Couldn't open dtb file %s\n",
binfo->dtb_filename);
goto fail;
}

file_size = get_image_size(filename, &err);
if (file_size <= 0 || file_size > INT_MAX) {
if (err) {
error_report_err(err);
err = NULL;
} else {
error_report("Invalid DTB file size for %s", filename);
}
goto fail;
}

if (addr_limit > addr && file_size > addr_limit - addr) {
return 0;
}

if (rom_add_file_fixed_as(filename, addr, -1, as) < 0) {
goto fail;
}
return file_size;
} else if (binfo->dtb_filename) {
char *filename;
filename = qemu_find_file(QEMU_FILE_TYPE_DTB, binfo->dtb_filename);
if (!filename) {
Expand Down Expand Up @@ -834,7 +869,13 @@ static void do_cpu_reset(void *opaque)
if (cpu == info->primary_cpu) {
AddressSpace *as = arm_boot_address_space(cpu, info);

cpu_set_pc(cs, info->loader_start);
if (info->confidential) {
assert(is_a64(env));
env->xregs[0] = info->dtb_start;
cpu_set_pc(cs, info->entry);
} else {
cpu_set_pc(cs, info->loader_start);
}

if (!have_dtb(info)) {
set_kernel_args(info, as);
Expand Down Expand Up @@ -924,7 +965,8 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
}

static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
hwaddr *entry, AddressSpace *as)
hwaddr *entry, AddressSpace *as,
bool confidential)
{
const size_t max_bytes = LOAD_IMAGE_MAX_DECOMPRESSED_BYTES;
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
Expand Down Expand Up @@ -976,7 +1018,8 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
* bootloader, we can just load it starting at 2MB+offset rather
* than 0MB + offset.
*/
if (kernel_load_offset < BOOTLOADER_MAX_SIZE) {
if (kernel_load_offset < BOOTLOADER_MAX_SIZE &&
!confidential) {
kernel_load_offset += 2 * MiB;
}
}
Expand Down Expand Up @@ -1060,7 +1103,8 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
kernel_size = load_aarch64_image(info->kernel_filename,
info->loader_start, &entry, as);
info->loader_start, &entry, as,
info->confidential);
is_linux = 1;
if (kernel_size >= 0) {
image_low_addr = entry;
Expand Down Expand Up @@ -1201,8 +1245,11 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
fixupcontext[FIXUP_ENTRYPOINT_LO] = entry;
fixupcontext[FIXUP_ENTRYPOINT_HI] = entry >> 32;

arm_write_bootloader("bootloader", as, info->loader_start,
primary_loader, fixupcontext);
/* Immediately jump to the kernel when guest is a Realm */
if (!info->confidential) {
arm_write_bootloader("bootloader", as, info->loader_start,
primary_loader, fixupcontext);
}

if (info->write_board_setup) {
info->write_board_setup(cpu, info);
Expand All @@ -1222,7 +1269,41 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
}
}

static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info)
static void arm_setup_confidential_firmware_boot(ARMCPU *cpu,
struct arm_boot_info *info,
const char *firmware_filename)
{
ssize_t fw_size;
g_autofree char *fname = NULL;
AddressSpace *as = arm_boot_address_space(cpu, info);

if (!firmware_filename) {
error_report("a confidential Arm guest requires either a kernel "
"or a firmware image");
exit(1);
}

fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware_filename);
if (!fname) {
error_report("Could not find firmware image '%s'", firmware_filename);
exit(1);
}

/*
* Load the firmware image in the Realm's address space. Mapping of the
* firmware area in the Realm's address space is done in function
* virt_confidential_firmware_init().
*/
fw_size = load_image_targphys_as(fname, info->firmware_base,
info->firmware_max_size, as, NULL);
if (fw_size <= 0) {
error_report("could not load firmware '%s'", firmware_filename);
exit(1);
}
}

static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info,
const char *firmware_filename)
{
/* Set up for booting firmware (which might load a kernel via fw_cfg) */

Expand Down Expand Up @@ -1273,6 +1354,10 @@ static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info)
}
}

if (info->confidential) {
arm_setup_confidential_firmware_boot(cpu, info, firmware_filename);
}

/*
* We will start from address 0 (typically a boot ROM image) in the
* same way as hardware. Leave env->boot_info NULL, so that
Expand Down Expand Up @@ -1316,7 +1401,7 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)

/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
arm_setup_firmware_boot(cpu, info);
arm_setup_firmware_boot(cpu, info, ms->firmware);
} else {
arm_setup_direct_kernel_boot(cpu, info);
}
Expand Down
Loading