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 37 struct acpi_osi_entry { 38 char string[OSI_STRING_LENGTH_MAX]; 39 bool enable; 40 }; 41 42 static 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 52 static struct acpi_osi_config osi_config; 53 static struct acpi_osi_entry 54 osi_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 61 static 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 81 void __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 131 static 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 143 static 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 */ 180 static 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 189 static 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 */ 204 static 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 237 static 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 254 bool acpi_osi_is_win8(void) 255 { 256 return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; 257 } 258 EXPORT_SYMBOL(acpi_osi_is_win8); 259 260 static 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 268 void __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 275 static 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 282 static 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 289 static 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 299 static 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 307 static 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 */ 321 static 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 504 static __init void acpi_osi_dmi_blacklisted(void) 505 { 506 dmi_check_system(acpi_osi_dmi_table); 507 } 508 509 int __init early_acpi_osi_init(void) 510 { 511 acpi_osi_dmi_blacklisted(); 512 513 return 0; 514 } 515 516 int __init acpi_osi_init(void) 517 { 518 acpi_install_interface_handler(acpi_osi_handler); 519 acpi_osi_setup_late(); 520 521 return 0; 522 } 523