firmware_uefi/service/
event_log.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! UEFI Event Logging subsystem.
5
6use crate::UefiDevice;
7use crate::platform::logger::BootInfo;
8use crate::platform::logger::UefiEvent;
9use crate::platform::logger::UefiLogger;
10use guestmem::GuestMemory;
11use guestmem::GuestMemoryError;
12use inspect::Inspect;
13use std::fmt::Debug;
14use thiserror::Error;
15use zerocopy::FromBytes;
16
17#[derive(Debug, Error)]
18pub enum EventLogError {
19    #[error("error converting bytes")]
20    ConvertBytes,
21    #[error("could not access guest memory")]
22    Memory(#[source] GuestMemoryError),
23    #[error("invalid event channel data size")]
24    EventChannelDataSize,
25    #[error("invalid boot event size")]
26    BootEventSize,
27    #[error("invalid event size")]
28    EventSize,
29    #[error("no boot events present in log")]
30    NoBootEvents,
31}
32
33#[derive(Inspect)]
34pub struct EventLogServices {
35    #[inspect(skip)]
36    logger: Box<dyn UefiLogger>,
37}
38
39impl EventLogServices {
40    pub fn new(logger: Box<dyn UefiLogger>) -> EventLogServices {
41        EventLogServices { logger }
42    }
43
44    pub fn reset(&mut self) {
45        // Nothing to do.
46    }
47
48    fn event_log_flush_inner(&mut self, gpa: u64, gm: &GuestMemory) -> Result<(), EventLogError> {
49        use uefi_specs::hyperv::bios_event_log::BiosEventChannel;
50        use uefi_specs::hyperv::bios_event_log::EfiEventDescriptor;
51        use uefi_specs::hyperv::boot_bios_log::BootDeviceStatus;
52        use uefi_specs::hyperv::boot_bios_log::BootEventDeviceEntry;
53
54        let event_channel = gm
55            .read_plain::<BiosEventChannel>(gpa)
56            .map_err(EventLogError::Memory)?;
57
58        // Limit max size, UEFI does not log many events
59        const EVENT_CHANNEL_MAX_DATA_SIZE: u32 = 16 * 1024;
60
61        // Sanity check data size
62        if event_channel.data_size < size_of::<EfiEventDescriptor>() as u32
63            || event_channel.data_size > EVENT_CHANNEL_MAX_DATA_SIZE
64        {
65            return Err(EventLogError::EventChannelDataSize);
66        }
67
68        // read channel data
69        let mut event_data = vec![0; event_channel.data_size as usize];
70        gm.read_at(gpa + size_of::<BiosEventChannel>() as u64, &mut event_data)
71            .map_err(EventLogError::Memory)?;
72        let mut event_data = event_data.as_slice();
73
74        // Merge the boot events together, aggregating an arbitrary subset of
75        // the available diagnostics information.
76        //
77        // TODO: determine if we really want to merge events in this way instead
78        // of just logging them individually.
79        let mut boot_succeeded = false;
80        let mut no_boot_devices = false;
81        let mut secure_boot_failure = None;
82        let mut last_boot_event = None;
83
84        while !event_data.is_empty() {
85            let desc = EfiEventDescriptor::read_from_prefix(event_data)
86                .map_err(|_| EventLogError::ConvertBytes)?
87                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
88
89            let data = event_data
90                .get(desc.header_size as usize..)
91                .ok_or(EventLogError::EventSize)?
92                .get(..desc.data_size as usize)
93                .ok_or(EventLogError::EventSize)?;
94
95            // Advance to the next event.
96            event_data = &event_data[(desc.header_size + desc.data_size) as usize..];
97
98            match desc.event_id {
99                uefi_specs::hyperv::boot_bios_log::BOOT_DEVICE_EVENT_ID => {
100                    let boot_entry = BootEventDeviceEntry::read_from_prefix(data)
101                        .map_err(|_| EventLogError::BootEventSize)?
102                        .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
103
104                    tracing::debug!(?boot_entry, "boot log entry");
105
106                    match boot_entry.status {
107                        BootDeviceStatus::BOOT_DEVICE_OS_LOADED => boot_succeeded = true,
108                        BootDeviceStatus::BOOT_DEVICE_NO_DEVICES => no_boot_devices = true,
109                        _ if boot_entry.status.get_boot_device_status_group()
110                            == BootDeviceStatus::SECURE_BOOT_FAILED.0 =>
111                        {
112                            secure_boot_failure = Some(boot_entry.status);
113                        }
114                        _ => {}
115                    }
116
117                    last_boot_event = Some(boot_entry);
118                }
119                id => {
120                    tracelimit::warn_ratelimited!(id, "unsupported uefi event log id");
121                }
122            }
123        }
124
125        let last_boot_event = last_boot_event.ok_or(EventLogError::NoBootEvents)?;
126        let boot_info = BootInfo {
127            secure_boot_succeeded: secure_boot_failure.is_none(),
128        };
129
130        // Don't log the secure boot failure code twice if it's the reason for
131        // the boot failure.
132        let secure_boot_error = if secure_boot_failure != Some(last_boot_event.status) {
133            secure_boot_failure.map(tracing::field::debug)
134        } else {
135            None
136        };
137
138        let event = if no_boot_devices {
139            tracelimit::info_ratelimited!("uefi boot: no boot devices");
140            UefiEvent::NoBootDevice
141        } else if boot_succeeded {
142            tracelimit::info_ratelimited!(secure_boot_error, "uefi boot: success");
143            UefiEvent::BootSuccess(boot_info)
144        } else {
145            tracelimit::info_ratelimited!(
146                error = ?last_boot_event.status,
147                extended_status = ?last_boot_event.extended_status,
148                secure_boot_error,
149                "uefi boot: failure",
150            );
151            UefiEvent::BootFailure(boot_info)
152        };
153        self.logger.log_event(event);
154        Ok(())
155    }
156}
157
158impl UefiDevice {
159    /// Reads guest memory and logs the boot status to the host.
160    pub(crate) fn event_log_flush(&mut self, data: u32) {
161        if let Err(err) = self
162            .service
163            .event_log
164            .event_log_flush_inner(data.into(), &self.gm)
165        {
166            tracelimit::error_ratelimited!(
167                error = &err as &dyn std::error::Error,
168                "event log flush error"
169            );
170        }
171    }
172}
173
174mod save_restore {
175    use super::*;
176    use vmcore::save_restore::NoSavedState;
177    use vmcore::save_restore::RestoreError;
178    use vmcore::save_restore::SaveError;
179    use vmcore::save_restore::SaveRestore;
180
181    impl SaveRestore for EventLogServices {
182        type SavedState = NoSavedState;
183
184        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
185            Ok(NoSavedState)
186        }
187
188        fn restore(&mut self, NoSavedState: Self::SavedState) -> Result<(), RestoreError> {
189            Ok(())
190        }
191    }
192}