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