Skip to content

Commit 2c1c380

Browse files
committed
sprout: measure xen and dom0 payloads and cmdlines into PCRs 9/11/12
Extend PCR 9 with the initrd, PCR 11 with the dom0 kernel, and PCR 12 with the Xen options and dom0 cmdline. Signed-off-by: Luca Di Maio <luca.dimaio1@gmail.com>
1 parent 076be95 commit 2c1c380

2 files changed

Lines changed: 78 additions & 47 deletions

File tree

crates/boot/src/actions/edera.rs

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{actions, context::SproutContext};
22
use alloc::rc::Rc;
3-
use alloc::string::String;
3+
use alloc::vec::Vec;
44
use alloc::{format, vec};
55
use anyhow::{Context, Result};
66
use edera_sprout_config::actions::chainload::ChainloadConfiguration;
@@ -12,74 +12,96 @@ use eficore::media_loader::{
1212
XEN_EFI_CONFIG_MEDIA_GUID, XEN_EFI_KERNEL_MEDIA_GUID, XEN_EFI_RAMDISK_MEDIA_GUID,
1313
},
1414
};
15+
use eficore::platform::tpm::PlatformTpm;
1516
use uefi::Guid;
1617

17-
/// Builds a configuration string for the Xen EFI stub using the specified `configuration`.
18-
fn make_xen_config(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> String {
19-
let xen_options = combine_options(context.stamp_iter(configuration.xen_options.iter()));
20-
let kernel_options = combine_options(context.stamp_iter(configuration.kernel_options.iter()));
21-
build_xen_config(&xen_options, &kernel_options)
22-
}
23-
24-
/// Register a media loader for some `text` with the vendor `guid`.
25-
/// `what` should indicate some identifying value for error messages
26-
/// like `config` or `kernel`.
27-
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
28-
fn register_media_loader_text(guid: Guid, what: &str, text: String) -> Result<MediaLoaderHandle> {
29-
MediaLoaderHandle::register(guid, text.as_bytes().to_vec().into_boxed_slice())
30-
.context(format!("unable to register {} media loader", what)) /* */
31-
}
32-
33-
/// Register a media loader for the file `path` with the vendor `guid`.
18+
/// Register a media loader for the provided `bytes` with the vendor `guid`.
3419
/// `what` should indicate some identifying value for error messages
3520
/// like `config` or `kernel`.
3621
/// Provides a [MediaLoaderHandle] that can be used to unregister the media loader.
37-
fn register_media_loader_file(
38-
context: &Rc<SproutContext>,
22+
fn register_media_loader_bytes(
3923
guid: Guid,
4024
what: &str,
41-
path: &str,
25+
bytes: Vec<u8>,
4226
) -> Result<MediaLoaderHandle> {
43-
// Stamp the path to the file.
27+
MediaLoaderHandle::register(guid, bytes.into_boxed_slice())
28+
.context(format!("unable to register {} media loader", what))
29+
}
30+
31+
/// Read the contents of the loader payload at `path` relative to the sprout image.
32+
/// `what` should indicate some identifying value for error messages
33+
/// like `kernel` or `initrd`.
34+
fn read_loader_payload(context: &Rc<SproutContext>, what: &str, path: &str) -> Result<Vec<u8>> {
4435
let path = context.stamp(path);
45-
// Read the file contents.
46-
let content =
47-
eficore::path::read_file_contents(Some(context.root().loaded_image_path()?), &path)
48-
.context(format!("unable to read {} file", what))?;
49-
// Register the media loader.
50-
let handle = MediaLoaderHandle::register(guid, content.into_boxed_slice())
51-
.context(format!("unable to register {} media loader", what))?;
52-
Ok(handle)
36+
eficore::path::read_file_contents(Some(context.root().loaded_image_path()?), &path)
37+
.context(format!("unable to read {} file", what))
5338
}
5439

5540
/// Executes the edera action which will boot the Edera hypervisor with the specified
5641
/// `configuration` and `context`. This action uses Edera-specific Xen EFI stub functionality.
5742
pub fn edera(context: Rc<SproutContext>, configuration: &EderaConfiguration) -> Result<()> {
58-
// Build the Xen config file content for this configuration.
59-
let config = make_xen_config(context.clone(), configuration);
43+
// Only register the initrd media loader if the user actually configured one.
44+
let xen_opts = combine_options(context.stamp_iter(configuration.xen_options.iter()));
45+
let dom0_args = combine_options(context.stamp_iter(configuration.kernel_options.iter()));
46+
47+
// Measure xen options and dom0 cmdline into PCR 12 in a fixed order.
48+
PlatformTpm::log_event(
49+
PlatformTpm::PCR_KERNEL_PARAMETERS,
50+
xen_opts.as_bytes(),
51+
"sprout: xen options",
52+
)
53+
.context("unable to measure xen options into the TPM")?;
54+
PlatformTpm::log_event(
55+
PlatformTpm::PCR_KERNEL_PARAMETERS,
56+
dom0_args.as_bytes(),
57+
"sprout: dom0 cmdline",
58+
)
59+
.context("unable to measure dom0 cmdline into the TPM")?;
60+
61+
// Build the Xen config text and register it as the config media loader. The
62+
// assembled text is a derived artifact, intentionally not measured.
63+
let config_handle = register_media_loader_bytes(
64+
XEN_EFI_CONFIG_MEDIA_GUID,
65+
"config",
66+
build_xen_config(&xen_opts, &dom0_args).into_bytes(),
67+
)
68+
.context("unable to register config media loader")?;
6069

61-
// Register the media loader for the config.
62-
let config = register_media_loader_text(XEN_EFI_CONFIG_MEDIA_GUID, "config", config)
63-
.context("unable to register config media loader")?;
70+
// Read the dom0 kernel, measure it into PCR 11, then register the kernel
71+
// media loader.
72+
let kernel_bytes = read_loader_payload(&context, "kernel", &configuration.kernel)?;
73+
PlatformTpm::log_event(
74+
PlatformTpm::PCR_KERNEL,
75+
&kernel_bytes,
76+
"sprout: dom0 kernel",
77+
)
78+
.context("unable to measure dom0 kernel into the TPM")?;
79+
let kernel_handle =
80+
register_media_loader_bytes(XEN_EFI_KERNEL_MEDIA_GUID, "kernel", kernel_bytes)
81+
.context("unable to register kernel media loader")?;
6482

65-
// Register the media loaders for the kernel.
66-
let kernel = register_media_loader_file(
67-
&context,
68-
XEN_EFI_KERNEL_MEDIA_GUID,
69-
"kernel",
70-
&configuration.kernel,
83+
// Extend PCR 9 with the initrd bytes (empty when no initrd is
84+
// configured).
85+
let initrd_bytes = match empty_is_none(configuration.initrd.as_ref()) {
86+
Some(p) => read_loader_payload(&context, "initrd", p)?,
87+
None => Vec::new(),
88+
};
89+
PlatformTpm::log_event(
90+
PlatformTpm::PCR_INITRD,
91+
&initrd_bytes,
92+
"sprout: dom0 initrd",
7193
)
72-
.context("unable to register kernel media loader")?;
94+
.context("unable to measure dom0 initrd into the TPM")?;
7395

7496
// Create a vector of media loaders to drop them only after this function completes.
75-
let mut media_loaders = vec![config, kernel];
97+
let mut media_loaders = vec![config_handle, kernel_handle];
7698

7799
// Register the initrd if it is provided.
78-
if let Some(initrd) = empty_is_none(configuration.initrd.as_ref()) {
79-
let initrd =
80-
register_media_loader_file(&context, XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd)
100+
if !initrd_bytes.is_empty() {
101+
let initrd_handle =
102+
register_media_loader_bytes(XEN_EFI_RAMDISK_MEDIA_GUID, "initrd", initrd_bytes)
81103
.context("unable to register initrd media loader")?;
82-
media_loaders.push(initrd);
104+
media_loaders.push(initrd_handle);
83105
}
84106

85107
// Chainload to the Xen EFI stub.

crates/eficore/src/platform/tpm.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ impl PlatformTpm {
3838
/// The PCR for measuring the bootloader configuration into.
3939
pub const PCR_BOOT_LOADER_CONFIG: PcrIndex = PcrIndex(5);
4040

41+
/// The PCR for measuring the dom0 initrd payload into.
42+
pub const PCR_INITRD: PcrIndex = PcrIndex(9);
43+
44+
/// The PCR for measuring the dom0 kernel payload into.
45+
pub const PCR_KERNEL: PcrIndex = PcrIndex(11);
46+
47+
/// The PCR for measuring kernel command line and xen options into.
48+
pub const PCR_KERNEL_PARAMETERS: PcrIndex = PcrIndex(12);
49+
4150
/// Acquire access to the TPM protocol handle, if possible.
4251
/// Returns None if TPM is not available.
4352
fn protocol() -> Result<Option<TpmProtocolHandle>> {

0 commit comments

Comments
 (0)