Repository: kubevirt/kubevirt (Go)

Issue:
Windows offline activation with ACPI MSDM table
**Is your feature request related to a problem? Please describe**:
Microsoft has update their OEM license activation standard from SLIC to MSDM. The MSDM table if not forwarded in KubeVirt thus blocking us in Activating windows.

**Describe the solution you'd like**:
Include the MSDM table in KubeVirt. SLIC support was implemented in https://github.com/kubevirt/kubevirt/pull/10774 by @victortoso. Would be great if a similar implementation would be done for MSDM.

**Describe alternatives you've considered**:

- Tested solutions read the key from the table and enter it with an alternative flow - this does not work as Microsoft has implemented additional control methods to block manual activation.
- Asked the hardware vendor to include SLIC, but he is not allowed to provide Microsoft licenses in SLIC format anymore.




Discussion:
Right. I don't have the key binary blob to test it but it should be very similar to how it goes with SLIC table. I'll try to make time for it soon.
Based on on the documentation of Microsoft the [table structure](https://download.microsoft.com/download/1/3/8/13818231-a8ad-4fe7-b4e1-a63cbc5d6027/microsoft-software-licensing-tables.docx) is identical.

I have a binary blob available if need to validate, just let me know
jfyi, I'm wondering if we could get a new entry in the acpi table support by libvirt first.
https://gitlab.com/libvirt/libvirt/-/issues/748

I'm not sure if using [qemu passthrough](https://libvirt.org/kbase/qemu-passthrough-security.html) is acceptable for this, I'd rather avoid if possible. Note that, if this is urgent to you, I think you can achieve what you want using KubeVirt's sidecars with kubevirt > 1.2.0, using a PVC to store the MSDN data and a script to change libvirt's xml
Please note that qemu cmd line passthrough is considered unsupported for production environments. And very likely won't work either because libvirt treats these additional arguments as an opaque string that's just appended onto the cmd line. But at the same time, it starts QEMU with an unique SELinux context and I doubt accessing "random" files is going to be allowed (as it would defy the whole purpose of SELinux).

There are patches on the libvirt list and I've reviewed them. Hopefully, this is gonna land in libvirt soon.
Alright. Support to MSDM was added to libvirt main branch, it'll be included in the next libvirt release which should be early April... it should be doable to have by kubevirt v1.6.0.

Cheers,
/assign
Thanks for your work on this request, looking forward to the 1.6 alpha release so that we can test this feature
libvirt 11.2 was released [Today](https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/thread/PB3CBBH75F2JOM4XRUSRRJ6INB756LHL). When it does show up in [c9s](https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/), I'll post a PR.
Quick update. On c9s there'll be no more rebases so we will not get 11.2 there. Still, the required feature in libvirt was backport and is now live on c9s:

[libvirt-10.10.0-9.el9.x86_64.rpm](https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages/libvirt-10.10.0-9.el9.x86_64.rpm)
Hi @victortoso , can you give an estimate for when this PR will be merged and released? 
>  can you give an estimate for when this PR will be merged and released?

The next planned release is KubeVirt 1.6 and here is the [schedule](https://github.com/kubevirt/sig-release/blob/main/releases/v1.6/schedule.md).

It has to be merged before the code freeze on June 25. Considering that it got a few rounds of reviews and it currently has `lgtm` we are in good pace for 1.6

Cheers,



Patch:
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index d6b9e2d057bc..2ec7b68a1d11 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -12727,6 +12727,10 @@
    "v1.ACPI": {
     "type": "object",
     "properties": {
+     "msdmNameRef": {
+      "description": "Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions. The above points to the spec of MSDM too.",
+      "type": "string"
+     },
      "slicNameRef": {
       "description": "SlicNameRef should match the volume name of a secret object. The data in the secret should be a binary blob that follows the ACPI SLIC standard, see: https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653305(v=vs.85)",
       "type": "string"
diff --git a/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter.go b/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter.go
index 1a251490c4d8..4a88b5d01084 100644
--- a/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter.go
+++ b/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter.go
@@ -1448,30 +1448,45 @@ func validateFirmwareACPI(field *k8sfield.Path, spec *v1.VirtualMachineInstanceS
 	}
 
 	acpi := spec.Domain.Firmware.ACPI
-	for _, volume := range spec.Volumes {
-		if acpi.SlicNameRef != volume.Name {
+	if acpi.SlicNameRef == "" && acpi.MsdmNameRef == "" {
+		return append(causes, metav1.StatusCause{
+			Type:    metav1.CauseTypeFieldValueInvalid,
+			Message: fmt.Sprintf("ACPI was set but no SLIC nor MSDM volume reference was set"),
+			Field:   field.String(),
+		})
+	}
+
+	causes = append(causes, validateACPIRef(field, acpi.SlicNameRef, spec.Volumes, "slicNameRef")...)
+	causes = append(causes, validateACPIRef(field, acpi.MsdmNameRef, spec.Volumes, "msdmNameRef")...)
+	return causes
+}
+
+func validateACPIRef(field *k8sfield.Path, nameRef string, volumes []v1.Volume, fieldName string) []metav1.StatusCause {
+	if nameRef == "" {
+		return nil
+	}
+
+	for _, volume := range volumes {
+		if nameRef != volume.Name {
 			continue
 		}
 
-		switch {
-		case volume.Secret != nil:
-		default:
-			causes = append(causes, metav1.StatusCause{
-				Type:    metav1.CauseTypeFieldValueInvalid,
-				Message: fmt.Sprintf("%s refers to Volume of unsupported type.", field.String()),
-				Field:   field.Child("slicNameRef").String(),
-			})
+		if volume.Secret != nil {
+			return nil
 		}
-		return causes
+
+		return []metav1.StatusCause{{
+			Type:    metav1.CauseTypeFieldValueInvalid,
+			Message: fmt.Sprintf("%s refers to Volume of unsupported type.", field.String()),
+			Field:   field.Child(fieldName).String(),
+		}}
 	}
 
-	causes = append(causes, metav1.StatusCause{
+	return []metav1.StatusCause{{
 		Type:    metav1.CauseTypeFieldValueInvalid,
 		Message: fmt.Sprintf("%s does not have a matching Volume.", field.String()),
-		Field:   field.String(),
-	})
-
-	return causes
+		Field:   field.Child(fieldName).String(),
+	}}
 }
 
 func validateFirmware(field *k8sfield.Path, firmware *v1.Firmware) []metav1.StatusCause {
diff --git a/pkg/virt-launcher/virtwrap/api/deepcopy_generated.go b/pkg/virt-launcher/virtwrap/api/deepcopy_generated.go
index 4a5456de2d9d..fe90983c4d33 100644
--- a/pkg/virt-launcher/virtwrap/api/deepcopy_generated.go
+++ b/pkg/virt-launcher/virtwrap/api/deepcopy_generated.go
@@ -2790,7 +2790,7 @@ func (in *OS) DeepCopyInto(out *OS) {
 	if in.ACPI != nil {
 		in, out := &in.ACPI, &out.ACPI
 		*out = new(OSACPI)
-		**out = **in
+		(*in).DeepCopyInto(*out)
 	}
 	if in.SMBios != nil {
 		in, out := &in.SMBios, &out.SMBios
@@ -2838,7 +2838,11 @@ func (in *OS) DeepCopy() *OS {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *OSACPI) DeepCopyInto(out *OSACPI) {
 	*out = *in
-	out.Table = in.Table
+	if in.Table != nil {
+		in, out := &in.Table, &out.Table
+		*out = make([]ACPITable, len(*in))
+		copy(*out, *in)
+	}
 	return
 }
 
diff --git a/pkg/virt-launcher/virtwrap/api/schema.go b/pkg/virt-launcher/virtwrap/api/schema.go
index 09627b7140b8..3c660edfcd85 100644
--- a/pkg/virt-launcher/virtwrap/api/schema.go
+++ b/pkg/virt-launcher/virtwrap/api/schema.go
@@ -998,7 +998,7 @@ type OSType struct {
 }
 
 type OSACPI struct {
-	Table ACPITable `xml:"table,omitempty"`
+	Table []ACPITable `xml:"table,omitempty"`
 }
 
 type ACPITable struct {
diff --git a/pkg/virt-launcher/virtwrap/converter/converter.go b/pkg/virt-launcher/virtwrap/converter/converter.go
index 3d4f4b8be0ee..4f6fb70f66a4 100644
--- a/pkg/virt-launcher/virtwrap/converter/converter.go
+++ b/pkg/virt-launcher/virtwrap/converter/converter.go
@@ -1231,41 +1231,67 @@ func Convert_v1_Firmware_To_related_apis(vmi *v1.VirtualMachineInstance, domain
 		domain.Spec.OS.KernelArgs = firmware.KernelBoot.KernelArgs
 	}
 
-	if firmware.ACPI != nil {
-		path, err := getSlicMountedPath(vmi.Spec.Volumes, firmware.ACPI.SlicNameRef)
-		if err != nil {
-			log.Log.Object(vmi).Warningf("Failed to get supported path for Volume: %s", firmware.ACPI.SlicNameRef)
-			return err
-		}
+	if err := Convert_v1_Firmware_ACPI_To_related_apis(firmware, domain, vmi.Spec.Volumes); err != nil {
+		return err
+	}
 
-		domain.Spec.OS.ACPI = &api.OSACPI{
-			Table: api.ACPITable{
-				Type: "slic",
-				Path: path,
-			},
-		}
+	return nil
+}
+
+func Convert_v1_Firmware_ACPI_To_related_apis(firmware *v1.Firmware, domain *api.Domain, volumes []v1.Volume) error {
+	if firmware.ACPI == nil {
+		return nil
 	}
 
+	if firmware.ACPI.SlicNameRef == "" && firmware.ACPI.MsdmNameRef == "" {
+		return fmt.Errorf("No ACPI tables were set. Expecting at least one.")
+	}
+
+	if domain.Spec.OS.ACPI == nil {
+		domain.Spec.OS.ACPI = &api.OSACPI{}
+	}
+
+	if val, err := createACPITable("slic", firmware.ACPI.SlicNameRef, volumes); err != nil {
+		return err
+	} else if val != nil {
+		domain.Spec.OS.ACPI.Table = append(domain.Spec.OS.ACPI.Table, *val)
+	}
+
+	if val, err := createACPITable("msdm", firmware.ACPI.MsdmNameRef, volumes); err != nil {
+		return err
+	} else if val != nil {
+		domain.Spec.OS.ACPI.Table = append(domain.Spec.OS.ACPI.Table, *val)
+	}
+
+	// if field was set but volume was not found, helper function will return error
 	return nil
 }
 
-func getSlicMountedPath(volumes []v1.Volume, name string) (string, error) {
-	// We need to know the the volume type referred by @name
+func createACPITable(source, volumeName string, volumes []v1.Volume) (*api.ACPITable, error) {
+	if volumeName == "" {
+		return nil, nil
+	}
+
 	for _, volume := range volumes {
-		if volume.Name != name {
+		if volume.Name != volumeName {
 			continue
 		}
 
 		if volume.Secret == nil {
-			return "", fmt.Errorf("Firmware's slic volume type is unsupported")
+			// Unsupported. This should have been blocked by webhook, so warn user.
+			return nil, fmt.Errorf("Firmware's volume type is unsupported for %s", source)
 		}
 
-		// Return path to slic binary data
-		sourcePath := config.GetSecretSourcePath(name)
-		return filepath.Join(sourcePath, "slic.bin"), nil
+		// Return path to table's binary data
+		sourcePath := config.GetSecretSourcePath(volumeName)
+		sourcePath = filepath.Join(sourcePath, fmt.Sprintf("%s.bin", source))
+		return &api.ACPITable{
+			Type: source,
+			Path: sourcePath,
+		}, nil
 	}
 
-	return "", fmt.Errorf("Firmware's slic volume type not found")
+	return nil, fmt.Errorf("Firmware's volume for %s was not found", source)
 }
 
 func hasIOThreads(vmi *v1.VirtualMachineInstance) bool {
diff --git a/pkg/virt-operator/resource/generate/components/validations_generated.go b/pkg/virt-operator/resource/generate/components/validations_generated.go
index 2e6ce5c1f736..a1a9a1c1e6e7 100644
--- a/pkg/virt-operator/resource/generate/components/validations_generated.go
+++ b/pkg/virt-operator/resource/generate/components/validations_generated.go
@@ -6768,6 +6768,11 @@ var CRDsValidation map[string]string = map[string]string{
                         acpi:
                           description: Information that can be set in the ACPI table
                           properties:
+                            msdmNameRef:
+                              description: |-
+                                Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+                                The above points to the spec of MSDM too.
+                              type: string
                             slicNameRef:
                               description: |-
                                 SlicNameRef should match the volume name of a secret object. The data in the secret should
@@ -12084,6 +12089,11 @@ var CRDsValidation map[string]string = map[string]string{
                 acpi:
                   description: Information that can be set in the ACPI table
                   properties:
+                    msdmNameRef:
+                      description: |-
+                        Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+                        The above points to the spec of MSDM too.
+                      type: string
                     slicNameRef:
                       description: |-
                         SlicNameRef should match the volume name of a secret object. The data in the secret should
@@ -15546,6 +15556,11 @@ var CRDsValidation map[string]string = map[string]string{
                 acpi:
                   description: Information that can be set in the ACPI table
                   properties:
+                    msdmNameRef:
+                      description: |-
+                        Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+                        The above points to the spec of MSDM too.
+                      type: string
                     slicNameRef:
                       description: |-
                         SlicNameRef should match the volume name of a secret object. The data in the secret should
@@ -17990,6 +18005,11 @@ var CRDsValidation map[string]string = map[string]string{
                         acpi:
                           description: Information that can be set in the ACPI table
                           properties:
+                            msdmNameRef:
+                              description: |-
+                                Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+                                The above points to the spec of MSDM too.
+                              type: string
                             slicNameRef:
                               description: |-
                                 SlicNameRef should match the volume name of a secret object. The data in the secret should
@@ -22522,6 +22542,11 @@ var CRDsValidation map[string]string = map[string]string{
                                   description: Information that can be set in the
                                     ACPI table
                                   properties:
+                                    msdmNameRef:
+                                      description: |-
+                                        Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+                                        The above points to the spec of MSDM too.
+                                      type: string
                                     slicNameRef:
                                       description: |-
                                         SlicNameRef should match the volume name of a secret object. The data in the secret should
@@ -27741,6 +27766,11 @@ var CRDsValidation map[string]string = map[string]string{
                                       description: Information that can be set in
                                         the ACPI table
                                       properties:
+                                        msdmNameRef:
+                                          description: |-
+                                            Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+                                            The above points to the spec of MSDM too.
+                                          type: string
                                         slicNameRef:
                                           description: |-
                                             SlicNameRef should match the volume name of a secret object. The data in the secret should
diff --git a/staging/src/kubevirt.io/api/core/v1/schema.go b/staging/src/kubevirt.io/api/core/v1/schema.go
index 12043dc9f832..edb6b6c7a7ba 100644
--- a/staging/src/kubevirt.io/api/core/v1/schema.go
+++ b/staging/src/kubevirt.io/api/core/v1/schema.go
@@ -440,6 +440,9 @@ type ACPI struct {
 	// be a binary blob that follows the ACPI SLIC standard, see:
 	// https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653305(v=vs.85)
 	SlicNameRef string `json:"slicNameRef,omitempty"`
+	// Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.
+	// The above points to the spec of MSDM too.
+	MsdmNameRef string `json:"msdmNameRef,omitempty"`
 }
 
 type Devices struct {
diff --git a/staging/src/kubevirt.io/api/core/v1/schema_swagger_generated.go b/staging/src/kubevirt.io/api/core/v1/schema_swagger_generated.go
index f3f2cb44a5d9..d0dc98418ce7 100644
--- a/staging/src/kubevirt.io/api/core/v1/schema_swagger_generated.go
+++ b/staging/src/kubevirt.io/api/core/v1/schema_swagger_generated.go
@@ -241,6 +241,7 @@ func (Firmware) SwaggerDoc() map[string]string {
 func (ACPI) SwaggerDoc() map[string]string {
 	return map[string]string{
 		"slicNameRef": "SlicNameRef should match the volume name of a secret object. The data in the secret should\nbe a binary blob that follows the ACPI SLIC standard, see:\nhttps://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653305(v=vs.85)",
+		"msdmNameRef": "Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions.\nThe above points to the spec of MSDM too.",
 	}
 }
 
diff --git a/staging/src/kubevirt.io/client-go/api/openapi_generated.go b/staging/src/kubevirt.io/client-go/api/openapi_generated.go
index 9d1d48818e28..76f8d25940b5 100644
--- a/staging/src/kubevirt.io/client-go/api/openapi_generated.go
+++ b/staging/src/kubevirt.io/client-go/api/openapi_generated.go
@@ -17574,6 +17574,13 @@ func schema_kubevirtio_api_core_v1_ACPI(ref common.ReferenceCallback) common.Ope
 							Format:      "",
 						},
 					},
+					"msdmNameRef": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Similar to SlicNameRef, another ACPI entry that is used in more recent Windows versions. The above points to the spec of MSDM too.",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
 				},
 			},
 		},


Test patch:
diff --git a/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter_test.go b/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter_test.go
index 64506dd3ff34..2301b66f2016 100644
--- a/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter_test.go
+++ b/pkg/virt-api/webhooks/validating-webhook/admitters/vmi-create-admitter_test.go
@@ -2899,7 +2899,7 @@ var _ = Describe("Validating VMICreate Admitter", func() {
 			}
 		},
 			Entry("Not set is ok", nil, []v1.Volume{}, 0, ""),
-			Entry("ACPI with Volume match is ok",
+			Entry("ACPI SLIC with Volume match is ok",
 				&v1.ACPI{SlicNameRef: "slic"},
 				[]v1.Volume{
 					{
@@ -2909,10 +2909,23 @@ var _ = Describe("Validating VMICreate Admitter", func() {
 						},
 					},
 				}, 0, ""),
-			Entry("ACPI without Volume match should fail",
+			Entry("ACPI MSDM with Volume match is ok",
+				&v1.ACPI{SlicNameRef: "msdm"},
+				[]v1.Volume{
+					{
+						Name: "msdm",
+						VolumeSource: v1.VolumeSource{
+							Secret: &v1.SecretVolumeSource{SecretName: "secret-msdm"},
+						},
+					},
+				}, 0, ""),
+			Entry("ACPI SLIC without Volume match should fail",
 				&v1.ACPI{SlicNameRef: "slic"},
 				[]v1.Volume{}, 1, "does not have a matching Volume"),
-			Entry("ACPI with wrong Volume type should fail",
+			Entry("ACPI MSDM without Volume match should fail",
+				&v1.ACPI{MsdmNameRef: "msdm"},
+				[]v1.Volume{}, 1, "does not have a matching Volume"),
+			Entry("ACPI SLIC with wrong Volume type should fail",
 				&v1.ACPI{SlicNameRef: "slic"},
 				[]v1.Volume{
 					{
@@ -2924,6 +2937,18 @@ var _ = Describe("Validating VMICreate Admitter", func() {
 						},
 					},
 				}, 1, "Volume of unsupported type"),
+			Entry("ACPI MSDM with wrong Volume type should fail",
+				&v1.ACPI{MsdmNameRef: "msdm"},
+				[]v1.Volume{
+					{
+						Name: "msdm",
+						VolumeSource: v1.VolumeSource{
+							ConfigMap: &v1.ConfigMapVolumeSource{
+								LocalObjectReference: k8sv1.LocalObjectReference{Name: "configmap-msdm"},
+							},
+						},
+					},
+				}, 1, "Volume of unsupported type"),
 		)
 
 		DescribeTable("validating cpu model with", func(model string, expectedLen int) {
diff --git a/pkg/virt-launcher/virtwrap/converter/converter_test.go b/pkg/virt-launcher/virtwrap/converter/converter_test.go
index fb6bb305b417..9edb0f5ad76d 100644
--- a/pkg/virt-launcher/virtwrap/converter/converter_test.go
+++ b/pkg/virt-launcher/virtwrap/converter/converter_test.go
@@ -2932,40 +2932,106 @@ var _ = Describe("Converter", func() {
 			Entry("VGA on ppc64le with EFI and BochsDisplayForEFIGuests set", ppc64le, v1.Bootloader{EFI: &v1.EFI{}}, true, "vga"),
 		)
 
-		DescribeTable("slic ACPI table should be set to", func(source v1.VolumeSource, isSupported bool, path string) {
-			slicName := "slic"
-			vmi.Spec.Domain.Firmware = &v1.Firmware{ACPI: &v1.ACPI{SlicNameRef: slicName}}
-			vmi.Spec.Volumes = []v1.Volume{
-				{
-					Name:         slicName,
-					VolumeSource: source,
-				},
-			}
+		DescribeTable("ACPI table should be set to", func(
+			slicVolumeName string, slicVol *v1.Volume,
+			msdmVolumeName string, msdmVol *v1.Volume,
+			errMatch string,
+		) {
+			acpi := &v1.ACPI{}
+			if slicVolumeName != "" {
+				acpi.SlicNameRef = slicVolumeName
+				vmi.Spec.Volumes = append(vmi.Spec.Volumes, *slicVol)
+			}
+			if msdmVolumeName != "" {
+				acpi.MsdmNameRef = msdmVolumeName
+				vmi.Spec.Volumes = append(vmi.Spec.Volumes, *msdmVol)
+			}
+			vmi.Spec.Domain.Firmware = &v1.Firmware{ACPI: acpi}
+
 			c = &ConverterContext{
 				Architecture:   archconverter.NewConverter(runtime.GOARCH),
 				VirtualMachine: vmi,
 				AllowEmulation: true,
 			}
-			if isSupported {
-				domainSpec := vmiToDomainXMLToDomainSpec(vmi, c)
-				Expect(domainSpec.OS.ACPI.Table.Type).To(Equal("slic"))
-				Expect(domainSpec.OS.ACPI.Table.Path).To(Equal(path))
-			} else {
+
+			if errMatch != "" {
+				// The error should be catch by webhook.
 				domain := &api.Domain{}
 				err := Convert_v1_VirtualMachineInstance_To_api_Domain(vmi, domain, c)
-				Expect(err).To(MatchError(ContainSubstring("Firmware's slic volume type is unsupported")))
+				Expect(err.Error()).To(ContainSubstring(errMatch))
+				return
+			}
+
+			domainSpec := vmiToDomainXMLToDomainSpec(vmi, c)
+			if slicVolumeName != "" {
+				Expect(domainSpec.OS.ACPI.Table).To(ContainElement(api.ACPITable{
+					Type: "slic",
+					Path: filepath.Join(config.GetSecretSourcePath(slicVolumeName), "slic.bin"),
+				}))
+			}
+
+			if msdmVolumeName != "" {
+				Expect(domainSpec.OS.ACPI.Table).To(ContainElement(api.ACPITable{
+					Type: "msdm",
+					Path: filepath.Join(config.GetSecretSourcePath(msdmVolumeName), "msdm.bin"),
+				}))
 			}
 		},
-			Entry("Secret set",
-				v1.VolumeSource{
-					Secret: &v1.SecretVolumeSource{SecretName: "secret-slic"},
-				}, true, filepath.Join(config.GetSecretSourcePath("slic"), "slic.bin")),
-			Entry("ConfigMap unset",
-				v1.VolumeSource{
-					ConfigMap: &v1.ConfigMapVolumeSource{
-						LocalObjectReference: k8sv1.LocalObjectReference{Name: "configmap-slic"},
-					},
-				}, false, ""),
+			// with Valid Secret volumes
+			Entry("slic with secret",
+				"vol-slic", &v1.Volume{
+					Name: "vol-slic",
+					VolumeSource: v1.VolumeSource{
+						Secret: &v1.SecretVolumeSource{
+							SecretName: "secret-slic",
+						},
+					},
+				}, "", nil, ""),
+			Entry("msdm with secret", "", nil,
+				"vol-msdm", &v1.Volume{
+					Name: "vol-msdm",
+					VolumeSource: v1.VolumeSource{
+						Secret: &v1.SecretVolumeSource{
+							SecretName: "secret-msdm",
+						},
+					},
+				}, ""),
+			// with not valid Volume source
+			Entry("slic with configmap",
+				"vol-slic", &v1.Volume{
+					Name: "vol-slic",
+					VolumeSource: v1.VolumeSource{
+						ConfigMap: &v1.ConfigMapVolumeSource{},
+					},
+				}, "", nil, "Firmware's volume type is unsupported for slic"),
+			Entry("msdm with configmap", "", nil,
+				"vol-msdm", &v1.Volume{
+					Name: "vol-msdm",
+					VolumeSource: v1.VolumeSource{
+						ConfigMap: &v1.ConfigMapVolumeSource{},
+					},
+				}, "Firmware's volume type is unsupported for msdm"),
+			// without matching volume source
+			Entry("slic without volume", "vol-slic", &v1.Volume{}, "", &v1.Volume{}, "Firmware's volume for slic was not found"),
+			Entry("msdm without volume", "", &v1.Volume{}, "vol-msdm", &v1.Volume{}, "Firmware's volume for msdm was not found"),
+			// try both togeter, correct input
+			Entry("slic and msdm with secret",
+				"vol-slic", &v1.Volume{
+					Name: "vol-slic",
+					VolumeSource: v1.VolumeSource{
+						Secret: &v1.SecretVolumeSource{
+							SecretName: "secret-slic",
+						},
+					},
+				},
+				"vol-msdm", &v1.Volume{
+					Name: "vol-msdm",
+					VolumeSource: v1.VolumeSource{
+						Secret: &v1.SecretVolumeSource{
+							SecretName: "secret-msdm",
+						},
+					},
+				}, ""),
 		)
 	})
 
diff --git a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.json b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.json
index f87939fba7bb..148fdbb49d99 100644
--- a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.json
+++ b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.json
@@ -169,7 +169,8 @@
               }
             },
             "acpi": {
-              "slicNameRef": "slicNameRefValue"
+              "slicNameRef": "slicNameRefValue",
+              "msdmNameRef": "msdmNameRefValue"
             }
           },
           "clock": {
diff --git a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.yaml b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.yaml
index d69e6d28e7d0..ee50b477d5a4 100644
--- a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.yaml
+++ b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachine.yaml
@@ -576,6 +576,7 @@ spec:
             enabled: true
         firmware:
           acpi:
+            msdmNameRef: msdmNameRefValue
             slicNameRef: slicNameRefValue
           bootloader:
             bios:
diff --git a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.json b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.json
index 5c31549dca93..9fb867d7fa8a 100644
--- a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.json
+++ b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.json
@@ -109,7 +109,8 @@
           }
         },
         "acpi": {
-          "slicNameRef": "slicNameRefValue"
+          "slicNameRef": "slicNameRefValue",
+          "msdmNameRef": "msdmNameRefValue"
         }
       },
       "clock": {
diff --git a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.yaml b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.yaml
index 98584fe2314e..cbfd59956205 100644
--- a/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.yaml
+++ b/staging/src/kubevirt.io/api/apitesting/testdata/HEAD/kubevirt.io.v1.VirtualMachineInstance.yaml
@@ -383,6 +383,7 @@ spec:
         enabled: true
     firmware:
       acpi:
+        msdmNameRef: msdmNameRefValue
         slicNameRef: slicNameRefValue
       bootloader:
         bios:
diff --git a/tests/vmi_configuration_test.go b/tests/vmi_configuration_test.go
index 577f4d974df9..9154f7e5d20e 100644
--- a/tests/vmi_configuration_test.go
+++ b/tests/vmi_configuration_test.go
@@ -22,6 +22,7 @@ package tests_test
 import (
 	"bufio"
 	"context"
+	"encoding/hex"
 	"fmt"
 	"path/filepath"
 	"runtime"
@@ -542,8 +543,8 @@ var _ = Describe("[sig-compute]Configurations", decorators.SigCompute, func() {
 			})
 		})
 
-		Context("with ACPI SLIC table", func() {
-			It("Should configure guest APCI SLIC with Secret file", func() {
+		Context("with ACPI table", func() {
+			It("Should configure guest ACPI SLIC with Secret file", func() {
 				const (
 					volumeSlicSecretName = "volume-slic-secret"
 					secretWithSlicName   = "secret-with-slic-data"
@@ -553,19 +554,12 @@ var _ = Describe("[sig-compute]Configurations", decorators.SigCompute, func() {
 					0x41, 0x53, 0x48, 0x20, 0x4d, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 					0x88, 0x04, 0x00, 0x00, 0x71, 0x65, 0x6d, 0x75, 0x00, 0x00, 0x00, 0x00,
 				}
-				// To easily compare with console output
-				var hexData string
-				for _, b := range slicTable {
-					hexData += fmt.Sprintf("%02x", b)
-				}
-
 				vmi := libvmifact.NewAlpine()
 
 				By("Creating a secret with the binary ACPI SLIC table")
 				secret := libsecret.New(secretWithSlicName, libsecret.DataBytes{"slic.bin": slicTable})
-				secret, err := virtClient.CoreV1().Secrets(testsuite.GetTestNamespace(vmi)).Create(context.Background(), secret, metav1.CreateOptions{})
+				_, err := virtClient.CoreV1().Secrets(testsuite.GetTestNamespace(vmi)).Create(context.Background(), secret, metav1.CreateOptions{})
 				Expect(err).ToNot(HaveOccurred())
-				Expect(secret).ToNot(BeNil())
 
 				By("Configuring the volume with the secret")
 				vmi.Spec.Volumes = append(vmi.Spec.Volumes, v1.Volume{
@@ -590,7 +584,51 @@ var _ = Describe("[sig-compute]Configurations", decorators.SigCompute, func() {
 				By("Checking the guest ACPI SLIC table matches the one provided")
 				Expect(console.SafeExpectBatch(vmi, []expect.Batcher{
 					&expect.BSnd{S: "xxd -p -c 40 /sys/firmware/acpi/tables/SLIC\n"},
-					&expect.BExp{R: console.RetValue(hexData)},
+					&expect.BExp{R: console.RetValue(hex.EncodeToString(slicTable))},
+				}, 3)).To(Succeed())
+			})
+
+			It("Should configure guest ACPI MSDM with Secret file", func() {
+				const (
+					volumeMsdmSecretName = "volume-msdm-secret"
+					secretWithMsdmName   = "secret-with-msdm-data"
+				)
+				var msdmTable = []byte{
+					0x4d, 0x53, 0x44, 0x4d, 0x24, 0x00, 0x00, 0x00, 0x01, 0x43, 0x43, 0x52,
+					0x41, 0x53, 0x48, 0x20, 0x4d, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+					0x88, 0x04, 0x00, 0x00, 0x71, 0x65, 0x6d, 0x75, 0x00, 0x00, 0x00, 0x00,
+				}
+				vmi := libvmifact.NewAlpine()
+
+				By("Creating a secret with the binary ACPI msdm table")
+				secret := libsecret.New(secretWithMsdmName, libsecret.DataBytes{"msdm.bin": msdmTable})
+				_, err := virtClient.CoreV1().Secrets(testsuite.GetTestNamespace(vmi)).Create(context.Background(), secret, metav1.CreateOptions{})
+				Expect(err).ToNot(HaveOccurred())
+
+				By("Configuring the volume with the secret")
+				vmi.Spec.Volumes = append(vmi.Spec.Volumes, v1.Volume{
+					Name: volumeMsdmSecretName,
+					VolumeSource: v1.VolumeSource{
+						Secret: &v1.SecretVolumeSource{
+							SecretName: secretWithMsdmName,
+						},
+					},
+				})
+
+				// The firmware needs to reference the volume name of msdm secret
+				By("Configuring the firmware option with volume name that contains the secret")
+				vmi.Spec.Domain.Firmware = &v1.Firmware{
+					ACPI: &v1.ACPI{
+						MsdmNameRef: volumeMsdmSecretName,
+					},
+				}
+				vmi = libvmops.RunVMIAndExpectLaunch(vmi, 360)
+				Expect(console.LoginToAlpine(vmi)).To(Succeed())
+
+				By("Checking the guest ACPI MSDM table matches the one provided")
+				Expect(console.SafeExpectBatch(vmi, []expect.Batcher{
+					&expect.BSnd{S: "xxd -p -c 40 /sys/firmware/acpi/tables/MSDM\n"},
+					&expect.BExp{R: console.RetValue(hex.EncodeToString(msdmTable))},
 				}, 3)).To(Succeed())
 			})
 		})