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