[PATCH v2 3/3] igvm: Fill MADT IGVM parameter field

Oliver Steffen posted 3 patches 5 days, 16 hours ago
[PATCH v2 3/3] igvm: Fill MADT IGVM parameter field
Posted by Oliver Steffen 5 days, 16 hours ago
Use the new acpi_build_madt_standalone() function to fill the MADT
parameter field.

The IGVM parameter can be consumed by Coconut SVSM [1], instead of
relying on the fw_cfg interface, which has caused problems before due to
unexpected access [2,3]. Using IGVM parameters is the default way for
Coconut SVSM; switching over would allow removing specialized code paths
for QEMU in Coconut.

In any case OVMF, which runs after SVSM has already been initialized,
will continue reading all ACPI tables via fw_cfg and provide fixed up
ACPI data to the OS as before.

Generating the MADT twice (here and during ACPI table building)
seems acceptable, since there is no infrastructure to obtain the MADT
out of the ACPI table memory area.

[1] https://github.com/coconut-svsm/svsm/pull/858
[2] https://gitlab.com/qemu-project/qemu/-/issues/2882
[3] https://github.com/coconut-svsm/svsm/issues/646

Signed-off-by: Oliver Steffen <osteffen@redhat.com>
---
 backends/igvm-cfg.c       |  8 +++++++-
 backends/igvm.c           | 37 ++++++++++++++++++++++++++++++++++++-
 include/system/igvm-cfg.h |  5 ++++-
 include/system/igvm.h     |  2 +-
 target/i386/sev.c         |  5 +++--
 5 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c
index c1b45401f4..0a77f7b7a1 100644
--- a/backends/igvm-cfg.c
+++ b/backends/igvm-cfg.c
@@ -17,6 +17,7 @@
 #include "qom/object_interfaces.h"
 #include "hw/qdev-core.h"
 #include "hw/boards.h"
+#include "hw/i386/acpi-build.h"
 
 #include "trace.h"
 
@@ -48,10 +49,15 @@ static void igvm_reset_hold(Object *obj, ResetType type)
 {
     MachineState *ms = MACHINE(qdev_get_machine());
     IgvmCfg *igvm = IGVM_CFG(obj);
+    GArray *madt = NULL;
 
     trace_igvm_reset_hold(type);
 
-    qigvm_process_file(igvm, ms->cgs, false, &error_fatal);
+    madt = acpi_build_madt_standalone(ms);
+
+    qigvm_process_file(igvm, ms->cgs, false, madt, &error_fatal);
+
+    g_array_free(madt, true);
 }
 
 static void igvm_reset_exit(Object *obj, ResetType type)
diff --git a/backends/igvm.c b/backends/igvm.c
index a350c890cc..7e56b19b0a 100644
--- a/backends/igvm.c
+++ b/backends/igvm.c
@@ -93,6 +93,7 @@ typedef struct QIgvm {
     unsigned region_start_index;
     unsigned region_last_index;
     unsigned region_page_count;
+    GArray *madt;
 } QIgvm;
 
 static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data,
@@ -120,6 +121,8 @@ static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data,
 static int qigvm_initialization_guest_policy(QIgvm *ctx,
                                        const uint8_t *header_data,
                                        Error **errp);
+static int qigvm_initialization_madt(QIgvm *ctx,
+                                     const uint8_t *header_data, Error **errp);
 
 struct QIGVMHandler {
     uint32_t type;
@@ -148,6 +151,8 @@ static struct QIGVMHandler handlers[] = {
       qigvm_directive_snp_id_block },
     { IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION,
       qigvm_initialization_guest_policy },
+    { IGVM_VHT_MADT, IGVM_HEADER_SECTION_DIRECTIVE,
+      qigvm_initialization_madt },
 };
 
 static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp)
@@ -764,6 +769,34 @@ static int qigvm_initialization_guest_policy(QIgvm *ctx,
     return 0;
 }
 
+static int qigvm_initialization_madt(QIgvm *ctx,
+                                     const uint8_t *header_data, Error **errp)
+{
+    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
+    QIgvmParameterData *param_entry;
+
+    if (ctx->madt == NULL) {
+        return 0;
+    }
+
+    /* Find the parameter area that should hold the device tree */
+    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
+    {
+        if (param_entry->index == param->parameter_area_index) {
+
+            if (ctx->madt->len > param_entry->size) {
+                error_setg(
+                    errp,
+                    "IGVM: MADT size exceeds parameter area defined in IGVM file");
+                return -1;
+            }
+            memcpy(param_entry->data, ctx->madt->data, ctx->madt->len);
+            break;
+        }
+    }
+    return 0;
+}
+
 static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
 {
     int32_t header_count;
@@ -892,7 +925,7 @@ IgvmHandle qigvm_file_init(char *filename, Error **errp)
 }
 
 int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
