#include <sys/param.h>
#include <sys/bus.h>
#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#include <dev/random/randomdev.h>
#include <dev/random/random_harvestq.h>
#include <dev/vmgenc/vmgenc_acpi.h>
#ifndef ACPI_NOTIFY_STATUS_CHANGED
#define ACPI_NOTIFY_STATUS_CHANGED 0x80
#endif
#define GUID_BYTES 16
static const char *vmgenc_ids[] = {
"VM_GEN_COUNTER",
NULL
};
#if 0
MODULE_PNP_INFO("Z:_CID", acpi, vmgenc, vmgenc_ids, nitems(vmgenc_ids) - 1);
#endif
struct vmgenc_softc {
volatile void *vmg_pguid;
uint8_t vmg_cache_guid[GUID_BYTES];
};
static void
vmgenc_harvest_all(const void *p, size_t sz)
{
size_t nbytes;
while (sz > 0) {
nbytes = MIN(sz,
sizeof(((struct harvest_event *)0)->he_entropy));
random_harvest_direct(p, nbytes, RANDOM_PURE_VMGENID);
p = (const char *)p + nbytes;
sz -= nbytes;
}
}
static void
vmgenc_status_changed(void *context)
{
uint8_t guid[GUID_BYTES];
struct vmgenc_softc *sc;
device_t dev;
dev = context;
sc = device_get_softc(dev);
memcpy(guid, __DEVOLATILE(void *, sc->vmg_pguid), sizeof(guid));
if (memcmp(guid, sc->vmg_cache_guid, GUID_BYTES) == 0)
return;
memcpy(sc->vmg_cache_guid, guid, GUID_BYTES);
vmgenc_harvest_all(sc->vmg_cache_guid, sizeof(sc->vmg_cache_guid));
EVENTHANDLER_INVOKE(acpi_vmgenc_event);
acpi_UserNotify("VMGenerationCounter", acpi_get_handle(dev), 0);
}
static void
vmgenc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
{
device_t dev;
dev = context;
switch (notify) {
case ACPI_NOTIFY_STATUS_CHANGED:
AcpiOsExecute(OSL_NOTIFY_HANDLER, vmgenc_status_changed, dev);
break;
default:
device_printf(dev, "unknown notify %#x\n", notify);
break;
}
}
static int
vmgenc_probe(device_t dev)
{
int rv;
if (acpi_disabled("vmgenc"))
return (ENXIO);
rv = ACPI_ID_PROBE(device_get_parent(dev), dev,
__DECONST(char **, vmgenc_ids), NULL);
if (rv <= 0)
device_set_desc(dev, "VM Generation Counter");
return (rv);
}
static const char *
vmgenc_acpi_getname(ACPI_HANDLE handle, char data[static 256])
{
ACPI_BUFFER buf;
buf.Length = 256;
buf.Pointer = data;
if (ACPI_SUCCESS(AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf)))
return (data);
return ("(unknown)");
}
static int
acpi_GetPackedUINT64(device_t dev, ACPI_HANDLE handle, char *path,
uint64_t *out)
{
char hpath[256];
ACPI_STATUS status;
ACPI_BUFFER buf;
ACPI_OBJECT param[3];
buf.Pointer = param;
buf.Length = sizeof(param);
status = AcpiEvaluateObject(handle, path, NULL, &buf);
if (!ACPI_SUCCESS(status)) {
device_printf(dev, "%s(%s::%s()): %s\n", __func__,
vmgenc_acpi_getname(handle, hpath), path,
AcpiFormatException(status));
return (ENXIO);
}
if (param[0].Type != ACPI_TYPE_PACKAGE) {
device_printf(dev, "%s(%s::%s()): Wrong type %#x\n", __func__,
vmgenc_acpi_getname(handle, hpath), path,
param[0].Type);
return (ENXIO);
}
if (param[0].Package.Count != 2) {
device_printf(dev, "%s(%s::%s()): Wrong number of results %u\n",
__func__, vmgenc_acpi_getname(handle, hpath), path,
param[0].Package.Count);
return (ENXIO);
}
if (param[0].Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
param[0].Package.Elements[1].Type != ACPI_TYPE_INTEGER) {
device_printf(dev, "%s(%s::%s()): Wrong type results %#x, %#x\n",
__func__, vmgenc_acpi_getname(handle, hpath), path,
param[0].Package.Elements[0].Type,
param[0].Package.Elements[1].Type);
return (ENXIO);
}
*out = (param[0].Package.Elements[0].Integer.Value & UINT32_MAX) |
((uint64_t)param[0].Package.Elements[1].Integer.Value << 32);
if (*out == 0)
return (ENXIO);
return (0);
}
static const struct random_source random_vmgenid = {
.rs_ident = "VM Generation ID",
.rs_source = RANDOM_PURE_VMGENID,
};
static int
vmgenc_attach(device_t dev)
{
struct vmgenc_softc *sc;
uint64_t guid_physaddr;
ACPI_HANDLE h;
int error;
h = acpi_get_handle(dev);
sc = device_get_softc(dev);
error = acpi_GetPackedUINT64(dev, h, "ADDR", &guid_physaddr);
if (error != 0)
return (error);
SYSCTL_ADD_OPAQUE(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "guid",
CTLFLAG_RD, sc->vmg_cache_guid, GUID_BYTES, "",
"latest cached VM generation counter (128-bit UUID)");
sc->vmg_pguid = AcpiOsMapMemory(guid_physaddr, GUID_BYTES);
memcpy(sc->vmg_cache_guid, __DEVOLATILE(void *, sc->vmg_pguid),
sizeof(sc->vmg_cache_guid));
random_source_register(&random_vmgenid);
vmgenc_harvest_all(sc->vmg_cache_guid, sizeof(sc->vmg_cache_guid));
AcpiInstallNotifyHandler(h, ACPI_DEVICE_NOTIFY, vmgenc_notify, dev);
return (0);
}
static device_method_t vmgenc_methods[] = {
DEVMETHOD(device_probe, vmgenc_probe),
DEVMETHOD(device_attach, vmgenc_attach),
DEVMETHOD_END
};
static driver_t vmgenc_driver = {
"vmgenc",
vmgenc_methods,
sizeof(struct vmgenc_softc),
};
DRIVER_MODULE(vmgenc, acpi, vmgenc_driver, NULL, NULL);
MODULE_DEPEND(vmgenc, acpi, 1, 1, 1);
MODULE_DEPEND(vmgenc, random_harvestq, 1, 1, 1);