Skip to content

petri: add NVMe emulator support#3378

Open
babayet2 wants to merge 17 commits intomicrosoft:mainfrom
babayet2:emulated-nvme
Open

petri: add NVMe emulator support#3378
babayet2 wants to merge 17 commits intomicrosoft:mainfrom
babayet2:emulated-nvme

Conversation

@babayet2
Copy link
Copy Markdown
Collaborator

@babayet2 babayet2 commented Apr 26, 2026

Add coverage for HyperV NVMe devices leveraging a closed source emulator

Copilot AI review requested due to automatic review settings April 26, 2026 22:36
@babayet2 babayet2 requested a review from a team as a code owner April 26, 2026 22:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Hyper-V NVMe coverage in the Petri-based vmm_tests suite by provisioning NVMe devices via a closed-source Hyper-V “NVMe emulator” and wiring it through the Hyper-V Petri backend.

Changes:

  • Adds an (unstable) Hyper-V OpenHCL Linux storage test that validates discovery + IO via an NVMe emulator device.
  • Extends the Hyper-V Petri backend to translate VmbusStorageType::Nvme into post-create NVMe emulator device attachment.
  • Adds PowerShell-side plumbing to attach NVMe emulator devices after New-CustomVM completes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
vmm_tests/vmm_tests/tests/tests/x86_64/storage.rs Adds an unstable Hyper-V NVMe emulator storage test that exercises discovery and IO.
petri/src/vm/hyperv/mod.rs Maps VmbusStorageType::Nvme controllers to NVMe emulator device configs for Hyper-V backend runs.
petri/src/vm/hyperv/powershell.rs Adds nvme_emulator_devices to VM creation args and attaches them post-create via PowerShell.

Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
@github-actions
Copy link
Copy Markdown

Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/hyperv.psm1 Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/hyperv.psm1
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Copilot AI review requested due to automatic review settings April 27, 2026 23:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

petri/src/vm/hyperv/mod.rs:389

  • ide_controllers is still computed above, but it is no longer passed into HyperVNewCustomVMArgs (only storage_controllers is set). This will silently drop IDE drive configuration for Hyper-V VMs and also leaves an unused ide_controllers local. Include the ide_controllers field in hyperv_args (or remove the mapping if IDE is intentionally unsupported).
            guest_state_path,
            storage_controllers,
            com_3: supports_com3,
            imc_hiv,
            management_vtl_settings,
            ..HyperVNewCustomVMArgs::from_config(&config, &properties)?
        };

        let vm = HyperVVM::new(hyperv_args, log_source.clone(), driver.clone()).await?;

Comment thread petri/src/vm/hyperv/powershell.rs
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/hyperv.psm1 Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/powershell.rs Outdated
@github-actions
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

Copilot AI review requested due to automatic review settings May 1, 2026 06:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

petri/src/vm/hyperv/mod.rs:365

  • ide_controllers is constructed above but never passed into HyperVNewCustomVMArgs (and the struct update default sets ide_controllers to empty). This looks like a functional regression: IDE disks/DVDs from the firmware config will no longer be attached on Hyper-V, and the local ide_controllers now becomes an unused variable. Pass ide_controllers into hyperv_args (or remove the mapping if IDE is intentionally unsupported here).
        let hyperv_args = HyperVNewCustomVMArgs {
            firmware_file: igvm_file.clone(),
            firmware_parameters: openhcl_command_line,
            guest_state_path,
            storage_controllers,
            com_3: supports_com3,
            imc_hiv,
            management_vtl_settings,
            ..HyperVNewCustomVMArgs::from_config(&config, &properties)?
        };