-                       bool onlyVpContext, Error **errp)
+                       bool onlyVpContext, GArray *madt, Error **errp)
 {
     int32_t header_count;
     QIgvmParameterData *parameter;
@@ -915,6 +948,8 @@ int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
     ctx.cgs = cgs;
     ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL;
 
+    ctx.madt = madt;
+
     /*
      * Check that the IGVM file provides configuration for the current
      * platform
diff --git a/include/system/igvm-cfg.h b/include/system/igvm-cfg.h
index 7dc48677fd..d5138f745c 100644
--- a/include/system/igvm-cfg.h
+++ b/include/system/igvm-cfg.h
@@ -40,10 +40,13 @@ typedef struct IgvmCfgClass {
      * in the IGVM file will be processed, allowing information about the
      * CPU state to be determined before processing the entire file.
      *
+     * @madt: Optional ACPI MADT data to pass to the guest via the IGVM_VHT_MADT
+     *       parameter. Only relevant if onlyVpContext is false.
+     *
      * Returns 0 for ok and -1 on error.
      */
     int (*process)(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
-                   bool onlyVpContext, Error **errp);
+                   bool onlyVpContext, GArray *madt, Error **errp);
 
 } IgvmCfgClass;
 
diff --git a/include/system/igvm.h b/include/system/igvm.h
index ec2538daa0..f2e580e4ee 100644
--- a/include/system/igvm.h
+++ b/include/system/igvm.h
@@ -18,7 +18,7 @@
 
 IgvmHandle qigvm_file_init(char *filename, Error **errp);
 int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs,
