This patch adds an optional test suite (CONFIG_TIS_TEST) for the TIS interface to SeaBIOS. If compiled into the BIOS, it can be invoked through the TPM-specific menu item 8.
1. Enable TPM 2. Disable TPM 3. Activate TPM 4. Deactivate TPM 5. Clear ownership 6. Allow installation of owner 7. Prevent installation of owner 8. TIS test
I would like to see this code become part of the SeaBIOS code base but I understand that a test suite in a BIOS is not the right place... Nevertheless, for testing the TIS emulation in Qemu, I am posting it here. The test suite fills up the available BIOS space from 92.6% at the previous patch to 98.4%.
v4: - adapted tis_test.c to be under LGPLv3
v3: - use if (CONFIG_TIS_TEST) ... where possible, otherwise use #if CONFIG_...
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 3 src/Kconfig | 7 src/tcgbios.c | 35 +- src/tis_test.c | 834 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tis_test.h | 52 +++ 5 files changed, 922 insertions(+), 9 deletions(-)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -19,6 +19,9 @@ #include "smbios.h" // get_smbios_entry_point #include "paravirt.h" // QEMU_CFG_KERNEL_*, QEMU_CFG_INITRD_*
+#if CONFIG_TIS_TEST +#include "tis_test.h" +#endif
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; static const u8 Startup_ST_STATE[2] = { 0x00, TPM_ST_STATE }; @@ -779,6 +782,9 @@ pass_through_to_tpm(struct pttti *pttti, iovec[1].data = NULL; iovec[1].length = 0;
+ if (CONFIG_TIS_TEST) + locty = pttti->reserved; + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen, TPM_DURATION_TYPE_LONG /* worst case */); if (rc) @@ -1889,26 +1895,29 @@ err_exit: }
-static void +static int show_tpm_state(void) { + int state = 0; struct tpm_permanent_flags pf; u8 has_owner;
if (read_permanent_flags((char *)&pf, sizeof(pf)) || read_has_owner(&has_owner)) - return; + return ~0;
printf("TPM is ");
- if (pf.flags[PERM_FLAG_IDX_DISABLE]) + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { printf("disabled"); - else + state |= 1 << PERM_FLAG_IDX_DISABLE; + } else printf("enabled");
- if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) + if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) { printf(", deactivated"); - else + state |= 1 << PERM_FLAG_IDX_DEACTIVATED; + } else printf(", active");
if (has_owner) @@ -1921,6 +1930,7 @@ show_tpm_state(void) printf("and an owner cannot be installed.\n"); }
+ return state; }
@@ -1979,7 +1989,7 @@ tcpa_menu(void) return;
int show_menu = 1; - int scan_code; + int scan_code, state = 0; u32 rc; tpm_bios_cfg_t cfg = { .op = 0, @@ -1998,9 +2008,12 @@ tcpa_menu(void) "5. Clear ownership\n" "6. Allow installation of owner\n" "7. Prevent installation of owner\n" +#if CONFIG_TIS_TEST + "8. TIS test\n" +#endif "Escape for previous menu.\n"); show_menu = 0; - show_tpm_state(); + state = show_tpm_state(); }
cfg.op = 0; @@ -2014,6 +2027,12 @@ tcpa_menu(void) case 2 ... 8: cfg.op = scan_code - 1; break; +#if CONFIG_TIS_TEST + case 9: + tis_test(state); +#else + (void)state; +#endif default: continue; } Index: seabios/src/tis_test.c =================================================================== --- /dev/null +++ seabios/src/tis_test.c @@ -0,0 +1,834 @@ +// +// TIS interface tests +// +// Copyright (C) 2006-2011 IBM Corporation +// Copyright (C) 2006-2011 Stefan Berger stefanb@us.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "config.h" + +#include "types.h" +#include "util.h" /* read{b,l}, write{b,l} */ +#include "tcgbios.h" +#include "tpm_drivers.h" +#include "tis_test.h" + + +static int +tis_check_reg(u32 reg, u8 locty, u32 mask, u32 exp, u32 timeout) +{ + u32 val; + u32 now = 0; + + while (now <= timeout) { + val = readl(TIS_REG(locty, reg)); + + if ((val & mask) == exp) + return 0; + + msleep(10); + + now += 10; + } + + return 1; +} + +static int +tis_check_status(u8 locty, u32 mask, u32 exp, u32 timeout) +{ + return tis_check_reg(TIS_REG_STS, locty, mask, exp, timeout); +} + +static int +tis_check_access(u8 locty, u32 mask, u32 exp, u32 timeout) +{ + return tis_check_reg(TIS_REG_ACCESS, locty, mask, exp, timeout); +} + +static void +wait_for_keystroke(void) +{ + printf("Press escape to continue.\n"); + while (1) { + switch (get_keystroke(1000)) { + case 1: + return; + } + } +} + +static void +check_access(u8 locty, u8 mask, u8 exp, + const char *succ_msg, + const char *fail_msg) +{ + u8 acc = readb(TIS_REG(locty, TIS_REG_ACCESS)); + u8 res = acc & mask; + + if (res == exp) + printf("%s -- access reg : %02x\n", succ_msg, acc); + else { + printf("ERROR: %s\n" + "value = %02x, mask = %02x, exp = %02x, actual = %02x, " + "bad = %02x\n", + fail_msg, acc, mask, exp, res, res ^ exp ); + wait_for_keystroke(); + } +} + +static void +tis_activate_locality(u8 new_locty) +{ + int locty; + + /* release locality in use top-downwards */ + for (locty = 4; locty >= 0; locty--) + writeb(TIS_REG(locty, TIS_REG_ACCESS), + TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_BEEN_SEIZED); + + printf("Requesting access to locality %d\n", new_locty); + + writeb(TIS_REG(new_locty, TIS_REG_ACCESS), + TIS_ACCESS_REQUEST_USE); + + check_access(new_locty, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Switch to the locality", + "Could not get the locality"); +} + +/* + * Test getting access to localities using the + * TIS_ACCESS_ACTIVATE_LOCALITY flag + * The test ends with locality 0 being active. + */ +static void +test_active_locality_flag(void) +{ + printf("Requesting access to locality 0\n"); + + writeb(TIS_REG(0, TIS_REG_ACCESS), + TIS_ACCESS_REQUEST_USE|TIS_ACCESS_BEEN_SEIZED); + + check_access(0, + TIS_ACCESS_TPM_ESTABLISHMENT, + TIS_ACCESS_TPM_ESTABLISHMENT, + "Valid TPM Establishment flag in locality 0", + "Invalid TPM Establishment flag in locality 0"); + + check_access(0, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY | TIS_ACCESS_BEEN_SEIZED, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 0", + "Could not get locality 0"); + + printf("Giving up locality 0\n"); + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + check_access(0, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80, + "Successfully released locality 0", + "Still have locality 0"); + + printf("Requesting access to locality 1\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 1", + "Could not get locality 1"); + + printf("Requesting access to locality 2; seizing locality.\n"); + + writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + check_access(1, + 0x80 | TIS_ACCESS_BEEN_SEIZED | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_BEEN_SEIZED, + "Access to locality 1 has been seized", + "Access to locality 1 has not been properly seized"); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 2", + "Could not get locality 2"); + + printf("Setting request to use by locality 1\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY| TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY| TIS_ACCESS_PENDING_REQUEST, + "Locality 2 sees pending request by locality 1", + "Locality 2 does not see pending request by locality 1"); + + check_access(1, + 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_REQUEST_USE, + "Locality 1 has request pending", + "Locality 1 does not have request pending"); + + printf("Setting request to use by locality 0\n"); + + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(0, + 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_REQUEST_USE, + "Locality 0 has request pending", + "Locality 0 does not have request pending"); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 2 sees pending request by locality 1", + "Locality 2 does not see pending request by locality 1"); + + printf("Setting request to use by locality 3\n"); + + writeb(TIS_REG(3, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(3, + 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_REQUEST_USE, + "Locality 3 has request pending", + "Locality 3 does not have request pending"); + + check_access(2, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 2 sees pending request by localities 0, 1 & 3", + "Locality 2 does not see pending request by localities " + "0, 1 & 3"); + + printf("Releasing use by locality 2\n"); + + writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + check_access(2, + 0x80 | TIS_ACCESS_PENDING_REQUEST|TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_PENDING_REQUEST, + "Locality 2 successfully relased usage", + "Locality 2 did not completely release usage"); + + check_access(3, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 3 is active and sees pending request by localities " + "0 & 1", + "Locality 3 does not see proper flags"); + + check_access(0, + 0x80|TIS_ACCESS_REQUEST_USE|TIS_ACCESS_PENDING_REQUEST| + TIS_ACCESS_ACTIVE_LOCALITY, + 0x80|TIS_ACCESS_REQUEST_USE|TIS_ACCESS_PENDING_REQUEST, + "Locality 0 has request pending and sees other pending request", + "Locality 0 does not have request pending"); + + printf("Locality 1 drops request\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + printf("Releasing use by locality 3\n"); + + writeb(TIS_REG(3, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + check_access(3, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80, + "Locality 3 successfully relased usage", + "Locality 3 did not completely release usage"); + + check_access(1, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80, + "Locality 1 successfully relased usage", + "Locality 1 did not completely release usage"); + + check_access(0, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY, + "Locality 0 is active", + "Locality 0 is not avtive"); +} + +/* + * Test the seize flag. It is assumed that locality 0 is active. + * The test ends with locality 2 being active + */ +static void +test_seize_locality_flag(void) +{ + printf("Requesting use by locality 1 using the SEIZE bit\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + check_access(0, + 0x80 | TIS_ACCESS_BEEN_SEIZED|TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_BEEN_SEIZED, + "Access to locality 0 has been seized", + "Access to locality 0 has not been properly seized"); + + check_access(1, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Locality 1 successfully seized usage", + "Locality 1 did not seize usage"); + + printf("Requesting use by locality 2 using the SEIZE bit\n"); + + writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + check_access(1, + 0x80 | TIS_ACCESS_BEEN_SEIZED | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_BEEN_SEIZED, + "Access to locality 1 has been seized", + "Access to locality 1 has not been properly seized"); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Locality 2 successfully seized usage", + "Locality 1 did not seize usage"); +} + +#define ENABLED(L4,L3,L2,L1,L0) \ + ((L4) << 4 | (L3) << 3 | (L2) << 2 | (L1) << 1 | (L0)) + +static const u8 pcr_resets[] = { + ENABLED(1,1,1,1,1), /* PCR 16 */ + ENABLED(1,0,0,0,0), + ENABLED(1,0,0,0,0), + ENABLED(1,0,0,0,0), + ENABLED(1,0,1,0,0), + ENABLED(0,0,1,0,0), + ENABLED(0,0,1,0,0), + ENABLED(1,1,1,1,1), +}; + +static const u8 pcr_extends[] = { + ENABLED(1,1,1,1,1), /* PCR 16 */ + ENABLED(1,1,1,0,0), + ENABLED(1,1,1,0,0), + ENABLED(0,1,1,0,0), + ENABLED(0,1,1,1,0), + ENABLED(0,0,1,0,0), + ENABLED(0,0,1,0,0), + ENABLED(1,1,1,1,1), +}; + + +static u32 +do_pcr_read(u32 pcrindex, u8 *result, u8 locty) +{ + u32 rc; + + struct pttto_pcrread pttto; + struct pttti_pcrread pttti = { + .pttti = { + .reserved = locty, + .ipblength = sizeof(struct pttti_pcrread), + .opblength = sizeof(struct pttto_pcrread), + }, + .req = { + .tag = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_PcrRead), + .pcrindex = htonl(pcrindex), + }, + }; + struct bregs regs; + regs.es = FLATPTR_TO_SEG (&pttti); + regs.edi = FLATPTR_TO_OFFSET(&pttti); + regs.ds = FLATPTR_TO_SEG (&pttto); + regs.esi = FLATPTR_TO_OFFSET(&pttto); + regs.eax = TCG_PassThroughToTPM; + + tcpa_interrupt_handler32(®s); + + rc = regs.eax; + + if (rc == 0) { + if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + if (rc == 0) + memcpy(result, pttto.rsp.digest, sizeof(pttto.rsp.digest)); + + return rc; +} + + +static u32 +do_pcr_extend(u8 *hash, u32 pcrindex, u8 locty) +{ + u32 rc; + + struct pttto_extend pttto; + struct pttti_extend pttti = { + .pttti = { + .reserved = locty, + .ipblength = sizeof(struct pttti_extend), + .opblength = sizeof(struct pttto_extend), + }, + .req = { + .tag = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_Extend), + .pcrindex = htonl(pcrindex), + }, + }; + struct bregs regs; + regs.es = FLATPTR_TO_SEG(&pttti); + regs.edi = FLATPTR_TO_OFFSET(&pttti); + regs.ds = FLATPTR_TO_SEG(&pttto); + regs.esi = FLATPTR_TO_OFFSET(&pttto); + regs.eax = TCG_PassThroughToTPM; + + memcpy(pttti.req.digest, hash, sizeof(pttti.req.digest)); + + tcpa_interrupt_handler32(®s); + + rc = regs.eax; + + if (rc == 0) { + if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + return rc; +} + + +static u32 +do_pcr_reset(u32 pcrindex, u8 locty) +{ + u32 rc; + + struct pttto_pcrreset pttto; + struct pttti_pcrreset pttti = { + .pttti = { + .reserved = locty, + .ipblength = sizeof(struct pttti_pcrreset), + .opblength = sizeof(struct pttto_pcrreset), + }, + .req = { + .tag = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_PCR_Reset), + .sizeOfSelect = htons(3), + }, + }; + struct bregs regs; + regs.es = FLATPTR_TO_SEG(&pttti); + regs.edi = FLATPTR_TO_OFFSET(&pttti); + regs.ds = FLATPTR_TO_SEG(&pttto); + regs.esi = FLATPTR_TO_OFFSET(&pttto); + regs.eax = TCG_PassThroughToTPM; + + pttti.req.pcrSelect[pcrindex >> 3] = 1 << (pcrindex & 0x7); + + tcpa_interrupt_handler32(®s); + + rc = regs.eax; + + if (rc == 0) { + if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + return rc; +} + + +static void +test_pcr_test(void) +{ + u32 pcr_num, res; + u8 locty; + u8 value_a[20], value_b[20], value_c[20]; + u8 reset_pcr_0s[20] = { /* all PCRs; 17-22 if TOSPresent = true */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + u8 reset_pcr_1s[20] = { /* 17-22 if TOSPresent = false */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + u8 value_ext[20] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + int extended; + int tos_present = !readb(TIS_REG(0, TIS_REG_ACCESS)) & + TIS_ACCESS_TPM_ESTABLISHMENT; + + printf("Testing PCR Extend and Reset\n"); + + for (pcr_num = 16; pcr_num <= 23; pcr_num++) { + + printf("PCR %d: ",pcr_num); + + for (locty = 0; locty <= 4; locty++) { + if ((res = do_pcr_read(pcr_num, value_a, locty))) { + printf("!F0[%d] = 0x%08x", locty, res); + continue; + } + if ((res = do_pcr_extend(value_ext, pcr_num, locty))) { + printf("!F1[%d] = 0x%08x", locty, res); + continue; + } + if ((res = do_pcr_read(pcr_num, value_b, locty))) { + printf("!F2[%d] = 0x%08x", locty, res); + continue; + } + /* did the PCR change? */ + if (memcmp(value_a, value_b, sizeof(value_a)) != 0) { + /* extend worked */ + extended = 1; + if ((pcr_extends[pcr_num-16] & (1 << locty)) == 0) + printf("!F3[%d] ", locty); + else + printf(" P1[%d] ", locty); + } else { + extended = 0; + /* extend did not work */ + if (pcr_extends[pcr_num-16] & (1 << locty)) + printf("!F4[%d] ", locty); + else + printf(" P2[%d] ", locty); + } + + if (!extended) { + /* Need to extend using locality 2; this always works */ + if ((res = do_pcr_extend(value_ext, pcr_num, 2))) { + printf("!F5[%d] = 0x%08x", locty, res); + continue; + } + } + + if ((res = do_pcr_reset(pcr_num, locty))) { + printf("!F6[%d] = 0x%08x", locty, res); + continue; + } + if (do_pcr_read(pcr_num, value_c, locty)) { + printf("!F7[%d] = 0x%08x", locty, res); + continue; + } + /* did reset work? */ + if ((( pcr_num <= 16 || + pcr_num == 23 || + (pcr_num >= 17 && pcr_num <= 22 && tos_present) + ) && + memcmp(value_c, reset_pcr_0s, sizeof(value_c)) == 0 + ) || + ( pcr_num >= 17 && pcr_num <= 22 && !tos_present && + memcmp(value_c, reset_pcr_1s, sizeof(value_c)) == 0 + ) + ) { + /* reset worked */ + if ((pcr_resets[pcr_num-16] & (1 << locty)) == 0) + printf("!F8[%d] ", locty); + else + printf(" P3[%d] ", locty); + } else { + /* reset did not work */ + if ((pcr_resets[pcr_num-16] & (1 << locty))) + printf("!F9[%d] ", locty); + else + printf(" P4[%d] ", locty); + } + } + printf("\n"); + } +} + + +/* send a command to the TPM without waiting for the response; + the caller must have activate the given locality */ +static int +send_command(u8 locty, const unsigned char *command, u32 cmd_length) +{ + u32 c; + u32 burst; + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + if (tis_check_status(locty, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) { + printf("Error: Could not bring the TPM " + "into ready state in locality %d\n", locty); + wait_for_keystroke(); + return 1; + } + + burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8; + + for (c = 0; c < cmd_length && burst > 0; c++) { + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), command[c]); + burst--; + } + + if (c != cmd_length) { + printf("Error: Could not write data into locality %d FIFO\n", locty); + wait_for_keystroke(); + return 1; + } + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_TPM_GO); + + return 0; +} + + +unsigned char pcr_read_cmd[] = { + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x00, 0x0a +}; + +static void +test_cmd_aborts(void) +{ + printf("Testing command aborts\n"); + + /* abort using COMMAND_READY flag */ + + tis_activate_locality(0); + + if (send_command(0, pcr_read_cmd, sizeof(pcr_read_cmd))) { + printf("Error: Sending command did not work.\n"); + wait_for_keystroke(); + } else { + writeb(TIS_REG(0, TIS_REG_STS), TIS_STS_COMMAND_READY); + + if (tis_check_status(0, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, + 1000)) { + printf("After abort, locality %d did not become ready in time.\n", + 0); + } + } + + /* abort using the Seize flag by a higher locality */ + + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_BEEN_SEIZED); + + if (send_command(0, pcr_read_cmd, sizeof(pcr_read_cmd))) { + printf("Error: Sending command did not work.\n"); + wait_for_keystroke(); + } else { + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + if (tis_check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + 1000)) { + printf("Error: Aborting of locality %d command by locality %d " + "using the SEIZE flag did not work.\n", + 0, 1); + wait_for_keystroke(); + } else { + check_access(0, + TIS_ACCESS_BEEN_SEIZED|TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_BEEN_SEIZED, + "Access to locality 0 has been seized", + "Access to locality 0 has not been properly seized"); + + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 1", + "Could not get locality 1"); + } + } + + /* abort using ACTIVE_LOCALITY flag */ + tis_activate_locality(1); + + /* put in request by locality 0 */ + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 1 sees pending request", + "Locality 1 does not see pending request"); + + if (send_command(1, pcr_read_cmd, sizeof(pcr_read_cmd))) { + printf("Error: Sending command did not work.\n"); + wait_for_keystroke(); + } else { + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + if (tis_check_access(0, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + 1000)) { + printf("Error: Aborting of locality %d command " + "using the ACTIVE_LOCALITY flag did not work.\n", + 1); + printf("access[0] : %02x\n", readb(TIS_REG(0, TIS_REG_ACCESS))); + printf("access[1] : %02x\n", readb(TIS_REG(1, TIS_REG_ACCESS))); + wait_for_keystroke(); + } else { + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + 0, + "Access to locality 1 relinquished", + "Access to locality 1 not relinquished"); + + check_access(0, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 0", + "Could not get locality 0"); + } + } +} + + +static int +test_various(u8 locty) +{ + int i; + + tis_activate_locality(locty); + + printf("Putting locality %d in ready state.\n", locty); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + if (tis_check_status(locty, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) { + printf("Error: Could not bring the TPM " + "into ready state in locality %d\n",locty); + wait_for_keystroke(); + return 1; + } + + printf("Reading data from FIFO. Expecting 0xff.\n"); + + if (readb(TIS_REG(locty, TIS_REG_DATA_FIFO)) != 0xff) { + printf("Error: Expected to 0xff from FIFO\n"); + wait_for_keystroke(); + } + + printf("Writing a byte into the FIFO.\n"); + + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), pcr_read_cmd[0]); + + if (tis_check_status(locty, + TIS_STS_EXPECT, TIS_STS_EXPECT, 0)) { + printf("Error: TIS is not expecting data in locality %d\n",locty); + wait_for_keystroke(); + return 1; + } + + printf("Sending single byte to be processed.\n"); + + /* tpm will process the single byte */ + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_TPM_GO); + + if (tis_check_status(locty, + TIS_STS_EXPECT, 0, 0)) { + printf("Error: TIS is still expecting data in locality %d [2nd]\n", + locty); + wait_for_keystroke(); + return 1; + } + + printf("Checking for result.\n"); + + if (tis_check_status(locty, + TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE, + 1000)) { + printf("Error: No data available in locality %d [2nd]\n", + locty); + wait_for_keystroke(); + return 1; + } + + printf("Putting locality %d in ready state.\n", locty); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + printf("Writing TPM_PcrRead command into FIFO.\n"); + + for (i = 0; i < sizeof(pcr_read_cmd); i++) { + if (i > 0 && tis_check_status(0, TIS_STS_EXPECT, TIS_STS_EXPECT, 0)) { + printf("Error: TIS is not expecting data anymore [%d/%d].\n", + i,sizeof(pcr_read_cmd)-1); + readb(TIS_REG(locty, 0xf90)); + wait_for_keystroke(); + } + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), pcr_read_cmd[i]); + } + + printf("Checking whether the TPM is still expecting data. " + "It should not.\n"); + + if (tis_check_status(locty, + TIS_STS_EXPECT, 0, 0)) { + printf("Error: TIS is still expecting data in locality %d\n", + locty); + wait_for_keystroke(); + return 1; + } + + printf("Sending command to put locality %d into ready state.\n", locty); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + printf("Checking that locality %d is in ready state.\n", locty); + + if (tis_check_status(locty, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) { + printf("Error: Could not bring the TPM " + "into ready state in locality %d [2nd]\n", locty); + wait_for_keystroke(); + return 1; + } + + return 0; +} + + +void +tis_test(int tpm_state) +{ + if (!CONFIG_TIS_TEST) + return; + + test_active_locality_flag(); + + /* locality 0 is active */ + + test_seize_locality_flag(); + + /* locality 2 is active */ + + if (tpm_state == 0) + test_pcr_test(); + + test_cmd_aborts(); + + test_various(0); + + tis_activate_locality(0); + + printf("END OF TEST.\n"); + wait_for_keystroke(); +} Index: seabios/src/tis_test.h =================================================================== --- /dev/null +++ seabios/src/tis_test.h @@ -0,0 +1,52 @@ +#ifndef TIS_TEST +#define TIS_TEST + +#include "tcgbios.h" + +#define TPM_ORD_PCR_Reset 0x000000c8 +#define TPM_ORD_PcrRead 0x00000015 + +void tis_test(int tpm_state); + +struct tpm_req_pcrread { + TPM_REQ_HEADER + u32 pcrindex; +} PACKED; + +struct tpm_rsp_pcrread { + TPM_RSP_HEADER + u8 digest[SHA1_BUFSIZE]; +} PACKED; + +struct pttti_pcrread { + struct pttti pttti; + struct tpm_req_pcrread req; +} PACKED; + +struct pttto_pcrread { + struct pttto pttto; + struct tpm_rsp_pcrread rsp; +} PACKED; + + +struct tpm_req_pcrreset { + TPM_REQ_HEADER + u16 sizeOfSelect; + u8 pcrSelect[3]; +} PACKED; + +struct tpm_rsp_pcrreset { + TPM_RSP_HEADER +} PACKED; + +struct pttti_pcrreset { + struct pttti pttti; + struct tpm_req_pcrreset req; +} PACKED; + +struct pttto_pcrreset { + struct pttto pttto; + struct tpm_rsp_pcrreset rsp; +} PACKED; + +#endif /* TIS_TEST */ Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -312,6 +312,13 @@ menu "BIOS interfaces" below the given size (in kb), otherwise use the internal SHA1 algorithm for calculating the SHA1.
+ config TIS_TEST + depends on TCGBIOS + bool "TPM TIS test" + default n + help + Test cases for the TPM TIS interface + endmenu
menu "BIOS Tables" Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,8 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c \ - biostables.c xen.c bmp.c tcgbios.c tpm_drivers.c sha1.c + biostables.c xen.c bmp.c tcgbios.c tpm_drivers.c sha1.c \ + tis_test.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \