cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

osi.c (13373B)


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