Message ID | 20200115200048.15137-7-stefanb@linux.ibm.com |
---|---|
State | Superseded |
Headers | show |
Series | Add vTPM 2.0 support to SLOF | expand |
On 16/01/2020 07:00, Stefan Berger wrote: > This patch adds TPM 2.0 support along with the firmware API > that Linux uses to transfer the firmware log. > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> > --- > board-qemu/slof/Makefile | 13 +- > board-qemu/slof/tree.fs | 3 + > board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++ > board-qemu/slof/vtpm-sml.fs | 115 ++++ > include/helpers.h | 1 + > lib/libtpm/Makefile | 2 +- > lib/libtpm/tcgbios.c | 918 ++++++++++++++++++++++++++++ > lib/libtpm/tcgbios.h | 32 + > lib/libtpm/tcgbios_int.h | 240 ++++++++ > lib/libtpm/tpm.code | 130 ++++ > lib/libtpm/tpm.in | 26 + > slof/fs/packages/disk-label.fs | 10 +- > slof/fs/start-up.fs | 5 + > 13 files changed, 1624 insertions(+), 5 deletions(-) > create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs > create mode 100644 board-qemu/slof/vtpm-sml.fs > create mode 100644 lib/libtpm/tcgbios.c > create mode 100644 lib/libtpm/tcgbios.h > create mode 100644 lib/libtpm/tpm.code > create mode 100644 lib/libtpm/tpm.in > > 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 \ s/vio-vtpm-cdriver.fs/vio-vtpm.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..8d17d0e > --- /dev/null > +++ b/board-qemu/slof/vio-vtpm-cdriver.fs > @@ -0,0 +1,134 @@ > +\ ***************************************************************************** > +\ * Copyright (c) 2015-2020 IBM Corporation > +\ * All rights reserved. > +\ * This program and the accompanying materials > +\ * are made available under the terms of the BSD License > +\ * which accompanies this distribution, and is available at > +\ * http://www.opensource.org/licenses/bsd-license.php > +\ * > +\ * Contributors: > +\ * IBM Corporation - initial implementation > +\ ****************************************************************************/ > + > +." Populating " pwd > + > +false VALUE vtpm-debug? > +0 VALUE vtpm-unit > +0 VALUE vtpm-ihandle > + > +: setup-alias > + " ibm,vtpm" find-alias 0= IF > + " ibm,vtpm" get-node node>path set-alias > + ELSE > + drop > + THEN > +; > + > +: vtpm-cleanup ( ) > + vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN > + tpm-finalize > + vtpm-unit 0 rtas-set-tce-bypass > +; > + > +: vtpm-init ( -- true | false ) > + 0 0 get-node open-node ?dup 0= IF EXIT THEN > + my-self >r > + dup to my-self > + > + vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN > + > + my-unit to vtpm-unit > + > + \ Enable TCE bypass special qemu feature > + vtpm-unit 1 rtas-set-tce-bypass > + > + \ Have TCE bypass cleaned up > + ['] vtpm-cleanup add-quiesce-xt > + > + tpm-start ?dup 0= IF > + vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN > + setup-alias > + ELSE > + ." VTPM: Error code from tpm-start: " . cr > + THEN > + > + close-node > + r> to my-self > +; > + > +\ forward a call to /ibm,vtpm, which implements the function with the > +\ given name > +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? ) > + \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned > + vtpm-ihandle 0= IF > + s" /ibm,vtpm" open-dev to vtpm-ihandle Why does not "open" do this? Is this vtpm supposed to run even before the client tries using the vtpm services? It does not look like it. > + THEN > + > + vtpm-ihandle 0<> IF > + vtpm-ihandle ( arg ... arg name namelen ihandle ) > + $call-method ( ret ... ret ) > + false ( ret ... ret false ) > + ELSE > + true ( true ) > + THEN > +; > + > +\ firmware API call > +: sml-get-allocated-size ( -- buffer-size) > + " sml-get-allocated-size" vtpm-call-forward IF > + \ vtpm-call-forward failed > + 0 > + THEN > +; > + > +\ firmware API call > +: sml-get-handover-size ( -- size) > + " sml-get-handover-size" vtpm-call-forward IF > + \ vtpm-call-forward failed > + 0 > + THEN > +; > + > +\ firmware API call > +: sml-handover ( dest size -- ) > + " sml-handover" vtpm-call-forward IF > + \ vtpm-call-forward failed; clean up stack > + 2drop > + THEN > +; > + > +\ firmware API call > +: get-failure-reason ( -- reason ) > + " get-failure-reason" vtpm-call-forward IF > + \ vtpm-call-forward failed; return a value > + 0 \ invalid > + THEN > +; > + > +0 0 s" ibm,sml-efi-reformat-supported" property > + > +\ firmware API call > +: reformat-sml-to-efi-alignment ( -- success ) > + " reformat-sml-to-efi-alignment" vtpm-call-forward IF > + false > + THEN > +; > + > +: open ( ) > + vtpm-debug? IF ." VTPM: vTPM open()" cr THEN > + true > +; > + > +: close ( ) > + vtpm-debug? IF ." VTPM: vTPM close()" cr THEN > +; > + > +\ setup alias and the RTAS bypass > +vtpm-init > + > +\ setup the log > +include vtpm-sml.fs > + > +s" /ibm,vtpm" find-node ?dup IF > + s" measure-scrtm" rot $call-static > +THEN The above 22 lines confuse me a lot. Why vtpm-sml.fs? Why "open" does not open? Why vtpm-init is not in "open"? Why the device methods are in vtpm-sml.fs? The Linux finds the device, opens it and calls methods (passing ihandle), why this complication? I am missing the point in all of this and 2 lines commit log does not help at all. > diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs > new file mode 100644 > index 0000000..865dce6 > --- /dev/null > +++ b/board-qemu/slof/vtpm-sml.fs > @@ -0,0 +1,115 @@ > +\ ***************************************************************************** > +\ * Copyright (c) 2015-2020 IBM Corporation > +\ * All rights reserved. > +\ * This program and the accompanying materials > +\ * are made available under the terms of the BSD License > +\ * which accompanies this distribution, and is available at > +\ * http://www.opensource.org/licenses/bsd-license.php > +\ * > +\ * Contributors: > +\ * IBM Corporation - initial implementation > +\ ****************************************************************************/ > + > +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm > + > +" /" find-device > + > +new-device > + > +false VALUE vtpm-debug? > +0 VALUE log-base > +40000 CONSTANT LOG-SIZE \ 256k per VTPM FW spec. What is this spec's name exactly? It may not be available to the public but I could try and get it in IBM internally. > + > +e CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER > + > +LOG-SIZE BUFFER: log-base > + > +\ create /ibm,vtpm > +s" ibm,vtpm" 2dup device-name device-type > + > +\ convey logbase and size to the C driver > +log-base LOG-SIZE tpm-set-log-parameters > + > +: sml-get-allocated-size ( -- buffer-size) > + vtpm-debug? IF > + ." Call to sml-get-allocated-size; size = 0x" LOG-SIZE . cr > + THEN > + LOG-SIZE > +; > + > +: sml-get-handover-size ( -- size ) > + tpm-get-logsize > + vtpm-debug? IF > + ." Call to sml-get-handover-size; size = 0x" dup . cr > + THEN > +; > + > +: sml-handover ( dest size -- ) > + vtpm-debug? IF > + 2dup > + ." Call to sml-handover; size = 0x" . ." dest = " . cr > + THEN > + log-base ( dest size src ) > + -rot ( src dest size ) > + move > + > + VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason > +; > + > +: get-failure-reason ( -- reason ) > + tpm-driver-get-failure-reason ( reason ) > + vtpm-debug? IF > + ." VTPM: Return value from tpm-driver-get-failure-reason: " dup . cr > + THEN > +; > + > +: reformat-sml-to-efi-alignment ( -- success? ) > + vtpm-debug? IF > + ." Call to reformat-sml-to-efi-alignment" cr > + THEN > + \ a no-op since already byte aligned > + true > +; > + > +\ > +\ internal API calls > +\ > + > +: separator-event ( start-pcr end-pcr -- ) > + tpm-add-event-separators ( errcode ) > + ?dup IF > + ." VTPM: Error code from tpm-add-event-separators: " . cr > + THEN > +; > + > +80 CONSTANT BCV_DEVICE_HDD > + > +: measure-hdd-mbr ( addr -- ) > + 0 7 separator-event > + 200 BCV_DEVICE_HDD ( addr length bootdrv ) > + -rot ( bootdrv addr length ) > + tpm-measure-bcv-mbr ( errcode ) > + ?dup 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 > 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 7efad28..24eacc1 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..4e81a42 > --- /dev/null > +++ b/lib/libtpm/tcgbios.c > @@ -0,0 +1,918 @@ > +/***************************************************************************** > + * 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 Is this "A Protocol for VTPM Communications" from M_vtpm_protocol_v0r3.pdf or something else? > and the TCG Specification > + * that can be found here under the following link: > + * http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios The link is broken. > + */ > + > +#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)) > + > +struct tpm_state { > + unsigned tpm_probed:1; > + unsigned tpm_found:1; > + unsigned tpm_working:1; > + > + /* base address of the log area */ > + uint8_t *log_base; > + > + /* size of the logging area */ > + size_t log_area_size; > + > + /* where to write the next log entry to */ > + uint8_t *log_area_next_entry; > +}; > + > +static struct tpm_state tpm_state; You do not pass a pointer to tpm_state anywhere (it would great if you did - this way a reader could tell what functions actually need it) so you do not need "struct tpm_state" type, can be just "struct tpm_state { ... } tpm_state;" > + > +/* > + * TPM 2 logs are written in little endian format. > + */ > +static inline uint32_t log32_to_cpu(uint32_t val) > +{ > + return le32_to_cpu(val); > +} > + > +static inline uint32_t cpu_to_log32(uint32_t val) > +{ > + return cpu_to_le32(val); > +} > + > +static inline uint16_t cpu_to_log16(uint16_t val) > +{ > + return cpu_to_le16(val); > +} > + > +/******************************************************** > + Extensions for TCG-enabled BIOS > + *******************************************************/ > + > +static void probe_tpm(void) > +{ > + tpm_state.tpm_probed = true; > + tpm_state.tpm_found = spapr_is_vtpm_present(); > + tpm_state.tpm_working = tpm_state.tpm_found; > +} > + > +/**************************************************************** > + * Digest formatting > + ****************************************************************/ > + > +static uint32_t tpm20_pcr_selection_size; > +static struct tpml_pcr_selection *tpm20_pcr_selection; Any reason these are not in tpm_state? > + > +/* A 'struct tpm_log_entry' is a local data structure containing a > + * 'tpm_log_header' followed by space for the maximum supported > + * digest. The digest is a series of tpm2_digest_value structs on tpm2.0. > + */ This comment is rather useless, what I would really be interested to is the endianness (little endian because it is v2.0?) and the name of the spec which defines it. > +struct tpm_log_entry { > + struct tpm_log_header hdr; > + uint8_t pad[sizeof(struct tpm2_digest_values) > + + 5 * sizeof(struct tpm2_digest_value) > + + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE > + + SHA512_BUFSIZE + SM3_256_BUFSIZE]; > +} __attribute__((packed)); > + > +static const struct hash_parameters { > + uint16_t hashalg; > + uint8_t hashalg_flag; > + uint8_t hash_buffersize; > + const char *name; name and hashalg_flag are not used. > +} hash_parameters[] = { > + { > + .hashalg = TPM2_ALG_SHA1, TPM2_ALG_xxx are basically 1<<index_in_this_array and can be dropped, can't they? > + .hash_buffersize = SHA1_BUFSIZE, > + }, { > + .hashalg = TPM2_ALG_SHA256, > + .hash_buffersize = SHA256_BUFSIZE, > + }, { > + .hashalg = TPM2_ALG_SHA384, > + .hash_buffersize = SHA384_BUFSIZE, > + }, { > + .hashalg = TPM2_ALG_SHA512, > + .hash_buffersize = SHA512_BUFSIZE, > + }, { > + .hashalg = TPM2_ALG_SM3_256, > + .hash_buffersize = SM3_256_BUFSIZE, > + } > +}; > + > +static int > +tpm20_get_hash_buffersize(uint16_t hashAlg) > +{ > + unsigned i; > + > + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { > + if (hash_parameters[i].hashalg == hashAlg) > + return hash_parameters[i].hash_buffersize; > + } > + return -1; Just make it return (const struct hash_parameters *) as 7/7 adds one helper like this for every field of hash_parameters. > +} > + > +/* > + * Build the TPM2 tpm2_digest_values data structure from the given hash. > + * Follow the PCR bank configuration of the TPM and write the same hash > + * in either truncated or zero-padded form in the areas of all the other > + * hashes. For example, write the sha1 hash in the area of the sha256 > + * hash and fill the remaining bytes with zeros. Or truncate the sha256 > + * hash when writing it in the area of the sha1 hash. It must be correct since you did this but what is the point of this? If the digest does not fit, fill it with 0xBAADF00D and return an error? > + * > + * 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) Is not it always little endian as it is v2.0 only? > +{ > + struct tpms_pcr_selection *sel; > + void *nsel, *end; > + void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values); > + uint32_t count; > + struct tpm2_digest_value *v; > + struct tpm2_digest_values *vs; > + > + if (!tpm20_pcr_selection) This is allocated at tpm20_startup() and if allocation failed, then we should not get this far, or can we? > + return -1; > + > + sel = tpm20_pcr_selection->selections; > + end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; > + > + for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) { > + int hsize; > + uint8_t sizeOfSelect = sel->sizeOfSelect; > + > + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; > + if (nsel > end) > + break; > + > + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); > + if (hsize < 0) { > + dprintf("TPM is using an unsupported hash: %d\n", > + be16_to_cpu(sel->hashAlg)); > + return -1; > + } > + > + /* buffer size sanity check before writing */ > + v = dest; > + if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) { > + dprintf("tpm_log_entry is too small\n"); > + return -1; > + } > + > + if (bigEndian) > + v->hashAlg = sel->hashAlg; > + else > + v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg)); > + > + memset(v->hash, 0, hsize); > + memcpy(v->hash, 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.digest; > + if (bigEndian) > + vs->count = cpu_to_be32(count); > + else > + vs->count = cpu_to_le32(count); > + > + return dest - (void*)le->hdr.digest; > +} > + > +/**************************************************************** > + * TPM hardware command wrappers > + ****************************************************************/ > + > +/* Helper function for sending TPM commands that take a single > + * optional parameter (0, 1, or 2 bytes) and have no special response. > + */ > +static int > +tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param, > + enum tpm_duration_type to_t) > +{ > + struct { > + struct tpm_req_header trqh; > + uint16_t param; > + } __attribute__((packed)) req = { > + .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size), > + .trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .trqh.ordinal = cpu_to_be32(ordinal), > + }; > + uint8_t obuffer[64]; > + struct tpm_rsp_header *trsh = (void *)obuffer; > + uint32_t obuffer_len = sizeof(obuffer); > + int ret; > + > + switch (param_size) { > + case 2: > + req.param = cpu_to_be16(param); > + break; > + case 1: > + *(uint8_t *)&req.param = param; > + break; > + } > + > + memset(obuffer, 0, sizeof(obuffer)); > + ret = tpmhw_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t); > + ret = ret ? -1 : be32_to_cpu(trsh->errcode); > + dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n", > + ordinal, param, ret); > + > + return ret; > +} > + > +static int > +tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count, > + struct tpm_rsp_header *rsp, uint32_t rsize) > +{ > + struct tpm2_req_getcapability trg = { > + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(trg)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), > + .capability = cpu_to_be32(capability), > + .property = cpu_to_be32(property), > + .propertycount = cpu_to_be32(count), > + }; > + > + uint32_t resp_size = rsize; > + int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size, > + TPM_DURATION_TYPE_SHORT); > + ret = (ret || > + rsize < be32_to_cpu(rsp->totlen)) ? -1 > + : be32_to_cpu(rsp->errcode); > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +static int > +tpm20_get_pcrbanks(void) > +{ > + uint8_t buffer[128]; > + uint32_t size; > + struct tpm2_res_getcapability *trg = > + (struct tpm2_res_getcapability *)&buffer; > + > + int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, > + sizeof(buffer)); > + if (ret) > + return ret; > + > + /* defend against (broken) TPM sending packets that are too short */ > + uint32_t resplen = be32_to_cpu(trg->hdr.totlen); -Werror=declaration-after-statement is not enforced in SLOF (may be I should fix it...) but we try follow the linux coding style here in SLOF :) There are few spots like this. > + if (resplen <= offset_of(struct tpm2_res_getcapability, data)) > + return -1; > + > + size = resplen - offset_of(struct tpm2_res_getcapability, data); > + /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/ > + if (size < offset_of(struct tpml_pcr_selection, selections) + > + offset_of(struct tpms_pcr_selection, pcrSelect)) > + return -1; > + > + tpm20_pcr_selection = SLOF_alloc_mem(size); > + if (tpm20_pcr_selection) { > + memcpy(tpm20_pcr_selection, &trg->data, size); > + tpm20_pcr_selection_size = size; > + } else { > + printf("TCGBIOS: Failed to allocated %u bytes.\n", size); > + ret = -1; return -1; > + } > + > + return ret; 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; > + > + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); > + memcpy(&tre->digest[0], le->hdr.digest, digest_len); > + > + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len); > + > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_SHORT); > + if (ret || resp_length != sizeof(rsp) || rsp.errcode) > + return -1; > + > + return 0; > +} > + > +static int tpm20_stirrandom(void) > +{ > + struct tpm2_req_stirrandom stir = { > + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(stir)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom), > + .size = cpu_to_be16(sizeof(stir.stir)), > + .stir = rand(), > + }; > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + int ret = tpmhw_transmit(0, &stir.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_SHORT); > + if (ret || resp_length != sizeof(rsp) || rsp.errcode) > + ret = -1; > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len) > +{ > + struct tpm2_res_getrandom rsp; > + struct tpm2_req_getrandom trgr = { > + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(trgr)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom), > + .bytesRequested = cpu_to_be16(buf_len), > + }; > + uint32_t resp_length = sizeof(rsp); > + > + if (buf_len > sizeof(rsp.rnd.buffer)) > + return -1; > + > + int ret = tpmhw_transmit(0, &trgr.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_MEDIUM); > + if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) > + ret = -1; > + else > + memcpy(buf, rsp.rnd.buffer, buf_len); > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +static int tpm20_hierarchychangeauth(uint8_t auth[20]) > +{ > + struct tpm2_req_hierarchychangeauth trhca = { > + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(trhca)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth), > + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), > + .authblocksize = cpu_to_be32(sizeof(trhca.authblock)), > + .authblock = { > + .handle = cpu_to_be32(TPM2_RS_PW), > + .noncesize = cpu_to_be16(0), > + .contsession = TPM2_YES, > + .pwdsize = cpu_to_be16(0), > + }, > + .newAuth = { > + .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)), > + }, > + }; > + memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer)); > + > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + int ret = tpmhw_transmit(0, &trhca.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_MEDIUM); > + if (ret || resp_length != sizeof(rsp) || rsp.errcode) > + ret = -1; > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state) > +{ > + /* we will try to deactivate the TPM now - ignoring all errors */ Is the comment correct? Neither the name of the function nor the structure below suggest deactivation, I may also be missing the point here. > + struct tpm2_req_hierarchycontrol trh = { > + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), > + .hdr.totlen = cpu_to_be32(sizeof(trh)), > + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl), > + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), > + .authblocksize = cpu_to_be32(sizeof(trh.authblock)), > + .authblock = { > + .handle = cpu_to_be32(TPM2_RS_PW), > + .noncesize = cpu_to_be16(0), > + .contsession = TPM2_YES, > + .pwdsize = cpu_to_be16(0), > + }, > + .enable = cpu_to_be32(hierarchy), > + .state = state, > + }; > + struct tpm_rsp_header rsp; > + uint32_t resp_length = sizeof(rsp); > + int ret = tpmhw_transmit(0, &trh.hdr, &rsp, &resp_length, > + TPM_DURATION_TYPE_MEDIUM); > + if (ret || resp_length != sizeof(rsp) || rsp.errcode) > + ret = -1; > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n", > + ret); > + > + return ret; > +} > + > +/**************************************************************** > + * Setup and Measurements > + ****************************************************************/ > + > +bool tpm_is_working(void) > +{ > + if (!tpm_state.tpm_probed) > + probe_tpm(); > + > + return tpm_state.tpm_working; > +} > + > +static void tpm_set_failure(void) > +{ > + tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO); > + tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO); > + > + tpm_state.tpm_working = false; > +} > + > +/* > + * Extend the OFDT log with the given entry by copying the > + * entry data into the log. > + * > + * @pcpes: Pointer to the structure to be copied into the log > + * @event: The event to be appended to 'pcpes' > + * @event_length: The length of the event > + * > + * Returns 0 on success, an error code otherwise. > + */ > +static uint32_t tpm_log_event_long(struct tpm_log_header *entry, > + int digest_len, > + const void *event, uint32_t event_length) > +{ > + size_t size, logsize; > + void *dest; > + > + dprintf("log base address = %p, next entry = %p\n", > + tpm_state.log_base, tpm_state.log_area_next_entry); > + > + if (tpm_state.log_area_next_entry == NULL) > + return TCGBIOS_LOGOVERFLOW; > + > + size = sizeof(*entry) + digest_len + > + sizeof(struct tpm_log_trailer) + event_length; > + logsize = (tpm_state.log_area_next_entry + size - > + tpm_state.log_base); > + if (logsize > tpm_state.log_area_size) { > + dprintf("TCGBIOS: LOG OVERFLOW: size = %zu\n", size); > + return TCGBIOS_LOGOVERFLOW; > + } > + > + dest = tpm_state.log_area_next_entry; > + memcpy(dest, entry, sizeof(*entry) + digest_len); > + struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len; > + t->eventdatasize = cpu_to_log32(event_length); > + if (event_length) > + memcpy(t->event, event, event_length); > + > + tpm_state.log_area_next_entry += size; > + > + return 0; > +} > + > +/* Add an entry at the start of the log describing digest formats > + */ > +static int tpm20_write_EfiSpecIdEventStruct(void) > +{ > + if (!tpm20_pcr_selection) > + return -1; > + > + struct { > + struct TCG_EfiSpecIdEventStruct hdr; > + uint32_t pad[256]; > + } event = { > + .hdr.signature = "Spec ID Event03", > + .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, > + .hdr.specVersionMinor = 0, > + .hdr.specVersionMajor = 2, > + .hdr.specErrata = 0, > + .hdr.uintnSize = 2, > + }; > + > + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; > + void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; > + int event_size; > + uint32_t *vendorInfoSize; > + struct tpm_log_entry le = { > + .hdr.eventtype = cpu_to_log32(EV_NO_ACTION), > + }; > + uint32_t count; > + > + for (count = 0; > + count < be32_to_cpu(tpm20_pcr_selection->count); > + count++) { > + int hsize; > + uint8_t sizeOfSelect = sel->sizeOfSelect; > + > + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; > + if (nsel > end) > + break; > + > + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); > + if (hsize < 0) { > + dprintf("TPM is using an unsupported hash: %d\n", > + be16_to_cpu(sel->hashAlg)); > + return -1; > + } > + > + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, > + digestSizes[count+1]); > + if (event_size > sizeof(event) - sizeof(uint32_t)) { > + dprintf("EfiSpecIdEventStruct pad too small\n"); > + return -1; > + } > + > + event.hdr.digestSizes[count].algorithmId = > + cpu_to_log16(be16_to_cpu(sel->hashAlg)); > + event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize); > + > + sel = nsel; > + } > + > + if (sel != end) { > + dprintf("Malformed pcr selection structure fron TPM\n"); > + return -1; > + } > + > + event.hdr.numberOfAlgorithms = cpu_to_log32(count); > + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, > + digestSizes[count]); > + vendorInfoSize = (void*)&event + event_size; > + *vendorInfoSize = 0; > + event_size += sizeof(*vendorInfoSize); > + > + return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size); > +} > + > +static int tpm20_startup(void) > +{ > + int ret; > + > + ret = tpm_simple_cmd(0, TPM2_CC_Startup, > + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); > + dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n", > + ret); > + > + if (ret) > + goto err_exit; > + > + ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, > + 1, TPM2_YES, TPM_DURATION_TYPE_LONG); > + > + dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n", > + ret); > + > + if (ret) > + goto err_exit; > + > + ret = tpm20_get_pcrbanks(); > + if (ret) > + goto err_exit; > + > + /* the log parameters will be passed from Forth layer */ > + > + return 0; > + > +err_exit: > + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_set_failure(); > + return -1; > +} > + > +uint32_t tpm_start(void) > +{ > + probe_tpm(); > + > + if (!tpm_is_working()) { > + dprintf("%s: Machine does not have a working TPM\n", > + __func__); > + return TCGBIOS_FATAL_COM_ERROR; > + } > + > + return tpm20_startup(); > +} > + > +void tpm_finalize(void) > +{ > + spapr_vtpm_finalize(); > +} > + > +static void tpm20_prepboot(void) > +{ > + uint8_t auth[20]; > + int ret = tpm20_stirrandom(); > + if (ret) > + goto err_exit; > + > + ret = tpm20_getrandom(&auth[0], sizeof(auth)); > + if (ret) > + goto err_exit; > + > + ret = tpm20_hierarchychangeauth(auth); > + if (ret) > + goto err_exit; > + > + return; > + > +err_exit: > + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_set_failure(); > +} > + > +/* > + * Prepare TPM for boot; this function has to be called before > + * the firmware transitions to the boot loader. I do not see any caller of this one. > + */ > +uint32_t tpm_leave_firmware(void) static? > +{ > + 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; > + > + sha256(hashdata, hashdatalen, hash); > + digest_len = tpm20_build_digest(&le, hash, true); > + if (digest_len < 0) > + return TCGBIOS_GENERAL_ERROR; > + int ret = tpm20_extend(&le, digest_len); > + if (ret) { > + tpm_set_failure(); > + return TCGBIOS_COMMAND_ERROR; > + } > + tpm20_build_digest(&le, hash, false); > + return tpm_log_event_long(&le.hdr, digest_len, info, infolen); > +} > + > +/* > + * Add an EV_ACTION measurement to the list of measurements > + */ > +static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string) > +{ > + uint32_t len = strlen(string); > + > + return tpm_add_measurement_to_log(pcrIndex, EV_ACTION, > + string, len, (uint8_t *)string, len); > +} > + > +/* > + * Add event separators for a range of PCRs > + */ > +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr) > +{ > + static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; > + uint32_t rc = 0; > + uint32_t pcrIndex; > + > + if (!tpm_is_working()) > + return TCGBIOS_GENERAL_ERROR; > + > + if (start_pcr >= 24 || start_pcr > end_pcr) > + return TCGBIOS_INVALID_INPUT_PARA; > + > + /* event separators need to be extended and logged for PCRs 0-7 */ > + for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { > + rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, > + NULL, 0, > + evt_separator, > + sizeof(evt_separator)); > + if (rc) > + break; return rc; > + } > + > + 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 | sha1sum > + */ > + string = "MBR"; What about GPT (I am not extemely familiar with MBR/GPT but I see my ubuntu images use GPT these days)? > + rc = tpm_add_measurement_to_log(4, EV_IPL, > + string, strlen(string), > + addr, 0x1b8); > + if (rc) > + return rc; > + > + /* > + * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum > + */ > + string = "MBR PARTITION TABLE"; > + return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, > + string, strlen(string), > + addr + 0x1b8, 0x48); > +} > + > +uint32_t tpm_measure_scrtm(void) > +{ > + uint32_t rc; > + char *version_start = strstr((char *)&print_version, "FW Version"); > + char *version_end; > + uint32_t version_length; > + char *slof_data_start = (char *)&_slof_data; > + char *slof_text_start = (char *)&_slof_text; > + uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data; > + uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text; > + const char *scrtm = "S-CRTM Contents"; > + > + version_end = strchr(version_start, '\r'); > + version_length = version_end - version_start; > + > + dprintf("Measure S-CRTM Version: addr = %p, length = %d\n", > + version_start, version_length); > + > + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION, > + version_start, version_length, > + (uint8_t *)version_start, > + version_length); > + if (rc) > + return rc; > + > + dprintf("Measure S-CRTM Content (data): start = %p, length = %d\n", > + slof_data_start, slof_data_length); > + > + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS, > + scrtm, strlen(scrtm), > + (uint8_t *)slof_data_start, > + slof_data_length); > + 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..3cc4f46 100644 > --- a/lib/libtpm/tcgbios_int.h > +++ b/lib/libtpm/tcgbios_int.h > @@ -15,6 +15,94 @@ > > #include <stdint.h> > > +/* internal error codes */ > +#define TCGBIOS_OK 0x0 > +#define TCGBIOS_LOGOVERFLOW 0x1 > +#define TCGBIOS_GENERAL_ERROR 0x2 > +#define TCGBIOS_FIRMWARE_ERROR 0x3 > +#define TCGBIOS_FATAL_COM_ERROR 0x4 > +#define TCGBIOS_INVALID_INPUT_PARA 0x5 > +#define TCGBIOS_COMMAND_ERROR 0x6 > +#define TCGBIOS_INTERFACE_SHUTDOWN 0x7 > + > +/* event types */ > +#define EV_POST_CODE 1 > +#define EV_NO_ACTION 3 > +#define EV_SEPARATOR 4 > +#define EV_ACTION 5 > +#define EV_EVENT_TAG 6 > +#define EV_S_CRTM_CONTENTS 7 > +#define EV_S_CRTM_VERSION 8 > +#define EV_IPL 13 > +#define EV_IPL_PARTITION_DATA 14 > + > +#define SHA1_BUFSIZE 20 > +#define SHA256_BUFSIZE 32 > +#define SHA384_BUFSIZE 48 > +#define SHA512_BUFSIZE 64 > +#define SM3_256_BUFSIZE 32 > + > +#define BCV_DEVICE_HDD 0x80 > + > +struct tpm2_digest_value { > + uint16_t hashAlg; > + uint8_t hash[0]; /* size depends on hashAlg */ > +} __attribute__((packed)); > + > +struct tpm2_digest_values { > + uint32_t count; > + struct tpm2_digest_value digest[0]; > +} __attribute__((packed)); > + > +/* Each entry in the TPM log contains: a tpm_log_header, a variable > + * length digest, a tpm_log_trailer, and a variable length event. The > + * 'digest' matches what is sent to the TPM hardware via the Extend > + * command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest > + * contains a tpm2_digest_values struct followed by a variable number > + * of tpm2_digest_value structs (as specified by the hardware via the > + * TPM2_CAP_PCRS request). > + */ > +struct tpm_log_header { > + uint32_t pcrindex; > + uint32_t eventtype; > + uint8_t digest[0]; > +} __attribute__((packed)); > + > +struct tpm_log_trailer { > + uint32_t eventdatasize; > + uint8_t event[0]; > +} __attribute__((packed)); > + > +struct TCG_EfiSpecIdEventStruct { > + uint8_t signature[16]; > + uint32_t platformClass; > +#define TPM_TCPA_ACPI_CLASS_CLIENT 0 > + uint8_t specVersionMinor; > + uint8_t specVersionMajor; > + uint8_t specErrata; > + uint8_t uintnSize; > + uint32_t numberOfAlgorithms; > + struct TCG_EfiSpecIdEventAlgorithmSize { > + uint16_t algorithmId; > + uint16_t digestSize; > + } digestSizes[0]; > + /* > + uint8_t vendorInfoSize; > + uint8_t vendorInfo[0]; > + */ > +} __attribute__((packed)); > + > +/* Input and Output blocks for the TCG BIOS commands */ > + > +/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */ > +struct pcpes { > + uint32_t pcrindex; > + uint32_t eventtype; > + uint8_t digest[SHA1_BUFSIZE]; > + uint32_t eventdatasize; > + uint32_t event; > +} __attribute__((packed)); > + > struct tpm_req_header { > uint16_t tag; > uint32_t totlen; > @@ -27,4 +115,156 @@ struct tpm_rsp_header { > uint32_t errcode; > } __attribute__((packed)); > > +/**************************************************************** > + * TPM v2.0 hardware commands > + ****************************************************************/ > + > +#define TPM2_NO 0 > +#define TPM2_YES 1 > + > +#define TPM2_SU_CLEAR 0x0000 > +#define TPM2_SU_STATE 0x0001 > + > +#define TPM2_RH_OWNER 0x40000001 > +#define TPM2_RS_PW 0x40000009 > +#define TPM2_RH_ENDORSEMENT 0x4000000b > +#define TPM2_RH_PLATFORM 0x4000000c > + > +#define TPM2_ALG_SHA1 0x0004 > +#define TPM2_ALG_SHA256 0x000b > +#define TPM2_ALG_SHA384 0x000c > +#define TPM2_ALG_SHA512 0x000d > +#define TPM2_ALG_SM3_256 0x0012 > + > +#define TPM2_ALG_SHA1_FLAG (1 << 0) > +#define TPM2_ALG_SHA256_FLAG (1 << 1) > +#define TPM2_ALG_SHA384_FLAG (1 << 2) > +#define TPM2_ALG_SHA512_FLAG (1 << 3) > +#define TPM2_ALG_SM3_256_FLAG (1 << 4) > + > +/* TPM 2 command tags */ > +#define TPM2_ST_NO_SESSIONS 0x8001 > +#define TPM2_ST_SESSIONS 0x8002 > + > +/* TPM 2 commands */ > +#define TPM2_CC_HierarchyControl 0x121 > +#define TPM2_CC_Clear 0x126 > +#define TPM2_CC_ClearControl 0x127 > +#define TPM2_CC_HierarchyChangeAuth 0x129 > +#define TPM2_CC_PCR_Allocate 0x12b > +#define TPM2_CC_SelfTest 0x143 > +#define TPM2_CC_Startup 0x144 > +#define TPM2_CC_Shutdown 0x145 > +#define TPM2_CC_StirRandom 0x146 > +#define TPM2_CC_GetCapability 0x17a > +#define TPM2_CC_GetRandom 0x17b > +#define TPM2_CC_PCR_Extend 0x182 > + > +/* TPM 2 Capabilities */ > +#define TPM2_CAP_PCRS 0x00000005 > + > +/* TPM 2 data structures */ > + > +struct tpm2_req_stirrandom { > + struct tpm_req_header hdr; > + uint16_t size; > + uint64_t stir; > +} __attribute__((packed)); > + > +struct tpm2_req_getrandom { > + struct tpm_req_header hdr; > + uint16_t bytesRequested; > +} __attribute__((packed)); > + > +struct tpm2b_20 { > + uint16_t size; > + uint8_t buffer[20]; > +} __attribute__((packed)); > + > +struct tpm2_res_getrandom { > + struct tpm_rsp_header hdr; > + struct tpm2b_20 rnd; > +} __attribute__((packed)); > + > +struct tpm2_authblock { > + uint32_t handle; > + uint16_t noncesize; /* always 0 */ > + uint8_t contsession; /* always TPM2_YES */ > + uint16_t pwdsize; /* always 0 */ > +} __attribute__((packed)); > + > +struct tpm2_req_hierarchychangeauth { > + struct tpm_req_header hdr; > + uint32_t authhandle; > + uint32_t authblocksize; > + struct tpm2_authblock authblock; > + struct tpm2b_20 newAuth; > +} __attribute__((packed)); > + > +struct tpm2_req_extend { > + struct tpm_req_header hdr; > + uint32_t pcrindex; > + uint32_t authblocksize; > + struct tpm2_authblock authblock; > + uint8_t digest[0]; > +} __attribute__((packed)); > + > +struct tpm2_req_clearcontrol { > + struct tpm_req_header hdr; > + uint32_t authhandle; > + uint32_t authblocksize; > + struct tpm2_authblock authblock; > + uint8_t disable; > +} __attribute__((packed)); > + > +struct tpm2_req_clear { > + struct tpm_req_header hdr; > + uint32_t authhandle; > + uint32_t authblocksize; > + struct tpm2_authblock authblock; > +} __attribute__((packed)); > + > +struct tpm2_req_hierarchycontrol { > + struct tpm_req_header hdr; > + uint32_t authhandle; > + uint32_t authblocksize; > + struct tpm2_authblock authblock; > + uint32_t enable; > + uint8_t state; > +} __attribute__((packed)); > + > +struct tpm2_req_getcapability { > + struct tpm_req_header hdr; > + uint32_t capability; > + uint32_t property; > + uint32_t propertycount; > +} __attribute__((packed)); > + > +struct tpm2_res_getcapability { > + struct tpm_rsp_header hdr; > + uint8_t moreData; > + uint32_t capability; > + uint8_t data[0]; /* capability dependent data */ > +} __attribute__((packed)); > + > +struct tpm2_req_pcr_allocate { > + struct tpm_req_header hdr; > + uint32_t authhandle; > + uint32_t authblocksize; > + struct tpm2_authblock authblock; > + uint32_t count; > + uint8_t tpms_pcr_selections[4]; > +} __attribute__((packed)); > + > +struct tpms_pcr_selection { > + uint16_t hashAlg; > + uint8_t sizeOfSelect; > + uint8_t pcrSelect[0]; > +} __attribute__((packed)); > + > +struct tpml_pcr_selection { > + uint32_t count; > + struct tpms_pcr_selection selections[0]; > +} __attribute__((packed)); > + Please separate somehow what is defined for SLOF only (is TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what spec. > #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() */ > +/************************************************/ What is calling this one? Now I am really not sure they all are actually have a user. Thanks, > +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..77bb0f5 100644 > --- a/slof/fs/packages/disk-label.fs > +++ b/slof/fs/packages/disk-label.fs > @@ -550,7 +550,15 @@ B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2 > \ load from a bootable partition > : load-from-boot-partition ( addr -- size ) > debug-disk-label? IF ." Trying DOS boot " .s cr THEN > - dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN > + dup load-from-dos-boot-partition ?dup 0 <> IF > + nip > + block s" /ibm,vtpm" find-node ?dup IF > + s" measure-hdd-mbr" rot $call-static > + ELSE > + drop > + THEN > + EXIT > + THEN > > debug-disk-label? IF ." Trying CHRP boot " .s cr THEN > 1 disk-chrp-boot ! > 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 >
On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote: > > On 16/01/2020 07:00, Stefan Berger wrote: >> This patch adds TPM 2.0 support along with the firmware API >> that Linux uses to transfer the firmware log. >> >> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> >> --- >> board-qemu/slof/Makefile | 13 +- >> board-qemu/slof/tree.fs | 3 + >> board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++ >> board-qemu/slof/vtpm-sml.fs | 115 ++++ >> include/helpers.h | 1 + >> lib/libtpm/Makefile | 2 +- >> lib/libtpm/tcgbios.c | 918 ++++++++++++++++++++++++++++ >> lib/libtpm/tcgbios.h | 32 + >> lib/libtpm/tcgbios_int.h | 240 ++++++++ >> lib/libtpm/tpm.code | 130 ++++ >> lib/libtpm/tpm.in | 26 + >> slof/fs/packages/disk-label.fs | 10 +- >> slof/fs/start-up.fs | 5 + >> 13 files changed, 1624 insertions(+), 5 deletions(-) >> create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs >> create mode 100644 board-qemu/slof/vtpm-sml.fs >> create mode 100644 lib/libtpm/tcgbios.c >> create mode 100644 lib/libtpm/tcgbios.h >> create mode 100644 lib/libtpm/tpm.code >> create mode 100644 lib/libtpm/tpm.in >> >> 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 \ > > s/vio-vtpm-cdriver.fs/vio-vtpm.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..8d17d0e >> --- /dev/null >> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs >> @@ -0,0 +1,134 @@ >> +\ ***************************************************************************** >> +\ * Copyright (c) 2015-2020 IBM Corporation >> +\ * All rights reserved. >> +\ * This program and the accompanying materials >> +\ * are made available under the terms of the BSD License >> +\ * which accompanies this distribution, and is available at >> +\ * http://www.opensource.org/licenses/bsd-license.php >> +\ * >> +\ * Contributors: >> +\ * IBM Corporation - initial implementation >> +\ ****************************************************************************/ >> + >> +." Populating " pwd >> + >> +false VALUE vtpm-debug? >> +0 VALUE vtpm-unit >> +0 VALUE vtpm-ihandle >> + >> +: setup-alias >> + " ibm,vtpm" find-alias 0= IF >> + " ibm,vtpm" get-node node>path set-alias >> + ELSE >> + drop >> + THEN >> +; >> + >> +: vtpm-cleanup ( ) >> + vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN >> + tpm-finalize >> + vtpm-unit 0 rtas-set-tce-bypass >> +; >> + >> +: vtpm-init ( -- true | false ) >> + 0 0 get-node open-node ?dup 0= IF EXIT THEN >> + my-self >r >> + dup to my-self >> + >> + vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN >> + >> + my-unit to vtpm-unit >> + >> + \ Enable TCE bypass special qemu feature >> + vtpm-unit 1 rtas-set-tce-bypass >> + >> + \ Have TCE bypass cleaned up >> + ['] vtpm-cleanup add-quiesce-xt >> + >> + tpm-start ?dup 0= IF >> + vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN >> + setup-alias >> + ELSE >> + ." VTPM: Error code from tpm-start: " . cr >> + THEN >> + >> + close-node >> + r> to my-self >> +; >> + >> +\ forward a call to /ibm,vtpm, which implements the function with the >> +\ given name >> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? ) >> + \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned >> + vtpm-ihandle 0= IF >> + s" /ibm,vtpm" open-dev to vtpm-ihandle > > Why does not "open" do this? Is this vtpm supposed to run even before > the client tries using the vtpm services? It does not look like it. Initialization of the vTPM is supposed to happen before any client can talk to the firmware. > > >> + THEN >> + >> + vtpm-ihandle 0<> IF >> + vtpm-ihandle ( arg ... arg name namelen ihandle ) >> + $call-method ( ret ... ret ) >> + false ( ret ... ret false ) >> + ELSE >> + true ( true ) >> + THEN >> +; >> + >> +\ firmware API call >> +: sml-get-allocated-size ( -- buffer-size) >> + " sml-get-allocated-size" vtpm-call-forward IF >> + \ vtpm-call-forward failed >> + 0 >> + THEN >> +; >> + >> +\ firmware API call >> +: sml-get-handover-size ( -- size) >> + " sml-get-handover-size" vtpm-call-forward IF >> + \ vtpm-call-forward failed >> + 0 >> + THEN >> +; >> + >> +\ firmware API call >> +: sml-handover ( dest size -- ) >> + " sml-handover" vtpm-call-forward IF >> + \ vtpm-call-forward failed; clean up stack >> + 2drop >> + THEN >> +; >> + >> +\ firmware API call >> +: get-failure-reason ( -- reason ) >> + " get-failure-reason" vtpm-call-forward IF >> + \ vtpm-call-forward failed; return a value >> + 0 \ invalid >> + THEN >> +; >> + >> +0 0 s" ibm,sml-efi-reformat-supported" property >> + >> +\ firmware API call >> +: reformat-sml-to-efi-alignment ( -- success ) >> + " reformat-sml-to-efi-alignment" vtpm-call-forward IF >> + false >> + THEN >> +; >> + >> +: open ( ) >> + vtpm-debug? IF ." VTPM: vTPM open()" cr THEN >> + true >> +; >> + >> +: close ( ) >> + vtpm-debug? IF ." VTPM: vTPM close()" cr THEN >> +; >> + >> +\ setup alias and the RTAS bypass >> +vtpm-init >> + >> +\ setup the log >> +include vtpm-sml.fs >> + >> +s" /ibm,vtpm" find-node ?dup IF >> + s" measure-scrtm" rot $call-static >> +THEN > > The above 22 lines confuse me a lot. > Why vtpm-sml.fs? You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM code into one file? > Why "open" does not open? > Why vtpm-init is not in "open"? > Why the device methods are in vtpm-sml.fs? > > The Linux finds the device, opens it and calls methods (passing > ihandle), why this complication? > > I am missing the point in all of this and 2 lines commit log does not > help at all. > > > > >> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs >> new file mode 100644 >> index 0000000..865dce6 >> --- /dev/null >> +++ b/board-qemu/slof/vtpm-sml.fs >> @@ -0,0 +1,115 @@ >> +\ ***************************************************************************** >> +\ * Copyright (c) 2015-2020 IBM Corporation >> +\ * All rights reserved. >> +\ * This program and the accompanying materials >> +\ * are made available under the terms of the BSD License >> +\ * which accompanies this distribution, and is available at >> +\ * http://www.opensource.org/licenses/bsd-license.php >> +\ * >> +\ * Contributors: >> +\ * IBM Corporation - initial implementation >> +\ ****************************************************************************/ >> + >> +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm >> + >> +" /" find-device >> + >> +new-device >> + >> +false VALUE vtpm-debug? >> +0 VALUE log-base >> +40000 CONSTANT LOG-SIZE \ 256k per VTPM FW spec. > > What is this spec's name exactly? It may not be available to the public > but I could try and get it in IBM internally. "PFW Virtual TPM Driver" Version 1.1 (or later) --- /dev/null >> +++ b/lib/libtpm/tcgbios.c >> @@ -0,0 +1,918 @@ >> +/***************************************************************************** >> + * 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 > > Is this "A Protocol for VTPM Communications" from > M_vtpm_protocol_v0r3.pdf or something else? The title of the document is 'PFW Virtual TPM Driver'. PFW_CTPM_CLDD_1.1-1.pdf is the file I have. > > >> and the TCG Specification >> + * that can be found here under the following link: >> + * http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios > > The link is broken. 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)) >> + >> +struct tpm_state { >> + unsigned tpm_probed:1; >> + unsigned tpm_found:1; >> + unsigned tpm_working:1; >> + >> + /* base address of the log area */ >> + uint8_t *log_base; >> + >> + /* size of the logging area */ >> + size_t log_area_size; >> + >> + /* where to write the next log entry to */ >> + uint8_t *log_area_next_entry; >> +}; >> + >> +static struct tpm_state tpm_state; > You do not pass a pointer to tpm_state anywhere (it would great if you > did - this way a reader could tell what functions actually need it) so > you do not need "struct tpm_state" type, can be just "struct tpm_state { > ... } tpm_state;" It's all global variables collected in one structure. I removed the name of the structure. > > > > >> + >> +/* >> + * TPM 2 logs are written in little endian format. >> + */ >> +static inline uint32_t log32_to_cpu(uint32_t val) >> +{ >> + return le32_to_cpu(val); >> +} >> + >> +static inline uint32_t cpu_to_log32(uint32_t val) >> +{ >> + return cpu_to_le32(val); >> +} >> + >> +static inline uint16_t cpu_to_log16(uint16_t val) >> +{ >> + return cpu_to_le16(val); >> +} >> + >> +/******************************************************** >> + Extensions for TCG-enabled BIOS >> + *******************************************************/ >> + >> +static void probe_tpm(void) >> +{ >> + tpm_state.tpm_probed = true; >> + tpm_state.tpm_found = spapr_is_vtpm_present(); >> + tpm_state.tpm_working = tpm_state.tpm_found; >> +} >> + >> +/**************************************************************** >> + * Digest formatting >> + ****************************************************************/ >> + >> +static uint32_t tpm20_pcr_selection_size; >> +static struct tpml_pcr_selection *tpm20_pcr_selection; > > Any reason these are not in tpm_state? I will move them there. > > >> + >> +/* A 'struct tpm_log_entry' is a local data structure containing a >> + * 'tpm_log_header' followed by space for the maximum supported >> + * digest. The digest is a series of tpm2_digest_value structs on tpm2.0. >> + */ > This comment is rather useless, what I would really be interested to is > the endianness (little endian because it is v2.0?) and the name of the > spec which defines it. PPC64 TPM 2.0 logging is supposed to be compatible with client code written for x86_64 Linux. While TPM 1.2 logs on PPC64 were big endian, TPM 2.0 logs are now little endian. I am cc'in Christopher Engel who may have a more recent spec. and told me this is how it's supposed to work. > > >> +struct tpm_log_entry { >> + struct tpm_log_header hdr; >> + uint8_t pad[sizeof(struct tpm2_digest_values) >> + + 5 * sizeof(struct tpm2_digest_value) >> + + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE >> + + SHA512_BUFSIZE + SM3_256_BUFSIZE]; >> +} __attribute__((packed)); >> + >> +static const struct hash_parameters { >> + uint16_t hashalg; >> + uint8_t hashalg_flag; >> + uint8_t hash_buffersize; >> + const char *name; > name and hashalg_flag are not used. True. This occurred when re-splitting the patch. > >> +} hash_parameters[] = { >> + { >> + .hashalg = TPM2_ALG_SHA1, > > TPM2_ALG_xxx are basically 1<<index_in_this_array and can be dropped, > can't they? No: lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA1 0x0004 lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA256 0x000b lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA384 0x000c lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA512 0x000d lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SM3_256 0x0012 > > >> + .hash_buffersize = SHA1_BUFSIZE, >> + }, { >> + .hashalg = TPM2_ALG_SHA256, >> + .hash_buffersize = SHA256_BUFSIZE, >> + }, { >> + .hashalg = TPM2_ALG_SHA384, >> + .hash_buffersize = SHA384_BUFSIZE, >> + }, { >> + .hashalg = TPM2_ALG_SHA512, >> + .hash_buffersize = SHA512_BUFSIZE, >> + }, { >> + .hashalg = TPM2_ALG_SM3_256, >> + .hash_buffersize = SM3_256_BUFSIZE, >> + } >> +}; >> + >> +static int >> +tpm20_get_hash_buffersize(uint16_t hashAlg) >> +{ >> + unsigned i; >> + >> + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { >> + if (hash_parameters[i].hashalg == hashAlg) >> + return hash_parameters[i].hash_buffersize; >> + } >> + return -1; > Just make it return (const struct hash_parameters *) as 7/7 adds one > helper like this for every field of hash_parameters. OK. > > >> +} >> + >> +/* >> + * Build the TPM2 tpm2_digest_values data structure from the given hash. >> + * Follow the PCR bank configuration of the TPM and write the same hash >> + * in either truncated or zero-padded form in the areas of all the other >> + * hashes. For example, write the sha1 hash in the area of the sha256 >> + * hash and fill the remaining bytes with zeros. Or truncate the sha256 >> + * hash when writing it in the area of the sha1 hash. > It must be correct since you did this but what is the point of this? If > the digest does not fit, fill it with 0xBAADF00D and return an error? A TPM 2 has typically 24 platform control registers (PCRs). These registers are storing each a hash (rather than an integer). Basically there is one operation on a hash, which is an 'extend': pcr1 = sha256(pcr1 || hash) So it takes the current value of PCR 1 and concatenates it with the 'hash' to extend it and hashes this concatenation and assigns the result to PCR 1. A TPM 2 may have several hash banks, such as for sha1, sha256, sha384, sha512, sm3-256 etc. So there may be multiple PCR 1 registers, one for the sha1 bank, one for the sha256 bank etc. For the logging of measurements we are only using one hash, that is sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for all the 'active' PCR banks basically. We log the 'hash' value that a PCR was extended so that someone looking at the current PCR value can use the log to replay (simulate) the extensions of the PCRs. Since we are now using sha256 for hashing we will need to truncate the sha256 when logging it for the sha1 bank and zero pad it for sha384 since sha384 has more bytes. There is the integrity measurement architecture (IMA) in Linux that basically does the same thing: > > >> + * >> + * 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) > > Is not it always little endian as it is v2.0 only? There are callers for this function who want to send the resulting data structures to the TPM. Other callers want this for the log written in little endian. > >> +{ >> + struct tpms_pcr_selection *sel; >> + void *nsel, *end; >> + void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values); >> + uint32_t count; >> + struct tpm2_digest_value *v; >> + struct tpm2_digest_values *vs; >> + >> + if (!tpm20_pcr_selection) > > This is allocated at tpm20_startup() and if allocation failed, then we > should not get this far, or can we? It's a safety net. Cannot keep ? > + > +static int > +tpm20_get_pcrbanks(void) > +{ > + uint8_t buffer[128]; > + uint32_t size; > + struct tpm2_res_getcapability *trg = > + (struct tpm2_res_getcapability *)&buffer; > + > + int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, > + sizeof(buffer)); > + if (ret) > + return ret; > + > + /* defend against (broken) TPM sending packets that are too short */ > + uint32_t resplen = be32_to_cpu(trg->hdr.totlen); > > -Werror=declaration-after-statement is not enforced in SLOF (may be I > should fix it...) but we try follow the linux coding style here in SLOF > :) There are few spots like this. I fixed these occurrences. > > > >> + if (resplen <= offset_of(struct tpm2_res_getcapability, data)) >> + return -1; >> + >> + size = resplen - offset_of(struct tpm2_res_getcapability, data); >> + /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/ >> + if (size < offset_of(struct tpml_pcr_selection, selections) + >> + offset_of(struct tpms_pcr_selection, pcrSelect)) >> + return -1; >> + >> + tpm20_pcr_selection = SLOF_alloc_mem(size); >> + if (tpm20_pcr_selection) { >> + memcpy(tpm20_pcr_selection, &trg->data, size); >> + tpm20_pcr_selection_size = size; >> + } else { >> + printf("TCGBIOS: Failed to allocated %u bytes.\n", size); >> + ret = -1; > return -1; > >> + } >> + >> + return ret; > return 0; Fixed. > >> +} >> + >> +static int tpm20_extend(struct tpm_log_entry *le, int digest_len) >> +{ >> + struct tpm2_req_extend tmp_tre = { >> + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), >> + .hdr.totlen = cpu_to_be32(0), >> + .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), >> + .pcrindex = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)), >> + .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), >> + .authblock = { >> + .handle = cpu_to_be32(TPM2_RS_PW), >> + .noncesize = cpu_to_be16(0), >> + .contsession = TPM2_YES, >> + .pwdsize = cpu_to_be16(0), >> + }, >> + }; >> + uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)]; >> + struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; >> + >> + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); >> + memcpy(&tre->digest[0], le->hdr.digest, digest_len); >> + >> + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len); >> + >> + struct tpm_rsp_header rsp; >> + uint32_t resp_length = sizeof(rsp); >> + int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, >> + TPM_DURATION_TYPE_SHORT); >> + if (ret || resp_length != sizeof(rsp) || rsp.errcode) >> + return -1; >> + >> + return 0; >> +} >> + >> +static int tpm20_stirrandom(void) >> +{ >> + struct tpm2_req_stirrandom stir = { >> + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), >> + .hdr.totlen = cpu_to_be32(sizeof(stir)), >> + .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom), >> + .size = cpu_to_be16(sizeof(stir.stir)), >> + .stir = rand(), >> + }; >> + struct tpm_rsp_header rsp; >> + uint32_t resp_length = sizeof(rsp); >> + int ret = tpmhw_transmit(0, &stir.hdr, &rsp, &resp_length, >> + TPM_DURATION_TYPE_SHORT); >> + if (ret || resp_length != sizeof(rsp) || rsp.errcode) >> + ret = -1; >> + >> + dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n", >> + ret); >> + >> + return ret; >> +} >> + >> +static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len) >> +{ >> + struct tpm2_res_getrandom rsp; >> + struct tpm2_req_getrandom trgr = { >> + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), >> + .hdr.totlen = cpu_to_be32(sizeof(trgr)), >> + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom), >> + .bytesRequested = cpu_to_be16(buf_len), >> + }; >> + uint32_t resp_length = sizeof(rsp); >> + >> + if (buf_len > sizeof(rsp.rnd.buffer)) >> + return -1; >> + >> + int ret = tpmhw_transmit(0, &trgr.hdr, &rsp, &resp_length, >> + TPM_DURATION_TYPE_MEDIUM); >> + if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) >> + ret = -1; >> + else >> + memcpy(buf, rsp.rnd.buffer, buf_len); >> + >> + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n", >> + ret); >> + >> + return ret; >> +} >> + >> +static int tpm20_hierarchychangeauth(uint8_t auth[20]) >> +{ >> + struct tpm2_req_hierarchychangeauth trhca = { >> + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), >> + .hdr.totlen = cpu_to_be32(sizeof(trhca)), >> + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth), >> + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), >> + .authblocksize = cpu_to_be32(sizeof(trhca.authblock)), >> + .authblock = { >> + .handle = cpu_to_be32(TPM2_RS_PW), >> + .noncesize = cpu_to_be16(0), >> + .contsession = TPM2_YES, >> + .pwdsize = cpu_to_be16(0), >> + }, >> + .newAuth = { >> + .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)), >> + }, >> + }; >> + memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer)); >> + >> + struct tpm_rsp_header rsp; >> + uint32_t resp_length = sizeof(rsp); >> + int ret = tpmhw_transmit(0, &trhca.hdr, &rsp, &resp_length, >> + TPM_DURATION_TYPE_MEDIUM); >> + if (ret || resp_length != sizeof(rsp) || rsp.errcode) >> + ret = -1; >> + >> + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n", >> + ret); >> + >> + return ret; >> +} >> + >> +static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state) >> +{ >> + /* we will try to deactivate the TPM now - ignoring all errors */ > > Is the comment correct? Neither the name of the function nor the > structure below suggest deactivation, I may also be missing the point here. I dropped the comment. A caller controlled hierarchy is disabled with this function. From the spec: "This command enables and disables use of a hierarchy and its associated NV storage. The command allows phEnable, phEnableNV, shEnable, and ehEnable to be changed when the proper authorization is provided." > >> + struct tpm2_req_hierarchycontrol trh = { >> + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), >> + .hdr.totlen = cpu_to_be32(sizeof(trh)), >> + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl), >> + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), >> + .authblocksize = cpu_to_be32(sizeof(trh.authblock)), >> + .authblock = { >> + .handle = cpu_to_be32(TPM2_RS_PW), >> + .noncesize = cpu_to_be16(0), >> + .contsession = TPM2_YES, >> + .pwdsize = cpu_to_be16(0), >> + }, >> + .enable = cpu_to_be32(hierarchy), >> + .state = state, >> + }; >> + struct tpm_rsp_header rsp; >> + uint32_t resp_length = sizeof(rsp); >> + int ret = tpmhw_transmit(0, &trh.hdr, &rsp, &resp_length, >> + TPM_DURATION_TYPE_MEDIUM); >> + if (ret || resp_length != sizeof(rsp) || rsp.errcode) >> + ret = -1; >> + >> + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n", >> + ret); >> + >> + return ret; >> +} >> + >> +/**************************************************************** >> + * Setup and Measurements >> + ****************************************************************/ >> + >> +bool tpm_is_working(void) >> +{ >> + if (!tpm_state.tpm_probed) >> + probe_tpm(); >> + >> + return tpm_state.tpm_working; >> +} >> + >> +static void tpm_set_failure(void) >> +{ >> + tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO); >> + tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO); >> + >> + tpm_state.tpm_working = false; >> +} >> + >> +/* >> + * Extend the OFDT log with the given entry by copying the >> + * entry data into the log. >> + * >> + * @pcpes: Pointer to the structure to be copied into the log >> + * @event: The event to be appended to 'pcpes' >> + * @event_length: The length of the event >> + * >> + * Returns 0 on success, an error code otherwise. >> + */ >> +static uint32_t tpm_log_event_long(struct tpm_log_header *entry, >> + int digest_len, >> + const void *event, uint32_t event_length) >> +{ >> + size_t size, logsize; >> + void *dest; >> + >> + dprintf("log base address = %p, next entry = %p\n", >> + tpm_state.log_base, tpm_state.log_area_next_entry); >> + >> + if (tpm_state.log_area_next_entry == NULL) >> + return TCGBIOS_LOGOVERFLOW; >> + >> + size = sizeof(*entry) + digest_len + >> + sizeof(struct tpm_log_trailer) + event_length; >> + logsize = (tpm_state.log_area_next_entry + size - >> + tpm_state.log_base); >> + if (logsize > tpm_state.log_area_size) { >> + dprintf("TCGBIOS: LOG OVERFLOW: size = %zu\n", size); >> + return TCGBIOS_LOGOVERFLOW; >> + } >> + >> + dest = tpm_state.log_area_next_entry; >> + memcpy(dest, entry, sizeof(*entry) + digest_len); >> + struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len; >> + t->eventdatasize = cpu_to_log32(event_length); >> + if (event_length) >> + memcpy(t->event, event, event_length); >> + >> + tpm_state.log_area_next_entry += size; >> + >> + return 0; >> +} >> + >> +/* Add an entry at the start of the log describing digest formats >> + */ >> +static int tpm20_write_EfiSpecIdEventStruct(void) >> +{ >> + if (!tpm20_pcr_selection) >> + return -1; >> + >> + struct { >> + struct TCG_EfiSpecIdEventStruct hdr; >> + uint32_t pad[256]; >> + } event = { >> + .hdr.signature = "Spec ID Event03", >> + .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, >> + .hdr.specVersionMinor = 0, >> + .hdr.specVersionMajor = 2, >> + .hdr.specErrata = 0, >> + .hdr.uintnSize = 2, >> + }; >> + >> + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; >> + void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; >> + int event_size; >> + uint32_t *vendorInfoSize; >> + struct tpm_log_entry le = { >> + .hdr.eventtype = cpu_to_log32(EV_NO_ACTION), >> + }; >> + uint32_t count; >> + >> + for (count = 0; >> + count < be32_to_cpu(tpm20_pcr_selection->count); >> + count++) { >> + int hsize; >> + uint8_t sizeOfSelect = sel->sizeOfSelect; >> + >> + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; >> + if (nsel > end) >> + break; >> + >> + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); >> + if (hsize < 0) { >> + dprintf("TPM is using an unsupported hash: %d\n", >> + be16_to_cpu(sel->hashAlg)); >> + return -1; >> + } >> + >> + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, >> + digestSizes[count+1]); >> + if (event_size > sizeof(event) - sizeof(uint32_t)) { >> + dprintf("EfiSpecIdEventStruct pad too small\n"); >> + return -1; >> + } >> + >> + event.hdr.digestSizes[count].algorithmId = >> + cpu_to_log16(be16_to_cpu(sel->hashAlg)); >> + event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize); >> + >> + sel = nsel; >> + } >> + >> + if (sel != end) { >> + dprintf("Malformed pcr selection structure fron TPM\n"); >> + return -1; >> + } >> + >> + event.hdr.numberOfAlgorithms = cpu_to_log32(count); >> + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, >> + digestSizes[count]); >> + vendorInfoSize = (void*)&event + event_size; >> + *vendorInfoSize = 0; >> + event_size += sizeof(*vendorInfoSize); >> + >> + return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size); >> +} >> + >> +static int tpm20_startup(void) >> +{ >> + int ret; >> + >> + ret = tpm_simple_cmd(0, TPM2_CC_Startup, >> + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); >> + dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n", >> + ret); >> + >> + if (ret) >> + goto err_exit; >> + >> + ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, >> + 1, TPM2_YES, TPM_DURATION_TYPE_LONG); >> + >> + dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n", >> + ret); >> + >> + if (ret) >> + goto err_exit; >> + >> + ret = tpm20_get_pcrbanks(); >> + if (ret) >> + goto err_exit; >> + >> + /* the log parameters will be passed from Forth layer */ >> + >> + return 0; >> + >> +err_exit: >> + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); >> + >> + tpm_set_failure(); >> + return -1; >> +} >> + >> +uint32_t tpm_start(void) >> +{ >> + probe_tpm(); >> + >> + if (!tpm_is_working()) { >> + dprintf("%s: Machine does not have a working TPM\n", >> + __func__); >> + return TCGBIOS_FATAL_COM_ERROR; >> + } >> + >> + return tpm20_startup(); >> +} >> + >> +void tpm_finalize(void) >> +{ >> + spapr_vtpm_finalize(); >> +} >> + >> +static void tpm20_prepboot(void) >> +{ >> + uint8_t auth[20]; >> + int ret = tpm20_stirrandom(); >> + if (ret) >> + goto err_exit; >> + >> + ret = tpm20_getrandom(&auth[0], sizeof(auth)); >> + if (ret) >> + goto err_exit; >> + >> + ret = tpm20_hierarchychangeauth(auth); >> + if (ret) >> + goto err_exit; >> + >> + return; >> + >> +err_exit: >> + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); >> + >> + tpm_set_failure(); >> +} >> + >> +/* >> + * Prepare TPM for boot; this function has to be called before >> + * the firmware transitions to the boot loader. > > I do not see any caller of this one. > > >> + */ >> +uint32_t tpm_leave_firmware(void) > static? > > >> +{ >> + 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; >> + >> + sha256(hashdata, hashdatalen, hash); >> + digest_len = tpm20_build_digest(&le, hash, true); >> + if (digest_len < 0) >> + return TCGBIOS_GENERAL_ERROR; >> + int ret = tpm20_extend(&le, digest_len); >> + if (ret) { >> + tpm_set_failure(); >> + return TCGBIOS_COMMAND_ERROR; >> + } >> + tpm20_build_digest(&le, hash, false); >> + return tpm_log_event_long(&le.hdr, digest_len, info, infolen); >> +} >> + >> +/* >> + * Add an EV_ACTION measurement to the list of measurements >> + */ >> +static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string) >> +{ >> + uint32_t len = strlen(string); >> + >> + return tpm_add_measurement_to_log(pcrIndex, EV_ACTION, >> + string, len, (uint8_t *)string, len); >> +} >> + >> +/* >> + * Add event separators for a range of PCRs >> + */ >> +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr) >> +{ >> + static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; >> + uint32_t rc = 0; >> + uint32_t pcrIndex; >> + >> + if (!tpm_is_working()) >> + return TCGBIOS_GENERAL_ERROR; >> + >> + if (start_pcr >= 24 || start_pcr > end_pcr) >> + return TCGBIOS_INVALID_INPUT_PARA; >> + >> + /* event separators need to be extended and logged for PCRs 0-7 */ >> + for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { >> + rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, >> + NULL, 0, >> + evt_separator, >> + sizeof(evt_separator)); >> + if (rc) >> + break; > return rc; > > >> + } >> + >> + 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 | sha1sum >> + */ >> + string = "MBR"; > > What about GPT (I am not extemely familiar with MBR/GPT but I see my > ubuntu images use GPT these days)? > > >> + rc = tpm_add_measurement_to_log(4, EV_IPL, >> + string, strlen(string), >> + addr, 0x1b8); >> + if (rc) >> + return rc; >> + >> + /* >> + * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum >> + */ >> + string = "MBR PARTITION TABLE"; >> + return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, >> + string, strlen(string), >> + addr + 0x1b8, 0x48); >> +} >> + >> +uint32_t tpm_measure_scrtm(void) >> +{ >> + uint32_t rc; >> + char *version_start = strstr((char *)&print_version, "FW Version"); >> + char *version_end; >> + uint32_t version_length; >> + char *slof_data_start = (char *)&_slof_data; >> + char *slof_text_start = (char *)&_slof_text; >> + uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data; >> + uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text; >> + const char *scrtm = "S-CRTM Contents"; >> + >> + version_end = strchr(version_start, '\r'); >> + version_length = version_end - version_start; >> + >> + dprintf("Measure S-CRTM Version: addr = %p, length = %d\n", >> + version_start, version_length); >> + >> + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION, >> + version_start, version_length, >> + (uint8_t *)version_start, >> + version_length); >> + if (rc) >> + return rc; >> + >> + dprintf("Measure S-CRTM Content (data): start = %p, length = %d\n", >> + slof_data_start, slof_data_length); >> + >> + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS, >> + scrtm, strlen(scrtm), >> + (uint8_t *)slof_data_start, >> + slof_data_length); >> + 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..3cc4f46 100644 >> --- a/lib/libtpm/tcgbios_int.h >> +++ b/lib/libtpm/tcgbios_int.h >> @@ -15,6 +15,94 @@ >> >> #include <stdint.h> >> >> +/* internal error codes */ >> +#define TCGBIOS_OK 0x0 >> +#define TCGBIOS_LOGOVERFLOW 0x1 >> +#define TCGBIOS_GENERAL_ERROR 0x2 >> +#define TCGBIOS_FIRMWARE_ERROR 0x3 >> +#define TCGBIOS_FATAL_COM_ERROR 0x4 >> +#define TCGBIOS_INVALID_INPUT_PARA 0x5 >> +#define TCGBIOS_COMMAND_ERROR 0x6 >> +#define TCGBIOS_INTERFACE_SHUTDOWN 0x7 >> + >> +/* event types */ >> +#define EV_POST_CODE 1 >> +#define EV_NO_ACTION 3 >> +#define EV_SEPARATOR 4 >> +#define EV_ACTION 5 >> +#define EV_EVENT_TAG 6 >> +#define EV_S_CRTM_CONTENTS 7 >> +#define EV_S_CRTM_VERSION 8 >> +#define EV_IPL 13 >> +#define EV_IPL_PARTITION_DATA 14 >> + >> +#define SHA1_BUFSIZE 20 >> +#define SHA256_BUFSIZE 32 >> +#define SHA384_BUFSIZE 48 >> +#define SHA512_BUFSIZE 64 >> +#define SM3_256_BUFSIZE 32 >> + >> +#define BCV_DEVICE_HDD 0x80 >> + >> +struct tpm2_digest_value { >> + uint16_t hashAlg; >> + uint8_t hash[0]; /* size depends on hashAlg */ >> +} __attribute__((packed)); >> + >> +struct tpm2_digest_values { >> + uint32_t count; >> + struct tpm2_digest_value digest[0]; >> +} __attribute__((packed)); >> + >> +/* Each entry in the TPM log contains: a tpm_log_header, a variable >> + * length digest, a tpm_log_trailer, and a variable length event. The >> + * 'digest' matches what is sent to the TPM hardware via the Extend >> + * command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest >> + * contains a tpm2_digest_values struct followed by a variable number >> + * of tpm2_digest_value structs (as specified by the hardware via the >> + * TPM2_CAP_PCRS request). >> + */ >> +struct tpm_log_header { >> + uint32_t pcrindex; >> + uint32_t eventtype; >> + uint8_t digest[0]; >> +} __attribute__((packed)); >> + >> +struct tpm_log_trailer { >> + uint32_t eventdatasize; >> + uint8_t event[0]; >> +} __attribute__((packed)); >> + >> +struct TCG_EfiSpecIdEventStruct { >> + uint8_t signature[16]; >> + uint32_t platformClass; >> +#define TPM_TCPA_ACPI_CLASS_CLIENT 0 >> + uint8_t specVersionMinor; >> + uint8_t specVersionMajor; >> + uint8_t specErrata; >> + uint8_t uintnSize; >> + uint32_t numberOfAlgorithms; >> + struct TCG_EfiSpecIdEventAlgorithmSize { >> + uint16_t algorithmId; >> + uint16_t digestSize; >> + } digestSizes[0]; >> + /* >> + uint8_t vendorInfoSize; >> + uint8_t vendorInfo[0]; >> + */ >> +} __attribute__((packed)); >> + >> +/* Input and Output blocks for the TCG BIOS commands */ >> + >> +/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */ >> +struct pcpes { >> + uint32_t pcrindex; >> + uint32_t eventtype; >> + uint8_t digest[SHA1_BUFSIZE]; >> + uint32_t eventdatasize; >> + uint32_t event; >> +} __attribute__((packed)); >> + >> struct tpm_req_header { >> uint16_t tag; >> uint32_t totlen; >> @@ -27,4 +115,156 @@ struct tpm_rsp_header { >> uint32_t errcode; >> } __attribute__((packed)); >> >> +/**************************************************************** >> + * TPM v2.0 hardware commands >> + ****************************************************************/ >> + >> +#define TPM2_NO 0 >> +#define TPM2_YES 1 >> + >> +#define TPM2_SU_CLEAR 0x0000 >> +#define TPM2_SU_STATE 0x0001 >> + >> +#define TPM2_RH_OWNER 0x40000001 >> +#define TPM2_RS_PW 0x40000009 >> +#define TPM2_RH_ENDORSEMENT 0x4000000b >> +#define TPM2_RH_PLATFORM 0x4000000c >> + >> +#define TPM2_ALG_SHA1 0x0004 >> +#define TPM2_ALG_SHA256 0x000b >> +#define TPM2_ALG_SHA384 0x000c >> +#define TPM2_ALG_SHA512 0x000d >> +#define TPM2_ALG_SM3_256 0x0012 >> + >> +#define TPM2_ALG_SHA1_FLAG (1 << 0) >> +#define TPM2_ALG_SHA256_FLAG (1 << 1) >> +#define TPM2_ALG_SHA384_FLAG (1 << 2) >> +#define TPM2_ALG_SHA512_FLAG (1 << 3) >> +#define TPM2_ALG_SM3_256_FLAG (1 << 4) >> + >> +/* TPM 2 command tags */ >> +#define TPM2_ST_NO_SESSIONS 0x8001 >> +#define TPM2_ST_SESSIONS 0x8002 >> + >> +/* TPM 2 commands */ >> +#define TPM2_CC_HierarchyControl 0x121 >> +#define TPM2_CC_Clear 0x126 >> +#define TPM2_CC_ClearControl 0x127 >> +#define TPM2_CC_HierarchyChangeAuth 0x129 >> +#define TPM2_CC_PCR_Allocate 0x12b >> +#define TPM2_CC_SelfTest 0x143 >> +#define TPM2_CC_Startup 0x144 >> +#define TPM2_CC_Shutdown 0x145 >> +#define TPM2_CC_StirRandom 0x146 >> +#define TPM2_CC_GetCapability 0x17a >> +#define TPM2_CC_GetRandom 0x17b >> +#define TPM2_CC_PCR_Extend 0x182 >> + >> +/* TPM 2 Capabilities */ >> +#define TPM2_CAP_PCRS 0x00000005 >> + >> +/* TPM 2 data structures */ >> + >> +struct tpm2_req_stirrandom { >> + struct tpm_req_header hdr; >> + uint16_t size; >> + uint64_t stir; >> +} __attribute__((packed)); >> + >> +struct tpm2_req_getrandom { >> + struct tpm_req_header hdr; >> + uint16_t bytesRequested; >> +} __attribute__((packed)); >> + >> +struct tpm2b_20 { >> + uint16_t size; >> + uint8_t buffer[20]; >> +} __attribute__((packed)); >> + >> +struct tpm2_res_getrandom { >> + struct tpm_rsp_header hdr; >> + struct tpm2b_20 rnd; >> +} __attribute__((packed)); >> + >> +struct tpm2_authblock { >> + uint32_t handle; >> + uint16_t noncesize; /* always 0 */ >> + uint8_t contsession; /* always TPM2_YES */ >> + uint16_t pwdsize; /* always 0 */ >> +} __attribute__((packed)); >> + >> +struct tpm2_req_hierarchychangeauth { >> + struct tpm_req_header hdr; >> + uint32_t authhandle; >> + uint32_t authblocksize; >> + struct tpm2_authblock authblock; >> + struct tpm2b_20 newAuth; >> +} __attribute__((packed)); >> + >> +struct tpm2_req_extend { >> + struct tpm_req_header hdr; >> + uint32_t pcrindex; >> + uint32_t authblocksize; >> + struct tpm2_authblock authblock; >> + uint8_t digest[0]; >> +} __attribute__((packed)); >> + >> +struct tpm2_req_clearcontrol { >> + struct tpm_req_header hdr; >> + uint32_t authhandle; >> + uint32_t authblocksize; >> + struct tpm2_authblock authblock; >> + uint8_t disable; >> +} __attribute__((packed)); >> + >> +struct tpm2_req_clear { >> + struct tpm_req_header hdr; >> + uint32_t authhandle; >> + uint32_t authblocksize; >> + struct tpm2_authblock authblock; >> +} __attribute__((packed)); >> + >> +struct tpm2_req_hierarchycontrol { >> + struct tpm_req_header hdr; >> + uint32_t authhandle; >> + uint32_t authblocksize; >> + struct tpm2_authblock authblock; >> + uint32_t enable; >> + uint8_t state; >> +} __attribute__((packed)); >> + >> +struct tpm2_req_getcapability { >> + struct tpm_req_header hdr; >> + uint32_t capability; >> + uint32_t property; >> + uint32_t propertycount; >> +} __attribute__((packed)); >> + >> +struct tpm2_res_getcapability { >> + struct tpm_rsp_header hdr; >> + uint8_t moreData; >> + uint32_t capability; >> + uint8_t data[0]; /* capability dependent data */ >> +} __attribute__((packed)); >> + >> +struct tpm2_req_pcr_allocate { >> + struct tpm_req_header hdr; >> + uint32_t authhandle; >> + uint32_t authblocksize; >> + struct tpm2_authblock authblock; >> + uint32_t count; >> + uint8_t tpms_pcr_selections[4]; >> +} __attribute__((packed)); >> + >> +struct tpms_pcr_selection { >> + uint16_t hashAlg; >> + uint8_t sizeOfSelect; >> + uint8_t pcrSelect[0]; >> +} __attribute__((packed)); >> + >> +struct tpml_pcr_selection { >> + uint32_t count; >> + struct tpms_pcr_selection selections[0]; >> +} __attribute__((packed)); >> + > > Please separate somehow what is defined for SLOF only (is > TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what > spec. > > > >> #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() */ >> +/************************************************/ > > What is calling this one? Now I am really not sure they all are actually > have a user. Thanks, > > >> +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..77bb0f5 100644 >> --- a/slof/fs/packages/disk-label.fs >> +++ b/slof/fs/packages/disk-label.fs >> @@ -550,7 +550,15 @@ B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2 >> \ load from a bootable partition >> : load-from-boot-partition ( addr -- size ) >> debug-disk-label? IF ." Trying DOS boot " .s cr THEN >> - dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN >> + dup load-from-dos-boot-partition ?dup 0 <> IF >> + nip >> + block s" /ibm,vtpm" find-node ?dup IF >> + s" measure-hdd-mbr" rot $call-static >> + ELSE >> + drop >> + THEN >> + EXIT >> + THEN >> >> debug-disk-label? IF ." Trying CHRP boot " .s cr THEN >> 1 disk-chrp-boot ! >> 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 >>
On 1/20/20 12:08 PM, Stefan Berger wrote: > On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote: >> I accidentally pressed the send button before. So here are more responses. > >>> + >>> +\ firmware API call >>> +: get-failure-reason ( -- reason ) >>> +   " get-failure-reason" vtpm-call-forward IF >>> +       \ vtpm-call-forward failed; return a value >>> +       0 \ invalid >>> +   THEN >>> +; >>> + >>> +0 0 s" ibm,sml-efi-reformat-supported" property >>> + >>> +\ firmware API call >>> +: reformat-sml-to-efi-alignment ( -- success ) >>> +   " reformat-sml-to-efi-alignment" vtpm-call-forward IF >>> +       false >>> +   THEN >>> +; >>> + >>> +: open ( ) >>> +   vtpm-debug? IF ." VTPM: vTPM open()" cr THEN >>> +   true >>> +; >>> + >>> +: close ( ) >>> +   vtpm-debug? IF ." VTPM: vTPM close()" cr THEN >>> +; >>> + >>> +\ setup alias and the RTAS bypass >>> +vtpm-init >>> + >>> +\ setup the log >>> +include vtpm-sml.fs >>> + >>> +s" /ibm,vtpm" find-node ?dup IF >>> + s" measure-scrtm" rot $call-static >>> +THEN >> >> The above 22 lines confuse me a lot. >> Why vtpm-sml.fs? > > You mean why does vtpm-sml.fs exist at all?Or why not just put all > vTPM code into one file? > > >> Why "open" does not open? >> Why vtpm-init is not in "open"? >> Why the device methods are in vtpm-sml.fs? The straight forward answer is that I have some 'challenges' with the FORTH part. I do not completely understand how to organize the code there. What is there right now is at least 'working'. >> >> The Linux finds the device, opens it and calls methods (passing >> ihandle), why this complication? Grub could call the API as well. I have removed all the other APIs now since the removal of TPM 1.2 support since I only knew of callers of the API from 'trusted' grub code supporting TPM 1.2. It doesn't support TPM 2. >> >> I am missing the point in all of this and 2 lines commit log does not >> help at all. >> >> + >> +/* >> + * Prepare TPM for boot; this function has to be called before >> + * the firmware transitions to the boot loader. >> >> I do not see any caller of this one. >> >> >>> + */ >>> +uint32_t tpm_leave_firmware(void) >> static? No, FORTH is calling it. board-qemu/slof/vtpm-sml.fs-: leave-firmware ( -- ) board-qemu/slof/vtpm-sml.fs: tpm-leave-firmware                        ( errcode ) board-qemu/slof/vtpm-sml.fs-   ?dup IF board-qemu/slof/vtpm-sml.fs:       ." VTPM: Error code from tpm-leave-firmware: " . cr board-qemu/slof/vtpm-sml.fs-   THEN board-qemu/slof/vtpm-sml.fs-; >> >> >>> +/* >>> + * Add event separators for a range of PCRs >>> + */ >>> +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t >>> end_pcr) >>> +{ >>> +   static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; >>> +   uint32_t rc = 0; >>> +   uint32_t pcrIndex; >>> + >>> +   if (!tpm_is_working()) >>> +       return TCGBIOS_GENERAL_ERROR; >>> + >>> +   if (start_pcr >= 24 || start_pcr > end_pcr) >>> +       return TCGBIOS_INVALID_INPUT_PARA; >>> + >>> +   /* event separators need to be extended and logged for PCRs 0-7 */ >>> +   for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { >>> +       rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, >>> +                       NULL, 0, >>> +                       evt_separator, >>> +                       sizeof(evt_separator)); >>> +       if (rc) >>> +           break; >> return rc; Fixed. >> >> >>> +   } >>> + >>> +   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 | sha1sum >>> +    */ >>> +   string = "MBR"; >> >> What about GPT (I am not extemely familiar with MBR/GPT but I see my >> ubuntu images use GPT these days)? I have a patch on top of this series that does measure GPT. I will post on the next round. + >>> +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)); >>> + >> >> Please separate somehow what is defined for SLOF only (is >> TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what >> spec. All are TPM 2 definitions. Most are related to commands, others are related to logging. I will try to leave some references to names of specs. >> + >>> +/************************************************/ >>> +/* Check whether the TPM is working            */ >>> +/* SLOF:  tpm-is-working ( -- true | false ) */ >>> +/* LIBTPM: bool = tpm_is_working()             */ >>> +/************************************************/ >> >> What is calling this one? Now I am really not sure they all are actually >> have a user. Thanks, There is a user for this one. board-qemu/slof/vtpm-sml.fs- board-qemu/slof/vtpm-sml.fs-: vtpm-menu board-qemu/slof/vtpm-sml.fs:   tpm-is-working IF board-qemu/slof/vtpm-sml.fs-       tpm20-menu board-qemu/slof/vtpm-sml.fs-   THEN
On 21/01/2020 04:08, Stefan Berger wrote: > On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote: >> >> On 16/01/2020 07:00, Stefan Berger wrote: >>> This patch adds TPM 2.0 support along with the firmware API >>> that Linux uses to transfer the firmware log. >>> >>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> >>> --- >>>  board-qemu/slof/Makefile           | 13 +- >>>  board-qemu/slof/tree.fs            |  3 + >>>  board-qemu/slof/vio-vtpm-cdriver.fs | 134 ++++ >>>  board-qemu/slof/vtpm-sml.fs        | 115 ++++ >>>  include/helpers.h                  |  1 + >>>  lib/libtpm/Makefile                |  2 +- >>>  lib/libtpm/tcgbios.c               | 918 ++++++++++++++++++++++++++++ >>>  lib/libtpm/tcgbios.h               | 32 + >>>  lib/libtpm/tcgbios_int.h           | 240 ++++++++ >>>  lib/libtpm/tpm.code                | 130 ++++ >>>  lib/libtpm/tpm.in                  | 26 + >>>  slof/fs/packages/disk-label.fs     | 10 +- >>>  slof/fs/start-up.fs                |  5 + >>>  13 files changed, 1624 insertions(+), 5 deletions(-) >>>  create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs >>>  create mode 100644 board-qemu/slof/vtpm-sml.fs >>>  create mode 100644 lib/libtpm/tcgbios.c >>>  create mode 100644 lib/libtpm/tcgbios.h >>>  create mode 100644 lib/libtpm/tpm.code >>>  create mode 100644 lib/libtpm/tpm.in >>> >>> 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 \ >> >> s/vio-vtpm-cdriver.fs/vio-vtpm.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..8d17d0e >>> --- /dev/null >>> +++ b/board-qemu/slof/vio-vtpm-cdriver.fs >>> @@ -0,0 +1,134 @@ >>> +\ >>> ***************************************************************************** >>> >>> +\ * Copyright (c) 2015-2020 IBM Corporation >>> +\ * All rights reserved. >>> +\ * This program and the accompanying materials >>> +\ * are made available under the terms of the BSD License >>> +\ * which accompanies this distribution, and is available at >>> +\ * http://www.opensource.org/licenses/bsd-license.php >>> +\ * >>> +\ * Contributors: >>> +\ *    IBM Corporation - initial implementation >>> +\ >>> ****************************************************************************/ >>> >>> + >>> +." Populating " pwd >>> + >>> +false VALUE vtpm-debug? >>> +0    VALUE vtpm-unit >>> +0    VALUE vtpm-ihandle >>> + >>> +: setup-alias >>> +   " ibm,vtpm" find-alias 0= IF >>> +       " ibm,vtpm" get-node node>path set-alias >>> +   ELSE >>> +       drop >>> +   THEN >>> +; >>> + >>> +: vtpm-cleanup ( ) >>> +   vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN >>> +   tpm-finalize >>> +   vtpm-unit 0 rtas-set-tce-bypass >>> +; >>> + >>> +: vtpm-init ( -- true | false ) >>> +   0 0 get-node open-node ?dup 0= IF EXIT THEN >>> +   my-self >r >>> +   dup to my-self >>> + >>> +   vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN >>> + >>> +   my-unit to vtpm-unit >>> + >>> +   \ Enable TCE bypass special qemu feature >>> +   vtpm-unit 1 rtas-set-tce-bypass >>> + >>> +   \ Have TCE bypass cleaned up >>> +   ['] vtpm-cleanup add-quiesce-xt >>> + >>> +   tpm-start ?dup 0= IF >>> +       vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN >>> +       setup-alias >>> +   ELSE >>> +       ." VTPM: Error code from tpm-start: " . cr >>> +   THEN >>> + >>> +   close-node >>> +   r> to my-self >>> +; >>> + >>> +\ forward a call to /ibm,vtpm, which implements the function with the >>> +\ given name >>> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret >>> failure? ) >>> +   \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned >>> +   vtpm-ihandle 0= IF >>> +       s" /ibm,vtpm" open-dev to vtpm-ihandle >> >> Why does not "open" do this? Is this vtpm supposed to run even before >> the client tries using the vtpm services? It does not look like it. > > Initialization of the vTPM is supposed to happen before any client can > talk to the firmware. And how doing it in "open" breaks this rule? If the client does not open the node, it cannot use the vtpm. > >> >> >>> +   THEN >>> + >>> +   vtpm-ihandle 0<> IF >>> +       vtpm-ihandle                  ( arg ... arg name namelen >>> ihandle ) >>> +       $call-method                  ( ret ... ret ) >>> +       false                         ( ret ... ret false ) >>> +   ELSE >>> +       true                          ( true ) >>> +   THEN >>> +; >>> + >>> +\ firmware API call >>> +: sml-get-allocated-size ( -- buffer-size) >>> +   " sml-get-allocated-size" vtpm-call-forward IF >>> +       \ vtpm-call-forward failed >>> +       0 >>> +   THEN >>> +; >>> + >>> +\ firmware API call >>> +: sml-get-handover-size ( -- size) >>> +   " sml-get-handover-size" vtpm-call-forward IF >>> +       \ vtpm-call-forward failed >>> +       0 >>> +   THEN >>> +; >>> + >>> +\ firmware API call >>> +: sml-handover ( dest size -- ) >>> +   " sml-handover" vtpm-call-forward IF >>> +       \ vtpm-call-forward failed; clean up stack >>> +       2drop >>> +   THEN >>> +; >>> + >>> +\ firmware API call >>> +: get-failure-reason ( -- reason ) >>> +   " get-failure-reason" vtpm-call-forward IF >>> +       \ vtpm-call-forward failed; return a value >>> +       0 \ invalid >>> +   THEN >>> +; >>> + >>> +0 0 s" ibm,sml-efi-reformat-supported" property >>> + >>> +\ firmware API call >>> +: reformat-sml-to-efi-alignment ( -- success ) >>> +   " reformat-sml-to-efi-alignment" vtpm-call-forward IF >>> +       false >>> +   THEN >>> +; >>> + >>> +: open ( ) >>> +   vtpm-debug? IF ." VTPM: vTPM open()" cr THEN >>> +   true >>> +; >>> + >>> +: close ( ) >>> +   vtpm-debug? IF ." VTPM: vTPM close()" cr THEN >>> +; >>> + >>> +\ setup alias and the RTAS bypass >>> +vtpm-init >>> + >>> +\ setup the log >>> +include vtpm-sml.fs >>> + >>> +s" /ibm,vtpm" find-node ?dup IF >>> + s" measure-scrtm" rot $call-static >>> +THEN >> >> The above 22 lines confuse me a lot. >> Why vtpm-sml.fs? > > You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM > code into one file? Yes. > >> Why "open" does not open? >> Why vtpm-init is not in "open"? >> Why the device methods are in vtpm-sml.fs? >> >> The Linux finds the device, opens it and calls methods (passing >> ihandle), why this complication? >> >> I am missing the point in all of this and 2 lines commit log does not >> help at all. >> >> >> >> >>> diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs >>> new file mode 100644 >>> index 0000000..865dce6 >>> --- /dev/null >>> +++ b/board-qemu/slof/vtpm-sml.fs >>> @@ -0,0 +1,115 @@ >>> +\ >>> ***************************************************************************** >>> >>> +\ * Copyright (c) 2015-2020 IBM Corporation >>> +\ * All rights reserved. >>> +\ * This program and the accompanying materials >>> +\ * are made available under the terms of the BSD License >>> +\ * which accompanies this distribution, and is available at >>> +\ * http://www.opensource.org/licenses/bsd-license.php >>> +\ * >>> +\ * Contributors: >>> +\ *    IBM Corporation - initial implementation >>> +\ >>> ****************************************************************************/ >>> >>> + >>> +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm >>> + >>> +" /" find-device >>> + >>> +new-device >>> + >>> +false VALUE   vtpm-debug? >>> +0    VALUE   log-base >>> +40000 CONSTANT LOG-SIZE  \ 256k per VTPM FW spec. >> >> What is this spec's name exactly? It may not be available to the public >> but I could try and get it in IBM internally. > > > "PFW Virtual TPM Driver" Version 1.1 (or later) Put this to the commit log. > > > --- /dev/null >>> +++ b/lib/libtpm/tcgbios.c >>> @@ -0,0 +1,918 @@ >>> +/***************************************************************************** >>> >>> + * 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 >> >> Is this "A Protocol for VTPM Communications" from >> M_vtpm_protocol_v0r3.pdf or something else? > > The title of the document is 'PFW Virtual TPM Driver'. > PFW_CTPM_CLDD_1.1-1.pdf is the file I have. > Cool, save it in the comment or commit log. > >> >> >>> and the TCG Specification >>> + * that can be found here under the following link: >>> + * >>> http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios >>> >> >> The link is broken. > > > https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/ and this one. > > > > >> >> >>> + */ >>> + >>> +#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)) >>> + >>> +struct tpm_state { >>> +   unsigned tpm_probed:1; >>> +   unsigned tpm_found:1; >>> +   unsigned tpm_working:1; >>> + >>> +   /* base address of the log area */ >>> +   uint8_t *log_base; >>> + >>> +   /* size of the logging area */ >>> +   size_t log_area_size; >>> + >>> +   /* where to write the next log entry to */ >>> +   uint8_t *log_area_next_entry; >>> +}; >>> + >>> +static struct tpm_state tpm_state; >> You do not pass a pointer to tpm_state anywhere (it would great if you >> did - this way a reader could tell what functions actually need it) so >> you do not need "struct tpm_state" type, can be just "struct tpm_state { >> ... } tpm_state;" > > > It's all global variables collected in one structure. I removed the name > of the structure. > > > >> >> >> >> >>> + >>> +/* >>> + * TPM 2 logs are written in little endian format. >>> + */ >>> +static inline uint32_t log32_to_cpu(uint32_t val) >>> +{ >>> +   return le32_to_cpu(val); >>> +} >>> + >>> +static inline uint32_t cpu_to_log32(uint32_t val) >>> +{ >>> +   return cpu_to_le32(val); >>> +} >>> + >>> +static inline uint16_t cpu_to_log16(uint16_t val) >>> +{ >>> +   return cpu_to_le16(val); >>> +} >>> + >>> +/******************************************************** >>> + Extensions for TCG-enabled BIOS >>> + *******************************************************/ >>> + >>> +static void probe_tpm(void) >>> +{ >>> +   tpm_state.tpm_probed = true; >>> +   tpm_state.tpm_found = spapr_is_vtpm_present(); >>> +   tpm_state.tpm_working = tpm_state.tpm_found; >>> +} >>> + >>> +/**************************************************************** >>> + * Digest formatting >>> + ****************************************************************/ >>> + >>> +static uint32_t tpm20_pcr_selection_size; >>> +static struct tpml_pcr_selection *tpm20_pcr_selection; >> >> Any reason these are not in tpm_state? > > I will move them there. > > >> >> >>> + >>> +/* A 'struct tpm_log_entry' is a local data structure containing a >>> + * 'tpm_log_header' followed by space for the maximum supported >>> + * digest. The digest is a series of tpm2_digest_value structs on >>> tpm2.0. >>> + */ >> This comment is rather useless, what I would really be interested to is >> the endianness (little endian because it is v2.0?) and the name of the >> spec which defines it. > > PPC64 TPM 2.0 logging is supposed to be compatible with client code > written for x86_64 Linux. While TPM 1.2 logs on PPC64 were big endian, > TPM 2.0 logs are now little endian. I am cc'in Christopher Engel who may > have a more recent spec. and told me this is how it's supposed to work. > > > >> >> >>> +struct tpm_log_entry { >>> +   struct tpm_log_header hdr; >>> +   uint8_t pad[sizeof(struct tpm2_digest_values) >>> +      + 5 * sizeof(struct tpm2_digest_value) >>> +      + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE >>> +      + SHA512_BUFSIZE + SM3_256_BUFSIZE]; >>> +} __attribute__((packed)); >>> + >>> +static const struct hash_parameters { >>> +   uint16_t hashalg; >>> +   uint8_t hashalg_flag; >>> +   uint8_t hash_buffersize; >>> +   const char *name; >> name and hashalg_flag are not used. > > True. This occurred when re-splitting the patch. > > >> >>> +} hash_parameters[] = { >>> +   { >>> +       .hashalg = TPM2_ALG_SHA1, >> >> TPM2_ALG_xxx are basically 1<<index_in_this_array and can be dropped, >> can't they? > > > No: > > lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA1 0x0004 > lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA256 0x000b > lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA384 0x000c > lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SHA512 0x000d > lib/libtpm/tcgbios_int.h:#define TPM2_ALG_SM3_256 0x0012 Oh right, I confused this with the other set: #define TPM2_ALG_SHA1_FLAG (1 << 0) #define TPM2_ALG_SHA256_FLAG (1 << 1) #define TPM2_ALG_SHA384_FLAG (1 << 2) #define TPM2_ALG_SHA512_FLAG (1 << 3) #define TPM2_ALG_SM3_256_FLAG (1 << 4) It is not used by this patch, 7/7 uses it and even then it would make more sense to define these as indexes into the hash_parameters[]. Or just drop. > > >> >> >>> +       .hash_buffersize = SHA1_BUFSIZE, >>> +   }, { >>> +       .hashalg = TPM2_ALG_SHA256, >>> +       .hash_buffersize = SHA256_BUFSIZE, >>> +   }, { >>> +       .hashalg = TPM2_ALG_SHA384, >>> +       .hash_buffersize = SHA384_BUFSIZE, >>> +   }, { >>> +       .hashalg = TPM2_ALG_SHA512, >>> +       .hash_buffersize = SHA512_BUFSIZE, >>> +   }, { >>> +       .hashalg = TPM2_ALG_SM3_256, >>> +       .hash_buffersize = SM3_256_BUFSIZE, >>> +   } >>> +}; >>> + >>> +static int >>> +tpm20_get_hash_buffersize(uint16_t hashAlg) >>> +{ >>> +   unsigned i; >>> + >>> +   for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { >>> +       if (hash_parameters[i].hashalg == hashAlg) >>> +           return hash_parameters[i].hash_buffersize; >>> +   } >>> +   return -1; >> Just make it return (const struct hash_parameters *) as 7/7 adds one >> helper like this for every field of hash_parameters. > > > OK. > > >> >> >>> +} >>> + >>> +/* >>> + * Build the TPM2 tpm2_digest_values data structure from the given >>> hash. >>> + * Follow the PCR bank configuration of the TPM and write the same hash >>> + * in either truncated or zero-padded form in the areas of all the >>> other >>> + * hashes. For example, write the sha1 hash in the area of the sha256 >>> + * hash and fill the remaining bytes with zeros. Or truncate the sha256 >>> + * hash when writing it in the area of the sha1 hash. >> It must be correct since you did this but what is the point of this? If >> the digest does not fit, fill it with 0xBAADF00D and return an error? > > A TPM 2 has typically 24 platform control registers (PCRs). These > registers are storing each a hash (rather than an integer). Basically > there is one operation on a hash, which is an 'extend': > > pcr1 = sha256(pcr1 || hash) > > So it takes the current value of PCR 1 and concatenates it with the > 'hash' to extend it and hashes this concatenation and assigns the result > to PCR 1. > > A TPM 2 may have several hash banks, such as for sha1, sha256, sha384, > sha512, sm3-256 etc. So there may be multiple PCR 1 registers, one for > the sha1 bank, one for the sha256 bank etc. > > For the logging of measurements we are only using one hash, that is > sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for > all the 'active' PCR banks basically. Do we have to do this for all active PCR banks? Sorry for stupid questions :) > We log the 'hash' value that a PCR > was extended so that someone looking at the current PCR value can use > the log to replay (simulate) the extensions of the PCRs. Since we are > now using sha256 for hashing we will need to truncate the sha256 when > logging it for the sha1 bank and zero pad it for sha384 since sha384 has > more bytes. There is the integrity measurement architecture (IMA) in > Linux that basically does the same thing: Something is missing after ":"? > > > >> >> >>> + * >>> + * 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) >> >> Is not it always little endian as it is v2.0 only? > > There are callers for this function who want to send the resulting data > structures to the TPM. Other callers want this for the log written in > little endian. The function prototype suggests it is always for the log which is always little endian as it is v2.0 only. > >> >>> +{ >>> +   struct tpms_pcr_selection *sel; >>> +   void *nsel, *end; >>> +   void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values); >>> +   uint32_t count; >>> +   struct tpm2_digest_value *v; >>> +   struct tpm2_digest_values *vs; >>> + >>> +   if (!tpm20_pcr_selection) >> >> This is allocated at tpm20_startup() and if allocation failed, then we >> should not get this far, or can we? > > > It's a safety net. Cannot keep ? It is moving into tpm_state and you already have tpm_probed/tpm_found/..., some are tested in various places, how many nets do you really need?
On 21/01/2020 05:22, Stefan Berger wrote: > On 1/20/20 12:08 PM, Stefan Berger wrote: >> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote: >>> > > I accidentally pressed the send button before. So here are more responses. > >> >>>> + >>>> +\ firmware API call >>>> +: get-failure-reason ( -- reason ) >>>> +   " get-failure-reason" vtpm-call-forward IF >>>> +       \ vtpm-call-forward failed; return a value >>>> +       0 \ invalid >>>> +   THEN >>>> +; >>>> + >>>> +0 0 s" ibm,sml-efi-reformat-supported" property >>>> + >>>> +\ firmware API call >>>> +: reformat-sml-to-efi-alignment ( -- success ) >>>> +   " reformat-sml-to-efi-alignment" vtpm-call-forward IF >>>> +       false >>>> +   THEN >>>> +; >>>> + >>>> +: open ( ) >>>> +   vtpm-debug? IF ." VTPM: vTPM open()" cr THEN >>>> +   true >>>> +; >>>> + >>>> +: close ( ) >>>> +   vtpm-debug? IF ." VTPM: vTPM close()" cr THEN >>>> +; >>>> + >>>> +\ setup alias and the RTAS bypass >>>> +vtpm-init >>>> + >>>> +\ setup the log >>>> +include vtpm-sml.fs >>>> + >>>> +s" /ibm,vtpm" find-node ?dup IF >>>> + s" measure-scrtm" rot $call-static >>>> +THEN >>> >>> The above 22 lines confuse me a lot. >>> Why vtpm-sml.fs? >> >> You mean why does vtpm-sml.fs exist at all?Or why not just put all >> vTPM code into one file? >> >> >>> Why "open" does not open? >>> Why vtpm-init is not in "open"? >>> Why the device methods are in vtpm-sml.fs? > > > The straight forward answer is that I have some 'challenges' with the > FORTH part. I do not completely understand how to organize the code > there. What is there right now is at least 'working'. > > >>> >>> The Linux finds the device, opens it and calls methods (passing >>> ihandle), why this complication? > > > Grub could call the API as well. I have removed all the other APIs now > since the removal of TPM 1.2 support since I only knew of callers of the > API from 'trusted' grub code supporting TPM 1.2. It doesn't support TPM 2. Then why are we ditching v1.2? I asked multiple times what needs v1.2 and grub never came up and if this is the only version what grub supports - we need it, no? Or we somehow are going to bypass grub completely and it is ok? > >>> >>> I am missing the point in all of this and 2 lines commit log does not >>> help at all. >>> > > > > >>> + >>> +/* >>> + * Prepare TPM for boot; this function has to be called before >>> + * the firmware transitions to the boot loader. >>> >>> I do not see any caller of this one. >>> >>> >>>> + */ >>>> +uint32_t tpm_leave_firmware(void) >>> static? > > > No, FORTH is calling it. ah yes. Forth is one great write-only language :) > > board-qemu/slof/vtpm-sml.fs-: leave-firmware ( -- ) > board-qemu/slof/vtpm-sml.fs: tpm-leave-firmware                        > ( errcode ) > board-qemu/slof/vtpm-sml.fs-   ?dup IF > board-qemu/slof/vtpm-sml.fs:       ." VTPM: Error code from > tpm-leave-firmware: " . cr > board-qemu/slof/vtpm-sml.fs-   THEN > board-qemu/slof/vtpm-sml.fs-; > > >>> >>> >>>> +/* >>>> + * Add event separators for a range of PCRs >>>> + */ >>>> +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t >>>> end_pcr) >>>> +{ >>>> +   static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; >>>> +   uint32_t rc = 0; >>>> +   uint32_t pcrIndex; >>>> + >>>> +   if (!tpm_is_working()) >>>> +       return TCGBIOS_GENERAL_ERROR; >>>> + >>>> +   if (start_pcr >= 24 || start_pcr > end_pcr) >>>> +       return TCGBIOS_INVALID_INPUT_PARA; >>>> + >>>> +   /* event separators need to be extended and logged for PCRs 0-7 */ >>>> +   for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { >>>> +       rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, >>>> +                       NULL, 0, >>>> +                       evt_separator, >>>> +                       sizeof(evt_separator)); >>>> +       if (rc) >>>> +           break; >>> return rc; > > > Fixed. > > > >>> >>> >>>> +   } >>>> + >>>> +   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 | sha1sum >>>> +    */ >>>> +   string = "MBR"; >>> >>> What about GPT (I am not extemely familiar with MBR/GPT but I see my >>> ubuntu images use GPT these days)? > > > I have a patch on top of this series that does measure GPT. I will post > on the next round. > > + >>>> +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)); >>>> + >>> >>> Please separate somehow what is defined for SLOF only (is >>> TPM2_ALG_SHA1_FLAG/etc?) and for everything else - what comes from what >>> spec. > > > All are TPM 2 definitions. Most are related to commands, others are > related to logging. > > I will try to leave some references to names of specs. > > > >>> + >>>> +/************************************************/ >>>> +/* Check whether the TPM is working            */ >>>> +/* SLOF:  tpm-is-working ( -- true | false ) */ >>>> +/* LIBTPM: bool = tpm_is_working()             */ >>>> +/************************************************/ >>> >>> What is calling this one? Now I am really not sure they all are actually >>> have a user. Thanks, > > > There is a user for this one. Then this should go to 7/7? > > board-qemu/slof/vtpm-sml.fs- > board-qemu/slof/vtpm-sml.fs-: vtpm-menu > board-qemu/slof/vtpm-sml.fs:   tpm-is-working IF > board-qemu/slof/vtpm-sml.fs-       tpm20-menu > board-qemu/slof/vtpm-sml.fs-   THEN > >
On 1/21/20 9:24 PM, Alexey Kardashevskiy wrote: > > On 21/01/2020 04:08, Stefan Berger wrote: >> On 1/20/20 3:09 AM, Alexey Kardashevskiy wrote: >>> On 16/01/2020 07:00, Stefan Berger wrote: >>>> This patch adds TPM 2.0 support along with the firmware API >>>> that Linux uses to transfer the firmware log. >>>> >>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com> >>>> >>>> +; >>>> + >>>> +\ forward a call to /ibm,vtpm, which implements the function with the >>>> +\ given name >>>> +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret >>>> failure? ) >>>> +   \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned >>>> +   vtpm-ihandle 0= IF >>>> +       s" /ibm,vtpm" open-dev to vtpm-ihandle >>> Why does not "open" do this? Is this vtpm supposed to run even before >>> the client tries using the vtpm services? It does not look like it. >> Initialization of the vTPM is supposed to happen before any client can >> talk to the firmware. > > And how doing it in "open" breaks this rule? If the client does not open > the node, it cannot use the vtpm. The firmware is supposed to take measurements while it is running. Before it can do that it needs to initialize the TPM, so that's why we are doing this without any higher level client coming in through the API. Further, when the user configures the TPM via the menu the device has to have been initialized already. > > > >>> >>>> +   THEN >>>> + >>>> +   vtpm-ihandle 0<> IF >>>> +       vtpm-ihandle                  ( arg ... arg name namelen >>>> ihandle ) >>>> +       $call-method                  ( ret ... ret ) >>>> +       false                         ( ret ... ret false ) >>>> +   ELSE >>>> +       true                          ( true ) >>>> +   THEN >>>> +; >>>> + >>>> +\ firmware API call >>>> +: sml-get-allocated-size ( -- buffer-size) >>>> +   " sml-get-allocated-size" vtpm-call-forward IF >>>> +       \ vtpm-call-forward failed >>>> +       0 >>>> +   THEN >>>> +; >>>> + >>>> +\ firmware API call >>>> +: sml-get-handover-size ( -- size) >>>> +   " sml-get-handover-size" vtpm-call-forward IF >>>> +       \ vtpm-call-forward failed >>>> +       0 >>>> +   THEN >>>> +; >>>> + >>>> +\ firmware API call >>>> +: sml-handover ( dest size -- ) >>>> +   " sml-handover" vtpm-call-forward IF >>>> +       \ vtpm-call-forward failed; clean up stack >>>> +       2drop >>>> +   THEN >>>> +; >>>> + >>>> +\ firmware API call >>>> +: get-failure-reason ( -- reason ) >>>> +   " get-failure-reason" vtpm-call-forward IF >>>> +       \ vtpm-call-forward failed; return a value >>>> +       0 \ invalid >>>> +   THEN >>>> +; >>>> + >>>> +0 0 s" ibm,sml-efi-reformat-supported" property >>>> + >>>> +\ firmware API call >>>> +: reformat-sml-to-efi-alignment ( -- success ) >>>> +   " reformat-sml-to-efi-alignment" vtpm-call-forward IF >>>> +       false >>>> +   THEN >>>> +; >>>> + >>>> +: open ( ) >>>> +   vtpm-debug? IF ." VTPM: vTPM open()" cr THEN >>>> +   true >>>> +; >>>> + >>>> +: close ( ) >>>> +   vtpm-debug? IF ." VTPM: vTPM close()" cr THEN >>>> +; >>>> + >>>> +\ setup alias and the RTAS bypass >>>> +vtpm-init >>>> + >>>> +\ setup the log >>>> +include vtpm-sml.fs >>>> + >>>> +s" /ibm,vtpm" find-node ?dup IF >>>> + s" measure-scrtm" rot $call-static >>>> +THEN >>> The above 22 lines confuse me a lot. >>> Why vtpm-sml.fs? >> You mean why does vtpm-sml.fs exist at all?Or why not just put all vTPM >> code into one file? > Yes. Have a look at v7. I would agree to switching the names of the files now as things have moved around, but keeping the code in different files seems to make it 'cleaner' -- at least to my eyes. As said, I do have some challenges with the Forth code and it's not clear to me what is the better code. Back then when I wrote this I derived it from from the vscsi code, which still does things similar today: https://github.com/stefanberger/SLOF-tpm/blob/master/board-qemu/slof/vio-vscsi.fs#L534 vtpm-init is similar to vscsi-init-and-scan. Right or wrong, it couldn't tell, but at least derived from some existing code that does it similarly. >> "PFW Virtual TPM Driver" Version 1.1 (or later) > > Put this to the commit log. See v7: https://lists.ozlabs.org/pipermail/slof/2020-January/002506.html >> The title of the document is 'PFW Virtual TPM Driver'. >> PFW_CTPM_CLDD_1.1-1.pdf is the file I have. >> > Cool, save it in the comment or commit log. v7 got it. > > >>> >>>> and the TCG Specification >>>> + * that can be found here under the following link: >>>> + * >>>> http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios >>>> >>> The link is broken. >> >> https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/ > > and this one. Updated the link in the file. >>>> +       .hash_buffersize = SHA1_BUFSIZE, >>>> +   }, { >>>> +       .hashalg = TPM2_ALG_SHA256, >>>> +       .hash_buffersize = SHA256_BUFSIZE, >>>> +   }, { >>>> +       .hashalg = TPM2_ALG_SHA384, >>>> +       .hash_buffersize = SHA384_BUFSIZE, >>>> +   }, { >>>> +       .hashalg = TPM2_ALG_SHA512, >>>> +       .hash_buffersize = SHA512_BUFSIZE, >>>> +   }, { >>>> +       .hashalg = TPM2_ALG_SM3_256, >>>> +       .hash_buffersize = SM3_256_BUFSIZE, >>>> +   } >>>> +}; >>>> + >>>> +static int >>>> +tpm20_get_hash_buffersize(uint16_t hashAlg) >>>> +{ >>>> +   unsigned i; >>>> + >>>> +   for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { >>>> +       if (hash_parameters[i].hashalg == hashAlg) >>>> +           return hash_parameters[i].hash_buffersize; >>>> +   } >>>> +   return -1; >>> Just make it return (const struct hash_parameters *) as 7/7 adds one >>> helper like this for every field of hash_parameters. >> >> OK. >> >> >>> >>>> +} >>>> + >>>> +/* >>>> + * Build the TPM2 tpm2_digest_values data structure from the given >>>> hash. >>>> + * Follow the PCR bank configuration of the TPM and write the same hash >>>> + * in either truncated or zero-padded form in the areas of all the >>>> other >>>> + * hashes. For example, write the sha1 hash in the area of the sha256 >>>> + * hash and fill the remaining bytes with zeros. Or truncate the sha256 >>>> + * hash when writing it in the area of the sha1 hash. >>> It must be correct since you did this but what is the point of this? If >>> the digest does not fit, fill it with 0xBAADF00D and return an error? >> A TPM 2 has typically 24 platform control registers (PCRs). These >> registers are storing each a hash (rather than an integer). Basically >> there is one operation on a hash, which is an 'extend': >> >> pcr1 = sha256(pcr1 || hash) >> >> So it takes the current value of PCR 1 and concatenates it with the >> 'hash' to extend it and hashes this concatenation and assigns the result >> to PCR 1. >> >> A TPM 2 may have several hash banks, such as for sha1, sha256, sha384, >> sha512, sm3-256 etc. So there may be multiple PCR 1 registers, one for >> the sha1 bank, one for the sha256 bank etc. >> >> For the logging of measurements we are only using one hash, that is >> sha256 now, and extend this in PCR1 of sha1, sha256, etc. We do this for >> all the 'active' PCR banks basically. > Do we have to do this for all active PCR banks? Sorry for stupid > questions :) Page 96 in pdf "TCG PC Client Platform Firmware Profile" https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf There SHALL be a Hash algorithm in the TCG PCClientPCREvent structure for all allocated PCR banks. There MAY be Hash algorithms in the TCG_PCClientPCREvent structure that do not correspond to allocated PCR banks. See Section 11 Predictive Event Logs. We chose to do both in SeaBIOS. From what I am hearing other firmwares may do the same and follow the 'MAY'. > >> We log the 'hash' value that a PCR >> was extended so that someone looking at the current PCR value can use >> the log to replay (simulate) the extensions of the PCRs. Since we are >> now using sha256 for hashing we will need to truncate the sha256 when >> logging it for the sha1 bank and zero pad it for sha384 since sha384 has >> more bytes. There is the integrity measurement architecture (IMA) in >> Linux that basically does the same thing: > Something is missing after ":"? IMA takes one measurement of a file and extends all active PCR banks with this hash. Depending on the algorithm used for hashing versus the hash algorithm of a PCR bank a hash may be trucated, fit exactly, or zero-padded at the end https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_queue.c#L147 later on size-adjusted: https://elixir.bootlin.com/linux/latest/source/drivers/char/tpm/tpm2-cmd.c#L275 > > >> >> >>> >>>> + * >>>> + * 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) >>> Is not it always little endian as it is v2.0 only? >> There are callers for this function who want to send the resulting data >> structures to the TPM. Other callers want this for the log written in >> little endian. > > The function prototype suggests it is always for the log which is always > little endian as it is v2.0 only. This is what the parameter description says. The function can be called for different purposes. * bigEndian: whether to build in big endian format for the TPM or log *           little endian for the log (TPM 2.0) We may need to send the result of this call to the TPM (TPM 2.0 that is) and it takes the data in big endian. The little endian use case is for writing into the log. This is from "TCG PC Client Platfrom Firmware Profile" (pdf page 92): https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf "Note that for the digests in the crypto agile log format, although the type names from the TPM 2.0 Library Specification are used(e.g. TPML_DIGEST_VALUES), the storage of Integers in the event log is little-endian. In this case, the Count and the AlgID fields, leveraged from the TPM 2.0 Library specification,are stored as little-endian. If Platform Firmware utilizes the TPM2_PCR_Event command to hash the data, this command returns a TPML_DIGEST_VALUES structure encoded in big-endian. Platform Firmware would need to modify the TPML_DIGEST_VALUES structure so that count and AlgID fields are little-endian before adding the event to 2655the event log. Likewise, if Platform Firmware is preparing the TPML_DIGEST_VALUES structure to send to the TPM using a TPM2_PCR_Extend command, the values must be big-endian for the TPM structure. There may be other instances where this type of re-encoding is required. This specification does not produce an exhaustive list."
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..8d17d0e --- /dev/null +++ b/board-qemu/slof/vio-vtpm-cdriver.fs @@ -0,0 +1,134 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015-2020 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +." Populating " pwd + +false VALUE vtpm-debug? +0 VALUE vtpm-unit +0 VALUE vtpm-ihandle + +: setup-alias + " ibm,vtpm" find-alias 0= IF + " ibm,vtpm" get-node node>path set-alias + ELSE + drop + THEN +; + +: vtpm-cleanup ( ) + vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN + tpm-finalize + vtpm-unit 0 rtas-set-tce-bypass +; + +: vtpm-init ( -- true | false ) + 0 0 get-node open-node ?dup 0= IF EXIT THEN + my-self >r + dup to my-self + + vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN + + my-unit to vtpm-unit + + \ Enable TCE bypass special qemu feature + vtpm-unit 1 rtas-set-tce-bypass + + \ Have TCE bypass cleaned up + ['] vtpm-cleanup add-quiesce-xt + + tpm-start ?dup 0= IF + vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN + setup-alias + ELSE + ." VTPM: Error code from tpm-start: " . cr + THEN + + close-node + r> to my-self +; + +\ forward a call to /ibm,vtpm, which implements the function with the +\ given name +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? ) + \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned + vtpm-ihandle 0= IF + s" /ibm,vtpm" open-dev to vtpm-ihandle + THEN + + vtpm-ihandle 0<> IF + vtpm-ihandle ( arg ... arg name namelen ihandle ) + $call-method ( ret ... ret ) + false ( ret ... ret false ) + ELSE + true ( true ) + THEN +; + +\ firmware API call +: sml-get-allocated-size ( -- buffer-size) + " sml-get-allocated-size" vtpm-call-forward IF + \ vtpm-call-forward failed + 0 + THEN +; + +\ firmware API call +: sml-get-handover-size ( -- size) + " sml-get-handover-size" vtpm-call-forward IF + \ vtpm-call-forward failed + 0 + THEN +; + +\ firmware API call +: sml-handover ( dest size -- ) + " sml-handover" vtpm-call-forward IF + \ vtpm-call-forward failed; clean up stack + 2drop + THEN +; + +\ firmware API call +: get-failure-reason ( -- reason ) + " get-failure-reason" vtpm-call-forward IF + \ vtpm-call-forward failed; return a value + 0 \ invalid + THEN +; + +0 0 s" ibm,sml-efi-reformat-supported" property + +\ firmware API call +: reformat-sml-to-efi-alignment ( -- success ) + " reformat-sml-to-efi-alignment" vtpm-call-forward IF + false + THEN +; + +: open ( ) + vtpm-debug? IF ." VTPM: vTPM open()" cr THEN + true +; + +: close ( ) + vtpm-debug? IF ." VTPM: vTPM close()" cr THEN +; + +\ setup alias and the RTAS bypass +vtpm-init + +\ setup the log +include vtpm-sml.fs + +s" /ibm,vtpm" find-node ?dup IF + s" measure-scrtm" rot $call-static +THEN diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs new file mode 100644 index 0000000..865dce6 --- /dev/null +++ b/board-qemu/slof/vtpm-sml.fs @@ -0,0 +1,115 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015-2020 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm + +" /" find-device + +new-device + +false VALUE vtpm-debug? +0 VALUE log-base +40000 CONSTANT LOG-SIZE \ 256k per VTPM FW spec. + +e CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER + +LOG-SIZE BUFFER: log-base + +\ create /ibm,vtpm +s" ibm,vtpm" 2dup device-name device-type + +\ convey logbase and size to the C driver +log-base LOG-SIZE tpm-set-log-parameters + +: sml-get-allocated-size ( -- buffer-size) + vtpm-debug? IF + ." Call to sml-get-allocated-size; size = 0x" LOG-SIZE . cr + THEN + LOG-SIZE +; + +: sml-get-handover-size ( -- size ) + tpm-get-logsize + vtpm-debug? IF + ." Call to sml-get-handover-size; size = 0x" dup . cr + THEN +; + +: sml-handover ( dest size -- ) + vtpm-debug? IF + 2dup + ." Call to sml-handover; size = 0x" . ." dest = " . cr + THEN + log-base ( dest size src ) + -rot ( src dest size ) + move + + VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason +; + +: get-failure-reason ( -- reason ) + tpm-driver-get-failure-reason ( reason ) + vtpm-debug? IF + ." VTPM: Return value from tpm-driver-get-failure-reason: " dup . cr + THEN +; + +: reformat-sml-to-efi-alignment ( -- success? ) + vtpm-debug? IF + ." Call to reformat-sml-to-efi-alignment" cr + THEN + \ a no-op since already byte aligned + true +; + +\ +\ internal API calls +\ + +: separator-event ( start-pcr end-pcr -- ) + tpm-add-event-separators ( errcode ) + ?dup IF + ." VTPM: Error code from tpm-add-event-separators: " . cr + THEN +; + +80 CONSTANT BCV_DEVICE_HDD + +: measure-hdd-mbr ( addr -- ) + 0 7 separator-event + 200 BCV_DEVICE_HDD ( addr length bootdrv ) + -rot ( bootdrv addr length ) + tpm-measure-bcv-mbr ( errcode ) + ?dup 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 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 7efad28..24eacc1 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..4e81a42 --- /dev/null +++ b/lib/libtpm/tcgbios.c @@ -0,0 +1,918 @@ +/***************************************************************************** + * 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: + * http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios + */ + +#include <stddef.h> +#include <stdlib.h> + +#include "types.h" +#include "byteorder.h" +#include "tpm_drivers.h" +#include "string.h" +#include "tcgbios.h" +#include "tcgbios_int.h" +#include "stdio.h" +#include "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)) + +struct tpm_state { + unsigned tpm_probed:1; + unsigned tpm_found:1; + unsigned tpm_working:1; + + /* base address of the log area */ + uint8_t *log_base; + + /* size of the logging area */ + size_t log_area_size; + + /* where to write the next log entry to */ + uint8_t *log_area_next_entry; +}; + +static struct tpm_state tpm_state; + +/* + * TPM 2 logs are written in little endian format. + */ +static inline uint32_t log32_to_cpu(uint32_t val) +{ + return le32_to_cpu(val); +} + +static inline uint32_t cpu_to_log32(uint32_t val) +{ + return cpu_to_le32(val); +} + +static inline uint16_t cpu_to_log16(uint16_t val) +{ + return cpu_to_le16(val); +} + +/******************************************************** + Extensions for TCG-enabled BIOS + *******************************************************/ + +static void probe_tpm(void) +{ + tpm_state.tpm_probed = true; + tpm_state.tpm_found = spapr_is_vtpm_present(); + tpm_state.tpm_working = tpm_state.tpm_found; +} + +/**************************************************************** + * Digest formatting + ****************************************************************/ + +static uint32_t tpm20_pcr_selection_size; +static struct tpml_pcr_selection *tpm20_pcr_selection; + +/* A 'struct tpm_log_entry' is a local data structure containing a + * 'tpm_log_header' followed by space for the maximum supported + * digest. The digest is a series of tpm2_digest_value structs on tpm2.0. + */ +struct tpm_log_entry { + struct tpm_log_header hdr; + uint8_t pad[sizeof(struct tpm2_digest_values) + + 5 * sizeof(struct tpm2_digest_value) + + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE + + SHA512_BUFSIZE + SM3_256_BUFSIZE]; +} __attribute__((packed)); + +static const struct hash_parameters { + uint16_t hashalg; + uint8_t hashalg_flag; + uint8_t hash_buffersize; + const char *name; +} hash_parameters[] = { + { + .hashalg = TPM2_ALG_SHA1, + .hash_buffersize = SHA1_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA256, + .hash_buffersize = SHA256_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA384, + .hash_buffersize = SHA384_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA512, + .hash_buffersize = SHA512_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SM3_256, + .hash_buffersize = SM3_256_BUFSIZE, + } +}; + +static int +tpm20_get_hash_buffersize(uint16_t hashAlg) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { + if (hash_parameters[i].hashalg == hashAlg) + return hash_parameters[i].hash_buffersize; + } + return -1; +} + +/* + * Build the TPM2 tpm2_digest_values data structure from the given hash. + * Follow the PCR bank configuration of the TPM and write the same hash + * in either truncated or zero-padded form in the areas of all the other + * hashes. For example, write the sha1 hash in the area of the sha256 + * hash and fill the remaining bytes with zeros. Or truncate the sha256 + * hash when writing it in the area of the sha1 hash. + * + * le: the log entry to build the digest in + * sha1: the sha1 hash value to use + * bigEndian: whether to build in big endian format for the TPM or log + * little endian for the log (TPM 2.0) + * + * Returns the digest size; -1 on fatal error + */ +static int tpm20_build_digest(struct tpm_log_entry *le, const uint8_t *sha256, + bool bigEndian) +{ + struct tpms_pcr_selection *sel; + void *nsel, *end; + void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values); + uint32_t count; + struct tpm2_digest_value *v; + struct tpm2_digest_values *vs; + + if (!tpm20_pcr_selection) + return -1; + + sel = tpm20_pcr_selection->selections; + end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; + + for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) { + int hsize; + uint8_t sizeOfSelect = sel->sizeOfSelect; + + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf("TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + /* buffer size sanity check before writing */ + v = dest; + if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) { + dprintf("tpm_log_entry is too small\n"); + return -1; + } + + if (bigEndian) + v->hashAlg = sel->hashAlg; + else + v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg)); + + memset(v->hash, 0, hsize); + memcpy(v->hash, 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.digest; + if (bigEndian) + vs->count = cpu_to_be32(count); + else + vs->count = cpu_to_le32(count); + + return dest - (void*)le->hdr.digest; +} + +/**************************************************************** + * TPM hardware command wrappers + ****************************************************************/ + +/* Helper function for sending TPM commands that take a single + * optional parameter (0, 1, or 2 bytes) and have no special response. + */ +static int +tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param, + enum tpm_duration_type to_t) +{ + struct { + struct tpm_req_header trqh; + uint16_t param; + } __attribute__((packed)) req = { + .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size), + .trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .trqh.ordinal = cpu_to_be32(ordinal), + }; + uint8_t obuffer[64]; + struct tpm_rsp_header *trsh = (void *)obuffer; + uint32_t obuffer_len = sizeof(obuffer); + int ret; + + switch (param_size) { + case 2: + req.param = cpu_to_be16(param); + break; + case 1: + *(uint8_t *)&req.param = param; + break; + } + + memset(obuffer, 0, sizeof(obuffer)); + ret = tpmhw_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t); + ret = ret ? -1 : be32_to_cpu(trsh->errcode); + dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n", + ordinal, param, ret); + + return ret; +} + +static int +tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count, + struct tpm_rsp_header *rsp, uint32_t rsize) +{ + struct tpm2_req_getcapability trg = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trg)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), + .capability = cpu_to_be32(capability), + .property = cpu_to_be32(property), + .propertycount = cpu_to_be32(count), + }; + + uint32_t resp_size = rsize; + int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size, + TPM_DURATION_TYPE_SHORT); + ret = (ret || + rsize < be32_to_cpu(rsp->totlen)) ? -1 + : be32_to_cpu(rsp->errcode); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", + ret); + + return ret; +} + +static int +tpm20_get_pcrbanks(void) +{ + uint8_t buffer[128]; + uint32_t size; + struct tpm2_res_getcapability *trg = + (struct tpm2_res_getcapability *)&buffer; + + int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, + sizeof(buffer)); + if (ret) + return ret; + + /* defend against (broken) TPM sending packets that are too short */ + uint32_t resplen = be32_to_cpu(trg->hdr.totlen); + if (resplen <= offset_of(struct tpm2_res_getcapability, data)) + return -1; + + size = resplen - offset_of(struct tpm2_res_getcapability, data); + /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/ + if (size < offset_of(struct tpml_pcr_selection, selections) + + offset_of(struct tpms_pcr_selection, pcrSelect)) + return -1; + + tpm20_pcr_selection = SLOF_alloc_mem(size); + if (tpm20_pcr_selection) { + memcpy(tpm20_pcr_selection, &trg->data, size); + tpm20_pcr_selection_size = size; + } else { + printf("TCGBIOS: Failed to allocated %u bytes.\n", size); + ret = -1; + } + + return ret; +} + +static int tpm20_extend(struct tpm_log_entry *le, int digest_len) +{ + struct tpm2_req_extend tmp_tre = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(0), + .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), + .pcrindex = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)), + .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + }; + uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)]; + struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; + + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); + memcpy(&tre->digest[0], le->hdr.digest, digest_len); + + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len); + + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + return -1; + + return 0; +} + +static int tpm20_stirrandom(void) +{ + struct tpm2_req_stirrandom stir = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(stir)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom), + .size = cpu_to_be16(sizeof(stir.stir)), + .stir = rand(), + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &stir.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len) +{ + struct tpm2_res_getrandom rsp; + struct tpm2_req_getrandom trgr = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trgr)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom), + .bytesRequested = cpu_to_be16(buf_len), + }; + uint32_t resp_length = sizeof(rsp); + + if (buf_len > sizeof(rsp.rnd.buffer)) + return -1; + + int ret = tpmhw_transmit(0, &trgr.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) + ret = -1; + else + memcpy(buf, rsp.rnd.buffer, buf_len); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_hierarchychangeauth(uint8_t auth[20]) +{ + struct tpm2_req_hierarchychangeauth trhca = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trhca)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trhca.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .newAuth = { + .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)), + }, + }; + memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer)); + + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &trhca.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state) +{ + /* we will try to deactivate the TPM now - ignoring all errors */ + struct tpm2_req_hierarchycontrol trh = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trh)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trh.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .enable = cpu_to_be32(hierarchy), + .state = state, + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &trh.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n", + ret); + + return ret; +} + +/**************************************************************** + * Setup and Measurements + ****************************************************************/ + +bool tpm_is_working(void) +{ + if (!tpm_state.tpm_probed) + probe_tpm(); + + return tpm_state.tpm_working; +} + +static void tpm_set_failure(void) +{ + tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO); + tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO); + + tpm_state.tpm_working = false; +} + +/* + * Extend the OFDT log with the given entry by copying the + * entry data into the log. + * + * @pcpes: Pointer to the structure to be copied into the log + * @event: The event to be appended to 'pcpes' + * @event_length: The length of the event + * + * Returns 0 on success, an error code otherwise. + */ +static uint32_t tpm_log_event_long(struct tpm_log_header *entry, + int digest_len, + const void *event, uint32_t event_length) +{ + size_t size, logsize; + void *dest; + + dprintf("log base address = %p, next entry = %p\n", + tpm_state.log_base, tpm_state.log_area_next_entry); + + if (tpm_state.log_area_next_entry == NULL) + return TCGBIOS_LOGOVERFLOW; + + size = sizeof(*entry) + digest_len + + sizeof(struct tpm_log_trailer) + event_length; + logsize = (tpm_state.log_area_next_entry + size - + tpm_state.log_base); + if (logsize > tpm_state.log_area_size) { + dprintf("TCGBIOS: LOG OVERFLOW: size = %zu\n", size); + return TCGBIOS_LOGOVERFLOW; + } + + dest = tpm_state.log_area_next_entry; + memcpy(dest, entry, sizeof(*entry) + digest_len); + struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len; + t->eventdatasize = cpu_to_log32(event_length); + if (event_length) + memcpy(t->event, event, event_length); + + tpm_state.log_area_next_entry += size; + + return 0; +} + +/* Add an entry at the start of the log describing digest formats + */ +static int tpm20_write_EfiSpecIdEventStruct(void) +{ + if (!tpm20_pcr_selection) + return -1; + + struct { + struct TCG_EfiSpecIdEventStruct hdr; + uint32_t pad[256]; + } event = { + .hdr.signature = "Spec ID Event03", + .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, + .hdr.specVersionMinor = 0, + .hdr.specVersionMajor = 2, + .hdr.specErrata = 0, + .hdr.uintnSize = 2, + }; + + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; + void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; + int event_size; + uint32_t *vendorInfoSize; + struct tpm_log_entry le = { + .hdr.eventtype = cpu_to_log32(EV_NO_ACTION), + }; + uint32_t count; + + for (count = 0; + count < be32_to_cpu(tpm20_pcr_selection->count); + count++) { + int hsize; + uint8_t sizeOfSelect = sel->sizeOfSelect; + + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf("TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, + digestSizes[count+1]); + if (event_size > sizeof(event) - sizeof(uint32_t)) { + dprintf("EfiSpecIdEventStruct pad too small\n"); + return -1; + } + + event.hdr.digestSizes[count].algorithmId = + cpu_to_log16(be16_to_cpu(sel->hashAlg)); + event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize); + + sel = nsel; + } + + if (sel != end) { + dprintf("Malformed pcr selection structure fron TPM\n"); + return -1; + } + + event.hdr.numberOfAlgorithms = cpu_to_log32(count); + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, + digestSizes[count]); + vendorInfoSize = (void*)&event + event_size; + *vendorInfoSize = 0; + event_size += sizeof(*vendorInfoSize); + + return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size); +} + +static int tpm20_startup(void) +{ + int ret; + + ret = tpm_simple_cmd(0, TPM2_CC_Startup, + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); + dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n", + ret); + + if (ret) + goto err_exit; + + ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, + 1, TPM2_YES, TPM_DURATION_TYPE_LONG); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n", + ret); + + if (ret) + goto err_exit; + + ret = tpm20_get_pcrbanks(); + if (ret) + goto err_exit; + + /* the log parameters will be passed from Forth layer */ + + return 0; + +err_exit: + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_set_failure(); + return -1; +} + +uint32_t tpm_start(void) +{ + probe_tpm(); + + if (!tpm_is_working()) { + dprintf("%s: Machine does not have a working TPM\n", + __func__); + return TCGBIOS_FATAL_COM_ERROR; + } + + return tpm20_startup(); +} + +void tpm_finalize(void) +{ + spapr_vtpm_finalize(); +} + +static void tpm20_prepboot(void) +{ + uint8_t auth[20]; + int ret = tpm20_stirrandom(); + if (ret) + goto err_exit; + + ret = tpm20_getrandom(&auth[0], sizeof(auth)); + if (ret) + goto err_exit; + + ret = tpm20_hierarchychangeauth(auth); + if (ret) + goto err_exit; + + return; + +err_exit: + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_set_failure(); +} + +/* + * Prepare TPM for boot; this function has to be called before + * the firmware transitions to the boot loader. + */ +uint32_t tpm_leave_firmware(void) +{ + tpm20_prepboot(); + + return 0; +} + +/**************************************************************** + * Forth interface + ****************************************************************/ + +void tpm_set_log_parameters(void *addr, size_t size) +{ + int ret; + + dprintf("Log is at 0x%llx; size is %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; + + sha256(hashdata, hashdatalen, hash); + digest_len = tpm20_build_digest(&le, hash, true); + if (digest_len < 0) + return TCGBIOS_GENERAL_ERROR; + int ret = tpm20_extend(&le, digest_len); + if (ret) { + tpm_set_failure(); + return TCGBIOS_COMMAND_ERROR; + } + tpm20_build_digest(&le, hash, false); + return tpm_log_event_long(&le.hdr, digest_len, info, infolen); +} + +/* + * Add an EV_ACTION measurement to the list of measurements + */ +static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string) +{ + uint32_t len = strlen(string); + + return tpm_add_measurement_to_log(pcrIndex, EV_ACTION, + string, len, (uint8_t *)string, len); +} + +/* + * Add event separators for a range of PCRs + */ +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr) +{ + static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; + uint32_t rc = 0; + uint32_t pcrIndex; + + if (!tpm_is_working()) + return TCGBIOS_GENERAL_ERROR; + + if (start_pcr >= 24 || start_pcr > end_pcr) + return TCGBIOS_INVALID_INPUT_PARA; + + /* event separators need to be extended and logged for PCRs 0-7 */ + for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { + rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, + NULL, 0, + evt_separator, + sizeof(evt_separator)); + if (rc) + break; + } + + return rc; +} + +uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr, + uint32_t length) +{ + uint32_t rc; + const char *string; + + if (!tpm_is_working()) + return TCGBIOS_GENERAL_ERROR; + + if (length < 0x200) + return TCGBIOS_INVALID_INPUT_PARA; + + string = "Booting BCV device 00h (Floppy)"; + if (bootdrv == BCV_DEVICE_HDD) + string = "Booting BCV device 80h (HDD)"; + + rc = tpm_add_action(4, string); + if (rc) + return rc; + + /* + * equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum + */ + string = "MBR"; + rc = tpm_add_measurement_to_log(4, EV_IPL, + string, strlen(string), + addr, 0x1b8); + if (rc) + return rc; + + /* + * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum + */ + string = "MBR PARTITION TABLE"; + return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, + string, strlen(string), + addr + 0x1b8, 0x48); +} + +uint32_t tpm_measure_scrtm(void) +{ + uint32_t rc; + char *version_start = strstr((char *)&print_version, "FW Version"); + char *version_end; + uint32_t version_length; + char *slof_data_start = (char *)&_slof_data; + char *slof_text_start = (char *)&_slof_text; + uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data; + uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text; + const char *scrtm = "S-CRTM Contents"; + + version_end = strchr(version_start, '\r'); + version_length = version_end - version_start; + + dprintf("Measure S-CRTM Version: addr = %p, length = %d\n", + version_start, version_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION, + version_start, version_length, + (uint8_t *)version_start, + version_length); + if (rc) + return rc; + + dprintf("Measure S-CRTM Content (data): start = %p, length = %d\n", + slof_data_start, slof_data_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS, + scrtm, strlen(scrtm), + (uint8_t *)slof_data_start, + slof_data_length); + 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..3cc4f46 100644 --- a/lib/libtpm/tcgbios_int.h +++ b/lib/libtpm/tcgbios_int.h @@ -15,6 +15,94 @@ #include <stdint.h> +/* internal error codes */ +#define TCGBIOS_OK 0x0 +#define TCGBIOS_LOGOVERFLOW 0x1 +#define TCGBIOS_GENERAL_ERROR 0x2 +#define TCGBIOS_FIRMWARE_ERROR 0x3 +#define TCGBIOS_FATAL_COM_ERROR 0x4 +#define TCGBIOS_INVALID_INPUT_PARA 0x5 +#define TCGBIOS_COMMAND_ERROR 0x6 +#define TCGBIOS_INTERFACE_SHUTDOWN 0x7 + +/* event types */ +#define EV_POST_CODE 1 +#define EV_NO_ACTION 3 +#define EV_SEPARATOR 4 +#define EV_ACTION 5 +#define EV_EVENT_TAG 6 +#define EV_S_CRTM_CONTENTS 7 +#define EV_S_CRTM_VERSION 8 +#define EV_IPL 13 +#define EV_IPL_PARTITION_DATA 14 + +#define SHA1_BUFSIZE 20 +#define SHA256_BUFSIZE 32 +#define SHA384_BUFSIZE 48 +#define SHA512_BUFSIZE 64 +#define SM3_256_BUFSIZE 32 + +#define BCV_DEVICE_HDD 0x80 + +struct tpm2_digest_value { + uint16_t hashAlg; + uint8_t hash[0]; /* size depends on hashAlg */ +} __attribute__((packed)); + +struct tpm2_digest_values { + uint32_t count; + struct tpm2_digest_value digest[0]; +} __attribute__((packed)); + +/* Each entry in the TPM log contains: a tpm_log_header, a variable + * length digest, a tpm_log_trailer, and a variable length event. The + * 'digest' matches what is sent to the TPM hardware via the Extend + * command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest + * contains a tpm2_digest_values struct followed by a variable number + * of tpm2_digest_value structs (as specified by the hardware via the + * TPM2_CAP_PCRS request). + */ +struct tpm_log_header { + uint32_t pcrindex; + uint32_t eventtype; + uint8_t digest[0]; +} __attribute__((packed)); + +struct tpm_log_trailer { + uint32_t eventdatasize; + uint8_t event[0]; +} __attribute__((packed)); + +struct TCG_EfiSpecIdEventStruct { + uint8_t signature[16]; + uint32_t platformClass; +#define TPM_TCPA_ACPI_CLASS_CLIENT 0 + uint8_t specVersionMinor; + uint8_t specVersionMajor; + uint8_t specErrata; + uint8_t uintnSize; + uint32_t numberOfAlgorithms; + struct TCG_EfiSpecIdEventAlgorithmSize { + uint16_t algorithmId; + uint16_t digestSize; + } digestSizes[0]; + /* + uint8_t vendorInfoSize; + uint8_t vendorInfo[0]; + */ +} __attribute__((packed)); + +/* Input and Output blocks for the TCG BIOS commands */ + +/* PCClient_PCREventStruct -- format of log entries; compatible with x86 */ +struct pcpes { + uint32_t pcrindex; + uint32_t eventtype; + uint8_t digest[SHA1_BUFSIZE]; + uint32_t eventdatasize; + uint32_t event; +} __attribute__((packed)); + struct tpm_req_header { uint16_t tag; uint32_t totlen; @@ -27,4 +115,156 @@ struct tpm_rsp_header { uint32_t errcode; } __attribute__((packed)); +/**************************************************************** + * TPM v2.0 hardware commands + ****************************************************************/ + +#define TPM2_NO 0 +#define TPM2_YES 1 + +#define TPM2_SU_CLEAR 0x0000 +#define TPM2_SU_STATE 0x0001 + +#define TPM2_RH_OWNER 0x40000001 +#define TPM2_RS_PW 0x40000009 +#define TPM2_RH_ENDORSEMENT 0x4000000b +#define TPM2_RH_PLATFORM 0x4000000c + +#define TPM2_ALG_SHA1 0x0004 +#define TPM2_ALG_SHA256 0x000b +#define TPM2_ALG_SHA384 0x000c +#define TPM2_ALG_SHA512 0x000d +#define TPM2_ALG_SM3_256 0x0012 + +#define TPM2_ALG_SHA1_FLAG (1 << 0) +#define TPM2_ALG_SHA256_FLAG (1 << 1) +#define TPM2_ALG_SHA384_FLAG (1 << 2) +#define TPM2_ALG_SHA512_FLAG (1 << 3) +#define TPM2_ALG_SM3_256_FLAG (1 << 4) + +/* TPM 2 command tags */ +#define TPM2_ST_NO_SESSIONS 0x8001 +#define TPM2_ST_SESSIONS 0x8002 + +/* TPM 2 commands */ +#define TPM2_CC_HierarchyControl 0x121 +#define TPM2_CC_Clear 0x126 +#define TPM2_CC_ClearControl 0x127 +#define TPM2_CC_HierarchyChangeAuth 0x129 +#define TPM2_CC_PCR_Allocate 0x12b +#define TPM2_CC_SelfTest 0x143 +#define TPM2_CC_Startup 0x144 +#define TPM2_CC_Shutdown 0x145 +#define TPM2_CC_StirRandom 0x146 +#define TPM2_CC_GetCapability 0x17a +#define TPM2_CC_GetRandom 0x17b +#define TPM2_CC_PCR_Extend 0x182 + +/* TPM 2 Capabilities */ +#define TPM2_CAP_PCRS 0x00000005 + +/* TPM 2 data structures */ + +struct tpm2_req_stirrandom { + struct tpm_req_header hdr; + uint16_t size; + uint64_t stir; +} __attribute__((packed)); + +struct tpm2_req_getrandom { + struct tpm_req_header hdr; + uint16_t bytesRequested; +} __attribute__((packed)); + +struct tpm2b_20 { + uint16_t size; + uint8_t buffer[20]; +} __attribute__((packed)); + +struct tpm2_res_getrandom { + struct tpm_rsp_header hdr; + struct tpm2b_20 rnd; +} __attribute__((packed)); + +struct tpm2_authblock { + uint32_t handle; + uint16_t noncesize; /* always 0 */ + uint8_t contsession; /* always TPM2_YES */ + uint16_t pwdsize; /* always 0 */ +} __attribute__((packed)); + +struct tpm2_req_hierarchychangeauth { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + struct tpm2b_20 newAuth; +} __attribute__((packed)); + +struct tpm2_req_extend { + struct tpm_req_header hdr; + uint32_t pcrindex; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint8_t digest[0]; +} __attribute__((packed)); + +struct tpm2_req_clearcontrol { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint8_t disable; +} __attribute__((packed)); + +struct tpm2_req_clear { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; +} __attribute__((packed)); + +struct tpm2_req_hierarchycontrol { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint32_t enable; + uint8_t state; +} __attribute__((packed)); + +struct tpm2_req_getcapability { + struct tpm_req_header hdr; + uint32_t capability; + uint32_t property; + uint32_t propertycount; +} __attribute__((packed)); + +struct tpm2_res_getcapability { + struct tpm_rsp_header hdr; + uint8_t moreData; + uint32_t capability; + uint8_t data[0]; /* capability dependent data */ +} __attribute__((packed)); + +struct tpm2_req_pcr_allocate { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint32_t count; + uint8_t tpms_pcr_selections[4]; +} __attribute__((packed)); + +struct tpms_pcr_selection { + uint16_t hashAlg; + uint8_t sizeOfSelect; + uint8_t pcrSelect[0]; +} __attribute__((packed)); + +struct tpml_pcr_selection { + uint32_t count; + struct tpms_pcr_selection selections[0]; +} __attribute__((packed)); + #endif /* TCGBIOS_INT_H */ 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..77bb0f5 100644 --- a/slof/fs/packages/disk-label.fs +++ b/slof/fs/packages/disk-label.fs @@ -550,7 +550,15 @@ B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2 \ load from a bootable partition : load-from-boot-partition ( addr -- size ) debug-disk-label? IF ." Trying DOS boot " .s cr THEN - dup load-from-dos-boot-partition ?dup 0 <> IF nip EXIT THEN + dup load-from-dos-boot-partition ?dup 0 <> IF + nip + block s" /ibm,vtpm" find-node ?dup IF + s" measure-hdd-mbr" rot $call-static + ELSE + drop + THEN + EXIT + THEN debug-disk-label? IF ." Trying CHRP boot " .s cr THEN 1 disk-chrp-boot ! 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. 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 | 134 ++++ board-qemu/slof/vtpm-sml.fs | 115 ++++ include/helpers.h | 1 + lib/libtpm/Makefile | 2 +- lib/libtpm/tcgbios.c | 918 ++++++++++++++++++++++++++++ lib/libtpm/tcgbios.h | 32 + lib/libtpm/tcgbios_int.h | 240 ++++++++ lib/libtpm/tpm.code | 130 ++++ lib/libtpm/tpm.in | 26 + slof/fs/packages/disk-label.fs | 10 +- slof/fs/start-up.fs | 5 + 13 files changed, 1624 insertions(+), 5 deletions(-) create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs create mode 100644 board-qemu/slof/vtpm-sml.fs create mode 100644 lib/libtpm/tcgbios.c create mode 100644 lib/libtpm/tcgbios.h create mode 100644 lib/libtpm/tpm.code create mode 100644 lib/libtpm/tpm.in