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

RFC: add optional disk table to the VMGS file #373

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
7 changes: 7 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4512,6 +4512,7 @@ dependencies = [
"build_rs_guest_arch",
"chipset",
"debug_worker",
"disk_crypt",
"disk_striped",
"hyperv_ic",
"mesh_worker",
Expand Down Expand Up @@ -6741,6 +6742,7 @@ dependencies = [
"disk_backend",
"disk_backend_resources",
"disk_blockdevice",
"disk_crypt_resources",
"disk_get_vmgs",
"disk_nvme",
"firmware_pcat",
Expand Down Expand Up @@ -6793,6 +6795,7 @@ dependencies = [
"pal_uring",
"parking_lot",
"profiler_worker",
"prost",
"safe_intrinsics",
"scsi_buffers",
"scsi_core",
Expand Down Expand Up @@ -6845,6 +6848,7 @@ dependencies = [
"vmcore",
"vmgs",
"vmgs_broker",
"vmgs_format",
"vmgs_resources",
"vmm_core",
"vmm_core_defs",
Expand Down Expand Up @@ -8000,6 +8004,8 @@ dependencies = [
"bitfield-struct",
"inspect",
"open_enum",
"prost",
"prost-build",
"static_assertions",
"zerocopy",
]
Expand Down Expand Up @@ -8037,6 +8043,7 @@ dependencies = [
"hcl_compat_uefi_nvram_storage",
"hex",
"pal_async",
"prost",
"serde",
"serde_json",
"tempfile",
Expand Down
3 changes: 3 additions & 0 deletions openhcl/openvmm_hcl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ uidevices = ["openvmm_hcl_resources/uidevices", "openvmm_hcl_resources/vnc_worke
# Enable NVMe emulation.
nvme = ["openvmm_hcl_resources/nvme", "underhill_entry/vpci"]

# Encrypted disk support.
disk_crypt = ["openvmm_hcl_resources/disk_crypt"]

[target.'cfg(target_os = "linux")'.dependencies]
underhill_entry.workspace = true
openvmm_hcl_resources.workspace = true
Expand Down
1 change: 1 addition & 0 deletions openhcl/openvmm_hcl_resources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ vmsocket.workspace = true
serial_core.workspace = true
vmbus_serial_guest.workspace = true

disk_crypt = { workspace = true, optional = true }
disk_striped.workspace = true

scsidisk.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions openhcl/openvmm_hcl_resources/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ vm_resource::register_static_resolvers! {
// `BlockDevice` and `NvmeDisk` are registered dynamically since they have
// runtime dependencies.
disk_striped::StripedDiskResolver,
#[cfg(feature = "disk_crypt")]
disk_crypt::resolver::DiskCryptResolver,

// SCSI
scsidisk::resolver::SimpleScsiResolver,
Expand Down
3 changes: 3 additions & 0 deletions openhcl/underhill_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ chipset_legacy.workspace = true
disk_backend.workspace = true
disk_backend_resources.workspace = true
disk_blockdevice.workspace = true
disk_crypt_resources.workspace = true
disk_get_vmgs.workspace = true
disk_nvme.workspace = true
firmware_uefi.workspace = true
Expand Down Expand Up @@ -121,6 +122,7 @@ guestmem.workspace = true
vmcore.workspace = true
vm_resource.workspace = true
vmgs = { workspace = true, features = ["encryption_ossl", "save_restore"] }
vmgs_format = { workspace = true, features = ["proto"] }
vmgs_broker = { workspace = true, features = ["encryption_ossl"] }
vmgs_resources.workspace = true
x86defs.workspace = true
Expand Down Expand Up @@ -155,6 +157,7 @@ futures.workspace = true
getrandom.workspace = true
libc.workspace = true
parking_lot.workspace = true
prost.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_helpers.workspace = true
serde_json.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions openhcl/underhill_core/src/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ pub(crate) struct LoadedVm {
pub nvme_manager: Option<NvmeManager>,
pub emuplat_servicing: EmuplatServicing,
pub device_interfaces: Option<DeviceInterfaces>,
pub disk_table: Option<vmgs_format::DiskTable>,
/// Memory map with IGVM types for each range.
pub vtl0_memory_map: Vec<(MemoryRangeWithNode, MemoryMapEntryType)>,

Expand Down Expand Up @@ -215,6 +216,7 @@ impl LoadedVm {
device_config_send,
self.get_client.clone(),
self.device_interfaces.take().unwrap(),
self.disk_table.take(),
);

threadpool.spawn("VTL2 settings services", {
Expand Down
55 changes: 52 additions & 3 deletions openhcl/underhill_core/src/dispatch/vtl2_settings_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ enum Error<'a> {
},
#[error("cannot modify IDE configuration at runtime")]
StorageCannotModifyIdeAtRuntime,
#[error("unknown disk cipher {cipher}")]
StorageBadCipher { cipher: i32 },
#[error("disk {disk_id} is missing VMGS disk table entry")]
StorageMissingDiskTableEntry { disk_id: &'a str },
}

impl Error<'_> {
Expand Down Expand Up @@ -179,6 +183,10 @@ impl Error<'_> {
Error::StorageChangeMediaFailed { .. } => {
Vtl2SettingsErrorCode::StorageChangeMediaFailed
}
Error::StorageBadCipher { .. } => Vtl2SettingsErrorCode::StorageInvalidDiskCipher,
Error::StorageMissingDiskTableEntry { .. } => {
Vtl2SettingsErrorCode::StorageMissingDiskTableEntry
}
}
}
}
Expand Down Expand Up @@ -233,6 +241,7 @@ pub struct Vtl2SettingsWorker {
device_config_send: mesh::Sender<Vtl2ConfigNicRpc>,
get_client: guest_emulation_transport::GuestEmulationTransportClient,
interfaces: DeviceInterfaces,
disk_table: Option<vmgs_format::DiskTable>,
}

pub struct DeviceInterfaces {
Expand All @@ -247,12 +256,14 @@ impl Vtl2SettingsWorker {
device_config_send: mesh::Sender<Vtl2ConfigNicRpc>,
get_client: guest_emulation_transport::GuestEmulationTransportClient,
interfaces: DeviceInterfaces,
disk_table: Option<vmgs_format::DiskTable>,
) -> Vtl2SettingsWorker {
Vtl2SettingsWorker {
old_settings: initial_settings,
device_config_send,
get_client,
interfaces,
disk_table,
}
}

Expand Down Expand Up @@ -368,6 +379,7 @@ impl Vtl2SettingsWorker {
&StorageContext {
uevent_listener,
use_nvme_vfio: self.interfaces.use_nvme_vfio,
disk_table: self.disk_table.as_ref(),
},
&disk,
false,
Expand All @@ -386,6 +398,7 @@ impl Vtl2SettingsWorker {
&StorageContext {
uevent_listener,
use_nvme_vfio: self.interfaces.use_nvme_vfio,
disk_table: self.disk_table.as_ref(),
},
&disk,
false,
Expand Down Expand Up @@ -894,13 +907,14 @@ pub enum StorageDevicePath {

async fn make_disk_type_from_physical_devices(
ctx: &mut CancelContext,
device_id: &str,
storage_context: &StorageContext<'_>,
physical_devices: &PhysicalDevices,
ntfs_guid: Option<Guid>,
read_only: bool,
is_restoring: bool,
) -> Result<Resource<DiskHandleKind>, Vtl2SettingsErrorInfo> {
let disk_type = match *physical_devices {
let mut disk_type = match *physical_devices {
PhysicalDevices::Single { ref device } => {
make_disk_type_from_physical_device(ctx, storage_context, device, read_only).await?
}
Expand Down Expand Up @@ -935,10 +949,37 @@ async fn make_disk_type_from_physical_devices(
} else {
// DEVNOTE: open-source OpenHCL does not currently have a resolver
// for `AutoFormattedDiskHandle`.
return Ok(Resource::new(AutoFormattedDiskHandle {
disk_type = Resource::new(AutoFormattedDiskHandle {
disk: disk_type,
guid: ntfs_guid.into(),
}));
});
}
}

if let Some(disk_table) = storage_context.disk_table {
let entry = disk_table
.disks
.iter()
.find(|k| k.disk_id == device_id)
.ok_or(Error::StorageMissingDiskTableEntry { disk_id: device_id })?;

let cipher = match entry.cipher() {
vmgs_format::DiskCipher::Unspecified => {
return Err(Error::StorageBadCipher {
cipher: entry.cipher,
}
.into());
}
vmgs_format::DiskCipher::None => None,
vmgs_format::DiskCipher::XtsAes256 => Some(disk_crypt_resources::Cipher::XtsAes256),
};

if let Some(cipher) = cipher {
disk_type = Resource::new(disk_crypt_resources::DiskCryptHandle {
disk: disk_type,
cipher,
key: entry.key.clone(),
});
}
}

Expand All @@ -948,6 +989,7 @@ async fn make_disk_type_from_physical_devices(
struct StorageContext<'a> {
uevent_listener: &'a UeventListener,
use_nvme_vfio: bool,
disk_table: Option<&'a vmgs_format::DiskTable>,
}

#[instrument(skip_all)]
Expand Down Expand Up @@ -1253,6 +1295,7 @@ async fn make_disk_type(
(false, physical_devices) => Some(
make_disk_type_from_physical_devices(
ctx,
&disk.disk_params().device_id,
storage_context,
physical_devices,
disk.ntfs_guid(),
Expand All @@ -1264,6 +1307,7 @@ async fn make_disk_type(
(true, physical_devices) => {
let disk_type = make_disk_type_from_physical_devices(
ctx,
&disk.disk_params().device_id,
storage_context,
physical_devices,
disk.ntfs_guid(),
Expand Down Expand Up @@ -1294,6 +1338,7 @@ async fn make_nvme_disk_config(
) -> Result<NamespaceDefinition, Vtl2SettingsErrorInfo> {
let disk_type = make_disk_type_from_physical_devices(
ctx,
&namespace.disk_params.device_id,
storage_context,
&namespace.physical_devices,
None,
Expand Down Expand Up @@ -1422,6 +1467,7 @@ pub async fn create_storage_controllers_from_vtl2_settings(
sub_channels: u16,
is_restoring: bool,
default_io_queue_depth: u32,
disk_table: Option<&vmgs_format::DiskTable>,
) -> Result<
(
Option<UhIdeControllerConfig>,
Expand All @@ -1433,6 +1479,7 @@ pub async fn create_storage_controllers_from_vtl2_settings(
let storage_context = StorageContext {
uevent_listener,
use_nvme_vfio,
disk_table,
};
let ide_controller =
make_ide_controller_config(ctx, &storage_context, settings, is_restoring).await?;
Expand Down Expand Up @@ -1772,6 +1819,7 @@ impl InitialControllers {
use_nvme_vfio: bool,
is_restoring: bool,
default_io_queue_depth: u32,
disk_table: Option<&vmgs_format::DiskTable>,
) -> anyhow::Result<Self> {
const VM_CONFIG_TIME_OUT_IN_SECONDS: u64 = 5;
let mut context =
Expand All @@ -1793,6 +1841,7 @@ impl InitialControllers {
fixed.scsi_sub_channels,
is_restoring,
default_io_queue_depth,
disk_table,
)
.await?
} else {
Expand Down
1 change: 1 addition & 0 deletions openhcl/underhill_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ async fn launch_workers(
no_sidecar_hotplug: opt.no_sidecar_hotplug,
gdbstub: opt.gdbstub,
hide_isolation: opt.hide_isolation,
disk_table: opt.disk_table,
};

let (mut remote_console_cfg, framebuffer_access) =
Expand Down
6 changes: 6 additions & 0 deletions openhcl/underhill_core/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ pub struct Options {
/// (OPENHCL_NO_SIDECAR_HOTPLUG=1) Leave sidecar VPs remote even if they
/// hit exits.
pub no_sidecar_hotplug: bool,

/// (OPENHCL_USE_DISK_TABLE=1) Read the disk table from the VMGS file to
/// determine the allowed set of disks and their associated encryption keys.
pub disk_table: bool,
}

impl Options {
Expand Down Expand Up @@ -181,6 +185,7 @@ impl Options {
let no_sidecar_hotplug = parse_legacy_env_bool("OPENHCL_NO_SIDECAR_HOTPLUG");
let gdbstub = parse_legacy_env_bool("OPENHCL_GDBSTUB");
let gdbstub_port = parse_legacy_env_number("OPENHCL_GDBSTUB_PORT")?.map(|x| x as u32);
let disk_table = parse_env_bool("OPENHCL_USE_DISK_TABLE");

let mut args = std::env::args().chain(extra_args);
// Skip our own filename.
Expand Down Expand Up @@ -234,6 +239,7 @@ impl Options {
hide_isolation,
halt_on_guest_halt,
no_sidecar_hotplug,
disk_table,
})
}

Expand Down
17 changes: 17 additions & 0 deletions openhcl/underhill_core/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ pub struct UnderhillEnvCfg {
pub gdbstub: bool,
/// Hide the isolation mode from the guest.
pub hide_isolation: bool,
/// Enable and require the disk table.
pub disk_table: bool,
}

/// Bundle of config + runtime objects for hooking into the underhill remote
Expand Down Expand Up @@ -1613,6 +1615,19 @@ async fn new_underhill_vm(
// Make the GET available for other resources.
resolver.add_resolver(get_client.clone());

// Read the disk table from VMGS.
let disk_table = if env_cfg.disk_table {
let table = match vmgs.read_file(vmgs::FileId::DISK_TABLE).await {
Ok(keys) => <vmgs_format::DiskTable as prost::Message>::decode(keys.as_slice())
.context("failed to decode disk table")?,
Err(vmgs::Error::FileInfoAllocated) => vmgs_format::DiskTable::default(),
Err(err) => return Err(err).context("failed to read disk table")?,
};
Some(table)
} else {
None
};

// Spawn the VMGS client for multi-task access.
let (vmgs_client, vmgs_handle) = spawn_vmgs_broker(get_spawner, vmgs);
resolver.add_resolver(VmgsFileResolver::new(vmgs_client.clone()));
Expand Down Expand Up @@ -1715,6 +1730,7 @@ async fn new_underhill_vm(
env_cfg.nvme_vfio,
is_restoring,
default_io_queue_depth,
disk_table.as_ref(),
)
.instrument(tracing::info_span!("new_initial_controllers"))
.await
Expand Down Expand Up @@ -2859,6 +2875,7 @@ async fn new_underhill_vm(
netvsp_state,
},
device_interfaces: Some(controllers.device_interfaces),
disk_table,
vtl0_memory_map,

vmbus_server,
Expand Down
12 changes: 10 additions & 2 deletions openvmm/openvmm_entry/src/storage_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,18 @@ impl StorageBuilder {
}
};

// Use a consistent device ID, based on the location.
let mut device_id = Guid::from_static_str("00000000-a4a3-11ef-9347-43f645bdedf8");
device_id.data1 = match target {
DiskLocation::Ide(_, _) => unreachable!(),
DiskLocation::Scsi(_) => location,
DiskLocation::Nvme(_) => location | (1 << 31),
};

luns.push(Lun {
location,
device_id: Guid::new_random().to_string(),
vendor_id: "HvLite".to_string(),
device_id: device_id.to_string(),
vendor_id: "OpenVMM".to_string(),
product_id: "Disk".to_string(),
product_revision_level: "1.0".to_string(),
serial_number: "0".to_string(),
Expand Down
Loading
Loading