[libvirt] [PATCH V1 2/6] Parse TPM in domain XML

Daniel P. Berrange berrange at redhat.com
Thu Mar 14 14:25:39 UTC 2013


On Wed, Mar 13, 2013 at 12:03:50PM -0400, Stefan Berger wrote:
> Parse the domain XML with TPM support.
> 
> Convert the strings from QEMU's QMP TPM commands into
> capability flags.
> 
> Signed-off-by: Stefan Berger <stefanb at linux.vnet.ibm.com>
> 
> ---
>  docs/schemas/domaincommon.rng |   43 ++++++
>  src/conf/domain_conf.c        |  268 ++++++++++++++++++++++++++++++++++++++++++
>  src/conf/domain_conf.h        |   34 +++++
>  src/libvirt_private.syms      |    5 
>  src/qemu/qemu_capabilities.c  |   59 +++++++++
>  5 files changed, 409 insertions(+)

The QEMU changes should be a separate patch from any XML schema
changes.

> @@ -1515,6 +1522,24 @@ void virDomainHostdevDefClear(virDomainH
>      }
>  }
>  
> +void virDomainTPMDefFree(virDomainTPMDefPtr def)
> +{
> +    if (!def)
> +        return;
> +
> +    switch (def->type) {
> +    case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
> +        VIR_FREE(def->data.passthrough.path);
> +        VIR_FREE(def->data.passthrough.cancel_path);
> +        break;
> +    case VIR_DOMAIN_TPM_TYPE_LAST:
> +        break;
> +    }
> +
> +    virDomainDeviceInfoClear(&def->info);
> +    VIR_FREE(def);
> +}
> +
>  void virDomainHostdevDefFree(virDomainHostdevDefPtr def)
>  {
>      if (!def)
> @@ -1776,6 +1801,8 @@ void virDomainDefFree(virDomainDefPtr de
>  
>      virDomainRNGDefFree(def->rng);
>  
> +    virDomainTPMDefFree(def->tpm);
> +
>      VIR_FREE(def->os.type);
>      VIR_FREE(def->os.machine);
>      VIR_FREE(def->os.init);
> @@ -6312,6 +6339,192 @@ error:
>      goto cleanup;
>  }
>  
> +/*
> + * Check whether the given base path, e.g.,  /sys/class/misc/tpm0/device,
> + * is the sysfs entry of a TPM. A TPM sysfs entry should be uniquely
> + * recognizable by the file entries 'pcrs' and 'cancel'.
> + * Upon success 'true' is returned and the basebath buffer has '/cancel'
> + * appended.
> + */
> +static bool
> +virDomainTPMCheckSysfsCancel(char *basepath, size_t bufsz)
> +{
> +    char *path = NULL;
> +    struct stat statbuf;
> +
> +    if (virAsprintf(&path, "%s/pcrs", basepath) < 0) {
> +        virReportOOMError();
> +        goto error;
> +    }
> +    if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode))
> +        goto error;
> +
> +    VIR_FREE(path);
> +
> +    if (virAsprintf(&path, "%s/cancel", basepath) < 0) {
> +        virReportOOMError();
> +        goto error;
> +    }
> +
> +    if (stat(path, &statbuf) == -1 || !S_ISREG(statbuf.st_mode))
> +        goto error;
> +
> +    if (!virStrncpy(basepath, path, strlen(path) + 1, bufsz)) {
> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> +                       _("Basepath buffer is too small"));
> +        goto error;
> +    }
> +
> +    VIR_FREE(path);
> +
> +    return true;
> +
> +error:
> +    VIR_FREE(path);
> +    return false;
> +}
> +
> +
> +static char *
> +virDomainTPMFindCancelPath(void)
> +{
> +    unsigned int idx;
> +    int len;
> +    DIR *pnp_dir;
> +    char path[100], *p;
> +    struct dirent entry, *result;
> +    bool found = false;
> +
> +    snprintf(path, sizeof(path), "/sys/class/misc");
> +    pnp_dir = opendir(path);
> +    if (pnp_dir != NULL) {
> +        while (readdir_r(pnp_dir, &entry, &result) == 0 &&
> +               result != NULL) {
> +            if (sscanf(entry.d_name, "tpm%u%n", &idx, &len) < 1 ||
> +                len <= strlen("tpm") ||
> +                len != strlen(entry.d_name)) {
> +                continue;
> +            }
> +            snprintf(path, sizeof(path), "/sys/class/misc/%s/device",
> +                     entry.d_name);
> +            if (!virDomainTPMCheckSysfsCancel(path, sizeof(path))) {
> +                continue;
> +            }
> +
> +            found = true;
> +            break;
> +        }
> +        closedir(pnp_dir);
> +    }
> +
> +    if (found) {
> +        if (!(p = strdup(path)))
> +            virReportOOMError();
> +        return p;
> +    }
> +
> +    return NULL;
> +}

