public inbox for qemu-arm@nongnu.org
 help / color / mirror / Atom feed
* [PATCH 00/10] Native Nitro Enclaves support
@ 2026-02-18  1:51 Alexander Graf
  2026-02-18  1:51 ` [PATCH 01/10] scripts/update-linux-headers: Add Nitro Enclaves header Alexander Graf
                   ` (9 more replies)
  0 siblings, 10 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

We had emulated Nitro Enclaves support in QEMU since 2024, but to launch
a native Nitro Enclave, you could only use the AWS nitro-cli tooling.

To simplify tooling and allow users to leverage the most convenient swiss
army knife of virtualization known to mankind (QEMU!), add native support
to launch a Nitro Enclave from within QEMU.

A Nitro Enclave is a Confidential Computing Virtual Machine spawned by
the Nitro Hypervisor which has a very basic machine model, with
virtio-vsock as the only real I/O between parent and enclave. This means
the amount of interactions between QEMU and the VM are limited, but for
debugging, experimentation and non-conventional use cases, it can be handy
to spawn a Nitro Enclave directly in a more fully featured virtualization
stack.

Example invocation:

  $ qemu-system-x86_64 -nographic -accel nitro,debug-mode=on -M nitro -kernel test.eif -smp 2
  QEMU 10.2.50 monitor - type 'help' for more information
  (qemu) [    0.000000] Linux version 4.14.256-209.484.amzn2.x86_64 (mockbuild@ip-10-0-50-84) (gcc version 7.3.1 20180712 (Red Hat 7.3.1-13) (GCC)) #1 SMP Tue Jan 11 21:47:36 UTC 2022
  [    0.000000] Command line: reboot=k panic=30 pci=off nomodules console=ttyS0 i8042.noaux i8042.nomux i8042.nopnp i8042.dumbkbd random.trust_cpu=on virtio_mmio.device=4K@0xd0000000:5 virtio_mmio.device=4K@0xd0001000:6
  [    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
  [    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
  [    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'

This patch set supports x86_64 as well as aarch64 Nitro Enclaves.
Virtio-vsock communication with the Enclave is handled directly through the
parent's virtio-vsock device.

Alex

Alexander Graf (10):
  scripts/update-linux-headers: Add Nitro Enclaves header
  linux-headers: Add nitro_enclaves.h
  accel: Add Nitro Enclaves accelerator
  hw/nitro/nitro-serial-vsock: Nitro Enclaves vsock console
  hw/nitro: Introduce Nitro Enclave Heartbeat device
  target/arm/cpu64: Allow -host for nitro
  hw/nitro: Add nitro machine
  hw/core/eif: Move definitions to header
  hw/nitro: Enable direct kernel boot
  docs: Add Nitro Enclaves documentation

 MAINTAINERS                                   |  12 +
 accel/Kconfig                                 |   3 +
 accel/meson.build                             |   1 +
 accel/nitro/meson.build                       |   3 +
 accel/nitro/nitro-accel.c                     | 334 ++++++++++++++++
 accel/nitro/trace-events                      |   6 +
 accel/nitro/trace.h                           |   2 +
 accel/stubs/meson.build                       |   1 +
 accel/stubs/nitro-stub.c                      |  11 +
 docs/system/confidential-guest-support.rst    |   1 +
 docs/system/index.rst                         |   1 +
 docs/system/nitro.rst                         | 114 ++++++
 hw/Kconfig                                    |   1 +
 hw/core/eif.c                                 |  38 --
 hw/core/eif.h                                 |  41 ++
 hw/meson.build                                |   1 +
 hw/nitro/Kconfig                              |  14 +
 hw/nitro/heartbeat.c                          | 118 ++++++
 hw/nitro/machine.c                            | 297 +++++++++++++++
 hw/nitro/meson.build                          |   3 +
 hw/nitro/serial-vsock.c                       | 155 ++++++++
 hw/nitro/trace-events                         |   8 +
 hw/nitro/trace.h                              |   1 +
 include/hw/nitro/heartbeat.h                  |  25 ++
 include/hw/nitro/machine.h                    |  20 +
 include/hw/nitro/serial-vsock.h               |  26 ++
 .../standard-headers/linux/nitro_enclaves.h   | 359 ++++++++++++++++++
 include/system/hw_accel.h                     |   1 +
 include/system/nitro-accel.h                  |  25 ++
 meson.build                                   |  12 +
 meson_options.txt                             |   2 +
 qemu-options.hx                               |   8 +-
 scripts/meson-buildoptions.sh                 |   3 +
 scripts/update-linux-headers.sh               |   1 +
 target/arm/cpu64.c                            |   8 +
 35 files changed, 1614 insertions(+), 42 deletions(-)
 create mode 100644 accel/nitro/meson.build
 create mode 100644 accel/nitro/nitro-accel.c
 create mode 100644 accel/nitro/trace-events
 create mode 100644 accel/nitro/trace.h
 create mode 100644 accel/stubs/nitro-stub.c
 create mode 100644 docs/system/nitro.rst
 create mode 100644 hw/nitro/Kconfig
 create mode 100644 hw/nitro/heartbeat.c
 create mode 100644 hw/nitro/machine.c
 create mode 100644 hw/nitro/meson.build
 create mode 100644 hw/nitro/serial-vsock.c
 create mode 100644 hw/nitro/trace-events
 create mode 100644 hw/nitro/trace.h
 create mode 100644 include/hw/nitro/heartbeat.h
 create mode 100644 include/hw/nitro/machine.h
 create mode 100644 include/hw/nitro/serial-vsock.h
 create mode 100644 include/standard-headers/linux/nitro_enclaves.h
 create mode 100644 include/system/nitro-accel.h

-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 01/10] scripts/update-linux-headers: Add Nitro Enclaves header
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  1:51 ` [PATCH 02/10] linux-headers: Add nitro_enclaves.h Alexander Graf
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

We want to enable QEMU to drive the /dev/nitro_enclaves device node. Add
its UAPI header into our kernel sync so we have all defines we need to
drive it.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 scripts/update-linux-headers.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index d09d8cf4c6..386d7a38e7 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -254,6 +254,7 @@ for i in "$hdrdir"/include/linux/*virtio*.h \
          "$hdrdir/include/linux/kvm_para.h" \
          "$hdrdir/include/linux/vhost_types.h" \
          "$hdrdir/include/linux/vmclock-abi.h" \
+         "$hdrdir/include/linux/nitro_enclaves.h" \
          "$hdrdir/include/linux/sysinfo.h"; do
     cp_portable "$i" "$output/include/standard-headers/linux"
 done
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 02/10] linux-headers: Add nitro_enclaves.h
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
  2026-02-18  1:51 ` [PATCH 01/10] scripts/update-linux-headers: Add Nitro Enclaves header Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  1:51 ` [PATCH 03/10] accel: Add Nitro Enclaves accelerator Alexander Graf
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

QEMU is learning to drive the /dev/nitro_enclaves device node. Include
its UAPI header into our local copy of kernel headers so it has all
defines we need to drive it.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 .../standard-headers/linux/nitro_enclaves.h   | 359 ++++++++++++++++++
 1 file changed, 359 insertions(+)
 create mode 100644 include/standard-headers/linux/nitro_enclaves.h

diff --git a/include/standard-headers/linux/nitro_enclaves.h b/include/standard-headers/linux/nitro_enclaves.h
new file mode 100644
index 0000000000..5545267dd9
--- /dev/null
+++ b/include/standard-headers/linux/nitro_enclaves.h
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ */
+
+#ifndef _LINUX_NITRO_ENCLAVES_H_
+#define _LINUX_NITRO_ENCLAVES_H_
+
+#include "standard-headers/linux/types.h"
+
+/**
+ * DOC: Nitro Enclaves (NE) Kernel Driver Interface
+ */
+
+/**
+ * NE_CREATE_VM - The command is used to create a slot that is associated with
+ *		  an enclave VM.
+ *		  The generated unique slot id is an output parameter.
+ *		  The ioctl can be invoked on the /dev/nitro_enclaves fd, before
+ *		  setting any resources, such as memory and vCPUs, for an
+ *		  enclave. Memory and vCPUs are set for the slot mapped to an enclave.
+ *		  A NE CPU pool has to be set before calling this function. The
+ *		  pool can be set after the NE driver load, using
+ *		  /sys/module/nitro_enclaves/parameters/ne_cpus.
+ *		  Its format is the detailed in the cpu-lists section:
+ *		  https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
+ *		  CPU 0 and its siblings have to remain available for the
+ *		  primary / parent VM, so they cannot be set for enclaves. Full
+ *		  CPU core(s), from the same NUMA node, need(s) to be included
+ *		  in the CPU pool.
+ *
+ * Context: Process context.
+ * Return:
+ * * Enclave file descriptor		- Enclave file descriptor used with
+ *					  ioctl calls to set vCPUs and memory
+ *					  regions, then start the enclave.
+ * *  -1				- There was a failure in the ioctl logic.
+ * On failure, errno is set to:
+ * * EFAULT				- copy_to_user() failure.
+ * * ENOMEM				- Memory allocation failure for internal
+ *					  bookkeeping variables.
+ * * NE_ERR_NO_CPUS_AVAIL_IN_POOL	- No NE CPU pool set / no CPUs available
+ *					  in the pool.
+ * * Error codes from get_unused_fd_flags() and anon_inode_getfile().
+ * * Error codes from the NE PCI device request.
+ */
+#define NE_CREATE_VM			_IOR(0xAE, 0x20, uint64_t)
+
+/**
+ * NE_ADD_VCPU - The command is used to set a vCPU for an enclave. The vCPU can
+ *		 be auto-chosen from the NE CPU pool or it can be set by the
+ *		 caller, with the note that it needs to be available in the NE
+ *		 CPU pool. Full CPU core(s), from the same NUMA node, need(s) to
+ *		 be associated with an enclave.
+ *		 The vCPU id is an input / output parameter. If its value is 0,
+ *		 then a CPU is chosen from the enclave CPU pool and returned via
+ *		 this parameter.
+ *		 The ioctl can be invoked on the enclave fd, before an enclave
+ *		 is started.
+ *
+ * Context: Process context.
+ * Return:
+ * * 0					- Logic successfully completed.
+ * *  -1				- There was a failure in the ioctl logic.
+ * On failure, errno is set to:
+ * * EFAULT				- copy_from_user() / copy_to_user() failure.
+ * * ENOMEM				- Memory allocation failure for internal
+ *					  bookkeeping variables.
+ * * EIO				- Current task mm is not the same as the one
+ *					  that created the enclave.
+ * * NE_ERR_NO_CPUS_AVAIL_IN_POOL	- No CPUs available in the NE CPU pool.
+ * * NE_ERR_VCPU_ALREADY_USED		- The provided vCPU is already used.
+ * * NE_ERR_VCPU_NOT_IN_CPU_POOL	- The provided vCPU is not available in the
+ *					  NE CPU pool.
+ * * NE_ERR_VCPU_INVALID_CPU_CORE	- The core id of the provided vCPU is invalid
+ *					  or out of range.
+ * * NE_ERR_NOT_IN_INIT_STATE		- The enclave is not in init state
+ *					  (init = before being started).
+ * * NE_ERR_INVALID_VCPU		- The provided vCPU is not in the available
+ *					  CPUs range.
+ * * Error codes from the NE PCI device request.
+ */
+#define NE_ADD_VCPU			_IOWR(0xAE, 0x21, uint32_t)
+
+/**
+ * NE_GET_IMAGE_LOAD_INFO - The command is used to get information needed for
+ *			    in-memory enclave image loading e.g. offset in
+ *			    enclave memory to start placing the enclave image.
+ *			    The image load info is an input / output parameter.
+ *			    It includes info provided by the caller - flags -
+ *			    and returns the offset in enclave memory where to
+ *			    start placing the enclave image.
+ *			    The ioctl can be invoked on the enclave fd, before
+ *			    an enclave is started.
+ *
+ * Context: Process context.
+ * Return:
+ * * 0				- Logic successfully completed.
+ * *  -1			- There was a failure in the ioctl logic.
+ * On failure, errno is set to:
+ * * EFAULT			- copy_from_user() / copy_to_user() failure.
+ * * NE_ERR_NOT_IN_INIT_STATE	- The enclave is not in init state (init =
+ *				  before being started).
+ * * NE_ERR_INVALID_FLAG_VALUE	- The value of the provided flag is invalid.
+ */
+#define NE_GET_IMAGE_LOAD_INFO		_IOWR(0xAE, 0x22, struct ne_image_load_info)
+
+/**
+ * NE_SET_USER_MEMORY_REGION - The command is used to set a memory region for an
+ *			       enclave, given the allocated memory from the
+ *			       userspace. Enclave memory needs to be from the
+ *			       same NUMA node as the enclave CPUs.
+ *			       The user memory region is an input parameter. It
+ *			       includes info provided by the caller - flags,
+ *			       memory size and userspace address.
+ *			       The ioctl can be invoked on the enclave fd,
+ *			       before an enclave is started.
+ *
+ * Context: Process context.
+ * Return:
+ * * 0					- Logic successfully completed.
+ * *  -1				- There was a failure in the ioctl logic.
+ * On failure, errno is set to:
+ * * EFAULT				- copy_from_user() failure.
+ * * EINVAL				- Invalid physical memory region(s) e.g.
+ *					  unaligned address.
+ * * EIO				- Current task mm is not the same as
+ *					  the one that created the enclave.
+ * * ENOMEM				- Memory allocation failure for internal
+ *					  bookkeeping variables.
+ * * NE_ERR_NOT_IN_INIT_STATE		- The enclave is not in init state
+ *					  (init = before being started).
+ * * NE_ERR_INVALID_MEM_REGION_SIZE	- The memory size of the region is not
+ *					  multiple of 2 MiB.
+ * * NE_ERR_INVALID_MEM_REGION_ADDR	- Invalid user space address given.
+ * * NE_ERR_UNALIGNED_MEM_REGION_ADDR	- Unaligned user space address given.
+ * * NE_ERR_MEM_REGION_ALREADY_USED	- The memory region is already used.
+ * * NE_ERR_MEM_NOT_HUGE_PAGE		- The memory region is not backed by
+ *					  huge pages.
+ * * NE_ERR_MEM_DIFFERENT_NUMA_NODE	- The memory region is not from the same
+ *					  NUMA node as the CPUs.
+ * * NE_ERR_MEM_MAX_REGIONS		- The number of memory regions set for
+ *					  the enclave reached maximum.
+ * * NE_ERR_INVALID_PAGE_SIZE		- The memory region is not backed by
+ *					  pages multiple of 2 MiB.
+ * * NE_ERR_INVALID_FLAG_VALUE		- The value of the provided flag is invalid.
+ * * Error codes from get_user_pages().
+ * * Error codes from the NE PCI device request.
+ */
+#define NE_SET_USER_MEMORY_REGION	_IOW(0xAE, 0x23, struct ne_user_memory_region)
+
+/**
+ * NE_START_ENCLAVE - The command is used to trigger enclave start after the
+ *		      enclave resources, such as memory and CPU, have been set.
+ *		      The enclave start info is an input / output parameter. It
+ *		      includes info provided by the caller - enclave cid and
+ *		      flags - and returns the cid (if input cid is 0).
+ *		      The ioctl can be invoked on the enclave fd, after an
+ *		      enclave slot is created and resources, such as memory and
+ *		      vCPUs are set for an enclave.
+ *
+ * Context: Process context.
+ * Return:
+ * * 0					- Logic successfully completed.
+ * *  -1				- There was a failure in the ioctl logic.
+ * On failure, errno is set to:
+ * * EFAULT				- copy_from_user() / copy_to_user() failure.
+ * * NE_ERR_NOT_IN_INIT_STATE		- The enclave is not in init state
+ *					  (init = before being started).
+ * * NE_ERR_NO_MEM_REGIONS_ADDED	- No memory regions are set.
+ * * NE_ERR_NO_VCPUS_ADDED		- No vCPUs are set.
+ * *  NE_ERR_FULL_CORES_NOT_USED	- Full core(s) not set for the enclave.
+ * * NE_ERR_ENCLAVE_MEM_MIN_SIZE	- Enclave memory is less than minimum
+ *					  memory size (64 MiB).
+ * * NE_ERR_INVALID_FLAG_VALUE		- The value of the provided flag is invalid.
+ * *  NE_ERR_INVALID_ENCLAVE_CID	- The provided enclave CID is invalid.
+ * * Error codes from the NE PCI device request.
+ */
+#define NE_START_ENCLAVE		_IOWR(0xAE, 0x24, struct ne_enclave_start_info)
+
+/**
+ * DOC: NE specific error codes
+ */
+
+/**
+ * NE_ERR_VCPU_ALREADY_USED - The provided vCPU is already used.
+ */
+#define NE_ERR_VCPU_ALREADY_USED		(256)
+/**
+ * NE_ERR_VCPU_NOT_IN_CPU_POOL - The provided vCPU is not available in the
+ *				 NE CPU pool.
+ */
+#define NE_ERR_VCPU_NOT_IN_CPU_POOL		(257)
+/**
+ * NE_ERR_VCPU_INVALID_CPU_CORE - The core id of the provided vCPU is invalid
+ *				  or out of range of the NE CPU pool.
+ */
+#define NE_ERR_VCPU_INVALID_CPU_CORE		(258)
+/**
+ * NE_ERR_INVALID_MEM_REGION_SIZE - The user space memory region size is not
+ *				    multiple of 2 MiB.
+ */
+#define NE_ERR_INVALID_MEM_REGION_SIZE		(259)
+/**
+ * NE_ERR_INVALID_MEM_REGION_ADDR - The user space memory region address range
+ *				    is invalid.
+ */
+#define NE_ERR_INVALID_MEM_REGION_ADDR		(260)
+/**
+ * NE_ERR_UNALIGNED_MEM_REGION_ADDR - The user space memory region address is
+ *				      not aligned.
+ */
+#define NE_ERR_UNALIGNED_MEM_REGION_ADDR	(261)
+/**
+ * NE_ERR_MEM_REGION_ALREADY_USED - The user space memory region is already used.
+ */
+#define NE_ERR_MEM_REGION_ALREADY_USED		(262)
+/**
+ * NE_ERR_MEM_NOT_HUGE_PAGE - The user space memory region is not backed by
+ *			      contiguous physical huge page(s).
+ */
+#define NE_ERR_MEM_NOT_HUGE_PAGE		(263)
+/**
+ * NE_ERR_MEM_DIFFERENT_NUMA_NODE - The user space memory region is backed by
+ *				    pages from different NUMA nodes than the CPUs.
+ */
+#define NE_ERR_MEM_DIFFERENT_NUMA_NODE		(264)
+/**
+ * NE_ERR_MEM_MAX_REGIONS - The supported max memory regions per enclaves has
+ *			    been reached.
+ */
+#define NE_ERR_MEM_MAX_REGIONS			(265)
+/**
+ * NE_ERR_NO_MEM_REGIONS_ADDED - The command to start an enclave is triggered
+ *				 and no memory regions are added.
+ */
+#define NE_ERR_NO_MEM_REGIONS_ADDED		(266)
+/**
+ * NE_ERR_NO_VCPUS_ADDED - The command to start an enclave is triggered and no
+ *			   vCPUs are added.
+ */
+#define NE_ERR_NO_VCPUS_ADDED			(267)
+/**
+ * NE_ERR_ENCLAVE_MEM_MIN_SIZE - The enclave memory size is lower than the
+ *				 minimum supported.
+ */
+#define NE_ERR_ENCLAVE_MEM_MIN_SIZE		(268)
+/**
+ * NE_ERR_FULL_CORES_NOT_USED - The command to start an enclave is triggered and
+ *				full CPU cores are not set.
+ */
+#define NE_ERR_FULL_CORES_NOT_USED		(269)
+/**
+ * NE_ERR_NOT_IN_INIT_STATE - The enclave is not in init state when setting
+ *			      resources or triggering start.
+ */
+#define NE_ERR_NOT_IN_INIT_STATE		(270)
+/**
+ * NE_ERR_INVALID_VCPU - The provided vCPU is out of range of the available CPUs.
+ */
+#define NE_ERR_INVALID_VCPU			(271)
+/**
+ * NE_ERR_NO_CPUS_AVAIL_IN_POOL - The command to create an enclave is triggered
+ *				  and no CPUs are available in the pool.
+ */
+#define NE_ERR_NO_CPUS_AVAIL_IN_POOL		(272)
+/**
+ * NE_ERR_INVALID_PAGE_SIZE - The user space memory region is not backed by pages
+ *			      multiple of 2 MiB.
+ */
+#define NE_ERR_INVALID_PAGE_SIZE		(273)
+/**
+ * NE_ERR_INVALID_FLAG_VALUE - The provided flag value is invalid.
+ */
+#define NE_ERR_INVALID_FLAG_VALUE		(274)
+/**
+ * NE_ERR_INVALID_ENCLAVE_CID - The provided enclave CID is invalid, either
+ *				being a well-known value or the CID of the
+ *				parent / primary VM.
+ */
+#define NE_ERR_INVALID_ENCLAVE_CID		(275)
+
+/**
+ * DOC: Image load info flags
+ */
+
+/**
+ * NE_EIF_IMAGE - Enclave Image Format (EIF)
+ */
+#define NE_EIF_IMAGE			(0x01)
+
+#define NE_IMAGE_LOAD_MAX_FLAG_VAL	(0x02)
+
+/**
+ * struct ne_image_load_info - Info necessary for in-memory enclave image
+ *			       loading (in / out).
+ * @flags:		Flags to determine the enclave image type
+ *			(e.g. Enclave Image Format - EIF) (in).
+ * @memory_offset:	Offset in enclave memory where to start placing the
+ *			enclave image (out).
+ */
+struct ne_image_load_info {
+	uint64_t	flags;
+	uint64_t	memory_offset;
+};
+
+/**
+ * DOC: User memory region flags
+ */
+
+/**
+ * NE_DEFAULT_MEMORY_REGION - Memory region for enclave general usage.
+ */
+#define NE_DEFAULT_MEMORY_REGION	(0x00)
+
+#define NE_MEMORY_REGION_MAX_FLAG_VAL	(0x01)
+
+/**
+ * struct ne_user_memory_region - Memory region to be set for an enclave (in).
+ * @flags:		Flags to determine the usage for the memory region (in).
+ * @memory_size:	The size, in bytes, of the memory region to be set for
+ *			an enclave (in).
+ * @userspace_addr:	The start address of the userspace allocated memory of
+ *			the memory region to set for an enclave (in).
+ */
+struct ne_user_memory_region {
+	uint64_t	flags;
+	uint64_t	memory_size;
+	uint64_t	userspace_addr;
+};
+
+/**
+ * DOC: Enclave start info flags
+ */
+
+/**
+ * NE_ENCLAVE_PRODUCTION_MODE - Start enclave in production mode.
+ */
+#define NE_ENCLAVE_PRODUCTION_MODE	(0x00)
+/**
+ * NE_ENCLAVE_DEBUG_MODE - Start enclave in debug mode.
+ */
+#define NE_ENCLAVE_DEBUG_MODE		(0x01)
+
+#define NE_ENCLAVE_START_MAX_FLAG_VAL	(0x02)
+
+/**
+ * struct ne_enclave_start_info - Setup info necessary for enclave start (in / out).
+ * @flags:		Flags for the enclave to start with (e.g. debug mode) (in).
+ * @enclave_cid:	Context ID (CID) for the enclave vsock device. If 0 as
+ *			input, the CID is autogenerated by the hypervisor and
+ *			returned back as output by the driver (in / out).
+ */
+struct ne_enclave_start_info {
+	uint64_t	flags;
+	uint64_t	enclave_cid;
+};
+
+#endif /* _LINUX_NITRO_ENCLAVES_H_ */
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 03/10] accel: Add Nitro Enclaves accelerator
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
  2026-02-18  1:51 ` [PATCH 01/10] scripts/update-linux-headers: Add Nitro Enclaves header Alexander Graf
  2026-02-18  1:51 ` [PATCH 02/10] linux-headers: Add nitro_enclaves.h Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-24 10:22   ` Paolo Bonzini
  2026-02-18  1:51 ` [PATCH 04/10] hw/nitro/nitro-serial-vsock: Nitro Enclaves vsock console Alexander Graf
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

Nitro Enclaves are a confidential compute technology which
allows a parent instance to carve out resources from itself
and spawn a confidential sibling VM next to itself. Similar
to other confidential compute solutions, this sibling is
controlled by an underlying vmm, but still has a higher level
vmm (QEMU) to implement some of its I/O functionality and
lifecycle.

Add an accelerator to drive this interface. In combination with
follow-on patches to enhance the Nitro Enclaves machine model, this
will allow users to run a Nitro Enclave using QEMU.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 MAINTAINERS                   |   6 +
 accel/Kconfig                 |   3 +
 accel/meson.build             |   1 +
 accel/nitro/meson.build       |   3 +
 accel/nitro/nitro-accel.c     | 333 ++++++++++++++++++++++++++++++++++
 accel/nitro/trace-events      |   6 +
 accel/nitro/trace.h           |   2 +
 accel/stubs/meson.build       |   1 +
 accel/stubs/nitro-stub.c      |  11 ++
 include/system/hw_accel.h     |   1 +
 include/system/nitro-accel.h  |  25 +++
 meson.build                   |  11 ++
 meson_options.txt             |   2 +
 qemu-options.hx               |   8 +-
 scripts/meson-buildoptions.sh |   3 +
 15 files changed, 412 insertions(+), 4 deletions(-)
 create mode 100644 accel/nitro/meson.build
 create mode 100644 accel/nitro/nitro-accel.c
 create mode 100644 accel/nitro/trace-events
 create mode 100644 accel/nitro/trace.h
 create mode 100644 accel/stubs/nitro-stub.c
 create mode 100644 include/system/nitro-accel.h

diff --git a/MAINTAINERS b/MAINTAINERS
index d3aa6d6732..3d002143ae 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -583,6 +583,12 @@ F: include/system/mshv.h
 F: include/hw/hyperv/hvgdk*.h
 F: include/hw/hyperv/hvhdk*.h
 
+Nitro Enclaves (native)
+M: Alexander Graf <graf@amazon.com>
+S: Maintained
+F: accel/nitro/
+F: include/system/nitro-accel.h
+
 X86 MSHV CPUs
 M: Magnus Kulke <magnus.kulke@linux.microsoft.com>
 R: Wei Liu <wei.liu@kernel.org>
diff --git a/accel/Kconfig b/accel/Kconfig
index a60f114923..6d052875ee 100644
--- a/accel/Kconfig
+++ b/accel/Kconfig
@@ -16,6 +16,9 @@ config KVM
 config MSHV
     bool
 
+config NITRO
+    bool
+
 config XEN
     bool
     select FSDEV_9P if VIRTFS
diff --git a/accel/meson.build b/accel/meson.build
index 289b7420ff..7da12b9741 100644
--- a/accel/meson.build
+++ b/accel/meson.build
@@ -12,6 +12,7 @@ if have_system
   subdir('xen')
   subdir('stubs')
   subdir('mshv')
+  subdir('nitro')
 endif
 
 # qtest
diff --git a/accel/nitro/meson.build b/accel/nitro/meson.build
new file mode 100644
index 0000000000..e01c1bab96
--- /dev/null
+++ b/accel/nitro/meson.build
@@ -0,0 +1,3 @@
+nitro_ss = ss.source_set()
+nitro_ss.add(files('nitro-accel.c'))
+system_ss.add_all(when: 'CONFIG_NITRO', if_true: nitro_ss)
diff --git a/accel/nitro/nitro-accel.c b/accel/nitro/nitro-accel.c
new file mode 100644
index 0000000000..bea76fcd0b
--- /dev/null
+++ b/accel/nitro/nitro-accel.c
@@ -0,0 +1,333 @@
+/*
+ * Nitro Enclaves accelerator
+ *
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors:
+ *   Alexander Graf <graf@amazon.com>
+ *
+ * Nitro Enclaves are a confidential compute technology which
+ * allows a parent instance to carve out resources from itself
+ * and spawn a confidential sibling VM next to itself. Similar
+ * to other confidential compute solutions, this sibling is
+ * controlled by an underlying vmm, but still has a higher level
+ * vmm (QEMU) to implement some of its I/O functionality and
+ * lifecycle.
+ *
+ * This accelerator drives /dev/nitro_enclaves to spawn a Nitro
+ * Enclave. It works in tandem with the nitro_enclaves machine
+ * which ensures the correct backend devices are available and
+ * that the initial seed (an EIF file) is loaded at the correct
+ * offset in memory.
+ *
+ * The accel starts the enclave on first vCPU 0 main loop entry,
+ * to ensure that all device setup is finished and that we have
+ * a working vCPU loop.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/module.h"
+#include "qemu/rcu.h"
+#include "qemu/accel.h"
+#include "qemu/guest-random.h"
+#include "qemu/main-loop.h"
+#include "accel/accel-ops.h"
+#include "accel/accel-cpu-ops.h"
+#include "system/cpus.h"
+#include "hw/core/cpu.h"
+#include "hw/core/boards.h"
+#include "hw/core/sysbus.h"
+#include "system/ramblock.h"
+#include "system/nitro-accel.h"
+#include "trace.h"
+
+#include <sys/ioctl.h>
+#include "standard-headers/linux/nitro_enclaves.h"
+
+bool nitro_allowed;
+
+typedef struct NitroAccelState {
+    AccelState parent_obj;
+
+    int ne_fd;
+    int enclave_fd;
+    uint64_t slot_uid;
+    uint64_t enclave_cid;
+    bool debug_mode;
+} NitroAccelState;
+
+static int nitro_init_machine(AccelState *as, MachineState *ms)
+{
+    NitroAccelState *s = NITRO_ACCEL(as);
+    uint64_t slot_uid = 0;
+    int ret;
+
+    s->ne_fd = open("/dev/nitro_enclaves", O_RDWR | O_CLOEXEC);
+    if (s->ne_fd < 0) {
+        error_report("nitro: failed to open /dev/nitro_enclaves: %s",
+                     strerror(errno));
+        return -errno;
+    }
+
+    ret = ioctl(s->ne_fd, NE_CREATE_VM, &slot_uid);
+    if (ret < 0) {
+        error_report("nitro: NE_CREATE_VM failed: %s", strerror(errno));
+        close(s->ne_fd);
+        return -errno;
+    }
+    s->enclave_fd = ret;
+    s->slot_uid = slot_uid;
+
+    return 0;
+}
+
+static int nitro_donate_ram_block(RAMBlock *rb, void *opaque)
+{
+    NitroAccelState *s = opaque;
+    struct ne_user_memory_region region = {
+        .flags = 0,
+        .memory_size = rb->used_length,
+        .userspace_addr = (uint64_t)(uintptr_t)rb->host,
+    };
+
+    if (!rb->used_length) {
+        return 0;
+    }
+
+    if (ioctl(s->enclave_fd, NE_SET_USER_MEMORY_REGION, &region) < 0) {
+        error_report("nitro: NE_SET_USER_MEMORY_REGION failed for %s "
+                     "(%" PRIu64 " bytes): %s", rb->idstr, rb->used_length,
+                     strerror(errno));
+        return -errno;
+    }
+    return 0;
+}
+
+/*
+ * Start the Enclave. This gets called when the first vCPU 0 enters its main
+ * loop. At this point memory is set up and the EIF is loaded. This function
+ * donates memory, adds vCPUs, and starts the enclave.
+ */
+static void nitro_do_start(NitroAccelState *s)
+{
+    MachineState *ms = MACHINE(qdev_get_machine());
+    int nr_cpus = ms->smp.cpus;
+    int i, ret;
+    struct ne_enclave_start_info start_info = {
+        .flags = s->debug_mode ? NE_ENCLAVE_DEBUG_MODE : 0,
+        .enclave_cid = s->enclave_cid,
+    };
+
+    ret = qemu_ram_foreach_block(nitro_donate_ram_block, s);
+    if (ret < 0) {
+        error_report("nitro: failed to donate memory");
+        exit(1);
+    }
+
+    for (i = 0; i < nr_cpus; i++) {
+        uint32_t cpu_id = 0;
+        if (ioctl(s->enclave_fd, NE_ADD_VCPU, &cpu_id) < 0) {
+            error_report("nitro: NE_ADD_VCPU failed: %s", strerror(errno));
+            exit(1);
+        }
+    }
+
+    ret = ioctl(s->enclave_fd, NE_START_ENCLAVE, &start_info);
+    if (ret < 0) {
+        switch (errno) {
+        case NE_ERR_NO_MEM_REGIONS_ADDED:
+            error_report("nitro: no memory regions added");
+            break;
+        case NE_ERR_NO_VCPUS_ADDED:
+            error_report("nitro: no vCPUs added");
+            break;
+        case NE_ERR_ENCLAVE_MEM_MIN_SIZE:
+            error_report("nitro: memory is below the minimum "
+                         "required size. Try increasing -m");
+            break;
+        case NE_ERR_FULL_CORES_NOT_USED:
+            error_report("nitro: requires full CPU cores. "
+                         "Try increasing -smp to a multiple of threads "
+                         "per core on this host (e.g. -smp 2)");
+            break;
+        case NE_ERR_NOT_IN_INIT_STATE:
+            error_report("nitro: not in init state");
+            break;
+        case NE_ERR_INVALID_FLAG_VALUE:
+            error_report("nitro: invalid flag value for NE_START_ENCLAVE");
+            break;
+        case NE_ERR_INVALID_ENCLAVE_CID:
+            error_report("nitro: invalid enclave CID");
+            break;
+        default:
+            error_report("nitro: NE_START_ENCLAVE failed: %s (errno %d)",
+                         strerror(errno), errno);
+            break;
+        }
+        exit(1);
+    }
+
+    s->enclave_cid = start_info.enclave_cid;
+    trace_nitro_enclave_started(s->enclave_cid);
+
+    /*
+     * Push enclave CID to all devices that need it.
+     * Each device handles its own connection (console, heartbeat).
+     */
+    {
+        BusState *sysbus = sysbus_get_default();
+        BusChild *kid;
+
+        QTAILQ_FOREACH(kid, &sysbus->children, sibling) {
+            DeviceState *dev = kid->child;
+            if (object_property_find(OBJECT(dev), "enclave-cid")) {
+                object_property_set_uint(OBJECT(dev), "enclave-cid",
+                                         s->enclave_cid, NULL);
+            }
+        }
+    }
+}
+
+/*
+ * vCPU dummy thread function. The real vCPUs run inside the enclave.
+ *
+ * Based on dummy_cpu_thread_fn() from accel/dummy-cpus.c.
+ */
+static void *nitro_vcpu_thread_fn(void *arg)
+{
+    CPUState *cpu = arg;
+    NitroAccelState *s = NITRO_ACCEL(current_accel());
+    sigset_t waitset;
+
+    rcu_register_thread();
+
+    bql_lock();
+    qemu_thread_get_self(cpu->thread);
+    cpu->thread_id = qemu_get_thread_id();
+    current_cpu = cpu;
+
+    sigemptyset(&waitset);
+    sigaddset(&waitset, SIG_IPI);
+
+    cpu_thread_signal_created(cpu);
+    qemu_guest_random_seed_thread_part2(cpu->random_seed);
+
+    /* vCPU 0 starts the enclave on first entry */
+    if (cpu->cpu_index == 0) {
+        nitro_do_start(s);
+    }
+
+    do {
+        qemu_process_cpu_events(cpu);
+        bql_unlock();
+        {
+            int sig;
+            while (sigwait(&waitset, &sig) == -1 &&
+                   (errno == EAGAIN || errno == EINTR)) {
+                /* retry */
+            }
+        }
+        bql_lock();
+    } while (!cpu->unplug);
+
+    bql_unlock();
+    rcu_unregister_thread();
+    return NULL;
+}
+
+static void nitro_start_vcpu_thread(CPUState *cpu)
+{
+    char thread_name[VCPU_THREAD_NAME_SIZE];
+
+    snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/Nitro",
+             cpu->cpu_index);
+    qemu_thread_create(cpu->thread, thread_name, nitro_vcpu_thread_fn,
+                       cpu, QEMU_THREAD_JOINABLE);
+}
+
+/* QOM properties */
+
+static bool nitro_get_debug_mode(Object *obj, Error **errp)
+{
+    return NITRO_ACCEL(obj)->debug_mode;
+}
+
+static void nitro_set_debug_mode(Object *obj, bool value, Error **errp)
+{
+    NITRO_ACCEL(obj)->debug_mode = value;
+}
+
+static void nitro_get_enclave_cid(Object *obj, Visitor *v,
+                                  const char *name, void *opaque,
+                                  Error **errp)
+{
+    uint64_t val = NITRO_ACCEL(obj)->enclave_cid;
+    visit_type_uint64(v, name, &val, errp);
+}
+
+static void nitro_set_enclave_cid(Object *obj, Visitor *v,
+                                  const char *name, void *opaque,
+                                  Error **errp)
+{
+    uint64_t val;
+    if (visit_type_uint64(v, name, &val, errp)) {
+        NITRO_ACCEL(obj)->enclave_cid = val;
+    }
+}
+
+static void nitro_accel_class_init(ObjectClass *oc, const void *data)
+{
+    AccelClass *ac = ACCEL_CLASS(oc);
+    ac->name = "Nitro";
+    ac->init_machine = nitro_init_machine;
+    ac->allowed = &nitro_allowed;
+
+    object_class_property_add_bool(oc, "debug-mode",
+                                   nitro_get_debug_mode,
+                                   nitro_set_debug_mode);
+    object_class_property_set_description(oc, "debug-mode",
+        "Start enclave in debug mode (enables console output)");
+
+    object_class_property_add(oc, "enclave-cid", "uint64",
+                              nitro_get_enclave_cid,
+                              nitro_set_enclave_cid,
+                              NULL, NULL);
+    object_class_property_set_description(oc, "enclave-cid",
+        "Enclave CID (0 = auto-assigned by Nitro)");
+}
+
+static const TypeInfo nitro_accel_type = {
+    .name = TYPE_NITRO_ACCEL,
+    .parent = TYPE_ACCEL,
+    .instance_size = sizeof(NitroAccelState),
+    .class_init = nitro_accel_class_init,
+};
+module_obj(TYPE_NITRO_ACCEL);
+
+static void nitro_accel_ops_class_init(ObjectClass *oc, const void *data)
+{
+    AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
+    ops->create_vcpu_thread = nitro_start_vcpu_thread;
+    ops->handle_interrupt = generic_handle_interrupt;
+}
+
+static const TypeInfo nitro_accel_ops_type = {
+    .name = ACCEL_OPS_NAME("nitro"),
+    .parent = TYPE_ACCEL_OPS,
+    .class_init = nitro_accel_ops_class_init,
+    .abstract = true,
+};
+module_obj(ACCEL_OPS_NAME("nitro"));
+
+static void nitro_type_init(void)
+{
+    type_register_static(&nitro_accel_type);
+    type_register_static(&nitro_accel_ops_type);
+}
+
+type_init(nitro_type_init);
diff --git a/accel/nitro/trace-events b/accel/nitro/trace-events
new file mode 100644
index 0000000000..9673eb5aa2
--- /dev/null
+++ b/accel/nitro/trace-events
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# See docs/devel/tracing.rst for syntax documentation.
+
+# nitro-accel.c
+nitro_enclave_started(uint64_t cid) "nitro: enclave started, CID=%"PRIu64
diff --git a/accel/nitro/trace.h b/accel/nitro/trace.h
new file mode 100644
index 0000000000..8c5564725d
--- /dev/null
+++ b/accel/nitro/trace.h
@@ -0,0 +1,2 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "trace/trace-accel_nitro.h"
diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build
index 48eccd1b86..5de4a279ff 100644
--- a/accel/stubs/meson.build
+++ b/accel/stubs/meson.build
@@ -3,6 +3,7 @@ system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
 system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
 system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
 system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
+system_stubs_ss.add(when: 'CONFIG_NITRO', if_false: files('nitro-stub.c'))
 system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
 system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
 system_stubs_ss.add(when: 'CONFIG_MSHV', if_false: files('mshv-stub.c'))
diff --git a/accel/stubs/nitro-stub.c b/accel/stubs/nitro-stub.c
new file mode 100644
index 0000000000..186c8444f8
--- /dev/null
+++ b/accel/stubs/nitro-stub.c
@@ -0,0 +1,11 @@
+/*
+ * Nitro accel stubs for QEMU
+ *
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+bool nitro_allowed;
diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h
index 628a50e066..f0c10b6d80 100644
--- a/include/system/hw_accel.h
+++ b/include/system/hw_accel.h
@@ -17,6 +17,7 @@
 #include "system/mshv.h"
 #include "system/whpx.h"
 #include "system/nvmm.h"
+#include "system/nitro-accel.h"
 
 /**
  * cpu_synchronize_state:
diff --git a/include/system/nitro-accel.h b/include/system/nitro-accel.h
new file mode 100644
index 0000000000..a93aa6fb00
--- /dev/null
+++ b/include/system/nitro-accel.h
@@ -0,0 +1,25 @@
+/*
+ * Nitro Enclaves accelerator - public interface
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SYSTEM_NITRO_ACCEL_H
+#define SYSTEM_NITRO_ACCEL_H
+
+#include "qemu/accel.h"
+
+extern bool nitro_allowed;
+
+static inline bool nitro_enabled(void)
+{
+    return nitro_allowed;
+}
+
+#define TYPE_NITRO_ACCEL ACCEL_CLASS_NAME("nitro")
+
+typedef struct NitroAccelState NitroAccelState;
+DECLARE_INSTANCE_CHECKER(NitroAccelState, NITRO_ACCEL,
+                         TYPE_NITRO_ACCEL)
+
+#endif /* SYSTEM_NITRO_ACCEL_H */
diff --git a/meson.build b/meson.build
index 4af32c3e1f..bdeee65db2 100644
--- a/meson.build
+++ b/meson.build
@@ -302,11 +302,13 @@ accelerator_targets += { 'CONFIG_XEN': xen_targets }
 if cpu == 'aarch64'
   accelerator_targets += {
     'CONFIG_HVF': ['aarch64-softmmu'],
+    'CONFIG_NITRO': ['aarch64-softmmu'],
     'CONFIG_WHPX': ['aarch64-softmmu']
   }
 elif cpu == 'x86_64'
   accelerator_targets += {
     'CONFIG_HVF': ['x86_64-softmmu'],
+    'CONFIG_NITRO': ['x86_64-softmmu'],
     'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'],
     'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'],
     'CONFIG_MSHV': ['x86_64-softmmu'],
@@ -881,6 +883,11 @@ if get_option('hvf').allowed()
   endif
 endif
 
+nitro = not_found
+if get_option('nitro').allowed() and host_os == 'linux'
+  accelerators += 'CONFIG_NITRO'
+endif
+
 nvmm = not_found
 if host_os == 'netbsd'
   nvmm = cc.find_library('nvmm', required: get_option('nvmm'))
@@ -922,6 +929,9 @@ endif
 if 'CONFIG_HVF' not in accelerators and get_option('hvf').enabled()
   error('HVF not available on this platform')
 endif
+if 'CONFIG_NITRO' not in accelerators and get_option('nitro').enabled()
+  error('NITRO not available on this platform')
+endif
 if 'CONFIG_NVMM' not in accelerators and get_option('nvmm').enabled()
   error('NVMM not available on this platform')
 endif
@@ -3593,6 +3603,7 @@ if have_system
     'accel/hvf',
     'accel/kvm',
     'accel/mshv',
+    'accel/nitro',
     'audio',
     'backends',
     'backends/tpm',
diff --git a/meson_options.txt b/meson_options.txt
index 2836156257..31d5916cfc 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -79,6 +79,8 @@ option('whpx', type: 'feature', value: 'auto',
        description: 'WHPX acceleration support')
 option('hvf', type: 'feature', value: 'auto',
        description: 'HVF acceleration support')
+option('nitro', type: 'feature', value: 'auto',
+       description: 'Nitro acceleration support')
 option('nvmm', type: 'feature', value: 'auto',
        description: 'NVMM acceleration support')
 option('xen', type: 'feature', value: 'auto',
diff --git a/qemu-options.hx b/qemu-options.hx
index 33fcfe7ce6..9b6fb247f7 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -28,7 +28,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
     "-machine [type=]name[,prop[=value][,...]]\n"
     "                selects emulated machine ('-machine help' for list)\n"
     "                property accel=accel1[:accel2[:...]] selects accelerator\n"
-    "                supported accelerators are kvm, xen, hvf, nvmm, whpx, mshv or tcg (default: tcg)\n"
+    "                supported accelerators are kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg (default: tcg)\n"
     "                vmport=on|off|auto controls emulation of vmport (default: auto)\n"
     "                dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
     "                mem-merge=on|off controls memory merge support (default: on)\n"
@@ -67,7 +67,7 @@ SRST
 
     ``accel=accels1[:accels2[:...]]``
         This is used to enable an accelerator. Depending on the target
-        architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be
+        architecture, kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg can be
         available. By default, tcg is used. If there is more than one
         accelerator specified, the next one is used if the previous one
         fails to initialize.
@@ -228,7 +228,7 @@ ERST
 
 DEF("accel", HAS_ARG, QEMU_OPTION_accel,
     "-accel [accel=]accelerator[,prop[=value][,...]]\n"
-    "                select accelerator (kvm, xen, hvf, nvmm, whpx, mshv or tcg; use 'help' for a list)\n"
+    "                select accelerator (kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg; use 'help' for a list)\n"
     "                igd-passthru=on|off (enable Xen integrated Intel graphics passthrough, default=off)\n"
     "                kernel-irqchip=on|off|split controls accelerated irqchip support (default=on)\n"
     "                kvm-shadow-mem=size of KVM shadow MMU in bytes\n"
@@ -243,7 +243,7 @@ DEF("accel", HAS_ARG, QEMU_OPTION_accel,
 SRST
 ``-accel name[,prop=value[,...]]``
     This is used to enable an accelerator. Depending on the target
-    architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be available.
+    architecture, kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg can be available.
     By default, tcg is used. If there is more than one accelerator
     specified, the next one is used if the previous one fails to
     initialize.
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index e8edc5252a..ca5b113119 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -158,6 +158,7 @@ meson_options_help() {
   printf "%s\n" '  multiprocess    Out of process device emulation support'
   printf "%s\n" '  netmap          netmap network backend support'
   printf "%s\n" '  nettle          nettle cryptography support'
+  printf "%s\n" '  nitro           Nitro acceleration support'
   printf "%s\n" '  numa            libnuma support'
   printf "%s\n" '  nvmm            NVMM acceleration support'
   printf "%s\n" '  opengl          OpenGL support'
@@ -418,6 +419,8 @@ _meson_option_parse() {
     --disable-netmap) printf "%s" -Dnetmap=disabled ;;
     --enable-nettle) printf "%s" -Dnettle=enabled ;;
     --disable-nettle) printf "%s" -Dnettle=disabled ;;
+    --enable-nitro) printf "%s" -Dnitro=enabled ;;
+    --disable-nitro) printf "%s" -Dnitro=disabled ;;
     --enable-numa) printf "%s" -Dnuma=enabled ;;
     --disable-numa) printf "%s" -Dnuma=disabled ;;
     --enable-nvmm) printf "%s" -Dnvmm=enabled ;;
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 04/10] hw/nitro/nitro-serial-vsock: Nitro Enclaves vsock console
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (2 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 03/10] accel: Add Nitro Enclaves accelerator Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  1:51 ` [PATCH 05/10] hw/nitro: Introduce Nitro Enclave Heartbeat device Alexander Graf
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

Nitro Enclaves support a special "debug" mode. When in debug mode, the
Nitro Hypervisor provides a vsock port that the parent can connect to to
receive serial console output of the Enclave. Add a new nitro-serial-vsock
driver that implements short-circuit logic to establish the vsock
connection to that port and feed its data into a chardev, so that a machine
model can use it as serial device.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 MAINTAINERS                     |   6 ++
 hw/Kconfig                      |   1 +
 hw/meson.build                  |   1 +
 hw/nitro/Kconfig                |   3 +
 hw/nitro/meson.build            |   1 +
 hw/nitro/serial-vsock.c         | 154 ++++++++++++++++++++++++++++++++
 hw/nitro/trace-events           |   4 +
 hw/nitro/trace.h                |   4 +
 include/hw/nitro/serial-vsock.h |  26 ++++++
 meson.build                     |   1 +
 10 files changed, 201 insertions(+)
 create mode 100644 hw/nitro/Kconfig
 create mode 100644 hw/nitro/meson.build
 create mode 100644 hw/nitro/serial-vsock.c
 create mode 100644 hw/nitro/trace-events
 create mode 100644 hw/nitro/trace.h
 create mode 100644 include/hw/nitro/serial-vsock.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 3d002143ae..53ce075e9a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3022,6 +3022,12 @@ F: hw/vmapple/*
 F: include/hw/vmapple/*
 F: docs/system/arm/vmapple.rst
 
+Nitro Enclaves (native)
+M: Alexander Graf <graf@amazon.com>
+S: Maintained
+F: hw/nitro/
+F: include/hw/nitro/
+
 Subsystems
 ----------
 Overall Audio backends
diff --git a/hw/Kconfig b/hw/Kconfig
index f8f92b5d03..b3ce1520a6 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -22,6 +22,7 @@ source isa/Kconfig
 source mem/Kconfig
 source misc/Kconfig
 source net/Kconfig
+source nitro/Kconfig
 source nubus/Kconfig
 source nvme/Kconfig
 source nvram/Kconfig
diff --git a/hw/meson.build b/hw/meson.build
index 66e46b8090..36da5322f7 100644
--- a/hw/meson.build
+++ b/hw/meson.build
@@ -44,6 +44,7 @@ subdir('isa')
 subdir('mem')
 subdir('misc')
 subdir('net')
+subdir('nitro')
 subdir('nubus')
 subdir('nvme')
 subdir('nvram')
diff --git a/hw/nitro/Kconfig b/hw/nitro/Kconfig
new file mode 100644
index 0000000000..86c817c766
--- /dev/null
+++ b/hw/nitro/Kconfig
@@ -0,0 +1,3 @@
+config NITRO_SERIAL_VSOCK
+    bool
+    depends on NITRO
diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build
new file mode 100644
index 0000000000..d95ed8dd79
--- /dev/null
+++ b/hw/nitro/meson.build
@@ -0,0 +1 @@
+system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
diff --git a/hw/nitro/serial-vsock.c b/hw/nitro/serial-vsock.c
new file mode 100644
index 0000000000..12d6804a33
--- /dev/null
+++ b/hw/nitro/serial-vsock.c
@@ -0,0 +1,154 @@
+/*
+ * Nitro Enclave Vsock Serial
+ *
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors:
+ *   Alexander Graf <graf@amazon.com>
+ *
+ * With Nitro Enclaves in debug mode, the Nitro Hypervisor provides a vsock
+ * port that the parent can connect to to receive serial console output of
+ * the Enclave. This driver implements short-circuit logic to establish the
+ * vsock connection to that port and feed its data into a chardev, so that
+ * a machine model can use it as serial device.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+#include "hw/core/sysbus.h"
+#include "hw/nitro/serial-vsock.h"
+#include "trace.h"
+
+#define CONSOLE_PORT_START 10000
+#define VMADDR_CID_HYPERVISOR_STR "0"
+
+static int nitro_serial_vsock_can_read(void *opaque)
+{
+    NitroSerialVsockState *s = opaque;
+
+    /* Refuse vsock input until the output backend is ready */
+    return qemu_chr_fe_backend_open(&s->output) ? 4096 : 0;
+}
+
+static void nitro_serial_vsock_read(void *opaque, const uint8_t *buf, int size)
+{
+    NitroSerialVsockState *s = opaque;
+
+    /* Forward all vsock data to the output chardev */
+    qemu_chr_fe_write_all(&s->output, buf, size);
+}
+
+static void nitro_serial_vsock_event(void *opaque, QEMUChrEvent event)
+{
+    /* No need to action on connect/disconnect events, but trace for debug */
+    trace_nitro_serial_vsock_event(event);
+}
+
+static void nitro_serial_vsock_set_cid(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
+{
+    NitroSerialVsockState *s = NITRO_SERIAL_VSOCK(obj);
+    uint32_t cid, port;
+    g_autofree char *chardev_id = NULL;
+    Chardev *chr;
+    ChardevBackend *backend;
+    ChardevSocket *sock;
+
+    if (!visit_type_uint32(v, name, &cid, errp)) {
+        return;
+    }
+
+    s->cid = cid;
+    port = cid + CONSOLE_PORT_START;
+
+    /*
+     * We know the Enclave CID to connect to now. Create a vsock
+     * client chardev that connects to the Enclave's console.
+     */
+    chardev_id = g_strdup_printf("nitro-console-%u", cid);
+
+    backend = g_new0(ChardevBackend, 1);
+    backend->type = CHARDEV_BACKEND_KIND_SOCKET;
+    sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
+    sock->addr = g_new0(SocketAddressLegacy, 1);
+    sock->addr->type = SOCKET_ADDRESS_TYPE_VSOCK;
+    sock->addr->u.vsock.data = g_new0(VsockSocketAddress, 1);
+    sock->addr->u.vsock.data->cid = g_strdup(VMADDR_CID_HYPERVISOR_STR);
+    sock->addr->u.vsock.data->port = g_strdup_printf("%u", port);
+    sock->server = false;
+    sock->has_server = true;
+
+    chr = qemu_chardev_new(chardev_id, TYPE_CHARDEV_SOCKET,
+                           backend, NULL, errp);
+    if (!chr) {
+        return;
+    }
+
+    if (!qemu_chr_fe_init(&s->vsock, chr, errp)) {
+        return;
+    }
+
+    qemu_chr_fe_set_handlers(&s->vsock,
+                             nitro_serial_vsock_can_read,
+                             nitro_serial_vsock_read,
+                             nitro_serial_vsock_event,
+                             NULL, s, NULL, true);
+}
+
+static void nitro_serial_vsock_get_cid(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
+{
+    NitroSerialVsockState *s = NITRO_SERIAL_VSOCK(obj);
+    uint32_t cid = s->cid;
+
+    visit_type_uint32(v, name, &cid, errp);
+}
+
+static void nitro_serial_vsock_realize(DeviceState *dev, Error **errp)
+{
+    /*
+     * At realize we don't know the Enclave CID yet, because the nitro accel
+     * first needs to launch the Enclave. Delay creation of the connection
+     * until the nitro accel pushes the CID as QOM property.
+     */
+}
+
+static const Property nitro_serial_vsock_props[] = {
+    DEFINE_PROP_CHR("chardev", NitroSerialVsockState, output),
+};
+
+static void nitro_serial_vsock_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    dc->realize = nitro_serial_vsock_realize;
+    device_class_set_props(dc, nitro_serial_vsock_props);
+
+    object_class_property_add(oc, "enclave-cid", "uint32",
+                              nitro_serial_vsock_get_cid,
+                              nitro_serial_vsock_set_cid,
+                              NULL, NULL);
+}
+
+static const TypeInfo nitro_serial_vsock_info = {
+    .name = TYPE_NITRO_SERIAL_VSOCK,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NitroSerialVsockState),
+    .class_init = nitro_serial_vsock_class_init,
+};
+
+static void nitro_serial_vsock_register(void)
+{
+    type_register_static(&nitro_serial_vsock_info);
+}
+
+type_init(nitro_serial_vsock_register);
diff --git a/hw/nitro/trace-events b/hw/nitro/trace-events
new file mode 100644
index 0000000000..20617a024a
--- /dev/null
+++ b/hw/nitro/trace-events
@@ -0,0 +1,4 @@
+# See docs/devel/tracing.rst for syntax documentation.
+
+# serial-vsock.c
+nitro_serial_vsock_event(int event) "event %d"
diff --git a/hw/nitro/trace.h b/hw/nitro/trace.h
new file mode 100644
index 0000000000..b455d6c17b
--- /dev/null
+++ b/hw/nitro/trace.h
@@ -0,0 +1,4 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "trace/trace-hw_nitro.h"
diff --git a/include/hw/nitro/serial-vsock.h b/include/hw/nitro/serial-vsock.h
new file mode 100644
index 0000000000..92c9374eeb
--- /dev/null
+++ b/include/hw/nitro/serial-vsock.h
@@ -0,0 +1,26 @@
+/*
+ * Nitro Enclave Serial (vsock)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_CHAR_NITRO_SERIAL_VSOCK_H
+#define HW_CHAR_NITRO_SERIAL_VSOCK_H
+
+#include "hw/core/qdev.h"
+#include "hw/core/sysbus.h"
+#include "chardev/char-fe.h"
+#include "qom/object.h"
+
+#define TYPE_NITRO_SERIAL_VSOCK "nitro-serial-vsock"
+OBJECT_DECLARE_SIMPLE_TYPE(NitroSerialVsockState, NITRO_SERIAL_VSOCK)
+
+struct NitroSerialVsockState {
+    SysBusDevice parent_obj;
+
+    CharFrontend output;    /* chardev to write console output to */
+    CharFrontend vsock;     /* vsock chardev to enclave console */
+    uint32_t cid;
+};
+
+#endif /* HW_CHAR_NITRO_SERIAL_VSOCK_H */
diff --git a/meson.build b/meson.build
index bdeee65db2..3c6fa7a55a 100644
--- a/meson.build
+++ b/meson.build
@@ -3634,6 +3634,7 @@ if have_system
     'hw/misc/macio',
     'hw/net',
     'hw/net/can',
