diff options
Diffstat (limited to 'drivers/iommu/intel-iommu.c')
-rw-r--r-- | drivers/iommu/intel-iommu.c | 93 |
1 files changed, 58 insertions, 35 deletions
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 2fb7d1598a68..7469b5346643 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
@@ -3932,6 +3932,10 @@ static int intel_iommu_domain_init(struct iommu_domain *domain) | |||
3932 | domain_update_iommu_cap(dmar_domain); | 3932 | domain_update_iommu_cap(dmar_domain); |
3933 | domain->priv = dmar_domain; | 3933 | domain->priv = dmar_domain; |
3934 | 3934 | ||
3935 | domain->geometry.aperture_start = 0; | ||
3936 | domain->geometry.aperture_end = __DOMAIN_MAX_ADDR(dmar_domain->gaw); | ||
3937 | domain->geometry.force_aperture = true; | ||
3938 | |||
3935 | return 0; | 3939 | return 0; |
3936 | } | 3940 | } |
3937 | 3941 | ||
@@ -4090,52 +4094,70 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, | |||
4090 | return 0; | 4094 | return 0; |
4091 | } | 4095 | } |
4092 | 4096 | ||
4093 | /* | 4097 | static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) |
4094 | * Group numbers are arbitrary. Device with the same group number | ||
4095 | * indicate the iommu cannot differentiate between them. To avoid | ||
4096 | * tracking used groups we just use the seg|bus|devfn of the lowest | ||
4097 | * level we're able to differentiate devices | ||
4098 | */ | ||
4099 | static int intel_iommu_device_group(struct device *dev, unsigned int *groupid) | ||
4100 | { | 4098 | { |
4101 | struct pci_dev *pdev = to_pci_dev(dev); | 4099 | pci_dev_put(*from); |
4102 | struct pci_dev *bridge; | 4100 | *from = to; |
4103 | union { | 4101 | } |
4104 | struct { | ||
4105 | u8 devfn; | ||
4106 | u8 bus; | ||
4107 | u16 segment; | ||
4108 | } pci; | ||
4109 | u32 group; | ||
4110 | } id; | ||
4111 | 4102 | ||
4112 | if (iommu_no_mapping(dev)) | 4103 | #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) |
4113 | return -ENODEV; | ||
4114 | 4104 | ||
4115 | id.pci.segment = pci_domain_nr(pdev->bus); | 4105 | static int intel_iommu_add_device(struct device *dev) |
4116 | id.pci.bus = pdev->bus->number; | 4106 | { |
4117 | id.pci.devfn = pdev->devfn; | 4107 | struct pci_dev *pdev = to_pci_dev(dev); |
4108 | struct pci_dev *bridge, *dma_pdev; | ||
4109 | struct iommu_group *group; | ||
4110 | int ret; | ||
4118 | 4111 | ||
4119 | if (!device_to_iommu(id.pci.segment, id.pci.bus, id.pci.devfn)) | 4112 | if (!device_to_iommu(pci_domain_nr(pdev->bus), |
4113 | pdev->bus->number, pdev->devfn)) | ||
4120 | return -ENODEV; | 4114 | return -ENODEV; |
4121 | 4115 | ||
4122 | bridge = pci_find_upstream_pcie_bridge(pdev); | 4116 | bridge = pci_find_upstream_pcie_bridge(pdev); |
4123 | if (bridge) { | 4117 | if (bridge) { |
4124 | if (pci_is_pcie(bridge)) { | 4118 | if (pci_is_pcie(bridge)) |
4125 | id.pci.bus = bridge->subordinate->number; | 4119 | dma_pdev = pci_get_domain_bus_and_slot( |
4126 | id.pci.devfn = 0; | 4120 | pci_domain_nr(pdev->bus), |
4127 | } else { | 4121 | bridge->subordinate->number, 0); |
4128 | id.pci.bus = bridge->bus->number; | 4122 | else |
4129 | id.pci.devfn = bridge->devfn; | 4123 | dma_pdev = pci_dev_get(bridge); |
4130 | } | 4124 | } else |
4125 | dma_pdev = pci_dev_get(pdev); | ||
4126 | |||
4127 | swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); | ||
4128 | |||
4129 | if (dma_pdev->multifunction && | ||
4130 | !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) | ||
4131 | swap_pci_ref(&dma_pdev, | ||
4132 | pci_get_slot(dma_pdev->bus, | ||
4133 | PCI_DEVFN(PCI_SLOT(dma_pdev->devfn), | ||
4134 | 0))); | ||
4135 | |||
4136 | while (!pci_is_root_bus(dma_pdev->bus)) { | ||
4137 | if (pci_acs_path_enabled(dma_pdev->bus->self, | ||
4138 | NULL, REQ_ACS_FLAGS)) | ||
4139 | break; | ||
4140 | |||
4141 | swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self)); | ||
4142 | } | ||
4143 | |||
4144 | group = iommu_group_get(&dma_pdev->dev); | ||
4145 | pci_dev_put(dma_pdev); | ||
4146 | if (!group) { | ||
4147 | group = iommu_group_alloc(); | ||
4148 | if (IS_ERR(group)) | ||
4149 | return PTR_ERR(group); | ||
4131 | } | 4150 | } |
4132 | 4151 | ||
4133 | if (!pdev->is_virtfn && iommu_group_mf) | 4152 | ret = iommu_group_add_device(group, dev); |
4134 | id.pci.devfn = PCI_DEVFN(PCI_SLOT(id.pci.devfn), 0); | ||
4135 | 4153 | ||
4136 | *groupid = id.group; | 4154 | iommu_group_put(group); |
4155 | return ret; | ||
4156 | } | ||
4137 | 4157 | ||
4138 | return 0; | 4158 | static void intel_iommu_remove_device(struct device *dev) |
4159 | { | ||
4160 | iommu_group_remove_device(dev); | ||
4139 | } | 4161 | } |
4140 | 4162 | ||
4141 | static struct iommu_ops intel_iommu_ops = { | 4163 | static struct iommu_ops intel_iommu_ops = { |
@@ -4147,7 +4169,8 @@ static struct iommu_ops intel_iommu_ops = { | |||
4147 | .unmap = intel_iommu_unmap, | 4169 | .unmap = intel_iommu_unmap, |
4148 | .iova_to_phys = intel_iommu_iova_to_phys, | 4170 | .iova_to_phys = intel_iommu_iova_to_phys, |
4149 | .domain_has_cap = intel_iommu_domain_has_cap, | 4171 | .domain_has_cap = intel_iommu_domain_has_cap, |
4150 | .device_group = intel_iommu_device_group, | 4172 | .add_device = intel_iommu_add_device, |
4173 | .remove_device = intel_iommu_remove_device, | ||
4151 | .pgsize_bitmap = INTEL_IOMMU_PGSIZES, | 4174 | .pgsize_bitmap = INTEL_IOMMU_PGSIZES, |
4152 | }; | 4175 | }; |
4153 | 4176 | ||