Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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