+    'hw/nitro',
     'hw/nubus',
     'hw/nvme',
     'hw/nvram',
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 05/10] hw/nitro: Introduce Nitro Enclave Heartbeat device
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (3 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 04/10] hw/nitro/nitro-serial-vsock: Nitro Enclaves vsock console Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  1:51 ` [PATCH 06/10] target/arm/cpu64: Allow -host for nitro Alexander Graf
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

Nitro Enclaves expect the parent instance to host a vsock heartbeat listener
at port 9000. To host a Nitro Enclave with the nitro accel in QEMU, add
such a heartbeat listener as device model, so that the machine can
easily instantiate it.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 hw/nitro/Kconfig             |   4 ++
 hw/nitro/heartbeat.c         | 115 +++++++++++++++++++++++++++++++++++
 hw/nitro/meson.build         |   1 +
 hw/nitro/trace-events        |   4 ++
 include/hw/nitro/heartbeat.h |  25 ++++++++
 5 files changed, 149 insertions(+)
 create mode 100644 hw/nitro/heartbeat.c
 create mode 100644 include/hw/nitro/heartbeat.h

diff --git a/hw/nitro/Kconfig b/hw/nitro/Kconfig
index 86c817c766..6fe050d35d 100644
--- a/hw/nitro/Kconfig
+++ b/hw/nitro/Kconfig
@@ -1,3 +1,7 @@
 config NITRO_SERIAL_VSOCK
     bool
     depends on NITRO
+
+config NITRO_HEARTBEAT
+    bool
+    depends on NITRO
diff --git a/hw/nitro/heartbeat.c b/hw/nitro/heartbeat.c
new file mode 100644
index 0000000000..3d2a371098
--- /dev/null
+++ b/hw/nitro/heartbeat.c
@@ -0,0 +1,115 @@
+/*
+ * Nitro Enclave Heartbeat device
+ *
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors:
+ *   Alexander Graf <graf@amazon.com>
+ *
+ * The Nitro Enclave init process sends a heartbeat byte (0xB7) to
+ * CID 3 (parent) port 9000 on boot to signal it reached initramfs.
+ * The parent must accept the connection, read the byte, and echo it
+ * back. If the enclave init cannot reach the listener, it exits.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "hw/nitro/heartbeat.h"
+#include "trace.h"
+
+#define HEARTBEAT_PORT      9000
+#define VMADDR_CID_ANY_STR  "4294967295"
+
+static int nitro_heartbeat_can_read(void *opaque)
+{
+    NitroHeartbeatState *s = opaque;
+
+    /* One-shot protocol: stop reading after the first heartbeat */
+    return s->done ? 0 : 1;
+}
+
+static void nitro_heartbeat_read(void *opaque, const uint8_t *buf, int size)
+{
+    NitroHeartbeatState *s = opaque;
+
+    if (s->done || size < 1) {
+        return;
+    }
+
+    /* Echo the heartbeat byte back and disconnect */
+    qemu_chr_fe_write_all(&s->vsock, buf, 1);
+    s->done = true;
+    qemu_chr_fe_deinit(&s->vsock, true);
+
+    trace_nitro_heartbeat_done();
+}
+
+static void nitro_heartbeat_event(void *opaque, QEMUChrEvent event)
+{
+    trace_nitro_heartbeat_event(event);
+}
+
+static void nitro_heartbeat_realize(DeviceState *dev, Error **errp)
+{
+    NitroHeartbeatState *s = NITRO_HEARTBEAT(dev);
+    g_autofree char *chardev_id = NULL;
+    Chardev *chr;
+    ChardevBackend *backend;
+    ChardevSocket *sock;
+
+    chardev_id = g_strdup_printf("nitro-heartbeat");
+
+    backend = g_new0(ChardevBackend, 1);
+    backend->type = CHARDEV_BACKEND_KIND_SOCKET;
+    sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
+    sock->addr = g_new0(SocketAddressLegacy, 1);
+    sock->addr->type = SOCKET_ADDRESS_TYPE_VSOCK;
+    sock->addr->u.vsock.data = g_new0(VsockSocketAddress, 1);
+    sock->addr->u.vsock.data->cid = g_strdup(VMADDR_CID_ANY_STR);
+    sock->addr->u.vsock.data->port = g_strdup_printf("%u", HEARTBEAT_PORT);
+    sock->server = true;
+    sock->has_server = true;
+    sock->wait = false;
+    sock->has_wait = true;
+
+    chr = qemu_chardev_new(chardev_id, TYPE_CHARDEV_SOCKET,
+                           backend, NULL, errp);
+    if (!chr) {
+        return;
+    }
+
+    if (!qemu_chr_fe_init(&s->vsock, chr, errp)) {
+        return;
+    }
+
+    qemu_chr_fe_set_handlers(&s->vsock,
+                             nitro_heartbeat_can_read,
+                             nitro_heartbeat_read,
+                             nitro_heartbeat_event,
+                             NULL, s, NULL, true);
+}
+
+static void nitro_heartbeat_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = nitro_heartbeat_realize;
+}
+
+static const TypeInfo nitro_heartbeat_info = {
+    .name = TYPE_NITRO_HEARTBEAT,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NitroHeartbeatState),
+    .class_init = nitro_heartbeat_class_init,
+};
+
+static void nitro_heartbeat_register(void)
+{
+    type_register_static(&nitro_heartbeat_info);
+}
+
+type_init(nitro_heartbeat_register);
diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build
index d95ed8dd79..b921da2b97 100644
--- a/hw/nitro/meson.build
+++ b/hw/nitro/meson.build
@@ -1 +1,2 @@
 system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
+system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c'))
diff --git a/hw/nitro/trace-events b/hw/nitro/trace-events
index 20617a024a..311ab78e69 100644
--- a/hw/nitro/trace-events
+++ b/hw/nitro/trace-events
@@ -2,3 +2,7 @@
 
 # serial-vsock.c
 nitro_serial_vsock_event(int event) "event %d"
+
+# heartbeat.c
+nitro_heartbeat_event(int event) "event %d"
+nitro_heartbeat_done(void) "enclave heartbeat received"
diff --git a/include/hw/nitro/heartbeat.h b/include/hw/nitro/heartbeat.h
new file mode 100644
index 0000000000..3ed4d40bac
--- /dev/null
+++ b/include/hw/nitro/heartbeat.h
@@ -0,0 +1,25 @@
+/*
+ * Nitro Heartbeat device
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_NITRO_HEARTBEAT_H
+#define HW_MISC_NITRO_HEARTBEAT_H
+
+#include "hw/core/qdev.h"
+#include "hw/core/sysbus.h"
+#include "chardev/char-fe.h"
+#include "qom/object.h"
+
+#define TYPE_NITRO_HEARTBEAT "nitro-heartbeat"
+OBJECT_DECLARE_SIMPLE_TYPE(NitroHeartbeatState, NITRO_HEARTBEAT)
+
+struct NitroHeartbeatState {
+    SysBusDevice parent_obj;
+
+    CharFrontend vsock;     /* vsock server chardev for heartbeat */
+    bool done;
+};
+
+#endif /* HW_MISC_NITRO_HEARTBEAT_H */
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 06/10] target/arm/cpu64: Allow -host for nitro
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (4 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 05/10] hw/nitro: Introduce Nitro Enclave Heartbeat device Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  1:51 ` [PATCH 07/10] hw/nitro: Add nitro machine Alexander Graf
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

