@@ -22,7 +22,8 @@ CPPFLAGS = -I$(LIBCMNDIR)/libbootmsg -I$(LIBCMNDIR)/libhvcall \
-I$(LIBCMNDIR)/libvirtio -I$(LIBCMNDIR)/libnvram \
-I$(LIBCMNDIR)/libusb -I$(LIBCMNDIR)/libveth \
-I$(LIBCMNDIR)/libe1k -I$(LIBCMNDIR)/libnet \
- -I$(LIBCMNDIR)/libbootmenu
+ -I$(LIBCMNDIR)/libbootmenu -I$(LIBCMNDIR)/libtpm
+
SLOF_LIBS = \
$(LIBCMNDIR)/libbootmsg.a \
$(LIBCMNDIR)/libelf.a \
@@ -33,7 +34,9 @@ SLOF_LIBS = \
$(LIBCMNDIR)/libveth.a \
$(LIBCMNDIR)/libe1k.a \
$(LIBCMNDIR)/libnet.a \
- $(LIBCMNDIR)/libbootmenu.a
+ $(LIBCMNDIR)/libbootmenu.a \
+ $(LIBCMNDIR)/libtpm.a
+
BOARD_SLOF_IN = \
$(LIBCMNDIR)/libhvcall/hvcall.in \
$(LIBCMNDIR)/libvirtio/virtio.in \
@@ -45,7 +48,9 @@ BOARD_SLOF_IN = \
$(LIBCMNDIR)/libveth/veth.in \
$(LIBCMNDIR)/libe1k/e1k.in \
$(LIBCMNDIR)/libnet/libnet.in \
- $(LIBCMNDIR)/libbootmenu/bootmenu.in
+ $(LIBCMNDIR)/libbootmenu/bootmenu.in \
+ $(LIBCMNDIR)/libtpm/tpm.in
+
BOARD_SLOF_CODE = $(BOARD_SLOF_IN:%.in=%.code)
include $(SLOFCMNDIR)/Makefile.inc
@@ -83,6 +88,7 @@ VIO_FFS_FILES = \
$(SLOFBRDDIR)/pci-device_1af4_1050.fs \
$(SLOFBRDDIR)/vio-hvterm.fs \
$(SLOFBRDDIR)/vio-vscsi.fs \
+ $(SLOFBRDDIR)/vio-vtpm-cdriver.fs \
$(SLOFBRDDIR)/vio-veth.fs \
$(SLOFBRDDIR)/rtas-nvram.fs \
$(SLOFBRDDIR)/virtio-net.fs \
@@ -114,6 +120,7 @@ OF_FFS_FILES = \
$(SLOFBRDDIR)/default-font.bin \
$(SLOFBRDDIR)/pci-phb.fs \
$(SLOFBRDDIR)/rtas.fs \
+ $(SLOFBRDDIR)/vtpm-sml.fs \
$(SLOFBRDDIR)/pci-device_1234_1111.fs \
$(SLOFBRDDIR)/pci-device_1013_00b8.fs \
$(SLOFBRDDIR)/pci-device_8086_100e.fs \
@@ -87,6 +87,9 @@ include fbuffer.fs
2dup " qemu,spapr-nvram" strequal IF
" rtas-nvram.fs" included
THEN
+ 2dup " IBM,vtpm20" strequal IF
+ " vio-vtpm-cdriver.fs" included
+ THEN
2drop
THEN
peer
new file mode 100644
@@ -0,0 +1,137 @@
+\ *****************************************************************************
+\ * Copyright (c) 2015-2020 IBM Corporation
+\ * All rights reserved.
+\ * This program and the accompanying materials
+\ * are made available under the terms of the BSD License
+\ * which accompanies this distribution, and is available at
+\ * http://www.opensource.org/licenses/bsd-license.php
+\ *
+\ * Contributors:
+\ * IBM Corporation - initial implementation
+\ ****************************************************************************/
+
+." Populating " pwd
+
+false VALUE vtpm-debug?
+0 VALUE vtpm-unit
+0 VALUE vtpm-ihandle
+
+: setup-alias
+ " ibm,vtpm" find-alias 0= IF
+ " ibm,vtpm" get-node node>path set-alias
+ ELSE
+ drop
+ THEN
+;
+
+: vtpm-cleanup ( )
+ vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
+ tpm-finalize
+ vtpm-unit 0 rtas-set-tce-bypass
+;
+
+: vtpm-init ( -- true | false )
+ 0 0 get-node open-node ?dup 0= IF EXIT THEN
+ my-self >r
+ dup to my-self
+
+ vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN
+
+ my-unit to vtpm-unit
+
+ \ Enable TCE bypass special qemu feature
+ vtpm-unit 1 rtas-set-tce-bypass
+
+ \ Have TCE bypass cleaned up
+ ['] vtpm-cleanup add-quiesce-xt
+
+ tpm-start dup 0= IF
+ vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
+ drop
+ setup-alias
+ ELSE
+ ." VTPM: Error code from tpm-start: " . cr
+ THEN
+
+ close-node
+ r> to my-self
+;
+
+\ forward a call to /ibm,vtpm, which implements the function with the
+\ given name
+: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? )
+ \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned
+ vtpm-ihandle 0= IF
+ s" /ibm,vtpm" open-dev to vtpm-ihandle
+ THEN
+
+ vtpm-ihandle 0<> IF
+ vtpm-ihandle ( arg ... arg name namelen ihandle )
+ $call-method ( ret ... ret )
+ false ( ret ... ret false )
+ ELSE
+ true ( true )
+ THEN
+;
+
+\ firmware API call
+: sml-get-allocated-size ( -- buffer-size)
+ " sml-get-allocated-size" vtpm-call-forward IF
+ \ vtpm-call-forward failed
+ 0
+ THEN
+;
+
+\ firmware API call
+: sml-get-handover-size ( -- size)
+ " sml-get-handover-size" vtpm-call-forward IF
+ \ vtpm-call-forward failed
+ 0
+ THEN
+;
+
+\ firmware API call
+: sml-handover ( dest size -- )
+ " sml-handover" vtpm-call-forward IF
+ \ vtpm-call-forward failed; clean up stack
+ 2drop
+ THEN
+;
+
+\ firmware API call
+: get-failure-reason ( -- reason )
+ " get-failure-reason" vtpm-call-forward IF
+ \ vtpm-call-forward failed; return a value
+ 0 \ invalid
+ THEN
+;
+
+0 0 s" ibm,sml-efi-reformat-supported" property
+
+\ firmware API call
+: reformat-sml-to-efi-alignment ( -- success )
+ " reformat-sml-to-efi-alignment" vtpm-call-forward IF
+ false
+ THEN
+;
+
+: open ( )
+ vtpm-debug? IF ." VTPM: vTPM open()" cr THEN
+ true
+;
+
+: close ( )
+ vtpm-debug? IF ." VTPM: vTPM close()" cr THEN
+;
+
+\ setup alias and the RTAS bypass
+vtpm-init
+
+\ setup the log
+include vtpm-sml.fs
+
+s" /ibm,vtpm" find-node dup IF
+ s" measure-scrtm" rot $call-static
+ELSE
+ drop
+THEN
new file mode 100644
@@ -0,0 +1,123 @@
+\ *****************************************************************************
+\ * Copyright (c) 2015-2020 IBM Corporation
+\ * All rights reserved.
+\ * This program and the accompanying materials
+\ * are made available under the terms of the BSD License
+\ * which accompanies this distribution, and is available at
+\ * http://www.opensource.org/licenses/bsd-license.php
+\ *
+\ * Contributors:
+\ * IBM Corporation - initial implementation
+\ ****************************************************************************/
+
+\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm
+
+" /" find-device
+
+new-device
+
+false VALUE vtpm-debug?
+0 VALUE log-base
+40000 CONSTANT LOG-SIZE \ 256k per VTPM FW spec.
+
+e CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER
+
+LOG-SIZE BUFFER: log-base
+
+\ create /ibm,vtpm
+s" ibm,vtpm" 2dup device-name device-type
+
+\ convey logbase and size to the C driver
+log-base LOG-SIZE tpm-set-log-parameters
+
+: sml-get-allocated-size ( -- buffer-size)
+ vtpm-debug? IF
+ ." Call to sml-get-allocated-size; size = 0x" LOG-SIZE . cr
+ THEN
+ LOG-SIZE
+;
+
+: sml-get-handover-size ( -- size )
+ tpm-get-logsize
+ vtpm-debug? IF
+ ." Call to sml-get-handover-size; size = 0x" dup . cr
+ THEN
+;
+
+: sml-handover ( dest size -- )
+ vtpm-debug? IF
+ 2dup
+ ." Call to sml-handover; size = 0x" . ." dest = " . cr
+ THEN
+ log-base ( dest size src )
+ -rot ( src dest size )
+ move
+
+ VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason
+;
+
+: get-failure-reason ( -- reason )
+ tpm-driver-get-failure-reason ( reason )
+ vtpm-debug? IF
+ ." VTPM: Return value from tpm-driver-get-failure-reason: " dup . cr
+ THEN
+;
+
+: reformat-sml-to-efi-alignment ( -- success? )
+ vtpm-debug? IF
+ ." Call to reformat-sml-to-efi-alignment" cr
+ THEN
+ \ a no-op since already byte aligned
+ true
+;
+
+\
+\ internal API calls
+\
+
+: separator-event ( start-pcr end-pcr -- )
+ tpm-add-event-separators ( errcode )
+ dup 0<> IF
+ ." VTPM: Error code from tpm-add-event-separators: " . cr
+ ELSE
+ drop
+ THEN
+;
+
+80 CONSTANT BCV_DEVICE_HDD
+
+: measure-hdd-mbr ( addr -- )
+ 0 7 separator-event
+ 200 BCV_DEVICE_HDD ( addr length bootdrv )
+ -rot ( bootdrv addr length )
+ tpm-measure-bcv-mbr ( errcode )
+ dup 0<> IF
+ ." VTPM: Error code from tpm-measure-hdd: " . cr
+ ELSE
+ drop
+ THEN
+;
+
+: leave-firmware ( -- )
+ tpm-leave-firmware ( errcode )
+ dup 0<> IF
+ ." VTPM: Error code from tpm-leave-firmware: " . cr
+ ELSE
+ drop
+ THEN
+;
+
+: measure-scrtm ( -- )
+ tpm-measure-scrtm ( errcode )
+ dup 0<> IF
+ ." VTPM: Error code from tpm-measure-scrtm: " . cr
+ ELSE
+ drop
+ THEN
+;
+
+: open true ;
+: close ;
+
+finish-device
+device-end
@@ -50,5 +50,6 @@ extern unsigned long SLOF_get_vtpm_unit(void);
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member)* struct_ptr = (ptr); \
(type *)((char *)struct_ptr - offset_of(type, member)); })
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#endif
@@ -23,7 +23,7 @@ TARGET = ../libtpm.a
all: $(TARGET)
-SRCS = tpm_drivers.c sha1.c
+SRCS = tpm_drivers.c sha1.c tcgbios.c
OBJS = $(SRCS:%.c=%.o)
new file mode 100644
@@ -0,0 +1,916 @@
+/*****************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ * Kevin O'Connor (SeaBIOS)
+ *****************************************************************************/
+
+/*
+ * Implementation of the TPM BIOS extension according to the specification
+ * described in the IBM VTPM Firmware document and the TCG Specification
+ * that can be found here under the following link:
+ * http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "types.h"
+#include "byteorder.h"
+#include "tpm_drivers.h"
+#include "string.h"
+#include "tcgbios.h"
+#include "tcgbios_int.h"
+#include "stdio.h"
+#include "sha1.h"
+#include "helpers.h"
+#include "version.h"
+#include "OF.h"
+
+#undef TCGBIOS_DEBUG
+//#define TCGBIOS_DEBUG
+#ifdef TCGBIOS_DEBUG
+#define dprintf(_x ...) do { printf("TCGBIOS: " _x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+struct tpm_state {
+ unsigned tpm_probed:1;
+ unsigned tpm_found:1;
+ unsigned tpm_working:1;
+
+ /* base address of the log area */
+ uint8_t *log_base;
+
+ /* size of the logging area */
+ size_t log_area_size;
+
+ /* where to write the next log entry to */
+ uint8_t *log_area_next_entry;
+};
+
+static struct tpm_state tpm_state;
+
+/*
+ * TPM 2 logs are written in little endian format.
+ */
+static inline uint32_t log32_to_cpu(uint32_t val)
+{
+ return le32_to_cpu(val);
+}
+
+static inline uint32_t cpu_to_log32(uint32_t val)
+{
+ return cpu_to_le32(val);
+}
+
+static inline uint16_t cpu_to_log16(uint16_t val)
+{
+ return cpu_to_le16(val);
+}
+
+/********************************************************
+ Extensions for TCG-enabled BIOS
+ *******************************************************/
+
+static void probe_tpm(void)
+{
+ tpm_state.tpm_probed = true;
+ tpm_state.tpm_found = spapr_is_vtpm_present();
+ tpm_state.tpm_working = tpm_state.tpm_found;
+}
+
+/****************************************************************
+ * Digest formatting
+ ****************************************************************/
+
+static uint32_t tpm20_pcr_selection_size;
+static struct tpml_pcr_selection *tpm20_pcr_selection;
+
+/* A 'struct tpm_log_entry' is a local data structure containing a
+ * 'tpm_log_header' followed by space for the maximum supported
+ * digest. (The digest is a sha1 hash on tpm1.2 or a series of
+ * tpm2_digest_value structs on tpm2.0)
+ */
+struct tpm_log_entry {
+ struct tpm_log_header hdr;
+ uint8_t pad[sizeof(struct tpm2_digest_values)
+ + 5 * sizeof(struct tpm2_digest_value)
+ + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
+ + SHA512_BUFSIZE + SM3_256_BUFSIZE];
+} __attribute__((packed));
+
+static const struct hash_parameters {
+ uint16_t hashalg;
+ uint8_t hashalg_flag;
+ uint8_t hash_buffersize;
+ const char *name;
+} hash_parameters[] = {
+ {
+ .hashalg = TPM2_ALG_SHA1,
+ .hash_buffersize = SHA1_BUFSIZE,
+ }, {
+ .hashalg = TPM2_ALG_SHA256,
+ .hash_buffersize = SHA256_BUFSIZE,
+ }, {
+ .hashalg = TPM2_ALG_SHA384,
+ .hash_buffersize = SHA384_BUFSIZE,
+ }, {
+ .hashalg = TPM2_ALG_SHA512,
+ .hash_buffersize = SHA512_BUFSIZE,
+ }, {
+ .hashalg = TPM2_ALG_SM3_256,
+ .hash_buffersize = SM3_256_BUFSIZE,
+ }
+};
+
+static int
+tpm20_get_hash_buffersize(uint16_t hashAlg)
+{
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+ if (hash_parameters[i].hashalg == hashAlg)
+ return hash_parameters[i].hash_buffersize;
+ }
+ return -1;
+}
+
+/*
+ * Build the TPM2 tpm2_digest_values data structure from the given hash.
+ * Follow the PCR bank configuration of the TPM and write the same hash
+ * in either truncated or zero-padded form in the areas of all the other
+ * hashes. For example, write the sha1 hash in the area of the sha256
+ * hash and fill the remaining bytes with zeros. Or truncate the sha256
+ * hash when writing it in the area of the sha1 hash.
+ *
+ * le: the log entry to build the digest in
+ * sha1: the sha1 hash value to use
+ * bigEndian: whether to build in big endian format for the TPM or log
+ * little endian for the log (TPM 2.0)
+ *
+ * Returns the digest size; -1 on fatal error
+ */
+static int tpm20_build_digest(struct tpm_log_entry *le, const uint8_t *sha1,
+ bool bigEndian)
+{
+ struct tpms_pcr_selection *sel;
+ void *nsel, *end;
+ void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values);
+ uint32_t count;
+ struct tpm2_digest_value *v;
+ struct tpm2_digest_values *vs;
+
+ if (!tpm20_pcr_selection)
+ return -1;
+
+ sel = tpm20_pcr_selection->selections;
+ end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size;
+
+ for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) {
+ int hsize;
+ uint8_t sizeOfSelect = sel->sizeOfSelect;
+
+ nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+ if (nsel > end)
+ break;
+
+ hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+ if (hsize < 0) {
+ dprintf("TPM is using an unsupported hash: %d\n",
+ be16_to_cpu(sel->hashAlg));
+ return -1;
+ }
+
+ /* buffer size sanity check before writing */
+ v = dest;
+ if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) {
+ dprintf("tpm_log_entry is too small\n");
+ return -1;
+ }
+
+ if (bigEndian)
+ v->hashAlg = sel->hashAlg;
+ else
+ v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg));
+
+ memset(v->hash, 0, hsize);
+ memcpy(v->hash, sha1, hsize > SHA1_BUFSIZE ? SHA1_BUFSIZE : hsize);
+
+ dest += sizeof(*v) + hsize;
+ sel = nsel;
+ }
+
+ if (sel != end) {
+ dprintf("Malformed pcr selection structure fron TPM\n");
+ return -1;
+ }
+
+ vs = (void*)le->hdr.digest;
+ if (bigEndian)
+ vs->count = cpu_to_be32(count);
+ else
+ vs->count = cpu_to_le32(count);
+
+ return dest - (void*)le->hdr.digest;
+}
+
+/****************************************************************
+ * TPM hardware command wrappers
+ ****************************************************************/
+
+/* Helper function for sending TPM commands that take a single
+ * optional parameter (0, 1, or 2 bytes) and have no special response.
+ */
+static int
+tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param,
+ enum tpm_duration_type to_t)
+{
+ struct {
+ struct tpm_req_header trqh;
+ uint16_t param;
+ } __attribute__((packed)) req = {
+ .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size),
+ .trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .trqh.ordinal = cpu_to_be32(ordinal),
+ };
+ uint8_t obuffer[64];
+ struct tpm_rsp_header *trsh = (void *)obuffer;
+ uint32_t obuffer_len = sizeof(obuffer);
+ int ret;
+
+ switch (param_size) {
+ case 2:
+ req.param = cpu_to_be16(param);
+ break;
+ case 1:
+ *(uint8_t *)&req.param = param;
+ break;
+ }
+
+ memset(obuffer, 0, sizeof(obuffer));
+ ret = tpmhw_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t);
+ ret = ret ? -1 : be32_to_cpu(trsh->errcode);
+ dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n",
+ ordinal, param, ret);
+
+ return ret;
+}
+
+static int
+tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count,
+ struct tpm_rsp_header *rsp, uint32_t rsize)
+{
+ struct tpm2_req_getcapability trg = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trg)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability),
+ .capability = cpu_to_be32(capability),
+ .property = cpu_to_be32(property),
+ .propertycount = cpu_to_be32(count),
+ };
+
+ uint32_t resp_size = rsize;
+ int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size,
+ TPM_DURATION_TYPE_SHORT);
+ ret = (ret ||
+ rsize < be32_to_cpu(rsp->totlen)) ? -1
+ : be32_to_cpu(rsp->errcode);
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int
+tpm20_get_pcrbanks(void)
+{
+ uint8_t buffer[128];
+ uint32_t size;
+ struct tpm2_res_getcapability *trg =
+ (struct tpm2_res_getcapability *)&buffer;
+
+ int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr,
+ sizeof(buffer));
+ if (ret)
+ return ret;
+
+ /* defend against (broken) TPM sending packets that are too short */
+ uint32_t resplen = be32_to_cpu(trg->hdr.totlen);
+ if (resplen <= offset_of(struct tpm2_res_getcapability, data))
+ return -1;
+
+ size = resplen - offset_of(struct tpm2_res_getcapability, data);
+ /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/
+ if (size < offset_of(struct tpml_pcr_selection, selections) +
+ offset_of(struct tpms_pcr_selection, pcrSelect))
+ return -1;
+
+ tpm20_pcr_selection = SLOF_alloc_mem(size);
+ if (tpm20_pcr_selection) {
+ memcpy(tpm20_pcr_selection, &trg->data, size);
+ tpm20_pcr_selection_size = size;
+ } else {
+ printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int tpm20_extend(struct tpm_log_entry *le, int digest_len)
+{
+ struct tpm2_req_extend tmp_tre = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(0),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend),
+ .pcrindex = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)),
+ .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ };
+ uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)];
+ struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer;
+
+ memcpy(tre, &tmp_tre, sizeof(tmp_tre));
+ memcpy(&tre->digest[0], le->hdr.digest, digest_len);
+
+ tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
+
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_SHORT);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ return -1;
+
+ return 0;
+}
+
+static int tpm20_stirrandom(void)
+{
+ struct tpm2_req_stirrandom stir = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(stir)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom),
+ .size = cpu_to_be16(sizeof(stir.stir)),
+ .stir = rand(),
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret = tpmhw_transmit(0, &stir.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_SHORT);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len)
+{
+ struct tpm2_res_getrandom rsp;
+ struct tpm2_req_getrandom trgr = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trgr)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom),
+ .bytesRequested = cpu_to_be16(buf_len),
+ };
+ uint32_t resp_length = sizeof(rsp);
+
+ if (buf_len > sizeof(rsp.rnd.buffer))
+ return -1;
+
+ int ret = tpmhw_transmit(0, &trgr.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode)
+ ret = -1;
+ else
+ memcpy(buf, rsp.rnd.buffer, buf_len);
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_hierarchychangeauth(uint8_t auth[20])
+{
+ struct tpm2_req_hierarchychangeauth trhca = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trhca)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trhca.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ .newAuth = {
+ .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)),
+ },
+ };
+ memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
+
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret = tpmhw_transmit(0, &trhca.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state)
+{
+ /* we will try to deactivate the TPM now - ignoring all errors */
+ struct tpm2_req_hierarchycontrol trh = {
+ .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+ .hdr.totlen = cpu_to_be32(sizeof(trh)),
+ .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl),
+ .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+ .authblocksize = cpu_to_be32(sizeof(trh.authblock)),
+ .authblock = {
+ .handle = cpu_to_be32(TPM2_RS_PW),
+ .noncesize = cpu_to_be16(0),
+ .contsession = TPM2_YES,
+ .pwdsize = cpu_to_be16(0),
+ },
+ .enable = cpu_to_be32(hierarchy),
+ .state = state,
+ };
+ struct tpm_rsp_header rsp;
+ uint32_t resp_length = sizeof(rsp);
+ int ret = tpmhw_transmit(0, &trh.hdr, &rsp, &resp_length,
+ TPM_DURATION_TYPE_MEDIUM);
+ if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+ ret = -1;
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+/****************************************************************
+ * Setup and Measurements
+ ****************************************************************/
+
+bool tpm_is_working(void)
+{
+ if (!tpm_state.tpm_probed)
+ probe_tpm();
+
+ return tpm_state.tpm_working;
+}
+
+static void tpm_set_failure(void)
+{
+ tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO);
+ tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO);
+
+ tpm_state.tpm_working = false;
+}
+
+/*
+ * Extend the OFDT log with the given entry by copying the
+ * entry data into the log.
+ *
+ * @pcpes: Pointer to the structure to be copied into the log
+ * @event: The event to be appended to 'pcpes'
+ * @event_length: The length of the event
+ *
+ * Returns 0 on success, an error code otherwise.
+ */
+static uint32_t tpm_log_event_long(struct tpm_log_header *entry,
+ int digest_len,
+ const void *event, uint32_t event_length)
+{
+ size_t size, logsize;
+ void *dest;
+
+ dprintf("log base address = %p, next entry = %p\n",
+ tpm_state.log_base, tpm_state.log_area_next_entry);
+
+ if (tpm_state.log_area_next_entry == NULL)
+ return TCGBIOS_LOGOVERFLOW;
+
+ size = sizeof(*entry) + digest_len +
+ sizeof(struct tpm_log_trailer) + event_length;
+ logsize = (tpm_state.log_area_next_entry + size -
+ tpm_state.log_base);
+ if (logsize > tpm_state.log_area_size) {
+ dprintf("TCGBIOS: LOG OVERFLOW: size = %u\n", size);
+ return TCGBIOS_LOGOVERFLOW;
+ }
+
+ dest = tpm_state.log_area_next_entry;
+ memcpy(dest, entry, sizeof(*entry) + digest_len);
+ struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len;
+ t->eventdatasize = cpu_to_log32(event_length);
+ if (event_length)
+ memcpy(t->event, event, event_length);
+
+ tpm_state.log_area_next_entry += size;
+
+ return 0;
+}
+
+/* Add an entry at the start of the log describing digest formats
+ */
+static int tpm20_write_EfiSpecIdEventStruct(void)
+{
+ if (!tpm20_pcr_selection)
+ return -1;
+
+ struct {
+ struct TCG_EfiSpecIdEventStruct hdr;
+ uint32_t pad[256];
+ } event = {
+ .hdr.signature = "Spec ID Event03",
+ .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT,
+ .hdr.specVersionMinor = 0,
+ .hdr.specVersionMajor = 2,
+ .hdr.specErrata = 0,
+ .hdr.uintnSize = 2,
+ };
+
+ struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
+ void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+ int event_size;
+ uint32_t *vendorInfoSize;
+ struct tpm_log_entry le = {
+ .hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
+ };
+ uint32_t count;
+
+ for (count = 0;
+ count < be32_to_cpu(tpm20_pcr_selection->count);
+ count++) {
+ int hsize;
+ uint8_t sizeOfSelect = sel->sizeOfSelect;
+
+ nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+ if (nsel > end)
+ break;
+
+ hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+ if (hsize < 0) {
+ dprintf("TPM is using an unsupported hash: %d\n",
+ be16_to_cpu(sel->hashAlg));
+ return -1;
+ }
+
+ event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
+ digestSizes[count+1]);
+ if (event_size > sizeof(event) - sizeof(uint32_t)) {
+ dprintf("EfiSpecIdEventStruct pad too small\n");
+ return -1;
+ }
+
+ event.hdr.digestSizes[count].algorithmId =
+ cpu_to_log16(be16_to_cpu(sel->hashAlg));
+ event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize);
+
+ sel = nsel;
+ }
+
+ if (sel != end) {
+ dprintf("Malformed pcr selection structure fron TPM\n");
+ return -1;
+ }
+
+ event.hdr.numberOfAlgorithms = cpu_to_log32(count);
+ event_size = offset_of(struct TCG_EfiSpecIdEventStruct,
+ digestSizes[count]);
+ vendorInfoSize = (void*)&event + event_size;
+ *vendorInfoSize = 0;
+ event_size += sizeof(*vendorInfoSize);
+
+ return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size);
+}
+
+static int tpm20_startup(void)
+{
+ int ret;
+
+ ret = tpm_simple_cmd(0, TPM2_CC_Startup,
+ 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n",
+ ret);
+
+ if (ret)
+ goto err_exit;
+
+ ret = tpm_simple_cmd(0, TPM2_CC_SelfTest,
+ 1, TPM2_YES, TPM_DURATION_TYPE_LONG);
+
+ dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n",
+ ret);
+
+ if (ret)
+ goto err_exit;
+
+ ret = tpm20_get_pcrbanks();
+ if (ret)
+ goto err_exit;
+
+ /* the log parameters will be passed from Forth layer */
+
+ return 0;
+
+err_exit:
+ dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+ tpm_set_failure();
+ return -1;
+}
+
+uint32_t tpm_start(void)
+{
+ probe_tpm();
+
+ if (!tpm_is_working()) {
+ dprintf("%s: Machine does not have a working TPM\n",
+ __func__);
+ return TCGBIOS_FATAL_COM_ERROR;
+ }
+
+ return tpm20_startup();
+}
+
+void tpm_finalize(void)
+{
+ spapr_vtpm_finalize();
+}
+
+static void tpm20_prepboot(void)
+{
+ uint8_t auth[20];
+ int ret = tpm20_stirrandom();
+ if (ret)
+ goto err_exit;
+
+ ret = tpm20_getrandom(&auth[0], sizeof(auth));
+ if (ret)
+ goto err_exit;
+
+ ret = tpm20_hierarchychangeauth(auth);
+ if (ret)
+ goto err_exit;
+
+ return;
+
+err_exit:
+ dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+ tpm_set_failure();
+}
+
+/*
+ * Prepare TPM for boot; this function has to be called before
+ * the firmware transitions to the boot loader.
+ */
+uint32_t tpm_leave_firmware(void)
+{
+ tpm20_prepboot();
+
+ return 0;
+}
+
+/****************************************************************
+ * Forth interface
+ ****************************************************************/
+
+void tpm_set_log_parameters(void *addr, size_t size)
+{
+ int ret;
+
+ dprintf("Log is at 0x%llx; size is %u bytes\n",
+ (uint64_t)addr, size);
+ tpm_state.log_base = addr;
+ tpm_state.log_area_next_entry = addr;
+ tpm_state.log_area_size = size;
+
+ ret = tpm20_write_EfiSpecIdEventStruct();
+ if (ret)
+ tpm_set_failure();
+}
+
+uint32_t tpm_get_logsize(void)
+{
+ uint32_t logsize = tpm_state.log_area_next_entry - tpm_state.log_base;
+
+ dprintf("log size: %u\n", logsize);
+
+ return logsize;
+}
+
+/*
+ * Add a measurement to the log;
+ *
+ * Input parameters:
+ * @pcrindex : PCR to extend
+ * @event_type : type of event
+ * @info : pointer to info (i.e., string) to be added to the log as-is
+ * @info_length: length of the info
+ * @hashdata : pointer to data to be hashed
+ * @hashdata_length: length of the data
+ *
+ */
+static uint32_t tpm_add_measurement_to_log(uint32_t pcrindex,
+ uint32_t eventtype,
+ const char *info,
+ uint32_t infolen,
+ const uint8_t *hashdata,
+ uint32_t hashdatalen)
+{
+ uint8_t hash[SHA1_BUFSIZE];
+ struct tpm_log_entry le = {
+ .hdr.pcrindex = cpu_to_log32(pcrindex),
+ .hdr.eventtype = cpu_to_log32(eventtype),
+ };
+ int digest_len;
+
+ sha1(hashdata, hashdatalen, hash);
+ digest_len = tpm20_build_digest(&le, hash, true);
+ if (digest_len < 0)
+ return TCGBIOS_GENERAL_ERROR;
+ int ret = tpm20_extend(&le, digest_len);
+ if (ret) {
+ tpm_set_failure();
+ return TCGBIOS_COMMAND_ERROR;
+ }
+ tpm20_build_digest(&le, hash, false);
+ return tpm_log_event_long(&le.hdr, digest_len, info, infolen);
+}
+
+/*
+ * Add an EV_ACTION measurement to the list of measurements
+ */
+static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string)
+{
+ uint32_t len = strlen(string);
+
+ return tpm_add_measurement_to_log(pcrIndex, EV_ACTION,
+ string, len, (uint8_t *)string, len);
+}
+
+/*
+ * Add event separators for a range of PCRs
+ */
+uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr)
+{
+ static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff};
+ uint32_t rc = 0;
+ uint32_t pcrIndex;
+
+ if (!tpm_is_working())
+ return TCGBIOS_GENERAL_ERROR;
+
+ if (start_pcr >= 24 || start_pcr > end_pcr)
+ return TCGBIOS_INVALID_INPUT_PARA;
+
+ /* event separators need to be extended and logged for PCRs 0-7 */
+ for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) {
+ rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR,
+ NULL, 0,
+ evt_separator,
+ sizeof(evt_separator));
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr,
+ uint32_t length)
+{
+ uint32_t rc;
+ const char *string;
+
+ if (!tpm_is_working())
+ return TCGBIOS_GENERAL_ERROR;
+
+ if (length < 0x200)
+ return TCGBIOS_INVALID_INPUT_PARA;
+
+ string = "Booting BCV device 00h (Floppy)";
+ if (bootdrv == BCV_DEVICE_HDD)
+ string = "Booting BCV device 80h (HDD)";
+
+ rc = tpm_add_action(4, string);
+ if (rc)
+ return rc;
+
+ /*
+ * equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum
+ */
+ string = "MBR";
+ rc = tpm_add_measurement_to_log(4, EV_IPL,
+ string, strlen(string),
+ addr, 0x1b8);
+ if (rc)
+ return rc;
+
+ /*
+ * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum
+ */
+ string = "MBR PARTITION TABLE";
+ return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA,
+ string, strlen(string),
+ addr + 0x1b8, 0x48);
+}
+
+uint32_t tpm_measure_scrtm(void)
+{
+ uint32_t rc;
+ char *version_start = strstr((char *)&print_version, "FW Version");
+ char *version_end;
+ uint32_t version_length;
+ char *slof_data_start = (char *)&_slof_data;
+ char *slof_text_start = (char *)&_slof_text;
+ uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data;
+ uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text;
+ const char *scrtm = "S-CRTM Contents";
+
+ version_end = strchr(version_start, '\r');
+ version_length = version_end - version_start;
+
+ dprintf("Measure S-CRTM Version: addr = %p, length = %d\n",
+ version_start, version_length);
+
+ rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION,
+ version_start, version_length,
+ (uint8_t *)version_start,
+ version_length);
+
+ if (rc)
+ return rc;
+
+ dprintf("Measure S-CRTM Content (data): start = %p, length = %d\n",
+ slof_data_start, slof_data_length);
+
+ rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
+ scrtm, strlen(scrtm),
+ (uint8_t *)slof_data_start,
+ slof_data_length);
+
+ dprintf("Measure S-CRTM Content (text): start = %p, length = %d\n",
+ slof_text_start, slof_text_length);
+
+ rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS,
+ scrtm, strlen(scrtm),
+ (uint8_t *)slof_text_start,
+ slof_text_length);
+
+ return rc;
+}
+
+/*
+ * tpm_driver_get_failure_reason: Function for interfacing with the firmware
+ * API
+ */
+uint32_t tpm_driver_get_failure_reason(void)
+{
+ /* do not check for a working TPM here */
+ if (!tpm_state.tpm_found)
+ return VTPM_DRV_STATE_INVALID;
+
+ return spapr_vtpm_get_error();
+}
+
+/*
+ * tpm_driver_set_failure_reason: Function for interfacing with the firmware
+ * API
+ */
+void tpm_driver_set_failure_reason(uint32_t errcode)
+{
+ if (!tpm_state.tpm_found)
+ return;
+
+ spapr_vtpm_set_error(errcode);
+}
new file mode 100644
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ * Copyright (c) 2015 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef TCGBIOS_H
+#define TCGBIOS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+uint32_t tpm_start(void);
+void tpm_finalize(void);
+uint32_t tpm_leave_firmware(void);
+uint32_t tpm_measure_scrtm(void);
+void tpm_set_log_parameters(void *address, size_t size);
+uint32_t tpm_get_logsize(void);
+uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr,
+ uint32_t length);
+uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr);
+uint32_t tpm_driver_get_failure_reason(void);
+void tpm_driver_set_failure_reason(uint32_t errcode);
+bool tpm_is_working(void);
+
+#endif /* TCGBIOS_H */
@@ -15,6 +15,94 @@
#include <stdint.h>
+/* internal error codes */
+#define TCGBIOS_OK 0x0
+#define TCGBIOS_LOGOVERFLOW 0x1
+#define TCGBIOS_GENERAL_ERROR 0x2
+#define TCGBIOS_FIRMWARE_ERROR 0x3
+#define TCGBIOS_FATAL_COM_ERROR 0x4
+#define TCGBIOS_INVALID_INPUT_PARA 0x5
+#define TCGBIOS_COMMAND_ERROR 0x6
+#define TCGBIOS_INTERFACE_SHUTDOWN 0x7
+
+/* event types */
+#define EV_POST_CODE 1
+#define EV_NO_ACTION 3
+#define EV_SEPARATOR 4
+#define EV_ACTION 5
+#define EV_EVENT_TAG 6
+#define EV_S_CRTM_CONTENTS 7
+#define EV_S_CRTM_VERSION 8
+#define EV_IPL 13
+#define EV_IPL_PARTITION_DATA 14
+
+#define SHA1_BUFSIZE 20
+#define SHA256_BUFSIZE 32
+#define SHA384_BUFSIZE 48
+#define SHA512_BUFSIZE 64
+#define SM3_256_BUFSIZE 32
+
+#define BCV_DEVICE_HDD 0x80
+
+struct tpm2_digest_value {
+ uint16_t hashAlg;
+ uint8_t hash[0]; /* size depends on hashAlg */
+} __attribute__((packed));
+
+struct tpm2_digest_values {
+ uint32_t count;
+ struct tpm2_digest_value digest[0];
+} __attribute__((packed));
+
+/* Each entry in the TPM log contains: a tpm_log_header, a variable
+ * length digest, a tpm_log_trailer, and a variable length event. The
+ * 'digest' matches what is sent to the TPM hardware via the Extend
+ * command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest
+ * contains a tpm2_digest_values struct followed by a variable number
+ * of tpm2_digest_value structs (as specified by the hardware via the
+ * TPM2_CAP_PCRS request).
+ */
+struct tpm_log_header {
+ uint32_t pcrindex;
+ uint32_t eventtype;
+ uint8_t digest[0];
+} __attribute__((packed));
+
+struct tpm_log_trailer {
+ uint32_t eventdatasize;
+ uint8_t event[0];
+} __attribute__((packed));
+
+struct TCG_EfiSpecIdEventStruct {
+ uint8_t signature[16];
+ uint32_t platformClass;
+#define TPM_TCPA_ACPI_CLASS_CLIENT 0
+ uint8_t specVersionMinor;
+ uint8_t specVersionMajor;
+ uint8_t specErrata;
+ uint8_t uintnSize;
+ uint32_t numberOfAlgorithms;
+ struct TCG_EfiSpecIdEventAlgorithmSize {
+ uint16_t algorithmId;
+ uint16_t digestSize;
+ } digestSizes[0];
+ /*
+ uint8_t vendorInfoSize;
+ uint8_t vendorInfo[0];
+ */
+} __attribute__((packed));
+
+/* Input and Output blocks for the TCG BIOS commands */
+
+/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */
+struct pcpes {
+ uint32_t pcrindex;
+ uint32_t eventtype;
+ uint8_t digest[SHA1_BUFSIZE];
+ uint32_t eventdatasize;
+ uint32_t event;
+} __attribute__((packed));
+
struct tpm_req_header {
uint16_t tag;
uint32_t totlen;
@@ -27,4 +115,156 @@ struct tpm_rsp_header {
uint32_t errcode;
} __attribute__((packed));
+/****************************************************************
+ * TPM v2.0 hardware commands
+ ****************************************************************/
+
+#define TPM2_NO 0
+#define TPM2_YES 1
+
+#define TPM2_SU_CLEAR 0x0000
+#define TPM2_SU_STATE 0x0001
+
+#define TPM2_RH_OWNER 0x40000001
+#define TPM2_RS_PW 0x40000009
+#define TPM2_RH_ENDORSEMENT 0x4000000b
+#define TPM2_RH_PLATFORM 0x4000000c
+
+#define TPM2_ALG_SHA1 0x0004
+#define TPM2_ALG_SHA256 0x000b
+#define TPM2_ALG_SHA384 0x000c
+#define TPM2_ALG_SHA512 0x000d
+#define TPM2_ALG_SM3_256 0x0012
+
+#define TPM2_ALG_SHA1_FLAG (1 << 0)
+#define TPM2_ALG_SHA256_FLAG (1 << 1)
+#define TPM2_ALG_SHA384_FLAG (1 << 2)
+#define TPM2_ALG_SHA512_FLAG (1 << 3)
+#define TPM2_ALG_SM3_256_FLAG (1 << 4)
+
+/* TPM 2 command tags */
+#define TPM2_ST_NO_SESSIONS 0x8001
+#define TPM2_ST_SESSIONS 0x8002
+
+/* TPM 2 commands */
+#define TPM2_CC_HierarchyControl 0x121
+#define TPM2_CC_Clear 0x126
+#define TPM2_CC_ClearControl 0x127
+#define TPM2_CC_HierarchyChangeAuth 0x129
+#define TPM2_CC_PCR_Allocate 0x12b
+#define TPM2_CC_SelfTest 0x143
+#define TPM2_CC_Startup 0x144
+#define TPM2_CC_Shutdown 0x145
+#define TPM2_CC_StirRandom 0x146
+#define TPM2_CC_GetCapability 0x17a
+#define TPM2_CC_GetRandom 0x17b
+#define TPM2_CC_PCR_Extend 0x182
+
+/* TPM 2 Capabilities */
+#define TPM2_CAP_PCRS 0x00000005
+
+/* TPM 2 data structures */
+
+struct tpm2_req_stirrandom {
+ struct tpm_req_header hdr;
+ uint16_t size;
+ uint64_t stir;
+} __attribute__((packed));
+
+struct tpm2_req_getrandom {
+ struct tpm_req_header hdr;
+ uint16_t bytesRequested;
+} __attribute__((packed));
+
+struct tpm2b_20 {
+ uint16_t size;
+ uint8_t buffer[20];
+} __attribute__((packed));
+
+struct tpm2_res_getrandom {
+ struct tpm_rsp_header hdr;
+ struct tpm2b_20 rnd;
+} __attribute__((packed));
+
+struct tpm2_authblock {
+ uint32_t handle;
+ uint16_t noncesize; /* always 0 */
+ uint8_t contsession; /* always TPM2_YES */
+ uint16_t pwdsize; /* always 0 */
+} __attribute__((packed));
+
+struct tpm2_req_hierarchychangeauth {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ struct tpm2b_20 newAuth;
+} __attribute__((packed));
+
+struct tpm2_req_extend {
+ struct tpm_req_header hdr;
+ uint32_t pcrindex;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint8_t digest[0];
+} __attribute__((packed));
+
+struct tpm2_req_clearcontrol {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint8_t disable;
+} __attribute__((packed));
+
+struct tpm2_req_clear {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+} __attribute__((packed));
+
+struct tpm2_req_hierarchycontrol {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint32_t enable;
+ uint8_t state;
+} __attribute__((packed));
+
+struct tpm2_req_getcapability {
+ struct tpm_req_header hdr;
+ uint32_t capability;
+ uint32_t property;
+ uint32_t propertycount;
+} __attribute__((packed));
+
+struct tpm2_res_getcapability {
+ struct tpm_rsp_header hdr;
+ uint8_t moreData;
+ uint32_t capability;
+ uint8_t data[0]; /* capability dependent data */
+} __attribute__((packed));
+
+struct tpm2_req_pcr_allocate {
+ struct tpm_req_header hdr;
+ uint32_t authhandle;
+ uint32_t authblocksize;
+ struct tpm2_authblock authblock;
+ uint32_t count;
+ uint8_t tpms_pcr_selections[4];
+} __attribute__((packed));
+
+struct tpms_pcr_selection {
+ uint16_t hashAlg;
+ uint8_t sizeOfSelect;
+ uint8_t pcrSelect[0];
+} __attribute__((packed));
+
+struct tpml_pcr_selection {
+ uint32_t count;
+ struct tpms_pcr_selection selections[0];
+} __attribute__((packed));
+
#endif /* TCGBIOS_INT_H */
new file mode 100644
@@ -0,0 +1,130 @@
+/******************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * libtpm bindings for SLOF - implementation
+ */
+
+#include <tcgbios.h>
+#include <stdbool.h>
+
+/************************************************/
+/* Startup TPM code */
+/* SLOF: tpm-start ( -- errcode ) */
+/* LIBTPM: tpm_start(void) */
+/************************************************/
+PRIM(tpm_X2d_start)
+ PUSH;
+ TOS.n = tpm_start();
+MIRP
+
+/************************************************/
+/* Shutdown TPM layer before OS takes over */
+/* SLOF: tpm-finalize ( -- ) */
+/* LIBTPM: tpm_finalize(void) */
+/************************************************/
+PRIM(tpm_X2d_finalize)
+ tpm_finalize();
+MIRP
+
+/***************************************************************/
+/* Prepare TPM state for bootloader */
+/* SLOF: tpm-leave-firwmare ( -- errcode ) */
+/* LIBTPM: tpm_leave_firmware(void) */
+/***************************************************************/
+PRIM(tpm_X2d_leave_X2d_firmware)
+ PUSH;
+ TOS.n = tpm_leave_firmware();
+MIRP
+
+/*************************************************************/
+/* Convey log address and size */
+/* SLOF: tpm-set-log-parameters ( addr size -- ) */
+/* LIBTPM: tpm_set_log_parameters(void *addr, uint64_t size) */
+/*************************************************************/
+PRIM(tpm_X2d_set_X2d_log_X2d_parameters)
+ int size = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ tpm_set_log_parameters(addr, size);
+MIRP
+
+/*********************************************************/
+/* Firmware API */
+/* SLOF: tpm-driver-get_failure-reason ( -- errcode) */
+/* LIBTPM: errcode = tpm_driver_get_failure_reason(void) */
+/*********************************************************/
+PRIM(tpm_X2d_driver_X2d_get_X2d_failure_X2d_reason)
+ PUSH;
+ TOS.n = tpm_driver_get_failure_reason();
+MIRP
+
+/********************************************************/
+/* Firmware API */
+/* SLOF: tpm-driver-set-failure_reason ( errcode -- ) */
+/* LIBTPM: tpm_driver_set_failure_reason(errcode) */
+/********************************************************/
+PRIM(tpm_X2d_driver_X2d_set_X2d_failure_X2d_reason)
+ int errcode = TOS.u; POP;
+ tpm_driver_set_failure_reason(errcode);
+MIRP
+
+/************************************************/
+/* Get the size of the log */
+/* SLOF: tpm-get-logsize ( -- size ) */
+/* LIBTPM: logsize = tpm_get_logsize(void) */
+/************************************************/
+PRIM(tpm_X2d_get_X2d_logsize)
+ PUSH;
+ TOS.n = tpm_get_logsize();
+MIRP
+
+/**********************************************************************/
+/* Measure and log event separators */
+/* SLOF: tpm-add-event-separators ( start-pcr end-pcr -- errcode) */
+/* LIBTPM: errcode = tpm_add_event_separators(start_pcr, end_pcr) */
+/**********************************************************************/
+PRIM(tpm_X2d_add_X2d_event_X2d_separators)
+ int end_pcr = TOS.u; POP;
+ int start_pcr = TOS.u;
+ TOS.n = tpm_add_event_separators(start_pcr, end_pcr);
+MIRP
+
+/*************************************************************************/
+/* Measure and log boot connect vector (bcv) device's master boot record */
+/* SLOF: tpm-measure-bcv-mbr ( bootdrv addr length -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_bcv_mbr(bbotdrv, addr, length) */
+/*************************************************************************/
+PRIM(tpm_X2d_measure_X2d_bcv_X2d_mbr)
+ int length = TOS.u; POP;
+ void *addr = TOS.a; POP;
+ int bootdrv = TOS.u;
+ TOS.n = tpm_measure_bcv_mbr(bootdrv, addr, length);
+MIRP
+
+/************************************************/
+/* Check whether the TPM is working */
+/* SLOF: tpm-is-working ( -- true | false ) */
+/* LIBTPM: bool = tpm_is_working() */
+/************************************************/
+PRIM(tpm_X2d_is_X2d_working)
+ PUSH;
+ TOS.n = tpm_is_working();
+MIRP
+
+/************************************************/
+/* Have the S-CRTM measured */
+/* SLOF: tpm-measure-scrtm ( -- errcode ) */
+/* LIBTPM: errcode = tpm_measure_scrtm */
+/************************************************/
+PRIM(tpm_X2d_measure_X2d_scrtm)
+ PUSH;
+ TOS.n = tpm_measure_scrtm();
+MIRP
new file mode 100644
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * Copyright (c) 2015-2020 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+/*
+ * libtpm bindings for SLOF - definitions
+ */
+
+cod(tpm-start)
+cod(tpm-finalize)
+cod(tpm-leave-firmware)
+cod(tpm-set-log-parameters)
+cod(tpm-get-logsize)
+cod(tpm-add-event-separators)
+cod(tpm-measure-bcv-mbr)
+cod(tpm-is-working)
+cod(tpm-measure-scrtm)
+cod(tpm-driver-get-failure-reason)
+cod(tpm-driver-set-failure-reason)
@@ -550,7 +550,15 @@ B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2
\ load from a bootable partition
: load-from-boot-partition ( addr -- size )
debug-disk-label? IF ." Trying DOS boot " .s cr THEN
- dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN
+ dup load-from-dos-boot-partition ?dup 0 <> IF
+ nip
+ block s" /ibm,vtpm" find-node dup IF
+ s" measure-hdd-mbr" rot $call-static
+ ELSE
+ 2drop
+ THEN
+ EXIT
+ THEN
debug-disk-label? IF ." Trying CHRP boot " .s cr THEN
1 disk-chrp-boot !
@@ -56,6 +56,13 @@
;
: (boot?) ( -- )
+ \ last step before we boot we give up physical presence on the TPM
+ s" /ibm,vtpm" find-node dup IF
+ s" leave-firmware" rot $call-static
+ ELSE
+ drop
+ THEN
+
of-prompt? not auto-boot? and IF
(boot)
THEN
This patch adds TPM 2.0 support along with the firmware API that Linux uses to transfer the firmware log. Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> --- board-qemu/slof/Makefile | 13 +- board-qemu/slof/tree.fs | 3 + board-qemu/slof/vio-vtpm-cdriver.fs | 137 +++++ board-qemu/slof/vtpm-sml.fs | 123 ++++ include/helpers.h | 1 + lib/libtpm/Makefile | 2 +- lib/libtpm/tcgbios.c | 916 ++++++++++++++++++++++++++++ lib/libtpm/tcgbios.h | 32 + lib/libtpm/tcgbios_int.h | 240 ++++++++ lib/libtpm/tpm.code | 130 ++++ lib/libtpm/tpm.in | 26 + slof/fs/packages/disk-label.fs | 10 +- slof/fs/start-up.fs | 7 + 13 files changed, 1635 insertions(+), 5 deletions(-) create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs create mode 100644 board-qemu/slof/vtpm-sml.fs create mode 100644 lib/libtpm/tcgbios.c create mode 100644 lib/libtpm/tcgbios.h create mode 100644 lib/libtpm/tpm.code create mode 100644 lib/libtpm/tpm.in