The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/vmgenc/vmgenc_acpi.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 DRIVER_MODULE(vmgenc, acpi, vmgenc_driver, NULL, NULL);
  260 MODULE_DEPEND(vmgenc, acpi, 1, 1, 1);
  261 MODULE_DEPEND(vemgenc, random_harvestq, 1, 1, 1);

Cache object: 1a045a47f3f75dbeb1c139ce2486ca67


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.