diff options
Diffstat (limited to 'drivers/pci/dmar.c')
-rw-r--r-- | drivers/pci/dmar.c | 110 |
1 files changed, 95 insertions, 15 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 6cdc931f7c17..83aae4747594 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -339,6 +339,35 @@ found: | |||
339 | } | 339 | } |
340 | #endif | 340 | #endif |
341 | 341 | ||
342 | #ifdef CONFIG_ACPI_NUMA | ||
343 | static int __init | ||
344 | dmar_parse_one_rhsa(struct acpi_dmar_header *header) | ||
345 | { | ||
346 | struct acpi_dmar_rhsa *rhsa; | ||
347 | struct dmar_drhd_unit *drhd; | ||
348 | |||
349 | rhsa = (struct acpi_dmar_rhsa *)header; | ||
350 | for_each_drhd_unit(drhd) { | ||
351 | if (drhd->reg_base_addr == rhsa->base_address) { | ||
352 | int node = acpi_map_pxm_to_node(rhsa->proximity_domain); | ||
353 | |||
354 | if (!node_online(node)) | ||
355 | node = -1; | ||
356 | drhd->iommu->node = node; | ||
357 | return 0; | ||
358 | } | ||
359 | } | ||
360 | WARN(1, "Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n" | ||
361 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
362 | drhd->reg_base_addr, | ||
363 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
364 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
365 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | #endif | ||
370 | |||
342 | static void __init | 371 | static void __init |
343 | dmar_table_print_dmar_entry(struct acpi_dmar_header *header) | 372 | dmar_table_print_dmar_entry(struct acpi_dmar_header *header) |
344 | { | 373 | { |
@@ -458,7 +487,9 @@ parse_dmar_table(void) | |||
458 | #endif | 487 | #endif |
459 | break; | 488 | break; |
460 | case ACPI_DMAR_HARDWARE_AFFINITY: | 489 | case ACPI_DMAR_HARDWARE_AFFINITY: |
461 | /* We don't do anything with RHSA (yet?) */ | 490 | #ifdef CONFIG_ACPI_NUMA |
491 | ret = dmar_parse_one_rhsa(entry_header); | ||
492 | #endif | ||
462 | break; | 493 | break; |
463 | default: | 494 | default: |
464 | printk(KERN_WARNING PREFIX | 495 | printk(KERN_WARNING PREFIX |
@@ -582,6 +613,8 @@ int __init dmar_table_init(void) | |||
582 | return 0; | 613 | return 0; |
583 | } | 614 | } |
584 | 615 | ||
616 | static int bios_warned; | ||
617 | |||
585 | int __init check_zero_address(void) | 618 | int __init check_zero_address(void) |
586 | { | 619 | { |
587 | struct acpi_table_dmar *dmar; | 620 | struct acpi_table_dmar *dmar; |
@@ -601,6 +634,9 @@ int __init check_zero_address(void) | |||
601 | } | 634 | } |
602 | 635 | ||
603 | if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { | 636 | if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { |
637 | void __iomem *addr; | ||
638 | u64 cap, ecap; | ||
639 | |||
604 | drhd = (void *)entry_header; | 640 | drhd = (void *)entry_header; |
605 | if (!drhd->address) { | 641 | if (!drhd->address) { |
606 | /* Promote an attitude of violence to a BIOS engineer today */ | 642 | /* Promote an attitude of violence to a BIOS engineer today */ |
@@ -609,17 +645,40 @@ int __init check_zero_address(void) | |||
609 | dmi_get_system_info(DMI_BIOS_VENDOR), | 645 | dmi_get_system_info(DMI_BIOS_VENDOR), |
610 | dmi_get_system_info(DMI_BIOS_VERSION), | 646 | dmi_get_system_info(DMI_BIOS_VERSION), |
611 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | 647 | dmi_get_system_info(DMI_PRODUCT_VERSION)); |
612 | #ifdef CONFIG_DMAR | 648 | bios_warned = 1; |
613 | dmar_disabled = 1; | 649 | goto failed; |
614 | #endif | 650 | } |
615 | return 0; | 651 | |
652 | addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); | ||
653 | if (!addr ) { | ||
654 | printk("IOMMU: can't validate: %llx\n", drhd->address); | ||
655 | goto failed; | ||
656 | } | ||
657 | cap = dmar_readq(addr + DMAR_CAP_REG); | ||
658 | ecap = dmar_readq(addr + DMAR_ECAP_REG); | ||
659 | early_iounmap(addr, VTD_PAGE_SIZE); | ||
660 | if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) { | ||
661 | /* Promote an attitude of violence to a BIOS engineer today */ | ||
662 | WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" | ||
663 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
664 | drhd->address, | ||
665 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
666 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
667 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
668 | bios_warned = 1; | ||
669 | goto failed; | ||
616 | } | 670 | } |
617 | break; | ||
618 | } | 671 | } |
619 | 672 | ||
620 | entry_header = ((void *)entry_header + entry_header->length); | 673 | entry_header = ((void *)entry_header + entry_header->length); |
621 | } | 674 | } |
622 | return 1; | 675 | return 1; |
676 | |||
677 | failed: | ||
678 | #ifdef CONFIG_DMAR | ||
679 | dmar_disabled = 1; | ||
680 | #endif | ||
681 | return 0; | ||
623 | } | 682 | } |
624 | 683 | ||
625 | void __init detect_intel_iommu(void) | 684 | void __init detect_intel_iommu(void) |
@@ -670,6 +729,18 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
670 | int agaw = 0; | 729 | int agaw = 0; |
671 | int msagaw = 0; | 730 | int msagaw = 0; |
672 | 731 | ||
732 | if (!drhd->reg_base_addr) { | ||
733 | if (!bios_warned) { | ||
734 | WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" | ||
735 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | ||
736 | dmi_get_system_info(DMI_BIOS_VENDOR), | ||
737 | dmi_get_system_info(DMI_BIOS_VERSION), | ||
738 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
739 | bios_warned = 1; | ||
740 | } | ||
741 | return -EINVAL; | ||
742 | } | ||
743 | |||
673 | iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); | 744 | iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); |
674 | if (!iommu) | 745 | if (!iommu) |
675 | return -ENOMEM; | 746 | return -ENOMEM; |
@@ -686,13 +757,16 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
686 | iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); | 757 | iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); |
687 | 758 | ||
688 | if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { | 759 | if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { |
689 | /* Promote an attitude of violence to a BIOS engineer today */ | 760 | if (!bios_warned) { |
690 | WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" | 761 | /* Promote an attitude of violence to a BIOS engineer today */ |
691 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", | 762 | WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" |
692 | drhd->reg_base_addr, | 763 | "BIOS vendor: %s; Ver: %s; Product Version: %s\n", |
693 | dmi_get_system_info(DMI_BIOS_VENDOR), | 764 | drhd->reg_base_addr, |
694 | dmi_get_system_info(DMI_BIOS_VERSION), | 765 | dmi_get_system_info(DMI_BIOS_VENDOR), |
695 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | 766 | dmi_get_system_info(DMI_BIOS_VERSION), |
767 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | ||
768 | bios_warned = 1; | ||
769 | } | ||
696 | goto err_unmap; | 770 | goto err_unmap; |
697 | } | 771 | } |
698 | 772 | ||
@@ -715,6 +789,8 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
715 | iommu->agaw = agaw; | 789 | iommu->agaw = agaw; |
716 | iommu->msagaw = msagaw; | 790 | iommu->msagaw = msagaw; |
717 | 791 | ||
792 | iommu->node = -1; | ||
793 | |||
718 | /* the registers might be more than one page */ | 794 | /* the registers might be more than one page */ |
719 | map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), | 795 | map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), |
720 | cap_max_fault_reg_offset(iommu->cap)); | 796 | cap_max_fault_reg_offset(iommu->cap)); |
@@ -1056,6 +1132,7 @@ static void __dmar_enable_qi(struct intel_iommu *iommu) | |||
1056 | int dmar_enable_qi(struct intel_iommu *iommu) | 1132 | int dmar_enable_qi(struct intel_iommu *iommu) |
1057 | { | 1133 | { |
1058 | struct q_inval *qi; | 1134 | struct q_inval *qi; |
1135 | struct page *desc_page; | ||
1059 | 1136 | ||
1060 | if (!ecap_qis(iommu->ecap)) | 1137 | if (!ecap_qis(iommu->ecap)) |
1061 | return -ENOENT; | 1138 | return -ENOENT; |
@@ -1072,13 +1149,16 @@ int dmar_enable_qi(struct intel_iommu *iommu) | |||
1072 | 1149 | ||
1073 | qi = iommu->qi; | 1150 | qi = iommu->qi; |
1074 | 1151 | ||
1075 | qi->desc = (void *)(get_zeroed_page(GFP_ATOMIC)); | 1152 | |
1076 | if (!qi->desc) { | 1153 | desc_page = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, 0); |
1154 | if (!desc_page) { | ||
1077 | kfree(qi); | 1155 | kfree(qi); |
1078 | iommu->qi = 0; | 1156 | iommu->qi = 0; |
1079 | return -ENOMEM; | 1157 | return -ENOMEM; |
1080 | } | 1158 | } |
1081 | 1159 | ||
1160 | qi->desc = page_address(desc_page); | ||
1161 | |||
1082 | qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); | 1162 | qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); |
1083 | if (!qi->desc_status) { | 1163 | if (!qi->desc_status) { |
1084 | free_page((unsigned long) qi->desc); | 1164 | free_page((unsigned long) qi->desc); |