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