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