Skip to content

Commit 1607f5d

Browse files
committed
uefi/helpers: logger logs to debugcon device
This modifies the logger so that it also prints to the debugcon device in QEMU respectively the debug-console device in cloud-hypervisor [0]. By default, this device is at I/O port 0xe9. This only works on x86. The major benefit is that one can get log output from the logger set up by uefi::helpers::init even after boot services has been exited. Now the memory map can be logged, for example. When the integration test runs, the output txt file is put into the root directory of the project. Also everyone else developing UEFI apps using uefi-rs and running them in QEMU or cloud-hypervisor (on x86) benefits. [0]: https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/
1 parent 9746ee6 commit 1607f5d

File tree

7 files changed

+89
-15
lines changed

7 files changed

+89
-15
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33

44
# Files generated by mdBook.
55
/book/book/
6+
7+
# Integration test output by QEMU.
8+
integration-test-debugcon.log

uefi-test-runner/src/main.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,23 @@ fn shutdown(mut st: SystemTable<Boot>) -> ! {
196196
// type of regression this prevents.
197197
info!("LOGGING_STILL_WORKING_RIGHT_BEFORE_EBS");
198198

199-
info!("Testing complete, shutting down...");
199+
info!("Testing complete, exiting boot services...");
200200

201201
// Exit boot services as a proof that it works :)
202-
let (st, _iter) = st.exit_boot_services(MemoryType::LOADER_DATA);
202+
let (st, mmap) = st.exit_boot_services(MemoryType::LOADER_DATA);
203+
204+
info!("Memory Map:");
205+
for desc in mmap.entries() {
206+
info!(
207+
"start=0x{:016x} size=0x{:016x} type={:?}, attr={:?}",
208+
desc.phys_start,
209+
desc.page_count * 4096,
210+
desc.ty,
211+
desc.att
212+
);
213+
}
214+
215+
info!("Shutting down...");
203216

204217
#[cfg(target_arch = "x86_64")]
205218
{

uefi/CHANGELOG.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
## Added
44
- Added `RuntimeServices::update_capsule`.
55
- Added `RuntimeServices::query_capsule_capabilities`.
6+
- The logger from `uefi::helpers` now also logs to the [debugcon](https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/)
7+
device (QEMU) respectively the debug-console (cloud-hypervisor). This only
8+
works on x86. It is activated by default (only on x86) and can be deactivated
9+
by removing the `log-debugcon` cargo feature. The major benefit is that one
10+
can get log messages even after one exited the boot services.
611

712
## Removed
813
- Removed the `panic-on-logger-errors` feature of the `uefi` crate. Logger
@@ -13,8 +18,6 @@
1318

1419
## Added
1520
- Added `ResetNotification` protocol.
16-
17-
## Added
1821
- Added `Timestamp` protocol.
1922
- Added `UnalignedSlice::as_ptr`.
2023
- Added common derives for `Event` and `Handle`.

uefi/Cargo.toml

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ repository.workspace = true
1313
rust-version.workspace = true
1414

1515
[features]
16+
default = [ "log-debugcon" ]
1617
alloc = []
1718

1819
# Generic gate to code that uses unstable features of Rust. You usually need a nightly toolchain.
@@ -22,7 +23,14 @@ unstable = []
2223
logger = []
2324
global_allocator = []
2425
panic_handler = []
25-
qemu = ["dep:qemu-exit", "panic_handler"] # panic_handler: logical, not technical dependency
26+
# Some convenience when running inside QEMU.
27+
# - dependency log-debugcon: logical, not technical
28+
# - dependency panic_handler: logical, not technical
29+
qemu = ["dep:qemu-exit", "panic_handler", "log-debugcon"]
30+
# Whether the internal logger from the helpers module should also log to
31+
# the debugcon device (QEMU) and debug-console (cloud-hypervisor). Only works
32+
# on x86.
33+
log-debugcon = []
2634

2735
[dependencies]
2836
bitflags.workspace = true

uefi/src/helpers/logger.rs

+47-8
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
//! The last part also means that some Unicode characters might not be
1313
//! supported by the UEFI console. Don't expect emoji output support.
1414
15-
use crate::proto::console::text::Output;
16-
1715
use crate::prelude::{Boot, SystemTable};
16+
use crate::proto::console::text::Output;
1817
use core::fmt::{self, Write};
1918
use core::ptr;
2019
use core::sync::atomic::{AtomicPtr, Ordering};
@@ -43,6 +42,31 @@ pub fn disable() {
4342
LOGGER.disable();
4443
}
4544

45+
/// Writer to the QEMU debugcon device and the debug-console of
46+
/// cloud-hypervisor.
47+
///
48+
/// More info: <https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/>
49+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
50+
#[derive(Copy, Clone, Debug)]
51+
struct DebugconWriter;
52+
53+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
54+
impl DebugconWriter {
55+
const IO_PORT: u16 = 0xe9;
56+
}
57+
58+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
59+
impl core::fmt::Write for DebugconWriter {
60+
fn write_str(&mut self, s: &str) -> fmt::Result {
61+
for &byte in s.as_bytes() {
62+
unsafe {
63+
core::arch::asm!("outb %al, %dx", in("al") byte, in("dx") DebugconWriter::IO_PORT, options(att_syntax))
64+
};
65+
}
66+
Ok(())
67+
}
68+
}
69+
4670
/// Logging implementation which writes to a UEFI output stream.
4771
///
4872
/// If this logger is used as a global logger, you must disable it using the
@@ -99,15 +123,13 @@ impl Logger {
99123

100124
impl log::Log for Logger {
101125
fn enabled(&self, _metadata: &log::Metadata) -> bool {
102-
!self.output().is_null()
126+
// We decide in `log` already if something is printed. We do not
127+
// need micro optimizations here.
128+
true
103129
}
104130

105131
fn log(&self, record: &log::Record) {
106-
let output = self.output();
107-
108-
if !output.is_null() {
109-
let writer = unsafe { &mut *output };
110-
132+
if let Some(writer) = unsafe { self.output().as_mut() } {
111133
// Ignore all errors. Since we're in the logger implementation we
112134
// can't log the error. We also don't want to panic, since logging
113135
// is generally not critical functionality.
@@ -119,6 +141,23 @@ impl log::Log for Logger {
119141
record.line().unwrap_or(0),
120142
);
121143
}
144+
145+
#[cfg(all(
146+
any(target_arch = "x86", target_arch = "x86_64"),
147+
feature = "log-debugcon"
148+
))]
149+
{
150+
// Ignore all errors. Since we're in the logger implementation we
151+
// can't log the error. We also don't want to panic, since logging
152+
// is generally not critical functionality.
153+
let _ = DecoratedLog::write(
154+
&mut DebugconWriter,
155+
record.level(),
156+
record.args(),
157+
record.file().unwrap_or("<unknown file>"),
158+
record.line().unwrap_or(0),
159+
);
160+
}
122161
}
123162

124163
fn flush(&self) {

uefi/src/helpers/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
//!
55
//! For now, this includes:
66
//! - using [`uefi::allocator::Allocator`] as global allocator (feature `global_allocator`)
7-
//! - an implementation of [`log::Log`] (feature `logger`)
7+
//! - an implementation of [`log::Log`] (feature `logger`) which logs to
8+
//! the stdout text protocol of UEFI (as long as boot services were not
9+
//! excited) and to the [debugcon device](https://phip1611.de/blog/how-to-use-qemus-debugcon-feature/)
10+
//! (only on x86) (feature `log-debugcon`).
811
//! - [`print!`][print_macro] and [`println!`][println_macro] macros defaulting
912
//! to the uefi boot service stdout stream
1013
//! - default panic handler (feature `panic_handler`)
@@ -73,7 +76,8 @@ pub fn system_table() -> SystemTable<Boot> {
7376
/// memory allocation capabilities.
7477
///
7578
/// **PLEASE NOTE** that these helpers are meant for the pre exit boot service
76-
/// epoch.
79+
/// epoch. Limited functionality might work after exiting them, such as logging
80+
/// to the debugcon device.
7781
pub fn init(st: &mut SystemTable<Boot>) -> Result<()> {
7882
if system_table_opt().is_some() {
7983
// Avoid double initialization.

xtask/src/qemu.rs

+4
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ pub fn run_qemu(arch: UefiArch, opt: &QemuOpt) -> Result<()> {
484484

485485
cmd.args(["-device", "virtio-rng-pci"]);
486486

487+
if arch == UefiArch::IA32 || arch == UefiArch::X86_64 {
488+
cmd.args(["-debugcon", "file:./integration-test-debugcon.log"]);
489+
}
490+
487491
// Set the boot menu timeout to zero. On aarch64 in particular this speeds
488492
// up the boot a lot. Note that we have to enable the menu here even though
489493
// we are skipping right past it, otherwise `splash-time` is ignored in

0 commit comments

Comments
 (0)