-                      bool onlyVpContext, Error **errp);
+                      bool onlyVpContext, GArray *madt, Error **errp);
 
 /* x86 native */
 int qigvm_x86_get_mem_map_entry(int index,
diff --git a/target/i386/sev.c b/target/i386/sev.c
index fd2dada013..55fd20510a 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -1888,11 +1888,12 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
          * we need to pre-process it here to extract sev_features in order to
          * provide it to KVM_SEV_INIT2. Each cgs_* function that is called by
          * the IGVM processor detects this pre-process by observing the state
-         * as SEV_STATE_UNINIT.
+         * as SEV_STATE_UNINIT. The IGVM file is only read here, no MADT data
+         * needs to be supplied.
          */
         if (x86machine->igvm) {
             if (IGVM_CFG_GET_CLASS(x86machine->igvm)
-                    ->process(x86machine->igvm, machine->cgs, true, errp) ==
+                    ->process(x86machine->igvm, machine->cgs, true, NULL, errp) ==
                 -1) {
                 return -1;
             }
-- 
2.52.0
Re: [PATCH v2 3/3] igvm: Fill MADT IGVM parameter field
Posted by Gerd Hoffmann 5 days, 16 hours ago
  Hi,

> +static int qigvm_initialization_madt(QIgvm *ctx,
> +                                     const uint8_t *header_data, Error **errp)
> +{
> +    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
> +    QIgvmParameterData *param_entry;
> +
> +    if (ctx->madt == NULL) {
> +        return 0;
> +    }
> +
> +    /* Find the parameter area that should hold the device tree */

cut+paste error in the comment.

> +    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
> +    {
> +        if (param_entry->index == param->parameter_area_index) {

Hmm, that is a pattern repeated a number of times already in the igvm
code.  Should we factor that out into a helper function?

>  static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
>  {
>      int32_t header_count;
> @@ -892,7 +925,7 @@ IgvmHandle qigvm_file_init(char *filename, Error **errp)
>  }
>  
>  int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
> -                       bool onlyVpContext, Error **errp)
> +                       bool onlyVpContext, GArray *madt, Error **errp)

I'd like to see less parameters for this function, not more.

I think sensible options here are:

  (a) store the madt pointer in IgvmCfg, or
  (b) pass MachineState instead of ConfidentialGuestSupport, so
      we can use the MachineState here to generate the madt.

Luigi, any opinion?  I think device tree support will need access to
MachineState too, and I think both madt and dt should take the same
approach here.

Long-term I'd like to also get rid of the onlyVpContext parameter.
That cleanup is something for another patch series though.

take care,
  Gerd
Re: [PATCH v2 3/3] igvm: Fill MADT IGVM parameter field
Posted by Luigi Leonardi 5 days, 15 hours ago
Hi,

On Thu, Dec 11, 2025 at 12:15:59PM +0100, Gerd Hoffmann wrote:
>  Hi,
>
>> +static int qigvm_initialization_madt(QIgvm *ctx,
>> +                                     const uint8_t *header_data, Error **errp)
>> +{
>> +    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
>> +    QIgvmParameterData *param_entry;
>> +
>> +    if (ctx->madt == NULL) {
>> +        return 0;
>> +    }
>> +
>> +    /* Find the parameter area that should hold the device tree */
>
>cut+paste error in the comment.
>
>> +    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
>> +    {
>> +        if (param_entry->index == param->parameter_area_index) {
>
>Hmm, that is a pattern repeated a number of times already in the igvm
>code.  Should we factor that out into a helper function?

+1

>
>>  static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
>>  {
>>      int32_t header_count;
>> @@ -892,7 +925,7 @@ IgvmHandle qigvm_file_init(char *filename, Error **errp)
>>  }
>>
>>  int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
>> -                       bool onlyVpContext, Error **errp)
>> +                       bool onlyVpContext, GArray *madt, Error **errp)
>
>I'd like to see less parameters for this function, not more.
>
>I think sensible options here are:
>
>  (a) store the madt pointer in IgvmCfg, or
>  (b) pass MachineState instead of ConfidentialGuestSupport, so
>      we can use the MachineState here to generate the madt.
>
>Luigi, any opinion?  I think device tree support will need access to
>MachineState too, and I think both madt and dt should take the same
>approach here.

I have a slight preference over MachineState as it's more generic and we 
don't need to add more fields in IgvmCfg for new features.

Luigi
Re: [PATCH v2 3/3] igvm: Fill MADT IGVM parameter field
Posted by Oliver Steffen 5 days, 15 hours ago
On Thu, Dec 11, 2025 at 12:30 PM Luigi Leonardi <leonardi@redhat.com> wrote:
>
> Hi,
>
> On Thu, Dec 11, 2025 at 12:15:59PM +0100, Gerd Hoffmann wrote:
> >  Hi,
> >
> >> +static int qigvm_initialization_madt(QIgvm *ctx,
> >> +                                     const uint8_t *header_data, Error **errp)
> >> +{
> >> +    const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data;
> >> +    QIgvmParameterData *param_entry;
> >> +
> >> +    if (ctx->madt == NULL) {
> >> +        return 0;
> >> +    }
> >> +
> >> +    /* Find the parameter area that should hold the device tree */
> >
> >cut+paste error in the comment.

Will do.

> >
> >> +    QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next)
> >> +    {
> >> +        if (param_entry->index == param->parameter_area_index) {
> >
> >Hmm, that is a pattern repeated a number of times already in the igvm
> >code.  Should we factor that out into a helper function?
>
> +1

Will do.

>
> >
> >>  static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp)
> >>  {
> >>      int32_t header_count;
> >> @@ -892,7 +925,7 @@ IgvmHandle qigvm_file_init(char *filename, Error **errp)
> >>  }
> >>
> >>  int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs,
> >> -                       bool onlyVpContext, Error **errp)
> >> +                       bool onlyVpContext, GArray *madt, Error **errp)
> >
> >I'd like to see less parameters for this function, not more.
> >
> >I think sensible options here are:
> >
> >  (a) store the madt pointer in IgvmCfg, or
> >  (b) pass MachineState instead of ConfidentialGuestSupport, so
> >      we can use the MachineState here to generate the madt.
> >
> >Luigi, any opinion?  I think device tree support will need access to
> >MachineState too, and I think both madt and dt should take the same
> >approach here.
>
> I have a slight preference over MachineState as it's more generic and we
> don't need to add more fields in IgvmCfg for new features.
>
Passing in MachineState would be easy, but do we really want to add machine
related logic (building of ACPI tables, and later maybe device trees)
into the igvm backend?

> Luigi
>
Re: [PATCH v2 3/3] igvm: Fill MADT IGVM parameter field
Posted by Gerd Hoffmann 5 days, 15 hours ago
  Hi,

> > >  (b) pass MachineState instead of ConfidentialGuestSupport, so
> > >      we can use the MachineState here to generate the madt.
> > >
> > >Luigi, any opinion?  I think device tree support will need access to
> > >MachineState too, and I think both madt and dt should take the same
> > >approach here.
> >
> > I have a slight preference over MachineState as it's more generic and we
> > don't need to add more fields in IgvmCfg for new features.
> >
> Passing in MachineState would be easy, but do we really want to add machine
> related logic (building of ACPI tables, and later maybe device trees)
> into the igvm backend?

Good point.  That clearly speaks for (b).  There already is
MachineState->fdt, filled in by machine code.  We can let machine code
store the madt in MachineState too, and the have the igvm code simply
pick it up from there.

take care,
  Gerd