From patchwork Tue Jan 21 20:01:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 1226744 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [203.11.71.2]) (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 482KFc3YlPz9sRG for ; Wed, 22 Jan 2020 07:02:28 +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 482KFc34KjzDqSR for ; Wed, 22 Jan 2020 07:02:28 +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.156.1; 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 (mx0a-001b2d01.pphosted.com [148.163.156.1]) (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 482KF203JNzDqPf for ; Wed, 22 Jan 2020 07:01:57 +1100 (AEDT) Received: from pps.filterd (m0098399.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id 00LJqm2j111267; Tue, 21 Jan 2020 15:01:54 -0500 Received: from ppma02dal.us.ibm.com (a.bd.3ea9.ip4.static.sl-reverse.com [169.62.189.10]) by mx0a-001b2d01.pphosted.com with ESMTP id 2xp3u69nf2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jan 2020 15:01:53 -0500 Received: from pps.filterd (ppma02dal.us.ibm.com [127.0.0.1]) by ppma02dal.us.ibm.com (8.16.0.27/8.16.0.27) with SMTP id 00LJqtE7015469; Tue, 21 Jan 2020 20:01:53 GMT Received: from b03cxnp08026.gho.boulder.ibm.com (b03cxnp08026.gho.boulder.ibm.com [9.17.130.18]) by ppma02dal.us.ibm.com with ESMTP id 2xksn6x5sd-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 21 Jan 2020 20:01:52 +0000 Received: from b03ledav003.gho.boulder.ibm.com (b03ledav003.gho.boulder.ibm.com [9.17.130.234]) by b03cxnp08026.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 00LK1pBK46072236 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 21 Jan 2020 20:01:51 GMT Received: from b03ledav003.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 826AD6A051; Tue, 21 Jan 2020 20:01:51 +0000 (GMT) Received: from b03ledav003.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 080AC6A047; Tue, 21 Jan 2020 20:01:50 +0000 (GMT) Received: from sbct-3.pok.ibm.com (unknown [9.47.158.153]) by b03ledav003.gho.boulder.ibm.com (Postfix) with ESMTP; Tue, 21 Jan 2020 20:01:50 +0000 (GMT) From: Stefan Berger To: slof@lists.ozlabs.org, aik@ozlabs.ru Date: Tue, 21 Jan 2020 15:01:43 -0500 Message-Id: <20200121200147.1002075-5-stefanb@linux.ibm.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200121200147.1002075-1-stefanb@linux.ibm.com> References: <20200121200147.1002075-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.634 definitions=2020-01-21_06:2020-01-21, 2020-01-21 signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 bulkscore=0 adultscore=0 mlxlogscore=999 lowpriorityscore=0 mlxscore=0 spamscore=0 malwarescore=0 priorityscore=1501 impostorscore=0 suspectscore=2 clxscore=1015 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-1910280000 definitions=main-2001210148 Subject: [SLOF] [PATCH v7 4/8] tpm: Add TPM CRQ driver implementation 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 a TPM driver for the CRQ interface as used by the QEMU PAPR implementation. Also add a Readme that explains the benefits and installation procedure for the vTPM. Signed-off-by: Stefan Berger --- board-qemu/Makefile | 2 +- include/helpers.h | 1 + lib/Makefile | 2 +- lib/libtpm/Makefile | 50 +++++ lib/libtpm/Readme | 57 +++++ lib/libtpm/tcgbios_int.h | 30 +++ lib/libtpm/tpm_drivers.c | 437 +++++++++++++++++++++++++++++++++++++++ lib/libtpm/tpm_drivers.h | 82 ++++++++ slof/helpers.c | 6 + 9 files changed, 665 insertions(+), 2 deletions(-) create mode 100644 lib/libtpm/Makefile create mode 100644 lib/libtpm/Readme create mode 100644 lib/libtpm/tcgbios_int.h create mode 100644 lib/libtpm/tpm_drivers.c create mode 100644 lib/libtpm/tpm_drivers.h diff --git a/board-qemu/Makefile b/board-qemu/Makefile index 61a1367..f419202 100644 --- a/board-qemu/Makefile +++ b/board-qemu/Makefile @@ -15,7 +15,7 @@ BOARD_TARGETS = tools_build romfs_build stage1 subdirs SUBDIRS = slof COMMON_LIBS = libc libbootmsg libbases libnvram libelf libhvcall libvirtio \ - libusb libveth libe1k libnet libbootmenu + libusb libveth libe1k libnet libbootmenu libtpm all: $(BOARD_TARGETS) $(MAKE) boot_rom.bin diff --git a/include/helpers.h b/include/helpers.h index 9a06a5c..2f460d6 100644 --- a/include/helpers.h +++ b/include/helpers.h @@ -44,6 +44,7 @@ extern int SLOF_get_property(const char *node, const char *propname, char **addr, int *len); extern int SLOF_get_keystroke(void); extern void SLOF_reset(void); +extern unsigned long SLOF_get_vtpm_unit(void); #define offset_of(type, member) ((long) &((type *)0)->member) #define container_of(ptr, type, member) ({ \ diff --git a/lib/Makefile b/lib/Makefile index 1e8bb62..7369894 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -11,7 +11,7 @@ # ****************************************************************************/ SUBDIRS = libc libipmi libbootmsg libbases libnvram libelf libhvcall libvirtio \ - libusb libveth libe1k libbcm libnet libbootmenu + libusb libveth libe1k libbcm libnet libbootmenu libtpm all: subdirs diff --git a/lib/libtpm/Makefile b/lib/libtpm/Makefile new file mode 100644 index 0000000..47fc5d0 --- /dev/null +++ b/lib/libtpm/Makefile @@ -0,0 +1,50 @@ +# ***************************************************************************** +# * 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 +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR) +CPPFLAGS += -I../libhvcall + +LDFLAGS = -nostdlib + +TARGET = ../libtpm.a + + +all: $(TARGET) + +SRCS = tpm_drivers.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/lib/libtpm/Readme b/lib/libtpm/Readme new file mode 100644 index 0000000..2c362ac --- /dev/null +++ b/lib/libtpm/Readme @@ -0,0 +1,57 @@ +This directory hosts (v)TPM related code. + +Background: +----------- + +A TPM is a crypto chip that is found in many systems. Besides it offering +a secure key store, among other functionality, it is also used to implement +'trusted boot'. This is realized by code in the firmware measuring parts of the +firmware's code and data as well as system data, such as the boot block, and +logging these measurements and storing (extending) them in the TPM's platform +configuration register (PCR). + +The benefits of having a TPM (or vTPM) in a system are: + +- enablement of trusted boot; this allow us to eventually extend the chain of + trust from the hypervisor to the guests +- enablement of attestation so that one can verify what software is running on + a machine (OpenPTS, OpenAttestation) +- provides TPM functionality to VMs, which includes a standardized mechanism + to store keys and other blobs (Linux trusted keys, GNU TLS's TPM extensions) + + +QEMU/KVM + SLOF support: +------------------------ + +vTPM for QEMU/KVM pSeries virtual machines is support in QEMU 5.0. + +To start a QEMU VM with an attached vTPM (swtpm), run the below shown commands. +The following will setup the vTPM so that its state will be stored in +/tmp/myvtpm1. A unique directory for each VM instance with attached vTPM +must be provided. Whenever QEMU is started, the swtpm has to be started +before it. The file 'boot_rom.bin' is SLOF with vTPM extensions built-in. + + #> mkdir -p /tmp/mytpm1 + #> swtpm socket --tpm2 --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock + + In another terminal: + + #> sudo qemu-system-ppc64 -display sdl \ + -machine pseries,accel=kvm \ + -m 1024 -bios boot_rom.bin -boot menu=on \ + -nodefaults -device VGA -device pci-ohci -device usb-kbd \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-spapr,tpmdev=tpm0 \ + -device spapr-vscsi,id=scsi0,reg=0x00002000 \ + -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ + -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0 + +Notes: + - The Linux kernel in the VM must have the tpm_ibmvtpm module available + or built-in. A recent kernel is needed that enables TPM 2.0 support + in this module. + + - 'swtpm_ioctl --unix /tmp/mytpm1/swtpm-sock -s' can be used to gracefully + shut down the vTPM. diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h new file mode 100644 index 0000000..835bd36 --- /dev/null +++ b/lib/libtpm/tcgbios_int.h @@ -0,0 +1,30 @@ +/***************************************************************************** + * 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_INT_H +#define TCGBIOS_INT_H + +#include + +struct tpm_req_header { + uint16_t tag; + uint32_t totlen; + uint32_t ordinal; +} __attribute__((packed)); + +struct tpm_rsp_header { + uint16_t tag; + uint32_t totlen; + uint32_t errcode; +} __attribute__((packed)); + +#endif /* TCGBIOS_INT_H */ diff --git a/lib/libtpm/tpm_drivers.c b/lib/libtpm/tpm_drivers.c new file mode 100644 index 0000000..5005176 --- /dev/null +++ b/lib/libtpm/tpm_drivers.c @@ -0,0 +1,437 @@ +/***************************************************************************** + * 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 + *****************************************************************************/ + +#include +#include +#include + +#include "string.h" +#include "helpers.h" +#include "byteorder.h" +#include "tcgbios_int.h" +#include "tpm_drivers.h" +#include "libhvcall.h" +#include "paflof.h" + +#undef PAPR_VTPM_DEBUG +//#define PAPR_VTPM_DEBUG +#ifdef PAPR_VTPM_DEBUG +#define dprintf(_x ...) do { printf("VTPM CRQ: " _x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + +/* layout of the command request queue for vTPM; all fields are big endian */ +struct crq { + uint8_t valid; + uint8_t msg; + uint16_t len; + uint32_t data; + uint64_t reserved; +} __attribute__((packed)); + +#define PAPR_VTPM_INIT_CRQ_COMMAND 0xC0 +#define PAPR_VTPM_VALID_COMMAND 0x80 +#define PAPR_VTPM_MSG_RESULT 0x80 + +/* crq.msg request types when crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND */ +#define PAPR_VTPM_INIT_CRQ_RESULT 0x1 + +/* crq.msg request types when crq.valid = PAPR_VTPM_VALID_COMMAND */ +#define PAPR_VTPM_GET_VERSION 0x1 +#define PAPR_VTPM_TPM_COMMAND 0x2 +#define PAPR_VTPM_GET_RTCE_BUFFER_SIZE 0x3 + +#define TPM2_DEFAULT_DURATION_SHORT 750000 /* us */ +#define TPM2_DEFAULT_DURATION_MEDIUM 2000000 /* us */ +#define TPM2_DEFAULT_DURATION_LONG 2000000 /* us */ + +static const uint32_t tpm2_durations[3] = { + TPM2_DEFAULT_DURATION_SHORT, + TPM2_DEFAULT_DURATION_MEDIUM, + TPM2_DEFAULT_DURATION_LONG, +}; + +#define QUEUE_SIZE 4096 + +/* state of the PAPR CRQ VTPM driver */ +static struct { + /* whether it driver been initialized */ + bool initialized; + + /* unit number */ + unsigned long unit; + + /* CRQ queue address and size */ + unsigned char *qaddr; + unsigned long qsize; + + /* current q_entry */ + unsigned int curr_q_entry; + + /* current response CRQ */ + struct crq *response; + + /* power firmware defined state and error code */ + vtpm_drv_state driver_state; + vtpm_drv_error driver_error; + + /* size of buffer supported by hypervisor */ + unsigned int buffer_size; + + /* buffer for commands and responses */ + char *buffer; +} spapr_vtpm = { + .qsize = QUEUE_SIZE, + .driver_state = VTPM_DRV_STATE_INVALID, + .driver_error = VTPM_DRV_ERROR_NO_FAILURE, +}; + +static void vtpm_drv_state_set(vtpm_drv_state s, vtpm_drv_error e) +{ + spapr_vtpm.driver_state = s; + spapr_vtpm.driver_error = e; +} + +static vtpm_drv_error vtpm_drv_error_get(void) +{ + return spapr_vtpm.driver_error; +} + +static struct crq *spapr_get_crq(void *qaddr, unsigned long q_entry) +{ + return &((struct crq *)qaddr)[q_entry]; +} + +/* + * Get the crq where the response will be found. This + * function will clear the CRQ's valid field and advance + * the entry counter to the next entry. + */ +static struct crq *spapr_get_response_crq(void) +{ + struct crq *crq; + + dprintf("curr_q_entry = %d\n", spapr_vtpm.curr_q_entry); + + crq = spapr_get_crq(spapr_vtpm.qaddr, spapr_vtpm.curr_q_entry); + memset(crq, 0, sizeof(*crq)); + + spapr_vtpm.curr_q_entry += 1; + if (spapr_vtpm.curr_q_entry == (spapr_vtpm.qsize / sizeof(struct crq))) + spapr_vtpm.curr_q_entry = 0; + + return crq; +} + +/* + * Send a message via CRQ and wait for the response + */ +static bool spapr_send_crq_and_wait(unsigned long unit, + struct crq *crq, + struct crq **response, + unsigned timeout, + vtpm_drv_state state1, + vtpm_drv_state state2) +{ + long rc; + unsigned i; + + *response = spapr_get_response_crq(); + + vtpm_drv_state_set(state1, VTPM_DRV_ERROR_NO_FAILURE); + + rc = hv_send_crq(unit, (uint64_t *)crq); + if (rc != H_SUCCESS) { + vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT, + VTPM_DRV_ERROR_TPM_CRQ_ERROR); + return false; + } + + vtpm_drv_state_set(state2, VTPM_DRV_ERROR_NO_FAILURE); + + for (i = 0; i < timeout; i += 1000) { + if (((*response)->valid & PAPR_VTPM_MSG_RESULT)) + return true; + SLOF_usleep(1000); + } + + vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE, + VTPM_DRV_ERROR_WAIT_TIMEOUT); + + dprintf("Received no response from CRQ\n"); + return false; +} + +/* + * Get parameters from the CRQ + */ +static bool spapr_vtpm_get_params(void) +{ + struct crq crq, *response; + static bool completed = false; /* only once */ + + if (completed) + return true; + + /* get the TPM's buffer size */ + crq.valid = PAPR_VTPM_VALID_COMMAND; + crq.msg = PAPR_VTPM_GET_RTCE_BUFFER_SIZE; + + if (!spapr_send_crq_and_wait(spapr_vtpm.unit, &crq, &response, 10, + VTPM_DRV_STATE_SEND_BUFSIZE_REQ, + VTPM_DRV_STATE_WAIT_BUFSIZE)) { + printf("%s: Failure getting RTCE buffer size from CRQ\n", + __func__); + return false; + } + + vtpm_drv_state_set(VTPM_DRV_STATE_ALLOC_RTCE_BUF, + VTPM_DRV_ERROR_NO_FAILURE); + + dprintf("RTCE buffer size: %u\n", be16_to_cpu(response->len)); + spapr_vtpm.buffer_size = be16_to_cpu(response->len); + if (spapr_vtpm.buffer_size < 1024) { + printf("%s: RTCE buffer size of %u bytes is too small. " + "Minimum is 1024 bytes.\n", __func__, + spapr_vtpm.buffer_size); + vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE, + VTPM_DRV_ERROR_BAD_RTCE_SIZE); + return false; + } + spapr_vtpm.buffer = SLOF_alloc_mem(spapr_vtpm.buffer_size); + if (!spapr_vtpm.buffer) { + printf("%s: Could not allocate buffer of size %u.\n", + __func__, spapr_vtpm.buffer_size); + vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE, + VTPM_DRV_ERROR_BAD_RTCE_SIZE); + return false; + } + + completed = true; + + return true; +} + +static bool spapr_vtpm_activate(void) +{ + long rc; + struct crq crq, *response; + + if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) { + printf("%s: CRQ: In failure mode\n", __func__); + return false; + } + + vtpm_drv_state_set(VTPM_DRV_STATE_REG_CRQ, + VTPM_DRV_ERROR_NO_FAILURE); + + rc = hv_reg_crq(spapr_vtpm.unit, (unsigned long)spapr_vtpm.qaddr, + spapr_vtpm.qsize); + if (rc != H_SUCCESS) { + vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT, + VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR); + printf("%s: CRQ registration failed\n", __func__); + return false; + } + + /* we always start with curr_q_entry 0 */ + spapr_vtpm.curr_q_entry = 0; + + if (!spapr_vtpm.initialized) { + + crq.valid = PAPR_VTPM_INIT_CRQ_COMMAND; + crq.msg = PAPR_VTPM_INIT_CRQ_RESULT; + + if (!spapr_send_crq_and_wait(spapr_vtpm.unit, + &crq, + &response, + 10, + VTPM_DRV_STATE_SEND_INIT, + VTPM_DRV_STATE_WAIT_INIT_COMP)) { + printf("%s: Initializing CRQ failed\n", __func__); + goto err_exit; + } + dprintf("Successfully initialized CRQ\n"); + + spapr_vtpm.initialized = true; + } + + if (spapr_vtpm_get_params()) + return true; + +err_exit: + hv_free_crq(spapr_vtpm.unit); + spapr_vtpm.unit = 0; + + return false; +} + +void spapr_vtpm_finalize(void) +{ + if (spapr_vtpm.unit) { + hv_free_crq(spapr_vtpm.unit); + spapr_vtpm.unit = 0; + } +} + +/* + * Check whether we have a CRQ underneath us; if we do, the CRQ will + * be left open. + */ +static bool spapr_vtpm_probe(void) +{ + if (!spapr_vtpm.qaddr) { + spapr_vtpm.qaddr = SLOF_alloc_mem(spapr_vtpm.qsize); + if (!spapr_vtpm.qaddr) { + printf("%s: Unable to allocate memory\n", __func__); + return false; + } + memset(spapr_vtpm.qaddr, 0, spapr_vtpm.qsize); + + dprintf("getting FORTH vtpm-unit\n"); + spapr_vtpm.unit = SLOF_get_vtpm_unit(); + if (!spapr_vtpm.unit) { + printf("%s: Could not get valid vtpm-unit\n", __func__); + return false; + } + } + + dprintf("vtpm_unit = %lx, buffer = %p\n", + spapr_vtpm.unit, spapr_vtpm.qaddr); + + if (!spapr_vtpm_activate()) + return false; + + return true; +} + +static bool spapr_vtpm_senddata(const uint8_t *const data, uint32_t len) +{ + struct crq crq; + long rc; + + if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) { + printf("%s: VTPM CRQ: In failure mode\n", __func__); + return false; + } + + if (len > spapr_vtpm.buffer_size) { + printf("%s: VTPM CRQ: Send buffer too large: %u > %u\n", + __func__, len, spapr_vtpm.buffer_size); + return false; + } + + spapr_vtpm.response = spapr_get_response_crq(); + spapr_vtpm.response->data = (uint64_t)spapr_vtpm.buffer; + + crq.valid = PAPR_VTPM_VALID_COMMAND; + crq.msg = PAPR_VTPM_TPM_COMMAND; + crq.len = cpu_to_be16(len); + crq.data = (uint64_t)spapr_vtpm.buffer; + memcpy(spapr_vtpm.buffer, data, MIN(len, spapr_vtpm.buffer_size)); + + vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD, + VTPM_DRV_ERROR_NO_FAILURE); + + rc = hv_send_crq(spapr_vtpm.unit, (uint64_t *)&crq); + + if (rc == H_SUCCESS) + vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_TPM_RSP, + VTPM_DRV_ERROR_NO_FAILURE); + else + vtpm_drv_state_set(VTPM_DRV_STATE_WAIT_INIT, + VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR); + + return (rc == H_SUCCESS); +} + +static bool spapr_vtpm_waitresponseready(enum tpm_duration_type to_t) +{ + uint32_t timeout = tpm2_durations[to_t]; + int i; + + if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) { + printf("%s: VTPM CRQ: In failure mode\n", __func__); + return false; + } + + for (i = 0; i < timeout; i += 1000) { + if (spapr_vtpm.response->valid & PAPR_VTPM_MSG_RESULT) { + /* TPM responded: move to Send tpm-cmd state */ + vtpm_drv_state_set(VTPM_DRV_STATE_SEND_TPM_CMD, + VTPM_DRV_ERROR_NO_FAILURE); + dprintf("Received response to TPM command\n"); + return true; + } + SLOF_usleep(1000); + } + + vtpm_drv_state_set(VTPM_DRV_STATE_FAILURE, + VTPM_DRV_ERROR_WAIT_TIMEOUT); + + dprintf("Received NO response to TPM command"); + + return false; +} + +static bool spapr_vtpm_readresponse(uint8_t *buffer, uint32_t *len) +{ + uint32_t length; + + if (vtpm_drv_error_get() != VTPM_DRV_ERROR_NO_FAILURE) { + printf("%s: VTPM CRQ: In failure mode\n", __func__); + return false; + } + + length = MIN(*len, be32_to_cpu(spapr_vtpm.response->len)); + + memcpy(buffer, (void *)(uint64_t)spapr_vtpm.response->data, length); + + dprintf("Length of copied response: %d\n", length); + + spapr_vtpm.response = NULL; + *len = length; + + return true; +} + +/**** higher layer interface ****/ + +vtpm_drv_error spapr_vtpm_get_error(void) +{ + return vtpm_drv_error_get(); +} + +void spapr_vtpm_set_error(vtpm_drv_error errcode) +{ + spapr_vtpm.driver_error = errcode; +} + +bool spapr_is_vtpm_present(void) +{ + return spapr_vtpm_probe(); +} + +int spapr_transmit(uint8_t locty, struct tpm_req_header *req, + void *respbuffer, uint32_t *respbufferlen, + enum tpm_duration_type to_t) +{ + if (!spapr_vtpm_senddata((uint8_t *)req, be32_to_cpu(req->totlen)) || + !spapr_vtpm_waitresponseready(to_t) || + !spapr_vtpm_readresponse(respbuffer, respbufferlen) || + *respbufferlen < sizeof(struct tpm_rsp_header)) + return -1; + return 0; +} diff --git a/lib/libtpm/tpm_drivers.h b/lib/libtpm/tpm_drivers.h new file mode 100644 index 0000000..b5d347f --- /dev/null +++ b/lib/libtpm/tpm_drivers.h @@ -0,0 +1,82 @@ +/***************************************************************************** + * 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 TPM_DRIVERS_H +#define TPM_DRIVERS_H + +#include +#include +#include + +#include "tcgbios_int.h" + +enum tpm_duration_type { + TPM_DURATION_TYPE_SHORT = 0, + TPM_DURATION_TYPE_MEDIUM, + TPM_DURATION_TYPE_LONG, +}; + +/* firmware driver states */ +typedef enum { + VTPM_DRV_STATE_INVALID = 0, + VTPM_DRV_STATE_INIT_CALLED = 1, + VTPM_DRV_STATE_REG_CRQ = 2, + VTPM_DRV_STATE_WAIT_INIT = 3, + VTPM_DRV_STATE_SEND_INIT = 4, + VTPM_DRV_STATE_FAILURE = 5, + VTPM_DRV_STATE_WAIT_INIT_COMP = 6, + VTPM_DRV_STATE_SEND_INIT_COMP = 7, + VTPM_DRV_STATE_SEND_GET_VERSION = 8, + VTPM_DRV_STATE_WAIT_VERSION = 9, + VTPM_DRV_STATE_CHECK_VERSION = 10, + VTPM_DRV_STATE_SEND_BUFSIZE_REQ = 11, + VTPM_DRV_STATE_WAIT_BUFSIZE = 12, + VTPM_DRV_STATE_ALLOC_RTCE_BUF = 13, + VTPM_DRV_STATE_SEND_TPM_CMD = 14, + VTPM_DRV_STATE_WAIT_TPM_RSP = 15, +} vtpm_drv_state; + +/* firmware driver errors */ +typedef enum { + VTPM_DRV_ERROR_NO_FAILURE = -1, + VTPM_DRV_ERROR_NOT_FOUND_TIMEOUT = 0, + VTPM_DRV_ERROR_UNEXPECTED_REG_ERROR = 1, + VTPM_DRV_ERROR_PARTNER_FAILED = 2, + VTPM_DRV_ERROR_UNEXPECTED_TSP_ERROR = 3, + VTPM_DRV_ERROR_TPM_PROTOCOL_ERROR = 4, + VTPM_DRV_ERROR_WAIT_TIMEOUT = 5, + VTPM_DRV_ERROR_UNEXPECTED_SEND_ERROR = 6, + VTPM_DRV_ERROR_CRQ_OPEN_FAIL = 7, + VTPM_DRV_ERROR_BAD_STATE = 8, + VTPM_DRV_ERROR_TPM_FAIL = 9, + VTPM_DRV_ERROR_TPM_CRQ_ERROR = 10, + VTPM_DRV_ERROR_BAD_VERSION = 11, + VTPM_DRV_ERROR_BAD_RTCE_SIZE = 12, + VTPM_DRV_ERROR_SML_FAILURE = 13, + VTPM_DRV_ERROR_SML_HANDED_OVER = 14, +} vtpm_drv_error; + +/* the max. buffer size by the external TPM is 4k */ +#define PAPR_VTPM_MAX_BUFFER_SIZE 4096 + +/* exported functions */ +bool spapr_is_vtpm_present(void); +void spapr_vtpm_finalize(void); +vtpm_drv_error spapr_vtpm_get_error(void); +void spapr_vtpm_set_error(vtpm_drv_error errcode); + +struct tpm_req_header; +int spapr_transmit(uint8_t locty, struct tpm_req_header *req, + void *respbuffer, uint32_t *respbufferlen, + enum tpm_duration_type to_t); + +#endif /* TPM_DRIVERS_H */ diff --git a/slof/helpers.c b/slof/helpers.c index 9d37bc3..64023b2 100644 --- a/slof/helpers.c +++ b/slof/helpers.c @@ -235,3 +235,9 @@ void SLOF_reset(void) { forth_eval("reset-all"); } + +unsigned long SLOF_get_vtpm_unit(void) +{ + forth_eval("vtpm-unit"); + return forth_pop(); +}