| // Implementation of the TCG BIOS extension according to the specification |
| // described in specs found at |
| // http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific_implementation_specification_for_conventional_bios |
| // |
| // Copyright (C) 2006-2011, 2014, 2015 IBM Corporation |
| // |
| // Authors: |
| // Stefan Berger <stefanb@linux.vnet.ibm.com> |
| // |
| // This file may be distributed under the terms of the GNU LGPLv3 license. |
| |
| #include "bregs.h" // struct bregs |
| #include "byteorder.h" // cpu_to_* |
| #include "config.h" // CONFIG_TCGBIOS |
| #include "farptr.h" // MAKE_FLATPTR |
| #include "fw/paravirt.h" // runningOnXen |
| #include "hw/tpm_drivers.h" // tpm_drivers[] |
| #include "output.h" // dprintf |
| #include "sha1.h" // sha1 |
| #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor |
| #include "std/smbios.h" // struct smbios_entry_point |
| #include "std/tcg.h" // TCG_PC_LOGOVERFLOW |
| #include "string.h" // checksum |
| #include "tcgbios.h"// tpm_*, prototypes |
| #include "util.h" // printf, get_keystroke |
| #include "stacks.h" // wait_threads, reset |
| #include "malloc.h" // malloc_high |
| |
| |
| /**************************************************************** |
| * ACPI TCPA table interface |
| ****************************************************************/ |
| |
| struct { |
| /* length of the TCPA log buffer */ |
| u32 log_area_minimum_length; |
| |
| /* start address of TCPA log buffer */ |
| u8 * log_area_start_address; |
| |
| /* number of log entries written */ |
| u32 entry_count; |
| |
| /* address to write next log entry to */ |
| u8 * log_area_next_entry; |
| |
| /* address of last entry written (need for TCG_StatusCheck) */ |
| u8 * log_area_last_entry; |
| } tpm_state VARLOW; |
| |
| static int tpm_set_log_area(u8 *log_area_start_address, |
| u32 log_area_minimum_length) |
| { |
| if (!log_area_start_address || !log_area_minimum_length) |
| return -1; |
| |
| memset(log_area_start_address, 0, log_area_minimum_length); |
| tpm_state.log_area_start_address = log_area_start_address; |
| tpm_state.log_area_minimum_length = log_area_minimum_length; |
| tpm_state.log_area_next_entry = log_area_start_address; |
| tpm_state.log_area_last_entry = NULL; |
| tpm_state.entry_count = 0; |
| return 0; |
| } |
| |
| static int |
| tpm_tcpa_probe(void) |
| { |
| struct tcpa_descriptor_rev2 *tcpa = find_acpi_table(TCPA_SIGNATURE); |
| if (!tcpa) |
| return -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: TCPA: LASA = %p, LAML = %u\n", |
| (u8 *)(long)tcpa->log_area_start_address, |
| tcpa->log_area_minimum_length); |
| |
| return tpm_set_log_area((u8*)(long)tcpa->log_area_start_address, |
| tcpa->log_area_minimum_length); |
| } |
| |
| static int |
| tpm_tpm2_probe(void) |
| { |
| struct tpm2_descriptor_rev2 *tpm2 = find_acpi_table(TPM2_SIGNATURE); |
| if (!tpm2) |
| return -1; |
| |
| if (tpm2->length < 76) |
| return -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: TPM2: LASA = %p, LAML = %u\n", |
| (u8 *)(long)tpm2->log_area_start_address, |
| tpm2->log_area_minimum_length); |
| |
| return tpm_set_log_area((u8*)(long)tpm2->log_area_start_address, |
| tpm2->log_area_minimum_length); |
| } |
| |
| /* |
| * Extend the ACPI log with the given entry by copying the |
| * entry data into the log. |
| * Input |
| * entry : The header data to use (including the variable length digest) |
| * digest_len : Length of the digest in 'entry' |
| * event : Pointer to the event body to be copied into the log |
| * event_len : Length of 'event' |
| * |
| * Output: |
| * Returns an error code in case of faiure, 0 in case of success |
| */ |
| static int |
| tpm_log_event(struct tpm_log_header *entry, int digest_len |
| , const void *event, int event_len) |
| { |
| dprintf(DEBUG_tcg, "TCGBIOS: LASA = %p, next entry = %p\n", |
| tpm_state.log_area_start_address, tpm_state.log_area_next_entry); |
| |
| if (tpm_state.log_area_next_entry == NULL) |
| return -1; |
| |
| u32 size = (sizeof(*entry) + digest_len |
| + sizeof(struct tpm_log_trailer) + event_len); |
| u32 logsize = (tpm_state.log_area_next_entry + size |
| - tpm_state.log_area_start_address); |
| if (logsize > tpm_state.log_area_minimum_length) { |
| dprintf(DEBUG_tcg, "TCGBIOS: LOG OVERFLOW: size = %d\n", size); |
| return -1; |
| } |
| |
| void *dest = tpm_state.log_area_next_entry; |
| memcpy(dest, entry, sizeof(*entry) + digest_len); |
| struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len; |
| t->eventdatasize = event_len; |
| memcpy(t->event, event, event_len); |
| |
| tpm_state.log_area_last_entry = tpm_state.log_area_next_entry; |
| tpm_state.log_area_next_entry += size; |
| tpm_state.entry_count++; |
| |
| return 0; |
| } |
| |
| |
| /**************************************************************** |
| * Digest formatting |
| ****************************************************************/ |
| |
| static TPMVersion TPM_version; |
| static u32 tpm20_pcr_selection_size; |
| static struct tpml_pcr_selection *tpm20_pcr_selection; |
| |
| // A 'struct tpm_log_entry' is a local data structure containing a |
| // 'tpm_log_header' followed by space for the maximum supported |
| // digest. (The digest is a sha1 hash on tpm1.2 or a series of |
| // tpm2_digest_value structs on tpm2.0) |
| struct tpm_log_entry { |
| struct tpm_log_header hdr; |
| u8 pad[sizeof(struct tpm2_digest_values) |
| + 5 * sizeof(struct tpm2_digest_value) |
| + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE |
| + SHA512_BUFSIZE + SM3_256_BUFSIZE]; |
| } PACKED; |
| |
| static const struct hash_parameters { |
| u16 hashalg; |
| u8 hashalg_flag; |
| u8 hash_buffersize; |
| const char *name; |
| } hash_parameters[] = { |
| { |
| .hashalg = TPM2_ALG_SHA1, |
| .hashalg_flag = TPM2_ALG_SHA1_FLAG, |
| .hash_buffersize = SHA1_BUFSIZE, |
| .name = "SHA1", |
| }, { |
| .hashalg = TPM2_ALG_SHA256, |
| .hashalg_flag = TPM2_ALG_SHA256_FLAG, |
| .hash_buffersize = SHA256_BUFSIZE, |
| .name = "SHA256", |
| }, { |
| .hashalg = TPM2_ALG_SHA384, |
| .hashalg_flag = TPM2_ALG_SHA384_FLAG, |
| .hash_buffersize = SHA384_BUFSIZE, |
| .name = "SHA384", |
| }, { |
| .hashalg = TPM2_ALG_SHA512, |
| .hashalg_flag = TPM2_ALG_SHA512_FLAG, |
| .hash_buffersize = SHA512_BUFSIZE, |
| .name = "SHA512", |
| }, { |
| .hashalg = TPM2_ALG_SM3_256, |
| .hashalg_flag = TPM2_ALG_SM3_256_FLAG, |
| .hash_buffersize = SM3_256_BUFSIZE, |
| .name = "SM3-256", |
| } |
| }; |
| |
| static int |
| tpm20_get_hash_buffersize(u16 hashAlg) |
| { |
| unsigned i; |
| |
| for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { |
| if (hash_parameters[i].hashalg == hashAlg) |
| return hash_parameters[i].hash_buffersize; |
| } |
| return -1; |
| } |
| |
| static u8 |
| tpm20_hashalg_to_flag(u16 hashAlg) |
| { |
| unsigned i; |
| |
| for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { |
| if (hash_parameters[i].hashalg == hashAlg) |
| return hash_parameters[i].hashalg_flag; |
| } |
| return 0; |
| } |
| |
| static u16 |
| tpm20_hashalg_flag_to_hashalg(u8 hashalg_flag) |
| { |
| unsigned i; |
| |
| for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { |
| if (hash_parameters[i].hashalg_flag == hashalg_flag) |
| return hash_parameters[i].hashalg; |
| } |
| return 0; |
| } |
| |
| static const char * |
| tpm20_hashalg_flag_to_name(u8 hashalg_flag) |
| { |
| unsigned i; |
| |
| for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) { |
| if (hash_parameters[i].hashalg_flag == hashalg_flag) |
| return hash_parameters[i].name; |
| } |
| return NULL; |
| } |
| |
| // Add an entry at the start of the log describing digest formats |
| static int |
| tpm20_write_EfiSpecIdEventStruct(void) |
| { |
| if (!tpm20_pcr_selection) |
| return -1; |
| |
| struct { |
| struct TCG_EfiSpecIdEventStruct hdr; |
| u8 pad[256]; |
| } event = { |
| .hdr.signature = "Spec ID Event03", |
| .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, |
| .hdr.specVersionMinor = 0, |
| .hdr.specVersionMajor = 2, |
| .hdr.specErrata = 0, |
| .hdr.uintnSize = 2, |
| }; |
| |
| struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; |
| void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; |
| |
| u32 count; |
| for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) { |
| u8 sizeOfSelect = sel->sizeOfSelect; |
| |
| nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; |
| if (nsel > end) |
| break; |
| |
| int hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); |
| if (hsize < 0) { |
| dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n", |
| be16_to_cpu(sel->hashAlg)); |
| return -1; |
| } |
| |
| int event_size = offsetof(struct TCG_EfiSpecIdEventStruct |
| , digestSizes[count+1]); |
| if (event_size > sizeof(event) - sizeof(u32)) { |
| dprintf(DEBUG_tcg, "EfiSpecIdEventStruct pad too small\n"); |
| return -1; |
| } |
| |
| event.hdr.digestSizes[count].algorithmId = be16_to_cpu(sel->hashAlg); |
| event.hdr.digestSizes[count].digestSize = hsize; |
| |
| sel = nsel; |
| } |
| |
| if (sel != end) { |
| dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n"); |
| return -1; |
| } |
| |
| event.hdr.numberOfAlgorithms = count; |
| int event_size = offsetof(struct TCG_EfiSpecIdEventStruct |
| , digestSizes[count]); |
| u32 *vendorInfoSize = (void*)&event + event_size; |
| *vendorInfoSize = 0; |
| event_size += sizeof(*vendorInfoSize); |
| |
| struct tpm_log_entry le = { |
| .hdr.eventtype = EV_NO_ACTION, |
| }; |
| return tpm_log_event(&le.hdr, SHA1_BUFSIZE, &event, event_size); |
| } |
| |
| /* |
| * Build the TPM2 tpm2_digest_values data structure from the given hash. |
| * Follow the PCR bank configuration of the TPM and write the same hash |
| * in either truncated or zero-padded form in the areas of all the other |
| * hashes. For example, write the sha1 hash in the area of the sha256 |
| * hash and fill the remaining bytes with zeros. Or truncate the sha256 |
| * hash when writing it in the area of the sha1 hash. |
| * |
| * le: the log entry to build the digest in |
| * sha1: the sha1 hash value to use |
| * bigEndian: whether to build in big endian format for the TPM or |
| * little endian for the log |
| * |
| * Returns the digest size; -1 on fatal error |
| */ |
| static int |
| tpm20_build_digest(struct tpm_log_entry *le, const u8 *sha1, int bigEndian) |
| { |
| if (!tpm20_pcr_selection) |
| return -1; |
| |
| struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; |
| void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; |
| void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values); |
| |
| u32 count; |
| for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) { |
| u8 sizeOfSelect = sel->sizeOfSelect; |
| |
| nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; |
| if (nsel > end) |
| break; |
| |
| int hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); |
| if (hsize < 0) { |
| dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n", |
| be16_to_cpu(sel->hashAlg)); |
| return -1; |
| } |
| |
| /* buffer size sanity check before writing */ |
| struct tpm2_digest_value *v = dest; |
| if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) { |
| dprintf(DEBUG_tcg, "tpm_log_entry is too small\n"); |
| return -1; |
| } |
| |
| if (bigEndian) |
| v->hashAlg = sel->hashAlg; |
| else |
| v->hashAlg = be16_to_cpu(sel->hashAlg); |
| |
| memset(v->hash, 0, hsize); |
| memcpy(v->hash, sha1, hsize > SHA1_BUFSIZE ? SHA1_BUFSIZE : hsize); |
| |
| dest += sizeof(*v) + hsize; |
| sel = nsel; |
| } |
| |
| if (sel != end) { |
| dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n"); |
| return -1; |
| } |
| |
| struct tpm2_digest_values *v = (void*)le->hdr.digest; |
| if (bigEndian) |
| v->count = cpu_to_be32(count); |
| else |
| v->count = count; |
| |
| return dest - (void*)le->hdr.digest; |
| } |
| |
| static int |
| tpm12_build_digest(struct tpm_log_entry *le, const u8 *sha1) |
| { |
| // On TPM 1.2 the digest contains just the SHA1 hash |
| memcpy(le->hdr.digest, sha1, SHA1_BUFSIZE); |
| return SHA1_BUFSIZE; |
| } |
| |
| static int |
| tpm_build_digest(struct tpm_log_entry *le, const u8 *sha1, int bigEndian) |
| { |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| return tpm12_build_digest(le, sha1); |
| case TPM_VERSION_2: |
| return tpm20_build_digest(le, sha1, bigEndian); |
| } |
| return -1; |
| } |
| |
| |
| /**************************************************************** |
| * TPM hardware command wrappers |
| ****************************************************************/ |
| |
| // Helper function for sending tpm commands that take a single |
| // optional parameter (0, 1, or 2 bytes) and have no special response. |
| static int |
| tpm_simple_cmd(u8 locty, u32 ordinal |
| , int param_size, u16 param, enum tpmDurationType to_t) |
| { |
| struct { |
| struct tpm_req_header trqh; |
| u16 param; |
| } PACKED req = { |
| .trqh.totlen = cpu_to_be32(sizeof(req.trqh) + param_size), |
| .trqh.ordinal = cpu_to_be32(ordinal), |
| .param = param_size == 2 ? cpu_to_be16(param) : param, |
| }; |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| req.trqh.tag = cpu_to_be16(TPM_TAG_RQU_CMD); |
| break; |
| case TPM_VERSION_2: |
| req.trqh.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); |
| break; |
| } |
| |
| u8 obuffer[64]; |
| struct tpm_rsp_header *trsh = (void*)obuffer; |
| u32 obuffer_len = sizeof(obuffer); |
| memset(obuffer, 0x0, sizeof(obuffer)); |
| |
| int ret = tpmhw_transmit(locty, &req.trqh, obuffer, &obuffer_len, to_t); |
| ret = ret ? -1 : be32_to_cpu(trsh->errcode); |
| dprintf(DEBUG_tcg, "Return from tpm_simple_cmd(%x, %x) = %x\n", |
| ordinal, param, ret); |
| return ret; |
| } |
| |
| static int |
| tpm20_getcapability(u32 capability, u32 property, u32 count, |
| struct tpm_rsp_header *rsp, u32 rsize) |
| { |
| struct tpm2_req_getcapability trg = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(trg)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), |
| .capability = cpu_to_be32(capability), |
| .property = cpu_to_be32(property), |
| .propertycount = cpu_to_be32(count), |
| }; |
| |
| u32 resp_size = rsize; |
| int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size, |
| TPM_DURATION_TYPE_SHORT); |
| ret = (ret || |
| rsize < be32_to_cpu(rsp->totlen)) ? -1 : be32_to_cpu(rsp->errcode); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_get_pcrbanks(void) |
| { |
| u8 buffer[128]; |
| struct tpm2_res_getcapability *trg = |
| (struct tpm2_res_getcapability *)&buffer; |
| |
| int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, |
| sizeof(buffer)); |
| if (ret) |
| return ret; |
| |
| /* defend against (broken) TPM sending packets that are too short */ |
| u32 resplen = be32_to_cpu(trg->hdr.totlen); |
| if (resplen <= offsetof(struct tpm2_res_getcapability, data)) |
| return -1; |
| |
| u32 size = resplen - offsetof(struct tpm2_res_getcapability, data); |
| /* we need a valid tpml_pcr_selection up to and including sizeOfSelect */ |
| if (size < offsetof(struct tpml_pcr_selection, selections) + |
| offsetof(struct tpms_pcr_selection, pcrSelect)) |
| return -1; |
| |
| tpm20_pcr_selection = malloc_high(size); |
| if (tpm20_pcr_selection) { |
| memcpy(tpm20_pcr_selection, &trg->data, size); |
| tpm20_pcr_selection_size = size; |
| } else { |
| warn_noalloc(); |
| ret = -1; |
| } |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_get_suppt_pcrbanks(u8 *suppt_pcrbanks, u8 *active_pcrbanks) |
| { |
| *suppt_pcrbanks = 0; |
| *active_pcrbanks = 0; |
| |
| if (!tpm20_pcr_selection) |
| return -1; |
| |
| struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; |
| void *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size; |
| |
| while (1) { |
| u8 sizeOfSelect = sel->sizeOfSelect; |
| void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect; |
| if (nsel > end) |
| return 0; |
| |
| u16 hashalg = be16_to_cpu(sel->hashAlg); |
| u8 hashalg_flag = tpm20_hashalg_to_flag(hashalg); |
| |
| *suppt_pcrbanks |= hashalg_flag; |
| |
| unsigned i; |
| for (i = 0; i < sizeOfSelect; i++) { |
| if (sel->pcrSelect[i]) { |
| *active_pcrbanks |= hashalg_flag; |
| break; |
| } |
| } |
| |
| sel = nsel; |
| } |
| } |
| |
| static int |
| tpm20_set_pcrbanks(u32 active_banks) |
| { |
| struct tpm2_req_pcr_allocate trpa = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate), |
| .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), |
| .authblocksize = cpu_to_be32(sizeof(trpa.authblock)), |
| .authblock = { |
| .handle = cpu_to_be32(TPM2_RS_PW), |
| .noncesize = cpu_to_be16(0), |
| .contsession = TPM2_YES, |
| .pwdsize = cpu_to_be16(0), |
| }, |
| }; |
| struct tpms_pcr_selection3 { |
| u16 hashAlg; |
| u8 sizeOfSelect; |
| u8 pcrSelect[3]; |
| } tps[ARRAY_SIZE(trpa.tpms_pcr_selections)]; |
| int i = 0; |
| u8 hashalg_flag = TPM2_ALG_SHA1_FLAG; |
| u8 dontcare, suppt_banks; |
| |
| tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare); |
| |
| while (hashalg_flag) { |
| if ((hashalg_flag & suppt_banks)) { |
| u16 hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag); |
| |
| if (hashalg) { |
| u8 mask = 0; |
| tps[i].hashAlg = cpu_to_be16(hashalg); |
| tps[i].sizeOfSelect = 3; |
| |
| if (active_banks & hashalg_flag) |
| mask = 0xff; |
| |
| tps[i].pcrSelect[0] = mask; |
| tps[i].pcrSelect[1] = mask; |
| tps[i].pcrSelect[2] = mask; |
| i++; |
| } |
| } |
| hashalg_flag <<= 1; |
| } |
| |
| trpa.count = cpu_to_be32(i); |
| memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0])); |
| trpa.hdr.totlen = cpu_to_be32(offsetof(struct tpm2_req_pcr_allocate, |
| tpms_pcr_selections) + |
| i * sizeof(tps[0])); |
| |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| |
| int ret = tpmhw_transmit(0, &trpa.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_SHORT); |
| ret = ret ? -1 : be32_to_cpu(rsp.errcode); |
| |
| return ret; |
| } |
| |
| static int tpm20_activate_pcrbanks(u32 active_banks) |
| { |
| int ret = tpm20_set_pcrbanks(active_banks); |
| if (!ret) |
| ret = tpm_simple_cmd(0, TPM2_CC_Shutdown, |
| 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); |
| if (!ret) |
| reset(); |
| return ret; |
| } |
| |
| static int |
| tpm12_get_capability(u32 cap, u32 subcap, struct tpm_rsp_header *rsp, u32 rsize) |
| { |
| struct tpm_req_getcap trgc = { |
| .hdr.tag = cpu_to_be16(TPM_TAG_RQU_CMD), |
| .hdr.totlen = cpu_to_be32(sizeof(trgc)), |
| .hdr.ordinal = cpu_to_be32(TPM_ORD_GetCapability), |
| .capArea = cpu_to_be32(cap), |
| .subCapSize = cpu_to_be32(sizeof(trgc.subCap)), |
| .subCap = cpu_to_be32(subcap) |
| }; |
| u32 resp_size = rsize; |
| int ret = tpmhw_transmit(0, &trgc.hdr, rsp, &resp_size, |
| TPM_DURATION_TYPE_SHORT); |
| ret = (ret || resp_size != rsize) ? -1 : be32_to_cpu(rsp->errcode); |
| dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(%d, %d)" |
| " = %x\n", cap, subcap, ret); |
| return ret; |
| } |
| |
| static int |
| tpm12_read_permanent_flags(char *buf, int buf_len) |
| { |
| memset(buf, 0, buf_len); |
| |
| struct tpm_res_getcap_perm_flags pf; |
| int ret = tpm12_get_capability(TPM_CAP_FLAG, TPM_CAP_FLAG_PERMANENT |
| , &pf.hdr, sizeof(pf)); |
| if (ret) |
| return -1; |
| |
| memcpy(buf, &pf.perm_flags, buf_len); |
| |
| return 0; |
| } |
| |
| static int |
| tpm12_determine_timeouts(void) |
| { |
| struct tpm_res_getcap_timeouts timeouts; |
| int ret = tpm12_get_capability(TPM_CAP_PROPERTY, TPM_CAP_PROP_TIS_TIMEOUT |
| , &timeouts.hdr, sizeof(timeouts)); |
| if (ret) |
| return ret; |
| |
| struct tpm_res_getcap_durations durations; |
| ret = tpm12_get_capability(TPM_CAP_PROPERTY, TPM_CAP_PROP_DURATION |
| , &durations.hdr, sizeof(durations)); |
| if (ret) |
| return ret; |
| |
| int i; |
| for (i = 0; i < 3; i++) |
| durations.durations[i] = be32_to_cpu(durations.durations[i]); |
| |
| for (i = 0; i < 4; i++) |
| timeouts.timeouts[i] = be32_to_cpu(timeouts.timeouts[i]); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: timeouts: %u %u %u %u\n", |
| timeouts.timeouts[0], |
| timeouts.timeouts[1], |
| timeouts.timeouts[2], |
| timeouts.timeouts[3]); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: durations: %u %u %u\n", |
| durations.durations[0], |
| durations.durations[1], |
| durations.durations[2]); |
| |
| tpmhw_set_timeouts(timeouts.timeouts, durations.durations); |
| |
| return 0; |
| } |
| |
| static void |
| tpm20_set_timeouts(void) |
| { |
| u32 durations[3] = { |
| TPM2_DEFAULT_DURATION_SHORT, |
| TPM2_DEFAULT_DURATION_MEDIUM, |
| TPM2_DEFAULT_DURATION_LONG, |
| }; |
| u32 timeouts[4] = { |
| TIS2_DEFAULT_TIMEOUT_A, |
| TIS2_DEFAULT_TIMEOUT_B, |
| TIS2_DEFAULT_TIMEOUT_C, |
| TIS2_DEFAULT_TIMEOUT_D, |
| }; |
| |
| tpmhw_set_timeouts(timeouts, durations); |
| } |
| |
| static int |
| tpm12_extend(struct tpm_log_entry *le, int digest_len) |
| { |
| struct tpm_req_extend tre = { |
| .hdr.tag = cpu_to_be16(TPM_TAG_RQU_CMD), |
| .hdr.totlen = cpu_to_be32(sizeof(tre)), |
| .hdr.ordinal = cpu_to_be32(TPM_ORD_Extend), |
| .pcrindex = cpu_to_be32(le->hdr.pcrindex), |
| }; |
| memcpy(tre.digest, le->hdr.digest, sizeof(tre.digest)); |
| |
| struct tpm_rsp_extend rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &tre.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_SHORT); |
| if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) |
| return -1; |
| |
| return 0; |
| } |
| |
| static int tpm20_extend(struct tpm_log_entry *le, int digest_len) |
| { |
| struct tpm2_req_extend tmp_tre = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), |
| .hdr.totlen = cpu_to_be32(0), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), |
| .pcrindex = cpu_to_be32(le->hdr.pcrindex), |
| .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), |
| .authblock = { |
| .handle = cpu_to_be32(TPM2_RS_PW), |
| .noncesize = cpu_to_be16(0), |
| .contsession = TPM2_YES, |
| .pwdsize = cpu_to_be16(0), |
| }, |
| }; |
| u8 buffer[sizeof(tmp_tre) + sizeof(le->pad)]; |
| struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; |
| |
| memcpy(tre, &tmp_tre, sizeof(tmp_tre)); |
| memcpy(&tre->digest[0], le->hdr.digest, digest_len); |
| |
| tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len); |
| |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_SHORT); |
| if (ret || resp_length != sizeof(rsp) || rsp.errcode) |
| return -1; |
| |
| return 0; |
| } |
| |
| static int |
| tpm_extend(struct tpm_log_entry *le, int digest_len) |
| { |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| return tpm12_extend(le, digest_len); |
| case TPM_VERSION_2: |
| return tpm20_extend(le, digest_len); |
| } |
| return -1; |
| } |
| |
| static int |
| tpm20_stirrandom(void) |
| { |
| struct tpm2_req_stirrandom stir = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(stir)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_StirRandom), |
| .size = cpu_to_be16(sizeof(stir.stir)), |
| .stir = rdtscll(), |
| }; |
| /* set more bits to stir with */ |
| stir.stir += swab64(rdtscll()); |
| |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &stir.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_SHORT); |
| if (ret || resp_length != sizeof(rsp) || rsp.errcode) |
| ret = -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_StirRandom = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_getrandom(u8 *buf, u16 buf_len) |
| { |
| struct tpm2_res_getrandom rsp; |
| |
| if (buf_len > sizeof(rsp.rnd.buffer)) |
| return -1; |
| |
| struct tpm2_req_getrandom trgr = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(trgr)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_GetRandom), |
| .bytesRequested = cpu_to_be16(buf_len), |
| }; |
| u32 resp_length = sizeof(rsp); |
| |
| int ret = tpmhw_transmit(0, &trgr.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_MEDIUM); |
| if (ret || resp_length != sizeof(rsp) || rsp.hdr.errcode) |
| ret = -1; |
| else |
| memcpy(buf, rsp.rnd.buffer, buf_len); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_GetRandom = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_hierarchycontrol(u32 hierarchy, u8 state) |
| { |
| /* we will try to deactivate the TPM now - ignoring all errors */ |
| struct tpm2_req_hierarchycontrol trh = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(trh)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyControl), |
| .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), |
| .authblocksize = cpu_to_be32(sizeof(trh.authblock)), |
| .authblock = { |
| .handle = cpu_to_be32(TPM2_RS_PW), |
| .noncesize = cpu_to_be16(0), |
| .contsession = TPM2_YES, |
| .pwdsize = cpu_to_be16(0), |
| }, |
| .enable = cpu_to_be32(hierarchy), |
| .state = state, |
| }; |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &trh.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_MEDIUM); |
| if (ret || resp_length != sizeof(rsp) || rsp.errcode) |
| ret = -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_HierarchyControl = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_hierarchychangeauth(u8 auth[20]) |
| { |
| struct tpm2_req_hierarchychangeauth trhca = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(trhca)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_HierarchyChangeAuth), |
| .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), |
| .authblocksize = cpu_to_be32(sizeof(trhca.authblock)), |
| .authblock = { |
| .handle = cpu_to_be32(TPM2_RS_PW), |
| .noncesize = cpu_to_be16(0), |
| .contsession = TPM2_YES, |
| .pwdsize = cpu_to_be16(0), |
| }, |
| .newAuth = { |
| .size = cpu_to_be16(sizeof(trhca.newAuth.buffer)), |
| }, |
| }; |
| memcpy(trhca.newAuth.buffer, auth, sizeof(trhca.newAuth.buffer)); |
| |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &trhca.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_MEDIUM); |
| if (ret || resp_length != sizeof(rsp) || rsp.errcode) |
| ret = -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_HierarchyChangeAuth = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| |
| /**************************************************************** |
| * Setup and Measurements |
| ****************************************************************/ |
| |
| static int TPM_has_physical_presence; |
| u8 TPM_working VARLOW; |
| |
| static int |
| tpm_is_working(void) |
| { |
| return CONFIG_TCGBIOS && TPM_working; |
| } |
| |
| static void |
| tpm_set_failure(void) |
| { |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| /* |
| * We will try to deactivate the TPM now - ignoring all errors |
| * Physical presence is asserted. |
| */ |
| |
| tpm_simple_cmd(0, TPM_ORD_SetTempDeactivated, |
| 0, 0, TPM_DURATION_TYPE_SHORT); |
| break; |
| case TPM_VERSION_2: |
| tpm20_hierarchycontrol(TPM2_RH_ENDORSEMENT, TPM2_NO); |
| tpm20_hierarchycontrol(TPM2_RH_OWNER, TPM2_NO); |
| break; |
| } |
| |
| TPM_working = 0; |
| } |
| |
| /* |
| * Add a measurement to the log; the data at data_seg:data/length are |
| * appended to the TCG_PCClientPCREventStruct |
| * |
| * Input parameters: |
| * pcrindex : which PCR to extend |
| * event_type : type of event; specs section on 'Event Types' |
| * event : pointer to info (e.g., string) to be added to log as-is |
| * event_length: length of the event |
| * hashdata : pointer to the data to be hashed |
| * hashdata_length: length of the data to be hashed |
| */ |
| static void |
| tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, |
| const char *event, u32 event_length, |
| const u8 *hashdata, u32 hashdata_length) |
| { |
| if (!tpm_is_working()) |
| return; |
| |
| u8 hash[SHA1_BUFSIZE]; |
| sha1(hashdata, hashdata_length, hash); |
| |
| struct tpm_log_entry le = { |
| .hdr.pcrindex = pcrindex, |
| .hdr.eventtype = event_type, |
| }; |
| int digest_len = tpm_build_digest(&le, hash, 1); |
| if (digest_len < 0) |
| return; |
| int ret = tpm_extend(&le, digest_len); |
| if (ret) { |
| tpm_set_failure(); |
| return; |
| } |
| tpm_build_digest(&le, hash, 0); |
| tpm_log_event(&le.hdr, digest_len, event, event_length); |
| } |
| |
| // Add an EV_ACTION measurement to the list of measurements |
| static void |
| tpm_add_action(u32 pcrIndex, const char *string) |
| { |
| u32 len = strlen(string); |
| tpm_add_measurement_to_log(pcrIndex, EV_ACTION, |
| string, len, (u8 *)string, len); |
| } |
| |
| /* |
| * Add event separators for PCRs 0 to 7; specs on 'Measuring Boot Events' |
| */ |
| static void |
| tpm_add_event_separators(void) |
| { |
| static const u8 evt_separator[] = {0xff,0xff,0xff,0xff}; |
| u32 pcrIndex; |
| for (pcrIndex = 0; pcrIndex <= 7; pcrIndex++) |
| tpm_add_measurement_to_log(pcrIndex, EV_SEPARATOR, |
| NULL, 0, |
| evt_separator, |
| sizeof(evt_separator)); |
| } |
| |
| static void |
| tpm_smbios_measure(void) |
| { |
| struct pcctes pcctes = { |
| .eventid = 1, |
| .eventdatasize = SHA1_BUFSIZE, |
| }; |
| struct smbios_entry_point *sep = SMBiosAddr; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: SMBIOS at %p\n", sep); |
| |
| if (!sep) |
| return; |
| |
| sha1((const u8 *)sep->structure_table_address, |
| sep->structure_table_length, pcctes.digest); |
| tpm_add_measurement_to_log(1, |
| EV_EVENT_TAG, |
| (const char *)&pcctes, sizeof(pcctes), |
| (u8 *)&pcctes, sizeof(pcctes)); |
| } |
| |
| static int |
| tpm12_assert_physical_presence(void) |
| { |
| int ret = tpm_simple_cmd(0, TPM_ORD_PhysicalPresence, |
| 2, TPM_PP_PRESENT, TPM_DURATION_TYPE_SHORT); |
| if (!ret) |
| return 0; |
| |
| struct tpm_permanent_flags pf; |
| ret = tpm12_read_permanent_flags((char *)&pf, sizeof(pf)); |
| if (ret) |
| return -1; |
| |
| /* check if hardware physical presence is supported */ |
| if (pf.flags[PERM_FLAG_IDX_PHYSICAL_PRESENCE_HW_ENABLE]) { |
| /* HW phys. presence may not be asserted... */ |
| return 0; |
| } |
| |
| if (!pf.flags[PERM_FLAG_IDX_PHYSICAL_PRESENCE_LIFETIME_LOCK] |
| && !pf.flags[PERM_FLAG_IDX_PHYSICAL_PRESENCE_CMD_ENABLE]) { |
| tpm_simple_cmd(0, TPM_ORD_PhysicalPresence, |
| 2, TPM_PP_CMD_ENABLE, TPM_DURATION_TYPE_SHORT); |
| |
| return tpm_simple_cmd(0, TPM_ORD_PhysicalPresence, |
| 2, TPM_PP_PRESENT, TPM_DURATION_TYPE_SHORT); |
| } |
| return -1; |
| } |
| |
| static int |
| tpm12_startup(void) |
| { |
| dprintf(DEBUG_tcg, "TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n"); |
| int ret = tpm_simple_cmd(0, TPM_ORD_Startup, |
| 2, TPM_ST_CLEAR, TPM_DURATION_TYPE_SHORT); |
| if (CONFIG_COREBOOT && ret == TPM_INVALID_POSTINIT) |
| /* with other firmware on the system the TPM may already have been |
| * initialized |
| */ |
| ret = 0; |
| if (ret) |
| goto err_exit; |
| |
| /* assertion of physical presence is only possible after startup */ |
| ret = tpm12_assert_physical_presence(); |
| if (!ret) |
| TPM_has_physical_presence = 1; |
| |
| ret = tpm12_determine_timeouts(); |
| if (ret) |
| goto err_exit; |
| |
| ret = tpm_simple_cmd(0, TPM_ORD_SelfTestFull, |
| 0, 0, TPM_DURATION_TYPE_LONG); |
| if (ret) |
| goto err_exit; |
| |
| ret = tpm_simple_cmd(3, TSC_ORD_ResetEstablishmentBit, |
| 0, 0, TPM_DURATION_TYPE_SHORT); |
| if (ret && ret != TPM_BAD_LOCALITY) |
| goto err_exit; |
| |
| return 0; |
| |
| err_exit: |
| dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); |
| |
| tpm_set_failure(); |
| return -1; |
| } |
| |
| static int |
| tpm20_startup(void) |
| { |
| tpm20_set_timeouts(); |
| |
| int ret = tpm_simple_cmd(0, TPM2_CC_Startup, |
| 2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_Startup(SU_CLEAR) = 0x%08x\n", |
| ret); |
| |
| if (CONFIG_COREBOOT && ret == TPM2_RC_INITIALIZE) |
| /* with other firmware on the system the TPM may already have been |
| * initialized |
| */ |
| ret = 0; |
| |
| if (ret) |
| goto err_exit; |
| |
| ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, |
| 1, TPM2_YES, TPM_DURATION_TYPE_LONG); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_SelfTest = 0x%08x\n", |
| ret); |
| |
| if (ret) |
| goto err_exit; |
| |
| ret = tpm20_get_pcrbanks(); |
| if (ret) |
| goto err_exit; |
| |
| ret = tpm20_write_EfiSpecIdEventStruct(); |
| if (ret) |
| goto err_exit; |
| |
| return 0; |
| |
| err_exit: |
| dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); |
| |
| tpm_set_failure(); |
| return -1; |
| } |
| |
| static int |
| tpm_startup(void) |
| { |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| return tpm12_startup(); |
| case TPM_VERSION_2: |
| return tpm20_startup(); |
| } |
| return -1; |
| } |
| |
| void |
| tpm_setup(void) |
| { |
| if (!CONFIG_TCGBIOS) |
| return; |
| |
| int ret = tpm_tpm2_probe(); |
| if (ret) { |
| ret = tpm_tcpa_probe(); |
| if (ret) |
| return; |
| } |
| |
| TPM_version = tpmhw_probe(); |
| if (TPM_version == TPM_VERSION_NONE) |
| return; |
| |
| dprintf(DEBUG_tcg, |
| "TCGBIOS: Detected a TPM %s.\n", |
| (TPM_version == TPM_VERSION_1_2) ? "1.2" : "2"); |
| |
| TPM_working = 1; |
| |
| if (runningOnXen()) |
| return; |
| |
| ret = tpm_startup(); |
| if (ret) |
| return; |
| |
| tpm_smbios_measure(); |
| tpm_add_action(2, "Start Option ROM Scan"); |
| } |
| |
| static void |
| tpm20_prepboot(void) |
| { |
| int ret = tpm20_stirrandom(); |
| if (ret) |
| goto err_exit; |
| |
| u8 auth[20]; |
| ret = tpm20_getrandom(&auth[0], sizeof(auth)); |
| if (ret) |
| goto err_exit; |
| |
| ret = tpm20_hierarchychangeauth(auth); |
| if (ret) |
| goto err_exit; |
| |
| return; |
| |
| err_exit: |
| dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); |
| |
| tpm_set_failure(); |
| } |
| |
| void |
| tpm_prepboot(void) |
| { |
| if (!CONFIG_TCGBIOS) |
| return; |
| |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| if (TPM_has_physical_presence) |
| tpm_simple_cmd(0, TPM_ORD_PhysicalPresence, |
| 2, TPM_PP_NOT_PRESENT_LOCK, TPM_DURATION_TYPE_SHORT); |
| break; |
| case TPM_VERSION_2: |
| tpm20_prepboot(); |
| break; |
| } |
| |
| tpm_add_action(4, "Calling INT 19h"); |
| tpm_add_event_separators(); |
| } |
| |
| /* |
| * Add measurement to the log about an option rom |
| */ |
| void |
| tpm_option_rom(const void *addr, u32 len) |
| { |
| if (!tpm_is_working()) |
| return; |
| |
| struct pcctes_romex pcctes = { |
| .eventid = 7, |
| .eventdatasize = sizeof(u16) + sizeof(u16) + SHA1_BUFSIZE, |
| }; |
| sha1((const u8 *)addr, len, pcctes.digest); |
| tpm_add_measurement_to_log(2, |
| EV_EVENT_TAG, |
| (const char *)&pcctes, sizeof(pcctes), |
| (u8 *)&pcctes, sizeof(pcctes)); |
| } |
| |
| void |
| tpm_add_bcv(u32 bootdrv, const u8 *addr, u32 length) |
| { |
| if (!tpm_is_working()) |
| return; |
| |
| if (length < 0x200) |
| return; |
| |
| const char *string = "Booting BCV device 00h (Floppy)"; |
| if (bootdrv == 0x80) |
| string = "Booting BCV device 80h (HDD)"; |
| tpm_add_action(4, string); |
| |
| /* specs: see section 'Hard Disk Device or Hard Disk-Like Devices' */ |
| /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ |
| string = "MBR"; |
| tpm_add_measurement_to_log(4, EV_IPL, |
| string, strlen(string), |
| addr, 0x1b8); |
| |
| /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ |
| string = "MBR PARTITION_TABLE"; |
| tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, |
| string, strlen(string), |
| addr + 0x1b8, 0x48); |
| } |
| |
| void |
| tpm_add_cdrom(u32 bootdrv, const u8 *addr, u32 length) |
| { |
| if (!tpm_is_working()) |
| return; |
| |
| tpm_add_action(4, "Booting from CD ROM device"); |
| |
| /* specs: see section 'El Torito' */ |
| const char *string = "EL TORITO IPL"; |
| tpm_add_measurement_to_log(4, EV_IPL, |
| string, strlen(string), |
| addr, length); |
| } |
| |
| void |
| tpm_add_cdrom_catalog(const u8 *addr, u32 length) |
| { |
| if (!tpm_is_working()) |
| return; |
| |
| tpm_add_action(4, "Booting from CD ROM device"); |
| |
| /* specs: see section 'El Torito' */ |
| const char *string = "BOOT CATALOG"; |
| tpm_add_measurement_to_log(5, EV_IPL_PARTITION_DATA, |
| string, strlen(string), |
| addr, length); |
| } |
| |
| void |
| tpm_s3_resume(void) |
| { |
| if (!tpm_is_working()) |
| return; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n"); |
| |
| int ret = -1; |
| |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| ret = tpm_simple_cmd(0, TPM_ORD_Startup, |
| 2, TPM_ST_STATE, TPM_DURATION_TYPE_SHORT); |
| break; |
| case TPM_VERSION_2: |
| ret = tpm_simple_cmd(0, TPM2_CC_Startup, |
| 2, TPM2_SU_STATE, TPM_DURATION_TYPE_SHORT); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_Startup(SU_STATE) = 0x%08x\n", |
| ret); |
| |
| if (ret) |
| goto err_exit; |
| |
| |
| ret = tpm_simple_cmd(0, TPM2_CC_SelfTest, |
| 1, TPM2_YES, TPM_DURATION_TYPE_LONG); |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_SelfTest() = 0x%08x\n", |
| ret); |
| |
| break; |
| } |
| |
| if (ret) |
| goto err_exit; |
| |
| return; |
| |
| err_exit: |
| dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); |
| |
| tpm_set_failure(); |
| } |
| |
| |
| /**************************************************************** |
| * BIOS interface |
| ****************************************************************/ |
| |
| u8 TPM_interface_shutdown VARLOW; |
| |
| static inline void *input_buf32(struct bregs *regs) |
| { |
| return MAKE_FLATPTR(regs->es, regs->di); |
| } |
| |
| static inline void *output_buf32(struct bregs *regs) |
| { |
| return MAKE_FLATPTR(regs->ds, regs->si); |
| } |
| |
| static u32 |
| hash_log_extend(struct pcpes *pcpes, const void *hashdata, u32 hashdata_length |
| , void *event, int extend) |
| { |
| if (pcpes->pcrindex >= 24) |
| return TCG_INVALID_INPUT_PARA; |
| if (hashdata) |
| sha1(hashdata, hashdata_length, pcpes->digest); |
| |
| struct tpm_log_entry le = { |
| .hdr.pcrindex = pcpes->pcrindex, |
| .hdr.eventtype = pcpes->eventtype, |
| }; |
| int digest_len = tpm_build_digest(&le, pcpes->digest, 1); |
| if (digest_len < 0) |
| return TCG_GENERAL_ERROR; |
| if (extend) { |
| int ret = tpm_extend(&le, digest_len); |
| if (ret) |
| return TCG_TCG_COMMAND_ERROR; |
| } |
| tpm_build_digest(&le, pcpes->digest, 0); |
| int ret = tpm_log_event(&le.hdr, digest_len |
| , pcpes->event, pcpes->eventdatasize); |
| if (ret) |
| return TCG_PC_LOGOVERFLOW; |
| return 0; |
| } |
| |
| static u32 |
| hash_log_extend_event_int(const struct hleei_short *hleei_s, |
| struct hleeo *hleeo) |
| { |
| u32 rc = 0; |
| struct hleei_long *hleei_l = (struct hleei_long *)hleei_s; |
| const void *logdataptr; |
| u32 logdatalen; |
| struct pcpes *pcpes; |
| u32 pcrindex; |
| |
| /* short or long version? */ |
| switch (hleei_s->ipblength) { |
| case sizeof(struct hleei_short): |
| /* short */ |
| logdataptr = hleei_s->logdataptr; |
| logdatalen = hleei_s->logdatalen; |
| pcrindex = hleei_s->pcrindex; |
| break; |
| |
| case sizeof(struct hleei_long): |
| /* long */ |
| logdataptr = hleei_l->logdataptr; |
| logdatalen = hleei_l->logdatalen; |
| pcrindex = hleei_l->pcrindex; |
| break; |
| |
| default: |
| /* bad input block */ |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| |
| pcpes = (struct pcpes *)logdataptr; |
| |
| if (pcpes->pcrindex != pcrindex |
| || logdatalen != sizeof(*pcpes) + pcpes->eventdatasize) { |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| rc = hash_log_extend(pcpes, hleei_s->hashdataptr, hleei_s->hashdatalen |
| , pcpes->event, 1); |
| if (rc) |
| goto err_exit; |
| |
| hleeo->opblength = sizeof(struct hleeo); |
| hleeo->reserved = 0; |
| hleeo->eventnumber = tpm_state.entry_count; |
| memcpy(hleeo->digest, pcpes->digest, sizeof(hleeo->digest)); |
| |
| err_exit: |
| if (rc != 0) { |
| hleeo->opblength = 4; |
| hleeo->reserved = 0; |
| } |
| |
| return rc; |
| } |
| |
| static u32 |
| pass_through_to_tpm_int(struct pttti *pttti, struct pttto *pttto) |
| { |
| u32 rc = 0; |
| struct tpm_req_header *trh = (void*)pttti->tpmopin; |
| |
| if (pttti->ipblength < sizeof(struct pttti) + sizeof(*trh) |
| || pttti->ipblength != sizeof(struct pttti) + be32_to_cpu(trh->totlen) |
| || pttti->opblength < sizeof(struct pttto)) { |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| |
| u16 tag = be16_to_cpu(trh->tag); |
| |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| if (tag != TPM_TAG_RQU_CMD && tag != TPM_TAG_RQU_AUTH1_CMD |
| && tag != TPM_TAG_RQU_AUTH2_CMD) { |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| break; |
| case TPM_VERSION_2: |
| if (tag != TPM2_ST_NO_SESSIONS && tag != TPM2_ST_SESSIONS) { |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| } |
| |
| u32 resbuflen = pttti->opblength - offsetof(struct pttto, tpmopout); |
| int ret = tpmhw_transmit(0, trh, pttto->tpmopout, &resbuflen, |
| TPM_DURATION_TYPE_LONG /* worst case */); |
| if (ret) { |
| rc = TCG_FATAL_COM_ERROR; |
| goto err_exit; |
| } |
| |
| pttto->opblength = offsetof(struct pttto, tpmopout) + resbuflen; |
| pttto->reserved = 0; |
| |
| err_exit: |
| if (rc != 0) { |
| pttto->opblength = 4; |
| pttto->reserved = 0; |
| } |
| |
| return rc; |
| } |
| |
| static u32 |
| shutdown_preboot_interface(void) |
| { |
| TPM_interface_shutdown = 1; |
| return 0; |
| } |
| |
| static u32 |
| hash_log_event_int(const struct hlei *hlei, struct hleo *hleo) |
| { |
| u32 rc = 0; |
| u16 size; |
| struct pcpes *pcpes; |
| |
| size = hlei->ipblength; |
| if (size != sizeof(*hlei)) { |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| |
| pcpes = (struct pcpes *)hlei->logdataptr; |
| |
| if (pcpes->pcrindex != hlei->pcrindex |
| || pcpes->eventtype != hlei->logeventtype |
| || hlei->logdatalen != sizeof(*pcpes) + pcpes->eventdatasize) { |
| rc = TCG_INVALID_INPUT_PARA; |
| goto err_exit; |
| } |
| rc = hash_log_extend(pcpes, hlei->hashdataptr, hlei->hashdatalen |
| , pcpes->event, 0); |
| if (rc) |
| goto err_exit; |
| |
| /* updating the log was fine */ |
| hleo->opblength = sizeof(struct hleo); |
| hleo->reserved = 0; |
| hleo->eventnumber = tpm_state.entry_count; |
| |
| err_exit: |
| if (rc != 0) { |
| hleo->opblength = 2; |
| hleo->reserved = 0; |
| } |
| |
| return rc; |
| } |
| |
| static u32 |
| hash_all_int(const struct hai *hai, u8 *hash) |
| { |
| if (hai->ipblength != sizeof(struct hai) || |
| hai->hashdataptr == 0 || |
| hai->hashdatalen == 0 || |
| hai->algorithmid != TPM_ALG_SHA) |
| return TCG_INVALID_INPUT_PARA; |
| |
| sha1((const u8 *)hai->hashdataptr, hai->hashdatalen, hash); |
| return 0; |
| } |
| |
| static u32 |
| tss_int(struct ti *ti, struct to *to) |
| { |
| to->opblength = sizeof(struct to); |
| to->reserved = 0; |
| |
| return TCG_PC_UNSUPPORTED; |
| } |
| |
| static u32 |
| compact_hash_log_extend_event_int(u8 *buffer, |
| u32 info, |
| u32 length, |
| u32 pcrindex, |
| u32 *edx_ptr) |
| { |
| struct pcpes pcpes = { |
| .pcrindex = pcrindex, |
| .eventtype = EV_COMPACT_HASH, |
| .eventdatasize = sizeof(info), |
| }; |
| u32 rc = hash_log_extend(&pcpes, buffer, length, &info, 1); |
| if (rc) |
| return rc; |
| |
| *edx_ptr = tpm_state.entry_count; |
| return 0; |
| } |
| |
| void VISIBLE32FLAT |
| tpm_interrupt_handler32(struct bregs *regs) |
| { |
| if (!CONFIG_TCGBIOS) |
| return; |
| |
| set_cf(regs, 0); |
| |
| if (TPM_interface_shutdown && regs->al) { |
| regs->eax = TCG_INTERFACE_SHUTDOWN; |
| return; |
| } |
| |
| switch ((enum irq_ids)regs->al) { |
| case TCG_StatusCheck: |
| if (!tpmhw_is_present()) { |
| /* no TPM available */ |
| regs->eax = TCG_PC_TPM_NOT_PRESENT; |
| } else { |
| regs->eax = 0; |
| regs->ebx = TCG_MAGIC; |
| regs->ch = TCG_VERSION_MAJOR; |
| regs->cl = TCG_VERSION_MINOR; |
| regs->edx = 0x0; |
| regs->esi = (u32)tpm_state.log_area_start_address; |
| regs->edi = (u32)tpm_state.log_area_last_entry; |
| } |
| break; |
| |
| case TCG_HashLogExtendEvent: |
| regs->eax = |
| hash_log_extend_event_int( |
| (struct hleei_short *)input_buf32(regs), |
| (struct hleeo *)output_buf32(regs)); |
| break; |
| |
| case TCG_PassThroughToTPM: |
| regs->eax = |
| pass_through_to_tpm_int((struct pttti *)input_buf32(regs), |
| (struct pttto *)output_buf32(regs)); |
| break; |
| |
| case TCG_ShutdownPreBootInterface: |
| regs->eax = shutdown_preboot_interface(); |
| break; |
| |
| case TCG_HashLogEvent: |
| regs->eax = hash_log_event_int((struct hlei*)input_buf32(regs), |
| (struct hleo*)output_buf32(regs)); |
| break; |
| |
| case TCG_HashAll: |
| regs->eax = |
| hash_all_int((struct hai*)input_buf32(regs), |
| (u8 *)output_buf32(regs)); |
| break; |
| |
| case TCG_TSS: |
| regs->eax = tss_int((struct ti*)input_buf32(regs), |
| (struct to*)output_buf32(regs)); |
| break; |
| |
| case TCG_CompactHashLogExtendEvent: |
| regs->eax = |
| compact_hash_log_extend_event_int((u8 *)input_buf32(regs), |
| regs->esi, |
| regs->ecx, |
| regs->edx, |
| ®s->edx); |
| break; |
| |
| default: |
| set_cf(regs, 1); |
| } |
| |
| return; |
| } |
| |
| |
| /**************************************************************** |
| * TPM Configuration Menu |
| ****************************************************************/ |
| |
| typedef u8 tpm_ppi_code; |
| |
| static int |
| tpm12_read_has_owner(int *has_owner) |
| { |
| struct tpm_res_getcap_ownerauth oauth; |
| int ret = tpm12_get_capability(TPM_CAP_PROPERTY, TPM_CAP_PROP_OWNER |
| , &oauth.hdr, sizeof(oauth)); |
| if (ret) |
| return -1; |
| |
| *has_owner = oauth.flag; |
| |
| return 0; |
| } |
| |
| static int |
| tpm12_enable_tpm(int enable, int verbose) |
| { |
| struct tpm_permanent_flags pf; |
| int ret = tpm12_read_permanent_flags((char *)&pf, sizeof(pf)); |
| if (ret) |
| return -1; |
| |
| if (pf.flags[PERM_FLAG_IDX_DISABLE] && !enable) |
| return 0; |
| |
| ret = tpm_simple_cmd(0, enable ? TPM_ORD_PhysicalEnable |
| : TPM_ORD_PhysicalDisable, |
| 0, 0, TPM_DURATION_TYPE_SHORT); |
| if (ret) { |
| if (enable) |
| dprintf(DEBUG_tcg, "TCGBIOS: Enabling the TPM failed.\n"); |
| else |
| dprintf(DEBUG_tcg, "TCGBIOS: Disabling the TPM failed.\n"); |
| } |
| return ret; |
| } |
| |
| static int |
| tpm12_activate_tpm(int activate, int allow_reset, int verbose) |
| { |
| struct tpm_permanent_flags pf; |
| int ret = tpm12_read_permanent_flags((char *)&pf, sizeof(pf)); |
| if (ret) |
| return -1; |
| |
| if (pf.flags[PERM_FLAG_IDX_DEACTIVATED] && !activate) |
| return 0; |
| |
| if (pf.flags[PERM_FLAG_IDX_DISABLE]) |
| return 0; |
| |
| ret = tpm_simple_cmd(0, TPM_ORD_PhysicalSetDeactivated, |
| 1, activate ? 0x00 : 0x01, TPM_DURATION_TYPE_SHORT); |
| if (ret) |
| return ret; |
| |
| if (activate && allow_reset) { |
| if (verbose) { |
| printf("Requiring a reboot to activate the TPM.\n"); |
| |
| msleep(2000); |
| } |
| reset(); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| tpm12_enable_activate(int allow_reset, int verbose) |
| { |
| int ret = tpm12_enable_tpm(1, verbose); |
| if (ret) |
| return ret; |
| |
| return tpm12_activate_tpm(1, allow_reset, verbose); |
| } |
| |
| static int |
| tpm12_force_clear(int enable_activate_before, int enable_activate_after, |
| int verbose) |
| { |
| int has_owner; |
| int ret = tpm12_read_has_owner(&has_owner); |
| if (ret) |
| return -1; |
| if (!has_owner) { |
| if (verbose) |
| printf("TPM does not have an owner.\n"); |
| return 0; |
| } |
| |
| if (enable_activate_before) { |
| ret = tpm12_enable_activate(0, verbose); |
| if (ret) { |
| dprintf(DEBUG_tcg, |
| "TCGBIOS: Enabling/activating the TPM failed.\n"); |
| return ret; |
| } |
| } |
| |
| ret = tpm_simple_cmd(0, TPM_ORD_ForceClear, |
| 0, 0, TPM_DURATION_TYPE_SHORT); |
| if (ret) |
| return ret; |
| |
| if (!enable_activate_after) { |
| if (verbose) |
| printf("Owner successfully cleared.\n" |
| "You will need to enable/activate the TPM again.\n\n"); |
| return 0; |
| } |
| |
| return tpm12_enable_activate(1, verbose); |
| } |
| |
| static int |
| tpm12_set_owner_install(int allow, int verbose) |
| { |
| int has_owner; |
| int ret = tpm12_read_has_owner(&has_owner); |
| if (ret) |
| return -1; |
| if (has_owner) { |
| if (verbose) |
| printf("Must first remove owner.\n"); |
| return 0; |
| } |
| |
| struct tpm_permanent_flags pf; |
| ret = tpm12_read_permanent_flags((char *)&pf, sizeof(pf)); |
| if (ret) |
| return -1; |
| |
| if (pf.flags[PERM_FLAG_IDX_DISABLE]) { |
| if (verbose) |
| printf("TPM must first be enable.\n"); |
| return 0; |
| } |
| |
| ret = tpm_simple_cmd(0, TPM_ORD_SetOwnerInstall, |
| 1, allow ? 0x01 : 0x00, TPM_DURATION_TYPE_SHORT); |
| if (ret) |
| return ret; |
| |
| if (verbose) |
| printf("Installation of owner %s.\n", allow ? "enabled" : "disabled"); |
| |
| return 0; |
| } |
| |
| static int |
| tpm12_process_cfg(tpm_ppi_code msgCode, int verbose) |
| { |
| int ret = 0; |
| |
| switch (msgCode) { |
| case TPM_PPI_OP_NOOP: /* no-op */ |
| break; |
| |
| case TPM_PPI_OP_ENABLE: |
| ret = tpm12_enable_tpm(1, verbose); |
| break; |
| |
| case TPM_PPI_OP_DISABLE: |
| ret = tpm12_enable_tpm(0, verbose); |
| break; |
| |
| case TPM_PPI_OP_ACTIVATE: |
| ret = tpm12_activate_tpm(1, 1, verbose); |
| break; |
| |
| case TPM_PPI_OP_DEACTIVATE: |
| ret = tpm12_activate_tpm(0, 1, verbose); |
| break; |
| |
| case TPM_PPI_OP_CLEAR: |
| ret = tpm12_force_clear(1, 0, verbose); |
| break; |
| |
| case TPM_PPI_OP_SET_OWNERINSTALL_TRUE: |
| ret = tpm12_set_owner_install(1, verbose); |
| break; |
| |
| case TPM_PPI_OP_SET_OWNERINSTALL_FALSE: |
| ret = tpm12_set_owner_install(0, verbose); |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (ret) |
| printf("Op %d: An error occurred: 0x%x\n", msgCode, ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_clearcontrol(u8 disable, int verbose) |
| { |
| struct tpm2_req_clearcontrol trc = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(trc)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_ClearControl), |
| .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), |
| .authblocksize = cpu_to_be32(sizeof(trc.authblock)), |
| .authblock = { |
| .handle = cpu_to_be32(TPM2_RS_PW), |
| .noncesize = cpu_to_be16(0), |
| .contsession = TPM2_YES, |
| .pwdsize = cpu_to_be16(0), |
| }, |
| .disable = disable, |
| }; |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &trc.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_SHORT); |
| if (ret || resp_length != sizeof(rsp) || rsp.errcode) |
| ret = -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_ClearControl = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_clear(void) |
| { |
| struct tpm2_req_clear trq = { |
| .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), |
| .hdr.totlen = cpu_to_be32(sizeof(trq)), |
| .hdr.ordinal = cpu_to_be32(TPM2_CC_Clear), |
| .authhandle = cpu_to_be32(TPM2_RH_PLATFORM), |
| .authblocksize = cpu_to_be32(sizeof(trq.authblock)), |
| .authblock = { |
| .handle = cpu_to_be32(TPM2_RS_PW), |
| .noncesize = cpu_to_be16(0), |
| .contsession = TPM2_YES, |
| .pwdsize = cpu_to_be16(0), |
| }, |
| }; |
| struct tpm_rsp_header rsp; |
| u32 resp_length = sizeof(rsp); |
| int ret = tpmhw_transmit(0, &trq.hdr, &rsp, &resp_length, |
| TPM_DURATION_TYPE_MEDIUM); |
| if (ret || resp_length != sizeof(rsp) || rsp.errcode) |
| ret = -1; |
| |
| dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_Clear = 0x%08x\n", |
| ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm20_process_cfg(tpm_ppi_code msgCode, int verbose) |
| { |
| int ret = 0; |
| |
| switch (msgCode) { |
| case TPM_PPI_OP_NOOP: /* no-op */ |
| break; |
| |
| case TPM_PPI_OP_CLEAR: |
| ret = tpm20_clearcontrol(0, verbose); |
| if (!ret) |
| ret = tpm20_clear(); |
| break; |
| } |
| |
| if (ret) |
| printf("Op %d: An error occurred: 0x%x\n", msgCode, ret); |
| |
| return ret; |
| } |
| |
| static int |
| tpm12_get_tpm_state(void) |
| { |
| int state = 0; |
| struct tpm_permanent_flags pf; |
| int has_owner; |
| |
| if (tpm12_read_permanent_flags((char *)&pf, sizeof(pf)) || |
| tpm12_read_has_owner(&has_owner)) |
| return ~0; |
| |
| if (!pf.flags[PERM_FLAG_IDX_DISABLE]) |
| state |= TPM_STATE_ENABLED; |
| |
| if (!pf.flags[PERM_FLAG_IDX_DEACTIVATED]) |
| state |= TPM_STATE_ACTIVE; |
| |
| if (has_owner) { |
| state |= TPM_STATE_OWNED; |
| } else { |
| if (pf.flags[PERM_FLAG_IDX_OWNERSHIP]) |
| state |= TPM_STATE_OWNERINSTALL; |
| } |
| |
| return state; |
| } |
| |
| static void |
| tpm12_show_tpm_menu(int state, int next_scancodes[7]) |
| { |
| int i = 0; |
| |
| printf("\nThe current state of the TPM is:\n"); |
| |
| if (state & TPM_STATE_ENABLED) |
| printf(" Enabled"); |
| else |
| printf(" Disabled"); |
| |
| if (state & TPM_STATE_ACTIVE) |
| printf(" and active\n"); |
| else |
| printf(" and deactivated\n"); |
| |
| if (state & TPM_STATE_OWNED) |
| printf(" Ownership has been taken\n"); |
| else { |
| printf(" Ownership has not been taken\n"); |
| if (state & TPM_STATE_OWNERINSTALL) |
| printf(" A user can take ownership of the TPM\n"); |
| else |
| printf(" Taking ownership of the TPM has been disabled\n"); |
| } |
| |
| if ((state & (TPM_STATE_ENABLED | TPM_STATE_ACTIVE)) != |
| (TPM_STATE_ENABLED | TPM_STATE_ACTIVE)) { |
| printf("\nNote: To make use of all functionality, the TPM must be " |
| "enabled and active.\n"); |
| } |
| |
| printf("\nAvailable options are:\n"); |
| if (state & TPM_STATE_ENABLED) { |
| printf(" d. Disable the TPM\n"); |
| next_scancodes[i++] = 32; |
| |
| if (state & TPM_STATE_ACTIVE) { |
| printf(" v. Deactivate the TPM\n"); |
| next_scancodes[i++] = 47; |
| |
| if (state & TPM_STATE_OWNERINSTALL) { |
| printf(" p. Prevent installation of an owner\n"); |
| next_scancodes[i++] = 25; |
| } else { |
| printf(" s. Allow installation of an owner\n"); |
| next_scancodes[i++] = 31; |
| } |
| } else { |
| printf(" a. Activate the TPM\n"); |
| next_scancodes[i++] = 30; |
| } |
| |
| } else { |
| printf(" e. Enable the TPM\n"); |
| next_scancodes[i++] = 18; |
| } |
| |
| if (state & TPM_STATE_OWNED) { |
| printf(" c. Clear ownership\n"); |
| next_scancodes[i++] = 46; |
| } |
| |
| next_scancodes[i++] = 0; |
| } |
| |
| static void |
| tpm12_menu(void) |
| { |
| int scancode, next_scancodes[7]; |
| tpm_ppi_code msgCode; |
| int state = 0, i; |
| int waitkey; |
| |
| printf("The Trusted Platform Module (TPM) is a hardware device in " |
| "this machine.\n" |
| "It can help verify the integrity of system software.\n\n"); |
| |
| for (;;) { |
| if ((state = tpm12_get_tpm_state()) != ~0) { |
| tpm12_show_tpm_menu(state, next_scancodes); |
| } else { |
| printf("TPM is not working correctly.\n"); |
| return; |
| } |
| |
| printf("\nIf no change is desired or if this menu was reached by " |
| "mistake, press ESC to\n" |
| "reboot the machine.\n"); |
| |
| msgCode = TPM_PPI_OP_NOOP; |
| |
| waitkey = 1; |
| |
| while (waitkey) { |
| while ((scancode = get_keystroke(1000)) == ~0) |
| ; |
| |
| switch (scancode) { |
| case 1: |
| // ESC |
| reset(); |
| break; |
| case 18: /* e. enable */ |
| msgCode = TPM_PPI_OP_ENABLE; |
| break; |
| case 32: /* d. disable */ |
| msgCode = TPM_PPI_OP_DISABLE; |
| break; |
| case 30: /* a. activate */ |
| msgCode = TPM_PPI_OP_ACTIVATE; |
| break; |
| case 47: /* v. deactivate */ |
| msgCode = TPM_PPI_OP_DEACTIVATE; |
| break; |
| case 46: /* c. clear owner */ |
| msgCode = TPM_PPI_OP_CLEAR; |
| break; |
| case 25: /* p. prevent ownerinstall */ |
| msgCode = TPM_PPI_OP_SET_OWNERINSTALL_FALSE; |
| break; |
| case 31: /* s. allow ownerinstall */ |
| msgCode = TPM_PPI_OP_SET_OWNERINSTALL_TRUE; |
| break; |
| default: |
| continue; |
| } |
| |
| /* |
| * Using the next_scancodes array, check whether the |
| * pressed key is currently a valid option. |
| */ |
| for (i = 0; i < sizeof(next_scancodes); i++) { |
| if (next_scancodes[i] == 0) |
| break; |
| |
| if (next_scancodes[i] == scancode) { |
| tpm12_process_cfg(msgCode, 1); |
| waitkey = 0; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| static int |
| tpm20_menu_change_active_pcrbanks(void) |
| { |
| u8 active_banks, suppt_banks; |
| |
| tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks); |
| |
| u8 activate_banks = active_banks; |
| |
| while (1) { |
| u8 hashalg_flag = TPM2_ALG_SHA1_FLAG; |
| u8 i = 0; |
| |
| printf("\nToggle active PCR banks by pressing number key\n\n"); |
| |
| while (hashalg_flag) { |
| u8 flag = hashalg_flag & suppt_banks; |
| const char *hashname = tpm20_hashalg_flag_to_name(flag); |
| |
| i++; |
| if (hashname) { |
| printf(" %d: %s", i, hashname); |
| if (activate_banks & hashalg_flag) |
| printf(" (enabled)"); |
| printf("\n"); |
| } |
| |
| hashalg_flag <<= 1; |
| } |
| printf("\n" |
| "ESC: return to previous menu without changes\n"); |
| if (activate_banks) |
| printf("A : activate selection\n"); |
| |
| u8 flagnum; |
| int show = 0; |
| while (!show) { |
| int scancode = get_keystroke(1000); |
| |
| switch (scancode) { |
| case ~0: |
| continue; |
| case 1: /* ESC */ |
| printf("\n"); |
| return -1; |
| case 2 ... 6: /* keys 1 .. 5 */ |
| flagnum = scancode - 1; |
| if (flagnum > i) |
| continue; |
| if (suppt_banks & (1 << (flagnum - 1))) { |
| activate_banks ^= 1 << (flagnum - 1); |
| show = 1; |
| } |
| break; |
| case 30: /* a */ |
| if (activate_banks) |
| tpm20_activate_pcrbanks(activate_banks); |
| } |
| } |
| } |
| } |
| |
| static void |
| tpm20_menu(void) |
| { |
| int scan_code; |
| tpm_ppi_code msgCode; |
| |
| for (;;) { |
| printf("1. Clear TPM\n"); |
| printf("2. Change active PCR banks\n"); |
| |
| printf("\nIf no change is desired or if this menu was reached by " |
| "mistake, press ESC to\n" |
| "reboot the machine.\n"); |
| |
| msgCode = TPM_PPI_OP_NOOP; |
| |
| while ((scan_code = get_keystroke(1000)) == ~0) |
| ; |
| |
| switch (scan_code) { |
| case 1: |
| // ESC |
| reset(); |
| break; |
| case 2: |
| msgCode = TPM_PPI_OP_CLEAR; |
| break; |
| case 3: |
| tpm20_menu_change_active_pcrbanks(); |
| continue; |
| default: |
| continue; |
| } |
| |
| tpm20_process_cfg(msgCode, 0); |
| } |
| } |
| |
| void |
| tpm_menu(void) |
| { |
| if (!CONFIG_TCGBIOS) |
| return; |
| |
| while (get_keystroke(0) >= 0) |
| ; |
| wait_threads(); |
| |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| tpm12_menu(); |
| break; |
| case TPM_VERSION_2: |
| tpm20_menu(); |
| break; |
| } |
| } |
| |
| int |
| tpm_can_show_menu(void) |
| { |
| switch (TPM_version) { |
| case TPM_VERSION_1_2: |
| return tpm_is_working() && TPM_has_physical_presence; |
| case TPM_VERSION_2: |
| return tpm_is_working(); |
| } |
| return 0; |
| } |