Len Brown | bc18461 | 2016-11-30 23:00:30 -0500 | [diff] [blame] | 1 | ACPI _OSI and _REV methods |
| 2 | -------------------------- |
| 3 | |
| 4 | An ACPI BIOS can use the "Operating System Interfaces" method (_OSI) |
| 5 | to find out what the operating system supports. Eg. If BIOS |
| 6 | AML code includes _OSI("XYZ"), the kernel's AML interpreter |
| 7 | can evaluate that method, look to see if it supports 'XYZ' |
| 8 | and answer YES or NO to the BIOS. |
| 9 | |
| 10 | The ACPI _REV method returns the "Revision of the ACPI specification |
| 11 | that OSPM supports" |
| 12 | |
| 13 | This document explains how and why the BIOS and Linux should use these methods. |
| 14 | It also explains how and why they are widely misused. |
| 15 | |
| 16 | How to use _OSI |
| 17 | --------------- |
| 18 | |
| 19 | Linux runs on two groups of machines -- those that are tested by the OEM |
| 20 | to be compatible with Linux, and those that were never tested with Linux, |
| 21 | but where Linux was installed to replace the original OS (Windows or OSX). |
| 22 | |
| 23 | The larger group is the systems tested to run only Windows. Not only that, |
| 24 | but many were tested to run with just one specific version of Windows. |
| 25 | So even though the BIOS may use _OSI to query what version of Windows is running, |
| 26 | only a single path through the BIOS has actually been tested. |
| 27 | Experience shows that taking untested paths through the BIOS |
| 28 | exposes Linux to an entire category of BIOS bugs. |
| 29 | For this reason, Linux _OSI defaults must continue to claim compatibility |
| 30 | with all versions of Windows. |
| 31 | |
| 32 | But Linux isn't actually compatible with Windows, and the Linux community |
| 33 | has also been hurt with regressions when Linux adds the latest version of |
| 34 | Windows to its list of _OSI strings. So it is possible that additional strings |
| 35 | will be more thoroughly vetted before shipping upstream in the future. |
| 36 | But it is likely that they will all eventually be added. |
| 37 | |
| 38 | What should an OEM do if they want to support Linux and Windows |
| 39 | using the same BIOS image? Often they need to do something different |
| 40 | for Linux to deal with how Linux is different from Windows. |
| 41 | Here the BIOS should ask exactly what it wants to know: |
| 42 | |
| 43 | _OSI("Linux-OEM-my_interface_name") |
| 44 | where 'OEM' is needed if this is an OEM-specific hook, |
| 45 | and 'my_interface_name' describes the hook, which could be a |
| 46 | quirk, a bug, or a bug-fix. |
| 47 | |
| 48 | In addition, the OEM should send a patch to upstream Linux |
| 49 | via the linux-acpi@vger.kernel.org mailing list. When that patch |
| 50 | is checked into Linux, the OS will answer "YES" when the BIOS |
| 51 | on the OEM's system uses _OSI to ask if the interface is supported |
| 52 | by the OS. Linux distributors can back-port that patch for Linux |
| 53 | pre-installs, and it will be included by all distributions that |
| 54 | re-base to upstream. If the distribution can not update the kernel binary, |
| 55 | they can also add an acpi_osi=Linux-OEM-my_interface_name |
| 56 | cmdline parameter to the boot loader, as needed. |
| 57 | |
| 58 | If the string refers to a feature where the upstream kernel |
| 59 | eventually grows support, a patch should be sent to remove |
| 60 | the string when that support is added to the kernel. |
| 61 | |
| 62 | That was easy. Read on, to find out how to do it wrong. |
| 63 | |
| 64 | Before _OSI, there was _OS |
| 65 | -------------------------- |
| 66 | |
| 67 | ACPI 1.0 specified "_OS" as an |
| 68 | "object that evaluates to a string that identifies the operating system." |
| 69 | |
| 70 | The ACPI BIOS flow would include an evaluation of _OS, and the AML |
| 71 | interpreter in the kernel would return to it a string identifying the OS: |
| 72 | |
| 73 | Windows 98, SE: "Microsoft Windows" |
| 74 | Windows ME: "Microsoft WindowsME:Millenium Edition" |
| 75 | Windows NT: "Microsoft Windows NT" |
| 76 | |
| 77 | The idea was on a platform tasked with running multiple OS's, |
| 78 | the BIOS could use _OS to enable devices that an OS |
| 79 | might support, or enable quirks or bug workarounds |
| 80 | necessary to make the platform compatible with that pre-existing OS. |
| 81 | |
| 82 | But _OS had fundamental problems. First, the BIOS needed to know the name |
| 83 | of every possible version of the OS that would run on it, and needed to know |
| 84 | all the quirks of those OS's. Certainly it would make more sense |
| 85 | for the BIOS to ask *specific* things of the OS, such |
| 86 | "do you support a specific interface", and thus in ACPI 3.0, |
| 87 | _OSI was born to replace _OS. |
| 88 | |
| 89 | _OS was abandoned, though even today, many BIOS look for |
| 90 | _OS "Microsoft Windows NT", though it seems somewhat far-fetched |
| 91 | that anybody would install those old operating systems |
| 92 | over what came with the machine. |
| 93 | |
| 94 | Linux answers "Microsoft Windows NT" to please that BIOS idiom. |
| 95 | That is the *only* viable strategy, as that is what modern Windows does, |
| 96 | and so doing otherwise could steer the BIOS down an untested path. |
| 97 | |
| 98 | _OSI is born, and immediately misused |
| 99 | -------------------------------------- |
| 100 | |
| 101 | With _OSI, the *BIOS* provides the string describing an interface, |
| 102 | and asks the OS: "YES/NO, are you compatible with this interface?" |
| 103 | |
| 104 | eg. _OSI("3.0 Thermal Model") would return TRUE if the OS knows how |
| 105 | to deal with the thermal extensions made to the ACPI 3.0 specification. |
| 106 | An old OS that doesn't know about those extensions would answer FALSE, |
| 107 | and a new OS may be able to return TRUE. |
| 108 | |
| 109 | For an OS-specific interface, the ACPI spec said that the BIOS and the OS |
| 110 | were to agree on a string of the form such as "Windows-interface_name". |
| 111 | |
| 112 | But two bad things happened. First, the Windows ecosystem used _OSI |
| 113 | not as designed, but as a direct replacement for _OS -- identifying |
| 114 | the OS version, rather than an OS supported interface. Indeed, right |
| 115 | from the start, the ACPI 3.0 spec itself codified this misuse |
| 116 | in example code using _OSI("Windows 2001"). |
| 117 | |
| 118 | This misuse was adopted and continues today. |
| 119 | |
| 120 | Linux had no choice but to also return TRUE to _OSI("Windows 2001") |
| 121 | and its successors. To do otherwise would virtually guarantee breaking |
| 122 | a BIOS that has been tested only with that _OSI returning TRUE. |
| 123 | |
| 124 | This strategy is problematic, as Linux is never completely compatible with |
| 125 | the latest version of Windows, and sometimes it takes more than a year |
| 126 | to iron out incompatibilities. |
| 127 | |
| 128 | Not to be out-done, the Linux community made things worse by returning TRUE |
| 129 | to _OSI("Linux"). Doing so is even worse than the Windows misuse |
| 130 | of _OSI, as "Linux" does not even contain any version information. |
| 131 | _OSI("Linux") led to some BIOS' malfunctioning due to BIOS writer's |
| 132 | using it in untested BIOS flows. But some OEM's used _OSI("Linux") |
| 133 | in tested flows to support real Linux features. In 2009, Linux |
| 134 | removed _OSI("Linux"), and added a cmdline parameter to restore it |
| 135 | for legacy systems still needed it. Further a BIOS_BUG warning prints |
| 136 | for all BIOS's that invoke it. |
| 137 | |
| 138 | No BIOS should use _OSI("Linux"). |
| 139 | |
| 140 | The result is a strategy for Linux to maximize compatibility with |
| 141 | ACPI BIOS that are tested on Windows machines. There is a real risk |
| 142 | of over-stating that compatibility; but the alternative has often been |
| 143 | catastrophic failure resulting from the BIOS taking paths that |
| 144 | were never validated under *any* OS. |
| 145 | |
| 146 | Do not use _REV |
| 147 | --------------- |
| 148 | |
| 149 | Since _OSI("Linux") went away, some BIOS writers used _REV |
| 150 | to support Linux and Windows differences in the same BIOS. |
| 151 | |
| 152 | _REV was defined in ACPI 1.0 to return the version of ACPI |
| 153 | supported by the OS and the OS AML interpreter. |
| 154 | |
| 155 | Modern Windows returns _REV = 2. Linux used ACPI_CA_SUPPORT_LEVEL, |
| 156 | which would increment, based on the version of the spec supported. |
| 157 | |
| 158 | Unfortunately, _REV was also misused. eg. some BIOS would check |
| 159 | for _REV = 3, and do something for Linux, but when Linux returned |
| 160 | _REV = 4, that support broke. |
| 161 | |
| 162 | In response to this problem, Linux returns _REV = 2 always, |
| 163 | from mid-2015 onward. The ACPI specification will also be updated |
| 164 | to reflect that _REV is deprecated, and always returns 2. |
| 165 | |
| 166 | Apple Mac and _OSI("Darwin") |
| 167 | ---------------------------- |
| 168 | |
| 169 | On Apple's Mac platforms, the ACPI BIOS invokes _OSI("Darwin") |
| 170 | to determine if the machine is running Apple OSX. |
| 171 | |
| 172 | Like Linux's _OSI("*Windows*") strategy, Linux defaults to |
| 173 | answering YES to _OSI("Darwin") to enable full access |
| 174 | to the hardware and validated BIOS paths seen by OSX. |
| 175 | Just like on Windows-tested platforms, this strategy has risks. |
| 176 | |
| 177 | Starting in Linux-3.18, the kernel answered YES to _OSI("Darwin") |
| 178 | for the purpose of enabling Mac Thunderbolt support. Further, |
| 179 | if the kernel noticed _OSI("Darwin") being invoked, it additionally |
| 180 | disabled all _OSI("*Windows*") to keep poorly written Mac BIOS |
| 181 | from going down untested combinations of paths. |
| 182 | |
| 183 | The Linux-3.18 change in default caused power regressions on Mac |
| 184 | laptops, and the 3.18 implementation did not allow changing |
| 185 | the default via cmdline "acpi_osi=!Darwin". Linux-4.7 fixed |
| 186 | the ability to use acpi_osi=!Darwin as a workaround, and |
| 187 | we hope to see Mac Thunderbolt power management support in Linux-4.11. |