xref: /openbmc/qemu/backends/igvm.c (revision c1d466d267cf40093c9489075906b385459a195f)
1 /*
2  * QEMU IGVM configuration backend for guests
3  *
4  * Copyright (C) 2023-2024 SUSE
5  *
6  * Authors:
7  *  Roy Hopkins <roy.hopkins@randomman.co.uk>
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 #include "qemu/osdep.h"
13 
14 #include "igvm.h"
15 #include "qapi/error.h"
16 #include "system/memory.h"
17 #include "system/address-spaces.h"
18 #include "hw/core/cpu.h"
19 
20 #include <igvm/igvm.h>
21 #include <igvm/igvm_defs.h>
22 
23 typedef struct QIgvmParameterData {
24     QTAILQ_ENTRY(QIgvmParameterData) next;
25     uint8_t *data;
26     uint32_t size;
27     uint32_t index;
28 } QIgvmParameterData;
29 
30 /*
31  * QIgvm contains the information required during processing
32  * of a single IGVM file.
33  */
34 typedef struct QIgvm {
35     IgvmHandle file;
36     ConfidentialGuestSupport *cgs;
37     ConfidentialGuestSupportClass *cgsc;
38     uint32_t compatibility_mask;
39     unsigned current_header_index;
40     QTAILQ_HEAD(, QIgvmParameterData) parameter_data;
41 
42     /* These variables keep track of contiguous page regions */
43     IGVM_VHS_PAGE_DATA region_prev_page_data;
44     uint64_t region_start;
45     unsigned region_start_index;
46     unsigned region_last_index;
47     unsigned region_page_count;
48 } QIgvm;
49 
50 static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data,
51                                      Error **errp);
52 static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
53                                       Error **errp);
54 static int qigvm_directive_parameter_area(QIgvm *ctx,
55                                           const uint8_t *header_data,
56                                           Error **errp);
57 static int qigvm_directive_parameter_insert(QIgvm *ctx,
58                                             const uint8_t *header_data,
59                                             Error **errp);
60 static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
61                                       Error **errp);
62 static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data,
63                                     Error **errp);
64 static int qigvm_directive_environment_info(QIgvm *ctx,
65                                             const uint8_t *header_data,
66                                             Error **errp);
67 static int qigvm_directive_required_memory(QIgvm *ctx,
68                                            const uint8_t *header_data,
69                                            Error **errp);
70 
71 struct QIGVMHandler {
72     uint32_t type;
73     uint32_t section;
74     int (*handler)(QIgvm *ctx, const uint8_t *header_data, Error **errp);
75 };
76 
77 static struct QIGVMHandler handlers[] = {
78     { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE,
79       qigvm_directive_page_data },
80     { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE,
81       qigvm_directive_vp_context },
82     { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE,
83       qigvm_directive_parameter_area },
84     { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE,
85       qigvm_directive_parameter_insert },
86     { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE,
87       qigvm_directive_memory_map },
88     { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
89       qigvm_directive_vp_count },
90     { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE,
91       qigvm_directive_environment_info },
92     { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE,
93       qigvm_directive_required_memory },
94 };
95 
96 static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp)
97 {
98     size_t handler;
99     IgvmHandle header_handle;
100     const uint8_t *header_data;
101     int result;
102 
103     for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) {
104         if (handlers[handler].type != type) {
105             continue;
106         }
107         header_handle = igvm_get_header(ctx->file, handlers[handler].section,
108                                         ctx->current_header_index);
109         if (header_handle < 0) {
110             error_setg(
111                 errp,
112                 "IGVM file is invalid: Failed to read directive header (code: %d)",
113                 (int)header_handle);
114             return -1;
115         }
116         header_data = igvm_get_buffer(ctx->file, header_handle) +
117                       sizeof(IGVM_VHS_VARIABLE_HEADER);
118         result = handlers[handler].handler(ctx, header_data, errp);
119         igvm_free_buffer(ctx->file, header_handle);
120         return result;
121     }
122     error_setg(errp,
123                "IGVM: Unknown header type encountered when processing file: "
124                "(type 0x%X)",
125                type);
126     return -1;
127 }
128 
129 static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size,
130                                   int region_identifier, Error **errp)
131 {
132     ERRP_GUARD();
133     MemoryRegion *igvm_pages = NULL;
134     Int128 gpa_region_size;
135     MemoryRegionSection mrs =
136         memory_region_find(get_system_memory(), addr, size);
137     if (mrs.mr) {
138         if (!memory_region_is_ram(mrs.mr)) {
139             memory_region_unref(mrs.mr);
140             error_setg(
141                 errp,
142                 "Processing of IGVM file failed: Could not prepare memory "
143                 "at address 0x%lX due to existing non-RAM region",
144                 addr);
145             return NULL;
146         }
147 
148         gpa_region_size = int128_make64(size);
149         if (int128_lt(mrs.size, gpa_region_size)) {
150             memory_region_unref(mrs.mr);
151             error_setg(
152                 errp,
153                 "Processing of IGVM file failed: Could not prepare memory "
154                 "at address 0x%lX: region size exceeded",
155                 addr);
156             return NULL;
157         }
158         return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region);
159     } else {
160         /*
161          * The region_identifier is the is the index of the IGVM directive that
162          * contains the page with the lowest GPA in the region. This will
163          * generate a unique region name.
164          */
165         g_autofree char *region_name =
166             g_strdup_printf("igvm.%X", region_identifier);
167         igvm_pages = g_new0(MemoryRegion, 1);
168         if (ctx->cgs && ctx->cgs->require_guest_memfd) {
169             if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL,
170                                                     region_name, size, errp)) {
171                 return NULL;
172             }
173         } else {
174             if (!memory_region_init_ram(igvm_pages, NULL, region_name, size,
175                                         errp)) {
176                 return NULL;
177             }
178         }
179         memory_region_add_subregion(get_system_memory(), addr, igvm_pages);
180         return memory_region_get_ram_ptr(igvm_pages);
181     }
182 }
183 
184 static int qigvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmeasured,
185                                   bool zero)
186 {
187     switch (memory_type) {
188     case IGVM_PAGE_DATA_TYPE_NORMAL: {
189         if (unmeasured) {
190             return CGS_PAGE_TYPE_UNMEASURED;
191         } else {
192             return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL;
193         }
194     }
195     case IGVM_PAGE_DATA_TYPE_SECRETS:
196         return CGS_PAGE_TYPE_SECRETS;
197     case IGVM_PAGE_DATA_TYPE_CPUID_DATA:
198         return CGS_PAGE_TYPE_CPUID;
199     case IGVM_PAGE_DATA_TYPE_CPUID_XF:
200         return CGS_PAGE_TYPE_CPUID;
201     default:
202         return -1;
203     }
204 }
205 
206 static bool qigvm_page_attrs_equal(IgvmHandle igvm, unsigned header_index,
207                                    const IGVM_VHS_PAGE_DATA *page_1,
208                                    const IGVM_VHS_PAGE_DATA *page_2)
209 {
210     IgvmHandle data_handle1, data_handle2;
211 
212     /*
213      * If one page has data and the other doesn't then this results in different
214      * page types: NORMAL vs ZERO.
215      */
216     data_handle1 = igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE,
217                                         header_index - 1);
218     data_handle2 =
219         igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_index);
220     if ((data_handle1 == IGVMAPI_NO_DATA ||
221          data_handle2 == IGVMAPI_NO_DATA) &&
222          data_handle1 != data_handle2) {
223         return false;
224     }
225     return ((*(const uint32_t *)&page_1->flags ==
226              *(const uint32_t *)&page_2->flags) &&
227             (page_1->data_type == page_2->data_type) &&
228             (page_1->compatibility_mask == page_2->compatibility_mask));
229 }
230 
231 static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index,
232                                     uint64_t gpa_start, unsigned page_count,
233                                     const IgvmPageDataFlags *flags,
234                                     const IgvmPageDataType page_type,
235                                     Error **errp)
236 {
237     uint8_t *region;
238     IgvmHandle data_handle;
239     const void *data;
240     uint32_t data_size;
241     unsigned page_index;
242     bool zero = true;
243     const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000;
244     int result;
245     int cgs_page_type;
246 
247     region = qigvm_prepare_memory(ctx, gpa_start, page_count * page_size,
248                                   start_index, errp);
249     if (!region) {
250         return -1;
251     }
252 
253     for (page_index = 0; page_index < page_count; page_index++) {
254         data_handle = igvm_get_header_data(
255             ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index);
256         if (data_handle == IGVMAPI_NO_DATA) {
257             /* No data indicates a zero page */
258             memset(&region[page_index * page_size], 0, page_size);
259         } else if (data_handle < 0) {
260             error_setg(
261                 errp,
262                 "IGVM file contains invalid page data for directive with "
263                 "index %d",
264                 page_index + start_index);
265             return -1;
266         } else {
267             zero = false;
268             data_size = igvm_get_buffer_size(ctx->file, data_handle);
269             if (data_size < page_size) {
270                 memset(&region[page_index * page_size], 0, page_size);
271             } else if (data_size > page_size) {
272                 error_setg(errp,
273                            "IGVM file contains page data with invalid size for "
274                            "directive with index %d",
275                            page_index + start_index);
276                 return -1;
277             }
278             data = igvm_get_buffer(ctx->file, data_handle);
279             memcpy(&region[page_index * page_size], data, data_size);
280             igvm_free_buffer(ctx->file, data_handle);
281         }
282     }
283 
284     /*
285      * If a confidential guest support object is provided then use it to set the
286      * guest state.
287      */
288     if (ctx->cgs) {
289         cgs_page_type =
290             qigvm_type_to_cgs_type(page_type, flags->unmeasured, zero);
291         if (cgs_page_type < 0) {
292             error_setg(errp,
293                        "Invalid page type in IGVM file. Directives: %d to %d, "
294                        "page type: %d",
295                        start_index, start_index + page_count, page_type);
296             return -1;
297         }
298 
299         result = ctx->cgsc->set_guest_state(
300             gpa_start, region, page_size * page_count, cgs_page_type, 0, errp);
301         if (result < 0) {
302             return result;
303         }
304     }
305     return 0;
306 }
307 
308 static int qigvm_process_mem_page(QIgvm *ctx,
309                                   const IGVM_VHS_PAGE_DATA *page_data,
310                                   Error **errp)
311 {
312     if (page_data) {
313         if (ctx->region_page_count == 0) {
314             ctx->region_start = page_data->gpa;
315             ctx->region_start_index = ctx->current_header_index;
316         } else {
317             if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index,
318                                         page_data,
319                                         &ctx->region_prev_page_data) ||
320                 ((ctx->region_prev_page_data.gpa +
321                   (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 :
322                                                                   0x1000)) !=
323                  page_data->gpa) ||
324                 (ctx->region_last_index != (ctx->current_header_index - 1))) {
325                 /* End of current region */
326                 if (qigvm_process_mem_region(
327                         ctx, ctx->region_start_index, ctx->region_start,
328                         ctx->region_page_count,
329                         &ctx->region_prev_page_data.flags,
330                         ctx->region_prev_page_data.data_type, errp) < 0) {
331                     return -1;
332                 }
333                 ctx->region_page_count = 0;
334                 ctx->region_start = page_data->gpa;
335                 ctx->region_start_index = ctx->current_header_index;
336             }
337         }
338         memcpy(&ctx->region_prev_page_data, page_data,
339                sizeof(ctx->region_prev_page_data));
340         ctx->region_last_index = ctx->current_header_index;
341         ctx->region_page_count++;
342     } else {
343         if (ctx->region_page_count > 0) {
344             if (qigvm_process_mem_region(
345                     ctx, ctx->region_start_index, ctx->region_start,
346                     ctx->region_page_count, &ctx->region_prev_page_data.flags,
347                     ctx->region_prev_page_data.data_type, errp) < 0) {
348                 return -1;
349             }
350             ctx->region_page_count = 0;
351         }
352     }
353     return 0;
354 }
355 
356 static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data,
357                                      Error **errp)
358 {
359     const IGVM_VHS_PAGE_DATA *page_data =
360         (const IGVM_VHS_PAGE_DATA *)header_data;
361     if (page_data->compatibility_mask & ctx->compatibility_mask) {
362         return qigvm_process_mem_page(ctx, page_data, errp);
363     }
364     return 0;
365 }
366 
367 static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data,
368                                       Error **errp)
369 {
370     const IGVM_VHS_VP_CONTEXT *vp_context =
371         (const IGVM_VHS_VP_CONTEXT *)header_data;
372     IgvmHandle data_handle;
373     uint8_t *data;
374     int result;
375 
376     if (!(vp_context->compatibility_mask & ctx->compatibility_mask)) {
377         return 0;
378     }
379 
380     /*
381      * A confidential guest support object must be provided for setting
382      * a VP context.
383      */
384     if (!ctx->cgs) {
385         error_setg(
386             errp,
387             "A VP context is present in the IGVM file but is not supported "
388             "by the current system.");
389         return -1;
390     }
391 
392     data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE,
393                                        ctx->current_header_index);
394     if (data_handle < 0) {
395         error_setg(errp, "Invalid VP context in IGVM file. Error code: %X",
396                    data_handle);
397         return -1;
398     }
399 
400     data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle);
401     result = ctx->cgsc->set_guest_state(
402         vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle),
403         CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp);
404     igvm_free_buffer(ctx->file, data_handle);
405     if (result < 0) {
406         return result;
407     }
408     return 0;
409 }
410 
411 static int qigvm_directive_parameter_area(QIgvm *ctx,
412                                           const uint8_t *header_data,
413                                           Error **errp)
414 {
415     const IGVM_VHS_PARAMETER_AREA *param_area =
416         (const IGVM_VHS_PARAMETER_AREA *)header_data;
417     QIgvmParameterData *param_entry;
418 
419     param_entry = g_new0(QIgvmParameterData, 1);
420     param_entry->size = param_area->number_of_bytes;
421     param_entry->index = param_area->parameter_area_index;
422     param_entry->data = g_malloc0(param_entry->size);
423 
424     QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next);
425     return 0;
426 }
427 
428 static int qigvm_directive_parameter_insert(QIgvm *ctx,
429                                             const uint8_t *header_data,
430                                             Error **errp)
431 {
432     const IGVM_VHS_PARAMETER_INSERT *param =
433         (const IGVM_VHS_PARAMETER_INSERT *)header_data;
434     QIgvmParameterData *param_entry;
435     int result;
436     void *region;
437 
438     if (!(param->compatibility_mask & ctx->compatibility_mask)) {
439         return 0;
440     }
441 
442     QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
443     {
444         if (param_entry->index == param->parameter_area_index) {
445             region = qigvm_prepare_memory(ctx, param->gpa, param_entry->size,
446                                           ctx->current_header_index, errp);
447             if (!region) {
448                 return -1;
449             }
450             memcpy(region, param_entry->data, param_entry->size);
451             g_free(param_entry->data);
452             param_entry->data = NULL;
453 
454             /*
455              * If a confidential guest support object is provided then use it to
456              * set the guest state.
457              */
458             if (ctx->cgs) {
459                 result = ctx->cgsc->set_guest_state(param->gpa, region,
460                                                     param_entry->size,
461                                                     CGS_PAGE_TYPE_UNMEASURED, 0,
462                                                     errp);
463                 if (result < 0) {
464                     return -1;
465                 }
466             }
467         }
468     }
469     return 0;
470 }
471 
472 static int qigvm_cmp_mm_entry(const void *a, const void *b)
473 {
474     const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a =
475         (const IGVM_VHS_MEMORY_MAP_ENTRY *)a;
476     const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b =
477         (const IGVM_VHS_MEMORY_MAP_ENTRY *)b;
478     if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_number) {
479         return -1;
480     } else if (entry_a->starting_gpa_page_number >
481                entry_b->starting_gpa_page_number) {
482         return 1;
483     } else {
484         return 0;
485     }
486 }
487 
488 static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data,
489                                       Error **errp)
490 {
491     const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
492     QIgvmParameterData *param_entry;
493     int max_entry_count;
494     int entry = 0;
495     IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry;
496     ConfidentialGuestMemoryMapEntry cgmm_entry;
497     int retval = 0;
498 
499     if (!ctx->cgs) {
500         error_setg(errp,
501                    "IGVM file contains a memory map but this is not supported "
502                    "by the current system.");
503         return -1;
504     }
505 
506     /* Find the parameter area that should hold the memory map */
507     QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
508     {
509         if (param_entry->index == param->parameter_area_index) {
510             max_entry_count =
511                 param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY);
512             mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data;
513 
514             retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp);
515             while (retval == 0) {
516                 if (entry > max_entry_count) {
517                     error_setg(
518                         errp,
519                         "IGVM: guest memory map size exceeds parameter area defined in IGVM file");
520                     return -1;
521                 }
522                 mm_entry[entry].starting_gpa_page_number = cgmm_entry.gpa >> 12;
523                 mm_entry[entry].number_of_pages = cgmm_entry.size >> 12;
524 
525                 switch (cgmm_entry.type) {
526                 case CGS_MEM_RAM:
527                     mm_entry[entry].entry_type =
528                         IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY;
529                     break;
530                 case CGS_MEM_RESERVED:
531                     mm_entry[entry].entry_type =
532                         IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
533                     break;
534                 case CGS_MEM_ACPI:
535                     mm_entry[entry].entry_type =
536                         IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
537                     break;
538                 case CGS_MEM_NVS:
539                     mm_entry[entry].entry_type =
540                         IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT;
541                     break;
542                 case CGS_MEM_UNUSABLE:
543                     mm_entry[entry].entry_type =
544                         IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED;
545                     break;
546                 }
547                 retval =
548                     ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp);
549             }
550             if (retval < 0) {
551                 return retval;
552             }
553             /* The entries need to be sorted */
554             qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY),
555                   qigvm_cmp_mm_entry);
556 
557             break;
558         }
559     }
560     return 0;
561 }
562 
563 static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data,
564                                     Error **errp)
565 {
566     const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
567     QIgvmParameterData *param_entry;
568     uint32_t *vp_count;
569     CPUState *cpu;
570 
571     QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
572     {
573         if (param_entry->index == param->parameter_area_index) {
574             vp_count = (uint32_t *)(param_entry->data + param->byte_offset);
575             *vp_count = 0;
576             CPU_FOREACH(cpu)
577             {
578                 (*vp_count)++;
579             }
580             break;
581         }
582     }
583     return 0;
584 }
585 
586 static int qigvm_directive_environment_info(QIgvm *ctx,
587                                             const uint8_t *header_data,
588                                             Error **errp)
589 {
590     const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
591     QIgvmParameterData *param_entry;
592     IgvmEnvironmentInfo *environmental_state;
593 
594     QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
595     {
596         if (param_entry->index == param->parameter_area_index) {
597             environmental_state =
598                 (IgvmEnvironmentInfo *)(param_entry->data + param->byte_offset);
599             environmental_state->memory_is_shared = 1;
600             break;
601         }
602     }
603     return 0;
604 }
605 
606 static int qigvm_directive_required_memory(QIgvm *ctx,
607                                            const uint8_t *header_data,
608                                            Error **errp)
609 {
610     const IGVM_VHS_REQUIRED_MEMORY *mem =
611         (const IGVM_VHS_REQUIRED_MEMORY *)header_data;
612     uint8_t *region;
613     int result;
614 
615     if (!(mem->compatibility_mask & ctx->compatibility_mask)) {
616         return 0;
617     }
618 
619     region = qigvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes,
620                                   ctx->current_header_index, errp);
621     if (!region) {
622         return -1;
623     }
624     if (ctx->cgs) {
625         result =
626             ctx->cgsc->set_guest_state(mem->gpa, region, mem->number_of_bytes,
627                                        CGS_PAGE_TYPE_REQUIRED_MEMORY, 0, errp);
628         if (result < 0) {
629             return result;
630         }
631     }
632     return 0;
633 }
634 
635 static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
636 {
637     int32_t header_count;
638     unsigned header_index;
639     IgvmHandle header_handle;
640     IGVM_VHS_SUPPORTED_PLATFORM *platform;
641     uint32_t compatibility_mask_sev = 0;
642     uint32_t compatibility_mask_sev_es = 0;
643     uint32_t compatibility_mask_sev_snp = 0;
644     uint32_t compatibility_mask = 0;
645 
646     header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM);
647     if (header_count < 0) {
648         error_setg(errp,
649                    "Invalid platform header count in IGVM file. Error code: %X",
650                    header_count);
651         return -1;
652     }
653 
654     for (header_index = 0; header_index < (unsigned)header_count;
655          header_index++) {
656         IgvmVariableHeaderType typ = igvm_get_header_type(
657             ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
658         if (typ == IGVM_VHT_SUPPORTED_PLATFORM) {
659             header_handle = igvm_get_header(
660                 ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index);
661             if (header_handle < 0) {
662                 error_setg(errp,
663                            "Invalid platform header in IGVM file. "
664                            "Index: %d, Error code: %X",
665                            header_index, header_handle);
666                 return -1;
667             }
668             platform =
669                 (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file,
670                                                                 header_handle) +
671                                                 sizeof(
672                                                     IGVM_VHS_VARIABLE_HEADER));
673             if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV_ES) &&
674                 ctx->cgs) {
675                 if (ctx->cgsc->check_support(
676                         CGS_PLATFORM_SEV_ES, platform->platform_version,
677                         platform->highest_vtl, platform->shared_gpa_boundary)) {
678                     compatibility_mask_sev_es = platform->compatibility_mask;
679                 }
680             } else if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV) &&
681                        ctx->cgs) {
682                 if (ctx->cgsc->check_support(
683                         CGS_PLATFORM_SEV, platform->platform_version,
684                         platform->highest_vtl, platform->shared_gpa_boundary)) {
685                     compatibility_mask_sev = platform->compatibility_mask;
686                 }
687             } else if ((platform->platform_type ==
688                         IGVM_PLATFORM_TYPE_SEV_SNP) &&
689                        ctx->cgs) {
690                 if (ctx->cgsc->check_support(
691                         CGS_PLATFORM_SEV_SNP, platform->platform_version,
692                         platform->highest_vtl, platform->shared_gpa_boundary)) {
693                     compatibility_mask_sev_snp = platform->compatibility_mask;
694                 }
695             } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) {
696                 compatibility_mask = platform->compatibility_mask;
697             }
698             igvm_free_buffer(ctx->file, header_handle);
699         }
700     }
701     /* Choose the strongest supported isolation technology */
702     if (compatibility_mask_sev_snp != 0) {
703         ctx->compatibility_mask = compatibility_mask_sev_snp;
704     } else if (compatibility_mask_sev_es != 0) {
705         ctx->compatibility_mask = compatibility_mask_sev_es;
706     } else if (compatibility_mask_sev != 0) {
707         ctx->compatibility_mask = compatibility_mask_sev;
708     } else if (compatibility_mask != 0) {
709         ctx->compatibility_mask = compatibility_mask;
710     } else {
711         error_setg(
712             errp,
713             "IGVM file does not describe a compatible supported platform");
714         return -1;
715     }
716     return 0;
717 }
718 
719 static IgvmHandle qigvm_file_init(char *filename, Error **errp)
720 {
721     IgvmHandle igvm;
722     g_autofree uint8_t *buf = NULL;
723     unsigned long len;
724     g_autoptr(GError) gerr = NULL;
725 
726     if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) {
727         error_setg(errp, "Unable to load %s: %s", filename, gerr->message);
728         return -1;
729     }
730 
731     igvm = igvm_new_from_binary(buf, len);
732     if (igvm < 0) {
733         error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igvm);
734         return -1;
735     }
736     return igvm;
737 }
738 
739 int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
740                        Error **errp)
741 {
742     int32_t header_count;
743     QIgvmParameterData *parameter;
744     int retval = -1;
745     QIgvm ctx;
746 
747     memset(&ctx, 0, sizeof(ctx));
748     ctx.file = qigvm_file_init(cfg->filename, errp);
749     if (ctx.file < 0) {
750         return -1;
751     }
752 
753     /*
754      * The ConfidentialGuestSupport object is optional and allows a confidential
755      * guest platform to perform extra processing, such as page measurement, on
756      * IGVM directives.
757      */
758     ctx.cgs = cgs;
759     ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
760 
761     /*
762      * Check that the IGVM file provides configuration for the current
763      * platform
764      */
765     if (qigvm_supported_platform_compat_mask(&ctx, errp) < 0) {
766         goto cleanup;
767     }
768 
769     header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE);
770     if (header_count <= 0) {
771         error_setg(
772             errp, "Invalid directive header count in IGVM file. Error code: %X",
773             header_count);
774         goto cleanup;
775     }
776 
777     QTAILQ_INIT(&ctx.parameter_data);
778 
779     for (ctx.current_header_index = 0;
780          ctx.current_header_index < (unsigned)header_count;
781          ctx.current_header_index++) {
782         IgvmVariableHeaderType type = igvm_get_header_type(
783             ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index);
784         if (qigvm_handler(&ctx, type, errp) < 0) {
785             goto cleanup_parameters;
786         }
787     }
788 
789     /*
790      * Contiguous pages of data with compatible flags are grouped together in
791      * order to reduce the number of memory regions we create. Make sure the
792      * last group is processed with this call.
793      */
794     retval = qigvm_process_mem_page(&ctx, NULL, errp);
795 
796 cleanup_parameters:
797     QTAILQ_FOREACH(parameter, &ctx.parameter_data, next)
798     {
799         g_free(parameter->data);
800         parameter->data = NULL;
801     }
802 
803 cleanup:
804     igvm_free(ctx.file);
805 
806     return retval;
807 }
808