osi.c 13 KB

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