File Index
Files and directories
Sign in
FILE

osi.c

File preview with syntax highlighting.

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