Skip to content

Commit 62f4691

Browse files
committed
uefi: serial: retry on timeout in fmt::Write to prevent partial writes
On real hardware, Status::TIMEOUT is common even during normal operation, not just on failure. The previous implementation treated it as a hard error, silently dropping bytes. Retrying until all bytes are sent makes the fmt::Write impl reliable and match the expectations of its callers. This uses an the same endless loop protection mentioned earlier.
1 parent b2fc29b commit 62f4691

File tree

3 files changed

+15
-2
lines changed

3 files changed

+15
-2
lines changed

uefi-test-runner/src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern crate alloc;
1010

1111
use alloc::string::ToString;
1212
use alloc::vec::Vec;
13+
use core::fmt::Write;
1314
use uefi::mem::memory_map::MemoryMap;
1415
use uefi::prelude::*;
1516
use uefi::proto::console::serial::Serial;
@@ -115,7 +116,8 @@ fn send_request_helper(serial: &mut Serial, request: HostRequest) -> Result {
115116
serial.set_attributes(&io_mode)?;
116117

117118
// Send a screenshot request to the host.
118-
serial.write(request.as_bytes()).discard_errdata()?;
119+
// We transitively test Serial::write_exact() and Serial::write()
120+
write!(serial, "{}", request).expect("should write all bytes");
119121

120122
// Wait for the host's acknowledgement before moving forward.
121123
let mut reply = [0; 3];

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
- **Breaking:** Renamed `DevicePathNode::to_string()` to `DevicePathNode::to_string16()`
4444
to better differentiate with the new `to_string()` coming from the new
4545
`Display`.
46+
- Fixed potential partial writes in `fmt::Write` impl for `Serial` protocol
4647

4748
# uefi - v0.36.1 (2025-11-05)
4849

uefi/src/proto/console/serial.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::proto::unsafe_protocol;
1010
use crate::{Error, Result, ResultExt, Status, StatusExt, boot};
1111
use core::time::Duration;
1212
use core::{cmp, fmt};
13+
use log::error;
1314
use uefi_raw::protocol::console::serial::{
1415
SerialIoProtocol, SerialIoProtocol_1_1, SerialIoProtocolRevision,
1516
};
@@ -420,7 +421,16 @@ impl Serial {
420421

421422
impl fmt::Write for Serial {
422423
fn write_str(&mut self, s: &str) -> fmt::Result {
423-
self.write(s.as_bytes()).map(|_| ()).map_err(|_| fmt::Error)
424+
// We retry on Status::TIMEOUT but propagate other errors
425+
self.write_exact(s.as_bytes()).map_err(|e| {
426+
let msg = "failed to write to serial device";
427+
// Simple check to prevent endless recursion if a logger
428+
// implementation uses the serial protocol
429+
if !s.contains(msg) {
430+
error!("{msg}: {e}");
431+
}
432+
fmt::Error
433+
})
424434
}
425435
}
426436

0 commit comments

Comments
 (0)