This patch adds invocactions 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_implementation_specification_for_conventional_bios_specification_version_12


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 []

v5:
 - call code for measuring CDROM boot sector

v4:
 - return TCG_GENERAL_ERROR if ! has_working_tpm()

v2:
 - dropping call to tcpa_measure_post
 - converting tcpa_option_rom and tcpa_ipl functions to get pointers
   rather than segments passed
 - introduce public get_smbios_entry_point() function and use it
   rather than searching for the entry point
 - use dprintf(DEBUG_tcg, ...)


Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 src/boot.c       |   11 +
 src/cdrom.c      |   10 +
 src/optionroms.c |    4 
 src/post.c       |    4 
 src/smbios.c     |   12 +
 src/smbios.h     |    1 
 src/tcgbios.c    |  359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tcgbios.h    |    8 +
 8 files changed, 409 insertions(+)

Index: seabios/src/post.c
===================================================================
--- seabios.orig/src/post.c
+++ seabios/src/post.c
@@ -197,6 +197,9 @@ init_hw(void)
 void VISIBLE32FLAT
 startBoot(void)
 {
+    tcpa_calling_int19h();
+    tcpa_add_event_separators();
+
     // Clear low-memory allocations (required by PMM spec).
     memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
 
@@ -254,6 +257,7 @@ maininit(void)
     // Initialize tpm (after acpi tables were written)
     tcpa_acpi_init();
     tcpa_startup();
+    tcpa_smbios_measure();
 
     // Run vga option rom
     vga_setup();
Index: seabios/src/optionroms.c
===================================================================
--- seabios.orig/src/optionroms.c
+++ seabios/src/optionroms.c
@@ -14,6 +14,7 @@
 #include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA
 #include "boot.h" // IPL
 #include "paravirt.h" // qemu_cfg_*
+#include "tcgbios.h" // tcpa_*
 
 
 /****************************************************************
@@ -136,6 +137,7 @@ is_valid_rom(struct rom_header *rom)
         if (EnforceChecksum)
             return 0;
     }
+    tcpa_option_rom(rom, len);
     return 1;
 }
 
@@ -399,6 +401,8 @@ optionrom_setup(void)
     memset(sources, 0, sizeof(sources));
     u32 post_vga = RomEnd;
 
+    tcpa_start_option_rom_scan();
+
     if (CONFIG_OPTIONROMS_DEPLOYED) {
         // Option roms are already deployed on the system.
         u32 pos = RomEnd;
Index: seabios/src/boot.c
===================================================================
--- seabios.orig/src/boot.c
+++ seabios/src/boot.c
@@ -547,6 +547,10 @@ boot_disk(u8 bootdrv, int checksig)
         }
     }
 
+    tcpa_add_bootdevice(0, bootdrv);
+    /* specs: 8.2.3 steps 4 and 5 */
+    tcpa_ipl(IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512);
+
     /* Canonicalize bootseg:bootip */
     u16 bootip = (bootseg & 0x0fff) << 4;
     bootseg &= 0xf000;
@@ -571,6 +575,11 @@ boot_cdrom(struct drive_s *drive_g)
     u16 ebda_seg = get_ebda_seg();
     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
+
+    tcpa_add_bootdevice(1, bootdrv);
+    /* specs: 8.2.5.6 */
+    tcpa_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512);
+
     /* Canonicalize bootseg:bootip */
     u16 bootip = (bootseg & 0x0fff) << 4;
     bootseg &= 0xf000;
@@ -634,6 +643,8 @@ do_boot(u16 seq_nr)
         break;
     }
 
+    tcpa_returned_via_int18h();
+
     // Boot failed: invoke the boot recovery function
     struct bregs br;
     memset(&br, 0, sizeof(br));
Index: seabios/src/cdrom.c
===================================================================
--- seabios.orig/src/cdrom.c
+++ seabios/src/cdrom.c
@@ -11,6 +11,7 @@
 #include "biosvar.h" // GET_EBDA
 #include "ata.h" // ATA_CMD_REQUEST_SENSE
 #include "blockcmd.h" // CDB_CMD_REQUEST_SENSE
+#include "tcgbios.h" // tcpa_*
 
 
 /****************************************************************
@@ -291,6 +292,11 @@ cdrom_boot(struct drive_s *drive_g)
     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) */
+    tcpa_add_bootdevice(1, 0);
+    tcpa_ipl(IPL_EL_TORITO_2, MAKE_FLATPTR(GET_SEG(SS), buffer), 2048);
+
     u16 ebda_seg = get_ebda_seg();
     u8 media = buffer[0x21];
     SET_EBDA2(ebda_seg, cdemu.media, media);
@@ -317,6 +323,10 @@ cdrom_boot(struct drive_s *drive_g)
     if (ret)
         return 12;
 
+    /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */
+    /* measure 1st 512 bytes */
+    tcpa_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(boot_segment, 0), 512);
+
     if (media == 0) {
         // No emulation requested - return success.
         SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid);
Index: seabios/src/tcgbios.c
===================================================================
--- seabios.orig/src/tcgbios.c
+++ seabios/src/tcgbios.c
@@ -16,6 +16,7 @@
 #include "tcgbios.h"// tcpa_*, prototypes
 #include "acpi.h"  // RSDP_SIGNATURE, rsdt_descriptor
 #include "sha1.h"  // sha1
