root/drivers/acpi/osi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. acpi_osi_handler
  2. acpi_osi_setup
  3. __acpi_osi_setup_darwin
  4. acpi_osi_setup_darwin
  5. __acpi_osi_setup_linux
  6. acpi_osi_setup_linux
  7. acpi_osi_setup_late
  8. osi_setup
  9. acpi_osi_is_win8
  10. acpi_osi_dmi_darwin
  11. acpi_osi_dmi_linux
  12. dmi_enable_osi_linux
  13. dmi_disable_osi_vista
  14. dmi_disable_osi_win7
  15. dmi_disable_osi_win8
  16. acpi_osi_dmi_blacklisted
  17. early_acpi_osi_init
  18. acpi_osi_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  osi.c - _OSI implementation
   4  *
   5  *  Copyright (C) 2016 Intel Corporation
   6  *    Author: Lv Zheng <lv.zheng@intel.com>
   7  */
   8 
   9 /* Uncomment next line to get verbose printout */
  10 /* #define DEBUG */
  11 #define pr_fmt(fmt) "ACPI: " fmt
  12 
  13 #include <linux/module.h>
  14 #include <linux/kernel.h>
  15 #include <linux/acpi.h>
  16 #include <linux/dmi.h>
  17 #include <linux/platform_data/x86/apple.h>
  18 
  19 #include "internal.h"
  20 
  21 
  22 #define OSI_STRING_LENGTH_MAX   64
  23 #define OSI_STRING_ENTRIES_MAX  16
  24 
  25 struct acpi_osi_entry {
  26         char string[OSI_STRING_LENGTH_MAX];
  27         bool enable;
  28 };
  29 
  30 static struct acpi_osi_config {
  31         u8              default_disabling;
  32         unsigned int    linux_enable:1;
  33         unsigned int    linux_dmi:1;
  34         unsigned int    linux_cmdline:1;
  35         unsigned int    darwin_enable:1;
  36         unsigned int    darwin_dmi:1;
  37         unsigned int    darwin_cmdline:1;
  38 } osi_config;
  39 
  40 static struct acpi_osi_config osi_config;
  41 static struct acpi_osi_entry
  42 osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
  43         {"Module Device", true},
  44         {"Processor Device", true},
  45         {"3.0 _SCP Extensions", true},
  46         {"Processor Aggregator Device", true},
  47         /*
  48          * Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
  49          * cards as RTD3 is not supported by drivers now.  Systems with NVidia
  50          * cards will hang without RTD3 disabled.
  51          *
  52          * Once NVidia drivers officially support RTD3, this _OSI strings can
  53          * be removed if both new and old graphics cards are supported.
  54          */
  55         {"Linux-Dell-Video", true},
  56         /*
  57          * Linux-Lenovo-NV-HDMI-Audio is used by BIOS to power on NVidia's HDMI
  58          * audio device which is turned off for power-saving in Windows OS.
  59          * This power management feature observed on some Lenovo Thinkpad
  60          * systems which will not be able to output audio via HDMI without
  61          * a BIOS workaround.
  62          */
  63         {"Linux-Lenovo-NV-HDMI-Audio", true},
  64         /*
  65          * Linux-HPI-Hybrid-Graphics is used by BIOS to enable dGPU to
  66          * output video directly to external monitors on HP Inc. mobile
  67          * workstations as Nvidia and AMD VGA drivers provide limited
  68          * hybrid graphics supports.
  69          */
  70         {"Linux-HPI-Hybrid-Graphics", true},
  71 };
  72 
  73 static u32 acpi_osi_handler(acpi_string interface, u32 supported)
  74 {
  75         if (!strcmp("Linux", interface)) {
  76                 pr_notice_once(FW_BUG
  77                         "BIOS _OSI(Linux) query %s%s\n",
  78                         osi_config.linux_enable ? "honored" : "ignored",
  79                         osi_config.linux_cmdline ? " via cmdline" :
  80                         osi_config.linux_dmi ? " via DMI" : "");
  81         }
  82         if (!strcmp("Darwin", interface)) {
  83                 pr_notice_once(
  84                         "BIOS _OSI(Darwin) query %s%s\n",
  85                         osi_config.darwin_enable ? "honored" : "ignored",
  86                         osi_config.darwin_cmdline ? " via cmdline" :
  87                         osi_config.darwin_dmi ? " via DMI" : "");
  88         }
  89 
  90         return supported;
  91 }
  92 
  93 void __init acpi_osi_setup(char *str)
  94 {
  95         struct acpi_osi_entry *osi;
  96         bool enable = true;
  97         int i;
  98 
  99         if (!acpi_gbl_create_osi_method)
 100                 return;
 101 
 102         if (str == NULL || *str == '\0') {
 103                 pr_info("_OSI method disabled\n");
 104                 acpi_gbl_create_osi_method = FALSE;
 105                 return;
 106         }
 107 
 108         if (*str == '!') {
 109                 str++;
 110                 if (*str == '\0') {
 111                         /* Do not override acpi_osi=!* */
 112                         if (!osi_config.default_disabling)
 113                                 osi_config.default_disabling =
 114                                         ACPI_DISABLE_ALL_VENDOR_STRINGS;
 115                         return;
 116                 } else if (*str == '*') {
 117                         osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS;
 118                         for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
 119                                 osi = &osi_setup_entries[i];
 120                                 osi->enable = false;
 121                         }
 122                         return;
 123                 } else if (*str == '!') {
 124                         osi_config.default_disabling = 0;
 125                         return;
 126                 }
 127                 enable = false;
 128         }
 129 
 130         for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
 131                 osi = &osi_setup_entries[i];
 132                 if (!strcmp(osi->string, str)) {
 133                         osi->enable = enable;
 134                         break;
 135                 } else if (osi->string[0] == '\0') {
 136                         osi->enable = enable;
 137                         strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
 138                         break;
 139                 }
 140         }
 141 }
 142 
 143 static void __init __acpi_osi_setup_darwin(bool enable)
 144 {
 145         osi_config.darwin_enable = !!enable;
 146         if (enable) {
 147                 acpi_osi_setup("!");
 148                 acpi_osi_setup("Darwin");
 149         } else {
 150                 acpi_osi_setup("!!");
 151                 acpi_osi_setup("!Darwin");
 152         }
 153 }
 154 
 155 static void __init acpi_osi_setup_darwin(bool enable)
 156 {
 157         /* Override acpi_osi_dmi_blacklisted() */
 158         osi_config.darwin_dmi = 0;
 159         osi_config.darwin_cmdline = 1;
 160         __acpi_osi_setup_darwin(enable);
 161 }
 162 
 163 /*
 164  * The story of _OSI(Linux)
 165  *
 166  * From pre-history through Linux-2.6.22, Linux responded TRUE upon a BIOS
 167  * OSI(Linux) query.
 168  *
 169  * Unfortunately, reference BIOS writers got wind of this and put
 170  * OSI(Linux) in their example code, quickly exposing this string as
 171  * ill-conceived and opening the door to an un-bounded number of BIOS
 172  * incompatibilities.
 173  *
 174  * For example, OSI(Linux) was used on resume to re-POST a video card on
 175  * one system, because Linux at that time could not do a speedy restore in
 176  * its native driver. But then upon gaining quick native restore
 177  * capability, Linux has no way to tell the BIOS to skip the time-consuming
 178  * POST -- putting Linux at a permanent performance disadvantage. On
 179  * another system, the BIOS writer used OSI(Linux) to infer native OS
 180  * support for IPMI!  On other systems, OSI(Linux) simply got in the way of
 181  * Linux claiming to be compatible with other operating systems, exposing
 182  * BIOS issues such as skipped device initialization.
 183  *
 184  * So "Linux" turned out to be a really poor chose of OSI string, and from
 185  * Linux-2.6.23 onward we respond FALSE.
 186  *
 187  * BIOS writers should NOT query _OSI(Linux) on future systems. Linux will
 188  * complain on the console when it sees it, and return FALSE. To get Linux
 189  * to return TRUE for your system  will require a kernel source update to
 190  * add a DMI entry, or boot with "acpi_osi=Linux"
 191  */
 192 static void __init __acpi_osi_setup_linux(bool enable)
 193 {
 194         osi_config.linux_enable = !!enable;
 195         if (enable)
 196                 acpi_osi_setup("Linux");
 197         else
 198                 acpi_osi_setup("!Linux");
 199 }
 200 
 201 static void __init acpi_osi_setup_linux(bool enable)
 202 {
 203         /* Override acpi_osi_dmi_blacklisted() */
 204         osi_config.linux_dmi = 0;
 205         osi_config.linux_cmdline = 1;
 206         __acpi_osi_setup_linux(enable);
 207 }
 208 
 209 /*
 210  * Modify the list of "OS Interfaces" reported to BIOS via _OSI
 211  *
 212  * empty string disables _OSI
 213  * string starting with '!' disables that string
 214  * otherwise string is added to list, augmenting built-in strings
 215  */
 216 static void __init acpi_osi_setup_late(void)
 217 {
 218         struct acpi_osi_entry *osi;
 219         char *str;
 220         int i;
 221         acpi_status status;
 222 
 223         if (osi_config.default_disabling) {
 224                 status = acpi_update_interfaces(osi_config.default_disabling);
 225                 if (ACPI_SUCCESS(status))
 226                         pr_info("Disabled all _OSI OS vendors%s\n",
 227                                 osi_config.default_disabling ==
 228                                 ACPI_DISABLE_ALL_STRINGS ?
 229                                 " and feature groups" : "");
 230         }
 231 
 232         for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
 233                 osi = &osi_setup_entries[i];
 234                 str = osi->string;
 235                 if (*str == '\0')
 236                         break;
 237                 if (osi->enable) {
 238                         status = acpi_install_interface(str);
 239                         if (ACPI_SUCCESS(status))
 240                                 pr_info("Added _OSI(%s)\n", str);
 241                 } else {
 242                         status = acpi_remove_interface(str);
 243                         if (ACPI_SUCCESS(status))
 244                                 pr_info("Deleted _OSI(%s)\n", str);
 245                 }
 246         }
 247 }
 248 
 249 static int __init osi_setup(char *str)
 250 {
 251         if (str && !strcmp("Linux", str))
 252                 acpi_osi_setup_linux(true);
 253         else if (str && !strcmp("!Linux", str))
 254                 acpi_osi_setup_linux(false);
 255         else if (str && !strcmp("Darwin", str))
 256                 acpi_osi_setup_darwin(true);
 257         else if (str && !strcmp("!Darwin", str))
 258                 acpi_osi_setup_darwin(false);
 259         else
 260                 acpi_osi_setup(str);
 261 
 262         return 1;
 263 }
 264 __setup("acpi_osi=", osi_setup);
 265 
 266 bool acpi_osi_is_win8(void)
 267 {
 268         return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
 269 }
 270 EXPORT_SYMBOL(acpi_osi_is_win8);
 271 
 272 static void __init acpi_osi_dmi_darwin(void)
 273 {
 274         pr_notice("DMI detected to setup _OSI(\"Darwin\"): Apple hardware\n");
 275         osi_config.darwin_dmi = 1;
 276         __acpi_osi_setup_darwin(true);
 277 }
 278 
 279 static void __init acpi_osi_dmi_linux(bool enable,
 280                                       const struct dmi_system_id *d)
 281 {
 282         pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident);
 283         osi_config.linux_dmi = 1;
 284         __acpi_osi_setup_linux(enable);
 285 }
 286 
 287 static int __init dmi_enable_osi_linux(const struct dmi_system_id *d)
 288 {
 289         acpi_osi_dmi_linux(true, d);
 290 
 291         return 0;
 292 }
 293 
 294 static int __init dmi_disable_osi_vista(const struct dmi_system_id *d)
 295 {
 296         pr_notice("DMI detected: %s\n", d->ident);
 297         acpi_osi_setup("!Windows 2006");
 298         acpi_osi_setup("!Windows 2006 SP1");
 299         acpi_osi_setup("!Windows 2006 SP2");
 300 
 301         return 0;
 302 }
 303 
 304 static int __init dmi_disable_osi_win7(const struct dmi_system_id *d)
 305 {
 306         pr_notice("DMI detected: %s\n", d->ident);
 307         acpi_osi_setup("!Windows 2009");
 308 
 309         return 0;
 310 }
 311 
 312 static int __init dmi_disable_osi_win8(const struct dmi_system_id *d)
 313 {
 314         pr_notice("DMI detected: %s\n", d->ident);
 315         acpi_osi_setup("!Windows 2012");
 316 
 317         return 0;
 318 }
 319 
 320 /*
 321  * Linux default _OSI response behavior is determined by this DMI table.
 322  *
 323  * Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden
 324  * by acpi_osi=!Linux/acpi_osi=!Darwin command line options.
 325  */
 326 static const struct dmi_system_id acpi_osi_dmi_table[] __initconst = {
 327         {
 328         .callback = dmi_disable_osi_vista,
 329         .ident = "Fujitsu Siemens",
 330         .matches = {
 331                      DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
 332                      DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
 333                 },
 334         },
 335         {
 336         /*
 337          * There have a NVIF method in MSI GX723 DSDT need call by Nvidia
 338          * driver (e.g. nouveau) when user press brightness hotkey.
 339          * Currently, nouveau driver didn't do the job and it causes there
 340          * have a infinite while loop in DSDT when user press hotkey.
 341          * We add MSI GX723's dmi information to this table for workaround
 342          * this issue.
 343          * Will remove MSI GX723 from the table after nouveau grows support.
 344          */
 345         .callback = dmi_disable_osi_vista,
 346         .ident = "MSI GX723",
 347         .matches = {
 348                      DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
 349                      DMI_MATCH(DMI_PRODUCT_NAME, "GX723"),
 350                 },
 351         },
 352         {
 353         .callback = dmi_disable_osi_vista,
 354         .ident = "Sony VGN-NS10J_S",
 355         .matches = {
 356                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 357                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
 358                 },
 359         },
 360         {
 361         .callback = dmi_disable_osi_vista,
 362         .ident = "Sony VGN-SR290J",
 363         .matches = {
 364                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 365                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"),
 366                 },
 367         },
 368         {
 369         .callback = dmi_disable_osi_vista,
 370         .ident = "VGN-NS50B_L",
 371         .matches = {
 372                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 373                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"),
 374                 },
 375         },
 376         {
 377         .callback = dmi_disable_osi_vista,
 378         .ident = "VGN-SR19XN",
 379         .matches = {
 380                      DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
 381                      DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
 382                 },
 383         },
 384         {
 385         .callback = dmi_disable_osi_vista,
 386         .ident = "Toshiba Satellite L355",
 387         .matches = {
 388                      DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
 389                      DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"),
 390                 },
 391         },
 392         {
 393         .callback = dmi_disable_osi_win7,
 394         .ident = "ASUS K50IJ",
 395         .matches = {
 396                      DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
 397                      DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"),
 398                 },
 399         },
 400         {
 401         .callback = dmi_disable_osi_vista,
 402         .ident = "Toshiba P305D",
 403         .matches = {
 404                      DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
 405                      DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"),
 406                 },
 407         },
 408         {
 409         .callback = dmi_disable_osi_vista,
 410         .ident = "Toshiba NB100",
 411         .matches = {
 412                      DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
 413                      DMI_MATCH(DMI_PRODUCT_NAME, "NB100"),
 414                 },
 415         },
 416 
 417         /*
 418          * The wireless hotkey does not work on those machines when
 419          * returning true for _OSI("Windows 2012")
 420          */
 421         {
 422         .callback = dmi_disable_osi_win8,
 423         .ident = "Dell Inspiron 7737",
 424         .matches = {
 425                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 426                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
 427                 },
 428         },
 429         {
 430         .callback = dmi_disable_osi_win8,
 431         .ident = "Dell Inspiron 7537",
 432         .matches = {
 433                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 434                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
 435                 },
 436         },
 437         {
 438         .callback = dmi_disable_osi_win8,
 439         .ident = "Dell Inspiron 5437",
 440         .matches = {
 441                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 442                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
 443                 },
 444         },
 445         {
 446         .callback = dmi_disable_osi_win8,
 447         .ident = "Dell Inspiron 3437",
 448         .matches = {
 449                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 450                     DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
 451                 },
 452         },
 453         {
 454         .callback = dmi_disable_osi_win8,
 455         .ident = "Dell Vostro 3446",
 456         .matches = {
 457                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 458                     DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
 459                 },
 460         },
 461         {
 462         .callback = dmi_disable_osi_win8,
 463         .ident = "Dell Vostro 3546",
 464         .matches = {
 465                     DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 466                     DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
 467                 },
 468         },
 469 
 470         /*
 471          * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
 472          * Linux ignores it, except for the machines enumerated below.
 473          */
 474 
 475         /*
 476          * Without this this EEEpc exports a non working WMI interface, with
 477          * this it exports a working "good old" eeepc_laptop interface, fixing
 478          * both brightness control, and rfkill not working.
 479          */
 480         {
 481         .callback = dmi_enable_osi_linux,
 482         .ident = "Asus EEE PC 1015PX",
 483         .matches = {
 484                      DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
 485                      DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
 486                 },
 487         },
 488         {}
 489 };
 490 
 491 static __init void acpi_osi_dmi_blacklisted(void)
 492 {
 493         dmi_check_system(acpi_osi_dmi_table);
 494 
 495         /* Enable _OSI("Darwin") for Apple platforms. */
 496         if (x86_apple_machine)
 497                 acpi_osi_dmi_darwin();
 498 }
 499 
 500 int __init early_acpi_osi_init(void)
 501 {
 502         acpi_osi_dmi_blacklisted();
 503 
 504         return 0;
 505 }
 506 
 507 int __init acpi_osi_init(void)
 508 {
 509         acpi_install_interface_handler(acpi_osi_handler);
 510         acpi_osi_setup_late();
 511 
 512         return 0;
 513 }

/* [<][>][^][v][top][bottom][index][help] */