]>
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; | |
164 | ||
165 | range.addr = (__u64)(unsigned long)host; | |
166 | range.size = size; | |
167 | ||
168 | trace_kvm_memcrypt_unregister_region(host, size); | |
169 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); | |
170 | if (r) { | |
171 | error_report("%s: failed to unregister region (%p+%#zx)", | |
172 | __func__, host, size); | |
173 | } | |
174 | } | |
175 | ||
176 | static struct RAMBlockNotifier sev_ram_notifier = { | |
177 | .ram_block_added = sev_ram_block_added, | |
178 | .ram_block_removed = sev_ram_block_removed, | |
179 | }; | |
180 | ||
a9b4942f BS |
181 | static void |
182 | qsev_guest_finalize(Object *obj) | |
183 | { | |
184 | } | |
185 | ||
186 | static char * | |
187 | qsev_guest_get_session_file(Object *obj, Error **errp) | |
188 | { | |
189 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
190 | ||
191 | return s->session_file ? g_strdup(s->session_file) : NULL; | |
192 | } | |
193 | ||
194 | static void | |
195 | qsev_guest_set_session_file(Object *obj, const char *value, Error **errp) | |
196 | { | |
197 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
198 | ||
199 | s->session_file = g_strdup(value); | |
200 | } | |
201 | ||
202 | static char * | |
203 | qsev_guest_get_dh_cert_file(Object *obj, Error **errp) | |
204 | { | |
205 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
206 | ||
207 | return g_strdup(s->dh_cert_file); | |
208 | } | |
209 | ||
210 | static void | |
211 | qsev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) | |
212 | { | |
213 | QSevGuestInfo *s = QSEV_GUEST_INFO(obj); | |
214 | ||
215 | s->dh_cert_file = g_strdup(value); | |
216 | } | |
217 | ||
218 | static char * | |
219 | qsev_guest_get_sev_device(Object *obj, Error **errp) | |
220 | { | |
221 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
222 | ||
223 | return g_strdup(sev->sev_device); | |
224 | } | |
225 | ||
226 | static void | |
227 | qsev_guest_set_sev_device(Object *obj, const char *value, Error **errp) | |
228 | { | |
229 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
230 | ||
231 | sev->sev_device = g_strdup(value); | |
232 | } | |
233 | ||
234 | static void | |
235 | qsev_guest_class_init(ObjectClass *oc, void *data) | |
236 | { | |
237 | object_class_property_add_str(oc, "sev-device", | |
238 | qsev_guest_get_sev_device, | |
239 | qsev_guest_set_sev_device, | |
240 | NULL); | |
241 | object_class_property_set_description(oc, "sev-device", | |
242 | "SEV device to use", NULL); | |
243 | object_class_property_add_str(oc, "dh-cert-file", | |
244 | qsev_guest_get_dh_cert_file, | |
245 | qsev_guest_set_dh_cert_file, | |
246 | NULL); | |
247 | object_class_property_set_description(oc, "dh-cert-file", | |
248 | "guest owners DH certificate (encoded with base64)", NULL); | |
249 | object_class_property_add_str(oc, "session-file", | |
250 | qsev_guest_get_session_file, | |
251 | qsev_guest_set_session_file, | |
252 | NULL); | |
253 | object_class_property_set_description(oc, "session-file", | |
254 | "guest owners session parameters (encoded with base64)", NULL); | |
255 | } | |
256 | ||
257 | static void | |
258 | qsev_guest_set_handle(Object *obj, Visitor *v, const char *name, | |
259 | void *opaque, Error **errp) | |
260 | { | |
261 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
262 | uint32_t value; | |
263 | ||
264 | visit_type_uint32(v, name, &value, errp); | |
265 | sev->handle = value; | |
266 | } | |
267 | ||
268 | static void | |
269 | qsev_guest_set_policy(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->policy = value; | |
277 | } | |
278 | ||
279 | static void | |
280 | qsev_guest_set_cbitpos(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->cbitpos = value; | |
288 | } | |
289 | ||
290 | static void | |
291 | qsev_guest_set_reduced_phys_bits(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->reduced_phys_bits = value; | |
299 | } | |
300 | ||
301 | static void | |
302 | qsev_guest_get_policy(Object *obj, Visitor *v, const char *name, | |
303 | void *opaque, Error **errp) | |
304 | { | |
305 | uint32_t value; | |
306 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
307 | ||
308 | value = sev->policy; | |
309 | visit_type_uint32(v, name, &value, errp); | |
310 | } | |
311 | ||
312 | static void | |
313 | qsev_guest_get_handle(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->handle; | |
320 | visit_type_uint32(v, name, &value, errp); | |
321 | } | |
322 | ||
323 | static void | |
324 | qsev_guest_get_cbitpos(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->cbitpos; | |
331 | visit_type_uint32(v, name, &value, errp); | |
332 | } | |
333 | ||
334 | static void | |
335 | qsev_guest_get_reduced_phys_bits(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->reduced_phys_bits; | |
342 | visit_type_uint32(v, name, &value, errp); | |
343 | } | |
344 | ||
345 | static void | |
346 | qsev_guest_init(Object *obj) | |
347 | { | |
348 | QSevGuestInfo *sev = QSEV_GUEST_INFO(obj); | |
349 | ||
350 | sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); | |
351 | sev->policy = DEFAULT_GUEST_POLICY; | |
352 | object_property_add(obj, "policy", "uint32", qsev_guest_get_policy, | |
353 | qsev_guest_set_policy, NULL, NULL, NULL); | |
354 | object_property_add(obj, "handle", "uint32", qsev_guest_get_handle, | |
355 | qsev_guest_set_handle, NULL, NULL, NULL); | |
356 | object_property_add(obj, "cbitpos", "uint32", qsev_guest_get_cbitpos, | |
357 | qsev_guest_set_cbitpos, NULL, NULL, NULL); | |
358 | object_property_add(obj, "reduced-phys-bits", "uint32", | |
359 | qsev_guest_get_reduced_phys_bits, | |
360 | qsev_guest_set_reduced_phys_bits, NULL, NULL, NULL); | |
361 | } | |
362 | ||
363 | /* sev guest info */ | |
364 | static const TypeInfo qsev_guest_info = { | |
365 | .parent = TYPE_OBJECT, | |
366 | .name = TYPE_QSEV_GUEST_INFO, | |
367 | .instance_size = sizeof(QSevGuestInfo), | |
368 | .instance_finalize = qsev_guest_finalize, | |
369 | .class_size = sizeof(QSevGuestInfoClass), | |
370 | .class_init = qsev_guest_class_init, | |
371 | .instance_init = qsev_guest_init, | |
372 | .interfaces = (InterfaceInfo[]) { | |
373 | { TYPE_USER_CREATABLE }, | |
374 | { } | |
375 | } | |
376 | }; | |
377 | ||
d8575c6c BS |
378 | static QSevGuestInfo * |
379 | lookup_sev_guest_info(const char *id) | |
380 | { | |
381 | Object *obj; | |
382 | QSevGuestInfo *info; | |
383 | ||
384 | obj = object_resolve_path_component(object_get_objects_root(), id); | |
385 | if (!obj) { | |
386 | return NULL; | |
387 | } | |
388 | ||
389 | info = (QSevGuestInfo *) | |
390 | object_dynamic_cast(obj, TYPE_QSEV_GUEST_INFO); | |
391 | if (!info) { | |
392 | return NULL; | |
393 | } | |
394 | ||
395 | return info; | |
396 | } | |
397 | ||
398 | bool | |
399 | sev_enabled(void) | |
400 | { | |
401 | return sev_state ? true : false; | |
402 | } | |
403 | ||
404 | uint64_t | |
405 | sev_get_me_mask(void) | |
406 | { | |
407 | return sev_state ? sev_state->me_mask : ~0; | |
408 | } | |
409 | ||
410 | uint32_t | |
411 | sev_get_cbit_position(void) | |
412 | { | |
413 | return sev_state ? sev_state->cbitpos : 0; | |
414 | } | |
415 | ||
416 | uint32_t | |
417 | sev_get_reduced_phys_bits(void) | |
418 | { | |
419 | return sev_state ? sev_state->reduced_phys_bits : 0; | |
420 | } | |
421 | ||
422 | SevInfo * | |
423 | sev_get_info(void) | |
424 | { | |
425 | SevInfo *info; | |
426 | ||
427 | info = g_new0(SevInfo, 1); | |
428 | info->enabled = sev_state ? true : false; | |
429 | ||
430 | if (info->enabled) { | |
431 | info->api_major = sev_state->api_major; | |
432 | info->api_minor = sev_state->api_minor; | |
433 | info->build_id = sev_state->build_id; | |
434 | info->policy = sev_state->policy; | |
435 | info->state = sev_state->state; | |
436 | info->handle = sev_state->handle; | |
437 | } | |
438 | ||
439 | return info; | |
440 | } | |
441 | ||
9f750794 BS |
442 | static int |
443 | sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, | |
444 | size_t *cert_chain_len) | |
445 | { | |
bf3175b4 PB |
446 | guchar *pdh_data = NULL; |
447 | guchar *cert_chain_data = NULL; | |
9f750794 BS |
448 | struct sev_user_data_pdh_cert_export export = {}; |
449 | int err, r; | |
450 | ||
451 | /* query the certificate length */ | |
452 | r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); | |
453 | if (r < 0) { | |
454 | if (err != SEV_RET_INVALID_LEN) { | |
455 | error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", | |
456 | r, err, fw_error_to_str(err)); | |
457 | return 1; | |
458 | } | |
459 | } | |
460 | ||
461 | pdh_data = g_new(guchar, export.pdh_cert_len); | |
462 | cert_chain_data = g_new(guchar, export.cert_chain_len); | |
463 | export.pdh_cert_address = (unsigned long)pdh_data; | |
464 | export.cert_chain_address = (unsigned long)cert_chain_data; | |
465 | ||
466 | r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); | |
467 | if (r < 0) { | |
468 | error_report("failed to export PDH cert ret=%d fw_err=%d (%s)", | |
469 | r, err, fw_error_to_str(err)); | |
470 | goto e_free; | |
471 | } | |
472 | ||
473 | *pdh = pdh_data; | |
474 | *pdh_len = export.pdh_cert_len; | |
475 | *cert_chain = cert_chain_data; | |
476 | *cert_chain_len = export.cert_chain_len; | |
477 | return 0; | |
478 | ||
479 | e_free: | |
480 | g_free(pdh_data); | |
481 | g_free(cert_chain_data); | |
482 | return 1; | |
483 | } | |
484 | ||
485 | SevCapability * | |
486 | sev_get_capabilities(void) | |
487 | { | |
bf3175b4 PB |
488 | SevCapability *cap = NULL; |
489 | guchar *pdh_data = NULL; | |
490 | guchar *cert_chain_data = NULL; | |
9f750794 BS |
491 | size_t pdh_len = 0, cert_chain_len = 0; |
492 | uint32_t ebx; | |
493 | int fd; | |
494 | ||
495 | fd = open(DEFAULT_SEV_DEVICE, O_RDWR); | |
496 | if (fd < 0) { | |
497 | error_report("%s: Failed to open %s '%s'", __func__, | |
498 | DEFAULT_SEV_DEVICE, strerror(errno)); | |
499 | return NULL; | |
500 | } | |
501 | ||
502 | if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, | |
503 | &cert_chain_data, &cert_chain_len)) { | |
bf3175b4 | 504 | goto out; |
9f750794 BS |
505 | } |
506 | ||
507 | cap = g_new0(SevCapability, 1); | |
508 | cap->pdh = g_base64_encode(pdh_data, pdh_len); | |
509 | cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len); | |
510 | ||
511 | host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); | |
512 | cap->cbitpos = ebx & 0x3f; | |
513 | ||
514 | /* | |
515 | * When SEV feature is enabled, we loose one bit in guest physical | |
516 | * addressing. | |
517 | */ | |
518 | cap->reduced_phys_bits = 1; | |
519 | ||
bf3175b4 | 520 | out: |
9f750794 BS |
521 | g_free(pdh_data); |
522 | g_free(cert_chain_data); | |
9f750794 BS |
523 | close(fd); |
524 | return cap; | |
525 | } | |
526 | ||
620fd55c BS |
527 | static int |
528 | sev_read_file_base64(const char *filename, guchar **data, gsize *len) | |
529 | { | |
530 | gsize sz; | |
531 | gchar *base64; | |
532 | GError *error = NULL; | |
533 | ||
534 | if (!g_file_get_contents(filename, &base64, &sz, &error)) { | |
535 | error_report("failed to read '%s' (%s)", filename, error->message); | |
536 | return -1; | |
537 | } | |
538 | ||
539 | *data = g_base64_decode(base64, len); | |
540 | return 0; | |
541 | } | |
542 | ||
543 | static int | |
544 | sev_launch_start(SEVState *s) | |
545 | { | |
546 | gsize sz; | |
547 | int ret = 1; | |
bf3175b4 | 548 | int fw_error, rc; |
620fd55c BS |
549 | QSevGuestInfo *sev = s->sev_info; |
550 | struct kvm_sev_launch_start *start; | |
551 | guchar *session = NULL, *dh_cert = NULL; | |
552 | ||
553 | start = g_new0(struct kvm_sev_launch_start, 1); | |
554 | ||
555 | start->handle = object_property_get_int(OBJECT(sev), "handle", | |
556 | &error_abort); | |
557 | start->policy = object_property_get_int(OBJECT(sev), "policy", | |
558 | &error_abort); | |
559 | if (sev->session_file) { | |
560 | if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { | |
bf3175b4 | 561 | goto out; |
620fd55c BS |
562 | } |
563 | start->session_uaddr = (unsigned long)session; | |
564 | start->session_len = sz; | |
565 | } | |
566 | ||
567 | if (sev->dh_cert_file) { | |
568 | if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { | |
bf3175b4 | 569 | goto out; |
620fd55c BS |
570 | } |
571 | start->dh_uaddr = (unsigned long)dh_cert; | |
572 | start->dh_len = sz; | |
573 | } | |
574 | ||
575 | trace_kvm_sev_launch_start(start->policy, session, dh_cert); | |
bf3175b4 PB |
576 | rc = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); |
577 | if (rc < 0) { | |
620fd55c BS |
578 | error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", |
579 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
bf3175b4 | 580 | goto out; |
620fd55c BS |
581 | } |
582 | ||
583 | object_property_set_int(OBJECT(sev), start->handle, "handle", | |
584 | &error_abort); | |
585 | sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); | |
586 | s->handle = start->handle; | |
587 | s->policy = start->policy; | |
bf3175b4 | 588 | ret = 0; |
620fd55c | 589 | |
bf3175b4 | 590 | out: |
620fd55c BS |
591 | g_free(start); |
592 | g_free(session); | |
593 | g_free(dh_cert); | |
bf3175b4 | 594 | return ret; |
620fd55c BS |
595 | } |
596 | ||
b738d630 BS |
597 | static int |
598 | sev_launch_update_data(uint8_t *addr, uint64_t len) | |
599 | { | |
600 | int ret, fw_error; | |
601 | struct kvm_sev_launch_update_data update; | |
602 | ||
603 | if (!addr || !len) { | |
604 | return 1; | |
605 | } | |
606 | ||
607 | update.uaddr = (__u64)(unsigned long)addr; | |
608 | update.len = len; | |
609 | trace_kvm_sev_launch_update_data(addr, len); | |
610 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, | |
611 | &update, &fw_error); | |
612 | if (ret) { | |
613 | error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", | |
614 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
615 | } | |
616 | ||
617 | return ret; | |
618 | } | |
619 | ||
c6c89c97 BS |
620 | static void |
621 | sev_launch_get_measure(Notifier *notifier, void *unused) | |
622 | { | |
623 | int ret, error; | |
624 | guchar *data; | |
625 | SEVState *s = sev_state; | |
626 | struct kvm_sev_launch_measure *measurement; | |
627 | ||
628 | if (!sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { | |
629 | return; | |
630 | } | |
631 | ||
632 | measurement = g_new0(struct kvm_sev_launch_measure, 1); | |
633 | ||
634 | /* query the measurement blob length */ | |
635 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, | |
636 | measurement, &error); | |
637 | if (!measurement->len) { | |
638 | error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", | |
639 | __func__, ret, error, fw_error_to_str(errno)); | |
640 | goto free_measurement; | |
641 | } | |
642 | ||
643 | data = g_new0(guchar, measurement->len); | |
644 | measurement->uaddr = (unsigned long)data; | |
645 | ||
646 | /* get the measurement blob */ | |
647 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_MEASURE, | |
648 | measurement, &error); | |
649 | if (ret) { | |
650 | error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", | |
651 | __func__, ret, error, fw_error_to_str(errno)); | |
652 | goto free_data; | |
653 | } | |
654 | ||
655 | sev_set_guest_state(SEV_STATE_LAUNCH_SECRET); | |
656 | ||
657 | /* encode the measurement value and emit the event */ | |
658 | s->measurement = g_base64_encode(data, measurement->len); | |
659 | trace_kvm_sev_launch_measurement(s->measurement); | |
660 | ||
661 | free_data: | |
662 | g_free(data); | |
663 | free_measurement: | |
664 | g_free(measurement); | |
665 | } | |
666 | ||
667 | char * | |
668 | sev_get_launch_measurement(void) | |
669 | { | |
670 | if (sev_state && | |
671 | sev_state->state >= SEV_STATE_LAUNCH_SECRET) { | |
672 | return g_strdup(sev_state->measurement); | |
673 | } | |
674 | ||
675 | return NULL; | |
676 | } | |
677 | ||
678 | static Notifier sev_machine_done_notify = { | |
679 | .notify = sev_launch_get_measure, | |
680 | }; | |
681 | ||
5dd0df7e BS |
682 | static void |
683 | sev_launch_finish(SEVState *s) | |
684 | { | |
685 | int ret, error; | |
8fa4466d | 686 | Error *local_err = NULL; |
5dd0df7e BS |
687 | |
688 | trace_kvm_sev_launch_finish(); | |
689 | ret = sev_ioctl(sev_state->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); | |
690 | if (ret) { | |
691 | error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", | |
692 | __func__, ret, error, fw_error_to_str(error)); | |
693 | exit(1); | |
694 | } | |
695 | ||
696 | sev_set_guest_state(SEV_STATE_RUNNING); | |
8fa4466d BS |
697 | |
698 | /* add migration blocker */ | |
699 | error_setg(&sev_mig_blocker, | |
700 | "SEV: Migration is not implemented"); | |
701 | ret = migrate_add_blocker(sev_mig_blocker, &local_err); | |
702 | if (local_err) { | |
703 | error_report_err(local_err); | |
704 | error_free(sev_mig_blocker); | |
705 | exit(1); | |
706 | } | |
5dd0df7e BS |
707 | } |
708 | ||
709 | static void | |
710 | sev_vm_state_change(void *opaque, int running, RunState state) | |
711 | { | |
712 | SEVState *s = opaque; | |
713 | ||
714 | if (running) { | |
715 | if (!sev_check_state(SEV_STATE_RUNNING)) { | |
716 | sev_launch_finish(s); | |
717 | } | |
718 | } | |
719 | } | |
720 | ||
d8575c6c BS |
721 | void * |
722 | sev_guest_init(const char *id) | |
723 | { | |
724 | SEVState *s; | |
725 | char *devname; | |
726 | int ret, fw_error; | |
727 | uint32_t ebx; | |
728 | uint32_t host_cbitpos; | |
729 | struct sev_user_data_status status = {}; | |
730 | ||
bf3175b4 | 731 | sev_state = s = g_new0(SEVState, 1); |
d8575c6c BS |
732 | s->sev_info = lookup_sev_guest_info(id); |
733 | if (!s->sev_info) { | |
734 | error_report("%s: '%s' is not a valid '%s' object", | |
735 | __func__, id, TYPE_QSEV_GUEST_INFO); | |
736 | goto err; | |
737 | } | |
738 | ||
d8575c6c BS |
739 | s->state = SEV_STATE_UNINIT; |
740 | ||
741 | host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); | |
742 | host_cbitpos = ebx & 0x3f; | |
743 | ||
744 | s->cbitpos = object_property_get_int(OBJECT(s->sev_info), "cbitpos", NULL); | |
745 | if (host_cbitpos != s->cbitpos) { | |
746 | error_report("%s: cbitpos check failed, host '%d' requested '%d'", | |
747 | __func__, host_cbitpos, s->cbitpos); | |
748 | goto err; | |
749 | } | |
750 | ||
751 | s->reduced_phys_bits = object_property_get_int(OBJECT(s->sev_info), | |
752 | "reduced-phys-bits", NULL); | |
753 | if (s->reduced_phys_bits < 1) { | |
754 | error_report("%s: reduced_phys_bits check failed, it should be >=1," | |
755 | "' requested '%d'", __func__, s->reduced_phys_bits); | |
756 | goto err; | |
757 | } | |
758 | ||
759 | s->me_mask = ~(1UL << s->cbitpos); | |
760 | ||
761 | devname = object_property_get_str(OBJECT(s->sev_info), "sev-device", NULL); | |
762 | s->sev_fd = open(devname, O_RDWR); | |
763 | if (s->sev_fd < 0) { | |
764 | error_report("%s: Failed to open %s '%s'", __func__, | |
765 | devname, strerror(errno)); | |
d8575c6c BS |
766 | } |
767 | g_free(devname); | |
5d7bc72a GK |
768 | if (s->sev_fd < 0) { |
769 | goto err; | |
770 | } | |
d8575c6c BS |
771 | |
772 | ret = sev_platform_ioctl(s->sev_fd, SEV_PLATFORM_STATUS, &status, | |
773 | &fw_error); | |
774 | if (ret) { | |
775 | error_report("%s: failed to get platform status ret=%d" | |
776 | "fw_error='%d: %s'", __func__, ret, fw_error, | |
777 | fw_error_to_str(fw_error)); | |
778 | goto err; | |
779 | } | |
780 | s->build_id = status.build; | |
781 | s->api_major = status.api_major; | |
782 | s->api_minor = status.api_minor; | |
783 | ||
784 | trace_kvm_sev_init(); | |
785 | ret = sev_ioctl(s->sev_fd, KVM_SEV_INIT, NULL, &fw_error); | |
786 | if (ret) { | |
787 | error_report("%s: failed to initialize ret=%d fw_error=%d '%s'", | |
788 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
789 | goto err; | |
790 | } | |
791 | ||
620fd55c BS |
792 | ret = sev_launch_start(s); |
793 | if (ret) { | |
794 | error_report("%s: failed to create encryption context", __func__); | |
795 | goto err; | |
796 | } | |
797 | ||
2b308e44 | 798 | ram_block_notifier_add(&sev_ram_notifier); |
c6c89c97 | 799 | qemu_add_machine_init_done_notifier(&sev_machine_done_notify); |
5dd0df7e | 800 | qemu_add_vm_change_state_handler(sev_vm_state_change, s); |
2b308e44 | 801 | |
d8575c6c BS |
802 | return s; |
803 | err: | |
804 | g_free(sev_state); | |
805 | sev_state = NULL; | |
806 | return NULL; | |
807 | } | |
808 | ||
b738d630 BS |
809 | int |
810 | sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len) | |
811 | { | |
812 | assert(handle); | |
813 | ||
814 | /* if SEV is in update state then encrypt the data else do nothing */ | |
815 | if (sev_check_state(SEV_STATE_LAUNCH_UPDATE)) { | |
816 | return sev_launch_update_data(ptr, len); | |
817 | } | |
818 | ||
819 | return 0; | |
820 | } | |
821 | ||
a9b4942f BS |
822 | static void |
823 | sev_register_types(void) | |
824 | { | |
825 | type_register_static(&qsev_guest_info); | |
826 | } | |
827 | ||
828 | type_init(sev_register_types); |