diff --git a/vm/devices/firmware/firmware_uefi/src/lib.rs b/vm/devices/firmware/firmware_uefi/src/lib.rs index 78e5135487..1fa60828fe 100644 --- a/vm/devices/firmware/firmware_uefi/src/lib.rs +++ b/vm/devices/firmware/firmware_uefi/src/lib.rs @@ -341,14 +341,39 @@ impl UefiDevice { Result::<_, std::convert::Infallible>::Ok(output.unwrap_or_else(|| USAGE.to_string())) }); } + + /// Process diagnostics if a GPA has been configured but logs have not yet + /// been processed. + fn process_pending_diagnostics(&mut self, trigger: &'static str) { + if self.service.diagnostics.has_unprocessed_diagnostics() { + tracing::info!(%trigger, "processing pending UEFI diagnostics"); + let _ = self.process_diagnostics( + false, + service::diagnostics::DiagnosticsEmitter::Tracing { limit: None }, + Some(LogLevel::make_info()), + ); + } + } +} + +impl Drop for UefiDevice { + fn drop(&mut self) { + // Best-effort fallback for teardown paths that do not invoke explicit + // ChangeDeviceState::stop/reset transitions. + self.process_pending_diagnostics("drop"); + } } impl ChangeDeviceState for UefiDevice { fn start(&mut self) {} - async fn stop(&mut self) {} + async fn stop(&mut self) { + self.process_pending_diagnostics("stop"); + } async fn reset(&mut self) { + self.process_pending_diagnostics("reset"); + self.address = 0; self.service.nvram.reset(); diff --git a/vm/devices/firmware/firmware_uefi/src/service/diagnostics/mod.rs b/vm/devices/firmware/firmware_uefi/src/service/diagnostics/mod.rs index 5e8b17bb15..2d3e91bb6b 100644 --- a/vm/devices/firmware/firmware_uefi/src/service/diagnostics/mod.rs +++ b/vm/devices/firmware/firmware_uefi/src/service/diagnostics/mod.rs @@ -178,6 +178,15 @@ impl DiagnosticsServices { self.processed = false; } + /// Returns true if a GPA has been set and diagnostics have not yet been processed. + /// + /// Used to detect cases where the guest resets or shuts down without going through + /// the normal UEFI crash path or ExitBootServices, so that the caller can trigger + /// processing before discarding the buffer. + pub fn has_unprocessed_diagnostics(&self) -> bool { + self.gpa.is_some() && !self.processed + } + /// Set the GPA of the diagnostics buffer pub fn set_gpa(&mut self, gpa: u32) { self.gpa = Gpa::new(gpa).ok();