.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Tegra host1x Job |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2010-2015, NVIDIA Corporation. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify it |
---|
7 | | - * under the terms and conditions of the GNU General Public License, |
---|
8 | | - * version 2, as published by the Free Software Foundation. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope it will be useful, but WITHOUT |
---|
11 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
12 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
13 | | - * more details. |
---|
14 | | - * |
---|
15 | | - * You should have received a copy of the GNU General Public License |
---|
16 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
17 | 6 | */ |
---|
18 | 7 | |
---|
19 | 8 | #include <linux/dma-mapping.h> |
---|
20 | 9 | #include <linux/err.h> |
---|
21 | 10 | #include <linux/host1x.h> |
---|
| 11 | +#include <linux/iommu.h> |
---|
22 | 12 | #include <linux/kref.h> |
---|
23 | 13 | #include <linux/module.h> |
---|
24 | 14 | #include <linux/scatterlist.h> |
---|
.. | .. |
---|
37 | 27 | u32 num_cmdbufs, u32 num_relocs) |
---|
38 | 28 | { |
---|
39 | 29 | struct host1x_job *job = NULL; |
---|
40 | | - unsigned int num_unpins = num_cmdbufs + num_relocs; |
---|
| 30 | + unsigned int num_unpins = num_relocs; |
---|
41 | 31 | u64 total; |
---|
42 | 32 | void *mem; |
---|
| 33 | + |
---|
| 34 | + if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) |
---|
| 35 | + num_unpins += num_cmdbufs; |
---|
43 | 36 | |
---|
44 | 37 | /* Check that we're not going to overflow */ |
---|
45 | 38 | total = sizeof(struct host1x_job) + |
---|
.. | .. |
---|
110 | 103 | |
---|
111 | 104 | static unsigned int pin_job(struct host1x *host, struct host1x_job *job) |
---|
112 | 105 | { |
---|
| 106 | + struct host1x_client *client = job->client; |
---|
| 107 | + struct device *dev = client->dev; |
---|
| 108 | + struct host1x_job_gather *g; |
---|
| 109 | + struct iommu_domain *domain; |
---|
113 | 110 | unsigned int i; |
---|
114 | 111 | int err; |
---|
115 | 112 | |
---|
| 113 | + domain = iommu_get_domain_for_dev(dev); |
---|
116 | 114 | job->num_unpins = 0; |
---|
117 | 115 | |
---|
118 | 116 | for (i = 0; i < job->num_relocs; i++) { |
---|
119 | 117 | struct host1x_reloc *reloc = &job->relocs[i]; |
---|
| 118 | + dma_addr_t phys_addr, *phys; |
---|
120 | 119 | struct sg_table *sgt; |
---|
121 | | - dma_addr_t phys_addr; |
---|
122 | 120 | |
---|
123 | 121 | reloc->target.bo = host1x_bo_get(reloc->target.bo); |
---|
124 | 122 | if (!reloc->target.bo) { |
---|
.. | .. |
---|
126 | 124 | goto unpin; |
---|
127 | 125 | } |
---|
128 | 126 | |
---|
129 | | - phys_addr = host1x_bo_pin(reloc->target.bo, &sgt); |
---|
| 127 | + /* |
---|
| 128 | + * If the client device is not attached to an IOMMU, the |
---|
| 129 | + * physical address of the buffer object can be used. |
---|
| 130 | + * |
---|
| 131 | + * Similarly, when an IOMMU domain is shared between all |
---|
| 132 | + * host1x clients, the IOVA is already available, so no |
---|
| 133 | + * need to map the buffer object again. |
---|
| 134 | + * |
---|
| 135 | + * XXX Note that this isn't always safe to do because it |
---|
| 136 | + * relies on an assumption that no cache maintenance is |
---|
| 137 | + * needed on the buffer objects. |
---|
| 138 | + */ |
---|
| 139 | + if (!domain || client->group) |
---|
| 140 | + phys = &phys_addr; |
---|
| 141 | + else |
---|
| 142 | + phys = NULL; |
---|
| 143 | + |
---|
| 144 | + sgt = host1x_bo_pin(dev, reloc->target.bo, phys); |
---|
| 145 | + if (IS_ERR(sgt)) { |
---|
| 146 | + err = PTR_ERR(sgt); |
---|
| 147 | + goto unpin; |
---|
| 148 | + } |
---|
| 149 | + |
---|
| 150 | + if (sgt) { |
---|
| 151 | + unsigned long mask = HOST1X_RELOC_READ | |
---|
| 152 | + HOST1X_RELOC_WRITE; |
---|
| 153 | + enum dma_data_direction dir; |
---|
| 154 | + |
---|
| 155 | + switch (reloc->flags & mask) { |
---|
| 156 | + case HOST1X_RELOC_READ: |
---|
| 157 | + dir = DMA_TO_DEVICE; |
---|
| 158 | + break; |
---|
| 159 | + |
---|
| 160 | + case HOST1X_RELOC_WRITE: |
---|
| 161 | + dir = DMA_FROM_DEVICE; |
---|
| 162 | + break; |
---|
| 163 | + |
---|
| 164 | + case HOST1X_RELOC_READ | HOST1X_RELOC_WRITE: |
---|
| 165 | + dir = DMA_BIDIRECTIONAL; |
---|
| 166 | + break; |
---|
| 167 | + |
---|
| 168 | + default: |
---|
| 169 | + err = -EINVAL; |
---|
| 170 | + goto unpin; |
---|
| 171 | + } |
---|
| 172 | + |
---|
| 173 | + err = dma_map_sgtable(dev, sgt, dir, 0); |
---|
| 174 | + if (err) |
---|
| 175 | + goto unpin; |
---|
| 176 | + |
---|
| 177 | + job->unpins[job->num_unpins].dev = dev; |
---|
| 178 | + job->unpins[job->num_unpins].dir = dir; |
---|
| 179 | + phys_addr = sg_dma_address(sgt->sgl); |
---|
| 180 | + } |
---|
130 | 181 | |
---|
131 | 182 | job->addr_phys[job->num_unpins] = phys_addr; |
---|
132 | 183 | job->unpins[job->num_unpins].bo = reloc->target.bo; |
---|
.. | .. |
---|
134 | 185 | job->num_unpins++; |
---|
135 | 186 | } |
---|
136 | 187 | |
---|
| 188 | + /* |
---|
| 189 | + * We will copy gathers BO content later, so there is no need to |
---|
| 190 | + * hold and pin them. |
---|
| 191 | + */ |
---|
| 192 | + if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) |
---|
| 193 | + return 0; |
---|
| 194 | + |
---|
137 | 195 | for (i = 0; i < job->num_gathers; i++) { |
---|
138 | | - struct host1x_job_gather *g = &job->gathers[i]; |
---|
139 | 196 | size_t gather_size = 0; |
---|
140 | 197 | struct scatterlist *sg; |
---|
141 | 198 | struct sg_table *sgt; |
---|
142 | 199 | dma_addr_t phys_addr; |
---|
143 | 200 | unsigned long shift; |
---|
144 | 201 | struct iova *alloc; |
---|
| 202 | + dma_addr_t *phys; |
---|
145 | 203 | unsigned int j; |
---|
146 | 204 | |
---|
| 205 | + g = &job->gathers[i]; |
---|
147 | 206 | g->bo = host1x_bo_get(g->bo); |
---|
148 | 207 | if (!g->bo) { |
---|
149 | 208 | err = -EINVAL; |
---|
150 | 209 | goto unpin; |
---|
151 | 210 | } |
---|
152 | 211 | |
---|
153 | | - phys_addr = host1x_bo_pin(g->bo, &sgt); |
---|
| 212 | + /** |
---|
| 213 | + * If the host1x is not attached to an IOMMU, there is no need |
---|
| 214 | + * to map the buffer object for the host1x, since the physical |
---|
| 215 | + * address can simply be used. |
---|
| 216 | + */ |
---|
| 217 | + if (!iommu_get_domain_for_dev(host->dev)) |
---|
| 218 | + phys = &phys_addr; |
---|
| 219 | + else |
---|
| 220 | + phys = NULL; |
---|
154 | 221 | |
---|
155 | | - if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && host->domain) { |
---|
156 | | - for_each_sg(sgt->sgl, sg, sgt->nents, j) |
---|
| 222 | + sgt = host1x_bo_pin(host->dev, g->bo, phys); |
---|
| 223 | + if (IS_ERR(sgt)) { |
---|
| 224 | + err = PTR_ERR(sgt); |
---|
| 225 | + goto put; |
---|
| 226 | + } |
---|
| 227 | + |
---|
| 228 | + if (host->domain) { |
---|
| 229 | + for_each_sgtable_sg(sgt, sg, j) |
---|
157 | 230 | gather_size += sg->length; |
---|
158 | 231 | gather_size = iova_align(&host->iova, gather_size); |
---|
159 | 232 | |
---|
.. | .. |
---|
162 | 235 | host->iova_end >> shift, true); |
---|
163 | 236 | if (!alloc) { |
---|
164 | 237 | err = -ENOMEM; |
---|
165 | | - goto unpin; |
---|
| 238 | + goto put; |
---|
166 | 239 | } |
---|
167 | 240 | |
---|
168 | | - err = iommu_map_sg(host->domain, |
---|
| 241 | + err = iommu_map_sgtable(host->domain, |
---|
169 | 242 | iova_dma_addr(&host->iova, alloc), |
---|
170 | | - sgt->sgl, sgt->nents, IOMMU_READ); |
---|
| 243 | + sgt, IOMMU_READ); |
---|
171 | 244 | if (err == 0) { |
---|
172 | 245 | __free_iova(&host->iova, alloc); |
---|
173 | 246 | err = -EINVAL; |
---|
174 | | - goto unpin; |
---|
| 247 | + goto put; |
---|
175 | 248 | } |
---|
176 | 249 | |
---|
177 | | - job->addr_phys[job->num_unpins] = |
---|
178 | | - iova_dma_addr(&host->iova, alloc); |
---|
179 | 250 | job->unpins[job->num_unpins].size = gather_size; |
---|
180 | | - } else { |
---|
181 | | - job->addr_phys[job->num_unpins] = phys_addr; |
---|
| 251 | + phys_addr = iova_dma_addr(&host->iova, alloc); |
---|
| 252 | + } else if (sgt) { |
---|
| 253 | + err = dma_map_sgtable(host->dev, sgt, DMA_TO_DEVICE, 0); |
---|
| 254 | + if (err) |
---|
| 255 | + goto put; |
---|
| 256 | + |
---|
| 257 | + job->unpins[job->num_unpins].dir = DMA_TO_DEVICE; |
---|
| 258 | + job->unpins[job->num_unpins].dev = host->dev; |
---|
| 259 | + phys_addr = sg_dma_address(sgt->sgl); |
---|
182 | 260 | } |
---|
183 | 261 | |
---|
184 | | - job->gather_addr_phys[i] = job->addr_phys[job->num_unpins]; |
---|
| 262 | + job->addr_phys[job->num_unpins] = phys_addr; |
---|
| 263 | + job->gather_addr_phys[i] = phys_addr; |
---|
185 | 264 | |
---|
186 | 265 | job->unpins[job->num_unpins].bo = g->bo; |
---|
187 | 266 | job->unpins[job->num_unpins].sgt = sgt; |
---|
.. | .. |
---|
190 | 269 | |
---|
191 | 270 | return 0; |
---|
192 | 271 | |
---|
| 272 | +put: |
---|
| 273 | + host1x_bo_put(g->bo); |
---|
193 | 274 | unpin: |
---|
194 | 275 | host1x_job_unpin(job); |
---|
195 | 276 | return err; |
---|
.. | .. |
---|
197 | 278 | |
---|
198 | 279 | static int do_relocs(struct host1x_job *job, struct host1x_job_gather *g) |
---|
199 | 280 | { |
---|
200 | | - u32 last_page = ~0; |
---|
201 | | - void *cmdbuf_page_addr = NULL; |
---|
| 281 | + void *cmdbuf_addr = NULL; |
---|
202 | 282 | struct host1x_bo *cmdbuf = g->bo; |
---|
203 | 283 | unsigned int i; |
---|
204 | 284 | |
---|
.. | .. |
---|
220 | 300 | goto patch_reloc; |
---|
221 | 301 | } |
---|
222 | 302 | |
---|
223 | | - if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) { |
---|
224 | | - if (cmdbuf_page_addr) |
---|
225 | | - host1x_bo_kunmap(cmdbuf, last_page, |
---|
226 | | - cmdbuf_page_addr); |
---|
| 303 | + if (!cmdbuf_addr) { |
---|
| 304 | + cmdbuf_addr = host1x_bo_mmap(cmdbuf); |
---|
227 | 305 | |
---|
228 | | - cmdbuf_page_addr = host1x_bo_kmap(cmdbuf, |
---|
229 | | - reloc->cmdbuf.offset >> PAGE_SHIFT); |
---|
230 | | - last_page = reloc->cmdbuf.offset >> PAGE_SHIFT; |
---|
231 | | - |
---|
232 | | - if (unlikely(!cmdbuf_page_addr)) { |
---|
| 306 | + if (unlikely(!cmdbuf_addr)) { |
---|
233 | 307 | pr_err("Could not map cmdbuf for relocation\n"); |
---|
234 | 308 | return -ENOMEM; |
---|
235 | 309 | } |
---|
236 | 310 | } |
---|
237 | 311 | |
---|
238 | | - target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK); |
---|
| 312 | + target = cmdbuf_addr + reloc->cmdbuf.offset; |
---|
239 | 313 | patch_reloc: |
---|
240 | 314 | *target = reloc_addr; |
---|
241 | 315 | } |
---|
242 | 316 | |
---|
243 | | - if (cmdbuf_page_addr) |
---|
244 | | - host1x_bo_kunmap(cmdbuf, last_page, cmdbuf_page_addr); |
---|
| 317 | + if (cmdbuf_addr) |
---|
| 318 | + host1x_bo_munmap(cmdbuf, cmdbuf_addr); |
---|
245 | 319 | |
---|
246 | 320 | return 0; |
---|
247 | 321 | } |
---|
.. | .. |
---|
569 | 643 | |
---|
570 | 644 | for (i = 0; i < job->num_unpins; i++) { |
---|
571 | 645 | struct host1x_job_unpin_data *unpin = &job->unpins[i]; |
---|
| 646 | + struct device *dev = unpin->dev ?: host->dev; |
---|
| 647 | + struct sg_table *sgt = unpin->sgt; |
---|
572 | 648 | |
---|
573 | 649 | if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && |
---|
574 | 650 | unpin->size && host->domain) { |
---|
.. | .. |
---|
578 | 654 | iova_pfn(&host->iova, job->addr_phys[i])); |
---|
579 | 655 | } |
---|
580 | 656 | |
---|
581 | | - host1x_bo_unpin(unpin->bo, unpin->sgt); |
---|
| 657 | + if (unpin->dev && sgt) |
---|
| 658 | + dma_unmap_sgtable(unpin->dev, sgt, unpin->dir, 0); |
---|
| 659 | + |
---|
| 660 | + host1x_bo_unpin(dev, unpin->bo, sgt); |
---|
582 | 661 | host1x_bo_put(unpin->bo); |
---|
583 | 662 | } |
---|
584 | 663 | |
---|