[SLOF] [PATCH v2 05/20] Extend internal firmware API
Stefan Berger
stefanb at us.ibm.com
Wed Nov 18 04:02:21 AEDT 2015
From: Stefan Berger <stefanb at linux.vnet.ibm.com>
Extend the internal API of the TPM firmware support with additional
functions for hashing data, extending the TPM's platform configuration
registers with a hash, and appending to the log that is recording
what was hashed.
Signed-off-by: Stefan Berger <stefanb at linux.vnet.ibm.com>
---
lib/libtpm/tcgbios.c | 285 +++++++++++++++++++++++++++++++++++++++++++++++
lib/libtpm/tcgbios_int.h | 2 +
2 files changed, 287 insertions(+)
diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c
index 7e2ae1e..e22c550 100644
--- a/lib/libtpm/tcgbios.c
+++ b/lib/libtpm/tcgbios.c
@@ -25,6 +25,9 @@
#include "tcgbios.h"
#include "tcgbios_int.h"
#include "stdio.h"
+#include "sha1.h"
+#include "stddef.h"
+#include "helpers.h"
#undef TCGBIOS_DEBUG
//#define TCGBIOS_DEBUG
@@ -82,6 +85,8 @@ extern struct tpm_driver tpm_drivers[];
static void *log_base;
static uint32_t log_area_size;
+/* next log entry goes here */
+static void *log_area_address_next;
/* prototypes */
static uint32_t build_and_send_cmd(uint32_t ordinal, const uint8_t *append,
@@ -89,6 +94,8 @@ static uint32_t build_and_send_cmd(uint32_t ordinal, const uint8_t *append,
uint32_t return_size, uint32_t *return_code,
enum tpm_duration_type to_t);
+static bool has_working_tpm(void);
+
/********************************************************
Extensions for TCG-enabled BIOS
*******************************************************/
@@ -98,6 +105,7 @@ void tpm_set_log_parameters(void *addr, unsigned int size)
dprintf("Log is at 0x%llx; size is %u bytes\n",
(uint64_t)addr, size);
log_base = addr;
+ log_area_address_next = addr;
log_area_size = size;
}
@@ -120,6 +128,66 @@ static void reset_ofdt_log(void)
memset(log_area_start_address, 0, get_log_area_size());
}
+static void set_log_area_address_next(void *next)
+{
+ log_area_address_next = next;
+}
+
+static void *get_log_area_address_next(void)
+{
+ return log_area_address_next;
+}
+
+/*
+ * Extend the ACPI 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
+ *
+ * Output:
+ * lower 16 bits of return code contain entry number
+ * if entry number is '0', then upper 16 bits contain error code.
+ */
+static uint32_t tpm_extend_ofdt_log(struct pcpes *pcpes,
+ const char *event, uint32_t event_length)
+{
+ uint32_t size;
+ uint32_t log_area_size = get_log_area_size();
+ uint8_t *log_area_start_address = get_log_base_ptr();
+ uint8_t *log_area_address_next = get_log_area_address_next();
+
+ if (!has_working_tpm()) {
+ dprintf("Not appending to log due to TPM error state\n");
+ return TCGBIOS_FATAL_COM_ERROR;
+ }
+
+ dprintf("LASA_BASE = %p, LASA_NEXT = %p\n",
+ log_area_start_address, log_area_address_next);
+
+ if (!log_area_address_next || log_area_size == 0)
+ return TCGBIOS_LOGOVERFLOW;
+
+ size = offset_of(struct pcpes, event) + event_length;
+
+ if ((log_area_address_next + size - log_area_start_address) >
+ log_area_size) {
+ dprintf("LOG OVERFLOW: size = %d\n", size);
+ return TCGBIOS_LOGOVERFLOW;
+ }
+
+ pcpes->eventdatasize = event_length;
+
+ memcpy(log_area_address_next, pcpes, offset_of(struct pcpes, event));
+ memcpy(log_area_address_next + offset_of(struct pcpes, event),
+ event, event_length);
+
+ set_log_area_address_next(log_area_address_next + size);
+
+ return 0;
+}
+
static bool is_tpm_present(void)
{
bool rc = false;
@@ -434,3 +502,220 @@ err_exit:
return rc;
return TCGBIOS_COMMAND_ERROR;
}
+
+static uint32_t tpm_sha1_calc(const uint8_t *data, uint32_t length,
+ uint8_t *hash)
+{
+ uint32_t rc;
+ uint32_t return_code;
+ struct tpm_res_sha1start start;
+ struct tpm_res_sha1complete complete;
+ uint32_t blocks = length / TPM_SHA1_BLK_SIZE;
+ uint32_t rest = length & (TPM_SHA1_BLK_SIZE - 1);
+ uint32_t numbytes, numbytes_nw_order;
+ uint32_t offset = 0;
+
+ rc = build_and_send_cmd(TPM_ORD_SHA1_START,
+ NULL, 0,
+ (uint8_t *)&start, sizeof(start),
+ &return_code, TPM_DURATION_TYPE_SHORT);
+
+ if (rc || return_code)
+ goto err_exit;
+
+ while (blocks > 0) {
+
+ numbytes = be32_to_cpu(start.max_num_bytes);
+ if (numbytes > blocks * 64)
+ numbytes = blocks * 64;
+
+ numbytes_nw_order = cpu_to_be32(numbytes);
+
+ rc = build_and_send_cmd_od(TPM_ORD_SHA1_UPDATE,
+ (uint8_t *)&numbytes_nw_order,
+ sizeof(numbytes_nw_order),
+ NULL, 0, &return_code,
+ &data[offset], numbytes,
+ TPM_DURATION_TYPE_SHORT);
+
+ if (rc || return_code)
+ goto err_exit;
+
+ offset += numbytes;
+ blocks -= (numbytes / 64);
+ }
+
+ numbytes_nw_order = cpu_to_be32(rest);
+
+ rc = build_and_send_cmd_od(TPM_ORD_SHA1_COMPLETE,
+ (uint8_t *)&numbytes_nw_order,
+ sizeof(numbytes_nw_order),
+ (uint8_t *)&complete, sizeof(complete),
+ &return_code,
+ &data[offset], rest,
+ TPM_DURATION_TYPE_SHORT);
+
+ if (rc || return_code)
+ goto err_exit;
+
+ memcpy(hash, complete.hash, sizeof(complete.hash));
+
+ return 0;
+
+err_exit:
+ dprintf("TPM SHA1 malfunctioning.\n");
+
+ tpm_set_failure();
+ if (rc)
+ return rc;
+ return TCGBIOS_COMMAND_ERROR;
+}
+
+static uint32_t sha1_calc(const uint8_t *data, uint32_t length, uint8_t *hash)
+{
+ if (tpm_state.tpm_drv &&
+ length < tpm_state.tpm_drv->sha1threshold)
+ return tpm_sha1_calc(data, length, hash);
+
+ return sha1(data, length, hash);
+}
+
+static uint32_t is_preboot_if_shutdown(void)
+{
+ return tpm_state.if_shutdown;
+}
+
+static uint32_t shutdown_preboot_interface(void)
+{
+ uint32_t rc = 0;
+
+ if (!is_preboot_if_shutdown()) {
+ tpm_state.if_shutdown = 1;
+ } else {
+ rc = TCGBIOS_INTERFACE_SHUTDOWN;
+ }
+
+ return rc;
+}
+
+static void tpm_shutdown(void)
+{
+ reset_ofdt_log();
+ shutdown_preboot_interface();
+}
+
+/*
+ * Pass a TPM command through to the TPM
+ *
+ * @req: request buffer to send
+ * @reqlen: request buffer length
+ * @to_t: timeout type
+ * @rsp: response buffer
+ * @rsplen: size of response buffer in input
+ * on output the number of bytes used in the buffer
+ *
+ * Returns an error code or 0 for successful sending of command
+ * and reception of response.
+ */
+static bool pass_through_to_tpm(unsigned char *req,
+ uint32_t reqlen,
+ enum tpm_duration_type to_t,
+ unsigned char *rsp,
+ uint32_t *rsplen)
+{
+ uint8_t locty = 0;
+ struct iovec iovec[2] = {{ 0 }};
+
+ if (!has_working_tpm())
+ return TCGBIOS_FATAL_COM_ERROR;
+
+ iovec[0].data = req;
+ iovec[0].length = reqlen;
+
+ return transmit(locty, iovec, rsp, rsplen, to_t);
+}
+
+/*
+ * Extend a PCR of the TPM with the given hash
+ *
+ * @hash: sha1 hash (20 bytes) to extend PCR with
+ * @pcrindex: the PCR to extend [ 0..23 ]
+ */
+static uint32_t tpm_extend(uint8_t *hash, uint32_t pcrindex)
+{
+ struct tpm_req_extend req = {
+ .tag = cpu_to_be16(TPM_TAG_RQU_CMD),
+ .totlen = cpu_to_be32(sizeof(req)),
+ .ordinal = cpu_to_be32(TPM_ORD_EXTEND),
+ .pcrindex = cpu_to_be32(pcrindex),
+ };
+ struct tpm_rsp_extend rsp;
+ uint32_t rsplen = sizeof(rsp);
+ uint32_t rc;
+
+ memcpy(req.digest, hash, sizeof(req.digest));
+
+ rc = pass_through_to_tpm((unsigned char *)&req, sizeof(req),
+ TPM_DURATION_TYPE_SHORT,
+ (unsigned char *)&rsp, &rsplen);
+ if (!rc) {
+ if (rsplen != sizeof(rsp)) {
+ dprintf("TPM_Extend response has unexpected size: %u\n",
+ rsplen);
+ rc = TCGBIOS_FATAL_COM_ERROR;
+ }
+ }
+
+ if (rc)
+ tpm_shutdown();
+
+ return rc;
+}
+
+/*
+ * Hash the given input data and append the hash to the log
+ *
+ * @hashdata: the data to hash
+ * @hashdatalen: the size of the data to hash
+ * @pcpes: the 'pcpes' to append to the log; the hash will be written into this
+ * structure
+ * @event: the event to append to the pcpes
+ * @event_length: the lenth of the event array
+ */
+static uint32_t hash_log_event(const void *hashdata,
+ uint32_t hashdatalen,
+ struct pcpes *pcpes,
+ const char *event, uint32_t event_length)
+{
+ uint32_t rc;
+
+ if (is_preboot_if_shutdown())
+ return TCGBIOS_INTERFACE_SHUTDOWN;
+
+ /* TPM has PCRs 0 to 23 */
+ if (pcpes->pcrindex >= 24)
+ return TCGBIOS_INVALID_INPUT_PARA;
+
+ if (hashdata) {
+ rc = sha1_calc(hashdata, hashdatalen, pcpes->digest);
+ if (rc)
+ return rc;
+ }
+
+ return tpm_extend_ofdt_log(pcpes, event, event_length);
+}
+
+static uint32_t hash_log_extend_event(const void *hashdata,
+ uint32_t hashdatalen,
+ struct pcpes *pcpes,
+ const char *event, uint32_t event_length,
+ uint32_t pcrindex)
+{
+ uint32_t rc = 0;
+
+ rc = hash_log_event(hashdata, hashdatalen, pcpes, event, event_length);
+ if (rc)
+ return rc;
+
+ return tpm_extend(pcpes->digest, pcrindex);
+}
diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h
index a6a0d8f..07fe5bf 100644
--- a/lib/libtpm/tcgbios_int.h
+++ b/lib/libtpm/tcgbios_int.h
@@ -47,6 +47,7 @@
#define TPM_ST_DEACTIVATED 0x3
#define TPM_TAG_RQU_CMD 0x00c1
+#define TPM_TAG_RSP_CMD 0x00c4
/* TPM command error codes */
#define TPM_INVALID_POSTINIT 0x26
@@ -63,6 +64,7 @@
#define STATUS_FLAG_SHUTDOWN (1 << 0)
#define SHA1_BUFSIZE 20
+#define TPM_SHA1_BLK_SIZE 64
struct iovec {
size_t length;
--
2.4.3
More information about the SLOF
mailing list