The nitro accel does not actually make use of CPU emulation or details:
It always uses the host CPU regardless of configuration. Machines for
the nitro accel select the host CPU type as default to have a clear
statement of the above and to have a unified cpu type across all
supported architectures.

The arm64 logic on Linux currently only allows -cpu host for KVM based
virtual machines. Add a special case for nitro so that when the nitro
accel is active, it allows use of the host cpu type.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 target/arm/cpu64.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 5d7c6b7fbb..c01051f038 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -765,6 +765,14 @@ static void aarch64_a53_initfn(Object *obj)
 static void aarch64_host_initfn(Object *obj)
 {
     ARMCPU *cpu = ARM_CPU(obj);
+
+#if defined(CONFIG_NITRO)
+    if (nitro_enabled()) {
+        /* The nitro accel uses -cpu host, but does not actually consume it */
+        return;
+    }
+#endif
+
 #if defined(CONFIG_KVM)
     kvm_arm_set_cpu_features_from_host(cpu);
     if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 07/10] hw/nitro: Add nitro machine
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (5 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 06/10] target/arm/cpu64: Allow -host for nitro Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  3:27   ` Mohamed Mediouni
  2026-02-20 14:59   ` Michael S. Tsirkin
  2026-02-18  1:51 ` [PATCH 08/10] hw/core/eif: Move definitions to header Alexander Graf
                   ` (2 subsequent siblings)
  9 siblings, 2 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

