1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5 * The "Virtual Machine Generation ID" is exposed via ACPI or DT and changes when a
6 * virtual machine forks or is cloned. This driver exists for shepherding that
7 * information to random.c.
10 #include <linux/acpi.h>
11 #include <linux/interrupt.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/random.h>
17 ACPI_MODULE_NAME("vmgenid");
19 enum { VMGENID_SIZE
= 16 };
21 struct vmgenid_state
{
23 u8 this_id
[VMGENID_SIZE
];
26 static void vmgenid_notify(struct device
*device
)
28 struct vmgenid_state
*state
= device
->driver_data
;
29 u8 old_id
[VMGENID_SIZE
];
31 memcpy(old_id
, state
->this_id
, sizeof(old_id
));
32 memcpy(state
->this_id
, state
->next_id
, sizeof(state
->this_id
));
33 if (!memcmp(old_id
, state
->this_id
, sizeof(old_id
)))
35 add_vmfork_randomness(state
->this_id
, sizeof(state
->this_id
));
38 static void setup_vmgenid_state(struct vmgenid_state
*state
, void *virt_addr
)
40 state
->next_id
= virt_addr
;
41 memcpy(state
->this_id
, state
->next_id
, sizeof(state
->this_id
));
42 add_device_randomness(state
->this_id
, sizeof(state
->this_id
));
46 static void vmgenid_acpi_handler(acpi_handle __always_unused handle
,
47 u32 __always_unused event
, void *dev
)
52 static int vmgenid_add_acpi(struct device
*dev
, struct vmgenid_state
*state
)
54 struct acpi_device
*device
= ACPI_COMPANION(dev
);
55 struct acpi_buffer parsed
= { ACPI_ALLOCATE_BUFFER
};
56 union acpi_object
*obj
;
57 phys_addr_t phys_addr
;
62 status
= acpi_evaluate_object(device
->handle
, "ADDR", NULL
, &parsed
);
63 if (ACPI_FAILURE(status
)) {
64 ACPI_EXCEPTION((AE_INFO
, status
, "Evaluating ADDR"));
68 if (!obj
|| obj
->type
!= ACPI_TYPE_PACKAGE
|| obj
->package
.count
!= 2 ||
69 obj
->package
.elements
[0].type
!= ACPI_TYPE_INTEGER
||
70 obj
->package
.elements
[1].type
!= ACPI_TYPE_INTEGER
) {
75 phys_addr
= (obj
->package
.elements
[0].integer
.value
<< 0) |
76 (obj
->package
.elements
[1].integer
.value
<< 32);
78 virt_addr
= devm_memremap(&device
->dev
, phys_addr
, VMGENID_SIZE
, MEMREMAP_WB
);
79 if (IS_ERR(virt_addr
)) {
80 ret
= PTR_ERR(virt_addr
);
83 setup_vmgenid_state(state
, virt_addr
);
85 status
= acpi_install_notify_handler(device
->handle
, ACPI_DEVICE_NOTIFY
,
86 vmgenid_acpi_handler
, dev
);
87 if (ACPI_FAILURE(status
)) {
92 dev
->driver_data
= state
;
94 ACPI_FREE(parsed
.pointer
);
98 static int vmgenid_add_acpi(struct device
*dev
, struct vmgenid_state
*state
)
104 static irqreturn_t
vmgenid_of_irq_handler(int __always_unused irq
, void *dev
)
110 static int vmgenid_add_of(struct platform_device
*pdev
,
111 struct vmgenid_state
*state
)
116 virt_addr
= devm_platform_get_and_ioremap_resource(pdev
, 0, NULL
);
117 if (IS_ERR(virt_addr
))
118 return PTR_ERR(virt_addr
);
120 setup_vmgenid_state(state
, virt_addr
);
122 ret
= platform_get_irq(pdev
, 0);
126 ret
= devm_request_irq(&pdev
->dev
, ret
, vmgenid_of_irq_handler
,
127 IRQF_SHARED
, "vmgenid", &pdev
->dev
);
131 pdev
->dev
.driver_data
= state
;
135 static int vmgenid_add(struct platform_device
*pdev
)
137 struct device
*dev
= &pdev
->dev
;
138 struct vmgenid_state
*state
;
141 state
= devm_kmalloc(dev
, sizeof(*state
), GFP_KERNEL
);
146 ret
= vmgenid_add_of(pdev
, state
);
148 ret
= vmgenid_add_acpi(dev
, state
);
151 devm_kfree(dev
, state
);
155 static const struct of_device_id vmgenid_of_ids
[] = {
156 { .compatible
= "microsoft,vmgenid", },
159 MODULE_DEVICE_TABLE(of
, vmgenid_of_ids
);
161 static const struct acpi_device_id vmgenid_acpi_ids
[] = {
163 { "VM_GEN_COUNTER", 0 },
166 MODULE_DEVICE_TABLE(acpi
, vmgenid_acpi_ids
);
168 static struct platform_driver vmgenid_plaform_driver
= {
169 .probe
= vmgenid_add
,
172 .acpi_match_table
= vmgenid_acpi_ids
,
173 .of_match_table
= vmgenid_of_ids
,
177 module_platform_driver(vmgenid_plaform_driver
)
179 MODULE_DESCRIPTION("Virtual Machine Generation ID");
180 MODULE_LICENSE("GPL v2");
181 MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");