kernel/acpica: Add tunables for editing the _OSI string list.
authorSascha Wildner <saw@online.de>
Tue, 1 Jul 2014 03:09:17 +0000 (05:09 +0200)
committerSascha Wildner <saw@online.de>
Tue, 1 Jul 2014 03:10:03 +0000 (05:10 +0200)
ACPICA has a built-in list of supported _OSI strings. A query on any of
those strings returns TRUE. It is meant to help the ASL code determine
which OS it is dealing with. See AcpiDefaultSupportedInterfaces[] in
utosi.c for the full list.

This commit adds two tunables, hw.acpi.{install,remove}_interface,
for adding to and removing from that list. Their contents are comma
separated and leading white space is ignored.

Examples:

# Add "FreeBSD" to the list of supported _OSI strings,
# i.e. _OSI("FreeBSD") from ASL will return TRUE.
#
hw.acpi.install_interface="FreeBSD"

# Remove "Windows 2000" and "Windows 2001" from the list
# of supported _OSI strings, i.e. _OSI("Windows 2000") and
# _OSI("Windows 2001") from ASL will return FALSE.
#
hw.acpi.remove_interface="Windows 2000, Windows 2001"

Taken-from: FreeBSD

share/man/man4/acpi.4
sys/dev/acpica/acpi.c

index b352167..5edfec7 100644 (file)
@@ -25,7 +25,7 @@
 .\"
 .\" $FreeBSD: src/share/man/man4/acpi.4,v 1.61.8.1 2009/04/15 03:14:26 kensmith Exp $
 .\"
-.Dd June 1, 2014
+.Dd July 1, 2014
 .Dt ACPI 4
 .Os
 .Sh NAME
@@ -129,6 +129,30 @@ to enter when the power button is pressed.
 Default is
 .Li S5
 (power-off nicely).
+.It Va hw.acpi.install_interface , hw.acpi.remove_interface
+Install or remove OS interface(s) to control the return value of the
+.Ql _OSI
+query method.
+When an OS interface is specified in
+.Va hw.acpi.install_interface ,
+the
+.Li _OSI
+query for the interface returns it is
+.Em supported .
+Conversely, when an OS interface is specified in
+.Va hw.acpi.remove_interface ,
+the
+.Li _OSI
+query returns it is
+.Em not supported .
+Multiple interfaces can be specified in a comma-separated list and
+any leading white spaces will be ignored.
+For example,
+.Qq Li FreeBSD, Linux
+is a valid list of two interfaces
+.Qq Li FreeBSD
+and
+.Qq Li Linux .
 .It Va hw.acpi.reset_video
 Reset the video adapter from real mode during the resume path.
 Some systems need this help, others have display problems if it is enabled.
index ae6877f..81b2f60 100644 (file)
@@ -83,6 +83,11 @@ static struct dev_ops acpi_ops = {
         .d_ioctl = acpiioctl
 };
 
+struct acpi_interface {
+       ACPI_STRING     *data;
+       int             num;
+};
+
 /* Global mutex for locking access to the ACPI subsystem. */
 struct lock acpi_lock;
 
@@ -155,6 +160,7 @@ static int  acpi_child_location_str_method(device_t acdev, device_t child,
 static int     acpi_child_pnpinfo_str_method(device_t acdev, device_t child,
                                              char *buf, size_t buflen);
 static void    acpi_enable_pcie(void);
+static void    acpi_reset_interfaces(device_t dev);
 
 static device_method_t acpi_methods[] = {
     /* Device interface */
@@ -226,6 +232,16 @@ static char acpi_ca_version[12];
 SYSCTL_STRING(_debug_acpi, OID_AUTO, acpi_ca_version, CTLFLAG_RD,
              acpi_ca_version, 0, "Version of Intel ACPICA");
 
+/*
+ * Allow overriding _OSI methods.
+ */
+static char acpi_install_interface[256];
+TUNABLE_STR("hw.acpi.install_interface", acpi_install_interface,
+    sizeof(acpi_install_interface));
+static char acpi_remove_interface[256];
+TUNABLE_STR("hw.acpi.remove_interface", acpi_remove_interface,
+    sizeof(acpi_remove_interface));
+
 /*
  * Use this tunable to disable the control method auto-serialization
  * mechanism that was added in 20140214 and superseded the previous
@@ -473,6 +489,9 @@ acpi_attach(device_t dev)
        goto out;
     }
 
+    /* Override OS interfaces if the user requested. */
+    acpi_reset_interfaces(dev);
+
     /* Load ACPI name space. */
     status = AcpiLoadTables();
     if (ACPI_FAILURE(status)) {
@@ -3414,6 +3433,93 @@ acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS)
        return (0);
 }
 
+
+static int
+acpi_parse_interfaces(char *str, struct acpi_interface *iface)
+{
+       char *p;
+       size_t len;
+       int i, j;
+
+       p = str;
+       while (isspace(*p) || *p == ',')
+               p++;
+       len = strlen(p);
+       if (len == 0)
+               return (0);
+       p = kstrdup(p, M_TEMP);
+       for (i = 0; i < len; i++)
+               if (p[i] == ',')
+                       p[i] = '\0';
+       i = j = 0;
+       while (i < len)
+               if (isspace(p[i]) || p[i] == '\0')
+                       i++;
+               else {
+                       i += strlen(p + i) + 1;
+                       j++;
+               }
+       if (j == 0) {
+               kfree(p, M_TEMP);
+               return (0);
+       }
+       iface->data = kmalloc(sizeof(*iface->data) * j, M_TEMP, M_WAITOK);
+       iface->num = j;
+       i = j = 0;
+       while (i < len)
+               if (isspace(p[i]) || p[i] == '\0')
+                       i++;
+               else {
+                       iface->data[j] = p + i;
+                       i += strlen(p + i) + 1;
+                       j++;
+               }
+
+       return (j);
+}
+
+static void
+acpi_free_interfaces(struct acpi_interface *iface)
+{
+       kfree(iface->data[0], M_TEMP);
+       kfree(iface->data, M_TEMP);
+}
+
+static void
+acpi_reset_interfaces(device_t dev)
+{
+       struct acpi_interface list;
+       ACPI_STATUS status;
+       int i;
+
+       if (acpi_parse_interfaces(acpi_install_interface, &list) > 0) {
+               for (i = 0; i < list.num; i++) {
+                       status = AcpiInstallInterface(list.data[i]);
+                       if (ACPI_FAILURE(status))
+                               device_printf(dev,
+                                   "failed to install _OSI(\"%s\"): %s\n",
+                                   list.data[i], AcpiFormatException(status));
+                       else if (bootverbose)
+                               device_printf(dev, "installed _OSI(\"%s\")\n",
+                                   list.data[i]);
+               }
+               acpi_free_interfaces(&list);
+       }
+       if (acpi_parse_interfaces(acpi_remove_interface, &list) > 0) {
+               for (i = 0; i < list.num; i++) {
+                       status = AcpiRemoveInterface(list.data[i]);
+                       if (ACPI_FAILURE(status))
+                               device_printf(dev,
+                                   "failed to remove _OSI(\"%s\"): %s\n",
+                                   list.data[i], AcpiFormatException(status));
+                       else if (bootverbose)
+                               device_printf(dev, "removed _OSI(\"%s\")\n",
+                                   list.data[i]);
+               }
+               acpi_free_interfaces(&list);
+       }
+}
+
 static int
 acpi_pm_func(u_long cmd, void *arg, ...)
 {