[SLOF] [PATCH v7 6/8] tcgbios: Add TPM 2.0 support and firmware API
Kevin O'Connor
kevin at koconnor.net
Sat Feb 15 02:36:17 AEDT 2020
On Tue, Jan 21, 2020 at 03:01:45PM -0500, Stefan Berger wrote:
> This patch adds TPM 2.0 support along with the firmware API that Linux
> uses to transfer the firmware log.
>
> The firmware API follows the "PFW Virtual TPM Driver" specification.
> The API has callers in existing Linux code (prom_init.c) from TPM 1.2
> times but the API also works for TPM 2.0 without modifications.
>
> The TPM 2.0 support logs PCR extensions of measurements of code and data.
> For this part we follow the TCG specification "TCG PC Client
> Platform Firmware Profile Specification" (section "Event Logging").
>
> Other relevant specs for the construction of TPM commands are:
> - Trusted Platform Module Library; Part 2 Structures
> - Trusted Platform Module Library; Part 3 Commands
>
> Signed-off-by: Stefan Berger <stefanb at linux.ibm.com>
Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
-Kevin
> ---
> board-qemu/slof/Makefile | 13 +-
> board-qemu/slof/tree.fs | 3 +
> board-qemu/slof/vio-vtpm-cdriver.fs | 105 ++++
> board-qemu/slof/vtpm-sml.fs | 68 ++
> include/helpers.h | 1 +
> lib/libtpm/Makefile | 2 +-
> lib/libtpm/tcgbios.c | 925 ++++++++++++++++++++++++++++
> lib/libtpm/tcgbios.h | 32 +
> lib/libtpm/tcgbios_int.h | 241 ++++++++
> lib/libtpm/tpm.code | 130 ++++
> lib/libtpm/tpm.in | 26 +
> slof/fs/packages/disk-label.fs | 9 +
> slof/fs/start-up.fs | 5 +
> 13 files changed, 1556 insertions(+), 4 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
>
> diff --git a/board-qemu/slof/Makefile b/board-qemu/slof/Makefile
> index d7ed2d7..a8cff6d 100644
> --- a/board-qemu/slof/Makefile
> +++ b/board-qemu/slof/Makefile
> @@ -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 \
> diff --git a/board-qemu/slof/tree.fs b/board-qemu/slof/tree.fs
> index d95fde3..7b34125 100644
> --- a/board-qemu/slof/tree.fs
> +++ b/board-qemu/slof/tree.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
> diff --git a/board-qemu/slof/vio-vtpm-cdriver.fs b/board-qemu/slof/vio-vtpm-cdriver.fs
> new file mode 100644
> index 0000000..7a2ffc3
> --- /dev/null
> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs
> @@ -0,0 +1,105 @@
> +\ *****************************************************************************
> +\ * 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 cr
> +
> +false VALUE vtpm-debug?
> +0 VALUE vtpm-unit
> +
> +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
> +
> +\ firmware API call
> +: sml-get-allocated-size ( -- buffer-size)
> + LOG-SIZE
> +;
> +
> +\ firmware API call
> +: sml-get-handover-size ( -- size)
> + tpm-get-logsize
> +;
> +
> +\ firmware API call
> +: sml-handover ( dest size -- )
> + log-base ( dest size src )
> + -rot ( src dest size )
> + move
> +
> + VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason
> +;
> +
> +\ firmware API call
> +: get-failure-reason ( -- reason )
> + tpm-driver-get-failure-reason ( reason )
> +;
> +
> +0 0 s" ibm,sml-efi-reformat-supported" property
> +
> +\ firmware API call
> +: reformat-sml-to-efi-alignment ( -- success )
> + true
> +;
> +
> +: open true ;
> +: close ;
> +
> +: vtpm-cleanup ( -- )
> + vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN
> + tpm-finalize
> + \ Disable TCE bypass
> + vtpm-unit 0 rtas-set-tce-bypass
> +;
> +
> +: vtpm-init ( -- success )
> + 0 0 get-node open-node ?dup 0= IF false 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
> +
> + \ close temporary node
> + close-node
> + r> to my-self
> +
> + tpm-start ?dup 0= IF
> + vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN
> + true
> + ELSE
> + ." VTPM: Error code from tpm-start: " . cr
> + false
> + THEN
> +;
> +
> +\ inititialize unit and set RTAS bypass
> +vtpm-init IF
> + \ pass logbase and size to the C driver; we may only do this after
> + \ init of the lower levels since this calls needs to know the PCR banks
> + \ when setting up the log
> + log-base LOG-SIZE tpm-set-log-parameters
> + s" vtpm-sml.fs" included
> +ELSE
> + ." VTPM: vtpm-init failed" cr
> +THEN
> +
> +
> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs
> new file mode 100644
> index 0000000..8e2ab6d
> --- /dev/null
> +++ b/board-qemu/slof/vtpm-sml.fs
> @@ -0,0 +1,68 @@
> +\ *****************************************************************************
> +\ * 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
> +\ ****************************************************************************/
> +
> +" /" find-device
> +
> +new-device
> +
> +false VALUE vtpm-debug?
> +
> +\ create /ibm,vtpm
> +s" ibm,vtpm" 2dup device-name device-type
> +
> +\
> +\ only internal API calls
> +\
> +
> +: separator-event ( start-pcr end-pcr -- )
> + tpm-add-event-separators ( errcode )
> + ?dup IF
> + ." VTPM: Error code from tpm-add-event-separators: " . cr
> + THEN
> +;
> +
> +80 CONSTANT BCV_DEVICE_HDD
> +
> +: measure-hdd-mbr ( addr length -- )
> + 0 7 separator-event
> + BCV_DEVICE_HDD ( addr length bootdrv )
> + -rot ( bootdrv addr length )
> + tpm-measure-bcv-mbr ( errcode )
> + ?dup IF
> + ." VTPM: Error code from tpm-measure-hdd: " . cr
> + THEN
> +;
> +
> +: leave-firmware ( -- )
> + tpm-leave-firmware ( errcode )
> + ?dup IF
> + ." VTPM: Error code from tpm-leave-firmware: " . cr
> + THEN
> +;
> +
> +: measure-scrtm ( -- )
> + tpm-measure-scrtm ( errcode )
> + ?dup IF
> + ." VTPM: Error code from tpm-measure-scrtm: " . cr
> + THEN
> +;
> +
> +: open true ;
> +: close ;
> +
> +finish-device
> +device-end
> +
> +s" /ibm,vtpm" find-node ?dup IF
> + s" measure-scrtm" rot $call-static
> +THEN
> +
> diff --git a/include/helpers.h b/include/helpers.h
> index 2f460d6..47b2674 100644
> --- a/include/helpers.h
> +++ b/include/helpers.h
> @@ -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
> diff --git a/lib/libtpm/Makefile b/lib/libtpm/Makefile
> index 8fa781e..bcfe88d 100644
> --- a/lib/libtpm/Makefile
> +++ b/lib/libtpm/Makefile
> @@ -23,7 +23,7 @@ TARGET = ../libtpm.a
>
> all: $(TARGET)
>
> -SRCS = tpm_drivers.c sha256.c
> +SRCS = tpm_drivers.c sha256.c tcgbios.c
>
> OBJS = $(SRCS:%.c=%.o)
>
> diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c
> new file mode 100644
> index 0000000..d81d392
> --- /dev/null
> +++ b/lib/libtpm/tcgbios.c
> @@ -0,0 +1,925 @@
> +/*****************************************************************************
> + * 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
> + * Stefan Berger, stefanb at linux.ibm.com
> + * Kevin O'Connor, kevin at koconnor.net
> + *****************************************************************************/
> +
> +/*
> + * 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:
> + * https://trustedcomputinggroup.org/resource/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 "sha256.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))
> +
> +static struct {
> + 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;
> +
> + /* PCR selection as received from TPM */
> + uint32_t tpm20_pcr_selection_size;
> + struct tpml_pcr_selection *tpm20_pcr_selection;
> +} 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
> + ****************************************************************/
> +
> +/* A 'struct tpm_log_entry' is a local data structure containing a
> + * 'TCG_PCR_EVENT2_Header' followed by space for the maximum supported
> + * digest. The digest is a series of TPMT_HA structs on tpm2.0.
> + */
> +struct tpm_log_entry {
> + TCG_PCR_EVENT2_Header hdr;
> + uint8_t pad[sizeof(struct TPML_DIGEST_VALUES)
> + + 5 * sizeof(struct TPMT_HA)
> + + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
> + + SHA512_BUFSIZE + SM3_256_BUFSIZE];
> +} __attribute__((packed));
> +
> +static const struct hash_parameters {
> + uint16_t hashalg;
> + uint8_t hash_buffersize;
> +} 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 const struct hash_parameters *tpm20_find_by_hashalg(uint16_t hashAlg)
> +{
> + unsigned i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
> + if (hash_parameters[i].hashalg == hashAlg)
> + return &hash_parameters[i];
> + }
> + return NULL;
> +}
> +
> +static inline int tpm20_get_hash_buffersize(uint16_t hashAlg)
> +{
> + const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg);
> +
> + if (hp)
> + return hp->hash_buffersize;
> + return -1;
> +}
> +
> +/*
> + * Build the TPM2 TPML_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 sha256 hash in the area of the sha384
> + * 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 *sha256,
> + bool bigEndian)
> +{
> + struct tpms_pcr_selection *sel;
> + void *nsel, *end;
> + void *dest = le->hdr.digests + sizeof(struct TPML_DIGEST_VALUES);
> + uint32_t count;
> + struct TPMT_HA *v;
> + struct TPML_DIGEST_VALUES *vs;
> +
> + sel = tpm_state.tpm20_pcr_selection->selections;
> + end = (void *)tpm_state.tpm20_pcr_selection +
> + tpm_state.tpm20_pcr_selection_size;
> +
> + for (count = 0;
> + count < be32_to_cpu(tpm_state.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, sha256,
> + hsize < SHA256_BUFSIZE ? hsize : SHA256_BUFSIZE);
> +
> + dest += sizeof(*v) + hsize;
> + sel = nsel;
> + }
> +
> + if (sel != end) {
> + dprintf("Malformed pcr selection structure fron TPM\n");
> + return -1;
> + }
> +
> + vs = (void*)le->hdr.digests;
> + if (bigEndian)
> + vs->count = cpu_to_be32(count);
> + else
> + vs->count = cpu_to_le32(count);
> +
> + return dest - (void*)le->hdr.digests;
> +}
> +
> +/****************************************************************
> + * 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 = spapr_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;
> +
> + ret = spapr_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;
> + uint32_t resplen;
> + int ret;
> +
> + 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 */
> + 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;
> +
> + tpm_state.tpm20_pcr_selection = SLOF_alloc_mem(size);
> + if (tpm_state.tpm20_pcr_selection) {
> + memcpy(tpm_state.tpm20_pcr_selection, &trg->data, size);
> + tpm_state.tpm20_pcr_selection_size = size;
> + } else {
> + printf("TCGBIOS: Failed to allocated %u bytes.\n", size);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +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;
> + struct tpm_rsp_header rsp;
> + uint32_t resp_length = sizeof(rsp);
> + int ret;
> +
> + memcpy(tre, &tmp_tre, sizeof(tmp_tre));
> + memcpy(&tre->digest[0], le->hdr.digests, digest_len);
> +
> + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
> +
> + ret = spapr_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 = spapr_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);
> + int ret;
> +
> + if (buf_len > sizeof(rsp.rnd.buffer))
> + return -1;
> +
> + ret = spapr_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)),
> + },
> + };
> + struct tpm_rsp_header rsp;
> + uint32_t resp_length = sizeof(rsp);
> + int ret;
> +
> + memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer));
> +
> + ret = spapr_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)
> +{
> + 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;
> +
> + ret = spapr_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(TCG_PCR_EVENT2_Header *entry,
> + int digest_len,
> + const void *event, uint32_t event_length)
> +{
> + size_t size, logsize;
> + void *dest;
> + TCG_PCR_EVENT2_Trailer *t;
> +
> + 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(TCG_PCR_EVENT2_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 = %zu\n", size);
> + return TCGBIOS_LOGOVERFLOW;
> + }
> +
> + dest = tpm_state.log_area_next_entry;
> + memcpy(dest, entry, sizeof(*entry) + digest_len);
> +
> + 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)
> +{
> + 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;
> + void *nsel, *end;
> + int event_size;
> + uint32_t *vendorInfoSize;
> + struct tpm_log_entry le = {
> + .hdr.eventtype = cpu_to_log32(EV_NO_ACTION),
> + };
> + uint32_t count;
> +
> + sel = tpm_state.tpm20_pcr_selection->selections;
> + end = (void*)tpm_state.tpm20_pcr_selection +
> + tpm_state.tpm20_pcr_selection_size;
> +
> + for (count = 0;
> + count < be32_to_cpu(tpm_state.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;
> +
> + 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 %zu 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[SHA256_BUFSIZE];
> + struct tpm_log_entry le = {
> + .hdr.pcrindex = cpu_to_log32(pcrindex),
> + .hdr.eventtype = cpu_to_log32(eventtype),
> + };
> + int digest_len;
> + int ret;
> +
> + sha256(hashdata, hashdatalen, hash);
> + digest_len = tpm20_build_digest(&le, hash, true);
> + if (digest_len < 0)
> + return TCGBIOS_GENERAL_ERROR;
> + 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 pcrIndex;
> + int rc;
> +
> + 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)
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +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 | sha256sum
> + */
> + 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 | sha256sum
> + */
> + 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_text_start = (char *)&_slof_text;
> + 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 (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);
> +}
> diff --git a/lib/libtpm/tcgbios.h b/lib/libtpm/tcgbios.h
> new file mode 100644
> index 0000000..e9f9c36
> --- /dev/null
> +++ b/lib/libtpm/tcgbios.h
> @@ -0,0 +1,32 @@
> +/*****************************************************************************
> + * 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
> + *****************************************************************************/
> +
> +#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 */
> diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h
> index 835bd36..6892e6f 100644
> --- a/lib/libtpm/tcgbios_int.h
> +++ b/lib/libtpm/tcgbios_int.h
> @@ -15,6 +15,83 @@
>
> #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 from spec:
> + * TCG PC Client Specific Implementation Specification
> + * for Conventional BIOS
> + */
> +#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 BCV_DEVICE_HDD 0x80
> +
> +/* hash sizes */
> +#define SHA1_BUFSIZE 20
> +#define SHA256_BUFSIZE 32
> +#define SHA384_BUFSIZE 48
> +#define SHA512_BUFSIZE 64
> +#define SM3_256_BUFSIZE 32
> +
> +/*
> + * Logging for TPM 2 is specified in TCG spec "TCG PC Client Platform
> + * Firmware Profile Specification" in section "Event Logging" and sub-
> + * section "TCG_PCR_EVENT2 structure"
> + *
> + * Each entry in the TPM log contains: a TCG_PCR_EVENT2_Header, a variable
> + * length digest, a TCG_PCR_EVENT2_Trailer, and a variable length event.
> + * The 'digest' matches what is sent to the TPM hardware via the Extend
> + * command. On TPM2.0 the digest contains a TPML_DIGEST_VALUES struct
> + * followed by a variable number of TPMT_HA structs (as specified by the
> + * hardware via the TPM2_CAP_PCRS request).
> + */
> +typedef struct tdTCG_PCR_EVENT2_Header {
> + uint32_t pcrindex;
> + uint32_t eventtype;
> + uint8_t digests[0];
> +} __attribute__((packed)) TCG_PCR_EVENT2_Header;
> +
> +typedef struct tdTCG_PCR_EVENT2_Trailer {
> + uint32_t eventdatasize;
> + uint8_t event[0];
> +} __attribute__((packed)) TCG_PCR_EVENT2_Trailer;
> +
> +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 headers for all TPM commands */
> struct tpm_req_header {
> uint16_t tag;
> uint32_t totlen;
> @@ -27,4 +104,168 @@ struct tpm_rsp_header {
> uint32_t errcode;
> } __attribute__((packed));
>
> +/****************************************************************
> + * TPM v2.0 hardware commands
> + *
> + * Relevant specs for #defines and commonly used structures:
> + * - Trusted Platform Module Library; Part 2: Structures
> + * Relevant specs for command structures:
> + * - Trusted Platform Module Library; Part 3: 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
> +
> +/* 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 TPMT_HA {
> + uint16_t hashAlg;
> + uint8_t hash[0]; /* size depends on hashAlg */
> +} __attribute__((packed));
> +
> +struct TPML_DIGEST_VALUES {
> + uint32_t count;
> + struct TPMT_HA digest[0]; /* variable number of entries */
> +} __attribute__((packed));
> +
> +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));
> +
> +/*
> + * tpm2_authblock is used in TPM 2 commands using 'Auth. Handle'
> + */
> +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 */
> diff --git a/lib/libtpm/tpm.code b/lib/libtpm/tpm.code
> new file mode 100644
> index 0000000..05f4547
> --- /dev/null
> +++ b/lib/libtpm/tpm.code
> @@ -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
> diff --git a/lib/libtpm/tpm.in b/lib/libtpm/tpm.in
> new file mode 100644
> index 0000000..22713e4
> --- /dev/null
> +++ b/lib/libtpm/tpm.in
> @@ -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)
> diff --git a/slof/fs/packages/disk-label.fs b/slof/fs/packages/disk-label.fs
> index 8859fb0..252a44e 100644
> --- a/slof/fs/packages/disk-label.fs
> +++ b/slof/fs/packages/disk-label.fs
> @@ -338,6 +338,14 @@ CONSTANT /gpt-part-entry
> dup c@ eb = swap 2+ c@ 90 = and
> ;
>
> +: measure-mbr ( addr length -- )
> + s" /ibm,vtpm" find-node ?dup IF
> + s" measure-hdd-mbr" rot $call-static
> + ELSE
> + 2drop
> + THEN
> +;
> +
> \ NOTE: block-size is always 512 bytes for DOS partition tables.
>
> : load-from-dos-boot-partition ( addr -- size )
> @@ -361,6 +369,7 @@ CONSTANT /gpt-part-entry
> block-size * to part-offset
> 0 0 seek drop ( addr offset )
> block-size * read ( size )
> + block block-size measure-mbr
> UNLOOP EXIT
> ELSE
> 2drop ( addr )
> diff --git a/slof/fs/start-up.fs b/slof/fs/start-up.fs
> index 7020f5c..c1f931a 100644
> --- a/slof/fs/start-up.fs
> +++ b/slof/fs/start-up.fs
> @@ -56,6 +56,11 @@
> ;
>
> : (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
> + THEN
> +
> of-prompt? not auto-boot? and IF
> (boot)
> THEN
> --
> 2.24.1
>
More information about the SLOF
mailing list