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 std::convert::TryInto;
74use std::ops::RangeInclusive;
75use std::task::Context;
76use thiserror::Error;
77use uefi_nvram_storage::VmmNvramStorage;
78use vmcore::device_state::ChangeDeviceState;
79use vmcore::vmtime::VmTimeSource;
80use watchdog_core::platform::WatchdogPlatform;
81
82#[derive(Debug, Error)]
83pub enum UefiInitError {
84 #[error("nvram setup error")]
85 NvramSetup(#[from] service::nvram::NvramSetupError),
86 #[error("nvram error")]
87 Nvram(#[from] service::nvram::NvramError),
88 #[error("event log error")]
89 EventLog(#[from] service::event_log::EventLogError),
90}
91
92#[derive(Inspect, PartialEq, Clone)]
93pub enum UefiCommandSet {
94 X64,
95 Aarch64,
96}
97
98#[derive(InspectMut)]
99struct UefiDeviceServices {
100 nvram: service::nvram::NvramServices,
101 event_log: service::event_log::EventLogServices,
102 uefi_watchdog: service::uefi_watchdog::UefiWatchdogServices,
103 #[inspect(mut)]
104 generation_id: service::generation_id::GenerationIdServices,
105 #[inspect(mut)]
106 time: service::time::TimeServices,
107 diagnostics: service::diagnostics::DiagnosticsServices,
108}
109
110const IO_PORT_RANGE_BEGIN: u16 = 0x28;
112const IO_PORT_RANGE_END: u16 = 0x2f;
113const MMIO_RANGE_BEGIN: u64 = 0xeffed000;
114const MMIO_RANGE_END: u64 = 0xeffedfff;
115
116const REGISTER_ADDRESS: u16 = 0x0;
117const REGISTER_DATA: u16 = 0x4;
118
119#[derive(Clone)]
121pub struct UefiConfig {
122 pub custom_uefi_vars: CustomVars,
123 pub secure_boot: bool,
124 pub initial_generation_id: [u8; 16],
125 pub use_mmio: bool,
126 pub command_set: UefiCommandSet,
127}
128
129pub struct UefiRuntimeDeps<'a> {
131 pub gm: GuestMemory,
132 pub nvram_storage: Box<dyn VmmNvramStorage>,
133 pub logger: Box<dyn UefiLogger>,
134 pub vmtime: &'a VmTimeSource,
135 pub watchdog_platform: Box<dyn WatchdogPlatform>,
136 pub generation_id_deps: generation_id::GenerationIdRuntimeDeps,
137 pub vsm_config: Option<Box<dyn VsmConfig>>,
138 pub time_source: Box<dyn InspectableLocalClock>,
139}
140
141#[derive(InspectMut)]
143pub struct UefiDevice {
144 use_mmio: bool,
146 command_set: UefiCommandSet,
147
148 gm: GuestMemory,
150
151 #[inspect(mut)]
153 service: UefiDeviceServices,
154
155 #[inspect(hex)]
157 address: u32,
158}
159
160impl UefiDevice {
161 pub async fn new(
162 runtime_deps: UefiRuntimeDeps<'_>,
163 cfg: UefiConfig,
164 is_restoring: bool,
165 ) -> Result<Self, UefiInitError> {
166 let UefiRuntimeDeps {
167 gm,
168 nvram_storage,
169 logger,
170 vmtime,
171 watchdog_platform,
172 generation_id_deps,
173 vsm_config,
174 time_source,
175 } = runtime_deps;
176
177 let uefi = UefiDevice {
178 use_mmio: cfg.use_mmio,
179 command_set: cfg.command_set,
180 address: 0,
181 gm,
182 service: UefiDeviceServices {
183 nvram: service::nvram::NvramServices::new(
184 nvram_storage,
185 cfg.custom_uefi_vars,
186 cfg.secure_boot,
187 vsm_config,
188 is_restoring,
189 )
190 .await?,
191 event_log: service::event_log::EventLogServices::new(logger),
192 uefi_watchdog: service::uefi_watchdog::UefiWatchdogServices::new(
193 vmtime.access("uefi-watchdog"),
194 watchdog_platform,
195 is_restoring,
196 )
197 .await,
198 generation_id: service::generation_id::GenerationIdServices::new(
199 cfg.initial_generation_id,
200 generation_id_deps,
201 ),
202 time: service::time::TimeServices::new(time_source),
203 diagnostics: service::diagnostics::DiagnosticsServices::new(),
204 },
205 };
206 Ok(uefi)
207 }
208
209 fn read_data(&mut self, addr: u32) -> u32 {
210 match UefiCommand(addr) {
211 UefiCommand::WATCHDOG_RESOLUTION
212 | UefiCommand::WATCHDOG_CONFIG
213 | UefiCommand::WATCHDOG_COUNT => {
214 let reg = bios_cmd_to_watchdog_register(UefiCommand(addr)).unwrap();
215 self.handle_watchdog_read(reg)
216 }
217 UefiCommand::NFIT_SIZE => 0, _ => {
219 tracelimit::warn_ratelimited!(?addr, "unknown uefi read");
220 !0
221 }
222 }
223 }
224
225 fn write_data(&mut self, addr: u32, data: u32) {
226 match UefiCommand(addr) {
227 UefiCommand::NVRAM => block_on(self.nvram_handle_command(data.into())),
228 UefiCommand::EVENT_LOG_FLUSH => self.event_log_flush(data),
229 UefiCommand::WATCHDOG_RESOLUTION
230 | UefiCommand::WATCHDOG_CONFIG
231 | UefiCommand::WATCHDOG_COUNT => {
232 let reg = bios_cmd_to_watchdog_register(UefiCommand(addr)).unwrap();
233 self.handle_watchdog_write(reg, data)
234 }
235 UefiCommand::GENERATION_ID_PTR_LOW => self.write_generation_id_low(data),
236 UefiCommand::GENERATION_ID_PTR_HIGH => self.write_generation_id_high(data),
237 UefiCommand::CRYPTO => self.crypto_handle_command(data.into()),
238 UefiCommand::BOOT_FINALIZE if self.command_set == UefiCommandSet::X64 => {
239 }
241 UefiCommand::GET_TIME if self.command_set == UefiCommandSet::Aarch64 => {
242 if let Err(err) = self.get_time(data as u64) {
243 tracelimit::error_ratelimited!(
244 error = &err as &dyn std::error::Error,
245 "failed to access memory for GET_TIME"
246 );
247 }
248 }
249 UefiCommand::SET_TIME if self.command_set == UefiCommandSet::Aarch64 => {
250 if let Err(err) = self.set_time(data as u64) {
251 tracelimit::error_ratelimited!(
252 error = &err as &dyn std::error::Error,
253 "failed to access memory for SET_TIME"
254 );
255 }
256 }
257 UefiCommand::SET_EFI_DIAGNOSTICS_GPA => self.service.diagnostics.set_gpa(data),
258 UefiCommand::PROCESS_EFI_DIAGNOSTICS => self.process_diagnostics(),
259 _ => tracelimit::warn_ratelimited!(addr, data, "unknown uefi write"),
260 }
261 }
262}
263
264impl ChangeDeviceState for UefiDevice {
265 fn start(&mut self) {}
266
267 async fn stop(&mut self) {}
268
269 async fn reset(&mut self) {
270 self.address = 0;
271
272 self.service.nvram.reset();
273 self.service.event_log.reset();
274 self.service.uefi_watchdog.watchdog.reset();
275 self.service.generation_id.reset();
276 }
277}
278
279impl ChipsetDevice for UefiDevice {
280 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
281 (!self.use_mmio).then_some(self)
282 }
283
284 fn supports_mmio(&mut self) -> Option<&mut dyn MmioIntercept> {
285 self.use_mmio.then_some(self)
286 }
287
288 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
289 Some(self)
290 }
291}
292
293impl PollDevice for UefiDevice {
294 fn poll_device(&mut self, cx: &mut Context<'_>) {
295 self.service.uefi_watchdog.watchdog.poll(cx);
296 self.service.generation_id.poll(cx);
297 }
298}
299
300impl PortIoIntercept for UefiDevice {
301 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
302 if data.len() != 4 {
303 return IoResult::Err(IoError::InvalidAccessSize);
304 }
305
306 let offset = io_port - IO_PORT_RANGE_BEGIN;
307
308 let v = match offset {
309 REGISTER_ADDRESS => self.address,
310 REGISTER_DATA => self.read_data(self.address),
311 _ => return IoResult::Err(IoError::InvalidRegister),
312 };
313
314 data.copy_from_slice(&v.to_ne_bytes());
315 IoResult::Ok
316 }
317
318 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
319 if data.len() != 4 {
320 return IoResult::Err(IoError::InvalidAccessSize);
321 }
322
323 let offset = io_port - IO_PORT_RANGE_BEGIN;
324
325 let v = u32::from_ne_bytes(data.try_into().unwrap());
326 match offset {
327 REGISTER_ADDRESS => {
328 self.address = v;
329 }
330 REGISTER_DATA => self.write_data(self.address, v),
331 _ => return IoResult::Err(IoError::InvalidRegister),
332 }
333 IoResult::Ok
334 }
335
336 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
337 &[("uefi", IO_PORT_RANGE_BEGIN..=IO_PORT_RANGE_END)]
338 }
339}
340
341impl MmioIntercept for UefiDevice {
342 fn mmio_read(&mut self, addr: u64, data: &mut [u8]) -> IoResult {
343 if data.len() != 4 {
344 return IoResult::Err(IoError::InvalidAccessSize);
345 }
346
347 let v = match (addr - MMIO_RANGE_BEGIN) as u16 {
348 REGISTER_ADDRESS => self.address,
349 REGISTER_DATA => self.read_data(self.address),
350 _ => return IoResult::Err(IoError::InvalidRegister),
351 };
352
353 data.copy_from_slice(&v.to_ne_bytes());
354 IoResult::Ok
355 }
356
357 fn mmio_write(&mut self, addr: u64, data: &[u8]) -> IoResult {
358 let Ok(data) = data.try_into() else {
359 return IoResult::Err(IoError::InvalidAccessSize);
360 };
361
362 let v = u32::from_ne_bytes(data);
363 match (addr - MMIO_RANGE_BEGIN) as u16 {
364 REGISTER_ADDRESS => {
365 self.address = v;
366 }
367 REGISTER_DATA => self.write_data(self.address, v),
368 _ => return IoResult::Err(IoError::InvalidRegister),
369 }
370 IoResult::Ok
371 }
372
373 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u64>)] {
374 &[("uefi", MMIO_RANGE_BEGIN..=MMIO_RANGE_END)]
375 }
376}
377
378fn bios_cmd_to_watchdog_register(cmd: UefiCommand) -> Option<watchdog_core::Register> {
379 let res = match cmd {
380 UefiCommand::WATCHDOG_RESOLUTION => watchdog_core::Register::Resolution,
381 UefiCommand::WATCHDOG_CONFIG => watchdog_core::Register::Config,
382 UefiCommand::WATCHDOG_COUNT => watchdog_core::Register::Count,
383 _ => return None,
384 };
385 Some(res)
386}
387
388open_enum::open_enum! {
389 pub enum UefiCommand: u32 {
390 GENERATION_ID_PTR_LOW = 0x0E,
391 GENERATION_ID_PTR_HIGH = 0x0F,
392 BOOT_FINALIZE = 0x1A,
393
394 PROCESSOR_REPLY_STATUS_INDEX = 0x13,
395 PROCESSOR_REPLY_STATUS = 0x14,
396 PROCESSOR_MAT_ENABLE = 0x15,
397
398 NVRAM = 0x24,
400 CRYPTO = 0x26,
401
402 WATCHDOG_CONFIG = 0x27,
404 WATCHDOG_RESOLUTION = 0x28,
405 WATCHDOG_COUNT = 0x29,
406
407 SET_EFI_DIAGNOSTICS_GPA = 0x2B,
409 PROCESS_EFI_DIAGNOSTICS = 0x2C,
410
411 EVENT_LOG_FLUSH = 0x30,
413
414 MOR_SET_VARIABLE = 0x31,
421
422 GET_TIME = 0x34,
424 SET_TIME = 0x35,
425
426 DEBUG_OUTPUT_STRING = 0x36,
428
429 NFIT_SIZE = 0x37,
431 NFIT_POPULATE = 0x38,
432 VPMEM_SET_ACPI_BUFFER = 0x39,
433 }
434}
435
436mod save_restore {
437 use super::*;
438 use vmcore::save_restore::RestoreError;
439 use vmcore::save_restore::SaveError;
440 use vmcore::save_restore::SaveRestore;
441
442 mod state {
443 use crate::service::diagnostics::DiagnosticsServices;
444 use crate::service::event_log::EventLogServices;
445 use crate::service::generation_id::GenerationIdServices;
446 use crate::service::nvram::NvramServices;
447 use crate::service::time::TimeServices;
448 use crate::service::uefi_watchdog::UefiWatchdogServices;
449 use mesh::payload::Protobuf;
450 use vmcore::save_restore::SaveRestore;
451 use vmcore::save_restore::SavedStateRoot;
452
453 #[derive(Protobuf, SavedStateRoot)]
454 #[mesh(package = "firmware.uefi")]
455 pub struct SavedState {
456 #[mesh(1)]
457 pub address: u32,
458
459 #[mesh(2)]
460 pub nvram: <NvramServices as SaveRestore>::SavedState,
461 #[mesh(3)]
462 pub event_log: <EventLogServices as SaveRestore>::SavedState,
463 #[mesh(4)]
464 pub watchdog: <UefiWatchdogServices as SaveRestore>::SavedState,
465 #[mesh(5)]
466 pub generation_id: <GenerationIdServices as SaveRestore>::SavedState,
467 #[mesh(6)]
468 pub time: <TimeServices as SaveRestore>::SavedState,
469 #[mesh(7)]
470 pub diagnostics: <DiagnosticsServices as SaveRestore>::SavedState,
471 }
472 }
473
474 impl SaveRestore for UefiDevice {
475 type SavedState = state::SavedState;
476
477 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
478 let Self {
479 use_mmio: _,
480 command_set: _,
481 gm: _,
482 service:
483 UefiDeviceServices {
484 nvram,
485 event_log,
486 uefi_watchdog,
487 generation_id,
488 time,
489 diagnostics,
490 },
491 address,
492 } = self;
493
494 Ok(state::SavedState {
495 address: *address,
496
497 nvram: nvram.save()?,
498 event_log: event_log.save()?,
499 watchdog: uefi_watchdog.save()?,
500 generation_id: generation_id.save()?,
501 time: time.save()?,
502 diagnostics: diagnostics.save()?,
503 })
504 }
505
506 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
507 let state::SavedState {
508 address,
509
510 nvram,
511 event_log,
512 watchdog,
513 generation_id,
514 time,
515 diagnostics,
516 } = state;
517
518 self.address = address;
519
520 self.service.nvram.restore(nvram)?;
521 self.service.event_log.restore(event_log)?;
522 self.service.uefi_watchdog.restore(watchdog)?;
523 self.service.generation_id.restore(generation_id)?;
524 self.service.time.restore(time)?;
525 self.service.diagnostics.restore(diagnostics)?;
526
527 Ok(())
528 }
529 }
530}