Add a machine model to spawn a Nitro Enclave. Unlike the existing -M
nitro-enclave, this machine model works exclusively with the -accel
nitro accelerator to drive real Nitro Enclave creation. It supports
memory allocation, number of CPU selection, both x86_64 as well as
aarch64, implements the Enclave heartbeat logic and debug serial
console.

To use it, create an EIF file and run

  $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
                       -kernel test.eif

or

  $ qemu-system-aarch64 -accel nitro,debug-mode=on -M nitro -nographic \
                       -kernel test.eif

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 hw/nitro/Kconfig           |   7 ++
 hw/nitro/machine.c         | 180 +++++++++++++++++++++++++++++++++++++
 hw/nitro/meson.build       |   1 +
 include/hw/nitro/machine.h |  20 +++++
 4 files changed, 208 insertions(+)
 create mode 100644 hw/nitro/machine.c
 create mode 100644 include/hw/nitro/machine.h

diff --git a/hw/nitro/Kconfig b/hw/nitro/Kconfig
index 6fe050d35d..910068c23c 100644
--- a/hw/nitro/Kconfig
+++ b/hw/nitro/Kconfig
@@ -5,3 +5,10 @@ config NITRO_SERIAL_VSOCK
 config NITRO_HEARTBEAT
     bool
     depends on NITRO
