diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2014-11-09 09:47:56 -0500 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2014-11-18 05:18:35 -0500 |
commit | c2a0b538d2c778aef7bf2fbe7973229192c9a392 (patch) | |
tree | 43fb6451526e573d70bd29cca9f2bef286b5a0ee | |
parent | 1a2262f90f493103496f3383741fb5d594c33738 (diff) |
iommu/vt-d: Introduce helper function dmar_walk_resources()
Introduce helper function dmar_walk_resources to walk resource entries
in DMAR table and ACPI buffer object returned by ACPI _DSM method
for IOMMU hot-plug.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r-- | drivers/iommu/dmar.c | 209 | ||||
-rw-r--r-- | drivers/iommu/intel-iommu.c | 4 | ||||
-rw-r--r-- | include/linux/dmar.h | 19 |
3 files changed, 122 insertions, 110 deletions
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index c5c61cabd6e3..586dd2aa2ca2 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c | |||
@@ -44,6 +44,14 @@ | |||
44 | 44 | ||
45 | #include "irq_remapping.h" | 45 | #include "irq_remapping.h" |
46 | 46 | ||
47 | typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *); | ||
48 | struct dmar_res_callback { | ||
49 | dmar_res_handler_t cb[ACPI_DMAR_TYPE_RESERVED]; | ||
50 | void *arg[ACPI_DMAR_TYPE_RESERVED]; | ||
51 | bool ignore_unhandled; | ||
52 | bool print_entry; | ||
53 | }; | ||
54 | |||
47 | /* | 55 | /* |
48 | * Assumptions: | 56 | * Assumptions: |
49 | * 1) The hotplug framework guarentees that DMAR unit will be hot-added | 57 | * 1) The hotplug framework guarentees that DMAR unit will be hot-added |
@@ -350,7 +358,7 @@ static struct notifier_block dmar_pci_bus_nb = { | |||
350 | * present in the platform | 358 | * present in the platform |
351 | */ | 359 | */ |
352 | static int __init | 360 | static int __init |
353 | dmar_parse_one_drhd(struct acpi_dmar_header *header) | 361 | dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg) |
354 | { | 362 | { |
355 | struct acpi_dmar_hardware_unit *drhd; | 363 | struct acpi_dmar_hardware_unit *drhd; |
356 | struct dmar_drhd_unit *dmaru; | 364 | struct dmar_drhd_unit *dmaru; |
@@ -381,6 +389,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) | |||
381 | return ret; | 389 | return ret; |
382 | } | 390 | } |
383 | dmar_register_drhd_unit(dmaru); | 391 | dmar_register_drhd_unit(dmaru); |
392 | |||
393 | if (arg) | ||
394 | (*(int *)arg)++; | ||
395 | |||
384 | return 0; | 396 | return 0; |
385 | } | 397 | } |
386 | 398 | ||
@@ -393,7 +405,8 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru) | |||
393 | kfree(dmaru); | 405 | kfree(dmaru); |
394 | } | 406 | } |
395 | 407 | ||
396 | static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) | 408 | static int __init dmar_parse_one_andd(struct acpi_dmar_header *header, |
409 | void *arg) | ||
397 | { | 410 | { |
398 | struct acpi_dmar_andd *andd = (void *)header; | 411 | struct acpi_dmar_andd *andd = (void *)header; |
399 | 412 | ||
@@ -415,7 +428,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) | |||
415 | 428 | ||
416 | #ifdef CONFIG_ACPI_NUMA | 429 | #ifdef CONFIG_ACPI_NUMA |
417 | static int __init | 430 | static int __init |
418 | dmar_parse_one_rhsa(struct acpi_dmar_header *header) | 431 | dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) |
419 | { | 432 | { |
420 | struct acpi_dmar_rhsa *rhsa; | 433 | struct acpi_dmar_rhsa *rhsa; |
421 | struct dmar_drhd_unit *drhd; | 434 | struct dmar_drhd_unit *drhd; |
@@ -442,6 +455,8 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header) | |||
442 | 455 | ||
443 | return 0; | 456 | return 0; |
444 | } | 457 | } |
458 | #else | ||
459 | #define dmar_parse_one_rhsa dmar_res_noop | ||
445 | #endif | 460 | #endif |
446 | 461 | ||
447 | static void __init | 462 | static void __init |
@@ -503,6 +518,52 @@ static int __init dmar_table_detect(void) | |||
503 | return (ACPI_SUCCESS(status) ? 1 : 0); | 518 | return (ACPI_SUCCESS(status) ? 1 : 0); |
504 | } | 519 | } |
505 | 520 | ||
521 | static int dmar_walk_remapping_entries(struct acpi_dmar_header *start, | ||
522 | size_t len, struct dmar_res_callback *cb) | ||
523 | { | ||
524 | int ret = 0; | ||
525 | struct acpi_dmar_header *iter, *next; | ||
526 | struct acpi_dmar_header *end = ((void *)start) + len; | ||
527 | |||
528 | for (iter = start; iter < end && ret == 0; iter = next) { | ||
529 | next = (void *)iter + iter->length; | ||
530 | if (iter->length == 0) { | ||
531 | /* Avoid looping forever on bad ACPI tables */ | ||
532 | pr_debug(FW_BUG "Invalid 0-length structure\n"); | ||
533 | break; | ||
534 | } else if (next > end) { | ||
535 | /* Avoid passing table end */ | ||
536 | pr_warn(FW_BUG "record passes table end\n"); | ||
537 | ret = -EINVAL; | ||
538 | break; | ||
539 | } | ||
540 | |||
541 | if (cb->print_entry) | ||
542 | dmar_table_print_dmar_entry(iter); | ||
543 | |||
544 | if (iter->type >= ACPI_DMAR_TYPE_RESERVED) { | ||
545 | /* continue for forward compatibility */ | ||
546 | pr_debug("Unknown DMAR structure type %d\n", | ||
547 | iter->type); | ||
548 | } else if (cb->cb[iter->type]) { | ||
549 | ret = cb->cb[iter->type](iter, cb->arg[iter->type]); | ||
550 | } else if (!cb->ignore_unhandled) { | ||
551 | pr_warn("No handler for DMAR structure type %d\n", | ||
552 | iter->type); | ||
553 | ret = -EINVAL; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | return ret; | ||
558 | } | ||
559 | |||
560 | static inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar, | ||
561 | struct dmar_res_callback *cb) | ||
562 | { | ||
563 | return dmar_walk_remapping_entries((void *)(dmar + 1), | ||
564 | dmar->header.length - sizeof(*dmar), cb); | ||
565 | } | ||
566 | |||
506 | /** | 567 | /** |
507 | * parse_dmar_table - parses the DMA reporting table | 568 | * parse_dmar_table - parses the DMA reporting table |
508 | */ | 569 | */ |
@@ -510,9 +571,18 @@ static int __init | |||
510 | parse_dmar_table(void) | 571 | parse_dmar_table(void) |
511 | { | 572 | { |
512 | struct acpi_table_dmar *dmar; | 573 | struct acpi_table_dmar *dmar; |
513 | struct acpi_dmar_header *entry_header; | ||
514 | int ret = 0; | 574 | int ret = 0; |
515 | int drhd_count = 0; | 575 | int drhd_count = 0; |
576 | struct dmar_res_callback cb = { | ||
577 | .print_entry = true, | ||
578 | .ignore_unhandled = true, | ||
579 | .arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count, | ||
580 | .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd, | ||
581 | .cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr, | ||
582 | .cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr, | ||
583 | .cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa, | ||
584 | .cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd, | ||
585 | }; | ||
516 | 586 | ||
517 | /* | 587 | /* |
518 | * Do it again, earlier dmar_tbl mapping could be mapped with | 588 | * Do it again, earlier dmar_tbl mapping could be mapped with |
@@ -536,51 +606,10 @@ parse_dmar_table(void) | |||
536 | } | 606 | } |
537 | 607 | ||
538 | pr_info("Host address width %d\n", dmar->width + 1); | 608 | pr_info("Host address width %d\n", dmar->width + 1); |
539 | 609 | ret = dmar_walk_dmar_table(dmar, &cb); | |
540 | entry_header = (struct acpi_dmar_header *)(dmar + 1); | 610 | if (ret == 0 && drhd_count == 0) |
541 | while (((unsigned long)entry_header) < | ||
542 | (((unsigned long)dmar) + dmar_tbl->length)) { | ||
543 | /* Avoid looping forever on bad ACPI tables */ | ||
544 | if (entry_header->length == 0) { | ||
545 | pr_warn("Invalid 0-length structure\n"); | ||
546 | ret = -EINVAL; | ||
547 | break; | ||
548 | } | ||
549 | |||
550 | dmar_table_print_dmar_entry(entry_header); | ||
551 | |||
552 | switch (entry_header->type) { | ||
553 | case ACPI_DMAR_TYPE_HARDWARE_UNIT: | ||
554 | drhd_count++; | ||
555 | ret = dmar_parse_one_drhd(entry_header); | ||
556 | break; | ||
557 | case ACPI_DMAR_TYPE_RESERVED_MEMORY: | ||
558 | ret = dmar_parse_one_rmrr(entry_header); | ||
559 | break; | ||
560 | case ACPI_DMAR_TYPE_ROOT_ATS: | ||
561 | ret = dmar_parse_one_atsr(entry_header); | ||
562 | break; | ||
563 | case ACPI_DMAR_TYPE_HARDWARE_AFFINITY: | ||
564 | #ifdef CONFIG_ACPI_NUMA | ||
565 | ret = dmar_parse_one_rhsa(entry_header); | ||
566 | #endif | ||
567 | break; | ||
568 | case ACPI_DMAR_TYPE_NAMESPACE: | ||
569 | ret = dmar_parse_one_andd(entry_header); | ||
570 | break; | ||
571 | default: | ||
572 | pr_warn("Unknown DMAR structure type %d\n", | ||
573 | entry_header->type); | ||
574 | ret = 0; /* for forward compatibility */ | ||
575 | break; | ||
576 | } | ||
577 | if (ret) | ||
578 | break; | ||
579 | |||
580 | entry_header = ((void *)entry_header + entry_header->length); | ||
581 | } | ||
582 | if (drhd_count == 0) | ||
583 | pr_warn(FW_BUG "No DRHD structure found in DMAR table\n"); | 611 | pr_warn(FW_BUG "No DRHD structure found in DMAR table\n"); |
612 | |||
584 | return ret; | 613 | return ret; |
585 | } | 614 | } |
586 | 615 | ||
@@ -778,76 +807,60 @@ static void warn_invalid_dmar(u64 addr, const char *message) | |||
778 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | 807 | dmi_get_system_info(DMI_PRODUCT_VERSION)); |
779 | } | 808 | } |
780 | 809 | ||
781 | static int __init check_zero_address(void) | 810 | static int __ref |
811 | dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg) | ||
782 | { | 812 | { |
783 | struct acpi_table_dmar *dmar; | ||
784 | struct acpi_dmar_header *entry_header; | ||
785 | struct acpi_dmar_hardware_unit *drhd; | 813 | struct acpi_dmar_hardware_unit *drhd; |
814 | void __iomem *addr; | ||
815 | u64 cap, ecap; | ||
786 | 816 | ||
787 | dmar = (struct acpi_table_dmar *)dmar_tbl; | 817 | drhd = (void *)entry; |
788 | entry_header = (struct acpi_dmar_header *)(dmar + 1); | 818 | if (!drhd->address) { |
789 | 819 | warn_invalid_dmar(0, ""); | |
790 | while (((unsigned long)entry_header) < | 820 | return -EINVAL; |
791 | (((unsigned long)dmar) + dmar_tbl->length)) { | 821 | } |
792 | /* Avoid looping forever on bad ACPI tables */ | ||
793 | if (entry_header->length == 0) { | ||
794 | pr_warn("Invalid 0-length structure\n"); | ||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { | ||
799 | void __iomem *addr; | ||
800 | u64 cap, ecap; | ||
801 | |||
802 | drhd = (void *)entry_header; | ||
803 | if (!drhd->address) { | ||
804 | warn_invalid_dmar(0, ""); | ||
805 | goto failed; | ||
806 | } | ||
807 | 822 | ||
808 | addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); | 823 | addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); |
809 | if (!addr ) { | 824 | if (!addr) { |
810 | printk("IOMMU: can't validate: %llx\n", drhd->address); | 825 | pr_warn("IOMMU: can't validate: %llx\n", drhd->address); |
811 | goto failed; | 826 | return -EINVAL; |
812 | } | 827 | } |
813 | cap = dmar_readq(addr + DMAR_CAP_REG); | 828 | cap = dmar_readq(addr + DMAR_CAP_REG); |
814 | ecap = dmar_readq(addr + DMAR_ECAP_REG); | 829 | ecap = dmar_readq(addr + DMAR_ECAP_REG); |
815 | early_iounmap(addr, VTD_PAGE_SIZE); | 830 | early_iounmap(addr, VTD_PAGE_SIZE); |
816 | if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) { | ||
817 | warn_invalid_dmar(drhd->address, | ||
818 | " returns all ones"); | ||
819 | goto failed; | ||
820 | } | ||
821 | } | ||
822 | 831 | ||
823 | entry_header = ((void *)entry_header + entry_header->length); | 832 | if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) { |
833 | warn_invalid_dmar(drhd->address, " returns all ones"); | ||
834 | return -EINVAL; | ||
824 | } | 835 | } |
825 | return 1; | ||
826 | 836 | ||
827 | failed: | ||
828 | return 0; | 837 | return 0; |
829 | } | 838 | } |
830 | 839 | ||
831 | int __init detect_intel_iommu(void) | 840 | int __init detect_intel_iommu(void) |
832 | { | 841 | { |
833 | int ret; | 842 | int ret; |
843 | struct dmar_res_callback validate_drhd_cb = { | ||
844 | .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd, | ||
845 | .ignore_unhandled = true, | ||
846 | }; | ||
834 | 847 | ||
835 | down_write(&dmar_global_lock); | 848 | down_write(&dmar_global_lock); |
836 | ret = dmar_table_detect(); | 849 | ret = dmar_table_detect(); |
837 | if (ret) | 850 | if (ret) |
838 | ret = check_zero_address(); | 851 | ret = !dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl, |
839 | { | 852 | &validate_drhd_cb); |
840 | if (ret && !no_iommu && !iommu_detected && !dmar_disabled) { | 853 | if (ret && !no_iommu && !iommu_detected && !dmar_disabled) { |
841 | iommu_detected = 1; | 854 | iommu_detected = 1; |
842 | /* Make sure ACS will be enabled */ | 855 | /* Make sure ACS will be enabled */ |
843 | pci_request_acs(); | 856 | pci_request_acs(); |
844 | } | 857 | } |
845 | 858 | ||
846 | #ifdef CONFIG_X86 | 859 | #ifdef CONFIG_X86 |
847 | if (ret) | 860 | if (ret) |
848 | x86_init.iommu.iommu_init = intel_iommu_init; | 861 | x86_init.iommu.iommu_init = intel_iommu_init; |
849 | #endif | 862 | #endif |
850 | } | 863 | |
851 | early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size); | 864 | early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size); |
852 | dmar_tbl = NULL; | 865 | dmar_tbl = NULL; |
853 | up_write(&dmar_global_lock); | 866 | up_write(&dmar_global_lock); |
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ba0fa2a8d696..b9cc9c2b03fc 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
@@ -3684,7 +3684,7 @@ static inline void init_iommu_pm_ops(void) {} | |||
3684 | #endif /* CONFIG_PM */ | 3684 | #endif /* CONFIG_PM */ |
3685 | 3685 | ||
3686 | 3686 | ||
3687 | int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header) | 3687 | int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg) |
3688 | { | 3688 | { |
3689 | struct acpi_dmar_reserved_memory *rmrr; | 3689 | struct acpi_dmar_reserved_memory *rmrr; |
3690 | struct dmar_rmrr_unit *rmrru; | 3690 | struct dmar_rmrr_unit *rmrru; |
@@ -3710,7 +3710,7 @@ int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header) | |||
3710 | return 0; | 3710 | return 0; |
3711 | } | 3711 | } |
3712 | 3712 | ||
3713 | int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr) | 3713 | int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr, void *arg) |
3714 | { | 3714 | { |
3715 | struct acpi_dmar_atsr *atsr; | 3715 | struct acpi_dmar_atsr *atsr; |
3716 | struct dmar_atsr_unit *atsru; | 3716 | struct dmar_atsr_unit *atsru; |
diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 593fff99e6bf..495df5e48f80 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h | |||
@@ -121,22 +121,21 @@ extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, | |||
121 | extern int detect_intel_iommu(void); | 121 | extern int detect_intel_iommu(void); |
122 | extern int enable_drhd_fault_handling(void); | 122 | extern int enable_drhd_fault_handling(void); |
123 | 123 | ||
124 | static inline int dmar_res_noop(struct acpi_dmar_header *hdr, void *arg) | ||
125 | { | ||
126 | return 0; | ||
127 | } | ||
128 | |||
124 | #ifdef CONFIG_INTEL_IOMMU | 129 | #ifdef CONFIG_INTEL_IOMMU |
125 | extern int iommu_detected, no_iommu; | 130 | extern int iommu_detected, no_iommu; |
126 | extern int intel_iommu_init(void); | 131 | extern int intel_iommu_init(void); |
127 | extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); | 132 | extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg); |
128 | extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); | 133 | extern int dmar_parse_one_atsr(struct acpi_dmar_header *header, void *arg); |
129 | extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); | 134 | extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); |
130 | #else /* !CONFIG_INTEL_IOMMU: */ | 135 | #else /* !CONFIG_INTEL_IOMMU: */ |
131 | static inline int intel_iommu_init(void) { return -ENODEV; } | 136 | static inline int intel_iommu_init(void) { return -ENODEV; } |
132 | static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header) | 137 | #define dmar_parse_one_rmrr dmar_res_noop |
133 | { | 138 | #define dmar_parse_one_atsr dmar_res_noop |
134 | return 0; | ||
135 | } | ||
136 | static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header) | ||
137 | { | ||
138 | return 0; | ||
139 | } | ||
140 | static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) | 139 | static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) |
141 | { | 140 | { |
142 | return 0; | 141 | return 0; |