In anticipation that the first 3 patches of the previous series are going to be pushed, here are now the remaining 3 patches with a reworked TPM menu and a reworked Physical Presence Interface.
Previous series:
http://www.seabios.org/pipermail/seabios/2015-March/008975.html
Stefan Berger (3): Add 'measurement' code to the BIOS Support for TPM Physical Presence Interface Add a menu for TPM control
src/boot.c | 26 +- src/cdrom.c | 11 + src/hw/tpm_drivers.h | 1 + src/optionroms.c | 4 + src/post.c | 7 + src/tcgbios.c | 1067 +++++++++++++++++++++++++++++++++++++++++++++++++- src/tcgbios.h | 58 +++ src/util.h | 1 + 8 files changed, 1165 insertions(+), 10 deletions(-)
This patch adds invocations of functions that measure various parts of the code and data through various parts of the BIOS code. It follows TCG specifications on what needs to be measured. It also adds the implementation of the called functions.
Reference for what needs to be measured can be found in section 3.2.2++ in
http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific...
The first measurements are done once the ACPI tables have been initialized.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 12 ++ src/cdrom.c | 11 ++ src/optionroms.c | 4 + src/post.c | 4 + src/tcgbios.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 9 ++ 6 files changed, 409 insertions(+)
diff --git a/src/boot.c b/src/boot.c index d6b1fb7..3044fb0 100644 --- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tpm_*
/**************************************************************** @@ -628,6 +629,10 @@ boot_disk(u8 bootdrv, int checksig) } }
+ /* specs: 8.2.3 steps 4 and 5 */ + tpm_add_bootdevice_ipl(0, bootdrv, + IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -651,6 +656,11 @@ boot_cdrom(struct drive_s *drive_g)
u8 bootdrv = CDEmu.emulated_drive; u16 bootseg = CDEmu.load_segment; + + /* specs: 8.2.5.6 */ + tpm_add_bootdevice_ipl(1, bootdrv, + IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
+ tpm_returned_via_int18h(); + // Boot failed: invoke the boot recovery function struct bregs br; memset(&br, 0, sizeof(br)); diff --git a/src/cdrom.c b/src/cdrom.c index 92f34f4..0866680 100644 --- a/src/cdrom.c +++ b/src/cdrom.c @@ -15,6 +15,7 @@ #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset #include "util.h" // cdrom_prepboot +#include "tcgbios.h" // tpm_*
// Locks for removable devices u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; @@ -192,6 +193,12 @@ cdrom_boot(struct drive_s *drive) if (buffer[0x20] != 0x88) return 11; // Bootable
+ /* specs: 8.2.3 step 5 and 8.2.5.6, measure El Torito boot catalog */ + /* measure 2048 bytes (one sector) */ + tpm_add_bootdevice_ipl(1, 0, + IPL_EL_TORITO_2, MAKE_FLATPTR(GET_SEG(SS), buffer), + 2048); + // Fill in el-torito cdrom emulation fields. emulated_drive_gf = drive; u8 media = buffer[0x21]; @@ -228,6 +235,10 @@ cdrom_boot(struct drive_s *drive) dop.buf_fl += count*CDROM_SECTOR_SIZE; }
+ /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */ + /* measure 1st 512 bytes */ + tpm_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(boot_segment, 0), 512); + if (media == 0) { // No emulation requested - return success. CDEmu.emulated_drive = EXTSTART_CD + cdid; diff --git a/src/optionroms.c b/src/optionroms.c index f6047e6..b93a4b1 100644 --- a/src/optionroms.c +++ b/src/optionroms.c @@ -19,6 +19,7 @@ #include "std/pnpbios.h" // PNP_SIGNATURE #include "string.h" // memset #include "util.h" // get_pnp_offset +#include "tcgbios.h" // tpm_*
static int EnforceChecksum, S3ResumeVga, RunPCIroms;
@@ -80,6 +81,7 @@ is_valid_rom(struct rom_header *rom) if (EnforceChecksum) return 0; } + tpm_option_rom(rom, len); return 1; }
@@ -354,6 +356,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = rom_get_last();
+ tpm_start_option_rom_scan(); + if (CONFIG_OPTIONROMS_DEPLOYED) { // Option roms are already deployed on the system. u32 pos = post_vga; diff --git a/src/post.c b/src/post.c index 0c7b36b..e273c18 100644 --- a/src/post.c +++ b/src/post.c @@ -197,6 +197,9 @@ prepareboot(void) void VISIBLE32FLAT startBoot(void) { + tpm_calling_int19h(); + tpm_add_event_separators(); + // Clear low-memory allocations (required by PMM spec). memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
@@ -223,6 +226,7 @@ maininit(void)
// Initialize TPM tpm_start(); + tpm_smbios_measure();
// Run vga option rom vgarom_setup(); diff --git a/src/tcgbios.c b/src/tcgbios.c index c837ab6..3f31231 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -23,6 +23,7 @@ #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "bregs.h" // struct bregs #include "sha1.h" // sha1 +#include "std/smbios.h"
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -56,6 +57,8 @@ static const u8 GetCapability_Durations[] = { 0x00, 0x00, 0x01, 0x20 };
+static u8 evt_separator[] = {0xff,0xff,0xff,0xff}; +
#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr)
@@ -1080,6 +1083,372 @@ tpm_interrupt_handler32(struct bregs *regs) return; }
+/* + * 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 10.4.1 + * data : pointer to the data (i.e., string) to be added to the log + * length : length of the data + */ +static u32 +tpm_add_measurement_to_log(u32 pcrIndex, + u32 event_type, + const char *data, u32 length) +{ + u32 rc = 0; + struct hleeo hleeo; + u8 _pcpes[offsetof(struct pcpes, event) + 400]; + struct pcpes *pcpes = (struct pcpes *)_pcpes; + + if (length < sizeof(_pcpes) - offsetof(struct pcpes, event)) { + + pcpes->pcrindex = pcrIndex; + pcpes->eventtype = event_type; + memset(&pcpes->digest, 0x0, sizeof(pcpes->digest)); + pcpes->eventdatasize = length; + memcpy(&pcpes->event, data, length); + + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = &pcpes->event, + .hashdatalen = length, + .pcrindex = pcrIndex, + .logdataptr = _pcpes, + .logdatalen = length + offsetof(struct pcpes, event), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + } else { + rc = TCG_GENERAL_ERROR; + } + + return rc; +} + + +/* + * Add a measurement to the log; further description of the data + * that are to be hashed are NOT appended to the TCG_PCClientPCREventStruc. + * Input parameters: + * pcrIndex : PCR to extend + * event_type : type of event; specs 10.4.1 + * ptr : 32 bit pointer to the data to be hashed + * length : length of the data to be hashed + * + * Returns lower 16 bit of return code of TCG_HashLogExtendEvent. '0' means + * success, otherwise an error is indicated. + */ +static u32 +tpm_add_measurement_to_log_simple(u32 pcrIndex, + u16 event_type, + const u8 *ptr, u32 length) +{ + struct hleeo hleeo; + struct pcpes pcpes = { + .pcrindex = pcrIndex, + .eventtype = event_type, + /* specs: 10.4.1, EV_IPL eventfield should not contain the code.*/ + .eventdatasize = 0, + .event = 0, + }; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = ptr, + .hashdatalen = length, + .pcrindex = pcrIndex, + .logdataptr = &pcpes, + .logdatalen = offsetof(struct pcpes, event), + }; + + return hash_log_extend_event(&hleei, &hleeo); +} + + +/* + * Add a measurement to the list of measurements + * pcrIndex : PCR to be extended + * event_type : type of event; specs 10.4.1 + * data : additional parameter; used as parameter for 10.4.3 + * 'action index' + */ +static u32 +tpm_add_measurement(u32 pcrIndex, + u16 event_type, + const char *string) +{ + u32 rc; + + switch (event_type) { + case EV_SEPARATOR: + rc = tpm_add_measurement_to_log_simple(pcrIndex, + event_type, + (u8 *)evt_separator, + 4); + break; + + case EV_ACTION: + rc = tpm_add_measurement_to_log(pcrIndex, + event_type, + string, + strlen(string)); + break; + + default: + rc = TCG_INVALID_INPUT_PARA; + } + + return rc; +} + + +u32 +tpm_calling_int19h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(4, EV_ACTION, + "Calling INT 19h"); +} + + +u32 +tpm_returned_via_int18h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(4, EV_ACTION, + "Return via INT 18h"); +} + + +/* + * Add event separators for PCRs 0 to 7; specs 8.2.3 + */ +u32 +tpm_add_event_separators(void) +{ + u32 rc; + u32 pcrIndex = 0; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + while (pcrIndex <= 7) { + rc = tpm_add_measurement(pcrIndex, EV_SEPARATOR, NULL); + if (rc) + break; + pcrIndex ++; + } + + return rc; +} + + +/* + * Add a measurement regarding the boot device (CDRom, Floppy, HDD) to + * the list of measurements. + */ +static u32 +tpm_add_bootdevice(u32 bootcd, u32 bootdrv) +{ + const char *string; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case 0: + switch (bootdrv) { + case 0: + string = "Booting BCV device 00h (Floppy)"; + break; + + case 0x80: + string = "Booting BCV device 80h (HDD)"; + break; + + default: + string = "Booting unknown device"; + break; + } + + break; + + default: + string = "Booting from CD ROM device"; + } + + return tpm_add_measurement_to_log(4, EV_ACTION, + string, strlen(string)); +} + + +/* + * Add measurement to the log about option rom scan + * 10.4.3 : action 14 + */ +u32 +tpm_start_option_rom_scan(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(2, EV_ACTION, + "Start Option ROM Scan"); +} + + +/* + * Add measurement to the log about an option rom + */ +u32 +tpm_option_rom(const void *addr, u32 len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes_romex pcctes = { + .eventid = 7, /* 10.4.2.3.7 */ + .eventdatasize = sizeof(u16) + sizeof(u16) + SHA1_BUFSIZE, + }; + + rc = sha1((const u8 *)addr, len, pcctes.digest); + if (rc) + return rc; + + return tpm_add_measurement_to_log(2, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +u32 +tpm_smbios_measure(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes pcctes = { + .eventid = 1, /* 10.4.2.3.1 */ + .eventdatasize = SHA1_BUFSIZE, + }; + struct smbios_entry_point *sep = SMBiosAddr; + + dprintf(DEBUG_tcg, "TCGBIOS: SMBIOS at %p\n", sep); + + if (!sep) + return 0; + + rc = sha1((const u8 *)sep->structure_table_address, + sep->structure_table_length, pcctes.digest); + if (rc) + return rc; + + return tpm_add_measurement_to_log(1, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +/* + * Add a measurement to the log in support of 8.2.5.3 + * Creates two log entries + * + * Input parameter: + * bootcd : 0: MBR of hdd, 1: boot image, 2: boot catalog of El Torito + * addr : address where the IP data are located + * length : IP data length in bytes + */ +u32 +tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 length) +{ + u32 rc; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case IPL_EL_TORITO_1: + /* specs: 8.2.5.6 El Torito */ + rc = tpm_add_measurement_to_log_simple(4, + EV_IPL, + addr, + length); + break; + + case IPL_EL_TORITO_2: + /* specs: 8.2.5.6 El Torito */ + rc = tpm_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr, + length); + break; + + default: + /* specs: 8.2.5.3 */ + /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ + rc = tpm_add_measurement_to_log_simple(4, + EV_IPL, + addr, + 0x1b8); + + if (rc) + break; + + /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ + rc = tpm_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr + 0x1b8, + 0x48); + } + + return rc; +} + + +u32 +tpm_add_bootdevice_ipl(u32 bootcd, u32 bootdrv, + enum ipltype ipltype, const u8 *addr, u32 length) +{ + u32 rc = tpm_add_bootdevice(bootcd, bootdrv); + if (rc) + return rc; + + return tpm_ipl(ipltype, addr, length); +}
u32 tpm_s3_resume(void) diff --git a/src/tcgbios.h b/src/tcgbios.h index 332d785..7cf4364 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -385,5 +385,14 @@ void tpm_interrupt_handler32(struct bregs *regs); u32 tpm_start(void); u32 tpm_leave_bios(void); u32 tpm_s3_resume(void); +u32 tpm_calling_int19h(void); +u32 tpm_returned_via_int18h(void); +u32 tpm_add_bootdevice_ipl(u32 bootcd, u32 bootdrv, + enum ipltype ipltype, const u8 *addr, u32 length); +u32 tpm_add_event_separators(void); +u32 tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 count); +u32 tpm_start_option_rom_scan(void); +u32 tpm_option_rom(const void *addr, u32 len); +u32 tpm_smbios_measure(void);
#endif /* TCGBIOS_H */
On Fri, May 08, 2015 at 01:45:46PM -0400, Stefan Berger wrote:
This patch adds invocations of functions that measure various parts of the code and data through various parts of the BIOS code. It follows TCG specifications on what needs to be measured. It also adds the implementation of the called functions.
Reference for what needs to be measured can be found in section 3.2.2++ in
http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific...
The first measurements are done once the ACPI tables have been initialized.
Thanks - I have some questions and comments below.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
Which of these measurements are required to be performed and which are optional?
--- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tpm_*
/**************************************************************** @@ -628,6 +629,10 @@ boot_disk(u8 bootdrv, int checksig) } }
- /* specs: 8.2.3 steps 4 and 5 */
- tpm_add_bootdevice_ipl(0, bootdrv,
IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512);
I think the comments with "specs:" are confusing as they are references to the tpm specs instead of the BIOS boot specifications. I think the "spec" comments should be moved into the tcg code.
Also, the parameters to tpm_add_bootdevice_ipl() seem complex - I wonder if a couple of additional functions (eg, tpm_add_bcv(), tpm_add_cdrom(), tpm_add_cdrom_catalog() ) would make the calling code simpler.
[...]
@@ -651,6 +656,11 @@ boot_cdrom(struct drive_s *drive_g)
u8 bootdrv = CDEmu.emulated_drive; u16 bootseg = CDEmu.load_segment;
- /* specs: 8.2.5.6 */
- tpm_add_bootdevice_ipl(1, bootdrv,
IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512);
This measurement seems redundant with the measurement already taken in cdrom.c.
[...]
@@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
- tpm_returned_via_int18h();
I don't understand the name returned_via_int18h here as we're calling int18 in this location, not returning from it.
[...]
--- a/src/optionroms.c +++ b/src/optionroms.c @@ -19,6 +19,7 @@ #include "std/pnpbios.h" // PNP_SIGNATURE #include "string.h" // memset #include "util.h" // get_pnp_offset +#include "tcgbios.h" // tpm_*
static int EnforceChecksum, S3ResumeVga, RunPCIroms;
@@ -80,6 +81,7 @@ is_valid_rom(struct rom_header *rom) if (EnforceChecksum) return 0; }
- tpm_option_rom(rom, len);
Taking a measurement from is_valid_rom() doesn't seem correct (as is_valid_rom can be called multiple times for the same rom). Wouldn't the measurement make more sense just before calling the rom (ie, in callrom() ) or just after copying it to memory?
[...]
@@ -354,6 +356,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = rom_get_last();
- tpm_start_option_rom_scan();
I think this should be folded into tpm_start().
[...]
--- a/src/post.c +++ b/src/post.c @@ -197,6 +197,9 @@ prepareboot(void) void VISIBLE32FLAT startBoot(void) {
- tpm_calling_int19h();
- tpm_add_event_separators();
These calls shouldn't be done from startBoot() - the VISIBLE32FLAT flag does something special to note the end of "init" code sections. I think these calls should be folded into tpm_leave_bios().
[...]
@@ -223,6 +226,7 @@ maininit(void)
// Initialize TPM tpm_start();
- tpm_smbios_measure();
This call should be folded into tpm_start().
-Kevin
On 05/21/2015 01:40 PM, Kevin O'Connor wrote:
On Fri, May 08, 2015 at 01:45:46PM -0400, Stefan Berger wrote:
This patch adds invocations of functions that measure various parts of the code and data through various parts of the BIOS code. It follows TCG specifications on what needs to be measured. It also adds the implementation of the called functions.
Reference for what needs to be measured can be found in section 3.2.2++ in
http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific...
The first measurements are done once the ACPI tables have been initialized.
Thanks - I have some questions and comments below.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
Which of these measurements are required to be performed and which are optional?
The above list shows some measurements from trusted grub, e.g. those after the first IPL Partition Data are from trusted grub. The spec doesn't say which ones are optional but we are more on the 'light' side in terms of measurements.
--- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tpm_*
/**************************************************************** @@ -628,6 +629,10 @@ boot_disk(u8 bootdrv, int checksig) } }
- /* specs: 8.2.3 steps 4 and 5 */
- tpm_add_bootdevice_ipl(0, bootdrv,
IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512);
I think the comments with "specs:" are confusing as they are references to the tpm specs instead of the BIOS boot specifications. I think the "spec" comments should be moved into the tcg code.
Will do. Maybe even remove them or reference via title. The numbers changed with different versions of the specs.
Also, the parameters to tpm_add_bootdevice_ipl() seem complex - I wonder if a couple of additional functions (eg, tpm_add_bcv(), tpm_add_cdrom(), tpm_add_cdrom_catalog() ) would make the calling code simpler.
Will look into it.
[...]
@@ -651,6 +656,11 @@ boot_cdrom(struct drive_s *drive_g)
u8 bootdrv = CDEmu.emulated_drive; u16 bootseg = CDEmu.load_segment;
- /* specs: 8.2.5.6 */
- tpm_add_bootdevice_ipl(1, bootdrv,
IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512);
This measurement seems redundant with the measurement already taken in cdrom.c.
[...]
@@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
- tpm_returned_via_int18h();
I don't understand the name returned_via_int18h here as we're calling int18 in this location, not returning from it.
I did this assuming that we will eventually have to return from int18.
[...]
--- a/src/optionroms.c +++ b/src/optionroms.c @@ -19,6 +19,7 @@ #include "std/pnpbios.h" // PNP_SIGNATURE #include "string.h" // memset #include "util.h" // get_pnp_offset +#include "tcgbios.h" // tpm_*
static int EnforceChecksum, S3ResumeVga, RunPCIroms;
@@ -80,6 +81,7 @@ is_valid_rom(struct rom_header *rom) if (EnforceChecksum) return 0; }
- tpm_option_rom(rom, len);
Taking a measurement from is_valid_rom() doesn't seem correct (as is_valid_rom can be called multiple times for the same rom). Wouldn't the measurement make more sense just before calling the rom (ie, in callrom() ) or just after copying it to memory?
[...]
@@ -354,6 +356,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = rom_get_last();
- tpm_start_option_rom_scan();
I think this should be folded into tpm_start().
Option ROMs will always be scanned, so ok, I'll also move it into tpm_start(). The intention previously was to do the measurements at the location where corresponding 'action' happens, but I think you don't want the code to be filled with these calls.
[...]
--- a/src/post.c +++ b/src/post.c @@ -197,6 +197,9 @@ prepareboot(void) void VISIBLE32FLAT startBoot(void) {
- tpm_calling_int19h();
- tpm_add_event_separators();
These calls shouldn't be done from startBoot() - the VISIBLE32FLAT flag does something special to note the end of "init" code sections. I think these calls should be folded into tpm_leave_bios().
It works the way it is. I can move it, though.
[...]
@@ -223,6 +226,7 @@ maininit(void)
// Initialize TPM tpm_start();
- tpm_smbios_measure();
This call should be folded into tpm_start().
Definitely.
Stefan
-Kevin
On Thu, May 21, 2015 at 06:09:23PM -0400, Stefan Berger wrote:
On 05/21/2015 01:40 PM, Kevin O'Connor wrote:
On Fri, May 08, 2015 at 01:45:46PM -0400, Stefan Berger wrote:
@@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
- tpm_returned_via_int18h();
I don't understand the name returned_via_int18h here as we're calling int18 in this location, not returning from it.
I did this assuming that we will eventually have to return from int18.
I don't understand - what does it mean to return from int18?
--- a/src/post.c +++ b/src/post.c @@ -197,6 +197,9 @@ prepareboot(void) void VISIBLE32FLAT startBoot(void) {
- tpm_calling_int19h();
- tpm_add_event_separators();
These calls shouldn't be done from startBoot() - the VISIBLE32FLAT flag does something special to note the end of "init" code sections. I think these calls should be folded into tpm_leave_bios().
It works the way it is. I can move it, though.
I didn't mean to imply moving would change functionality. Placing the code in a VISIBLE32FLAT function, however causes more code to be in the "fixed" code area instead of the "init" code area (as reported in the "Total size" line during the build). Using more "fixed" area is not desireable as it reduces available ram for option roms and other low-memory consumers. Moving the calls up into the caller of startBoot() (or even further up into tpm_leave_bios) is functionally identical, but has the added benefit of allowing more code to be placed in the "init" code area instaed of the "fixed" code area.
Thanks.
-Kevin
On 05/21/2015 06:32 PM, Kevin O'Connor wrote:
On Thu, May 21, 2015 at 06:09:23PM -0400, Stefan Berger wrote:
On 05/21/2015 01:40 PM, Kevin O'Connor wrote:
On Fri, May 08, 2015 at 01:45:46PM -0400, Stefan Berger wrote:
@@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
- tpm_returned_via_int18h();
I don't understand the name returned_via_int18h here as we're calling int18 in this location, not returning from it.
I did this assuming that we will eventually have to return from int18.
I don't understand - what does it mean to return from int18?
Dropping this for now. Though the spec says:
"If the IPL Code on an IPL device returns back to the BIOS through INT 18h or INT 19h, that event must be measured."
http://www.trustedcomputinggroup.org/files/resource_files/CB0B2BFA-1A4B-B294...
section 8.1
--- a/src/post.c +++ b/src/post.c @@ -197,6 +197,9 @@ prepareboot(void) void VISIBLE32FLAT startBoot(void) {
- tpm_calling_int19h();
- tpm_add_event_separators();
These calls shouldn't be done from startBoot() - the VISIBLE32FLAT flag does something special to note the end of "init" code sections. I think these calls should be folded into tpm_leave_bios().
It works the way it is. I can move it, though.
I didn't mean to imply moving would change functionality. Placing the code in a VISIBLE32FLAT function, however causes more code to be in the "fixed" code area instead of the "init" code area (as reported in the "Total size" line during the build). Using more "fixed" area is not desireable as it reduces available ram for option roms and other low-memory consumers. Moving the calls up into the caller of startBoot() (or even further up into tpm_leave_bios) is functionally identical, but has the added benefit of allowing more code to be placed in the "init" code area instaed of the "fixed" code area.
Move them also into tpm_leave_bios.
Stefan
Thanks.
-Kevin
On Tue, May 26, 2015 at 02:36:42PM -0400, Stefan Berger wrote:
On 05/21/2015 06:32 PM, Kevin O'Connor wrote:
On Thu, May 21, 2015 at 06:09:23PM -0400, Stefan Berger wrote:
On 05/21/2015 01:40 PM, Kevin O'Connor wrote:
On Fri, May 08, 2015 at 01:45:46PM -0400, Stefan Berger wrote:
@@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
- tpm_returned_via_int18h();
I don't understand the name returned_via_int18h here as we're calling int18 in this location, not returning from it.
I did this assuming that we will eventually have to return from int18.
I don't understand - what does it mean to return from int18?
Dropping this for now. Though the spec says:
"If the IPL Code on an IPL device returns back to the BIOS through INT 18h or INT 19h, that event must be measured."
http://www.trustedcomputinggroup.org/files/resource_files/CB0B2BFA-1A4B-B294...
section 8.1
I interpret the above to mean "if the IPL code on an IPL device re-invokes the BIOS by rasing an INT 18h or INT 19h, then that event must be measured."
As I understand it, the INT 18/19 handlers are never supposed to return (in the sense of the "iret" instruction) and the bootloader code is never supposed to directly return to the bios (in the sense of the "ret" instruction).
-Kevin
This patch implements the specification found here:
http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interfa...
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 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 stefanb@linux.vnet.ibm.com --- 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);
On Fri, 8 May 2015 13:45:47 -0400 Stefan Berger stefanb@linux.vnet.ibm.com wrote:
This patch implements the specification found here:
http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interfa...
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 stefanb@linux.vnet.ibm.com
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);
On Fri, May 08, 2015 at 01:45:47PM -0400, Stefan Berger wrote:
This patch implements the specification found here:
http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interfa...
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.
[...]
--- 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();
I think both of these calls should be folded into tpm_start().
-Kevin
On 05/21/2015 01:46 PM, Kevin O'Connor wrote:
On Fri, May 08, 2015 at 01:45:47PM -0400, Stefan Berger wrote:
This patch implements the specification found here:
http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interfa...
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.
[...]
--- 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();
I think both of these calls should be folded into tpm_start().
Agreed. Done.
Stefan
This patch provides an addtional menu entry that enables the user to control certain aspects of the TPM's state.
If a working TPM has been detected, the boot menu will look like this:
Select boot device:
1. ata0-1: QEMU HARDDISK ATA-7 Hard-Disk (6144 MiBytes) 2. Legacy option rom 3. iPXE (PCI 00:03.0)
T. TPM Menu
Upon pressing T the TPM menu will be shown:
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 Escape for previous menu. TPM is enabled, active, does not have an owner but one can be installed.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 14 ++++++-- src/tcgbios.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 2 ++ src/util.h | 1 + 4 files changed, 127 insertions(+), 2 deletions(-)
diff --git a/src/boot.c b/src/boot.c index 3044fb0..3c077d0 100644 --- a/src/boot.c +++ b/src/boot.c @@ -427,7 +427,7 @@ get_raw_keystroke(void) }
// Read a keystroke - waiting up to 'msec' milliseconds. -static int +int get_keystroke(int msec) { u32 end = irqtimer_calc(msec); @@ -461,7 +461,7 @@ interactive_bootmenu(void)
char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); int menukey = romfile_loadint("etc/boot-menu-key", 1); - printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n\n"); + printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n"); free(bootmsg);
u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); @@ -474,6 +474,7 @@ interactive_bootmenu(void) while (get_keystroke(0) >= 0) ;
+show_boot_menu: printf("Select boot device:\n\n"); wait_threads();
@@ -486,6 +487,9 @@ interactive_bootmenu(void) printf("%d. %s\n", maxmenu , strtcpy(desc, pos->description, ARRAY_SIZE(desc))); } + if (tpm_is_detected()) { + printf("\nT. TPM menu\n"); + }
// Get key press. If the menu key is ESC, do not restart boot unless // 1.5 seconds have passed. Otherwise users (trained by years of @@ -496,6 +500,12 @@ interactive_bootmenu(void) scan_code = get_keystroke(1000); if (scan_code == 1 && !irqtimer_check(esc_accepted_time)) continue; + if (tpm_is_detected() && scan_code == 20 /* T */) { + printf("\n"); + tpm_menu(); + printf("\n"); + goto show_boot_menu; + } if (scan_code >= 1 && scan_code <= maxmenu+1) break; } diff --git a/src/tcgbios.c b/src/tcgbios.c index 610ed12..2b74775 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -26,6 +26,7 @@ #include "std/smbios.h" #include "malloc.h" // malloc_* #include "fw/paravirt.h" // runningOnQEMU +#include "stacks.h" // wait_threads
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -135,6 +136,17 @@ probe_tpm(void) } }
+int +tpm_is_detected(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + probe_tpm(); + + return tpm_state.tpm_found; +} + static int has_working_tpm(void) { @@ -2054,3 +2066,103 @@ tpm_ppi_process(void) } } } + + +static void +show_tpm_state(void) +{ + struct tpm_permanent_flags pf; + u8 has_owner; + + if (read_permanent_flags((char *)&pf, sizeof(pf)) || + read_has_owner(&has_owner)) + return; + + printf("TPM is "); + + if (pf.flags[PERM_FLAG_IDX_DISABLE]) + printf("disabled"); + else + printf("enabled"); + + if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) + printf(", deactivated"); + else + printf(", active"); + + if (has_owner) + printf(" and has an owner.\n"); + else { + printf(", does not have an owner "); + if (pf.flags[PERM_FLAG_IDX_OWNERSHIP]) + printf("but one can be installed.\n"); + else + printf("and an owner cannot be installed.\n"); + } + +} + + +void +tpm_menu(void) +{ + if (!CONFIG_TCGBIOS) + return; + + int scan_code; + u32 rc, returnCode; + u8 next_step; + tpm_bios_cfg_t cfg = { + .op = 0, + }; + + while (get_keystroke(0) >= 0) + ; + wait_threads(); + + for (;;) { + if (has_working_tpm()) { + printf("1. Enable TPM\n" + "2. Disable TPM\n" + "3. Activate TPM\n" + "4. Deactivate TPM\n" + "5. Clear ownership\n" + "6. Allow installation of owner\n" + "7. Prevent installation of owner\n"); + } else { + printf("TPM is not working correctly.\n"); + } + + printf("Escape for previous menu.\n"); + + if (has_working_tpm()) { + show_tpm_state(); + } + + cfg.op = 0; + + while ((scan_code = get_keystroke(1000)) == ~0) + ; + + switch (scan_code) { + case 1: + // ESC + return; + case 2 ... 6: + cfg.op = scan_code - 1; + break; + case 7 ... 8: + cfg.op = scan_code + 1; + break; + default: + continue; + } + + if (has_working_tpm()) { + rc = tpm_process_cfg(&cfg, 1, &returnCode, &next_step); + + if (rc) + printf("An error occurred: 0x%x\n", rc); + } + } +} diff --git a/src/tcgbios.h b/src/tcgbios.h index 9811827..e71470f 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -441,5 +441,7 @@ u32 tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 count); u32 tpm_start_option_rom_scan(void); u32 tpm_option_rom(const void *addr, u32 len); u32 tpm_smbios_measure(void); +int tpm_is_detected(void); +void tpm_menu(void);
#endif /* TCGBIOS_H */ diff --git a/src/util.h b/src/util.h index 2244090..bb24744 100644 --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,7 @@ int bootprio_find_pci_rom(struct pci_device *pci, int instance); int bootprio_find_named_rom(const char *name, int instance); struct usbdevice_s; int bootprio_find_usb(struct usbdevice_s *usbdev, int lun); +int get_keystroke(int msec);
// bootsplash.c void enable_vga_console(void);