+
+config NITRO_MACHINE
+    bool
+    default y
+    depends on NITRO
+    select NITRO_HEARTBEAT
+    select NITRO_SERIAL_VSOCK
diff --git a/hw/nitro/machine.c b/hw/nitro/machine.c
new file mode 100644
index 0000000000..197adfbdb5
--- /dev/null
+++ b/hw/nitro/machine.c
@@ -0,0 +1,180 @@
+/*
+ * Nitro Enclaves (accel) machine
+ *
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Authors:
+ *   Alexander Graf <graf@amazon.com>
+ *
+ * Nitro Enclaves machine model for -accel nitro. This machine behaves
+ * like the nitro-enclave machine, but uses the real Nitro Enclaves
+ * backend to launch the virtual machine. It requires use of the -accel
+ * nitro.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qom/object_interfaces.h"
+#include "chardev/char.h"
+#include "hw/core/boards.h"
+#include "hw/core/cpu.h"
+#include "hw/core/sysbus.h"
+#include "hw/core/qdev-properties-system.h"
+#include "hw/nitro/heartbeat.h"
+#include "hw/nitro/machine.h"
+#include "hw/nitro/serial-vsock.h"
+#include "system/address-spaces.h"
+#include "system/hostmem.h"
+#include "system/system.h"
+#include "system/nitro-accel.h"
+#include "qemu/accel.h"
+#include "hw/arm/machines-qom.h"
+
+#define EIF_LOAD_ADDR   (8 * 1024 * 1024)
+
+static void nitro_create_cpu(const char *cpu_type, int index)
+{
+    Object *obj = object_new(cpu_type);
+
+    /* x86 CPUs require an apic-id before realize */
+    if (object_property_find(obj, "apic-id")) {
+        object_property_set_int(obj, "apic-id", index, &error_fatal);
+    }
+
+    qdev_realize(DEVICE(obj), NULL, &error_fatal);
+}
+
+static void nitro_machine_init(MachineState *machine)
+{
+    const char *eif_path = machine->kernel_filename;
+    const char *cpu_type = machine->cpu_type;
+    g_autofree char *eif_data = NULL;
+    gsize eif_size;
+    int i;
+
+    if (!nitro_enabled()) {
+        error_report("The 'nitro' machine requires -accel nitro");
+        exit(1);
+    }
+
+    if (!cpu_type) {
+        ObjectClass *oc = cpu_class_by_name(target_cpu_type(), "host");
+
+        if (!oc) {
+            error_report("nitro: no 'host' CPU available");
+            exit(1);
+        }
+        cpu_type = object_class_get_name(oc);
+    }
+
+    if (!eif_path) {
+        error_report("nitro: -kernel <eif-file> is required");
+        exit(1);
+    }
+
+    /* Expose memory as normal QEMU RAM. Needs to be huge page backed. */
+    memory_region_add_subregion(get_system_memory(), 0, machine->ram);
+
+    /*
+     * Load EIF (-kernel) as raw blob at the EIF_LOAD_ADDR into guest RAM.
+     * The Nitro Hypervisor will extract its contents and bootstrap the
+     * Enclave from it.
+     */
+    if (!g_file_get_contents(eif_path, &eif_data, &eif_size, NULL)) {
+        error_report("nitro: failed to read EIF '%s'", eif_path);
+        exit(1);
+    }
+    address_space_write(&address_space_memory, EIF_LOAD_ADDR,
+                        MEMTXATTRS_UNSPECIFIED, eif_data, eif_size);
+
+    /* Nitro Enclaves require a heartbeat device. Provide one. */
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(qdev_new(TYPE_NITRO_HEARTBEAT)),
+                             &error_fatal);
+
+    /*
+     * In debug mode, Nitro Enclaves expose the guest's serial output via
+     * vsock. When the accel is in debug mode, wire the vsock serial to
+     * the machine's serial port so that -nographic automatically works
+     */
+    if (object_property_get_bool(OBJECT(current_accel()), "debug-mode", NULL)) {
+        Chardev *chr = serial_hd(0);
+
+        if (chr) {
+            DeviceState *dev = qdev_new(TYPE_NITRO_SERIAL_VSOCK);
+
+            qdev_prop_set_chr(dev, "chardev", chr);
+            sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+        }
+    }
+
+    /*
+     * Spawn vCPUs. While the real Nitro Enclaves CPUs are owned by the
+     * underlying hypervisor, we still want to maintain a local view of
+     * them to trigger VM creation when vCPU 0 starts and to give us an
+     * object to interact with.
+     */
+    for (i = 0; i < machine->smp.cpus; i++) {
+        nitro_create_cpu(cpu_type, i);
+    }
+}
+
+static bool nitro_create_memfd_backend(MachineState *ms, const char *path,
+                                       Error **errp)
+{
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    Object *root = object_get_objects_root();
+    Object *obj;
+    bool r = false;
+
+    obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
+
+    /* Nitro Enclaves require huge page backing */
+    if (!object_property_set_int(obj, "size", ms->ram_size, errp) ||
+        !object_property_set_bool(obj, "hugetlb", true, errp)) {
+        goto out;
+    }
+
+    object_property_add_child(root, mc->default_ram_id, obj);
+
+    if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
+        goto out;
+    }
+    r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
+
+out:
+    object_unref(obj);
+    return r;
+}
+
+static void nitro_machine_class_init(ObjectClass *oc, const void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->desc = "Nitro Enclave";
+    mc->init = nitro_machine_init;
+    mc->create_default_memdev = nitro_create_memfd_backend;
+    mc->default_ram_id = "ram";
+    mc->max_cpus = 4096;
+}
+
+static const TypeInfo nitro_machine_info = {
+    .name = TYPE_NITRO_MACHINE,
+    .parent = TYPE_MACHINE,
+    .instance_size = sizeof(NitroMachineState),
+    .class_init = nitro_machine_class_init,
+    .interfaces = (const InterfaceInfo[]) {
+        /* x86_64 and aarch64 only */
+        { TYPE_TARGET_AARCH64_MACHINE },
+        { }
+    },
+};
+
+static void nitro_machine_register(void)
+{
+    type_register_static(&nitro_machine_info);
+}
+
+type_init(nitro_machine_register);
diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build
index b921da2b97..813f5a9c87 100644
--- a/hw/nitro/meson.build
+++ b/hw/nitro/meson.build
@@ -1,2 +1,3 @@
 system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
 system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c'))
+system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: files('machine.c'))
diff --git a/include/hw/nitro/machine.h b/include/hw/nitro/machine.h
new file mode 100644
index 0000000000..d78ba7d6dc
--- /dev/null
+++ b/include/hw/nitro/machine.h
@@ -0,0 +1,20 @@
+/*
+ * Nitro Enclaves (accel) machine
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_NITRO_MACHINE_H
+#define HW_NITRO_MACHINE_H
+
+#include "hw/core/boards.h"
+#include "qom/object.h"
+
+#define TYPE_NITRO_MACHINE MACHINE_TYPE_NAME("nitro")
+OBJECT_DECLARE_SIMPLE_TYPE(NitroMachineState, NITRO_MACHINE)
+
+struct NitroMachineState {
+    MachineState parent;
+};
+
+#endif /* HW_NITRO_MACHINE_H */
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 08/10] hw/core/eif: Move definitions to header
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (6 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 07/10] hw/nitro: Add nitro machine Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18 15:12   ` Dorjoy Chowdhury
  2026-02-18  1:51 ` [PATCH 09/10] hw/nitro: Enable direct kernel boot Alexander Graf
  2026-02-18  1:51 ` [PATCH 10/10] docs: Add Nitro Enclaves documentation Alexander Graf
  9 siblings, 1 reply; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

In follow-up patches we need some EIF file definitions that are
currently in the eif.c file, but want to access them from a separate
device. Move them into the header instead.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 hw/core/eif.c | 38 --------------------------------------
 hw/core/eif.h | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/hw/core/eif.c b/hw/core/eif.c
index 513caec6b4..96f1d76578 100644
--- a/hw/core/eif.c
+++ b/hw/core/eif.c
@@ -18,44 +18,6 @@
 
 #include "hw/core/eif.h"
 
-#define MAX_SECTIONS 32
-
-/* members are ordered according to field order in .eif file */
-typedef struct EifHeader {
-    uint8_t  magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */
-    uint16_t version;
-    uint16_t flags;
-    uint64_t default_memory;
-    uint64_t default_cpus;
-    uint16_t reserved;
-    uint16_t section_cnt;
-    uint64_t section_offsets[MAX_SECTIONS];
-    uint64_t section_sizes[MAX_SECTIONS];
-    uint32_t unused;
-    uint32_t eif_crc32;
-} QEMU_PACKED EifHeader;
-
-/* members are ordered according to field order in .eif file */
-typedef struct EifSectionHeader {
-    /*
-     * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature,
-     * 5 = metadata
-     */
-    uint16_t section_type;
-    uint16_t flags;
-    uint64_t section_size;
-} QEMU_PACKED EifSectionHeader;
-
-enum EifSectionTypes {
-    EIF_SECTION_INVALID = 0,
-    EIF_SECTION_KERNEL = 1,
-    EIF_SECTION_CMDLINE = 2,
-    EIF_SECTION_RAMDISK = 3,
-    EIF_SECTION_SIGNATURE = 4,
-    EIF_SECTION_METADATA = 5,
-    EIF_SECTION_MAX = 6,
-};
-
 static const char *section_type_to_string(uint16_t type)
 {
     const char *str;
diff --git a/hw/core/eif.h b/hw/core/eif.h
index fed3cb5514..a3412377a9 100644
--- a/hw/core/eif.h
+++ b/hw/core/eif.h
@@ -11,6 +11,44 @@
 #ifndef HW_CORE_EIF_H
 #define HW_CORE_EIF_H
 
+#define MAX_SECTIONS 32
+
+/* members are ordered according to field order in .eif file */
+typedef struct EifHeader {
+    uint8_t  magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */
+    uint16_t version;
+    uint16_t flags;
+    uint64_t default_memory;
+    uint64_t default_cpus;
+    uint16_t reserved;
+    uint16_t section_cnt;
+    uint64_t section_offsets[MAX_SECTIONS];
+    uint64_t section_sizes[MAX_SECTIONS];
+    uint32_t unused;
+    uint32_t eif_crc32;
+} QEMU_PACKED EifHeader;
+
+/* members are ordered according to field order in .eif file */
+typedef struct EifSectionHeader {
+    /*
+     * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature,
+     * 5 = metadata
+     */
+    uint16_t section_type;
+    uint16_t flags;
+    uint64_t section_size;
+} QEMU_PACKED EifSectionHeader;
+
+enum EifSectionTypes {
+    EIF_SECTION_INVALID = 0,
+    EIF_SECTION_KERNEL = 1,
+    EIF_SECTION_CMDLINE = 2,
+    EIF_SECTION_RAMDISK = 3,
+    EIF_SECTION_SIGNATURE = 4,
+    EIF_SECTION_METADATA = 5,
+    EIF_SECTION_MAX = 6,
+};
+
 bool read_eif_file(const char *eif_path, const char *machine_initrd,
                    char **kernel_path, char **initrd_path,
                    char **kernel_cmdline, uint8_t *image_sha384,
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 09/10] hw/nitro: Enable direct kernel boot
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (7 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 08/10] hw/core/eif: Move definitions to header Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-18  1:51 ` [PATCH 10/10] docs: Add Nitro Enclaves documentation Alexander Graf
  9 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

Nitro Enclaves can only boot EIF files which are a combination of
kernel, initramfs and cmdline in a single file. When the kernel image is
not an EIF, treat it like a kernel image and assemble an EIF image on
the fly. This way, users can call QEMU with a direct
kernel/initrd/cmdline combination and everything "just works".

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 hw/core/eif.h        |   3 ++
 hw/nitro/machine.c   | 116 +++++++++++++++++++++++++++++++++++++++++++
 hw/nitro/meson.build |   2 +-
 3 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/hw/core/eif.h b/hw/core/eif.h
index a3412377a9..0c432dbc2d 100644
--- a/hw/core/eif.h
+++ b/hw/core/eif.h
@@ -12,6 +12,7 @@
 #define HW_CORE_EIF_H
 
 #define MAX_SECTIONS 32
+#define EIF_HDR_ARCH_ARM64 0x1
 
 /* members are ordered according to field order in .eif file */
 typedef struct EifHeader {
@@ -49,6 +50,8 @@ enum EifSectionTypes {
     EIF_SECTION_MAX = 6,
 };
 
+#define EIF_MAGIC { '.', 'e', 'i', 'f' }
+
 bool read_eif_file(const char *eif_path, const char *machine_initrd,
                    char **kernel_path, char **initrd_path,
                    char **kernel_cmdline, uint8_t *image_sha384,
diff --git a/hw/nitro/machine.c b/hw/nitro/machine.c
index 197adfbdb5..33b0749288 100644
--- a/hw/nitro/machine.c
+++ b/hw/nitro/machine.c
@@ -32,9 +32,104 @@
 #include "system/nitro-accel.h"
 #include "qemu/accel.h"
 #include "hw/arm/machines-qom.h"
+#include "hw/core/eif.h"
+#include <zlib.h> /* for crc32 */
 
 #define EIF_LOAD_ADDR   (8 * 1024 * 1024)
 
+static bool is_eif(char *eif, gsize len)
+{
+    const char eif_magic[] = EIF_MAGIC;
+
+    return len >= sizeof(eif_magic) &&
+           !memcmp(eif, eif_magic, sizeof(eif_magic));
+}
+
+static void build_eif_section(EifHeader *hdr, GByteArray *buf, uint16_t type,
+                              const char *data, uint64_t size)
+{
+    uint16_t section = be16_to_cpu(hdr->section_cnt);
+    EifSectionHeader shdr = {
+        .section_type = cpu_to_be16(type),
+        .flags = 0,
+        .section_size = cpu_to_be64(size),
+    };
+
+    hdr->section_offsets[section] = cpu_to_be64(buf->len);
+    hdr->section_sizes[section] = cpu_to_be64(size);
+
+    g_byte_array_append(buf, (const uint8_t *)&shdr, sizeof(shdr));
+    if (size) {
+        g_byte_array_append(buf, (const uint8_t *)data, size);
+    }
+
+    hdr->section_cnt = cpu_to_be16(section + 1);
+}
+
+/*
+ * Nitro Enclaves only support loading EIF files. When the user provides
+ * a Linux kernel, initrd and cmdline, convert them into EIF format.
+ */
+static char *build_eif(const char *kernel_data, gsize kernel_size,
+                       const char *initrd_path, const char *cmdline,
+                       gsize *out_size, Error **errp)
+{
+    g_autofree char *initrd_data = NULL;
+    static const char metadata[] = "{}";
+    size_t metadata_len = sizeof(metadata) - 1;
+    gsize initrd_size = 0;
+    GByteArray *buf;
+    EifHeader hdr;
+    uint32_t crc = 0;
+    size_t cmdline_len;
+
+    if (initrd_path) {
+        if (!g_file_get_contents(initrd_path, &initrd_data,
+                                 &initrd_size, NULL)) {
+            error_setg(errp, "Failed to read initrd '%s'", initrd_path);
+            return NULL;
+        }
+    }
+
+    buf = g_byte_array_new();
+
+    cmdline_len = cmdline ? strlen(cmdline) : 0;
+
+    hdr = (EifHeader) {
+        .magic = EIF_MAGIC,
+        .version = cpu_to_be16(4),
+        .flags = cpu_to_be16(target_aarch64() ? EIF_HDR_ARCH_ARM64 : 0),
+    };
+
+    g_byte_array_append(buf, (const uint8_t *)&hdr, sizeof(hdr));
+
+    /* Kernel */
+    build_eif_section(&hdr, buf, EIF_SECTION_KERNEL, kernel_data, kernel_size);
+
+    /* Command line */
+    build_eif_section(&hdr, buf, EIF_SECTION_CMDLINE, cmdline, cmdline_len);
+
+    /* Initramfs */
+    build_eif_section(&hdr, buf, EIF_SECTION_RAMDISK, initrd_data, initrd_size);
+
+    /* Metadata */
+    build_eif_section(&hdr, buf, EIF_SECTION_METADATA, metadata, metadata_len);
+
+    /*
+     * Patch the header into the buffer first (with real section offsets
+     * and sizes), then compute CRC over everything except the CRC field.
+     */
+    memcpy(buf->data, &hdr, sizeof(hdr));
+    crc = crc32(crc, buf->data, offsetof(EifHeader, eif_crc32));
+    crc = crc32(crc, &buf->data[sizeof(hdr)], buf->len - sizeof(hdr));
+
+    /* Finally write the CRC into the in-buffer header */
+    ((EifHeader *)buf->data)->eif_crc32 = cpu_to_be32(crc);
+
+    *out_size = buf->len;
+    return (char *)g_byte_array_free(buf, false);
+}
+
 static void nitro_create_cpu(const char *cpu_type, int index)
 {
     Object *obj = object_new(cpu_type);
@@ -87,6 +182,27 @@ static void nitro_machine_init(MachineState *machine)
         error_report("nitro: failed to read EIF '%s'", eif_path);
         exit(1);
     }
+
+    if (!is_eif(eif_data, eif_size)) {
+        char *kernel_data = eif_data;
+        gsize kernel_size = eif_size;
+        Error *err = NULL;
+
+        /*
+         * The user gave us a non-EIF kernel, likely a Linux kernel image.
+         * Assemble an EIF file from it, the -initrd and the -append arguments,
+         * so that users can perform a natural direct kernel boot.
+         */
+        eif_data = build_eif(kernel_data, kernel_size, machine->initrd_filename,
+                             machine->kernel_cmdline, &eif_size, &err);
+        if (!eif_data) {
+            error_report_err(err);
+            exit(1);
+        }
+
+        g_free(kernel_data);
+    }
+
     address_space_write(&address_space_memory, EIF_LOAD_ADDR,
                         MEMTXATTRS_UNSPECIFIED, eif_data, eif_size);
 
diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build
index 813f5a9c87..7b23f71d5a 100644
--- a/hw/nitro/meson.build
+++ b/hw/nitro/meson.build
@@ -1,3 +1,3 @@
 system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
 system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c'))
-system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: files('machine.c'))
+system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: [files('machine.c'), zlib])
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 10/10] docs: Add Nitro Enclaves documentation
  2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
                   ` (8 preceding siblings ...)
  2026-02-18  1:51 ` [PATCH 09/10] hw/nitro: Enable direct kernel boot Alexander Graf
@ 2026-02-18  1:51 ` Alexander Graf
  2026-02-24 10:26   ` Paolo Bonzini
  9 siblings, 1 reply; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  1:51 UTC (permalink / raw)
  To: qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

Now that all pieces are in place to spawn Nitro Enclaves using
a special purpose accelerator and machine model, document how
to use it.

Signed-off-by: Alexander Graf <graf@amazon.com>
---
 MAINTAINERS                                |   1 +
 docs/system/confidential-guest-support.rst |   1 +
 docs/system/index.rst                      |   1 +
 docs/system/nitro.rst                      | 128 +++++++++++++++++++++
 4 files changed, 131 insertions(+)
 create mode 100644 docs/system/nitro.rst

diff --git a/MAINTAINERS b/MAINTAINERS
index 53ce075e9a..5e9e429530 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3027,6 +3027,7 @@ M: Alexander Graf <graf@amazon.com>
 S: Maintained
 F: hw/nitro/
 F: include/hw/nitro/
+F: docs/system/nitro.rst
 
 Subsystems
 ----------
