]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/sun4v/sun4v/hviommu.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / sun4v / sun4v / hviommu.c
1 /*-
2  * Copyright (c) 1999, 2000 Matthew R. Green
3  * Copyright (c) 2001-2003 Thomas Moestl
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*-
30  * Copyright (c) 1998 The NetBSD Foundation, Inc.
31  * All rights reserved.
32  *
33  * This code is derived from software contributed to The NetBSD Foundation
34  * by Paul Kranenburg.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. All advertising materials mentioning features or use of this software
45  *    must display the following acknowledgement:
46  *        This product includes software developed by the NetBSD
47  *        Foundation, Inc. and its contributors.
48  * 4. Neither the name of The NetBSD Foundation nor the names of its
49  *    contributors may be used to endorse or promote products derived
50  *    from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
53  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
54  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
55  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
56  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
59  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
60  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
61  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62  * POSSIBILITY OF SUCH DAMAGE.
63  */
64 /*-
65  * Copyright (c) 1992, 1993
66  *      The Regents of the University of California.  All rights reserved.
67  *
68  * This software was developed by the Computer Systems Engineering group
69  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
70  * contributed to Berkeley.
71  *
72  * Redistribution and use in source and binary forms, with or without
73  * modification, are permitted provided that the following conditions
74  * are met:
75  * 1. Redistributions of source code must retain the above copyright
76  *    notice, this list of conditions and the following disclaimer.
77  * 2. Redistributions in binary form must reproduce the above copyright
78  *    notice, this list of conditions and the following disclaimer in the
79  *    documentation and/or other materials provided with the distribution.
80  * 4. Neither the name of the University nor the names of its contributors
81  *    may be used to endorse or promote products derived from this software
82  *    without specific prior written permission.
83  *
84  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
85  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
86  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
87  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
88  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
89  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
90  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
91  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
92  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
93  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94  * SUCH DAMAGE.
95  *
96  *      from: NetBSD: sbus.c,v 1.13 1999/05/23 07:24:02 mrg Exp
97  *      from: @(#)sbus.c        8.1 (Berkeley) 6/11/93
98  *      from: NetBSD: iommu.c,v 1.42 2001/08/06 22:02:58 eeh Exp
99  *
100  * $FreeBSD$
101  */
102
103 #include <sys/param.h>
104 #include <sys/kernel.h>
105 #include <sys/lock.h>
106 #include <sys/malloc.h>
107 #include <sys/mbuf.h>
108 #include <sys/mutex.h>
109 #include <sys/proc.h>
110 #include <sys/uio.h>
111
112 #include <vm/vm.h>
113 #include <vm/pmap.h>
114 #include <vm/vm_map.h>
115
116 #include <machine/bus.h>
117 #include <machine/bus_private.h>
118 #include <machine/hviommu.h>
119 #include <machine/pmap.h>
120 #include <machine/resource.h>
121
122 #include <machine/hypervisorvar.h>
123 #include <machine/hv_api.h>
124
125
126 #include <sys/rman.h>
127
128 /*
129  * Tuning constants.
130  */
131 #define IOMMU_MAX_PRE           (32 * 1024)
132 #define IOMMU_MAX_PRE_SEG       3
133
134 #define IO_PAGE_SIZE            PAGE_SIZE_8K
135 #define IO_PAGE_MASK            PAGE_MASK_8K
136 #define IO_PAGE_SHIFT           PAGE_SHIFT_8K
137 #define round_io_page(x)        round_page(x)
138 #define trunc_io_page(x)        trunc_page(x)
139
140
141 MALLOC_DEFINE(M_HVIOMMU, "hviommu", "HyperVisor IOMMU");
142
143 TAILQ_HEAD(hviommu_maplruq_head, bus_dmamap);
144
145 struct hviommu {
146         struct mtx      him_mtx;
147
148         devhandle_t     him_handle;
149         u_long          him_dvmabase;
150         u_long          him_dvmasize;
151
152         struct hviommu_maplruq_head him_maplruq;
153         struct rman     him_rman;
154 };
155
156 #define VA_TO_TSBID(him, va)    ((va - (him)->him_dvmabase) >> IO_PAGE_SHIFT)
157
158 #ifdef IOMMU_DEBUG
159 #define DPRINTF printf
160 #else 
161 #define DPRINTF(...)
162 #endif
163
164 /*
165  * Always overallocate one page; this is needed to handle alignment of the
166  * buffer, so it makes sense using a lazy allocation scheme.
167  */
168 #define IOMMU_SIZE_ROUNDUP(sz)                                          \
169         (round_io_page(sz) + IO_PAGE_SIZE)
170
171 /* Resource helpers */
172 #define IOMMU_RES_TO(v) ((v) >> IO_PAGE_SHIFT)
173 #define IOMMU_RES_START(res)                                            \
174         ((bus_addr_t)rman_get_start(res) << IO_PAGE_SHIFT)
175 #define IOMMU_RES_END(res)                                              \
176         ((bus_addr_t)(rman_get_end(res) + 1) << IO_PAGE_SHIFT)
177 #define IOMMU_RES_SIZE(res)                                             \
178         ((bus_size_t)rman_get_size(res) << IO_PAGE_SHIFT)
179
180 /* Helpers for struct bus_dmamap_res */
181 #define BDR_START(r)    IOMMU_RES_START((r)->dr_res)
182 #define BDR_END(r)      IOMMU_RES_END((r)->dr_res)
183 #define BDR_SIZE(r)     IOMMU_RES_SIZE((r)->dr_res)
184
185 /* Locking macros. */
186 #define HIM_LOCK(him)   mtx_lock(&him->him_mtx)
187 #define HIM_LOCK_ASSERT(him)    mtx_assert(&him->him_mtx, MA_OWNED)
188 #define HIM_UNLOCK(him) mtx_unlock(&him->him_mtx)
189
190 /* LRU queue handling for lazy resource allocation. */
191 static __inline void
192 hviommu_map_insq(struct hviommu *him, bus_dmamap_t map)
193 {
194
195         HIM_LOCK_ASSERT(him);
196         if (!SLIST_EMPTY(&map->dm_reslist)) {
197                 if (map->dm_onq)
198                         TAILQ_REMOVE(&him->him_maplruq, map, dm_maplruq);
199                 TAILQ_INSERT_TAIL(&him->him_maplruq, map, dm_maplruq);
200                 map->dm_onq = 1;
201         }
202 }
203
204 static __inline void
205 hviommu_map_remq(struct hviommu *him, bus_dmamap_t map)
206 {
207
208         HIM_LOCK_ASSERT(him);
209         if (map->dm_onq)
210                 TAILQ_REMOVE(&him->him_maplruq, map, dm_maplruq);
211         map->dm_onq = 0;
212 }
213
214 struct hviommu *
215 hviommu_init(devhandle_t dh, u_long dvmabase, u_long dvmasize)
216 {
217         struct hviommu *him;
218         u_long end;
219
220         him = malloc(sizeof *him, M_HVIOMMU, M_WAITOK|M_ZERO);
221
222         mtx_init(&him->him_mtx, "hviommu", NULL, MTX_DEF);
223         him->him_handle = dh;
224         him->him_dvmabase = dvmabase;
225         him->him_dvmasize = dvmasize;
226
227         TAILQ_INIT(&him->him_maplruq);
228         him->him_rman.rm_type = RMAN_ARRAY;
229         him->him_rman.rm_descr = "HyperVisor IOMMU Memory";
230         end = him->him_dvmabase + him->him_dvmasize - 1;
231         if (rman_init(&him->him_rman) != 0 ||
232             rman_manage_region(&him->him_rman, him->him_dvmabase >>
233             IO_PAGE_SHIFT, end >> IO_PAGE_SHIFT) != 0)
234                 panic("%s: can't initalize rman", __func__);
235
236         return him;
237 }
238
239 static void
240 hviommu_remove(struct hviommu *him, vm_offset_t va, vm_size_t len)
241 {
242         uint64_t error;
243         pages_t demapped;
244
245         KASSERT(va >= him->him_dvmabase,
246             ("%s: va 0x%lx not in DVMA space", __func__, (u_long)va));
247         KASSERT(va + len >= va,
248             ("%s: va 0x%lx + len 0x%lx wraps", __func__, (long)va, (long)len));
249         KASSERT((va & IO_PAGE_MASK) == 0 && (len & IO_PAGE_MASK) == 0,
250             ("%s: va %#lx or len %#lx not page aligned", __func__, va, len));
251         while (len > 0) {
252                 if ((error = hv_pci_iommu_demap(him->him_handle,
253                     VA_TO_TSBID(him, va), len >> IO_PAGE_SHIFT, &demapped))) {
254                         printf("%s: demap: va: %#lx, npages: %#lx, err: %ld\n",
255                             __func__, va, len >> IO_PAGE_SHIFT, error);
256                         demapped = 1;
257                 }
258                 va += demapped << IO_PAGE_SHIFT;
259                 len -= demapped << IO_PAGE_SHIFT;
260         }
261 }
262
263 /*
264  * Allocate DVMA virtual memory for a map. The map may not be on a queue, so
265  * that it can be freely modified.
266  */
267 static int
268 hviommu_dvma_valloc(bus_dma_tag_t t, struct hviommu *him, bus_dmamap_t map,
269     bus_size_t size)
270 {
271         struct resource *res;
272         struct bus_dmamap_res *bdr;
273         bus_size_t align, sgsize;
274
275         KASSERT(!map->dm_onq, ("hviommu_dvma_valloc: map on queue!"));
276         if ((bdr = malloc(sizeof(*bdr), M_HVIOMMU, M_NOWAIT)) == NULL)
277                 return (EAGAIN);
278
279         /*
280          * If a boundary is specified, a map cannot be larger than it; however
281          * we do not clip currently, as that does not play well with the lazy
282          * allocation code.
283          * Alignment to a page boundary is always enforced.
284          */
285         align = (t->dt_alignment + IO_PAGE_MASK) >> IO_PAGE_SHIFT;
286         sgsize = IOMMU_RES_TO(round_io_page(size));
287         if (t->dt_boundary > 0 && t->dt_boundary < IO_PAGE_SIZE)
288                 panic("hviommu_dvmamap_load: illegal boundary specified");
289         res = rman_reserve_resource_bound(&him->him_rman, 0L,
290             IOMMU_RES_TO(t->dt_lowaddr), sgsize,
291             IOMMU_RES_TO(t->dt_boundary),
292             RF_ACTIVE | rman_make_alignment_flags(align), NULL);
293         if (res == NULL) {
294                 free(bdr, M_HVIOMMU);
295                 return (ENOMEM);
296         }
297
298         bdr->dr_res = res;
299         bdr->dr_used = 0;
300         SLIST_INSERT_HEAD(&map->dm_reslist, bdr, dr_link);
301         return (0);
302 }
303
304 /* Unload the map and mark all resources as unused, but do not free them. */
305 static void
306 hviommu_dvmamap_vunload(struct hviommu *him, bus_dmamap_t map)
307 {
308         struct bus_dmamap_res *r;
309
310         SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
311                 hviommu_remove(him, BDR_START(r), BDR_SIZE(r));
312                 r->dr_used = 0;
313         }
314 }
315
316 /* Free a DVMA virtual memory resource. */
317 static __inline void
318 hviommu_dvma_vfree_res(bus_dmamap_t map, struct bus_dmamap_res *r)
319 {
320
321         KASSERT(r->dr_used == 0, ("hviommu_dvma_vfree_res: resource busy!"));
322         if (r->dr_res != NULL && rman_release_resource(r->dr_res) != 0)
323                 printf("warning: DVMA space lost\n");
324         SLIST_REMOVE(&map->dm_reslist, r, bus_dmamap_res, dr_link);
325         free(r, M_HVIOMMU);
326 }
327
328 /* Free all DVMA virtual memory for a map. */
329 static void
330 hviommu_dvma_vfree(struct hviommu *him, bus_dmamap_t map)
331 {
332
333         HIM_LOCK(him);
334         hviommu_map_remq(him, map);
335         hviommu_dvmamap_vunload(him, map);
336         HIM_UNLOCK(him);
337         while (!SLIST_EMPTY(&map->dm_reslist))
338                 hviommu_dvma_vfree_res(map, SLIST_FIRST(&map->dm_reslist));
339 }
340
341 /* Prune a map, freeing all unused DVMA resources. */
342 static bus_size_t
343 hviommu_dvma_vprune(struct hviommu *him, bus_dmamap_t map)
344 {
345         struct bus_dmamap_res *r, *n;
346         bus_size_t freed = 0;
347
348         HIM_LOCK_ASSERT(him);
349         for (r = SLIST_FIRST(&map->dm_reslist); r != NULL; r = n) {
350                 n = SLIST_NEXT(r, dr_link);
351                 if (r->dr_used == 0) {
352                         freed += BDR_SIZE(r);
353                         hviommu_dvma_vfree_res(map, r);
354                 }
355         }
356         if (SLIST_EMPTY(&map->dm_reslist))
357                 hviommu_map_remq(him, map);
358         return (freed);
359 }
360
361 /*
362  * Try to find a suitably-sized (and if requested, -aligned) slab of DVMA
363  * memory with IO page offset voffs.
364  */
365 static bus_addr_t
366 hviommu_dvma_vfindseg(bus_dmamap_t map, vm_offset_t voffs, bus_size_t size,
367     bus_addr_t amask)
368 {
369         struct bus_dmamap_res *r;
370         bus_addr_t dvmaddr, dvmend;
371
372         KASSERT(!map->dm_onq, ("hviommu_dvma_vfindseg: map on queue!"));
373         SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
374                 dvmaddr = round_io_page(BDR_START(r) + r->dr_used);
375                 /* Alignment can only work with voffs == 0. */
376                 dvmaddr = (dvmaddr + amask) & ~amask;
377                 dvmaddr += voffs;
378                 dvmend = dvmaddr + size;
379                 if (dvmend <= BDR_END(r)) {
380                         r->dr_used = dvmend - BDR_START(r);
381                         r->dr_offset = voffs;
382                         return (dvmaddr);
383                 }
384         }
385         return (0);
386 }
387
388 /*
389  * Try to find or allocate a slab of DVMA space; see above.
390  */
391 static int
392 hviommu_dvma_vallocseg(bus_dma_tag_t dt, struct hviommu *him, bus_dmamap_t map,
393     vm_offset_t voffs, bus_size_t size, bus_addr_t amask, bus_addr_t *addr)
394 {
395         bus_dmamap_t tm, last;
396         bus_addr_t dvmaddr, freed;
397         int error, complete = 0;
398
399         dvmaddr = hviommu_dvma_vfindseg(map, voffs, size, amask);
400
401         /* Need to allocate. */
402         if (dvmaddr == 0) {
403                 while ((error = hviommu_dvma_valloc(dt, him, map,
404                         voffs + size)) == ENOMEM && !complete) {
405                         /*
406                          * Free the allocated DVMA of a few maps until
407                          * the required size is reached. This is an
408                          * approximation to not have to call the allocation
409                          * function too often; most likely one free run
410                          * will not suffice if not one map was large enough
411                          * itself due to fragmentation.
412                          */
413                         HIM_LOCK(him);
414                         freed = 0;
415                         last = TAILQ_LAST(&him->him_maplruq, hviommu_maplruq_head);
416                         do {
417                                 tm = TAILQ_FIRST(&him->him_maplruq);
418                                 complete = tm == last;
419                                 if (tm == NULL)
420                                         break;
421                                 freed += hviommu_dvma_vprune(him, tm);
422                                 /* Move to the end. */
423                                 hviommu_map_insq(him, tm);
424                         } while (freed < size && !complete);
425                         HIM_UNLOCK(him);
426                 }
427                 if (error != 0)
428                         return (error);
429                 dvmaddr = hviommu_dvma_vfindseg(map, voffs, size, amask);
430                 KASSERT(dvmaddr != 0,
431                     ("hviommu_dvma_vallocseg: allocation failed unexpectedly!"));
432         }
433         *addr = dvmaddr;
434         return (0);
435 }
436
437 static int
438 hviommu_dvmamem_alloc(bus_dma_tag_t dt, void **vaddr, int flags,
439     bus_dmamap_t *mapp)
440 {
441         struct hviommu *him = dt->dt_cookie;
442         int error, mflags;
443
444         /*
445          * XXX: This will break for 32 bit transfers on machines with more than
446          * 16G (1 << 34 bytes) of memory.
447          */
448         if ((error = sparc64_dma_alloc_map(dt, mapp)) != 0)
449                 return (error);
450
451         if ((flags & BUS_DMA_NOWAIT) != 0)
452                 mflags = M_NOWAIT;
453         else
454                 mflags = M_WAITOK;
455         if ((flags & BUS_DMA_ZERO) != 0)
456                 mflags |= M_ZERO;
457
458         if ((*vaddr = malloc(dt->dt_maxsize, M_HVIOMMU, mflags)) == NULL) {
459                 error = ENOMEM;
460                 sparc64_dma_free_map(dt, *mapp);
461                 return (error);
462         }
463         if ((flags & BUS_DMA_COHERENT) != 0)
464                 (*mapp)->dm_flags |= DMF_COHERENT;
465         /*
466          * Try to preallocate DVMA space. If this fails, it is retried at load
467          * time.
468          */
469         hviommu_dvma_valloc(dt, him, *mapp, IOMMU_SIZE_ROUNDUP(dt->dt_maxsize));
470         HIM_LOCK(him);
471         hviommu_map_insq(him, *mapp);
472         HIM_UNLOCK(him);
473         return (0);
474 }
475
476 static void
477 hviommu_dvmamem_free(bus_dma_tag_t dt, void *vaddr, bus_dmamap_t map)
478 {
479         struct hviommu *him = dt->dt_cookie;
480
481         hviommu_dvma_vfree(him, map);
482         sparc64_dma_free_map(dt, map);
483         free(vaddr, M_HVIOMMU);
484 }
485
486 static int
487 hviommu_dvmamap_create(bus_dma_tag_t dt, int flags, bus_dmamap_t *mapp)
488 {
489         struct hviommu *him = dt->dt_cookie;
490         bus_size_t totsz, presz, currsz;
491         int error, i, maxpre;
492
493         if ((error = sparc64_dma_alloc_map(dt, mapp)) != 0)
494                 return (error);
495         if ((flags & BUS_DMA_COHERENT) != 0)
496                 (*mapp)->dm_flags |= DMF_COHERENT;
497         /*
498          * Preallocate DVMA space; if this fails now, it is retried at load
499          * time. Through bus_dmamap_load_mbuf() and bus_dmamap_load_uio(), it
500          * is possible to have multiple discontiguous segments in a single map,
501          * which is handled by allocating additional resources, instead of
502          * increasing the size, to avoid fragmentation.
503          * Clamp preallocation to IOMMU_MAX_PRE. In some situations we can
504          * handle more; that case is handled by reallocating at map load time.
505          */
506         totsz = ulmin(IOMMU_SIZE_ROUNDUP(dt->dt_maxsize), IOMMU_MAX_PRE); 
507         error = hviommu_dvma_valloc(dt, him, *mapp, totsz);
508         if (error != 0)
509                 return (0);
510         /*
511          * Try to be smart about preallocating some additional segments if
512          * needed.
513          */
514         maxpre = imin(dt->dt_nsegments, IOMMU_MAX_PRE_SEG);
515         presz = dt->dt_maxsize / maxpre;
516         KASSERT(presz != 0, ("hviommu_dvmamap_create: bogus preallocation size "
517             ", nsegments = %d, maxpre = %d, maxsize = %lu", dt->dt_nsegments,
518             maxpre, dt->dt_maxsize));
519         for (i = 1; i < maxpre && totsz < IOMMU_MAX_PRE; i++) {
520                 currsz = round_io_page(ulmin(presz, IOMMU_MAX_PRE - totsz));
521                 error = hviommu_dvma_valloc(dt, him, *mapp, currsz);
522                 if (error != 0)
523                         break;
524                 totsz += currsz;
525         }
526         HIM_LOCK(him);
527         hviommu_map_insq(him, *mapp);
528         HIM_UNLOCK(him);
529         return (0);
530 }
531
532 static int
533 hviommu_dvmamap_destroy(bus_dma_tag_t dt, bus_dmamap_t map)
534 {
535         struct hviommu *him = dt->dt_cookie;
536
537         hviommu_dvma_vfree(him, map);
538         sparc64_dma_free_map(dt, map);
539         return (0);
540 }
541
542 #define IOTTE_CNT       64
543
544 static void
545 hviommu_map_pages(struct hviommu *him, bus_addr_t dvmaddr, uint64_t *iottes, pages_t iottecnt)
546 {
547         uint64_t err;
548 #ifdef IOMMU_DEBUG
549         bus_addr_t ra;
550         io_attributes_t ioattr;
551 #endif
552         pages_t mapcnt;
553         int cntdone;
554         int i;
555
556         DPRINTF("mapping: dh: %#lx, dvmaddr: %#lx, tsbid: %#lx, cnt: %d\n",
557             him->him_handle, dvmaddr, VA_TO_TSBID(him, dvmaddr), iottecnt);
558         for (i = 0; i < iottecnt; i++) {
559                 DPRINTF("iotte:%#lx\n", iottes[i]);
560         }
561
562         /* push tte's */
563         cntdone = 0;
564         while (cntdone < iottecnt) {
565                 if ((err = hv_pci_iommu_map(him->him_handle, VA_TO_TSBID(him,
566                     dvmaddr), iottecnt, PCI_MAP_ATTR_READ | PCI_MAP_ATTR_WRITE,
567                     (io_page_list_t)pmap_kextract((vm_offset_t)&iottes[0]),
568                     &mapcnt))) {
569                         DPRINTF("iommu_map: err: %ld\n", err);
570                         mapcnt = 1;
571                 }
572                 cntdone += mapcnt;
573         }
574         for (i = 0; i < iottecnt; i++) {
575                 DPRINTF("err: %ld", hv_pci_iommu_getmap(him->him_handle,
576                     VA_TO_TSBID(him, dvmaddr + i * IO_PAGE_SIZE),
577                                                      &ioattr, &ra));
578                 DPRINTF(", ioattr: %d, raddr: %#lx\n", ioattr, ra);
579         }
580 }
581
582 /*
583  * IOMMU DVMA operations, common to SBUS and PCI.
584  */
585 static int
586 hviommu_dvmamap_load_buffer(bus_dma_tag_t dt, struct hviommu *him,
587     bus_dmamap_t map, void *buf, bus_size_t buflen, struct thread *td,
588     int flags, bus_dma_segment_t *segs, int *segp, int align)
589 {
590         uint64_t iottes[IOTTE_CNT];
591         bus_addr_t amask, dvmaddr, iottebase;
592         bus_size_t sgsize, esize;
593         vm_offset_t vaddr, voffs;
594         vm_paddr_t curaddr;
595         int error, sgcnt, firstpg;
596         pmap_t pmap = NULL;
597         pages_t iottecnt;
598
599         KASSERT(buflen != 0, ("hviommu_dvmamap_load_buffer: buflen == 0!"));
600         if (buflen > dt->dt_maxsize)
601                 return (EINVAL);
602
603         if (td != NULL)
604                 pmap = vmspace_pmap(td->td_proc->p_vmspace);
605
606         vaddr = (vm_offset_t)buf;
607         voffs = vaddr & IO_PAGE_MASK;
608         amask = align ? dt->dt_alignment - 1 : 0;
609
610         /* Try to find a slab that is large enough. */
611         error = hviommu_dvma_vallocseg(dt, him, map, voffs, buflen, amask,
612             &dvmaddr);
613         if (error != 0)
614                 return (error);
615
616         DPRINTF("vallocseg: dvmaddr: %#lx, voffs: %#lx, buflen: %#lx\n",
617             dvmaddr, voffs, buflen);
618         sgcnt = *segp;
619         firstpg = 1;
620         iottecnt = 0;
621         iottebase = 0;  /* shutup gcc */
622         for (; buflen > 0; ) {
623                 /*
624                  * Get the physical address for this page.
625                  */
626                 if (pmap != NULL)
627                         curaddr = pmap_extract(pmap, vaddr);
628                 else
629                         curaddr = pmap_kextract(vaddr);
630
631                 /*
632                  * Compute the segment size, and adjust counts.
633                  */
634                 sgsize = IO_PAGE_SIZE - ((u_long)vaddr & IO_PAGE_MASK);
635                 if (buflen < sgsize)
636                         sgsize = buflen;
637
638                 buflen -= sgsize;
639                 vaddr += sgsize;
640
641 #if 0
642                 hviommu_enter(him, trunc_io_page(dvmaddr), trunc_io_page(curaddr),
643                     flags);
644 #else
645                 if (iottecnt == 0)
646                         iottebase = trunc_io_page(dvmaddr);
647                 DPRINTF("adding: %#lx\n", trunc_io_page(curaddr));
648                 iottes[iottecnt++] = trunc_io_page(curaddr);
649
650                 if (iottecnt >= IOTTE_CNT) {
651                         hviommu_map_pages(him, iottebase, iottes, iottecnt);
652                         iottecnt = 0;
653                 }
654 #endif
655
656                 /*
657                  * Chop the chunk up into segments of at most maxsegsz, but try
658                  * to fill each segment as well as possible.
659                  */
660                 if (!firstpg) {
661                         esize = ulmin(sgsize,
662                             dt->dt_maxsegsz - segs[sgcnt].ds_len);
663                         segs[sgcnt].ds_len += esize;
664                         sgsize -= esize;
665                         dvmaddr += esize;
666                 }
667                 while (sgsize > 0) {
668                         sgcnt++;
669                         if (sgcnt >= dt->dt_nsegments)
670                                 return (EFBIG);
671                         /*
672                          * No extra alignment here - the common practice in the
673                          * busdma code seems to be that only the first segment
674                          * needs to satisfy the alignment constraints (and that
675                          * only for bus_dmamem_alloc()ed maps). It is assumed
676                          * that such tags have maxsegsize >= maxsize.
677                          */
678                         esize = ulmin(sgsize, dt->dt_maxsegsz);
679                         segs[sgcnt].ds_addr = dvmaddr;
680                         segs[sgcnt].ds_len = esize;
681                         sgsize -= esize;
682                         dvmaddr += esize;
683                 }
684
685                 firstpg = 0;
686         }
687         hviommu_map_pages(him, iottebase, iottes, iottecnt);
688         *segp = sgcnt;
689         return (0);
690 }
691
692 static int
693 hviommu_dvmamap_load(bus_dma_tag_t dt, bus_dmamap_t map, void *buf,
694     bus_size_t buflen, bus_dmamap_callback_t *cb, void *cba,
695     int flags)
696 {
697         struct hviommu *him = dt->dt_cookie;
698         int error, seg = -1;
699
700         if ((map->dm_flags & DMF_LOADED) != 0) {
701 #ifdef DIAGNOSTIC
702                 printf("hviommu_dvmamap_load: map still in use\n");
703 #endif
704                 bus_dmamap_unload(dt, map);
705         }
706
707         /*
708          * Make sure that the map is not on a queue so that the resource list
709          * may be safely accessed and modified without needing the lock to
710          * cover the whole operation.
711          */
712         HIM_LOCK(him);
713         hviommu_map_remq(him, map);
714         HIM_UNLOCK(him);
715
716         error = hviommu_dvmamap_load_buffer(dt, him, map, buf, buflen, NULL,
717             flags, dt->dt_segments, &seg, 1);
718
719         HIM_LOCK(him);
720         hviommu_map_insq(him, map);
721         if (error != 0) {
722                 hviommu_dvmamap_vunload(him, map);
723                 HIM_UNLOCK(him);
724                 (*cb)(cba, dt->dt_segments, 0, error);
725         } else {
726                 HIM_UNLOCK(him);
727                 map->dm_flags |= DMF_LOADED;
728                 (*cb)(cba, dt->dt_segments, seg + 1, 0);
729         }
730
731         return (error);
732 }
733
734 static int
735 hviommu_dvmamap_load_mbuf(bus_dma_tag_t dt, bus_dmamap_t map, struct mbuf *m0,
736     bus_dmamap_callback2_t *cb, void *cba, int flags)
737 {
738         struct hviommu *him = dt->dt_cookie;
739         struct mbuf *m;
740         int error = 0, first = 1, nsegs = -1;
741
742         M_ASSERTPKTHDR(m0);
743
744         if ((map->dm_flags & DMF_LOADED) != 0) {
745 #ifdef DIAGNOSTIC
746                 printf("hviommu_dvmamap_load_mbuf: map still in use\n");
747 #endif
748                 bus_dmamap_unload(dt, map);
749         }
750
751         HIM_LOCK(him);
752         hviommu_map_remq(him, map);
753         HIM_UNLOCK(him);
754
755         if (m0->m_pkthdr.len <= dt->dt_maxsize) {
756                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
757                         if (m->m_len == 0)
758                                 continue;
759                         error = hviommu_dvmamap_load_buffer(dt, him, map,
760                             m->m_data, m->m_len, NULL, flags, dt->dt_segments,
761                             &nsegs, first);
762                         first = 0;
763                 }
764         } else
765                 error = EINVAL;
766
767         HIM_LOCK(him);
768         hviommu_map_insq(him, map);
769         if (error != 0) {
770                 hviommu_dvmamap_vunload(him, map);
771                 HIM_UNLOCK(him);
772                 /* force "no valid mappings" in callback */
773                 (*cb)(cba, dt->dt_segments, 0, 0, error);
774         } else {
775                 HIM_UNLOCK(him);
776                 map->dm_flags |= DMF_LOADED;
777                 (*cb)(cba, dt->dt_segments, nsegs + 1, m0->m_pkthdr.len, 0);
778         }
779         return (error);
780 }
781
782 static int
783 hviommu_dvmamap_load_mbuf_sg(bus_dma_tag_t dt, bus_dmamap_t map,
784     struct mbuf *m0, bus_dma_segment_t *segs, int *nsegs, int flags)
785 {
786         struct hviommu *him = dt->dt_cookie;
787         struct mbuf *m;
788         int error = 0, first = 1;
789
790         M_ASSERTPKTHDR(m0);
791
792         *nsegs = -1;
793         if ((map->dm_flags & DMF_LOADED) != 0) {
794 #ifdef DIAGNOSTIC
795                 printf("hviommu_dvmamap_load_mbuf: map still in use\n");
796 #endif
797                 bus_dmamap_unload(dt, map);
798         }
799
800         HIM_LOCK(him);
801         hviommu_map_remq(him, map);
802         HIM_UNLOCK(him);
803
804         if (m0->m_pkthdr.len <= dt->dt_maxsize) {
805                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
806                         if (m->m_len == 0)
807                                 continue;
808                         error = hviommu_dvmamap_load_buffer(dt, him, map,
809                             m->m_data, m->m_len, NULL, flags, segs,
810                             nsegs, first);
811                         first = 0;
812                 }
813         } else
814                 error = EINVAL;
815
816         HIM_LOCK(him);
817         hviommu_map_insq(him, map);
818         if (error != 0) {
819                 hviommu_dvmamap_vunload(him, map);
820         } else {
821                 map->dm_flags |= DMF_LOADED;
822                 ++*nsegs;
823         }
824         HIM_UNLOCK(him);
825         return (error);
826 }
827
828 static int
829 hviommu_dvmamap_load_uio(bus_dma_tag_t dt, bus_dmamap_t map, struct uio *uio,
830     bus_dmamap_callback2_t *cb,  void *cba, int flags)
831 {
832         struct hviommu *him = dt->dt_cookie;
833         struct iovec *iov;
834         struct thread *td = NULL;
835         bus_size_t minlen, resid;
836         int nsegs = -1, error = 0, first = 1, i;
837
838         if ((map->dm_flags & DMF_LOADED) != 0) {
839 #ifdef DIAGNOSTIC
840                 printf("hviommu_dvmamap_load_uio: map still in use\n");
841 #endif
842                 bus_dmamap_unload(dt, map);
843         }
844
845         HIM_LOCK(him);
846         hviommu_map_remq(him, map);
847         HIM_UNLOCK(him);
848
849         resid = uio->uio_resid;
850         iov = uio->uio_iov;
851
852         if (uio->uio_segflg == UIO_USERSPACE) {
853                 td = uio->uio_td;
854                 KASSERT(td != NULL,
855                     ("%s: USERSPACE but no proc", __func__));
856         }
857
858         for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) {
859                 /*
860                  * Now at the first iovec to load.  Load each iovec
861                  * until we have exhausted the residual count.
862                  */
863                 minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len;
864                 if (minlen == 0)
865                         continue;
866
867                 error = hviommu_dvmamap_load_buffer(dt, him, map,
868                     iov[i].iov_base, minlen, td, flags, dt->dt_segments, 
869                     &nsegs, first);
870                 first = 0;
871
872                 resid -= minlen;
873         }
874
875         HIM_LOCK(him);
876         hviommu_map_insq(him, map);
877         if (error) {
878                 hviommu_dvmamap_vunload(him, map);
879                 HIM_UNLOCK(him);
880                 /* force "no valid mappings" in callback */
881                 (*cb)(cba, dt->dt_segments, 0, 0, error);
882         } else {
883                 HIM_UNLOCK(him);
884                 map->dm_flags |= DMF_LOADED;
885                 (*cb)(cba, dt->dt_segments, nsegs + 1, uio->uio_resid, 0);
886         }
887         return (error);
888 }
889
890 static void
891 hviommu_dvmamap_unload(bus_dma_tag_t dt, bus_dmamap_t map)
892 {
893         struct hviommu *him = dt->dt_cookie;
894
895         if ((map->dm_flags & DMF_LOADED) == 0)
896                 return;
897         HIM_LOCK(him);
898         hviommu_dvmamap_vunload(him, map);
899         hviommu_map_insq(him, map);
900         HIM_UNLOCK(him);
901         map->dm_flags &= ~DMF_LOADED;
902 }
903
904 static void
905 hviommu_dvmamap_sync(bus_dma_tag_t dt, bus_dmamap_t map, bus_dmasync_op_t op)
906 {
907         struct hviommu *him = dt->dt_cookie;
908         struct bus_dmamap_res *r;
909         vm_offset_t va;
910         vm_size_t len;
911         size_t synced;
912         bus_addr_t ra;
913         uint64_t err;
914         io_attributes_t ioattr;
915         vm_paddr_t raddr;
916         io_sync_direction_t iodir;
917
918         if ((map->dm_flags & DMF_LOADED) == 0)
919                 return;
920
921         iodir = 0;
922
923         if (op & (BUS_DMASYNC_POSTREAD))
924                 iodir |= IO_SYNC_CPU;
925         if (op & (BUS_DMASYNC_PREWRITE))
926                 iodir |= IO_SYNC_DEVICE;
927
928         if ((op & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_POSTWRITE)) != 0)
929                 membar(Sync);
930
931         /* nothing to be done */
932         if (!iodir) 
933                 return;
934
935         HIM_LOCK(him);
936         SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
937                 va = (vm_offset_t)BDR_START(r) + r->dr_offset ;
938                 len = r->dr_used;
939                 while (len > 0) {
940                         if ((err = hv_pci_iommu_getmap(him->him_handle,
941                             VA_TO_TSBID(him, va), &ioattr, &ra))) {
942                                 if (err != H_ENOMAP)
943                                         printf("failed to _g=etmap: err: %ld, handle: %#lx, tsbid: %#lx\n", 
944                                                err, him->him_handle, VA_TO_TSBID(him, va));
945                                 continue;
946                         }
947                         if ((err = hv_pci_dma_sync(him->him_handle, ra,
948                             ulmin(len, (trunc_io_page(ra) + IO_PAGE_SIZE) - ra),
949                             iodir, &synced))) {
950                                 printf("failed to dma_sync: err: %ld, handle: %#lx, ra: %#lx, len: %#lx, dir: %d\n",
951                                     err, him->him_handle, ra, ulmin(len,
952                                     (trunc_io_page(ra) + IO_PAGE_SIZE) - ra),
953                                     iodir);
954                                 synced = ulmin(len, (trunc_io_page(ra) + IO_PAGE_SIZE) - ra);
955                                 printf("err: %ld", hv_pci_iommu_getmap(him->him_handle, VA_TO_TSBID(him, va),
956                                                                      &ioattr, &raddr));
957                                 printf(", ioattr: %d, raddr: %#lx\n", ioattr, raddr);
958                         }
959                         va += synced;
960                         len -= synced;
961                 }
962         }
963         HIM_UNLOCK(him);
964
965         if ((op & BUS_DMASYNC_PREWRITE) != 0)
966                 membar(Sync);
967 }
968
969 struct bus_dma_methods hviommu_dma_methods = {
970         .dm_dmamap_create = hviommu_dvmamap_create,
971         .dm_dmamap_destroy = hviommu_dvmamap_destroy,
972         .dm_dmamap_load = hviommu_dvmamap_load,
973         .dm_dmamap_load_mbuf = hviommu_dvmamap_load_mbuf,
974         .dm_dmamap_load_mbuf_sg = hviommu_dvmamap_load_mbuf_sg,
975         .dm_dmamap_load_uio = hviommu_dvmamap_load_uio,
976         .dm_dmamap_unload = hviommu_dvmamap_unload,
977         .dm_dmamap_sync = hviommu_dvmamap_sync,
978         .dm_dmamem_alloc = hviommu_dvmamem_alloc,
979         .dm_dmamem_free = hviommu_dvmamem_free,
980 };