On Fri, 8 May 2015 13:45:47 -0400 Stefan Berger <[email protected]> wrote:
> This patch implements the specification found here: > > http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interface_specification > > It adds the necessary BIOS code so that for example an administrator can send > messages from the OS to the BIOS for the BIOS to change the state of the TPM > upon reboot. With the help of this interface, an administrator does not have > to manually interact with the BIOS. > > As an example, on Linux the root use can send an opcode to the BIOS through > the > TPM's sysfs entries following the opcodes listed in table 2 of the above > specs. To for example disable and deactivate the TPM, the root user would > send opcode '7' to the BIOS: > > #> cd /sys/devices/pnp0/00:0?/ppi > > #> echo 7 > request > > #> reboot > > To exchange data between the OS and SeaBIOS, we use the TIS's vendor > specific extensions in locations 0xf90-fff where RAM locations reside > that are not reset during a machine reboot. This memory is initialized > and two locations receive a 32 bit value 'TCG_MAGIC' that the ACPI code > and SeaBIOS are looking for to accept the memory. If after a reboot the Why do you need to look for TCG_MAGIC? buffer is at the fixed known address so BIOS could just use it, in addition it will save 8 bytes of precious low memory. Also I'd pass buffer address via fwcfg file instead of hardcoding it in QEMU and SeaBIOS. > signature is found by SeaBIOS, the opcode (sent from the OS) is looked > at and acted upon. > > The implementation requires an ACPI _DSM method to be implemented for the > TPM's SSDT. The code in the _DSM will write the administrator's opcode > into the TIS's RAM locations. The _DSM method is invoked when the root > user interacts with the entries shown in the above ppi sysfs directory. > The patch implementing the _DSM will be posted independently. > > This patch supports opcodes 1-11, 14, 21, and 22. > > Signed-off-by: Stefan Berger <[email protected]> > --- > src/hw/tpm_drivers.h | 1 + > src/post.c | 3 + > src/tcgbios.c | 570 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > src/tcgbios.h | 47 +++++ > 4 files changed, 621 insertions(+) > > diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h > index 34bb12d..83e4a62 100644 > --- a/src/hw/tpm_drivers.h > +++ b/src/hw/tpm_drivers.h > @@ -53,6 +53,7 @@ extern struct tpm_driver tpm_drivers[]; > #define TIS_REG_DATA_FIFO 0x24 > #define TIS_REG_DID_VID 0xf00 > #define TIS_REG_RID 0xf04 > +#define TIS_REG_RAM 0xfa0 > > #define TIS_STS_VALID (1 << 7) /* 0x80 */ > #define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ > diff --git a/src/post.c b/src/post.c > index e273c18..4c944c8 100644 > --- a/src/post.c > +++ b/src/post.c > @@ -219,6 +219,7 @@ maininit(void) > > // Setup platform devices. > platform_hardware_setup(); > + tpm_ppi_init(); > > // Start hardware initialization (if threads allowed during optionroms) > if (threads_during_optionroms()) > @@ -240,6 +241,8 @@ maininit(void) > // Run option roms > optionrom_setup(); > > + // Process user-requested TPM state change > + tpm_ppi_process(); > // Allow user to modify overall boot order. > interactive_bootmenu(); > wait_threads(); > diff --git a/src/tcgbios.c b/src/tcgbios.c > index 3f31231..610ed12 100644 > --- a/src/tcgbios.c > +++ b/src/tcgbios.c > @@ -24,6 +24,8 @@ > #include "bregs.h" // struct bregs > #include "sha1.h" // sha1 > #include "std/smbios.h" > +#include "malloc.h" // malloc_* > +#include "fw/paravirt.h" // runningOnQEMU > > > static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; > @@ -42,6 +44,11 @@ static const u8 GetCapability_Permanent_Flags[12] = { > 0x00, 0x00, 0x01, 0x08 > }; > > +static const u8 GetCapability_STClear_Flags[12] = { > + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, > + 0x00, 0x00, 0x01, 0x09 > +}; > + > static const u8 GetCapability_OwnerAuth[12] = { > 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, > 0x00, 0x00, 0x01, 0x11 > @@ -89,6 +96,10 @@ static tpm_state_t tpm_state = { > .tpm_driver_to_use = TPM_INVALID_DRIVER, > }; > > +typedef struct { > + u8 op; > +} tpm_bios_cfg_t; > + > > /******************************************************** > Extensions for TCG-enabled BIOS > @@ -1438,6 +1449,521 @@ tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 > length) > return rc; > } > > +static u32 > +read_stclear_flags(char *buf, int buf_len) > +{ > + u32 rc; > + u32 returnCode; > + struct tpm_res_getcap_stclear_flags stcf; > + > + memset(buf, 0x0, buf_len); > + > + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, > + GetCapability_STClear_Flags, > + sizeof(GetCapability_STClear_Flags), > + (u8 *)&stcf, > + sizeof(struct tpm_res_getcap_stclear_flags), > + &returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " > + "= 0x%08x\n", returnCode); > + > + if (rc || returnCode) > + goto err_exit; > + > + memcpy(buf, &stcf.stclear_flags, buf_len); > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +assert_physical_presence(int verbose) > +{ > + u32 rc = 0; > + u32 returnCode; > + struct tpm_stclear_flags stcf; > + > + rc = read_stclear_flags((char *)&stcf, sizeof(stcf)); > + if (rc) { > + dprintf(DEBUG_tcg, > + "Error reading STClear flags: 0x%08x\n", rc); > + return rc; > + } > + > + if (stcf.flags[STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE]) > + /* physical presence already asserted */ > + return 0; > + > + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, > + PhysicalPresence_CMD_ENABLE, > + sizeof(PhysicalPresence_CMD_ENABLE), > + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, > + "Return code from TSC_PhysicalPresence(CMD_ENABLE) = 0x%08x\n", > + returnCode); > + > + if (rc || returnCode) { > + if (verbose) > + printf("Error: Could not enable physical presence.\n\n"); > + goto err_exit; > + } > + > + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, > + PhysicalPresence_PRESENT, > + sizeof(PhysicalPresence_PRESENT), > + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, > + "Return code from TSC_PhysicalPresence(PRESENT) = 0x%08x\n", > + returnCode); > + > + if (rc || returnCode) { > + if (verbose) > + printf("Error: Could not set presence flag.\n\n"); > + goto err_exit; > + } > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +read_permanent_flags(char *buf, int buf_len) > +{ > + u32 rc; > + u32 returnCode; > + struct tpm_res_getcap_perm_flags pf; > + > + memset(buf, 0x0, buf_len); > + > + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, > + GetCapability_Permanent_Flags, > + sizeof(GetCapability_Permanent_Flags), > + (u8 *)&pf, > + sizeof(struct tpm_res_getcap_perm_flags), > + &returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " > + "= 0x%08x\n", returnCode); > + > + if (rc || returnCode) > + goto err_exit; > + > + memcpy(buf, &pf.perm_flags, buf_len); > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +read_has_owner(u8 *has_owner) > +{ > + u32 rc; > + u32 returnCode; > + struct tpm_res_getcap_ownerauth oauth; > + > + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, > + GetCapability_OwnerAuth, > + sizeof(GetCapability_OwnerAuth), > + (u8 *)&oauth, > + sizeof(struct tpm_res_getcap_ownerauth), > + &returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " > + "= 0x%08x\n", returnCode); > + > + if (rc || returnCode) > + goto err_exit; > + > + *has_owner = oauth.flag; > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +disable_tpm(int disable, u32 *returnCode, int verbose) > +{ > + u32 rc; > + struct tpm_permanent_flags pf; > + > + rc = read_permanent_flags((char *)&pf, sizeof(pf)); > + if (rc) > + return rc; > + > + if (!!pf.flags[PERM_FLAG_IDX_DISABLE] == !!disable) { > + if (verbose) > + printf("TPM is already %s.\n,", > + disable ? "disabled" : "enabled"); > + return 0; > + } > + > + rc = assert_physical_presence(verbose); > + if (rc) { > + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); > + return rc; > + } > + > + rc = build_and_send_cmd(0, disable ? TPM_ORD_PhysicalDisable > + : TPM_ORD_PhysicalEnable, > + NULL, 0, NULL, 10, returnCode, > + TPM_DURATION_TYPE_SHORT); > + dprintf(DEBUG_tcg, "Return code from TPM_Physical%sable = 0x%08x\n", > + disable ? "Dis" : "En", *returnCode); > + > + if (rc || *returnCode) > + goto err_exit; > + > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: %sabling the TPM failed.\n", > + disable ? "Dis" : "En"); > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +deactivate_tpm(int deactivate, int allow_reset, u32 *returnCode, int verbose) > +{ > + u32 rc; > + struct tpm_permanent_flags pf; > + > + rc = read_permanent_flags((char *)&pf, sizeof(pf)); > + if (rc) > + return rc; > + > + if (!!pf.flags[PERM_FLAG_IDX_DEACTIVATED] == !!deactivate) { > + if (verbose) > + printf("TPM is already %s.\n", > + deactivate ? "deactivated" : "activated"); > + return 0; > + } > + > + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { > + if (verbose) > + printf("TPM must first be enabled.\n"); > + return 0; > + } > + > + rc = assert_physical_presence(verbose); > + if (rc) { > + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); > + return rc; > + } > + > + rc = build_and_send_cmd(0, TPM_ORD_PhysicalSetDeactivated, > + deactivate ? CommandFlag_TRUE > + : CommandFlag_FALSE, > + deactivate ? sizeof(CommandFlag_TRUE) > + : sizeof(CommandFlag_FALSE), > + NULL, 10, returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, > + "Return code from PhysicalSetDeactivated(%d) = 0x%08x\n", > + deactivate ? 1 : 0, *returnCode); > + > + if (rc || *returnCode) > + goto err_exit; > + > + if (!deactivate && allow_reset) { > + if (verbose) { > + printf("Requiring a reboot to activate the TPM.\n"); > + > + msleep(2000); > + } > + extern void reset_vector(void) __noreturn; > + reset_vector(); > + } > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +enable_activate(int allow_reset, u32 *returnCode, int verbose) > +{ > + u32 rc; > + > + rc = disable_tpm(0, returnCode, verbose); > + if (rc) > + return rc; > + > + rc = deactivate_tpm(0, allow_reset, returnCode, verbose); > + > + return rc; > +} > + > + > +static u32 > +force_clear(int enable_activate_before, int enable_activate_after, > + u32 *returnCode, int verbose) > +{ > + u32 rc; > + u8 has_owner; > + > + rc = read_has_owner(&has_owner); > + if (rc) > + return rc; > + if (!has_owner) { > + if (verbose) > + printf("TPM does not have an owner.\n"); > + return 0; > + } > + > + if (enable_activate_before) { > + rc = enable_activate(0, returnCode, verbose); > + if (rc) { > + dprintf(DEBUG_tcg, > + "TCGBIOS: Enabling/activating the TPM failed.\n"); > + return rc; > + } > + } > + > + rc = assert_physical_presence(verbose); > + if (rc) { > + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); > + return rc; > + } > + > + rc = build_and_send_cmd(0, TPM_ORD_ForceClear, > + NULL, 0, NULL, 10, returnCode, > + TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, "Return code from TPM_ForceClear() = 0x%08x\n", > + *returnCode); > + > + if (rc || *returnCode) > + goto err_exit; > + > + if (!enable_activate_after) { > + if (verbose) > + printf("Owner successfully cleared.\n" > + "You will need to enable/activate the TPM again.\n\n"); > + return 0; > + } > + > + enable_activate(1, returnCode, verbose); > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +set_owner_install(int allow, u32 *returnCode, int verbose) > +{ > + u32 rc; > + u8 has_owner; > + struct tpm_permanent_flags pf; > + > + rc = read_has_owner(&has_owner); > + if (rc) > + return rc; > + if (has_owner) { > + if (verbose) > + printf("Must first remove owner.\n"); > + return 0; > + } > + > + rc = read_permanent_flags((char *)&pf, sizeof(pf)); > + if (rc) > + return rc; > + > + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { > + if (verbose) > + printf("TPM must first be enable.\n"); > + return 0; > + } > + > + rc = assert_physical_presence(verbose); > + if (rc) { > + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); > + return rc; > + } > + > + rc = build_and_send_cmd(0, TPM_ORD_SetOwnerInstall, > + (allow) ? CommandFlag_TRUE : > + CommandFlag_FALSE, > + sizeof(CommandFlag_TRUE), > + NULL, 10, returnCode, TPM_DURATION_TYPE_SHORT); > + > + dprintf(DEBUG_tcg, "Return code from TPM_SetOwnerInstall() = 0x%08x\n", > + *returnCode); > + > + if (rc || *returnCode) > + goto err_exit; > + > + if (verbose) > + printf("Installation of owner %s.\n", allow ? "enabled" : > "disabled"); > + > + return 0; > + > +err_exit: > + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); > + tpm_state.tpm_working = 0; > + if (rc) > + return rc; > + return TCG_TCG_COMMAND_ERROR; > +} > + > + > +static u32 > +tpm_process_cfg(const tpm_bios_cfg_t *cfg, int verbose, > + u32 *returnCode, u8 *next_step) > +{ > + u32 rc = 0; > + > + switch (cfg->op) { > + case 0: /* no-op */ > + break; > + > + case 1: > + rc = disable_tpm(0, returnCode, verbose); > + break; > + > + case 2: > + rc = disable_tpm(1, returnCode, verbose); > + break; > + > + case 3: > + rc = deactivate_tpm(0, 1, returnCode, verbose); > + break; > + > + case 4: > + rc = deactivate_tpm(1, 1, returnCode, verbose); > + break; > + > + case 5: > + rc = force_clear(1, 0, returnCode, verbose); > + break; > + > + case 6: > + rc = enable_activate(1, returnCode, verbose); > + break; > + > + case 7: > + rc = deactivate_tpm(1, 1, returnCode, verbose); > + if (!rc) > + rc = disable_tpm(1, returnCode, verbose); > + break; > + > + case 8: > + rc = set_owner_install(1, returnCode, verbose); > + break; > + > + case 9: > + rc = set_owner_install(0, returnCode, verbose); > + break; > + > + case 10: > + *next_step = 8; > + rc = enable_activate(1, returnCode, verbose); > + /* no reboot happened */ > + if (!rc) > + rc = set_owner_install(1, returnCode, verbose); > + break; > + > + case 11: > + rc = set_owner_install(0, returnCode, verbose); > + if (!rc) > + rc = deactivate_tpm(1, 0, returnCode, verbose); > + if (!rc) > + rc = disable_tpm(1, returnCode, verbose); > + break; > + > + case 14: > + rc = force_clear(0, 0, returnCode, verbose); > + if (!rc) > + rc = enable_activate(1, returnCode, verbose); > + break; > + > + case 21: > + *next_step = 5; > + rc = enable_activate(1, returnCode, verbose); > + /* no reboot happened */ > + if (!rc) > + rc = force_clear(0, 0, returnCode, verbose); > + break; > + > + case 22: > + *next_step = 14; > + rc = enable_activate(1, returnCode, verbose); > + /* no reboot happened */ > + if (!rc) > + rc = force_clear(0, 0, returnCode, verbose); > + *next_step = 0; > + if (!rc) > + rc = enable_activate(1, returnCode, verbose); > + break; > + > + default: > + break; > + } > + > + if (rc) > + printf("Op %d: An error occurred: 0x%x\n", cfg->op, rc); > + > + /* no reboot, no next step */ > + *next_step = 0; > + > + return rc; > +} > > u32 > tpm_add_bootdevice_ipl(u32 bootcd, u32 bootdrv, > @@ -1484,3 +2010,47 @@ err_exit: > return rc; > return TCG_TCG_COMMAND_ERROR; > } > + > +void > +tpm_ppi_init(void) > +{ > + if (runningOnQEMU()) { > + struct tpm_ppi *tp = (void *)(TPM_TIS_BASE_ADDRESS + TIS_REG_RAM); > + > + if (tp->sign1 != TCG_MAGIC || tp->sign2 != TCG_MAGIC) { > + memset(tp, 0x0, sizeof(*tp)); > + tp->sign1 = TCG_MAGIC; > + tp->sign2 = TCG_MAGIC; > + /* set number of bytes that ACPI can read/write */ > + tp->size = sizeof(tp->opcode) + sizeof(tp->failure) + > + sizeof(tp->recent_opcode) + sizeof(tp->response); > + } > + } > +} > + > +void > +tpm_ppi_process(void) > +{ > + tpm_bios_cfg_t cfg; > + > + if (runningOnQEMU()) { > + struct tpm_ppi *tp = (void *)(TPM_TIS_BASE_ADDRESS + TIS_REG_RAM); > + > + if (tp->sign1 == TCG_MAGIC && tp->sign2 == TCG_MAGIC) { > + cfg.op = tp->opcode; > + dprintf(DEBUG_tcg, "TCGBIOS: PPI opcode found: 0x%08x\n", > tp->opcode); > + if (!cfg.op) { > + /* intermediate step after a reboot? */ > + cfg.op = tp->next_step; > + } else { > + /* last full opcode */ > + tp->recent_opcode = cfg.op; > + } > + if (cfg.op) { > + printf("Processing TPM PPI opcode %d\n", cfg.op); > + tp->failure = (tpm_process_cfg(&cfg, 0, &tp->response, > + &tp->next_step) != 0); > + } > + } > + } > +} > diff --git a/src/tcgbios.h b/src/tcgbios.h > index 7cf4364..9811827 100644 > --- a/src/tcgbios.h > +++ b/src/tcgbios.h > @@ -328,6 +328,36 @@ struct tpm_res_getcap_perm_flags { > } PACKED; > > > +struct tpm_req_getcap_stclear_flags { > + TPM_REQ_HEADER > + u32 capArea; > + u32 subCapSize; > + u32 subCap; > +} PACKED; > + > + > +struct tpm_stclear_flags { > + u16 tag; > + u8 flags[5]; > +} PACKED; > + > + > +enum stclearFlagsIndex { > + STCLEAR_FLAG_IDX_DEACTIVATED = 0, > + STCLEAR_FLAG_IDX_DISABLE_FORCE_CLEAR, > + STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE, > + STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE_LOCK, > + STCLEAR_FLAG_IDX_GLOBAL_LOCK, > +}; > + > + > +struct tpm_res_getcap_stclear_flags { > + TPM_RSP_HEADER > + u32 size; > + struct tpm_stclear_flags stclear_flags; > +} PACKED; > + > + > struct tpm_res_getcap_ownerauth { > TPM_RSP_HEADER > u32 size; > @@ -378,6 +408,23 @@ enum ipltype { > IPL_EL_TORITO_2 > }; > > +/* > + * physical presence interface > + */ > + > +struct tpm_ppi { > + u32 sign1; > + u16 size; // number of subsequent bytes for ACPI to access > + u8 opcode; // set by ACPI > + u8 failure; // set by BIOS (0 = success) > + u8 recent_opcode; // set by BIOS > + u32 response; // set by BIOS > + u8 next_step; // BIOS only > + u32 sign2; > +} PACKED; > + > +void tpm_ppi_init(void); > +void tpm_ppi_process(void); > > struct bregs; > void tpm_interrupt_handler32(struct bregs *regs); _______________________________________________ SeaBIOS mailing list [email protected] http://www.seabios.org/mailman/listinfo/seabios