diff --git a/docs/system/confidential-guest-support.rst b/docs/system/confidential-guest-support.rst
index 66129fbab6..562a7c3c28 100644
--- a/docs/system/confidential-guest-support.rst
+++ b/docs/system/confidential-guest-support.rst
@@ -41,5 +41,6 @@ Currently supported confidential guest mechanisms are:
 * Intel Trust Domain Extension (TDX) (see :doc:`i386/tdx`)
 * 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`)
 
 Other mechanisms may be supported in future.
diff --git a/docs/system/index.rst b/docs/system/index.rst
index 427b020483..d297a95282 100644
--- a/docs/system/index.rst
+++ b/docs/system/index.rst
@@ -39,5 +39,6 @@ or Hypervisor.Framework.
    multi-process
    confidential-guest-support
    igvm
+   nitro
    vm-templating
    sriov
diff --git a/docs/system/nitro.rst b/docs/system/nitro.rst
new file mode 100644
index 0000000000..ebf118a108
--- /dev/null
+++ b/docs/system/nitro.rst
@@ -0,0 +1,128 @@
+AWS Nitro Enclaves
+==================
+
+`AWS Nitro Enclaves <https://aws.amazon.com/ec2/nitro/nitro-enclaves/>`_
+are isolated compute environments that run alongside EC2 instances.
+They are created by partitioning CPU and memory resources from a parent
+instance and launching a signed Enclave Image Format (EIF) file inside
+a confidential VM managed by the Nitro Hypervisor.
+
+QEMU supports launching Nitro Enclaves on EC2 instances that have
+enclave support enabled, using the ``nitro`` accelerator and the
+``nitro`` machine type.
+
+Prerequisites
+-------------
+
+* An EC2 instance with Nitro Enclaves enabled
+* The ``nitro_enclaves`` kernel module loaded (provides ``/dev/nitro_enclaves``)
+* CPU cores allocated to the Nitro Enclaves pool via ``nitro-enclaves-allocator``
+* Huge pages allocated for Nitro Enclaves via ``nitro-enclaves-allocator``
+
+Quick Start
+-----------
+
+Launch a Nitro Enclave from a pre-built EIF file::
+
+    $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
+        -smp 2 -m 512M -kernel enclave.eif
+
+Launch an enclave from individual kernel and initrd files::
+
+    $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
+        -smp 2 -m 512M -kernel vmlinuz -initrd initrd.cpio \
+        -append "console=ttyS0"
+
+The same commands work with ``qemu-system-aarch64`` on Graviton based EC2
+instances.
+
+Accelerator
+-----------
+
+The ``nitro`` accelerator (``-accel nitro``) drives the
+``/dev/nitro_enclaves`` device to create and manage a Nitro Enclave.
+It handles:
+
+* Creating the enclave VM slot
+* Donating memory regions (must be huge page backed)
+* Adding vCPUs (must be full physical cores)
+* Starting the enclave
+* Forwarding the enclave CID to devices that need it
+
+Accelerator options:
+
+``debug-mode=on|off``
+    Enable debug mode. When enabled, the Nitro Hypervisor exposes the
+    enclave's serial console output via a vsock port that the machine
+    model automatically connects to. In debug mode, PCR values are zero.
+    Default is ``off``.
+
+Machine
+-------
+
+The ``nitro`` machine (``-M nitro``) is a minimal, architecture-independent
+machine that provides only what a Nitro Enclave needs:
+
+* RAM (huge page backed via memfd)
+* vCPUs (defaults to ``host`` CPU type)
+* A heartbeat device (vsock server on port 9000)
+* A serial console bridge (vsock client, debug mode only)
+
+Communication to the Nitro Enclave is limited to virtio-vsock. The Enclave
+is allocated a CID at launch at which it is reachable. The CID is available
+as QOM property in the accelerator and as a trace event.
+
+EIF Image Format
+^^^^^^^^^^^^^^^^
+
+Nitro Enclaves boot from EIF (Enclave Image Format) files. When
+``-kernel`` points to an EIF file (detected by the ``.eif`` magic
+bytes), it is loaded directly into guest memory.
+
+When ``-kernel`` points to a regular kernel image (e.g. a bzImage or
+Image), the machine automatically assembles a minimal EIF on the fly
+from ``-kernel``, ``-initrd``, and ``-append``. This allows standard
+direct kernel boot without external EIF tooling.
+
+CPU Requirements
+^^^^^^^^^^^^^^^^
+
+Nitro Enclaves require full physical CPU cores. On hyperthreaded
+systems, this means ``-smp`` must be a multiple of the threads per
+core (typically 2).
+
+Nitro Enclaves can only consume cores that are donated to the Nitro Enclave
+CPU pool. You can configure the CPU pool using the ``nitro-enclaves-allocator``
+tool or manually by writing to the nitro_enclaves cpu pool parameter. To
+allocate vCPUs 1, 2 and 3, you can call::
+
+  $ echo 1,2,3 | sudo tee /sys/module/nitro_enclaves/parameters/ne_cpus
+
+Beware that on x86-64 systems, hyperthread siblings are not consecutive
+and must be added in pairs to the pool. Consult tools like ``lstopo``
+or ``lscpu`` for details about your instance's CPU topology.
+
+Memory Requirements
+^^^^^^^^^^^^^^^^^^^
+
+Enclave memory must be huge page backed. The machine automatically
+creates a memfd memory backend with huge pages enabled. To make the
+huge page allocation work, ensure that huge pages are reserved in
+the system. To reserve 1 GiB of memory on a 4 KiB PAGE_SIZE system,
+you can call::
+
+    $ echo 512 | sudo tee /proc/sys/vm/nr_hugepages
+
+Emulated Nitro Enclaves
+-----------------------
+
+In addition to the native Nitro Enclaves invocation, you can also use
+the emulated nitro-enclave machine target (see :doc:`i386/nitro-enclave`)
+which implements the x86 Nitro Enclave device model. While -M nitro
+delegates virtual machine device emulation to the Nitro Hypervisor, -M
+nitro-enclave implements all devices itself, which means it also works
+on non-EC2 instances.
+
+If you require NSM based attestation backed by valid AWS certificates,
+you must use -M nitro. The -M nitro-enclave model does not provide
+you with an AWS signed attestation document.
-- 
2.47.1




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 07/10] hw/nitro: Add nitro machine
  2026-02-18  1:51 ` [PATCH 07/10] hw/nitro: Add nitro machine Alexander Graf
@ 2026-02-18  3:27   ` Mohamed Mediouni
  2026-02-18  9:20     ` Alexander Graf
  2026-02-20 14:59   ` Michael S. Tsirkin
  1 sibling, 1 reply; 19+ messages in thread
From: Mohamed Mediouni @ 2026-02-18  3:27 UTC (permalink / raw)
  To: Alexander Graf
  Cc: qemu-devel, qemu-arm, Peter Maydell, Thomas Huth, alex.bennee,
	philmd, berrange, marcandre.lureau, Cornelia Huck, mst,
	Dorjoy Chowdhury, Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli,
	mknaust, nh-open-source



> On 18. Feb 2026, at 02:51, Alexander Graf <graf@amazon.com> wrote:
> 
> Add a machine model to spawn a Nitro Enclave. Unlike the existing -M
> nitro-enclave, this machine model works exclusively with the -accel
> nitro accelerator to drive real Nitro Enclave creation. It supports
> memory allocation, number of CPU selection, both x86_64 as well as
> aarch64, implements the Enclave heartbeat logic and debug serial
> console.
> 
> To use it, create an EIF file and run
> 
>  $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
>                       -kernel test.eif
> 
> or
> 
>  $ qemu-system-aarch64 -accel nitro,debug-mode=on -M nitro -nographic \
>                       -kernel test.eif
> 
> Signed-off-by: Alexander Graf <graf@amazon.com>
Hi,

The separation between -M nitro and -M nitro-enclave looks potentially confusing
to users. Could it potentially make sense to share the name or is that an option not
on the cards?

 -M nitro as a name is a bit ambiguous both in the short and long run
> ---
> hw/nitro/Kconfig           |   7 ++
> hw/nitro/machine.c         | 180 +++++++++++++++++++++++++++++++++++++
> hw/nitro/meson.build       |   1 +
> include/hw/nitro/machine.h |  20 +++++
> 4 files changed, 208 insertions(+)
> create mode 100644 hw/nitro/machine.c
> create mode 100644 include/hw/nitro/machine.h
> 
> diff --git a/hw/nitro/Kconfig b/hw/nitro/Kconfig
> index 6fe050d35d..910068c23c 100644
> --- a/hw/nitro/Kconfig
> +++ b/hw/nitro/Kconfig
> @@ -5,3 +5,10 @@ config NITRO_SERIAL_VSOCK
> config NITRO_HEARTBEAT
>     bool
>     depends on NITRO
> +
> +config NITRO_MACHINE
> +    bool
> +    default y
> +    depends on NITRO
> +    select NITRO_HEARTBEAT
> +    select NITRO_SERIAL_VSOCK
> diff --git a/hw/nitro/machine.c b/hw/nitro/machine.c
> new file mode 100644
> index 0000000000..197adfbdb5
> --- /dev/null
> +++ b/hw/nitro/machine.c
> @@ -0,0 +1,180 @@
> +/*
> + * Nitro Enclaves (accel) machine
> + *
> + * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
> + *
> + * Authors:
> + *   Alexander Graf <graf@amazon.com>
> + *
> + * Nitro Enclaves machine model for -accel nitro. This machine behaves
> + * like the nitro-enclave machine, but uses the real Nitro Enclaves
> + * backend to launch the virtual machine. It requires use of the -accel
> + * nitro.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qapi/error.h"
> +#include "qom/object_interfaces.h"
> +#include "chardev/char.h"
> +#include "hw/core/boards.h"
> +#include "hw/core/cpu.h"
> +#include "hw/core/sysbus.h"
> +#include "hw/core/qdev-properties-system.h"
> +#include "hw/nitro/heartbeat.h"
> +#include "hw/nitro/machine.h"
> +#include "hw/nitro/serial-vsock.h"
> +#include "system/address-spaces.h"
> +#include "system/hostmem.h"
> +#include "system/system.h"
> +#include "system/nitro-accel.h"
> +#include "qemu/accel.h"
> +#include "hw/arm/machines-qom.h"
> +
> +#define EIF_LOAD_ADDR   (8 * 1024 * 1024)
> +
> +static void nitro_create_cpu(const char *cpu_type, int index)
> +{
> +    Object *obj = object_new(cpu_type);
> +
> +    /* x86 CPUs require an apic-id before realize */
> +    if (object_property_find(obj, "apic-id")) {
> +        object_property_set_int(obj, "apic-id", index, &error_fatal);
> +    }
> +
> +    qdev_realize(DEVICE(obj), NULL, &error_fatal);
> +}
> +
> +static void nitro_machine_init(MachineState *machine)
> +{
> +    const char *eif_path = machine->kernel_filename;
> +    const char *cpu_type = machine->cpu_type;
> +    g_autofree char *eif_data = NULL;
> +    gsize eif_size;
> +    int i;
> +
> +    if (!nitro_enabled()) {
> +        error_report("The 'nitro' machine requires -accel nitro");
> +        exit(1);
> +    }
> +
> +    if (!cpu_type) {
> +        ObjectClass *oc = cpu_class_by_name(target_cpu_type(), "host");
> +
> +        if (!oc) {
> +            error_report("nitro: no 'host' CPU available");
> +            exit(1);
> +        }
> +        cpu_type = object_class_get_name(oc);
> +    }
> +
> +    if (!eif_path) {
> +        error_report("nitro: -kernel <eif-file> is required");
> +        exit(1);
> +    }
> +
> +    /* Expose memory as normal QEMU RAM. Needs to be huge page backed. */
> +    memory_region_add_subregion(get_system_memory(), 0, machine->ram);
> +
> +    /*
> +     * Load EIF (-kernel) as raw blob at the EIF_LOAD_ADDR into guest RAM.
> +     * The Nitro Hypervisor will extract its contents and bootstrap the
> +     * Enclave from it.
> +     */
> +    if (!g_file_get_contents(eif_path, &eif_data, &eif_size, NULL)) {
> +        error_report("nitro: failed to read EIF '%s'", eif_path);
> +        exit(1);
> +    }
> +    address_space_write(&address_space_memory, EIF_LOAD_ADDR,
> +                        MEMTXATTRS_UNSPECIFIED, eif_data, eif_size);
> +
> +    /* Nitro Enclaves require a heartbeat device. Provide one. */
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(qdev_new(TYPE_NITRO_HEARTBEAT)),
> +                             &error_fatal);
> +
> +    /*
> +     * In debug mode, Nitro Enclaves expose the guest's serial output via
> +     * vsock. When the accel is in debug mode, wire the vsock serial to
> +     * the machine's serial port so that -nographic automatically works
> +     */
> +    if (object_property_get_bool(OBJECT(current_accel()), "debug-mode", NULL)) {
> +        Chardev *chr = serial_hd(0);
> +
> +        if (chr) {
> +            DeviceState *dev = qdev_new(TYPE_NITRO_SERIAL_VSOCK);
> +
> +            qdev_prop_set_chr(dev, "chardev", chr);
> +            sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +        }
> +    }
> +
> +    /*
> +     * Spawn vCPUs. While the real Nitro Enclaves CPUs are owned by the
> +     * underlying hypervisor, we still want to maintain a local view of
> +     * them to trigger VM creation when vCPU 0 starts and to give us an
> +     * object to interact with.
> +     */
> +    for (i = 0; i < machine->smp.cpus; i++) {
> +        nitro_create_cpu(cpu_type, i);
> +    }
> +}
> +
> +static bool nitro_create_memfd_backend(MachineState *ms, const char *path,
> +                                       Error **errp)
> +{
> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> +    Object *root = object_get_objects_root();
> +    Object *obj;
> +    bool r = false;
> +
> +    obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
> +
> +    /* Nitro Enclaves require huge page backing */
> +    if (!object_property_set_int(obj, "size", ms->ram_size, errp) ||
> +        !object_property_set_bool(obj, "hugetlb", true, errp)) {
> +        goto out;
> +    }
> +
> +    object_property_add_child(root, mc->default_ram_id, obj);
> +
> +    if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
> +        goto out;
> +    }
> +    r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
> +
> +out:
> +    object_unref(obj);
> +    return r;
> +}
> +
> +static void nitro_machine_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "Nitro Enclave";
> +    mc->init = nitro_machine_init;
> +    mc->create_default_memdev = nitro_create_memfd_backend;
> +    mc->default_ram_id = "ram";
> +    mc->max_cpus = 4096;
> +}
> +
> +static const TypeInfo nitro_machine_info = {
> +    .name = TYPE_NITRO_MACHINE,
> +    .parent = TYPE_MACHINE,
> +    .instance_size = sizeof(NitroMachineState),
> +    .class_init = nitro_machine_class_init,
> +    .interfaces = (const InterfaceInfo[]) {
> +        /* x86_64 and aarch64 only */
> +        { TYPE_TARGET_AARCH64_MACHINE },
> +        { }
> +    },
> +};
> +
> +static void nitro_machine_register(void)
> +{
> +    type_register_static(&nitro_machine_info);
> +}
> +
> +type_init(nitro_machine_register);
> diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build
> index b921da2b97..813f5a9c87 100644
> --- a/hw/nitro/meson.build
> +++ b/hw/nitro/meson.build
> @@ -1,2 +1,3 @@
> system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
> system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c'))
> +system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: files('machine.c'))
> diff --git a/include/hw/nitro/machine.h b/include/hw/nitro/machine.h
> new file mode 100644
> index 0000000000..d78ba7d6dc
> --- /dev/null
> +++ b/include/hw/nitro/machine.h
> @@ -0,0 +1,20 @@
> +/*
> + * Nitro Enclaves (accel) machine
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_NITRO_MACHINE_H
> +#define HW_NITRO_MACHINE_H
> +
> +#include "hw/core/boards.h"
> +#include "qom/object.h"
> +
> +#define TYPE_NITRO_MACHINE MACHINE_TYPE_NAME("nitro")
> +OBJECT_DECLARE_SIMPLE_TYPE(NitroMachineState, NITRO_MACHINE)
> +
> +struct NitroMachineState {
> +    MachineState parent;
> +};
> +
> +#endif /* HW_NITRO_MACHINE_H */
> -- 
> 2.47.1
> 
> 
> 
> 
> Amazon Web Services Development Center Germany GmbH
> Tamara-Danz-Str. 13
> 10243 Berlin
> Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
> Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
> Sitz: Berlin
> Ust-ID: DE 365 538 597



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 07/10] hw/nitro: Add nitro machine
  2026-02-18  3:27   ` Mohamed Mediouni
@ 2026-02-18  9:20     ` Alexander Graf
  0 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-18  9:20 UTC (permalink / raw)
  To: Mohamed Mediouni
  Cc: qemu-devel, qemu-arm, Peter Maydell, Thomas Huth, alex.bennee,
	philmd, berrange, marcandre.lureau, Cornelia Huck, mst,
	Dorjoy Chowdhury, Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli,
	mknaust, nh-open-source

Hi Mohamed,

On 18.02.26 04:27, Mohamed Mediouni wrote:
>> On 18. Feb 2026, at 02:51, Alexander Graf <graf@amazon.com> wrote:
>>
>> Add a machine model to spawn a Nitro Enclave. Unlike the existing -M
>> nitro-enclave, this machine model works exclusively with the -accel
>> nitro accelerator to drive real Nitro Enclave creation. It supports
>> memory allocation, number of CPU selection, both x86_64 as well as
>> aarch64, implements the Enclave heartbeat logic and debug serial
>> console.
>>
>> To use it, create an EIF file and run
>>
>>   $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
>>                        -kernel test.eif
>>
>> or
>>
>>   $ qemu-system-aarch64 -accel nitro,debug-mode=on -M nitro -nographic \
>>                        -kernel test.eif
>>
>> Signed-off-by: Alexander Graf <graf@amazon.com>
> Hi,
>
> The separation between -M nitro and -M nitro-enclave looks potentially confusing
> to users. Could it potentially make sense to share the name or is that an option not
> on the cards?


