sev/i386: add support to LAUNCH_MEASURE command
[qemu/ar7.git] / target / i386 / sev.c
blob0132beaf24b4610d044d583f2b1c6fa78287dd88
1 /*
2 * QEMU SEV support
4 * Copyright Advanced Micro Devices 2016-2018
6 * Author:
7 * Brijesh Singh <brijesh.singh@amd.com>
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.
14 #include <linux/kvm.h>
15 #include <linux/psp-sev.h>
17 #include <sys/ioctl.h>
19 #include "qemu/osdep.h"
20 #include "qapi/error.h"
21 #include "qom/object_interfaces.h"
22 #include "qemu/base64.h"
23 #include "sysemu/kvm.h"
24 #include "sev_i386.h"
25 #include "sysemu/sysemu.h"
26 #include "trace.h"
28 #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */
29 #define DEFAULT_SEV_DEVICE "/dev/sev"
31 static SEVState *sev_state;
33 static const char *const sev_fw_errlist[] = {
34 "",
35 "Platform state is invalid",
36 "Guest state is invalid",
37 "Platform configuration is invalid",
38 "Buffer too small",
39 "Platform is already owned",
40 "Certificate is invalid",
41 "Policy is not allowed",
42 "Guest is not active",
43 "Invalid address",
44 "Bad signature",
45 "Bad measurement",
46 "Asid is already owned",
47 "Invalid ASID",
48 "WBINVD is required",
49 "DF_FLUSH is required",
50 "Guest handle is invalid",
51 "Invalid command",
52 "Guest is active",
53 "Hardware error",
54 "Hardware unsafe",
55 "Feature not supported",
56 "Invalid parameter"
59 #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist)
61 static int
62 sev_ioctl(int fd, int cmd, void *data, int *error)
64 int r;
65 struct kvm_sev_cmd input;
67 memset(&input, 0x0, sizeof(input));
69 input.id = cmd;
70 input.sev_fd = fd;
71 input.data = (__u64)(unsigned long)data;
73 r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input);
75 if (error) {
76 *error = input.error;
79 return r;
82 static int
83 sev_platform_ioctl(int fd, int cmd, void *data, int *error)
85 int r;
86 struct sev_issue_cmd arg;
88 arg.cmd = cmd;
89 arg.data = (unsigned long)data;
90 r = ioctl(fd, SEV_ISSUE_CMD, &arg);
91 if (error) {
92 *error = arg.error;
95 return r;
98 static const char *
99 fw_error_to_str(int code)
101 if (code < 0 || code >= SEV_FW_MAX_ERROR) {
102 return "unknown error";
105 return sev_fw_errlist[code];
108 static bool
109 sev_check_state(SevState state)
111 assert(sev_state);
112 return sev_state->state == state ? true : false;
115 static void
116 sev_set_guest_state(SevState new_state)
118 assert(new_state < SEV_STATE__MAX);
119 assert(sev_state);
121 trace_kvm_sev_change_state(SevState_str(sev_state->state),
122 SevState_str(new_state));
123 sev_state->state = new_state;
126 static void
127 sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size)
129 int r;
130 struct kvm_enc_region range;
132 range.addr = (__u64)(unsigned long)host;
133 range.size = size;
135 trace_kvm_memcrypt_register_region(host, size);
136 r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
137 if (r) {
138 error_report("%s: failed to register region (%p+%#zx) error '%s'",
139 __func__, host, size, strerror(errno));
140 exit(1);
144 static void
145 sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size)
147 int r;
148 struct kvm_enc_region range;
150 range.addr = (__u64)(unsigned long)host;
151 range.size = size;
153 trace_kvm_memcrypt_unregister_region(host, size);
154 r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range);
155 if (r) {
156 error_report("%s: failed to unregister region (%p+%#zx)",
157 __func__, host, size);
161 static struct RAMBlockNotifier sev_ram_notifier = {
162 .ram_block_added = sev_ram_block_added,
163 .ram_block_removed = sev_ram_block_removed,
166 static void
167 qsev_guest_finalize(Object *obj)
171 static char *
172 qsev_guest_get_session_file(Object *obj, Error **errp)
174 QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
176 return s->session_file ? g_strdup(s->session_file) : NULL;
179 static void
180 qsev_guest_set_session_file(Object *obj, const char *value, Error **errp)
182 QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
184 s->session_file = g_strdup(value);
187 static char *
188 qsev_guest_get_dh_cert_file(Object *obj, Error **errp)
190 QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
192 return g_strdup(s->dh_cert_file);
195 static void
196 qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp)
198 QSevGuestInfo *s = QSEV_GUEST_INFO(obj);
200 s->dh_cert_file = g_strdup(value);
203 static char *
204 qsev_guest_get_sev_device(Object *obj, Error **errp)
206 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
208 return g_strdup(sev->sev_device);
211 static void
212 qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp)
214 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
216 sev->sev_device = g_strdup(value);
219 static void
220 qsev_guest_class_init(ObjectClass *oc, void *data)
222 object_class_property_add_str(oc, "sev-device",
223 qsev_guest_get_sev_device,
224 qsev_guest_set_sev_device,
225 NULL);
226 object_class_property_set_description(oc, "sev-device",
227 "SEV device to use", NULL);
228 object_class_property_add_str(oc, "dh-cert-file",
229 qsev_guest_get_dh_cert_file,
230 qsev_guest_set_dh_cert_file,
231 NULL);
232 object_class_property_set_description(oc, "dh-cert-file",
233 "guest owners DH certificate (encoded with base64)", NULL);
234 object_class_property_add_str(oc, "session-file",
235 qsev_guest_get_session_file,
236 qsev_guest_set_session_file,
237 NULL);
238 object_class_property_set_description(oc, "session-file",
239 "guest owners session parameters (encoded with base64)", NULL);
242 static void
243 qsev_guest_set_handle(Object *obj, Visitor *v, const char *name,
244 void *opaque, Error **errp)
246 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
247 uint32_t value;
249 visit_type_uint32(v, name, &value, errp);
250 sev->handle = value;
253 static void
254 qsev_guest_set_policy(Object *obj, Visitor *v, const char *name,
255 void *opaque, Error **errp)
257 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
258 uint32_t value;
260 visit_type_uint32(v, name, &value, errp);
261 sev->policy = value;
264 static void
265 qsev_guest_set_cbitpos(Object *obj, Visitor *v, const char *name,
266 void *opaque, Error **errp)
268 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
269 uint32_t value;
271 visit_type_uint32(v, name, &value, errp);
272 sev->cbitpos = value;
275 static void
276 qsev_guest_set_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
277 void *opaque, Error **errp)
279 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
280 uint32_t value;
282 visit_type_uint32(v, name, &value, errp);
283 sev->reduced_phys_bits = value;
286 static void
287 qsev_guest_get_policy(Object *obj, Visitor *v, const char *name,
288 void *opaque, Error **errp)
290 uint32_t value;
291 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
293 value = sev->policy;
294 visit_type_uint32(v, name, &value, errp);
297 static void
298 qsev_guest_get_handle(Object *obj, Visitor *v, const char *name,
299 void *opaque, Error **errp)
301 uint32_t value;
302 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
304 value = sev->handle;
305 visit_type_uint32(v, name, &value, errp);
308 static void
309 qsev_guest_get_cbitpos(Object *obj, Visitor *v, const char *name,
310 void *opaque, Error **errp)
312 uint32_t value;
313 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
315 value = sev->cbitpos;
316 visit_type_uint32(v, name, &value, errp);
319 static void
320 qsev_guest_get_reduced_phys_bits(Object *obj, Visitor *v, const char *name,
321 void *opaque, Error **errp)
323 uint32_t value;
324 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
326 value = sev->reduced_phys_bits;
327 visit_type_uint32(v, name, &value, errp);
330 static void
331 qsev_guest_init(Object *obj)
333 QSevGuestInfo *sev = QSEV_GUEST_INFO(obj);
335 sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE);
336 sev->policy = DEFAULT_GUEST_POLICY;
337 object_property_add(obj, "policy", "uint32", qsev_guest_get_policy,
338 qsev_guest_set_policy, NULL, NULL, NULL);
339 object_property_add(obj, "handle", "uint32", qsev_guest_get_handle,
340 qsev_guest_set_handle, NULL, NULL, NULL);
341 object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos,
342 qsev_guest_set_cbitpos, NULL, NULL, NULL);
343 object_property_add(obj, "reduced-phys-bits", "uint32",
344 qsev_guest_get_reduced_phys_bits,
345 qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL);
348 /* sev guest info */
349 static const TypeInfo qsev_guest_info = {
350 .parent = TYPE_OBJECT,
351 .name = TYPE_QSEV_GUEST_INFO,
352 .instance_size = sizeof(QSevGuestInfo),
353 .instance_finalize = qsev_guest_finalize,
354 .class_size = sizeof(QSevGuestInfoClass),
355 .class_init = qsev_guest_class_init,
356 .instance_init = qsev_guest_init,
357 .interfaces = (InterfaceInfo[]) {
358 { TYPE_USER_CREATABLE },
363 static QSevGuestInfo *
364 lookup_sev_guest_info(const char *id)
366 Object *obj;
367 QSevGuestInfo *info;
369 obj = object_resolve_path_component(object_get_objects_root(), id);
370 if (!obj) {
371 return NULL;
374 info = (QSevGuestInfo *)
375 object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO);
376 if (!info) {
377 return NULL;
380 return info;
383 bool
384 sev_enabled(void)
386 return sev_state ? true : false;
389 uint64_t
390 sev_get_me_mask(void)
392 return sev_state ? sev_state->me_mask : ~0;
395 uint32_t
396 sev_get_cbit_position(void)
398 return sev_state ? sev_state->cbitpos : 0;
401 uint32_t
402 sev_get_reduced_phys_bits(void)
404 return sev_state ? sev_state->reduced_phys_bits : 0;
407 SevInfo *
408 sev_get_info(void)
410 SevInfo *info;
412 info = g_new0(SevInfo, 1);
413 info->enabled = sev_state ? true : false;
415 if (info->enabled) {
416 info->api_major = sev_state->api_major;
417 info->api_minor = sev_state->api_minor;
418 info->build_id = sev_state->build_id;
419 info->policy = sev_state->policy;
420 info->state = sev_state->state;
421 info->handle = sev_state->handle;
424 return info;
427 static int
428 sev_read_file_base64(const char *filename, guchar **data, gsize *len)
430 gsize sz;
431 gchar *base64;
432 GError *error = NULL;
434 if (!g_file_get_contents(filename, &base64, &sz, &error)) {
435 error_report("failed to read '%s' (%s)", filename, error->message);
436 return -1;
439 *data = g_base64_decode(base64, len);
440 return 0;
443 static int
444 sev_launch_start(SEVState *s)
446 gsize sz;
447 int ret = 1;
448 int fw_error;
449 QSevGuestInfo *sev = s->sev_info;
450 struct kvm_sev_launch_start *start;
451 guchar *session = NULL, *dh_cert = NULL;
453 start = g_new0(struct kvm_sev_launch_start, 1);
455 start->handle = object_property_get_int(OBJECT(sev), "handle",
456 &error_abort);
457 start->policy = object_property_get_int(OBJECT(sev), "policy",
458 &error_abort);
459 if (sev->session_file) {
460 if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) {
461 return 1;
463 start->session_uaddr = (unsigned long)session;
464 start->session_len = sz;
467 if (sev->dh_cert_file) {
468 if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) {
469 return 1;
471 start->dh_uaddr = (unsigned long)dh_cert;
472 start->dh_len = sz;
475 trace_kvm_sev_launch_start(start->policy, session, dh_cert);
476 ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error);
477 if (ret < 0) {
478 error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'",
479 __func__, ret, fw_error, fw_error_to_str(fw_error));
480 return 1;
483 object_property_set_int(OBJECT(sev), start->handle, "handle",
484 &error_abort);
485 sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE);
486 s->handle = start->handle;
487 s->policy = start->policy;
489 g_free(start);
490 g_free(session);
491 g_free(dh_cert);
493 return 0;
496 static int
497 sev_launch_update_data(uint8_t *addr, uint64_t len)
499 int ret, fw_error;
500 struct kvm_sev_launch_update_data update;
502 if (!addr || !len) {
503 return 1;
506 update.uaddr = (__u64)(unsigned long)addr;
507 update.len = len;
508 trace_kvm_sev_launch_update_data(addr, len);
509 ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA,
510 &update, &fw_error);
511 if (ret) {
512 error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'",
513 __func__, ret, fw_error, fw_error_to_str(fw_error));
516 return ret;
519 static void
520 sev_launch_get_measure(Notifier *notifier, void *unused)
522 int ret, error;
523 guchar *data;
524 SEVState *s = sev_state;
525 struct kvm_sev_launch_measure *measurement;
527 if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
528 return;
531 measurement = g_new0(struct kvm_sev_launch_measure, 1);
533 /* query the measurement blob length */
534 ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
535 measurement, &error);
536 if (!measurement->len) {
537 error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
538 __func__, ret, error, fw_error_to_str(errno));
539 goto free_measurement;
542 data = g_new0(guchar, measurement->len);
543 measurement->uaddr = (unsigned long)data;
545 /* get the measurement blob */
546 ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE,
547 measurement, &error);
548 if (ret) {
549 error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'",
550 __func__, ret, error, fw_error_to_str(errno));
551 goto free_data;
554 sev_set_guest_state(SEV_STATE_LAUNCH_SECRET);
556 /* encode the measurement value and emit the event */
557 s->measurement = g_base64_encode(data, measurement->len);
558 trace_kvm_sev_launch_measurement(s->measurement);
560 free_data:
561 g_free(data);
562 free_measurement:
563 g_free(measurement);
566 char *
567 sev_get_launch_measurement(void)
569 if (sev_state &&
570 sev_state->state >= SEV_STATE_LAUNCH_SECRET) {
571 return g_strdup(sev_state->measurement);
574 return NULL;
577 static Notifier sev_machine_done_notify = {
578 .notify = sev_launch_get_measure,
581 void *
582 sev_guest_init(const char *id)
584 SEVState *s;
585 char *devname;
586 int ret, fw_error;
587 uint32_t ebx;
588 uint32_t host_cbitpos;
589 struct sev_user_data_status status = {};
591 s = g_new0(SEVState, 1);
592 s->sev_info = lookup_sev_guest_info(id);
593 if (!s->sev_info) {
594 error_report("%s: '%s' is not a valid '%s' object",
595 __func__, id, TYPE_QSEV_GUEST_INFO);
596 goto err;
599 sev_state = s;
600 s->state = SEV_STATE_UNINIT;
602 host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL);
603 host_cbitpos = ebx & 0x3f;
605 s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL);
606 if (host_cbitpos != s->cbitpos) {
607 error_report("%s: cbitpos check failed, host '%d' requested '%d'",
608 __func__, host_cbitpos, s->cbitpos);
609 goto err;
612 s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info),
613 "reduced-phys-bits", NULL);
614 if (s->reduced_phys_bits < 1) {
615 error_report("%s: reduced_phys_bits check failed, it should be >=1,"
616 "' requested '%d'", __func__, s->reduced_phys_bits);
617 goto err;
620 s->me_mask = ~(1UL << s->cbitpos);
622 devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL);
623 s->sev_fd = open(devname, O_RDWR);
624 if (s->sev_fd < 0) {
625 error_report("%s: Failed to open %s '%s'", __func__,
626 devname, strerror(errno));
627 goto err;
629 g_free(devname);
631 ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status,
632 &fw_error);
633 if (ret) {
634 error_report("%s: failed to get platform status ret=%d"
635 "fw_error='%d: %s'", __func__, ret, fw_error,
636 fw_error_to_str(fw_error));
637 goto err;
639 s->build_id = status.build;
640 s->api_major = status.api_major;
641 s->api_minor = status.api_minor;
643 trace_kvm_sev_init();
644 ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error);
645 if (ret) {
646 error_report("%s: failed to initialize ret=%d fw_error=%d '%s'",
647 __func__, ret, fw_error, fw_error_to_str(fw_error));
648 goto err;
651 ret = sev_launch_start(s);
652 if (ret) {
653 error_report("%s: failed to create encryption context", __func__);
654 goto err;
657 ram_block_notifier_add(&sev_ram_notifier);
658 qemu_add_machine_init_done_notifier(&sev_machine_done_notify);
660 return s;
661 err:
662 g_free(sev_state);
663 sev_state = NULL;
664 return NULL;
668 sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
670 assert(handle);
672 /* if SEV is in update state then encrypt the data else do nothing */
673 if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) {
674 return sev_launch_update_data(ptr, len);
677 return 0;
680 static void
681 sev_register_types(void)
683 type_register_static(&qsev_guest_info);
686 type_init(sev_register_types);