These two funtions have nothing todo with the XML schema so should
not be in this file. Either some helper file in src/util or in the
QEMU code.

> +    switch (def->type) {
> +    case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
> +        path = virXPathString("string(./backend/device/@path)", ctxt);
> +        if (!path && !(path = strdup(VIR_DOMAIN_TPM_DEFAULT_DEVICE))) {
> +            virReportOOMError();
> +            goto error;
> +        }
> +        def->data.passthrough.path = path;
> +        path = NULL;
> +        /* cancel_path is read-only */
> +        def->data.passthrough.cancel_path = virDomainTPMFindCancelPath();

This data is driver specific state so should not be in the
XML schema structs - it should be in QEMU code where needed

> +# define VIR_DOMAIN_TPM_DEFAULT_DEVICE "/dev/tpm0"
> +
> +typedef struct _virDomainTPMDef virDomainTPMDef;
> +typedef virDomainTPMDef *virDomainTPMDefPtr;
> +struct _virDomainTPMDef {
> +    enum virDomainTPMBackendType type;
> +    virDomainDeviceInfo info;
> +    enum virDomainTPMModel model;
> +    union {
> +        struct {
> +            char *path;
> +            char *cancel_path;

Per previous comments 'cancel_path' should not be in this struct.

> +        } passthrough;
> +    } data;
> +};
> +

> Index: libvirt/src/qemu/qemu_capabilities.c
> ===================================================================
> --- libvirt.orig/src/qemu/qemu_capabilities.c
> +++ libvirt/src/qemu/qemu_capabilities.c
> @@ -2110,6 +2110,63 @@ virQEMUCapsProbeQMPCPUDefinitions(virQEM
>  
>  
>  static int
> +virQEMUCapsProbeQMPTPM(virQEMUCapsPtr qemuCaps,
> +                       qemuMonitorPtr mon)
> +{
> +    int nentries, i;
> +    char **entries = NULL;
> +    struct typeToCaps {
> +        int type;
> +        enum virQEMUCapsFlags caps;
> +    };
> +    const struct typeToCaps tpmTypesToCaps[] = {
> +        {
> +            .type = VIR_DOMAIN_TPM_TYPE_PASSTHROUGH,
> +            .caps = QEMU_CAPS_DEVICE_TPM_PASSTHROUGH,
> +        },
> +    };
> +    const struct typeToCaps tpmModelsToCaps[] = {
> +        {
> +            .type = VIR_DOMAIN_TPM_MODEL_TIS,
> +            .caps = QEMU_CAPS_DEVICE_TPM_TIS,
> +        },
> +    };
> +
> +    if ((nentries = qemuMonitorGetTPMModels(mon, &entries)) < 0)
> +        return -1;
> +
> +    if (nentries > 0) {
> +        for (i = 0; i < ARRAY_CARDINALITY(tpmModelsToCaps); i++) {
> +            const char *needle = virDomainTPMModelTypeToString(
> +                tpmModelsToCaps[i].type);
> +            if (virStrArrayHasString(entries, nentries, needle))
> +                virQEMUCapsSet(qemuCaps, tpmModelsToCaps[i].caps);
> +        }
> +        for (i = 0; i < nentries; i++)
> +            VIR_FREE(entries[i]);

If you make the monitor commands return a NULL terminated list,
you can just use virStringFreeList. This also avoids the need
to pass around 'nentries'; to virStrArrayHasString.


Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list