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