| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2018 Facebook Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <console/console.h> |
| #include <fmap.h> |
| #include <cbfs.h> |
| #include <security/vboot/vboot_crtm.h> |
| #include <security/vboot/misc.h> |
| #include <string.h> |
| |
| /* |
| * This functions sets the TCPA log namespace |
| * for the cbfs file (region) lookup. |
| */ |
| static int create_tcpa_metadata(const struct region_device *rdev, |
| const char *cbfs_name, char log_string[TCPA_PCR_HASH_NAME]) |
| { |
| int i; |
| struct region_device fmap; |
| static const char *fmap_cbfs_names[] = { |
| "COREBOOT", |
| "FW_MAIN_A", |
| "FW_MAIN_B", |
| "RW_LEGACY"}; |
| |
| for (i = 0; i < ARRAY_SIZE(fmap_cbfs_names); i++) { |
| if (fmap_locate_area_as_rdev(fmap_cbfs_names[i], &fmap) == 0) { |
| if (region_is_subregion(region_device_region(&fmap), |
| region_device_region(rdev))) { |
| snprintf(log_string, TCPA_PCR_HASH_NAME, |
| "FMAP: %s CBFS: %s", |
| fmap_cbfs_names[i], cbfs_name); |
| return 0; |
| } |
| } |
| } |
| |
| return -1; |
| } |
| |
| uint32_t vboot_init_crtm(void) |
| { |
| struct prog bootblock = PROG_INIT(PROG_BOOTBLOCK, "bootblock"); |
| struct prog verstage = |
| PROG_INIT(PROG_VERSTAGE, CONFIG_CBFS_PREFIX "/verstage"); |
| struct prog romstage = |
| PROG_INIT(PROG_ROMSTAGE, CONFIG_CBFS_PREFIX "/romstage"); |
| char tcpa_metadata[TCPA_PCR_HASH_NAME]; |
| |
| /* Initialize TCPE PRERAM log. */ |
| tcpa_preram_log_clear(); |
| |
| /* measure bootblock from RO */ |
| struct cbfsf bootblock_data; |
| struct region_device bootblock_fmap; |
| if (fmap_locate_area_as_rdev("BOOTBLOCK", &bootblock_fmap) == 0) { |
| if (tpm_measure_region(&bootblock_fmap, |
| TPM_CRTM_PCR, |
| "FMAP: BOOTBLOCK")) |
| return VB2_ERROR_UNKNOWN; |
| } else { |
| if (cbfs_boot_locate(&bootblock_data, |
| prog_name(&bootblock), NULL) == 0) { |
| cbfs_file_data(prog_rdev(&bootblock), &bootblock_data); |
| |
| if (create_tcpa_metadata(prog_rdev(&bootblock), |
| prog_name(&bootblock), tcpa_metadata) < 0) |
| return VB2_ERROR_UNKNOWN; |
| |
| if (tpm_measure_region(prog_rdev(&bootblock), |
| TPM_CRTM_PCR, |
| tcpa_metadata)) |
| return VB2_ERROR_UNKNOWN; |
| } else { |
| printk(BIOS_INFO, |
| "VBOOT: Couldn't measure bootblock into CRTM!\n"); |
| return VB2_ERROR_UNKNOWN; |
| } |
| } |
| |
| if (CONFIG(VBOOT_STARTS_IN_ROMSTAGE)) { |
| struct cbfsf romstage_data; |
| /* measure romstage from RO */ |
| if (cbfs_boot_locate(&romstage_data, |
| prog_name(&romstage), NULL) == 0) { |
| cbfs_file_data(prog_rdev(&romstage), &romstage_data); |
| |
| if (create_tcpa_metadata(prog_rdev(&romstage), |
| prog_name(&romstage), tcpa_metadata) < 0) |
| return VB2_ERROR_UNKNOWN; |
| |
| if (tpm_measure_region(prog_rdev(&romstage), |
| TPM_CRTM_PCR, |
| tcpa_metadata)) |
| return VB2_ERROR_UNKNOWN; |
| } else { |
| printk(BIOS_INFO, |
| "VBOOT: Couldn't measure %s into CRTM!\n", |
| CONFIG_CBFS_PREFIX "/romstage"); |
| return VB2_ERROR_UNKNOWN; |
| } |
| } |
| |
| if (CONFIG(VBOOT_SEPARATE_VERSTAGE)) { |
| struct cbfsf verstage_data; |
| /* measure verstage from RO */ |
| if (cbfs_boot_locate(&verstage_data, |
| prog_name(&verstage), NULL) == 0) { |
| cbfs_file_data(prog_rdev(&verstage), &verstage_data); |
| |
| if (create_tcpa_metadata(prog_rdev(&verstage), |
| prog_name(&verstage), tcpa_metadata) < 0) |
| return VB2_ERROR_UNKNOWN; |
| |
| if (tpm_measure_region(prog_rdev(&verstage), |
| TPM_CRTM_PCR, |
| tcpa_metadata)) |
| return VB2_ERROR_UNKNOWN; |
| } else { |
| printk(BIOS_INFO, |
| "VBOOT: Couldn't measure %s into CRTM!\n", |
| CONFIG_CBFS_PREFIX "/verstage"); |
| return VB2_ERROR_UNKNOWN; |
| } |
| } |
| |
| return VB2_SUCCESS; |
| } |
| |
| static bool is_runtime_data(const char *name) |
| { |
| const char *whitelist = CONFIG_VBOOT_MEASURED_BOOT_RUNTIME_DATA; |
| size_t whitelist_len = sizeof(CONFIG_VBOOT_MEASURED_BOOT_RUNTIME_DATA) - 1; |
| size_t name_len = strlen(name); |
| int i; |
| |
| if (!whitelist_len || !name_len) |
| return false; |
| |
| for (i = 0; (i + name_len) <= whitelist_len; i++) { |
| if (!strcmp(whitelist + i, name)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| uint32_t vboot_measure_cbfs_hook(struct cbfsf *fh, const char *name) |
| { |
| uint32_t pcr_index; |
| uint32_t cbfs_type; |
| struct region_device rdev; |
| char tcpa_metadata[TCPA_PCR_HASH_NAME]; |
| |
| if (!vboot_logic_executed()) |
| return 0; |
| |
| cbfsf_file_type(fh, &cbfs_type); |
| cbfs_file_data(&rdev, fh); |
| |
| switch (cbfs_type) { |
| case CBFS_TYPE_MRC: |
| case CBFS_TYPE_MRC_CACHE: |
| pcr_index = TPM_RUNTIME_DATA_PCR; |
| break; |
| case CBFS_TYPE_STAGE: |
| case CBFS_TYPE_SELF: |
| case CBFS_TYPE_FIT: |
| pcr_index = TPM_CRTM_PCR; |
| break; |
| default: |
| if (is_runtime_data(name)) |
| pcr_index = TPM_RUNTIME_DATA_PCR; |
| else |
| pcr_index = TPM_CRTM_PCR; |
| break; |
| } |
| |
| if (create_tcpa_metadata(&rdev, name, tcpa_metadata) < 0) |
| return VB2_ERROR_UNKNOWN; |
| |
| return tpm_measure_region(&rdev, pcr_index, tcpa_metadata); |
| } |