aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiang Liu <jiang.liu@linux.intel.com>2014-11-09 09:47:56 -0500
committerJoerg Roedel <jroedel@suse.de>2014-11-18 05:18:35 -0500
commitc2a0b538d2c778aef7bf2fbe7973229192c9a392 (patch)
tree43fb6451526e573d70bd29cca9f2bef286b5a0ee
parent1a2262f90f493103496f3383741fb5d594c33738 (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.c209
-rw-r--r--drivers/iommu/intel-iommu.c4
-rw-r--r--include/linux/dmar.h19
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
47typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *);
48struct 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 */
352static int __init 360static int __init
353dmar_parse_one_drhd(struct acpi_dmar_header *header) 361dmar_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
396static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) 408static 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
417static int __init 430static int __init
418dmar_parse_one_rhsa(struct acpi_dmar_header *header) 431dmar_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
447static void __init 462static 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
521static 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
560static 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
510parse_dmar_table(void) 571parse_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
781static int __init check_zero_address(void) 810static int __ref
811dmar_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
827failed:
828 return 0; 837 return 0;
829} 838}
830 839
831int __init detect_intel_iommu(void) 840int __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
3687int __init dmar_parse_one_rmrr(struct acpi_dmar_header *header) 3687int __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
3713int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr) 3713int __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,
121extern int detect_intel_iommu(void); 121extern int detect_intel_iommu(void);
122extern int enable_drhd_fault_handling(void); 122extern int enable_drhd_fault_handling(void);
123 123
124static 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
125extern int iommu_detected, no_iommu; 130extern int iommu_detected, no_iommu;
126extern int intel_iommu_init(void); 131extern int intel_iommu_init(void);
127extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); 132extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header, void *arg);
128extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); 133extern int dmar_parse_one_atsr(struct acpi_dmar_header *header, void *arg);
129extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); 134extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
130#else /* !CONFIG_INTEL_IOMMU: */ 135#else /* !CONFIG_INTEL_IOMMU: */
131static inline int intel_iommu_init(void) { return -ENODEV; } 136static inline int intel_iommu_init(void) { return -ENODEV; }
132static 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}
136static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header)
137{
138 return 0;
139}
140static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) 139static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
141{ 140{
142 return 0; 141 return 0;