Visitar URL original
devices: acpi: Add VM Generation ID device by saravan2 · Pull Request #8907 · cloud-hypervisor/cloud-hypervisor · GitHub
Skip to content

devices: acpi: Add VM Generation ID device - #8907

Open
saravan2 wants to merge 4 commits into
cloud-hypervisor:mainfrom
saravan2:vmgenid
Open

saravan2 wants to merge 4 commits into
cloud-hypervisor:mainfrom
saravan2:vmgenid

Conversation

@saravan2

@saravan2 saravan2 commented Sep 22, 2026 •

Copy link
Copy Markdown
Member
  • Adds a default VM Generation ID device. The guest kernel requires CONFIG_VMGENID to get notified and reseed the random number generator.
  • The 128 bits value lives in a page the device owns, mapped into the guest at an address the platform MMIO allocator assigns. The location is recorded in the device tree as an MMIO address range, so it survives a snapshot and a restore sequence can access it.
  • The spec permits device MMIO, but the Linux vmgenid driver implementation reads it with a plain memcpy, which becomes a load pair on arm64. arm64 does not offer KVM syndrome for load pairs so it wont be able to emulate it. For this reason the page is backed by a KVM memslot.
  • Live migration generates a new VM generation id, because the destination completes the migration through the restore path.
  • The change notification uses the existing GED interrupt.
  • Tests :
    • unit test, test_vmgenid_regenerate, covers value regeneration.
    • integration test, test_snapshot_restore_vmgenid, snapshots and restores a VM and asserts the GED interrupt fired and the guest reseeded, on both arches with arm64 using acpi=on.

@saravan2 saravan2 self-assigned this Sep 22, 2026
@saravan2
saravan2 marked this pull request as ready for review September 22, 2026 08:18
@saravan2
saravan2 requested a review from a team as a code owner September 22, 2026 08:18

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we can make it unconditional - if the guest is enlightened to use it it will use it.

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The spec says it can be device MMIO as well as RAM. Could we do that instead?

Comment thread arch/src/aarch64/layout.rs Outdated
@saravan2

Copy link
Copy Markdown
Member Author

I think we can make it unconditional - if the guest is enlightened to use it it will use it.

Yay. I made it optional to get through upstream review.

@rbradford

Copy link
Copy Markdown
Member

I think we can make it unconditional - if the guest is enlightened to use it it will use it.

Yay. I made it optional to get through upstream review.

Adding an option is a sure fire way to get me to give a "Reqeust changes" review :-)

@rbradford

Copy link
Copy Markdown
Member

The spec says it can be device MMIO as well as RAM. Could we do that instead?

Maybe just piggy back onto the existing ACPI MMIO device rather than needing to add a new one?

@phip1611

phip1611 commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

The spec says it can be device MMIO as well as RAM. Could we do that instead?

Maybe just piggy back onto the existing ACPI MMIO device rather than needing to add a new one?

