diff options
| -rw-r--r-- | Documentation/acpi/initrd_table_override.txt | 94 | ||||
| -rw-r--r-- | arch/x86/kernel/acpi/boot.c | 6 | ||||
| -rw-r--r-- | arch/x86/kernel/setup.c | 4 | ||||
| -rw-r--r-- | drivers/acpi/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/acpi/osl.c | 202 | ||||
| -rw-r--r-- | include/linux/acpi.h | 16 | ||||
| -rw-r--r-- | include/linux/earlycpio.h | 17 | ||||
| -rw-r--r-- | lib/Makefile | 3 | ||||
| -rw-r--r-- | lib/earlycpio.c | 145 |
9 files changed, 484 insertions, 12 deletions
diff --git a/Documentation/acpi/initrd_table_override.txt b/Documentation/acpi/initrd_table_override.txt new file mode 100644 index 000000000000..35c3f5415476 --- /dev/null +++ b/Documentation/acpi/initrd_table_override.txt | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | Overriding ACPI tables via initrd | ||
| 2 | ================================= | ||
| 3 | |||
| 4 | 1) Introduction (What is this about) | ||
| 5 | 2) What is this for | ||
| 6 | 3) How does it work | ||
| 7 | 4) References (Where to retrieve userspace tools) | ||
| 8 | |||
| 9 | 1) What is this about | ||
| 10 | --------------------- | ||
| 11 | |||
| 12 | If the ACPI_INITRD_TABLE_OVERRIDE compile option is true, it is possible to | ||
| 13 | override nearly any ACPI table provided by the BIOS with an instrumented, | ||
| 14 | modified one. | ||
| 15 | |||
| 16 | For a full list of ACPI tables that can be overridden, take a look at | ||
| 17 | the char *table_sigs[MAX_ACPI_SIGNATURE]; definition in drivers/acpi/osl.c | ||
| 18 | All ACPI tables iasl (Intel's ACPI compiler and disassembler) knows should | ||
| 19 | be overridable, except: | ||
| 20 | - ACPI_SIG_RSDP (has a signature of 6 bytes) | ||
| 21 | - ACPI_SIG_FACS (does not have an ordinary ACPI table header) | ||
| 22 | Both could get implemented as well. | ||
| 23 | |||
| 24 | |||
| 25 | 2) What is this for | ||
| 26 | ------------------- | ||
| 27 | |||
| 28 | Please keep in mind that this is a debug option. | ||
| 29 | ACPI tables should not get overridden for productive use. | ||
| 30 | If BIOS ACPI tables are overridden the kernel will get tainted with the | ||
| 31 | TAINT_OVERRIDDEN_ACPI_TABLE flag. | ||
| 32 | Complain to your platform/BIOS vendor if you find a bug which is so sever | ||
| 33 | that a workaround is not accepted in the Linux kernel. | ||
| 34 | |||
| 35 | Still, it can and should be enabled in any kernel, because: | ||
| 36 | - There is no functional change with not instrumented initrds | ||
| 37 | - It provides a powerful feature to easily debug and test ACPI BIOS table | ||
| 38 | compatibility with the Linux kernel. | ||
| 39 | |||
| 40 | |||
| 41 | 3) How does it work | ||
| 42 | ------------------- | ||
| 43 | |||
| 44 | # Extract the machine's ACPI tables: | ||
| 45 | cd /tmp | ||
| 46 | acpidump >acpidump | ||
| 47 | acpixtract -a acpidump | ||
| 48 | # Disassemble, modify and recompile them: | ||
| 49 | iasl -d *.dat | ||
| 50 | # For example add this statement into a _PRT (PCI Routing Table) function | ||
| 51 | # of the DSDT: | ||
| 52 | Store("HELLO WORLD", debug) | ||
| 53 | iasl -sa dsdt.dsl | ||
| 54 | # Add the raw ACPI tables to an uncompressed cpio archive. | ||
| 55 | # They must be put into a /kernel/firmware/acpi directory inside the | ||
| 56 | # cpio archive. | ||
| 57 | # The uncompressed cpio archive must be the first. | ||
| 58 | # Other, typically compressed cpio archives, must be | ||
| 59 | # concatenated on top of the uncompressed one. | ||
| 60 | mkdir -p kernel/firmware/acpi | ||
| 61 | cp dsdt.aml kernel/firmware/acpi | ||
| 62 | # A maximum of: #define ACPI_OVERRIDE_TABLES 10 | ||
| 63 | # tables are currently allowed (see osl.c): | ||
| 64 | iasl -sa facp.dsl | ||
| 65 | iasl -sa ssdt1.dsl | ||
| 66 | cp facp.aml kernel/firmware/acpi | ||
| 67 | cp ssdt1.aml kernel/firmware/acpi | ||
| 68 | # Create the uncompressed cpio archive and concatenate the original initrd | ||
| 69 | # on top: | ||
| 70 | find kernel | cpio -H newc --create > /boot/instrumented_initrd | ||
| 71 | cat /boot/initrd >>/boot/instrumented_initrd | ||
| 72 | # reboot with increased acpi debug level, e.g. boot params: | ||
| 73 | acpi.debug_level=0x2 acpi.debug_layer=0xFFFFFFFF | ||
| 74 | # and check your syslog: | ||
| 75 | [ 1.268089] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0._PRT] | ||
| 76 | [ 1.272091] [ACPI Debug] String [0x0B] "HELLO WORLD" | ||
| 77 | |||
| 78 | iasl is able to disassemble and recompile quite a lot different, | ||
| 79 | also static ACPI tables. | ||
| 80 | |||
| 81 | |||
| 82 | 4) Where to retrieve userspace tools | ||
| 83 | ------------------------------------ | ||
| 84 | |||
| 85 | iasl and acpixtract are part of Intel's ACPICA project: | ||
| 86 | http://acpica.org/ | ||
| 87 | and should be packaged by distributions (for example in the acpica package | ||
| 88 | on SUSE). | ||
| 89 | |||
| 90 | acpidump can be found in Len Browns pmtools: | ||
| 91 | ftp://kernel.org/pub/linux/kernel/people/lenb/acpi/utils/pmtools/acpidump | ||
| 92 | This tool is also part of the acpica package on SUSE. | ||
| 93 | Alternatively, used ACPI tables can be retrieved via sysfs in latest kernels: | ||
| 94 | /sys/firmware/acpi/tables | ||
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index e48cafcf92ae..bacf4b0d91f4 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c | |||
| @@ -1706,3 +1706,9 @@ int __acpi_release_global_lock(unsigned int *lock) | |||
| 1706 | } while (unlikely (val != old)); | 1706 | } while (unlikely (val != old)); |
| 1707 | return old & 0x1; | 1707 | return old & 0x1; |
| 1708 | } | 1708 | } |
| 1709 | |||
| 1710 | void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) | ||
| 1711 | { | ||
| 1712 | e820_add_region(addr, size, E820_ACPI); | ||
| 1713 | update_e820(); | ||
| 1714 | } | ||
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index c228322ca180..23ddd558fbd5 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c | |||
| @@ -952,6 +952,10 @@ void __init setup_arch(char **cmdline_p) | |||
| 952 | 952 | ||
| 953 | reserve_initrd(); | 953 | reserve_initrd(); |
| 954 | 954 | ||
| 955 | #if defined(CONFIG_ACPI) && defined(CONFIG_BLK_DEV_INITRD) | ||
| 956 | acpi_initrd_override((void *)initrd_start, initrd_end - initrd_start); | ||
| 957 | #endif | ||
| 958 | |||
| 955 | reserve_crashkernel(); | 959 | reserve_crashkernel(); |
| 956 | 960 | ||
| 957 | vsmp_init(); | 961 | vsmp_init(); |
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 0300bf612946..38c5078da11d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
| @@ -267,6 +267,15 @@ config ACPI_CUSTOM_DSDT | |||
| 267 | bool | 267 | bool |
| 268 | default ACPI_CUSTOM_DSDT_FILE != "" | 268 | default ACPI_CUSTOM_DSDT_FILE != "" |
| 269 | 269 | ||
| 270 | config ACPI_INITRD_TABLE_OVERRIDE | ||
| 271 | bool "ACPI tables can be passed via uncompressed cpio in initrd" | ||
| 272 | default n | ||
| 273 | help | ||
| 274 | This option provides functionality to override arbitrary ACPI tables | ||
| 275 | via initrd. No functional change if no ACPI tables are passed via | ||
| 276 | initrd, therefore it's safe to say Y. | ||
| 277 | See Documentation/acpi/initrd_table_override.txt for details | ||
| 278 | |||
| 270 | config ACPI_BLACKLIST_YEAR | 279 | config ACPI_BLACKLIST_YEAR |
| 271 | int "Disable ACPI for systems before Jan 1st this year" if X86_32 | 280 | int "Disable ACPI for systems before Jan 1st this year" if X86_32 |
| 272 | default 0 | 281 | default 0 |
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 6dc4a2b1e956..3ff267861541 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
| @@ -534,6 +534,137 @@ 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 | |||
| 659 | static void acpi_table_taint(struct acpi_table_header *table) | ||
| 660 | { | ||
| 661 | pr_warn(PREFIX | ||
| 662 | "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n", | ||
| 663 | table->signature, table->oem_table_id); | ||
| 664 | add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); | ||
| 665 | } | ||
| 666 | |||
| 667 | |||
| 537 | acpi_status | 668 | acpi_status |
| 538 | acpi_os_table_override(struct acpi_table_header * existing_table, | 669 | acpi_os_table_override(struct acpi_table_header * existing_table, |
| 539 | struct acpi_table_header ** new_table) | 670 | struct acpi_table_header ** new_table) |
| @@ -547,24 +678,73 @@ acpi_os_table_override(struct acpi_table_header * existing_table, | |||
| 547 | if (strncmp(existing_table->signature, "DSDT", 4) == 0) | 678 | if (strncmp(existing_table->signature, "DSDT", 4) == 0) |
| 548 | *new_table = (struct acpi_table_header *)AmlCode; | 679 | *new_table = (struct acpi_table_header *)AmlCode; |
| 549 | #endif | 680 | #endif |
| 550 | if (*new_table != NULL) { | 681 | if (*new_table != NULL) |
| 551 | printk(KERN_WARNING PREFIX "Override [%4.4s-%8.8s], " | 682 | acpi_table_taint(existing_table); |
| 552 | "this is unsafe: tainting kernel\n", | ||
| 553 | existing_table->signature, | ||
| 554 | existing_table->oem_table_id); | ||
| 555 | add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); | ||
| 556 | } | ||
| 557 | return AE_OK; | 683 | return AE_OK; |
| 558 | } | 684 | } |
| 559 | 685 | ||
| 560 | acpi_status | 686 | acpi_status |
| 561 | acpi_os_physical_table_override(struct acpi_table_header *existing_table, | 687 | acpi_os_physical_table_override(struct acpi_table_header *existing_table, |
| 562 | acpi_physical_address * new_address, | 688 | acpi_physical_address *address, |
| 563 | u32 *new_table_length) | 689 | u32 *table_length) |
| 564 | { | 690 | { |
| 565 | return AE_SUPPORT; | 691 | #ifndef CONFIG_ACPI_INITRD_TABLE_OVERRIDE |
| 566 | } | 692 | *table_length = 0; |
| 693 | *address = 0; | ||
| 694 | return AE_OK; | ||
| 695 | #else | ||
| 696 | int table_offset = 0; | ||
| 697 | struct acpi_table_header *table; | ||
| 698 | |||
| 699 | *table_length = 0; | ||
| 700 | *address = 0; | ||
| 701 | |||
| 702 | if (!acpi_tables_addr) | ||
| 703 | return AE_OK; | ||
| 704 | |||
| 705 | do { | ||
| 706 | if (table_offset + ACPI_HEADER_SIZE > all_tables_size) { | ||
| 707 | WARN_ON(1); | ||
| 708 | return AE_OK; | ||
| 709 | } | ||
| 567 | 710 | ||
| 711 | table = acpi_os_map_memory(acpi_tables_addr + table_offset, | ||
| 712 | ACPI_HEADER_SIZE); | ||
| 713 | |||
| 714 | if (table_offset + table->length > all_tables_size) { | ||
| 715 | acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); | ||
| 716 | WARN_ON(1); | ||
| 717 | return AE_OK; | ||
| 718 | } | ||
| 719 | |||
| 720 | table_offset += table->length; | ||
| 721 | |||
| 722 | if (memcmp(existing_table->signature, table->signature, 4)) { | ||
| 723 | acpi_os_unmap_memory(table, | ||
| 724 | ACPI_HEADER_SIZE); | ||
| 725 | continue; | ||
| 726 | } | ||
| 727 | |||
| 728 | /* Only override tables with matching oem id */ | ||
| 729 | if (memcmp(table->oem_table_id, existing_table->oem_table_id, | ||
| 730 | ACPI_OEM_TABLE_ID_SIZE)) { | ||
| 731 | acpi_os_unmap_memory(table, | ||
| 732 | ACPI_HEADER_SIZE); | ||
| 733 | continue; | ||
| 734 | } | ||
| 735 | |||
| 736 | table_offset -= table->length; | ||
| 737 | *table_length = table->length; | ||
| 738 | acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); | ||
| 739 | *address = acpi_tables_addr + table_offset; | ||
| 740 | break; | ||
| 741 | } while (table_offset + ACPI_HEADER_SIZE < all_tables_size); | ||
| 742 | |||
| 743 | if (*address != 0) | ||
| 744 | acpi_table_taint(existing_table); | ||
| 745 | return AE_OK; | ||
| 746 | #endif | ||
| 747 | } | ||
| 568 | 748 | ||
| 569 | static irqreturn_t acpi_irq(int irq, void *dev_id) | 749 | static irqreturn_t acpi_irq(int irq, void *dev_id) |
| 570 | { | 750 | { |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index c33fa3ce9b7c..3994d7790b23 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
| @@ -78,6 +78,14 @@ typedef int (*acpi_table_handler) (struct acpi_table_header *table); | |||
| 78 | 78 | ||
| 79 | typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end); | 79 | typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end); |
| 80 | 80 | ||
| 81 | #ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE | ||
| 82 | void acpi_initrd_override(void *data, size_t size); | ||
| 83 | #else | ||
| 84 | static inline void acpi_initrd_override(void *data, size_t size) | ||
| 85 | { | ||
| 86 | } | ||
| 87 | #endif | ||
| 88 | |||
| 81 | char * __acpi_map_table (unsigned long phys_addr, unsigned long size); | 89 | char * __acpi_map_table (unsigned long phys_addr, unsigned long size); |
| 82 | void __acpi_unmap_table(char *map, unsigned long size); | 90 | void __acpi_unmap_table(char *map, unsigned long size); |
| 83 | int early_acpi_boot_init(void); | 91 | int early_acpi_boot_init(void); |
| @@ -479,6 +487,14 @@ void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, | |||
| 479 | 487 | ||
| 480 | acpi_status acpi_os_prepare_sleep(u8 sleep_state, | 488 | acpi_status acpi_os_prepare_sleep(u8 sleep_state, |
| 481 | u32 pm1a_control, u32 pm1b_control); | 489 | u32 pm1a_control, u32 pm1b_control); |
| 490 | #ifdef CONFIG_X86 | ||
| 491 | void arch_reserve_mem_area(acpi_physical_address addr, size_t size); | ||
| 492 | #else | ||
| 493 | static inline void arch_reserve_mem_area(acpi_physical_address addr, | ||
| 494 | size_t size) | ||
| 495 | { | ||
| 496 | } | ||
| 497 | #endif /* CONFIG_X86 */ | ||
| 482 | #else | 498 | #else |
| 483 | #define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) | 499 | #define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) |
| 484 | #endif | 500 | #endif |
diff --git a/include/linux/earlycpio.h b/include/linux/earlycpio.h new file mode 100644 index 000000000000..111f46d83d00 --- /dev/null +++ b/include/linux/earlycpio.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef _LINUX_EARLYCPIO_H | ||
| 2 | #define _LINUX_EARLYCPIO_H | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | |||
| 6 | #define MAX_CPIO_FILE_NAME 18 | ||
| 7 | |||
| 8 | struct cpio_data { | ||
| 9 | void *data; | ||
| 10 | size_t size; | ||
| 11 | char name[MAX_CPIO_FILE_NAME]; | ||
| 12 | }; | ||
| 13 | |||
| 14 | struct cpio_data find_cpio_data(const char *path, void *data, size_t len, | ||
| 15 | long *offset); | ||
| 16 | |||
| 17 | #endif /* _LINUX_EARLYCPIO_H */ | ||
diff --git a/lib/Makefile b/lib/Makefile index e3723c7527da..e2152fa7ff4d 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
| @@ -12,7 +12,8 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ | |||
| 12 | idr.o int_sqrt.o extable.o \ | 12 | idr.o int_sqrt.o extable.o \ |
| 13 | sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ | 13 | sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ |
| 14 | proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \ | 14 | proportions.o flex_proportions.o prio_heap.o ratelimit.o show_mem.o \ |
| 15 | is_single_threaded.o plist.o decompress.o kobject_uevent.o | 15 | is_single_threaded.o plist.o decompress.o kobject_uevent.o \ |
| 16 | earlycpio.o | ||
| 16 | 17 | ||
| 17 | lib-$(CONFIG_MMU) += ioremap.o | 18 | lib-$(CONFIG_MMU) += ioremap.o |
| 18 | lib-$(CONFIG_SMP) += cpumask.o | 19 | lib-$(CONFIG_SMP) += cpumask.o |
diff --git a/lib/earlycpio.c b/lib/earlycpio.c new file mode 100644 index 000000000000..8078ef49cb79 --- /dev/null +++ b/lib/earlycpio.c | |||
| @@ -0,0 +1,145 @@ | |||
| 1 | /* ----------------------------------------------------------------------- * | ||
| 2 | * | ||
| 3 | * Copyright 2012 Intel Corporation; author H. Peter Anvin | ||
| 4 | * | ||
| 5 | * This file is part of the Linux kernel, and is made available | ||
| 6 | * under the terms of the GNU General Public License version 2, as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but | ||
| 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * General Public License for more details. | ||
| 13 | * | ||
| 14 | * ----------------------------------------------------------------------- */ | ||
| 15 | |||
| 16 | /* | ||
| 17 | * earlycpio.c | ||
| 18 | * | ||
| 19 | * Find a specific cpio member; must precede any compressed content. | ||
| 20 | * This is used to locate data items in the initramfs used by the | ||
| 21 | * kernel itself during early boot (before the main initramfs is | ||
| 22 | * decompressed.) It is the responsibility of the initramfs creator | ||
| 23 | * to ensure that these items are uncompressed at the head of the | ||
| 24 | * blob. Depending on the boot loader or package tool that may be a | ||
| 25 | * separate file or part of the same file. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/earlycpio.h> | ||
| 29 | #include <linux/kernel.h> | ||
| 30 | #include <linux/string.h> | ||
| 31 | |||
| 32 | enum cpio_fields { | ||
| 33 | C_MAGIC, | ||
| 34 | C_INO, | ||
| 35 | C_MODE, | ||
| 36 | C_UID, | ||
| 37 | C_GID, | ||
| 38 | C_NLINK, | ||
| 39 | C_MTIME, | ||
| 40 | C_FILESIZE, | ||
| 41 | C_MAJ, | ||
| 42 | C_MIN, | ||
| 43 | C_RMAJ, | ||
| 44 | C_RMIN, | ||
| 45 | C_NAMESIZE, | ||
| 46 | C_CHKSUM, | ||
| 47 | C_NFIELDS | ||
| 48 | }; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * cpio_data find_cpio_data - Search for files in an uncompressed cpio | ||
| 52 | * @path: The directory to search for, including a slash at the end | ||
| 53 | * @data: Pointer to the the cpio archive or a header inside | ||
| 54 | * @len: Remaining length of the cpio based on data pointer | ||
| 55 | * @offset: When a matching file is found, this is the offset to the | ||
| 56 | * beginning of the cpio. It can be used to iterate through | ||
| 57 | * the cpio to find all files inside of a directory path | ||
| 58 | * | ||
| 59 | * @return: struct cpio_data containing the address, length and | ||
| 60 | * filename (with the directory path cut off) of the found file. | ||
| 61 | * If you search for a filename and not for files in a directory, | ||
| 62 | * pass the absolute path of the filename in the cpio and make sure | ||
| 63 | * the match returned an empty filename string. | ||
| 64 | */ | ||
| 65 | |||
| 66 | struct cpio_data __cpuinit find_cpio_data(const char *path, void *data, | ||
| 67 | size_t len, long *offset) | ||
| 68 | { | ||
| 69 | const size_t cpio_header_len = 8*C_NFIELDS - 2; | ||
| 70 | struct cpio_data cd = { NULL, 0, "" }; | ||
| 71 | const char *p, *dptr, *nptr; | ||
| 72 | unsigned int ch[C_NFIELDS], *chp, v; | ||
| 73 | unsigned char c, x; | ||
| 74 | size_t mypathsize = strlen(path); | ||
| 75 | int i, j; | ||
| 76 | |||
| 77 | p = data; | ||
| 78 | |||
| 79 | while (len > cpio_header_len) { | ||
| 80 | if (!*p) { | ||
| 81 | /* All cpio headers need to be 4-byte aligned */ | ||
| 82 | p += 4; | ||
| 83 | len -= 4; | ||
| 84 | continue; | ||
| 85 | } | ||
| 86 | |||
| 87 | j = 6; /* The magic field is only 6 characters */ | ||
| 88 | chp = ch; | ||
| 89 | for (i = C_NFIELDS; i; i--) { | ||
| 90 | v = 0; | ||
| 91 | while (j--) { | ||
| 92 | v <<= 4; | ||
| 93 | c = *p++; | ||
| 94 | |||
| 95 | x = c - '0'; | ||
| 96 | if (x < 10) { | ||
| 97 | v += x; | ||
| 98 | continue; | ||
| 99 | } | ||
| 100 | |||
| 101 | x = (c | 0x20) - 'a'; | ||
| 102 | if (x < 6) { | ||
| 103 | v += x + 10; | ||
| 104 | continue; | ||
| 105 | } | ||
| 106 | |||
| 107 | goto quit; /* Invalid hexadecimal */ | ||
| 108 | } | ||
| 109 | *chp++ = v; | ||
| 110 | j = 8; /* All other fields are 8 characters */ | ||
| 111 | } | ||
| 112 | |||
| 113 | if ((ch[C_MAGIC] - 0x070701) > 1) | ||
| 114 | goto quit; /* Invalid magic */ | ||
| 115 | |||
| 116 | len -= cpio_header_len; | ||
| 117 | |||
| 118 | dptr = PTR_ALIGN(p + ch[C_NAMESIZE], 4); | ||
| 119 | nptr = PTR_ALIGN(dptr + ch[C_FILESIZE], 4); | ||
| 120 | |||
| 121 | if (nptr > p + len || dptr < p || nptr < dptr) | ||
| 122 | goto quit; /* Buffer overrun */ | ||
| 123 | |||
| 124 | if ((ch[C_MODE] & 0170000) == 0100000 && | ||
| 125 | ch[C_NAMESIZE] >= mypathsize && | ||
| 126 | !memcmp(p, path, mypathsize)) { | ||
| 127 | *offset = (long)nptr - (long)data; | ||
| 128 | if (ch[C_NAMESIZE] - mypathsize >= MAX_CPIO_FILE_NAME) { | ||
| 129 | pr_warn( | ||
| 130 | "File %s exceeding MAX_CPIO_FILE_NAME [%d]\n", | ||
| 131 | p, MAX_CPIO_FILE_NAME); | ||
| 132 | } | ||
| 133 | strlcpy(cd.name, p + mypathsize, MAX_CPIO_FILE_NAME); | ||
| 134 | |||
| 135 | cd.data = (void *)dptr; | ||
| 136 | cd.size = ch[C_FILESIZE]; | ||
| 137 | return cd; /* Found it! */ | ||
| 138 | } | ||
| 139 | len -= (nptr - p); | ||
| 140 | p = nptr; | ||
| 141 | } | ||
| 142 | |||
| 143 | quit: | ||
| 144 | return cd; | ||
| 145 | } | ||
