diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 0d1352b7a7a10666ba82392c57eaf1754fdb0c6b..6abcce3f33e26cd826f5c7ada51d8f3e4408355a 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -364,6 +364,25 @@ static int iova_reserve_pci_windows(struct pci_dev *dev, return 0; } +int iova_reserve_domain_addr(struct iommu_domain *domain, dma_addr_t start, + dma_addr_t end) +{ + struct iommu_dma_cookie *cookie = domain->iova_cookie; + struct iova_domain *iovad = &cookie->iovad; + unsigned long lo, hi; + + lo = iova_pfn(iovad, start); + hi = iova_pfn(iovad, end); + + if (!cookie) + return -EINVAL; + + reserve_iova(iovad, lo, hi); + + return 0; +} +EXPORT_SYMBOL_GPL(iova_reserve_domain_addr); + static int iova_reserve_iommu_regions(struct device *dev, struct iommu_domain *domain) { @@ -470,7 +489,7 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, return -EFAULT; } - return 0; + return iova_reserve_iommu_regions(dev, domain); } init_iova_domain(iovad, 1UL << order, base_pfn); diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c index d6548cbfcfe70aa24cddbeb33837048a593f487c..a661b6b3b29f923cf52a44bcf022a9bc04b642b9 100644 --- a/drivers/iommu/intel/dmar.c +++ b/drivers/iommu/intel/dmar.c @@ -775,6 +775,59 @@ static void __init dmar_acpi_insert_dev_scope(u8 device_number, device_number, dev_name(&adev->dev)); } +/* Return: > 0 if match found, 0 if no match found */ +bool dmar_rmrr_acpi_insert_dev_scope(u8 device_number, + struct acpi_device *adev, + void *start, void *end, + struct dmar_dev_scope *devices, + int devices_cnt) +{ + struct acpi_dmar_device_scope *scope; + struct device *tmp; + int i; + struct acpi_dmar_pci_path *path; + + for (; start < end; start += scope->length) { + scope = start; + if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_NAMESPACE) + continue; + if (scope->enumeration_id != device_number) + continue; + path = (void *)(scope + 1); + pr_info("ACPI device \"%s\" under DMAR as %02x:%02x.%d\n", dev_name(&adev->dev), + scope->bus, path->device, path->function); + for_each_dev_scope(devices, devices_cnt, i, tmp) + if (tmp == NULL) { + devices[i].bus = scope->bus; + devices[i].devfn = PCI_DEVFN(path->device, path->function); + rcu_assign_pointer(devices[i].dev, get_device(&adev->dev)); + return true; + } + WARN_ON(i >= devices_cnt); + } + return false; +} + +static int dmar_acpi_bus_add_dev(u8 device_number, struct acpi_device *adev) +{ + struct dmar_drhd_unit *dmaru; + struct acpi_dmar_hardware_unit *drhd; + int ret; + + for_each_drhd_unit(dmaru) { + drhd = container_of(dmaru->hdr, struct acpi_dmar_hardware_unit, header); + ret = dmar_rmrr_acpi_insert_dev_scope(device_number, adev, (void *)(drhd+1), + ((void *)drhd)+drhd->header.length, + dmaru->devices, dmaru->devices_cnt); + if (ret) + break; + } + if (ret > 0) + ret = dmar_rmrr_add_acpi_dev(device_number, adev); + + return ret; +} + static int __init dmar_acpi_dev_scope_init(void) { struct acpi_dmar_andd *andd; @@ -801,7 +854,11 @@ static int __init dmar_acpi_dev_scope_init(void) andd->device_name); continue; } - dmar_acpi_insert_dev_scope(andd->device_number, adev); + + if (apply_zhaoxin_dmar_acpi_a_behavior()) + dmar_acpi_bus_add_dev(andd->device_number, adev); + else + dmar_acpi_insert_dev_scope(andd->device_number, adev); } } return 0; diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 56d2643125f273f928d34de0819abca278cdfc96..b8a43d5095f17cf666f570fb3543e6fc9dc15686 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -4221,6 +4221,24 @@ int dmar_ats_supported(struct pci_dev *dev, struct intel_iommu *iommu) return ret; } +int dmar_rmrr_add_acpi_dev(u8 device_number, struct acpi_device *adev) +{ + int ret; + struct dmar_rmrr_unit *rmrru; + struct acpi_dmar_reserved_memory *rmrr; + + list_for_each_entry(rmrru, &dmar_rmrr_units, list) { + rmrr = container_of(rmrru->hdr, struct acpi_dmar_reserved_memory, header); + ret = dmar_rmrr_acpi_insert_dev_scope(device_number, adev, (void *)(rmrr + 1), + ((void *)rmrr) + rmrr->header.length, + rmrru->devices, rmrru->devices_cnt); + if (ret) + break; + } + + return 0; +} + int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) { int ret; @@ -4486,6 +4504,44 @@ static int __init platform_optin_force_iommu(void) return 1; } +static inline int acpi_rmrr_device_create_direct_mappings(struct iommu_group *group, + struct device *dev) +{ + int ret; + + pr_info("rmrr andd dev:%s enter to %s\n", dev_name(dev), __func__); + ret = __acpi_rmrr_device_create_direct_mappings(group, dev); + + return ret; +} + +static inline int acpi_rmrr_andd_probe(struct device *dev) +{ + struct intel_iommu *iommu = NULL; + struct pci_dev *pci_device = NULL; + u8 bus, devfn; + int ret = 0; + + dev->bus->iommu_ops = &intel_iommu_ops; + ret = iommu_probe_device(dev); + + iommu = device_to_iommu(dev, &bus, &devfn); + if (!iommu) { + pr_info("cannot get acpi device corresponding iommu\n"); + return -EINVAL; + } + + pci_device = pci_get_domain_bus_and_slot(iommu->segment, bus, devfn); + if (!pci_device) { + pr_info("cannot get acpi devie corresponding pci_device\n"); + return -EINVAL; + } + ret = acpi_rmrr_device_create_direct_mappings(iommu_group_get(&pci_device->dev), + dev); + + return ret; +} + static int __init probe_acpi_namespace_devices(void) { struct dmar_drhd_unit *drhd; @@ -4508,6 +4564,10 @@ static int __init probe_acpi_namespace_devices(void) mutex_lock(&adev->physical_node_lock); list_for_each_entry(pn, &adev->physical_node_list, node) { + + if (apply_zhaoxin_dmar_acpi_a_behavior()) + ret = acpi_rmrr_andd_probe(dev); + group = iommu_group_get(pn->dev); if (group) { iommu_group_put(group); @@ -4516,6 +4576,7 @@ static int __init probe_acpi_namespace_devices(void) pn->dev->bus->iommu_ops = &intel_iommu_ops; ret = iommu_probe_device(pn->dev); + if (ret) break; } diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index ec1c8f83f2f1018bcfaf1449201282f774ff00d4..34adf36814424910aa10a9824ede143ae802a98a 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -832,7 +832,8 @@ static int iommu_create_device_direct_mappings(struct iommu_group *group, map_size = 0; } } - + if (apply_zhaoxin_dmar_acpi_a_behavior()) + iova_reserve_domain_addr(domain, start, end); } iommu_flush_iotlb_all(domain); @@ -852,6 +853,16 @@ static bool iommu_is_attach_deferred(struct iommu_domain *domain, return false; } +int __acpi_rmrr_device_create_direct_mappings(struct iommu_group *group, struct device *dev) +{ + int ret; + + ret = iommu_create_device_direct_mappings(group, dev); + + return ret; +} +EXPORT_SYMBOL_GPL(__acpi_rmrr_device_create_direct_mappings); + /** * iommu_group_add_device - add a device to an iommu group * @group: the group into which to add the device (reference should be held) diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 778e07455a854bc5c3776580ca85584e240c264d..05cff7f3e0f2296ceda0658088547db2f271da96 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -118,6 +118,9 @@ extern int dmar_insert_dev_scope(struct dmar_pci_notify_info *info, void *start, void*end, u16 segment, struct dmar_dev_scope *devices, int devices_cnt); +extern bool dmar_rmrr_acpi_insert_dev_scope(u8 device_number, + struct acpi_device *adev, void *start, void *end, + struct dmar_dev_scope *devices, int devices_cnt); extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, u16 segment, struct dmar_dev_scope *devices, int count); @@ -142,6 +145,7 @@ extern int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg); extern int dmar_parse_one_satc(struct acpi_dmar_header *hdr, void *arg); extern int dmar_release_one_atsr(struct acpi_dmar_header *hdr, void *arg); extern int dmar_iommu_hotplug(struct dmar_drhd_unit *dmaru, bool insert); +extern int dmar_rmrr_add_acpi_dev(u8 device_number, struct acpi_device *adev); extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); #else /* !CONFIG_INTEL_IOMMU: */ static inline int intel_iommu_init(void) { return -ENODEV; } @@ -153,6 +157,11 @@ static inline void intel_iommu_shutdown(void) { } #define dmar_release_one_atsr dmar_res_noop #define dmar_parse_one_satc dmar_res_noop +static inline int dmar_rmrr_add_acpi_dev(u8 device_number, struct acpi_device *adev) +{ + return 0; +} + static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) { return 0; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index b565d2f4840bbf3b02d6d1f2a811cf413d8c6154..e164bcb8a0766db305d99626ff9c8f9c7f6bd181 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -642,6 +642,23 @@ extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, extern int report_iommu_fault(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags); +static inline bool apply_zhaoxin_dmar_acpi_a_behavior(void) +{ +#if defined(CONFIG_CPU_SUP_ZHAOXIN) || defined(CONFIG_CPU_SUP_CENTAUR) + if (((boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR) || + (boot_cpu_data.x86_vendor == X86_VENDOR_ZHAOXIN)) && + ((boot_cpu_data.x86 == 7) && (boot_cpu_data.x86_model == 0x3b))) + return true; +#endif + return false; +} + +extern int iova_reserve_domain_addr(struct iommu_domain *domain, + dma_addr_t start, dma_addr_t end); + +int __acpi_rmrr_device_create_direct_mappings(struct iommu_group *group, + struct device *dev); + static inline void iommu_flush_iotlb_all(struct iommu_domain *domain) { if (domain->ops->flush_iotlb_all)