[RFC PATCH V1 3/5] tpm: Add TPM support for Qemu

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds support for TPM for the Qemu driver to
- detect whether Qemu supports TPM and then sets the TPM capability flag
  appropriately
- create the Qemu command line to add a TPM to the VM
- create a QCoW2 file that holds the TPM persistent state
- parse a Qemu command line

This patch also moves the function qemuFindQemuImgBinary(void) from
qemu_driver to qemu_command and makes it a non-static function. qemu_command
seemed a better place for 'public' functions that qemu_driver.


Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxxxxxxxxxx>

---
 src/qemu/qemu_capabilities.c |   54 ++++++++++
 src/qemu/qemu_capabilities.h |    3 
 src/qemu/qemu_command.c      |  232 +++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_command.h      |    1 
 src/qemu/qemu_driver.c       |   37 ++++--
 src/qemu/qemu_migration.c    |    1 
 6 files changed, 314 insertions(+), 14 deletions(-)

Index: libvirt-acl/src/qemu/qemu_command.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_command.c
+++ libvirt-acl/src/qemu/qemu_command.c
@@ -910,6 +910,109 @@ int qemuDomainPCIAddressSetNextAddr(qemu
     return -1;
 }
 
+
+static int qemudTPMCreateBlockStore(const char *tpmstorefile,
+                                    const char *qemu_cmd)
+{
+    int rc = 0;
+    struct stat statbuf;
+    char *qemuimg = NULL;
+    int status;
+    char filesize[10];
+    unsigned int bssize;
+    const char *argv[] = { qemuimg, "create", "-f", "qcow2", tpmstorefile,
+                           filesize, NULL};
+
+    if (stat(tpmstorefile, &statbuf) == -1 || statbuf.st_size == 0) {
+        if (errno == ENOENT || statbuf.st_size == 0) {
+            /* determine size of qcow2 */
+            if (qemuCapsGetTPMBuiltinBSSize(qemu_cmd,
+                                            &bssize) < 0) {
+                qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                "%s",
+                                _("Could not determine TPM store file size"));
+                rc = 1;
+                goto err_exit;
+            }
+
+            /* create the file */
+            qemuimg = qemuFindQemuImgBinary();
+            if (!qemuimg) {
+                rc = 1;
+                goto err_exit;
+            }
+            argv[0] = qemuimg;
+            snprintf(filesize, sizeof(filesize),"%dk", bssize);
+
+            VIR_DEBUG("Creating BS file %s of size %dkb\n",
+                      tpmstorefile,
+                      bssize);
+
+            if (virRun(argv, &status) != 0 || status != 0) {
+                qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                "%s", _("Could not create TPM store file"));
+                rc = 1;
+                goto err_exit;
+            }
+        } else {
+            // FIXME: Not sure what to do in all the other error cases
+            virReportSystemError(errno, "%s",
+                      _("Error while stating TPM blockstore file"));
+            rc = 1;
+        }
+    } else {
+        VIR_DEBUG("TPM Blockstorage file %s exists\n", tpmstorefile);
+    }
+
+ err_exit:
+    VIR_FREE(qemuimg);
+    return rc;
+}
+
+
+static char * qemudBuildCommandLineTPMDevStr(const virDomainDefPtr def,
+                                             const char *qemu_cmd)
+{
+    int rc = 0;
+    char *tmp = NULL;
+    const virDomainTPMDefPtr tpm = def->tpm;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    switch (tpm->type) {
+    case VIR_DOMAIN_TPM_TYPE_BUILTIN:
+        tmp = virDomainTPMGetStorageFilename(tpm, def->uuid);
+        if (!tmp) {
+            rc = -1;
+            goto err_exit;
+        }
+        if (qemudTPMCreateBlockStore(tmp, qemu_cmd)) {
+            rc = -1;
+            goto err_exit;
+        }
+        virBufferVSprintf(&buf, "type=builtin,path=%s", tmp);
+        if (virBufferError(&buf)) {
+            virReportOOMError();
+            rc = -1;
+            goto err_exit;
+        }
+        break;
+
+    default:
+        rc = -1;
+        goto err_exit;
+    }
+
+    VIR_FREE(tmp);
+
+    return virBufferContentAndReset(&buf);
+
+ err_exit:
+    virBufferFreeAndReset(&buf);
+    VIR_FREE(tmp);
+    return NULL;
+}
+
+
 /*
  * This assigns static PCI slots to all configured devices.
  * The ordering here is chosen to match the ordering used
@@ -3831,6 +3934,21 @@ qemuBuildCommandLine(virConnectPtr conn,
         }
     }
 
+    if (qemuCapsGet(qemuCaps, QEMU_CAPS_TPM)) {
+        if (!def->tpm) {
+            virCommandAddArg(cmd, "-tpm");
+            virCommandAddArg(cmd, "none");
+        } else {
+            char *optstr;
+            if (!(optstr = qemudBuildCommandLineTPMDevStr(def, emulator)))
+                goto error;
+
+            virCommandAddArg(cmd, "-tpm");
+            virCommandAddArg(cmd, optstr);
+            VIR_FREE(optstr);
+        }
+    }
+
     virCommandAddArg(cmd, "-usb");
     for (i = 0 ; i < def->ninputs ; i++) {
         virDomainInputDefPtr input = def->inputs[i];
@@ -5508,6 +5626,99 @@ error:
 
 
 static int
+qemuParseCommandLineTPM(virDomainDefPtr dom,
+                        const char *val)
+{
+    int rc = 0;
+    virDomainTPMDefPtr tpm;
+    char **keywords;
+    char **values;
+    int nkeywords;
+    int i;
+
+    if (dom->tpm)
+        goto error;
+
+    nkeywords = qemuParseKeywords(val, &keywords, &values, 1);
+    if (nkeywords < 0)
+        goto error;
+
+    if (VIR_ALLOC(tpm) < 0)
+        goto no_memory;
+
+    tpm->type = VIR_DOMAIN_TPM_TYPE_BUILTIN;
+
+    for (i = 0; i < nkeywords; i++) {
+        if (STREQ(keywords[i], "type")) {
+            if (values[i] && STREQ(values[i], "builtin"))
+                tpm->type = VIR_DOMAIN_TPM_TYPE_BUILTIN;
+        } else if (STREQ(keywords[i], "path")) {
+            if (values[i]) {
+                tpm->data.builtin.storage = values[i];
+                values[i] = NULL;
+            } else
+                goto syntax;
+        }
+    }
+
+    /* sanity checks */
+    switch (tpm->type) {
+    case VIR_DOMAIN_TPM_TYPE_BUILTIN:
+        if (!tpm->data.builtin.storage) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s",
+                            _("missing required file=<filename> option"));
+            goto bad_definition;
+        }
+        break;
+    default:
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        "%s",
+                        _("unknown TPM type"));
+        goto bad_definition;
+    }
+
+    /* all ok */
+    dom->tpm = tpm;
+
+cleanup:
+    for (i = 0 ; i < nkeywords ; i++) {
+        VIR_FREE(keywords[i]);
+        VIR_FREE(values[i]);
+    }
+    VIR_FREE(keywords);
+    VIR_FREE(values);
+
+
+    return rc;
+
+syntax:
+    virDomainTPMDefFree(tpm);
+
+    qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                    _("unknown TPM syntax '%s'"), val);
+    rc = -1;
+    goto cleanup;
+
+bad_definition:
+    virDomainTPMDefFree(tpm);
+
+    rc = -1;
+    goto cleanup;
+
+no_memory:
+    virReportOOMError();
+
+    rc = -1;
+    goto cleanup;
+
+
+error:
+    return -1;
+}
+
+
+static int
 qemuParseCommandLineSmp(virDomainDefPtr dom,
                         const char *val)
 {
@@ -6141,6 +6352,12 @@ virDomainDefPtr qemuParseCommandLine(vir
             /* ignore, used internally by libvirt */
         } else if (STREQ(arg, "-S")) {
             /* ignore, always added by libvirt */
+        } else if (STREQ(arg, "-tpm")) {
+            WANT_VALUE();
+            if (STRNEQ(val, "none")) {
+                if (qemuParseCommandLineTPM(def, val) < 0)
+                    goto error;
+            }
         } else {
             /* something we can't yet parse.  Add it to the qemu namespace
              * cmdline/environment advanced options and hope for the best
@@ -6330,3 +6547,18 @@ cleanup:
 
     return def;
 }
+
+
+char *qemuFindQemuImgBinary(void)
+{
+    char *ret;
+
+    ret = virFindFileInPath("kvm-img");
+    if (ret == NULL)
+        ret = virFindFileInPath("qemu-img");
+    if (ret == NULL)
+        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                        "%s", _("unable to find kvm-img or qemu-img"));
+
+    return ret;
+}
Index: libvirt-acl/src/qemu/qemu_command.h
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_command.h
+++ libvirt-acl/src/qemu/qemu_command.h
@@ -172,5 +172,6 @@ qemuParseKeywords(const char *str,
                   char ***retvalues,
                   int allowEmptyValue);
 
+char *qemuFindQemuImgBinary(void);
 
 #endif /* __QEMU_COMMAND_H__*/
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -1268,6 +1268,7 @@ static virDomainPtr qemudDomainCreate(vi
                          (flags & VIR_DOMAIN_START_PAUSED) != 0,
                          -1, NULL, VIR_VM_OP_CREATE) < 0) {
         qemuAuditDomainStart(vm, "booted", false);
+        virDomainTPMDelete(vm, false);
         if (qemuDomainObjEndJob(vm) > 0)
             virDomainRemoveInactive(&driver->domains,
                                     vm);
@@ -1496,6 +1497,7 @@ static int qemudDomainDestroy(virDomainP
     qemuAuditDomainStop(vm, "destroyed");
 
     if (!vm->persistent) {
+        virDomainTPMDelete(vm, false);
         if (qemuDomainObjEndJob(vm) > 0)
             virDomainRemoveInactive(&driver->domains,
                                     vm);
@@ -3693,6 +3695,8 @@ static int qemudDomainUndefine(virDomain
         goto cleanup;
     }
 
+    virDomainTPMDelete(vm, false);
+
     if (virDomainDeleteConfig(driver->configDir, driver->autostartDir, vm) < 0)
         goto cleanup;
 
@@ -5856,20 +5860,6 @@ cleanup:
     return ret;
 }
 
-static char *qemuFindQemuImgBinary(void)
-{
-    char *ret;
-
-    ret = virFindFileInPath("kvm-img");
-    if (ret == NULL)
-        ret = virFindFileInPath("qemu-img");
-    if (ret == NULL)
-        qemuReportError(VIR_ERR_INTERNAL_ERROR,
-                        "%s", _("unable to find kvm-img or qemu-img"));
-
-    return ret;
-}
-
 static int qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm,
                                            virDomainSnapshotObjPtr snapshot,
                                            char *snapshotDir)
@@ -6474,6 +6464,7 @@ static int qemuDomainSnapshotDiscard(str
     int i;
     qemuDomainObjPrivatePtr priv;
     virDomainSnapshotObjPtr parentsnap;
+    virDomainTPMDefPtr tpm;
 
     if (!virDomainObjIsActive(vm)) {
         qemuimgarg[0] = qemuFindQemuImgBinary();
@@ -6504,6 +6495,24 @@ static int qemuDomainSnapshotDiscard(str
                 }
             }
         }
+
+        tpm = vm->def->tpm;
+        if (tpm) {
+            switch (tpm->type) {
+            case VIR_DOMAIN_TPM_TYPE_BUILTIN:
+                qemuimgarg[4] = virDomainTPMGetStorageFilename(tpm,
+                                                               vm->def->uuid);
+                if (virRun(qemuimgarg, NULL) < 0) {
+                    /* we continue on even in the face of error
+                     */
+                }
+                VIR_FREE(qemuimgarg[4]);
+            break;
+
+            case VIR_DOMAIN_TPM_TYPE_LAST:
+            break;
+            }
+        }
     }
     else {
         priv = vm->privateData;
Index: libvirt-acl/src/qemu/qemu_migration.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_migration.c
+++ libvirt-acl/src/qemu/qemu_migration.c
@@ -1090,6 +1090,7 @@ int qemuMigrationPerform(struct qemud_dr
                                      VIR_DOMAIN_EVENT_STOPPED_MIGRATED);
     if (!vm->persistent || (flags & VIR_MIGRATE_UNDEFINE_SOURCE)) {
         virDomainDeleteConfig(driver->configDir, driver->autostartDir, vm);
+        virDomainTPMDelete(vm, true);
         if (qemuDomainObjEndJob(vm) > 0)
             virDomainRemoveInactive(&driver->domains, vm);
         vm = NULL;
Index: libvirt-acl/src/qemu/qemu_capabilities.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_capabilities.c
+++ libvirt-acl/src/qemu/qemu_capabilities.c
@@ -40,6 +40,7 @@
 #include <sys/wait.h>
 #include <sys/utsname.h>
 #include <stdarg.h>
+#include <c-ctype.h>
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -808,6 +809,8 @@ qemuCapsComputeCmdFlags(const char *help
         qemuCapsSet(flags, QEMU_CAPS_XEN_DOMID);
     else if (strstr(help, "-domid"))
         qemuCapsSet(flags, QEMU_CAPS_DOMID);
+    if (strstr(help, "-tpm"))
+        qemuCapsSet(flags, QEMU_CAPS_TPM);
     if (strstr(help, "-drive")) {
         qemuCapsSet(flags, QEMU_CAPS_DRIVE);
         if (strstr(help, "cache=") &&
@@ -1238,6 +1241,57 @@ int qemuCapsExtractVersion(virCapsPtr ca
 }
 
 
+int qemuCapsGetTPMBuiltinBSSize(const char *qemu,
+                                unsigned int *bssize)
+{
+    int ret = -1;
+    char *output = NULL, *pos;
+    virCommandPtr cmd;
+
+    /* Make sure the binary we are about to try exec'ing exists.
+     * Technically we could catch the exec() failure, but that's
+     * in a sub-process so it's hard to feed back a useful error.
+     */
+    if (virFileIsExecutable(qemu) < 0) {
+        virReportSystemError(errno, _("Cannot find QEMU binary %s"), qemu);
+        return -1;
+    }
+
+    cmd = virCommandNewArgList(qemu, "-tpm", "?", NULL);
+    virCommandAddEnvPassCommon(cmd);
+    virCommandSetOutputBuffer(cmd, &output);
+    virCommandClearCaps(cmd);
+
+    if (virCommandRun(cmd, NULL) < 0)
+        goto cleanup;
+
+    pos = strstr(output, "builtin");
+    if (!pos)
+        goto cleanup;
+
+    while (1) {
+        if (*pos == '\n' || *pos == 0)
+            goto cleanup;
+
+        if (c_isdigit(*pos)) {
+            if (sscanf(pos, "%dkb", bssize) != 1)
+                goto cleanup;
+            else
+                break;
+        }
+        pos++;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(output);
+    virCommandFree(cmd);
+
+    return ret;
+}
+
+
 virBitmapPtr
 qemuCapsNew(void)
 {
Index: libvirt-acl/src/qemu/qemu_capabilities.h
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_capabilities.h
+++ libvirt-acl/src/qemu/qemu_capabilities.h
@@ -95,6 +95,7 @@ enum qemuCapsFlags {
     QEMU_CAPS_DEVICE_SPICEVMC   = 57, /* older -device spicevmc*/
     QEMU_CAPS_VIRTIO_TX_ALG     = 58, /* -device virtio-net-pci,tx=string */
     QEMU_CAPS_DEVICE_QXL_VGA    = 59, /* Is the primary and vga campatible qxl device named qxl-vga? */
+    QEMU_CAPS_TPM               = 60, /* if TPM (-tpm) support is available*/
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
@@ -141,5 +142,7 @@ int qemuCapsParseHelpStr(const char *qem
 int qemuCapsParseDeviceStr(const char *str,
                            virBitmapPtr qemuCaps);
 
+int qemuCapsGetTPMBuiltinBSSize(const char *qemu,
+                                unsigned int *size);
 
 #endif /* __QEMU_CAPABILITIES_H__*/

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]