aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Renninger <trenn@suse.de>2012-09-30 18:23:54 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2012-09-30 21:03:23 -0400
commit53aac44c904abbad9f474f652f099de13b5c3563 (patch)
treebda5d70da49a83cb2c0c1dc2c35aae8d9235b782
parent8e30524dcc0d0ac1a18a5cee482b9d9cde3cb332 (diff)
ACPI: Store valid ACPI tables passed via early initrd in reserved memblock areas
A later patch will compare them with ACPI tables that get loaded at boot or runtime and if criteria match, a stored one is loaded. Signed-off-by: Thomas Renninger <trenn@suse.de> Link: http://lkml.kernel.org/r/1349043837-22659-4-git-send-email-trenn@suse.de Cc: Len Brown <lenb@kernel.org> Cc: Robert Moore <robert.moore@intel.com> Cc: Yinghai Lu <yinghai@kernel.org> Cc: Eric Piel <eric.piel@tremplin-utc.net> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/kernel/setup.c2
-rw-r--r--drivers/acpi/Kconfig9
-rw-r--r--drivers/acpi/osl.c122
-rw-r--r--include/linux/acpi.h8
4 files changed, 141 insertions, 0 deletions
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index f4b9b80e1b95..764e543b2297 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -941,6 +941,8 @@ void __init setup_arch(char **cmdline_p)
941 941
942 reserve_initrd(); 942 reserve_initrd();
943 943
944 acpi_initrd_override((void *)initrd_start, initrd_end - initrd_start);
945
944 reserve_crashkernel(); 946 reserve_crashkernel();
945 947
946 vsmp_init(); 948 vsmp_init();
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 80998958cf45..8cf719547b51 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -261,6 +261,15 @@ config ACPI_CUSTOM_DSDT
261 bool 261 bool
262 default ACPI_CUSTOM_DSDT_FILE != "" 262 default ACPI_CUSTOM_DSDT_FILE != ""
263 263
264config ACPI_INITRD_TABLE_OVERRIDE
265 bool "ACPI tables can be passed via uncompressed cpio in initrd"
266 default n
267 help
268 This option provides functionality to override arbitrary ACPI tables
269 via initrd. No functional change if no ACPI tables are passed via
270 initrd, therefore it's safe to say Y.
271 See Documentation/acpi/initrd_table_override.txt for details
272
264config ACPI_BLACKLIST_YEAR 273config ACPI_BLACKLIST_YEAR
265 int "Disable ACPI for systems before Jan 1st this year" if X86_32 274 int "Disable ACPI for systems before Jan 1st this year" if X86_32
266 default 0 275 default 0
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 9eaf708f5885..b20b07903218 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -534,6 +534,128 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
534 return AE_OK; 534 return AE_OK;
535} 535}
536 536
537#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
538#include <linux/earlycpio.h>
539#include <linux/memblock.h>
540
541static u64 acpi_tables_addr;
542static int all_tables_size;
543
544/* Copied from acpica/tbutils.c:acpi_tb_checksum() */
545u8 __init acpi_table_checksum(u8 *buffer, u32 length)
546{
547 u8 sum = 0;
548 u8 *end = buffer + length;
549
550 while (buffer < end)
551 sum = (u8) (sum + *(buffer++));
552 return sum;
553}
554
555/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
556static const char * const table_sigs[] = {
557 ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ,
558 ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT,
559 ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF,
560 ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET,
561 ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI,
562 ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA,
563 ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT,
564 ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT,
565 ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL };
566
567/* Non-fatal errors: Affected tables/files are ignored */
568#define INVALID_TABLE(x, path, name) \
569 { pr_err("ACPI OVERRIDE: " x " [%s%s]\n", path, name); continue; }
570
571#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
572
573/* Must not increase 10 or needs code modification below */
574#define ACPI_OVERRIDE_TABLES 10
575
576void __init acpi_initrd_override(void *data, size_t size)
577{
578 int sig, no, table_nr = 0, total_offset = 0;
579 long offset = 0;
580 struct acpi_table_header *table;
581 char cpio_path[32] = "kernel/firmware/acpi/";
582 struct cpio_data file;
583 struct cpio_data early_initrd_files[ACPI_OVERRIDE_TABLES];
584 char *p;
585
586 if (data == NULL || size == 0)
587 return;
588
589 for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) {
590 file = find_cpio_data(cpio_path, data, size, &offset);
591 if (!file.data)
592 break;
593
594 data += offset;
595 size -= offset;
596
597 if (file.size < sizeof(struct acpi_table_header))
598 INVALID_TABLE("Table smaller than ACPI header",
599 cpio_path, file.name);
600
601 table = file.data;
602
603 for (sig = 0; table_sigs[sig]; sig++)
604 if (!memcmp(table->signature, table_sigs[sig], 4))
605 break;
606
607 if (!table_sigs[sig])
608 INVALID_TABLE("Unknown signature",
609 cpio_path, file.name);
610 if (file.size != table->length)
611 INVALID_TABLE("File length does not match table length",
612 cpio_path, file.name);
613 if (acpi_table_checksum(file.data, table->length))
614 INVALID_TABLE("Bad table checksum",
615 cpio_path, file.name);
616
617 pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n",
618 table->signature, cpio_path, file.name, table->length);
619
620 all_tables_size += table->length;
621 early_initrd_files[table_nr].data = file.data;
622 early_initrd_files[table_nr].size = file.size;
623 table_nr++;
624 }
625 if (table_nr == 0)
626 return;
627
628 acpi_tables_addr =
629 memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
630 all_tables_size, PAGE_SIZE);
631 if (!acpi_tables_addr) {
632 WARN_ON(1);
633 return;
634 }
635 /*
636 * Only calling e820_add_reserve does not work and the
637 * tables are invalid (memory got used) later.
638 * memblock_reserve works as expected and the tables won't get modified.
639 * But it's not enough on X86 because ioremap will
640 * complain later (used by acpi_os_map_memory) that the pages
641 * that should get mapped are not marked "reserved".
642 * Both memblock_reserve and e820_add_region (via arch_reserve_mem_area)
643 * works fine.
644 */
645 memblock_reserve(acpi_tables_addr, acpi_tables_addr + all_tables_size);
646 arch_reserve_mem_area(acpi_tables_addr, all_tables_size);
647
648 p = early_ioremap(acpi_tables_addr, all_tables_size);
649
650 for (no = 0; no < table_nr; no++) {
651 memcpy(p + total_offset, early_initrd_files[no].data,
652 early_initrd_files[no].size);
653 total_offset += early_initrd_files[no].size;
654 }
655 early_iounmap(p, all_tables_size);
656}
657#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */
658
537acpi_status 659acpi_status
538acpi_os_table_override(struct acpi_table_header * existing_table, 660acpi_os_table_override(struct acpi_table_header * existing_table,
539 struct acpi_table_header ** new_table) 661 struct acpi_table_header ** new_table)
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 946fd1ea79ff..d14081c10c17 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -76,6 +76,14 @@ typedef int (*acpi_table_handler) (struct acpi_table_header *table);
76 76
77typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end); 77typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end);
78 78
79#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE
80void acpi_initrd_override(void *data, size_t size);
81#else
82static inline void acpi_initrd_override(void *data, size_t size)
83{
84}
85#endif
86
79char * __acpi_map_table (unsigned long phys_addr, unsigned long size); 87char * __acpi_map_table (unsigned long phys_addr, unsigned long size);
80void __acpi_unmap_table(char *map, unsigned long size); 88void __acpi_unmap_table(char *map, unsigned long size);
81int early_acpi_boot_init(void); 89int early_acpi_boot_init(void);