1af6b54e2SJason A. Donenfeld // SPDX-License-Identifier: GPL-2.0
2af6b54e2SJason A. Donenfeld /*
3af6b54e2SJason A. Donenfeld * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
4af6b54e2SJason A. Donenfeld *
5af6b54e2SJason A. Donenfeld * The "Virtual Machine Generation ID" is exposed via ACPI and changes when a
6af6b54e2SJason A. Donenfeld * virtual machine forks or is cloned. This driver exists for shepherding that
7af6b54e2SJason A. Donenfeld * information to random.c.
8af6b54e2SJason A. Donenfeld */
9af6b54e2SJason A. Donenfeld
10af6b54e2SJason A. Donenfeld #include <linux/kernel.h>
11af6b54e2SJason A. Donenfeld #include <linux/module.h>
12af6b54e2SJason A. Donenfeld #include <linux/acpi.h>
13af6b54e2SJason A. Donenfeld #include <linux/random.h>
14af6b54e2SJason A. Donenfeld
15af6b54e2SJason A. Donenfeld ACPI_MODULE_NAME("vmgenid");
16af6b54e2SJason A. Donenfeld
17af6b54e2SJason A. Donenfeld enum { VMGENID_SIZE = 16 };
18af6b54e2SJason A. Donenfeld
19af6b54e2SJason A. Donenfeld struct vmgenid_state {
20af6b54e2SJason A. Donenfeld u8 *next_id;
21af6b54e2SJason A. Donenfeld u8 this_id[VMGENID_SIZE];
22af6b54e2SJason A. Donenfeld };
23af6b54e2SJason A. Donenfeld
vmgenid_add(struct acpi_device * device)24af6b54e2SJason A. Donenfeld static int vmgenid_add(struct acpi_device *device)
25af6b54e2SJason A. Donenfeld {
26af6b54e2SJason A. Donenfeld struct acpi_buffer parsed = { ACPI_ALLOCATE_BUFFER };
27af6b54e2SJason A. Donenfeld struct vmgenid_state *state;
28af6b54e2SJason A. Donenfeld union acpi_object *obj;
29af6b54e2SJason A. Donenfeld phys_addr_t phys_addr;
30af6b54e2SJason A. Donenfeld acpi_status status;
31af6b54e2SJason A. Donenfeld int ret = 0;
32af6b54e2SJason A. Donenfeld
33af6b54e2SJason A. Donenfeld state = devm_kmalloc(&device->dev, sizeof(*state), GFP_KERNEL);
34af6b54e2SJason A. Donenfeld if (!state)
35af6b54e2SJason A. Donenfeld return -ENOMEM;
36af6b54e2SJason A. Donenfeld
37af6b54e2SJason A. Donenfeld status = acpi_evaluate_object(device->handle, "ADDR", NULL, &parsed);
38af6b54e2SJason A. Donenfeld if (ACPI_FAILURE(status)) {
39af6b54e2SJason A. Donenfeld ACPI_EXCEPTION((AE_INFO, status, "Evaluating ADDR"));
40af6b54e2SJason A. Donenfeld return -ENODEV;
41af6b54e2SJason A. Donenfeld }
42af6b54e2SJason A. Donenfeld obj = parsed.pointer;
43af6b54e2SJason A. Donenfeld if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2 ||
44af6b54e2SJason A. Donenfeld obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
45af6b54e2SJason A. Donenfeld obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
46af6b54e2SJason A. Donenfeld ret = -EINVAL;
47af6b54e2SJason A. Donenfeld goto out;
48af6b54e2SJason A. Donenfeld }
49af6b54e2SJason A. Donenfeld
50af6b54e2SJason A. Donenfeld phys_addr = (obj->package.elements[0].integer.value << 0) |
51af6b54e2SJason A. Donenfeld (obj->package.elements[1].integer.value << 32);
52af6b54e2SJason A. Donenfeld state->next_id = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE, MEMREMAP_WB);
53af6b54e2SJason A. Donenfeld if (IS_ERR(state->next_id)) {
54af6b54e2SJason A. Donenfeld ret = PTR_ERR(state->next_id);
55af6b54e2SJason A. Donenfeld goto out;
56af6b54e2SJason A. Donenfeld }
57af6b54e2SJason A. Donenfeld
58af6b54e2SJason A. Donenfeld memcpy(state->this_id, state->next_id, sizeof(state->this_id));
59af6b54e2SJason A. Donenfeld add_device_randomness(state->this_id, sizeof(state->this_id));
60af6b54e2SJason A. Donenfeld
61af6b54e2SJason A. Donenfeld device->driver_data = state;
62af6b54e2SJason A. Donenfeld
63af6b54e2SJason A. Donenfeld out:
64af6b54e2SJason A. Donenfeld ACPI_FREE(parsed.pointer);
65af6b54e2SJason A. Donenfeld return ret;
66af6b54e2SJason A. Donenfeld }
67af6b54e2SJason A. Donenfeld
vmgenid_notify(struct acpi_device * device,u32 event)68af6b54e2SJason A. Donenfeld static void vmgenid_notify(struct acpi_device *device, u32 event)
69af6b54e2SJason A. Donenfeld {
70af6b54e2SJason A. Donenfeld struct vmgenid_state *state = acpi_driver_data(device);
71af6b54e2SJason A. Donenfeld u8 old_id[VMGENID_SIZE];
72af6b54e2SJason A. Donenfeld
73af6b54e2SJason A. Donenfeld memcpy(old_id, state->this_id, sizeof(old_id));
74af6b54e2SJason A. Donenfeld memcpy(state->this_id, state->next_id, sizeof(state->this_id));
75af6b54e2SJason A. Donenfeld if (!memcmp(old_id, state->this_id, sizeof(old_id)))
76af6b54e2SJason A. Donenfeld return;
77af6b54e2SJason A. Donenfeld add_vmfork_randomness(state->this_id, sizeof(state->this_id));
78af6b54e2SJason A. Donenfeld }
79af6b54e2SJason A. Donenfeld
80af6b54e2SJason A. Donenfeld static const struct acpi_device_id vmgenid_ids[] = {
81*0396e46dSMichael Kelley { "VMGENCTR", 0 },
82af6b54e2SJason A. Donenfeld { "VM_GEN_COUNTER", 0 },
83af6b54e2SJason A. Donenfeld { }
84af6b54e2SJason A. Donenfeld };
85af6b54e2SJason A. Donenfeld
86af6b54e2SJason A. Donenfeld static struct acpi_driver vmgenid_driver = {
87af6b54e2SJason A. Donenfeld .name = "vmgenid",
88af6b54e2SJason A. Donenfeld .ids = vmgenid_ids,
89af6b54e2SJason A. Donenfeld .owner = THIS_MODULE,
90af6b54e2SJason A. Donenfeld .ops = {
91af6b54e2SJason A. Donenfeld .add = vmgenid_add,
92af6b54e2SJason A. Donenfeld .notify = vmgenid_notify
93af6b54e2SJason A. Donenfeld }
94af6b54e2SJason A. Donenfeld };
95af6b54e2SJason A. Donenfeld
96af6b54e2SJason A. Donenfeld module_acpi_driver(vmgenid_driver);
97af6b54e2SJason A. Donenfeld
98af6b54e2SJason A. Donenfeld MODULE_DEVICE_TABLE(acpi, vmgenid_ids);
99af6b54e2SJason A. Donenfeld MODULE_DESCRIPTION("Virtual Machine Generation ID");
100af6b54e2SJason A. Donenfeld MODULE_LICENSE("GPL v2");
101af6b54e2SJason A. Donenfeld MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");
102