From patchwork Sat Jan 11 01:21:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 1221520 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47vht3632Dz9sP6 for ; Sat, 11 Jan 2020 12:22:35 +1100 (AEDT) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 47vht35GByzDqkZ for ; Sat, 11 Jan 2020 12:22:35 +1100 (AEDT) X-Original-To: slof@lists.ozlabs.org Delivered-To: slof@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=linux.ibm.com (client-ip=148.163.158.5; helo=mx0a-001b2d01.pphosted.com; envelope-from=stefanb@linux.ibm.com; receiver=) Authentication-Results: lists.ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.ibm.com Received: from mx0a-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 47vhsW0874zDqfB for ; Sat, 11 Jan 2020 12:22:06 +1100 (AEDT) Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 00B1Hxaw045500; Fri, 10 Jan 2020 20:22:03 -0500 Received: from ppma01wdc.us.ibm.com (fd.55.37a9.ip4.static.sl-reverse.com [169.55.85.253]) by mx0b-001b2d01.pphosted.com with ESMTP id 2xee145vrh-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 10 Jan 2020 20:22:03 -0500 Received: from pps.filterd (ppma01wdc.us.ibm.com [127.0.0.1]) by ppma01wdc.us.ibm.com (8.16.0.27/8.16.0.27) with SMTP id 00B1LZKq009930; Sat, 11 Jan 2020 01:22:09 GMT Received: from b03cxnp08027.gho.boulder.ibm.com (b03cxnp08027.gho.boulder.ibm.com [9.17.130.19]) by ppma01wdc.us.ibm.com with ESMTP id 2xajb7ba3f-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sat, 11 Jan 2020 01:22:08 +0000 Received: from b03ledav001.gho.boulder.ibm.com (b03ledav001.gho.boulder.ibm.com [9.17.130.232]) by b03cxnp08027.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 00B1M1lW60883278 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sat, 11 Jan 2020 01:22:01 GMT Received: from b03ledav001.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 85DD46E04E; Sat, 11 Jan 2020 01:22:01 +0000 (GMT) Received: from b03ledav001.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id DE8656E053; Sat, 11 Jan 2020 01:22:00 +0000 (GMT) Received: from sbct-3.pok.ibm.com (unknown [9.47.158.153]) by b03ledav001.gho.boulder.ibm.com (Postfix) with ESMTP; Sat, 11 Jan 2020 01:22:00 +0000 (GMT) From: Stefan Berger To: slof@lists.ozlabs.org, aik@ozlabs.ru Date: Fri, 10 Jan 2020 20:21:54 -0500 Message-Id: <20200111012155.3350198-7-stefanb@linux.ibm.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200111012155.3350198-1-stefanb@linux.ibm.com> References: <20200111012155.3350198-1-stefanb@linux.ibm.com> MIME-Version: 1.0 X-TM-AS-GCONF: 00 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.138, 18.0.572 definitions=2020-01-10_04:2020-01-10, 2020-01-10 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 suspectscore=9 malwarescore=0 mlxlogscore=999 impostorscore=0 adultscore=0 phishscore=0 lowpriorityscore=0 mlxscore=0 spamscore=0 bulkscore=0 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-1910280000 definitions=main-2001110008 Subject: [SLOF] [PATCH v5 6/7] tcgbios: Add TPM 2.0 support and firmware API X-BeenThere: slof@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Patches for https://github.com/aik/SLOF" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kevin@koconnor.net, Stefan Berger Errors-To: slof-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "SLOF" 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 --- board-qemu/slof/Makefile | 13 +- board-qemu/slof/tree.fs | 3 + board-qemu/slof/vio-vtpm-cdriver.fs | 137 +++++ board-qemu/slof/vtpm-sml.fs | 123 ++++ include/helpers.h | 1 + lib/libtpm/Makefile | 2 +- lib/libtpm/tcgbios.c | 916 ++++++++++++++++++++++++++++ lib/libtpm/tcgbios.h | 32 + lib/libtpm/tcgbios_int.h | 240 ++++++++ lib/libtpm/tpm.code | 130 ++++ lib/libtpm/tpm.in | 26 + slof/fs/packages/disk-label.fs | 10 +- slof/fs/start-up.fs | 7 + 13 files changed, 1635 insertions(+), 5 deletions(-) create mode 100644 board-qemu/slof/vio-vtpm-cdriver.fs create mode 100644 board-qemu/slof/vtpm-sml.fs create mode 100644 lib/libtpm/tcgbios.c create mode 100644 lib/libtpm/tcgbios.h create mode 100644 lib/libtpm/tpm.code create mode 100644 lib/libtpm/tpm.in 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..24b5290 --- /dev/null +++ b/board-qemu/slof/vio-vtpm-cdriver.fs @@ -0,0 +1,137 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015-2020 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +." Populating " pwd + +false VALUE vtpm-debug? +0 VALUE vtpm-unit +0 VALUE vtpm-ihandle + +: setup-alias + " ibm,vtpm" find-alias 0= IF + " ibm,vtpm" get-node node>path set-alias + ELSE + drop + THEN +; + +: vtpm-cleanup ( ) + vtpm-debug? IF ." VTPM: Disabling RTAS bypass" cr THEN + tpm-finalize + vtpm-unit 0 rtas-set-tce-bypass +; + +: vtpm-init ( -- true | false ) + 0 0 get-node open-node ?dup 0= IF EXIT THEN + my-self >r + dup to my-self + + vtpm-debug? IF ." VTPM: Initializing for c-driver" cr THEN + + my-unit to vtpm-unit + + \ Enable TCE bypass special qemu feature + vtpm-unit 1 rtas-set-tce-bypass + + \ Have TCE bypass cleaned up + ['] vtpm-cleanup add-quiesce-xt + + tpm-start dup 0= IF + vtpm-debug? IF ." VTPM: Success from tpm-start" cr THEN + drop + setup-alias + ELSE + ." VTPM: Error code from tpm-start: " . cr + THEN + + close-node + r> to my-self +; + +\ forward a call to /ibm,vtpm, which implements the function with the +\ given name +: vtpm-call-forward ( arg ... arg name namelen -- ret ... ret failure? ) + \ assign /ibm,vtpm node to vtpm-ihandle, if not assigned + vtpm-ihandle 0= IF + s" /ibm,vtpm" open-dev to vtpm-ihandle + THEN + + vtpm-ihandle 0<> IF + vtpm-ihandle ( arg ... arg name namelen ihandle ) + $call-method ( ret ... ret ) + false ( ret ... ret false ) + ELSE + true ( true ) + THEN +; + +\ firmware API call +: sml-get-allocated-size ( -- buffer-size) + " sml-get-allocated-size" vtpm-call-forward IF + \ vtpm-call-forward failed + 0 + THEN +; + +\ firmware API call +: sml-get-handover-size ( -- size) + " sml-get-handover-size" vtpm-call-forward IF + \ vtpm-call-forward failed + 0 + THEN +; + +\ firmware API call +: sml-handover ( dest size -- ) + " sml-handover" vtpm-call-forward IF + \ vtpm-call-forward failed; clean up stack + 2drop + THEN +; + +\ firmware API call +: get-failure-reason ( -- reason ) + " get-failure-reason" vtpm-call-forward IF + \ vtpm-call-forward failed; return a value + 0 \ invalid + THEN +; + +0 0 s" ibm,sml-efi-reformat-supported" property + +\ firmware API call +: reformat-sml-to-efi-alignment ( -- success ) + " reformat-sml-to-efi-alignment" vtpm-call-forward IF + false + THEN +; + +: open ( ) + vtpm-debug? IF ." VTPM: vTPM open()" cr THEN + true +; + +: close ( ) + vtpm-debug? IF ." VTPM: vTPM close()" cr THEN +; + +\ setup alias and the RTAS bypass +vtpm-init + +\ setup the log +include vtpm-sml.fs + +s" /ibm,vtpm" find-node dup IF + s" measure-scrtm" rot $call-static +ELSE + drop +THEN diff --git a/board-qemu/slof/vtpm-sml.fs b/board-qemu/slof/vtpm-sml.fs new file mode 100644 index 0000000..a140276 --- /dev/null +++ b/board-qemu/slof/vtpm-sml.fs @@ -0,0 +1,123 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015-2020 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +\ KVM/qemu TPM Stored Measurement Log (SML) entries in /ibm,vtpm + +" /" find-device + +new-device + +false VALUE vtpm-debug? +0 VALUE log-base +40000 CONSTANT LOG-SIZE \ 256k per VTPM FW spec. + +e CONSTANT VTPM_DRV_ERROR_SML_HANDED_OVER + +LOG-SIZE BUFFER: log-base + +\ create /ibm,vtpm +s" ibm,vtpm" 2dup device-name device-type + +\ convey logbase and size to the C driver +log-base LOG-SIZE tpm-set-log-parameters + +: sml-get-allocated-size ( -- buffer-size) + vtpm-debug? IF + ." Call to sml-get-allocated-size; size = 0x" LOG-SIZE . cr + THEN + LOG-SIZE +; + +: sml-get-handover-size ( -- size ) + tpm-get-logsize + vtpm-debug? IF + ." Call to sml-get-handover-size; size = 0x" dup . cr + THEN +; + +: sml-handover ( dest size -- ) + vtpm-debug? IF + 2dup + ." Call to sml-handover; size = 0x" . ." dest = " . cr + THEN + log-base ( dest size src ) + -rot ( src dest size ) + move + + VTPM_DRV_ERROR_SML_HANDED_OVER tpm-driver-set-failure-reason +; + +: get-failure-reason ( -- reason ) + tpm-driver-get-failure-reason ( reason ) + vtpm-debug? IF + ." VTPM: Return value from tpm-driver-get-failure-reason: " dup . cr + THEN +; + +: reformat-sml-to-efi-alignment ( -- success? ) + vtpm-debug? IF + ." Call to reformat-sml-to-efi-alignment" cr + THEN + \ a no-op since already byte aligned + true +; + +\ +\ internal API calls +\ + +: separator-event ( start-pcr end-pcr -- ) + tpm-add-event-separators ( errcode ) + dup 0<> IF + ." VTPM: Error code from tpm-add-event-separators: " . cr + ELSE + drop + THEN +; + +80 CONSTANT BCV_DEVICE_HDD + +: measure-hdd-mbr ( addr -- ) + 0 7 separator-event + 200 BCV_DEVICE_HDD ( addr length bootdrv ) + -rot ( bootdrv addr length ) + tpm-measure-bcv-mbr ( errcode ) + dup 0<> IF + ." VTPM: Error code from tpm-measure-hdd: " . cr + ELSE + drop + THEN +; + +: leave-firmware ( -- ) + tpm-leave-firmware ( errcode ) + dup 0<> IF + ." VTPM: Error code from tpm-leave-firmware: " . cr + ELSE + drop + THEN +; + +: measure-scrtm ( -- ) + tpm-measure-scrtm ( errcode ) + dup 0<> IF + ." VTPM: Error code from tpm-measure-scrtm: " . cr + ELSE + drop + THEN +; + +: open true ; +: close ; + +finish-device +device-end 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 52d22f2..2e3d644 100644 --- a/lib/libtpm/Makefile +++ b/lib/libtpm/Makefile @@ -23,7 +23,7 @@ TARGET = ../libtpm.a all: $(TARGET) -SRCS = tpm_drivers.c sha1.c +SRCS = tpm_drivers.c sha1.c tcgbios.c OBJS = $(SRCS:%.c=%.o) diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c new file mode 100644 index 0000000..ba5cc46 --- /dev/null +++ b/lib/libtpm/tcgbios.c @@ -0,0 +1,916 @@ +/***************************************************************************** + * Copyright (c) 2015-2020 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + * Kevin O'Connor (SeaBIOS) + *****************************************************************************/ + +/* + * Implementation of the TPM BIOS extension according to the specification + * described in the IBM VTPM Firmware document and the TCG Specification + * that can be found here under the following link: + * http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios + */ + +#include +#include + +#include "types.h" +#include "byteorder.h" +#include "tpm_drivers.h" +#include "string.h" +#include "tcgbios.h" +#include "tcgbios_int.h" +#include "stdio.h" +#include "sha1.h" +#include "helpers.h" +#include "version.h" +#include "OF.h" + +#undef TCGBIOS_DEBUG +//#define TCGBIOS_DEBUG +#ifdef TCGBIOS_DEBUG +#define dprintf(_x ...) do { printf("TCGBIOS: " _x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +struct tpm_state { + unsigned tpm_probed:1; + unsigned tpm_found:1; + unsigned tpm_working:1; + + /* base address of the log area */ + uint8_t *log_base; + + /* size of the logging area */ + size_t log_area_size; + + /* where to write the next log entry to */ + uint8_t *log_area_next_entry; +}; + +static struct tpm_state tpm_state; + +/* + * TPM 2 logs are written in little endian format. + */ +static inline uint32_t log32_to_cpu(uint32_t val) +{ + return le32_to_cpu(val); +} + +static inline uint32_t cpu_to_log32(uint32_t val) +{ + return cpu_to_le32(val); +} + +static inline uint16_t cpu_to_log16(uint16_t val) +{ + return cpu_to_le16(val); +} + +/******************************************************** + Extensions for TCG-enabled BIOS + *******************************************************/ + +static void probe_tpm(void) +{ + tpm_state.tpm_probed = true; + tpm_state.tpm_found = spapr_is_vtpm_present(); + tpm_state.tpm_working = tpm_state.tpm_found; +} + +/**************************************************************** + * Digest formatting + ****************************************************************/ + +static uint32_t tpm20_pcr_selection_size; +static struct tpml_pcr_selection *tpm20_pcr_selection; + +/* A 'struct tpm_log_entry' is a local data structure containing a + * 'tpm_log_header' followed by space for the maximum supported + * digest. (The digest is a sha1 hash on tpm1.2 or a series of + * tpm2_digest_value structs on tpm2.0) + */ +struct tpm_log_entry { + struct tpm_log_header hdr; + uint8_t pad[sizeof(struct tpm2_digest_values) + + 5 * sizeof(struct tpm2_digest_value) + + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE + + SHA512_BUFSIZE + SM3_256_BUFSIZE]; +} __attribute__((packed)); + +static const struct hash_parameters { + uint16_t hashalg; + uint8_t hashalg_flag; + uint8_t hash_buffersize; + const char *name; +} hash_parameters[] = { + { + .hashalg = TPM2_ALG_SHA1, + .hash_buffersize = SHA1_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA256, + .hash_buffersize = SHA256_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA384, + .hash_buffersize = SHA384_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SHA512, + .hash_buffersize = SHA512_BUFSIZE, + }, { + .hashalg = TPM2_ALG_SM3_256, + .hash_buffersize = SM3_256_BUFSIZE, + } +}; + +static int +tpm20_get_hash_buffersize(uint16_t hashAlg) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { + if (hash_parameters[i].hashalg == hashAlg) + return hash_parameters[i].hash_buffersize; + } + return -1; +} + +/* + * Build the TPM2 tpm2_digest_values data structure from the given hash. + * Follow the PCR bank configuration of the TPM and write the same hash + * in either truncated or zero-padded form in the areas of all the other + * hashes. For example, write the sha1 hash in the area of the sha256 + * hash and fill the remaining bytes with zeros. Or truncate the sha256 + * hash when writing it in the area of the sha1 hash. + * + * le: the log entry to build the digest in + * sha1: the sha1 hash value to use + * bigEndian: whether to build in big endian format for the TPM or log + * little endian for the log (TPM 2.0) + * + * Returns the digest size; -1 on fatal error + */ +static int tpm20_build_digest(struct tpm_log_entry *le, const uint8_t *sha1, + bool bigEndian) +{ + struct tpms_pcr_selection *sel; + void *nsel, *end; + void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values); + uint32_t count; + struct tpm2_digest_value *v; + struct tpm2_digest_values *vs; + + if (!tpm20_pcr_selection) + return -1; + + sel = tpm20_pcr_selection->selections; + end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; + + for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) { + int hsize; + uint8_t sizeOfSelect = sel->sizeOfSelect; + + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf("TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + /* buffer size sanity check before writing */ + v = dest; + if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) { + dprintf("tpm_log_entry is too small\n"); + return -1; + } + + if (bigEndian) + v->hashAlg = sel->hashAlg; + else + v->hashAlg = cpu_to_le16(be16_to_cpu(sel->hashAlg)); + + memset(v->hash, 0, hsize); + memcpy(v->hash, sha1, hsize > SHA1_BUFSIZE ? SHA1_BUFSIZE : hsize); + + dest += sizeof(*v) + hsize; + sel = nsel; + } + + if (sel != end) { + dprintf("Malformed pcr selection structure fron TPM\n"); + return -1; + } + + vs = (void*)le->hdr.digest; + if (bigEndian) + vs->count = cpu_to_be32(count); + else + vs->count = cpu_to_le32(count); + + return dest - (void*)le->hdr.digest; +} + +/**************************************************************** + * TPM hardware command wrappers + ****************************************************************/ + +/* Helper function for sending TPM commands that take a single + * optional parameter (0, 1, or 2 bytes) and have no special response. + */ +static int +tpm_simple_cmd(uint8_t locty, uint32_t ordinal, int param_size, uint16_t param, + enum tpm_duration_type to_t) +{ + struct { + struct tpm_req_header trqh; + uint16_t param; + } __attribute__((packed)) req = { + .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size), + .trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .trqh.ordinal = cpu_to_be32(ordinal), + }; + uint8_t obuffer[64]; + struct tpm_rsp_header *trsh = (void *)obuffer; + uint32_t obuffer_len = sizeof(obuffer); + int ret; + + switch (param_size) { + case 2: + req.param = cpu_to_be16(param); + break; + case 1: + *(uint8_t *)&req.param = param; + break; + } + + memset(obuffer, 0, sizeof(obuffer)); + ret = tpmhw_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t); + ret = ret ? -1 : be32_to_cpu(trsh->errcode); + dprintf("Return from tpm_simple_cmd(%x, %x) = %x\n", + ordinal, param, ret); + + return ret; +} + +static int +tpm20_getcapability(uint32_t capability, uint32_t property, uint32_t count, + struct tpm_rsp_header *rsp, uint32_t rsize) +{ + struct tpm2_req_getcapability trg = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trg)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), + .capability = cpu_to_be32(capability), + .property = cpu_to_be32(property), + .propertycount = cpu_to_be32(count), + }; + + uint32_t resp_size = rsize; + int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size, + TPM_DURATION_TYPE_SHORT); + ret = (ret || + rsize < be32_to_cpu(rsp->totlen)) ? -1 + : be32_to_cpu(rsp->errcode); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", + ret); + + return ret; +} + +static int +tpm20_get_pcrbanks(void) +{ + uint8_t buffer[128]; + uint32_t size; + struct tpm2_res_getcapability *trg = + (struct tpm2_res_getcapability *)&buffer; + + int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, + sizeof(buffer)); + if (ret) + return ret; + + /* defend against (broken) TPM sending packets that are too short */ + uint32_t resplen = be32_to_cpu(trg->hdr.totlen); + if (resplen <= offset_of(struct tpm2_res_getcapability, data)) + return -1; + + size = resplen - offset_of(struct tpm2_res_getcapability, data); + /* we need a valid tpml_pcr_selection up to and including sizeOfSelect*/ + if (size < offset_of(struct tpml_pcr_selection, selections) + + offset_of(struct tpms_pcr_selection, pcrSelect)) + return -1; + + tpm20_pcr_selection = SLOF_alloc_mem(size); + if (tpm20_pcr_selection) { + memcpy(tpm20_pcr_selection, &trg->data, size); + tpm20_pcr_selection_size = size; + } else { + printf("TCGBIOS: Failed to allocated %u bytes.\n", size); + ret = -1; + } + + return ret; +} + +static int tpm20_extend(struct tpm_log_entry *le, int digest_len) +{ + struct tpm2_req_extend tmp_tre = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(0), + .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), + .pcrindex = cpu_to_be32(log32_to_cpu(le->hdr.pcrindex)), + .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + }; + uint8_t buffer[sizeof(tmp_tre) + sizeof(le->pad)]; + struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; + + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); + memcpy(&tre->digest[0], le->hdr.digest, digest_len); + + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len); + + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + return -1; + + return 0; +} + +static int tpm20_stirrandom(void) +{ + struct tpm2_req_stirrandom stir = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(stir)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom), + .size = cpu_to_be16(sizeof(stir.stir)), + .stir = rand(), + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &stir.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_SHORT); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_getrandom(uint8_t *buf, uint16_t buf_len) +{ + struct tpm2_res_getrandom rsp; + struct tpm2_req_getrandom trgr = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trgr)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom), + .bytesRequested = cpu_to_be16(buf_len), + }; + uint32_t resp_length = sizeof(rsp); + + if (buf_len > sizeof(rsp.rnd.buffer)) + return -1; + + int ret = tpmhw_transmit(0, &trgr.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) + ret = -1; + else + memcpy(buf, rsp.rnd.buffer, buf_len); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_hierarchychangeauth(uint8_t auth[20]) +{ + struct tpm2_req_hierarchychangeauth trhca = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trhca)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trhca.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .newAuth = { + .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)), + }, + }; + memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer)); + + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &trhca.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n", + ret); + + return ret; +} + +static int tpm20_hierarchycontrol(uint32_t hierarchy, uint8_t state) +{ + /* we will try to deactivate the TPM now - ignoring all errors */ + struct tpm2_req_hierarchycontrol trh = { + .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trh)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl), + .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), + .authblocksize = cpu_to_be32(sizeof(trh.authblock)), + .authblock = { + .handle = cpu_to_be32(TPM2_RS_PW), + .noncesize = cpu_to_be16(0), + .contsession = TPM2_YES, + .pwdsize = cpu_to_be16(0), + }, + .enable = cpu_to_be32(hierarchy), + .state = state, + }; + struct tpm_rsp_header rsp; + uint32_t resp_length = sizeof(rsp); + int ret = tpmhw_transmit(0, &trh.hdr, &rsp, &resp_length, + TPM_DURATION_TYPE_MEDIUM); + if (ret || resp_length != sizeof(rsp) || rsp.errcode) + ret = -1; + + dprintf("TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n", + ret); + + return ret; +} + +/**************************************************************** + * Setup and Measurements + ****************************************************************/ + +bool tpm_is_working(void) +{ + if (!tpm_state.tpm_probed) + probe_tpm(); + + return tpm_state.tpm_working; +} + +static void tpm_set_failure(void) +{ + tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO); + tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO); + + tpm_state.tpm_working = false; +} + +/* + * Extend the OFDT log with the given entry by copying the + * entry data into the log. + * + * @pcpes: Pointer to the structure to be copied into the log + * @event: The event to be appended to 'pcpes' + * @event_length: The length of the event + * + * Returns 0 on success, an error code otherwise. + */ +static uint32_t tpm_log_event_long(struct tpm_log_header *entry, + int digest_len, + const void *event, uint32_t event_length) +{ + size_t size, logsize; + void *dest; + + dprintf("log base address = %p, next entry = %p\n", + tpm_state.log_base, tpm_state.log_area_next_entry); + + if (tpm_state.log_area_next_entry == NULL) + return TCGBIOS_LOGOVERFLOW; + + size = sizeof(*entry) + digest_len + + sizeof(struct tpm_log_trailer) + event_length; + logsize = (tpm_state.log_area_next_entry + size - + tpm_state.log_base); + if (logsize > tpm_state.log_area_size) { + dprintf("TCGBIOS: LOG OVERFLOW: size = %u\n", size); + return TCGBIOS_LOGOVERFLOW; + } + + dest = tpm_state.log_area_next_entry; + memcpy(dest, entry, sizeof(*entry) + digest_len); + struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len; + t->eventdatasize = cpu_to_log32(event_length); + if (event_length) + memcpy(t->event, event, event_length); + + tpm_state.log_area_next_entry += size; + + return 0; +} + +/* Add an entry at the start of the log describing digest formats + */ +static int tpm20_write_EfiSpecIdEventStruct(void) +{ + if (!tpm20_pcr_selection) + return -1; + + struct { + struct TCG_EfiSpecIdEventStruct hdr; + uint32_t pad[256]; + } event = { + .hdr.signature = "Spec ID Event03", + .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, + .hdr.specVersionMinor = 0, + .hdr.specVersionMajor = 2, + .hdr.specErrata = 0, + .hdr.uintnSize = 2, + }; + + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; + void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; + int event_size; + uint32_t *vendorInfoSize; + struct tpm_log_entry le = { + .hdr.eventtype = cpu_to_log32(EV_NO_ACTION), + }; + uint32_t count; + + for (count = 0; + count < be32_to_cpu(tpm20_pcr_selection->count); + count++) { + int hsize; + uint8_t sizeOfSelect = sel->sizeOfSelect; + + nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf("TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, + digestSizes[count+1]); + if (event_size > sizeof(event) - sizeof(uint32_t)) { + dprintf("EfiSpecIdEventStruct pad too small\n"); + return -1; + } + + event.hdr.digestSizes[count].algorithmId = + cpu_to_log16(be16_to_cpu(sel->hashAlg)); + event.hdr.digestSizes[count].digestSize = cpu_to_log16(hsize); + + sel = nsel; + } + + if (sel != end) { + dprintf("Malformed pcr selection structure fron TPM\n"); + return -1; + } + + event.hdr.numberOfAlgorithms = cpu_to_log32(count); + event_size = offset_of(struct TCG_EfiSpecIdEventStruct, + digestSizes[count]); + vendorInfoSize = (void*)&event + event_size; + *vendorInfoSize = 0; + event_size += sizeof(*vendorInfoSize); + + return tpm_log_event_long(&le.hdr, SHA1_BUFSIZE, &event, event_size); +} + +static int tpm20_startup(void) +{ + int ret; + + ret = tpm_simple_cmd(0, TPM2_CC_Startup, + 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); + dprintf("TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n", + ret); + + if (ret) + goto err_exit; + + ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, + 1, TPM2_YES, TPM_DURATION_TYPE_LONG); + + dprintf("TCGBIOS: Return value from sending TPM2_CC_SELF_TEST = 0x%08x\n", + ret); + + if (ret) + goto err_exit; + + ret = tpm20_get_pcrbanks(); + if (ret) + goto err_exit; + + /* the log parameters will be passed from Forth layer */ + + return 0; + +err_exit: + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_set_failure(); + return -1; +} + +uint32_t tpm_start(void) +{ + probe_tpm(); + + if (!tpm_is_working()) { + dprintf("%s: Machine does not have a working TPM\n", + __func__); + return TCGBIOS_FATAL_COM_ERROR; + } + + return tpm20_startup(); +} + +void tpm_finalize(void) +{ + spapr_vtpm_finalize(); +} + +static void tpm20_prepboot(void) +{ + uint8_t auth[20]; + int ret = tpm20_stirrandom(); + if (ret) + goto err_exit; + + ret = tpm20_getrandom(&auth[0], sizeof(auth)); + if (ret) + goto err_exit; + + ret = tpm20_hierarchychangeauth(auth); + if (ret) + goto err_exit; + + return; + +err_exit: + dprintf("TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_set_failure(); +} + +/* + * Prepare TPM for boot; this function has to be called before + * the firmware transitions to the boot loader. + */ +uint32_t tpm_leave_firmware(void) +{ + tpm20_prepboot(); + + return 0; +} + +/**************************************************************** + * Forth interface + ****************************************************************/ + +void tpm_set_log_parameters(void *addr, size_t size) +{ + int ret; + + dprintf("Log is at 0x%llx; size is %u bytes\n", + (uint64_t)addr, size); + tpm_state.log_base = addr; + tpm_state.log_area_next_entry = addr; + tpm_state.log_area_size = size; + + ret = tpm20_write_EfiSpecIdEventStruct(); + if (ret) + tpm_set_failure(); +} + +uint32_t tpm_get_logsize(void) +{ + uint32_t logsize = tpm_state.log_area_next_entry - tpm_state.log_base; + + dprintf("log size: %u\n", logsize); + + return logsize; +} + +/* + * Add a measurement to the log; + * + * Input parameters: + * @pcrindex : PCR to extend + * @event_type : type of event + * @info : pointer to info (i.e., string) to be added to the log as-is + * @info_length: length of the info + * @hashdata : pointer to data to be hashed + * @hashdata_length: length of the data + * + */ +static uint32_t tpm_add_measurement_to_log(uint32_t pcrindex, + uint32_t eventtype, + const char *info, + uint32_t infolen, + const uint8_t *hashdata, + uint32_t hashdatalen) +{ + uint8_t hash[SHA1_BUFSIZE]; + struct tpm_log_entry le = { + .hdr.pcrindex = cpu_to_log32(pcrindex), + .hdr.eventtype = cpu_to_log32(eventtype), + }; + int digest_len; + + sha1(hashdata, hashdatalen, hash); + digest_len = tpm20_build_digest(&le, hash, true); + if (digest_len < 0) + return TCGBIOS_GENERAL_ERROR; + int ret = tpm20_extend(&le, digest_len); + if (ret) { + tpm_set_failure(); + return TCGBIOS_COMMAND_ERROR; + } + tpm20_build_digest(&le, hash, false); + return tpm_log_event_long(&le.hdr, digest_len, info, infolen); +} + +/* + * Add an EV_ACTION measurement to the list of measurements + */ +static uint32_t tpm_add_action(uint32_t pcrIndex, const char *string) +{ + uint32_t len = strlen(string); + + return tpm_add_measurement_to_log(pcrIndex, EV_ACTION, + string, len, (uint8_t *)string, len); +} + +/* + * Add event separators for a range of PCRs + */ +uint32_t tpm_add_event_separators(uint32_t start_pcr, uint32_t end_pcr) +{ + static const uint8_t evt_separator[] = {0xff,0xff,0xff,0xff}; + uint32_t rc = 0; + uint32_t pcrIndex; + + if (!tpm_is_working()) + return TCGBIOS_GENERAL_ERROR; + + if (start_pcr >= 24 || start_pcr > end_pcr) + return TCGBIOS_INVALID_INPUT_PARA; + + /* event separators need to be extended and logged for PCRs 0-7 */ + for (pcrIndex = start_pcr; pcrIndex <= end_pcr; pcrIndex++) { + rc = tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, + NULL, 0, + evt_separator, + sizeof(evt_separator)); + if (rc) + break; + } + + return rc; +} + +uint32_t tpm_measure_bcv_mbr(uint32_t bootdrv, const uint8_t *addr, + uint32_t length) +{ + uint32_t rc; + const char *string; + + if (!tpm_is_working()) + return TCGBIOS_GENERAL_ERROR; + + if (length < 0x200) + return TCGBIOS_INVALID_INPUT_PARA; + + string = "Booting BCV device 00h (Floppy)"; + if (bootdrv == BCV_DEVICE_HDD) + string = "Booting BCV device 80h (HDD)"; + + rc = tpm_add_action(4, string); + if (rc) + return rc; + + /* + * equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum + */ + string = "MBR"; + rc = tpm_add_measurement_to_log(4, EV_IPL, + string, strlen(string), + addr, 0x1b8); + if (rc) + return rc; + + /* + * equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum + */ + string = "MBR PARTITION TABLE"; + return tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, + string, strlen(string), + addr + 0x1b8, 0x48); +} + +uint32_t tpm_measure_scrtm(void) +{ + uint32_t rc; + char *version_start = strstr((char *)&print_version, "FW Version"); + char *version_end; + uint32_t version_length; + char *slof_data_start = (char *)&_slof_data; + char *slof_text_start = (char *)&_slof_text; + uint32_t slof_data_length = (long)&_slof_data_end - (long)&_slof_data; + uint32_t slof_text_length = (long)&_slof_text_end - (long)&_slof_text; + const char *scrtm = "S-CRTM Contents"; + + version_end = strchr(version_start, '\r'); + version_length = version_end - version_start; + + dprintf("Measure S-CRTM Version: addr = %p, length = %d\n", + version_start, version_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_VERSION, + version_start, version_length, + (uint8_t *)version_start, + version_length); + + if (rc) + return rc; + + dprintf("Measure S-CRTM Content (data): start = %p, length = %d\n", + slof_data_start, slof_data_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS, + scrtm, strlen(scrtm), + (uint8_t *)slof_data_start, + slof_data_length); + + dprintf("Measure S-CRTM Content (text): start = %p, length = %d\n", + slof_text_start, slof_text_length); + + rc = tpm_add_measurement_to_log(0, EV_S_CRTM_CONTENTS, + scrtm, strlen(scrtm), + (uint8_t *)slof_text_start, + slof_text_length); + + return rc; +} + +/* + * tpm_driver_get_failure_reason: Function for interfacing with the firmware + * API + */ +uint32_t tpm_driver_get_failure_reason(void) +{ + /* do not check for a working TPM here */ + if (!tpm_state.tpm_found) + return VTPM_DRV_STATE_INVALID; + + return spapr_vtpm_get_error(); +} + +/* + * tpm_driver_set_failure_reason: Function for interfacing with the firmware + * API + */ +void tpm_driver_set_failure_reason(uint32_t errcode) +{ + if (!tpm_state.tpm_found) + return; + + spapr_vtpm_set_error(errcode); +} diff --git a/lib/libtpm/tcgbios.h b/lib/libtpm/tcgbios.h new file mode 100644 index 0000000..a9fbbb8 --- /dev/null +++ b/lib/libtpm/tcgbios.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * Copyright (c) 2015 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef TCGBIOS_H +#define TCGBIOS_H + +#include +#include + +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 +/* 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 +#include + +/************************************************/ +/* 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..b130743 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 + 2drop + 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..8254eba 100644 --- a/slof/fs/start-up.fs +++ b/slof/fs/start-up.fs @@ -56,6 +56,13 @@ ; : (boot?) ( -- ) + \ last step before we boot we give up physical presence on the TPM + s" /ibm,vtpm" find-node dup IF + s" leave-firmware" rot $call-static + ELSE + drop + THEN + of-prompt? not auto-boot? and IF (boot) THEN