[PATCH 07/10] hw/nitro: Add nitro machine

Alexander Graf posted 10 patches 1 month, 3 weeks ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Alexander Graf <graf@amazon.com>, Pierrick Bouvier <pierrick.bouvier@linaro.org>, Dorjoy Chowdhury <dorjoychy111@gmail.com>, "Michael S. Tsirkin" <mst@redhat.com>, Cornelia Huck <cohuck@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, "Alex Bennée" <alex.bennee@linaro.org>, Thomas Huth <thuth@redhat.com>, Peter Maydell <peter.maydell@linaro.org>
There is a newer version of this series
[PATCH 07/10] hw/nitro: Add nitro machine
Posted by Alexander Graf 1 month, 3 weeks ago
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
Re: [PATCH 07/10] hw/nitro: Add nitro machine
Posted by Michael S. Tsirkin 1 month, 2 weeks ago
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
Re: [PATCH 07/10] hw/nitro: Add nitro machine
Posted by Alexander Graf 1 month, 2 weeks ago
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
Re: [PATCH 07/10] hw/nitro: Add nitro machine
Posted by Mohamed Mediouni 1 month, 3 weeks ago

> 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
Re: [PATCH 07/10] hw/nitro: Add nitro machine
Posted by Alexander Graf 1 month, 3 weeks ago
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