Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openhcl: report bootloader log via device tree when configured #719

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions openhcl/bootloader_fdt_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ pub struct ParsedBootDtInfo {
/// VTL2 range for private pool memory.
#[inspect(iter_by_index)]
pub private_pool_ranges: Vec<MemoryRangeWithNode>,
/// The text boot log reported by the bootloader.
pub boot_log: String,
}

fn err_to_owned(e: fdt::parser::Error<'_>) -> anyhow::Error {
Expand Down Expand Up @@ -211,6 +213,7 @@ struct OpenhclInfo {
memory_allocation_mode: MemoryAllocationMode,
isolation: IsolationType,
private_pool_ranges: Vec<MemoryRangeWithNode>,
boot_log: String,
}

fn parse_memory_openhcl(node: &Node<'_>) -> anyhow::Result<AddressRange> {
Expand Down Expand Up @@ -347,6 +350,12 @@ fn parse_openhcl(node: &Node<'_>) -> anyhow::Result<OpenhclInfo> {
memory.sort_by_key(|r| r.range().start());
accepted_memory.sort_by_key(|r| r.start());

let boot_log = try_find_property(node, "boot-log")
.context("missing boot-log")?
.read_str()
.map_err(err_to_owned)?
.to_string();

// Report config ranges in a separate vec as well, for convenience.
let config_ranges = memory
.iter()
Expand Down Expand Up @@ -420,6 +429,7 @@ fn parse_openhcl(node: &Node<'_>) -> anyhow::Result<OpenhclInfo> {
memory_allocation_mode,
isolation,
private_pool_ranges,
boot_log,
})
}

Expand Down Expand Up @@ -514,6 +524,7 @@ impl ParsedBootDtInfo {
let mut isolation = IsolationType::None;
let mut vtl2_reserved_range = MemoryRange::EMPTY;
let mut private_pool_ranges = Vec::new();
let mut boot_log = String::new();

let parser = Parser::new(raw)
.map_err(err_to_owned)
Expand Down Expand Up @@ -548,6 +559,7 @@ impl ParsedBootDtInfo {
memory_allocation_mode: n_memory_allocation_mode,
isolation: n_isolation,
private_pool_ranges: n_private_pool_ranges,
boot_log: n_boot_log,
} = parse_openhcl(&child)?;
vtl0_mmio = n_vtl0_mmio;
config_ranges = n_config_ranges;
Expand All @@ -558,6 +570,7 @@ impl ParsedBootDtInfo {
isolation = n_isolation;
vtl2_reserved_range = n_vtl2_reserved_range;
private_pool_ranges = n_private_pool_ranges;
boot_log = n_boot_log;
}

_ if child.name.starts_with("memory@") => {
Expand Down Expand Up @@ -591,6 +604,7 @@ impl ParsedBootDtInfo {
isolation,
vtl2_reserved_range,
private_pool_ranges,
boot_log,
})
}
}
Expand Down Expand Up @@ -795,6 +809,9 @@ mod tests {
openhcl_builder = openhcl_builder.add_u64(p_vtl0_alias_map, data)?;
}

let p_boot_log = openhcl_builder.add_string("boot-log")?;
openhcl_builder = openhcl_builder.add_str(p_boot_log, &info.boot_log)?;

openhcl_builder = openhcl_builder
.start_node("vmbus-vtl0")?
.add_u32(p_address_cells, 2)?
Expand Down Expand Up @@ -962,6 +979,7 @@ mod tests {
range: MemoryRange::new(0x60000..0x70000),
vnode: 0,
}],
boot_log: "hello world".to_string(),
};

let dt = build_dt(&orig_info).unwrap();
Expand Down
21 changes: 21 additions & 0 deletions openhcl/openhcl_boot/src/boot_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use crate::arch::tdx::TdxIoAccess;
use crate::host_params::shim_params::IsolationType;
use crate::single_threaded::SingleThreaded;
use arrayvec::ArrayString;
use core::cell::RefCell;
use core::fmt;
use core::fmt::Write;
Expand Down Expand Up @@ -46,12 +47,16 @@ impl Logger {
}
}

const MAX_LOG_SIZE: usize = 4096 * 4;

pub struct BootLogger {
logger: SingleThreaded<RefCell<Logger>>,
memory_log: SingleThreaded<RefCell<ArrayString<MAX_LOG_SIZE>>>,
}

pub static BOOT_LOGGER: BootLogger = BootLogger {
logger: SingleThreaded(RefCell::new(Logger::None)),
memory_log: SingleThreaded(RefCell::new(ArrayString::new_const())),
};

/// Initialize the boot logger. This replaces any previous init calls.
Expand All @@ -74,10 +79,26 @@ pub fn boot_logger_init(isolation_type: IsolationType, logger_type: LoggerType)

impl Write for &BootLogger {
fn write_str(&mut self, s: &str) -> fmt::Result {
if let Ok(mut memory_log) = self.memory_log.try_borrow_mut() {
// TODO: Use a circular buffer instead of just ignoring logs after
// the buffer is full. This requires some other kind of
// non-allocating circular buffer, as arrayvec doesn't have any such
// data structures.
let _ = memory_log.write_str(s);
}

self.logger.borrow_mut().write_str(s)
}
}

impl BootLogger {
/// Get the current log buffer. This holds a borrow on the memory buffer, so
/// until this is dropped no new messages will be added.
pub fn log_buffer(&self) -> core::cell::Ref<'_, ArrayString<MAX_LOG_SIZE>> {
self.memory_log.borrow()
}
}

/// Log a message. These messages are always emitted regardless of debug or
/// release, if a corresponding logger was configured.
///
Expand Down
42 changes: 36 additions & 6 deletions openhcl/openhcl_boot/src/cmdline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use underhill_confidentiality::OPENHCL_CONFIDENTIAL_DEBUG_ENV_VAR_NAME;
const BOOT_LOG: &str = "OPENHCL_BOOT_LOG=";
const SERIAL_LOGGER: &str = "com3";

