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;
18#[cfg(target_arch = "x86_64")]
19use minimal_rt::arch::InstrIoAccess;
20use minimal_rt::arch::Serial;
21
22/// The logging type to use.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum LoggerType {
25    Serial,
26}
27
28enum Logger {
29    #[cfg(target_arch = "x86_64")]
30    Serial(Serial<InstrIoAccess>),
31    #[cfg(target_arch = "aarch64")]
32    Serial(Serial),
33    #[cfg(target_arch = "x86_64")]
34    TdxSerial(Serial<TdxIoAccess>),
35    None,
36}
37
38impl Logger {
39    fn write_str(&mut self, s: &str) -> fmt::Result {
40        match self {
41            Logger::Serial(serial) => serial.write_str(s),
42            #[cfg(target_arch = "x86_64")]
43            Logger::TdxSerial(serial) => serial.write_str(s),
44            Logger::None => Ok(()),
45        }
46    }
47}
48
49pub struct BootLogger {
50    logger: SingleThreaded<RefCell<Logger>>,
51}
52
53pub static BOOT_LOGGER: BootLogger = BootLogger {
54    logger: SingleThreaded(RefCell::new(Logger::None)),
55};
56
57/// Initialize the boot logger. This replaces any previous init calls.
58///
59/// If a given `logger_type` is unavailable on a given isolation type, the
60/// logger will ignore it, and no logging will be initialized.
61pub fn boot_logger_init(isolation_type: IsolationType, logger_type: LoggerType) {
62    let mut logger = BOOT_LOGGER.logger.borrow_mut();
63
64    *logger = match (isolation_type, logger_type) {
65        #[cfg(target_arch = "x86_64")]
66        (IsolationType::None, LoggerType::Serial) => Logger::Serial(Serial::init(InstrIoAccess)),
67        #[cfg(target_arch = "aarch64")]
68        (IsolationType::None, LoggerType::Serial) => Logger::Serial(Serial::init()),
69        #[cfg(target_arch = "x86_64")]
70        (IsolationType::Tdx, LoggerType::Serial) => Logger::TdxSerial(Serial::init(TdxIoAccess)),
71        _ => Logger::None,
72    };
73}
74
75impl Write for &BootLogger {
76    fn write_str(&mut self, s: &str) -> fmt::Result {
77        self.logger.borrow_mut().write_str(s)
78    }
79}
80
81/// Log a message. These messages are always emitted regardless of debug or
82/// release, if a corresponding logger was configured.
83///
84/// If you want to log something just for local debugging, use [`debug_log!`]
85/// instead.
86macro_rules! log {
87    () => {};
88    ($($arg:tt)*) => {
89        {
90            use core::fmt::Write;
91            let _ = writeln!(&$crate::boot_logger::BOOT_LOGGER, $($arg)*);
92        }
93    };
94}
95
96pub(crate) use log;
97
98/// This emits the same as [`log!`], but is intended for local debugging and is
99/// linted against to not pass CI. Use for local development when you just need
100/// debug prints.
101//
102// Expect unused macros for the same reason as unused_imports below, as there
103// should be no usage of this macro normally.
104#[expect(unused_macros)]
105macro_rules! debug_log {
106    ($($arg:tt)*) => {
107        $crate::boot_logger::log!($($arg)*)
108    };
109}
110
111// Expect unused imports because there should be no normal usage in code due to
112// lints against it in CI.
113#[expect(unused_imports)]
114pub(crate) use debug_log;