Commit efc499f9 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branches 'acpi-numa', 'acpi-tables' and 'acpi-osi'

* acpi-numa:
  ACPI / SRAT: fix SRAT parsing order with both LAPIC and X2APIC present

* acpi-tables:
  ACPI / tables: Fix DSDT override mechanism
  ACPI / tables: Convert initrd table override to table upgrade mechanism
  ACPI / x86: Cleanup initrd related code
  ACPI / tables: Move table override mechanisms to tables.c

* acpi-osi:
  ACPI / osi: Collect _OSI handling into one single file
  ACPI / osi: Cleanup coding style issues before creating a separate OSI source file
  ACPI / osi: Cleanup OSI handling code to use bool
  ACPI / osi: Fix default _OSI(Darwin) support
  ACPI / osi: Add acpi_osi=!! to allow reverting acpi_osi=!
  ACPI / osi: Cleanup _OSI("Linux") related code before introducing new support
  ACPI / osi: Fix an issue that acpi_osi=!* cannot disable ACPICA internal strings

Conflicts:
	drivers/acpi/internal.h
Overriding ACPI tables via initrd
=================================
Upgrading ACPI tables via initrd
================================
1) Introduction (What is this about)
2) What is this for
......@@ -9,12 +9,14 @@ Overriding ACPI tables via initrd
1) What is this about
---------------------
If the ACPI_INITRD_TABLE_OVERRIDE compile option is true, it is possible to
override nearly any ACPI table provided by the BIOS with an instrumented,
modified one.
If the ACPI_TABLE_UPGRADE compile option is true, it is possible to
upgrade the ACPI execution environment that is defined by the ACPI tables
via upgrading the ACPI tables provided by the BIOS with an instrumented,
modified, more recent version one, or installing brand new ACPI tables.
For a full list of ACPI tables that can be overridden, take a look at
the char *table_sigs[MAX_ACPI_SIGNATURE]; definition in drivers/acpi/osl.c
For a full list of ACPI tables that can be upgraded/installed, take a look
at the char *table_sigs[MAX_ACPI_SIGNATURE]; definition in
drivers/acpi/tables.c.
All ACPI tables iasl (Intel's ACPI compiler and disassembler) knows should
be overridable, except:
- ACPI_SIG_RSDP (has a signature of 6 bytes)
......@@ -25,17 +27,20 @@ Both could get implemented as well.
2) What is this for
-------------------
Please keep in mind that this is a debug option.
ACPI tables should not get overridden for productive use.
If BIOS ACPI tables are overridden the kernel will get tainted with the
TAINT_OVERRIDDEN_ACPI_TABLE flag.
Complain to your platform/BIOS vendor if you find a bug which is so sever
that a workaround is not accepted in the Linux kernel.
Complain to your platform/BIOS vendor if you find a bug which is so severe
that a workaround is not accepted in the Linux kernel. And this facility
allows you to upgrade the buggy tables before your platform/BIOS vendor
releases an upgraded BIOS binary.
Still, it can and should be enabled in any kernel, because:
- There is no functional change with not instrumented initrds
- It provides a powerful feature to easily debug and test ACPI BIOS table
compatibility with the Linux kernel.
This facility can be used by platform/BIOS vendors to provide a Linux
compatible environment without modifying the underlying platform firmware.
This facility also provides a powerful feature to easily debug and test
ACPI BIOS table compatibility with the Linux kernel by modifying old
platform provided ACPI tables or inserting new ACPI tables.
It can and should be enabled in any kernel because there is no functional
change with not instrumented initrds.
3) How does it work
......@@ -50,23 +55,31 @@ iasl -d *.dat
# For example add this statement into a _PRT (PCI Routing Table) function
# of the DSDT:
Store("HELLO WORLD", debug)
# And increase the OEM Revision. For example, before modification:
DefinitionBlock ("DSDT.aml", "DSDT", 2, "INTEL ", "TEMPLATE", 0x00000000)
# After modification:
DefinitionBlock ("DSDT.aml", "DSDT", 2, "INTEL ", "TEMPLATE", 0x00000001)
iasl -sa dsdt.dsl
# Add the raw ACPI tables to an uncompressed cpio archive.
# They must be put into a /kernel/firmware/acpi directory inside the
# cpio archive.
# The uncompressed cpio archive must be the first.
# Other, typically compressed cpio archives, must be
# concatenated on top of the uncompressed one.
# They must be put into a /kernel/firmware/acpi directory inside the cpio
# archive. Note that if the table put here matches a platform table
# (similar Table Signature, and similar OEMID, and similar OEM Table ID)
# with a more recent OEM Revision, the platform table will be upgraded by
# this table. If the table put here doesn't match a platform table
# (dissimilar Table Signature, or dissimilar OEMID, or dissimilar OEM Table
# ID), this table will be appended.
mkdir -p kernel/firmware/acpi
cp dsdt.aml kernel/firmware/acpi
# A maximum of: #define ACPI_OVERRIDE_TABLES 10
# tables are currently allowed (see osl.c):
# A maximum of "NR_ACPI_INITRD_TABLES (64)" tables are currently allowed
# (see osl.c):
iasl -sa facp.dsl
iasl -sa ssdt1.dsl
cp facp.aml kernel/firmware/acpi
cp ssdt1.aml kernel/firmware/acpi
# Create the uncompressed cpio archive and concatenate the original initrd
# on top:
# The uncompressed cpio archive must be the first. Other, typically
# compressed cpio archives, must be concatenated on top of the uncompressed
# one. Following command creates the uncompressed cpio archive and
# concatenates the original initrd on top:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >>/boot/instrumented_initrd
# reboot with increased acpi debug level, e.g. boot params:
......
......@@ -312,6 +312,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
acpi_osi=!* # remove all strings
acpi_osi=! # disable all built-in OS vendor
strings
acpi_osi=!! # enable all built-in OS vendor
strings
acpi_osi= # disable all strings
'acpi_osi=!' can be used in combination with single or
......
......@@ -398,6 +398,11 @@ static void __init reserve_initrd(void)
memblock_free(ramdisk_image, ramdisk_end - ramdisk_image);
}
static void __init early_initrd_acpi_init(void)
{
early_acpi_table_init((void *)initrd_start, initrd_end - initrd_start);
}
#else
static void __init early_reserve_initrd(void)
{
......@@ -405,6 +410,9 @@ static void __init early_reserve_initrd(void)
static void __init reserve_initrd(void)
{
}
static void __init early_initrd_acpi_init(void)
{
}
#endif /* CONFIG_BLK_DEV_INITRD */
static void __init parse_setup_data(void)
......@@ -1138,9 +1146,7 @@ void __init setup_arch(char **cmdline_p)
reserve_initrd();
#if defined(CONFIG_ACPI) && defined(CONFIG_BLK_DEV_INITRD)
acpi_initrd_override((void *)initrd_start, initrd_end - initrd_start);
#endif
early_initrd_acpi_init();
vsmp_init();
......
......@@ -311,12 +311,12 @@ config ACPI_CUSTOM_DSDT
bool
default ACPI_CUSTOM_DSDT_FILE != ""
config ACPI_INITRD_TABLE_OVERRIDE
bool "ACPI tables override via initrd"
config ACPI_TABLE_UPGRADE
bool "Allow upgrading ACPI tables via initrd"
depends on BLK_DEV_INITRD && X86
default n
default y
help
This option provides functionality to override arbitrary ACPI tables
This option provides functionality to upgrade arbitrary ACPI tables
via initrd. No functional change if no ACPI tables are passed via
initrd, therefore it's safe to say Y.
See Documentation/acpi/initrd_table_override.txt for details
......
......@@ -18,7 +18,7 @@ obj-$(CONFIG_ACPI) += acpi.o \
acpica/
# All the builtin files are in the "acpi." module_param namespace.
acpi-y += osl.o utils.o reboot.o
acpi-y += osi.o osl.o utils.o reboot.o
acpi-y += nvs.o
# Power management related files
......
......@@ -3,7 +3,7 @@
*
* Check to see if the given machine has a known bad ACPI BIOS
* or if the BIOS is too old.
* Check given machine against acpi_osi_dmi_table[].
* Check given machine against acpi_rev_dmi_table[].
*
* Copyright (C) 2004 Len Brown <len.brown@intel.com>
* Copyright (C) 2002 Andy Grover <andrew.grover@intel.com>
......@@ -47,7 +47,7 @@ struct acpi_blacklist_item {
u32 is_critical_error;
};
static struct dmi_system_id acpi_osi_dmi_table[] __initdata;
static struct dmi_system_id acpi_rev_dmi_table[] __initdata;
/*
* POLICY: If *anything* doesn't work, put it on the blacklist.
......@@ -128,36 +128,12 @@ int __init acpi_blacklisted(void)
}
}
dmi_check_system(acpi_osi_dmi_table);
(void)early_acpi_osi_init();
dmi_check_system(acpi_rev_dmi_table);
return blacklisted;
}
#ifdef CONFIG_DMI
static int __init dmi_enable_osi_linux(const struct dmi_system_id *d)
{
acpi_dmi_osi_linux(1, d); /* enable */
return 0;
}
static int __init dmi_disable_osi_vista(const struct dmi_system_id *d)
{
printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
acpi_osi_setup("!Windows 2006");
acpi_osi_setup("!Windows 2006 SP1");
acpi_osi_setup("!Windows 2006 SP2");
return 0;
}
static int __init dmi_disable_osi_win7(const struct dmi_system_id *d)
{
printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
acpi_osi_setup("!Windows 2009");
return 0;
}
static int __init dmi_disable_osi_win8(const struct dmi_system_id *d)
{
printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
acpi_osi_setup("!Windows 2012");
return 0;
}
#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE
static int __init dmi_enable_rev_override(const struct dmi_system_id *d)
{
......@@ -168,169 +144,7 @@ static int __init dmi_enable_rev_override(const struct dmi_system_id *d)
}
#endif
static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
{
.callback = dmi_disable_osi_vista,
.ident = "Fujitsu Siemens",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
},
},
{
/*
* There have a NVIF method in MSI GX723 DSDT need call by Nvidia
* driver (e.g. nouveau) when user press brightness hotkey.
* Currently, nouveau driver didn't do the job and it causes there
* have a infinite while loop in DSDT when user press hotkey.
* We add MSI GX723's dmi information to this table for workaround
* this issue.
* Will remove MSI GX723 from the table after nouveau grows support.
*/
.callback = dmi_disable_osi_vista,
.ident = "MSI GX723",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "GX723"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Sony VGN-NS10J_S",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Sony VGN-SR290J",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "VGN-NS50B_L",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "VGN-SR19XN",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba Satellite L355",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"),
},
},
{
.callback = dmi_disable_osi_win7,
.ident = "ASUS K50IJ",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba P305D",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba NB100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "NB100"),
},
},
/*
* The wireless hotkey does not work on those machines when
* returning true for _OSI("Windows 2012")
*/
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 7737",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 7537",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 5437",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 3437",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Vostro 3446",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Vostro 3546",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
},
},
/*
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
* Linux ignores it, except for the machines enumerated below.
*/
/*
* Without this this EEEpc exports a non working WMI interface, with
* this it exports a working "good old" eeepc_laptop interface, fixing
* both brightness control, and rfkill not working.
*/
{
.callback = dmi_enable_osi_linux,
.ident = "Asus EEE PC 1015PX",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
},
},
static struct dmi_system_id acpi_rev_dmi_table[] __initdata = {
#ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE
/*
* DELL XPS 13 (2015) switches sound between HDA and I2S
......
......@@ -20,7 +20,8 @@
#define PREFIX "ACPI: "
void acpi_initrd_initialize_tables(void);
int early_acpi_osi_init(void);
int acpi_osi_init(void);
acpi_status acpi_os_initialize1(void);
void init_acpi_device_notify(void);
int acpi_scan_init(void);
......
......@@ -327,10 +327,18 @@ int __init acpi_numa_init(void)
/* SRAT: Static Resource Affinity Table */
if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
acpi_table_parse_srat(ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY,
acpi_parse_x2apic_affinity, 0);
acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY,
acpi_parse_processor_affinity, 0);
struct acpi_subtable_proc srat_proc[2];
memset(srat_proc, 0, sizeof(srat_proc));
srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY;
srat_proc[0].handler = acpi_parse_processor_affinity;
srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY;
srat_proc[1].handler = acpi_parse_x2apic_affinity;
acpi_table_parse_entries_array(ACPI_SIG_SRAT,
sizeof(struct acpi_table_srat),
srat_proc, ARRAY_SIZE(srat_proc), 0);
cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
acpi_parse_memory_affinity,
NR_NODE_MEMBLKS);
......
/*
* osi.c - _OSI implementation
*
* Copyright (C) 2016 Intel Corporation
* Author: Lv Zheng <lv.zheng@intel.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* Uncomment next line to get verbose printout */
/* #define DEBUG */
#define pr_fmt(fmt) "ACPI: " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include "internal.h"
#define OSI_STRING_LENGTH_MAX 64
#define OSI_STRING_ENTRIES_MAX 16
struct acpi_osi_entry {
char string[OSI_STRING_LENGTH_MAX];
bool enable;
};
static struct acpi_osi_config {
u8 default_disabling;
unsigned int linux_enable:1;
unsigned int linux_dmi:1;
unsigned int linux_cmdline:1;
unsigned int darwin_enable:1;
unsigned int darwin_dmi:1;
unsigned int darwin_cmdline:1;
} osi_config;
static struct acpi_osi_config osi_config;
static struct acpi_osi_entry
osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
{"Module Device", true},
{"Processor Device", true},
{"3.0 _SCP Extensions", true},
{"Processor Aggregator Device", true},
};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)
{
if (!strcmp("Linux", interface)) {
pr_notice_once(FW_BUG
"BIOS _OSI(Linux) query %s%s\n",
osi_config.linux_enable ? "honored" : "ignored",
osi_config.linux_cmdline ? " via cmdline" :
osi_config.linux_dmi ? " via DMI" : "");
}
if (!strcmp("Darwin", interface)) {
pr_notice_once(
"BIOS _OSI(Darwin) query %s%s\n",
osi_config.darwin_enable ? "honored" : "ignored",
osi_config.darwin_cmdline ? " via cmdline" :
osi_config.darwin_dmi ? " via DMI" : "");
}
return supported;
}
void __init acpi_osi_setup(char *str)
{
struct acpi_osi_entry *osi;
bool enable = true;
int i;
if (!acpi_gbl_create_osi_method)
return;
if (str == NULL || *str == '\0') {
pr_info("_OSI method disabled\n");
acpi_gbl_create_osi_method = FALSE;
return;
}
if (*str == '!') {
str++;
if (*str == '\0') {
/* Do not override acpi_osi=!* */
if (!osi_config.default_disabling)
osi_config.default_disabling =
ACPI_DISABLE_ALL_VENDOR_STRINGS;
return;
} else if (*str == '*') {
osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS;
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
osi->enable = false;
}
return;
} else if (*str == '!') {
osi_config.default_disabling = 0;
return;
}
enable = false;
}
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
if (!strcmp(osi->string, str)) {
osi->enable = enable;
break;
} else if (osi->string[0] == '\0') {
osi->enable = enable;
strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
break;
}
}
}
static void __init __acpi_osi_setup_darwin(bool enable)
{
osi_config.darwin_enable = !!enable;
if (enable) {
acpi_osi_setup("!");
acpi_osi_setup("Darwin");
} else {
acpi_osi_setup("!!");
acpi_osi_setup("!Darwin");
}
}
static void __init acpi_osi_setup_darwin(bool enable)
{
/* Override acpi_osi_dmi_blacklisted() */
osi_config.darwin_dmi = 0;
osi_config.darwin_cmdline = 1;
__acpi_osi_setup_darwin(enable);
}
/*
* The story of _OSI(Linux)
*
* From pre-history through Linux-2.6.22, Linux responded TRUE upon a BIOS
* OSI(Linux) query.
*
* Unfortunately, reference BIOS writers got wind of this and put
* OSI(Linux) in their example code, quickly exposing this string as
* ill-conceived and opening the door to an un-bounded number of BIOS
* incompatibilities.
*
* For example, OSI(Linux) was used on resume to re-POST a video card on
* one system, because Linux at that time could not do a speedy restore in
* its native driver. But then upon gaining quick native restore
* capability, Linux has no way to tell the BIOS to skip the time-consuming
* POST -- putting Linux at a permanent performance disadvantage. On
* another system, the BIOS writer used OSI(Linux) to infer native OS
* support for IPMI! On other systems, OSI(Linux) simply got in the way of
* Linux claiming to be compatible with other operating systems, exposing
* BIOS issues such as skipped device initialization.
*
* So "Linux" turned out to be a really poor chose of OSI string, and from
* Linux-2.6.23 onward we respond FALSE.
*
* BIOS writers should NOT query _OSI(Linux) on future systems. Linux will
* complain on the console when it sees it, and return FALSE. To get Linux
* to return TRUE for your system will require a kernel source update to
* add a DMI entry, or boot with "acpi_osi=Linux"
*/
static void __init __acpi_osi_setup_linux(bool enable)
{
osi_config.linux_enable = !!enable;
if (enable)
acpi_osi_setup("Linux");
else
acpi_osi_setup("!Linux");
}
static void __init acpi_osi_setup_linux(bool enable)
{
/* Override acpi_osi_dmi_blacklisted() */
osi_config.linux_dmi = 0;
osi_config.linux_cmdline = 1;
__acpi_osi_setup_linux(enable);
}
/*
* Modify the list of "OS Interfaces" reported to BIOS via _OSI
*
* empty string disables _OSI
* string starting with '!' disables that string
* otherwise string is added to list, augmenting built-in strings
*/
static void __init acpi_osi_setup_late(void)
{
struct acpi_osi_entry *osi;
char *str;
int i;
acpi_status status;
if (osi_config.default_disabling) {
status = acpi_update_interfaces(osi_config.default_disabling);
if (ACPI_SUCCESS(status))
pr_info("Disabled all _OSI OS vendors%s\n",
osi_config.default_disabling ==
ACPI_DISABLE_ALL_STRINGS ?
" and feature groups" : "");
}
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
str = osi->string;
if (*str == '\0')
break;
if (osi->enable) {
status = acpi_install_interface(str);
if (ACPI_SUCCESS(status))
pr_info("Added _OSI(%s)\n", str);
} else {
status = acpi_remove_interface(str);
if (ACPI_SUCCESS(status))
pr_info("Deleted _OSI(%s)\n", str);
}
}
}
static int __init osi_setup(char *str)
{
if (str && !strcmp("Linux", str))
acpi_osi_setup_linux(true);
else if (str && !strcmp("!Linux", str))
acpi_osi_setup_linux(false);
else if (str && !strcmp("Darwin", str))
acpi_osi_setup_darwin(true);
else if (str && !strcmp("!Darwin", str))
acpi_osi_setup_darwin(false);
else
acpi_osi_setup(str);
return 1;
}
__setup("acpi_osi=", osi_setup);
bool acpi_osi_is_win8(void)
{
return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
}
EXPORT_SYMBOL(acpi_osi_is_win8);
static void __init acpi_osi_dmi_darwin(bool enable,
const struct dmi_system_id *d)
{
pr_notice("DMI detected to setup _OSI(\"Darwin\"): %s\n", d->ident);
osi_config.darwin_dmi = 1;
__acpi_osi_setup_darwin(enable);
}
void __init acpi_osi_dmi_linux(bool enable, const struct dmi_system_id *d)
{
pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident);
osi_config.linux_dmi = 1;
__acpi_osi_setup_linux(enable);
}
static int __init dmi_enable_osi_darwin(const struct dmi_system_id *d)
{
acpi_osi_dmi_darwin(true, d);
return 0;
}
static int __init dmi_enable_osi_linux(const struct dmi_system_id *d)
{
acpi_osi_dmi_linux(true, d);
return 0;
}
static int __init dmi_disable_osi_vista(const struct dmi_system_id *d)
{
pr_notice("DMI detected: %s\n", d->ident);
acpi_osi_setup("!Windows 2006");
acpi_osi_setup("!Windows 2006 SP1");
acpi_osi_setup("!Windows 2006 SP2");
return 0;
}
static int __init dmi_disable_osi_win7(const struct dmi_system_id *d)
{
pr_notice("DMI detected: %s\n", d->ident);
acpi_osi_setup("!Windows 2009");
return 0;
}
static int __init dmi_disable_osi_win8(const struct dmi_system_id *d)
{
pr_notice("DMI detected: %s\n", d->ident);
acpi_osi_setup("!Windows 2012");
return 0;
}
/*
* Linux default _OSI response behavior is determined by this DMI table.
*
* Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden
* by acpi_osi=!Linux/acpi_osi=!Darwin command line options.
*/
static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
{
.callback = dmi_disable_osi_vista,
.ident = "Fujitsu Siemens",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
},
},
{
/*
* There have a NVIF method in MSI GX723 DSDT need call by Nvidia
* driver (e.g. nouveau) when user press brightness hotkey.
* Currently, nouveau driver didn't do the job and it causes there
* have a infinite while loop in DSDT when user press hotkey.
* We add MSI GX723's dmi information to this table for workaround
* this issue.
* Will remove MSI GX723 from the table after nouveau grows support.
*/
.callback = dmi_disable_osi_vista,
.ident = "MSI GX723",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "GX723"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Sony VGN-NS10J_S",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Sony VGN-SR290J",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "VGN-NS50B_L",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "VGN-SR19XN",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba Satellite L355",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"),
},
},
{
.callback = dmi_disable_osi_win7,
.ident = "ASUS K50IJ",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba P305D",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba NB100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "NB100"),
},
},
/*
* The wireless hotkey does not work on those machines when
* returning true for _OSI("Windows 2012")
*/
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 7737",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 7537",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 5437",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Inspiron 3437",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Vostro 3446",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
},
},
{
.callback = dmi_disable_osi_win8,
.ident = "Dell Vostro 3546",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
},
},
/*
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
* Linux ignores it, except for the machines enumerated below.
*/
/*
* Without this this EEEpc exports a non working WMI interface, with
* this it exports a working "good old" eeepc_laptop interface, fixing
* both brightness control, and rfkill not working.
*/
{
.callback = dmi_enable_osi_linux,
.ident = "Asus EEE PC 1015PX",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
},
},
/*
* Enable _OSI("Darwin") for all apple platforms.
*/
{
.callback = dmi_enable_osi_darwin,
.ident = "Apple hardware",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
},
},
{
.callback = dmi_enable_osi_darwin,
.ident = "Apple hardware",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."),
},
},
{}
};
static __init void acpi_osi_dmi_blacklisted(void)
{
dmi_check_system(acpi_osi_dmi_table);
}
int __init early_acpi_osi_init(void)
{
acpi_osi_dmi_blacklisted();
return 0;
}
int __init acpi_osi_init(void)
{
acpi_install_interface_handler(acpi_osi_handler);
acpi_osi_setup_late();
return 0;
}
......@@ -56,10 +56,6 @@ struct acpi_os_dpc {
struct work_struct work;
};
#ifdef CONFIG_ACPI_CUSTOM_DSDT
#include CONFIG_ACPI_CUSTOM_DSDT_FILE
#endif
#ifdef ENABLE_DEBUGGER
#include <linux/kdb.h>
......@@ -96,72 +92,6 @@ struct acpi_ioremap {
static LIST_HEAD(acpi_ioremaps);
static DEFINE_MUTEX(acpi_ioremap_lock);
static void __init acpi_osi_setup_late(void);
/*
* The story of _OSI(Linux)
*
* From pre-history through Linux-2.6.22,
* Linux responded TRUE upon a BIOS OSI(Linux) query.
*
* Unfortunately, reference BIOS writers got wind of this
* and put OSI(Linux) in their example code, quickly exposing
* this string as ill-conceived and opening the door to
* an un-bounded number of BIOS incompatibilities.
*
* For example, OSI(Linux) was used on resume to re-POST a
* video card on one system, because Linux at that time
* could not do a speedy restore in its native driver.
* But then upon gaining quick native restore capability,
* Linux has no way to tell the BIOS to skip the time-consuming
* POST -- putting Linux at a permanent performance disadvantage.
* On another system, the BIOS writer used OSI(Linux)
* to infer native OS support for IPMI! On other systems,
* OSI(Linux) simply got in the way of Linux claiming to
* be compatible with other operating systems, exposing
* BIOS issues such as skipped device initialization.
*
* So "Linux" turned out to be a really poor chose of
* OSI string, and from Linux-2.6.23 onward we respond FALSE.
*
* BIOS writers should NOT query _OSI(Linux) on future systems.
* Linux will complain on the console when it sees it, and return FALSE.
* To get Linux to return TRUE for your system will require
* a kernel source update to add a DMI entry,
* or boot with "acpi_osi=Linux"
*/
static struct osi_linux {
unsigned int enable:1;
unsigned int dmi:1;
unsigned int cmdline:1;
unsigned int default_disabling:1;
} osi_linux = {0, 0, 0, 0};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)
{
if (!strcmp("Linux", interface)) {
printk_once(KERN_NOTICE FW_BUG PREFIX
"BIOS _OSI(Linux) query %s%s\n",
osi_linux.enable ? "honored" : "ignored",
osi_linux.cmdline ? " via cmdline" :
osi_linux.dmi ? " via DMI" : "");
}
if (!strcmp("Darwin", interface)) {
/*
* Apple firmware will behave poorly if it receives positive
* answers to "Darwin" and any other OS. Respond positively
* to Darwin and then disable all other vendor strings.
*/
acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
supported = ACPI_UINT32_MAX;
}
return supported;
}
static void __init acpi_request_region (struct acpi_generic_address *gas,
unsigned int length, char *desc)
{
......@@ -602,280 +532,6 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
return AE_OK;
}
static void acpi_table_taint(struct acpi_table_header *table)
{
pr_warn(PREFIX
"Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
table->signature, table->oem_table_id);
add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
}
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
#include <linux/earlycpio.h>
#include <linux/memblock.h>
static u64 acpi_tables_addr;
static int all_tables_size;
/* Copied from acpica/tbutils.c:acpi_tb_checksum() */
static u8 __init acpi_table_checksum(u8 *buffer, u32 length)
{
u8 sum = 0;
u8 *end = buffer + length;
while (buffer < end)
sum = (u8) (sum + *(buffer++));
return sum;
}
/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
static const char * const table_sigs[] = {
ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ,
ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT,
ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF,
ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET,
ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI,
ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA,
ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT,
ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT,
ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL };
#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
#define ACPI_OVERRIDE_TABLES 64
static struct cpio_data __initdata acpi_initrd_files[ACPI_OVERRIDE_TABLES];
static DECLARE_BITMAP(acpi_initrd_installed, ACPI_OVERRIDE_TABLES);
#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT)
void __init acpi_initrd_override(void *data, size_t size)
{
int sig, no, table_nr = 0, total_offset = 0;
long offset = 0;
struct acpi_table_header *table;
char cpio_path[32] = "kernel/firmware/acpi/";
struct cpio_data file;
if (data == NULL || size == 0)
return;
for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
file = find_cpio_data(cpio_path, data, size, &offset);
if (!file.data)
break;
data += offset;
size -= offset;
if (file.size < sizeof(struct acpi_table_header)) {
pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n",
cpio_path, file.name);
continue;
}
table = file.data;
for (sig = 0; table_sigs[sig]; sig++)
if (!memcmp(table->signature, table_sigs[sig], 4))
break;
if (!table_sigs[sig]) {
pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n",
cpio_path, file.name);
continue;
}
if (file.size != table->length) {
pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n",
cpio_path, file.name);
continue;
}
if (acpi_table_checksum(file.data, table->length)) {
pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n",
cpio_path, file.name);
continue;
}
pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
table->signature, cpio_path, file.name, table->length);
all_tables_size += table->length;
acpi_initrd_files[table_nr].data = file.data;
acpi_initrd_files[table_nr].size = file.size;
table_nr++;
}
if (table_nr == 0)
return;
acpi_tables_addr =
memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
all_tables_size, PAGE_SIZE);
if (!acpi_tables_addr) {
WARN_ON(1);
return;
}
/*
* Only calling e820_add_reserve does not work and the
* tables are invalid (memory got used) later.
* memblock_reserve works as expected and the tables won't get modified.
* But it's not enough on X86 because ioremap will
* complain later (used by acpi_os_map_memory) that the pages
* that should get mapped are not marked "reserved".
* Both memblock_reserve and e820_add_region (via arch_reserve_mem_area)
* works fine.
*/
memblock_reserve(acpi_tables_addr, all_tables_size);
arch_reserve_mem_area(acpi_tables_addr, all_tables_size);
/*
* early_ioremap only can remap 256k one time. If we map all
* tables one time, we will hit the limit. Need to map chunks
* one by one during copying the same as that in relocate_initrd().
*/
for (no = 0; no < table_nr; no++) {
unsigned char *src_p = acpi_initrd_files[no].data;
phys_addr_t size = acpi_initrd_files[no].size;
phys_addr_t dest_addr = acpi_tables_addr + total_offset;
phys_addr_t slop, clen;
char *dest_p;
total_offset += size;
while (size) {
slop = dest_addr & ~PAGE_MASK;
clen = size;
if (clen > MAP_CHUNK_SIZE - slop)
clen = MAP_CHUNK_SIZE - slop;
dest_p = early_ioremap(dest_addr & PAGE_MASK,
clen + slop);
memcpy(dest_p + slop, src_p, clen);
early_iounmap(dest_p, clen + slop);
src_p += clen;
dest_addr += clen;
size -= clen;
}
}
}
acpi_status
acpi_os_physical_table_override(struct acpi_table_header *existing_table,
acpi_physical_address *address, u32 *length)
{
int table_offset = 0;
int table_index = 0;
struct acpi_table_header *table;
u32 table_length;
*length = 0;
*address = 0;
if (!acpi_tables_addr)
return AE_OK;
while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) {
table = acpi_os_map_memory(acpi_tables_addr + table_offset,
ACPI_HEADER_SIZE);
if (table_offset + table->length > all_tables_size) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
WARN_ON(1);
return AE_OK;
}
table_length = table->length;
/* Only override tables matched */
if (test_bit(table_index, acpi_initrd_installed) ||
memcmp(existing_table->signature, table->signature, 4) ||
memcmp(table->oem_table_id, existing_table->oem_table_id,
ACPI_OEM_TABLE_ID_SIZE)) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
goto next_table;
}
*length = table_length;
*address = acpi_tables_addr + table_offset;
acpi_table_taint(existing_table);
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
set_bit(table_index, acpi_initrd_installed);
break;
next_table:
table_offset += table_length;
table_index++;
}
return AE_OK;
}
void __init acpi_initrd_initialize_tables(void)
{
int table_offset = 0;
int table_index = 0;
u32 table_length;
struct acpi_table_header *table;
if (!acpi_tables_addr)
return;
while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) {
table = acpi_os_map_memory(acpi_tables_addr + table_offset,
ACPI_HEADER_SIZE);
if (table_offset + table->length > all_tables_size) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
WARN_ON(1);
return;
}
table_length = table->length;
/* Skip RSDT/XSDT which should only be used for override */
if (test_bit(table_index, acpi_initrd_installed) ||
ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) ||
ACPI_COMPARE_NAME(table->signature, ACPI_SIG_XSDT)) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
goto next_table;
}
acpi_table_taint(table);
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
acpi_install_table(acpi_tables_addr + table_offset, TRUE);
set_bit(table_index, acpi_initrd_installed);
next_table:
table_offset += table_length;
table_index++;
}
}
#else
acpi_status
acpi_os_physical_table_override(struct acpi_table_header *existing_table,
acpi_physical_address *address,
u32 *table_length)
{
*table_length = 0;
*address = 0;
return AE_OK;
}
void __init acpi_initrd_initialize_tables(void)
{
}
#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */
acpi_status
acpi_os_table_override(struct acpi_table_header *existing_table,
struct acpi_table_header **new_table)
{
if (!existing_table || !new_table)
return AE_BAD_PARAMETER;
*new_table = NULL;
#ifdef CONFIG_ACPI_CUSTOM_DSDT
if (strncmp(existing_table->signature, "DSDT", 4) == 0)
*new_table = (struct acpi_table_header *)AmlCode;
#endif
if (*new_table != NULL)
acpi_table_taint(existing_table);
return AE_OK;
}
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
u32 handled;
......@@ -1717,156 +1373,6 @@ static int __init acpi_os_name_setup(char *str)
__setup("acpi_os_name=", acpi_os_name_setup);
#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */
#define OSI_STRING_ENTRIES_MAX 16 /* arbitrary */
struct osi_setup_entry {
char string[OSI_STRING_LENGTH_MAX];
bool enable;
};
static struct osi_setup_entry
osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = {
{"Module Device", true},
{"Processor Device", true},
{"3.0 _SCP Extensions", true},
{"Processor Aggregator Device", true},
};
void __init acpi_osi_setup(char *str)
{
struct osi_setup_entry *osi;
bool enable = true;
int i;
if (!acpi_gbl_create_osi_method)
return;
if (str == NULL || *str == '\0') {
printk(KERN_INFO PREFIX "_OSI method disabled\n");
acpi_gbl_create_osi_method = FALSE;
return;
}
if (*str == '!') {
str++;
if (*str == '\0') {
osi_linux.default_disabling = 1;
return;
} else if (*str == '*') {
acpi_update_interfaces(ACPI_DISABLE_ALL_STRINGS);
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
osi->enable = false;
}
return;
}
enable = false;
}
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
if (!strcmp(osi->string, str)) {
osi->enable = enable;
break;
} else if (osi->string[0] == '\0') {
osi->enable = enable;
strncpy(osi->string, str, OSI_STRING_LENGTH_MAX);
break;
}
}
}
static void __init set_osi_linux(unsigned int enable)
{
if (osi_linux.enable != enable)
osi_linux.enable = enable;
if (osi_linux.enable)
acpi_osi_setup("Linux");
else
acpi_osi_setup("!Linux");
return;
}
static void __init acpi_cmdline_osi_linux(unsigned int enable)
{
osi_linux.cmdline = 1; /* cmdline set the default and override DMI */
osi_linux.dmi = 0;
set_osi_linux(enable);
return;
}
void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d)
{
printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident);
if (enable == -1)
return;
osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */
set_osi_linux(enable);
return;
}
/*
* Modify the list of "OS Interfaces" reported to BIOS via _OSI
*
* empty string disables _OSI
* string starting with '!' disables that string
* otherwise string is added to list, augmenting built-in strings
*/
static void __init acpi_osi_setup_late(void)
{
struct osi_setup_entry *osi;
char *str;
int i;
acpi_status status;
if (osi_linux.default_disabling) {
status = acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
if (ACPI_SUCCESS(status))
printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors\n");
}
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
str = osi->string;
if (*str == '\0')
break;
if (osi->enable) {
status = acpi_install_interface(str);
if (ACPI_SUCCESS(status))
printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str);
} else {
status = acpi_remove_interface(str);
if (ACPI_SUCCESS(status))
printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str);
}
}
}
static int __init osi_setup(char *str)
{
if (str && !strcmp("Linux", str))
acpi_cmdline_osi_linux(1);
else if (str && !strcmp("!Linux", str))
acpi_cmdline_osi_linux(0);
else
acpi_osi_setup(str);
return 1;
}
__setup("acpi_osi=", osi_setup);
/*
* Disable the auto-serialization of named objects creation methods.
*
......@@ -1986,12 +1492,6 @@ int acpi_resources_are_enforced(void)
}
EXPORT_SYMBOL(acpi_resources_are_enforced);
bool acpi_osi_is_win8(void)
{
return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
}
EXPORT_SYMBOL(acpi_osi_is_win8);
/*
* Deallocate the memory for a spinlock.
*/
......@@ -2157,8 +1657,7 @@ acpi_status __init acpi_os_initialize1(void)
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
BUG_ON(!kacpi_hotplug_wq);
acpi_install_interface_handler(acpi_osi_handler);
acpi_osi_setup_late();
acpi_osi_init();
return AE_OK;
}
......
......@@ -32,8 +32,14 @@
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/bootmem.h>
#include <linux/earlycpio.h>
#include <linux/memblock.h>
#include "internal.h"
#ifdef CONFIG_ACPI_CUSTOM_DSDT
#include CONFIG_ACPI_CUSTOM_DSDT_FILE
#endif
#define ACPI_MAX_TABLES 128
static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" };
......@@ -433,6 +439,314 @@ static void __init check_multiple_madt(void)
return;
}
static void acpi_table_taint(struct acpi_table_header *table)
{
pr_warn("Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n",
table->signature, table->oem_table_id);
add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE);
}
#ifdef CONFIG_ACPI_TABLE_UPGRADE
static u64 acpi_tables_addr;
static int all_tables_size;
/* Copied from acpica/tbutils.c:acpi_tb_checksum() */
static u8 __init acpi_table_checksum(u8 *buffer, u32 length)
{
u8 sum = 0;
u8 *end = buffer + length;
while (buffer < end)
sum = (u8) (sum + *(buffer++));
return sum;
}
/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
static const char * const table_sigs[] = {
ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ,
ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT,
ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF,
ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET,
ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI,
ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA,
ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT,
ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT,
ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL };
#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
#define NR_ACPI_INITRD_TABLES 64
static struct cpio_data __initdata acpi_initrd_files[NR_ACPI_INITRD_TABLES];
static DECLARE_BITMAP(acpi_initrd_installed, NR_ACPI_INITRD_TABLES);
#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT)
static void __init acpi_table_initrd_init(void *data, size_t size)
{
int sig, no, table_nr = 0, total_offset = 0;
long offset = 0;
struct acpi_table_header *table;
char cpio_path[32] = "kernel/firmware/acpi/";
struct cpio_data file;
if (data == NULL || size == 0)
return;
for (no = 0; no < NR_ACPI_INITRD_TABLES; no++) {
file = find_cpio_data(cpio_path, data, size, &offset);
if (!file.data)
break;
data += offset;
size -= offset;
if (file.size < sizeof(struct acpi_table_header)) {
pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n",
cpio_path, file.name);
continue;
}
table = file.data;
for (sig = 0; table_sigs[sig]; sig++)
if (!memcmp(table->signature, table_sigs[sig], 4))
break;
if (!table_sigs[sig]) {
pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n",
cpio_path, file.name);
continue;
}
if (file.size != table->length) {
pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n",
cpio_path, file.name);
continue;
}
if (acpi_table_checksum(file.data, table->length)) {
pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n",
cpio_path, file.name);
continue;
}
pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
table->signature, cpio_path, file.name, table->length);
all_tables_size += table->length;
acpi_initrd_files[table_nr].data = file.data;
acpi_initrd_files[table_nr].size = file.size;
table_nr++;
}
if (table_nr == 0)
return;
acpi_tables_addr =
memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
all_tables_size, PAGE_SIZE);
if (!acpi_tables_addr) {
WARN_ON(1);
return;
}
/*
* Only calling e820_add_reserve does not work and the
* tables are invalid (memory got used) later.
* memblock_reserve works as expected and the tables won't get modified.
* But it's not enough on X86 because ioremap will
* complain later (used by acpi_os_map_memory) that the pages
* that should get mapped are not marked "reserved".
* Both memblock_reserve and e820_add_region (via arch_reserve_mem_area)
* works fine.
*/
memblock_reserve(acpi_tables_addr, all_tables_size);
arch_reserve_mem_area(acpi_tables_addr, all_tables_size);
/*
* early_ioremap only can remap 256k one time. If we map all
* tables one time, we will hit the limit. Need to map chunks
* one by one during copying the same as that in relocate_initrd().
*/
for (no = 0; no < table_nr; no++) {
unsigned char *src_p = acpi_initrd_files[no].data;
phys_addr_t size = acpi_initrd_files[no].size;
phys_addr_t dest_addr = acpi_tables_addr + total_offset;
phys_addr_t slop, clen;
char *dest_p;
total_offset += size;
while (size) {
slop = dest_addr & ~PAGE_MASK;
clen = size;
if (clen > MAP_CHUNK_SIZE - slop)
clen = MAP_CHUNK_SIZE - slop;
dest_p = early_ioremap(dest_addr & PAGE_MASK,
clen + slop);
memcpy(dest_p + slop, src_p, clen);
early_iounmap(dest_p, clen + slop);
src_p += clen;
dest_addr += clen;
size -= clen;
}
}
}
static acpi_status
acpi_table_initrd_override(struct acpi_table_header *existing_table,
acpi_physical_address *address, u32 *length)
{
int table_offset = 0;
int table_index = 0;
struct acpi_table_header *table;
u32 table_length;
*length = 0;
*address = 0;
if (!acpi_tables_addr)
return AE_OK;
while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) {
table = acpi_os_map_memory(acpi_tables_addr + table_offset,
ACPI_HEADER_SIZE);
if (table_offset + table->length > all_tables_size) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
WARN_ON(1);
return AE_OK;
}
table_length = table->length;
/* Only override tables matched */
if (memcmp(existing_table->signature, table->signature, 4) ||
memcmp(table->oem_id, existing_table->oem_id,
ACPI_OEM_ID_SIZE) ||
memcmp(table->oem_table_id, existing_table->oem_table_id,
ACPI_OEM_TABLE_ID_SIZE)) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
goto next_table;
}
/*
* Mark the table to avoid being used in
* acpi_table_initrd_scan() and check the revision.
*/
if (test_and_set_bit(table_index, acpi_initrd_installed) ||
existing_table->oem_revision >= table->oem_revision) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
goto next_table;
}
*length = table_length;
*address = acpi_tables_addr + table_offset;
pr_info("Table Upgrade: override [%4.4s-%6.6s-%8.8s]\n",
table->signature, table->oem_id,
table->oem_table_id);
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
break;
next_table:
table_offset += table_length;
table_index++;
}
return AE_OK;
}
static void __init acpi_table_initrd_scan(void)
{
int table_offset = 0;
int table_index = 0;
u32 table_length;
struct acpi_table_header *table;
if (!acpi_tables_addr)
return;
while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) {
table = acpi_os_map_memory(acpi_tables_addr + table_offset,
ACPI_HEADER_SIZE);
if (table_offset + table->length > all_tables_size) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
WARN_ON(1);
return;
}
table_length = table->length;
/* Skip RSDT/XSDT which should only be used for override */
if (ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) ||
ACPI_COMPARE_NAME(table->signature, ACPI_SIG_XSDT)) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
goto next_table;
}
/*
* Mark the table to avoid being used in
* acpi_table_initrd_override(). Though this is not possible
* because override is disabled in acpi_install_table().
*/
if (test_and_set_bit(table_index, acpi_initrd_installed)) {
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
goto next_table;
}
pr_info("Table Upgrade: install [%4.4s-%6.6s-%8.8s]\n",
table->signature, table->oem_id,
table->oem_table_id);
acpi_os_unmap_memory(table, ACPI_HEADER_SIZE);
acpi_install_table(acpi_tables_addr + table_offset, TRUE);
next_table:
table_offset += table_length;
table_index++;
}
}
#else
static void __init acpi_table_initrd_init(void *data, size_t size)
{
}
static acpi_status
acpi_table_initrd_override(struct acpi_table_header *existing_table,
acpi_physical_address *address,
u32 *table_length)
{
*table_length = 0;
*address = 0;
return AE_OK;
}
static void __init acpi_table_initrd_scan(void)
{
}
#endif /* CONFIG_ACPI_TABLE_UPGRADE */
acpi_status
acpi_os_physical_table_override(struct acpi_table_header *existing_table,
acpi_physical_address *address,
u32 *table_length)
{
return acpi_table_initrd_override(existing_table, address,
table_length);
}
acpi_status
acpi_os_table_override(struct acpi_table_header *existing_table,
struct acpi_table_header **new_table)
{
if (!existing_table || !new_table)
return AE_BAD_PARAMETER;
*new_table = NULL;
#ifdef CONFIG_ACPI_CUSTOM_DSDT
if (strncmp(existing_table->signature, "DSDT", 4) == 0)
*new_table = (struct acpi_table_header *)AmlCode;
#endif
if (*new_table != NULL)
acpi_table_taint(existing_table);
return AE_OK;
}
void __init early_acpi_table_init(void *data, size_t size)
{
acpi_table_initrd_init(data, size);
}
/*
* acpi_table_init()
*
......@@ -457,7 +771,7 @@ int __init acpi_table_init(void)
status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0);
if (ACPI_FAILURE(status))
return -EINVAL;
acpi_initrd_initialize_tables();
acpi_table_initrd_scan();
check_multiple_madt();
return 0;
......
......@@ -190,14 +190,6 @@ static inline int acpi_debugger_notify_command_complete(void)
}
#endif
#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
void acpi_initrd_override(void *data, size_t size);
#else
static inline void acpi_initrd_override(void *data, size_t size)
{
}
#endif
#define BAD_MADT_ENTRY(entry, end) ( \
(!entry) || (unsigned long)entry + sizeof(*entry) > end || \
((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
......@@ -216,6 +208,7 @@ void acpi_boot_table_init (void);
int acpi_mps_check (void);
int acpi_numa_init (void);
void early_acpi_table_init(void *data, size_t size);
int acpi_table_init (void);
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
int __init acpi_parse_entries(char *id, unsigned long table_size,
......@@ -360,7 +353,6 @@ extern bool wmi_has_guid(const char *guid);
extern char acpi_video_backlight_string[];
extern long acpi_is_video_device(acpi_handle handle);
extern int acpi_blacklisted(void);
extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d);
extern void acpi_osi_setup(char *str);
extern bool acpi_osi_is_win8(void);
......@@ -597,6 +589,7 @@ static inline const char *acpi_dev_name(struct acpi_device *adev)
return NULL;
}
static inline void early_acpi_table_init(void *data, size_t size) { }
static inline void acpi_early_init(void) { }
static inline void acpi_subsystem_init(void) { }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment