diff options
| -rw-r--r-- | drivers/iommu/ipmmu-vmsa.c | 115 |
1 files changed, 86 insertions, 29 deletions
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 368c852d9ee7..5d080cf11ba5 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c | |||
| @@ -47,7 +47,8 @@ struct ipmmu_vmsa_domain { | |||
| 47 | 47 | ||
| 48 | struct ipmmu_vmsa_archdata { | 48 | struct ipmmu_vmsa_archdata { |
| 49 | struct ipmmu_vmsa_device *mmu; | 49 | struct ipmmu_vmsa_device *mmu; |
| 50 | unsigned int utlb; | 50 | unsigned int *utlbs; |
| 51 | unsigned int num_utlbs; | ||
| 51 | }; | 52 | }; |
| 52 | 53 | ||
| 53 | static DEFINE_SPINLOCK(ipmmu_devices_lock); | 54 | static DEFINE_SPINLOCK(ipmmu_devices_lock); |
| @@ -900,6 +901,7 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, | |||
| 900 | struct ipmmu_vmsa_device *mmu = archdata->mmu; | 901 | struct ipmmu_vmsa_device *mmu = archdata->mmu; |
| 901 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | 902 | struct ipmmu_vmsa_domain *domain = io_domain->priv; |
| 902 | unsigned long flags; | 903 | unsigned long flags; |
| 904 | unsigned int i; | ||
| 903 | int ret = 0; | 905 | int ret = 0; |
| 904 | 906 | ||
| 905 | if (!mmu) { | 907 | if (!mmu) { |
| @@ -928,7 +930,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, | |||
| 928 | if (ret < 0) | 930 | if (ret < 0) |
| 929 | return ret; | 931 | return ret; |
| 930 | 932 | ||
| 931 | ipmmu_utlb_enable(domain, archdata->utlb); | 933 | for (i = 0; i < archdata->num_utlbs; ++i) |
| 934 | ipmmu_utlb_enable(domain, archdata->utlbs[i]); | ||
| 932 | 935 | ||
| 933 | return 0; | 936 | return 0; |
| 934 | } | 937 | } |
| @@ -938,8 +941,10 @@ static void ipmmu_detach_device(struct iommu_domain *io_domain, | |||
| 938 | { | 941 | { |
| 939 | struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; | 942 | struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; |
| 940 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | 943 | struct ipmmu_vmsa_domain *domain = io_domain->priv; |
| 944 | unsigned int i; | ||
| 941 | 945 | ||
| 942 | ipmmu_utlb_disable(domain, archdata->utlb); | 946 | for (i = 0; i < archdata->num_utlbs; ++i) |
| 947 | ipmmu_utlb_disable(domain, archdata->utlbs[i]); | ||
| 943 | 948 | ||
| 944 | /* | 949 | /* |
| 945 | * TODO: Optimize by disabling the context when no device is attached. | 950 | * TODO: Optimize by disabling the context when no device is attached. |
| @@ -1003,10 +1008,12 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, | |||
| 1003 | return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); | 1008 | return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); |
| 1004 | } | 1009 | } |
| 1005 | 1010 | ||
| 1006 | static int ipmmu_find_utlb(struct ipmmu_vmsa_device *mmu, struct device *dev) | 1011 | static int ipmmu_find_utlbs(struct ipmmu_vmsa_device *mmu, struct device *dev, |
| 1012 | unsigned int **_utlbs) | ||
| 1007 | { | 1013 | { |
| 1008 | struct of_phandle_args args; | 1014 | unsigned int *utlbs; |
| 1009 | int ret; | 1015 | unsigned int i; |
| 1016 | int count; | ||
| 1010 | 1017 | ||
| 1011 | if (mmu->pdata) { | 1018 | if (mmu->pdata) { |
| 1012 | const struct ipmmu_vmsa_master *master = mmu->pdata->masters; | 1019 | const struct ipmmu_vmsa_master *master = mmu->pdata->masters; |
| @@ -1014,32 +1021,64 @@ static int ipmmu_find_utlb(struct ipmmu_vmsa_device *mmu, struct device *dev) | |||
| 1014 | unsigned int i; | 1021 | unsigned int i; |
| 1015 | 1022 | ||
| 1016 | for (i = 0; i < mmu->pdata->num_masters; ++i, ++master) { | 1023 | for (i = 0; i < mmu->pdata->num_masters; ++i, ++master) { |
| 1017 | if (strcmp(master->name, devname) == 0) | 1024 | if (strcmp(master->name, devname) == 0) { |
| 1018 | return master->utlb; | 1025 | utlbs = kmalloc(sizeof(*utlbs), GFP_KERNEL); |
| 1026 | if (!utlbs) | ||
| 1027 | return -ENOMEM; | ||
| 1028 | |||
| 1029 | utlbs[0] = master->utlb; | ||
| 1030 | |||
| 1031 | *_utlbs = utlbs; | ||
| 1032 | return 1; | ||
| 1033 | } | ||
| 1019 | } | 1034 | } |
| 1020 | 1035 | ||
| 1021 | return -1; | 1036 | return -EINVAL; |
| 1022 | } | 1037 | } |
| 1023 | 1038 | ||
| 1024 | ret = of_parse_phandle_with_args(dev->of_node, "iommus", | 1039 | count = of_count_phandle_with_args(dev->of_node, "iommus", |
| 1025 | "#iommu-cells", 0, &args); | 1040 | "#iommu-cells"); |
| 1026 | if (ret < 0) | 1041 | if (count < 0) |
| 1027 | return -1; | 1042 | return -EINVAL; |
| 1043 | |||
| 1044 | utlbs = kcalloc(count, sizeof(*utlbs), GFP_KERNEL); | ||
| 1045 | if (!utlbs) | ||
| 1046 | return -ENOMEM; | ||
| 1047 | |||
| 1048 | for (i = 0; i < count; ++i) { | ||
| 1049 | struct of_phandle_args args; | ||
| 1050 | int ret; | ||
| 1051 | |||
| 1052 | ret = of_parse_phandle_with_args(dev->of_node, "iommus", | ||
| 1053 | "#iommu-cells", i, &args); | ||
| 1054 | if (ret < 0) | ||
| 1055 | goto error; | ||
| 1056 | |||
| 1057 | of_node_put(args.np); | ||
| 1058 | |||
| 1059 | if (args.np != mmu->dev->of_node || args.args_count != 1) | ||
| 1060 | goto error; | ||
| 1061 | |||
| 1062 | utlbs[i] = args.args[0]; | ||
| 1063 | } | ||
| 1028 | 1064 | ||
| 1029 | of_node_put(args.np); | 1065 | *_utlbs = utlbs; |
| 1030 | 1066 | ||
| 1031 | if (args.np != mmu->dev->of_node || args.args_count != 1) | 1067 | return count; |
| 1032 | return -1; | ||
| 1033 | 1068 | ||
| 1034 | return args.args[0]; | 1069 | error: |
| 1070 | kfree(utlbs); | ||
| 1071 | return -EINVAL; | ||
| 1035 | } | 1072 | } |
| 1036 | 1073 | ||
| 1037 | static int ipmmu_add_device(struct device *dev) | 1074 | static int ipmmu_add_device(struct device *dev) |
| 1038 | { | 1075 | { |
| 1039 | struct ipmmu_vmsa_archdata *archdata; | 1076 | struct ipmmu_vmsa_archdata *archdata; |
| 1040 | struct ipmmu_vmsa_device *mmu; | 1077 | struct ipmmu_vmsa_device *mmu; |
| 1041 | struct iommu_group *group; | 1078 | struct iommu_group *group = NULL; |
| 1042 | int utlb = -1; | 1079 | unsigned int *utlbs = NULL; |
| 1080 | unsigned int i; | ||
| 1081 | int num_utlbs = 0; | ||
| 1043 | int ret; | 1082 | int ret; |
| 1044 | 1083 | ||
| 1045 | if (dev->archdata.iommu) { | 1084 | if (dev->archdata.iommu) { |
| @@ -1052,8 +1091,8 @@ static int ipmmu_add_device(struct device *dev) | |||
| 1052 | spin_lock(&ipmmu_devices_lock); | 1091 | spin_lock(&ipmmu_devices_lock); |
| 1053 | 1092 | ||
| 1054 | list_for_each_entry(mmu, &ipmmu_devices, list) { | 1093 | list_for_each_entry(mmu, &ipmmu_devices, list) { |
| 1055 | utlb = ipmmu_find_utlb(mmu, dev); | 1094 | num_utlbs = ipmmu_find_utlbs(mmu, dev, &utlbs); |
| 1056 | if (utlb >= 0) { | 1095 | if (num_utlbs) { |
| 1057 | /* | 1096 | /* |
| 1058 | * TODO Take a reference to the MMU to protect | 1097 | * TODO Take a reference to the MMU to protect |
| 1059 | * against device removal. | 1098 | * against device removal. |
| @@ -1064,17 +1103,22 @@ static int ipmmu_add_device(struct device *dev) | |||
| 1064 | 1103 | ||
| 1065 | spin_unlock(&ipmmu_devices_lock); | 1104 | spin_unlock(&ipmmu_devices_lock); |
| 1066 | 1105 | ||
| 1067 | if (utlb < 0) | 1106 | if (num_utlbs <= 0) |
| 1068 | return -ENODEV; | 1107 | return -ENODEV; |
| 1069 | 1108 | ||
| 1070 | if (utlb >= mmu->num_utlbs) | 1109 | for (i = 0; i < num_utlbs; ++i) { |
| 1071 | return -EINVAL; | 1110 | if (utlbs[i] >= mmu->num_utlbs) { |
| 1111 | ret = -EINVAL; | ||
| 1112 | goto error; | ||
| 1113 | } | ||
| 1114 | } | ||
| 1072 | 1115 | ||
| 1073 | /* Create a device group and add the device to it. */ | 1116 | /* Create a device group and add the device to it. */ |
| 1074 | group = iommu_group_alloc(); | 1117 | group = iommu_group_alloc(); |
| 1075 | if (IS_ERR(group)) { | 1118 | if (IS_ERR(group)) { |
| 1076 | dev_err(dev, "Failed to allocate IOMMU group\n"); | 1119 | dev_err(dev, "Failed to allocate IOMMU group\n"); |
| 1077 | return PTR_ERR(group); | 1120 | ret = PTR_ERR(group); |
| 1121 | goto error; | ||
| 1078 | } | 1122 | } |
| 1079 | 1123 | ||
| 1080 | ret = iommu_group_add_device(group, dev); | 1124 | ret = iommu_group_add_device(group, dev); |
| @@ -1082,7 +1126,8 @@ static int ipmmu_add_device(struct device *dev) | |||
| 1082 | 1126 | ||
| 1083 | if (ret < 0) { | ||
