diff options
author | Thomas Renninger <trenn@suse.de> | 2012-09-30 18:23:54 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2012-09-30 21:03:23 -0400 |
commit | 53aac44c904abbad9f474f652f099de13b5c3563 (patch) | |
tree | bda5d70da49a83cb2c0c1dc2c35aae8d9235b782 | |
parent | 8e30524dcc0d0ac1a18a5cee482b9d9cde3cb332 (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.c | 2 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 9 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 122 | ||||
-rw-r--r-- | include/linux/acpi.h | 8 |
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 | ||
264 | config 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 | |||
264 | config ACPI_BLACKLIST_YEAR | 273 | config 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 | |||
541 | static u64 acpi_tables_addr; | ||
542 | static int all_tables_size; | ||
543 | |||
544 | /* Copied from acpica/tbutils.c:acpi_tb_checksum() */ | ||
545 | u8 __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: */ | ||
556 | static 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 | |||
576 | void __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 | |||
537 | acpi_status | 659 | acpi_status |
538 | acpi_os_table_override(struct acpi_table_header * existing_table, | 660 | acpi_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 | ||
77 | typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end); | 77 | typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end); |
78 | 78 | ||
79 | #ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE | ||
80 | void acpi_initrd_override(void *data, size_t size); | ||
81 | #else | ||
82 | static inline void acpi_initrd_override(void *data, size_t size) | ||
83 | { | ||
84 | } | ||
85 | #endif | ||
86 | |||
79 | char * __acpi_map_table (unsigned long phys_addr, unsigned long size); | 87 | char * __acpi_map_table (unsigned long phys_addr, unsigned long size); |
80 | void __acpi_unmap_table(char *map, unsigned long size); | 88 | void __acpi_unmap_table(char *map, unsigned long size); |
81 | int early_acpi_boot_init(void); | 89 | int early_acpi_boot_init(void); |