]>
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" |
b2f73a07 | 25 | #include "qemu/uuid.h" |
cff03145 | 26 | #include "crypto/hash.h" |
a9b4942f | 27 | #include "sysemu/kvm.h" |
93777de3 | 28 | #include "sev.h" |
a9b4942f | 29 | #include "sysemu/sysemu.h" |
54d31236 | 30 | #include "sysemu/runstate.h" |
d8575c6c | 31 | #include "trace.h" |
8fa4466d | 32 | #include "migration/blocker.h" |
db1015e9 | 33 | #include "qom/object.h" |
c7f7e697 | 34 | #include "monitor/monitor.h" |
3208de1c PMD |
35 | #include "qapi/qapi-commands-misc-target.h" |
36 | #include "qapi/qmp/qerror.h" | |
f91f9f25 | 37 | #include "exec/confidential-guest-support.h" |
b2f73a07 | 38 | #include "hw/i386/pc.h" |
a9b4942f | 39 | |
d2d8a198 | 40 | #define TYPE_SEV_GUEST "sev-guest" |
8063396b | 41 | OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) |
a86ab19d | 42 | |
a86ab19d DG |
43 | |
44 | /** | |
d2d8a198 | 45 | * SevGuestState: |
a86ab19d | 46 | * |
d2d8a198 DG |
47 | * The SevGuestState object is used for creating and managing a SEV |
48 | * guest. | |
a86ab19d DG |
49 | * |
50 | * # $QEMU \ | |
51 | * -object sev-guest,id=sev0 \ | |
52 | * -machine ...,memory-encryption=sev0 | |
53 | */ | |
d2d8a198 | 54 | struct SevGuestState { |
f91f9f25 | 55 | ConfidentialGuestSupport parent_obj; |
a86ab19d | 56 | |
75a877e3 | 57 | /* configuration parameters */ |
a86ab19d DG |
58 | char *sev_device; |
59 | uint32_t policy; | |
a86ab19d DG |
60 | char *dh_cert_file; |
61 | char *session_file; | |
62 | uint32_t cbitpos; | |
63 | uint32_t reduced_phys_bits; | |
a86ab19d | 64 | |
75a877e3 | 65 | /* runtime state */ |
cf504cd6 | 66 | uint32_t handle; |
421522eb DG |
67 | uint8_t api_major; |
68 | uint8_t api_minor; | |
69 | uint8_t build_id; | |
421522eb DG |
70 | int sev_fd; |
71 | SevState state; | |
72 | gchar *measurement; | |
b2f73a07 PB |
73 | |
74 | uint32_t reset_cs; | |
75 | uint32_t reset_ip; | |
76 | bool reset_data_valid; | |
a86ab19d DG |
77 | }; |
78 | ||
a9b4942f BS |
79 | #define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ |
80 | #define DEFAULT_SEV_DEVICE "/dev/sev" | |
81 | ||
b2f73a07 PB |
82 | #define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" |
83 | typedef struct __attribute__((__packed__)) SevInfoBlock { | |
84 | /* SEV-ES Reset Vector Address */ | |
85 | uint32_t reset_addr; | |
86 | } SevInfoBlock; | |
87 | ||
cff03145 DM |
88 | #define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454" |
89 | typedef struct QEMU_PACKED SevHashTableDescriptor { | |
90 | /* SEV hash table area guest address */ | |
91 | uint32_t base; | |
92 | /* SEV hash table area size (in bytes) */ | |
93 | uint32_t size; | |
94 | } SevHashTableDescriptor; | |
95 | ||
96 | /* hard code sha256 digest size */ | |
97 | #define HASH_SIZE 32 | |
98 | ||
99 | typedef struct QEMU_PACKED SevHashTableEntry { | |
100 | QemuUUID guid; | |
101 | uint16_t len; | |
102 | uint8_t hash[HASH_SIZE]; | |
103 | } SevHashTableEntry; | |
104 | ||
105 | typedef struct QEMU_PACKED SevHashTable { | |
106 | QemuUUID guid; | |
107 | uint16_t len; | |
108 | SevHashTableEntry cmdline; | |
109 | SevHashTableEntry initrd; | |
110 | SevHashTableEntry kernel; | |
111 | uint8_t padding[]; | |
112 | } SevHashTable; | |
113 | ||
8673dee3 | 114 | static SevGuestState *sev_guest; |
8fa4466d | 115 | static Error *sev_mig_blocker; |
d8575c6c BS |
116 | |
117 | static const char *const sev_fw_errlist[] = { | |
5811b936 CK |
118 | [SEV_RET_SUCCESS] = "", |
119 | [SEV_RET_INVALID_PLATFORM_STATE] = "Platform state is invalid", | |
120 | [SEV_RET_INVALID_GUEST_STATE] = "Guest state is invalid", | |
121 | [SEV_RET_INAVLID_CONFIG] = "Platform configuration is invalid", | |
122 | [SEV_RET_INVALID_LEN] = "Buffer too small", | |
123 | [SEV_RET_ALREADY_OWNED] = "Platform is already owned", | |
124 | [SEV_RET_INVALID_CERTIFICATE] = "Certificate is invalid", | |
125 | [SEV_RET_POLICY_FAILURE] = "Policy is not allowed", | |
126 | [SEV_RET_INACTIVE] = "Guest is not active", | |
127 | [SEV_RET_INVALID_ADDRESS] = "Invalid address", | |
128 | [SEV_RET_BAD_SIGNATURE] = "Bad signature", | |
129 | [SEV_RET_BAD_MEASUREMENT] = "Bad measurement", | |
130 | [SEV_RET_ASID_OWNED] = "ASID is already owned", | |
131 | [SEV_RET_INVALID_ASID] = "Invalid ASID", | |
132 | [SEV_RET_WBINVD_REQUIRED] = "WBINVD is required", | |
133 | [SEV_RET_DFFLUSH_REQUIRED] = "DF_FLUSH is required", | |
134 | [SEV_RET_INVALID_GUEST] = "Guest handle is invalid", | |
135 | [SEV_RET_INVALID_COMMAND] = "Invalid command", | |
136 | [SEV_RET_ACTIVE] = "Guest is active", | |
137 | [SEV_RET_HWSEV_RET_PLATFORM] = "Hardware error", | |
138 | [SEV_RET_HWSEV_RET_UNSAFE] = "Hardware unsafe", | |
139 | [SEV_RET_UNSUPPORTED] = "Feature not supported", | |
140 | [SEV_RET_INVALID_PARAM] = "Invalid parameter", | |
d47b8550 CK |
141 | [SEV_RET_RESOURCE_LIMIT] = "Required firmware resource depleted", |
142 | [SEV_RET_SECURE_DATA_INVALID] = "Part-specific integrity check failure", | |
d8575c6c BS |
143 | }; |
144 | ||
145 | #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) | |
146 | ||
147 | static int | |
148 | sev_ioctl(int fd, int cmd, void *data, int *error) | |
149 | { | |
150 | int r; | |
151 | struct kvm_sev_cmd input; | |
152 | ||
153 | memset(&input, 0x0, sizeof(input)); | |
154 | ||
155 | input.id = cmd; | |
156 | input.sev_fd = fd; | |
157 | input.data = (__u64)(unsigned long)data; | |
158 | ||
159 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &input); | |
160 | ||
161 | if (error) { | |
162 | *error = input.error; | |
163 | } | |
164 | ||
165 | return r; | |
166 | } | |
167 | ||
168 | static int | |
169 | sev_platform_ioctl(int fd, int cmd, void *data, int *error) | |
170 | { | |
171 | int r; | |
172 | struct sev_issue_cmd arg; | |
173 | ||
174 | arg.cmd = cmd; | |
175 | arg.data = (unsigned long)data; | |
176 | r = ioctl(fd, SEV_ISSUE_CMD, &arg); | |
177 | if (error) { | |
178 | *error = arg.error; | |
179 | } | |
180 | ||
181 | return r; | |
182 | } | |
183 | ||
184 | static const char * | |
185 | fw_error_to_str(int code) | |
186 | { | |
187 | if (code < 0 || code >= SEV_FW_MAX_ERROR) { | |
188 | return "unknown error"; | |
189 | } | |
190 | ||
191 | return sev_fw_errlist[code]; | |
192 | } | |
193 | ||
b738d630 | 194 | static bool |
8673dee3 | 195 | sev_check_state(const SevGuestState *sev, SevState state) |
b738d630 | 196 | { |
8673dee3 | 197 | assert(sev); |
421522eb | 198 | return sev->state == state ? true : false; |
b738d630 BS |
199 | } |
200 | ||
620fd55c | 201 | static void |
8673dee3 | 202 | sev_set_guest_state(SevGuestState *sev, SevState new_state) |
620fd55c BS |
203 | { |
204 | assert(new_state < SEV_STATE__MAX); | |
8673dee3 | 205 | assert(sev); |
620fd55c | 206 | |
421522eb | 207 | trace_kvm_sev_change_state(SevState_str(sev->state), |
620fd55c | 208 | SevState_str(new_state)); |
421522eb | 209 | sev->state = new_state; |
620fd55c BS |
210 | } |
211 | ||
2b308e44 | 212 | static void |
8f44304c DH |
213 | sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, |
214 | size_t max_size) | |
2b308e44 BS |
215 | { |
216 | int r; | |
217 | struct kvm_enc_region range; | |
cedc0ad5 BS |
218 | ram_addr_t offset; |
219 | MemoryRegion *mr; | |
220 | ||
221 | /* | |
222 | * The RAM device presents a memory region that should be treated | |
223 | * as IO region and should not be pinned. | |
224 | */ | |
225 | mr = memory_region_from_host(host, &offset); | |
226 | if (mr && memory_region_is_ram_device(mr)) { | |
227 | return; | |
228 | } | |
2b308e44 BS |
229 | |
230 | range.addr = (__u64)(unsigned long)host; | |
8f44304c | 231 | range.size = max_size; |
2b308e44 | 232 | |
8f44304c | 233 | trace_kvm_memcrypt_register_region(host, max_size); |
2b308e44 BS |
234 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); |
235 | if (r) { | |
236 | error_report("%s: failed to register region (%p+%#zx) error '%s'", | |
8f44304c | 237 | __func__, host, max_size, strerror(errno)); |
2b308e44 BS |
238 | exit(1); |
239 | } | |
240 | } | |
241 | ||
242 | static void | |
8f44304c DH |
243 | sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, |
244 | size_t max_size) | |
2b308e44 BS |
245 | { |
246 | int r; | |
247 | struct kvm_enc_region range; | |
56e2ec94 AW |
248 | ram_addr_t offset; |
249 | MemoryRegion *mr; | |
250 | ||
251 | /* | |
252 | * The RAM device presents a memory region that should be treated | |
253 | * as IO region and should not have been pinned. | |
254 | */ | |
255 | mr = memory_region_from_host(host, &offset); | |
256 | if (mr && memory_region_is_ram_device(mr)) { | |
257 | return; | |
258 | } | |
2b308e44 BS |
259 | |
260 | range.addr = (__u64)(unsigned long)host; | |
8f44304c | 261 | range.size = max_size; |
2b308e44 | 262 | |
8f44304c | 263 | trace_kvm_memcrypt_unregister_region(host, max_size); |
2b308e44 BS |
264 | r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); |
265 | if (r) { | |
266 | error_report("%s: failed to unregister region (%p+%#zx)", | |
8f44304c | 267 | __func__, host, max_size); |
2b308e44 BS |
268 | } |
269 | } | |
270 | ||
271 | static struct RAMBlockNotifier sev_ram_notifier = { | |
272 | .ram_block_added = sev_ram_block_added, | |
273 | .ram_block_removed = sev_ram_block_removed, | |
274 | }; | |
275 | ||
a9b4942f | 276 | static void |
d2d8a198 | 277 | sev_guest_finalize(Object *obj) |
a9b4942f BS |
278 | { |
279 | } | |
280 | ||
281 | static char * | |
d2d8a198 | 282 | sev_guest_get_session_file(Object *obj, Error **errp) |
a9b4942f | 283 | { |
d2d8a198 | 284 | SevGuestState *s = SEV_GUEST(obj); |
a9b4942f BS |
285 | |
286 | return s->session_file ? g_strdup(s->session_file) : NULL; | |
287 | } | |
288 | ||
289 | static void | |
d2d8a198 | 290 | sev_guest_set_session_file(Object *obj, const char *value, Error **errp) |
a9b4942f | 291 | { |
d2d8a198 | 292 | SevGuestState *s = SEV_GUEST(obj); |
a9b4942f BS |
293 | |
294 | s->session_file = g_strdup(value); | |
295 | } | |
296 | ||
297 | static char * | |
d2d8a198 | 298 | sev_guest_get_dh_cert_file(Object *obj, Error **errp) |
a9b4942f | 299 | { |
d2d8a198 | 300 | SevGuestState *s = SEV_GUEST(obj); |
a9b4942f BS |
301 | |
302 | return g_strdup(s->dh_cert_file); | |
303 | } | |
304 | ||
305 | static void | |
d2d8a198 | 306 | sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) |
a9b4942f | 307 | { |
d2d8a198 | 308 | SevGuestState *s = SEV_GUEST(obj); |
a9b4942f BS |
309 | |
310 | s->dh_cert_file = g_strdup(value); | |
311 | } | |
312 | ||
313 | static char * | |
d2d8a198 | 314 | sev_guest_get_sev_device(Object *obj, Error **errp) |
a9b4942f | 315 | { |
d2d8a198 | 316 | SevGuestState *sev = SEV_GUEST(obj); |
a9b4942f BS |
317 | |
318 | return g_strdup(sev->sev_device); | |
319 | } | |
320 | ||
321 | static void | |
d2d8a198 | 322 | sev_guest_set_sev_device(Object *obj, const char *value, Error **errp) |
a9b4942f | 323 | { |
d2d8a198 | 324 | SevGuestState *sev = SEV_GUEST(obj); |
a9b4942f BS |
325 | |
326 | sev->sev_device = g_strdup(value); | |
327 | } | |
328 | ||
329 | static void | |
d2d8a198 | 330 | sev_guest_class_init(ObjectClass *oc, void *data) |
a9b4942f BS |
331 | { |
332 | object_class_property_add_str(oc, "sev-device", | |
d2d8a198 DG |
333 | sev_guest_get_sev_device, |
334 | sev_guest_set_sev_device); | |
a9b4942f | 335 | object_class_property_set_description(oc, "sev-device", |
7eecec7d | 336 | "SEV device to use"); |
a9b4942f | 337 | object_class_property_add_str(oc, "dh-cert-file", |
d2d8a198 DG |
338 | sev_guest_get_dh_cert_file, |
339 | sev_guest_set_dh_cert_file); | |
a9b4942f | 340 | object_class_property_set_description(oc, "dh-cert-file", |
7eecec7d | 341 | "guest owners DH certificate (encoded with base64)"); |
a9b4942f | 342 | object_class_property_add_str(oc, "session-file", |
d2d8a198 DG |
343 | sev_guest_get_session_file, |
344 | sev_guest_set_session_file); | |
a9b4942f | 345 | object_class_property_set_description(oc, "session-file", |
7eecec7d | 346 | "guest owners session parameters (encoded with base64)"); |
a9b4942f BS |
347 | } |
348 | ||
a9b4942f | 349 | static void |
d2d8a198 | 350 | sev_guest_instance_init(Object *obj) |
a9b4942f | 351 | { |
d2d8a198 | 352 | SevGuestState *sev = SEV_GUEST(obj); |
a9b4942f BS |
353 | |
354 | sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); | |
355 | sev->policy = DEFAULT_GUEST_POLICY; | |
64a7b8de | 356 | object_property_add_uint32_ptr(obj, "policy", &sev->policy, |
d2623129 | 357 | OBJ_PROP_FLAG_READWRITE); |
64a7b8de | 358 | object_property_add_uint32_ptr(obj, "handle", &sev->handle, |
d2623129 | 359 | OBJ_PROP_FLAG_READWRITE); |
64a7b8de | 360 | object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, |
d2623129 | 361 | OBJ_PROP_FLAG_READWRITE); |
64a7b8de FF |
362 | object_property_add_uint32_ptr(obj, "reduced-phys-bits", |
363 | &sev->reduced_phys_bits, | |
d2623129 | 364 | OBJ_PROP_FLAG_READWRITE); |
a9b4942f BS |
365 | } |
366 | ||
367 | /* sev guest info */ | |
d2d8a198 | 368 | static const TypeInfo sev_guest_info = { |
f91f9f25 | 369 | .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, |
d2d8a198 DG |
370 | .name = TYPE_SEV_GUEST, |
371 | .instance_size = sizeof(SevGuestState), | |
372 | .instance_finalize = sev_guest_finalize, | |
373 | .class_init = sev_guest_class_init, | |
374 | .instance_init = sev_guest_instance_init, | |
a9b4942f BS |
375 | .interfaces = (InterfaceInfo[]) { |
376 | { TYPE_USER_CREATABLE }, | |
377 | { } | |
378 | } | |
379 | }; | |
380 | ||
d8575c6c BS |
381 | bool |
382 | sev_enabled(void) | |
383 | { | |
8673dee3 | 384 | return !!sev_guest; |
d8575c6c BS |
385 | } |
386 | ||
6b98e96f TL |
387 | bool |
388 | sev_es_enabled(void) | |
389 | { | |
027b524d | 390 | return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES); |
6b98e96f TL |
391 | } |
392 | ||
d8575c6c BS |
393 | uint32_t |
394 | sev_get_cbit_position(void) | |
395 | { | |
a06d2bad | 396 | return sev_guest ? sev_guest->cbitpos : 0; |
d8575c6c BS |
397 | } |
398 | ||
399 | uint32_t | |
400 | sev_get_reduced_phys_bits(void) | |
401 | { | |
a06d2bad | 402 | return sev_guest ? sev_guest->reduced_phys_bits : 0; |
d8575c6c BS |
403 | } |
404 | ||
405 | SevInfo * | |
406 | sev_get_info(void) | |
407 | { | |
408 | SevInfo *info; | |
409 | ||
410 | info = g_new0(SevInfo, 1); | |
8673dee3 | 411 | info->enabled = sev_enabled(); |
d8575c6c BS |
412 | |
413 | if (info->enabled) { | |
421522eb DG |
414 | info->api_major = sev_guest->api_major; |
415 | info->api_minor = sev_guest->api_minor; | |
416 | info->build_id = sev_guest->build_id; | |
0bd15277 | 417 | info->policy = sev_guest->policy; |
421522eb | 418 | info->state = sev_guest->state; |
cf504cd6 | 419 | info->handle = sev_guest->handle; |
d8575c6c BS |
420 | } |
421 | ||
422 | return info; | |
423 | } | |
424 | ||
9f750794 BS |
425 | static int |
426 | sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, | |
e4f62785 | 427 | size_t *cert_chain_len, Error **errp) |
9f750794 | 428 | { |
bf3175b4 PB |
429 | guchar *pdh_data = NULL; |
430 | guchar *cert_chain_data = NULL; | |
9f750794 BS |
431 | struct sev_user_data_pdh_cert_export export = {}; |
432 | int err, r; | |
433 | ||
434 | /* query the certificate length */ | |
435 | r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); | |
436 | if (r < 0) { | |
437 | if (err != SEV_RET_INVALID_LEN) { | |
2c7233eb PMD |
438 | error_setg(errp, "SEV: Failed to export PDH cert" |
439 | " ret=%d fw_err=%d (%s)", | |
e4f62785 | 440 | r, err, fw_error_to_str(err)); |
9f750794 BS |
441 | return 1; |
442 | } | |
443 | } | |
444 | ||
445 | pdh_data = g_new(guchar, export.pdh_cert_len); | |
446 | cert_chain_data = g_new(guchar, export.cert_chain_len); | |
447 | export.pdh_cert_address = (unsigned long)pdh_data; | |
448 | export.cert_chain_address = (unsigned long)cert_chain_data; | |
449 | ||
450 | r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err); | |
451 | if (r < 0) { | |
2c7233eb | 452 | error_setg(errp, "SEV: Failed to export PDH cert ret=%d fw_err=%d (%s)", |
e4f62785 | 453 | r, err, fw_error_to_str(err)); |
9f750794 BS |
454 | goto e_free; |
455 | } | |
456 | ||
457 | *pdh = pdh_data; | |
458 | *pdh_len = export.pdh_cert_len; | |
459 | *cert_chain = cert_chain_data; | |
460 | *cert_chain_len = export.cert_chain_len; | |
461 | return 0; | |
462 | ||
463 | e_free: | |
464 | g_free(pdh_data); | |
465 | g_free(cert_chain_data); | |
466 | return 1; | |
467 | } | |
468 | ||
469 | SevCapability * | |
e4f62785 | 470 | sev_get_capabilities(Error **errp) |
9f750794 | 471 | { |
bf3175b4 PB |
472 | SevCapability *cap = NULL; |
473 | guchar *pdh_data = NULL; | |
474 | guchar *cert_chain_data = NULL; | |
9f750794 BS |
475 | size_t pdh_len = 0, cert_chain_len = 0; |
476 | uint32_t ebx; | |
477 | int fd; | |
478 | ||
1b38750c PB |
479 | if (!kvm_enabled()) { |
480 | error_setg(errp, "KVM not enabled"); | |
481 | return NULL; | |
482 | } | |
483 | if (kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, NULL) < 0) { | |
484 | error_setg(errp, "SEV is not enabled in KVM"); | |
485 | return NULL; | |
486 | } | |
487 | ||
9f750794 BS |
488 | fd = open(DEFAULT_SEV_DEVICE, O_RDWR); |
489 | if (fd < 0) { | |
2c7233eb | 490 | error_setg_errno(errp, errno, "SEV: Failed to open %s", |
e4f62785 | 491 | DEFAULT_SEV_DEVICE); |
9f750794 BS |
492 | return NULL; |
493 | } | |
494 | ||
495 | if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, | |
e4f62785 | 496 | &cert_chain_data, &cert_chain_len, errp)) { |
bf3175b4 | 497 | goto out; |
9f750794 BS |
498 | } |
499 | ||
500 | cap = g_new0(SevCapability, 1); | |
501 | cap->pdh = g_base64_encode(pdh_data, pdh_len); | |
502 | cap->cert_chain = g_base64_encode(cert_chain_data, cert_chain_len); | |
503 | ||
504 | host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); | |
505 | cap->cbitpos = ebx & 0x3f; | |
506 | ||
507 | /* | |
508 | * When SEV feature is enabled, we loose one bit in guest physical | |
509 | * addressing. | |
510 | */ | |
511 | cap->reduced_phys_bits = 1; | |
512 | ||
bf3175b4 | 513 | out: |
9f750794 BS |
514 | g_free(pdh_data); |
515 | g_free(cert_chain_data); | |
9f750794 BS |
516 | close(fd); |
517 | return cap; | |
518 | } | |
519 | ||
3208de1c PMD |
520 | static SevAttestationReport *sev_get_attestation_report(const char *mnonce, |
521 | Error **errp) | |
3ea1a802 BS |
522 | { |
523 | struct kvm_sev_attestation_report input = {}; | |
524 | SevAttestationReport *report = NULL; | |
525 | SevGuestState *sev = sev_guest; | |
ed84ae72 DDAG |
526 | g_autofree guchar *data = NULL; |
527 | g_autofree guchar *buf = NULL; | |
3ea1a802 BS |
528 | gsize len; |
529 | int err = 0, ret; | |
530 | ||
531 | if (!sev_enabled()) { | |
532 | error_setg(errp, "SEV is not enabled"); | |
533 | return NULL; | |
534 | } | |
535 | ||
536 | /* lets decode the mnonce string */ | |
537 | buf = g_base64_decode(mnonce, &len); | |
538 | if (!buf) { | |
539 | error_setg(errp, "SEV: failed to decode mnonce input"); | |
540 | return NULL; | |
541 | } | |
542 | ||
543 | /* verify the input mnonce length */ | |
544 | if (len != sizeof(input.mnonce)) { | |
545 | error_setg(errp, "SEV: mnonce must be %zu bytes (got %" G_GSIZE_FORMAT ")", | |
546 | sizeof(input.mnonce), len); | |
3ea1a802 BS |
547 | return NULL; |
548 | } | |
549 | ||
550 | /* Query the report length */ | |
551 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, | |
552 | &input, &err); | |
553 | if (ret < 0) { | |
554 | if (err != SEV_RET_INVALID_LEN) { | |
2c7233eb PMD |
555 | error_setg(errp, "SEV: Failed to query the attestation report" |
556 | " length ret=%d fw_err=%d (%s)", | |
557 | ret, err, fw_error_to_str(err)); | |
3ea1a802 BS |
558 | return NULL; |
559 | } | |
560 | } | |
561 | ||
562 | data = g_malloc(input.len); | |
563 | input.uaddr = (unsigned long)data; | |
564 | memcpy(input.mnonce, buf, sizeof(input.mnonce)); | |
565 | ||
566 | /* Query the report */ | |
567 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, | |
568 | &input, &err); | |
569 | if (ret) { | |
2c7233eb | 570 | error_setg_errno(errp, errno, "SEV: Failed to get attestation report" |
3ea1a802 | 571 | " ret=%d fw_err=%d (%s)", ret, err, fw_error_to_str(err)); |
ed84ae72 | 572 | return NULL; |
3ea1a802 BS |
573 | } |
574 | ||
575 | report = g_new0(SevAttestationReport, 1); | |
576 | report->data = g_base64_encode(data, input.len); | |
577 | ||
578 | trace_kvm_sev_attestation_report(mnonce, report->data); | |
579 | ||
3ea1a802 BS |
580 | return report; |
581 | } | |
582 | ||
3208de1c PMD |
583 | SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, |
584 | Error **errp) | |
585 | { | |
586 | return sev_get_attestation_report(mnonce, errp); | |
587 | } | |
588 | ||
620fd55c BS |
589 | static int |
590 | sev_read_file_base64(const char *filename, guchar **data, gsize *len) | |
591 | { | |
592 | gsize sz; | |
523a3d95 | 593 | g_autofree gchar *base64 = NULL; |
620fd55c BS |
594 | GError *error = NULL; |
595 | ||
596 | if (!g_file_get_contents(filename, &base64, &sz, &error)) { | |
2c7233eb | 597 | error_report("SEV: Failed to read '%s' (%s)", filename, error->message); |
efacd5b8 | 598 | g_error_free(error); |
620fd55c BS |
599 | return -1; |
600 | } | |
601 | ||
602 | *data = g_base64_decode(base64, len); | |
603 | return 0; | |
604 | } | |
605 | ||
606 | static int | |
75a877e3 | 607 | sev_launch_start(SevGuestState *sev) |
620fd55c BS |
608 | { |
609 | gsize sz; | |
610 | int ret = 1; | |
bf3175b4 | 611 | int fw_error, rc; |
620fd55c BS |
612 | struct kvm_sev_launch_start *start; |
613 | guchar *session = NULL, *dh_cert = NULL; | |
614 | ||
615 | start = g_new0(struct kvm_sev_launch_start, 1); | |
616 | ||
cf504cd6 | 617 | start->handle = sev->handle; |
0bd15277 | 618 | start->policy = sev->policy; |
620fd55c BS |
619 | if (sev->session_file) { |
620 | if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { | |
bf3175b4 | 621 | goto out; |
620fd55c BS |
622 | } |
623 | start->session_uaddr = (unsigned long)session; | |
624 | start->session_len = sz; | |
625 | } | |
626 | ||
627 | if (sev->dh_cert_file) { | |
628 | if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { | |
bf3175b4 | 629 | goto out; |
620fd55c BS |
630 | } |
631 | start->dh_uaddr = (unsigned long)dh_cert; | |
632 | start->dh_len = sz; | |
633 | } | |
634 | ||
635 | trace_kvm_sev_launch_start(start->policy, session, dh_cert); | |
421522eb | 636 | rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); |
bf3175b4 | 637 | if (rc < 0) { |
620fd55c BS |
638 | error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", |
639 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
bf3175b4 | 640 | goto out; |
620fd55c BS |
641 | } |
642 | ||
8673dee3 | 643 | sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE); |
cf504cd6 | 644 | sev->handle = start->handle; |
bf3175b4 | 645 | ret = 0; |
620fd55c | 646 | |
bf3175b4 | 647 | out: |
620fd55c BS |
648 | g_free(start); |
649 | g_free(session); | |
650 | g_free(dh_cert); | |
bf3175b4 | 651 | return ret; |
620fd55c BS |
652 | } |
653 | ||
b738d630 | 654 | static int |
8673dee3 | 655 | sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) |
b738d630 BS |
656 | { |
657 | int ret, fw_error; | |
658 | struct kvm_sev_launch_update_data update; | |
659 | ||
660 | if (!addr || !len) { | |
661 | return 1; | |
662 | } | |
663 | ||
664 | update.uaddr = (__u64)(unsigned long)addr; | |
665 | update.len = len; | |
666 | trace_kvm_sev_launch_update_data(addr, len); | |
421522eb | 667 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, |
b738d630 BS |
668 | &update, &fw_error); |
669 | if (ret) { | |
670 | error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", | |
671 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
672 | } | |
673 | ||
674 | return ret; | |
675 | } | |
676 | ||
6b98e96f TL |
677 | static int |
678 | sev_launch_update_vmsa(SevGuestState *sev) | |
679 | { | |
680 | int ret, fw_error; | |
681 | ||
682 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); | |
683 | if (ret) { | |
684 | error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", | |
685 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
686 | } | |
687 | ||
688 | return ret; | |
689 | } | |
690 | ||
c6c89c97 BS |
691 | static void |
692 | sev_launch_get_measure(Notifier *notifier, void *unused) | |
693 | { | |
8673dee3 | 694 | SevGuestState *sev = sev_guest; |
c6c89c97 | 695 | int ret, error; |
2f573c41 PMD |
696 | g_autofree guchar *data = NULL; |
697 | g_autofree struct kvm_sev_launch_measure *measurement = NULL; | |
c6c89c97 | 698 | |
8673dee3 | 699 | if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) { |
c6c89c97 BS |
700 | return; |
701 | } | |
702 | ||
6b98e96f TL |
703 | if (sev_es_enabled()) { |
704 | /* measure all the VM save areas before getting launch_measure */ | |
705 | ret = sev_launch_update_vmsa(sev); | |
706 | if (ret) { | |
707 | exit(1); | |
708 | } | |
709 | } | |
710 | ||
c6c89c97 BS |
711 | measurement = g_new0(struct kvm_sev_launch_measure, 1); |
712 | ||
713 | /* query the measurement blob length */ | |
421522eb | 714 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, |
c6c89c97 BS |
715 | measurement, &error); |
716 | if (!measurement->len) { | |
717 | error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", | |
718 | __func__, ret, error, fw_error_to_str(errno)); | |
2f573c41 | 719 | return; |
c6c89c97 BS |
720 | } |
721 | ||
722 | data = g_new0(guchar, measurement->len); | |
723 | measurement->uaddr = (unsigned long)data; | |
724 | ||
725 | /* get the measurement blob */ | |
421522eb | 726 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, |
c6c89c97 BS |
727 | measurement, &error); |
728 | if (ret) { | |
729 | error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", | |
730 | __func__, ret, error, fw_error_to_str(errno)); | |
2f573c41 | 731 | return; |
c6c89c97 BS |
732 | } |
733 | ||
8673dee3 | 734 | sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET); |
c6c89c97 BS |
735 | |
736 | /* encode the measurement value and emit the event */ | |
421522eb DG |
737 | sev->measurement = g_base64_encode(data, measurement->len); |
738 | trace_kvm_sev_launch_measurement(sev->measurement); | |
c6c89c97 BS |
739 | } |
740 | ||
741 | char * | |
742 | sev_get_launch_measurement(void) | |
743 | { | |
8673dee3 | 744 | if (sev_guest && |
421522eb DG |
745 | sev_guest->state >= SEV_STATE_LAUNCH_SECRET) { |
746 | return g_strdup(sev_guest->measurement); | |
c6c89c97 BS |
747 | } |
748 | ||
749 | return NULL; | |
750 | } | |
751 | ||
752 | static Notifier sev_machine_done_notify = { | |
753 | .notify = sev_launch_get_measure, | |
754 | }; | |
755 | ||
5dd0df7e | 756 | static void |
8673dee3 | 757 | sev_launch_finish(SevGuestState *sev) |
5dd0df7e BS |
758 | { |
759 | int ret, error; | |
760 | ||
761 | trace_kvm_sev_launch_finish(); | |
421522eb | 762 | ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); |
5dd0df7e BS |
763 | if (ret) { |
764 | error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", | |
765 | __func__, ret, error, fw_error_to_str(error)); | |
766 | exit(1); | |
767 | } | |
768 | ||
8673dee3 | 769 | sev_set_guest_state(sev, SEV_STATE_RUNNING); |
8fa4466d BS |
770 | |
771 | /* add migration blocker */ | |
772 | error_setg(&sev_mig_blocker, | |
773 | "SEV: Migration is not implemented"); | |
f9734d5d | 774 | migrate_add_blocker(sev_mig_blocker, &error_fatal); |
5dd0df7e BS |
775 | } |
776 | ||
777 | static void | |
538f0497 | 778 | sev_vm_state_change(void *opaque, bool running, RunState state) |
5dd0df7e | 779 | { |
8673dee3 | 780 | SevGuestState *sev = opaque; |
5dd0df7e BS |
781 | |
782 | if (running) { | |
8673dee3 DG |
783 | if (!sev_check_state(sev, SEV_STATE_RUNNING)) { |
784 | sev_launch_finish(sev); | |
5dd0df7e BS |
785 | } |
786 | } | |
787 | } | |
788 | ||
c9f5aaa6 | 789 | int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) |
d8575c6c | 790 | { |
ec78e2cd DG |
791 | SevGuestState *sev |
792 | = (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); | |
d8575c6c | 793 | char *devname; |
6b98e96f | 794 | int ret, fw_error, cmd; |
d8575c6c BS |
795 | uint32_t ebx; |
796 | uint32_t host_cbitpos; | |
797 | struct sev_user_data_status status = {}; | |
798 | ||
ec78e2cd DG |
799 | if (!sev) { |
800 | return 0; | |
801 | } | |
802 | ||
fee3f3ba DH |
803 | ret = ram_block_discard_disable(true); |
804 | if (ret) { | |
805 | error_report("%s: cannot disable RAM discard", __func__); | |
aacdb844 | 806 | return -1; |
fee3f3ba DH |
807 | } |
808 | ||
8673dee3 | 809 | sev_guest = sev; |
421522eb | 810 | sev->state = SEV_STATE_UNINIT; |
d8575c6c BS |
811 | |
812 | host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); | |
813 | host_cbitpos = ebx & 0x3f; | |
814 | ||
a06d2bad | 815 | if (host_cbitpos != sev->cbitpos) { |
c9f5aaa6 DG |
816 | error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", |
817 | __func__, host_cbitpos, sev->cbitpos); | |
d8575c6c BS |
818 | goto err; |
819 | } | |
820 | ||
a06d2bad | 821 | if (sev->reduced_phys_bits < 1) { |
c9f5aaa6 DG |
822 | error_setg(errp, "%s: reduced_phys_bits check failed, it should be >=1," |
823 | " requested '%d'", __func__, sev->reduced_phys_bits); | |
d8575c6c BS |
824 | goto err; |
825 | } | |
826 | ||
75a877e3 | 827 | devname = object_property_get_str(OBJECT(sev), "sev-device", NULL); |
421522eb DG |
828 | sev->sev_fd = open(devname, O_RDWR); |
829 | if (sev->sev_fd < 0) { | |
c9f5aaa6 DG |
830 | error_setg(errp, "%s: Failed to open %s '%s'", __func__, |
831 | devname, strerror(errno)); | |
832 | g_free(devname); | |
5d7bc72a GK |
833 | goto err; |
834 | } | |
c9f5aaa6 | 835 | g_free(devname); |
d8575c6c | 836 | |
421522eb | 837 | ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status, |
d8575c6c BS |
838 | &fw_error); |
839 | if (ret) { | |
c9f5aaa6 DG |
840 | error_setg(errp, "%s: failed to get platform status ret=%d " |
841 | "fw_error='%d: %s'", __func__, ret, fw_error, | |
842 | fw_error_to_str(fw_error)); | |
d8575c6c BS |
843 | goto err; |
844 | } | |
421522eb DG |
845 | sev->build_id = status.build; |
846 | sev->api_major = status.api_major; | |
847 | sev->api_minor = status.api_minor; | |
d8575c6c | 848 | |
6b98e96f | 849 | if (sev_es_enabled()) { |
9681f867 TL |
850 | if (!kvm_kernel_irqchip_allowed()) { |
851 | error_report("%s: SEV-ES guests require in-kernel irqchip support", | |
852 | __func__); | |
853 | goto err; | |
854 | } | |
855 | ||
6b98e96f TL |
856 | if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { |
857 | error_report("%s: guest policy requires SEV-ES, but " | |
858 | "host SEV-ES support unavailable", | |
859 | __func__); | |
860 | goto err; | |
861 | } | |
862 | cmd = KVM_SEV_ES_INIT; | |
863 | } else { | |
864 | cmd = KVM_SEV_INIT; | |
865 | } | |
866 | ||
d8575c6c | 867 | trace_kvm_sev_init(); |
6b98e96f | 868 | ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); |
d8575c6c | 869 | if (ret) { |
c9f5aaa6 DG |
870 | error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", |
871 | __func__, ret, fw_error, fw_error_to_str(fw_error)); | |
d8575c6c BS |
872 | goto err; |
873 | } | |
874 | ||
75a877e3 | 875 | ret = sev_launch_start(sev); |
620fd55c | 876 | if (ret) { |
c9f5aaa6 | 877 | error_setg(errp, "%s: failed to create encryption context", __func__); |
620fd55c BS |
878 | goto err; |
879 | } | |
880 | ||
2b308e44 | 881 | ram_block_notifier_add(&sev_ram_notifier); |
c6c89c97 | 882 | qemu_add_machine_init_done_notifier(&sev_machine_done_notify); |
8673dee3 | 883 | qemu_add_vm_change_state_handler(sev_vm_state_change, sev); |
2b308e44 | 884 | |
abc27d42 DG |
885 | cgs->ready = true; |
886 | ||
aacdb844 | 887 | return 0; |
d8575c6c | 888 | err: |
8673dee3 | 889 | sev_guest = NULL; |
fee3f3ba | 890 | ram_block_discard_disable(false); |
aacdb844 | 891 | return -1; |
d8575c6c BS |
892 | } |
893 | ||
b738d630 | 894 | int |
aacdb844 | 895 | sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) |
b738d630 | 896 | { |
aacdb844 DG |
897 | if (!sev_guest) { |
898 | return 0; | |
899 | } | |
b738d630 BS |
900 | |
901 | /* if SEV is in update state then encrypt the data else do nothing */ | |
aacdb844 DG |
902 | if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) { |
903 | int ret = sev_launch_update_data(sev_guest, ptr, len); | |
904 | if (ret < 0) { | |
2c7233eb | 905 | error_setg(errp, "SEV: Failed to encrypt pflash rom"); |
aacdb844 DG |
906 | return ret; |
907 | } | |
b738d630 BS |
908 | } |
909 | ||
910 | return 0; | |
911 | } | |
912 | ||
c7f7e697 TFF |
913 | int sev_inject_launch_secret(const char *packet_hdr, const char *secret, |
914 | uint64_t gpa, Error **errp) | |
915 | { | |
916 | struct kvm_sev_launch_secret input; | |
917 | g_autofree guchar *data = NULL, *hdr = NULL; | |
918 | int error, ret = 1; | |
919 | void *hva; | |
920 | gsize hdr_sz = 0, data_sz = 0; | |
921 | MemoryRegion *mr = NULL; | |
922 | ||
923 | if (!sev_guest) { | |
2c7233eb | 924 | error_setg(errp, "SEV not enabled for guest"); |
c7f7e697 TFF |
925 | return 1; |
926 | } | |
927 | ||
928 | /* secret can be injected only in this state */ | |
929 | if (!sev_check_state(sev_guest, SEV_STATE_LAUNCH_SECRET)) { | |
930 | error_setg(errp, "SEV: Not in correct state. (LSECRET) %x", | |
931 | sev_guest->state); | |
932 | return 1; | |
933 | } | |
934 | ||
935 | hdr = g_base64_decode(packet_hdr, &hdr_sz); | |
936 | if (!hdr || !hdr_sz) { | |
937 | error_setg(errp, "SEV: Failed to decode sequence header"); | |
938 | return 1; | |
939 | } | |
940 | ||
941 | data = g_base64_decode(secret, &data_sz); | |
942 | if (!data || !data_sz) { | |
943 | error_setg(errp, "SEV: Failed to decode data"); | |
944 | return 1; | |
945 | } | |
946 | ||
947 | hva = gpa2hva(&mr, gpa, data_sz, errp); | |
948 | if (!hva) { | |
949 | error_prepend(errp, "SEV: Failed to calculate guest address: "); | |
950 | return 1; | |
951 | } | |
952 | ||
953 | input.hdr_uaddr = (uint64_t)(unsigned long)hdr; | |
954 | input.hdr_len = hdr_sz; | |
955 | ||
956 | input.trans_uaddr = (uint64_t)(unsigned long)data; | |
957 | input.trans_len = data_sz; | |
958 | ||
959 | input.guest_uaddr = (uint64_t)(unsigned long)hva; | |
960 | input.guest_len = data_sz; | |
961 | ||
962 | trace_kvm_sev_launch_secret(gpa, input.guest_uaddr, | |
963 | input.trans_uaddr, input.trans_len); | |
964 | ||
965 | ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_LAUNCH_SECRET, | |
966 | &input, &error); | |
967 | if (ret) { | |
968 | error_setg(errp, "SEV: failed to inject secret ret=%d fw_error=%d '%s'", | |
969 | ret, error, fw_error_to_str(error)); | |
970 | return ret; | |
971 | } | |
972 | ||
973 | return 0; | |
974 | } | |
975 | ||
11a6ed0e PMD |
976 | #define SEV_SECRET_GUID "4c2eb361-7d9b-4cc3-8081-127c90d3d294" |
977 | struct sev_secret_area { | |
978 | uint32_t base; | |
979 | uint32_t size; | |
980 | }; | |
981 | ||
982 | void qmp_sev_inject_launch_secret(const char *packet_hdr, | |
983 | const char *secret, | |
984 | bool has_gpa, uint64_t gpa, | |
985 | Error **errp) | |
986 | { | |
987 | if (!sev_enabled()) { | |
988 | error_setg(errp, "SEV not enabled for guest"); | |
989 | return; | |
990 | } | |
991 | if (!has_gpa) { | |
992 | uint8_t *data; | |
993 | struct sev_secret_area *area; | |
994 | ||
995 | if (!pc_system_ovmf_table_find(SEV_SECRET_GUID, &data, NULL)) { | |
996 | error_setg(errp, "SEV: no secret area found in OVMF," | |
997 | " gpa must be specified."); | |
998 | return; | |
999 | } | |
1000 | area = (struct sev_secret_area *)data; | |
1001 | gpa = area->base; | |
1002 | } | |
1003 | ||
1004 | sev_inject_launch_secret(packet_hdr, secret, gpa, errp); | |
1005 | } | |
1006 | ||
b2f73a07 PB |
1007 | static int |
1008 | sev_es_parse_reset_block(SevInfoBlock *info, uint32_t *addr) | |
1009 | { | |
1010 | if (!info->reset_addr) { | |
1011 | error_report("SEV-ES reset address is zero"); | |
1012 | return 1; | |
1013 | } | |
1014 | ||
1015 | *addr = info->reset_addr; | |
1016 | ||
1017 | return 0; | |
1018 | } | |
1019 | ||
1020 | static int | |
1021 | sev_es_find_reset_vector(void *flash_ptr, uint64_t flash_size, | |
1022 | uint32_t *addr) | |
1023 | { | |
1024 | QemuUUID info_guid, *guid; | |
1025 | SevInfoBlock *info; | |
1026 | uint8_t *data; | |
1027 | uint16_t *len; | |
1028 | ||
1029 | /* | |
1030 | * Initialize the address to zero. An address of zero with a successful | |
1031 | * return code indicates that SEV-ES is not active. | |
1032 | */ | |
1033 | *addr = 0; | |
1034 | ||
1035 | /* | |
1036 | * Extract the AP reset vector for SEV-ES guests by locating the SEV GUID. | |
1037 | * The SEV GUID is located on its own (original implementation) or within | |
1038 | * the Firmware GUID Table (new implementation), either of which are | |
1039 | * located 32 bytes from the end of the flash. | |
1040 | * | |
1041 | * Check the Firmware GUID Table first. | |
1042 | */ | |
1043 | if (pc_system_ovmf_table_find(SEV_INFO_BLOCK_GUID, &data, NULL)) { | |
1044 | return sev_es_parse_reset_block((SevInfoBlock *)data, addr); | |
1045 | } | |
1046 | ||
1047 | /* | |
1048 | * SEV info block not found in the Firmware GUID Table (or there isn't | |
1049 | * a Firmware GUID Table), fall back to the original implementation. | |
1050 | */ | |
1051 | data = flash_ptr + flash_size - 0x20; | |
1052 | ||
1053 | qemu_uuid_parse(SEV_INFO_BLOCK_GUID, &info_guid); | |
1054 | info_guid = qemu_uuid_bswap(info_guid); /* GUIDs are LE */ | |
1055 | ||
1056 | guid = (QemuUUID *)(data - sizeof(info_guid)); | |
1057 | if (!qemu_uuid_is_equal(guid, &info_guid)) { | |
1058 | error_report("SEV information block/Firmware GUID Table block not found in pflash rom"); | |
1059 | return 1; | |
1060 | } | |
1061 | ||
1062 | len = (uint16_t *)((uint8_t *)guid - sizeof(*len)); | |
1063 | info = (SevInfoBlock *)(data - le16_to_cpu(*len)); | |
1064 | ||
1065 | return sev_es_parse_reset_block(info, addr); | |
1066 | } | |
1067 | ||
1068 | void sev_es_set_reset_vector(CPUState *cpu) | |
1069 | { | |
1070 | X86CPU *x86; | |
1071 | CPUX86State *env; | |
1072 | ||
1073 | /* Only update if we have valid reset information */ | |
1074 | if (!sev_guest || !sev_guest->reset_data_valid) { | |
1075 | return; | |
1076 | } | |
1077 | ||
1078 | /* Do not update the BSP reset state */ | |
1079 | if (cpu->cpu_index == 0) { | |
1080 | return; | |
1081 | } | |
1082 | ||
1083 | x86 = X86_CPU(cpu); | |
1084 | env = &x86->env; | |
1085 | ||
1086 | cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_guest->reset_cs, 0xffff, | |
1087 | DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | | |
1088 | DESC_R_MASK | DESC_A_MASK); | |
1089 | ||
1090 | env->eip = sev_guest->reset_ip; | |
1091 | } | |
1092 | ||
1093 | int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) | |
1094 | { | |
1095 | CPUState *cpu; | |
1096 | uint32_t addr; | |
1097 | int ret; | |
1098 | ||
1099 | if (!sev_es_enabled()) { | |
1100 | return 0; | |
1101 | } | |
1102 | ||
1103 | addr = 0; | |
1104 | ret = sev_es_find_reset_vector(flash_ptr, flash_size, | |
1105 | &addr); | |
1106 | if (ret) { | |
1107 | return ret; | |
1108 | } | |
1109 | ||
1110 | if (addr) { | |
1111 | sev_guest->reset_cs = addr & 0xffff0000; | |
1112 | sev_guest->reset_ip = addr & 0x0000ffff; | |
1113 | sev_guest->reset_data_valid = true; | |
1114 | ||
1115 | CPU_FOREACH(cpu) { | |
1116 | sev_es_set_reset_vector(cpu); | |
1117 | } | |
1118 | } | |
1119 | ||
1120 | return 0; | |
1121 | } | |
1122 | ||
cff03145 DM |
1123 | static const QemuUUID sev_hash_table_header_guid = { |
1124 | .data = UUID_LE(0x9438d606, 0x4f22, 0x4cc9, 0xb4, 0x79, 0xa7, 0x93, | |
1125 | 0xd4, 0x11, 0xfd, 0x21) | |
1126 | }; | |
1127 | ||
1128 | static const QemuUUID sev_kernel_entry_guid = { | |
1129 | .data = UUID_LE(0x4de79437, 0xabd2, 0x427f, 0xb8, 0x35, 0xd5, 0xb1, | |
1130 | 0x72, 0xd2, 0x04, 0x5b) | |
1131 | }; | |
1132 | static const QemuUUID sev_initrd_entry_guid = { | |
1133 | .data = UUID_LE(0x44baf731, 0x3a2f, 0x4bd7, 0x9a, 0xf1, 0x41, 0xe2, | |
1134 | 0x91, 0x69, 0x78, 0x1d) | |
1135 | }; | |
1136 | static const QemuUUID sev_cmdline_entry_guid = { | |
1137 | .data = UUID_LE(0x97d02dd8, 0xbd20, 0x4c94, 0xaa, 0x78, 0xe7, 0x71, | |
1138 | 0x4d, 0x36, 0xab, 0x2a) | |
1139 | }; | |
1140 | ||
1141 | /* | |
1142 | * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page | |
1143 | * which is included in SEV's initial memory measurement. | |
1144 | */ | |
1145 | bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) | |
1146 | { | |
1147 | uint8_t *data; | |
1148 | SevHashTableDescriptor *area; | |
1149 | SevHashTable *ht; | |
1150 | uint8_t cmdline_hash[HASH_SIZE]; | |
1151 | uint8_t initrd_hash[HASH_SIZE]; | |
1152 | uint8_t kernel_hash[HASH_SIZE]; | |
1153 | uint8_t *hashp; | |
1154 | size_t hash_len = HASH_SIZE; | |
1155 | int aligned_len; | |
1156 | ||
1157 | if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { | |
1158 | error_setg(errp, "SEV: kernel specified but OVMF has no hash table guid"); | |
1159 | return false; | |
1160 | } | |
1161 | area = (SevHashTableDescriptor *)data; | |
1162 | ||
1163 | /* | |
1164 | * Calculate hash of kernel command-line with the terminating null byte. If | |
1165 | * the user doesn't supply a command-line via -append, the 1-byte "\0" will | |
1166 | * be used. | |
1167 | */ | |
1168 | hashp = cmdline_hash; | |
1169 | if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->cmdline_data, | |
1170 | ctx->cmdline_size, &hashp, &hash_len, errp) < 0) { | |
1171 | return false; | |
1172 | } | |
1173 | assert(hash_len == HASH_SIZE); | |
1174 | ||
1175 | /* | |
1176 | * Calculate hash of initrd. If the user doesn't supply an initrd via | |
1177 | * -initrd, an empty buffer will be used (ctx->initrd_size == 0). | |
1178 | */ | |
1179 | hashp = initrd_hash; | |
1180 | if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->initrd_data, | |
1181 | ctx->initrd_size, &hashp, &hash_len, errp) < 0) { | |
1182 | return false; | |
1183 | } | |
1184 | assert(hash_len == HASH_SIZE); | |
1185 | ||
1186 | /* Calculate hash of the kernel */ | |
1187 | hashp = kernel_hash; | |
1188 | struct iovec iov[2] = { | |
1189 | { .iov_base = ctx->setup_data, .iov_len = ctx->setup_size }, | |
1190 | { .iov_base = ctx->kernel_data, .iov_len = ctx->kernel_size } | |
1191 | }; | |
1192 | if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, iov, ARRAY_SIZE(iov), | |
1193 | &hashp, &hash_len, errp) < 0) { | |
1194 | return false; | |
1195 | } | |
1196 | assert(hash_len == HASH_SIZE); | |
1197 | ||
1198 | /* | |
1199 | * Populate the hashes table in the guest's memory at the OVMF-designated | |
1200 | * area for the SEV hashes table | |
1201 | */ | |
1202 | ht = qemu_map_ram_ptr(NULL, area->base); | |
1203 | ||
1204 | ht->guid = sev_hash_table_header_guid; | |
1205 | ht->len = sizeof(*ht); | |
1206 | ||
1207 | ht->cmdline.guid = sev_cmdline_entry_guid; | |
1208 | ht->cmdline.len = sizeof(ht->cmdline); | |
1209 | memcpy(ht->cmdline.hash, cmdline_hash, sizeof(ht->cmdline.hash)); | |
1210 | ||
1211 | ht->initrd.guid = sev_initrd_entry_guid; | |
1212 | ht->initrd.len = sizeof(ht->initrd); | |
1213 | memcpy(ht->initrd.hash, initrd_hash, sizeof(ht->initrd.hash)); | |
1214 | ||
1215 | ht->kernel.guid = sev_kernel_entry_guid; | |
1216 | ht->kernel.len = sizeof(ht->kernel); | |
1217 | memcpy(ht->kernel.hash, kernel_hash, sizeof(ht->kernel.hash)); | |
1218 | ||
1219 | /* When calling sev_encrypt_flash, the length has to be 16 byte aligned */ | |
1220 | aligned_len = ROUND_UP(ht->len, 16); | |
1221 | if (aligned_len != ht->len) { | |
1222 | /* zero the excess data so the measurement can be reliably calculated */ | |
1223 | memset(ht->padding, 0, aligned_len - ht->len); | |
1224 | } | |
1225 | ||
1226 | if (sev_encrypt_flash((uint8_t *)ht, aligned_len, errp) < 0) { | |
1227 | return false; | |
1228 | } | |
1229 | ||
1230 | return true; | |
1231 | } | |
1232 | ||
a9b4942f BS |
1233 | static void |
1234 | sev_register_types(void) | |
1235 | { | |
d2d8a198 | 1236 | type_register_static(&sev_guest_info); |
a9b4942f BS |
1237 | } |
1238 | ||
1239 | type_init(sev_register_types); |