On Fri, Jun 13, 2025 at 7:24 PM Roy Hopkins <[email protected]> wrote:
>
> Adds an IGVM loader to QEMU which processes a given IGVM file and
> applies the directives within the file to the current guest
> configuration.
>
> The IGVM loader can be used to configure both confidential and
> non-confidential guests. For confidential guests, the
> ConfidentialGuestSupport object for the system is used to encrypt
> memory, apply the initial CPU state and perform other confidential guest
> operations.
>
> The loader is configured via a new IgvmCfg QOM object which allows the
> user to provide a path to the IGVM file to process.
>
> Signed-off-by: Roy Hopkins <[email protected]>
> Acked-by: Michael S. Tsirkin <[email protected]>
> Acked-by: Gerd Hoffman <[email protected]>
> Reviewed-by: Stefano Garzarella <[email protected]>
> ---
>  backends/igvm-cfg.c       |  51 +++
>  backends/igvm.c           | 807 ++++++++++++++++++++++++++++++++++++++
>  backends/igvm.h           |  22 ++
>  backends/meson.build      |   2 +
>  include/system/igvm-cfg.h |  46 +++
>  qapi/qom.json             |  17 +
>  6 files changed, 945 insertions(+)
>  create mode 100644 backends/igvm-cfg.c
>  create mode 100644 backends/igvm.c
>  create mode 100644 backends/igvm.h
>  create mode 100644 include/system/igvm-cfg.h
>

<snip>

> +
> +static int 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)
> +{
> +    uint8_t *region;
> +    IgvmHandle data_handle;
> +    const void *data;
> +    uint32_t data_size;
> +    unsigned page_index;
> +    bool zero = true;
> +    const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000;
> +    int result;
> +    int cgs_page_type;
> +
> +    region = qigvm_prepare_memory(ctx, gpa_start, page_count * page_size,
> +                                  start_index, errp);
> +    if (!region) {
> +        return -1;
> +    }
> +
> +    for (page_index = 0; page_index < page_count; page_index++) {
> +        data_handle = igvm_get_header_data(
> +            ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + 
> start_index);
> +        if (data_handle == IGVMAPI_NO_DATA) {
> +            /* No data indicates a zero page */
> +            memset(&region[page_index * page_size], 0, page_size);
> +        } else if (data_handle < 0) {
> +            error_setg(
> +                errp,
> +                "IGVM file contains invalid page data for directive with "
> +                "index %d",
> +                page_index + start_index);
> +            return -1;
> +        } else {
> +            zero = false;
> +            data_size = igvm_get_buffer_size(ctx->file, data_handle);
> +            if (data_size < page_size) {
> +                memset(&region[page_index * page_size], 0, page_size);
> +            } else if (data_size > page_size) {
> +                error_setg(errp,
> +                           "IGVM file contains page data with invalid size 
> for "
> +                           "directive with index %d",
> +                           page_index + start_index);
> +                return -1;
> +            }
> +            data = igvm_get_buffer(ctx->file, data_handle);
> +            memcpy(&region[page_index * page_size], data, data_size);
> +            igvm_free_buffer(ctx->file, data_handle);
> +        }
> +    }
> +
> +    /*
> +     * If a confidential guest support object is provided then use it to set 
> the
> +     * guest state.
> +     */
> +    if (ctx->cgs) {
> +        cgs_page_type =
> +            qigvm_type_to_cgs_type(page_type, flags->unmeasured, zero);
> +        if (cgs_page_type < 0) {
> +            error_setg(errp,
> +                       "Invalid page type in IGVM file. Directives: %d to 
> %d, "
> +                       "page type: %d",
> +                       start_index, start_index + page_count, page_type);
> +            return -1;
> +        }
> +
> +        result = ctx->cgsc->set_guest_state(
> +            gpa_start, region, page_size * page_count, cgs_page_type, 0, 
> errp);
> +        if (result < 0) {
> +            return result;
> +        }
> +    }
> +    return 0;
> +}
> +
> +static int qigvm_process_mem_page(QIgvm *ctx,
> +                                  const IGVM_VHS_PAGE_DATA *page_data,
> +                                  Error **errp)
> +{
> +    if (page_data) {
> +        if (ctx->region_page_count == 0) {
> +            ctx->region_start = page_data->gpa;
> +            ctx->region_start_index = ctx->current_header_index;
> +        } else {
> +            if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index,
> +                                        page_data,
> +                                        &ctx->region_prev_page_data) ||
> +                ((ctx->region_prev_page_data.gpa +
> +                  (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 :
> +                                                                  0x1000)) !=
> +                 page_data->gpa) ||
> +                (ctx->region_last_index != (ctx->current_header_index - 1))) 
> {
> +                /* End of current region */
> +                if (qigvm_process_mem_region(
> +                        ctx, ctx->region_start_index, ctx->region_start,
> +                        ctx->region_page_count,
> +                        &ctx->region_prev_page_data.flags,
> +                        ctx->region_prev_page_data.data_type, errp) < 0) {
> +                    return -1;
> +                }
> +                ctx->region_page_count = 0;
> +                ctx->region_start = page_data->gpa;
> +                ctx->region_start_index = ctx->current_header_index;

Should we return here? Is there any need for the memcpy() below?

> +            }
> +        }
> +        memcpy(&ctx->region_prev_page_data, page_data,
> +               sizeof(ctx->region_prev_page_data));
> +        ctx->region_last_index = ctx->current_header_index;
> +        ctx->region_page_count++;
> +    } else {
> +        if (ctx->region_page_count > 0) {
> +            if (qigvm_process_mem_region(
> +                    ctx, ctx->region_start_index, ctx->region_start,
> +                    ctx->region_page_count, 
> &ctx->region_prev_page_data.flags,
> +                    ctx->region_prev_page_data.data_type, errp) < 0) {
> +                return -1;
> +            }
> +            ctx->region_page_count = 0;
> +        }
> +    }
> +    return 0;
> +}
> +


Reply via email to