linux_bootloader/
measure.rs

1use log::info;
2use uefi::{
3    cstr16,
4    proto::tcg::PcrIndex,
5    table::{runtime::VariableAttributes, Boot, SystemTable},
6};
7
8use crate::{
9    efivars::BOOT_LOADER_VENDOR_UUID, pe_section::pe_section_data, tpm::tpm_log_event_ascii,
10    uefi_helpers::PeInMemory, unified_sections::UnifiedSection,
11};
12
13const TPM_PCR_INDEX_KERNEL_IMAGE: PcrIndex = PcrIndex(11);
14
15pub unsafe fn measure_image(
16    system_table: &SystemTable<Boot>,
17    image: PeInMemory,
18) -> uefi::Result<u32> {
19    let runtime_services = system_table.runtime_services();
20    let boot_services = system_table.boot_services();
21
22    // SAFETY: We get a slice that represents our currently running
23    // image and then parse the PE data structures from it. This is
24    // safe, because we don't touch any data in the data sections that
25    // might conceivably change while we look at the slice.
26    // (data sections := all unified sections that can be measured.)
27    let pe_binary = unsafe { image.as_slice() };
28    let pe = goblin::pe::PE::parse(pe_binary).map_err(|_err| uefi::Status::LOAD_ERROR)?;
29
30    let mut measurements = 0;
31    for section in pe.sections {
32        let section_name = section.name().map_err(|_err| uefi::Status::UNSUPPORTED)?;
33        if let Ok(unified_section) = UnifiedSection::try_from(section_name) {
34            // UNSTABLE: && in the previous if is an unstable feature
35            // https://github.com/rust-lang/rust/issues/53667
36            if unified_section.should_be_measured() {
37                // Here, perform the TPM log event in ASCII.
38                if let Some(data) = pe_section_data(pe_binary, &section) {
39                    info!("Measuring section `{}`...", section_name);
40                    if tpm_log_event_ascii(
41                        boot_services,
42                        TPM_PCR_INDEX_KERNEL_IMAGE,
43                        data,
44                        section_name,
45                    )? {
46                        measurements += 1;
47                    }
48                }
49            }
50        }
51    }
52
53    if measurements > 0 {
54        // If we did some measurements, expose a variable encoding the PCR where
55        // we have done the measurements.
56        runtime_services.set_variable(
57            cstr16!("StubPcrKernelImage"),
58            &BOOT_LOADER_VENDOR_UUID,
59            VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS,
60            &TPM_PCR_INDEX_KERNEL_IMAGE.0.to_le_bytes(),
61        )?;
62    }
63
64    Ok(measurements)
65}