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