master
c 284 lines 8.35 KB
Raw
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, &region) < 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);