[PATCH] libvirt: configure TPM device in the XML
Parses vtpm.present from VMX files and converts to libvirt TPM device with CRB model and emulator backend. VMware vTPM uses TPM 2.0 with the CRB Signed-off-by: Srihari Parimi <sparimi@redhat.com> --- src/vmx/vmx.c | 50 ++++++++++++++++++++++++++++++++++++++ tests/vmx2xmldata/vtpm.vmx | 22 +++++++++++++++++ tests/vmx2xmldata/vtpm.xml | 32 ++++++++++++++++++++++++ tests/vmx2xmltest.c | 2 ++ 4 files changed, 106 insertions(+) create mode 100644 tests/vmx2xmldata/vtpm.vmx create mode 100644 tests/vmx2xmldata/vtpm.xml diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 57dfd57cfc..9231968175 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -599,6 +599,7 @@ static int virVMXParseSerial(virVMXContext *ctx, virConf *conf, int port, static int virVMXParseParallel(virVMXContext *ctx, virConf *conf, int port, virDomainChrDef **def); static int virVMXParseSVGA(virConf *conf, virDomainVideoDef **def); +static int virVMXParseTPM(virConf *conf, virDomainTPMDef **def); static int virVMXFormatVNC(virDomainGraphicsDef *def, virBuffer *buffer); static int virVMXFormatDisk(virVMXContext *ctx, virDomainDiskDef *def, @@ -1403,6 +1404,7 @@ virVMXParseConfig(virVMXContext *ctx, char *guestOS = NULL; bool smbios_reflecthost = false; bool uefi_secureboot = false; + bool vtpm_present = false; int controller; int bus; int port; @@ -1938,6 +1940,16 @@ virVMXParseConfig(virVMXContext *ctx, def->nvideos = 1; + /* def:tpms */ + { + virDomainTPMDef *tpm = NULL; + if (virVMXParseTPM(conf, &tpm) < 0) + goto cleanup; + + if (tpm) + VIR_APPEND_ELEMENT(def->tpms, def->ntpms, tpm); + } + /* def:sounds */ /* FIXME */ @@ -2001,6 +2013,18 @@ virVMXParseConfig(virVMXContext *ctx, } } + /* vmx: vtpm.present (optional) */ + if (virVMXGetConfigBoolean(conf, "vtpm.present", + &vtpm_present, false, true) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to parse vtpm.present")); + goto cleanup; + } + if (vtpm_present) { + VIR_DEBUG("Processing vtpm_present: %s", + (vtpm_present == true) ? "yes" : "no"); + } + /* vmx:uefi.secureBoot.enabled */ if (virVMXGetConfigBoolean(conf, "uefi.secureBoot.enabled", &uefi_secureboot, false, true) < 0) { @@ -3367,6 +3391,32 @@ virVMXParseSVGA(virConf *conf, virDomainVideoDef **def) return result; } +static int +virVMXParseTPM(virConf *conf, virDomainTPMDef **def) +{ + bool vtpm_present = false; + + if (def == NULL || *def != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + } + + /* vmx:vtpm.present */ + if (virVMXGetConfigBoolean(conf, "vtpm.present", &vtpm_present, + false, true) < 0) { + return -1; + } + + if (!vtpm_present) + return 0; + + *def = g_new0(virDomainTPMDef, 1); + (*def)->type = VIR_DOMAIN_TPM_TYPE_EMULATOR; + (*def)->model = VIR_DOMAIN_TPM_MODEL_CRB; + (*def)->data.emulator.version = VIR_DOMAIN_TPM_VERSION_2_0; + + return 0; +} /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * diff --git a/tests/vmx2xmldata/vtpm.vmx b/tests/vmx2xmldata/vtpm.vmx new file mode 100644 index 0000000000..6e2fd725b7 --- /dev/null +++ b/tests/vmx2xmldata/vtpm.vmx @@ -0,0 +1,22 @@ +config.version = "8" +virtualHW.version = "19" +displayName = "test-vtpm" +memsize = "4096" +numvcpus = "2" +guestOS = "windows9-64" + +# Disk Configuration +scsi0.present = "TRUE" +scsi0.virtualDev = "lsisas1068" +scsi0:0.present = "TRUE" +scsi0:0.deviceType = "scsi-hardDisk" +scsi0:0.fileName = "test_disk.vmdk" + +# vTPM configuration +vtpm.present = "TRUE" + +# Network Configuration +ethernet0.present = "TRUE" +ethernet0.connectionType = "nat" +ethernet0.virtualDev = "e1000e" +ethernet0.addressType = "generated" diff --git a/tests/vmx2xmldata/vtpm.xml b/tests/vmx2xmldata/vtpm.xml new file mode 100644 index 0000000000..cbb23ce673 --- /dev/null +++ b/tests/vmx2xmldata/vtpm.xml @@ -0,0 +1,32 @@ +<domain type='vmware'> + <name>test-vtpm</name> + <uuid>00000000-0000-0000-0000-000000000000</uuid> + <memory unit='KiB'>4194304</memory> + <currentMemory unit='KiB'>4194304</currentMemory> + <vcpu placement='static'>2</vcpu> + <os> + <type arch='x86_64'>hvm</type> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <disk type='file' device='disk'> + <source file='[datastore] directory/test_disk.vmdk'/> + <target dev='sda' bus='scsi'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='scsi' index='0' model='lsisas1068'/> + <interface type='user'> + <mac address='00:00:00:00:00:00' type='generated'/> + <model type='e1000e'/> + </interface> + <tpm model='tpm-crb'> + <backend type='emulator' version='2.0'/> + </tpm> + <video> + <model type='vmvga' vram='4096' primary='yes'/> + </video> + </devices> +</domain> diff --git a/tests/vmx2xmltest.c b/tests/vmx2xmltest.c index fcca765bed..3ffc04fda4 100644 --- a/tests/vmx2xmltest.c +++ b/tests/vmx2xmltest.c @@ -243,6 +243,8 @@ mymain(void) DO_TEST("firmware-efi"); + DO_TEST("vtpm"); + ctx.datacenterPath = "folder1/folder2/datacenter1"; DO_TEST("datacenterpath"); -- 2.53.0
In summary of the commit mention 'vmx' as the driver instead of the obvious 'libvirt' On Fri, Apr 17, 2026 at 11:45:15 +0530, Srihari Parimi via Devel wrote:
Parses vtpm.present from VMX files and converts to libvirt TPM device with CRB model and emulator backend. VMware vTPM uses TPM 2.0 with the CRB
Do you know of any docs to point to for which version of TPM is used?
Signed-off-by: Srihari Parimi <sparimi@redhat.com> --- src/vmx/vmx.c | 50 ++++++++++++++++++++++++++++++++++++++ tests/vmx2xmldata/vtpm.vmx | 22 +++++++++++++++++ tests/vmx2xmldata/vtpm.xml | 32 ++++++++++++++++++++++++ tests/vmx2xmltest.c | 2 ++ 4 files changed, 106 insertions(+) create mode 100644 tests/vmx2xmldata/vtpm.vmx create mode 100644 tests/vmx2xmldata/vtpm.xml
diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 57dfd57cfc..9231968175 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -599,6 +599,7 @@ static int virVMXParseSerial(virVMXContext *ctx, virConf *conf, int port, static int virVMXParseParallel(virVMXContext *ctx, virConf *conf, int port, virDomainChrDef **def); static int virVMXParseSVGA(virConf *conf, virDomainVideoDef **def); +static int virVMXParseTPM(virConf *conf, virDomainTPMDef **def);
static int virVMXFormatVNC(virDomainGraphicsDef *def, virBuffer *buffer); static int virVMXFormatDisk(virVMXContext *ctx, virDomainDiskDef *def, @@ -1403,6 +1404,7 @@ virVMXParseConfig(virVMXContext *ctx, char *guestOS = NULL; bool smbios_reflecthost = false; bool uefi_secureboot = false; + bool vtpm_present = false; int controller; int bus; int port; @@ -1938,6 +1940,16 @@ virVMXParseConfig(virVMXContext *ctx,
def->nvideos = 1;
+ /* def:tpms */ + { + virDomainTPMDef *tpm = NULL; + if (virVMXParseTPM(conf, &tpm) < 0) + goto cleanup;
So this parses 'vtpm.present'
+ + if (tpm) + VIR_APPEND_ELEMENT(def->tpms, def->ntpms, tpm); + } + /* def:sounds */ /* FIXME */
@@ -2001,6 +2013,18 @@ virVMXParseConfig(virVMXContext *ctx, } }
+ /* vmx: vtpm.present (optional) */
And inside the same function ...
+ if (virVMXGetConfigBoolean(conf, "vtpm.present", + &vtpm_present, false, true) < 0) {
You parse it again ...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to parse vtpm.present")); + goto cleanup; + } + if (vtpm_present) { + VIR_DEBUG("Processing vtpm_present: %s", + (vtpm_present == true) ? "yes" : "no");
just to add a debug statement? Can you explain this please?
+ } + /* vmx:uefi.secureBoot.enabled */ if (virVMXGetConfigBoolean(conf, "uefi.secureBoot.enabled", &uefi_secureboot, false, true) < 0) { @@ -3367,6 +3391,32 @@ virVMXParseSVGA(virConf *conf, virDomainVideoDef **def) return result; }
+static int +virVMXParseTPM(virConf *conf, virDomainTPMDef **def) +{ + bool vtpm_present = false; + + if (def == NULL || *def != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + }
The only caller always passes these, so this check isn't needed.
+ + /* vmx:vtpm.present */ + if (virVMXGetConfigBoolean(conf, "vtpm.present", &vtpm_present, + false, true) < 0) { + return -1; + } + + if (!vtpm_present) + return 0; + + *def = g_new0(virDomainTPMDef, 1); + (*def)->type = VIR_DOMAIN_TPM_TYPE_EMULATOR; + (*def)->model = VIR_DOMAIN_TPM_MODEL_CRB; + (*def)->data.emulator.version = VIR_DOMAIN_TPM_VERSION_2_0; + + return 0; +}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
On 4/17/26 6:15 AM, Peter Krempa via Devel wrote:
In summary of the commit mention 'vmx' as the driver instead of the obvious 'libvirt'
On Fri, Apr 17, 2026 at 11:45:15 +0530, Srihari Parimi via Devel wrote:
Parses vtpm.present from VMX files and converts to libvirt TPM device with CRB model and emulator backend. VMware vTPM uses TPM 2.0 with the CRB
Do you know of any docs to point to for which version of TPM is used?
I wrote the original RFE so I'll provide this piece. Docs: https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/8-0/vsphere-s... Has this quote: "A virtual Trusted Platform Module (vTPM) is a software-based representation of a physical Trusted Platform Module 2.0 chip." So, emulated version 2.0. Can't find anything about CRB vs TIS but I believe CRB is default for TPM 2.0 devices in general so I took that to be implied. I lack a setup to confirm at the moment but I can scrounge one up if you'd like Thanks, Cole
On Fri, Apr 17, 2026 at 3:45 PM Peter Krempa <pkrempa@redhat.com> wrote:
In summary of the commit mention 'vmx' as the driver instead of the obvious 'libvirt'
On Fri, Apr 17, 2026 at 11:45:15 +0530, Srihari Parimi via Devel wrote:
Parses vtpm.present from VMX files and converts to libvirt TPM device with CRB model and emulator backend. VMware vTPM uses TPM 2.0 with the CRB
Do you know of any docs to point to for which version of TPM is used?
Google search general suggestions 1. TIS supports TPM 1.2, TPM 2.0 2. CRB strictly supports TPM 2.0 3. CRB is generally recommended for Windows 11 or for UEFI based systems which requires TPM 2.0
Signed-off-by: Srihari Parimi <sparimi@redhat.com> --- src/vmx/vmx.c | 50 ++++++++++++++++++++++++++++++++++++++ tests/vmx2xmldata/vtpm.vmx | 22 +++++++++++++++++ tests/vmx2xmldata/vtpm.xml | 32 ++++++++++++++++++++++++ tests/vmx2xmltest.c | 2 ++ 4 files changed, 106 insertions(+) create mode 100644 tests/vmx2xmldata/vtpm.vmx create mode 100644 tests/vmx2xmldata/vtpm.xml
diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 57dfd57cfc..9231968175 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -599,6 +599,7 @@ static int virVMXParseSerial(virVMXContext *ctx,
static int virVMXParseParallel(virVMXContext *ctx, virConf *conf, int
virConf *conf, int port, port,
virDomainChrDef **def); static int virVMXParseSVGA(virConf *conf, virDomainVideoDef **def); +static int virVMXParseTPM(virConf *conf, virDomainTPMDef **def);
static int virVMXFormatVNC(virDomainGraphicsDef *def, virBuffer
*buffer);
static int virVMXFormatDisk(virVMXContext *ctx, virDomainDiskDef *def, @@ -1403,6 +1404,7 @@ virVMXParseConfig(virVMXContext *ctx, char *guestOS = NULL; bool smbios_reflecthost = false; bool uefi_secureboot = false; + bool vtpm_present = false; int controller; int bus; int port; @@ -1938,6 +1940,16 @@ virVMXParseConfig(virVMXContext *ctx,
def->nvideos = 1;
+ /* def:tpms */ + { + virDomainTPMDef *tpm = NULL; + if (virVMXParseTPM(conf, &tpm) < 0) + goto cleanup;
So this parses 'vtpm.present'
+ + if (tpm) + VIR_APPEND_ELEMENT(def->tpms, def->ntpms, tpm); + } + /* def:sounds */ /* FIXME */
@@ -2001,6 +2013,18 @@ virVMXParseConfig(virVMXContext *ctx, } }
+ /* vmx: vtpm.present (optional) */
And inside the same function ...
+ if (virVMXGetConfigBoolean(conf, "vtpm.present", + &vtpm_present, false, true) < 0) {
You parse it again ...
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to parse vtpm.present")); + goto cleanup; + } + if (vtpm_present) { + VIR_DEBUG("Processing vtpm_present: %s", + (vtpm_present == true) ? "yes" : "no");
just to add a debug statement? Can you explain this please?
Agree - this is not needed. Mistake on my part when I moved the code into the function *virVMXParseTPM*, this should have been removed. I need to review the patch before posting it.
+ } + /* vmx:uefi.secureBoot.enabled */ if (virVMXGetConfigBoolean(conf, "uefi.secureBoot.enabled", &uefi_secureboot, false, true) < 0) { @@ -3367,6 +3391,32 @@ virVMXParseSVGA(virConf *conf, virDomainVideoDef **def) return result; }
+static int +virVMXParseTPM(virConf *conf, virDomainTPMDef **def) +{ + bool vtpm_present = false; + + if (def == NULL || *def != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid argument")); + return -1; + }
The only caller always passes these, so this check isn't needed.
Agree. I have removed the checks. Was wondering if we need to be more defensive ?
+ + /* vmx:vtpm.present */ + if (virVMXGetConfigBoolean(conf, "vtpm.present", &vtpm_present, + false, true) < 0) { + return -1; + } + + if (!vtpm_present) + return 0; + + *def = g_new0(virDomainTPMDef, 1); + (*def)->type = VIR_DOMAIN_TPM_TYPE_EMULATOR; + (*def)->model = VIR_DOMAIN_TPM_MODEL_CRB; + (*def)->data.emulator.version = VIR_DOMAIN_TPM_VERSION_2_0; + + return 0; +}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
participants (3)
-
Cole Robinson -
Peter Krempa -
Srihari Parimi