Loading Documentation/ABI/testing/sysfs-class-iommu 0 → 100644 +17 −0 Original line number Diff line number Diff line What: /sys/class/iommu/<iommu>/devices/ Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: IOMMU drivers are able to link devices managed by a given IOMMU here to allow association of IOMMU to device. What: /sys/devices/.../iommu Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: IOMMU drivers are able to link the IOMMU for a given device here to allow association of device to IOMMU. Documentation/ABI/testing/sysfs-class-iommu-amd-iommu 0 → 100644 +14 −0 Original line number Diff line number Diff line What: /sys/class/iommu/<iommu>/amd-iommu/cap Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: IOMMU capability header as documented in the AMD IOMMU specification. Format: %x What: /sys/class/iommu/<iommu>/amd-iommu/features Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: Extended features of the IOMMU. Format: %llx Documentation/ABI/testing/sysfs-class-iommu-intel-iommu 0 → 100644 +32 −0 Original line number Diff line number Diff line What: /sys/class/iommu/<iommu>/intel-iommu/address Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: Physical address of the VT-d DRHD for this IOMMU. Format: %llx. This allows association of a sysfs intel-iommu with a DMAR DRHD table entry. What: /sys/class/iommu/<iommu>/intel-iommu/cap Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: The cached hardware capability register value of this DRHD unit. Format: %llx. What: /sys/class/iommu/<iommu>/intel-iommu/ecap Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: The cached hardware extended capability register value of this DRHD unit. Format: %llx. What: /sys/class/iommu/<iommu>/intel-iommu/version Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: The architecture version as reported from the VT-d VER_REG. Format: %d:%d, major:minor drivers/iommu/Makefile +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o Loading drivers/iommu/amd_iommu.c +73 −151 Original line number Diff line number Diff line Loading @@ -46,7 +46,6 @@ #include "amd_iommu_proto.h" #include "amd_iommu_types.h" #include "irq_remapping.h" #include "pci.h" #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) Loading Loading @@ -81,7 +80,7 @@ LIST_HEAD(hpet_map); */ static struct protection_domain *pt_domain; static struct iommu_ops amd_iommu_ops; static const struct iommu_ops amd_iommu_ops; static ATOMIC_NOTIFIER_HEAD(ppr_notifier); int amd_iommu_max_glx_val = -1; Loading Loading @@ -133,9 +132,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data) list_del(&dev_data->dev_data_list); spin_unlock_irqrestore(&dev_data_list_lock, flags); if (dev_data->group) iommu_group_put(dev_data->group); kfree(dev_data); } Loading Loading @@ -264,167 +260,79 @@ static bool check_device(struct device *dev) return true; } static struct pci_bus *find_hosted_bus(struct pci_bus *bus) { while (!bus->self) { if (!pci_is_root_bus(bus)) bus = bus->parent; else return ERR_PTR(-ENODEV); } return bus; } #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) static struct pci_dev *get_isolation_root(struct pci_dev *pdev) static int init_iommu_group(struct device *dev) { struct pci_dev *dma_pdev = pdev; /* Account for quirked devices */ swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); /* * If it's a multifunction device that does not support our * required ACS flags, add to the same group as lowest numbered * function that also does not suport the required ACS flags. */ if (dma_pdev->multifunction && !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) { u8 i, slot = PCI_SLOT(dma_pdev->devfn); for (i = 0; i < 8; i++) { struct pci_dev *tmp; tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i)); if (!tmp) continue; if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) { swap_pci_ref(&dma_pdev, tmp); break; } pci_dev_put(tmp); } } /* * Devices on the root bus go through the iommu. If that's not us, * find the next upstream device and test ACS up to the root bus. * Finding the next device may require skipping virtual buses. */ while (!pci_is_root_bus(dma_pdev->bus)) { struct pci_bus *bus = find_hosted_bus(dma_pdev->bus); if (IS_ERR(bus)) break; if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS)) break; swap_pci_ref(&dma_pdev, pci_dev_get(bus->self)); } return dma_pdev; } struct iommu_group *group; static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev) { struct iommu_group *group = iommu_group_get(&pdev->dev); int ret; group = iommu_group_get_for_dev(dev); if (!group) { group = iommu_group_alloc(); if (IS_ERR(group)) return PTR_ERR(group); WARN_ON(&pdev->dev != dev); } ret = iommu_group_add_device(group, dev); iommu_group_put(group); return ret; } static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data, struct device *dev) { if (!dev_data->group) { struct iommu_group *group = iommu_group_alloc(); if (IS_ERR(group)) return PTR_ERR(group); dev_data->group = group; } return iommu_group_add_device(dev_data->group, dev); return 0; } static int init_iommu_group(struct device *dev) static int __last_alias(struct pci_dev *pdev, u16 alias, void *data) { struct iommu_dev_data *dev_data; struct iommu_group *group; struct pci_dev *dma_pdev; int ret; group = iommu_group_get(dev); if (group) { iommu_group_put(group); *(u16 *)data = alias; return 0; } dev_data = find_dev_data(get_device_id(dev)); if (!dev_data) return -ENOMEM; static u16 get_alias(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); u16 devid, ivrs_alias, pci_alias; if (dev_data->alias_data) { u16 alias; struct pci_bus *bus; devid = get_device_id(dev); ivrs_alias = amd_iommu_alias_table[devid]; pci_for_each_dma_alias(pdev, __last_alias, &pci_alias); if (dev_data->alias_data->group) goto use_group; if (ivrs_alias == pci_alias) return ivrs_alias; /* * If the alias device exists, it's effectively just a first * level quirk for finding the DMA source. * DMA alias showdown * * The IVRS is fairly reliable in telling us about aliases, but it * can't know about every screwy device. If we don't have an IVRS * reported alias, use the PCI reported alias. In that case we may * still need to initialize the rlookup and dev_table entries if the * alias is to a non-existent device. */ alias = amd_iommu_alias_table[dev_data->devid]; dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); if (dma_pdev) { dma_pdev = get_isolation_root(dma_pdev); goto use_pdev; if (ivrs_alias == devid) { if (!amd_iommu_rlookup_table[pci_alias]) { amd_iommu_rlookup_table[pci_alias] = amd_iommu_rlookup_table[devid]; memcpy(amd_iommu_dev_table[pci_alias].data, amd_iommu_dev_table[devid].data, sizeof(amd_iommu_dev_table[pci_alias].data)); } /* * If the alias is virtual, try to find a parent device * and test whether the IOMMU group is actualy rooted above * the alias. Be careful to also test the parent device if * we think the alias is the root of the group. */ bus = pci_find_bus(0, alias >> 8); if (!bus) goto use_group; bus = find_hosted_bus(bus); if (IS_ERR(bus) || !bus->self) goto use_group; return pci_alias; } dma_pdev = get_isolation_root(pci_dev_get(bus->self)); if (dma_pdev != bus->self || (dma_pdev->multifunction && !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))) goto use_pdev; pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d " "for device %s[%04x:%04x], kernel reported alias " "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device, PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias), PCI_FUNC(pci_alias)); pci_dev_put(dma_pdev); goto use_group; /* * If we don't have a PCI DMA alias and the IVRS alias is on the same * bus, then the IVRS table may know about a quirk that we don't. */ if (pci_alias == devid && PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; pdev->dma_alias_devfn = ivrs_alias & 0xff; pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n", PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), dev_name(dev)); } dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev))); use_pdev: ret = use_pdev_iommu_group(dma_pdev, dev); pci_dev_put(dma_pdev); return ret; use_group: return use_dev_data_iommu_group(dev_data->alias_data, dev); return ivrs_alias; } static int iommu_init_device(struct device *dev) Loading @@ -441,7 +349,8 @@ static int iommu_init_device(struct device *dev) if (!dev_data) return -ENOMEM; alias = amd_iommu_alias_table[dev_data->devid]; alias = get_alias(dev); if (alias != dev_data->devid) { struct iommu_dev_data *alias_data; Loading Loading @@ -470,6 +379,9 @@ static int iommu_init_device(struct device *dev) dev->archdata.iommu = dev_data; iommu_device_link(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev, dev); return 0; } Loading @@ -489,12 +401,22 @@ static void iommu_ignore_device(struct device *dev) static void iommu_uninit_device(struct device *dev) { struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev)); if (!dev_data) return; iommu_device_unlink(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev, dev); iommu_group_remove_device(dev); /* Unlink from alias, it may change if another device is re-plugged */ dev_data->alias_data = NULL; /* * Nothing to do here - we keep dev_data around for unplugged devices * and reuse it when the device is re-plugged - not doing so would * introduce a ton of races. * We keep dev_data around for unplugged devices and reuse it when the * device is re-plugged - not doing so would introduce a ton of races. */ } Loading Loading @@ -3473,7 +3395,7 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } static struct iommu_ops amd_iommu_ops = { static const struct iommu_ops amd_iommu_ops = { .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, .attach_dev = amd_iommu_attach_device, Loading Loading
Documentation/ABI/testing/sysfs-class-iommu 0 → 100644 +17 −0 Original line number Diff line number Diff line What: /sys/class/iommu/<iommu>/devices/ Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: IOMMU drivers are able to link devices managed by a given IOMMU here to allow association of IOMMU to device. What: /sys/devices/.../iommu Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: IOMMU drivers are able to link the IOMMU for a given device here to allow association of device to IOMMU.
Documentation/ABI/testing/sysfs-class-iommu-amd-iommu 0 → 100644 +14 −0 Original line number Diff line number Diff line What: /sys/class/iommu/<iommu>/amd-iommu/cap Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: IOMMU capability header as documented in the AMD IOMMU specification. Format: %x What: /sys/class/iommu/<iommu>/amd-iommu/features Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: Extended features of the IOMMU. Format: %llx
Documentation/ABI/testing/sysfs-class-iommu-intel-iommu 0 → 100644 +32 −0 Original line number Diff line number Diff line What: /sys/class/iommu/<iommu>/intel-iommu/address Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: Physical address of the VT-d DRHD for this IOMMU. Format: %llx. This allows association of a sysfs intel-iommu with a DMAR DRHD table entry. What: /sys/class/iommu/<iommu>/intel-iommu/cap Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: The cached hardware capability register value of this DRHD unit. Format: %llx. What: /sys/class/iommu/<iommu>/intel-iommu/ecap Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: The cached hardware extended capability register value of this DRHD unit. Format: %llx. What: /sys/class/iommu/<iommu>/intel-iommu/version Date: June 2014 KernelVersion: 3.17 Contact: Alex Williamson <alex.williamson@redhat.com> Description: The architecture version as reported from the VT-d VER_REG. Format: %d:%d, major:minor
drivers/iommu/Makefile +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_IOMMU_API) += iommu.o obj-$(CONFIG_IOMMU_API) += iommu-traces.o obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o Loading
drivers/iommu/amd_iommu.c +73 −151 Original line number Diff line number Diff line Loading @@ -46,7 +46,6 @@ #include "amd_iommu_proto.h" #include "amd_iommu_types.h" #include "irq_remapping.h" #include "pci.h" #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) Loading Loading @@ -81,7 +80,7 @@ LIST_HEAD(hpet_map); */ static struct protection_domain *pt_domain; static struct iommu_ops amd_iommu_ops; static const struct iommu_ops amd_iommu_ops; static ATOMIC_NOTIFIER_HEAD(ppr_notifier); int amd_iommu_max_glx_val = -1; Loading Loading @@ -133,9 +132,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data) list_del(&dev_data->dev_data_list); spin_unlock_irqrestore(&dev_data_list_lock, flags); if (dev_data->group) iommu_group_put(dev_data->group); kfree(dev_data); } Loading Loading @@ -264,167 +260,79 @@ static bool check_device(struct device *dev) return true; } static struct pci_bus *find_hosted_bus(struct pci_bus *bus) { while (!bus->self) { if (!pci_is_root_bus(bus)) bus = bus->parent; else return ERR_PTR(-ENODEV); } return bus; } #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) static struct pci_dev *get_isolation_root(struct pci_dev *pdev) static int init_iommu_group(struct device *dev) { struct pci_dev *dma_pdev = pdev; /* Account for quirked devices */ swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); /* * If it's a multifunction device that does not support our * required ACS flags, add to the same group as lowest numbered * function that also does not suport the required ACS flags. */ if (dma_pdev->multifunction && !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) { u8 i, slot = PCI_SLOT(dma_pdev->devfn); for (i = 0; i < 8; i++) { struct pci_dev *tmp; tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i)); if (!tmp) continue; if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) { swap_pci_ref(&dma_pdev, tmp); break; } pci_dev_put(tmp); } } /* * Devices on the root bus go through the iommu. If that's not us, * find the next upstream device and test ACS up to the root bus. * Finding the next device may require skipping virtual buses. */ while (!pci_is_root_bus(dma_pdev->bus)) { struct pci_bus *bus = find_hosted_bus(dma_pdev->bus); if (IS_ERR(bus)) break; if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS)) break; swap_pci_ref(&dma_pdev, pci_dev_get(bus->self)); } return dma_pdev; } struct iommu_group *group; static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev) { struct iommu_group *group = iommu_group_get(&pdev->dev); int ret; group = iommu_group_get_for_dev(dev); if (!group) { group = iommu_group_alloc(); if (IS_ERR(group)) return PTR_ERR(group); WARN_ON(&pdev->dev != dev); } ret = iommu_group_add_device(group, dev); iommu_group_put(group); return ret; } static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data, struct device *dev) { if (!dev_data->group) { struct iommu_group *group = iommu_group_alloc(); if (IS_ERR(group)) return PTR_ERR(group); dev_data->group = group; } return iommu_group_add_device(dev_data->group, dev); return 0; } static int init_iommu_group(struct device *dev) static int __last_alias(struct pci_dev *pdev, u16 alias, void *data) { struct iommu_dev_data *dev_data; struct iommu_group *group; struct pci_dev *dma_pdev; int ret; group = iommu_group_get(dev); if (group) { iommu_group_put(group); *(u16 *)data = alias; return 0; } dev_data = find_dev_data(get_device_id(dev)); if (!dev_data) return -ENOMEM; static u16 get_alias(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); u16 devid, ivrs_alias, pci_alias; if (dev_data->alias_data) { u16 alias; struct pci_bus *bus; devid = get_device_id(dev); ivrs_alias = amd_iommu_alias_table[devid]; pci_for_each_dma_alias(pdev, __last_alias, &pci_alias); if (dev_data->alias_data->group) goto use_group; if (ivrs_alias == pci_alias) return ivrs_alias; /* * If the alias device exists, it's effectively just a first * level quirk for finding the DMA source. * DMA alias showdown * * The IVRS is fairly reliable in telling us about aliases, but it * can't know about every screwy device. If we don't have an IVRS * reported alias, use the PCI reported alias. In that case we may * still need to initialize the rlookup and dev_table entries if the * alias is to a non-existent device. */ alias = amd_iommu_alias_table[dev_data->devid]; dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); if (dma_pdev) { dma_pdev = get_isolation_root(dma_pdev); goto use_pdev; if (ivrs_alias == devid) { if (!amd_iommu_rlookup_table[pci_alias]) { amd_iommu_rlookup_table[pci_alias] = amd_iommu_rlookup_table[devid]; memcpy(amd_iommu_dev_table[pci_alias].data, amd_iommu_dev_table[devid].data, sizeof(amd_iommu_dev_table[pci_alias].data)); } /* * If the alias is virtual, try to find a parent device * and test whether the IOMMU group is actualy rooted above * the alias. Be careful to also test the parent device if * we think the alias is the root of the group. */ bus = pci_find_bus(0, alias >> 8); if (!bus) goto use_group; bus = find_hosted_bus(bus); if (IS_ERR(bus) || !bus->self) goto use_group; return pci_alias; } dma_pdev = get_isolation_root(pci_dev_get(bus->self)); if (dma_pdev != bus->self || (dma_pdev->multifunction && !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))) goto use_pdev; pr_info("AMD-Vi: Using IVRS reported alias %02x:%02x.%d " "for device %s[%04x:%04x], kernel reported alias " "%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device, PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias), PCI_FUNC(pci_alias)); pci_dev_put(dma_pdev); goto use_group; /* * If we don't have a PCI DMA alias and the IVRS alias is on the same * bus, then the IVRS table may know about a quirk that we don't. */ if (pci_alias == devid && PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; pdev->dma_alias_devfn = ivrs_alias & 0xff; pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n", PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), dev_name(dev)); } dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev))); use_pdev: ret = use_pdev_iommu_group(dma_pdev, dev); pci_dev_put(dma_pdev); return ret; use_group: return use_dev_data_iommu_group(dev_data->alias_data, dev); return ivrs_alias; } static int iommu_init_device(struct device *dev) Loading @@ -441,7 +349,8 @@ static int iommu_init_device(struct device *dev) if (!dev_data) return -ENOMEM; alias = amd_iommu_alias_table[dev_data->devid]; alias = get_alias(dev); if (alias != dev_data->devid) { struct iommu_dev_data *alias_data; Loading Loading @@ -470,6 +379,9 @@ static int iommu_init_device(struct device *dev) dev->archdata.iommu = dev_data; iommu_device_link(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev, dev); return 0; } Loading @@ -489,12 +401,22 @@ static void iommu_ignore_device(struct device *dev) static void iommu_uninit_device(struct device *dev) { struct iommu_dev_data *dev_data = search_dev_data(get_device_id(dev)); if (!dev_data) return; iommu_device_unlink(amd_iommu_rlookup_table[dev_data->devid]->iommu_dev, dev); iommu_group_remove_device(dev); /* Unlink from alias, it may change if another device is re-plugged */ dev_data->alias_data = NULL; /* * Nothing to do here - we keep dev_data around for unplugged devices * and reuse it when the device is re-plugged - not doing so would * introduce a ton of races. * We keep dev_data around for unplugged devices and reuse it when the * device is re-plugged - not doing so would introduce a ton of races. */ } Loading Loading @@ -3473,7 +3395,7 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } static struct iommu_ops amd_iommu_ops = { static const struct iommu_ops amd_iommu_ops = { .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, .attach_dev = amd_iommu_attach_device, Loading