Message ID | 1427830813-639306-3-git-send-email-stefanb@linux.vnet.ibm.com |
---|---|
State | New |
Headers | show |
> -----Original Message----- > From: Stefan Berger [mailto:stefanb@linux.vnet.ibm.com] > Sent: Wednesday, April 01, 2015 3:40 AM > To: qemu-devel@nongnu.org; mst@redhat.com > Cc: Xu, Quan; Stefan Berger; Stefan Berger > Subject: [PATCH 2/3] tpm: Probe for connected TPM 1.2 or TPM 2 > > In the TPM passthrough backend driver, modify the probing code so that we can > check whether a TPM 1.2 or TPM 2 is being used and adapt the behavior of the > TPM TIS accordingly. > > Move the code that tested for a TPM 1.2 into tpm_utils.c and extend it with test > for probing for TPM 2. Have the function return the version of TPM found. > > Signed-off-by: Stefan Berger <stefanb@us.ibm.com> > --- > hw/tpm/Makefile.objs | 2 +- > hw/tpm/tpm_int.h | 6 +++ > hw/tpm/tpm_passthrough.c | 59 +++------------------- > hw/tpm/tpm_util.c | 126 > +++++++++++++++++++++++++++++++++++++++++++++++ > hw/tpm/tpm_util.h | 28 +++++++++++ > 5 files changed, 167 insertions(+), 54 deletions(-) create mode 100644 > hw/tpm/tpm_util.c create mode 100644 hw/tpm/tpm_util.h > > diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index > 99f5983..64cecc3 100644 > --- a/hw/tpm/Makefile.objs > +++ b/hw/tpm/Makefile.objs > @@ -1,2 +1,2 @@ > common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o > -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o > +common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o > tpm_util.o > diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h index 24e12ce..edab824 > 100644 > --- a/hw/tpm/tpm_int.h > +++ b/hw/tpm/tpm_int.h > @@ -66,4 +66,10 @@ struct tpm_resp_hdr { #define > TPM_ORD_ContinueSelfTest 0x53 > #define TPM_ORD_GetTicks 0xf1 > > + > +/* TPM2 defines */ > +#define TPM_ST_NO_SESSIONS 0x8001 > + > +#define TPM_CC_ReadClock 0x00000181 > + Could you define TPM2 macro definitions beginning with 'TPM2_*'? > #endif /* TPM_TPM_INT_H */ > diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index > dd769a7..2e65703 100644 > --- a/hw/tpm/tpm_passthrough.c > +++ b/hw/tpm/tpm_passthrough.c > @@ -33,6 +33,7 @@ > #include "hw/i386/pc.h" > #include "sysemu/tpm_backend_int.h" > #include "tpm_tis.h" > +#include "tpm_util.h" > > #define DEBUG_TPM 0 > > @@ -69,6 +70,8 @@ struct TPMPassthruState { > bool tpm_op_canceled; > int cancel_fd; > bool had_startup_error; > + > + enum TPMVersion tpm_version; > }; > > typedef struct TPMPassthruState TPMPassthruState; @@ -333,59 +336,9 @@ > static const char *tpm_passthrough_create_desc(void) > > static enum TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) > { > - return TPMVersion1_2; > -} > - > -/* > - * A basic test of a TPM device. We expect a well formatted response header > - * (error response is fine) within one second. > - */ > -static int tpm_passthrough_test_tpmdev(int fd) -{ > - struct tpm_req_hdr req = { > - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), > - .len = cpu_to_be32(sizeof(req)), > - .ordinal = cpu_to_be32(TPM_ORD_GetTicks), > - }; > - struct tpm_resp_hdr *resp; > - fd_set readfds; > - int n; > - struct timeval tv = { > - .tv_sec = 1, > - .tv_usec = 0, > - }; > - unsigned char buf[1024]; > - > - n = write(fd, &req, sizeof(req)); > - if (n < 0) { > - return errno; > - } > - if (n != sizeof(req)) { > - return EFAULT; > - } > - > - FD_ZERO(&readfds); > - FD_SET(fd, &readfds); > - > - /* wait for a second */ > - n = select(fd + 1, &readfds, NULL, NULL, &tv); > - if (n != 1) { > - return errno; > - } > - > - n = read(fd, &buf, sizeof(buf)); > - if (n < sizeof(struct tpm_resp_hdr)) { > - return EFAULT; > - } > - > - resp = (struct tpm_resp_hdr *)buf; > - /* check the header */ > - if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND || > - be32_to_cpu(resp->len) != n) { > - return EBADMSG; > - } > + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); > > - return 0; > + return tpm_pt->tpm_version; > } > > /* > @@ -455,7 +408,7 @@ static int > tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) > goto err_free_parameters; > } > > - if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { > + if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { > error_report("'%s' is not a TPM device.", > tpm_pt->tpm_dev); > goto err_close_tpmdev; > diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c new file mode 100644 index > 0000000..f9fb9c1 > --- /dev/null > +++ b/hw/tpm/tpm_util.c > @@ -0,0 +1,126 @@ > +/* > + * TPM utility functions > + * > + * Copyright (c) 2010 - 2015 IBM Corporation > + * Authors: > + * Stefan Berger <stefanb@us.ibm.com> > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see > +<http://www.gnu.org/licenses/> */ > + > +#include "tpm_util.h" > +#include "tpm_int.h" > + > +/* > + * A basic test of a TPM device. We expect a well formatted response > +header > + * (error response is fine) within one second. > + */ > +static int tpm_util_test(int fd, > + unsigned char *request, > + size_t requestlen, > + uint16_t *returnTag) { > + struct tpm_resp_hdr *resp; > + fd_set readfds; > + int n; > + struct timeval tv = { > + .tv_sec = 1, > + .tv_usec = 0, > + }; > + unsigned char buf[1024]; > + > + n = write(fd, request, requestlen); > + if (n < 0) { > + return errno; > + } > + if (n != requestlen) { > + return EFAULT; > + } > + > + FD_ZERO(&readfds); > + FD_SET(fd, &readfds); > + > + /* wait for a second */ > + n = select(fd + 1, &readfds, NULL, NULL, &tv); > + if (n != 1) { > + return errno; > + } > + > + n = read(fd, &buf, sizeof(buf)); > + if (n < sizeof(struct tpm_resp_hdr)) { > + return EFAULT; > + } > + > + resp = (struct tpm_resp_hdr *)buf; > + /* check the header */ > + if (be32_to_cpu(resp->len) != n) { > + return EBADMSG; > + } > + > + *returnTag = be16_to_cpu(resp->tag); > + > + return 0; > +} > + > +/* > + * Probe for the TPM device in the back > + * Returns 0 on success with the version of the probed TPM set, 1 on failure. > + */ > +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version) { > + /* > + * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 > + * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) > + * > + * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the > + * header. > + * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag > + * in the header and an error code. > + */ > + const struct tpm_req_hdr test_req = { > + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), > + .len = cpu_to_be32(sizeof(test_req)), > + .ordinal = cpu_to_be32(TPM_ORD_GetTicks), > + }; > + > + const struct tpm_req_hdr test_req_tpm2 = { > + .tag = cpu_to_be16(TPM_ST_NO_SESSIONS), > + .len = cpu_to_be32(sizeof(test_req_tpm2)), > + .ordinal = cpu_to_be32(TPM_CC_ReadClock), > + }; > + uint16_t returnTag; > + int ret; > + > + /* Send TPM 2 command */ > + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2, > + sizeof(test_req_tpm2), &returnTag); > + /* TPM 2 would respond with a tag of TPM_ST_NO_SESSIONS */ > + if (!ret && returnTag == TPM_ST_NO_SESSIONS) { > + *tpm_version = TPMVersion2_0; > + return 0; > + } > + > + /* Send TPM 1.2 command */ > + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req, > + sizeof(test_req), &returnTag); > + if (!ret && returnTag == TPM_TAG_RSP_COMMAND) { > + *tpm_version = TPMVersion1_2; > + /* this is a TPM 1.2 */ > + return 0; > + } > + > + *tpm_version = TPMVersion_Unspec; > + > + return 1; > +} In my opinion, I prefer to point out tpm_version in QEMU command line options, then tpm_util_test_tpmdev() tries to verify it. Intel Quan Xu > diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h new file mode 100644 index > 0000000..3ce25c8 > --- /dev/null > +++ b/hw/tpm/tpm_util.h > @@ -0,0 +1,28 @@ > +/* > + * TPM utility functions > + * > + * Copyright (c) 2010 - 2015 IBM Corporation > + * Authors: > + * Stefan Berger <stefanb@us.ibm.com> > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see > +<http://www.gnu.org/licenses/> */ #ifndef TPM_TPM_UTILS_H #define > +TPM_TPM_UTILS_H > + > +#include "sysemu/tpm_backend.h" > + > +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version); > + > +#endif /* TPM_TPM_UTILS_H */ > -- > 1.9.3
On 04/07/2015 04:54 AM, Xu, Quan wrote: > >> -----Original Message----- >> From: Stefan Berger [mailto:stefanb@linux.vnet.ibm.com] >> Sent: Wednesday, April 01, 2015 3:40 AM >> To: qemu-devel@nongnu.org; mst@redhat.com >> Cc: Xu, Quan; Stefan Berger; Stefan Berger >> Subject: [PATCH 2/3] tpm: Probe for connected TPM 1.2 or TPM 2 >> >> In the TPM passthrough backend driver, modify the probing code so that we can >> check whether a TPM 1.2 or TPM 2 is being used and adapt the behavior of the >> TPM TIS accordingly. >> >> Move the code that tested for a TPM 1.2 into tpm_utils.c and extend it with test >> for probing for TPM 2. Have the function return the version of TPM found. >> >> Signed-off-by: Stefan Berger <stefanb@us.ibm.com> >> --- >> hw/tpm/Makefile.objs | 2 +- >> hw/tpm/tpm_int.h | 6 +++ >> hw/tpm/tpm_passthrough.c | 59 +++------------------- >> hw/tpm/tpm_util.c | 126 >> +++++++++++++++++++++++++++++++++++++++++++++++ >> hw/tpm/tpm_util.h | 28 +++++++++++ >> 5 files changed, 167 insertions(+), 54 deletions(-) create mode 100644 >> hw/tpm/tpm_util.c create mode 100644 hw/tpm/tpm_util.h >> >> diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index >> 99f5983..64cecc3 100644 >> --- a/hw/tpm/Makefile.objs >> +++ b/hw/tpm/Makefile.objs >> @@ -1,2 +1,2 @@ >> common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o >> -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o >> +common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o >> tpm_util.o >> diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h index 24e12ce..edab824 >> 100644 >> --- a/hw/tpm/tpm_int.h >> +++ b/hw/tpm/tpm_int.h >> @@ -66,4 +66,10 @@ struct tpm_resp_hdr { #define >> TPM_ORD_ContinueSelfTest 0x53 >> #define TPM_ORD_GetTicks 0xf1 >> >> + >> +/* TPM2 defines */ >> +#define TPM_ST_NO_SESSIONS 0x8001 >> + >> +#define TPM_CC_ReadClock 0x00000181 >> + > Could you define TPM2 macro definitions beginning with 'TPM2_*'? Ok, will do. [...] > +/* > + * Probe for the TPM device in the back > + * Returns 0 on success with the version of the probed TPM set, 1 on failure. > + */ > +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version) { > + /* > + * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 > + * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) > + * > + * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the > + * header. > + * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag > + * in the header and an error code. > + */ > + const struct tpm_req_hdr test_req = { > + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), > + .len = cpu_to_be32(sizeof(test_req)), > + .ordinal = cpu_to_be32(TPM_ORD_GetTicks), > + }; > + > + const struct tpm_req_hdr test_req_tpm2 = { > + .tag = cpu_to_be16(TPM_ST_NO_SESSIONS), > + .len = cpu_to_be32(sizeof(test_req_tpm2)), > + .ordinal = cpu_to_be32(TPM_CC_ReadClock), > + }; > + uint16_t returnTag; > + int ret; > + > + /* Send TPM 2 command */ > + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2, > + sizeof(test_req_tpm2), &returnTag); > + /* TPM 2 would respond with a tag of TPM_ST_NO_SESSIONS */ > + if (!ret && returnTag == TPM_ST_NO_SESSIONS) { > + *tpm_version = TPMVersion2_0; > + return 0; > + } > + > + /* Send TPM 1.2 command */ > + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req, > + sizeof(test_req), &returnTag); > + if (!ret && returnTag == TPM_TAG_RSP_COMMAND) { > + *tpm_version = TPMVersion1_2; > + /* this is a TPM 1.2 */ > + return 0; > + } > + > + *tpm_version = TPMVersion_Unspec; > + > + return 1; > +} > > In my opinion, I prefer to point out tpm_version in QEMU command line options, then > tpm_util_test_tpmdev() tries to verify it. The only reason why I am not doing this was that libvirt for example will need to probe for whether the additional parameter indicating the TPM version is supported. Besides that I thought it should be possible to probe on any platform and get a reliable result. Maybe Eric has a comment. I have recently seen a discussion where an additional parameter to an existing option was to be added, but cannot remember which option that was. Stefan
On 04/12/2015 02:59 PM, Stefan Berger wrote: > On 04/07/2015 04:54 AM, Xu, Quan wrote: >> >> In my opinion, I prefer to point out tpm_version in QEMU command line >> options, then >> tpm_util_test_tpmdev() tries to verify it. > > The only reason why I am not doing this was that libvirt for example > will need to probe for whether the additional parameter indicating the > TPM version is supported. Besides that I thought it should be possible > to probe on any platform and get a reliable result. > > Maybe Eric has a comment. I have recently seen a discussion where an > additional parameter to an existing option was to be added, but cannot > remember which option that was. > Hopefully, we are going to get introspection working for 2.4; at which point, libvirt can use that to query what options are new to any other command. Meanwhile, doesn't query-tpm-models already serve as a way to query additions on this front?
Eric Blake <eblake@redhat.com> wrote on 04/13/2015 10:43:40 AM: > From: Eric Blake <eblake@redhat.com> > To: Stefan Berger <stefanb@linux.vnet.ibm.com>, "Xu, Quan" > <quan.xu@intel.com>, "qemu-devel@nongnu.org" <qemu- > devel@nongnu.org>, "mst@redhat.com" <mst@redhat.com> > Cc: Stefan Berger/Watson/IBM@IBMUS > Date: 04/13/2015 10:43 AM > Subject: Re: [Qemu-devel] [PATCH 2/3] tpm: Probe for connected TPM > 1.2 or TPM 2 > > On 04/12/2015 02:59 PM, Stefan Berger wrote: > > On 04/07/2015 04:54 AM, Xu, Quan wrote: > >> > > >> In my opinion, I prefer to point out tpm_version in QEMU command line > >> options, then > >> tpm_util_test_tpmdev() tries to verify it. > > > > The only reason why I am not doing this was that libvirt for example > > will need to probe for whether the additional parameter indicating the > > TPM version is supported. Besides that I thought it should be possible > > to probe on any platform and get a reliable result. > > > > Maybe Eric has a comment. I have recently seen a discussion where an > > additional parameter to an existing option was to be added, but cannot > > remember which option that was. > > > > Hopefully, we are going to get introspection working for 2.4; at which > point, libvirt can use that to query what options are new to any other > command. Meanwhile, doesn't query-tpm-models already serve as a way to > query additions on this front? It would be a new command line option parameter tpm-version (or similar) introduced in 2.4: -tpmdev passthrough,id=tpm0,...,tpm-version=2 \ -device tpm-tis,id=tpm0,tpmdev=tpm0 \ It would also be reflected through an extensions of the existing TPMPassthroughOptions in qapi-schema.json. { 'type': 'TPMPassthroughOptions', 'data': { '*path' : 'str', '*cancel-path' : 'str', '*tpm-version' : 'str'} } Stefan
On Tue, Mar 31, 2015 at 03:40:12PM -0400, Stefan Berger wrote: > In the TPM passthrough backend driver, modify the probing code so > that we can check whether a TPM 1.2 or TPM 2 is being used > and adapt the behavior of the TPM TIS accordingly. > > Move the code that tested for a TPM 1.2 into tpm_utils.c > and extend it with test for probing for TPM 2. Have the > function return the version of TPM found. > > Signed-off-by: Stefan Berger <stefanb@us.ibm.com> > --- > hw/tpm/Makefile.objs | 2 +- > hw/tpm/tpm_int.h | 6 +++ > hw/tpm/tpm_passthrough.c | 59 +++------------------- > hw/tpm/tpm_util.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++ > hw/tpm/tpm_util.h | 28 +++++++++++ > 5 files changed, 167 insertions(+), 54 deletions(-) > create mode 100644 hw/tpm/tpm_util.c > create mode 100644 hw/tpm/tpm_util.h > > diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs > index 99f5983..64cecc3 100644 > --- a/hw/tpm/Makefile.objs > +++ b/hw/tpm/Makefile.objs > @@ -1,2 +1,2 @@ > common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o > -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o > +common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o > diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h > index 24e12ce..edab824 100644 > --- a/hw/tpm/tpm_int.h > +++ b/hw/tpm/tpm_int.h > @@ -66,4 +66,10 @@ struct tpm_resp_hdr { > #define TPM_ORD_ContinueSelfTest 0x53 > #define TPM_ORD_GetTicks 0xf1 > > + > +/* TPM2 defines */ > +#define TPM_ST_NO_SESSIONS 0x8001 > + > +#define TPM_CC_ReadClock 0x00000181 > + > #endif /* TPM_TPM_INT_H */ > diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c > index dd769a7..2e65703 100644 > --- a/hw/tpm/tpm_passthrough.c > +++ b/hw/tpm/tpm_passthrough.c > @@ -33,6 +33,7 @@ > #include "hw/i386/pc.h" > #include "sysemu/tpm_backend_int.h" > #include "tpm_tis.h" > +#include "tpm_util.h" > > #define DEBUG_TPM 0 > > @@ -69,6 +70,8 @@ struct TPMPassthruState { > bool tpm_op_canceled; > int cancel_fd; > bool had_startup_error; > + > + enum TPMVersion tpm_version; Use a typedef please, without enum. > }; > > typedef struct TPMPassthruState TPMPassthruState; > @@ -333,59 +336,9 @@ static const char *tpm_passthrough_create_desc(void) > > static enum TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) > { > - return TPMVersion1_2; > -} > - > -/* > - * A basic test of a TPM device. We expect a well formatted response header > - * (error response is fine) within one second. > - */ > -static int tpm_passthrough_test_tpmdev(int fd) > -{ > - struct tpm_req_hdr req = { > - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), > - .len = cpu_to_be32(sizeof(req)), > - .ordinal = cpu_to_be32(TPM_ORD_GetTicks), > - }; > - struct tpm_resp_hdr *resp; > - fd_set readfds; > - int n; > - struct timeval tv = { > - .tv_sec = 1, > - .tv_usec = 0, > - }; > - unsigned char buf[1024]; > - > - n = write(fd, &req, sizeof(req)); > - if (n < 0) { > - return errno; > - } > - if (n != sizeof(req)) { > - return EFAULT; > - } > - > - FD_ZERO(&readfds); > - FD_SET(fd, &readfds); > - > - /* wait for a second */ > - n = select(fd + 1, &readfds, NULL, NULL, &tv); > - if (n != 1) { > - return errno; > - } > - > - n = read(fd, &buf, sizeof(buf)); > - if (n < sizeof(struct tpm_resp_hdr)) { > - return EFAULT; > - } > - > - resp = (struct tpm_resp_hdr *)buf; > - /* check the header */ > - if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND || > - be32_to_cpu(resp->len) != n) { > - return EBADMSG; > - } > + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); > > - return 0; > + return tpm_pt->tpm_version; > } > > /* > @@ -455,7 +408,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) > goto err_free_parameters; > } > > - if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { > + if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { > error_report("'%s' is not a TPM device.", > tpm_pt->tpm_dev); > goto err_close_tpmdev; > diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c > new file mode 100644 > index 0000000..f9fb9c1 > --- /dev/null > +++ b/hw/tpm/tpm_util.c > @@ -0,0 +1,126 @@ > +/* > + * TPM utility functions > + * > + * Copyright (c) 2010 - 2015 IBM Corporation > + * Authors: > + * Stefan Berger <stefanb@us.ibm.com> > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > + > +#include "tpm_util.h" > +#include "tpm_int.h" > + > +/* > + * A basic test of a TPM device. We expect a well formatted response header > + * (error response is fine) within one second. > + */ > +static int tpm_util_test(int fd, > + unsigned char *request, > + size_t requestlen, > + uint16_t *returnTag) > +{ > + struct tpm_resp_hdr *resp; > + fd_set readfds; > + int n; > + struct timeval tv = { > + .tv_sec = 1, > + .tv_usec = 0, > + }; > + unsigned char buf[1024]; > + > + n = write(fd, request, requestlen); > + if (n < 0) { > + return errno; > + } > + if (n != requestlen) { > + return EFAULT; > + } > + > + FD_ZERO(&readfds); > + FD_SET(fd, &readfds); > + > + /* wait for a second */ > + n = select(fd + 1, &readfds, NULL, NULL, &tv); > + if (n != 1) { > + return errno; > + } > + > + n = read(fd, &buf, sizeof(buf)); > + if (n < sizeof(struct tpm_resp_hdr)) { > + return EFAULT; > + } > + > + resp = (struct tpm_resp_hdr *)buf; > + /* check the header */ > + if (be32_to_cpu(resp->len) != n) { > + return EBADMSG; > + } > + > + *returnTag = be16_to_cpu(resp->tag); > + > + return 0; > +} > + > +/* > + * Probe for the TPM device in the back > + * Returns 0 on success with the version of the probed TPM set, 1 on failure. > + */ > +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version) > +{ > + /* > + * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 > + * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) > + * > + * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the > + * header. > + * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag > + * in the header and an error code. > + */ > + const struct tpm_req_hdr test_req = { > + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), > + .len = cpu_to_be32(sizeof(test_req)), > + .ordinal = cpu_to_be32(TPM_ORD_GetTicks), > + }; > + > + const struct tpm_req_hdr test_req_tpm2 = { > + .tag = cpu_to_be16(TPM_ST_NO_SESSIONS), > + .len = cpu_to_be32(sizeof(test_req_tpm2)), > + .ordinal = cpu_to_be32(TPM_CC_ReadClock), > + }; > + uint16_t returnTag; use lower case for vars pls > + int ret; > + > + /* Send TPM 2 command */ > + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2, > + sizeof(test_req_tpm2), &returnTag); > + /* TPM 2 would respond with a tag of TPM_ST_NO_SESSIONS */ > + if (!ret && returnTag == TPM_ST_NO_SESSIONS) { > + *tpm_version = TPMVersion2_0; > + return 0; > + } > + > + /* Send TPM 1.2 command */ > + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req, > + sizeof(test_req), &returnTag); > + if (!ret && returnTag == TPM_TAG_RSP_COMMAND) { > + *tpm_version = TPMVersion1_2; > + /* this is a TPM 1.2 */ > + return 0; > + } > + > + *tpm_version = TPMVersion_Unspec; > + > + return 1; > +} > diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h > new file mode 100644 > index 0000000..3ce25c8 > --- /dev/null > +++ b/hw/tpm/tpm_util.h > @@ -0,0 +1,28 @@ > +/* > + * TPM utility functions > + * > + * Copyright (c) 2010 - 2015 IBM Corporation > + * Authors: > + * Stefan Berger <stefanb@us.ibm.com> > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with this library; if not, see <http://www.gnu.org/licenses/> > + */ > +#ifndef TPM_TPM_UTILS_H > +#define TPM_TPM_UTILS_H > + > +#include "sysemu/tpm_backend.h" > + > +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version); > + > +#endif /* TPM_TPM_UTILS_H */ > -- > 1.9.3
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 99f5983..64cecc3 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,2 +1,2 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o -common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o +common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o diff --git a/hw/tpm/tpm_int.h b/hw/tpm/tpm_int.h index 24e12ce..edab824 100644 --- a/hw/tpm/tpm_int.h +++ b/hw/tpm/tpm_int.h @@ -66,4 +66,10 @@ struct tpm_resp_hdr { #define TPM_ORD_ContinueSelfTest 0x53 #define TPM_ORD_GetTicks 0xf1 + +/* TPM2 defines */ +#define TPM_ST_NO_SESSIONS 0x8001 + +#define TPM_CC_ReadClock 0x00000181 + #endif /* TPM_TPM_INT_H */ diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index dd769a7..2e65703 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -33,6 +33,7 @@ #include "hw/i386/pc.h" #include "sysemu/tpm_backend_int.h" #include "tpm_tis.h" +#include "tpm_util.h" #define DEBUG_TPM 0 @@ -69,6 +70,8 @@ struct TPMPassthruState { bool tpm_op_canceled; int cancel_fd; bool had_startup_error; + + enum TPMVersion tpm_version; }; typedef struct TPMPassthruState TPMPassthruState; @@ -333,59 +336,9 @@ static const char *tpm_passthrough_create_desc(void) static enum TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) { - return TPMVersion1_2; -} - -/* - * A basic test of a TPM device. We expect a well formatted response header - * (error response is fine) within one second. - */ -static int tpm_passthrough_test_tpmdev(int fd) -{ - struct tpm_req_hdr req = { - .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), - .len = cpu_to_be32(sizeof(req)), - .ordinal = cpu_to_be32(TPM_ORD_GetTicks), - }; - struct tpm_resp_hdr *resp; - fd_set readfds; - int n; - struct timeval tv = { - .tv_sec = 1, - .tv_usec = 0, - }; - unsigned char buf[1024]; - - n = write(fd, &req, sizeof(req)); - if (n < 0) { - return errno; - } - if (n != sizeof(req)) { - return EFAULT; - } - - FD_ZERO(&readfds); - FD_SET(fd, &readfds); - - /* wait for a second */ - n = select(fd + 1, &readfds, NULL, NULL, &tv); - if (n != 1) { - return errno; - } - - n = read(fd, &buf, sizeof(buf)); - if (n < sizeof(struct tpm_resp_hdr)) { - return EFAULT; - } - - resp = (struct tpm_resp_hdr *)buf; - /* check the header */ - if (be16_to_cpu(resp->tag) != TPM_TAG_RSP_COMMAND || - be32_to_cpu(resp->len) != n) { - return EBADMSG; - } + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - return 0; + return tpm_pt->tpm_version; } /* @@ -455,7 +408,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) goto err_free_parameters; } - if (tpm_passthrough_test_tpmdev(tpm_pt->tpm_fd)) { + if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { error_report("'%s' is not a TPM device.", tpm_pt->tpm_dev); goto err_close_tpmdev; diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c new file mode 100644 index 0000000..f9fb9c1 --- /dev/null +++ b/hw/tpm/tpm_util.c @@ -0,0 +1,126 @@ +/* + * TPM utility functions + * + * Copyright (c) 2010 - 2015 IBM Corporation + * Authors: + * Stefan Berger <stefanb@us.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#include "tpm_util.h" +#include "tpm_int.h" + +/* + * A basic test of a TPM device. We expect a well formatted response header + * (error response is fine) within one second. + */ +static int tpm_util_test(int fd, + unsigned char *request, + size_t requestlen, + uint16_t *returnTag) +{ + struct tpm_resp_hdr *resp; + fd_set readfds; + int n; + struct timeval tv = { + .tv_sec = 1, + .tv_usec = 0, + }; + unsigned char buf[1024]; + + n = write(fd, request, requestlen); + if (n < 0) { + return errno; + } + if (n != requestlen) { + return EFAULT; + } + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + /* wait for a second */ + n = select(fd + 1, &readfds, NULL, NULL, &tv); + if (n != 1) { + return errno; + } + + n = read(fd, &buf, sizeof(buf)); + if (n < sizeof(struct tpm_resp_hdr)) { + return EFAULT; + } + + resp = (struct tpm_resp_hdr *)buf; + /* check the header */ + if (be32_to_cpu(resp->len) != n) { + return EBADMSG; + } + + *returnTag = be16_to_cpu(resp->tag); + + return 0; +} + +/* + * Probe for the TPM device in the back + * Returns 0 on success with the version of the probed TPM set, 1 on failure. + */ +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version) +{ + /* + * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 + * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) + * + * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the + * header. + * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag + * in the header and an error code. + */ + const struct tpm_req_hdr test_req = { + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .len = cpu_to_be32(sizeof(test_req)), + .ordinal = cpu_to_be32(TPM_ORD_GetTicks), + }; + + const struct tpm_req_hdr test_req_tpm2 = { + .tag = cpu_to_be16(TPM_ST_NO_SESSIONS), + .len = cpu_to_be32(sizeof(test_req_tpm2)), + .ordinal = cpu_to_be32(TPM_CC_ReadClock), + }; + uint16_t returnTag; + int ret; + + /* Send TPM 2 command */ + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req_tpm2, + sizeof(test_req_tpm2), &returnTag); + /* TPM 2 would respond with a tag of TPM_ST_NO_SESSIONS */ + if (!ret && returnTag == TPM_ST_NO_SESSIONS) { + *tpm_version = TPMVersion2_0; + return 0; + } + + /* Send TPM 1.2 command */ + ret = tpm_util_test(tpm_fd, (unsigned char *)&test_req, + sizeof(test_req), &returnTag); + if (!ret && returnTag == TPM_TAG_RSP_COMMAND) { + *tpm_version = TPMVersion1_2; + /* this is a TPM 1.2 */ + return 0; + } + + *tpm_version = TPMVersion_Unspec; + + return 1; +} diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h new file mode 100644 index 0000000..3ce25c8 --- /dev/null +++ b/hw/tpm/tpm_util.h @@ -0,0 +1,28 @@ +/* + * TPM utility functions + * + * Copyright (c) 2010 - 2015 IBM Corporation + * Authors: + * Stefan Berger <stefanb@us.ibm.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ +#ifndef TPM_TPM_UTILS_H +#define TPM_TPM_UTILS_H + +#include "sysemu/tpm_backend.h" + +int tpm_util_test_tpmdev(int tpm_fd, enum TPMVersion *tpm_version); + +#endif /* TPM_TPM_UTILS_H */
In the TPM passthrough backend driver, modify the probing code so that we can check whether a TPM 1.2 or TPM 2 is being used and adapt the behavior of the TPM TIS accordingly. Move the code that tested for a TPM 1.2 into tpm_utils.c and extend it with test for probing for TPM 2. Have the function return the version of TPM found. Signed-off-by: Stefan Berger <stefanb@us.ibm.com> --- hw/tpm/Makefile.objs | 2 +- hw/tpm/tpm_int.h | 6 +++ hw/tpm/tpm_passthrough.c | 59 +++------------------- hw/tpm/tpm_util.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++ hw/tpm/tpm_util.h | 28 +++++++++++ 5 files changed, 167 insertions(+), 54 deletions(-) create mode 100644 hw/tpm/tpm_util.c create mode 100644 hw/tpm/tpm_util.h