accel: Add Nitro Enclaves accelerator
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> Link: https://lore.kernel.org/r/20260225220807.33092-5-graf@amazon.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Alexander Graf committed
Feb 25, 2026 at 22:07 UTC
8155bca60d436e6422ee08be3b93c952540e45da
15 files changed
+364
-4
MAINTAINERS
+6
@@ -586,6 +586,12 @@ F: include/system/mshv.h
586
F: include/hw/hyperv/hvgdk*.h
587
F: include/hw/hyperv/hvhdk*.h
588
589
+Nitro Enclaves (native)
590
+M: Alexander Graf <graf@amazon.com>
591
+S: Maintained
592
+F: accel/nitro/
593
+F: include/system/nitro-accel.h
594
+
595
X86 MSHV CPUs
596
M: Magnus Kulke <magnus.kulke@linux.microsoft.com>
597
R: Wei Liu <wei.liu@kernel.org>
accel/Kconfig
+3
@@ -16,6 +16,9 @@ config KVM
16
config MSHV
17
bool
18
19
+config NITRO
20
+ bool
21
+
22
config XEN
23
bool
24
select FSDEV_9P if VIRTFS
accel/meson.build
+1
@@ -12,6 +12,7 @@ if have_system
12
subdir('xen')
13
subdir('stubs')
14
subdir('mshv')
15
+ subdir('nitro')
16
endif
17
18
# qtest
accel/nitro/meson.build
new
+3
@@ -0,0 +1,3 @@
1
+nitro_ss = ss.source_set()
2
+nitro_ss.add(files('nitro-accel.c'))
3
+system_ss.add_all(when: 'CONFIG_NITRO', if_true: nitro_ss)
accel/nitro/nitro-accel.c
new
+284
@@ -0,0 +1,284 @@
1
+/*
2
+ * Nitro Enclaves accelerator
3
+ *
4
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
+ *
6
+ * Authors:
7
+ * Alexander Graf <graf@amazon.com>
8
+ *
9
+ * Nitro Enclaves are a confidential compute technology which
10
+ * allows a parent instance to carve out resources from itself
11
+ * and spawn a confidential sibling VM next to itself. Similar
12
+ * to other confidential compute solutions, this sibling is
13
+ * controlled by an underlying vmm, but still has a higher level
14
+ * vmm (QEMU) to implement some of its I/O functionality and
15
+ * lifecycle.
16
+ *
17
+ * This accelerator drives /dev/nitro_enclaves to spawn a Nitro
18
+ * Enclave. It works in tandem with the nitro_enclaves machine
19
+ * which ensures the correct backend devices are available and
20
+ * that the initial seed (an EIF file) is loaded at the correct
21
+ * offset in memory.
22
+ *
23
+ * The accel starts the enclave when the machine starts, after
24
+ * all device setup is finished.
25
+ *
26
+ * SPDX-License-Identifier: GPL-2.0-or-later
27
+ */
28
+
29
+#include "qemu/osdep.h"
30
+#include "qemu/error-report.h"
31
+#include "qapi/error.h"
32
+#include "qapi/visitor.h"
33
+#include "qemu/module.h"
34
+#include "qemu/rcu.h"
35
+#include "qemu/accel.h"
36
+#include "qemu/guest-random.h"
37
+#include "qemu/main-loop.h"
38
+#include "accel/accel-ops.h"
39
+#include "accel/accel-cpu-ops.h"
40
+#include "accel/dummy-cpus.h"
41
+#include "system/cpus.h"
42
+#include "hw/core/cpu.h"
43
+#include "hw/core/boards.h"
44
+#include "hw/nitro/nitro-vsock-bus.h"
45
+#include "system/ramblock.h"
46
+#include "system/nitro-accel.h"
47
+#include "trace.h"
48
+
49
+#include <sys/ioctl.h>
50
+#include "standard-headers/linux/nitro_enclaves.h"
51
+
52
+bool nitro_allowed;
53
+
54
+typedef struct NitroAccelState {
55
+ AccelState parent_obj;
56
+
57
+ int ne_fd;
58
+ int enclave_fd;
59
+ uint64_t slot_uid;
60
+ uint64_t enclave_cid;
61
+ bool debug_mode;
62
+} NitroAccelState;
63
+
64
+static int nitro_init_machine(AccelState *as, MachineState *ms)
65
+{
66
+ NitroAccelState *s = NITRO_ACCEL(as);
67
+ uint64_t slot_uid = 0;
68
+ int ret;
69
+
70
+ s->ne_fd = open("/dev/nitro_enclaves", O_RDWR | O_CLOEXEC);
71
+ if (s->ne_fd < 0) {
72
+ error_report("nitro: failed to open /dev/nitro_enclaves: %s",
73
+ strerror(errno));
74
+ return -errno;
75
+ }
76
+
77
+ ret = ioctl(s->ne_fd, NE_CREATE_VM, &slot_uid);
78
+ if (ret < 0) {
79
+ error_report("nitro: NE_CREATE_VM failed: %s", strerror(errno));
80
+ close(s->ne_fd);
81
+ return -errno;
82
+ }
83
+ s->enclave_fd = ret;
84
+ s->slot_uid = slot_uid;
85
+
86
+ return 0;
87
+}
88
+
89
+static int nitro_donate_ram_block(RAMBlock *rb, void *opaque)
90
+{
91
+ NitroAccelState *s = opaque;
92
+ struct ne_user_memory_region region = {
93
+ .flags = 0,
94
+ .memory_size = rb->used_length,
95
+ .userspace_addr = (uint64_t)(uintptr_t)rb->host,
96
+ };
97
+
98
+ if (!rb->used_length) {
99
+ return 0;
100
+ }
101
+
102
+ if (ioctl(s->enclave_fd, NE_SET_USER_MEMORY_REGION, ®ion) < 0) {
103
+ error_report("nitro: NE_SET_USER_MEMORY_REGION failed for %s "
104
+ "(%" PRIu64 " bytes): %s", rb->idstr, rb->used_length,
105
+ strerror(errno));
106
+ return -errno;
107
+ }
108
+ return 0;
109
+}
110
+
111
+/*
112
+ * Start the Enclave. At this point memory is set up and the EIF is loaded.
113
+ * This function donates memory, adds vCPUs, and starts the enclave.
114
+ */
115
+static void nitro_setup_post(AccelState *as)
116
+{
117
+ MachineState *ms = MACHINE(qdev_get_machine());
118
+ NitroAccelState *s = NITRO_ACCEL(as);
119
+ int nr_cpus = ms->smp.cpus;
120
+ int i, ret;
121
+ struct ne_enclave_start_info start_info = {
122
+ .flags = s->debug_mode ? NE_ENCLAVE_DEBUG_MODE : 0,
123
+ .enclave_cid = s->enclave_cid,
124
+ };
125
+
126
+ ret = qemu_ram_foreach_block(nitro_donate_ram_block, s);
127
+ if (ret < 0) {
128
+ error_report("nitro: failed to donate memory");
129
+ exit(1);
130
+ }
131
+
132
+ for (i = 0; i < nr_cpus; i++) {
133
+ uint32_t cpu_id = 0;
134
+ if (ioctl(s->enclave_fd, NE_ADD_VCPU, &cpu_id) < 0) {
135
+ error_report("nitro: NE_ADD_VCPU failed: %s", strerror(errno));
136
+ exit(1);
137
+ }
138
+ }
139
+
140
+ ret = ioctl(s->enclave_fd, NE_START_ENCLAVE, &start_info);
141
+ if (ret < 0) {
142
+ switch (errno) {
143
+ case NE_ERR_NO_MEM_REGIONS_ADDED:
144
+ error_report("nitro: no memory regions added");
145
+ break;
146
+ case NE_ERR_NO_VCPUS_ADDED:
147
+ error_report("nitro: no vCPUs added");
148
+ break;
149
+ case NE_ERR_ENCLAVE_MEM_MIN_SIZE:
150
+ error_report("nitro: memory is below the minimum "
151
+ "required size. Try increasing -m");
152
+ break;
153
+ case NE_ERR_FULL_CORES_NOT_USED:
154
+ error_report("nitro: requires full CPU cores. "
155
+ "Try increasing -smp to a multiple of threads "
156
+ "per core on this host (e.g. -smp 2)");
157
+ break;
158
+ case NE_ERR_NOT_IN_INIT_STATE:
159
+ error_report("nitro: not in init state");
160
+ break;
161
+ case NE_ERR_INVALID_FLAG_VALUE:
162
+ error_report("nitro: invalid flag value for NE_START_ENCLAVE");
163
+ break;
164
+ case NE_ERR_INVALID_ENCLAVE_CID:
165
+ error_report("nitro: invalid enclave CID");
166
+ break;
167
+ default:
168
+ error_report("nitro: NE_START_ENCLAVE failed: %s (errno %d)",
169
+ strerror(errno), errno);
170
+ break;
171
+ }
172
+ exit(1);
173
+ }
174
+
175
+ s->enclave_cid = start_info.enclave_cid;
176
+ trace_nitro_enclave_started(s->enclave_cid);
177
+
178
+ /*
179
+ * Notify all Nitro vsock bus devices that the enclave has started
180
+ * and provide them with the CID for vsock connections.
181
+ */
182
+ {
183
+ NitroVsockBridge *bridge = nitro_vsock_bridge_find();
184
+ Error *err = NULL;
185
+
186
+ if (bridge) {
187
+ nitro_vsock_bridge_start_enclave(bridge,
188
+ (uint32_t)s->enclave_cid, &err);
189
+ if (err) {
190
+ error_report_err(err);
191
+ exit(1);
192
+ }
193
+ }
194
+ }
195
+}
196
+
197
+/* QOM properties */
198
+
199
+static bool nitro_get_debug_mode(Object *obj, Error **errp)
200
+{
201
+ return NITRO_ACCEL(obj)->debug_mode;
202
+}
203
+
204
+static void nitro_set_debug_mode(Object *obj, bool value, Error **errp)
205
+{
206
+ NITRO_ACCEL(obj)->debug_mode = value;
207
+}
208
+
209
+static void nitro_get_enclave_cid(Object *obj, Visitor *v,
210
+ const char *name, void *opaque,
211
+ Error **errp)
212
+{
213
+ uint64_t val = NITRO_ACCEL(obj)->enclave_cid;
214
+ visit_type_uint64(v, name, &val, errp);
215
+}
216
+
217
+static void nitro_set_enclave_cid(Object *obj, Visitor *v,
218
+ const char *name, void *opaque,
219
+ Error **errp)
220
+{
221
+ uint64_t val;
222
+ if (visit_type_uint64(v, name, &val, errp)) {
223
+ NITRO_ACCEL(obj)->enclave_cid = val;
224
+ }
225
+}
226
+
227
+static void nitro_accel_class_init(ObjectClass *oc, const void *data)
228
+{
229
+ AccelClass *ac = ACCEL_CLASS(oc);
230
+ ac->name = "Nitro";
231
+ ac->init_machine = nitro_init_machine;
232
+ ac->setup_post = nitro_setup_post;
233
+ ac->allowed = &nitro_allowed;
234
+
235
+ object_class_property_add_bool(oc, "debug-mode",
236
+ nitro_get_debug_mode,
237
+ nitro_set_debug_mode);
238
+ object_class_property_set_description(oc, "debug-mode",
239
+ "Start enclave in debug mode (enables console output)");
240
+
241
+ object_class_property_add(oc, "enclave-cid", "uint64",
242
+ nitro_get_enclave_cid,
243
+ nitro_set_enclave_cid,
244
+ NULL, NULL);
245
+ object_class_property_set_description(oc, "enclave-cid",
246
+ "Enclave CID (0 = auto-assigned by Nitro)");
247
+}
248
+
249
+static const TypeInfo nitro_accel_type = {
250
+ .name = TYPE_NITRO_ACCEL,
251
+ .parent = TYPE_ACCEL,
252
+ .instance_size = sizeof(NitroAccelState),
253
+ .class_init = nitro_accel_class_init,
254
+};
255
+module_obj(TYPE_NITRO_ACCEL);
256
+
257
+static bool nitro_cpus_are_resettable(void)
258
+{
259
+ return false;
260
+}
261
+
262
+static void nitro_accel_ops_class_init(ObjectClass *oc, const void *data)
263
+{
264
+ AccelOpsClass *ops = ACCEL_OPS_CLASS(oc);
265
+ ops->create_vcpu_thread = dummy_start_vcpu_thread;
266
+ ops->handle_interrupt = generic_handle_interrupt;
267
+ ops->cpus_are_resettable = nitro_cpus_are_resettable;
268
+}
269
+
270
+static const TypeInfo nitro_accel_ops_type = {
271
+ .name = ACCEL_OPS_NAME("nitro"),
272
+ .parent = TYPE_ACCEL_OPS,
273
+ .class_init = nitro_accel_ops_class_init,
274
+ .abstract = true,
275
+};
276
+module_obj(ACCEL_OPS_NAME("nitro"));
277
+
278
+static void nitro_type_init(void)
279
+{
280
+ type_register_static(&nitro_accel_type);
281
+ type_register_static(&nitro_accel_ops_type);
282
+}
283
+
284
+type_init(nitro_type_init);
accel/nitro/trace-events
new
+6
@@ -0,0 +1,6 @@
1
+# SPDX-License-Identifier: GPL-2.0-or-later
2
+#
3
+# See docs/devel/tracing.rst for syntax documentation.
4
+
5
+# nitro-accel.c
6
+nitro_enclave_started(uint64_t cid) "nitro: enclave started, CID=%"PRIu64
accel/nitro/trace.h
new
+2
@@ -0,0 +1,2 @@
1
+/* SPDX-License-Identifier: GPL-2.0-or-later */
2
+#include "trace/trace-accel_nitro.h"
accel/stubs/meson.build
+1
@@ -3,6 +3,7 @@ system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c'))
3
system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))
4
system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c'))
5
system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c'))
6
+system_stubs_ss.add(when: 'CONFIG_NITRO', if_false: files('nitro-stub.c'))
7
system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c'))
8
system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c'))
9
system_stubs_ss.add(when: 'CONFIG_MSHV', if_false: files('mshv-stub.c'))
accel/stubs/nitro-stub.c
new
+11
@@ -0,0 +1,11 @@
1
+/*
2
+ * Nitro accel stubs for QEMU
3
+ *
4
+ * Copyright © 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
+ *
6
+ * SPDX-License-Identifier: GPL-2.0-or-later
7
+ */
8
+
9
+#include "qemu/osdep.h"
10
+
11
+bool nitro_allowed;
include/system/hw_accel.h
+1
@@ -17,6 +17,7 @@
17
#include "system/mshv.h"
18
#include "system/whpx.h"
19
#include "system/nvmm.h"
20
+#include "system/nitro-accel.h"
21
22
/**
23
* cpu_synchronize_state:
include/system/nitro-accel.h
new
+25
@@ -0,0 +1,25 @@
1
+/*
2
+ * Nitro Enclaves accelerator - public interface
3
+ *
4
+ * SPDX-License-Identifier: GPL-2.0-or-later
5
+ */
6
+
7
+#ifndef SYSTEM_NITRO_ACCEL_H
8
+#define SYSTEM_NITRO_ACCEL_H
9
+
10
+#include "qemu/accel.h"
11
+
12
+extern bool nitro_allowed;
13
+
14
+static inline bool nitro_enabled(void)
15
+{
16
+ return nitro_allowed;
17
+}
18
+
19
+#define TYPE_NITRO_ACCEL ACCEL_CLASS_NAME("nitro")
20
+
21
+typedef struct NitroAccelState NitroAccelState;
22
+DECLARE_INSTANCE_CHECKER(NitroAccelState, NITRO_ACCEL,
23
+ TYPE_NITRO_ACCEL)
24
+
25
+#endif /* SYSTEM_NITRO_ACCEL_H */
meson.build
+12
@@ -302,11 +302,13 @@ accelerator_targets += { 'CONFIG_XEN': xen_targets }
302
if cpu == 'aarch64'
303
accelerator_targets += {
304
'CONFIG_HVF': ['aarch64-softmmu'],
305
+ 'CONFIG_NITRO': ['aarch64-softmmu'],
306
'CONFIG_WHPX': ['aarch64-softmmu']
307
}
308
elif cpu == 'x86_64'
309
accelerator_targets += {
310
'CONFIG_HVF': ['x86_64-softmmu'],
311
+ 'CONFIG_NITRO': ['x86_64-softmmu'],
312
'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'],
313
'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'],
314
'CONFIG_MSHV': ['x86_64-softmmu'],
@@ -880,6 +882,11 @@ if get_option('hvf').allowed()
882
endif
883
endif
884
885
+nitro = not_found
886
+if get_option('nitro').allowed() and host_os == 'linux'
887
+ accelerators += 'CONFIG_NITRO'
888
+endif
889
+
890
nvmm = not_found
891
if host_os == 'netbsd'
892
nvmm = cc.find_library('nvmm', required: get_option('nvmm'))
@@ -921,6 +928,9 @@ endif
928
if 'CONFIG_HVF' not in accelerators and get_option('hvf').enabled()
929
error('HVF not available on this platform')
930
endif
931
+if 'CONFIG_NITRO' not in accelerators and get_option('nitro').enabled()
932
+ error('NITRO not available on this platform')
933
+endif
934
if 'CONFIG_NVMM' not in accelerators and get_option('nvmm').enabled()
935
error('NVMM not available on this platform')
936
endif
@@ -3590,6 +3600,7 @@ if have_system
3600
'accel/hvf',
3601
'accel/kvm',
3602
'accel/mshv',
3603
+ 'accel/nitro',
3604
'audio',
3605
'backends',
3606
'backends/tpm',
@@ -4789,6 +4800,7 @@ endif
4800
summary_info = {}
4801
if have_system
4802
summary_info += {'KVM support': config_all_accel.has_key('CONFIG_KVM')}
4803
+ summary_info += {'Nitro support': config_all_accel.has_key('CONFIG_NITRO')}
4804
summary_info += {'HVF support': config_all_accel.has_key('CONFIG_HVF')}
4805
summary_info += {'WHPX support': config_all_accel.has_key('CONFIG_WHPX')}
4806
summary_info += {'NVMM support': config_all_accel.has_key('CONFIG_NVMM')}
meson_options.txt
+2
@@ -79,6 +79,8 @@ option('whpx', type: 'feature', value: 'auto',
79
description: 'WHPX acceleration support')
80
option('hvf', type: 'feature', value: 'auto',
81
description: 'HVF acceleration support')
82
+option('nitro', type: 'feature', value: 'auto',
83
+ description: 'Nitro acceleration support')
84
option('nvmm', type: 'feature', value: 'auto',
85
description: 'NVMM acceleration support')
86
option('xen', type: 'feature', value: 'auto',
qemu-options.hx
+4
-4
@@ -28,7 +28,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
28
"-machine [type=]name[,prop[=value][,...]]\n"
29
" selects emulated machine ('-machine help' for list)\n"
30
" property accel=accel1[:accel2[:...]] selects accelerator\n"
31
- " supported accelerators are kvm, xen, hvf, nvmm, whpx, mshv or tcg (default: tcg)\n"
31
+ " supported accelerators are kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg (default: tcg)\n"
32
" vmport=on|off|auto controls emulation of vmport (default: auto)\n"
33
" dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
34
" mem-merge=on|off controls memory merge support (default: on)\n"
@@ -67,7 +67,7 @@ SRST
67
68
``accel=accels1[:accels2[:...]]``
69
This is used to enable an accelerator. Depending on the target
70
- architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be
70
+ architecture, kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg can be
71
available. By default, tcg is used. If there is more than one
72
accelerator specified, the next one is used if the previous one
73
fails to initialize.
@@ -228,7 +228,7 @@ ERST
228
229
DEF("accel", HAS_ARG, QEMU_OPTION_accel,
230
"-accel [accel=]accelerator[,prop[=value][,...]]\n"
231
- " select accelerator (kvm, xen, hvf, nvmm, whpx, mshv or tcg; use 'help' for a list)\n"
231
+ " select accelerator (kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg; use 'help' for a list)\n"
232
" igd-passthru=on|off (enable Xen integrated Intel graphics passthrough, default=off)\n"
233
" kernel-irqchip=on|off|split controls accelerated irqchip support (default=on)\n"
234
" kvm-shadow-mem=size of KVM shadow MMU in bytes\n"
@@ -243,7 +243,7 @@ DEF("accel", HAS_ARG, QEMU_OPTION_accel,
243
SRST
244
``-accel name[,prop=value[,...]]``
245
This is used to enable an accelerator. Depending on the target
246
- architecture, kvm, xen, hvf, nvmm, whpx, mshv or tcg can be available.
246
+ architecture, kvm, xen, hvf, nitro, nvmm, whpx, mshv or tcg can be available.
247
By default, tcg is used. If there is more than one accelerator
248
specified, the next one is used if the previous one fails to
249
initialize.
scripts/meson-buildoptions.sh
+3
@@ -158,6 +158,7 @@ meson_options_help() {
158
printf "%s\n" ' multiprocess Out of process device emulation support'
159
printf "%s\n" ' netmap netmap network backend support'
160
printf "%s\n" ' nettle nettle cryptography support'
161
+ printf "%s\n" ' nitro Nitro acceleration support'
162
printf "%s\n" ' numa libnuma support'
163
printf "%s\n" ' nvmm NVMM acceleration support'
164
printf "%s\n" ' opengl OpenGL support'
@@ -418,6 +419,8 @@ _meson_option_parse() {
419
--disable-netmap) printf "%s" -Dnetmap=disabled ;;
420
--enable-nettle) printf "%s" -Dnettle=enabled ;;
421
--disable-nettle) printf "%s" -Dnettle=disabled ;;
422
+ --enable-nitro) printf "%s" -Dnitro=enabled ;;
423
+ --disable-nitro) printf "%s" -Dnitro=disabled ;;
424
--enable-numa) printf "%s" -Dnuma=enabled ;;
425
--disable-numa) printf "%s" -Dnuma=disabled ;;
426
--enable-nvmm) printf "%s" -Dnvmm=enabled ;;