This patch extends the TPM's device XML with TPM 2 support. This only works
for the emulator type backend and looks as follows:
<tpm model='tpm-tis'>
<backend type='emulator' tpmversion='2'/>
</tpm>
Once the version of a TPM has been chosen it cannot be changed anymore unless
one removes the TPM device first and then reads it. However, one looses all
the secrets stored inside or tied to the emulated TPM by doing this.
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
docs/formatdomain.html.in | 17 ++++++-
docs/schemas/domaincommon.rng | 13 ++++++
src/conf/domain_conf.c | 36 ++++++++++++++-
src/conf/domain_conf.h | 6 +++
src/util/virtpm.c | 64 +++++++++++++++++++++++++-
tests/qemuxml2argvdata/tpm-emulator-tpm2.args | 24 ++++++++++
tests/qemuxml2argvdata/tpm-emulator-tpm2.xml | 30 ++++++++++++
tests/qemuxml2argvtest.c | 3 ++
tests/qemuxml2xmloutdata/tpm-emulator-tpm2.xml | 34 ++++++++++++++
9 files changed, 223 insertions(+), 4 deletions(-)
create mode 100644 tests/qemuxml2argvdata/tpm-emulator-tpm2.args
create mode 100644 tests/qemuxml2argvdata/tpm-emulator-tpm2.xml
create mode 100644 tests/qemuxml2xmloutdata/tpm-emulator-tpm2.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index bd6fedc..e5463a0 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -7635,7 +7635,7 @@ qemu-kvm -net nic,model=? /dev/null
...
<devices>
<tpm model='tpm-tis'>
- <backend type='emulator'>
+ <backend type='emulator' tpmversion='2'>
</backend>
</tpm>
</devices>
@@ -7684,6 +7684,21 @@ qemu-kvm -net nic,model=? /dev/null
</dd>
</dl>
</dd>
+ <dt><code>tpmversion</code></dt>
+ <dd>
+ <p>
+ The <code>tpmversion</code> attribute indicates the version
+ of the TPM. By default a TPM 1.2 is created. This attribute
+ only works with the <code>emulator</code> backend. The following
+ versions are supported:
+ </p>
+ <ul>
+ <li>'1.2' : creates a TPM 1.2</li>
+ <li>'2.0' or '2' : creates a TPM 2</li>
+ </ul>
+ Note that once a certain version of a TPM has been created for
+ a guest, the version must not be changed anymore.
+ </dd>
</dl>
<h4><a id="elementsNVRAM">NVRAM device</a></h4>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index d628444..77328bd 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -4140,6 +4140,19 @@
</attribute>
</group>
</choice>
+ <choice>
+ <group>
+ <optional>
+ <attribute name="tpmversion">
+ <choice>
+ <value>1.2</value>
+ <value>2</value>
+ <value>2.0</value>
+ </choice>
+ </attribute>
+ </optional>
+ </group>
+ </choice>
</element>
</define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 72e4412..5498e2e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -12557,7 +12557,7 @@ virDomainSmartcardDefParseXML(virDomainXMLOptionPtr xmlopt,
* or like this:
*
* <tpm model='tpm-tis'>
- * <backend type='emulator'/>
+ * <backend type='emulator' tpmversion='2'/>
* </tpm>
*/
static virDomainTPMDefPtr
@@ -12570,6 +12570,7 @@ virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlopt,
char *path = NULL;
char *model = NULL;
char *backend = NULL;
+ char *tpmversion = NULL;
virDomainTPMDefPtr def;
xmlNodePtr save = ctxt->node;
xmlNodePtr *backends = NULL;
@@ -12616,6 +12617,20 @@ virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlopt,
goto error;
}
+ tpmversion = virXMLPropString(backends[0], "tpmversion");
+ if (!tpmversion || STREQ(tpmversion, "1.2")) {
+ def->tpmversion = VIR_DOMAIN_TPM_VERSION_1_2;
+ /* only TIS available for emulator */
+ if (def->type == VIR_DOMAIN_TPM_TYPE_EMULATOR)
+ def->model = VIR_DOMAIN_TPM_MODEL_TIS;
+ } else if (STREQ(tpmversion, "2.0") || STREQ(tpmversion, "2")) {
+ def->tpmversion = VIR_DOMAIN_TPM_VERSION_2;
+ } else {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("Unsupported TPM version '%s'"),
+ tpmversion);
+ }
+
switch (def->type) {
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
path = virXPathString("string(./backend/device/@path)", ctxt);
@@ -12640,6 +12655,7 @@ virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlopt,
VIR_FREE(model);
VIR_FREE(backend);
VIR_FREE(backends);
+ VIR_FREE(tpmversion);
ctxt->node = save;
return def;
@@ -24803,6 +24819,8 @@ virDomainTPMDefFormat(virBufferPtr buf,
virBufferAdjustIndent(buf, 2);
virBufferAsprintf(buf, "<backend type='%s'",
virDomainTPMBackendTypeToString(def->type));
+ if (def->tpmversion == VIR_DOMAIN_TPM_VERSION_2)
+ virBufferAddLit(buf, " tpmversion='2'");
virBufferAdjustIndent(buf, 2);
switch (def->type) {
@@ -29430,6 +29448,22 @@ virDomainCheckTPMChanges(virDomainDefPtr def,
if (newDef->tpm->type != def->tpm->type) {
/* type changed */
virDomainTPMDeleteAny(def);
+ } else {
+ switch (def->tpm->type) {
+ case VIR_DOMAIN_TPM_TYPE_EMULATOR:
+ if (def->tpm->tpmversion != newDef->tpm->tpmversion) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("The version of the TPM cannot "
+ "be changed; the TPM must be removed "
+ "from the VM and a new TPM added; "
+ "Note: all secrets will be lost"));
+ ret = -1;
+ }
+ break;
+ case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_TPM_TYPE_LAST:
+ break;
+ }
}
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 1f479b0..4aab54b 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1289,12 +1289,18 @@ typedef enum {
VIR_DOMAIN_TPM_TYPE_LAST
} virDomainTPMBackendType;
+typedef enum {
+ VIR_DOMAIN_TPM_VERSION_1_2,
+ VIR_DOMAIN_TPM_VERSION_2,
+} virDomainTPMVersion;
+
# define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0"
struct _virDomainTPMDef {
virDomainTPMBackendType type;
virDomainDeviceInfo info;
virDomainTPMModel model;
+ virDomainTPMVersion tpmversion;
union {
struct {
virDomainChrSourceDef source;
diff --git a/src/util/virtpm.c b/src/util/virtpm.c
index 354761e..7390895 100644
--- a/src/util/virtpm.c
+++ b/src/util/virtpm.c
@@ -52,6 +52,8 @@ static char *swtpm_path;
static char *swtpm_setup;
static char *swtpm_ioctl;
+static bool swtpm_supports_tpm2;
+
/**
* virTPMCreateCancelPath:
* @devpath: Path to the TPM device
@@ -96,6 +98,40 @@ virTPMCreateCancelPath(const char *devpath)
}
/*
+ * virTPMCheckForTPM2Support
+ *
+ * Check whether swtpm_setup supports TPM 2
+ */
+static void
+virTPMCheckForTPM2Support(void)
+{
+ virCommandPtr cmd;
+ char *help = NULL;
+
+ if (!swtpm_setup)
+ return;
+
+ cmd = virCommandNew(swtpm_setup);
+ if (!cmd)
+ return;
+
+ virCommandAddArg(cmd, "--help");
+ virCommandSetOutputBuffer(cmd, &help);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ if (strstr(help, "--tpm2")) {
+ fprintf(stderr, "TPM2 is supported by swtpm_setup\n");
+ swtpm_supports_tpm2 = true;
+ }
+
+ cleanup:
+ virCommandFree(cmd);
+ VIR_FREE(help);
+}
+
+/*
* virTPMEmulatorInit
*
* Initialize the Emulator functions by searching for necessary
@@ -134,6 +170,7 @@ virTPMEmulatorInit(void)
VIR_FREE(swtpm_setup);
return -1;
}
+ virTPMCheckForTPM2Support();
}
if (!swtpm_ioctl) {
@@ -311,12 +348,14 @@ virTPMTryConnect(const char *pathname, unsigned long timeout_ms)
* typically this should be 'tss'
* @logfile: The file to write the log into; it must be writable
* for the user given by userid or 'tss'
+ * @tpmversion: The version of the TPM, either a TPM 1.2 or TPM 2
*
* Setup the external swtpm
*/
static int
virTPMSetupEmulator(const char *storagepath, const unsigned char *vmuuid,
- uid_t swtpm_user, const char *logfile)
+ uid_t swtpm_user, const char *logfile,
+ const virDomainTPMVersion tpmversion)
{
virCommandPtr cmd = NULL;
int exitstatus;
@@ -335,6 +374,18 @@ virTPMSetupEmulator(const char *storagepath, const unsigned char
*vmuuid,
virCommandAddArg(cmd, "--runas");
virCommandAddArgFormat(cmd, "%u", swtpm_user);
}
+
+ switch (tpmversion) {
+ case VIR_DOMAIN_TPM_VERSION_1_2:
+ break;
+ case VIR_DOMAIN_TPM_VERSION_2:
+ virCommandAddArgList(cmd, "--tpm2", NULL);
+ if (!swtpm_supports_tpm2) {
+ goto cleanup;
+ }
+ break;
+ }
+
virCommandAddArgList(cmd,
"--tpm-state", storagepath,
"--vmid", uuid,
@@ -400,7 +451,8 @@ virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const unsigned char
*vmuuid,
goto error;
if (created &&
- virTPMSetupEmulator(storagepath, vmuuid, swtpm_user, logfile) < 0)
+ virTPMSetupEmulator(storagepath, vmuuid, swtpm_user, logfile,
+ tpm->tpmversion) < 0)
goto error;
if (!(tpm->data.emulator.source.data.nix.path =
@@ -437,6 +489,14 @@ virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const unsigned
char *vmuuid,
virCommandAllowCap(cmd, CAP_SETUID);
}
+ switch (tpm->tpmversion) {
+ case VIR_DOMAIN_TPM_VERSION_1_2:
+ break;
+ case VIR_DOMAIN_TPM_VERSION_2:
+ virCommandAddArg(cmd, "--tpm2");
+ break;
+ }
+
tpm->data.emulator.storagepath = storagepath;
VIR_FREE(tpm->data.emulator.logfile);
tpm->data.emulator.logfile = logfile;
diff --git a/tests/qemuxml2argvdata/tpm-emulator-tpm2.args
b/tests/qemuxml2argvdata/tpm-emulator-tpm2.args
new file mode 100644
index 0000000..9418c74
--- /dev/null
+++ b/tests/qemuxml2argvdata/tpm-emulator-tpm2.args
@@ -0,0 +1,24 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/home/test \
+USER=test \
+LOGNAME=test \
+QEMU_AUDIO_DRV=none \
+/usr/bin/qemu-system-x86_64 \
+-name TPM-VM \
+-S \
+-M pc-0.12 \
+-m 2048 \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid 11d7cd22-da89-3094-6212-079a48a309a1 \
+-nographic \
+-nodefaults \
+-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-TPM-VM/monitor.sock,\
+server,nowait \
+-mon chardev=charmonitor,id=monitor,mode=readline \
+-boot c \
+-usb \
+-tpmdev emulator,id=tpm-tpm0,chardev=chrtpm \
+-chardev socket,id=chrtpm,path=/dev/test \
+-device tpm-tis,tpmdev=tpm-tpm0,id=tpm0 \
+-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3
diff --git a/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml
b/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml
new file mode 100644
index 0000000..070bedb
--- /dev/null
+++ b/tests/qemuxml2argvdata/tpm-emulator-tpm2.xml
@@ -0,0 +1,30 @@
+<domain type='qemu'>
+ <name>TPM-VM</name>
+ <uuid>11d7cd22-da89-3094-6212-079a48a309a1</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>512288</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-0.12'>hvm</type>
+ <boot dev='hd'/>
+ <bootmenu enable='yes'/>
+ </os>
+ <features>
+ <acpi/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <tpm model='tpm-tis'>
+ <backend type='emulator' tpmversion='2'/>
+ </tpm>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 92846c3..5d0650b 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -2156,6 +2156,9 @@ mymain(void)
DO_TEST("tpm-emulator",
QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_EMULATOR,
QEMU_CAPS_DEVICE_TPM_TIS);
+ DO_TEST("tpm-emulator-tpm2",
+ QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_EMULATOR,
+ QEMU_CAPS_DEVICE_TPM_TIS);
DO_TEST_PARSE_ERROR("pci-domain-invalid", NONE);
diff --git a/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.xml
b/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.xml
new file mode 100644
index 0000000..4a68bd8
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/tpm-emulator-tpm2.xml
@@ -0,0 +1,34 @@
+<domain type='qemu'>
+ <name>TPM-VM</name>
+ <uuid>11d7cd22-da89-3094-6212-079a48a309a1</uuid>
+ <memory unit='KiB'>2097152</memory>
+ <currentMemory unit='KiB'>512288</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc-0.12'>hvm</type>
+ <boot dev='hd'/>
+ <bootmenu enable='yes'/>
+ </os>
+ <features>
+ <acpi/>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <controller type='usb' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <tpm model='tpm-tis'>
+ <backend type='emulator' tpmversion='2'/>
+ </tpm>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
--
2.5.5