]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vmgenc/vmgenc_acpi.c
ZFS: MFV 2.0-rc1-ga00c61
[FreeBSD/FreeBSD.git] / sys / dev / vmgenc / vmgenc_acpi.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 /*
29  * VM Generation Counter driver
30  *
31  * See, e.g., the "Virtual Machine Generation ID" white paper:
32  * https://go.microsoft.com/fwlink/p/?LinkID=260709 , and perhaps also:
33  * https://docs.microsoft.com/en-us/windows/win32/hyperv_v2/virtual-machine-generation-identifier ,
34  * https://azure.microsoft.com/en-us/blog/accessing-and-using-azure-vm-unique-id/
35  *
36  * Microsoft introduced the concept in 2013 or so and seems to have
37  * successfully driven it to a consensus standard among hypervisors, not just
38  * HyperV/Azure:
39  * - QEMU: https://bugzilla.redhat.com/show_bug.cgi?id=1118834
40  * - VMware/ESXi: https://kb.vmware.com/s/article/2032586
41  * - Xen: https://github.com/xenserver/xen-4.5/blob/master/tools/firmware/hvmloader/acpi/dsdt.asl#L456
42  */
43
44 #include <sys/cdefs.h>
45 __FBSDID("$FreeBSD$");
46
47 #include <sys/param.h>
48 #include <sys/bus.h>
49 #include <sys/eventhandler.h>
50 #include <sys/kernel.h>
51 #include <sys/lock.h>
52 #include <sys/malloc.h>
53 #include <sys/module.h>
54 #include <sys/mutex.h>
55 #include <sys/random.h>
56 #include <sys/sysctl.h>
57 #include <sys/systm.h>
58
59 #include <contrib/dev/acpica/include/acpi.h>
60
61 #include <dev/acpica/acpivar.h>
62 #include <dev/random/random_harvestq.h>
63 #include <dev/vmgenc/vmgenc_acpi.h>
64
65 #ifndef ACPI_NOTIFY_STATUS_CHANGED
66 #define ACPI_NOTIFY_STATUS_CHANGED      0x80
67 #endif
68
69 #define GUID_BYTES      16
70
71 static const char *vmgenc_ids[] = {
72         "VM_GEN_COUNTER",
73         NULL
74 };
75 #if 0
76 MODULE_PNP_INFO("Z:_CID", acpi, vmgenc, vmgenc_ids, nitems(vmgenc_ids) - 1);
77 #endif
78
79 struct vmgenc_softc {
80         volatile void   *vmg_pguid;
81         uint8_t         vmg_cache_guid[GUID_BYTES];
82 };
83
84 static void
85 vmgenc_harvest_all(const void *p, size_t sz)
86 {
87         size_t nbytes;
88
89         while (sz > 0) {
90                 nbytes = MIN(sz,
91                     sizeof(((struct harvest_event *)0)->he_entropy));
92                 random_harvest_direct(p, nbytes, RANDOM_PURE_VMGENID);
93                 p = (const char *)p + nbytes;
94                 sz -= nbytes;
95         }
96 }
97
98 static void
99 vmgenc_status_changed(void *context)
100 {
101         uint8_t guid[GUID_BYTES];
102         struct vmgenc_softc *sc;
103         device_t dev;
104
105         dev = context;
106         sc = device_get_softc(dev);
107
108         /* Check for spurious notify events. */
109         memcpy(guid, __DEVOLATILE(void *, sc->vmg_pguid), sizeof(guid));
110         if (memcmp(guid, sc->vmg_cache_guid, GUID_BYTES) == 0)
111                 return; /* No change. */
112
113         /* Update cache. */
114         memcpy(sc->vmg_cache_guid, guid, GUID_BYTES);
115
116         vmgenc_harvest_all(sc->vmg_cache_guid, sizeof(sc->vmg_cache_guid));
117
118         EVENTHANDLER_INVOKE(acpi_vmgenc_event);
119         acpi_UserNotify("VMGenerationCounter", acpi_get_handle(dev), 0);
120 }
121
122 static void
123 vmgenc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
124 {
125         device_t dev;
126
127         dev = context;
128         switch (notify) {
129         case ACPI_NOTIFY_STATUS_CHANGED:
130                 /*
131                  * We're possibly in GPE / interrupt context, kick the event up
132                  * to a taskqueue.
133                  */
134                 AcpiOsExecute(OSL_NOTIFY_HANDLER, vmgenc_status_changed, dev);
135                 break;
136         default:
137                 device_printf(dev, "unknown notify %#x\n", notify);
138                 break;
139         }
140 }
141
142 static int
143 vmgenc_probe(device_t dev)
144 {
145         int rv;
146
147         if (acpi_disabled("vmgenc"))
148                 return (ENXIO);
149         rv = ACPI_ID_PROBE(device_get_parent(dev), dev,
150             __DECONST(char **, vmgenc_ids), NULL);
151         if (rv <= 0)
152                 device_set_desc(dev, "VM Generation Counter");
153         return (rv);
154 }
155
156 static const char *
157 vmgenc_acpi_getname(ACPI_HANDLE handle, char data[static 256])
158 {
159     ACPI_BUFFER buf;
160
161     buf.Length = 256;
162     buf.Pointer = data;
163
164     if (ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf)))
165         return (data);
166     return ("(unknown)");
167 }
168
169 static int
170 acpi_GetPackedUINT64(device_t dev, ACPI_HANDLE handle, char *path,
171     uint64_t *out)
172 {
173         char hpath[256];
174         ACPI_STATUS status;
175         ACPI_BUFFER buf;
176         ACPI_OBJECT param[3];
177
178         buf.Pointer = param;
179         buf.Length = sizeof(param);
180         status = AcpiEvaluateObject(handle, path, NULL, &buf);
181         if (!ACPI_SUCCESS(status)) {
182                 device_printf(dev, "%s(%s::%s()): %s\n", __func__,
183                     vmgenc_acpi_getname(handle, hpath), path,
184                     AcpiFormatException(status));
185                 return (ENXIO);
186         }
187         if (param[0].Type != ACPI_TYPE_PACKAGE) {
188                 device_printf(dev, "%s(%s::%s()): Wrong type %#x\n", __func__,
189                     vmgenc_acpi_getname(handle, hpath), path,
190                     param[0].Type);
191                 return (ENXIO);
192         }
193         if (param[0].Package.Count != 2) {
194                 device_printf(dev, "%s(%s::%s()): Wrong number of results %u\n",
195                     __func__, vmgenc_acpi_getname(handle, hpath), path,
196                     param[0].Package.Count);
197                 return (ENXIO);
198         }
199         if (param[0].Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
200             param[0].Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
201                 device_printf(dev, "%s(%s::%s()): Wrong type results %#x, %#x\n",
202                     __func__, vmgenc_acpi_getname(handle, hpath), path,
203                     param[0].Package.Elements[0].Type,
204                     param[0].Package.Elements[1].Type);
205                 return (ENXIO);
206         }
207
208         *out = (param[0].Package.Elements[0].Integer.Value & UINT32_MAX) |
209             ((uint64_t)param[0].Package.Elements[1].Integer.Value << 32);
210         if (*out == 0)
211                 return (ENXIO);
212         return (0);
213
214 }
215
216 static int
217 vmgenc_attach(device_t dev)
218 {
219         struct vmgenc_softc *sc;
220         uint64_t guid_physaddr;
221         ACPI_HANDLE h;
222         int error;
223
224         h = acpi_get_handle(dev);
225         sc = device_get_softc(dev);
226
227         error = acpi_GetPackedUINT64(dev, h, "ADDR", &guid_physaddr);
228         if (error != 0)
229                 return (error);
230
231         SYSCTL_ADD_OPAQUE(device_get_sysctl_ctx(dev),
232             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "guid",
233             CTLFLAG_RD, sc->vmg_cache_guid, GUID_BYTES, "",
234             "latest cached VM generation counter (128-bit UUID)");
235
236         sc->vmg_pguid = AcpiOsMapMemory(guid_physaddr, GUID_BYTES);
237         memcpy(sc->vmg_cache_guid, __DEVOLATILE(void *, sc->vmg_pguid),
238             sizeof(sc->vmg_cache_guid));
239
240         random_harvest_register_source(RANDOM_PURE_VMGENID);
241         vmgenc_harvest_all(sc->vmg_cache_guid, sizeof(sc->vmg_cache_guid));
242
243         AcpiInstallNotifyHandler(h, ACPI_DEVICE_NOTIFY, vmgenc_notify, dev);
244         return (0);
245 }
246
247 static device_method_t vmgenc_methods[] = {
248         DEVMETHOD(device_probe,         vmgenc_probe),
249         DEVMETHOD(device_attach,        vmgenc_attach),
250         DEVMETHOD_END
251 };
252
253 static driver_t vmgenc_driver = {
254         "vmgenc",
255         vmgenc_methods,
256         sizeof(struct vmgenc_softc),
257 };
258
259 static devclass_t vmgenc_devclass;
260 DRIVER_MODULE(vmgenc, acpi, vmgenc_driver, vmgenc_devclass, NULL, NULL);
261 MODULE_DEPEND(vmgenc, acpi, 1, 1, 1);
262 MODULE_DEPEND(vemgenc, random_harvestq, 1, 1, 1);