1ffac39deSConrad Meyer /*- 2ffac39deSConrad Meyer * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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/cdefs.h> 45ffac39deSConrad Meyer __FBSDID("$FreeBSD$"); 46ffac39deSConrad Meyer 47ffac39deSConrad Meyer #include <sys/param.h> 48ffac39deSConrad Meyer #include <sys/bus.h> 49ffac39deSConrad Meyer #include <sys/eventhandler.h> 50ffac39deSConrad Meyer #include <sys/kernel.h> 51ffac39deSConrad Meyer #include <sys/lock.h> 52ffac39deSConrad Meyer #include <sys/malloc.h> 53ffac39deSConrad Meyer #include <sys/module.h> 54ffac39deSConrad Meyer #include <sys/mutex.h> 55767991d2SConrad Meyer #include <sys/random.h> 56ffac39deSConrad Meyer #include <sys/sysctl.h> 57ffac39deSConrad Meyer #include <sys/systm.h> 58ffac39deSConrad Meyer 59ffac39deSConrad Meyer #include <contrib/dev/acpica/include/acpi.h> 60ffac39deSConrad Meyer 61ffac39deSConrad Meyer #include <dev/acpica/acpivar.h> 62767991d2SConrad Meyer #include <dev/random/random_harvestq.h> 63ffac39deSConrad Meyer #include <dev/vmgenc/vmgenc_acpi.h> 64ffac39deSConrad Meyer 65ffac39deSConrad Meyer #ifndef ACPI_NOTIFY_STATUS_CHANGED 66ffac39deSConrad Meyer #define ACPI_NOTIFY_STATUS_CHANGED 0x80 67ffac39deSConrad Meyer #endif 68ffac39deSConrad Meyer 69ffac39deSConrad Meyer #define GUID_BYTES 16 70ffac39deSConrad Meyer 71ffac39deSConrad Meyer static const char *vmgenc_ids[] = { 72ffac39deSConrad Meyer "VM_GEN_COUNTER", 73ffac39deSConrad Meyer NULL 74ffac39deSConrad Meyer }; 75ffac39deSConrad Meyer #if 0 76ffac39deSConrad Meyer MODULE_PNP_INFO("Z:_CID", acpi, vmgenc, vmgenc_ids, nitems(vmgenc_ids) - 1); 77ffac39deSConrad Meyer #endif 78ffac39deSConrad Meyer 79ffac39deSConrad Meyer struct vmgenc_softc { 80ffac39deSConrad Meyer volatile void *vmg_pguid; 81ffac39deSConrad Meyer uint8_t vmg_cache_guid[GUID_BYTES]; 82ffac39deSConrad Meyer }; 83ffac39deSConrad Meyer 84ffac39deSConrad Meyer static void 85767991d2SConrad Meyer vmgenc_harvest_all(const void *p, size_t sz) 86767991d2SConrad Meyer { 87767991d2SConrad Meyer size_t nbytes; 88767991d2SConrad Meyer 89767991d2SConrad Meyer while (sz > 0) { 90767991d2SConrad Meyer nbytes = MIN(sz, 91767991d2SConrad Meyer sizeof(((struct harvest_event *)0)->he_entropy)); 92767991d2SConrad Meyer random_harvest_direct(p, nbytes, RANDOM_PURE_VMGENID); 93767991d2SConrad Meyer p = (const char *)p + nbytes; 94767991d2SConrad Meyer sz -= nbytes; 95767991d2SConrad Meyer } 96767991d2SConrad Meyer } 97767991d2SConrad Meyer 98767991d2SConrad Meyer static void 99ffac39deSConrad Meyer vmgenc_status_changed(void *context) 100ffac39deSConrad Meyer { 101ffac39deSConrad Meyer uint8_t guid[GUID_BYTES]; 102ffac39deSConrad Meyer struct vmgenc_softc *sc; 103ffac39deSConrad Meyer device_t dev; 104ffac39deSConrad Meyer 105ffac39deSConrad Meyer dev = context; 106ffac39deSConrad Meyer sc = device_get_softc(dev); 107ffac39deSConrad Meyer 108ffac39deSConrad Meyer /* Check for spurious notify events. */ 109ffac39deSConrad Meyer memcpy(guid, __DEVOLATILE(void *, sc->vmg_pguid), sizeof(guid)); 110ffac39deSConrad Meyer if (memcmp(guid, sc->vmg_cache_guid, GUID_BYTES) == 0) 111ffac39deSConrad Meyer return; /* No change. */ 112ffac39deSConrad Meyer 113ffac39deSConrad Meyer /* Update cache. */ 114ffac39deSConrad Meyer memcpy(sc->vmg_cache_guid, guid, GUID_BYTES); 115ffac39deSConrad Meyer 116767991d2SConrad Meyer vmgenc_harvest_all(sc->vmg_cache_guid, sizeof(sc->vmg_cache_guid)); 117767991d2SConrad Meyer 118ffac39deSConrad Meyer EVENTHANDLER_INVOKE(acpi_vmgenc_event); 119ffac39deSConrad Meyer acpi_UserNotify("VMGenerationCounter", acpi_get_handle(dev), 0); 120ffac39deSConrad Meyer } 121ffac39deSConrad Meyer 122ffac39deSConrad Meyer static void 123ffac39deSConrad Meyer vmgenc_notify(ACPI_HANDLE h, UINT32 notify, void *context) 124ffac39deSConrad Meyer { 125ffac39deSConrad Meyer device_t dev; 126ffac39deSConrad Meyer 127ffac39deSConrad Meyer dev = context; 128ffac39deSConrad Meyer switch (notify) { 129ffac39deSConrad Meyer case ACPI_NOTIFY_STATUS_CHANGED: 130ffac39deSConrad Meyer /* 131ffac39deSConrad Meyer * We're possibly in GPE / interrupt context, kick the event up 132ffac39deSConrad Meyer * to a taskqueue. 133ffac39deSConrad Meyer */ 134ffac39deSConrad Meyer AcpiOsExecute(OSL_NOTIFY_HANDLER, vmgenc_status_changed, dev); 135ffac39deSConrad Meyer break; 136ffac39deSConrad Meyer default: 137ffac39deSConrad Meyer device_printf(dev, "unknown notify %#x\n", notify); 138ffac39deSConrad Meyer break; 139ffac39deSConrad Meyer } 140ffac39deSConrad Meyer } 141ffac39deSConrad Meyer 142ffac39deSConrad Meyer static int 143ffac39deSConrad Meyer vmgenc_probe(device_t dev) 144ffac39deSConrad Meyer { 145ffac39deSConrad Meyer int rv; 146ffac39deSConrad Meyer 147ffac39deSConrad Meyer if (acpi_disabled("vmgenc")) 148ffac39deSConrad Meyer return (ENXIO); 149ffac39deSConrad Meyer rv = ACPI_ID_PROBE(device_get_parent(dev), dev, 150ffac39deSConrad Meyer __DECONST(char **, vmgenc_ids), NULL); 151ffac39deSConrad Meyer if (rv <= 0) 152ffac39deSConrad Meyer device_set_desc(dev, "VM Generation Counter"); 153ffac39deSConrad Meyer return (rv); 154ffac39deSConrad Meyer } 155ffac39deSConrad Meyer 156ffac39deSConrad Meyer static const char * 157ffac39deSConrad Meyer vmgenc_acpi_getname(ACPI_HANDLE handle, char data[static 256]) 158ffac39deSConrad Meyer { 159ffac39deSConrad Meyer ACPI_BUFFER buf; 160ffac39deSConrad Meyer 161ffac39deSConrad Meyer buf.Length = 256; 162ffac39deSConrad Meyer buf.Pointer = data; 163ffac39deSConrad Meyer 164ffac39deSConrad Meyer if (ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf))) 165ffac39deSConrad Meyer return (data); 166ffac39deSConrad Meyer return ("(unknown)"); 167ffac39deSConrad Meyer } 168ffac39deSConrad Meyer 169ffac39deSConrad Meyer static int 170ffac39deSConrad Meyer acpi_GetPackedUINT64(device_t dev, ACPI_HANDLE handle, char *path, 171ffac39deSConrad Meyer uint64_t *out) 172ffac39deSConrad Meyer { 173ffac39deSConrad Meyer char hpath[256]; 174ffac39deSConrad Meyer ACPI_STATUS status; 175ffac39deSConrad Meyer ACPI_BUFFER buf; 176ffac39deSConrad Meyer ACPI_OBJECT param[3]; 177ffac39deSConrad Meyer 178ffac39deSConrad Meyer buf.Pointer = param; 179ffac39deSConrad Meyer buf.Length = sizeof(param); 180ffac39deSConrad Meyer status = AcpiEvaluateObject(handle, path, NULL, &buf); 181ffac39deSConrad Meyer if (!ACPI_SUCCESS(status)) { 182ffac39deSConrad Meyer device_printf(dev, "%s(%s::%s()): %s\n", __func__, 183ffac39deSConrad Meyer vmgenc_acpi_getname(handle, hpath), path, 184ffac39deSConrad Meyer AcpiFormatException(status)); 185ffac39deSConrad Meyer return (ENXIO); 186ffac39deSConrad Meyer } 187ffac39deSConrad Meyer if (param[0].Type != ACPI_TYPE_PACKAGE) { 188ffac39deSConrad Meyer device_printf(dev, "%s(%s::%s()): Wrong type %#x\n", __func__, 189ffac39deSConrad Meyer vmgenc_acpi_getname(handle, hpath), path, 190ffac39deSConrad Meyer param[0].Type); 191ffac39deSConrad Meyer return (ENXIO); 192ffac39deSConrad Meyer } 193ffac39deSConrad Meyer if (param[0].Package.Count != 2) { 194ffac39deSConrad Meyer device_printf(dev, "%s(%s::%s()): Wrong number of results %u\n", 195ffac39deSConrad Meyer __func__, vmgenc_acpi_getname(handle, hpath), path, 196ffac39deSConrad Meyer param[0].Package.Count); 197ffac39deSConrad Meyer return (ENXIO); 198ffac39deSConrad Meyer } 199ffac39deSConrad Meyer if (param[0].Package.Elements[0].Type != ACPI_TYPE_INTEGER || 200ffac39deSConrad Meyer param[0].Package.Elements[1].Type != ACPI_TYPE_INTEGER) { 201ffac39deSConrad Meyer device_printf(dev, "%s(%s::%s()): Wrong type results %#x, %#x\n", 202ffac39deSConrad Meyer __func__, vmgenc_acpi_getname(handle, hpath), path, 203ffac39deSConrad Meyer param[0].Package.Elements[0].Type, 204ffac39deSConrad Meyer param[0].Package.Elements[1].Type); 205ffac39deSConrad Meyer return (ENXIO); 206ffac39deSConrad Meyer } 207ffac39deSConrad Meyer 208ffac39deSConrad Meyer *out = (param[0].Package.Elements[0].Integer.Value & UINT32_MAX) | 209ffac39deSConrad Meyer ((uint64_t)param[0].Package.Elements[1].Integer.Value << 32); 210ffac39deSConrad Meyer if (*out == 0) 211ffac39deSConrad Meyer return (ENXIO); 212ffac39deSConrad Meyer return (0); 213ffac39deSConrad Meyer 214ffac39deSConrad Meyer } 215ffac39deSConrad Meyer 216ffac39deSConrad Meyer static int 217ffac39deSConrad Meyer vmgenc_attach(device_t dev) 218ffac39deSConrad Meyer { 219ffac39deSConrad Meyer struct vmgenc_softc *sc; 220ffac39deSConrad Meyer uint64_t guid_physaddr; 221ffac39deSConrad Meyer ACPI_HANDLE h; 222ffac39deSConrad Meyer int error; 223ffac39deSConrad Meyer 224ffac39deSConrad Meyer h = acpi_get_handle(dev); 225ffac39deSConrad Meyer sc = device_get_softc(dev); 226ffac39deSConrad Meyer 227ffac39deSConrad Meyer error = acpi_GetPackedUINT64(dev, h, "ADDR", &guid_physaddr); 228ffac39deSConrad Meyer if (error != 0) 229ffac39deSConrad Meyer return (error); 230ffac39deSConrad Meyer 231ffac39deSConrad Meyer SYSCTL_ADD_OPAQUE(device_get_sysctl_ctx(dev), 232ffac39deSConrad Meyer SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "guid", 233ffac39deSConrad Meyer CTLFLAG_RD, sc->vmg_cache_guid, GUID_BYTES, "", 234ffac39deSConrad Meyer "latest cached VM generation counter (128-bit UUID)"); 235ffac39deSConrad Meyer 236ffac39deSConrad Meyer sc->vmg_pguid = AcpiOsMapMemory(guid_physaddr, GUID_BYTES); 237ffac39deSConrad Meyer memcpy(sc->vmg_cache_guid, __DEVOLATILE(void *, sc->vmg_pguid), 238ffac39deSConrad Meyer sizeof(sc->vmg_cache_guid)); 239ffac39deSConrad Meyer 240767991d2SConrad Meyer random_harvest_register_source(RANDOM_PURE_VMGENID); 241767991d2SConrad Meyer vmgenc_harvest_all(sc->vmg_cache_guid, sizeof(sc->vmg_cache_guid)); 242767991d2SConrad Meyer 243ffac39deSConrad Meyer AcpiInstallNotifyHandler(h, ACPI_DEVICE_NOTIFY, vmgenc_notify, dev); 244ffac39deSConrad Meyer return (0); 245ffac39deSConrad Meyer } 246ffac39deSConrad Meyer 247ffac39deSConrad Meyer static device_method_t vmgenc_methods[] = { 248ffac39deSConrad Meyer DEVMETHOD(device_probe, vmgenc_probe), 249ffac39deSConrad Meyer DEVMETHOD(device_attach, vmgenc_attach), 250ffac39deSConrad Meyer DEVMETHOD_END 251ffac39deSConrad Meyer }; 252ffac39deSConrad Meyer 253ffac39deSConrad Meyer static driver_t vmgenc_driver = { 254ffac39deSConrad Meyer "vmgenc", 255ffac39deSConrad Meyer vmgenc_methods, 256ffac39deSConrad Meyer sizeof(struct vmgenc_softc), 257ffac39deSConrad Meyer }; 258ffac39deSConrad Meyer 259ffac39deSConrad Meyer static devclass_t vmgenc_devclass; 260ffac39deSConrad Meyer DRIVER_MODULE(vmgenc, acpi, vmgenc_driver, vmgenc_devclass, NULL, NULL); 261ffac39deSConrad Meyer MODULE_DEPEND(vmgenc, acpi, 1, 1, 1); 262767991d2SConrad Meyer MODULE_DEPEND(vemgenc, random_harvestq, 1, 1, 1); 263