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