Skip to content

Commit 6e3b06c

Browse files
authored
Merge pull request #978 from nicholasbishop/bishop-atomic-logger
Change Logger to use an atomic pointer internally
2 parents 47635fe + 35374d4 commit 6e3b06c

File tree

3 files changed

+49
-28
lines changed

3 files changed

+49
-28
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
had to add `&BootServices` as additional parameter.
1212
- `BootServices::free_pages` and `BootServices::free_pool` are now `unsafe` to
1313
call, since it is possible to trigger UB by freeing memory that is still in use.
14+
- `Logger` no longer requires exterior mutability. `Logger::new` is now `const`,
15+
takes no arguments, and creates the logger in a disabled state. Call
16+
`Logger::set_output` to enable it.
1417

1518
## uefi-macros - [Unreleased]
1619

uefi-services/src/lib.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
5555

5656
/// Global logger object
5757
#[cfg(feature = "logger")]
58-
static mut LOGGER: Option<uefi::logger::Logger> = None;
58+
static LOGGER: uefi::logger::Logger = uefi::logger::Logger::new();
5959

6060
#[must_use]
6161
fn system_table_opt() -> Option<SystemTable<Boot>> {
@@ -164,16 +164,11 @@ macro_rules! println {
164164
/// disable() on exit from UEFI boot services.
165165
#[cfg(feature = "logger")]
166166
unsafe fn init_logger(st: &mut SystemTable<Boot>) {
167-
let stdout = st.stdout();
168-
169-
// Construct the logger.
170-
let logger = {
171-
LOGGER = Some(uefi::logger::Logger::new(stdout));
172-
LOGGER.as_ref().unwrap()
173-
};
167+
// Connect the logger to stdout.
168+
LOGGER.set_output(st.stdout());
174169

175170
// Set the logger.
176-
log::set_logger(logger).unwrap(); // Can only fail if already initialized.
171+
log::set_logger(&LOGGER).unwrap(); // Can only fail if already initialized.
177172

178173
// Set logger max level to level specified by log features
179174
log::set_max_level(log::STATIC_MAX_LEVEL);
@@ -191,9 +186,7 @@ unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option<NonNull<c_v
191186
SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release);
192187

193188
#[cfg(feature = "logger")]
194-
if let Some(ref mut logger) = LOGGER {
195-
logger.disable();
196-
}
189+
LOGGER.disable();
197190

198191
uefi::allocator::exit_boot_services();
199192
}

uefi/src/logger.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
use crate::proto::console::text::Output;
1616

1717
use core::fmt::{self, Write};
18-
use core::ptr::NonNull;
18+
use core::ptr;
19+
use core::sync::atomic::{AtomicPtr, Ordering};
1920

2021
/// Logging implementation which writes to a UEFI output stream.
2122
///
@@ -24,39 +25,63 @@ use core::ptr::NonNull;
2425
/// undefined behaviour from inadvertent logging.
2526
#[derive(Debug)]
2627
pub struct Logger {
27-
writer: Option<NonNull<Output>>,
28+
writer: AtomicPtr<Output>,
2829
}
2930

3031
impl Logger {
3132
/// Creates a new logger.
3233
///
33-
/// You must arrange for the `disable` method to be called or for this logger
34-
/// to be otherwise discarded before boot services are exited.
34+
/// The logger is initially disabled. Call [`set_output`] to enable it.
3535
///
36-
/// # Safety
37-
///
38-
/// Undefined behaviour may occur if this logger is still active after the
39-
/// application has exited the boot services stage.
40-
pub unsafe fn new(output: &mut Output) -> Self {
36+
/// [`set_output`]: Self::set_output
37+
#[must_use]
38+
pub const fn new() -> Self {
4139
Logger {
42-
writer: NonNull::new(output as *const _ as *mut _),
40+
writer: AtomicPtr::new(ptr::null_mut()),
4341
}
4442
}
4543

46-
/// Disable the logger
47-
pub fn disable(&mut self) {
48-
self.writer = None;
44+
/// Get the output pointer (may be null).
45+
#[must_use]
46+
fn output(&self) -> *mut Output {
47+
self.writer.load(Ordering::Acquire)
48+
}
49+
50+
/// Set the [`Output`] to which the logger will write.
51+
///
52+
/// If a null pointer is passed for `output`, this method is equivalent to
53+
/// calling [`disable`].
54+
///
55+
/// # Safety
56+
///
57+
/// The `output` pointer must either be null or point to a valid [`Output`]
58+
/// object. That object must remain valid until the logger is either
59+
/// disabled, or `set_output` is called with a different `output`.
60+
///
61+
/// You must arrange for the [`disable`] method to be called or for this
62+
/// logger to be otherwise discarded before boot services are exited.
63+
///
64+
/// [`disable`]: Self::disable
65+
pub unsafe fn set_output(&self, output: *mut Output) {
66+
self.writer.store(output, Ordering::Release);
67+
}
68+
69+
/// Disable the logger.
70+
pub fn disable(&self) {
71+
unsafe { self.set_output(ptr::null_mut()) }
4972
}
5073
}
5174

5275
impl log::Log for Logger {
5376
fn enabled(&self, _metadata: &log::Metadata) -> bool {
54-
self.writer.is_some()
77+
!self.output().is_null()
5578
}
5679

5780
fn log(&self, record: &log::Record) {
58-
if let Some(mut ptr) = self.writer {
59-
let writer = unsafe { ptr.as_mut() };
81+
let output = self.output();
82+
83+
if !output.is_null() {
84+
let writer = unsafe { &mut *output };
6085
let result = DecoratedLog::write(
6186
writer,
6287
record.level(),

0 commit comments

Comments
 (0)