openhcl_boot/
boot_logger.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Logging support for the bootshim.
5//!
6//! The bootshim performs no filtering of its logging messages when running in
7//! a confidential VM. This is because it runs before any keys can be accessed
8//! or any guest code is executed, and therefore it can not leak anything
9//! sensitive.
10
11#[cfg(target_arch = "x86_64")]
12use crate::arch::tdx::TdxIoAccess;
13use crate::host_params::shim_params::IsolationType;
14use crate::single_threaded::SingleThreaded;
15use core::cell::RefCell;
16use core::fmt;
17use core::fmt::Write;
18use memory_range::MemoryRange;
19#[cfg(target_arch = "x86_64")]
20use minimal_rt::arch::InstrIoAccess;
21use minimal_rt::arch::Serial;
22use string_page_buf::StringBuffer;
23
24enum Logger {
25    #[cfg(target_arch = "x86_64")]
26    Serial(Serial<InstrIoAccess>),
27    #[cfg(target_arch = "aarch64")]
28    Serial(Serial),
29    #[cfg(target_arch = "x86_64")]
30    TdxSerial(Serial<TdxIoAccess>),
31    None,
32}
33
34impl Logger {
35    fn write_str(&mut self, s: &str) -> fmt::Result {
36        match self {
37            Logger::Serial(serial) => serial.write_str(s),
38            #[cfg(target_arch = "x86_64")]
39            Logger::TdxSerial(serial) => serial.write_str(s),
40            Logger::None => Ok(()),
41        }
42    }
43}
44
45pub struct BootLogger {
46    logger: SingleThreaded<RefCell<Logger>>,
47    in_memory_logger: SingleThreaded<RefCell<Option<StringBuffer<'static>>>>,
48}
49
50pub static BOOT_LOGGER: BootLogger = BootLogger {
51    logger: SingleThreaded(RefCell::new(Logger::None)),
52    in_memory_logger: SingleThreaded(RefCell::new(None)),
53};
54
55/// Initialize the in-memory log buffer. This range must be identity mapped, and
56/// unused by anything else.
57pub fn boot_logger_memory_init(buffer: MemoryRange) {
58    if buffer.is_empty() {
59        return;
60    }
61
62    let log_buffer_ptr = buffer.start() as *mut u8;
63    // SAFETY: At file build time, this range is enforced to be unused by
64    // anything else. The rest of the bootshim will mark this range as reserved
65    // and not free to be used by anything else.
66    //
67    // The VA is valid as we are identity mapped.
68    let log_buffer_slice =
69        unsafe { core::slice::from_raw_parts_mut(log_buffer_ptr, buffer.len() as usize) };
70
71    *BOOT_LOGGER.in_memory_logger.borrow_mut() = Some(
72        StringBuffer::new(log_buffer_slice)
73            .expect("log buffer should be valid from fixed at build config"),
74    );
75}
76
77/// Initialize the runtime boot logger, for logging to serial or other outputs.
78pub fn boot_logger_runtime_init(isolation_type: IsolationType, com3_serial_available: bool) {
79    let mut logger = BOOT_LOGGER.logger.borrow_mut();
80
81    *logger = match (isolation_type, com3_serial_available) {
82        #[cfg(target_arch = "x86_64")]
83        (IsolationType::None, true) => Logger::Serial(Serial::init(InstrIoAccess)),
84        #[cfg(target_arch = "aarch64")]
85        (IsolationType::None, true) => Logger::Serial(Serial::init()),
86        #[cfg(target_arch = "x86_64")]
87        (IsolationType::Tdx, true) => Logger::TdxSerial(Serial::init(TdxIoAccess)),
88        _ => Logger::None,
89    };
90}
91
92impl Write for &BootLogger {
93    fn write_str(&mut self, s: &str) -> fmt::Result {
94        if let Some(buf) = self.in_memory_logger.borrow_mut().as_mut() {
95            // Ignore the errors from the in memory logger.
96            let _ = buf.append(s);
97        }
98        self.logger.borrow_mut().write_str(s)
99    }
100}
101
102/// Log a message. These messages are always emitted regardless of debug or
103/// release, if a corresponding logger was configured.
104///
105/// If you want to log something just for local debugging, use [`debug_log!`]
106/// instead.
107macro_rules! log {
108    () => {};
109    ($($arg:tt)*) => {
110        {
111            use core::fmt::Write;
112            let _ = writeln!(&$crate::boot_logger::BOOT_LOGGER, $($arg)*);
113        }
114    };
115}
116
117pub(crate) use log;
118
119/// This emits the same as [`log!`], but is intended for local debugging and is
120/// linted against to not pass CI. Use for local development when you just need
121/// debug prints.
122//
123// Expect unused macros for the same reason as unused_imports below, as there
124// should be no usage of this macro normally.
125#[expect(unused_macros)]
126macro_rules! debug_log {
127    ($($arg:tt)*) => {
128        $crate::boot_logger::log!($($arg)*)
129    };
130}
131
132// Expect unused imports because there should be no normal usage in code due to
133// lints against it in CI.
134#[expect(unused_imports)]
135pub(crate) use debug_log;