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