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