firmware_uefi/service/diagnostics/
mod.rs1use crate::UefiDevice;
18use formatting::EfiDiagnosticsLog;
19use formatting::log_diagnostic_ratelimited;
20use formatting::log_diagnostic_unrestricted;
21use guestmem::GuestMemory;
22use inspect::Inspect;
23use mesh::payload::Protobuf;
24use processor::ProcessingError;
25use uefi_specs::hyperv::debug_level::DEBUG_ERROR;
26use uefi_specs::hyperv::debug_level::DEBUG_INFO;
27use uefi_specs::hyperv::debug_level::DEBUG_WARN;
28
29mod formatting;
30mod message_accumulator;
31mod parser;
32mod processor;
33
34pub const DEFAULT_LOGS_PER_PERIOD: u32 = 150;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Protobuf)]
39#[mesh(transparent)]
40pub struct LogLevel(u32);
41
42impl LogLevel {
43 pub const fn make_default() -> Self {
45 Self(DEBUG_ERROR | DEBUG_WARN)
46 }
47
48 pub const fn make_info() -> Self {
50 Self(DEBUG_ERROR | DEBUG_WARN | DEBUG_INFO)
51 }
52
53 pub const fn make_full() -> Self {
55 Self(u32::MAX)
56 }
57
58 pub fn should_log(self, raw_debug_level: u32) -> bool {
60 if self.0 == u32::MAX {
61 true } else {
63 (raw_debug_level & self.0) != 0
64 }
65 }
66}
67
68impl Default for LogLevel {
69 fn default() -> Self {
70 Self::make_default()
71 }
72}
73
74impl Inspect for LogLevel {
75 fn inspect(&self, req: inspect::Request<'_>) {
76 let human_readable = formatting::debug_level_to_string(self.0);
77 req.respond()
78 .field("raw_value", self.0)
79 .field("debug_levels", human_readable.as_ref());
80 }
81}
82
83#[derive(Inspect)]
85pub struct DiagnosticsServices {
86 gpa: Option<u32>,
88 has_guest_processed_before: bool,
90 log_level: LogLevel,
92}
93
94impl DiagnosticsServices {
95 pub fn new(log_level: LogLevel) -> DiagnosticsServices {
97 DiagnosticsServices {
98 gpa: None,
99 has_guest_processed_before: false,
100 log_level,
101 }
102 }
103
104 pub fn reset(&mut self) {
106 self.gpa = None;
107 self.has_guest_processed_before = false;
108 }
109
110 pub fn set_gpa(&mut self, gpa: u32) {
112 self.gpa = match gpa {
113 0 => None,
114 _ => Some(gpa),
115 }
116 }
117
118 fn process_diagnostics<F>(
125 &mut self,
126 allow_reprocess: bool,
127 gm: &GuestMemory,
128 log_handler: F,
129 ) -> Result<(), ProcessingError>
130 where
131 F: FnMut(EfiDiagnosticsLog<'_>, u32),
132 {
133 processor::process_diagnostics_internal(
135 &mut self.gpa,
136 &mut self.has_guest_processed_before,
137 allow_reprocess,
138 gm,
139 self.log_level,
140 log_handler,
141 )
142 }
143}
144
145impl UefiDevice {
146 pub(crate) fn process_diagnostics(&mut self, allow_reprocess: bool, limit: Option<u32>) {
155 if let Err(error) = self.service.diagnostics.process_diagnostics(
156 allow_reprocess,
157 &self.gm,
158 |log, raw_debug_level| match limit {
159 Some(limit) => log_diagnostic_ratelimited(log, raw_debug_level, limit),
160 None => log_diagnostic_unrestricted(log, raw_debug_level),
161 },
162 ) {
163 tracelimit::error_ratelimited!(
164 error = &error as &dyn std::error::Error,
165 "failed to process diagnostics buffer"
166 );
167 }
168 }
169}
170
171mod save_restore {
172 use super::*;
173 use vmcore::save_restore::RestoreError;
174 use vmcore::save_restore::SaveError;
175 use vmcore::save_restore::SaveRestore;
176
177 mod state {
178 use super::LogLevel;
179 use mesh::payload::Protobuf;
180 use vmcore::save_restore::SavedStateRoot;
181
182 #[derive(Protobuf, SavedStateRoot)]
183 #[mesh(package = "firmware.uefi.diagnostics")]
184 pub struct SavedState {
185 #[mesh(1)]
186 pub gpa: Option<u32>,
187 #[mesh(2)]
188 pub did_flush: bool,
189 #[mesh(3)]
190 pub log_level: LogLevel,
191 }
192 }
193
194 impl SaveRestore for DiagnosticsServices {
195 type SavedState = state::SavedState;
196
197 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
198 Ok(state::SavedState {
199 gpa: self.gpa,
200 did_flush: self.has_guest_processed_before,
201 log_level: self.log_level,
202 })
203 }
204
205 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
206 let state::SavedState {
207 gpa,
208 did_flush,
209 log_level,
210 } = state;
211 self.gpa = gpa;
212 self.has_guest_processed_before = did_flush;
213 self.log_level = log_level;
214 Ok(())
215 }
216 }
217}