1 /* $OpenBSD: viommu.c,v 1.20 2021/05/16 15:10:19 deraadt Exp $ */
2 /* $NetBSD: iommu.c,v 1.47 2002/02/08 20:03:45 eeh Exp $ */
3
4 /*
5 * Coptright (c) 2008 Mark Kettenis
6 * Copyright (c) 2003 Henric Jungheim
7 * Copyright (c) 2001, 2002 Eduardo Horvath
8 * Copyright (c) 1999, 2000 Matthew R. Green
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 /*
36 * UltraSPARC Hypervisor IOMMU support.
37 */
38
39 #include <sys/param.h>
40 #include <sys/extent.h>
41 #include <sys/malloc.h>
42 #include <sys/systm.h>
43 #include <sys/proc.h>
44 #include <sys/device.h>
45 #include <sys/mbuf.h>
46
47 #include <uvm/uvm_extern.h>
48
49 #include <machine/bus.h>
50 #include <sparc64/sparc64/cache.h>
51 #include <sparc64/dev/iommureg.h>
52 #include <sparc64/dev/iommuvar.h>
53 #include <sparc64/dev/viommuvar.h>
54
55 #include <machine/autoconf.h>
56 #include <machine/cpu.h>
57 #include <machine/hypervisor.h>
58
59 #ifdef DDB
60 #include <machine/db_machdep.h>
61 #include <ddb/db_sym.h>
62 #include <ddb/db_extern.h>
63 #endif
64
65 #ifdef DEBUG
66 #define IDB_BUSDMA 0x1
67 #define IDB_IOMMU 0x2
68 #define IDB_INFO 0x4
69 #define IDB_SYNC 0x8
70 #define IDB_XXX 0x10
71 #define IDB_PRINT_MAP 0x20
72 #define IDB_BREAK 0x40
73 extern int iommudebug;
74 #define DPRINTF(l, s) do { if (iommudebug & l) printf s; } while (0)
75 #else
76 #define DPRINTF(l, s)
77 #endif
78
79 void viommu_enter(struct iommu_state *, struct strbuf_ctl *, bus_addr_t,
80 paddr_t, int);
81 void viommu_remove(struct iommu_state *, struct strbuf_ctl *, bus_addr_t);
82 int viommu_dvmamap_load_seg(bus_dma_tag_t, struct iommu_state *,
83 bus_dmamap_t, bus_dma_segment_t *, int, int, bus_size_t, bus_size_t);
84 int viommu_dvmamap_load_mlist(bus_dma_tag_t, struct iommu_state *,
85 bus_dmamap_t, struct pglist *, int, bus_size_t, bus_size_t);
86 int viommu_dvmamap_append_range(bus_dma_tag_t, bus_dmamap_t, paddr_t,
87 bus_size_t, int, bus_size_t);
88 int iommu_iomap_insert_page(struct iommu_map_state *, paddr_t);
89 bus_addr_t iommu_iomap_translate(struct iommu_map_state *, paddr_t);
90 void viommu_iomap_load_map(struct iommu_state *, struct iommu_map_state *,
91 bus_addr_t, int);
92 void viommu_iomap_unload_map(struct iommu_state *, struct iommu_map_state *);
93 struct iommu_map_state *viommu_iomap_create(int);
94 void iommu_iomap_destroy(struct iommu_map_state *);
95 void iommu_iomap_clear_pages(struct iommu_map_state *);
96 void _viommu_dvmamap_sync(bus_dma_tag_t, bus_dma_tag_t, bus_dmamap_t,
97 bus_addr_t, bus_size_t, int);
98
99 /*
100 * initialise the UltraSPARC IOMMU (Hypervisior):
101 * - allocate and setup the iotsb.
102 * - enable the IOMMU
103 * - create a private DVMA map.
104 */
105 void
viommu_init(char * name,struct iommu_state * is,int tsbsize,u_int32_t iovabase)106 viommu_init(char *name, struct iommu_state *is, int tsbsize,
107 u_int32_t iovabase)
108 {
109 struct vm_page *m;
110 struct pglist mlist;
111
112 /*
113 * Setup the iommu.
114 *
115 * The sun4v iommu is accessed through the hypervisor so we will
116 * deal with it here..
117 */
118 is->is_tsbsize = tsbsize;
119 if (iovabase == (u_int32_t)-1) {
120 is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize);
121 is->is_dvmaend = IOTSB_VEND;
122 } else {
123 is->is_dvmabase = iovabase;
124 is->is_dvmaend = iovabase + IOTSB_VSIZE(tsbsize) - 1;
125 }
126
127 TAILQ_INIT(&mlist);
128 if (uvm_pglistalloc(PAGE_SIZE, 0, -1, PAGE_SIZE, 0, &mlist, 1,
129 UVM_PLA_NOWAIT | UVM_PLA_ZERO) != 0)
130 panic("%s: no memory", __func__);
131 m = TAILQ_FIRST(&mlist);
132 is->is_scratch = VM_PAGE_TO_PHYS(m);
133
134 /*
135 * Allocate a dvma map.
136 */
137 printf("dvma map %x-%x", is->is_dvmabase, is->is_dvmaend);
138 is->is_dvmamap = extent_create(name,
139 is->is_dvmabase, (u_long)is->is_dvmaend + 1,
140 M_DEVBUF, NULL, 0, EX_NOCOALESCE);
141 mtx_init(&is->is_mtx, IPL_HIGH);
142
143 printf("\n");
144 }
145
146 /*
147 * Add an entry to the IOMMU table.
148 */
149 void
viommu_enter(struct iommu_state * is,struct strbuf_ctl * sb,bus_addr_t va,paddr_t pa,int flags)150 viommu_enter(struct iommu_state *is, struct strbuf_ctl *sb, bus_addr_t va,
151 paddr_t pa, int flags)
152 {
153 u_int64_t tsbid = IOTSBSLOT(va, is->is_tsbsize);
154 paddr_t page_list[1], addr;
155 u_int64_t attr, nmapped;
156 int err;
157
158 KASSERT(sb == NULL);
159
160 #ifdef DIAGNOSTIC
161 if (va < is->is_dvmabase || (va + PAGE_MASK) > is->is_dvmaend)
162 panic("viommu_enter: va %#lx not in DVMA space", va);
163 #endif
164
165 attr = PCI_MAP_ATTR_READ | PCI_MAP_ATTR_WRITE;
166 if (flags & BUS_DMA_READ)
167 attr &= ~PCI_MAP_ATTR_READ;
168 if (flags & BUS_DMA_WRITE)
169 attr &= ~PCI_MAP_ATTR_WRITE;
170
171 page_list[0] = trunc_page(pa);
172 if (!pmap_extract(pmap_kernel(), (vaddr_t)page_list, &addr))
173 panic("viommu_enter: pmap_extract failed");
174 err = hv_pci_iommu_map(is->is_devhandle, tsbid, 1, attr,
175 addr, &nmapped);
176 if (err != H_EOK || nmapped != 1)
177 panic("hv_pci_iommu_map: err=%d", err);
178 }
179
180 /*
181 * Remove an entry from the IOMMU table.
182 */
183 void
viommu_remove(struct iommu_state * is,struct strbuf_ctl * sb,bus_addr_t va)184 viommu_remove(struct iommu_state *is, struct strbuf_ctl *sb, bus_addr_t va)
185 {
186 u_int64_t tsbid = IOTSBSLOT(va, is->is_tsbsize);
187 u_int64_t ndemapped;
188 int err;
189
190 KASSERT(sb == NULL);
191
192 #ifdef DIAGNOSTIC
193 if (va < is->is_dvmabase || (va + PAGE_MASK) > is->is_dvmaend)
194 panic("iommu_remove: va 0x%lx not in DVMA space", (u_long)va);
195 if (va != trunc_page(va)) {
196 printf("iommu_remove: unaligned va: %lx\n", va);
197 va = trunc_page(va);
198 }
199 #endif
200
201 err = hv_pci_iommu_demap(is->is_devhandle, tsbid, 1, &ndemapped);
202 if (err != H_EOK || ndemapped != 1)
203 panic("hv_pci_iommu_unmap: err=%d", err);
204 }
205
206 /*
207 * IOMMU DVMA operations, sun4v hypervisor version.
208 */
209
210 #define BUS_DMA_FIND_PARENT(t, fn) \
211 if (t->_parent == NULL) \
212 panic("null bus_dma parent (" #fn ")"); \
213 for (t = t->_parent; t->fn == NULL; t = t->_parent) \
214 if (t->_parent == NULL) \
215 panic("no bus_dma " #fn " located");
216
217 int
viommu_dvmamap_create(bus_dma_tag_t t,bus_dma_tag_t t0,struct iommu_state * is,bus_size_t size,int nsegments,bus_size_t maxsegsz,bus_size_t boundary,int flags,bus_dmamap_t * dmamap)218 viommu_dvmamap_create(bus_dma_tag_t t, bus_dma_tag_t t0,
219 struct iommu_state *is, bus_size_t size, int nsegments,
220 bus_size_t maxsegsz, bus_size_t boundary, int flags,
221 bus_dmamap_t *dmamap)
222 {
223 int ret;
224 bus_dmamap_t map;
225 struct iommu_map_state *ims;
226
227 BUS_DMA_FIND_PARENT(t, _dmamap_create);
228 ret = (*t->_dmamap_create)(t, t0, size, nsegments, maxsegsz, boundary,
229 flags, &map);
230
231 if (ret)
232 return (ret);
233
234 ims = viommu_iomap_create(atop(round_page(size)));
235
236 if (ims == NULL) {
237 bus_dmamap_destroy(t0, map);
238 return (ENOMEM);
239 }
240
241 ims->ims_iommu = is;
242 map->_dm_cookie = ims;
243
244 *dmamap = map;
245
246 return (0);
247 }
248
249 void
viommu_dvmamap_destroy(bus_dma_tag_t t,bus_dma_tag_t t0,bus_dmamap_t map)250 viommu_dvmamap_destroy(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map)
251 {
252 /*
253 * The specification (man page) requires a loaded
254 * map to be unloaded before it is destroyed.
255 */
256 if (map->dm_nsegs)
257 bus_dmamap_unload(t0, map);
258
259 if (map->_dm_cookie)
260 iommu_iomap_destroy(map->_dm_cookie);
261 map->_dm_cookie = NULL;
262
263 BUS_DMA_FIND_PARENT(t, _dmamap_destroy);
264 (*t->_dmamap_destroy)(t, t0, map);
265 }
266
267 /*
268 * Load a contiguous kva buffer into a dmamap. The physical pages are
269 * not assumed to be contiguous. Two passes are made through the buffer
270 * and both call pmap_extract() for the same va->pa translations. It
271 * is possible to run out of pa->dvma mappings; the code should be smart
272 * enough to resize the iomap (when the "flags" permit allocation). It
273 * is trivial to compute the number of entries required (round the length
274 * up to the page size and then divide by the page size)...
275 */
276 int
viommu_dvmamap_load(bus_dma_tag_t t,bus_dma_tag_t t0,bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags)277 viommu_dvmamap_load(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
278 void *buf, bus_size_t buflen, struct proc *p, int flags)
279 {
280 int err = 0;
281 bus_size_t sgsize;
282 u_long dvmaddr, sgstart, sgend;
283 bus_size_t align, boundary;
284 struct iommu_state *is;
285 struct iommu_map_state *ims = map->_dm_cookie;
286 pmap_t pmap;
287
288 #ifdef DIAGNOSTIC
289 if (ims == NULL)
290 panic("viommu_dvmamap_load: null map state");
291 if (ims->ims_iommu == NULL)
292 panic("viommu_dvmamap_load: null iommu");
293 #endif
294 is = ims->ims_iommu;
295
296 if (map->dm_nsegs) {
297 /*
298 * Is it still in use? _bus_dmamap_load should have taken care
299 * of this.
300 */
301 #ifdef DIAGNOSTIC
302 panic("iommu_dvmamap_load: map still in use");
303 #endif
304 bus_dmamap_unload(t0, map);
305 }
306
307 /*
308 * Make sure that on error condition we return "no valid mappings".
309 */
310 map->dm_nsegs = 0;
311
312 if (buflen < 1 || buflen > map->_dm_size) {
313 DPRINTF(IDB_BUSDMA,
314 ("iommu_dvmamap_load(): error %d > %d -- "
315 "map size exceeded!\n", (int)buflen, (int)map->_dm_size));
316 return (EINVAL);
317 }
318
319 /*
320 * A boundary presented to bus_dmamem_alloc() takes precedence
321 * over boundary in the map.
322 */
323 if ((boundary = (map->dm_segs[0]._ds_boundary)) == 0)
324 boundary = map->_dm_boundary;
325 align = MAX(map->dm_segs[0]._ds_align, PAGE_SIZE);
326
327 pmap = p ? p->p_vmspace->vm_map.pmap : pmap_kernel();
328
329 /* Count up the total number of pages we need */
330 iommu_iomap_clear_pages(ims);
331 { /* Scope */
332 bus_addr_t a, aend;
333 bus_addr_t addr = (bus_addr_t)buf;
334 int seg_len = buflen;
335
336 aend = round_page(addr + seg_len);
337 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
338 paddr_t pa;
339
340 if (pmap_extract(pmap, a, &pa) == FALSE)
341 panic("iomap pmap error addr 0x%lx", a);
342
343 err = iommu_iomap_insert_page(ims, pa);
344 if (err) {
345 printf("iomap insert error: %d for "
346 "va 0x%lx pa 0x%lx "
347 "(buf %p len %ld/%lx)\n",
348 err, a, pa, buf, buflen, buflen);
349 iommu_iomap_clear_pages(ims);
350 return (EFBIG);
351 }
352 }
353 }
354 if (flags & BUS_DMA_OVERRUN) {
355 err = iommu_iomap_insert_page(ims, is->is_scratch);
356 if (err) {
357 iommu_iomap_clear_pages(ims);
358 return (EFBIG);
359 }
360 }
361 sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
362
363 mtx_enter(&is->is_mtx);
364 if (flags & BUS_DMA_24BIT) {
365 sgstart = MAX(is->is_dvmamap->ex_start, 0xff000000);
366 sgend = MIN(is->is_dvmamap->ex_end, 0xffffffff);
367 } else {
368 sgstart = is->is_dvmamap->ex_start;
369 sgend = is->is_dvmamap->ex_end;
370 }
371
372 /*
373 * If our segment size is larger than the boundary we need to
374 * split the transfer up into little pieces ourselves.
375 */
376 err = extent_alloc_subregion_with_descr(is->is_dvmamap, sgstart, sgend,
377 sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
378 EX_NOWAIT | EX_BOUNDZERO, &ims->ims_er, (u_long *)&dvmaddr);
379 mtx_leave(&is->is_mtx);
380
381 #ifdef DEBUG
382 if (err || (dvmaddr == (bus_addr_t)-1)) {
383 printf("iommu_dvmamap_load(): extent_alloc(%d, %x) failed!\n",
384 (int)sgsize, flags);
385 #ifdef DDB
386 if (iommudebug & IDB_BREAK)
387 db_enter();
388 #endif
389 }
390 #endif
391 if (err != 0) {
392 iommu_iomap_clear_pages(ims);
393 return (err);
394 }
395
396 /* Set the active DVMA map */
397 map->_dm_dvmastart = dvmaddr;
398 map->_dm_dvmasize = sgsize;
399
400 map->dm_mapsize = buflen;
401
402 viommu_iomap_load_map(is, ims, dvmaddr, flags);
403
404 { /* Scope */
405 bus_addr_t a, aend;
406 bus_addr_t addr = (bus_addr_t)buf;
407 int seg_len = buflen;
408
409 aend = round_page(addr + seg_len);
410 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
411 bus_addr_t pgstart;
412 bus_addr_t pgend;
413 paddr_t pa;
414 int pglen;
415
416 /* Yuck... Redoing the same pmap_extract... */
417 if (pmap_extract(pmap, a, &pa) == FALSE)
418 panic("iomap pmap error addr 0x%lx", a);
419
420 pgstart = pa | (MAX(a, addr) & PAGE_MASK);
421 pgend = pa | (MIN(a + PAGE_SIZE - 1,
422 addr + seg_len - 1) & PAGE_MASK);
423 pglen = pgend - pgstart + 1;
424
425 if (pglen < 1)
426 continue;
427
428 err = viommu_dvmamap_append_range(t, map, pgstart,
429 pglen, flags, boundary);
430 if (err == EFBIG)
431 break;
432 else if (err) {
433 printf("iomap load seg page: %d for "
434 "va 0x%lx pa %lx (%lx - %lx) "
435 "for %d/0x%x\n",
436 err, a, pa, pgstart, pgend, pglen, pglen);
437 break;
438 }
439 }
440 }
441
442 if (err)
443 viommu_dvmamap_unload(t, t0, map);
444
445 return (err);
446 }
447
448 /*
449 * Load a dvmamap from an array of segs or an mlist (if the first
450 * "segs" entry's mlist is non-null). It calls iommu_dvmamap_load_segs()
451 * or iommu_dvmamap_load_mlist() for part of the 2nd pass through the
452 * mapping. This is ugly. A better solution would probably be to have
453 * function pointers for implementing the traversal. That way, there
454 * could be one core load routine for each of the three required algorithms
455 * (buffer, seg, and mlist). That would also mean that the traversal
456 * algorithm would then only need one implementation for each algorithm
457 * instead of two (one for populating the iomap and one for populating
458 * the dvma map).
459 */
460 int
viommu_dvmamap_load_raw(bus_dma_tag_t t,bus_dma_tag_t t0,bus_dmamap_t map,bus_dma_segment_t * segs,int nsegs,bus_size_t size,int flags)461 viommu_dvmamap_load_raw(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
462 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
463 {
464 int i;
465 int left;
466 int err = 0;
467 bus_size_t sgsize;
468 bus_size_t boundary, align;
469 u_long dvmaddr, sgstart, sgend;
470 struct iommu_state *is;
471 struct iommu_map_state *ims = map->_dm_cookie;
472
473 #ifdef DIAGNOSTIC
474 if (ims == NULL)
475 panic("viommu_dvmamap_load_raw: null map state");
476 if (ims->ims_iommu == NULL)
477 panic("viommu_dvmamap_load_raw: null iommu");
478 #endif
479 is = ims->ims_iommu;
480
481 if (map->dm_nsegs) {
482 /* Already in use?? */
483 #ifdef DIAGNOSTIC
484 panic("iommu_dvmamap_load_raw: map still in use");
485 #endif
486 bus_dmamap_unload(t0, map);
487 }
488
489 /*
490 * A boundary presented to bus_dmamem_alloc() takes precedence
491 * over boundary in the map.
492 */
493 if ((boundary = segs[0]._ds_boundary) == 0)
494 boundary = map->_dm_boundary;
495
496 align = MAX(segs[0]._ds_align, PAGE_SIZE);
497
498 /*
499 * Make sure that on error condition we return "no valid mappings".
500 */
501 map->dm_nsegs = 0;
502
503 iommu_iomap_clear_pages(ims);
504 if (segs[0]._ds_mlist) {
505 struct pglist *mlist = segs[0]._ds_mlist;
506 struct vm_page *m;
507 for (m = TAILQ_FIRST(mlist); m != NULL;
508 m = TAILQ_NEXT(m,pageq)) {
509 err = iommu_iomap_insert_page(ims, VM_PAGE_TO_PHYS(m));
510
511 if(err) {
512 printf("iomap insert error: %d for "
513 "pa 0x%lx\n", err, VM_PAGE_TO_PHYS(m));
514 iommu_iomap_clear_pages(ims);
515 return (EFBIG);
516 }
517 }
518 } else {
519 /* Count up the total number of pages we need */
520 for (i = 0, left = size; left > 0 && i < nsegs; i++) {
521 bus_addr_t a, aend;
522 bus_size_t len = segs[i].ds_len;
523 bus_addr_t addr = segs[i].ds_addr;
524 int seg_len = MIN(left, len);
525
526 if (len < 1)
527 continue;
528
529 aend = round_page(addr + seg_len);
530 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
531
532 err = iommu_iomap_insert_page(ims, a);
533 if (err) {
534 printf("iomap insert error: %d for "
535 "pa 0x%lx\n", err, a);
536 iommu_iomap_clear_pages(ims);
537 return (EFBIG);
538 }
539 }
540
541 left -= seg_len;
542 }
543 }
544 if (flags & BUS_DMA_OVERRUN) {
545 err = iommu_iomap_insert_page(ims, is->is_scratch);
546 if (err) {
547 iommu_iomap_clear_pages(ims);
548 return (EFBIG);
549 }
550 }
551 sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
552
553 mtx_enter(&is->is_mtx);
554 if (flags & BUS_DMA_24BIT) {
555 sgstart = MAX(is->is_dvmamap->ex_start, 0xff000000);
556 sgend = MIN(is->is_dvmamap->ex_end, 0xffffffff);
557 } else {
558 sgstart = is->is_dvmamap->ex_start;
559 sgend = is->is_dvmamap->ex_end;
560 }
561
562 /*
563 * If our segment size is larger than the boundary we need to
564 * split the transfer up into little pieces ourselves.
565 */
566 err = extent_alloc_subregion_with_descr(is->is_dvmamap, sgstart, sgend,
567 sgsize, align, 0, (sgsize > boundary) ? 0 : boundary,
568 EX_NOWAIT | EX_BOUNDZERO, &ims->ims_er, (u_long *)&dvmaddr);
569 mtx_leave(&is->is_mtx);
570
571 if (err != 0) {
572 iommu_iomap_clear_pages(ims);
573 return (err);
574 }
575
576 #ifdef DEBUG
577 if (dvmaddr == (bus_addr_t)-1) {
578 printf("iommu_dvmamap_load_raw(): extent_alloc(%d, %x) "
579 "failed!\n", (int)sgsize, flags);
580 #ifdef DDB
581 if (iommudebug & IDB_BREAK)
582 db_enter();
583 #else
584 panic("");
585 #endif
586 }
587 #endif
588
589 /* Set the active DVMA map */
590 map->_dm_dvmastart = dvmaddr;
591 map->_dm_dvmasize = sgsize;
592
593 map->dm_mapsize = size;
594
595 viommu_iomap_load_map(is, ims, dvmaddr, flags);
596
597 if (segs[0]._ds_mlist)
598 err = viommu_dvmamap_load_mlist(t, is, map, segs[0]._ds_mlist,
599 flags, size, boundary);
600 else
601 err = viommu_dvmamap_load_seg(t, is, map, segs, nsegs,
602 flags, size, boundary);
603
604 if (err)
605 viommu_dvmamap_unload(t, t0, map);
606
607 return (err);
608 }
609
610 /*
611 * Insert a range of addresses into a loaded map respecting the specified
612 * boundary and alignment restrictions. The range is specified by its
613 * physical address and length. The range cannot cross a page boundary.
614 * This code (along with most of the rest of the function in this file)
615 * assumes that the IOMMU page size is equal to PAGE_SIZE.
616 */
617 int
viommu_dvmamap_append_range(bus_dma_tag_t t,bus_dmamap_t map,paddr_t pa,bus_size_t length,int flags,bus_size_t boundary)618 viommu_dvmamap_append_range(bus_dma_tag_t t, bus_dmamap_t map, paddr_t pa,
619 bus_size_t length, int flags, bus_size_t boundary)
620 {
621 struct iommu_map_state *ims = map->_dm_cookie;
622 bus_addr_t sgstart, sgend, bd_mask;
623 bus_dma_segment_t *seg = NULL;
624 int i = map->dm_nsegs;
625
626 #ifdef DEBUG
627 if (ims == NULL)
628 panic("iommu_dvmamap_append_range: null map state");
629 #endif
630
631 sgstart = iommu_iomap_translate(ims, pa);
632 sgend = sgstart + length - 1;
633
634 #ifdef DIAGNOSTIC
635 if (sgstart == 0 || sgstart > sgend) {
636 printf("append range invalid mapping for %lx "
637 "(0x%lx - 0x%lx)\n", pa, sgstart, sgend);
638 map->dm_nsegs = 0;
639 return (EINVAL);
640 }
641 #endif
642
643 #ifdef DEBUG
644 if (trunc_page(sgstart) != trunc_page(sgend)) {
645 printf("append range crossing page boundary! "
646 "pa %lx length %ld/0x%lx sgstart %lx sgend %lx\n",
647 pa, length, length, sgstart, sgend);
648 }
649 #endif
650
651 /*
652 * We will attempt to merge this range with the previous entry
653 * (if there is one).
654 */
655 if (i > 0) {
656 seg = &map->dm_segs[i - 1];
657 if (sgstart == seg->ds_addr + seg->ds_len) {
658 length += seg->ds_len;
659 sgstart = seg->ds_addr;
660 sgend = sgstart + length - 1;
661 } else
662 seg = NULL;
663 }
664
665 if (seg == NULL) {
666 seg = &map->dm_segs[i];
667 if (++i > map->_dm_segcnt) {
668 map->dm_nsegs = 0;
669 return (EFBIG);
670 }
671 }
672
673 /*
674 * At this point, "i" is the index of the *next* bus_dma_segment_t
675 * (the segment count, aka map->dm_nsegs) and "seg" points to the
676 * *current* entry. "length", "sgstart", and "sgend" reflect what
677 * we intend to put in "*seg". No assumptions should be made about
678 * the contents of "*seg". Only "boundary" issue can change this
679 * and "boundary" is often zero, so explicitly test for that case
680 * (the test is strictly an optimization).
681 */
682 if (boundary != 0) {
683 bd_mask = ~(boundary - 1);
684
685 while ((sgstart & bd_mask) != (sgend & bd_mask)) {
686 /*
687 * We are crossing a boundary so fill in the current
688 * segment with as much as possible, then grab a new
689 * one.
690 */
691
692 seg->ds_addr = sgstart;
693 seg->ds_len = boundary - (sgstart & ~bd_mask);
694
695 sgstart += seg->ds_len; /* sgend stays the same */
696 length -= seg->ds_len;
697
698 seg = &map->dm_segs[i];
699 if (++i > map->_dm_segcnt) {
700 map->dm_nsegs = 0;
701 return (EFBIG);
702 }
703 }
704 }
705
706 seg->ds_addr = sgstart;
707 seg->ds_len = length;
708 map->dm_nsegs = i;
709
710 return (0);
711 }
712
713 /*
714 * Populate the iomap from a bus_dma_segment_t array. See note for
715 * iommu_dvmamap_load() * regarding page entry exhaustion of the iomap.
716 * This is less of a problem for load_seg, as the number of pages
717 * is usually similar to the number of segments (nsegs).
718 */
719 int
viommu_dvmamap_load_seg(bus_dma_tag_t t,struct iommu_state * is,bus_dmamap_t map,bus_dma_segment_t * segs,int nsegs,int flags,bus_size_t size,bus_size_t boundary)720 viommu_dvmamap_load_seg(bus_dma_tag_t t, struct iommu_state *is,
721 bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, int flags,
722 bus_size_t size, bus_size_t boundary)
723 {
724 int i;
725 int left;
726 int seg;
727
728 /*
729 * This segs is made up of individual physical
730 * segments, probably by _bus_dmamap_load_uio() or
731 * _bus_dmamap_load_mbuf(). Ignore the mlist and
732 * load each one individually.
733 */
734
735 /*
736 * Keep in mind that each segment could span
737 * multiple pages and that these are not always
738 * adjacent. The code is no longer adding dvma
739 * aliases to the IOMMU. The STC will not cross
740 * page boundaries anyway and a IOMMU table walk
741 * vs. what may be a streamed PCI DMA to a ring
742 * descriptor is probably a wash. It eases TLB
743 * pressure and in the worst possible case, it is
744 * only as bad a non-IOMMUed architecture. More
745 * importantly, the code is not quite as hairy.
746 * (It's bad enough as it is.)
747 */
748 left = size;
749 seg = 0;
750 for (i = 0; left > 0 && i < nsegs; i++) {
751 bus_addr_t a, aend;
752 bus_size_t len = segs[i].ds_len;
753 bus_addr_t addr = segs[i].ds_addr;
754 int seg_len = MIN(left, len);
755
756 if (len < 1)
757 continue;
758
759 aend = round_page(addr + seg_len);
760 for (a = trunc_page(addr); a < aend; a += PAGE_SIZE) {
761 bus_addr_t pgstart;
762 bus_addr_t pgend;
763 int pglen;
764 int err;
765
766 pgstart = MAX(a, addr);
767 pgend = MIN(a + PAGE_SIZE - 1, addr + seg_len - 1);
768 pglen = pgend - pgstart + 1;
769
770 if (pglen < 1)
771 continue;
772
773 err = viommu_dvmamap_append_range(t, map, pgstart,
774 pglen, flags, boundary);
775 if (err == EFBIG)
776 return (err);
777 if (err) {
778 printf("iomap load seg page: %d for "
779 "pa 0x%lx (%lx - %lx for %d/%x\n",
780 err, a, pgstart, pgend, pglen, pglen);
781 return (err);
782 }
783
784 }
785
786 left -= seg_len;
787 }
788 return (0);
789 }
790
791 /*
792 * Populate the iomap from an mlist. See note for iommu_dvmamap_load()
793 * regarding page entry exhaustion of the iomap.
794 */
795 int
viommu_dvmamap_load_mlist(bus_dma_tag_t t,struct iommu_state * is,bus_dmamap_t map,struct pglist * mlist,int flags,bus_size_t size,bus_size_t boundary)796 viommu_dvmamap_load_mlist(bus_dma_tag_t t, struct iommu_state *is,
797 bus_dmamap_t map, struct pglist *mlist, int flags,
798 bus_size_t size, bus_size_t boundary)
799 {
800 struct vm_page *m;
801 paddr_t pa;
802 int err;
803
804 /*
805 * This was allocated with bus_dmamem_alloc.
806 * The pages are on an `mlist'.
807 */
808 for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq)) {
809 pa = VM_PAGE_TO_PHYS(m);
810
811 err = viommu_dvmamap_append_range(t, map, pa,
812 MIN(PAGE_SIZE, size), flags, boundary);
813 if (err == EFBIG)
814 return (err);
815 if (err) {
816 printf("iomap load seg page: %d for pa 0x%lx "
817 "(%lx - %lx for %d/%x\n", err, pa, pa,
818 pa + PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
819 return (err);
820 }
821 if (size < PAGE_SIZE)
822 break;
823 size -= PAGE_SIZE;
824 }
825
826 return (0);
827 }
828
829 /*
830 * Unload a dvmamap.
831 */
832 void
viommu_dvmamap_unload(bus_dma_tag_t t,bus_dma_tag_t t0,bus_dmamap_t map)833 viommu_dvmamap_unload(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map)
834 {
835 struct iommu_state *is;
836 struct iommu_map_state *ims = map->_dm_cookie;
837 bus_addr_t dvmaddr = map->_dm_dvmastart;
838 bus_size_t sgsize = map->_dm_dvmasize;
839 int error;
840
841 #ifdef DEBUG
842 if (ims == NULL)
843 panic("viommu_dvmamap_unload: null map state");
844 if (ims->ims_iommu == NULL)
845 panic("viommu_dvmamap_unload: null iommu");
846 #endif /* DEBUG */
847
848 is = ims->ims_iommu;
849
850 /* Remove the IOMMU entries */
851 viommu_iomap_unload_map(is, ims);
852
853 /* Clear the iomap */
854 iommu_iomap_clear_pages(ims);
855
856 bus_dmamap_unload(t->_parent, map);
857
858 /* Mark the mappings as invalid. */
859 map->dm_mapsize = 0;
860 map->dm_nsegs = 0;
861
862 mtx_enter(&is->is_mtx);
863 error = extent_free(is->is_dvmamap, dvmaddr, sgsize, EX_NOWAIT);
864 map->_dm_dvmastart = 0;
865 map->_dm_dvmasize = 0;
866 mtx_leave(&is->is_mtx);
867 if (error != 0)
868 printf("warning: %ld of DVMA space lost\n", sgsize);
869 }
870
871 void
viommu_dvmamap_sync(bus_dma_tag_t t,bus_dma_tag_t t0,bus_dmamap_t map,bus_addr_t offset,bus_size_t len,int ops)872 viommu_dvmamap_sync(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dmamap_t map,
873 bus_addr_t offset, bus_size_t len, int ops)
874 {
875 #ifdef DIAGNOSTIC
876 struct iommu_map_state *ims = map->_dm_cookie;
877
878 if (ims == NULL)
879 panic("viommu_dvmamap_sync: null map state");
880 if (ims->ims_iommu == NULL)
881 panic("viommu_dvmamap_sync: null iommu");
882 #endif
883 if (len == 0)
884 return;
885
886 if (ops & BUS_DMASYNC_PREWRITE)
887 __membar("#MemIssue");
888
889 #if 0
890 if (ops & (BUS_DMASYNC_POSTREAD | BUS_DMASYNC_PREWRITE))
891 _viommu_dvmamap_sync(t, t0, map, offset, len, ops);
892 #endif
893
894 if (ops & BUS_DMASYNC_POSTREAD)
895 __membar("#MemIssue");
896 }
897
898 int
viommu_dvmamem_alloc(bus_dma_tag_t t,bus_dma_tag_t t0,bus_size_t size,bus_size_t alignment,bus_size_t boundary,bus_dma_segment_t * segs,int nsegs,int * rsegs,int flags)899 viommu_dvmamem_alloc(bus_dma_tag_t t, bus_dma_tag_t t0, bus_size_t size,
900 bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs,
901 int nsegs, int *rsegs, int flags)
902 {
903
904 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_alloc: sz %llx align %llx "
905 "bound %llx segp %p flags %d\n", (unsigned long long)size,
906 (unsigned long long)alignment, (unsigned long long)boundary,
907 segs, flags));
908 BUS_DMA_FIND_PARENT(t, _dmamem_alloc);
909 return ((*t->_dmamem_alloc)(t, t0, size, alignment, boundary,
910 segs, nsegs, rsegs, flags | BUS_DMA_DVMA));
911 }
912
913 void
viommu_dvmamem_free(bus_dma_tag_t t,bus_dma_tag_t t0,bus_dma_segment_t * segs,int nsegs)914 viommu_dvmamem_free(bus_dma_tag_t t, bus_dma_tag_t t0, bus_dma_segment_t *segs,
915 int nsegs)
916 {
917
918 DPRINTF(IDB_BUSDMA, ("iommu_dvmamem_free: segp %p nsegs %d\n",
919 segs, nsegs));
920 BUS_DMA_FIND_PARENT(t, _dmamem_free);
921 (*t->_dmamem_free)(t, t0, segs, nsegs);
922 }
923
924 /*
925 * Create a new iomap.
926 */
927 struct iommu_map_state *
viommu_iomap_create(int n)928 viommu_iomap_create(int n)
929 {
930 struct iommu_map_state *ims;
931
932 /* Safety for heavily fragmented data, such as mbufs */
933 n += 4;
934 if (n < 16)
935 n = 16;
936
937 ims = malloc(sizeof(*ims) + (n - 1) * sizeof(ims->ims_map.ipm_map[0]),
938 M_DEVBUF, M_NOWAIT | M_ZERO);
939 if (ims == NULL)
940 return (NULL);
941
942 /* Initialize the map. */
943 ims->ims_map.ipm_maxpage = n;
944 SPLAY_INIT(&ims->ims_map.ipm_tree);
945
946 return (ims);
947 }
948
949 /*
950 * Locate the iomap by filling in the pa->va mapping and inserting it
951 * into the IOMMU tables.
952 */
953 void
viommu_iomap_load_map(struct iommu_state * is,struct iommu_map_state * ims,bus_addr_t vmaddr,int flags)954 viommu_iomap_load_map(struct iommu_state *is, struct iommu_map_state *ims,
955 bus_addr_t vmaddr, int flags)
956 {
957 struct iommu_page_map *ipm = &ims->ims_map;
958 struct iommu_page_entry *e;
959 int i;
960
961 for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e) {
962 e->ipe_va = vmaddr;
963 viommu_enter(is, NULL, e->ipe_va, e->ipe_pa, flags);
964 vmaddr += PAGE_SIZE;
965 }
966 }
967
968 /*
969 * Remove the iomap from the IOMMU.
970 */
971 void
viommu_iomap_unload_map(struct iommu_state * is,struct iommu_map_state * ims)972 viommu_iomap_unload_map(struct iommu_state *is, struct iommu_map_state *ims)
973 {
974 struct iommu_page_map *ipm = &ims->ims_map;
975 struct iommu_page_entry *e;
976 int i;
977
978 for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e)
979 viommu_remove(is, NULL, e->ipe_va);
980 }
981