osi.c 13 KB

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