osi.c 12 KB

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