]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/sun4v/sun4v/hviommu.c
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.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         for (i = 1; i < maxpre && totsz < IOMMU_MAX_PRE; i++) {
517                 currsz = round_io_page(ulmin(presz, IOMMU_MAX_PRE - totsz));
518                 error = hviommu_dvma_valloc(dt, him, *mapp, currsz);
519                 if (error != 0)
520                         break;
521                 totsz += currsz;
522         }
523         HIM_LOCK(him);
524         hviommu_map_insq(him, *mapp);
525         HIM_UNLOCK(him);
526         return (0);
527 }
528
529 static int
530 hviommu_dvmamap_destroy(bus_dma_tag_t dt, bus_dmamap_t map)
531 {
532         struct hviommu *him = dt->dt_cookie;
533
534         hviommu_dvma_vfree(him, map);
535         sparc64_dma_free_map(dt, map);
536         return (0);
537 }
538
539 #define IOTTE_CNT       64
540
541 static void
542 hviommu_map_pages(struct hviommu *him, bus_addr_t dvmaddr, uint64_t *iottes, pages_t iottecnt)
543 {
544         uint64_t err;
545 #ifdef IOMMU_DEBUG
546         bus_addr_t ra;
547         io_attributes_t ioattr;
548 #endif
549         pages_t mapcnt;
550         int cntdone;
551         int i;
552
553         DPRINTF("mapping: dh: %#lx, dvmaddr: %#lx, tsbid: %#lx, cnt: %d\n",
554             him->him_handle, dvmaddr, VA_TO_TSBID(him, dvmaddr), iottecnt);
555         for (i = 0; i < iottecnt; i++) {
556                 DPRINTF("iotte:%#lx\n", iottes[i]);
557         }
558
559         /* push tte's */
560         cntdone = 0;
561         while (cntdone < iottecnt) {
562                 if ((err = hv_pci_iommu_map(him->him_handle, VA_TO_TSBID(him,
563                     dvmaddr), iottecnt, PCI_MAP_ATTR_READ | PCI_MAP_ATTR_WRITE,
564                     (io_page_list_t)pmap_kextract((vm_offset_t)&iottes[0]),
565                     &mapcnt))) {
566                         DPRINTF("iommu_map: err: %ld\n", err);
567                         mapcnt = 1;
568                 }
569                 cntdone += mapcnt;
570         }
571         for (i = 0; i < iottecnt; i++) {
572                 DPRINTF("err: %ld", hv_pci_iommu_getmap(him->him_handle,
573                     VA_TO_TSBID(him, dvmaddr + i * IO_PAGE_SIZE),
574                                                      &ioattr, &ra));
575                 DPRINTF(", ioattr: %d, raddr: %#lx\n", ioattr, ra);
576         }
577 }
578
579 /*
580  * IOMMU DVMA operations, common to SBUS and PCI.
581  */
582 static int
583 hviommu_dvmamap_load_buffer(bus_dma_tag_t dt, struct hviommu *him,
584     bus_dmamap_t map, void *buf, bus_size_t buflen, struct thread *td,
585     int flags, bus_dma_segment_t *segs, int *segp, int align)
586 {
587         uint64_t iottes[IOTTE_CNT];
588         bus_addr_t amask, dvmaddr, iottebase;
589         bus_size_t sgsize, esize;
590         vm_offset_t vaddr, voffs;
591         vm_paddr_t curaddr;
592         int error, sgcnt, firstpg;
593         pmap_t pmap = NULL;
594         pages_t iottecnt;
595
596         KASSERT(buflen != 0, ("hviommu_dvmamap_load_buffer: buflen == 0!"));
597         if (buflen > dt->dt_maxsize)
598                 return (EINVAL);
599
600         if (td != NULL)
601                 pmap = vmspace_pmap(td->td_proc->p_vmspace);
602
603         vaddr = (vm_offset_t)buf;
604         voffs = vaddr & IO_PAGE_MASK;
605         amask = align ? dt->dt_alignment - 1 : 0;
606
607         /* Try to find a slab that is large enough. */
608         error = hviommu_dvma_vallocseg(dt, him, map, voffs, buflen, amask,
609             &dvmaddr);
610         if (error != 0)
611                 return (error);
612
613         DPRINTF("vallocseg: dvmaddr: %#lx, voffs: %#lx, buflen: %#lx\n",
614             dvmaddr, voffs, buflen);
615         sgcnt = *segp;
616         firstpg = 1;
617         iottecnt = 0;
618         iottebase = 0;  /* shutup gcc */
619         for (; buflen > 0; ) {
620                 /*
621                  * Get the physical address for this page.
622                  */
623                 if (pmap != NULL)
624                         curaddr = pmap_extract(pmap, vaddr);
625                 else
626                         curaddr = pmap_kextract(vaddr);
627
628                 /*
629                  * Compute the segment size, and adjust counts.
630                  */
631                 sgsize = IO_PAGE_SIZE - ((u_long)vaddr & IO_PAGE_MASK);
632                 if (buflen < sgsize)
633                         sgsize = buflen;
634
635                 buflen -= sgsize;
636                 vaddr += sgsize;
637
638 #if 0
639                 hviommu_enter(him, trunc_io_page(dvmaddr), trunc_io_page(curaddr),
640                     flags);
641 #else
642                 if (iottecnt == 0)
643                         iottebase = trunc_io_page(dvmaddr);
644                 DPRINTF("adding: %#lx\n", trunc_io_page(curaddr));
645                 iottes[iottecnt++] = trunc_io_page(curaddr);
646
647                 if (iottecnt >= IOTTE_CNT) {
648                         hviommu_map_pages(him, iottebase, iottes, iottecnt);
649                         iottecnt = 0;
650                 }
651 #endif
652
653                 /*
654                  * Chop the chunk up into segments of at most maxsegsz, but try
655                  * to fill each segment as well as possible.
656                  */
657                 if (!firstpg) {
658                         esize = ulmin(sgsize,
659                             dt->dt_maxsegsz - segs[sgcnt].ds_len);
660                         segs[sgcnt].ds_len += esize;
661                         sgsize -= esize;
662                         dvmaddr += esize;
663                 }
664                 while (sgsize > 0) {
665                         sgcnt++;
666                         if (sgcnt >= dt->dt_nsegments)
667                                 return (EFBIG);
668                         /*
669                          * No extra alignment here - the common practice in the
670                          * busdma code seems to be that only the first segment
671                          * needs to satisfy the alignment constraints (and that
672                          * only for bus_dmamem_alloc()ed maps). It is assumed
673                          * that such tags have maxsegsize >= maxsize.
674                          */
675                         esize = ulmin(sgsize, dt->dt_maxsegsz);
676                         segs[sgcnt].ds_addr = dvmaddr;
677                         segs[sgcnt].ds_len = esize;
678                         sgsize -= esize;
679                         dvmaddr += esize;
680                 }
681
682                 firstpg = 0;
683         }
684         hviommu_map_pages(him, iottebase, iottes, iottecnt);
685         *segp = sgcnt;
686         return (0);
687 }
688
689 static int
690 hviommu_dvmamap_load(bus_dma_tag_t dt, bus_dmamap_t map, void *buf,
691     bus_size_t buflen, bus_dmamap_callback_t *cb, void *cba,
692     int flags)
693 {
694         struct hviommu *him = dt->dt_cookie;
695         int error, seg = -1;
696
697         if ((map->dm_flags & DMF_LOADED) != 0) {
698 #ifdef DIAGNOSTIC
699                 printf("hviommu_dvmamap_load: map still in use\n");
700 #endif
701                 bus_dmamap_unload(dt, map);
702         }
703
704         /*
705          * Make sure that the map is not on a queue so that the resource list
706          * may be safely accessed and modified without needing the lock to
707          * cover the whole operation.
708          */
709         HIM_LOCK(him);
710         hviommu_map_remq(him, map);
711         HIM_UNLOCK(him);
712
713         error = hviommu_dvmamap_load_buffer(dt, him, map, buf, buflen, NULL,
714             flags, dt->dt_segments, &seg, 1);
715
716         HIM_LOCK(him);
717         hviommu_map_insq(him, map);
718         if (error != 0) {
719                 hviommu_dvmamap_vunload(him, map);
720                 HIM_UNLOCK(him);
721                 (*cb)(cba, dt->dt_segments, 0, error);
722         } else {
723                 HIM_UNLOCK(him);
724                 map->dm_flags |= DMF_LOADED;
725                 (*cb)(cba, dt->dt_segments, seg + 1, 0);
726         }
727
728         return (error);
729 }
730
731 static int
732 hviommu_dvmamap_load_mbuf(bus_dma_tag_t dt, bus_dmamap_t map, struct mbuf *m0,
733     bus_dmamap_callback2_t *cb, void *cba, int flags)
734 {
735         struct hviommu *him = dt->dt_cookie;
736         struct mbuf *m;
737         int error = 0, first = 1, nsegs = -1;
738
739         M_ASSERTPKTHDR(m0);
740
741         if ((map->dm_flags & DMF_LOADED) != 0) {
742 #ifdef DIAGNOSTIC
743                 printf("hviommu_dvmamap_load_mbuf: map still in use\n");
744 #endif
745                 bus_dmamap_unload(dt, map);
746         }
747
748         HIM_LOCK(him);
749         hviommu_map_remq(him, map);
750         HIM_UNLOCK(him);
751
752         if (m0->m_pkthdr.len <= dt->dt_maxsize) {
753                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
754                         if (m->m_len == 0)
755                                 continue;
756                         error = hviommu_dvmamap_load_buffer(dt, him, map,
757                             m->m_data, m->m_len, NULL, flags, dt->dt_segments,
758                             &nsegs, first);
759                         first = 0;
760                 }
761         } else
762                 error = EINVAL;
763
764         HIM_LOCK(him);
765         hviommu_map_insq(him, map);
766         if (error != 0) {
767                 hviommu_dvmamap_vunload(him, map);
768                 HIM_UNLOCK(him);
769                 /* force "no valid mappings" in callback */
770                 (*cb)(cba, dt->dt_segments, 0, 0, error);
771         } else {
772                 HIM_UNLOCK(him);
773                 map->dm_flags |= DMF_LOADED;
774                 (*cb)(cba, dt->dt_segments, nsegs + 1, m0->m_pkthdr.len, 0);
775         }
776         return (error);
777 }
778
779 static int
780 hviommu_dvmamap_load_mbuf_sg(bus_dma_tag_t dt, bus_dmamap_t map,
781     struct mbuf *m0, bus_dma_segment_t *segs, int *nsegs, int flags)
782 {
783         struct hviommu *him = dt->dt_cookie;
784         struct mbuf *m;
785         int error = 0, first = 1;
786
787         M_ASSERTPKTHDR(m0);
788
789         *nsegs = -1;
790         if ((map->dm_flags & DMF_LOADED) != 0) {
791 #ifdef DIAGNOSTIC
792                 printf("hviommu_dvmamap_load_mbuf: map still in use\n");
793 #endif
794                 bus_dmamap_unload(dt, map);
795         }
796
797         HIM_LOCK(him);
798         hviommu_map_remq(him, map);
799         HIM_UNLOCK(him);
800
801         if (m0->m_pkthdr.len <= dt->dt_maxsize) {
802                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
803                         if (m->m_len == 0)
804                                 continue;
805                         error = hviommu_dvmamap_load_buffer(dt, him, map,
806                             m->m_data, m->m_len, NULL, flags, segs,
807                             nsegs, first);
808                         first = 0;
809                 }
810         } else
811                 error = EINVAL;
812
813         HIM_LOCK(him);
814         hviommu_map_insq(him, map);
815         if (error != 0) {
816                 hviommu_dvmamap_vunload(him, map);
817         } else {
818                 map->dm_flags |= DMF_LOADED;
819                 ++*nsegs;
820         }
821         HIM_UNLOCK(him);
822         return (error);
823 }
824
825 static int
826 hviommu_dvmamap_load_uio(bus_dma_tag_t dt, bus_dmamap_t map, struct uio *uio,
827     bus_dmamap_callback2_t *cb,  void *cba, int flags)
828 {
829         struct hviommu *him = dt->dt_cookie;
830         struct iovec *iov;
831         struct thread *td = NULL;
832         bus_size_t minlen, resid;
833         int nsegs = -1, error = 0, first = 1, i;
834
835         if ((map->dm_flags & DMF_LOADED) != 0) {
836 #ifdef DIAGNOSTIC
837                 printf("hviommu_dvmamap_load_uio: map still in use\n");
838 #endif
839                 bus_dmamap_unload(dt, map);
840         }
841
842         HIM_LOCK(him);
843         hviommu_map_remq(him, map);
844         HIM_UNLOCK(him);
845
846         resid = uio->uio_resid;
847         iov = uio->uio_iov;
848
849         if (uio->uio_segflg == UIO_USERSPACE) {
850                 td = uio->uio_td;
851                 KASSERT(td != NULL,
852                     ("%s: USERSPACE but no proc", __func__));
853         }
854
855         for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) {
856                 /*
857                  * Now at the first iovec to load.  Load each iovec
858                  * until we have exhausted the residual count.
859                  */
860                 minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len;
861                 if (minlen == 0)
862                         continue;
863
864                 error = hviommu_dvmamap_load_buffer(dt, him, map,
865                     iov[i].iov_base, minlen, td, flags, dt->dt_segments, 
866                     &nsegs, first);
867                 first = 0;
868
869                 resid -= minlen;
870         }
871
872         HIM_LOCK(him);
873         hviommu_map_insq(him, map);
874         if (error) {
875                 hviommu_dvmamap_vunload(him, map);
876                 HIM_UNLOCK(him);
877                 /* force "no valid mappings" in callback */
878                 (*cb)(cba, dt->dt_segments, 0, 0, error);
879         } else {
880                 HIM_UNLOCK(him);
881                 map->dm_flags |= DMF_LOADED;
882                 (*cb)(cba, dt->dt_segments, nsegs + 1, uio->uio_resid, 0);
883         }
884         return (error);
885 }
886
887 static void
888 hviommu_dvmamap_unload(bus_dma_tag_t dt, bus_dmamap_t map)
889 {
890         struct hviommu *him = dt->dt_cookie;
891
892         if ((map->dm_flags & DMF_LOADED) == 0)
893                 return;
894         HIM_LOCK(him);
895         hviommu_dvmamap_vunload(him, map);
896         hviommu_map_insq(him, map);
897         HIM_UNLOCK(him);
898         map->dm_flags &= ~DMF_LOADED;
899 }
900
901 static void
902 hviommu_dvmamap_sync(bus_dma_tag_t dt, bus_dmamap_t map, bus_dmasync_op_t op)
903 {
904         struct hviommu *him = dt->dt_cookie;
905         struct bus_dmamap_res *r;
906         vm_offset_t va;
907         vm_size_t len;
908         size_t synced;
909         bus_addr_t ra;
910         uint64_t err;
911         io_attributes_t ioattr;
912         vm_paddr_t raddr;
913         io_sync_direction_t iodir;
914
915         if ((map->dm_flags & DMF_LOADED) == 0)
916                 return;
917
918         iodir = 0;
919
920         if (op & (BUS_DMASYNC_POSTREAD))
921                 iodir |= IO_SYNC_CPU;
922         if (op & (BUS_DMASYNC_PREWRITE))
923                 iodir |= IO_SYNC_DEVICE;
924
925         if ((op & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_POSTWRITE)) != 0)
926                 membar(Sync);
927
928         /* nothing to be done */
929         if (!iodir) 
930                 return;
931
932         HIM_LOCK(him);
933         SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
934                 va = (vm_offset_t)BDR_START(r) + r->dr_offset ;
935                 len = r->dr_used;
936                 while (len > 0) {
937                         if ((err = hv_pci_iommu_getmap(him->him_handle,
938                             VA_TO_TSBID(him, va), &ioattr, &ra))) {
939                                 if (err != H_ENOMAP)
940                                         printf("failed to _g=etmap: err: %ld, handle: %#lx, tsbid: %#lx\n", 
941                                                err, him->him_handle, VA_TO_TSBID(him, va));
942                                 continue;
943                         }
944                         if ((err = hv_pci_dma_sync(him->him_handle, ra,
945                             ulmin(len, (trunc_io_page(ra) + IO_PAGE_SIZE) - ra),
946                             iodir, &synced))) {
947                                 printf("failed to dma_sync: err: %ld, handle: %#lx, ra: %#lx, len: %#lx, dir: %d\n",
948                                     err, him->him_handle, ra, ulmin(len,
949                                     (trunc_io_page(ra) + IO_PAGE_SIZE) - ra),
950                                     iodir);
951                                 synced = ulmin(len, (trunc_io_page(ra) + IO_PAGE_SIZE) - ra);
952                                 printf("err: %ld", hv_pci_iommu_getmap(him->him_handle, VA_TO_TSBID(him, va),
953                                                                      &ioattr, &raddr));
954                                 printf(", ioattr: %d, raddr: %#lx\n", ioattr, raddr);
955                         }
956                         va += synced;
957                         len -= synced;
958                 }
959         }
960         HIM_UNLOCK(him);
961
962         if ((op & BUS_DMASYNC_PREWRITE) != 0)
963                 membar(Sync);
964 }
965
966 struct bus_dma_methods hviommu_dma_methods = {
967         .dm_dmamap_create = hviommu_dvmamap_create,
968         .dm_dmamap_destroy = hviommu_dvmamap_destroy,
969         .dm_dmamap_load = hviommu_dvmamap_load,
970         .dm_dmamap_load_mbuf = hviommu_dvmamap_load_mbuf,
971         .dm_dmamap_load_mbuf_sg = hviommu_dvmamap_load_mbuf_sg,
972         .dm_dmamap_load_uio = hviommu_dvmamap_load_uio,
973         .dm_dmamap_unload = hviommu_dvmamap_unload,
974         .dm_dmamap_sync = hviommu_dvmamap_sync,
975         .dm_dmamem_alloc = hviommu_dvmamem_alloc,
976         .dm_dmamem_free = hviommu_dvmamem_free,
977 };