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
This commit is contained in:
commit
efc499f980
@ -1,5 +1,5 @@
|
||||
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);
|
||||
|
522
drivers/acpi/osi.c
Normal file
522
drivers/acpi/osi.c
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
* 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) { }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user