You mean DeviceManager which implements BusDevice? I wanted to split DeviceManager into its true core and AcpiPciHotplugController already to keep the code maintainable and prevent a possible race condition (#8028). So please don't introduce another functionality into this gigantic type. I am advocating for a dedicated device, using the MMIO path as Rob pointed out.

@rbradford

Copy link
Copy Markdown
Member

The spec says it can be device MMIO as well as RAM. Could we do that instead?

Maybe just piggy back onto the existing ACPI MMIO device rather than needing to add a new one?

You mean DeviceManager which implements BusDevice? I wanted to split DeviceManager into its true core and AcpiPciHotplugController already to keep the code maintainable and prevent a possible race condition (#8028). So please don't introduce another functionality into this gigantic type. I am advocating for a dedicated device, using the MMIO path as Rob pointed out.

I'd forgotten that ACPI shutdown and hotplug were separate MMIO devices now. Indeed it doesn't make sense to lump into either of those. But BusDevice implementations are cheap and easy so we can have a new one.

@saravan2

Copy link
Copy Markdown
Member Author

I think we can make it unconditional - if the guest is enlightened to use it it will use it.

Yay. I made it optional to get through upstream review.

Adding an option is a sure fire way to get me to give a "Reqeust changes" review :-)

The device is unconditional now, and --platform no longer has a vmgenid key.

@saravan2

Copy link
Copy Markdown
Member Author

The spec says it can be device MMIO as well as RAM. Could we do that instead?

Maybe just piggy back onto the existing ACPI MMIO device rather than needing to add a new one?

You mean DeviceManager which implements BusDevice? I wanted to split DeviceManager into its true core and AcpiPciHotplugController already to keep the code maintainable and prevent a possible race condition (#8028). So please don't introduce another functionality into this gigantic type. I am advocating for a dedicated device, using the MMIO path as Rob pointed out.

VmGenIdDevice is its own type with its own page, and nothing was added to DeviceManager's BusDevice implementation.

@saravan2

Copy link
Copy Markdown
Member Author

The spec says it can be device MMIO as well as RAM. Could we do that instead?

Maybe just piggy back onto the existing ACPI MMIO device rather than needing to add a new one?

The value now lives in a page the device owns, mapped into the guest at an address from the platform MMIO allocator. So there is no guest RAM carve out and no layout constant.

Comment thread devices/src/acpi.rs
@yamahata

Copy link
Copy Markdown
Contributor

Now it became much better.
It's good to carve out the region from MMIO region and transfer the address on migration.
Lastly could you please include a pointer to the spec?

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unfortunately I think you missed the point of putting on the MMIO bus.

Comment thread vmm/src/device_manager.rs

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add an integration test? The unit tests have too many excessive helpers.

Comment thread vmm/src/device_manager.rs Outdated
Comment thread devices/src/acpi.rs Outdated
Comment thread devices/src/acpi.rs Outdated
Comment thread devices/src/acpi.rs Outdated
Comment thread vmm/src/device_manager.rs Outdated
Comment thread vmm/src/device_manager.rs Outdated
@rbradford

Copy link
Copy Markdown
Member

I also strongly suspect you used Claude to write the PR ("Where the value lives", "land", "draws", "closes the gap", etc). Please don't do that it's disrespectful to the human reviewers.

@saravan2

saravan2 commented Sep 27, 2026 •

Copy link
Copy Markdown
Member Author

Add an integration test? The unit tests have too many excessive helpers.

Our CI kernel (ch-release-v6.16.9-20260508) does not have CONFIG_VMGENID turned on :
https://github.com/cloud-hypervisor/linux
To create this integration test, we would require a new kernel release and an update to scripts/test_assets.yaml

@saravan2
saravan2 marked this pull request as draft September 27, 2026 18:08

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you - looking good! I think the page size is a blocker and I would love it if we could push some complexity out from device manager into the device.

Comment thread cloud-hypervisor/tests/integration.rs Outdated
Comment thread devices/src/acpi.rs Outdated
Comment thread vmm/src/device_manager.rs Outdated
Comment thread vmm/src/device_manager.rs Outdated
Comment thread cloud-hypervisor/tests/integration.rs Outdated

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comments in the integration tests are a bit wordy. Otherwise lgtm! Thanks for the patience. This will be a nice improvement.

Comment thread cloud-hypervisor/tests/integration.rs Outdated

@likebreath likebreath left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@saravan2 Can you address the last comment above and rebase? It would be good to have it for v54.0.

@saravan2

saravan2 commented Oct 9, 2026

Copy link
Copy Markdown
Member Author

Addressed final review suggestions. Thanks a ton.

@likebreath
likebreath added this pull request to the merge queue Oct 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@likebreath
likebreath added this pull request to the merge queue Oct 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@likebreath

Copy link
Copy Markdown
Member

@rbradford @rhakobyan The SEV-SNP job has been failing twice in a roll from the MQ. Can you please take a look? Thank you!

@rhakobyan

rhakobyan commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

@rbradford @rhakobyan The SEV-SNP job has been failing twice in a roll from the MQ. Can you please take a look? Thank you!

@likebreath ch-linux kernel is missing this commit which is needed for VMGENID to work under SEV-SNP. I manually backported it onto the SEV-SNP worker and the tests now pass. But we probably want to backport it to the upstream ch-linux as well.

@rbradford

Copy link
Copy Markdown
Member

@rbradford @rhakobyan The SEV-SNP job has been failing twice in a roll from the MQ. Can you please take a look? Thank you!

@likebreath ch-linux kernel is missing this commit which is needed for VMGENID to work under SEV-SNP. I manually backported it onto the SEV-SNP worker and the tests now pass. But we probably want to backport it to the upstream ch-linux as well.

I'm on it!

@saravan2

saravan2 commented Oct 9, 2026 •

Copy link
Copy Markdown
Member Author

@rbradford, @likebreath, @phip1611

test_snapshot_restore_vmgenid passes on integration-arm64

        PASS [  45.738s] (13/19) cloud-hypervisor::integration ivshmem::test_snapshot_restore_vmgenid

Once the underlying kernel used in integration-sev-snp is patched, this PR should clear the merge queue. I hope this happens before v54 release.

@tutkodev tutkodev left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tested-by: Martin Tutko martin@tutko.dev

Tested 446a2db against its merge base 125bd3d on x86_64 KVM. Guest: Linux 7.2.9 with CONFIG_VMGENID=y, direct kernel boot, 2 vCPU, 2 GiB. One snapshot per VMM, taken at guest uptime 40 s. In each guest the test checks three things: VMGENCTR under /sys/bus/acpi/devices, the GED interrupt count in /proc/interrupts, and "crng reseeded due to virtual machine fork" in dmesg.

Case 446a2db 125bd3d
Cold boot device present, GED 0, no reseed no device
Restore, memory_restore_mode=copy, 10 clones 10/10: GED 1, reseeded 0/10
Restore, memory_restore_mode=copyonwrite, 10 clones 10/10: GED 1, reseeded 0/10
Pause/resume of a restored VM no new GED, no new reseed no device
Snapshot of a restored VM, then restore GED 2, reseeded twice no device
Live migration of a restored VM (same host, unix socket) destination: GED 2, reseeded twice no device

The last three rows are not covered by test_snapshot_restore_vmgenid.

The same four commits, applied on main 3e32289 together with #9035 and an unrelated two-line local change, have also run as the VMM for up to 200 concurrent guests whose kernels lack the vmgenid driver. No regressions were observed.

@rbradford

Copy link
Copy Markdown
Member

@rbradford, @likebreath, @phip1611

test_snapshot_restore_vmgenid passes on integration-arm64

        PASS [  45.738s] (13/19) cloud-hypervisor::integration ivshmem::test_snapshot_restore_vmgenid

Once the underlying kernel used in integration-sev-snp is patched, this PR should clear the merge queue. I hope this happens before v54 release.

If you rebase you should be able to get the kernel update.

The vmgenid driver binds to an ACPI device carrying a 128 bit
identifier and reseeds the kernel random number generator whenever that
identifier changes.

Add the device model and the AML the driver expects, which is a
hardware ID it matches on and an ADDR package holding the low and high
halves of its address. The device writes the value into a region it
owns rather than into guest RAM.

Assisted-by: Claude [Claude Code]
Signed-off-by: Saravanan D <saravanand@crusoe.ai>
Every VM restored from a snapshot starts with the guest random number
generator state that snapshot captured, so two clones of one template
produce the same stream from /dev/urandom until something reseeds
them.

Give the device a page of its own, mapped into the guest at an address
from the platform allocator, and write a fresh value into it on every
restore. One snapshot can be restored any number of times and the VMM
cannot tell a clone from an ordinary resume, so every restore is
treated as a fork. The change notification uses the existing GED
interrupt.

The VM Generation ID specification permits the value in device MMIO, but
the Linux driver does not read it that way. It uses a plain memcpy
through a memremap mapping rather than the MMIO accessors, which on
arm64 compiles to a load pair. The arm64 fault for a load pair reports
no instruction syndrome, so KVM cannot emulate the access and a trapping
region would fail there. Backing the page with a memory slot keeps the
read a normal memory access on amd64 and arm64 architectures.

The address is recorded in the device tree, so a restored VM reuses it
rather than allocating again.

An incoming live migration completes through the same restore path, so
it writes a new value as well.

Assisted-by: Claude [Claude Code]
Signed-off-by: Saravanan D <saravanand@crusoe.ai>
Describe the device, the guest CONFIG_VMGENID requirement, how a direct
boot reaches ACPI on x86 and aarch64, and the reseed on restore and on
live migration. The reseed reaches the kernel generator only, so a
program that seeded a generator of its own is left untouched.

Assisted-by: Claude [Claude Code]
Signed-off-by: Saravanan D <saravanand@crusoe.ai>
Boot a VM, snapshot it, restore it, and confirm the restore signalled
the generation ID change. The restored guest must show the GED
interrupt fired and its kernel RNG reseeded through the vmgenid
driver.

The test runs on x86 and aarch64. On aarch64 the guest reaches ACPI on
a direct boot through the EFI stub tables and prefers it over the device
tree with acpi=on.

Assisted-by: Claude [Claude Code]
Signed-off-by: Saravanan D <saravanand@crusoe.ai>
@saravan2

Copy link
Copy Markdown
Member Author

@rbradford Rebased.

@rbradford

Copy link
Copy Markdown
Member

@saravan2 We dropped the Assisted-by requirement.

@rbradford rbradford left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did you ask the LLM to add the trait or did it do that on its own accord to the prompt of needing to free the memory? It's not pattern we have elsewhere in the project for the single user like this.

Comment thread vmm/src/device_manager.rs
}

#[cfg(not(target_arch = "riscv64"))]
impl devices::VmGenIdOps for VmGenIdHandler {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't like the addition of this trait for just one use (and it doesn't match anything else we do in the codebase). You could directly code the region setup into add_vmgenid_device() which is already busy with the MMIO allocation and the release into DeviceManager::drop().

A good abstraction, that i'm not proposing you add to this PR. Would be the idea of an OwnedMemoryRegion that would handle its own release.

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.

7 participants