On Sat, Oct 31, 2020 at 12:51:22PM -0400, Daniel P. Smith wrote: > Access to PCR extend functionality is needed early in the compressed > kernel so that Secure Launch can measure items into the DRTM PCRs > before these items are used. The items include the boot parameters and > associated information, the kernel command line, any external initrd > and the OS-MLE TXT heap structure. > > NOTE: for the RFC, early_pcr_extend.c is built unconditionally in the > Makefile. In the full Secure Launch patch set it is conditionally built > if CONFIG_SECURE_LAUNCH is defined. > > Signed-off-by: Daniel P. Smith <dpsmith@xxxxxxxxxxxxxxxxxxxx> > Signed-off-by: Ross Philipson <ross.philipson@xxxxxxxxxx> We don't want two copies of TIS implementation and TPM helper functions. This is a problem that the patch set must resolve in order to be acceptable for the mainline. /Jarkko > --- > arch/x86/boot/compressed/Makefile | 2 + > arch/x86/boot/compressed/early_pcr_extend.c | 311 ++++++++++++++++++++++++++++ > arch/x86/boot/compressed/early_pcr_extend.h | 92 ++++++++ > 3 files changed, 405 insertions(+) > create mode 100644 arch/x86/boot/compressed/early_pcr_extend.c > create mode 100644 arch/x86/boot/compressed/early_pcr_extend.h > > diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile > index 5a828fde7a42..8f0b29dce9da 100644 > --- a/arch/x86/boot/compressed/Makefile > +++ b/arch/x86/boot/compressed/Makefile > @@ -93,6 +93,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o > vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o > efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a > > +vmlinux-objs-y += $(obj)/early_pcr_extend.o > + > # The compressed kernel is built with -fPIC/-fPIE so that a boot loader > # can place it anywhere in memory and it will still run. However, since > # it is executed as-is without any ELF relocation processing performed > diff --git a/arch/x86/boot/compressed/early_pcr_extend.c b/arch/x86/boot/compressed/early_pcr_extend.c > new file mode 100644 > index 000000000000..94ee3cc9814e > --- /dev/null > +++ b/arch/x86/boot/compressed/early_pcr_extend.c > @@ -0,0 +1,311 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2020 Apertus Solutions, LLC > + * > + * Author(s): > + * Daniel P. Smith <dpsmith@xxxxxxxxxxxxxxxxxxxx> > + * > + * The code in this file is based on the article "Writing a TPM Device Driver" > + * published on http://ptgmedia.pearsoncmg.com. > + * > + * The scope of the TPM functionality here is solely to allow DRTM PCRs to be > + * extended early in the compressed kernel. > + */ > + > +#include <linux/types.h> > +#include <linux/bits.h> > +#include <linux/errno.h> > +#include <linux/string.h> > +#include <asm/byteorder.h> > +#include <asm/io.h> > + > +#define COMPRESSED_KERNEL > +#include <crypto/sha.h> > +#include <linux/tpm_buffer.h> > +#include <linux/tpm_command.h> > +#include <linux/tpm_core.h> > +#include "../../../../drivers/char/tpm/tpm_tis_defs.h" > +#include "early_pcr_extend.h" > + > +#define tpm_read8(f) readb((void *)(u64)(TPM_MMIO_BASE | f)) > +#define tpm_write8(v, f) writeb(v, (void *)(u64)(TPM_MMIO_BASE | f)) > +#define tpm_read32(f) readl((void *)(u64)(TPM_MMIO_BASE | f)); > + > +static struct tpm tpm; > +static u8 locality = TPM_NO_LOCALITY; > + > +static void tpm_io_delay(void) > +{ > + /* This is the default delay type in native_io_delay */ > + asm volatile ("outb %al, $0x80"); > +} > + > +static void tpm_udelay(int loops) > +{ > + while (loops--) > + tpm_io_delay(); /* Approximately 1 us */ > +} > + > +static u32 burst_wait(void) > +{ > + u32 count = 0; > + > + while (count == 0) { > + count = tpm_read8(TPM_STS(locality) + 1); > + count += tpm_read8(TPM_STS(locality) + 2) << 8; > + > + /* Wait for FIFO to drain */ > + if (count == 0) > + tpm_udelay(TPM_BURST_MIN_DELAY); > + } > + > + return count; > +} > + > +static void tis_relinquish_locality(void) > +{ > + if (locality < TPM_MAX_LOCALITY) > + tpm_write8(TPM_ACCESS_ACTIVE_LOCALITY, TPM_ACCESS(locality)); > + > + locality = TPM_NO_LOCALITY; > +} > + > +static u8 tis_request_locality(u8 l) > +{ > + if (l > TPM_MAX_LOCALITY) > + return TPM_NO_LOCALITY; > + > + if (l == locality) > + return locality; > + > + tis_relinquish_locality(); > + > + tpm_write8(TPM_ACCESS_REQUEST_USE, TPM_ACCESS(l)); > + > + /* wait for locality to be granted */ > + if (tpm_read8(TPM_ACCESS(l)) & TPM_ACCESS_ACTIVE_LOCALITY) > + locality = l; > + > + return locality; > +} > + > +static size_t tis_send(struct tpm_buf *buf) > +{ > + u8 status, *buf_ptr; > + u32 length, count = 0, burstcnt = 0; > + > + if (locality > TPM_MAX_LOCALITY) > + return 0; > + > + for (status = 0; (status & TPM_STS_COMMAND_READY) == 0; ) { > + tpm_write8(TPM_STS_COMMAND_READY, TPM_STS(locality)); > + status = tpm_read8(TPM_STS(locality)); > + } > + > + buf_ptr = buf->data; > + length = tpm_buf_length(buf); > + > + /* send all but the last byte */ > + while (count < (length - 1)) { > + burstcnt = burst_wait(); > + for (; burstcnt > 0 && count < (length - 1); burstcnt--) { > + tpm_write8(buf_ptr[count], TPM_DATA_FIFO(locality)); > + count++; > + } > + > + /* check for overflow */ > + for (status = 0; (status & TPM_STS_VALID) == 0; ) > + status = tpm_read8(TPM_STS(locality)); > + > + if ((status & TPM_STS_DATA_EXPECT) == 0) > + return 0; > + } > + > + /* write last byte */ > + tpm_write8(buf_ptr[length - 1], TPM_DATA_FIFO(locality)); > + count++; > + > + /* make sure it stuck */ > + for (status = 0; (status & TPM_STS_VALID) == 0; ) > + status = tpm_read8(TPM_STS(locality)); > + > + if ((status & TPM_STS_DATA_EXPECT) != 0) > + return 0; > + > + /* go and do it */ > + tpm_write8(TPM_STS_GO, TPM_STS(locality)); > + > + return (size_t)count; > +} > + > +static u8 tis_init(struct tpm *t) > +{ > + locality = TPM_NO_LOCALITY; > + > + if (tis_request_locality(0) != 0) > + return 0; > + > + t->vendor = tpm_read32(TPM_DID_VID(0)); > + if ((t->vendor & 0xFFFF) == 0xFFFF) > + return 0; > + > + return 1; > +} > + > +static u16 tpm_alg_size(u16 alg_id) > +{ > + if (alg_id == TPM_ALG_SHA1) > + return SHA1_DIGEST_SIZE; > + else if (alg_id == TPM_ALG_SHA256) > + return SHA256_DIGEST_SIZE; > + else if (alg_id == TPM_ALG_SHA512) > + return SHA512_DIGEST_SIZE; > + > + return 0; > +} > + > +static int tpm1_pcr_extend(struct tpm *t, u32 pcr, struct tpm_digest *d) > +{ > + struct tpm_buf buf; > + int ret; > + > + ret = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND); > + if (ret) > + return ret; > + > + tpm_buf_append_u32(&buf, pcr); > + tpm_buf_append(&buf, d->digest, tpm_alg_size(TPM_ALG_SHA1)); > + > + if (tpm_buf_length(&buf) != tis_send(&buf)) > + ret = -EAGAIN; > + > + return ret; > +} > + > +static int tpm2_extend_pcr(struct tpm *t, u32 pcr, struct tpm_digest *digest) > +{ > + struct tpm_buf buf; > + u8 auth_area[NULL_AUTH_SIZE] = {0}; > + u32 *handle; > + int ret; > + > + ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND); > + if (ret) > + return ret; > + > + tpm_buf_append_u32(&buf, pcr); > + > + /* > + * The handle, the first element, is the > + * only non-zero value in a NULL auth > + */ > + handle = (u32 *)&auth_area; > + *handle = cpu_to_be32(TPM2_RS_PW); > + > + tpm_buf_append_u32(&buf, NULL_AUTH_SIZE); > + tpm_buf_append(&buf, (const unsigned char *)&auth_area, > + NULL_AUTH_SIZE); > + > + tpm_buf_append_u32(&buf, 1); > + > + tpm_buf_append_u16(&buf, digest->alg_id); > + tpm_buf_append(&buf, (const unsigned char *)digest->digest, > + tpm_alg_size(digest->alg_id)); > + > + if (tpm_buf_length(&buf) != tis_send(&buf)) > + ret = -EAGAIN; > + > + return ret; > +} > + > +static void find_interface_and_family(struct tpm *t) > +{ > + struct tpm_interface_id intf_id; > + struct tpm_intf_capability intf_cap; > + > + /* Sort out whether if it is 1.2 */ > + intf_cap.val = tpm_read32(TPM_INTF_CAPABILITY_0); > + if ((intf_cap.interface_version == TPM12_TIS_INTF_12) || > + (intf_cap.interface_version == TPM12_TIS_INTF_13)) { > + t->family = TPM12; > + t->intf = TPM_TIS; > + return; > + } > + > + /* Assume that it is 2.0 and TIS */ > + t->family = TPM20; > + t->intf = TPM_TIS; > + > + /* Check if the interface is CRB */ > + intf_id.val = tpm_read32(TPM_INTERFACE_ID_0); > + if (intf_id.interface_type == TPM_CRB_INTF_ACTIVE) > + t->intf = TPM_CRB; > +} > + > +struct tpm *enable_tpm(void) > +{ > + struct tpm *t = &tpm; > + > + find_interface_and_family(t); > + > + switch (t->intf) { > + case TPM_TIS: > + if (!tis_init(t)) > + return NULL; > + break; > + case TPM_CRB: > + return NULL; > + } > + > + return t; > +} > + > +u8 tpm_request_locality(u8 l) > +{ > + return tis_request_locality(l); > +} > + > +int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, > + u8 *digest) > +{ > + int ret = -EINVAL; > + > + if (t->family == TPM12) { > + struct tpm_digest d; > + > + if (algo != TPM_ALG_SHA1) > + return -EINVAL; > + > + memcpy((void *)d.digest, digest, SHA1_DIGEST_SIZE); > + > + ret = tpm1_pcr_extend(t, pcr, &d); > + } else if (t->family == TPM20) { > + struct tpm_digest *d; > + u8 buf[MAX_TPM_EXTEND_SIZE]; > + > + d = (struct tpm_digest *) buf; > + d->alg_id = algo; > + switch (algo) { > + case TPM_ALG_SHA1: > + memcpy(d->digest, digest, SHA1_DIGEST_SIZE); > + break; > + case TPM_ALG_SHA256: > + memcpy(d->digest, digest, SHA256_DIGEST_SIZE); > + break; > + case TPM_ALG_SHA512: > + memcpy(d->digest, digest, SHA512_DIGEST_SIZE); > + break; > + default: > + return -EINVAL; > + } > + > + ret = tpm2_extend_pcr(t, pcr, d); > + } > + > + return ret; > +} > + > +void free_tpm(void) > +{ > + tis_relinquish_locality(); > +} > diff --git a/arch/x86/boot/compressed/early_pcr_extend.h b/arch/x86/boot/compressed/early_pcr_extend.h > new file mode 100644 > index 000000000000..bcd6d57d8c56 > --- /dev/null > +++ b/arch/x86/boot/compressed/early_pcr_extend.h > @@ -0,0 +1,92 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2020 Apertus Solutions, LLC > + * > + * Author(s): > + * Daniel P. Smith <dpsmith@xxxxxxxxxxxxxxxxxxxx> > + * > + */ > + > +#ifndef BOOT_COMPRESSED_EARLY_PCR_EXTEND_H > +#define BOOT_COMPRESSED_EARLY_PCR_EXTEND_H > + > +#define TPM_MMIO_BASE 0xFED40000 > +#define TPM_MAX_LOCALITY 4 > +#define TPM_NO_LOCALITY 0xFF > +#define TPM_BURST_MIN_DELAY 100 /* 100us */ > +#define TPM_ORD_PCR_EXTEND 20 > +#define NULL_AUTH_SIZE 9 > +#define MAX_TPM_EXTEND_SIZE 68 /* TPM2 SHA512 is the largest */ > + > +#define TPM_INTERFACE_ID_0 0x30 > +#define TPM_TIS_INTF_ACTIVE 0x00 > +#define TPM_CRB_INTF_ACTIVE 0x01 > + > +struct tpm_interface_id { > + union { > + u32 val; > + struct { > + u32 interface_type:4; > + u32 interface_version:4; > + u32 cap_locality:1; > + u32 reserved1:4; > + u32 cap_tis:1; > + u32 cap_crb:1; > + u32 cap_if_res:2; > + u32 interface_selector:2; > + u32 intf_sel_lock:1; > + u32 reserved2:4; > + u32 reserved3:8; > + }; > + }; > +} __packed; > + > +#define TPM_INTF_CAPABILITY_0 0x14 > +#define TPM12_TIS_INTF_12 0x00 > +#define TPM12_TIS_INTF_13 0x02 > +#define TPM20_TIS_INTF_13 0x03 > + > +struct tpm_intf_capability { > + union { > + u32 val; > + struct { > + u32 data_avail_int_support:1; > + u32 sts_valid_int_support:1; > + u32 locality_change_int_support:1; > + u32 interrupt_level_high:1; > + u32 interrupt_level_low:1; > + u32 interrupt_edge_rising:1; > + u32 interrupt_edge_falling:1; > + u32 command_ready_int_support:1; > + u32 burst_count_static:1; > + u32 data_transfer_size_support:2; > + u32 reserved1:17; > + u32 interface_version:3; > + u32 reserved2:1; > + }; > + }; > +} __packed; > + > +enum tpm_hw_intf { > + TPM_TIS, > + TPM_CRB > +}; > + > +enum tpm_family { > + TPM12, > + TPM20 > +}; > + > +struct tpm { > + u32 vendor; > + enum tpm_family family; > + enum tpm_hw_intf intf; > +}; > + > +extern struct tpm *enable_tpm(void); > +extern u8 tpm_request_locality(u8 l); > +extern int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo, > + u8 *digest); > +extern void free_tpm(void); > + > +#endif > -- > 2.11.0 >