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 | 20 +++++-
src/conf/domain_conf.h | 6 ++
src/util/virtpm.c | 84 +++++++++++++++++++++++---
tests/qemuxml2argvdata/tpm-emulator-tpm2.args | 24 ++++++++
tests/qemuxml2argvdata/tpm-emulator-tpm2.xml | 30 +++++++++
tests/qemuxml2argvtest.c | 2 +
tests/qemuxml2xmloutdata/tpm-emulator-tpm2.xml | 34 +++++++++++
9 files changed, 221 insertions(+), 9 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 b5f1c3f..0bbb547 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -12552,7 +12552,7 @@ virDomainSmartcardDefParseXML(virDomainXMLOptionPtr xmlopt,
* or like this:
*
* <tpm model='tpm-tis'>
- * <backend type='emulator'/>
+ * <backend type='emulator' tpmversion='2'/>
* </tpm>
*/
static virDomainTPMDefPtr
@@ -12565,6 +12565,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;
@@ -12611,6 +12612,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);
@@ -12635,6 +12650,7 @@ virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlopt,
VIR_FREE(model);
VIR_FREE(backend);
VIR_FREE(backends);
+ VIR_FREE(tpmversion);
ctxt->node = save;
return def;
@@ -24798,6 +24814,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) {
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index f632184..80f599c 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 649153e..3bb911e 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,38 @@ 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"))
+ swtpm_supports_tpm2 = true;
+
+ cleanup:
+ virCommandFree(cmd);
+ VIR_FREE(help);
+}
+
+/*
* virTPMEmulatorInit
*
* Initialize the Emulator functions by searching for necessary
@@ -134,6 +168,7 @@ virTPMEmulatorInit(void)
VIR_FREE(swtpm_setup);
return -1;
}
+ virTPMCheckForTPM2Support();
}
if (!swtpm_ioctl) {
@@ -160,16 +195,28 @@ virTPMEmulatorInit(void)
*
* @swtpmStorageDir: directory for swtpm persistent state
* @vmname: The name of the VM for which to create the storage
+ * @tpmversion: version of the TPM
*
* Create the swtpm's storage path
*/
static char *
virTPMCreateEmulatorStoragePath(const char *swtpmStorageDir,
- const char *vmname)
+ const char *vmname,
+ virDomainTPMVersion tpmversion)
{
char *path = NULL;
+ const char *dir = "";
+
+ switch (tpmversion) {
+ case VIR_DOMAIN_TPM_VERSION_1_2:
+ dir = "tpm1.2";
+ break;
+ case VIR_DOMAIN_TPM_VERSION_2:
+ dir = "tpm2";
+ break;
+ }
- ignore_value(virAsprintf(&path, "%s/%s/tpm1.2", swtpmStorageDir,
vmname));
+ ignore_value(virAsprintf(&path, "%s/%s/%s", swtpmStorageDir, vmname,
dir));
return path;
}
@@ -313,9 +360,10 @@ int virTPMEmulatorInitPaths(virDomainTPMDefPtr tpm,
const char *swtpmStorageDir,
const char *vmname)
{
- if (!tpm->data.emulator.storagepath &&
- !(tpm->data.emulator.storagepath =
- virTPMCreateEmulatorStoragePath(swtpmStorageDir, vmname)))
+ VIR_FREE(tpm->data.emulator.storagepath);
+ if (!(tpm->data.emulator.storagepath =
+ virTPMCreateEmulatorStoragePath(swtpmStorageDir, vmname,
+ tpm->tpmversion)))
return -1;
return 0;
@@ -398,13 +446,15 @@ int virTPMEmulatorPrepareHost(virDomainTPMDefPtr tpm,
* typically this should be the uid of 'tss' or 'root'
* @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
virTPMEmulatorRunSetup(const char *storagepath, const char *vmname,
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;
@@ -425,6 +475,17 @@ virTPMEmulatorRunSetup(const char *storagepath, const char *vmname,
virCommandSetUID(cmd, swtpm_user);
virCommandSetGID(cmd, 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", vmid,
@@ -482,7 +543,8 @@ virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const char
*vmname,
if (created &&
virTPMEmulatorRunSetup(tpm->data.emulator.storagepath, vmname, vmuuid,
- swtpm_user, tpm->data.emulator.logfile) < 0)
+ swtpm_user, tpm->data.emulator.logfile,
+ tpm->tpmversion) < 0)
goto error;
unlink(tpm->data.emulator.source.data.nix.path);
@@ -507,6 +569,14 @@ virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const char
*vmname,
virCommandSetUID(cmd, swtpm_user);
virCommandSetGID(cmd, swtpm_user);
+ switch (tpm->tpmversion) {
+ case VIR_DOMAIN_TPM_VERSION_1_2:
+ break;
+ case VIR_DOMAIN_TPM_VERSION_2:
+ virCommandAddArg(cmd, "--tpm2");
+ break;
+ }
+
return cmd;
error:
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 06dca97..ac80a64 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -2154,6 +2154,8 @@ mymain(void)
QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_TIS);
DO_TEST("tpm-emulator",
QEMU_CAPS_DEVICE_TPM_EMULATOR, QEMU_CAPS_DEVICE_TPM_TIS);
+ DO_TEST("tpm-emulator-tpm2",
+ 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