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