+#include "smbios.h" // get_smbios_entry_point
 
 
 static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR };
@@ -49,6 +50,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)
 
@@ -1088,4 +1091,360 @@ tcpa_interrupt_handler32(struct bregs *r
 }
 
 
+/*
+ * 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
+tcpa_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
+tcpa_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
+tcpa_add_measurement(u32 pcrIndex,
+                     u16 event_type,
+                     const char *string)
+{
+    u32 rc;
+
+    switch (event_type) {
+    case EV_SEPARATOR:
+        rc = tcpa_add_measurement_to_log_simple(pcrIndex,
+                                                event_type,
+                                                (u8 *)evt_separator,
+                                                4);
+        break;
+
+    case EV_ACTION:
+        rc = tcpa_add_measurement_to_log(pcrIndex,
+                                         event_type,
+                                         string,
+                                         strlen(string));
+        break;
+
+    default:
+        rc = TCG_INVALID_INPUT_PARA;
+    }
+
+    return rc;
+}
+
+
+u32
+tcpa_calling_int19h(void)
+{
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
+    if (!has_working_tpm())
+        return TCG_GENERAL_ERROR;
+
+    return tcpa_add_measurement(4, EV_ACTION,
+                                "Calling INT 19h");
+}
+
+
+u32
+tcpa_returned_via_int18h(void)
+{
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
+    if (!has_working_tpm())
+        return TCG_GENERAL_ERROR;
+
+    return tcpa_add_measurement(4, EV_ACTION,
+                                "Return via INT 18h");
+}
+
+
+/*
+ * Add event separators for PCRs 0 to 7; specs 8.2.3
+ */
+u32
+tcpa_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 = tcpa_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.
+ */
+u32
+tcpa_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 tcpa_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
+tcpa_start_option_rom_scan(void)
+{
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
+    if (!has_working_tpm())
+        return TCG_GENERAL_ERROR;
+
+    return tcpa_add_measurement(2, EV_ACTION,
+                                "Start Option ROM Scan");
+}
+
+
+/*
+ * Add measurement to the log about an option rom
+ */
+u32
+tcpa_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 tcpa_add_measurement_to_log(2,
+                                       EV_EVENT_TAG,
+                                       (const char *)&pcctes,
+                                       sizeof(pcctes));
+}
+
+
+u32
+tcpa_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 = get_smbios_entry_point();
+
+    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 tcpa_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
+tcpa_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 = tcpa_add_measurement_to_log_simple(4,
+                                                EV_IPL,
+                                                addr,
+                                                length);
+        break;
+
+    case IPL_EL_TORITO_2:
+        /* specs: 8.2.5.6 El Torito */
+        rc = tcpa_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 = tcpa_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 = tcpa_add_measurement_to_log_simple(5,
+                                                EV_IPL_PARTITION_DATA,
+                                                addr + 0x1b8,
+                                                0x48);
+    }
+
+    return rc;
+}
+
+
 
Index: seabios/src/smbios.c
===================================================================
--- seabios.orig/src/smbios.c
+++ seabios/src/smbios.c
@@ -12,6 +12,8 @@
 
 struct smbios_entry_point *SMBiosAddr;
 
+static struct smbios_entry_point *smbios_ep;
+
 static void
 smbios_entry_point_init(u16 max_structure_size,
                         u16 structure_table_length,
@@ -32,6 +34,7 @@ smbios_entry_point_init(u16 max_structur
         free(finaltable);
         return;
     }
+    smbios_ep = ep;
     memcpy(finaltable, structure_table_address, structure_table_length);
 
     memcpy(ep->anchor_string, "_SM_", 4);
@@ -522,3 +525,12 @@ smbios_init(void)
     smbios_entry_point_init(max_struct_size, p - start, start, nr_structs);
     free(start);
 }
+
+struct smbios_entry_point *
+get_smbios_entry_point(void)
+{
+    if (!smbios_ep || memcmp(smbios_ep->anchor_string, "_SM_", 4) != 0)
+        return 0;
+
+    return smbios_ep;
+}
Index: seabios/src/smbios.h
===================================================================
--- seabios.orig/src/smbios.h
+++ seabios/src/smbios.h
@@ -3,6 +3,7 @@
 
 // smbios.c
 void smbios_init(void);
+struct smbios_entry_point *get_smbios_entry_point();
 
 /* SMBIOS entry point -- must be written to a 16-bit aligned address
    between 0xf0000 and 0xfffff.
Index: seabios/src/tcgbios.h
===================================================================
--- seabios.orig/src/tcgbios.h
+++ seabios/src/tcgbios.h
@@ -382,6 +382,14 @@ int has_working_tpm(void);
 u32 tcpa_startup(void);
 u32 tcpa_leave_bios(void);
 u32 tcpa_s3_resume(void);
+u32 tcpa_calling_int19h(void);
+u32 tcpa_returned_via_int18h(void);
+u32 tcpa_add_bootdevice(u32 bootcd, u32 bootdrv);
+u32 tcpa_add_event_separators(void);
+u32 tcpa_ipl(enum ipltype bootcd, const u8 *addr, u32 count);
+u32 tcpa_start_option_rom_scan(void);
+u32 tcpa_option_rom(const void *addr, u32 len);
+u32 tcpa_smbios_measure(void);
 
 
 #endif /* TCGBIOS_H */


