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