/// Enable boot log to be reported to usermode via the device tree.
const REPORT_BOOT_LOG: &str = "OPENHCL_REPORT_BOOT_LOG=1";

/// Enable the private VTL2 GPA pool for page allocations. This is only enabled
/// via the command line, because in order to support the VTL2 GPA pool
/// generically, the boot shim must read serialized data from the previous
Expand All @@ -30,6 +33,7 @@ pub struct BootCommandLineOptions {
pub logger: Option<LoggerType>,
pub confidential_debug: bool,
pub enable_vtl2_gpa_pool: Option<u64>,
pub report_boot_log: bool,
}

/// Parse arguments from a command line.
Expand All @@ -38,6 +42,7 @@ pub fn parse_boot_command_line(cmdline: &str) -> BootCommandLineOptions {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None,
report_boot_log: false,
};

for arg in cmdline.split_whitespace() {
Expand Down Expand Up @@ -66,6 +71,8 @@ pub fn parse_boot_command_line(cmdline: &str) -> BootCommandLineOptions {
Some(num)
}
});
} else if arg.starts_with(REPORT_BOOT_LOG) {
result.report_boot_log = true;
}
}

Expand All @@ -83,7 +90,8 @@ mod tests {
BootCommandLineOptions {
logger: Some(LoggerType::Serial),
confidential_debug: false,
enable_vtl2_gpa_pool: None
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);

Expand All @@ -92,7 +100,8 @@ mod tests {
BootCommandLineOptions {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);

Expand All @@ -101,7 +110,8 @@ mod tests {
BootCommandLineOptions {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);

Expand All @@ -110,7 +120,8 @@ mod tests {
BootCommandLineOptions {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);

Expand All @@ -119,7 +130,8 @@ mod tests {
BootCommandLineOptions {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);

Expand All @@ -129,7 +141,8 @@ mod tests {
BootCommandLineOptions {
logger: Some(LoggerType::Serial),
confidential_debug: true,
enable_vtl2_gpa_pool: None
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);
}
Expand All @@ -142,6 +155,7 @@ mod tests {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: Some(1),
report_boot_log: false,
}
);
assert_eq!(
Expand All @@ -150,6 +164,7 @@ mod tests {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);
assert_eq!(
Expand All @@ -158,6 +173,7 @@ mod tests {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None,
report_boot_log: false,
}
);
assert_eq!(
Expand All @@ -166,6 +182,20 @@ mod tests {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: Some(1024),
report_boot_log: false,
}
);
}

#[test]
fn test_report_boot_log() {
assert_eq!(
parse_boot_command_line("OPENHCL_REPORT_BOOT_LOG=1"),
BootCommandLineOptions {
logger: None,
confidential_debug: false,
enable_vtl2_gpa_pool: None,
report_boot_log: true,
}
);
}
Expand Down
15 changes: 15 additions & 0 deletions openhcl/openhcl_boot/src/dt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub fn write_dt(
cmdline: &ArrayString<COMMAND_LINE_SIZE>,
sidecar: Option<&SidecarConfig<'_>>,
boot_times: Option<BootTimes>,
report_boot_log: bool,
) -> Result<(), DtError> {
// First, the reservation map is built. That keyes off of the x86 E820 memory map.
// The `/memreserve/` is used to tell the kernel that the reserved memory is RAM
Expand Down Expand Up @@ -461,6 +462,20 @@ pub fn write_dt(
};
openhcl_builder = openhcl_builder.add_str(p_isolation_type, isolation_type)?;

// If requested, capture the current memory log and report it to usermode.
//
// NOTE: This log is not all-inclusive, as any logs after this point are not
// saved to the device tree.
let p_boot_log = openhcl_builder.add_string("boot-log")?;
if report_boot_log {
openhcl_builder = openhcl_builder.add_str(
p_boot_log,
crate::boot_logger::BOOT_LOGGER.log_buffer().as_str(),
)?;
} else {
openhcl_builder = openhcl_builder.add_str(p_boot_log, "")?;
}

// Indicate what kind of memory allocation mode was done by the bootloader
// to usermode.
let p_memory_allocation_mode = openhcl_builder.add_string("memory-allocation-mode")?;
Expand Down
7 changes: 7 additions & 0 deletions openhcl/openhcl_boot/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
boot_logger_init(p.isolation_type, typ);
log!("openhcl_boot: early debugging enabled");
}
let mut report_boot_log = static_options.report_boot_log;

let can_trust_host =
p.isolation_type == IsolationType::None || static_options.confidential_debug;
Expand Down Expand Up @@ -626,6 +627,8 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
// if it wasn't otherwise requested.
boot_logger_init(p.isolation_type, LoggerType::Serial);
}

report_boot_log |= dynamic_options.report_boot_log;
}

log!("openhcl_boot: entered shim_main");
Expand Down Expand Up @@ -748,6 +751,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
&cmdline,
sidecar.as_ref(),
boot_times,
report_boot_log,
)
.unwrap();

Expand Down Expand Up @@ -939,6 +943,7 @@ mod test {
&ArrayString::from("test").unwrap_or_default(),
None,
None,
false,
)
.unwrap();
}
Expand Down Expand Up @@ -1012,6 +1017,7 @@ mod test {
&ArrayString::from("test").unwrap_or_default(),
None,
None,
false,
)
.unwrap();

Expand Down Expand Up @@ -1040,6 +1046,7 @@ mod test {
&ArrayString::from("test").unwrap_or_default(),
None,
None,
false,
)
.unwrap();

Expand Down
Loading