1#![expect(missing_docs)]
41#![forbid(unsafe_code)]
42
43pub mod resolver;
44#[cfg(feature = "fuzzing")]
45pub mod service;
46#[cfg(not(feature = "fuzzing"))]
47mod service;
48
49use chipset_device::ChipsetDevice;
50use chipset_device::io::IoError;
51use chipset_device::io::IoResult;
52use chipset_device::mmio::MmioIntercept;
53use chipset_device::pio::PortIoIntercept;
54use chipset_device::poll_device::PollDevice;
55use firmware_uefi_resources::LogLevel;
56use firmware_uefi_resources::UefiCommandSet;
57use firmware_uefi_resources::UefiConfig;
58use firmware_uefi_resources::platform::UefiLogger;
59use firmware_uefi_resources::platform::VsmConfig;
60use guestmem::GuestMemory;
61use inspect::InspectMut;
62use local_clock::InspectableLocalClock;
63use pal_async::local::block_on;
64use service::diagnostics::DEFAULT_LOGS_PER_PERIOD;
65use service::diagnostics::WATCHDOG_LOGS_PER_PERIOD;
66use std::convert::TryInto;
67use std::ops::RangeInclusive;
68use std::task::Context;
69use std::task::Poll;
70use thiserror::Error;
71use uefi_nvram_storage::VmmNvramStorage;
72use vmcore::device_state::ChangeDeviceState;
73use vmcore::vmtime::VmTimeSource;
74use watchdog_core::platform::WatchdogPlatform;
75
76#[derive(Debug, Error)]
77pub enum UefiInitError {
78 #[error("nvram setup error")]
79 NvramSetup(#[from] service::nvram::NvramSetupError),
80 #[error("nvram error")]
81 Nvram(#[from] service::nvram::NvramError),
82 #[error("event log error")]
83 EventLog(#[from] service::event_log::EventLogError),
84}
85
86#[derive(InspectMut)]
87struct UefiDeviceServices {
88 nvram: service::nvram::NvramServices,
89 event_log: service::event_log::EventLogServices,
90 uefi_watchdog: service::uefi_watchdog::UefiWatchdogServices,
91 #[inspect(mut)]
92 generation_id: service::generation_id::GenerationIdServices,
93 #[inspect(mut)]
94 time: service::time::TimeServices,
95 diagnostics: service::diagnostics::DiagnosticsServices,
96}
97
98const IO_PORT_RANGE_BEGIN: u16 = 0x28;
100const IO_PORT_RANGE_END: u16 = 0x2d;
104const MMIO_RANGE_BEGIN: u64 = 0xeffed000;
105const MMIO_RANGE_END: u64 = 0xeffedfff;
106
107const REGISTER_ADDRESS: u16 = 0x0;
108const REGISTER_DATA: u16 = 0x4;
109
110pub struct UefiRuntimeDeps<'a> {
112 pub gm: GuestMemory,
113 pub nvram_storage: Box<dyn VmmNvramStorage>,
114 pub logger: Box<dyn UefiLogger>,
115 pub vmtime: &'a VmTimeSource,
116 pub watchdog_platform: Box<dyn WatchdogPlatform>,
117 pub watchdog_recv: mesh::Receiver<()>,
118 pub generation_id_deps: generation_id::GenerationIdRuntimeDeps,
119 pub vsm_config: Option<Box<dyn VsmConfig>>,
120 pub time_source: Box<dyn InspectableLocalClock>,
121}
122
123#[derive(InspectMut)]
125#[inspect(extra = "UefiDevice::inspect_extra")]
126pub struct UefiDevice {
127 use_mmio: bool,
129 command_set: UefiCommandSet,
130 diagnostics_rate_limit: Option<u32>,
133
134 gm: GuestMemory,
136
137 #[inspect(mut)]
139 service: UefiDeviceServices,
140
141 #[inspect(hex)]
143 address: u32,
144
145 #[inspect(skip)]
147 watchdog_recv: mesh::Receiver<()>,
148}
149
150impl UefiDevice {
151 pub(crate) async fn new(
152 runtime_deps: UefiRuntimeDeps<'_>,
153 cfg: UefiConfig,
154 is_restoring: bool,
155 ) -> Result<Self, UefiInitError> {
156 let UefiRuntimeDeps {
157 gm,
158 nvram_storage,
159 logger,
160 vmtime,
161 watchdog_platform,
162 watchdog_recv,
163 generation_id_deps,
164 vsm_config,
165 time_source,
166 } = runtime_deps;
167
168 let uefi = UefiDevice {
170 use_mmio: cfg.use_mmio,
171 command_set: cfg.command_set,
172 diagnostics_rate_limit: cfg.diagnostics_rate_limit,
173 address: 0,
174 gm,
175 watchdog_recv,
176 service: UefiDeviceServices {
177 nvram: service::nvram::NvramServices::new(
178 nvram_storage,
179 cfg.base_template,
180 cfg.custom_uefi_json,
181 cfg.secure_boot,
182 vsm_config,
183 is_restoring,
184 )
185 .await?,
186 event_log: service::event_log::EventLogServices::new(logger),
187 uefi_watchdog: service::uefi_watchdog::UefiWatchdogServices::new(
188 vmtime.access("uefi-watchdog"),
189 watchdog_platform,
190 is_restoring,
191 )
192 .await,
193 generation_id: service::generation_id::GenerationIdServices::new(
194 cfg.initial_generation_id,
195 generation_id_deps,
196 ),
197 time: service::time::TimeServices::new(time_source),
198 diagnostics: service::diagnostics::DiagnosticsServices::new(
199 cfg.diagnostics_log_level,
200 ),
201 },
202 };
203
204 Ok(uefi)
205 }
206
207 fn resolve_rate_limit(&self, default_limit: u32) -> Option<u32> {
214 match self.diagnostics_rate_limit {
215 None => Some(default_limit),
216 Some(0) => None,
217 Some(n) => Some(n),
218 }
219 }
220
221 fn read_data(&mut self, addr: u32) -> u32 {
222 match UefiCommand(addr) {
223 UefiCommand::WATCHDOG_RESOLUTION
224 | UefiCommand::WATCHDOG_CONFIG
225 | UefiCommand::WATCHDOG_COUNT => {
226 let reg = bios_cmd_to_watchdog_register(UefiCommand(addr)).unwrap();
227 self.handle_watchdog_read(reg)
228 }
229 UefiCommand::NFIT_SIZE => 0, _ => {
231 tracelimit::warn_ratelimited!(?addr, "unknown uefi read");
232 !0
233 }
234 }
235 }
236
237 fn write_data(&mut self, addr: u32, data: u32) {
238 match UefiCommand(addr) {
239 UefiCommand::NVRAM => block_on(self.nvram_handle_command(data.into())),
240 UefiCommand::EVENT_LOG_FLUSH => self.event_log_flush(data),
241 UefiCommand::WATCHDOG_RESOLUTION
242 | UefiCommand::WATCHDOG_CONFIG
243 | UefiCommand::WATCHDOG_COUNT => {
244 let reg = bios_cmd_to_watchdog_register(UefiCommand(addr)).unwrap();
245 self.handle_watchdog_write(reg, data)
246 }
247 UefiCommand::GENERATION_ID_PTR_LOW => self.write_generation_id_low(data),
248 UefiCommand::GENERATION_ID_PTR_HIGH => self.write_generation_id_high(data),
249 UefiCommand::CRYPTO => self.crypto_handle_command(data.into()),
250 UefiCommand::BOOT_FINALIZE if self.command_set == UefiCommandSet::X64 => {
251 }
253 UefiCommand::GET_TIME if self.command_set == UefiCommandSet::Aarch64 => {
254 if let Err(err) = self.get_time(data as u64) {
255 tracelimit::error_ratelimited!(
256 error = &err as &dyn std::error::Error,
257 "failed to access memory for GET_TIME"
258 );
259 }
260 }
261 UefiCommand::SET_TIME if self.command_set == UefiCommandSet::Aarch64 => {
262 if let Err(err) = self.set_time(data as u64) {
263 tracelimit::error_ratelimited!(
264 error = &err as &dyn std::error::Error,
265 "failed to access memory for SET_TIME"
266 );
267 }
268 }
269 UefiCommand::SET_EFI_DIAGNOSTICS_GPA => {
270 tracelimit::info_ratelimited!(?addr, data, "set gpa for diagnostics");
271 self.service.diagnostics.set_gpa(data)
272 }
273 UefiCommand::PROCESS_EFI_DIAGNOSTICS => {
274 let _ = self.process_diagnostics(
275 false,
276 service::diagnostics::DiagnosticsEmitter::Tracing {
277 limit: self.resolve_rate_limit(DEFAULT_LOGS_PER_PERIOD),
278 },
279 None,
280 );
281 }
282 _ => tracelimit::warn_ratelimited!(addr, data, "unknown uefi write"),
283 }
284 }
285
286 fn inspect_extra(&mut self, resp: &mut inspect::Response<'_>) {
288 const USAGE: &str =
289 "Use: inspect -u <default|info|full>,<stdout|tracing> vm/uefi/process_diagnostics";
290
291 resp.field_mut_with("process_diagnostics", |v| {
292 let output = (|| {
293 let value = v?;
294 let (level_str, dest_str) = value.split_once(',').unwrap_or((value, "stdout"));
295
296 let log_level_override = match level_str {
297 "default" => Some(LogLevel::make_default()),
298 "info" => Some(LogLevel::make_info()),
299 "full" => Some(LogLevel::make_full()),
300 _ => return None,
301 };
302
303 Some(match dest_str {
304 "stdout" => match self.process_diagnostics(
305 true,
306 service::diagnostics::DiagnosticsEmitter::String,
307 log_level_override,
308 ) {
309 Ok(Some(output)) if output.is_empty() => {
310 "(no diagnostics entries found)".to_string()
311 }
312 Ok(Some(output)) => output,
313 Ok(None) => unreachable!("String emitter should return output"),
314 Err(error) => format!("error processing diagnostics: {error}"),
315 },
316 "tracing" => {
317 match self.process_diagnostics(
318 true,
319 service::diagnostics::DiagnosticsEmitter::Tracing { limit: None },
320 log_level_override,
321 ) {
322 Ok(_) => format!(
323 "processed diagnostics via tracing \
324 (log_level_override: {level_str})"
325 ),
326 Err(error) => {
327 format!("error processing diagnostics: {error}")
328 }
329 }
330 }
331 _ => return None,
332 })
333 })();
334
335 Result::<_, std::convert::Infallible>::Ok(output.unwrap_or_else(|| USAGE.to_string()))
336 });
337 }
338}
339
340impl ChangeDeviceState for UefiDevice {
341 fn start(&mut self) {}
342
343 async fn stop(&mut self) {}
344
345 async fn reset(&mut self) {
346 self.address = 0;
347
348 self.service.nvram.reset();
349 self.service.event_log.reset();
350 self.service.uefi_watchdog.watchdog.reset();
351 self.service.generation_id.reset();
352 self.service.diagnostics.reset();
353 }
354}
355
356impl ChipsetDevice for UefiDevice {
357 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
358 (!self.use_mmio).then_some(self)
359 }
360
361 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
362 self.use_mmio.then_some(self)
363 }
364
365 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
366 Some(self)
367 }
368}
369
370impl PollDevice for UefiDevice {
371 fn poll_device(&mut self, cx: &mut Context<'_>) {
372 self.service.uefi_watchdog.watchdog.poll(cx);
374 self.service.generation_id.poll(cx);
375
376 if let Poll::Ready(Ok(())) = self.watchdog_recv.poll_recv(cx) {
378 let _ = self.process_diagnostics(
382 false,
383 service::diagnostics::DiagnosticsEmitter::Tracing {
384 limit: self.resolve_rate_limit(WATCHDOG_LOGS_PER_PERIOD),
385 },
386 Some(LogLevel::make_info()),
387 );
388 }
389 }
390}
391
392impl PortIoIntercept for UefiDevice {
393 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
394 if data.len() != 4 {
395 return IoResult::Err(IoError::InvalidAccessSize);
396 }
397
398 let offset = io_port - IO_PORT_RANGE_BEGIN;
399
400 let v = match offset {
401 REGISTER_ADDRESS => self.address,
402 REGISTER_DATA => self.read_data(self.address),
403 _ => return IoResult::Err(IoError::InvalidRegister),
404 };
405
406 data.copy_from_slice(&v.to_ne_bytes());
407 IoResult::Ok
408 }
409
410 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
411 if data.len() != 4 {
412 return IoResult::Err(IoError::InvalidAccessSize);
413 }
414
415 let offset = io_port - IO_PORT_RANGE_BEGIN;
416
417 let v = u32::from_ne_bytes(data.try_into().unwrap());
418 match offset {
419 REGISTER_ADDRESS => {
420 self.address = v;
421 }
422 REGISTER_DATA => self.write_data(self.address, v),
423 _ => return IoResult::Err(IoError::InvalidRegister),
424 }
425 IoResult::Ok
426 }
427
428 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
429 &[("uefi", IO_PORT_RANGE_BEGIN..=IO_PORT_RANGE_END)]
430 }
431}
432
433impl MmioIntercept for UefiDevice {
434 fn mmio_read(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
435 if data.len() != 4 {
436 return IoResult::Err(IoError::InvalidAccessSize);
437 }
438
439 let v = match (addr - MMIO_RANGE_BEGIN) as u16 {
440 REGISTER_ADDRESS => self.address,
441 REGISTER_DATA => self.read_data(self.address),
442 _ => return IoResult::Err(IoError::InvalidRegister),
443 };
444
445 data.copy_from_slice(&v.to_ne_bytes());
446 IoResult::Ok
447 }
448
449 fn mmio_write(&mut self, addr: u64, data: &[u8]) -> IoResult {
450 let Ok(data) = data.try_into() else {
451 return IoResult::Err(IoError::InvalidAccessSize);
452 };
453
454 let v = u32::from_ne_bytes(data);
455 match (addr - MMIO_RANGE_BEGIN) as u16 {
456 REGISTER_ADDRESS => {
457 self.address = v;
458 }
459 REGISTER_DATA => self.write_data(self.address, v),
460 _ => return IoResult::Err(IoError::InvalidRegister),
461 }
462 IoResult::Ok
463 }
464
465 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
466 &[("uefi", MMIO_RANGE_BEGIN..=MMIO_RANGE_END)]
467 }
468}
469
470fn bios_cmd_to_watchdog_register(cmd: UefiCommand) -> Option<watchdog_core::Register> {
471 let res = match cmd {
472 UefiCommand::WATCHDOG_RESOLUTION => watchdog_core::Register::Resolution,
473 UefiCommand::WATCHDOG_CONFIG => watchdog_core::Register::Config,
474 UefiCommand::WATCHDOG_COUNT => watchdog_core::Register::Count,
475 _ => return None,
476 };
477 Some(res)
478}
479
480open_enum::open_enum! {
481 pub enum UefiCommand: u32 {
482 GENERATION_ID_PTR_LOW = 0x0E,
483 GENERATION_ID_PTR_HIGH = 0x0F,
484 BOOT_FINALIZE = 0x1A,
485
486 PROCESSOR_REPLY_STATUS_INDEX = 0x13,
487 PROCESSOR_REPLY_STATUS = 0x14,
488 PROCESSOR_MAT_ENABLE = 0x15,
489
490 NVRAM = 0x24,
492 CRYPTO = 0x26,
493
494 WATCHDOG_CONFIG = 0x27,
496 WATCHDOG_RESOLUTION = 0x28,
497 WATCHDOG_COUNT = 0x29,
498
499 SET_EFI_DIAGNOSTICS_GPA = 0x2B,
501 PROCESS_EFI_DIAGNOSTICS = 0x2C,
502
503 EVENT_LOG_FLUSH = 0x30,
505
506 MOR_SET_VARIABLE = 0x31,
513
514 GET_TIME = 0x34,
516 SET_TIME = 0x35,
517
518 DEBUG_OUTPUT_STRING = 0x36,
520
521 NFIT_SIZE = 0x37,
523 NFIT_POPULATE = 0x38,
524 VPMEM_SET_ACPI_BUFFER = 0x39,
525 }
526}
527
528mod save_restore {
529 use super::*;
530 use vmcore::save_restore::RestoreError;
531 use vmcore::save_restore::SaveError;
532 use vmcore::save_restore::SaveRestore;
533
534 mod state {
535 use crate::service::diagnostics::DiagnosticsServices;
536 use crate::service::event_log::EventLogServices;
537 use crate::service::generation_id::GenerationIdServices;
538 use crate::service::nvram::NvramServices;
539 use crate::service::time::TimeServices;
540 use crate::service::uefi_watchdog::UefiWatchdogServices;
541 use mesh::payload::Protobuf;
542 use vmcore::save_restore::SaveRestore;
543 use vmcore::save_restore::SavedStateRoot;
544
545 #[derive(Protobuf, SavedStateRoot)]
546 #[mesh(package = "firmware.uefi")]
547 pub struct SavedState {
548 #[mesh(1)]
549 pub address: u32,
550
551 #[mesh(2)]
552 pub nvram: <NvramServices as SaveRestore>::SavedState,
553 #[mesh(3)]
554 pub event_log: <EventLogServices as SaveRestore>::SavedState,
555 #[mesh(4)]
556 pub watchdog: <UefiWatchdogServices as SaveRestore>::SavedState,
557 #[mesh(5)]
558 pub generation_id: <GenerationIdServices as SaveRestore>::SavedState,
559 #[mesh(6)]
560 pub time: <TimeServices as SaveRestore>::SavedState,
561 #[mesh(7)]
562 pub diagnostics: <DiagnosticsServices as SaveRestore>::SavedState,
563 }
564 }
565
566 impl SaveRestore for UefiDevice {
567 type SavedState = state::SavedState;
568
569 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
570 let Self {
571 use_mmio: _,
572 command_set: _,
573 gm: _,
574 watchdog_recv: _,
575 service:
576 UefiDeviceServices {
577 nvram,
578 event_log,
579 uefi_watchdog,
580 generation_id,
581 time,
582 diagnostics,
583 },
584 address,
585 diagnostics_rate_limit: _,
586 } = self;
587
588 Ok(state::SavedState {
589 address: *address,
590
591 nvram: nvram.save()?,
592 event_log: event_log.save()?,
593 watchdog: uefi_watchdog.save()?,
594 generation_id: generation_id.save()?,
595 time: time.save()?,
596 diagnostics: diagnostics.save()?,
597 })
598 }
599
600 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
601 let state::SavedState {
602 address,
603
604 nvram,
605 event_log,
606 watchdog,
607 generation_id,
608 time,
609 diagnostics,
610 } = state;
611
612 self.address = address;
613
614 self.service.nvram.restore(nvram)?;
615 self.service.event_log.restore(event_log)?;
616 self.service.uefi_watchdog.restore(watchdog)?;
617 self.service.generation_id.restore(generation_id)?;
618 self.service.time.restore(time)?;
619 self.service.diagnostics.restore(diagnostics)?;
620
621 Ok(())
622 }
623 }
624}