I originally built all of this inside the nitro-enclave machine, but 
that turned out super awkward as well, because its complete 
initialization is different. The nitro-enclave machine is derived from 
the MicroVM machine type, which is not at all what we need for the accel 
based variant. And on top of that makes it super difficult to enable 
aarch64 support.

So I opted to go with the separate machine name instead.


Alex




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 08/10] hw/core/eif: Move definitions to header
  2026-02-18  1:51 ` [PATCH 08/10] hw/core/eif: Move definitions to header Alexander Graf
@ 2026-02-18 15:12   ` Dorjoy Chowdhury
  0 siblings, 0 replies; 19+ messages in thread
From: Dorjoy Chowdhury @ 2026-02-18 15:12 UTC (permalink / raw)
  To: Alexander Graf
  Cc: qemu-devel, qemu-arm, Peter Maydell, Thomas Huth, alex.bennee,
	philmd, berrange, marcandre.lureau, Cornelia Huck, mst,
	Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli, mknaust,
	nh-open-source

On Wed, Feb 18, 2026 at 7:53 AM Alexander Graf <graf@amazon.com> wrote:
>
> In follow-up patches we need some EIF file definitions that are
> currently in the eif.c file, but want to access them from a separate
> device. Move them into the header instead.
>
> Signed-off-by: Alexander Graf <graf@amazon.com>
> ---
>  hw/core/eif.c | 38 --------------------------------------
>  hw/core/eif.h | 38 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 38 insertions(+), 38 deletions(-)

Reviewed-by: Dorjoy Chowdhury <dorjoychy111@gmail.com>

Regards,
Dorjoy

>
> diff --git a/hw/core/eif.c b/hw/core/eif.c
> index 513caec6b4..96f1d76578 100644
> --- a/hw/core/eif.c
> +++ b/hw/core/eif.c
> @@ -18,44 +18,6 @@
>
>  #include "hw/core/eif.h"
>
> -#define MAX_SECTIONS 32
> -
> -/* members are ordered according to field order in .eif file */
> -typedef struct EifHeader {
> -    uint8_t  magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */
> -    uint16_t version;
> -    uint16_t flags;
> -    uint64_t default_memory;
> -    uint64_t default_cpus;
> -    uint16_t reserved;
> -    uint16_t section_cnt;
> -    uint64_t section_offsets[MAX_SECTIONS];
> -    uint64_t section_sizes[MAX_SECTIONS];
> -    uint32_t unused;
> -    uint32_t eif_crc32;
> -} QEMU_PACKED EifHeader;
> -
> -/* members are ordered according to field order in .eif file */
> -typedef struct EifSectionHeader {
> -    /*
> -     * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature,
> -     * 5 = metadata
> -     */
> -    uint16_t section_type;
> -    uint16_t flags;
> -    uint64_t section_size;
> -} QEMU_PACKED EifSectionHeader;
> -
> -enum EifSectionTypes {
> -    EIF_SECTION_INVALID = 0,
> -    EIF_SECTION_KERNEL = 1,
> -    EIF_SECTION_CMDLINE = 2,
> -    EIF_SECTION_RAMDISK = 3,
> -    EIF_SECTION_SIGNATURE = 4,
> -    EIF_SECTION_METADATA = 5,
> -    EIF_SECTION_MAX = 6,
> -};
> -
>  static const char *section_type_to_string(uint16_t type)
>  {
>      const char *str;
> diff --git a/hw/core/eif.h b/hw/core/eif.h
> index fed3cb5514..a3412377a9 100644
> --- a/hw/core/eif.h
> +++ b/hw/core/eif.h
> @@ -11,6 +11,44 @@
>  #ifndef HW_CORE_EIF_H
>  #define HW_CORE_EIF_H
>
> +#define MAX_SECTIONS 32
> +
> +/* members are ordered according to field order in .eif file */
> +typedef struct EifHeader {
> +    uint8_t  magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */
> +    uint16_t version;
> +    uint16_t flags;
> +    uint64_t default_memory;
> +    uint64_t default_cpus;
> +    uint16_t reserved;
> +    uint16_t section_cnt;
> +    uint64_t section_offsets[MAX_SECTIONS];
> +    uint64_t section_sizes[MAX_SECTIONS];
> +    uint32_t unused;
> +    uint32_t eif_crc32;
> +} QEMU_PACKED EifHeader;
> +
> +/* members are ordered according to field order in .eif file */
> +typedef struct EifSectionHeader {
> +    /*
> +     * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature,
> +     * 5 = metadata
> +     */
> +    uint16_t section_type;
> +    uint16_t flags;
> +    uint64_t section_size;
> +} QEMU_PACKED EifSectionHeader;
> +
> +enum EifSectionTypes {
> +    EIF_SECTION_INVALID = 0,
> +    EIF_SECTION_KERNEL = 1,
> +    EIF_SECTION_CMDLINE = 2,
> +    EIF_SECTION_RAMDISK = 3,
> +    EIF_SECTION_SIGNATURE = 4,
> +    EIF_SECTION_METADATA = 5,
> +    EIF_SECTION_MAX = 6,
> +};
> +
>  bool read_eif_file(const char *eif_path, const char *machine_initrd,
>                     char **kernel_path, char **initrd_path,
>                     char **kernel_cmdline, uint8_t *image_sha384,
> --
> 2.47.1
>
>
>
>
> Amazon Web Services Development Center Germany GmbH
> Tamara-Danz-Str. 13
> 10243 Berlin
> Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
> Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
> Sitz: Berlin
> Ust-ID: DE 365 538 597
>


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 07/10] hw/nitro: Add nitro machine
  2026-02-18  1:51 ` [PATCH 07/10] hw/nitro: Add nitro machine Alexander Graf
  2026-02-18  3:27   ` Mohamed Mediouni
@ 2026-02-20 14:59   ` Michael S. Tsirkin
  2026-02-20 15:07     ` Alexander Graf
  1 sibling, 1 reply; 19+ messages in thread
From: Michael S. Tsirkin @ 2026-02-20 14:59 UTC (permalink / raw)
  To: Alexander Graf
  Cc: qemu-devel, qemu-arm, Peter Maydell, Thomas Huth, alex.bennee,
	philmd, berrange, marcandre.lureau, Cornelia Huck,
	Dorjoy Chowdhury, Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli,
	mknaust, nh-open-source

On Wed, Feb 18, 2026 at 01:51:47AM +0000, Alexander Graf wrote:
> Add a machine model to spawn a Nitro Enclave. Unlike the existing -M
> nitro-enclave, this machine model works exclusively with the -accel
> nitro accelerator to drive real Nitro Enclave creation. It supports
> memory allocation, number of CPU selection, both x86_64 as well as
> aarch64, implements the Enclave heartbeat logic and debug serial
> console.
> 
> To use it, create an EIF file and run
> 
>   $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
>                        -kernel test.eif
> 
> or
> 
>   $ qemu-system-aarch64 -accel nitro,debug-mode=on -M nitro -nographic \
>                        -kernel test.eif
> 
> Signed-off-by: Alexander Graf <graf@amazon.com>
> ---
>  hw/nitro/Kconfig           |   7 ++
>  hw/nitro/machine.c         | 180 +++++++++++++++++++++++++++++++++++++
>  hw/nitro/meson.build       |   1 +
>  include/hw/nitro/machine.h |  20 +++++
>  4 files changed, 208 insertions(+)
>  create mode 100644 hw/nitro/machine.c
>  create mode 100644 include/hw/nitro/machine.h
> 
> diff --git a/hw/nitro/Kconfig b/hw/nitro/Kconfig
> index 6fe050d35d..910068c23c 100644
> --- a/hw/nitro/Kconfig
> +++ b/hw/nitro/Kconfig
> @@ -5,3 +5,10 @@ config NITRO_SERIAL_VSOCK
>  config NITRO_HEARTBEAT
>      bool
>      depends on NITRO
> +
> +config NITRO_MACHINE
> +    bool
> +    default y
> +    depends on NITRO
> +    select NITRO_HEARTBEAT
> +    select NITRO_SERIAL_VSOCK
> diff --git a/hw/nitro/machine.c b/hw/nitro/machine.c
> new file mode 100644
> index 0000000000..197adfbdb5
> --- /dev/null
> +++ b/hw/nitro/machine.c
> @@ -0,0 +1,180 @@
> +/*
> + * Nitro Enclaves (accel) machine
> + *
> + * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
> + *
> + * Authors:
> + *   Alexander Graf <graf@amazon.com>
> + *
> + * Nitro Enclaves machine model for -accel nitro. This machine behaves
> + * like the nitro-enclave machine, but uses the real Nitro Enclaves
> + * backend to launch the virtual machine. It requires use of the -accel
> + * nitro.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/error-report.h"
> +#include "qapi/error.h"
> +#include "qom/object_interfaces.h"
> +#include "chardev/char.h"
> +#include "hw/core/boards.h"
> +#include "hw/core/cpu.h"
> +#include "hw/core/sysbus.h"
> +#include "hw/core/qdev-properties-system.h"
> +#include "hw/nitro/heartbeat.h"
> +#include "hw/nitro/machine.h"
> +#include "hw/nitro/serial-vsock.h"
> +#include "system/address-spaces.h"
> +#include "system/hostmem.h"
> +#include "system/system.h"
> +#include "system/nitro-accel.h"
> +#include "qemu/accel.h"
> +#include "hw/arm/machines-qom.h"
> +
> +#define EIF_LOAD_ADDR   (8 * 1024 * 1024)
> +
> +static void nitro_create_cpu(const char *cpu_type, int index)
> +{
> +    Object *obj = object_new(cpu_type);
> +
> +    /* x86 CPUs require an apic-id before realize */
> +    if (object_property_find(obj, "apic-id")) {
> +        object_property_set_int(obj, "apic-id", index, &error_fatal);
> +    }
> +
> +    qdev_realize(DEVICE(obj), NULL, &error_fatal);
> +}
> +
> +static void nitro_machine_init(MachineState *machine)
> +{
> +    const char *eif_path = machine->kernel_filename;
> +    const char *cpu_type = machine->cpu_type;
> +    g_autofree char *eif_data = NULL;
> +    gsize eif_size;
> +    int i;
> +
> +    if (!nitro_enabled()) {
> +        error_report("The 'nitro' machine requires -accel nitro");
> +        exit(1);
> +    }
> +
> +    if (!cpu_type) {
> +        ObjectClass *oc = cpu_class_by_name(target_cpu_type(), "host");
> +
> +        if (!oc) {
> +            error_report("nitro: no 'host' CPU available");
> +            exit(1);
> +        }
> +        cpu_type = object_class_get_name(oc);
> +    }
> +
> +    if (!eif_path) {
> +        error_report("nitro: -kernel <eif-file> is required");
> +        exit(1);
> +    }
> +
> +    /* Expose memory as normal QEMU RAM. Needs to be huge page backed. */
> +    memory_region_add_subregion(get_system_memory(), 0, machine->ram);
> +
> +    /*
> +     * Load EIF (-kernel) as raw blob at the EIF_LOAD_ADDR into guest RAM.
> +     * The Nitro Hypervisor will extract its contents and bootstrap the
> +     * Enclave from it.
> +     */
> +    if (!g_file_get_contents(eif_path, &eif_data, &eif_size, NULL)) {
> +        error_report("nitro: failed to read EIF '%s'", eif_path);
> +        exit(1);
> +    }
> +    address_space_write(&address_space_memory, EIF_LOAD_ADDR,
> +                        MEMTXATTRS_UNSPECIFIED, eif_data, eif_size);
> +
> +    /* Nitro Enclaves require a heartbeat device. Provide one. */
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(qdev_new(TYPE_NITRO_HEARTBEAT)),
> +                             &error_fatal);
> +
> +    /*
> +     * In debug mode, Nitro Enclaves expose the guest's serial output via
> +     * vsock. When the accel is in debug mode, wire the vsock serial to
> +     * the machine's serial port so that -nographic automatically works
> +     */
> +    if (object_property_get_bool(OBJECT(current_accel()), "debug-mode", NULL)) {
> +        Chardev *chr = serial_hd(0);
> +
> +        if (chr) {
> +            DeviceState *dev = qdev_new(TYPE_NITRO_SERIAL_VSOCK);
> +
> +            qdev_prop_set_chr(dev, "chardev", chr);
> +            sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +        }
> +    }


Would  respecting -nodefaults and then maybe not wiring up all the
vsock things make sense? allows users to set it up any way
they want ...

> +
> +    /*
> +     * Spawn vCPUs. While the real Nitro Enclaves CPUs are owned by the
> +     * underlying hypervisor, we still want to maintain a local view of
> +     * them to trigger VM creation when vCPU 0 starts and to give us an
> +     * object to interact with.
> +     */
> +    for (i = 0; i < machine->smp.cpus; i++) {
> +        nitro_create_cpu(cpu_type, i);
> +    }
> +}
> +
> +static bool nitro_create_memfd_backend(MachineState *ms, const char *path,
> +                                       Error **errp)
> +{
> +    MachineClass *mc = MACHINE_GET_CLASS(ms);
> +    Object *root = object_get_objects_root();
> +    Object *obj;
> +    bool r = false;
> +
> +    obj = object_new(TYPE_MEMORY_BACKEND_MEMFD);
> +
> +    /* Nitro Enclaves require huge page backing */
> +    if (!object_property_set_int(obj, "size", ms->ram_size, errp) ||
> +        !object_property_set_bool(obj, "hugetlb", true, errp)) {
> +        goto out;
> +    }
> +
> +    object_property_add_child(root, mc->default_ram_id, obj);
> +
> +    if (!user_creatable_complete(USER_CREATABLE(obj), errp)) {
> +        goto out;
> +    }
> +    r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp);
> +
> +out:
> +    object_unref(obj);
> +    return r;
> +}
> +
> +static void nitro_machine_class_init(ObjectClass *oc, const void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->desc = "Nitro Enclave";
> +    mc->init = nitro_machine_init;
> +    mc->create_default_memdev = nitro_create_memfd_backend;
> +    mc->default_ram_id = "ram";
> +    mc->max_cpus = 4096;
> +}
> +
> +static const TypeInfo nitro_machine_info = {
> +    .name = TYPE_NITRO_MACHINE,
> +    .parent = TYPE_MACHINE,
> +    .instance_size = sizeof(NitroMachineState),
> +    .class_init = nitro_machine_class_init,
> +    .interfaces = (const InterfaceInfo[]) {
> +        /* x86_64 and aarch64 only */
> +        { TYPE_TARGET_AARCH64_MACHINE },
> +        { }
> +    },
> +};
> +
> +static void nitro_machine_register(void)
> +{
> +    type_register_static(&nitro_machine_info);
> +}
> +
> +type_init(nitro_machine_register);
> diff --git a/hw/nitro/meson.build b/hw/nitro/meson.build
> index b921da2b97..813f5a9c87 100644
> --- a/hw/nitro/meson.build
> +++ b/hw/nitro/meson.build
> @@ -1,2 +1,3 @@
>  system_ss.add(when: 'CONFIG_NITRO_SERIAL_VSOCK', if_true: files('serial-vsock.c'))
>  system_ss.add(when: 'CONFIG_NITRO_HEARTBEAT', if_true: files('heartbeat.c'))
> +system_ss.add(when: 'CONFIG_NITRO_MACHINE', if_true: files('machine.c'))
> diff --git a/include/hw/nitro/machine.h b/include/hw/nitro/machine.h
> new file mode 100644
> index 0000000000..d78ba7d6dc
> --- /dev/null
> +++ b/include/hw/nitro/machine.h
> @@ -0,0 +1,20 @@
> +/*
> + * Nitro Enclaves (accel) machine
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_NITRO_MACHINE_H
> +#define HW_NITRO_MACHINE_H
> +
> +#include "hw/core/boards.h"
> +#include "qom/object.h"
> +
> +#define TYPE_NITRO_MACHINE MACHINE_TYPE_NAME("nitro")
> +OBJECT_DECLARE_SIMPLE_TYPE(NitroMachineState, NITRO_MACHINE)
> +
> +struct NitroMachineState {
> +    MachineState parent;
> +};
> +
> +#endif /* HW_NITRO_MACHINE_H */
> -- 
> 2.47.1
> 
> 
> 
> 
> Amazon Web Services Development Center Germany GmbH
> Tamara-Danz-Str. 13
> 10243 Berlin
> Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
> Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
> Sitz: Berlin
> Ust-ID: DE 365 538 597



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 07/10] hw/nitro: Add nitro machine
  2026-02-20 14:59   ` Michael S. Tsirkin
@ 2026-02-20 15:07     ` Alexander Graf
  0 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-20 15:07 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: qemu-devel, qemu-arm, Peter Maydell, Thomas Huth, alex.bennee,
	philmd, berrange, marcandre.lureau, Cornelia Huck,
	Dorjoy Chowdhury, Pierrick Bouvier, Paolo Bonzini, Tyler Fanelli,
	mknaust, nh-open-source

Hey Michael,

