]>
Commit | Line | Data |
---|---|---|
a9b4942f BS |
1 | /* |
2 | * QEMU SEV support | |
3 | * | |
4 | * Copyright Advanced Micro Devices 2016-2018 | |
5 | * | |
6 | * Author: | |
7 | * Brijesh Singh <brijesh.singh@amd.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | * | |
12 | */ | |
13 | ||
b7d89466 MA |
14 | #include "qemu/osdep.h" |
15 | ||
d8575c6c BS |
16 | #include <linux/kvm.h> |
17 | #include <linux/psp-sev.h> | |
18 | ||
19 | #include <sys/ioctl.h> | |
20 | ||
a9b4942f BS |
21 | #include "qapi/error.h" |
22 | #include "qom/object_interfaces.h" | |
23 | #include "qemu/base64.h" | |
0b8fa32f | 24 | #include "qemu/module.h" |
a9b4942f BS |
25 | #include "sysemu/kvm.h" |
26 | #include "sev_i386.h" | |
27 | #include "sysemu/sysemu.h" | |
54d31236 | 28 | #include "sysemu/runstate.h" |
d8575c6c | 29 | #include "trace.h" |
8fa4466d | 30 | #include "migration/blocker.h" |
a9b4942f BS |
31 | |
32 | #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ | |
33 | #define DEFAULT_SEV_DEVICE "/dev/sev" | |
34 | ||
d8575c6c | 35 | static SEVState *sev_state; |
8fa4466d | 36 | static Error *sev_mig_blocker; |
d8575c6c BS |
37 | |
38 | static const char *const sev_fw_errlist[] = { | |
39 | "", | |
40 | "Platform state is invalid", | |
41 | "Guest state is invalid", | |
42 | "Platform configuration is invalid", | |
43 | "Buffer too small", | |
44 | "Platform is already owned", | |
45 | "Certificate is invalid", | |
46 | "Policy is not allowed", | |
47 | "Guest is not active", | |
48 | "Invalid address", | |
49 | "Bad signature", | |
50 | "Bad measurement", | |
51 | "Asid is already owned", | |
52 | "Invalid ASID", | |
53 | "WBINVD is required", | |
54 | "DF_FLUSH is required", | |
55 | "Guest handle is invalid", | |
56 | "Invalid command", | |
57 | "Guest is active", | |
58 | "Hardware error", | |
59 | "Hardware unsafe", | |
60 | "Feature not supported", | |
61 | "Invalid parameter" | |
62 | }; | |
63 | ||
64 | #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) | |
65 | ||
66 | static int | |
67 | sev_ioctl(int fd, int cmd, void *data, int *error) | |
68 | { | |
69 | int r; | |
70 | struct kvm_sev_cmd input; | |
71 | ||
72 | memset(&input, 0x0, sizeof(input)); | |
73 | ||
74 | input.id = cmd; | |
75 | input.sev_fd = fd; | |
76 | input.data = (__u64)(unsigned long)data; | |
77 | ||
78 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input); | |
79 | ||
80 | if (error) { | |
81 | *error = input.error; | |
82 | } | |
83 | ||
84 | return r; | |
85 | } | |
86 | ||
87 | static int | |
88 | sev_platform_ioctl(int fd, int cmd, void *data, int *error) | |
89 | { | |
90 | int r; | |
91 | struct sev_issue_cmd arg; | |
92 | ||
93 | arg.cmd = cmd; | |
94 | arg.data = (unsigned long)data; | |
95 | r = ioctl(fd, SEV_ISSUE_CMD, &arg); | |
96 | if (error) { | |
97 | *error = arg.error; | |
98 | } | |
99 | ||
100 | return r; | |
101 | } | |
102 | ||
103 | static const char * | |
104 | fw_error_to_str(int code) | |
105 | { | |
106 | if (code < 0 || code >= SEV_FW_MAX_ERROR) { | |
107 | return "unknown error"; | |
108 | } | |
109 | ||
110 | return sev_fw_errlist[code]; | |
111 | } | |
112 | ||
b738d630 BS |
113 | static bool |
114 | sev_check_state(SevState state) | |
115 | { | |
116 | assert(sev_state); | |
117 | return sev_state->state == state ? true : false; | |
118 | } | |
119 | ||
620fd55c BS |
120 | static void |
121 | sev_set_guest_state(SevState new_state) | |
122 | { | |
123 | assert(new_state < SEV_STATE__MAX); | |
124 | assert(sev_state); | |
125 | ||
126 | trace_kvm_sev_change_state(SevState_str(sev_state->state), | |
127 | SevState_str(new_state)); | |
128 | sev_state->state = new_state; | |
129 | } | |
130 | ||
2b308e44 BS |
131 | static void |
132 | sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) | |
133 | { | |
134 | int r; | |
135 | struct kvm_enc_region range; | |
cedc0ad5 BS |
136 | ram_addr_t offset; |
137 | MemoryRegion *mr; | |
138 | ||
139 | /* | |
140 | * The RAM device presents a memory region that should be treated | |
141 | * as IO region and should not be pinned. | |
142 | */ | |
143 | mr = memory_region_from_host(host, &offset); | |
144 | if (mr && memory_region_is_ram_device(mr)) { | |
145 | return; | |
146 | } | |
2b308e44 BS |
147 | |
148 | range.addr = (__u64)(unsigned long)host; | |
149 | range.size = size; | |
150 | ||
151 | trace_kvm_memcrypt_register_region(host, size); | |
152 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); | |
153 | if (r) { | |
154 | error_report("%s: failed to register region (%p+%#zx) error '%s'", | |
155 | __func__, host, size, strerror(errno)); | |
156 | exit(1); | |
157 | } | |
158 | } | |
159 | ||
160 | static void | |
161 | sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size) | |
162 | { | |
163 | int r; | |
164 | struct kvm_enc_region range; | |
56e2ec94 AW |
165 | ram_addr_t offset; |
166 | MemoryRegion *mr; | |
167 | ||
168 | /* | |
169 | * The RAM device presents a memory region that should be treated | |
170 | * as IO region and should not have been pinned. | |
171 | */ | |
172 | mr = memory_region_from_host(host, &offset); | |
173 | if (mr && memory_region_is_ram_device(mr)) { | |
174 | return; | |
175 | } | |
2b308e44 BS |
176 | |
177 | range.addr = (__u64)(unsigned long)host; | |
178 | range.size = size; | |
179 | ||
180 | trace_kvm_memcrypt_unregister_region(host, size); | |
181 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); | |
182 | if (r) { | |
183 | error_report("%s: failed to unregister region (%p+%#zx)", | |
184 | __func__, host, size); | |
185 | } | |
186 | } | |
187 | ||
188 | static struct RAMBlockNotifier sev_ram_notifier = { | |
189 | .ram_block_added = sev_ram_block_added, | |
190 | .ram_block_removed = sev_ram_block_removed, | |
191 | }; | |
192 | ||
a9b4942f BS |
193 | static void |
194 | qsev_guest_finalize(Object *obj) | |
195 | { | |
196 | } | |
197 | ||
198 | static char * | |
199 | qsev_guest_get_session_file(Object *obj, Error **errp) | |
200 | { | |
201 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
202 | ||
203 | return s->session_file ? g_strdup(s->session_file) : NULL; | |
204 | } | |
205 | ||
206 | static void | |
207 | qsev_guest_set_session_file(Object *obj, const char *value, Error **errp) | |
208 | { | |
209 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
210 | ||
211 | s->session_file = g_strdup(value); | |
212 | } | |
213 | ||
214 | static char * | |
215 | qsev_guest_get_dh_cert_file(Object *obj, Error **errp) | |
216 | { | |
217 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
218 | ||
219 | return g_strdup(s->dh_cert_file); | |
220 | } | |
221 | ||
222 | static void | |
223 | qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) | |
224 | { | |
225 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
226 | ||
227 | s->dh_cert_file = g_strdup(value); | |
228 | } | |
229 | ||
230 | static char * | |
231 | qsev_guest_get_sev_device(Object *obj, Error **errp) | |
232 | { | |
233 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
234 | ||
235 | return g_strdup(sev->sev_device); | |
236 | } | |
237 | ||
238 | static void | |
239 | qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp) | |
240 | { | |
241 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
242 | ||
243 | sev->sev_device = g_strdup(value); | |
244 | } | |
245 | ||
246 | static void | |
247 | qsev_guest_class_init(ObjectClass *oc, void *data) | |
248 | { | |
249 | object_class_property_add_str(oc, "sev-device", | |
250 | qsev_guest_get_sev_device, | |
251 | qsev_guest_set_sev_device, | |
252 | NULL); | |
253 | object_class_property_set_description(oc, "sev-device", | |
254 | "SEV device to use", NULL); | |
255 | object_class_property_add_str(oc, "dh-cert-file", | |
256 | qsev_guest_get_dh_cert_file, | |
257 | qsev_guest_set_dh_cert_file, | |
258 | NULL); | |
259 | object_class_property_set_description(oc, "dh-cert-file", | |
260 | "guest owners DH certificate (encoded with base64)", NULL); | |
261 | object_class_property_add_str(oc, "session-file", | |
262 | qsev_guest_get_session_file, | |
263 | qsev_guest_set_session_file, | |
264 | NULL); | |
265 | object_class_property_set_description(oc, "session-file", | |
266 | "guest owners session parameters (encoded with base64)", NULL); | |
267 | } | |
268 | ||
269 | static void | |
270 | qsev_guest_set_handle(Object *obj, Visitor *v, const char *name, | |
271 | void *opaque, Error **errp) | |
272 | { | |
273 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
274 | uint32_t value; | |
275 | ||
276 | visit_type_uint32(v, name, &value, errp); | |
277 | sev->handle = value; | |
278 | } | |
279 | ||
280 | static void | |
281 | qsev_guest_set_policy(Object *obj, Visitor *v, const char *name, | |
282 | void *opaque, Error **errp) | |
283 | { | |
284 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
285 | uint32_t value; | |
286 | ||
287 | visit_type_uint32(v, name, &value, errp); | |
288 | sev->policy = value; | |
289 | } | |
290 | ||
291 | static void | |
292 | qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name, | |
293 | void *opaque, Error **errp) | |
294 | { | |
295 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
296 | uint32_t value; | |
297 | ||
298 | visit_type_uint32(v, name, &value, errp); | |
299 | sev->cbitpos = value; | |
300 | } | |
301 | ||
302 | static void | |
303 | qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name, | |
304 | void *opaque, Error **errp) | |
305 | { | |
306 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
307 | uint32_t value; | |
308 | ||
309 | visit_type_uint32(v, name, &value, errp); | |
310 | sev->reduced_phys_bits = value; | |
311 | } | |
312 | ||
313 | static void | |
314 | qsev_guest_get_policy(Object *obj, Visitor *v, const char *name, | |
315 | void *opaque, Error **errp) | |
316 | { | |
317 | uint32_t value; | |
318 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
319 | ||
320 | value = sev->policy; | |
321 | visit_type_uint32(v, name, &value, errp); | |
322 | } | |
323 | ||
324 | static void | |
325 | qsev_guest_get_handle(Object *obj, Visitor *v, const char *name, | |
326 | void *opaque, Error **errp) | |
327 | { | |
328 | uint32_t value; | |
329 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
330 | ||
331 | value = sev->handle; | |
332 | visit_type_uint32(v, name, &value, errp); | |
333 | } | |
334 | ||
335 | static void | |
336 | qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name, | |
337 | void *opaque, Error **errp) | |
338 | { | |
339 | uint32_t value; | |
340 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
341 | ||
342 | value = sev->cbitpos; | |
343 | visit_type_uint32(v, name, &value, errp); | |
344 | } | |
345 | ||
346 | static void | |
347 | qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name, | |
348 | void *opaque, Error **errp) | |
349 | { | |
350 | uint32_t value; | |
351 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
352 | ||
353 | value = sev->reduced_phys_bits; | |
354 | visit_type_uint32(v, name, &value, errp); | |
355 | } | |
356 | ||
357 | static void | |
358 | qsev_guest_init(Object *obj) | |
359 | { | |
360 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
361 | ||
362 | sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); | |
363 | sev->policy = DEFAULT_GUEST_POLICY; | |
364 | object_property_add(obj, "policy", "uint32", qsev_guest_get_policy, | |
365 | qsev_guest_set_policy, NULL, NULL, NULL); | |
366 | object_property_add(obj, "handle", "uint32", qsev_guest_get_handle, | |
367 | qsev_guest_set_handle, NULL, NULL, NULL); | |
368 | object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos, | |
369 | qsev_guest_set_cbitpos, NULL, NULL, NULL); | |
370 | object_property_add(obj, "reduced-phys-bits", "uint32", | |
371 | qsev_guest_get_reduced_phys_bits, | |
372 | qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL); | |
373 | } | |
374 | ||
375 | /* sev guest info */ | |
376 | static const TypeInfo qsev_guest_info = { | |
377 | .parent = TYPE_OBJECT, | |
378 | .name = TYPE_QSEV_GUEST_INFO, | |
379 | .instance_size = sizeof(QSevGuestInfo), | |
380 | .instance_finalize = qsev_guest_finalize, | |
381 | .class_size = sizeof(QSevGuestInfoClass), | |
382 | .class_init = qsev_guest_class_init, | |
383 | .instance_init = qsev_guest_init, | |
384 | .interfaces = (InterfaceInfo[]) { | |
385 | { TYPE_USER_CREATABLE }, | |
386 | { } | |
387 | } | |
388 | }; | |
389 | ||
d8575c6c BS |
390 | static QSevGuestInfo * |
391 | lookup_sev_guest_info(const char *id) | |
392 | { | |
393 | Object *obj; | |
394 | QSevGuestInfo *info; | |
395 | ||
396 | obj = object_resolve_path_component(object_get_objects_root(), id); | |
397 | if (!obj) { | |
398 | return NULL; | |
399 | } | |
400 | ||
401 | info = (QSevGuestInfo *) | |
402 | object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO); | |
403 | if (!info) { | |
404 | return NULL; | |
405 | } | |
406 | ||
407 | return info; | |
408 | } | |
409 | ||
410 | bool | |
411 | sev_enabled(void) | |
412 | { | |
413 | return sev_state ? true : false; | |
414 | } | |
415 | ||
416 | uint64_t | |
417 | sev_get_me_mask(void) | |
418 | { | |
419 | return sev_state ? sev_state->me_mask : ~0; | |
420 | } | |
421 | ||
422 | uint32_t | |
423 | sev_get_cbit_position(void) | |
424 | { | |
425 | return sev_state ? sev_state->cbitpos : 0; | |
426 | } | |
427 | ||
428 | uint32_t | |
429 | sev_get_reduced_phys_bits(void) | |
430 | { | |
431 | return sev_state ? sev_state->reduced_phys_bits : 0; | |
432 | } | |
433 | ||
434 | SevInfo * | |
435 | sev_get_info(void) | |
436 | { | |
437 | SevInfo *info; | |
438 | ||
439 | info = g_new0(SevInfo, 1); | |
440 | info->enabled = sev_state ? true : false; | |
441 | ||
442 | if (info->enabled) { | |
443 | info->api_major = sev_state->api_major; | |
444 | info->api_minor = sev_state->api_minor; | |
445 | info->build_id = sev_state->build_id; | |
446 | info->policy = sev_state->policy; | |
447 | info->state = sev_state->state; | |
448 | info->handle = sev_state->handle; | |
449 | } | |
450 | ||
451 | return info; | |
452 | } | |
453 | ||
9f750794 BS |
454 | static int |
455 | sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, | |
456 | size_t *cert_chain_len) | |
457 | { | |
bf3175b4 PB |
458 | guchar *pdh_data = NULL; |
459 | guchar *cert_chain_data = NULL; | |
9f750794 BS |
460 | struct sev_user_data_pdh_cert_export export = {}; |
461 | int err, r; | |
462 | ||
463 | /* query the certificate length */ | |
464 | r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); | |
465 | if (r < 0) { | |
466 | if (err != SEV_RET_INVALID_LEN) { | |
467 | error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", | |
468 | r, err, fw_error_to_str(err)); | |
469 | return 1; | |
470 | } | |
471 | } | |
472 | ||
473 | pdh_data = g_new(guchar, export.pdh_cert_len); | |
474 | cert_chain_data = g_new(guchar, export.cert_chain_len); | |
475 | export.pdh_cert_address = (unsigned long)pdh_data; | |
476 | export.cert_chain_address = (unsigned long)cert_chain_data; | |
477 | ||
478 | r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); | |
479 | if (r < 0) { | |
480 | error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", | |
481 | r, err, fw_error_to_str(err)); | |
482 | goto e_free; | |
483 | } | |
484 | ||
485 | *pdh = pdh_data; | |
486 | *pdh_len = export.pdh_cert_len; | |
487 | *cert_chain = cert_chain_data; | |
488 | *cert_chain_len = export.cert_chain_len; | |
489 | return 0; | |
490 | ||
491 | e_free: | |
492 | g_free(pdh_data); | |
493 | g_free(cert_chain_data); | |
494 | return 1; | |
495 | } | |
496 | ||
497 | SevCapability * | |
498 | sev_get_capabilities(void) | |
499 | { | |
bf3175b4 PB |
500 | SevCapability *cap = NULL; |
501 | guchar *pdh_data = NULL; | |
502 | guchar *cert_chain_data = NULL; | |
9f750794 BS |
503 | size_t pdh_len = 0, cert_chain_len = 0; |
504 | uint32_t ebx; | |
505 | int fd; | |
506 | ||
507 | fd = open(DEFAULT_SEV_DEVICE, O_RDWR); | |
508 | if (fd < 0) { | |
509 | error_report("%s: Failed to open %s '%s'", __func__, | |
510 | DEFAULT_SEV_DEVICE, strerror(errno)); | |
511 | return NULL; | |
512 | } | |
513 | ||
514 | if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, | |
515 | &cert_chain_data, &cert_chain_len)) { | |
bf3175b4 | 516 | goto out; |
9f750794 BS |
517 | } |
518 | ||
519 | cap = g_new0(SevCapability, 1); | |
520 | cap->pdh = g_base64_encode(pdh_data, pdh_len); | |
521 | cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len); | |
522 | ||
523 | host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); | |
524 | cap->cbitpos = ebx & 0x3f; | |
525 | ||
526 | /* | |
527 | * When SEV feature is enabled, we loose one bit in guest physical | |
528 | * addressing. | |
529 | */ | |
530 | cap->reduced_phys_bits = 1; | |
531 | ||
bf3175b4 | 532 | out: |
9f750794 BS |
533 | g_free(pdh_data); |
534 | g_free(cert_chain_data); | |
9f750794 BS |
535 | close(fd); |
536 | return cap; | |
537 | } | |
538 | ||
620fd55c BS |
539 | static int |
540 | sev_read_file_base64(const char *filename, guchar **data, gsize *len) | |
541 | { | |
542 | gsize sz; | |
543 | gchar *base64; | |
544 | GError *error = NULL; | |
545 | ||
546 | if (!g_file_get_contents(filename, &base64, &sz, &error)) { | |
547 | error_report("failed to read '%s' (%s)", filename, error->message); | |
548 | return -1; | |
549 | } | |
550 | ||
551 | *data = g_base64_decode(base64, len); | |
552 | return 0; | |
553 | } | |
554 | ||
555 | static int | |
556 | sev_launch_start(SEVState *s) | |
557 | { | |
558 | gsize sz; | |
559 | int ret = 1; | |
bf3175b4 | 560 | int fw_error, rc; |
620fd55c BS |
561 | QSevGuestInfo *sev = s->sev_info; |
562 | struct kvm_sev_launch_start *start; | |
563 | guchar *session = NULL, *dh_cert = NULL; | |
564 | ||
565 | start = g_new0(struct kvm_sev_launch_start, 1); | |
566 | ||
567 | start->handle = object_property_get_int(OBJECT(sev), "handle", | |
568 | &error_abort); | |
569 | start->policy = object_property_get_int(OBJECT(sev), "policy", | |
570 | &error_abort); | |
571 | if (sev->session_file) { | |
572 | if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { | |
bf3175b4 | 573 | goto out; |
620fd55c BS |
574 | } |
575 | start->session_uaddr = (unsigned long)session; | |
576 | start->session_len = sz; | |
577 | } | |
578 | ||
579 | if (sev->dh_cert_file) { | |
580 | if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { | |
bf3175b4 | 581 | goto out; |
620fd55c BS |
582 | } |
583 | start->dh_uaddr = (unsigned long)dh_cert; | |
584 | start->dh_len = sz; | |
585 | } | |
586 | ||
587 | trace_kvm_sev_launch_start(start->policy, session, dh_cert); | |
bf3175b4 PB |
588 | rc = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); |
589 | if (rc < 0) { | |
620fd55c BS |
590 | error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", |
591 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
bf3175b4 | 592 | goto out; |
620fd55c BS |
593 | } |
594 | ||
595 | object_property_set_int(OBJECT(sev), start->handle, "handle", | |
596 | &error_abort); | |
597 | sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); | |
598 | s->handle = start->handle; | |
599 | s->policy = start->policy; | |
bf3175b4 | 600 | ret = 0; |
620fd55c | 601 | |
bf3175b4 | 602 | out: |
620fd55c BS |
603 | g_free(start); |
604 | g_free(session); | |
605 | g_free(dh_cert); | |
bf3175b4 | 606 | return ret; |
620fd55c BS |
607 | } |
608 | ||
b738d630 BS |
609 | static int |
610 | sev_launch_update_data(uint8_t *addr, uint64_t len) | |
611 | { | |
612 | int ret, fw_error; | |
613 | struct kvm_sev_launch_update_data update; | |
614 | ||
615 | if (!addr || !len) { | |
616 | return 1; | |
617 | } | |
618 | ||
619 | update.uaddr = (__u64)(unsigned long)addr; | |
620 | update.len = len; | |
621 | trace_kvm_sev_launch_update_data(addr, len); | |
622 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, | |
623 | &update, &fw_error); | |
624 | if (ret) { | |
625 | error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", | |
626 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
627 | } | |
628 | ||
629 | return ret; | |
630 | } | |
631 | ||
c6c89c97 BS |
632 | static void |
633 | sev_launch_get_measure(Notifier *notifier, void *unused) | |
634 | { | |
635 | int ret, error; | |
636 | guchar *data; | |
637 | SEVState *s = sev_state; | |
638 | struct kvm_sev_launch_measure *measurement; | |
639 | ||
640 | if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { | |
641 | return; | |
642 | } | |
643 | ||
644 | measurement = g_new0(struct kvm_sev_launch_measure, 1); | |
645 | ||
646 | /* query the measurement blob length */ | |
647 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, | |
648 | measurement, &error); | |
649 | if (!measurement->len) { | |
650 | error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", | |
651 | __func__, ret, error, fw_error_to_str(errno)); | |
652 | goto free_measurement; | |
653 | } | |
654 | ||
655 | data = g_new0(guchar, measurement->len); | |
656 | measurement->uaddr = (unsigned long)data; | |
657 | ||
658 | /* get the measurement blob */ | |
659 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, | |
660 | measurement, &error); | |
661 | if (ret) { | |
662 | error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", | |
663 | __func__, ret, error, fw_error_to_str(errno)); | |
664 | goto free_data; | |
665 | } | |
666 | ||
667 | sev_set_guest_state(SEV_STATE_LAUNCH_SECRET); | |
668 | ||
669 | /* encode the measurement value and emit the event */ | |
670 | s->measurement = g_base64_encode(data, measurement->len); | |
671 | trace_kvm_sev_launch_measurement(s->measurement); | |
672 | ||
673 | free_data: | |
674 | g_free(data); | |
675 | free_measurement: | |
676 | g_free(measurement); | |
677 | } | |
678 | ||
679 | char * | |
680 | sev_get_launch_measurement(void) | |
681 | { | |
682 | if (sev_state && | |
683 | sev_state->state >= SEV_STATE_LAUNCH_SECRET) { | |
684 | return g_strdup(sev_state->measurement); | |
685 | } | |
686 | ||
687 | return NULL; | |
688 | } | |
689 | ||
690 | static Notifier sev_machine_done_notify = { | |
691 | .notify = sev_launch_get_measure, | |
692 | }; | |
693 | ||
5dd0df7e BS |
694 | static void |
695 | sev_launch_finish(SEVState *s) | |
696 | { | |
697 | int ret, error; | |
8fa4466d | 698 | Error *local_err = NULL; |
5dd0df7e BS |
699 | |
700 | trace_kvm_sev_launch_finish(); | |
701 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); | |
702 | if (ret) { | |
703 | error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", | |
704 | __func__, ret, error, fw_error_to_str(error)); | |
705 | exit(1); | |
706 | } | |
707 | ||
708 | sev_set_guest_state(SEV_STATE_RUNNING); | |
8fa4466d BS |
709 | |
710 | /* add migration blocker */ | |
711 | error_setg(&sev_mig_blocker, | |
712 | "SEV: Migration is not implemented"); | |
713 | ret = migrate_add_blocker(sev_mig_blocker, &local_err); | |
714 | if (local_err) { | |
715 | error_report_err(local_err); | |
716 | error_free(sev_mig_blocker); | |
717 | exit(1); | |
718 | } | |
5dd0df7e BS |
719 | } |
720 | ||
721 | static void | |
722 | sev_vm_state_change(void *opaque, int running, RunState state) | |
723 | { | |
724 | SEVState *s = opaque; | |
725 | ||
726 | if (running) { | |
727 | if (!sev_check_state(SEV_STATE_RUNNING)) { | |
728 | sev_launch_finish(s); | |
729 | } | |
730 | } | |
731 | } | |
732 | ||
d8575c6c BS |
733 | void * |
734 | sev_guest_init(const char *id) | |
735 | { | |
736 | SEVState *s; | |
737 | char *devname; | |
738 | int ret, fw_error; | |
739 | uint32_t ebx; | |
740 | uint32_t host_cbitpos; | |
741 | struct sev_user_data_status status = {}; | |
742 | ||
bf3175b4 | 743 | sev_state = s = g_new0(SEVState, 1); |
d8575c6c BS |
744 | s->sev_info = lookup_sev_guest_info(id); |
745 | if (!s->sev_info) { | |
746 | error_report("%s: '%s' is not a valid '%s' object", | |
747 | __func__, id, TYPE_QSEV_GUEST_INFO); | |
748 | goto err; | |
749 | } | |
750 | ||
d8575c6c BS |
751 | s->state = SEV_STATE_UNINIT; |
752 | ||
753 | host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); | |
754 | host_cbitpos = ebx & 0x3f; | |
755 | ||
756 | s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL); | |
757 | if (host_cbitpos != s->cbitpos) { | |
758 | error_report("%s: cbitpos check failed, host '%d' requested '%d'", | |
759 | __func__, host_cbitpos, s->cbitpos); | |
760 | goto err; | |
761 | } | |
762 | ||
763 | s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info), | |
764 | "reduced-phys-bits", NULL); | |
765 | if (s->reduced_phys_bits < 1) { | |
766 | error_report("%s: reduced_phys_bits check failed, it should be >=1," | |
d4b976c0 | 767 | " requested '%d'", __func__, s->reduced_phys_bits); |
d8575c6c BS |
768 | goto err; |
769 | } | |
770 | ||
771 | s->me_mask = ~(1UL << s->cbitpos); | |
772 | ||
773 | devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL); | |
774 | s->sev_fd = open(devname, O_RDWR); | |
775 | if (s->sev_fd < 0) { | |
776 | error_report("%s: Failed to open %s '%s'", __func__, | |
777 | devname, strerror(errno)); | |
d8575c6c BS |
778 | } |
779 | g_free(devname); | |
5d7bc72a GK |
780 | if (s->sev_fd < 0) { |
781 | goto err; | |
782 | } | |
d8575c6c BS |
783 | |
784 | ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status, | |
785 | &fw_error); | |
786 | if (ret) { | |
d4b976c0 | 787 | error_report("%s: failed to get platform status ret=%d " |
d8575c6c BS |
788 | "fw_error='%d: %s'", __func__, ret, fw_error, |
789 | fw_error_to_str(fw_error)); | |
790 | goto err; | |
791 | } | |
792 | s->build_id = status.build; | |
793 | s->api_major = status.api_major; | |
794 | s->api_minor = status.api_minor; | |
795 | ||
796 | trace_kvm_sev_init(); | |
797 | ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error); | |
798 | if (ret) { | |
799 | error_report("%s: failed to initialize ret=%d fw_error=%d '%s'", | |
800 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
801 | goto err; | |
802 | } | |
803 | ||
620fd55c BS |
804 | ret = sev_launch_start(s); |
805 | if (ret) { | |
806 | error_report("%s: failed to create encryption context", __func__); | |
807 | goto err; | |
808 | } | |
809 | ||
2b308e44 | 810 | ram_block_notifier_add(&sev_ram_notifier); |
c6c89c97 | 811 | qemu_add_machine_init_done_notifier(&sev_machine_done_notify); |
5dd0df7e | 812 | qemu_add_vm_change_state_handler(sev_vm_state_change, s); |
2b308e44 | 813 | |
d8575c6c BS |
814 | return s; |
815 | err: | |
816 | g_free(sev_state); | |
817 | sev_state = NULL; | |
818 | return NULL; | |
819 | } | |
820 | ||
b738d630 BS |
821 | int |
822 | sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) | |
823 | { | |
824 | assert(handle); | |
825 | ||
826 | /* if SEV is in update state then encrypt the data else do nothing */ | |
827 | if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { | |
828 | return sev_launch_update_data(ptr, len); | |
829 | } | |
830 | ||
831 | return 0; | |
832 | } | |
833 | ||
a9b4942f BS |
834 | static void |
835 | sev_register_types(void) | |
836 | { | |
837 | type_register_static(&qsev_guest_info); | |
838 | } | |
839 | ||
840 | type_init(sev_register_types); |