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