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