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
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
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
> 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
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
© 2016 - 2026 Red Hat, Inc.