xref: /openbmc/linux/drivers/virt/vmgenid.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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