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

Vmgenid on aarch64 #4687

Merged
merged 9 commits into from
Jul 22, 2024
Merged
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ and this project adheres to

### Added

- [#4687](https://github.com/firecracker-microvm/firecracker/pull/4687): Added
VMGenID support for microVMs running on ARM hosts with 6.1 guest kernels.
Support for VMGenID via DeviceTree bindings exists only on mainline 6.10 Linux
onwards. Users of Firecracker will need to backport the relevant patches on
top of their 6.1 kernels to make use of the feature.

### Changed

### Deprecated
Expand Down
34 changes: 20 additions & 14 deletions docs/snapshotting/random-for-clones.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ and there’s also an input pool which gathers entropy from various sources
available on the system, and is used to feed into or seed the other two
components. A very detailed description is available [here][3].

### Linux kernels from 4.8 until 5.17 (included)

The details of this newer implementation are used to make the recommendations
present in the document. There are in-kernel interfaces used to obtain random
numbers as well, but they are similar to using `/dev/urandom` (or `getrandom`
Expand Down Expand Up @@ -103,15 +101,17 @@ not increase the current entropy estimation. There is also an `ioctl` interface
which, given the appropriate privileges, can be used to add data to the input
entropy pool while also increasing the count, or completely empty all pools.

### Linux kernels from 5.18 onwards

Since version 5.18, Linux has support for the
[Virtual Machine Generation Identifier](https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/virtual-machine-generation-identifier).
The purpose of VMGenID is to notify the guest about time shift events, such as
resuming from a snapshot. The device exposes a 16-byte cryptographically random
identifier in guest memory. Firecracker implements VMGenID. When resuming a
microVM from a snapshot Firecracker writes a new identifier and injects a
notification to the guest. Linux,
### Linux kernels with VMGenID support

Linux has support for the
[Virtual Machine Generation Identifier](https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/virtual-machine-generation-identifier)
since 5.18 for ACPI systems. Since 6.10, Linux added support also for systems
that use DeviceTree instead of ACPI. The purpose of VMGenID is to notify the
guest about time shift events, such as resuming from a snapshot. The device
exposes a 16-byte cryptographically random identifier in guest memory.
Firecracker implements VMGenID. When resuming a microVM from a snapshot
Firecracker writes a new identifier and injects a notification to the guest.
Linux,
[uses this value](https://elixir.bootlin.com/linux/v5.18.19/source/drivers/virt/vmgenid.c#L77)
[as new randomness for its CSPRNG](https://elixir.bootlin.com/linux/v5.18.19/source/drivers/char/random.c#L908).
Quoting the random.c implementation of the kernel:
Expand All @@ -133,9 +133,15 @@ to emit a uevent to user space when it handles the notification. User space can
poll this uevent to know when it is safe to use `getrandom()`, et al. avoiding
the race condition.

Please note that, Firecracker will always enable VMGenID. In kernels earlier
than 5.18, where there is no VMGenID driver, the device will not have any effect
in the guest.
Firecracker supports VMGenID on ARM systems using the DeviceTree binding that
was added for the device in Linux 6.10. However, the latest Linux kernel that
Firecracker supports is 6.1. As a result, in order to use VMGenID on ARM
systems, users need to use a 6.1 kernel with the DeviceTree binding support
backported from 6.10. We provide a set of patches that apply cleanly on mainline
Linux 6.1 [here](../../resources/patches/vmgenid_dt).

Please note that, Firecracker will always enable VMGenID. In kernels where there
is no VMGenID driver, the device will not have any effect in the guest.

### User space considerations

Expand Down
21 changes: 9 additions & 12 deletions docs/snapshotting/snapshot-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -584,28 +584,25 @@ we also consider microVM A insecure if it resumes execution.
(VMGenID) is a virtual device that allows VM guests to detect when they have
resumed from a snapshot. It works by exposing a cryptographically random
16-bytes identifier to the guest. The VMM ensures that the value of the
indentifier changes every time the VM a time shift happens in the lifecycle of
identifier changes every time the VM a time shift happens in the lifecycle of
the VM, e.g. when it resumes from a snapshot.

Linux supports VMGenID since version 5.18. When Linux detects a change in the
identifier, it uses its value to reseed its internal PRNG. Moreover,
[since version 6.8](https://lkml.org/lkml/2023/5/31/414) Linux VMGenID driver
also emits to userspace a uevent. User space processes can monitor this uevent
for detecting snapshot resume events.
Linux supports VMGenID since version 5.18 for systems with ACPI support. Linux
6.10 added support also for systems that use DeviceTree instead of ACPI. When
Linux detects a change in the identifier, it uses its value to reseed its
internal PRNG.

Firecracker supports VMGenID device on x86 platforms. Firecracker will always
enable the device. During snapshot resume, Firecracker will update the 16-byte
generation ID and inject a notification in the guest before resuming its vCPUs.
Firecracker supports VMGenID device both on x86 and Aarch64 platforms.
Firecracker will always enable the device. During snapshot resume, Firecracker
will update the 16-byte generation ID and inject a notification in the guest
before resuming its vCPUs.

As a result, guests that run Linux versions >= 5.18 will re-seed their in-kernel
PRNG upon snapshot resume. User space applications can rely on the guest kernel
for randomness. State other than the guest kernel entropy pool, such as unique
identifiers, cached random numbers, cryptographic tokens, etc **will** still be
replicated across multiple microVMs resumed from the same snapshot. Users need
to implement mechanisms for ensuring de-duplication of such state, where needed.
On guests that run Linux versions >= 6.8, users can make use of the uevent that
VMGenID driver emits upon resuming from a snapshot, to be notified about
snapshot resume events.

## Vsock device limitation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1943,6 +1943,7 @@ CONFIG_CHARLCD_BL_FLASH=y
# CONFIG_UIO is not set
# CONFIG_VFIO is not set
CONFIG_VIRT_DRIVERS=y
CONFIG_VMGENID=y
CONFIG_VIRTIO=y
CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_BALLOON=y
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
From 1e96eb7dd95b1908428195ef69a30effd562f947 Mon Sep 17 00:00:00 2001
From: Sudan Landge <[email protected]>
Date: Wed, 17 Apr 2024 12:40:42 +0200
Subject: [PATCH 1/3] virt: vmgenid: change implementation to use a platform
driver

Re-implement vmgenid as a platform driver in preparation for adding
devicetree bindings support in next commits.

Signed-off-by: Sudan Landge <[email protected]>
Reviewed-by: Alexander Graf <[email protected]>
Tested-by: Babis Chalios <[email protected]>
[Jason: - Small style cleanups and refactoring.]
Signed-off-by: Jason A. Donenfeld <[email protected]>
---
drivers/virt/vmgenid.c | 99 +++++++++++++++++++++++++++---------------
1 file changed, 65 insertions(+), 34 deletions(-)

diff --git a/drivers/virt/vmgenid.c b/drivers/virt/vmgenid.c
index a1c467a0e9f71..0522107f9beeb 100644
--- a/drivers/virt/vmgenid.c
+++ b/drivers/virt/vmgenid.c
@@ -7,9 +7,10 @@
* information to random.c.
*/

+#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/acpi.h>
+#include <linux/platform_device.h>
#include <linux/random.h>

ACPI_MODULE_NAME("vmgenid");
@@ -21,19 +22,41 @@ struct vmgenid_state {
u8 this_id[VMGENID_SIZE];
};

-static int vmgenid_add(struct acpi_device *device)
+static void vmgenid_notify(struct device *device)
+{
+ struct vmgenid_state *state = device->driver_data;
+ u8 old_id[VMGENID_SIZE];
+
+ memcpy(old_id, state->this_id, sizeof(old_id));
+ memcpy(state->this_id, state->next_id, sizeof(state->this_id));
+ if (!memcmp(old_id, state->this_id, sizeof(old_id)))
+ return;
+ add_vmfork_randomness(state->this_id, sizeof(state->this_id));
+}
+
+static void setup_vmgenid_state(struct vmgenid_state *state, void *virt_addr)
{
+ state->next_id = virt_addr;
+ memcpy(state->this_id, state->next_id, sizeof(state->this_id));
+ add_device_randomness(state->this_id, sizeof(state->this_id));
+}
+
+static void vmgenid_acpi_handler(acpi_handle __always_unused handle,
+ u32 __always_unused event, void *dev)
+{
+ vmgenid_notify(dev);
+}
+
+static int vmgenid_add_acpi(struct device *dev, struct vmgenid_state *state)
+{
+ struct acpi_device *device = ACPI_COMPANION(dev);
struct acpi_buffer parsed = { ACPI_ALLOCATE_BUFFER };
- struct vmgenid_state *state;
union acpi_object *obj;
phys_addr_t phys_addr;
acpi_status status;
+ void *virt_addr;
int ret = 0;

- state = devm_kmalloc(&device->dev, sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
status = acpi_evaluate_object(device->handle, "ADDR", NULL, &parsed);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating ADDR"));
@@ -49,53 +72,61 @@ static int vmgenid_add(struct acpi_device *device)

phys_addr = (obj->package.elements[0].integer.value << 0) |
(obj->package.elements[1].integer.value << 32);
- state->next_id = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE, MEMREMAP_WB);
- if (IS_ERR(state->next_id)) {
- ret = PTR_ERR(state->next_id);
+
+ virt_addr = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE, MEMREMAP_WB);
+ if (IS_ERR(virt_addr)) {
+ ret = PTR_ERR(virt_addr);
goto out;
}
+ setup_vmgenid_state(state, virt_addr);

- memcpy(state->this_id, state->next_id, sizeof(state->this_id));
- add_device_randomness(state->this_id, sizeof(state->this_id));
-
- device->driver_data = state;
+ status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+ vmgenid_acpi_handler, dev);
+ if (ACPI_FAILURE(status)) {
+ ret = -ENODEV;
+ goto out;
+ }

+ dev->driver_data = state;
out:
ACPI_FREE(parsed.pointer);
return ret;
}

-static void vmgenid_notify(struct acpi_device *device, u32 event)
+static int vmgenid_add(struct platform_device *pdev)
{
- struct vmgenid_state *state = acpi_driver_data(device);
- u8 old_id[VMGENID_SIZE];
+ struct device *dev = &pdev->dev;
+ struct vmgenid_state *state;
+ int ret;

- memcpy(old_id, state->this_id, sizeof(old_id));
- memcpy(state->this_id, state->next_id, sizeof(state->this_id));
- if (!memcmp(old_id, state->this_id, sizeof(old_id)))
- return;
- add_vmfork_randomness(state->this_id, sizeof(state->this_id));
+ state = devm_kmalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ ret = vmgenid_add_acpi(dev, state);
+
+ if (ret < 0)
+ devm_kfree(dev, state);
+ return ret;
}

-static const struct acpi_device_id vmgenid_ids[] = {
+static const struct acpi_device_id vmgenid_acpi_ids[] = {
{ "VMGENCTR", 0 },
{ "VM_GEN_COUNTER", 0 },
{ }
};
-
-static struct acpi_driver vmgenid_driver = {
- .name = "vmgenid",
- .ids = vmgenid_ids,
- .owner = THIS_MODULE,
- .ops = {
- .add = vmgenid_add,
- .notify = vmgenid_notify
- }
+MODULE_DEVICE_TABLE(acpi, vmgenid_acpi_ids);
+
+static struct platform_driver vmgenid_plaform_driver = {
+ .probe = vmgenid_add,
+ .driver = {
+ .name = "vmgenid",
+ .acpi_match_table = vmgenid_acpi_ids,
+ },
};

-module_acpi_driver(vmgenid_driver);
+module_platform_driver(vmgenid_plaform_driver)

-MODULE_DEVICE_TABLE(acpi, vmgenid_ids);
MODULE_DESCRIPTION("Virtual Machine Generation ID");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jason A. Donenfeld <[email protected]>");
--
2.34.1

Loading
Loading