Skip to content
Open
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
4 changes: 4 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7562,6 +7562,7 @@ name = "tpm_protocol"
version = "0.0.0"
dependencies = [
"bitfield-struct 0.11.0",
"open_enum",
"thiserror 2.0.16",
"zerocopy 0.8.27",
]
Expand Down Expand Up @@ -8008,6 +8009,7 @@ dependencies = [
"thiserror 2.0.16",
"time",
"tpm_device",
"tpm_protocol",
"tpm_resources",
"tracelimit",
"tracing",
Expand Down Expand Up @@ -8043,6 +8045,7 @@ dependencies = [
"vmcore",
"vmgs",
"vmgs_broker",
"vmgs_format",
"vmgs_resources",
"vmm_core",
"vmm_core_defs",
Expand Down Expand Up @@ -9268,6 +9271,7 @@ dependencies = [
"inspect",
"open_enum",
"static_assertions",
"tpm_protocol",
"zerocopy 0.8.27",
]

Expand Down
2 changes: 2 additions & 0 deletions openhcl/underhill_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ serial_16550_resources.workspace = true
storage_string.workspace = true
storvsp.workspace = true
storvsp_resources.workspace = true
tpm_protocol.workspace = true
tpm_resources.workspace = true
tpm_device = { workspace = true, features = ["tpm"] }
tracelimit.workspace = true
Expand Down Expand Up @@ -129,6 +130,7 @@ vmcore.workspace = true
vm_resource.workspace = true
vmgs = { workspace = true, features = ["encryption_ossl", "save_restore"] }
vmgs_broker = { workspace = true, features = ["encryption_ossl"] }
vmgs_format.workspace = true
vmgs_resources.workspace = true
x86defs.workspace = true

Expand Down
35 changes: 35 additions & 0 deletions openhcl/underhill_core/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ use vmcore::vmtime::VmTimeKeeper;
use vmgs::Vmgs;
use vmgs_broker::resolver::VmgsFileResolver;
use vmgs_broker::spawn_vmgs_broker;
use vmgs_format::ProvisioningMarker;
use vmgs_format::VmgsProvisioner;
use vmgs_resources::VmgsFileHandle;
use vmm_core::input_distributor::InputDistributor;
use vmm_core::partition_unit::Halt;
Expand All @@ -186,6 +188,7 @@ use vmotherboard::options::BaseChipsetFoundation;
use watchdog_core::platform::WatchdogCallback;
use watchdog_core::platform::WatchdogPlatform;
use zerocopy::FromZeros;
use zerocopy::IntoBytes;

pub(crate) const PM_BASE: u16 = 0x400;
pub(crate) const SYSTEM_IRQ_ACPI: u32 = 9;
Expand Down Expand Up @@ -1746,6 +1749,38 @@ async fn new_underhill_vm(
}
};

if let Some((_, ref mut vmgs)) = vmgs {
if vmgs.was_provisioned_this_boot() {
let mut rev = [0u8; vmgs_format::HCL_VERSION_LENGTH];
let rev_bytes = build_info::get().scm_revision().as_bytes();
let rev_len = rev_bytes.len().min(40);
rev[..rev_len].copy_from_slice(&rev_bytes[..rev_len]);

let reset_by_gsl_flag = (matches!(
dps.general.guest_state_lifetime,
GuestStateLifetime::Reprovision
) || (matches!(
dps.general.guest_state_lifetime,
GuestStateLifetime::ReprovisionOnFailure
) && vmgs.was_formatted_on_failure())) as u8;

let marker = ProvisioningMarker {
marker_version: vmgs_format::PROVISIONING_MARKER_CURRENT_VERSION,
provisioner: VmgsProvisioner::OPENHCL,
reset_by_gsl_flag,
vtpm_version: tpm_protocol::TPM_DEFAULT_VERSION,
vtpm_nvram_size: tpm_protocol::TPM_DEFAULT_SIZE,
vtpm_akcert_size: tpm_protocol::TPM_DEFAULT_AKCERT_SIZE,
vtpm_akcert_attrs: tpm_protocol::platform_akcert_attributes().into(),
hcl_version: rev,
..FromZeros::new_zeroed()
};

vmgs.write_file(vmgs::FileId::PROVISIONING_MARKER, marker.as_bytes())
.await?;
}
}

// Determine if the VTL0 alias map is in use.
let vtl0_alias_map_bit =
runtime_params
Expand Down
1 change: 1 addition & 0 deletions vm/devices/tpm/tpm_protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition.workspace = true

[dependencies]
bitfield-struct.workspace = true
open_enum.workspace = true
thiserror.workspace = true
zerocopy.workspace = true

Expand Down
37 changes: 37 additions & 0 deletions vm/devices/tpm/tpm_protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@

pub mod tpm20proto;

use open_enum::open_enum;
use tpm20proto::NV_INDEX_RANGE_BASE_PLATFORM_MANUFACTURER;
use tpm20proto::NV_INDEX_RANGE_BASE_TCG_ASSIGNED;
use tpm20proto::ReservedHandle;
use tpm20proto::TPM20_HT_PERSISTENT;
use tpm20proto::TpmaNvBits;
use tpm20proto::TpmaObject;
use tpm20proto::TpmaObjectBits;
use zerocopy::FromBytes;
use zerocopy::Immutable;
use zerocopy::IntoBytes;
use zerocopy::KnownLayout;

// --- Reserved handles for Storage Primary Key ranges from 0x81000000 – 0x810000ff ---

Expand Down Expand Up @@ -55,3 +61,34 @@ pub fn expected_ak_attributes() -> TpmaObject {
.with_sign_encrypt(true)
.into()
}

/// Expected NVRAM index attributes for a platform-created AKCert index.
pub fn platform_akcert_attributes() -> TpmaNvBits {
TpmaNvBits::new()
.with_nv_authread(true)
.with_nv_authwrite(true)
.with_nv_ownerread(true)
.with_nv_platformcreate(true)
.with_nv_no_da(true)
}

open_enum! {
/// vTPM versions whose state can be provisioned in a new VMGS file. Note that
/// these constants are part of the VMGS diagnostic provisioning marker format
/// defined in vmgs_format.
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
pub enum TpmVersion: u32 {
/// TPM version 1.38
VER_138 = 1,
}
}

/// Default vTPM version provisioned for a new VMGS.
pub const TPM_DEFAULT_VERSION: TpmVersion = TpmVersion::VER_138;

/// Default vTPM NVRAM size provisioned for a new VMGS.
pub const TPM_DEFAULT_SIZE: u32 = 32768;

// TODO: combine this with MAX_NV_INDEX_SIZE
/// Default NVRAM index size provisioned for AKCert.
pub const TPM_DEFAULT_AKCERT_SIZE: u32 = 4096;
2 changes: 1 addition & 1 deletion vm/vmgs/vmgs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ anyhow.workspace = true
async-trait.workspace = true
cfg-if.workspace = true
crc32fast.workspace = true
cvm_tracing.workspace = true
getrandom.workspace = true
openssl = { optional = true, features = ["vendored"], workspace = true }
thiserror.workspace = true
tracing.workspace = true
cvm_tracing.workspace = true
zerocopy.workspace = true
[target.'cfg(windows)'.dependencies.windows]
workspace = true
Expand Down
15 changes: 14 additions & 1 deletion vm/vmgs/vmgs/src/vmgs_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub struct Vmgs {
encrypted_metadata_keys: [VmgsEncryptionKey; 2],
reprovisioned: bool,
provisioned_this_boot: bool,
formatted_on_failure: bool,

#[cfg_attr(feature = "inspect", inspect(skip))]
logger: Option<Arc<dyn VmgsLogger>>,
Expand Down Expand Up @@ -178,7 +179,10 @@ impl Vmgs {
}
Err(err) if format_on_failure => {
tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
Self::format_new(disk, logger).await
Self::format_new(disk, logger).await.map(|mut vmgs| {
vmgs.formatted_on_failure = true;
vmgs
})
}
Err(err) => {
let event_log_id = match err {
Expand Down Expand Up @@ -364,6 +368,7 @@ impl Vmgs {
encrypted_metadata_keys,
reprovisioned,
provisioned_this_boot: false,
formatted_on_failure: false,

#[cfg(feature = "inspect")]
stats: Default::default(),
Expand Down Expand Up @@ -1524,6 +1529,12 @@ impl Vmgs {
self.provisioned_this_boot
}

/// Whether the VMGS file was reprovisioned during the most recent boot
/// because it was corrupted
pub fn was_formatted_on_failure(&self) -> bool {
self.formatted_on_failure
}

fn prepare_new_header(&self, file_table_fcb: &ResolvedFileControlBlock) -> VmgsHeader {
VmgsHeader {
signature: VMGS_SIGNATURE,
Expand Down Expand Up @@ -2023,6 +2034,7 @@ pub mod save_restore {
}),
reprovisioned,
provisioned_this_boot: false,
formatted_on_failure: false,
logger,
}
}
Expand Down Expand Up @@ -2052,6 +2064,7 @@ pub mod save_restore {
logger: _,
reprovisioned,
provisioned_this_boot: _,
formatted_on_failure: _,
} = self;

state::SavedVmgsState {
Expand Down
1 change: 1 addition & 0 deletions vm/vmgs/vmgs_format/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ inspect = { workspace = true, optional = true }

bitfield-struct.workspace = true
static_assertions.workspace = true
tpm_protocol.workspace = true
zerocopy.workspace = true
[lints]
workspace = true
42 changes: 42 additions & 0 deletions vm/vmgs/vmgs_format/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use core::ops::IndexMut;
use inspect::Inspect;
use open_enum::open_enum;
use static_assertions::const_assert;
use tpm_protocol::TpmVersion;
use zerocopy::FromBytes;
use zerocopy::Immutable;
use zerocopy::IntoBytes;
Expand Down Expand Up @@ -49,6 +50,7 @@ open_enum! {
HIBERNATION_FIRMWARE = 14,
PLATFORM_SEED = 15,
PROVENANCE_DOC = 16,
PROVISIONING_MARKER = 17,

EXTENDED_FILE_TABLE = 63,
}
Expand Down Expand Up @@ -215,6 +217,46 @@ open_enum! {
}
}

open_enum! {
/// Entities that can provision a new VMGS file.
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
pub enum VmgsProvisioner: u32 {
HCL = 1,
OPENHCL = 2,
HOST_AGENT_VMGSTOOL = 3,
CPS_VMGSTOOL_CVM = 4,
CPS_VMGSTOOL_TVM = 5,
HCL_POST_PROVISIONING = 6,
}
}

/// Current version of the VMGS provisioning diagnostic marker.
pub const PROVISIONING_MARKER_CURRENT_VERSION: u32 = 1;

/// Length of HCL version field.
pub const HCL_VERSION_LENGTH: usize = 40;

/// Diagnostic marker that describes how a VMGS file was provisioned.
#[repr(C, packed)]
#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
pub struct ProvisioningMarker {
pub marker_version: u32,
pub provisioner: VmgsProvisioner,
pub reset_by_gsl_flag: u8,
pub _reserved1: [u8; 3],
pub vtpm_version: TpmVersion,
pub vtpm_nvram_size: u32,
pub vtpm_akcert_size: u32,
pub vtpm_akcert_attrs: u32,
// provisioner == OPENHCL: string representation of commit hash; otherwise undefined
pub hcl_version: [u8; HCL_VERSION_LENGTH],
pub _reserved2: [u8; 956],
}

// Size of the provisioning marker.
const PROVISIONING_MARKER_SIZE: usize = 1024;
static_assertions::const_assert_eq!(PROVISIONING_MARKER_SIZE, size_of::<ProvisioningMarker>());

/// Markers used internally to indicate how the VMGS should be treated
#[cfg_attr(feature = "inspect", derive(Inspect))]
#[bitfield(u16)]
Expand Down