diff options
-rw-r--r-- | board-qemu/slof/Makefile | 13 | ||||
-rw-r--r-- | board-qemu/slof/tree.fs | 3 | ||||
-rw-r--r-- | board-qemu/slof/vio-vtpm-cdriver.fs | 103 | ||||
-rw-r--r-- | board-qemu/slof/vtpm-sml.fs | 67 | ||||
-rw-r--r-- | include/helpers.h | 1 | ||||
-rw-r--r-- | lib/libtpm/Makefile | 2 | ||||
-rw-r--r-- | lib/libtpm/tcgbios.c | 925 | ||||
-rw-r--r-- | lib/libtpm/tcgbios.h | 32 | ||||
-rw-r--r-- | lib/libtpm/tcgbios_int.h | 241 | ||||
-rw-r--r-- | lib/libtpm/tpm.code | 130 | ||||
-rw-r--r-- | lib/libtpm/tpm.in | 26 | ||||
-rw-r--r-- | slof/fs/packages/disk-label.fs | 9 | ||||
-rw-r--r-- | slof/fs/start-up.fs | 5 |
13 files changed, 1553 insertions, 4 deletions
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..51b3b9f --- /dev/null +++ b/board-qemu/slof/vio-vtpm-cdriver.fs @@ -0,0 +1,103 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015-2020 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +." Populating " pwd cr + +false VALUE vtpm-debug? +0 VALUE vtpm-unit + +0 VALUE log-base +40000 CONSTANT LOG-SIZE \ 256k per VTPM FW spec. + +e CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER + +LOG-SIZE BUFFER: log-base + +\ firmware API call +: sml-get-allocated-size ( -- buffer-size) + LOG-SIZE +; + +\ firmware API call +: sml-get-handover-size ( -- size) + tpm-get-logsize +; + +\ firmware API call +: sml-handover ( dest size -- ) + log-base ( dest size src ) + -rot ( src dest size ) + move + + VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason +; + +\ firmware API call +: get-failure-reason ( -- reason ) + tpm-driver-get-failure-reason ( reason ) +; + +0 0 s" ibm,sml-efi-reformat-supported" property + +\ firmware API call +: reformat-sml-to-efi-alignment ( -- success ) + true +; + +: open true ; +: close ; + +: vtpm-cleanup ( -- ) + vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN + tpm-finalize + \ Disable TCE bypass + vtpm-unit 0 rtas-set-tce-bypass +; + +: vtpm-init ( -- success ) + 0 0 get-node open-node ?dup 0= IF false EXIT THEN + my-self >r + dup to my-self + + vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN + + my-unit to vtpm-unit + + \ Enable TCE bypass special qemu feature + vtpm-unit 1 rtas-set-tce-bypass + + \ Have TCE bypass cleaned up + ['] vtpm-cleanup add-quiesce-xt + + \ close temporary node + close-node + r> to my-self + + tpm-start ?dup 0= IF + vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN + true + ELSE + ." VTPM: Error code from tpm-start: " . cr + false + THEN +; + +\ inititialize unit and set RTAS bypass +vtpm-init IF + \ pass logbase and size to the C driver; we may only do this after + \ init of the lower levels since this calls needs to know the PCR banks + \ when setting up the log + log-base LOG-SIZE tpm-set-log-parameters + s" vtpm-sml.fs" included +ELSE + ." VTPM: vtpm-init failed" cr +THEN diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs new file mode 100644 index 0000000..a1fb8ec --- /dev/null +++ b/board-qemu/slof/vtpm-sml.fs @@ -0,0 +1,67 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015-2020 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +" /" find-device + +new-device + +false VALUE vtpm-debug? + +\ create /ibm,vtpm +s" ibm,vtpm" 2dup device-name device-type + +\ +\ only internal API calls +\ + +: separator-event ( start-pcr end-pcr -- ) + tpm-add-event-separators ( errcode ) + ?dup IF + ." VTPM: Error code from tpm-add-event-separators: " . cr + THEN +; + +80 CONSTANT BCV_DEVICE_HDD + +: measure-hdd-mbr ( addr length -- ) + 0 7 separator-event + BCV_DEVICE_HDD ( addr length bootdrv ) + -rot ( bootdrv addr length ) + tpm-measure-bcv-mbr ( errcode ) + ?dup IF + ." VTPM: Error code from tpm-measure-hdd: " . cr + THEN +; + +: leave-firmware ( -- ) + tpm-leave-firmware ( errcode ) + ?dup IF + ." VTPM: Error code from tpm-leave-firmware: " . cr + THEN +; + +: measure-scrtm ( -- ) + tpm-measure-scrtm ( errcode ) + ?dup IF + ." VTPM: Error code from tpm-measure-scrtm: " . cr + THEN +; + +: open true ; +: close ; + +finish-device +device-end + +s" /ibm,vtpm" find-node ?dup IF + s" measure-scrtm" rot $call-static +THEN diff --git a/include/helpers.h b/include/helpers.h index 2f460d6..47b2674 100644 --- a/include/helpers.h +++ b/include/helpers.h @@ -50,5 +50,6 @@ extern unsigned long SLOF_get_vtpm_unit(void); #define container_of(ptr, type, member) ({ \ const typeof(((type *)0)->member)* struct_ptr = (ptr); \ (type *)((char *)struct_ptr - offset_of(type, member)); }) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif diff --git a/lib/libtpm/Makefile b/lib/libtpm/Makefile index 8fa781e..bcfe88d 100644 --- a/lib/libtpm/Makefile +++ b/lib/libtpm/Makefile @@ -23,7 +23,7 @@ TARGET = ../libtpm.a all: $(TARGET) -SRCS = tpm_drivers.c sha256.c +SRCS = tpm_drivers.c sha256.c tcgbios.c OBJS = $(SRCS:%.c=%.o) diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c new file mode 100644 index 0000000..d81d392 --- /dev/null +++ b/lib/libtpm/tcgbios.c @@ -0,0 +1,925 @@ +/***************************************************************************** + * Copyright (c) 2015-2020 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + * Stefan Berger, stefanb@linux.ibm.com + * Kevin O'Connor, kevin@koconnor.net + *****************************************************************************/ + +/* + * Implementation of the TPM BIOS extension according to the specification + * described in the IBM VTPM Firmware document and the TCG Specification + * that can be found here under the following link: + * https://trustedcomputinggroup.org/resource/pc-client-work-group-specific-implementation-specification-for-conventional-bios/ + */ + +#include <stddef.h> +#include <stdlib.h> + +#include "types.h" +#include "byteorder.h" +#include "tpm_drivers.h" +#include "string.h" +#include "tcgbios.h" +#include "tcgbios_int.h" +#include "stdio.h" +#include "sha256.h" +#include "helpers.h" +#include "version.h" +#include "OF.h" + +#undef TCGBIOS_DEBUG +//#define TCGBIOS_DEBUG +#ifdef TCGBIOS_DEBUG +#define dprintf(_x ...) do { printf("TCGBIOS: " _x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static struct { + unsigned tpm_probed:1; + unsigned tpm_found:1; + unsigned tpm_working:1; + + /* base address of the log area */ + uint8_t *log_base; + + /* size of the logging area */ + size_t log_area_size; + + /* where to write the next log entry to */ + uint8_t *log_area_next_entry; + + /* PCR selection as received from TPM */ + uint32_t tpm20_pcr_selection_size; + struct tpml_pcr_selection *tpm20_pcr_selection; +} tpm_state; + +/* + * TPM 2 logs are written in little endian format. + */ +static inline uint32_t log32_to_cpu(uint32_t val) +{ + return le32_to_cpu(val); +} + +static inline uint32_t cpu_to_log32(uint32_t val) +{ + return cpu_to_le32(val); +} + +static inline uint16_t cpu_to_log16(uint16_t val) +{ + return cpu_to_le16(val); +} + +/******************************************************** + Extensions for TCG-enabled BIOS + *******************************************************/ + +static void probe_tpm(void) +{ + tpm_state.tpm_probed = true; + tpm_state.tpm_found = spapr_is_vtpm_present(); + tpm_state.tpm_working = tpm_state.tpm_found; +} + +/**************************************************************** + * Digest formatting + ****************************************************************/ + +/* A 'struct tpm_log_entry' is a local data structure containing a + * 'TCG_PCR_EVENT2_Header' followed by space for the maximum supported + * digest. The digest is a series of TPMT_HA structs on tpm2.0. + */ +struct tpm_log_entry { + TCG_PCR_EVENT2_Header hdr; + uint8_t pad[sizeof(struct TPML_DIGEST_VALUES) + + 5 * sizeof(struct TPMT_HA) + + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE + + SHA512_BUFSIZE + SM3_256_BUFSIZE]; +} __attribute__((packed)); + +static const struct hash_parameters { + uint16_t hashalg; + uint8_t hash_buffersize; +} hash_parameters[] = { + { + .hashalg = TPM2_ALG_SHA1, + .hash_buffersize = SHA1_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA256, + .hash_buffersize = SHA256_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA384, + .hash_buffersize = SHA384_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA512, + .hash_buffersize = SHA512_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SM3_256, + .hash_buffersize = SM3_256_BUFSIZE, + } +}; + +static const struct hash_parameters *tpm20_find_by_hashalg(uint16_t hashAlg) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { + if (hash_parameters[i].hashalg == hashAlg) + return &hash_parameters[i]; + } + return NULL; +} + +static inline int tpm20_get_hash_buffersize(uint16_t hashAlg) +{ + const struct hash_parameters *hp = tpm20_find_by_hashalg(hashAlg); + + if (hp) + return hp->hash_buffersize; + return -1; +} + +/* + * Build the TPM2 TPML_DIGEST_VALUES data structure from the given hash. + * Follow the PCR bank configuration of the TPM and write the same hash + * in either truncated or zero-padded form in the areas of all the other + * hashes. For example, write the sha256 hash in the area of the sha384 + * hash and fill the remaining bytes with zeros. Or truncate the sha256 + * hash when writing it in the area of the sha1 hash. + * + * le: the log entry to build the digest in + * sha1: the sha1 hash value to use + * bigEndian: whether to build in big endian format for the TPM or log + * little endian for the log (TPM 2.0) + * + * Returns the digest size; -1 on fatal error + */ +static int tpm20_build_digest(struct tpm_log_entry *le, const uint8_t *sha256, + bool bigEndian) +{ + struct tpms_pcr_selection *sel; + void *nsel, *end; + void *dest = le->hdr.digests + sizeof(struct TPML_DIGEST_VALUES); + uint32_t count; + struct TPMT_HA *v; + struct TPML_DIGEST_VALUES *vs; + + sel = tpm_state.tpm20_pcr_selection->selections; + end = (void *)tpm_state.tpm20_pcr_selection + + tpm_state.tpm20_pcr_selection_size; + + for (count = 0; + count < be32_to_cpu(tpm_state.tpm20_pcr_selection->count); + count++) { + int hsize; + uint8_t sizeOfSelect = sel->sizeOfSelect; + + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf("TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + /* buffer size sanity check before writing */ + v = dest; + if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) { + dprintf("tpm_log_entry is too small\n"); + return -1; + } + + if (bigEndian) + v->hashAlg = sel->hashAlg; + else + v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg)); + + memset(v->hash, 0, hsize); + memcpy(v->hash, sha256, + hsize < SHA256_BUFSIZE ? hsize : SHA256_BUFSIZE); + + dest += sizeof(*v) + hsize; + sel = nsel; + } + + if (sel != end) { + dprintf("Malformed pcr selection structure fron TPM\n"); + return -1; + } + + vs = (void*)le->hdr.digests; + if (bigEndian) + vs->count = cpu_to_be32(count); + else + vs->count = cpu_to_le32(count); + + return dest - (void*)le->hdr.digests; +} + +/**************************************************************** + * TPM hardware command wrappers + ****************************************************************/ + +/* Helper function for sending TPM commands that take a single + * optional parameter (0, 1, or 2 bytes) and have no special response. + */ +static int +tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param, + enum tpm_duration_type to_t) +{ + struct { + struct tpm_req_header trqh; + uint16_t param; + } __attribute__((packed)) req = { + .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size), + .trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .trqh.ordinal = cpu_to_be32(ordinal), + }; + uint8_t obuffer[64]; + struct tpm_rsp_header *trsh = (void *)obuffer; + uint32_t obuffer_len = sizeof(obuffer); + int ret; + + switch (param_size) { + case 2: + req.param = cpu_to_be16(param); + break; + case 1: + *(uint8_t *)&req.param = param; + break; + } + + memset(obuffer, 0, sizeof(obuffer)); + ret = spapr_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t); + ret = ret ? -1 : be32_to_cpu(trsh->errcode); + dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n", + ordinal, param, ret); + + return ret; +} + +static int +tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count, + struct tpm_rsp_header *rsp, uint32_t rsize) +{ + struct tpm2_req_getcapability trg = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trg)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), + .capability = cpu_to_be32(capability), + .property = cpu_to_be32(property), + .propertycount = cpu_to_be32(count), + }; + uint32_t resp_size = rsize; + int ret; + + ret = spapr_transmit(0, &trg.hdr, rsp, &resp_size, + TPM_DURATION_TYPE_SHORT); + ret = (ret || + rsize < be32_to_cpu(rsp->totlen)) ? -1 + : be32_to_cpu(rsp->errcode); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", + ret); + + return ret; +} + +static int +tpm20_get_pcrbanks(void) +{ + uint8_t buffer[128]; + uint32_t size; + struct tpm2_res_getcapability *trg = + (struct tpm2_res_getcapability *)&buffer; + uint32_t resplen; + int ret; + + ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, + sizeof(buffer)); + if (ret) + return ret; + + /* defend against (broken) TPM sending packets that are too short */ + resplen = be32_to_cpu(trg->hdr.totlen); + if (resplen <= offset_of(struct tpm2_res_getcapability, data)) + return -1; + + size = resplen - offset_of(struct tpm2_res_getcapability, data); + /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/ + if (size < offset_of(struct tpml_pcr_selection, selections) + + offset_of(struct tpms_pcr_selection, pcrSelect)) + return -1; + + tpm_state.tpm20_pcr_selection = SLOF_alloc_mem(size); + if (tpm_state.tpm20_pcr_selection) { + memcpy(tpm_state.tpm20_pcr_selection, &trg->data, size); + tpm_state.tpm20_pcr_selection_size = size; + } else { + printf("TCGBIOS: Failed to allocated %u bytes.\n", size); + return -1; + } + + return 0; +} + +static int tpm20_extend(struct tpm_log_entry *le, int digest_len) +{ + struct tpm2_req_extend tmp_tre = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(0), + .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), + .pcrindex = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)), + .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + }; + uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)]; + struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret; + + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); + memcpy(&tre->digest[0], le->hdr.digests, digest_len); + + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len); + + ret = spapr_transmit(0, &tre->hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + return -1; + + return 0; +} + +static int tpm20_stirrandom(void) +{ + struct tpm2_req_stirrandom stir = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(stir)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom), + .size = cpu_to_be16(sizeof(stir.stir)), + .stir = rand(), + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = spapr_transmit(0, &stir.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len) +{ + struct tpm2_res_getrandom rsp; + struct tpm2_req_getrandom trgr = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trgr)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom), + .bytesRequested = cpu_to_be16(buf_len), + }; + uint32_t resp_length = sizeof(rsp); + int ret; + + if (buf_len > sizeof(rsp.rnd.buffer)) + return -1; + + ret = spapr_transmit(0, &trgr.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) + ret = -1; + else + memcpy(buf, rsp.rnd.buffer, buf_len); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_hierarchychangeauth(uint8_t auth[20]) +{ + struct tpm2_req_hierarchychangeauth trhca = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trhca)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trhca.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .newAuth = { + .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)), + }, + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret; + + memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer)); + + ret = spapr_transmit(0, &trhca.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state) +{ + struct tpm2_req_hierarchycontrol trh = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trh)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trh.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .enable = cpu_to_be32(hierarchy), + .state = state, + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret; + + ret = spapr_transmit(0, &trh.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n", + ret); + + return ret; +} + +/**************************************************************** + * Setup and Measurements + ****************************************************************/ + +bool tpm_is_working(void) +{ + if (!tpm_state.tpm_probed) + probe_tpm(); + + return tpm_state.tpm_working; +} + +static void tpm_set_failure(void) +{ + tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO); + tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO); + + tpm_state.tpm_working = false; +} + +/* + * Extend the OFDT log with the given entry by copying the + * entry data into the log. + * + * @pcpes: Pointer to the structure to be copied into the log + * @event: The event to be appended to 'pcpes' + * @event_length: The length of the event + * + * Returns 0 on success, an error code otherwise. + */ +static uint32_t tpm_log_event_long(TCG_PCR_EVENT2_Header *entry, + int digest_len, + const void *event, uint32_t event_length) +{ + size_t size, logsize; + void *dest; + TCG_PCR_EVENT2_Trailer *t; + + dprintf("log base address = %p, next entry = %p\n", + tpm_state.log_base, tpm_state.log_area_next_entry); + + if (tpm_state.log_area_next_entry == NULL) + return TCGBIOS_LOGOVERFLOW; + + size = sizeof(*entry) + digest_len + + sizeof(TCG_PCR_EVENT2_Trailer) + event_length; + logsize = (tpm_state.log_area_next_entry + size - + tpm_state.log_base); + if (logsize > tpm_state.log_area_size) { + dprintf("TCGBIOS: LOG OVERFLOW: size = %zu\n", size); + return TCGBIOS_LOGOVERFLOW; + } + + dest = tpm_state.log_area_next_entry; + memcpy(dest, entry, sizeof(*entry) + digest_len); + + t = dest + sizeof(*entry) + digest_len; + t->eventdatasize = cpu_to_log32(event_length); + if (event_length) + memcpy(t->event, event, event_length); + + tpm_state.log_area_next_entry += size; + + return 0; +} + +/* Add an entry at the start of the log describing digest formats + */ +static int tpm20_write_EfiSpecIdEventStruct(void) +{ + struct { + struct TCG_EfiSpecIdEventStruct hdr; + uint32_t pad[256]; + } event = { + .hdr.signature = "Spec ID Event03", + .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, + .hdr.specVersionMinor = 0, + .hdr.specVersionMajor = 2, + .hdr.specErrata = 0, + .hdr.uintnSize = 2, + }; + struct tpms_pcr_selection *sel; + void *nsel, *end; + int event_size; + uint32_t *vendorInfoSize; + struct tpm_log_entry le = { + .hdr.eventtype = cpu_to_log32(EV_NO_ACTION), + }; + uint32_t count; + + sel = tpm_state.tpm20_pcr_selection->selections; + end = (void*)tpm_state.tpm20_pcr_selection + + tpm_state.tpm20_pcr_selection_size; + + for (count = 0; + count < be32_to_cpu(tpm_state.tpm20_pcr_selection->count); + count++) { + int hsize; + uint8_t sizeOfSelect = sel->sizeOfSelect; + + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf("TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, + digestSizes[count+1]); + if (event_size > sizeof(event) - sizeof(uint32_t)) { + dprintf("EfiSpecIdEventStruct pad too small\n"); + return -1; + } + + event.hdr.digestSizes[count].algorithmId = + cpu_to_log16(be16_to_cpu(sel->hashAlg)); + event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize); + + sel = nsel; + } + + if (sel != end) { + dprintf("Malformed pcr selection structure fron TPM\n"); + return -1; + } + + event.hdr.numberOfAlgorithms = cpu_to_log32(count); + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, + digestSizes[count]); + vendorInfoSize = (void*)&event + event_size; + *vendorInfoSize = 0; + event_size += sizeof(*vendorInfoSize); + + return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size); +} + +static int tpm20_startup(void) +{ + int ret; + + ret = tpm_simple_cmd(0, TPM2_CC_Startup, + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); + dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n", + ret); + + if (ret) + goto err_exit; + + ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, + 1, TPM2_YES, TPM_DURATION_TYPE_LONG); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n", + ret); + + if (ret) + goto err_exit; + + ret = tpm20_get_pcrbanks(); + if (ret) + goto err_exit; + + /* the log parameters will be passed from Forth layer */ + + return 0; + +err_exit: + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_set_failure(); + return -1; +} + +uint32_t tpm_start(void) +{ + probe_tpm(); + + if (!tpm_is_working()) { + dprintf("%s: Machine does not have a working TPM\n", + __func__); + return TCGBIOS_FATAL_COM_ERROR; + } + + return tpm20_startup(); +} + +void tpm_finalize(void) +{ + spapr_vtpm_finalize(); +} + +static void tpm20_prepboot(void) +{ + uint8_t auth[20]; + int ret; + + ret = tpm20_stirrandom(); + if (ret) + goto err_exit; + + ret = tpm20_getrandom(&auth[0], sizeof(auth)); + if (ret) + goto err_exit; + + ret = tpm20_hierarchychangeauth(auth); + if (ret) + goto err_exit; + + return; + +err_exit: + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_set_failure(); +} + +/* + * Prepare TPM for boot; this function has to be called before + * the firmware transitions to the boot loader. + */ +uint32_t tpm_leave_firmware(void) +{ + tpm20_prepboot(); + + return 0; +} + +/**************************************************************** + * Forth interface + ****************************************************************/ + +void tpm_set_log_parameters(void *addr, size_t size) +{ + int ret; + + dprintf("Log is at 0x%llx; size is %zu bytes\n", + (uint64_t)addr, size); + tpm_state.log_base = addr; + tpm_state.log_area_next_entry = addr; + tpm_state.log_area_size = size; + + ret = tpm20_write_EfiSpecIdEventStruct(); + if (ret) + tpm_set_failure(); +} + +uint32_t tpm_get_logsize(void) +{ + uint32_t logsize = tpm_state.log_area_next_entry - tpm_state.log_base; + + dprintf("log size: %u\n", logsize); + + return logsize; +} + +/* + * Add a measurement to the log; + * + * Input parameters: + * @pcrindex : PCR to extend + * @event_type : type of event + * @info : pointer to info (i.e., string) to be added to the log as-is + * @info_length: length of the info + * @hashdata : pointer to data to be hashed + * @hashdata_length: length of the data + * + */ +static uint32_t tpm_add_measurement_to_log(uint32_t pcrindex, + uint32_t eventtype, + const char *info, + uint32_t infolen, + const uint8_t *hashdata, + uint32_t hashdatalen) +{ + uint8_t hash[SHA256_BUFSIZE]; + struct tpm_log_entry le = { + .hdr.pcrindex = cpu_to_log32(pcrindex), + .hdr.eventtype = cpu_to_log32(eventtype), + }; + int digest_len; + int ret; + + sha256(hashdata, hashdatalen, hash); + digest_len = tpm20_build_digest(&le, hash, true); + if (digest_len < 0) + return TCGBIOS_GENERAL_ERROR; + ret = tpm20_extend(&le, digest_len); + if (ret) { + tpm_set_failure(); + return TCGBIOS_COMMAND_ERROR; + } + tpm20_build_digest(&le, hash, false); + return tpm_log_event_long(&le.hdr, digest_len, info, infolen); +} + +/* + * Add an EV_ACTION measurement to the list of measurements + */ +static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string) +{ + uint32_t len = strlen(string); + + return tpm_add_measurement_to_log(pcrIndex, EV_ACTION, + string, len, (uint8_t *)string, len); +} + +/* + * Add event separators for a range of PCRs + */ +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr) +{ + static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; + uint32_t pcrIndex; + int rc; + + if (!tpm_is_working()) + return TCGBIOS_GENERAL_ERROR; + + if (start_pcr >= 24 || start_pcr > end_pcr) + return TCGBIOS_INVALID_INPUT_PARA; + + /* event separators need to be extended and logged for PCRs 0-7 */ + for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { + rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, + NULL, 0, + evt_separator, + sizeof(evt_separator)); + if (rc) + return rc; + } + + return 0; +} + +uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr, + uint32_t length) +{ + uint32_t rc; + const char *string; + + if (!tpm_is_working()) + return TCGBIOS_GENERAL_ERROR; + + if (length < 0x200) + return TCGBIOS_INVALID_INPUT_PARA; + + string = "Booting BCV device 00h (Floppy)"; + if (bootdrv == BCV_DEVICE_HDD) + string = "Booting BCV device 80h (HDD)"; + + rc = tpm_add_action(4, string); + if (rc) + return rc; + + /* + * equivalent to: dd if=/dev/hda ibs=1 count=440 | sha256sum + */ + string = "MBR"; + rc = tpm_add_measurement_to_log(4, EV_IPL, + string, strlen(string), + addr, 0x1b8); + if (rc) + return rc; + + /* + * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha256sum + */ + string = "MBR PARTITION TABLE"; + return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, + string, strlen(string), + addr + 0x1b8, 0x48); +} + +uint32_t tpm_measure_scrtm(void) +{ + uint32_t rc; + char *version_start = strstr((char *)&print_version, "FW Version"); + char *version_end; + uint32_t version_length; + char *slof_text_start = (char *)&_slof_text; + uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text; + const char *scrtm = "S-CRTM Contents"; + + version_end = strchr(version_start, '\r'); + version_length = version_end - version_start; + + dprintf("Measure S-CRTM Version: addr = %p, length = %d\n", + version_start, version_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION, + version_start, version_length, + (uint8_t *)version_start, + version_length); + if (rc) + return rc; + + dprintf("Measure S-CRTM Content (text): start = %p, length = %d\n", + slof_text_start, slof_text_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS, + scrtm, strlen(scrtm), + (uint8_t *)slof_text_start, + slof_text_length); + + return rc; +} + +/* + * tpm_driver_get_failure_reason: Function for interfacing with the firmware + * API + */ +uint32_t tpm_driver_get_failure_reason(void) +{ + /* do not check for a working TPM here */ + if (!tpm_state.tpm_found) + return VTPM_DRV_STATE_INVALID; + + return spapr_vtpm_get_error(); +} + +/* + * tpm_driver_set_failure_reason: Function for interfacing with the firmware + * API + */ +void tpm_driver_set_failure_reason(uint32_t errcode) +{ + if (!tpm_state.tpm_found) + return; + + spapr_vtpm_set_error(errcode); +} diff --git a/lib/libtpm/tcgbios.h b/lib/libtpm/tcgbios.h new file mode 100644 index 0000000..e9f9c36 --- /dev/null +++ b/lib/libtpm/tcgbios.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * Copyright (c) 2015-2020 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef TCGBIOS_H +#define TCGBIOS_H + +#include <stdint.h> +#include <stdbool.h> + +uint32_t tpm_start(void); +void tpm_finalize(void); +uint32_t tpm_leave_firmware(void); +uint32_t tpm_measure_scrtm(void); +void tpm_set_log_parameters(void *address, size_t size); +uint32_t tpm_get_logsize(void); +uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr, + uint32_t length); +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr); +uint32_t tpm_driver_get_failure_reason(void); +void tpm_driver_set_failure_reason(uint32_t errcode); +bool tpm_is_working(void); + +#endif /* TCGBIOS_H */ diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h index 835bd36..6892e6f 100644 --- a/lib/libtpm/tcgbios_int.h +++ b/lib/libtpm/tcgbios_int.h @@ -15,6 +15,83 @@ #include <stdint.h> +/* internal error codes */ +#define TCGBIOS_OK 0x0 +#define TCGBIOS_LOGOVERFLOW 0x1 +#define TCGBIOS_GENERAL_ERROR 0x2 +#define TCGBIOS_FIRMWARE_ERROR 0x3 +#define TCGBIOS_FATAL_COM_ERROR 0x4 +#define TCGBIOS_INVALID_INPUT_PARA 0x5 +#define TCGBIOS_COMMAND_ERROR 0x6 +#define TCGBIOS_INTERFACE_SHUTDOWN 0x7 + +/* + * event types from spec: + * TCG PC Client Specific Implementation Specification + * for Conventional BIOS + */ +#define EV_POST_CODE 1 +#define EV_NO_ACTION 3 +#define EV_SEPARATOR 4 +#define EV_ACTION 5 +#define EV_EVENT_TAG 6 +#define EV_S_CRTM_CONTENTS 7 +#define EV_S_CRTM_VERSION 8 +#define EV_IPL 13 +#define EV_IPL_PARTITION_DATA 14 + +#define BCV_DEVICE_HDD 0x80 + +/* hash sizes */ +#define SHA1_BUFSIZE 20 +#define SHA256_BUFSIZE 32 +#define SHA384_BUFSIZE 48 +#define SHA512_BUFSIZE 64 +#define SM3_256_BUFSIZE 32 + +/* + * Logging for TPM 2 is specified in TCG spec "TCG PC Client Platform + * Firmware Profile Specification" in section "Event Logging" and sub- + * section "TCG_PCR_EVENT2 structure" + * + * Each entry in the TPM log contains: a TCG_PCR_EVENT2_Header, a variable + * length digest, a TCG_PCR_EVENT2_Trailer, and a variable length event. + * The 'digest' matches what is sent to the TPM hardware via the Extend + * command. On TPM2.0 the digest contains a TPML_DIGEST_VALUES struct + * followed by a variable number of TPMT_HA structs (as specified by the + * hardware via the TPM2_CAP_PCRS request). + */ +typedef struct tdTCG_PCR_EVENT2_Header { + uint32_t pcrindex; + uint32_t eventtype; + uint8_t digests[0]; +} __attribute__((packed)) TCG_PCR_EVENT2_Header; + +typedef struct tdTCG_PCR_EVENT2_Trailer { + uint32_t eventdatasize; + uint8_t event[0]; +} __attribute__((packed)) TCG_PCR_EVENT2_Trailer; + +struct TCG_EfiSpecIdEventStruct { + uint8_t signature[16]; + uint32_t platformClass; +#define TPM_TCPA_ACPI_CLASS_CLIENT 0 + uint8_t specVersionMinor; + uint8_t specVersionMajor; + uint8_t specErrata; + uint8_t uintnSize; + uint32_t numberOfAlgorithms; + struct TCG_EfiSpecIdEventAlgorithmSize { + uint16_t algorithmId; + uint16_t digestSize; + } digestSizes[0]; + /* + uint8_t vendorInfoSize; + uint8_t vendorInfo[0]; + */ +} __attribute__((packed)); + +/* Input and Output headers for all TPM commands */ struct tpm_req_header { uint16_t tag; uint32_t totlen; @@ -27,4 +104,168 @@ struct tpm_rsp_header { uint32_t errcode; } __attribute__((packed)); +/**************************************************************** + * TPM v2.0 hardware commands + * + * Relevant specs for #defines and commonly used structures: + * - Trusted Platform Module Library; Part 2: Structures + * Relevant specs for command structures: + * - Trusted Platform Module Library; Part 3: Commands + ****************************************************************/ + +#define TPM2_NO 0 +#define TPM2_YES 1 + +#define TPM2_SU_CLEAR 0x0000 +#define TPM2_SU_STATE 0x0001 + +#define TPM2_RH_OWNER 0x40000001 +#define TPM2_RS_PW 0x40000009 +#define TPM2_RH_ENDORSEMENT 0x4000000b +#define TPM2_RH_PLATFORM 0x4000000c + +#define TPM2_ALG_SHA1 0x0004 +#define TPM2_ALG_SHA256 0x000b +#define TPM2_ALG_SHA384 0x000c +#define TPM2_ALG_SHA512 0x000d +#define TPM2_ALG_SM3_256 0x0012 + +/* TPM 2 command tags */ +#define TPM2_ST_NO_SESSIONS 0x8001 +#define TPM2_ST_SESSIONS 0x8002 + +/* TPM 2 commands */ +#define TPM2_CC_HierarchyControl 0x121 +#define TPM2_CC_Clear 0x126 +#define TPM2_CC_ClearControl 0x127 +#define TPM2_CC_HierarchyChangeAuth 0x129 +#define TPM2_CC_PCR_Allocate 0x12b +#define TPM2_CC_SelfTest 0x143 +#define TPM2_CC_Startup 0x144 +#define TPM2_CC_Shutdown 0x145 +#define TPM2_CC_StirRandom 0x146 +#define TPM2_CC_GetCapability 0x17a +#define TPM2_CC_GetRandom 0x17b +#define TPM2_CC_PCR_Extend 0x182 + +/* TPM 2 Capabilities */ +#define TPM2_CAP_PCRS 0x00000005 + +/* TPM 2 data structures */ + +struct TPMT_HA { + uint16_t hashAlg; + uint8_t hash[0]; /* size depends on hashAlg */ +} __attribute__((packed)); + +struct TPML_DIGEST_VALUES { + uint32_t count; + struct TPMT_HA digest[0]; /* variable number of entries */ +} __attribute__((packed)); + +struct tpm2_req_stirrandom { + struct tpm_req_header hdr; + uint16_t size; + uint64_t stir; +} __attribute__((packed)); + +struct tpm2_req_getrandom { + struct tpm_req_header hdr; + uint16_t bytesRequested; +} __attribute__((packed)); + +struct tpm2b_20 { + uint16_t size; + uint8_t buffer[20]; +} __attribute__((packed)); + +struct tpm2_res_getrandom { + struct tpm_rsp_header hdr; + struct tpm2b_20 rnd; +} __attribute__((packed)); + +/* + * tpm2_authblock is used in TPM 2 commands using 'Auth. Handle' + */ +struct tpm2_authblock { + uint32_t handle; + uint16_t noncesize; /* always 0 */ + uint8_t contsession; /* always TPM2_YES */ + uint16_t pwdsize; /* always 0 */ +} __attribute__((packed)); + +struct tpm2_req_hierarchychangeauth { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + struct tpm2b_20 newAuth; +} __attribute__((packed)); + +struct tpm2_req_extend { + struct tpm_req_header hdr; + uint32_t pcrindex; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint8_t digest[0]; +} __attribute__((packed)); + +struct tpm2_req_clearcontrol { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint8_t disable; +} __attribute__((packed)); + +struct tpm2_req_clear { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; +} __attribute__((packed)); + +struct tpm2_req_hierarchycontrol { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint32_t enable; + uint8_t state; +} __attribute__((packed)); + +struct tpm2_req_getcapability { + struct tpm_req_header hdr; + uint32_t capability; + uint32_t property; + uint32_t propertycount; +} __attribute__((packed)); + +struct tpm2_res_getcapability { + struct tpm_rsp_header hdr; + uint8_t moreData; + uint32_t capability; + uint8_t data[0]; /* capability dependent data */ +} __attribute__((packed)); + +struct tpm2_req_pcr_allocate { + struct tpm_req_header hdr; + uint32_t authhandle; + uint32_t authblocksize; + struct tpm2_authblock authblock; + uint32_t count; + uint8_t tpms_pcr_selections[4]; +} __attribute__((packed)); + +struct tpms_pcr_selection { + uint16_t hashAlg; + uint8_t sizeOfSelect; + uint8_t pcrSelect[0]; +} __attribute__((packed)); + +struct tpml_pcr_selection { + uint32_t count; + struct tpms_pcr_selection selections[0]; +} __attribute__((packed)); + #endif /* TCGBIOS_INT_H */ diff --git a/lib/libtpm/tpm.code b/lib/libtpm/tpm.code new file mode 100644 index 0000000..05f4547 --- /dev/null +++ b/lib/libtpm/tpm.code @@ -0,0 +1,130 @@ +/****************************************************************************** + * Copyright (c) 2015-2020 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * libtpm bindings for SLOF - implementation + */ + +#include <tcgbios.h> +#include <stdbool.h> + +/************************************************/ +/* Startup TPM code */ +/* SLOF: tpm-start ( -- errcode ) */ +/* LIBTPM: tpm_start(void) */ +/************************************************/ +PRIM(tpm_X2d_start) + PUSH; + TOS.n = tpm_start(); +MIRP + +/************************************************/ +/* Shutdown TPM layer before OS takes over */ +/* SLOF: tpm-finalize ( -- ) */ +/* LIBTPM: tpm_finalize(void) */ +/************************************************/ +PRIM(tpm_X2d_finalize) + tpm_finalize(); +MIRP + +/***************************************************************/ +/* Prepare TPM state for bootloader */ +/* SLOF: tpm-leave-firwmare ( -- errcode ) */ +/* LIBTPM: tpm_leave_firmware(void) */ +/***************************************************************/ +PRIM(tpm_X2d_leave_X2d_firmware) + PUSH; + TOS.n = tpm_leave_firmware(); +MIRP + +/*************************************************************/ +/* Convey log address and size */ +/* SLOF: tpm-set-log-parameters ( addr size -- ) */ +/* LIBTPM: tpm_set_log_parameters(void *addr, uint64_t size) */ +/*************************************************************/ +PRIM(tpm_X2d_set_X2d_log_X2d_parameters) + int size = TOS.u; POP; + void *addr = TOS.a; POP; + tpm_set_log_parameters(addr, size); +MIRP + +/*********************************************************/ +/* Firmware API */ +/* SLOF: tpm-driver-get_failure-reason ( -- errcode) */ +/* LIBTPM: errcode = tpm_driver_get_failure_reason(void) */ +/*********************************************************/ +PRIM(tpm_X2d_driver_X2d_get_X2d_failure_X2d_reason) + PUSH; + TOS.n = tpm_driver_get_failure_reason(); +MIRP + +/********************************************************/ +/* Firmware API */ +/* SLOF: tpm-driver-set-failure_reason ( errcode -- ) */ +/* LIBTPM: tpm_driver_set_failure_reason(errcode) */ +/********************************************************/ +PRIM(tpm_X2d_driver_X2d_set_X2d_failure_X2d_reason) + int errcode = TOS.u; POP; + tpm_driver_set_failure_reason(errcode); +MIRP + +/************************************************/ +/* Get the size of the log */ +/* SLOF: tpm-get-logsize ( -- size ) */ +/* LIBTPM: logsize = tpm_get_logsize(void) */ +/************************************************/ +PRIM(tpm_X2d_get_X2d_logsize) + PUSH; + TOS.n = tpm_get_logsize(); +MIRP + +/**********************************************************************/ +/* Measure and log event separators */ +/* SLOF: tpm-add-event-separators ( start-pcr end-pcr -- errcode) */ +/* LIBTPM: errcode = tpm_add_event_separators(start_pcr, end_pcr) */ +/**********************************************************************/ +PRIM(tpm_X2d_add_X2d_event_X2d_separators) + int end_pcr = TOS.u; POP; + int start_pcr = TOS.u; + TOS.n = tpm_add_event_separators(start_pcr, end_pcr); +MIRP + +/*************************************************************************/ +/* Measure and log boot connect vector (bcv) device's master boot record */ +/* SLOF: tpm-measure-bcv-mbr ( bootdrv addr length -- errcode ) */ +/* LIBTPM: errcode = tpm_measure_bcv_mbr(bbotdrv, addr, length) */ +/*************************************************************************/ +PRIM(tpm_X2d_measure_X2d_bcv_X2d_mbr) + int length = TOS.u; POP; + void *addr = TOS.a; POP; + int bootdrv = TOS.u; + TOS.n = tpm_measure_bcv_mbr(bootdrv, addr, length); +MIRP + +/************************************************/ +/* Check whether the TPM is working */ +/* SLOF: tpm-is-working ( -- true | false ) */ +/* LIBTPM: bool = tpm_is_working() */ +/************************************************/ +PRIM(tpm_X2d_is_X2d_working) + PUSH; + TOS.n = tpm_is_working(); +MIRP + +/************************************************/ +/* Have the S-CRTM measured */ +/* SLOF: tpm-measure-scrtm ( -- errcode ) */ +/* LIBTPM: errcode = tpm_measure_scrtm */ +/************************************************/ +PRIM(tpm_X2d_measure_X2d_scrtm) + PUSH; + TOS.n = tpm_measure_scrtm(); +MIRP diff --git a/lib/libtpm/tpm.in b/lib/libtpm/tpm.in new file mode 100644 index 0000000..22713e4 --- /dev/null +++ b/lib/libtpm/tpm.in @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2015-2020 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * libtpm bindings for SLOF - definitions + */ + +cod(tpm-start) +cod(tpm-finalize) +cod(tpm-leave-firmware) +cod(tpm-set-log-parameters) +cod(tpm-get-logsize) +cod(tpm-add-event-separators) +cod(tpm-measure-bcv-mbr) +cod(tpm-is-working) +cod(tpm-measure-scrtm) +cod(tpm-driver-get-failure-reason) +cod(tpm-driver-set-failure-reason) diff --git a/slof/fs/packages/disk-label.fs b/slof/fs/packages/disk-label.fs index 790d433..969eb00 100644 --- a/slof/fs/packages/disk-label.fs +++ b/slof/fs/packages/disk-label.fs @@ -338,6 +338,14 @@ CONSTANT /gpt-part-entry dup c@ eb = swap 2+ c@ 90 = and ; +: measure-mbr ( addr length -- ) + s" /ibm,vtpm" find-node ?dup IF + s" measure-hdd-mbr" rot $call-static + ELSE + 2drop + THEN +; + \ NOTE: block-size is always 512 bytes for DOS partition tables. : load-from-dos-boot-partition ( addr -- size ) @@ -361,6 +369,7 @@ CONSTANT /gpt-part-entry block-size * to part-offset 0 0 seek drop ( addr offset ) block-size * read ( size ) + block block-size measure-mbr UNLOOP EXIT ELSE 2drop ( addr ) diff --git a/slof/fs/start-up.fs b/slof/fs/start-up.fs index 7020f5c..c1f931a 100644 --- a/slof/fs/start-up.fs +++ b/slof/fs/start-up.fs @@ -56,6 +56,11 @@ ; : (boot?) ( -- ) + \ last step before we boot we give up physical presence on the TPM + s" /ibm,vtpm" find-node ?dup IF + s" leave-firmware" rot $call-static + THEN + of-prompt? not auto-boot? and IF (boot) THEN |