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