On 20.02.26 15:59, Michael S. Tsirkin wrote:
> On Wed, Feb 18, 2026 at 01:51:47AM +0000, Alexander Graf wrote:
>> Add a machine model to spawn a Nitro Enclave. Unlike the existing -M
>> nitro-enclave, this machine model works exclusively with the -accel
>> nitro accelerator to drive real Nitro Enclave creation. It supports
>> memory allocation, number of CPU selection, both x86_64 as well as
>> aarch64, implements the Enclave heartbeat logic and debug serial
>> console.
>>
>> To use it, create an EIF file and run
>>
>>    $ qemu-system-x86_64 -accel nitro,debug-mode=on -M nitro -nographic \
>>                         -kernel test.eif
>>
>> or
>>
>>    $ qemu-system-aarch64 -accel nitro,debug-mode=on -M nitro -nographic \
>>                         -kernel test.eif
>>
>> Signed-off-by: Alexander Graf <graf@amazon.com>


[...]


>> +
>> +    /*
>> +     * In debug mode, Nitro Enclaves expose the guest's serial output via
>> +     * vsock. When the accel is in debug mode, wire the vsock serial to
>> +     * the machine's serial port so that -nographic automatically works
>> +     */
>> +    if (object_property_get_bool(OBJECT(current_accel()), "debug-mode", NULL)) {
>> +        Chardev *chr = serial_hd(0);
>> +
>> +        if (chr) {
>> +            DeviceState *dev = qdev_new(TYPE_NITRO_SERIAL_VSOCK);
>> +
>> +            qdev_prop_set_chr(dev, "chardev", chr);
>> +            sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
>> +        }
>> +    }
>
> Would  respecting -nodefaults and then maybe not wiring up all the
> vsock things make sense? allows users to set it up any way
> they want ...


So you would set up the individual special-purpose nitro devices via 
-device or handle the respective virtio streams externally instead? That 
definitely makes a lot of sense! Happy to add in v2.


Alex




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 03/10] accel: Add Nitro Enclaves accelerator
  2026-02-18  1:51 ` [PATCH 03/10] accel: Add Nitro Enclaves accelerator Alexander Graf
@ 2026-02-24 10:22   ` Paolo Bonzini
  2026-02-24 23:16     ` Alexander Graf
  0 siblings, 1 reply; 19+ messages in thread
From: Paolo Bonzini @ 2026-02-24 10:22 UTC (permalink / raw)
  To: Alexander Graf, qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Tyler Fanelli, mknaust, nh-open-source

On 2/18/26 02:51, Alexander Graf wrote:
> +/*
> + * Start the Enclave. This gets called when the first vCPU 0 enters its main
> + * loop. At this point memory is set up and the EIF is loaded. This function
> + * donates memory, adds vCPUs, and starts the enclave.
> + */
> +static void nitro_do_start(NitroAccelState *s)
> +{
> +    MachineState *ms = MACHINE(qdev_get_machine());
> +    int nr_cpus = ms->smp.cpus;
> +    int i, ret;
> +    struct ne_enclave_start_info start_info = {
> +        .flags = s->debug_mode ? NE_ENCLAVE_DEBUG_MODE : 0,
> +        .enclave_cid = s->enclave_cid,
> +    };
> +
> +    ret = qemu_ram_foreach_block(nitro_donate_ram_block, s);
> +    if (ret < 0) {
> +        error_report("nitro: failed to donate memory");
> +        exit(1);
> +    }
> +
> +    for (i = 0; i < nr_cpus; i++) {
> +        uint32_t cpu_id = 0;
> +        if (ioctl(s->enclave_fd, NE_ADD_VCPU, &cpu_id) < 0) {
> +            error_report("nitro: NE_ADD_VCPU failed: %s", strerror(errno));
> +            exit(1);
> +        }
> +    }
> +
> +    ret = ioctl(s->enclave_fd, NE_START_ENCLAVE, &start_info);
> +    if (ret < 0) {
> +        switch (errno) {
> +        case NE_ERR_NO_MEM_REGIONS_ADDED:
> +            error_report("nitro: no memory regions added");
> +            break;
> +        case NE_ERR_NO_VCPUS_ADDED:
> +            error_report("nitro: no vCPUs added");
> +            break;
> +        case NE_ERR_ENCLAVE_MEM_MIN_SIZE:
> +            error_report("nitro: memory is below the minimum "
> +                         "required size. Try increasing -m");
> +            break;
> +        case NE_ERR_FULL_CORES_NOT_USED:
> +            error_report("nitro: requires full CPU cores. "
> +                         "Try increasing -smp to a multiple of threads "
> +                         "per core on this host (e.g. -smp 2)");
> +            break;
> +        case NE_ERR_NOT_IN_INIT_STATE:
> +            error_report("nitro: not in init state");
> +            break;
> +        case NE_ERR_INVALID_FLAG_VALUE:
> +            error_report("nitro: invalid flag value for NE_START_ENCLAVE");
> +            break;
> +        case NE_ERR_INVALID_ENCLAVE_CID:
> +            error_report("nitro: invalid enclave CID");
> +            break;
> +        default:
> +            error_report("nitro: NE_START_ENCLAVE failed: %s (errno %d)",
> +                         strerror(errno), errno);
> +            break;
> +        }
> +        exit(1);
> +    }
> +
> +    s->enclave_cid = start_info.enclave_cid;
> +    trace_nitro_enclave_started(s->enclave_cid);
> +
> +    /*
> +     * Push enclave CID to all devices that need it.
> +     * Each device handles its own connection (console, heartbeat).
> +     */
> +    {
> +        BusState *sysbus = sysbus_get_default();
> +        BusChild *kid;
> +
> +        QTAILQ_FOREACH(kid, &sysbus->children, sibling) {
> +            DeviceState *dev = kid->child;
> +            if (object_property_find(OBJECT(dev), "enclave-cid")) {
> +                object_property_set_uint(OBJECT(dev), "enclave-cid",
> +                                         s->enclave_cid, NULL);
> +            }
> +        }

I think it makes more sense to have a "nitro bus" with its own subclass 
and an enclave_started(NitroDevice *dev, int enclave_cid, Error **errp) 
method.  Properties can be set with -device and that gets either awkward 
or buggy pretty quickly.

> +    }
> +}
> +
> +/*
> + * vCPU dummy thread function. The real vCPUs run inside the enclave.
> + *
> + * Based on dummy_cpu_thread_fn() from accel/dummy-cpus.c.
> + */
> +static void *nitro_vcpu_thread_fn(void *arg)
> +{
> +    CPUState *cpu = arg;
> +    NitroAccelState *s = NITRO_ACCEL(current_accel());
> +    sigset_t waitset;
> +
> +    rcu_register_thread();
> +
> +    bql_lock();
> +    qemu_thread_get_self(cpu->thread);
> +    cpu->thread_id = qemu_get_thread_id();
> +    current_cpu = cpu;
> +
> +    sigemptyset(&waitset);
> +    sigaddset(&waitset, SIG_IPI);
> +
> +    cpu_thread_signal_created(cpu);
> +    qemu_guest_random_seed_thread_part2(cpu->random_seed);
> +
> +    /* vCPU 0 starts the enclave on first entry */
> +    if (cpu->cpu_index == 0) {
> +        nitro_do_start(s);
> +    }

Can you replace this with an async_run_on_cpu() call when vCPU 0 is 
created (in nitro_start_vcpu_thread) and just reuse dummy_cpu_thread_fn()?

Or alternatively with a machine ready notifier.

> diff --git a/accel/stubs/nitro-stub.c b/accel/stubs/nitro-stub.c
> new file mode 100644
> index 0000000000..186c8444f8
> --- /dev/null
> +++ b/accel/stubs/nitro-stub.c
> @@ -0,0 +1,11 @@
> +/*
> + * Nitro accel stubs for QEMU
> + *
> + * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +
> +bool nitro_allowed;

Maybe all these *_allowed variables should be in a .c file in accel/.

Paolo



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 10/10] docs: Add Nitro Enclaves documentation
  2026-02-18  1:51 ` [PATCH 10/10] docs: Add Nitro Enclaves documentation Alexander Graf
@ 2026-02-24 10:26   ` Paolo Bonzini
  0 siblings, 0 replies; 19+ messages in thread
From: Paolo Bonzini @ 2026-02-24 10:26 UTC (permalink / raw)
  To: Alexander Graf, qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Tyler Fanelli, mknaust, nh-open-source

On 2/18/26 02:51, Alexander Graf wrote:
> +Machine
> +-------
> +
> +The ``nitro`` machine (``-M nitro``) is a minimal, architecture-independent
> +machine that provides only what a Nitro Enclave needs:
> +
> +* RAM (huge page backed via memfd)
> +* vCPUs (defaults to ``host`` CPU type)
> +* A heartbeat device (vsock server on port 9000)
> +* A serial console bridge (vsock client, debug mode only)
> +
> +Communication to the Nitro Enclave is limited to virtio-vsock. The Enclave
> +is allocated a CID at launch at which it is reachable. The CID is available
> +as QOM property in the accelerator and as a trace event.

Maybe add a sample qom-get invocation to retrieve it?

Paolo



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 03/10] accel: Add Nitro Enclaves accelerator
  2026-02-24 10:22   ` Paolo Bonzini
@ 2026-02-24 23:16     ` Alexander Graf
  0 siblings, 0 replies; 19+ messages in thread
From: Alexander Graf @ 2026-02-24 23:16 UTC (permalink / raw)
  To: Paolo Bonzini, qemu-devel
  Cc: qemu-arm, Peter Maydell, Thomas Huth, alex.bennee, philmd,
	berrange, marcandre.lureau, Cornelia Huck, mst, Dorjoy Chowdhury,
	Pierrick Bouvier, Tyler Fanelli, mknaust, nh-open-source


On 24.02.26 11:22, Paolo Bonzini wrote:
> On 2/18/26 02:51, Alexander Graf wrote:
>> +/*
>> + * Start the Enclave. This gets called when the first vCPU 0 enters 
>> its main
>> + * loop. At this point memory is set up and the EIF is loaded. This 
>> function
>> + * donates memory, adds vCPUs, and starts the enclave.
>> + */
>> +static void nitro_do_start(NitroAccelState *s)
>> +{
>> +    MachineState *ms = MACHINE(qdev_get_machine());
>> +    int nr_cpus = ms->smp.cpus;
>> +    int i, ret;
>> +    struct ne_enclave_start_info start_info = {
>> +        .flags = s->debug_mode ? NE_ENCLAVE_DEBUG_MODE : 0,
>> +        .enclave_cid = s->enclave_cid,
>> +    };
>> +
>> +    ret = qemu_ram_foreach_block(nitro_donate_ram_block, s);
>> +    if (ret < 0) {
>> +        error_report("nitro: failed to donate memory");
>> +        exit(1);
>> +    }
>> +
>> +    for (i = 0; i < nr_cpus; i++) {
>> +        uint32_t cpu_id = 0;
>> +        if (ioctl(s->enclave_fd, NE_ADD_VCPU, &cpu_id) < 0) {
>> +            error_report("nitro: NE_ADD_VCPU failed: %s", 
>> strerror(errno));
>> +            exit(1);
>> +        }
>> +    }
>> +
>> +    ret = ioctl(s->enclave_fd, NE_START_ENCLAVE, &start_info);
>> +    if (ret < 0) {
>> +        switch (errno) {
>> +        case NE_ERR_NO_MEM_REGIONS_ADDED:
>> +            error_report("nitro: no memory regions added");
>> +            break;
>> +        case NE_ERR_NO_VCPUS_ADDED:
>> +            error_report("nitro: no vCPUs added");
>> +            break;
>> +        case NE_ERR_ENCLAVE_MEM_MIN_SIZE:
>> +            error_report("nitro: memory is below the minimum "
>> +                         "required size. Try increasing -m");
>> +            break;
>> +        case NE_ERR_FULL_CORES_NOT_USED:
>> +            error_report("nitro: requires full CPU cores. "
>> +                         "Try increasing -smp to a multiple of 
>> threads "
>> +                         "per core on this host (e.g. -smp 2)");
>> +            break;
>> +        case NE_ERR_NOT_IN_INIT_STATE:
>> +            error_report("nitro: not in init state");
>> +            break;
>> +        case NE_ERR_INVALID_FLAG_VALUE:
>> +            error_report("nitro: invalid flag value for 
>> NE_START_ENCLAVE");
>> +            break;
>> +        case NE_ERR_INVALID_ENCLAVE_CID:
>> +            error_report("nitro: invalid enclave CID");
>> +            break;
>> +        default:
>> +            error_report("nitro: NE_START_ENCLAVE failed: %s (errno 
>> %d)",
>> +                         strerror(errno), errno);
>> +            break;
>> +        }
>> +        exit(1);
>> +    }
>> +
>> +    s->enclave_cid = start_info.enclave_cid;
>> +    trace_nitro_enclave_started(s->enclave_cid);
>> +
>> +    /*
>> +     * Push enclave CID to all devices that need it.
>> +     * Each device handles its own connection (console, heartbeat).
>> +     */
>> +    {
>> +        BusState *sysbus = sysbus_get_default();
>> +        BusChild *kid;
>> +
>> +        QTAILQ_FOREACH(kid, &sysbus->children, sibling) {
>> +            DeviceState *dev = kid->child;
>> +            if (object_property_find(OBJECT(dev), "enclave-cid")) {
>> +                object_property_set_uint(OBJECT(dev), "enclave-cid",
>> +                                         s->enclave_cid, NULL);
>> +            }
>> +        }
>
> I think it makes more sense to have a "nitro bus" with its own 
> subclass and an enclave_started(NitroDevice *dev, int enclave_cid, 
> Error **errp) method.  Properties can be set with -device and that 
> gets either awkward or buggy pretty quickly.


I tend to agree. Maybe even a "nitro vsock bus". Let me cook on that a bit.


>
>> +    }
>> +}
>> +
>> +/*
>> + * vCPU dummy thread function. The real vCPUs run inside the enclave.
>> + *
>> + * Based on dummy_cpu_thread_fn() from accel/dummy-cpus.c.
>> + */
>> +static void *nitro_vcpu_thread_fn(void *arg)
>> +{
>> +    CPUState *cpu = arg;
>> +    NitroAccelState *s = NITRO_ACCEL(current_accel());
>> +    sigset_t waitset;
>> +
>> +    rcu_register_thread();
>> +
>> +    bql_lock();
>> +    qemu_thread_get_self(cpu->thread);
>> +    cpu->thread_id = qemu_get_thread_id();
>> +    current_cpu = cpu;
>> +
>> +    sigemptyset(&waitset);
>> +    sigaddset(&waitset, SIG_IPI);
>> +
>> +    cpu_thread_signal_created(cpu);
>> +    qemu_guest_random_seed_thread_part2(cpu->random_seed);
>> +
>> +    /* vCPU 0 starts the enclave on first entry */
>> +    if (cpu->cpu_index == 0) {
>> +        nitro_do_start(s);
>> +    }
>
> Can you replace this with an async_run_on_cpu() call when vCPU 0 is 
> created (in nitro_start_vcpu_thread) and just reuse 
> dummy_cpu_thread_fn()?
>
> Or alternatively with a machine ready notifier.


I moved it into setup_post() which is a more natural place anyway. That 
way I can also exclusively use the dummy cpu thread. Definitely much 
cleaner, thank you :).


>
>> diff --git a/accel/stubs/nitro-stub.c b/accel/stubs/nitro-stub.c
>> new file mode 100644
>> index 0000000000..186c8444f8
>> --- /dev/null
>> +++ b/accel/stubs/nitro-stub.c
>> @@ -0,0 +1,11 @@
>> +/*
>> + * Nitro accel stubs for QEMU
>> + *
>> + * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights 
>> Reserved.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +
>> +bool nitro_allowed;
>
> Maybe all these *_allowed variables should be in a .c file in accel/.


Hm, maybe. But I'd like to leave that for a different patch set so I 
don't end up boiling the ocean :)


Alex




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-02-24 23:17 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-18  1:51 [PATCH 00/10] Native Nitro Enclaves support Alexander Graf
2026-02-18  1:51 ` [PATCH 01/10] scripts/update-linux-headers: Add Nitro Enclaves header Alexander Graf
2026-02-18  1:51 ` [PATCH 02/10] linux-headers: Add nitro_enclaves.h Alexander Graf
2026-02-18  1:51 ` [PATCH 03/10] accel: Add Nitro Enclaves accelerator Alexander Graf
2026-02-24 10:22   ` Paolo Bonzini
2026-02-24 23:16     ` Alexander Graf
2026-02-18  1:51 ` [PATCH 04/10] hw/nitro/nitro-serial-vsock: Nitro Enclaves vsock console Alexander Graf
2026-02-18  1:51 ` [PATCH 05/10] hw/nitro: Introduce Nitro Enclave Heartbeat device Alexander Graf
2026-02-18  1:51 ` [PATCH 06/10] target/arm/cpu64: Allow -host for nitro Alexander Graf
2026-02-18  1:51 ` [PATCH 07/10] hw/nitro: Add nitro machine Alexander Graf
2026-02-18  3:27   ` Mohamed Mediouni
2026-02-18  9:20     ` Alexander Graf
2026-02-20 14:59   ` Michael S. Tsirkin
2026-02-20 15:07     ` Alexander Graf
2026-02-18  1:51 ` [PATCH 08/10] hw/core/eif: Move definitions to header Alexander Graf
2026-02-18 15:12   ` Dorjoy Chowdhury
2026-02-18  1:51 ` [PATCH 09/10] hw/nitro: Enable direct kernel boot Alexander Graf
2026-02-18  1:51 ` [PATCH 10/10] docs: Add Nitro Enclaves documentation Alexander Graf
2026-02-24 10:26   ` Paolo Bonzini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox