diff options
Diffstat (limited to 'drivers/iommu/dmar.c')
-rw-r--r-- | drivers/iommu/dmar.c | 532 |
1 files changed, 423 insertions, 109 deletions
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index c5c61cabd6e3..9847613085e1 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 |
@@ -62,11 +70,12 @@ LIST_HEAD(dmar_drhd_units); | |||
62 | struct acpi_table_header * __initdata dmar_tbl; | 70 | struct acpi_table_header * __initdata dmar_tbl; |
63 | static acpi_size dmar_tbl_size; | 71 | static acpi_size dmar_tbl_size; |
64 | static int dmar_dev_scope_status = 1; | 72 | static int dmar_dev_scope_status = 1; |
73 | static unsigned long dmar_seq_ids[BITS_TO_LONGS(DMAR_UNITS_SUPPORTED)]; | ||
65 | 74 | ||
66 | static int alloc_iommu(struct dmar_drhd_unit *drhd); | 75 | static int alloc_iommu(struct dmar_drhd_unit *drhd); |
67 | static void free_iommu(struct intel_iommu *iommu); | 76 | static void free_iommu(struct intel_iommu *iommu); |
68 | 77 | ||
69 | static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) | 78 | static void dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) |
70 | { | 79 | { |
71 | /* | 80 | /* |
72 | * add INCLUDE_ALL at the tail, so scan the list will find it at | 81 | * add INCLUDE_ALL at the tail, so scan the list will find it at |
@@ -344,24 +353,45 @@ static struct notifier_block dmar_pci_bus_nb = { | |||
344 | .priority = INT_MIN, | 353 | .priority = INT_MIN, |
345 | }; | 354 | }; |
346 | 355 | ||
356 | static struct dmar_drhd_unit * | ||
357 | dmar_find_dmaru(struct acpi_dmar_hardware_unit *drhd) | ||
358 | { | ||
359 | struct dmar_drhd_unit *dmaru; | ||
360 | |||
361 | list_for_each_entry_rcu(dmaru, &dmar_drhd_units, list) | ||
362 | if (dmaru->segment == drhd->segment && | ||
363 | dmaru->reg_base_addr == drhd->address) | ||
364 | return dmaru; | ||
365 | |||
366 | return NULL; | ||
367 | } | ||
368 | |||
347 | /** | 369 | /** |
348 | * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition | 370 | * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition |
349 | * structure which uniquely represent one DMA remapping hardware unit | 371 | * structure which uniquely represent one DMA remapping hardware unit |
350 | * present in the platform | 372 | * present in the platform |
351 | */ | 373 | */ |
352 | static int __init | 374 | static int dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg) |
353 | dmar_parse_one_drhd(struct acpi_dmar_header *header) | ||
354 | { | 375 | { |
355 | struct acpi_dmar_hardware_unit *drhd; | 376 | struct acpi_dmar_hardware_unit *drhd; |
356 | struct dmar_drhd_unit *dmaru; | 377 | struct dmar_drhd_unit *dmaru; |
357 | int ret = 0; | 378 | int ret = 0; |
358 | 379 | ||
359 | drhd = (struct acpi_dmar_hardware_unit *)header; | 380 | drhd = (struct acpi_dmar_hardware_unit *)header; |
360 | dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); | 381 | dmaru = dmar_find_dmaru(drhd); |
382 | if (dmaru) | ||
383 | goto out; | ||
384 | |||
385 | dmaru = kzalloc(sizeof(*dmaru) + header->length, GFP_KERNEL); | ||
361 | if (!dmaru) | 386 | if (!dmaru) |
362 | return -ENOMEM; | 387 | return -ENOMEM; |
363 | 388 | ||
364 | dmaru->hdr = header; | 389 | /* |
390 | * If header is allocated from slab by ACPI _DSM method, we need to | ||
391 | * copy the content because the memory buffer will be freed on return. | ||
392 | */ | ||
393 | dmaru->hdr = (void *)(dmaru + 1); | ||
394 | memcpy(dmaru->hdr, header, header->length); | ||
365 | dmaru->reg_base_addr = drhd->address; | 395 | dmaru->reg_base_addr = drhd->address; |
366 | dmaru->segment = drhd->segment; | 396 | dmaru->segment = drhd->segment; |
367 | dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ | 397 | dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ |
@@ -381,6 +411,11 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) | |||
381 | return ret; | 411 | return ret; |
382 | } | 412 | } |
383 | dmar_register_drhd_unit(dmaru); | 413 | dmar_register_drhd_unit(dmaru); |
414 | |||
415 | out: | ||
416 | if (arg) | ||
417 | (*(int *)arg)++; | ||
418 | |||
384 | return 0; | 419 | return 0; |
385 | } | 420 | } |
386 | 421 | ||
@@ -393,7 +428,8 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru) | |||
393 | kfree(dmaru); | 428 | kfree(dmaru); |
394 | } | 429 | } |
395 | 430 | ||
396 | static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) | 431 | static int __init dmar_parse_one_andd(struct acpi_dmar_header *header, |
432 | void *arg) | ||
397 | { | 433 | { |
398 | struct acpi_dmar_andd *andd = (void *)header; | 434 | struct acpi_dmar_andd *andd = (void *)header; |
399 | 435 | ||
@@ -414,8 +450,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) | |||
414 | } | 450 | } |
415 | 451 | ||
416 | #ifdef CONFIG_ACPI_NUMA | 452 | #ifdef CONFIG_ACPI_NUMA |
417 | static int __init | 453 | static int dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) |
418 | dmar_parse_one_rhsa(struct acpi_dmar_header *header) | ||
419 | { | 454 | { |
420 | struct acpi_dmar_rhsa *rhsa; | 455 | struct acpi_dmar_rhsa *rhsa; |
421 | struct dmar_drhd_unit *drhd; | 456 | struct dmar_drhd_unit *drhd; |
@@ -442,6 +477,8 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header) | |||
442 | 477 | ||
443 | return 0; | 478 | return 0; |
444 | } | 479 | } |
480 | #else | ||
481 | #define dmar_parse_one_rhsa dmar_res_noop | ||
445 | #endif | 482 | #endif |
446 | 483 | ||
447 | static void __init | 484 | static void __init |
@@ -503,6 +540,52 @@ static int __init dmar_table_detect(void) | |||
503 | return (ACPI_SUCCESS(status) ? 1 : 0); | 540 | return (ACPI_SUCCESS(status) ? 1 : 0); |
504 | } | 541 | } |
505 | 542 | ||
543 | static int dmar_walk_remapping_entries(struct acpi_dmar_header *start, | ||
544 | size_t len, struct dmar_res_callback *cb) | ||
545 | { | ||
546 | int ret = 0; | ||
547 | struct acpi_dmar_header *iter, *next; | ||
548 | struct acpi_dmar_header *end = ((void *)start) + len; | ||
549 | |||
550 | for (iter = start; iter < end && ret == 0; iter = next) { | ||
551 | next = (void *)iter + iter->length; | ||
552 | if (iter->length == 0) { | ||
553 | /* Avoid looping forever on bad ACPI tables */ | ||
554 | pr_debug(FW_BUG "Invalid 0-length structure\n"); | ||
555 | break; | ||
556 | } else if (next > end) { | ||
557 | /* Avoid passing table end */ | ||
558 | pr_warn(FW_BUG "record passes table end\n"); | ||
559 | ret = -EINVAL; | ||
560 | break; | ||
561 | } | ||
562 | |||
563 | if (cb->print_entry) | ||
564 | dmar_table_print_dmar_entry(iter); | ||
565 | |||
566 | if (iter->type >= ACPI_DMAR_TYPE_RESERVED) { | ||
567 | /* continue for forward compatibility */ | ||
568 | pr_debug("Unknown DMAR structure type %d\n", | ||
569 | iter->type); | ||
570 | } else if (cb->cb[iter->type]) { | ||
571 | ret = cb->cb[iter->type](iter, cb->arg[iter->type]); | ||
572 | } else if (!cb->ignore_unhandled) { | ||
573 | pr_warn("No handler for DMAR structure type %d\n", | ||
574 | iter->type); | ||
575 | ret = -EINVAL; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | return ret; | ||
580 | } | ||
581 | |||
582 | static inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar, | ||
583 | struct dmar_res_callback *cb) | ||
584 | { | ||
585 | return dmar_walk_remapping_entries((void *)(dmar + 1), | ||
586 | dmar->header.length - sizeof(*dmar), cb); | ||
587 | } | ||
588 | |||
506 | /** | 589 | /** |
507 | * parse_dmar_table - parses the DMA reporting table | 590 | * parse_dmar_table - parses the DMA reporting table |
508 | */ | 591 | */ |
@@ -510,9 +593,18 @@ static int __init | |||
510 | parse_dmar_table(void) | 593 | parse_dmar_table(void) |
511 | { | 594 | { |
512 | struct acpi_table_dmar *dmar; | 595 | struct acpi_table_dmar *dmar; |
513 | struct acpi_dmar_header *entry_header; | ||
514 | int ret = 0; | 596 | int ret = 0; |
515 | int drhd_count = 0; | 597 | int drhd_count = 0; |
598 | struct dmar_res_callback cb = { | ||
599 | .print_entry = true, | ||
600 | .ignore_unhandled = true, | ||
601 | .arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count, | ||
602 | .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd, | ||
603 | .cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr, | ||
604 | .cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr, | ||
605 | .cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa, | ||
606 | .cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd, | ||
607 | }; | ||
516 | 608 | ||
517 | /* | 609 | /* |
518 | * Do it again, earlier dmar_tbl mapping could be mapped with | 610 | * Do it again, earlier dmar_tbl mapping could be mapped with |
@@ -536,51 +628,10 @@ parse_dmar_table(void) | |||
536 | } | 628 | } |
537 | 629 | ||
538 | pr_info("Host address width %d\n", dmar->width + 1); | 630 | pr_info("Host address width %d\n", dmar->width + 1); |
539 | 631 | ret = dmar_walk_dmar_table(dmar, &cb); | |
540 | entry_header = (struct acpi_dmar_header *)(dmar + 1); | 632 | 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"); | 633 | pr_warn(FW_BUG "No DRHD structure found in DMAR table\n"); |
634 | |||
584 | return ret; | 635 | return ret; |
585 | } | 636 | } |
586 | 637 | ||
@@ -778,76 +829,68 @@ static void warn_invalid_dmar(u64 addr, const char *message) | |||
778 | dmi_get_system_info(DMI_PRODUCT_VERSION)); | 829 | dmi_get_system_info(DMI_PRODUCT_VERSION)); |
779 | } | 830 | } |
780 | 831 | ||
781 | static int __init check_zero_address(void) | 832 | static int __ref |
833 | dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg) | ||
782 | { | 834 | { |
783 | struct acpi_table_dmar *dmar; | ||
784 | struct acpi_dmar_header *entry_header; | ||
785 | struct acpi_dmar_hardware_unit *drhd; | 835 | struct acpi_dmar_hardware_unit *drhd; |
836 | void __iomem *addr; | ||
837 | u64 cap, ecap; | ||
786 | 838 | ||
787 | dmar = (struct acpi_table_dmar *)dmar_tbl; | 839 | drhd = (void *)entry; |
788 | entry_header = (struct acpi_dmar_header *)(dmar + 1); | 840 | if (!drhd->address) { |
789 | 841 | warn_invalid_dmar(0, ""); | |
790 | while (((unsigned long)entry_header) < | 842 | return -EINVAL; |
791 | (((unsigned long)dmar) + dmar_tbl->length)) { | 843 | } |
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 | 844 | ||
798 | if (entry_header->type == ACPI_DMAR_TYPE_HARDWARE_UNIT) { | 845 | if (arg) |
799 | void __iomem *addr; | 846 | addr = ioremap(drhd->address, VTD_PAGE_SIZE); |
800 | u64 cap, ecap; | 847 | else |
848 | addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); | ||
849 | if (!addr) { | ||
850 | pr_warn("IOMMU: can't validate: %llx\n", drhd->address); | ||
851 | return -EINVAL; | ||
852 | } | ||
801 | 853 | ||
802 | drhd = (void *)entry_header; | 854 | cap = dmar_readq(addr + DMAR_CAP_REG); |
803 | if (!drhd->address) { | 855 | ecap = dmar_readq(addr + DMAR_ECAP_REG); |
804 | warn_invalid_dmar(0, ""); | ||
805 | goto failed; | ||
806 | } | ||
807 | 856 | ||
808 | addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); | 857 | if (arg) |
809 | if (!addr ) { | 858 | iounmap(addr); |
810 | printk("IOMMU: can't validate: %llx\n", drhd->address); | 859 | else |
811 | goto failed; | 860 | early_iounmap(addr, VTD_PAGE_SIZE); |
812 | } | ||
813 | cap = dmar_readq(addr + DMAR_CAP_REG); | ||
814 | ecap = dmar_readq(addr + DMAR_ECAP_REG); | ||
815 | 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 | 861 | ||
823 | entry_header = ((void *)entry_header + entry_header->length); | 862 | if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) { |
863 | warn_invalid_dmar(drhd->address, " returns all ones"); | ||
864 | return -EINVAL; | ||
824 | } | 865 | } |
825 | return 1; | ||
826 | 866 | ||
827 | failed: | ||
828 | return 0; | 867 | return 0; |
829 | } | 868 | } |
830 | 869 | ||
831 | int __init detect_intel_iommu(void) | 870 | int __init detect_intel_iommu(void) |
832 | { | 871 | { |
833 | int ret; | 872 | int ret; |
873 | struct dmar_res_callback validate_drhd_cb = { | ||
874 | .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd, | ||
875 | .ignore_unhandled = true, | ||
876 | }; | ||
834 | 877 | ||
835 | down_write(&dmar_global_lock); | 878 | down_write(&dmar_global_lock); |
836 | ret = dmar_table_detect(); | 879 | ret = dmar_table_detect(); |
837 | if (ret) | 880 | if (ret) |
838 | ret = check_zero_address(); | 881 | ret = !dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl, |
839 | { | 882 | &validate_drhd_cb); |
840 | if (ret && !no_iommu && !iommu_detected && !dmar_disabled) { | 883 | if (ret && !no_iommu && !iommu_detected && !dmar_disabled) { |
841 | iommu_detected = 1; | 884 | iommu_detected = 1; |
842 | /* Make sure ACS will be enabled */ | 885 | /* Make sure ACS will be enabled */ |
843 | pci_request_acs(); | 886 | pci_request_acs(); |
844 | } | 887 | } |
845 | 888 | ||
846 | #ifdef CONFIG_X86 | 889 | #ifdef CONFIG_X86 |
847 | if (ret) | 890 | if (ret) |
848 | x86_init.iommu.iommu_init = intel_iommu_init; | 891 | x86_init.iommu.iommu_init = intel_iommu_init; |
849 | #endif | 892 | #endif |
850 | } | 893 | |
851 | early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size); | 894 | early_acpi_os_unmap_memory((void __iomem *)dmar_tbl, dmar_tbl_size); |
852 | dmar_tbl = NULL; | 895 | dmar_tbl = NULL; |
853 | up_write(&dmar_global_lock); | 896 | up_write(&dmar_global_lock); |
@@ -931,11 +974,32 @@ out: | |||
931 | return err; | 974 | return err; |
932 | } | 975 | } |
933 | 976 | ||
977 | static int dmar_alloc_seq_id(struct intel_iommu *iommu) | ||
978 | { | ||
979 | iommu->seq_id = find_first_zero_bit(dmar_seq_ids, | ||
980 | DMAR_UNITS_SUPPORTED); | ||
981 | if (iommu->seq_id >= DMAR_UNITS_SUPPORTED) { | ||
982 | iommu->seq_id = -1; | ||
983 | } else { | ||
984 | set_bit(iommu->seq_id, dmar_seq_ids); | ||
985 | sprintf(iommu->name, "dmar%d", iommu->seq_id); | ||
986 | } | ||
987 | |||
988 | return iommu->seq_id; | ||
989 | } | ||
990 | |||
991 | static void dmar_free_seq_id(struct intel_iommu *iommu) | ||
992 | { | ||
993 | if (iommu->seq_id >= 0) { | ||
994 | clear_bit(iommu->seq_id, dmar_seq_ids); | ||
995 | iommu->seq_id = -1; | ||
996 | } | ||
997 | } | ||
998 | |||
934 | static int alloc_iommu(struct dmar_drhd_unit *drhd) | 999 | static int alloc_iommu(struct dmar_drhd_unit *drhd) |
935 | { | 1000 | { |
936 | struct intel_iommu *iommu; | 1001 | struct intel_iommu *iommu; |
937 | u32 ver, sts; | 1002 | u32 ver, sts; |
938 | static int iommu_allocated = 0; | ||
939 | int agaw = 0; | 1003 | int agaw = 0; |
940 | int msagaw = 0; | 1004 | int msagaw = 0; |
941 | int err; | 1005 | int err; |
@@ -949,13 +1013,16 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
949 | if (!iommu) | 1013 | if (!iommu) |
950 | return -ENOMEM; | 1014 | return -ENOMEM; |
951 | 1015 | ||
952 | iommu->seq_id = iommu_allocated++; | 1016 | if (dmar_alloc_seq_id(iommu) < 0) { |
953 | sprintf (iommu->name, "dmar%d", iommu->seq_id); | 1017 | pr_err("IOMMU: failed to allocate seq_id\n"); |
1018 | err = -ENOSPC; | ||
1019 | goto error; | ||
1020 | } | ||
954 | 1021 | ||
955 | err = map_iommu(iommu, drhd->reg_base_addr); | 1022 | err = map_iommu(iommu, drhd->reg_base_addr); |
956 | if (err) { | 1023 | if (err) { |
957 | pr_err("IOMMU: failed to map %s\n", iommu->name); | 1024 | pr_err("IOMMU: failed to map %s\n", iommu->name); |
958 | goto error; | 1025 | goto error_free_seq_id; |
959 | } | 1026 | } |
960 | 1027 | ||
961 | err = -EINVAL; | 1028 | err = -EINVAL; |
@@ -1005,9 +1072,11 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
1005 | 1072 | ||
1006 | return 0; | 1073 | return 0; |
1007 | 1074 | ||
1008 | err_unmap: | 1075 | err_unmap: |
1009 | unmap_iommu(iommu); | 1076 | unmap_iommu(iommu); |
1010 | error: | 1077 | error_free_seq_id: |
1078 | dmar_free_seq_id(iommu); | ||
1079 | error: | ||
1011 | kfree(iommu); | 1080 | kfree(iommu); |
1012 | return err; | 1081 | return err; |
1013 | } | 1082 | } |
@@ -1031,6 +1100,7 @@ static void free_iommu(struct intel_iommu *iommu) | |||
1031 | if (iommu->reg) | 1100 | if (iommu->reg) |
1032 | unmap_iommu(iommu); | 1101 | unmap_iommu(iommu); |
1033 | 1102 | ||
1103 | dmar_free_seq_id(iommu); | ||
1034 | kfree(iommu); | 1104 | kfree(iommu); |
1035 | } | 1105 | } |
1036 | 1106 | ||
@@ -1661,12 +1731,17 @@ int __init dmar_ir_support(void) | |||
1661 | return dmar->flags & 0x1; | 1731 | return dmar->flags & 0x1; |
1662 | } | 1732 | } |
1663 | 1733 | ||
1734 | /* Check whether DMAR units are in use */ | ||
1735 | static inline bool dmar_in_use(void) | ||
1736 | { | ||
1737 | return irq_remapping_enabled || intel_iommu_enabled; | ||
1738 | } | ||
1739 | |||
1664 | static int __init dmar_free_unused_resources(void) | 1740 | static int __init dmar_free_unused_resources(void) |
1665 | { | 1741 | { |
1666 | struct dmar_drhd_unit *dmaru, *dmaru_n; | 1742 | struct dmar_drhd_unit *dmaru, *dmaru_n; |
1667 | 1743 | ||
1668 | /* DMAR units are in use */ | 1744 | if (dmar_in_use()) |
1669 | if (irq_remapping_enabled || intel_iommu_enabled) | ||
1670 | return 0; | 1745 | return 0; |
1671 | 1746 | ||
1672 | if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units)) | 1747 | if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units)) |
@@ -1684,3 +1759,242 @@ static int __init dmar_free_unused_resources(void) | |||
1684 | 1759 | ||
1685 | late_initcall(dmar_free_unused_resources); | 1760 | late_initcall(dmar_free_unused_resources); |
1686 | IOMMU_INIT_POST(detect_intel_iommu); | 1761 | IOMMU_INIT_POST(detect_intel_iommu); |
1762 | |||
1763 | /* | ||
1764 | * DMAR Hotplug Support | ||
1765 | * For more details, please refer to Intel(R) Virtualization Technology | ||
1766 | * for Directed-IO Architecture Specifiction, Rev 2.2, Section 8.8 | ||
1767 | * "Remapping Hardware Unit Hot Plug". | ||
1768 | */ | ||
1769 | static u8 dmar_hp_uuid[] = { | ||
1770 | /* 0000 */ 0xA6, 0xA3, 0xC1, 0xD8, 0x9B, 0xBE, 0x9B, 0x4C, | ||
1771 | /* 0008 */ 0x91, 0xBF, 0xC3, 0xCB, 0x81, 0xFC, 0x5D, 0xAF | ||
1772 | }; | ||
1773 | |||
1774 | /* | ||
1775 | * Currently there's only one revision and BIOS will not check the revision id, | ||
1776 | * so use 0 for safety. | ||
1777 | */ | ||
1778 | #define DMAR_DSM_REV_ID 0 | ||
1779 | #define DMAR_DSM_FUNC_DRHD 1 | ||
1780 | #define DMAR_DSM_FUNC_ATSR 2 | ||
1781 | #define DMAR_DSM_FUNC_RHSA 3 | ||
1782 | |||
1783 | static inline bool dmar_detect_dsm(acpi_handle handle, int func) | ||
1784 | { | ||
1785 | return acpi_check_dsm(handle, dmar_hp_uuid, DMAR_DSM_REV_ID, 1 << func); | ||
1786 | } | ||
1787 | |||
1788 | static int dmar_walk_dsm_resource(acpi_handle handle, int func, | ||
1789 | dmar_res_handler_t handler, void *arg) | ||
1790 | { | ||
1791 | int ret = -ENODEV; | ||
1792 | union acpi_object *obj; | ||
1793 | struct acpi_dmar_header *start; | ||
1794 | struct dmar_res_callback callback; | ||
1795 | static int res_type[] = { | ||
1796 | [DMAR_DSM_FUNC_DRHD] = ACPI_DMAR_TYPE_HARDWARE_UNIT, | ||
1797 | [DMAR_DSM_FUNC_ATSR] = ACPI_DMAR_TYPE_ROOT_ATS, | ||
1798 | [DMAR_DSM_FUNC_RHSA] = ACPI_DMAR_TYPE_HARDWARE_AFFINITY, | ||
1799 | }; | ||
1800 | |||
1801 | if (!dmar_detect_dsm(handle, func)) | ||
1802 | return 0; | ||
1803 | |||
1804 | obj = acpi_evaluate_dsm_typed(handle, dmar_hp_uuid, DMAR_DSM_REV_ID, | ||
1805 | func, NULL, ACPI_TYPE_BUFFER); | ||
1806 | if (!obj) | ||
1807 | return -ENODEV; | ||
1808 | |||
1809 | memset(&callback, 0, sizeof(callback)); | ||
1810 | callback.cb[res_type[func]] = handler; | ||
1811 | callback.arg[res_type[func]] = arg; | ||
1812 | start = (struct acpi_dmar_header *)obj->buffer.pointer; | ||
1813 | ret = dmar_walk_remapping_entries(start, obj->buffer.length, &callback); | ||
1814 | |||
1815 | ACPI_FREE(obj); | ||
1816 | |||
1817 | return ret; | ||
1818 | } | ||
1819 | |||
1820 | static int dmar_hp_add_drhd(struct acpi_dmar_header *header, void *arg) | ||
1821 | { | ||
1822 | int ret; | ||
1823 | struct dmar_drhd_unit *dmaru; | ||
1824 | |||
1825 | dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header); | ||
1826 | if (!dmaru) | ||
1827 | return -ENODEV; | ||
1828 | |||
1829 | ret = dmar_ir_hotplug(dmaru, true); | ||
1830 | if (ret == 0) | ||
1831 | ret = dmar_iommu_hotplug(dmaru, true); | ||
1832 | |||
1833 | return ret; | ||
1834 | } | ||
1835 | |||
1836 | static int dmar_hp_remove_drhd(struct acpi_dmar_header *header, void *arg) | ||
1837 | { | ||
1838 | int i, ret; | ||
1839 | struct device *dev; | ||
1840 | struct dmar_drhd_unit *dmaru; | ||
1841 | |||
1842 | dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header); | ||
1843 | if (!dmaru) | ||
1844 | return 0; | ||
1845 | |||
1846 | /* | ||
1847 | * All PCI devices managed by this unit should have been destroyed. | ||
1848 | */ | ||
1849 | if (!dmaru->include_all && dmaru->devices && dmaru->devices_cnt) | ||
1850 | for_each_active_dev_scope(dmaru->devices, | ||
1851 | dmaru->devices_cnt, i, dev) | ||
1852 | return -EBUSY; | ||
1853 | |||
1854 | ret = dmar_ir_hotplug(dmaru, false); | ||
1855 | if (ret == 0) | ||
1856 | ret = dmar_iommu_hotplug(dmaru, false); | ||
1857 | |||
1858 | return ret; | ||
1859 | } | ||
1860 | |||
1861 | static int dmar_hp_release_drhd(struct acpi_dmar_header *header, void *arg) | ||
1862 | { | ||
1863 | struct dmar_drhd_unit *dmaru; | ||
1864 | |||
1865 | dmaru = dmar_find_dmaru((struct acpi_dmar_hardware_unit *)header); | ||
1866 | if (dmaru) { | ||
1867 | list_del_rcu(&dmaru->list); | ||
1868 | synchronize_rcu(); | ||
1869 | dmar_free_drhd(dmaru); | ||
1870 | } | ||
1871 | |||
1872 | return 0; | ||
1873 | } | ||
1874 | |||
1875 | static int dmar_hotplug_insert(acpi_handle handle) | ||
1876 | { | ||
1877 | int ret; | ||
1878 | int drhd_count = 0; | ||
1879 | |||
1880 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1881 | &dmar_validate_one_drhd, (void *)1); | ||
1882 | if (ret) | ||
1883 | goto out; | ||
1884 | |||
1885 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1886 | &dmar_parse_one_drhd, (void *)&drhd_count); | ||
1887 | if (ret == 0 && drhd_count == 0) { | ||
1888 | pr_warn(FW_BUG "No DRHD structures in buffer returned by _DSM method\n"); | ||
1889 | goto out; | ||
1890 | } else if (ret) { | ||
1891 | goto release_drhd; | ||
1892 | } | ||
1893 | |||
1894 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_RHSA, | ||
1895 | &dmar_parse_one_rhsa, NULL); | ||
1896 | if (ret) | ||
1897 | goto release_drhd; | ||
1898 | |||
1899 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR, | ||
1900 | &dmar_parse_one_atsr, NULL); | ||
1901 | if (ret) | ||
1902 | goto release_atsr; | ||
1903 | |||
1904 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1905 | &dmar_hp_add_drhd, NULL); | ||
1906 | if (!ret) | ||
1907 | return 0; | ||
1908 | |||
1909 | dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1910 | &dmar_hp_remove_drhd, NULL); | ||
1911 | release_atsr: | ||
1912 | dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR, | ||
1913 | &dmar_release_one_atsr, NULL); | ||
1914 | release_drhd: | ||
1915 | dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1916 | &dmar_hp_release_drhd, NULL); | ||
1917 | out: | ||
1918 | return ret; | ||
1919 | } | ||
1920 | |||
1921 | static int dmar_hotplug_remove(acpi_handle handle) | ||
1922 | { | ||
1923 | int ret; | ||
1924 | |||
1925 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR, | ||
1926 | &dmar_check_one_atsr, NULL); | ||
1927 | if (ret) | ||
1928 | return ret; | ||
1929 | |||
1930 | ret = dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1931 | &dmar_hp_remove_drhd, NULL); | ||
1932 | if (ret == 0) { | ||
1933 | WARN_ON(dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_ATSR, | ||
1934 | &dmar_release_one_atsr, NULL)); | ||
1935 | WARN_ON(dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1936 | &dmar_hp_release_drhd, NULL)); | ||
1937 | } else { | ||
1938 | dmar_walk_dsm_resource(handle, DMAR_DSM_FUNC_DRHD, | ||
1939 | &dmar_hp_add_drhd, NULL); | ||
1940 | } | ||
1941 | |||
1942 | return ret; | ||
1943 | } | ||
1944 | |||
1945 | static acpi_status dmar_get_dsm_handle(acpi_handle handle, u32 lvl, | ||
1946 | void *context, void **retval) | ||
1947 | { | ||
1948 | acpi_handle *phdl = retval; | ||
1949 | |||
1950 | if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) { | ||
1951 | *phdl = handle; | ||
1952 | return AE_CTRL_TERMINATE; | ||
1953 | } | ||
1954 | |||
1955 | return AE_OK; | ||
1956 | } | ||
1957 | |||
1958 | static int dmar_device_hotplug(acpi_handle handle, bool insert) | ||
1959 | { | ||
1960 | int ret; | ||
1961 | acpi_handle tmp = NULL; | ||
1962 | acpi_status status; | ||
1963 | |||
1964 | if (!dmar_in_use()) | ||
1965 | return 0; | ||
1966 | |||
1967 | if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) { | ||
1968 | tmp = handle; | ||
1969 | } else { | ||
1970 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, | ||
1971 | ACPI_UINT32_MAX, | ||
1972 | dmar_get_dsm_handle, | ||
1973 | NULL, NULL, &tmp); | ||
1974 | if (ACPI_FAILURE(status)) { | ||
1975 | pr_warn("Failed to locate _DSM method.\n"); | ||
1976 | return -ENXIO; | ||
1977 | } | ||
1978 | } | ||
1979 | if (tmp == NULL) | ||
1980 | return 0; | ||
1981 | |||
1982 | down_write(&dmar_global_lock); | ||
1983 | if (insert) | ||
1984 | ret = dmar_hotplug_insert(tmp); | ||
1985 | else | ||
1986 | ret = dmar_hotplug_remove(tmp); | ||
1987 | up_write(&dmar_global_lock); | ||
1988 | |||
1989 | return ret; | ||
1990 | } | ||
1991 | |||
1992 | int dmar_device_add(acpi_handle handle) | ||
1993 | { | ||
1994 | return dmar_device_hotplug(handle, true); | ||
1995 | } | ||
1996 | |||
1997 | int dmar_device_remove(acpi_handle handle) | ||
1998 | { | ||
1999 | return dmar_device_hotplug(handle, false); | ||
2000 | } | ||