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