diff options
author | Len Brown <len.brown@intel.com> | 2007-05-30 00:10:38 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-05-30 00:10:38 -0400 |
commit | f507654d450d329c81a70eec0096d5dfe67802ec (patch) | |
tree | bdc34cda973fac170cc90d5b6aa61d27ac558949 /drivers/acpi/osl.c | |
parent | ae00d812436dc968f4a5dea7757b6a94910b6dc4 (diff) |
ACPI: Make _OSI(Linux) a special case
_OSI("Linux") is like _OS("Linux"), it is ill-defined and
virtually no BIOS vendors test interaction with it.
As a result, it can do more damage than good because
it causes the BIOS to follow un-tested paths.
Recently, several machines have turned up that erroneously
test this string in a way which causes them to _not_ test other
compatibility strings, including the ZI9 and Toshiba.
So it appears that this bad code has made it into
a BIOS vendor's reference BIOS.
Linux has no choice but to stop advertising compatibility
with _OSI string "Linux" - as there are an unbounded
number of possible incompatibilities going forward.
But some BIOSes have already shipped which do use it
for things like conditionally re-enabling video on resume
from S3. (Too bad they didn't do that unconditionally)
Add special case code for _OSI(Linux)
Squawk to dmesg if _OSI(Linux) is requested
Add DMI list both to enable and disable _OSI(Linux)
But for now, keep the default enabled via
#define OSI_LINUX_ENABLED.
http://bugzilla.kernel.org/show_bug.cgi?id=7787
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f4760cfa61e1..e349879d9246 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/interrupt.h> | 33 | #include <linux/interrupt.h> |
34 | #include <linux/kmod.h> | 34 | #include <linux/kmod.h> |
35 | #include <linux/delay.h> | 35 | #include <linux/delay.h> |
36 | #include <linux/dmi.h> | ||
36 | #include <linux/workqueue.h> | 37 | #include <linux/workqueue.h> |
37 | #include <linux/nmi.h> | 38 | #include <linux/nmi.h> |
38 | #include <linux/acpi.h> | 39 | #include <linux/acpi.h> |
@@ -76,6 +77,18 @@ static struct workqueue_struct *kacpi_notify_wq; | |||
76 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ | 77 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ |
77 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; | 78 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; |
78 | 79 | ||
80 | #define OSI_LINUX_ENABLED | ||
81 | #ifdef OSI_LINUX_ENABLED | ||
82 | int osi_linux = 1; /* enable _OSI(Linux) by default */ | ||
83 | #else | ||
84 | int osi_linux; /* disable _OSI(Linux) by default */ | ||
85 | #endif | ||
86 | |||
87 | |||
88 | #ifdef CONFIG_DMI | ||
89 | static struct dmi_system_id acpi_osl_dmi_table[]; | ||
90 | #endif | ||
91 | |||
79 | static void __init acpi_request_region (struct acpi_generic_address *addr, | 92 | static void __init acpi_request_region (struct acpi_generic_address *addr, |
80 | unsigned int length, char *desc) | 93 | unsigned int length, char *desc) |
81 | { | 94 | { |
@@ -126,6 +139,7 @@ device_initcall(acpi_reserve_resources); | |||
126 | 139 | ||
127 | acpi_status acpi_os_initialize(void) | 140 | acpi_status acpi_os_initialize(void) |
128 | { | 141 | { |
142 | dmi_check_system(acpi_osl_dmi_table); | ||
129 | return AE_OK; | 143 | return AE_OK; |
130 | } | 144 | } |
131 | 145 | ||
@@ -963,6 +977,16 @@ static int __init acpi_os_name_setup(char *str) | |||
963 | 977 | ||
964 | __setup("acpi_os_name=", acpi_os_name_setup); | 978 | __setup("acpi_os_name=", acpi_os_name_setup); |
965 | 979 | ||
980 | static void enable_osi_linux(int enable) { | ||
981 | |||
982 | if (osi_linux != enable) | ||
983 | printk(KERN_INFO PREFIX "%sabled _OSI(Linux)\n", | ||
984 | enable ? "En": "Dis"); | ||
985 | |||
986 | osi_linux = enable; | ||
987 | return; | ||
988 | } | ||
989 | |||
966 | /* | 990 | /* |
967 | * Modify the list of "OS Interfaces" reported to BIOS via _OSI | 991 | * Modify the list of "OS Interfaces" reported to BIOS via _OSI |
968 | * | 992 | * |
@@ -978,6 +1002,10 @@ static int __init acpi_osi_setup(char *str) | |||
978 | } else if (*str == '!') { | 1002 | } else if (*str == '!') { |
979 | if (acpi_osi_invalidate(++str) == AE_OK) | 1003 | if (acpi_osi_invalidate(++str) == AE_OK) |
980 | printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); | 1004 | printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); |
1005 | } else if (!strcmp("!Linux", str)) { | ||
1006 | enable_osi_linux(0); | ||
1007 | } else if (!strcmp("Linux", str)) { | ||
1008 | enable_osi_linux(1); | ||
981 | } else if (*osi_additional_string == '\0') { | 1009 | } else if (*osi_additional_string == '\0') { |
982 | strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); | 1010 | strncpy(osi_additional_string, str, OSI_STRING_LENGTH_MAX); |
983 | printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); | 1011 | printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); |
@@ -1152,6 +1180,23 @@ acpi_os_validate_interface (char *interface) | |||
1152 | { | 1180 | { |
1153 | if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) | 1181 | if (!strncmp(osi_additional_string, interface, OSI_STRING_LENGTH_MAX)) |
1154 | return AE_OK; | 1182 | return AE_OK; |
1183 | if (!strcmp("Linux", interface)) { | ||
1184 | printk(KERN_WARNING PREFIX | ||
1185 | "System BIOS is requesting _OSI(Linux)\n"); | ||
1186 | #ifdef OSI_LINUX_ENABLED | ||
1187 | printk(KERN_WARNING PREFIX | ||
1188 | "Please test with \"acpi_osi=!Linux\"\n" | ||
1189 | "Please send dmidecode " | ||
1190 | "to linux-acpi@vger.kernel.org\n"); | ||
1191 | #else | ||
1192 | printk(KERN_WARNING PREFIX | ||
1193 | "If \"acpi_osi=Linux\" works better,\n" | ||
1194 | "Please send dmidecode " | ||
1195 | "to linux-acpi@vger.kernel.org\n"); | ||
1196 | #endif | ||
1197 | if(osi_linux) | ||
1198 | return AE_OK; | ||
1199 | } | ||
1155 | return AE_SUPPORT; | 1200 | return AE_SUPPORT; |
1156 | } | 1201 | } |
1157 | 1202 | ||
@@ -1181,5 +1226,51 @@ acpi_os_validate_address ( | |||
1181 | return AE_OK; | 1226 | return AE_OK; |
1182 | } | 1227 | } |
1183 | 1228 | ||
1229 | #ifdef CONFIG_DMI | ||
1230 | #ifdef OSI_LINUX_ENABLED | ||
1231 | static int dmi_osi_not_linux(struct dmi_system_id *d) | ||
1232 | { | ||
1233 | printk(KERN_NOTICE "%s detected: requires not _OSI(Linux)\n", d->ident); | ||
1234 | enable_osi_linux(0); | ||
1235 | return 0; | ||
1236 | } | ||
1237 | #else | ||
1238 | static int dmi_osi_linux(struct dmi_system_id *d) | ||
1239 | { | ||
1240 | printk(KERN_NOTICE "%s detected: requires _OSI(Linux)\n", d->ident); | ||
1241 | enable_osi_linux(1); | ||
1242 | return 0; | ||
1243 | } | ||
1244 | #endif | ||
1245 | |||
1246 | static struct dmi_system_id acpi_osl_dmi_table[] = { | ||
1247 | #ifdef OSI_LINUX_ENABLED | ||
1248 | /* | ||
1249 | * Boxes that need NOT _OSI(Linux) | ||
1250 | */ | ||
1251 | { | ||
1252 | .callback = dmi_osi_not_linux, | ||
1253 | .ident = "Toshiba Satellite P100", | ||
1254 | .matches = { | ||
1255 | DMI_MATCH(DMI_BOARD_VENDOR, "TOSHIBA"), | ||
1256 | DMI_MATCH(DMI_BOARD_NAME, "Satellite P100"), | ||
1257 | }, | ||
1258 | }, | ||
1259 | #else | ||
1260 | /* | ||
1261 | * Boxes that need _OSI(Linux) | ||
1262 | */ | ||
1263 | { | ||
1264 | .callback = dmi_osi_linux, | ||
1265 | .ident = "Intel Napa CRB", | ||
1266 | .matches = { | ||
1267 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), | ||
1268 | DMI_MATCH(DMI_BOARD_NAME, "MPAD-MSAE Customer Reference Boards"), | ||
1269 | }, | ||
1270 | }, | ||
1271 | #endif | ||
1272 | {} | ||
1273 | }; | ||
1274 | #endif /* CONFIG_DMI */ | ||
1184 | 1275 | ||
1185 | #endif | 1276 | #endif |