xref: /freebsd/sys/dev/vmgenc/vmgenc_acpi.c (revision c14a5a8800a0f7a007f8cd197b4cad4d26a78f8c)
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/sysctl.h>
56 #include <sys/systm.h>
57 
58 #include <contrib/dev/acpica/include/acpi.h>
59 
60 #include <dev/acpica/acpivar.h>
61 #include <dev/vmgenc/vmgenc_acpi.h>
62 
63 #ifndef	ACPI_NOTIFY_STATUS_CHANGED
64 #define	ACPI_NOTIFY_STATUS_CHANGED	0x80
65 #endif
66 
67 #define	GUID_BYTES	16
68 
69 static const char *vmgenc_ids[] = {
70 	"VM_GEN_COUNTER",
71 	NULL
72 };
73 #if 0
74 MODULE_PNP_INFO("Z:_CID", acpi, vmgenc, vmgenc_ids, nitems(vmgenc_ids) - 1);
75 #endif
76 
77 struct vmgenc_softc {
78 	volatile void	*vmg_pguid;
79 	uint8_t		vmg_cache_guid[GUID_BYTES];
80 };
81 
82 static void
83 vmgenc_status_changed(void *context)
84 {
85 	uint8_t guid[GUID_BYTES];
86 	struct vmgenc_softc *sc;
87 	device_t dev;
88 
89 	dev = context;
90 	sc = device_get_softc(dev);
91 
92 	/* Check for spurious notify events. */
93 	memcpy(guid, __DEVOLATILE(void *, sc->vmg_pguid), sizeof(guid));
94 	if (memcmp(guid, sc->vmg_cache_guid, GUID_BYTES) == 0)
95 		return; /* No change. */
96 
97 	/* Update cache. */
98 	memcpy(sc->vmg_cache_guid, guid, GUID_BYTES);
99 
100 	EVENTHANDLER_INVOKE(acpi_vmgenc_event);
101 	acpi_UserNotify("VMGenerationCounter", acpi_get_handle(dev), 0);
102 }
103 
104 static void
105 vmgenc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
106 {
107 	device_t dev;
108 
109 	dev = context;
110 	switch (notify) {
111 	case ACPI_NOTIFY_STATUS_CHANGED:
112 		/*
113 		 * We're possibly in GPE / interrupt context, kick the event up
114 		 * to a taskqueue.
115 		 */
116 		AcpiOsExecute(OSL_NOTIFY_HANDLER, vmgenc_status_changed, dev);
117 		break;
118 	default:
119 		device_printf(dev, "unknown notify %#x\n", notify);
120 		break;
121 	}
122 }
123 
124 static int
125 vmgenc_probe(device_t dev)
126 {
127 	int rv;
128 
129 	if (acpi_disabled("vmgenc"))
130 		return (ENXIO);
131 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev,
132 	    __DECONST(char **, vmgenc_ids), NULL);
133 	if (rv <= 0)
134 		device_set_desc(dev, "VM Generation Counter");
135 	return (rv);
136 }
137 
138 static const char *
139 vmgenc_acpi_getname(ACPI_HANDLE handle, char data[static 256])
140 {
141     ACPI_BUFFER buf;
142 
143     buf.Length = 256;
144     buf.Pointer = data;
145 
146     if (ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf)))
147 	return (data);
148     return ("(unknown)");
149 }
150 
151 static int
152 acpi_GetPackedUINT64(device_t dev, ACPI_HANDLE handle, char *path,
153     uint64_t *out)
154 {
155 	char hpath[256];
156 	ACPI_STATUS status;
157 	ACPI_BUFFER buf;
158 	ACPI_OBJECT param[3];
159 
160 	buf.Pointer = param;
161 	buf.Length = sizeof(param);
162 	status = AcpiEvaluateObject(handle, path, NULL, &buf);
163 	if (!ACPI_SUCCESS(status)) {
164 		device_printf(dev, "%s(%s::%s()): %s\n", __func__,
165 		    vmgenc_acpi_getname(handle, hpath), path,
166 		    AcpiFormatException(status));
167 		return (ENXIO);
168 	}
169 	if (param[0].Type != ACPI_TYPE_PACKAGE) {
170 		device_printf(dev, "%s(%s::%s()): Wrong type %#x\n", __func__,
171 		    vmgenc_acpi_getname(handle, hpath), path,
172 		    param[0].Type);
173 		return (ENXIO);
174 	}
175 	if (param[0].Package.Count != 2) {
176 		device_printf(dev, "%s(%s::%s()): Wrong number of results %u\n",
177 		    __func__, vmgenc_acpi_getname(handle, hpath), path,
178 		    param[0].Package.Count);
179 		return (ENXIO);
180 	}
181 	if (param[0].Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
182 	    param[0].Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
183 		device_printf(dev, "%s(%s::%s()): Wrong type results %#x, %#x\n",
184 		    __func__, vmgenc_acpi_getname(handle, hpath), path,
185 		    param[0].Package.Elements[0].Type,
186 		    param[0].Package.Elements[1].Type);
187 		return (ENXIO);
188 	}
189 
190 	*out = (param[0].Package.Elements[0].Integer.Value & UINT32_MAX) |
191 	    ((uint64_t)param[0].Package.Elements[1].Integer.Value << 32);
192 	if (*out == 0)
193 		return (ENXIO);
194 	return (0);
195 
196 }
197 
198 static int
199 vmgenc_attach(device_t dev)
200 {
201 	struct vmgenc_softc *sc;
202 	uint64_t guid_physaddr;
203 	ACPI_HANDLE h;
204 	int error;
205 
206 	h = acpi_get_handle(dev);
207 	sc = device_get_softc(dev);
208 
209 	error = acpi_GetPackedUINT64(dev, h, "ADDR", &guid_physaddr);
210 	if (error != 0)
211 		return (error);
212 
213 	SYSCTL_ADD_OPAQUE(device_get_sysctl_ctx(dev),
214 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "guid",
215 	    CTLFLAG_RD, sc->vmg_cache_guid, GUID_BYTES, "",
216 	    "latest cached VM generation counter (128-bit UUID)");
217 
218 	sc->vmg_pguid = AcpiOsMapMemory(guid_physaddr, GUID_BYTES);
219 	memcpy(sc->vmg_cache_guid, __DEVOLATILE(void *, sc->vmg_pguid),
220 	    sizeof(sc->vmg_cache_guid));
221 
222 	AcpiInstallNotifyHandler(h, ACPI_DEVICE_NOTIFY, vmgenc_notify, dev);
223 	return (0);
224 }
225 
226 static device_method_t vmgenc_methods[] = {
227 	DEVMETHOD(device_probe,		vmgenc_probe),
228 	DEVMETHOD(device_attach,	vmgenc_attach),
229 	DEVMETHOD_END
230 };
231 
232 static driver_t vmgenc_driver = {
233 	"vmgenc",
234 	vmgenc_methods,
235 	sizeof(struct vmgenc_softc),
236 };
237 
238 static devclass_t vmgenc_devclass;
239 DRIVER_MODULE(vmgenc, acpi, vmgenc_driver, vmgenc_devclass, NULL, NULL);
240 MODULE_DEPEND(vmgenc, acpi, 1, 1, 1);
241