use log::info; use uefi::{ cstr16, proto::tcg::PcrIndex, table::{runtime::VariableAttributes, Boot, SystemTable}, }; use crate::{ efivars::BOOT_LOADER_VENDOR_UUID, pe_section::pe_section_data, tpm::tpm_log_event_ascii, uefi_helpers::PeInMemory, unified_sections::UnifiedSection, }; const TPM_PCR_INDEX_KERNEL_IMAGE: PcrIndex = PcrIndex(11); pub unsafe fn measure_image( system_table: &SystemTable, image: PeInMemory, ) -> uefi::Result { let runtime_services = system_table.runtime_services(); let boot_services = system_table.boot_services(); // SAFETY: We get a slice that represents our currently running // image and then parse the PE data structures from it. This is // safe, because we don't touch any data in the data sections that // might conceivably change while we look at the slice. // (data sections := all unified sections that can be measured.) let pe_binary = unsafe { image.as_slice() }; let pe = goblin::pe::PE::parse(pe_binary).map_err(|_err| uefi::Status::LOAD_ERROR)?; let mut measurements = 0; for section in pe.sections { let section_name = section.name().map_err(|_err| uefi::Status::UNSUPPORTED)?; if let Ok(unified_section) = UnifiedSection::try_from(section_name) { // UNSTABLE: && in the previous if is an unstable feature // https://github.com/rust-lang/rust/issues/53667 if unified_section.should_be_measured() { // Here, perform the TPM log event in ASCII. if let Some(data) = pe_section_data(pe_binary, §ion) { info!("Measuring section `{}`...", section_name); if tpm_log_event_ascii( boot_services, TPM_PCR_INDEX_KERNEL_IMAGE, data, section_name, )? { measurements += 1; } } } } } if measurements > 0 { // If we did some measurements, expose a variable encoding the PCR where // we have done the measurements. runtime_services.set_variable( cstr16!("StubPcrKernelImage"), &BOOT_LOADER_VENDOR_UUID, VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS, &TPM_PCR_INDEX_KERNEL_IMAGE.0.to_le_bytes(), )?; } Ok(measurements) }