Comment thread petri/src/vm/hyperv/hyperv.psm1 Outdated
if ($NvmeControllers) {
foreach ($controller in $NvmeControllers.GetEnumerator()) {
foreach ($vhdPath in $controller.Value["Drives"]) {
icacls $vhdPath /grant "NT VIRTUAL MACHINE\${vmid}:(F)" | Out-Null
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icacls call's result is discarded (| Out-Null) and failures won't stop VM creation. If ACL granting fails, the NVMe emulator will likely fail later with a harder-to-diagnose error. Consider checking $LASTEXITCODE (or using -ErrorAction Stop equivalents) and throwing a clear error that includes the VHD path and VMID.

Suggested change
icacls $vhdPath /grant "NT VIRTUAL MACHINE\${vmid}:(F)" | Out-Null
$icaclsOutput = icacls $vhdPath /grant "NT VIRTUAL MACHINE\${vmid}:(F)" 2>&1
$icaclsExitCode = $LASTEXITCODE
if ($icaclsExitCode -ne 0) {
$icaclsOutputText = ($icaclsOutput | Out-String).Trim()
throw ("Failed to grant NVMe VHD access with icacls for VHD path '{0}' and VMID '{1}' (exit code {2}). icacls output: {3}" -f `
$vhdPath,
$vmid,
$icaclsExitCode,
$icaclsOutputText)
}

Copilot uses AI. Check for mistakes.
Comment on lines +690 to +703
// Sort drives by namespace ID and validate they are exactly
// 1..N — the emulator assigns NSIDs sequentially by VHD
// argument order.
let mut sorted_drives: Vec<_> = drives.into_iter().collect();
sorted_drives.sort_by_key(|(nsid, _)| *nsid);
let expected: Vec<u32> = (1..=sorted_drives.len() as u32).collect();
let actual: Vec<u32> = sorted_drives.iter().map(|(nsid, _)| *nsid).collect();
anyhow::ensure!(
actual == expected,
"NVMe namespace IDs must be 1..{}, got {:?}",
expected.len(),
actual
);
nvme_entries.push((
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NVMe controllers with an empty drives map will currently serialize as Drives = @() and pass the actual == expected check (both empty). That likely results in a confusing failure from New-NvmeEmulatorRasd. Consider explicitly rejecting empty NVMe controllers with a clear error (including VSID).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the behavior if you have an empty NVMe controller? Is that valid?

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

Comment thread petri/src/vm/hyperv/hyperv.psm1 Outdated
Comment thread petri/src/vm/hyperv/powershell.rs
Comment thread vmm_tests/vmm_tests/tests/tests/x86_64/storage.rs Outdated
Copilot AI review requested due to automatic review settings May 4, 2026 21:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

petri/src/vm/hyperv/mod.rs:365

  • ide_controllers is still built above, but it is no longer passed into HyperVNewCustomVMArgs (the struct update only sets storage_controllers). This drops IDE controller configuration for the Hyper-V backend and likely breaks any VM config that relies on IDE (and also leaves ide_controllers unused). Pass the computed ide_controllers into the args struct again.
        let hyperv_args = HyperVNewCustomVMArgs {
            firmware_file: igvm_file.clone(),
            firmware_parameters: openhcl_command_line,
            guest_state_path,
            storage_controllers,
            com_3: supports_com3,
            imc_hiv,
            management_vtl_settings,
            ..HyperVNewCustomVMArgs::from_config(&config, &properties)?
        };

Comment thread petri/src/vm/hyperv/mod.rs Outdated
.filter(|c| matches!(c.controller_type, crate::VmbusStorageType::Nvme))
.flat_map(|c| c.drives.values())
.filter_map(|drive| match &drive.disk {
Some(Disk::Persistent(path)) => Some(path),
Comment on lines +695 to +699
let expected: Vec<u32> = (1..=sorted_drives.len() as u32).collect();
let actual: Vec<u32> = sorted_drives.iter().map(|(nsid, _)| *nsid).collect();
anyhow::ensure!(
actual == expected,
"NVMe namespace IDs must be 1..{}, got {:?}",
Comment on lines +703 to +714
nvme_entries.push((
format!("\"{vsid}\""),
ps::Value::new(ps::HashTable::new([
("Vtl", ps::Value::new(target_vtl as u32)),
(
"Drives",
ps::Value::new(ps::Array::new(sorted_drives.into_iter().map(
|(_, HyperVDrive { disk, .. })| {
disk.expect("NVMe drives must have disk paths")
},
))),
),
let scsi_instance = Guid::new_random();
const NVME_DISK_SECTORS: u64 = 0x5_0000;
const SECTOR_SIZE: u64 = 512;
const EXPECTED_NVME_DISK_SIZE_BYTES: u64 = NVME_DISK_SECTORS * SECTOR_SIZE;
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

Copilot AI review requested due to automatic review settings May 5, 2026 00:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

petri/src/vm/hyperv/mod.rs:365

  • ide_controllers is built from the firmware config above, but it isn’t passed into hyperv_args anymore (it falls back to the default empty map via ..from_config). This looks like a functional regression for any VM that relies on IDE attachments. Include ide_controllers in HyperVNewCustomVMArgs construction (or remove the mapping if IDE is intentionally unsupported).
        let hyperv_args = HyperVNewCustomVMArgs {
            firmware_file: igvm_file.clone(),
            firmware_parameters: openhcl_command_line,
            guest_state_path,
            storage_controllers,
            com_3: supports_com3,
            imc_hiv,
            management_vtl_settings,
            ..HyperVNewCustomVMArgs::from_config(&config, &properties)?
        };

Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread vmm_tests/vmm_tests/tests/tests/x86_64/storage.rs
Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread petri/src/vm/hyperv/mod.rs
Copilot AI review requested due to automatic review settings May 5, 2026 23:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread petri/src/vm/hyperv/mod.rs Outdated
Comment thread vmm_tests/vmm_tests/tests/tests/x86_64/storage.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants