[Skiboot] [PATCH 10/15] libstb: Add tpm interface
Stewart Smith
stewart at linux.vnet.ibm.com
Tue Sep 20 17:43:36 AEST 2016
Claudio Carvalho <cclaudio at linux.vnet.ibm.com> writes:
> This adds the TPM interface for libstb:
>
> - tpm_init(): load drivers compatible with the tpm devices.
>
> - tpm_extendl(): record and event to the event log and extend the pcr of
> both sha1 and sha256 banks.
>
> The tpm interface is documented in 'libstb/tpm.h' and the tpm device
> tree node is documented in 'doc/device-tree/tpm.txt'
>
> Signed-off-by: Claudio Carvalho <cclaudio at linux.vnet.ibm.com>
> ---
> libstb/Makefile.inc | 2 +-
> libstb/status_codes.h | 27 +++++
> libstb/tpm.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++
> libstb/tpm.h | 43 +++++++
> 4 files changed, 375 insertions(+), 1 deletion(-)
> create mode 100644 libstb/status_codes.h
> create mode 100644 libstb/tpm.c
>
> diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc
> index 642269f..4cffd54 100644
> --- a/libstb/Makefile.inc
> +++ b/libstb/Makefile.inc
> @@ -4,7 +4,7 @@ LIBSTB_DIR = libstb
>
> SUBDIRS += $(LIBSTB_DIR)
>
> -LIBSTB_SRCS = container.c
> +LIBSTB_SRCS = container.c tpm.c
> LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o)
> LIBSTB = $(LIBSTB_DIR)/built-in.o
>
> diff --git a/libstb/status_codes.h b/libstb/status_codes.h
> new file mode 100644
> index 0000000..3a742c3
> --- /dev/null
> +++ b/libstb/status_codes.h
> @@ -0,0 +1,27 @@
> +/* Copyright 2013-2016 IBM Corp.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> + * implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef __STB_STATUS_CODES_H
> +#define __STB_STATUS_CODES_H
> +
> +/* general return codes */
> +#define STB_SUCCESS 0
We have TB_SUCCESS in tss and STB_SUCCESS here. The former is okay as
that's wrapping code from HB (even though it doesn't technically need it
unless you're a real stickler for the semantic difference between zero
and null). The latter, is it really needed? Zero as success is pretty
much assumed, and if we want semantic meaning rather than just an
integer, we should likely use an enum.
> +
> +/* trusted boot */
> +#define STB_EVENTLOG_FAILED -200
> +#define STB_PCR_EXTEND_FAILED -201
> +
> +#endif /* __STB_STATUS_CODES_H */
> diff --git a/libstb/tpm.c b/libstb/tpm.c
> new file mode 100644
> index 0000000..26b411b
> --- /dev/null
> +++ b/libstb/tpm.c
> @@ -0,0 +1,304 @@
> +/* Copyright 2013-2016 IBM Corp.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> + * implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <skiboot.h>
> +#include <device.h>
> +#include <string.h>
> +
> +#include "status_codes.h"
> +#include "container.h"
> +#include "tss/trustedbootCmds.H"
> +#include "tpm.h"
> +
> +/* For debugging only */
> +//#define STB_DEBUG
> +
> +enum tpm_chip_id {
> + TPM_MASTER,
> + TPM_BACKUP,
> + MAX_TPM_CHIP_ID
> +};
> +
> +static const char *tpm_label[MAX_TPM_CHIP_ID] = {
> + "tpm",
> + "tpm-backup"
> +};
> +
> +static struct list_head tpm_list = LIST_HEAD_INIT(tpm_list);
> +
> +static struct tpm_chip* tpm_lookup(enum tpm_chip_id id)
> +{
> + struct tpm_chip *tpm = NULL;
> +
> + list_for_each(&tpm_list, tpm, link) {
> + if (tpm->id == id)
> + return tpm;
> + }
> + return NULL;
> +}
Are there any plans for machines with multiple TPMs? Is two a hard
limit? How does one do any failover? i.e. why is there a master and
backup rather than just a list of TPMs? How does one safely commit
changes to keys stored in multiple TPMs?
Current code seems to bail out on first failure, so if the purpose of a
backup TPM is to still be able to boot on TPM failure, how do we achieve
that?
> +#ifdef STB_DEBUG
> +static void tpm_print_pcr(struct tpm_chip *tpm, TPM_Pcr pcr, TPM_Alg_Id alg,
> + size_t size)
> +{
> + int rc;
> + uint8_t digest[TPM_ALG_SHA256_SIZE];
> +
> + memset(digest, 0, size);
> +
> + /* print the result of the extend operation */
> + rc = tpmCmdPcrRead(tpm, pcr, alg, digest, size);
> + if (rc) {
> + /**
> + * @fwts-label STBPCRReadFailed¬
> + * @fwts-advice STB_DEBUG should not be enabled
> + * in production. PCR read operation failed.
> + * This TSS implementation is part of hostboot,
> + * but the source code is shared with skiboot.
> + * 1) The hostboot TSS may have been updated.
> + * 2) This may be caused by the short I2C
> + * timeout and can be fixed by increasing the
> + * timeout. Otherwise this indicates a bug in
> + * the TSS or the TPM device driver. Each one
> + * has local debug macros that can help.
> + */
> + prlog(PR_ERR, "STB: tpmCmdPcrRead() failed: "
> + "tpm%d, alg=%x, pcr%d, rc=%d\n", tpm->id,
> + alg, pcr, rc);
> + }
> +
> + prlog(PR_NOTICE,"STB: print pcr-read: tpm%d alg=%x pcr%d\n",
> + tpm->id, alg, pcr);
> + stb_print_data(digest, size);
> +}
> +#endif
> +
> +struct tpm_chip* tpm_allocate_chip(const struct dt_node *node)
> +{
> + int i, rc;
> + uint64_t sml_base;
> + uint32_t sml_size;
> + const struct dt_property *p;
> + struct tpm_chip *tpm;
> +
> + tpm = (struct tpm_chip*) malloc(sizeof(struct tpm_chip));
> + assert(tpm);
> +
> + /* id */
> + p = dt_find_property(node, "label");
> + if (p) {
> + for (i = 0; i < MAX_TPM_CHIP_ID; i++) {
> + if (!strcasecmp(p->prop, tpm_label[i])) {
> + tpm->id = i;
> + break;
> + }
> + }
> + if (i == MAX_TPM_CHIP_ID) {
> + /**
> + * @fwts-label TPMInvalidLabel
> + * @fwts-advice TPM label value invalid. Hostboot
> + * creates the tpm node and the label property. Check
> + * that the tpm node still have the same design.
> + */
> + prlog(PR_ERR, "STB: invalid tpm label=%s\n", p->prop);
> + goto error;
> +
> + } else if (tpm_lookup(i)) {
> + /**
> + * @fwts-label TPMAlreadyRegistered
> + * @fwts-advice TPM node already registered. The same
> + * node is being registered twice or there is a
> + * tpm node duplicate in the device tree
> + */
> + prlog(PR_WARNING, "TPM: tpm%d already registered\n", i);
> + goto error;
> + }
> + }
> +
> + tpm->node = (struct dt_node*) node;
> + tpm->enabled = true;
> +
> + /**
> + * event log
> + * sml = shared memory log
> + */
> + sml_base = dt_prop_get_u64(node, "linux,sml-base");
> + sml_size = dt_prop_get_u32(node, "linux,sml-size");
It seems odd that we have a "linux,sml-base" in skiboot. Where does this
dt binding come from?
> + rc = TpmLogMgr_initializeUsingExistingLog(&tpm->logmgr,
> + (uint8_t*) sml_base, sml_size);
> +
> + if (rc) {
> + /**
> + * @fwts-label TPMInitEventLogFailed
> + * @fwts-advice Hostboot creates and adds entries to the
> + * event log. The failed init function is part of hostboot,
> + * but the source code is shared with skiboot. If the hostboot
> + * TpmLogMgr code (or friends) has been updated, the changes
> + * need to be applied to skiboot as well.
> + */
> + prlog(PR_ERR, "TPM: eventlog init failed: tpm%d rc=%d",
> + tpm->id, rc);
> + goto error;
> + }
> + return tpm;
> +
> +error:
> + free(tpm);
> + return NULL;
> +}
> +
> +void tpm_init(void)
> +{
> + if (!list_empty(&tpm_list)) {
> + /**
> + * @fwts-label TPMAlreadyInitialized
> + * @fwts-advice TPM already initialized. Check if tpm is being
> + * initialized more than once.
> + */
> + prlog(PR_WARNING, "TPM: tpm device(s) already initialized\n");
> + return;
> + }
> +
> + list_head_init(&tpm_list);
> +
> + /* tpm drivers supported */
> +
> + if (list_empty(&tpm_list)) {
> + /**
> + * @fwts-label TPMNotInitialized
> + * @fwts-advice No TPM chip has been initialized. We may not
> + * have a compatible tpm driver or there is no tpm node in the
> + * device tree with the expected bindings.
> + */
> + prlog(PR_ERR, "TPM: no tpm chip has been initialized\n");
> + }
> +}
> +
> +void tpm_cleanup(void)
> +{
> + struct tpm_chip *tpm = NULL;
> +
> + tpm = list_pop(&tpm_list, struct tpm_chip, link);
> + while (tpm) {
> +
> + /* deallocate memory */
> + if (tpm->dev)
> + free(tpm->dev);
> +
> + tpm->driver = NULL;
> + free(tpm);
> +
> + tpm = list_pop(&tpm_list, struct tpm_chip, link);
> + }
> + list_head_init(&tpm_list);
> +}
> +
> +void tpm_register_chip(struct tpm_chip *tpm)
> +{
> + list_add_tail(&tpm_list, &tpm->link);
> + prlog(PR_NOTICE, "TPM: tpm%d registered: driver=%s elsz=%d\n",
> + tpm->id, tpm->driver->name, tpm->logmgr.logSize);
> +}
> +
> +int tpm_extendl(TPM_Pcr pcr,
> + TPM_Alg_Id alg1, uint8_t* buf1, size_t size1,
> + TPM_Alg_Id alg2, uint8_t* buf2, size_t size2,
> + uint32_t etype, const char* emsg)
> +{
> + int rc;
> + TCG_PCR_EVENT2 event;
> + struct tpm_chip *tpm = NULL;
> +
> + rc = STB_SUCCESS;
> +
> + list_for_each(&tpm_list, tpm, link) {
> +
> + if (!tpm->enabled)
> + continue;
> +
> + event = TpmLogMgr_genLogEventPcrExtend(pcr, alg1, buf1, size1,
> + alg2, buf2, size2, etype, emsg);
> +
> + /* eventlog recording */
> + rc = TpmLogMgr_addEvent(&tpm->logmgr, &event);
> +
> + if (rc) {
> + /**
> + * @fwts-label STBAddEventFailed
> + * @fwts-advice TpmLogMgr failed to add a new event to the event
> + * log. TpmLogMgr is part of hostboot, but the source code is
> + * shared with skiboot. 1) The hostboot TpmLogMgr code may have
> + * been updated. 2) Check that max event log size was not
> + * reached and log marshall executed with no error. Enabling the
> + * trace routines in trustedbootUtils.H may help.
> + */
> + prlog(PR_ERR, "TPM: %s -> elog%d FAILED: pcr%d et=%x rc=%d\n",
> + emsg, tpm->id, pcr, etype, rc);
> + rc = STB_EVENTLOG_FAILED;
> + goto error;
> + }
> +
> + prlog(PR_DEBUG, "TPM: %s -> elog%d: pcr%d et=%x ls=%d\n",
> + emsg, tpm->id, pcr, etype, tpm->logmgr.logSize);
> +
> +#ifdef STB_DEBUG
> + tpm_print_pcr(tpm, pcr, alg1, size1);
> + tpm_print_pcr(tpm, pcr, alg2, size2);
> +#endif
> + /* extend pcr of both sha1 and sha256 banks*/
> + rc = tpmCmdPcrExtend2Hash(tpm, pcr,
> + alg1, buf1, size1,
> + alg2, buf2, size2);
> +
> + if (rc) {
> + /**
> + * @fwts-label STBPCRExtendFailed
> + * @fwts-advice PCR extend operation failed. This TSS
> + * implementation is part of hostboot, but the source code is
> + * shared with skiboot. 1) The hostboot TSS may have been
> + * updated. 2) This may be caused by the short I2C timeout and
> + * can be fixed by increasing the timeout.
> + * Otherwise this indicates a bug in the TSS or the TPM
> + * device driver. Each one has local debug macros that can help.
> + */
> + prlog(PR_ERR, "TPM: %s -> tpm%d FAILED: pcr%d rc=%d\n",
> + emsg, tpm->id, pcr, rc);
> + rc = STB_PCR_EXTEND_FAILED;
> + goto error;
> + }
> +
> + prlog(PR_DEBUG, "TPM: %s -> tpm%d: pcr%d\n", emsg, tpm->id, pcr);
> +
> +#ifdef STB_DEBUG
> + tpm_print_pcr(tpm, pcr, alg1, size1);
> + tpm_print_pcr(tpm, pcr, alg2, size2);
> +#endif
> + }
> + return rc;
> +
> +error:
> + tpm->enabled = false;
> + return rc;
> +}
> +
> +void tpm_add_status_property(void) {
> + struct tpm_chip *tpm;
> + list_for_each(&tpm_list, tpm, link) {
> + dt_add_property_string(tpm->node, "status",
> + tpm->enabled ? "okay" : "disabled");
> + }
> +}
> diff --git a/libstb/tpm.h b/libstb/tpm.h
> index 546458e..0de1a35 100644
> --- a/libstb/tpm.h
> +++ b/libstb/tpm.h
> @@ -23,26 +23,69 @@
> #include "tss/trustedTypes.H"
>
> struct tpm_dev {
> +
> + /* TPM bus id */
> int bus_id;
> +
> + /* TPM address in the bus */
> int xscom_base;
> };
>
> struct tpm_driver {
> +
> + /* Driver name */
> const char* name;
> +
> + /* Transmit the TPM command stored in buf to the tpm device */
> int (*transmit)(struct tpm_dev *dev, uint8_t* buf, size_t cmdlen,
> size_t *buflen);
> };
>
> struct tpm_chip {
> +
> + /* TPM chip id */
> int id;
> +
> + /* Indicates whether or not the device is functional */
> bool enabled;
> +
> + /* TPM device tree node */
> struct dt_node *node;
> +
> + /* Event log handler */
> struct _TpmLogMgr logmgr;
> +
> + /* TPM device handler */
> struct tpm_dev *dev;
> +
> + /* TPM driver handler */
> struct tpm_driver *driver;
> +
> +
> struct list_node link;
> };
>
> +/* TSS tweak */
> typedef struct tpm_chip TpmTarget;
>
> +/* Allocate and register tpm chip structures for drivers */
> +extern struct tpm_chip* tpm_allocate_chip(const struct dt_node *node);
> +extern void tpm_register_chip(struct tpm_chip *tpm);
> +
> +/**
> + * Extend the two measurements (hashes) to the indicated PCR in a single
> + * TPM operation. It also creates an event accordingly and record it into the
> + * event log
> + */
> +extern int tpm_extendl(TPM_Pcr pcr,
> + TPM_Alg_Id alg1, uint8_t* buf1, size_t size1,
> + TPM_Alg_Id alg2, uint8_t* buf2, size_t size2,
> + uint32_t etype, const char* emsg);
etype / emsg is a bit ambiguous, are these error related or event
related?
Is there any fundamental reason why there's a limit on 2 algorithms?
Must these be in the SHA1/SHA256 order? Or would things be the same if
specified in any order?
This may be a candidate for va_args to allow for future expansion?
Later on, patch 15, the code that calls this passes the same digest to
both algorithms... which is... decidedly odd looking and makes me wonder
if this is the correct API.
--
Stewart Smith
OPAL Architect, IBM.
More information about the Skiboot
mailing list