osi.c 12 KB

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