/* Copyright 2013-2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef pr_fmt #define pr_fmt(fmt) "STB: " fmt #endif #include #include #include #include "container.h" #include "tpm_chip.h" #include "drivers/tpm_i2c_nuvoton.h" #include "tss/trustedbootCmds.H" /* For debugging only */ //#define STB_DEBUG static struct list_head tpm_list = LIST_HEAD_INIT(tpm_list); #ifdef STB_DEBUG static void tpm_print_pcr(struct tpm_chip *tpm, TPM_Pcr pcr, TPM_Alg_Id alg, size_t size) { int rc; uint8_t digest[TPM_ALG_SHA256_SIZE]; memset(digest, 0, size); rc = tpmCmdPcrRead(tpm, pcr, alg, digest, size); if (rc) { /** * @fwts-label STBPCRReadFailed * @fwts-advice STB_DEBUG should not be enabled * in production. PCR read operation failed. * This TSS implementation is part of hostboot, * but the source code is shared with skiboot. * 1) The hostboot TSS may have been updated. * 2) This may be caused by the short I2C * timeout and can be fixed by increasing the * timeout. Otherwise this indicates a bug in * the TSS or the TPM device driver. Each one * has local debug macros that can help. */ prlog(PR_ERR, "tpmCmdPcrRead() failed: " "tpm%d, alg=%x, pcr%d, rc=%d\n", tpm->id, alg, pcr, rc); } else { prlog(PR_NOTICE,"print pcr-read: tpm%d alg=0x%x pcr%d\n", tpm->id, alg, pcr); stb_print_data(digest, size); } } #endif int tpm_register_chip(struct dt_node *node, struct tpm_dev *dev, struct tpm_driver *driver) { int i, rc; uint64_t sml_base; uint32_t sml_size; struct tpm_chip *tpm; i = 0; list_for_each(&tpm_list, tpm, link) { if (tpm->node == node) { /** * @fwts-label TPMAlreadyRegistered * @fwts-advice TPM node already registered. The same * node is being registered twice or there is a * tpm node duplicate in the device tree */ prlog(PR_WARNING, "tpm%d already registered\n", tpm->id); return -1; } i++; } tpm = (struct tpm_chip*) malloc(sizeof(struct tpm_chip)); assert(tpm); tpm->id = i; /* * Read event log info from the tpm device tree node. Both * linux,sml-base and linux,sml-size properties are documented in * 'doc/device-tree/tpm.rst' */ sml_base = dt_prop_get_u64_def(node, "linux,sml-base", 0); /* Check if sml-base is really 0 or it just doesn't exist */ if (!sml_base && !dt_find_property(node, "linux,sml-base")) { /** * @fwts-label TPMSmlBaseNotFound * @fwts-advice linux,sml-base property not found. This * indicates a Hostboot bug if the property really * doesn't exist in the tpm node. */ prlog(PR_ERR, "linux,sml-base property not found " "tpm node %p\n", node); goto disable; } sml_size = dt_prop_get_u32_def(node, "linux,sml-size", 0); if (!sml_size) { /** * @fwts-label TPMSmlSizeNotFound * @fwts-advice linux,sml-size property not found. This * indicates a Hostboot bug if the property really * doesn't exist in the tpm node. */ prlog(PR_ERR, "linux,sml-size property not found, " "tpm node %p\n", node); goto disable; } /* * Initialize the event log manager by walking through the log to identify * what is the next free position in the log */ rc = TpmLogMgr_initializeUsingExistingLog(&tpm->logmgr, (uint8_t*) sml_base, sml_size); if (rc) { /** * @fwts-label TPMInitEventLogFailed * @fwts-advice Hostboot creates and adds entries to the * event log. The failed init function is part of hostboot, * but the source code is shared with skiboot. If the hostboot * TpmLogMgr code (or friends) has been updated, the changes * need to be applied to skiboot as well. */ prlog(PR_ERR, "eventlog init failed: tpm%d rc=%d\n", tpm->id, rc); goto disable; } tpm->enabled = true; tpm->node = node; tpm->dev = dev; tpm->driver = driver; list_add_tail(&tpm_list, &tpm->link); prlog(PR_NOTICE, "Found tpm%d,%s evLogLen=%d evLogSize=%d\n", tpm->id, tpm->driver->name, tpm->logmgr.logSize, tpm->logmgr.logMaxSize); return 0; disable: dt_add_property_string(node, "status", "disabled"); prlog(PR_NOTICE, "tpm node %p disabled\n", node); free(tpm); return -1; } int tpm_init(void) { if (!list_empty(&tpm_list)) return 0; list_head_init(&tpm_list); /* tpm drivers supported */ tpm_i2c_nuvoton_probe(); if (list_empty(&tpm_list)) { prlog(PR_INFO, "no compatible tpm device found!\n"); return -1; } return 0; } void tpm_cleanup(void) { struct tpm_chip *tpm = NULL; tpm = list_pop(&tpm_list, struct tpm_chip, link); while (tpm) { if (tpm->dev) free(tpm->dev); tpm->driver = NULL; free(tpm); tpm = list_pop(&tpm_list, struct tpm_chip, link); } list_head_init(&tpm_list); } static void tpm_disable(struct tpm_chip *tpm) { assert(tpm); tpm->enabled = false; prlog(PR_NOTICE, "tpm%d disabled\n", tpm->id); } int tpm_extendl(TPM_Pcr pcr, TPM_Alg_Id alg1, uint8_t* digest1, size_t size1, TPM_Alg_Id alg2, uint8_t* digest2, size_t size2, uint32_t event_type, const char* event_msg) { int rc, failed; TCG_PCR_EVENT2 event; struct tpm_chip *tpm = NULL; failed = 0; if (list_empty(&tpm_list)) { prlog(PR_ERR, "%s (pcr%d) NOT MEASURED. No TPM " "registered/enabled\n", (event_type==EV_SEPARATOR) ? "EV_SEPARATOR" : event_msg, pcr); return -1; } list_for_each(&tpm_list, tpm, link) { if (!tpm->enabled) continue; event = TpmLogMgr_genLogEventPcrExtend(pcr, alg1, digest1, size1, alg2, digest2, size2, event_type, event_msg); /* eventlog recording */ rc = TpmLogMgr_addEvent(&tpm->logmgr, &event); if (rc) { /** * @fwts-label STBAddEventFailed * @fwts-advice TpmLogMgr failed to add a new event * to the event log. TpmLogMgr is part of hostboot, * but the source code is shared with skiboot. * 1) The hostboot TpmLogMgr code may have * been updated. * 2) Check that max event log size was not reached * and log marshall executed with no error. Enabling the * trace routines in trustedbootUtils.H may help. */ prlog(PR_ERR, "%s -> evLog%d FAILED: pcr%d evType=0x%x rc=%d\n", (event_type==EV_SEPARATOR) ? "EV_SEPARATOR" : event_msg, tpm->id, pcr, event_type, rc); tpm_disable(tpm); failed++; continue; } #ifdef STB_DEBUG if (rc == 0) prlog(PR_NOTICE, "%s -> evLog%d: pcr%d evType=0x%x " "evLogLen=%d\n", (event_type==EV_SEPARATOR) ? "EV_SEPARATOR" : event_msg, tpm->id, pcr, event_type, tpm->logmgr.logSize); tpm_print_pcr(tpm, pcr, alg1, size1); tpm_print_pcr(tpm, pcr, alg2, size2); #endif /* extend the pcr number in both sha1 and sha256 banks*/ rc = tpmCmdPcrExtend2Hash(tpm, pcr, alg1, digest1, size1, alg2, digest2, size2); if (rc) { /** * @fwts-label STBPCRExtendFailed * @fwts-advice PCR extend operation failed. This TSS * implementation is part of hostboot, but the source * code is shared with skiboot. * 1) The hostboot TSS may have been updated. * 2) This may be caused by the short I2C timeout and * can be fixed by increasing the timeout. Otherwise, * this indicates a bug in the TSS or the TPM * device driver. Each one has local debug macros that * can help. */ prlog(PR_ERR, "%s -> tpm%d FAILED: pcr%d rc=%d\n", (event_type==EV_SEPARATOR) ? "EV_SEPARATOR" : event_msg, tpm->id, pcr, rc); tpm_disable(tpm); failed++; continue; } #ifdef STB_DEBUG if (rc == 0) { prlog(PR_NOTICE, "%s -> tpm%d: pcr%d\n", (event_type==EV_SEPARATOR) ? "EV_SEPARATOR" : event_msg, tpm->id, pcr); tpm_print_pcr(tpm, pcr, alg1, size1); tpm_print_pcr(tpm, pcr, alg2, size2); } #endif prlog(PR_NOTICE, "%s measured on pcr%d (tpm%d, evType 0x%x, " "evLogLen %d)\n", (event_type==EV_SEPARATOR) ? "EV_SEPARATOR" : event_msg, pcr, tpm->id, event_type, tpm->logmgr.logSize); } if (failed > 0) return -2; return 0; } void tpm_add_status_property(void) { struct tpm_chip *tpm; list_for_each(&tpm_list, tpm, link) { dt_add_property_string(tpm->node, "status", tpm->enabled ? "okay" : "disabled"); } }