Message ID | 20200121200147.1002075-7-stefanb@linux.ibm.com |
---|---|
State | Accepted |
Headers | show |
Series | Add vTPM 2.0 support to SLOF | expand |
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@linux.ibm.com> Signed-off-by: Kevin O'Connor <kevin@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@linux.ibm.com > + * Kevin O'Connor, kevin@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 >
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@linux.ibm.com + * Kevin O'Connor, kevin@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
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@linux.ibm.com> --- 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