diff options
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 224 |
1 files changed, 205 insertions, 19 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 9eaf708f5885..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 | } | ||
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 | } | ||
567 | 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 | { |
@@ -932,7 +1112,7 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, | |||
932 | * having a static work_struct. | 1112 | * having a static work_struct. |
933 | */ | 1113 | */ |
934 | 1114 | ||
935 | dpc = kmalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC); | 1115 | dpc = kzalloc(sizeof(struct acpi_os_dpc), GFP_ATOMIC); |
936 | if (!dpc) | 1116 | if (!dpc) |
937 | return AE_NO_MEMORY; | 1117 | return AE_NO_MEMORY; |
938 | 1118 | ||
@@ -944,17 +1124,22 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, | |||
944 | * because the hotplug code may call driver .remove() functions, | 1124 | * because the hotplug code may call driver .remove() functions, |
945 | * which invoke flush_scheduled_work/acpi_os_wait_events_complete | 1125 | * which invoke flush_scheduled_work/acpi_os_wait_events_complete |
946 | * to flush these workqueues. | 1126 | * to flush these workqueues. |
1127 | * | ||
1128 | * To prevent lockdep from complaining unnecessarily, make sure that | ||
1129 | * there is a different static lockdep key for each workqueue by using | ||
1130 | * INIT_WORK() for each of them separately. | ||
947 | */ | 1131 | */ |
948 | queue = hp ? kacpi_hotplug_wq : | 1132 | if (hp) { |
949 | (type == OSL_NOTIFY_HANDLER ? kacpi_notify_wq : kacpid_wq); | 1133 | queue = kacpi_hotplug_wq; |
950 | dpc->wait = hp ? 1 : 0; | 1134 | dpc->wait = 1; |
951 | |||
952 | if (queue == kacpi_hotplug_wq) | ||
953 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); | 1135 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); |
954 | else if (queue == kacpi_notify_wq) | 1136 | } else if (type == OSL_NOTIFY_HANDLER) { |
1137 | queue = kacpi_notify_wq; | ||
955 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); | 1138 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); |
956 | else | 1139 | } else { |
1140 | queue = kacpid_wq; | ||
957 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); | 1141 | INIT_WORK(&dpc->work, acpi_os_execute_deferred); |
1142 | } | ||
958 | 1143 | ||
959 | /* | 1144 | /* |
960 | * On some machines, a software-initiated SMI causes corruption unless | 1145 | * On some machines, a software-initiated SMI causes corruption unless |
@@ -986,6 +1171,7 @@ acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, | |||
986 | { | 1171 | { |
987 | return __acpi_os_execute(0, function, context, 1); | 1172 | return __acpi_os_execute(0, function, context, 1); |
988 | } | 1173 | } |
1174 | EXPORT_SYMBOL(acpi_os_hotplug_execute); | ||
989 | 1175 | ||
990 | void acpi_os_wait_events_complete(void) | 1176 | void acpi_os_wait_events_complete(void) |
991 | { | 1177 | { |