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); 263