xref: /openbsd/sys/arch/sparc64/dev/viommu.c (revision 4123b6a7)
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