diff options
author | Joerg Roedel <joerg.roedel@amd.com> | 2011-11-17 11:24:28 -0500 |
---|---|---|
committer | Joerg Roedel <joerg.roedel@amd.com> | 2011-12-12 09:18:57 -0500 |
commit | 52815b75682e25db45545911fd2b09ef5856e695 (patch) | |
tree | a54d7812e6d68d0663e6929f29dadf0ce5f74c59 /drivers/iommu | |
parent | 132bd68f180dd5de9176e20532910503f6393f14 (diff) |
iommu/amd: Add support for IOMMUv2 domain mode
This patch adds support for protection domains that
implement two-level paging for devices.
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/Kconfig | 4 | ||||
-rw-r--r-- | drivers/iommu/amd_iommu.c | 144 | ||||
-rw-r--r-- | drivers/iommu/amd_iommu_init.c | 9 | ||||
-rw-r--r-- | drivers/iommu/amd_iommu_proto.h | 1 | ||||
-rw-r--r-- | drivers/iommu/amd_iommu_types.h | 27 |
5 files changed, 180 insertions, 5 deletions
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 5414253b185a..220dfc24b311 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
@@ -34,7 +34,9 @@ config AMD_IOMMU | |||
34 | bool "AMD IOMMU support" | 34 | bool "AMD IOMMU support" |
35 | select SWIOTLB | 35 | select SWIOTLB |
36 | select PCI_MSI | 36 | select PCI_MSI |
37 | select PCI_IOV | 37 | select PCI_ATS |
38 | select PCI_PRI | ||
39 | select PCI_PASID | ||
38 | select IOMMU_API | 40 | select IOMMU_API |
39 | depends on X86_64 && PCI && ACPI | 41 | depends on X86_64 && PCI && ACPI |
40 | ---help--- | 42 | ---help--- |
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 6ed536769102..7dda0d4a8f8c 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c | |||
@@ -63,6 +63,7 @@ static struct protection_domain *pt_domain; | |||
63 | static struct iommu_ops amd_iommu_ops; | 63 | static struct iommu_ops amd_iommu_ops; |
64 | 64 | ||
65 | static ATOMIC_NOTIFIER_HEAD(ppr_notifier); | 65 | static ATOMIC_NOTIFIER_HEAD(ppr_notifier); |
66 | int amd_iommu_max_glx_val = -1; | ||
66 | 67 | ||
67 | /* | 68 | /* |
68 | * general struct to manage commands send to an IOMMU | 69 | * general struct to manage commands send to an IOMMU |
@@ -1598,6 +1599,11 @@ static void free_pagetable(struct protection_domain *domain) | |||
1598 | domain->pt_root = NULL; | 1599 | domain->pt_root = NULL; |
1599 | } | 1600 | } |
1600 | 1601 | ||
1602 | static void free_gcr3_table(struct protection_domain *domain) | ||
1603 | { | ||
1604 | free_page((unsigned long)domain->gcr3_tbl); | ||
1605 | } | ||
1606 | |||
1601 | /* | 1607 | /* |
1602 | * Free a domain, only used if something went wrong in the | 1608 | * Free a domain, only used if something went wrong in the |
1603 | * allocation path and we need to free an already allocated page table | 1609 | * allocation path and we need to free an already allocated page table |
@@ -1699,6 +1705,32 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats) | |||
1699 | if (ats) | 1705 | if (ats) |
1700 | flags |= DTE_FLAG_IOTLB; | 1706 | flags |= DTE_FLAG_IOTLB; |
1701 | 1707 | ||
1708 | if (domain->flags & PD_IOMMUV2_MASK) { | ||
1709 | u64 gcr3 = __pa(domain->gcr3_tbl); | ||
1710 | u64 glx = domain->glx; | ||
1711 | u64 tmp; | ||
1712 | |||
1713 | pte_root |= DTE_FLAG_GV; | ||
1714 | pte_root |= (glx & DTE_GLX_MASK) << DTE_GLX_SHIFT; | ||
1715 | |||
1716 | /* First mask out possible old values for GCR3 table */ | ||
1717 | tmp = DTE_GCR3_VAL_B(~0ULL) << DTE_GCR3_SHIFT_B; | ||
1718 | flags &= ~tmp; | ||
1719 | |||
1720 | tmp = DTE_GCR3_VAL_C(~0ULL) << DTE_GCR3_SHIFT_C; | ||
1721 | flags &= ~tmp; | ||
1722 | |||
1723 | /* Encode GCR3 table into DTE */ | ||
1724 | tmp = DTE_GCR3_VAL_A(gcr3) << DTE_GCR3_SHIFT_A; | ||
1725 | pte_root |= tmp; | ||
1726 | |||
1727 | tmp = DTE_GCR3_VAL_B(gcr3) << DTE_GCR3_SHIFT_B; | ||
1728 | flags |= tmp; | ||
1729 | |||
1730 | tmp = DTE_GCR3_VAL_C(gcr3) << DTE_GCR3_SHIFT_C; | ||
1731 | flags |= tmp; | ||
1732 | } | ||
1733 | |||
1702 | flags &= ~(0xffffUL); | 1734 | flags &= ~(0xffffUL); |
1703 | flags |= domain->id; | 1735 | flags |= domain->id; |
1704 | 1736 | ||
@@ -1803,6 +1835,46 @@ out_unlock: | |||
1803 | return ret; | 1835 | return ret; |
1804 | } | 1836 | } |
1805 | 1837 | ||
1838 | |||
1839 | static void pdev_iommuv2_disable(struct pci_dev *pdev) | ||
1840 | { | ||
1841 | pci_disable_ats(pdev); | ||
1842 | pci_disable_pri(pdev); | ||
1843 | pci_disable_pasid(pdev); | ||
1844 | } | ||
1845 | |||
1846 | static int pdev_iommuv2_enable(struct pci_dev *pdev) | ||
1847 | { | ||
1848 | int ret; | ||
1849 | |||
1850 | /* Only allow access to user-accessible pages */ | ||
1851 | ret = pci_enable_pasid(pdev, 0); | ||
1852 | if (ret) | ||
1853 | goto out_err; | ||
1854 | |||
1855 | /* First reset the PRI state of the device */ | ||
1856 | ret = pci_reset_pri(pdev); | ||
1857 | if (ret) | ||
1858 | goto out_err; | ||
1859 | |||
1860 | /* FIXME: Hardcode number of outstanding requests for now */ | ||
1861 | ret = pci_enable_pri(pdev, 32); | ||
1862 | if (ret) | ||
1863 | goto out_err; | ||
1864 | |||
1865 | ret = pci_enable_ats(pdev, PAGE_SHIFT); | ||
1866 | if (ret) | ||
1867 | goto out_err; | ||
1868 | |||
1869 | return 0; | ||
1870 | |||
1871 | out_err: | ||
1872 | pci_disable_pri(pdev); | ||
1873 | pci_disable_pasid(pdev); | ||
1874 | |||
1875 | return ret; | ||
1876 | } | ||
1877 | |||
1806 | /* | 1878 | /* |
1807 | * If a device is not yet associated with a domain, this function does | 1879 | * If a device is not yet associated with a domain, this function does |
1808 | * assigns it visible for the hardware | 1880 | * assigns it visible for the hardware |
@@ -1817,7 +1889,17 @@ static int attach_device(struct device *dev, | |||
1817 | 1889 | ||
1818 | dev_data = get_dev_data(dev); | 1890 | dev_data = get_dev_data(dev); |
1819 | 1891 | ||
1820 | if (amd_iommu_iotlb_sup && pci_enable_ats(pdev, PAGE_SHIFT) == 0) { | 1892 | if (domain->flags & PD_IOMMUV2_MASK) { |
1893 | if (!dev_data->iommu_v2 || !dev_data->passthrough) | ||
1894 | return -EINVAL; | ||
1895 | |||
1896 | if (pdev_iommuv2_enable(pdev) != 0) | ||
1897 | return -EINVAL; | ||
1898 | |||
1899 | dev_data->ats.enabled = true; | ||
1900 | dev_data->ats.qdep = pci_ats_queue_depth(pdev); | ||
1901 | } else if (amd_iommu_iotlb_sup && | ||
1902 | pci_enable_ats(pdev, PAGE_SHIFT) == 0) { | ||
1821 | dev_data->ats.enabled = true; | 1903 | dev_data->ats.enabled = true; |
1822 | dev_data->ats.qdep = pci_ats_queue_depth(pdev); | 1904 | dev_data->ats.qdep = pci_ats_queue_depth(pdev); |
1823 | } | 1905 | } |
@@ -1877,20 +1959,24 @@ static void __detach_device(struct iommu_dev_data *dev_data) | |||
1877 | */ | 1959 | */ |
1878 | static void detach_device(struct device *dev) | 1960 | static void detach_device(struct device *dev) |
1879 | { | 1961 | { |
1962 | struct protection_domain *domain; | ||
1880 | struct iommu_dev_data *dev_data; | 1963 | struct iommu_dev_data *dev_data; |
1881 | unsigned long flags; | 1964 | unsigned long flags; |
1882 | 1965 | ||
1883 | dev_data = get_dev_data(dev); | 1966 | dev_data = get_dev_data(dev); |
1967 | domain = dev_data->domain; | ||
1884 | 1968 | ||
1885 | /* lock device table */ | 1969 | /* lock device table */ |
1886 | write_lock_irqsave(&amd_iommu_devtable_lock, flags); | 1970 | write_lock_irqsave(&amd_iommu_devtable_lock, flags); |
1887 | __detach_device(dev_data); | 1971 | __detach_device(dev_data); |
1888 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); | 1972 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); |
1889 | 1973 | ||
1890 | if (dev_data->ats.enabled) { | 1974 | if (domain->flags & PD_IOMMUV2_MASK) |
1975 | pdev_iommuv2_disable(to_pci_dev(dev)); | ||
1976 | else if (dev_data->ats.enabled) | ||
1891 | pci_disable_ats(to_pci_dev(dev)); | 1977 | pci_disable_ats(to_pci_dev(dev)); |
1892 | dev_data->ats.enabled = false; | 1978 | |
1893 | } | 1979 | dev_data->ats.enabled = false; |
1894 | } | 1980 | } |
1895 | 1981 | ||
1896 | /* | 1982 | /* |
@@ -2788,6 +2874,9 @@ static void amd_iommu_domain_destroy(struct iommu_domain *dom) | |||
2788 | if (domain->mode != PAGE_MODE_NONE) | 2874 | if (domain->mode != PAGE_MODE_NONE) |
2789 | free_pagetable(domain); | 2875 | free_pagetable(domain); |
2790 | 2876 | ||
2877 | if (domain->flags & PD_IOMMUV2_MASK) | ||
2878 | free_gcr3_table(domain); | ||
2879 | |||
2791 | protection_domain_free(domain); | 2880 | protection_domain_free(domain); |
2792 | 2881 | ||
2793 | dom->priv = NULL; | 2882 | dom->priv = NULL; |
@@ -3010,3 +3099,50 @@ void amd_iommu_domain_direct_map(struct iommu_domain *dom) | |||
3010 | spin_unlock_irqrestore(&domain->lock, flags); | 3099 | spin_unlock_irqrestore(&domain->lock, flags); |
3011 | } | 3100 | } |
3012 | EXPORT_SYMBOL(amd_iommu_domain_direct_map); | 3101 | EXPORT_SYMBOL(amd_iommu_domain_direct_map); |
3102 | |||
3103 | int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids) | ||
3104 | { | ||
3105 | struct protection_domain *domain = dom->priv; | ||
3106 | unsigned long flags; | ||
3107 | int levels, ret; | ||
3108 | |||
3109 | if (pasids <= 0 || pasids > (PASID_MASK + 1)) | ||
3110 | return -EINVAL; | ||
3111 | |||
3112 | /* Number of GCR3 table levels required */ | ||
3113 | for (levels = 0; (pasids - 1) & ~0x1ff; pasids >>= 9) | ||
3114 | levels += 1; | ||
3115 | |||
3116 | if (levels > amd_iommu_max_glx_val) | ||
3117 | return -EINVAL; | ||
3118 | |||
3119 | spin_lock_irqsave(&domain->lock, flags); | ||
3120 | |||
3121 | /* | ||
3122 | * Save us all sanity checks whether devices already in the | ||
3123 | * domain support IOMMUv2. Just force that the domain has no | ||
3124 | * devices attached when it is switched into IOMMUv2 mode. | ||
3125 | */ | ||
3126 | ret = -EBUSY; | ||
3127 | if (domain->dev_cnt > 0 || domain->flags & PD_IOMMUV2_MASK) | ||
3128 | goto out; | ||
3129 | |||
3130 | ret = -ENOMEM; | ||
3131 | domain->gcr3_tbl = (void *)get_zeroed_page(GFP_ATOMIC); | ||
3132 | if (domain->gcr3_tbl == NULL) | ||
3133 | goto out; | ||
3134 | |||
3135 | domain->glx = levels; | ||
3136 | domain->flags |= PD_IOMMUV2_MASK; | ||
3137 | domain->updated = true; | ||
3138 | |||
3139 | update_domain(domain); | ||
3140 | |||
3141 | ret = 0; | ||
3142 | |||
3143 | out: | ||
3144 | spin_unlock_irqrestore(&domain->lock, flags); | ||
3145 | |||
3146 | return ret; | ||
3147 | } | ||
3148 | EXPORT_SYMBOL(amd_iommu_domain_enable_v2); | ||
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 7c3fd572a23b..c7a5d7e14547 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c | |||
@@ -755,6 +755,7 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu) | |||
755 | iommu->features = ((u64)high << 32) | low; | 755 | iommu->features = ((u64)high << 32) | low; |
756 | 756 | ||
757 | if (iommu_feature(iommu, FEATURE_GT)) { | 757 | if (iommu_feature(iommu, FEATURE_GT)) { |
758 | int glxval; | ||
758 | u32 pasids; | 759 | u32 pasids; |
759 | u64 shift; | 760 | u64 shift; |
760 | 761 | ||
@@ -763,6 +764,14 @@ static void __init init_iommu_from_pci(struct amd_iommu *iommu) | |||
763 | pasids = (1 << shift); | 764 | pasids = (1 << shift); |
764 | 765 | ||
765 | amd_iommu_max_pasids = min(amd_iommu_max_pasids, pasids); | 766 | amd_iommu_max_pasids = min(amd_iommu_max_pasids, pasids); |
767 | |||
768 | glxval = iommu->features & FEATURE_GLXVAL_MASK; | ||
769 | glxval >>= FEATURE_GLXVAL_SHIFT; | ||
770 | |||
771 | if (amd_iommu_max_glx_val == -1) | ||
772 | amd_iommu_max_glx_val = glxval; | ||
773 | else | ||
774 | amd_iommu_max_glx_val = min(amd_iommu_max_glx_val, glxval); | ||
766 | } | 775 | } |
767 | 776 | ||
768 | if (iommu_feature(iommu, FEATURE_GT) && | 777 | if (iommu_feature(iommu, FEATURE_GT) && |
diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h index 2c4554e963c7..d207b1d951b2 100644 --- a/drivers/iommu/amd_iommu_proto.h +++ b/drivers/iommu/amd_iommu_proto.h | |||
@@ -39,6 +39,7 @@ extern bool amd_iommu_v2_supported(void); | |||
39 | extern int amd_iommu_register_ppr_notifier(struct notifier_block *nb); | 39 | extern int amd_iommu_register_ppr_notifier(struct notifier_block *nb); |
40 | extern int amd_iommu_unregister_ppr_notifier(struct notifier_block *nb); | 40 | extern int amd_iommu_unregister_ppr_notifier(struct notifier_block *nb); |
41 | extern void amd_iommu_domain_direct_map(struct iommu_domain *dom); | 41 | extern void amd_iommu_domain_direct_map(struct iommu_domain *dom); |
42 | extern int amd_iommu_domain_enable_v2(struct iommu_domain *dom, int pasids); | ||
42 | 43 | ||
43 | #ifndef CONFIG_AMD_IOMMU_STATS | 44 | #ifndef CONFIG_AMD_IOMMU_STATS |
44 | 45 | ||
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index c9e080cf595f..b7583cb5ad66 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h | |||
@@ -93,6 +93,11 @@ | |||
93 | #define FEATURE_PASID_SHIFT 32 | 93 | #define FEATURE_PASID_SHIFT 32 |
94 | #define FEATURE_PASID_MASK (0x1fULL << FEATURE_PASID_SHIFT) | 94 | #define FEATURE_PASID_MASK (0x1fULL << FEATURE_PASID_SHIFT) |
95 | 95 | ||
96 | #define FEATURE_GLXVAL_SHIFT 14 | ||
97 | #define FEATURE_GLXVAL_MASK (0x03ULL << FEATURE_GLXVAL_SHIFT) | ||
98 | |||
99 | #define PASID_MASK 0x000fffff | ||
100 | |||
96 | /* MMIO status bits */ | 101 | /* MMIO status bits */ |
97 | #define MMIO_STATUS_COM_WAIT_INT_MASK (1 << 2) | 102 | #define MMIO_STATUS_COM_WAIT_INT_MASK (1 << 2) |
98 | #define MMIO_STATUS_PPR_INT_MASK (1 << 6) | 103 | #define MMIO_STATUS_PPR_INT_MASK (1 << 6) |
@@ -257,6 +262,22 @@ | |||
257 | #define IOMMU_PTE_IW (1ULL << 62) | 262 | #define IOMMU_PTE_IW (1ULL << 62) |
258 | 263 | ||
259 | #define DTE_FLAG_IOTLB (0x01UL << 32) | 264 | #define DTE_FLAG_IOTLB (0x01UL << 32) |
265 | #define DTE_FLAG_GV (0x01ULL << 55) | ||
266 | #define DTE_GLX_SHIFT (56) | ||
267 | #define DTE_GLX_MASK (3) | ||
268 | |||
269 | #define DTE_GCR3_VAL_A(x) (((x) >> 12) & 0x00007ULL) | ||
270 | #define DTE_GCR3_VAL_B(x) (((x) >> 15) & 0x0ffffULL) | ||
271 | #define DTE_GCR3_VAL_C(x) (((x) >> 31) & 0xfffffULL) | ||
272 | |||
273 | #define DTE_GCR3_INDEX_A 0 | ||
274 | #define DTE_GCR3_INDEX_B 1 | ||
275 | #define DTE_GCR3_INDEX_C 1 | ||
276 | |||
277 | #define DTE_GCR3_SHIFT_A 58 | ||
278 | #define DTE_GCR3_SHIFT_B 16 | ||
279 | #define DTE_GCR3_SHIFT_C 43 | ||
280 | |||
260 | 281 | ||
261 | #define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL) | 282 | #define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL) |
262 | #define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P) | 283 | #define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P) |
@@ -283,6 +304,7 @@ | |||
283 | domain for an IOMMU */ | 304 | domain for an IOMMU */ |
284 | #define PD_PASSTHROUGH_MASK (1UL << 2) /* domain has no page | 305 | #define PD_PASSTHROUGH_MASK (1UL << 2) /* domain has no page |
285 | translation */ | 306 | translation */ |
307 | #define PD_IOMMUV2_MASK (1UL << 3) /* domain has gcr3 table */ | ||
286 | 308 | ||
287 | extern bool amd_iommu_dump; | 309 | extern bool amd_iommu_dump; |
288 | #define DUMP_printk(format, arg...) \ | 310 | #define DUMP_printk(format, arg...) \ |
@@ -344,6 +366,8 @@ struct protection_domain { | |||
344 | u16 id; /* the domain id written to the device table */ | 366 | u16 id; /* the domain id written to the device table */ |
345 | int mode; /* paging mode (0-6 levels) */ | 367 | int mode; /* paging mode (0-6 levels) */ |
346 | u64 *pt_root; /* page table root pointer */ | 368 | u64 *pt_root; /* page table root pointer */ |
369 | int glx; /* Number of levels for GCR3 table */ | ||
370 | u64 *gcr3_tbl; /* Guest CR3 table */ | ||
347 | unsigned long flags; /* flags to find out type of domain */ | 371 | unsigned long flags; /* flags to find out type of domain */ |
348 | bool updated; /* complete domain flush required */ | 372 | bool updated; /* complete domain flush required */ |
349 | unsigned dev_cnt; /* devices assigned to this domain */ | 373 | unsigned dev_cnt; /* devices assigned to this domain */ |
@@ -611,6 +635,9 @@ extern bool amd_iommu_v2_present; | |||
611 | 635 | ||
612 | extern bool amd_iommu_force_isolation; | 636 | extern bool amd_iommu_force_isolation; |
613 | 637 | ||
638 | /* Max levels of glxval supported */ | ||
639 | extern int amd_iommu_max_glx_val; | ||
640 | |||
614 | /* takes bus and device/function and returns the device id | 641 | /* takes bus and device/function and returns the device id |
615 | * FIXME: should that be in generic PCI code? */ | 642 | * FIXME: should that be in generic PCI code? */ |
616 | static inline u16 calc_devid(u8 bus, u8 devfn) | 643 | static inline u16 calc_devid(u8 bus, u8 devfn) |