From 93147094ec93ae4f22a1b078d1985c56e28dbdd0 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Mon, 15 Sep 2014 22:04:18 +0200
Subject: [PATCH] kernel/acpi: Add acpi_eval_osc() as a generic function for
 running _OSCs.

This is a hopefully true-to-the-spec implementation, as described in
chapter "6.2.11 _OSC (Operating System Capabilities)" of the 5.1 spec.

This also fixes a bug in the error handling of the previous
implementation where AcpiEvaluateObject() didn't return anything.

In-discussion-with: sephe
---
 sys/dev/acpica/acpi.c     | 62 +++++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/acpica/acpi_cpu.c | 40 ++++++------------------------
 sys/dev/acpica/acpivar.h  |  5 ++++
 3 files changed, 75 insertions(+), 32 deletions(-)

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index f88c499..2443032 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -47,6 +47,7 @@
 #include <sys/device.h>
 #include <sys/spinlock.h>
 #include <sys/spinlock2.h>
+#include <sys/uuid.h>
 
 #include <sys/rman.h>
 #include <bus/isa/isavar.h>
@@ -1211,6 +1212,67 @@ acpi_bus_alloc_gas(device_t dev, int *type, int *rid, ACPI_GENERIC_ADDRESS *gas,
     return (error);
 }
 
+ACPI_STATUS
+acpi_eval_osc(device_t dev, ACPI_HANDLE handle, const char *uuidstr,
+    int revision, uint32_t *buf, size_t size)
+{
+    ACPI_BUFFER		retbuf = { ACPI_ALLOCATE_BUFFER, NULL };
+    ACPI_OBJECT_LIST	arglist;
+    ACPI_OBJECT		arg[4];
+    ACPI_STATUS		status;
+    struct uuid		uuid;
+    uint8_t		oscuuid[16];
+    int			i;
+
+    if (parse_uuid(uuidstr, &uuid) != 0)
+	    return (AE_ERROR);
+    le_uuid_enc(oscuuid, &uuid);
+
+    arglist.Pointer = arg;
+    arglist.Count = 4;
+    arg[0].Type = ACPI_TYPE_BUFFER;
+    arg[0].Buffer.Length = sizeof(oscuuid);
+    arg[0].Buffer.Pointer = oscuuid;		/* UUID */
+    arg[1].Type = ACPI_TYPE_INTEGER;
+    arg[1].Integer.Value = revision;		/* revision */
+    arg[2].Type = ACPI_TYPE_INTEGER;
+    arg[2].Integer.Value = size / sizeof(uint32_t); /* # of cap integers */
+    arg[3].Type = ACPI_TYPE_BUFFER;
+    arg[3].Buffer.Length = size;		/* capabilities buffer */
+    arg[3].Buffer.Pointer = (uint8_t *)buf;
+
+    status = AcpiEvaluateObject(handle, "_OSC", &arglist, &retbuf);
+    if (ACPI_SUCCESS(status)) {
+	if (buf[0] & ACPI_OSCERR_OSCFAIL) {
+	    device_printf(dev, "_OSC unable to process request\n");
+	    status = AE_ERROR;
+	}
+	if (buf[0] & ACPI_OSCERR_UUID) {
+	    device_printf(dev, "_OSC unrecognized UUID (%s)\n", uuidstr);
+	    status = AE_ERROR;
+	}
+	if (buf[0] & ACPI_OSCERR_REVISION) {
+	    device_printf(dev, "_OSC unrecognized revision ID (%d)\n",
+		revision);
+	    status = AE_ERROR;
+	}
+	if (buf[0] & ACPI_OSCERR_CAPSMASKED) {
+	    if (((uint32_t *)retbuf.Pointer)[0] & ACPI_OSC_QUERY_SUPPORT)
+		goto done;
+	    for (i = 1; i <= size / sizeof(uint32_t); i++) {
+		device_printf(dev,
+		    "_OSC capabilities have been masked: buf[%d]:%#x\n",
+		    i, buf[i] & ~((uint32_t *)retbuf.Pointer)[i]);
+	    }
+	    status = AE_SUPPORT;
+	}
+    }
+
+done:
+    AcpiOsFree(retbuf.Pointer);
+    return (status);
+}
+
 /* Probe _HID and _CID for compatible ISA PNP ids. */
 static uint32_t
 acpi_isa_get_logicalid(device_t dev)
diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c
index 903b0ec..2ae795d 100644
--- a/sys/dev/acpica/acpi_cpu.c
+++ b/sys/dev/acpica/acpi_cpu.c
@@ -191,43 +191,19 @@ acpi_cpu_attach(device_t dev)
      * revision, count, and one or more capabilities.
      */
     if (cpu_features) {
-	ACPI_OBJECT_LIST arglist;
 	uint32_t cap_set[3];
-	ACPI_OBJECT arg[4];
 	ACPI_STATUS status;
 
-	/* UUID needed by _OSC evaluation */
-	static uint8_t cpu_oscuuid[16] = {
-	   0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29, 0xBE, 0x47,
-	   0x9E, 0xBD, 0xD8, 0x70, 0x58, 0x71, 0x39, 0x53
-	};
-
-	arglist.Pointer = arg;
-	arglist.Count = 4;
-	arg[0].Type = ACPI_TYPE_BUFFER;
-	arg[0].Buffer.Length = sizeof(cpu_oscuuid);
-	arg[0].Buffer.Pointer = cpu_oscuuid;	/* UUID */
-	arg[1].Type = ACPI_TYPE_INTEGER;
-	arg[1].Integer.Value = 1;		/* revision */
-	arg[2].Type = ACPI_TYPE_INTEGER;
-	arg[2].Integer.Value = 2;		/* # of capabilities integers */
-	arg[3].Type = ACPI_TYPE_BUFFER;
-	arg[3].Buffer.Length = sizeof(cap_set[0]) * 2; /* capabilities buffer */
-	arg[3].Buffer.Pointer = (uint8_t *)cap_set;
 	cap_set[0] = 0;
 	cap_set[1] = cpu_features;
-	status = AcpiEvaluateObject(handle, "_OSC", &arglist, NULL);
-	if (ACPI_SUCCESS(status)) {
-	    if (cap_set[0] & ACPI_OSCERR_OSCFAIL)
-		device_printf(dev, "_OSC unable to process request\n");
-	    if (cap_set[0] & ACPI_OSCERR_UUID)
-		device_printf(dev, "_OSC unrecognized UUID\n");
-	    if (cap_set[0] & ACPI_OSCERR_REVISION)
-		device_printf(dev, "_OSC unrecognized revision ID\n");
-	    if (cap_set[0] & ACPI_OSCERR_CAPSMASKED)
-		device_printf(dev, "_OSC capabilities have been masked: %#x\n",
-		    cpu_features & ~cap_set[1]);
-	} else {
+	status = acpi_eval_osc(dev, handle,
+	    "4077A616-290C-47BE-9EBD-D87058713953", 1, cap_set,
+	    sizeof(uint32_t) * 2);
+
+	if (ACPI_FAILURE(status)) {
+	    ACPI_OBJECT_LIST arglist;
+	    ACPI_OBJECT arg[4];
+
 	    if (bootverbose)
 		device_printf(dev, "_OSC failed, using _PDC\n");
 
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index 5ac55bc..9acf651 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -209,6 +209,8 @@ extern struct lock acpi_lock;
 #define ACPI_PDC_MP_C2C3_NATIVE	(1 << 9) /* MP C2 and C3 support */
 #define ACPI_PDC_PX_HWCOORD	(1 << 11)/* Hardware coordination of Px */
 
+#define ACPI_OSC_QUERY_SUPPORT	(1 << 0) /* Query Support Flag */
+
 #define ACPI_OSCERR_OSCFAIL	(1 << 1) /* _OSC failure */
 #define ACPI_OSCERR_UUID	(1 << 2) /* Unrecognized UUID */
 #define ACPI_OSCERR_REVISION	(1 << 3) /* Unrecognized revision ID */
@@ -360,6 +362,9 @@ void		acpi_UserNotify(const char *subsystem, ACPI_HANDLE h,
 int		acpi_bus_alloc_gas(device_t dev, int *type, int *rid,
 		    ACPI_GENERIC_ADDRESS *gas, struct resource **res,
 		    u_int flags);
+ACPI_STATUS	acpi_eval_osc(device_t dev, ACPI_HANDLE handle,
+		    const char *uuidstr, int revision, uint32_t *buf,
+		    size_t size);
 
 struct acpi_parse_resource_set {
     void	(*set_init)(device_t dev, void *arg, void **context);
-- 
1.9.3

