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 | |
14 | #include "qemu/osdep.h" |
15 | |
16 | #include <linux/kvm.h> |
17 | #include <linux/psp-sev.h> |
18 | |
19 | #include <sys/ioctl.h> |
20 | |
21 | #include "qapi/error.h" |
22 | #include "qom/object_interfaces.h" |
23 | #include "qemu/base64.h" |
24 | #include "qemu/module.h" |
25 | #include "sysemu/kvm.h" |
26 | #include "sev_i386.h" |
27 | #include "sysemu/sysemu.h" |
28 | #include "sysemu/runstate.h" |
29 | #include "trace.h" |
30 | #include "migration/blocker.h" |
31 | |
32 | #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ |
33 | #define DEFAULT_SEV_DEVICE "/dev/sev" |
34 | |
35 | static SEVState *sev_state; |
36 | static Error *sev_mig_blocker; |
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 | |
113 | static bool |
114 | sev_check_state(SevState state) |
115 | { |
116 | assert(sev_state); |
117 | return sev_state->state == state ? true : false; |
118 | } |
119 | |
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 | |
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; |
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 | } |
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; |
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 | } |
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 | |
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 | |
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 | |
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 | { |
458 | guchar *pdh_data = NULL; |
459 | guchar *cert_chain_data = NULL; |
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 | { |
500 | SevCapability *cap = NULL; |
501 | guchar *pdh_data = NULL; |
502 | guchar *cert_chain_data = NULL; |
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)) { |
516 | goto out; |
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 | |
532 | out: |
533 | g_free(pdh_data); |
534 | g_free(cert_chain_data); |
535 | close(fd); |
536 | return cap; |
537 | } |
538 | |
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; |
560 | int fw_error, rc; |
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) { |
573 | goto out; |
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) { |
581 | goto out; |
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); |
588 | rc = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); |
589 | if (rc < 0) { |
590 | error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'" , |
591 | __func__, ret, fw_error, fw_error_to_str(fw_error)); |
592 | goto out; |
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; |
600 | ret = 0; |
601 | |
602 | out: |
603 | g_free(start); |
604 | g_free(session); |
605 | g_free(dh_cert); |
606 | return ret; |
607 | } |
608 | |
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 | |
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 | |
694 | static void |
695 | sev_launch_finish(SEVState *s) |
696 | { |
697 | int ret, error; |
698 | Error *local_err = NULL; |
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); |
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 | } |
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 | |
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 | |
743 | sev_state = s = g_new0(SEVState, 1); |
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 | |
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," |
767 | " requested '%d'" , __func__, s->reduced_phys_bits); |
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)); |
778 | } |
779 | g_free(devname); |
780 | if (s->sev_fd < 0) { |
781 | goto err; |
782 | } |
783 | |
784 | ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status, |
785 | &fw_error); |
786 | if (ret) { |
787 | error_report("%s: failed to get platform status ret=%d " |
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 | |
804 | ret = sev_launch_start(s); |
805 | if (ret) { |
806 | error_report("%s: failed to create encryption context" , __func__); |
807 | goto err; |
808 | } |
809 | |
810 | ram_block_notifier_add(&sev_ram_notifier); |
811 | qemu_add_machine_init_done_notifier(&sev_machine_done_notify); |
812 | qemu_add_vm_change_state_handler(sev_vm_state_change, s); |
813 | |
814 | return s; |
815 | err: |
816 | g_free(sev_state); |
817 | sev_state = NULL; |
818 | return NULL; |
819 | } |
820 | |
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 | |
834 | static void |
835 | sev_register_types(void) |
836 | { |
837 | type_register_static(&qsev_guest_info); |
838 | } |
839 | |
840 | type_init(sev_register_types); |
841 | |