qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: Eric Blake <eblake@redhat.com>,
	mst@redhat.com, qemu-devel@nongnu.org, imammedo@redhat.com
Cc: quan.xu@intel.com
Subject: Re: [Qemu-devel] [PATCH v3 1/6] Provide support for the CUSE TPM
Date: Tue, 26 May 2015 21:53:10 -0400	[thread overview]
Message-ID: <55652386.6010307@linux.vnet.ibm.com> (raw)
In-Reply-To: <5564FC54.6070604@redhat.com>

On 05/26/2015 07:05 PM, Eric Blake wrote:
> On 05/26/2015 03:33 PM, Stefan Berger wrote:
>> Rather than integrating TPM functionality into QEMU directly
>> using the TPM emulation of libtpms, we now integrate an external
>> emulated TPM device. This device is expected to implement a Linux
>> CUSE interface (CUSE = character device in userspace).
>>
>> QEMU talks to the CUSE TPM using much functionality of the
>> passthrough driver. For example, the TPM commands and responses
>> are sent to the CUSE TPM using the read()/write() interface.
>> However, some out-of-band control needs to be done using the CUSE
>> TPM's ioctl's. The CUSE TPM currently defines and implements 14
>> different ioctls for controlling certain life-cycle aspects of
>> the emulated TPM. The ioctls can be regarded as a replacement for
>> direct function calls to a TPM emulator if the TPM were to be
>> directly integrated into QEMU.
>>
>> One  of the ioctl's allows to get a bitmask of supported capabilities.
>> Each returned bit indicates which capabilties have been implemented.
> s/capabilties/capabilities/
>
>> An include file defining the various ioctls is added to QEMU.
>>
>> The CUSE TPM and associated tools can be found here:
>>
>> https://github.com/stefanberger/swtpm
>>
>>
>> To use the external CUSE TPM, the CUSE TPM should be started as follows:
>>
>> /usr/bin/swtpm_cuse -n vtpm-test
>>
>> QEMU can then be started using the following parameters:
>>
>> qemu-system-x86_64 \
>> 	[...] \
>>          -tpmdev cuse-tpm,id=tpm0,cancel-path=/dev/null,path=/dev/vtpm-test \
>>          -device tpm-tis,id=tpm0,tpmdev=tpm0 \
>> 	[...]
>>
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>> Cc: Eric Blake <eblake@redhat.com>
>> ---
> At this point, I'm only doing a high-level overview (public interface,
> blatant findings) and not a fine-grained reading of the implementation.


Thanks anyway.


>
>> diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h
>> new file mode 100644
>> index 0000000..d36e702
>> --- /dev/null
>> +++ b/hw/tpm/tpm_ioctl.h
>> @@ -0,0 +1,178 @@
>> +/*
>> + * tpm_ioctl.h
>> + *
>> + * This file is licensed under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either version 2.1 of
>> + * the License, or (at your option) any later version.
>> + */
> My understanding of copyleft (and insert the obligatory IANAL
> disclaimer) is that it works by exploiting the copyright law - that is,
> something cannot be [L]GPL unless there is also an assertion of
> copyright ownership (I don't care who, merely that a copyright claim
> exists somewhere in the file, nearby the license).

I added the copyright now, which was obviously missing.


>
>> +/*
>> + * Data structure to get state blobs from the TPM. If the size of the
>> + * blob exceeds the STATE_BLOB_SIZE, multiple reads with
>> + * adjusted offset are necessary. The last packet is indicated by
>> + * the length being smaller than the STATE_BLOB_SIZE.
> If the read size is exactly STATE_BLOB_SIZE, does that result in 1
> packet or 2?  Does it cause a 0-length packet to be attempted, or is it
> broken into STATE_BLOB_SIZE-1 and 1?


It would be 2 packets, the 2nd one having 0-length.


>
>> +
>> +/* state_flags above : */
>> +#define STATE_FLAG_DECRYPTED     1 /* on input:  get decrypted state */
>> +#define STATE_FLAG_ENCRYPTED     2 /* on output: state is encrytped */
> s/encrytped/encrypted/
>
>> +
>> +/*
>> + * Data structure to set state blobs in the TPM. If the size of the
>> + * blob exceeds the STATE_BLOB_SIZE, multiple 'writes' are necessary.
>> + * The last packet is indicated by the length being smaller than the
>> + * STATE_BLOB_SIZE.
>> + */
> Same question as on read about an exact STATE_BLOB_SIZE write

