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