osi.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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. /* Uncomment next line to get verbose printout */
  22. /* #define DEBUG */
  23. #define pr_fmt(fmt) "ACPI: " fmt
  24. #include <linux/module.h>
  25. #include <linux/kernel.h>
  26. #include <linux/acpi.h>
  27. #include <linux/dmi.h>
  28. #include <linux/platform_data/x86/apple.h>
  29. #include "internal.h"
  30. #define OSI_STRING_LENGTH_MAX 64
  31. #define OSI_STRING_ENTRIES_MAX 16
  32. struct acpi_osi_entry {
  33. char string[OSI_STRING_LENGTH_MAX];
  34. bool enable;
  35. };
  36. static struct acpi_osi_config {
  37. u8 default_disabling;
  38. unsigned int linux_enable:1;
  39. unsigned int linux_dmi:1;
  40. unsigned int linux_cmdline:1;
  41. unsigned int darwin_enable:1;
  42. unsigned int darwin_dmi:1;
  43. unsigned int darwin_cmdline:1;
  44. } osi_config;
  45. static struct acpi_osi_config osi_config;
  46. static struct acpi_osi_entry
  47. osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
  48. {"Module Device", true},
  49. {"Processor Device", true},
  50. {"3.0 _SCP Extensions", true},
  51. {"Processor Aggregator Device", true},
  52. /*
  53. * Linux-Dell-Video is used by BIOS to disable RTD3 for NVidia graphics
  54. * cards as RTD3 is not supported by drivers now. Systems with NVidia
  55. * cards will hang without RTD3 disabled.
  56. *
  57. * Once NVidia drivers officially support RTD3, this _OSI strings can
  58. * be removed if both new and old graphics cards are supported.
  59. */
  60. {"Linux-Dell-Video", true},
  61. /*
  62. * Linux-Lenovo-NV-HDMI-Audio is used by BIOS to power on NVidia's HDMI
  63. * audio device which is turned off for power-saving in Windows OS.
  64. * This power management feature observed on some Lenovo Thinkpad
  65. * systems which will not be able to output audio via HDMI without
  66. * a BIOS workaround.
  67. */
  68. {"Linux-Lenovo-NV-HDMI-Audio", true},
  69. };
  70. static u32 acpi_osi_handler(acpi_string interface, u32 supported)
  71. {
  72. if (!strcmp("Linux", interface)) {
  73. pr_notice_once(FW_BUG
  74. "BIOS _OSI(Linux) query %s%s\n",
  75. osi_config.linux_enable ? "honored" : "ignored",
  76. osi_config.linux_cmdline ? " via cmdline" :
  77. osi_config.linux_dmi ? " via DMI" : "");
  78. }
  79. if (!strcmp("Darwin", interface)) {
  80. pr_notice_once(
  81. "BIOS _OSI(Darwin) query %s%s\n",
  82. osi_config.darwin_enable ? "honored" : "ignored",
  83. osi_config.darwin_cmdline ? " via cmdline" :
  84. osi_config.darwin_dmi ? " via DMI" : "");
  85. }
  86. return supported;
  87. }
  88. void __init acpi_osi_setup(char *str)
  89. {
  90. struct acpi_osi_entry *osi;
  91. bool enable = true;
  92. int i;
  93. if (!acpi_gbl_create_osi_method)
  94. return;
  95. if (str == NULL || *str == '\0') {
  96. pr_info("_OSI method disabled\n");
  97. acpi_gbl_create_osi_method = FALSE;
  98. return;
  99. }
  100. if (*str == '!') {
  101. str++;
  102. if (*str == '\0') {
  103. /* Do not override acpi_osi=!* */
  104. if (!osi_config.default_disabling)
  105. osi_config.default_disabling =
  106. ACPI_DISABLE_ALL_VENDOR_STRINGS;
  107. return;
  108. } else if (*str == '*') {
  109. osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS;
  110. for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
  111. osi = &osi_setup_entries[i];
  112. osi->enable = false;
  113. }
  114. return;
  115. } else if (*str == '!') {
  116. osi_config.default_disabling = 0;
  117. return;
  118. }
  119. enable = false;
  120. }
  121. for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
  122. osi = &osi_setup_entries[i];
  123. if (!strcmp(osi->string, str)) {
  124. osi->enable = enable;
  125. break;
  126. } else if (osi->string[0] == '\0') {
  127. osi->enable = enable;
  128. strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
  129. break;
  130. }
  131. }
  132. }
  133. static void __init __acpi_osi_setup_darwin(bool enable)
  134. {
  135. osi_config.darwin_enable = !!enable;
  136. if (enable) {
  137. acpi_osi_setup("!");
  138. acpi_osi_setup("Darwin");
  139. } else {
  140. acpi_osi_setup("!!");
  141. acpi_osi_setup("!Darwin");
  142. }
  143. }
  144. static void __init acpi_osi_setup_darwin(bool enable)
  145. {
  146. /* Override acpi_osi_dmi_blacklisted() */
  147. osi_config.darwin_dmi = 0;
  148. osi_config.darwin_cmdline = 1;
  149. __acpi_osi_setup_darwin(enable);
  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. static void __init acpi_osi_setup_linux(bool enable)
  189. {
  190. /* Override acpi_osi_dmi_blacklisted() */
  191. osi_config.linux_dmi = 0;
  192. osi_config.linux_cmdline = 1;
  193. __acpi_osi_setup_linux(enable);
  194. }
  195. /*
  196. * Modify the list of "OS Interfaces" reported to BIOS via _OSI
  197. *
  198. * empty string disables _OSI
  199. * string starting with '!' disables that string
  200. * otherwise string is added to list, augmenting built-in strings
  201. */
  202. static void __init acpi_osi_setup_late(void)
  203. {
  204. struct acpi_osi_entry *osi;
  205. char *str;
  206. int i;
  207. acpi_status status;
  208. if (osi_config.default_disabling) {
  209. status = acpi_update_interfaces(osi_config.default_disabling);
  210. if (ACPI_SUCCESS(status))
  211. pr_info("Disabled all _OSI OS vendors%s\n",
  212. osi_config.default_disabling ==
  213. ACPI_DISABLE_ALL_STRINGS ?
  214. " and feature groups" : "");
  215. }
  216. for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
  217. osi = &osi_setup_entries[i];
  218. str = osi->string;
  219. if (*str == '\0')
  220. break;
  221. if (osi->enable) {
  222. status = acpi_install_interface(str);
  223. if (ACPI_SUCCESS(status))
  224. pr_info("Added _OSI(%s)\n", str);
  225. } else {
  226. status = acpi_remove_interface(str);
  227. if (ACPI_SUCCESS(status))
  228. pr_info("Deleted _OSI(%s)\n", str);
  229. }
  230. }
  231. }
  232. static int __init osi_setup(char *str)
  233. {
  234. if (str && !strcmp("Linux", str))
  235. acpi_osi_setup_linux(true);
  236. else if (str && !strcmp("!Linux", str))
  237. acpi_osi_setup_linux(false);
  238. else if (str && !strcmp("Darwin", str))
  239. acpi_osi_setup_darwin(true);
  240. else if (str && !strcmp("!Darwin", str))
  241. acpi_osi_setup_darwin(false);
  242. else
  243. acpi_osi_setup(str);
  244. return 1;
  245. }
  246. __setup("acpi_osi=", osi_setup);
  247. bool acpi_osi_is_win8(void)
  248. {
  249. return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
  250. }
  251. EXPORT_SYMBOL(acpi_osi_is_win8);
  252. static void __init acpi_osi_dmi_darwin(void)
  253. {
  254. pr_notice("DMI detected to setup _OSI(\"Darwin\"): Apple hardware\n");
  255. osi_config.darwin_dmi = 1;
  256. __acpi_osi_setup_darwin(true);
  257. }
  258. static void __init acpi_osi_dmi_linux(bool enable,
  259. const struct dmi_system_id *d)
  260. {
  261. pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident);
  262. osi_config.linux_dmi = 1;
  263. __acpi_osi_setup_linux(enable);
  264. }
  265. static int __init dmi_enable_osi_linux(const struct dmi_system_id *d)
  266. {
  267. acpi_osi_dmi_linux(true, d);
  268. return 0;
  269. }
  270. static int __init dmi_disable_osi_vista(const struct dmi_system_id *d)
  271. {
  272. pr_notice("DMI detected: %s\n", d->ident);
  273. acpi_osi_setup("!Windows 2006");
  274. acpi_osi_setup("!Windows 2006 SP1");
  275. acpi_osi_setup("!Windows 2006 SP2");
  276. return 0;
  277. }
  278. static int __init dmi_disable_osi_win7(const struct dmi_system_id *d)
  279. {
  280. pr_notice("DMI detected: %s\n", d->ident);
  281. acpi_osi_setup("!Windows 2009");
  282. return 0;
  283. }
  284. static int __init dmi_disable_osi_win8(const struct dmi_system_id *d)
  285. {
  286. pr_notice("DMI detected: %s\n", d->ident);
  287. acpi_osi_setup("!Windows 2012");
  288. return 0;
  289. }
  290. /*
  291. * Linux default _OSI response behavior is determined by this DMI table.
  292. *
  293. * Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden
  294. * by acpi_osi=!Linux/acpi_osi=!Darwin command line options.
  295. */
  296. static const struct dmi_system_id acpi_osi_dmi_table[] __initconst = {
  297. {
  298. .callback = dmi_disable_osi_vista,
  299. .ident = "Fujitsu Siemens",
  300. .matches = {
  301. DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
  302. DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
  303. },
  304. },
  305. {
  306. /*
  307. * There have a NVIF method in MSI GX723 DSDT need call by Nvidia
  308. * driver (e.g. nouveau) when user press brightness hotkey.
  309. * Currently, nouveau driver didn't do the job and it causes there
  310. * have a infinite while loop in DSDT when user press hotkey.
  311. * We add MSI GX723's dmi information to this table for workaround
  312. * this issue.
  313. * Will remove MSI GX723 from the table after nouveau grows support.
  314. */
  315. .callback = dmi_disable_osi_vista,
  316. .ident = "MSI GX723",
  317. .matches = {
  318. DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
  319. DMI_MATCH(DMI_PRODUCT_NAME, "GX723"),
  320. },
  321. },
  322. {
  323. .callback = dmi_disable_osi_vista,
  324. .ident = "Sony VGN-NS10J_S",
  325. .matches = {
  326. DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
  327. DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
  328. },
  329. },
  330. {
  331. .callback = dmi_disable_osi_vista,
  332. .ident = "Sony VGN-SR290J",
  333. .matches = {
  334. DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
  335. DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"),
  336. },
  337. },
  338. {
  339. .callback = dmi_disable_osi_vista,
  340. .ident = "VGN-NS50B_L",
  341. .matches = {
  342. DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
  343. DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"),
  344. },
  345. },
  346. {
  347. .callback = dmi_disable_osi_vista,
  348. .ident = "VGN-SR19XN",
  349. .matches = {
  350. DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
  351. DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
  352. },
  353. },
  354. {
  355. .callback = dmi_disable_osi_vista,
  356. .ident = "Toshiba Satellite L355",
  357. .matches = {
  358. DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
  359. DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"),
  360. },
  361. },
  362. {
  363. .callback = dmi_disable_osi_win7,
  364. .ident = "ASUS K50IJ",
  365. .matches = {
  366. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
  367. DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"),
  368. },
  369. },
  370. {
  371. .callback = dmi_disable_osi_vista,
  372. .ident = "Toshiba P305D",
  373. .matches = {
  374. DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
  375. DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"),
  376. },
  377. },
  378. {
  379. .callback = dmi_disable_osi_vista,
  380. .ident = "Toshiba NB100",
  381. .matches = {
  382. DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
  383. DMI_MATCH(DMI_PRODUCT_NAME, "NB100"),
  384. },
  385. },
  386. /*
  387. * The wireless hotkey does not work on those machines when
  388. * returning true for _OSI("Windows 2012")
  389. */
  390. {
  391. .callback = dmi_disable_osi_win8,
  392. .ident = "Dell Inspiron 7737",
  393. .matches = {
  394. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  395. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
  396. },
  397. },
  398. {
  399. .callback = dmi_disable_osi_win8,
  400. .ident = "Dell Inspiron 7537",
  401. .matches = {
  402. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  403. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
  404. },
  405. },
  406. {
  407. .callback = dmi_disable_osi_win8,
  408. .ident = "Dell Inspiron 5437",
  409. .matches = {
  410. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  411. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
  412. },
  413. },
  414. {
  415. .callback = dmi_disable_osi_win8,
  416. .ident = "Dell Inspiron 3437",
  417. .matches = {
  418. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  419. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
  420. },
  421. },
  422. {
  423. .callback = dmi_disable_osi_win8,
  424. .ident = "Dell Vostro 3446",
  425. .matches = {
  426. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  427. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
  428. },
  429. },
  430. {
  431. .callback = dmi_disable_osi_win8,
  432. .ident = "Dell Vostro 3546",
  433. .matches = {
  434. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  435. DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
  436. },
  437. },
  438. /*
  439. * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
  440. * Linux ignores it, except for the machines enumerated below.
  441. */
  442. /*
  443. * Without this this EEEpc exports a non working WMI interface, with
  444. * this it exports a working "good old" eeepc_laptop interface, fixing
  445. * both brightness control, and rfkill not working.
  446. */
  447. {
  448. .callback = dmi_enable_osi_linux,
  449. .ident = "Asus EEE PC 1015PX",
  450. .matches = {
  451. DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
  452. DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
  453. },
  454. },
  455. {}
  456. };
  457. static __init void acpi_osi_dmi_blacklisted(void)
  458. {
  459. dmi_check_system(acpi_osi_dmi_table);
  460. /* Enable _OSI("Darwin") for Apple platforms. */
  461. if (x86_apple_machine)
  462. acpi_osi_dmi_darwin();
  463. }
  464. int __init early_acpi_osi_init(void)
  465. {
  466. acpi_osi_dmi_blacklisted();
  467. return 0;
  468. }
  469. int __init acpi_osi_init(void)
  470. {
  471. acpi_install_interface_handler(acpi_osi_handler);
  472. acpi_osi_setup_late();
  473. return 0;
  474. }