[Skiboot] [PATCH 10/15] libstb: Add tpm interface

Claudio Carvalho cclaudio at linux.vnet.ibm.com
Thu Aug 11 15:23:52 AEST 2016


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
+
+/* 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;
+}
+
+#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");
+
+	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);
+
+/* Add status property to the TPM devices */
+extern void tpm_add_status_property(void);
+
+extern void tpm_init(void);
+extern void tpm_cleanup(void);
+
 #endif /* __TPM_H */
-- 
1.9.1



More information about the Skiboot mailing list