tpm: Add TPM CRQ driver implementation
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 <stefanb@linux.ibm.com> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
This commit is contained in:
parent
4f18bac8b0
commit
a2ffcd9d65
|
@ -15,7 +15,7 @@ BOARD_TARGETS = tools_build romfs_build stage1 subdirs
|
||||||
SUBDIRS = slof
|
SUBDIRS = slof
|
||||||
|
|
||||||
COMMON_LIBS = libc libbootmsg libbases libnvram libelf libhvcall libvirtio \
|
COMMON_LIBS = libc libbootmsg libbases libnvram libelf libhvcall libvirtio \
|
||||||
libusb libveth libe1k libnet libbootmenu
|
libusb libveth libe1k libnet libbootmenu libtpm
|
||||||
|
|
||||||
all: $(BOARD_TARGETS)
|
all: $(BOARD_TARGETS)
|
||||||
$(MAKE) boot_rom.bin
|
$(MAKE) boot_rom.bin
|
||||||
|
|
|
@ -44,6 +44,7 @@ extern int SLOF_get_property(const char *node, const char *propname,
|
||||||
char **addr, int *len);
|
char **addr, int *len);
|
||||||
extern int SLOF_get_keystroke(void);
|
extern int SLOF_get_keystroke(void);
|
||||||
extern void SLOF_reset(void);
|
extern void SLOF_reset(void);
|
||||||
|
extern unsigned long SLOF_get_vtpm_unit(void);
|
||||||
|
|
||||||
#define offset_of(type, member) ((long) &((type *)0)->member)
|
#define offset_of(type, member) ((long) &((type *)0)->member)
|
||||||
#define container_of(ptr, type, member) ({ \
|
#define container_of(ptr, type, member) ({ \
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
# ****************************************************************************/
|
# ****************************************************************************/
|
||||||
|
|
||||||
SUBDIRS = libc libipmi libbootmsg libbases libnvram libelf libhvcall libvirtio \
|
SUBDIRS = libc libipmi libbootmsg libbases libnvram libelf libhvcall libvirtio \
|
||||||
libusb libveth libe1k libbcm libnet libbootmenu
|
libusb libveth libe1k libbcm libnet libbootmenu libtpm
|
||||||
|
|
||||||
all: subdirs
|
all: subdirs
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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.
|
|
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
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 */
|
|
@ -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 <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -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 <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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 */
|
|
@ -235,3 +235,9 @@ void SLOF_reset(void)
|
||||||
{
|
{
|
||||||
forth_eval("reset-all");
|
forth_eval("reset-all");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long SLOF_get_vtpm_unit(void)
|
||||||
|
{
|
||||||
|
forth_eval("vtpm-unit");
|
||||||
|
return forth_pop();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue