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