diff options
-rw-r--r-- | arch/powerpc/include/asm/iommu.h | 11 | ||||
-rw-r--r-- | arch/powerpc/kernel/iommu.c | 12 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 43 | ||||
-rw-r--r-- | drivers/vfio/vfio_iommu_spapr_tce.c | 70 |
4 files changed, 103 insertions, 33 deletions
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h index 44a20ccf06b4..489133cf7c5e 100644 --- a/arch/powerpc/include/asm/iommu.h +++ b/arch/powerpc/include/asm/iommu.h | |||
@@ -93,7 +93,6 @@ struct iommu_table { | |||
93 | unsigned long it_page_shift;/* table iommu page size */ | 93 | unsigned long it_page_shift;/* table iommu page size */ |
94 | struct list_head it_group_list;/* List of iommu_table_group_link */ | 94 | struct list_head it_group_list;/* List of iommu_table_group_link */ |
95 | struct iommu_table_ops *it_ops; | 95 | struct iommu_table_ops *it_ops; |
96 | void (*set_bypass)(struct iommu_table *tbl, bool enable); | ||
97 | }; | 96 | }; |
98 | 97 | ||
99 | /* Pure 2^n version of get_order */ | 98 | /* Pure 2^n version of get_order */ |
@@ -126,6 +125,15 @@ extern struct iommu_table *iommu_init_table(struct iommu_table * tbl, | |||
126 | int nid); | 125 | int nid); |
127 | #define IOMMU_TABLE_GROUP_MAX_TABLES 1 | 126 | #define IOMMU_TABLE_GROUP_MAX_TABLES 1 |
128 | 127 | ||
128 | struct iommu_table_group; | ||
129 | |||
130 | struct iommu_table_group_ops { | ||
131 | /* Switch ownership from platform code to external user (e.g. VFIO) */ | ||
132 | void (*take_ownership)(struct iommu_table_group *table_group); | ||
133 | /* Switch ownership from external user (e.g. VFIO) back to core */ | ||
134 | void (*release_ownership)(struct iommu_table_group *table_group); | ||
135 | }; | ||
136 | |||
129 | struct iommu_table_group_link { | 137 | struct iommu_table_group_link { |
130 | struct list_head next; | 138 | struct list_head next; |
131 | struct rcu_head rcu; | 139 | struct rcu_head rcu; |
@@ -135,6 +143,7 @@ struct iommu_table_group_link { | |||
135 | struct iommu_table_group { | 143 | struct iommu_table_group { |
136 | struct iommu_group *group; | 144 | struct iommu_group *group; |
137 | struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES]; | 145 | struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES]; |
146 | struct iommu_table_group_ops *ops; | ||
138 | }; | 147 | }; |
139 | 148 | ||
140 | #ifdef CONFIG_IOMMU_API | 149 | #ifdef CONFIG_IOMMU_API |
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index be258b2ecb10..e7f81b7399ba 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c | |||
@@ -1047,14 +1047,6 @@ int iommu_take_ownership(struct iommu_table *tbl) | |||
1047 | 1047 | ||
1048 | memset(tbl->it_map, 0xff, sz); | 1048 | memset(tbl->it_map, 0xff, sz); |
1049 | 1049 | ||
1050 | /* | ||
1051 | * Disable iommu bypass, otherwise the user can DMA to all of | ||
1052 | * our physical memory via the bypass window instead of just | ||
1053 | * the pages that has been explicitly mapped into the iommu | ||
1054 | */ | ||
1055 | if (tbl->set_bypass) | ||
1056 | tbl->set_bypass(tbl, false); | ||
1057 | |||
1058 | return 0; | 1050 | return 0; |
1059 | } | 1051 | } |
1060 | EXPORT_SYMBOL_GPL(iommu_take_ownership); | 1052 | EXPORT_SYMBOL_GPL(iommu_take_ownership); |
@@ -1068,10 +1060,6 @@ void iommu_release_ownership(struct iommu_table *tbl) | |||
1068 | /* Restore bit#0 set by iommu_init_table() */ | 1060 | /* Restore bit#0 set by iommu_init_table() */ |
1069 | if (tbl->it_offset == 0) | 1061 | if (tbl->it_offset == 0) |
1070 | set_bit(0, tbl->it_map); | 1062 | set_bit(0, tbl->it_map); |
1071 | |||
1072 | /* The kernel owns the device now, we can restore the iommu bypass */ | ||
1073 | if (tbl->set_bypass) | ||
1074 | tbl->set_bypass(tbl, true); | ||
1075 | } | 1063 | } |
1076 | EXPORT_SYMBOL_GPL(iommu_release_ownership); | 1064 | EXPORT_SYMBOL_GPL(iommu_release_ownership); |
1077 | 1065 | ||
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 3b4130697b05..17a77522ea0d 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -1919,13 +1919,8 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, | |||
1919 | } | 1919 | } |
1920 | } | 1920 | } |
1921 | 1921 | ||
1922 | static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable) | 1922 | static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable) |
1923 | { | 1923 | { |
1924 | struct iommu_table_group_link *tgl = list_first_entry_or_null( | ||
1925 | &tbl->it_group_list, struct iommu_table_group_link, | ||
1926 | next); | ||
1927 | struct pnv_ioda_pe *pe = container_of(tgl->table_group, | ||
1928 | struct pnv_ioda_pe, table_group); | ||
1929 | uint16_t window_id = (pe->pe_number << 1 ) + 1; | 1924 | uint16_t window_id = (pe->pe_number << 1 ) + 1; |
1930 | int64_t rc; | 1925 | int64_t rc; |
1931 | 1926 | ||
@@ -1952,19 +1947,31 @@ static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable) | |||
1952 | pe->tce_bypass_enabled = enable; | 1947 | pe->tce_bypass_enabled = enable; |
1953 | } | 1948 | } |
1954 | 1949 | ||
1955 | static void pnv_pci_ioda2_setup_bypass_pe(struct pnv_phb *phb, | 1950 | #ifdef CONFIG_IOMMU_API |
1956 | struct pnv_ioda_pe *pe) | 1951 | static void pnv_ioda2_take_ownership(struct iommu_table_group *table_group) |
1957 | { | 1952 | { |
1958 | /* TVE #1 is selected by PCI address bit 59 */ | 1953 | struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe, |
1959 | pe->tce_bypass_base = 1ull << 59; | 1954 | table_group); |
1960 | 1955 | ||
1961 | /* Install set_bypass callback for VFIO */ | 1956 | iommu_take_ownership(table_group->tables[0]); |
1962 | pe->table_group.tables[0]->set_bypass = pnv_pci_ioda2_set_bypass; | 1957 | pnv_pci_ioda2_set_bypass(pe, false); |
1958 | } | ||
1963 | 1959 | ||
1964 | /* Enable bypass by default */ | 1960 | static void pnv_ioda2_release_ownership(struct iommu_table_group *table_group) |
1965 | pnv_pci_ioda2_set_bypass(pe->table_group.tables[0], true); | 1961 | { |
1962 | struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe, | ||
1963 | table_group); | ||
1964 | |||
1965 | iommu_release_ownership(table_group->tables[0]); | ||
1966 | pnv_pci_ioda2_set_bypass(pe, true); | ||
1966 | } | 1967 | } |
1967 | 1968 | ||
1969 | static struct iommu_table_group_ops pnv_pci_ioda2_ops = { | ||
1970 | .take_ownership = pnv_ioda2_take_ownership, | ||
1971 | .release_ownership = pnv_ioda2_release_ownership, | ||
1972 | }; | ||
1973 | #endif | ||
1974 | |||
1968 | static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, | 1975 | static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, |
1969 | struct pnv_ioda_pe *pe) | 1976 | struct pnv_ioda_pe *pe) |
1970 | { | 1977 | { |
@@ -1979,6 +1986,9 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, | |||
1979 | if (WARN_ON(pe->tce32_seg >= 0)) | 1986 | if (WARN_ON(pe->tce32_seg >= 0)) |
1980 | return; | 1987 | return; |
1981 | 1988 | ||
1989 | /* TVE #1 is selected by PCI address bit 59 */ | ||
1990 | pe->tce_bypass_base = 1ull << 59; | ||
1991 | |||
1982 | tbl = pnv_pci_table_alloc(phb->hose->node); | 1992 | tbl = pnv_pci_table_alloc(phb->hose->node); |
1983 | iommu_register_group(&pe->table_group, phb->hose->global_number, | 1993 | iommu_register_group(&pe->table_group, phb->hose->global_number, |
1984 | pe->pe_number); | 1994 | pe->pe_number); |
@@ -2033,6 +2043,9 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, | |||
2033 | } | 2043 | } |
2034 | tbl->it_ops = &pnv_ioda2_iommu_ops; | 2044 | tbl->it_ops = &pnv_ioda2_iommu_ops; |
2035 | iommu_init_table(tbl, phb->hose->node); | 2045 | iommu_init_table(tbl, phb->hose->node); |
2046 | #ifdef CONFIG_IOMMU_API | ||
2047 | pe->table_group.ops = &pnv_pci_ioda2_ops; | ||
2048 | #endif | ||
2036 | 2049 | ||
2037 | if (pe->flags & PNV_IODA_PE_DEV) { | 2050 | if (pe->flags & PNV_IODA_PE_DEV) { |
2038 | /* | 2051 | /* |
@@ -2047,7 +2060,7 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, | |||
2047 | 2060 | ||
2048 | /* Also create a bypass window */ | 2061 | /* Also create a bypass window */ |
2049 | if (!pnv_iommu_bypass_disabled) | 2062 | if (!pnv_iommu_bypass_disabled) |
2050 | pnv_pci_ioda2_setup_bypass_pe(phb, pe); | 2063 | pnv_pci_ioda2_set_bypass(pe, true); |
2051 | 2064 | ||
2052 | return; | 2065 | return; |
2053 | fail: | 2066 | fail: |
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c index ffc634a75dba..9c720de46c33 100644 --- a/drivers/vfio/vfio_iommu_spapr_tce.c +++ b/drivers/vfio/vfio_iommu_spapr_tce.c | |||
@@ -486,6 +486,61 @@ static long tce_iommu_ioctl(void *iommu_data, | |||
486 | return -ENOTTY; | 486 | return -ENOTTY; |
487 | } | 487 | } |
488 | 488 | ||
489 | static void tce_iommu_release_ownership(struct tce_container *container, | ||
490 | struct iommu_table_group *table_group) | ||
491 | { | ||
492 | int i; | ||
493 | |||
494 | for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { | ||
495 | struct iommu_table *tbl = table_group->tables[i]; | ||
496 | |||
497 | if (!tbl) | ||
498 | continue; | ||
499 | |||
500 | tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size); | ||
501 | if (tbl->it_map) | ||
502 | iommu_release_ownership(tbl); | ||
503 | } | ||
504 | } | ||
505 | |||
506 | static int tce_iommu_take_ownership(struct tce_container *container, | ||
507 | struct iommu_table_group *table_group) | ||
508 | { | ||
509 | int i, j, rc = 0; | ||
510 | |||
511 | for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) { | ||
512 | struct iommu_table *tbl = table_group->tables[i]; | ||
513 | |||
514 | if (!tbl || !tbl->it_map) | ||
515 | continue; | ||
516 | |||
517 | rc = iommu_take_ownership(tbl); | ||
518 | if (rc) { | ||
519 | for (j = 0; j < i; ++j) | ||
520 | iommu_release_ownership( | ||
521 | table_group->tables[j]); | ||
522 | |||
523 | return rc; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static void tce_iommu_release_ownership_ddw(struct tce_container *container, | ||
531 | struct iommu_table_group *table_group) | ||
532 | { | ||
533 | table_group->ops->release_ownership(table_group); | ||
534 | } | ||
535 | |||
536 | static long tce_iommu_take_ownership_ddw(struct tce_container *container, | ||
537 | struct iommu_table_group *table_group) | ||
538 | { | ||
539 | table_group->ops->take_ownership(table_group); | ||
540 | |||
541 | return 0; | ||
542 | } | ||
543 | |||
489 | static int tce_iommu_attach_group(void *iommu_data, | 544 | static int tce_iommu_attach_group(void *iommu_data, |
490 | struct iommu_group *iommu_group) | 545 | struct iommu_group *iommu_group) |
491 | { | 546 | { |
@@ -518,7 +573,12 @@ static int tce_iommu_attach_group(void *iommu_data, | |||
518 | goto unlock_exit; | 573 | goto unlock_exit; |
519 | } | 574 | } |
520 | 575 | ||
521 | ret = iommu_take_ownership(table_group->tables[0]); | 576 | if (!table_group->ops || !table_group->ops->take_ownership || |
577 | !table_group->ops->release_ownership) | ||
578 | ret = tce_iommu_take_ownership(container, table_group); | ||
579 | else | ||
580 | ret = tce_iommu_take_ownership_ddw(container, table_group); | ||
581 | |||
522 | if (!ret) | 582 | if (!ret) |
523 | container->grp = iommu_group; | 583 | container->grp = iommu_group; |
524 | 584 | ||
@@ -533,7 +593,6 @@ static void tce_iommu_detach_group(void *iommu_data, | |||
533 | { | 593 | { |
534 | struct tce_container *container = iommu_data; | 594 | struct tce_container *container = iommu_data; |
535 | struct iommu_table_group *table_group; | 595 | struct iommu_table_group *table_group; |
536 | struct iommu_table *tbl; | ||
537 | 596 | ||
538 | mutex_lock(&container->lock); | 597 | mutex_lock(&container->lock); |
539 | if (iommu_group != container->grp) { | 598 | if (iommu_group != container->grp) { |
@@ -556,9 +615,10 @@ static void tce_iommu_detach_group(void *iommu_data, | |||
556 | table_group = iommu_group_get_iommudata(iommu_group); | 615 | table_group = iommu_group_get_iommudata(iommu_group); |
557 | BUG_ON(!table_group); | 616 | BUG_ON(!table_group); |
558 | 617 | ||
559 | tbl = table_group->tables[0]; | 618 | if (!table_group->ops || !table_group->ops->release_ownership) |
560 | tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size); | 619 | tce_iommu_release_ownership(container, table_group); |
561 | iommu_release_ownership(tbl); | 620 | else |
621 | tce_iommu_release_ownership_ddw(container, table_group); | ||
562 | 622 | ||
563 | unlock_exit: | 623 | unlock_exit: |
564 | mutex_unlock(&container->lock); | 624 | mutex_unlock(&container->lock); |