Same here. 2 packets.

>> +struct ptm_setstate {
>> +    union {
>> +        struct {
>> +            uint32_t state_flags; /* may be STATE_FLAG_ENCRYPTED */
>> +            uint32_t tpm_number;  /* always set to 0 */
>> +            uint8_t type;         /* which blob to set */
>> +            uint32_t length;
>> +            uint8_t data[STATE_BLOB_SIZE];
> This struct has padding blanks; is that going to matter?


The problem here could be a 64bit variable that would allign differently 
on a 32bit machine versus a 64bit machine or a 32bit executable running 
on a 64bit machine.At least there are no 64bit variables here, so it 
would be ok. However, we can still make the type member 32bit.


>
>> +typedef uint64_t ptmcap_t;
>> +typedef struct ptmest  ptmest_t;
>> +typedef struct ptmreset_est ptmreset_est_t;
>> +typedef struct ptmloc  ptmloc_t;
>> +typedef struct ptmhdata ptmhdata_t;
> Why a change in 1 vs. 2 spaces on some of the types?
>
> Technically, POSIX reserves the entire *_t namespace to itself, I'm a
> bit worried that by doing 'typedef struct foo foo_t' we are not being
> consistent with the rest of qemu, which does 'typedef struct foo foo'.


So remove the _t entirely?


>
>> +++ b/hw/tpm/tpm_passthrough.c
>> @@ -72,12 +74,18 @@ struct TPMPassthruState {
>>       bool had_startup_error;
>>   
>>       TPMVersion tpm_version;
>> +    ptmcap_t cuse_cap; /* capabilties of the CUSE TPM */
>> +    uint8_t cur_locty_number; /* last set locality */
> s/capabilties/capabilities/
>
>>   };
>>   
>>   typedef struct TPMPassthruState TPMPassthruState;
>>   
>>   #define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0"
>>   
>> +#define TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt) (tpm_pt->cuse_cap != 0)
>> +
>> +#define TPM_CUSE_IMPLEMENTS(tpm_tr, cap) ((tpm_pt->cuse_cap & cap) == cap)
> Evaluates cap more than once, which may not be ideal.  Also
> under-parenthesized in the face of arbitrary expressions for tpm_tr or cap.
>
> Umm, how does the macro argument tpm_tr get used, and where is the macro
> body tpm_pt scoped?
>
> Better might be this (depending on your intent):
> #define TPM_CUSE_IMPLEMENTS(tpm_tr, cap) \
>     (((tpm_tr)->cuse_cap & (cap)) != 0)
>
> if you know that cap will always be passed as one bit.  But if someone
> intends to use the macro to test multiple bits at once, and return true
> only if all of the bits are set, then living with multiple evaluation of
> 'cap' may be better.

The usage so far asks for whether a certain set of capabilities are 
_all_ implemented and for this the
evaluation above is good in call cases. I'll add the additional 
parenthesis, though.


>
>> +static int tpm_passthrough_set_locality(TPMPassthruState *tpm_pt,
>> +                                        uint8_t locty_number)
>> +{
>> +    int n;
>> +    ptmloc_t loc;
>> +
>> +    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
>> +        if (tpm_pt->cur_locty_number != locty_number) {
>> +            loc.u.req.loc = locty_number;
>> +            n = ioctl(tpm_pt->tpm_fd, PTM_SET_LOCALITY, &loc);
>> +            if (n < 0) {
>> +                error_report("tpm_cuse: could not set locality on "
>> +                             "CUSE TPM: %s (%i)",
>> +                             strerror(errno), errno);
> Hmm, I wonder if error_setg_errno() followed by error_report_err() is
> any nicer than manually calling strerror().  Probably not worth worrying
> about.
>
> On the other hand, this code is not strictly portable - passing both
> errno and strerror(errno) as arguments to a function has no sequencing
> point defined on whether errno is collected first or second; if it is
> collected second, strerror() may have clobbered errno.  Most code
> doesn't bother with printing "%s (%i)" for errors; the %s alone is
> sufficient.

Ok.

>
>
>>   /*
>> + * Gracefully shut down the external CUSE TPM
>> + */
>> +static void tpm_passthrough_shutdown(TPMPassthruState *tpm_pt)
>> +{
>> +    int n;
>> +    ptmres_t res;
>> +
>> +    if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) {
>> +        n = ioctl(tpm_pt->tpm_fd, PTM_SHUTDOWN, &res);
>> +        if (n < 0) {
>> +            error_report("tpm_cuse: Could not cleanly shut down "
>> +                         "the CUSE TPM: %s (%i)",
>> +                         strerror(errno), errno);
> Why not just 'if (ioctl(...) < 0) {' without needing 'n'?

Thought it was a coding style requirement .. but it isn't.


>
>> +        }
>> +    }
>> +}
>> +
>> +/*
>> + * Probe for the CUSE TPM by sending an ioctl() requesting its
>> + * capability flags.
>> + */
>> +static int tpm_passthrough_cuse_probe(TPMPassthruState *tpm_pt)
>> +{
>> +    int rc = 0;
>> +    int n;
>> +
>> +    n = ioctl(tpm_pt->tpm_fd, PTM_GET_CAPABILITY, &tpm_pt->cuse_cap);
>> +    if (n < 0) {
>> +        error_report("Error: CUSE TPM was requested, but probing failed.");
> Most qemu error messages intentionally do not end in period

Removed.

>
>> @@ -306,6 +472,8 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb)
>>   {
>>       TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
>>       int n;
>> +    ptmres_t res;
>> +    static int error_printed;
> You're using this as a bool...
>
>
>> +                } else if (res != TPM_SUCCESS) {
>> +                    if (!error_printed) {
>> +                        error_report("TPM error code from command "
>> +                                     "cancellation of CUSE TPM: 0x%x", res);
>> +                        error_printed = true;
>> +                    }
> ...so declare it as one.

Ok.


>
>
>> +++ b/qapi-schema.json
>> @@ -2974,10 +2974,11 @@
>>   # An enumeration of TPM types
>>   #
>>   # @passthrough: TPM passthrough type
>> +# @cuse-tpm: CUSE TPM type
> Missing '(since 2.4)' designator.
>

Definitely.

Thanks!

    Stefan

  reply	other threads:[~2015-05-27  1:53 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-26 21:33 [Qemu-devel] [PATCH v3 0/6] Extend TPM support with a QEMU-external TPM Stefan Berger
2015-05-26 21:33 ` [Qemu-devel] [PATCH v3 1/6] Provide support for the CUSE TPM Stefan Berger
2015-05-26 23:05   ` Eric Blake
2015-05-27  1:53     ` Stefan Berger [this message]
2015-05-26 21:33 ` [Qemu-devel] [PATCH v3 2/6] Introduce RAM location in vendor specific area in TIS Stefan Berger
2015-05-26 21:33 ` [Qemu-devel] [PATCH v3 3/6] Support Physical Presence Interface Spec Stefan Berger
2015-05-31 18:11   ` Michael S. Tsirkin
2015-06-02  3:11     ` Stefan Berger
2015-06-02  9:15       ` Michael S. Tsirkin
2015-06-02 13:22         ` Stefan Berger
2015-06-02 13:30           ` Michael S. Tsirkin
2015-06-02 14:28             ` Stefan Berger
2015-06-02 14:46               ` Michael S. Tsirkin
2015-06-02 15:06                 ` Stefan Berger
2015-06-02 15:11                   ` Michael S. Tsirkin
2015-06-02 16:28                     ` Stefan Berger
2015-06-02 15:18                 ` Kevin O'Connor
2015-06-02 16:18                   ` Stefan Berger
2015-06-02 15:00               ` Michael S. Tsirkin
2015-05-26 21:33 ` [Qemu-devel] [PATCH v3 4/6] Introduce condition to notifiy waiters of completed command Stefan Berger
2015-05-31 18:11   ` Michael S. Tsirkin
2015-05-26 21:33 ` [Qemu-devel] [PATCH v3 5/6] Introduce condition in TPM backend for notification Stefan Berger
2015-05-26 21:33 ` [Qemu-devel] [PATCH v3 6/6] Add support for VM suspend/resume for TPM TIS Stefan Berger
2015-05-31 18:11 ` [Qemu-devel] [PATCH v3 0/6] Extend TPM support with a QEMU-external TPM Michael S. Tsirkin
2015-06-02 13:17   ` Stefan Berger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=55652386.6010307@linux.vnet.ibm.com \
    --to=stefanb@linux.vnet.ibm.com \
    --cc=eblake@redhat.com \
    --cc=imammedo@redhat.com \
    --cc=mst@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quan.xu@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).