22/*
33 * Copyright (C) 2022 Jason A. Donenfeld <[email protected] >. All Rights Reserved. 44 *
5- * The "Virtual Machine Generation ID" is exposed via ACPI and changes when a
5+ * The "Virtual Machine Generation ID" is exposed via ACPI or DT and changes when a
66 * virtual machine forks or is cloned. This driver exists for shepherding that
77 * information to random.c.
88 */
99
10+ #include <linux/acpi.h>
11+ #include <linux/interrupt.h>
1012#include <linux/kernel.h>
1113#include <linux/module.h>
12- #include <linux/acpi .h>
14+ #include <linux/platform_device .h>
1315#include <linux/random.h>
1416
1517ACPI_MODULE_NAME ("vmgenid" );
@@ -21,19 +23,42 @@ struct vmgenid_state {
2123 u8 this_id [VMGENID_SIZE ];
2224};
2325
24- static int vmgenid_add (struct acpi_device * device )
26+ static void vmgenid_notify (struct device * device )
27+ {
28+ struct vmgenid_state * state = device -> driver_data ;
29+ u8 old_id [VMGENID_SIZE ];
30+
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 )))
34+ return ;
35+ add_vmfork_randomness (state -> this_id , sizeof (state -> this_id ));
36+ }
37+
38+ static void setup_vmgenid_state (struct vmgenid_state * state , void * virt_addr )
39+ {
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 ));
43+ }
44+
45+ #ifdef CONFIG_ACPI
46+ static void vmgenid_acpi_handler (acpi_handle __always_unused handle ,
47+ u32 __always_unused event , void * dev )
48+ {
49+ vmgenid_notify (dev );
50+ }
51+
52+ static int vmgenid_add_acpi (struct device * dev , struct vmgenid_state * state )
2553{
54+ struct acpi_device * device = ACPI_COMPANION (dev );
2655 struct acpi_buffer parsed = { ACPI_ALLOCATE_BUFFER };
27- struct vmgenid_state * state ;
2856 union acpi_object * obj ;
2957 phys_addr_t phys_addr ;
3058 acpi_status status ;
59+ void * virt_addr ;
3160 int ret = 0 ;
3261
33- state = devm_kmalloc (& device -> dev , sizeof (* state ), GFP_KERNEL );
34- if (!state )
35- return - ENOMEM ;
36-
3762 status = acpi_evaluate_object (device -> handle , "ADDR" , NULL , & parsed );
3863 if (ACPI_FAILURE (status )) {
3964 ACPI_EXCEPTION ((AE_INFO , status , "Evaluating ADDR" ));
@@ -49,52 +74,108 @@ static int vmgenid_add(struct acpi_device *device)
4974
5075 phys_addr = (obj -> package .elements [0 ].integer .value << 0 ) |
5176 (obj -> package .elements [1 ].integer .value << 32 );
52- state -> next_id = devm_memremap (& device -> dev , phys_addr , VMGENID_SIZE , MEMREMAP_WB );
53- if (IS_ERR (state -> next_id )) {
54- ret = PTR_ERR (state -> next_id );
77+
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 );
5581 goto out ;
5682 }
83+ setup_vmgenid_state (state , virt_addr );
5784
58- memcpy (state -> this_id , state -> next_id , sizeof (state -> this_id ));
59- add_device_randomness (state -> this_id , sizeof (state -> this_id ));
60-
61- device -> driver_data = state ;
85+ status = acpi_install_notify_handler (device -> handle , ACPI_DEVICE_NOTIFY ,
86+ vmgenid_acpi_handler , dev );
87+ if (ACPI_FAILURE (status )) {
88+ ret = - ENODEV ;
89+ goto out ;
90+ }
6291
92+ dev -> driver_data = state ;
6393out :
6494 ACPI_FREE (parsed .pointer );
6595 return ret ;
6696}
97+ #else
98+ static int vmgenid_add_acpi (struct device * dev , struct vmgenid_state * state )
99+ {
100+ return - EINVAL ;
101+ }
102+ #endif
67103
68- static void vmgenid_notify ( struct acpi_device * device , u32 event )
104+ static irqreturn_t vmgenid_of_irq_handler ( int __always_unused irq , void * dev )
69105{
70- struct vmgenid_state * state = acpi_driver_data (device );
71- u8 old_id [VMGENID_SIZE ];
106+ vmgenid_notify (dev );
107+ return IRQ_HANDLED ;
108+ }
72109
73- memcpy (old_id , state -> this_id , sizeof (old_id ));
74- memcpy (state -> this_id , state -> next_id , sizeof (state -> this_id ));
75- if (!memcmp (old_id , state -> this_id , sizeof (old_id )))
76- return ;
77- add_vmfork_randomness (state -> this_id , sizeof (state -> this_id ));
110+ static int vmgenid_add_of (struct platform_device * pdev ,
111+ struct vmgenid_state * state )
112+ {
113+ void * virt_addr ;
114+ int ret ;
115+
116+ virt_addr = devm_platform_get_and_ioremap_resource (pdev , 0 , NULL );
117+ if (IS_ERR (virt_addr ))
118+ return PTR_ERR (virt_addr );
119+
120+ setup_vmgenid_state (state , virt_addr );
121+
122+ ret = platform_get_irq (pdev , 0 );
123+ if (ret < 0 )
124+ return ret ;
125+
126+ ret = devm_request_irq (& pdev -> dev , ret , vmgenid_of_irq_handler ,
127+ IRQF_SHARED , "vmgenid" , & pdev -> dev );
128+ if (ret < 0 )
129+ return ret ;
130+
131+ pdev -> dev .driver_data = state ;
132+ return 0 ;
78133}
79134
80- static const struct acpi_device_id vmgenid_ids [] = {
135+ static int vmgenid_add (struct platform_device * pdev )
136+ {
137+ struct device * dev = & pdev -> dev ;
138+ struct vmgenid_state * state ;
139+ int ret ;
140+
141+ state = devm_kmalloc (dev , sizeof (* state ), GFP_KERNEL );
142+ if (!state )
143+ return - ENOMEM ;
144+
145+ if (dev -> of_node )
146+ ret = vmgenid_add_of (pdev , state );
147+ else
148+ ret = vmgenid_add_acpi (dev , state );
149+
150+ if (ret < 0 )
151+ devm_kfree (dev , state );
152+ return ret ;
153+ }
154+
155+ static const struct of_device_id vmgenid_of_ids [] = {
156+ { .compatible = "microsoft,vmgenid" , },
157+ { },
158+ };
159+ MODULE_DEVICE_TABLE (of , vmgenid_of_ids );
160+
161+ static const struct acpi_device_id vmgenid_acpi_ids [] = {
81162 { "VMGENCTR" , 0 },
82163 { "VM_GEN_COUNTER" , 0 },
83164 { }
84165};
85-
86- static struct acpi_driver vmgenid_driver = {
87- .name = "vmgenid" ,
88- .ids = vmgenid_ids ,
89- .ops = {
90- .add = vmgenid_add ,
91- .notify = vmgenid_notify
92- }
166+ MODULE_DEVICE_TABLE (acpi , vmgenid_acpi_ids );
167+
168+ static struct platform_driver vmgenid_plaform_driver = {
169+ .probe = vmgenid_add ,
170+ .driver = {
171+ .name = "vmgenid" ,
172+ .acpi_match_table = vmgenid_acpi_ids ,
173+ .of_match_table = vmgenid_of_ids ,
174+ },
93175};
94176
95- module_acpi_driver ( vmgenid_driver );
177+ module_platform_driver ( vmgenid_plaform_driver )
96178
97- MODULE_DEVICE_TABLE (acpi , vmgenid_ids );
98179MODULE_DESCRIPTION ("Virtual Machine Generation ID" );
99180MODULE_LICENSE ("GPL v2" );
100181MODULE_AUTHOR (
"Jason A. Donenfeld <[email protected] >" );
0 commit comments