]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iommu/iommu_gas.c
Remove unneeded cast to struct iommu_domain *.
[FreeBSD/FreeBSD.git] / sys / dev / iommu / iommu_gas.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
8  * under sponsorship from the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #define RB_AUGMENT(entry) iommu_gas_augment_entry(entry)
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/bus.h>
41 #include <sys/interrupt.h>
42 #include <sys/kernel.h>
43 #include <sys/ktr.h>
44 #include <sys/lock.h>
45 #include <sys/proc.h>
46 #include <sys/rwlock.h>
47 #include <sys/memdesc.h>
48 #include <sys/mutex.h>
49 #include <sys/sysctl.h>
50 #include <sys/rman.h>
51 #include <sys/taskqueue.h>
52 #include <sys/tree.h>
53 #include <sys/uio.h>
54 #include <sys/vmem.h>
55 #include <vm/vm.h>
56 #include <vm/vm_extern.h>
57 #include <vm/vm_kern.h>
58 #include <vm/vm_object.h>
59 #include <vm/vm_page.h>
60 #include <vm/vm_map.h>
61 #include <vm/uma.h>
62 #include <dev/pci/pcireg.h>
63 #include <dev/pci/pcivar.h>
64 #include <dev/iommu/iommu.h>
65 #include <machine/atomic.h>
66 #include <machine/bus.h>
67 #include <machine/md_var.h>
68 #if defined(__amd64__) || defined(__i386__)
69 #include <x86/iommu/intel_reg.h>
70 #endif
71 #include <dev/iommu/busdma_iommu.h>
72
73 /*
74  * Guest Address Space management.
75  */
76
77 static uma_zone_t iommu_map_entry_zone;
78
79 #ifdef INVARIANTS
80 static int iommu_check_free;
81 #endif
82
83 static void
84 intel_gas_init(void)
85 {
86
87         iommu_map_entry_zone = uma_zcreate("IOMMU_MAP_ENTRY",
88             sizeof(struct iommu_map_entry), NULL, NULL,
89             NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NODUMP);
90 }
91 SYSINIT(intel_gas, SI_SUB_DRIVERS, SI_ORDER_FIRST, intel_gas_init, NULL);
92
93 struct iommu_map_entry *
94 iommu_gas_alloc_entry(struct iommu_domain *domain, u_int flags)
95 {
96         struct iommu_map_entry *res;
97
98         KASSERT((flags & ~(IOMMU_PGF_WAITOK)) == 0,
99             ("unsupported flags %x", flags));
100
101         res = uma_zalloc(iommu_map_entry_zone, ((flags & IOMMU_PGF_WAITOK) !=
102             0 ? M_WAITOK : M_NOWAIT) | M_ZERO);
103         if (res != NULL) {
104                 res->domain = domain;
105                 atomic_add_int(&domain->entries_cnt, 1);
106         }
107         return (res);
108 }
109
110 void
111 iommu_gas_free_entry(struct iommu_domain *domain, struct iommu_map_entry *entry)
112 {
113
114         KASSERT(domain == entry->domain,
115             ("mismatched free domain %p entry %p entry->domain %p", domain,
116             entry, entry->domain));
117         atomic_subtract_int(&domain->entries_cnt, 1);
118         uma_zfree(iommu_map_entry_zone, entry);
119 }
120
121 static int
122 iommu_gas_cmp_entries(struct iommu_map_entry *a, struct iommu_map_entry *b)
123 {
124
125         /* Last entry have zero size, so <= */
126         KASSERT(a->start <= a->end, ("inverted entry %p (%jx, %jx)",
127             a, (uintmax_t)a->start, (uintmax_t)a->end));
128         KASSERT(b->start <= b->end, ("inverted entry %p (%jx, %jx)",
129             b, (uintmax_t)b->start, (uintmax_t)b->end));
130         KASSERT(a->end <= b->start || b->end <= a->start ||
131             a->end == a->start || b->end == b->start,
132             ("overlapping entries %p (%jx, %jx) %p (%jx, %jx)",
133             a, (uintmax_t)a->start, (uintmax_t)a->end,
134             b, (uintmax_t)b->start, (uintmax_t)b->end));
135
136         if (a->end < b->end)
137                 return (-1);
138         else if (b->end < a->end)
139                 return (1);
140         return (0);
141 }
142
143 static void
144 iommu_gas_augment_entry(struct iommu_map_entry *entry)
145 {
146         struct iommu_map_entry *child;
147         iommu_gaddr_t free_down;
148
149         free_down = 0;
150         if ((child = RB_LEFT(entry, rb_entry)) != NULL) {
151                 free_down = MAX(free_down, child->free_down);
152                 free_down = MAX(free_down, entry->start - child->last);
153                 entry->first = child->first;
154         } else
155                 entry->first = entry->start;
156         
157         if ((child = RB_RIGHT(entry, rb_entry)) != NULL) {
158                 free_down = MAX(free_down, child->free_down);
159                 free_down = MAX(free_down, child->first - entry->end);
160                 entry->last = child->last;
161         } else
162                 entry->last = entry->end;
163         entry->free_down = free_down;
164 }
165
166 RB_GENERATE(iommu_gas_entries_tree, iommu_map_entry, rb_entry,
167     iommu_gas_cmp_entries);
168
169 #ifdef INVARIANTS
170 static void
171 iommu_gas_check_free(struct iommu_domain *domain)
172 {
173         struct iommu_map_entry *entry, *l, *r;
174         iommu_gaddr_t v;
175
176         RB_FOREACH(entry, iommu_gas_entries_tree, &domain->rb_root) {
177                 KASSERT(domain == entry->domain,
178                     ("mismatched free domain %p entry %p entry->domain %p",
179                     domain, entry, entry->domain));
180                 l = RB_LEFT(entry, rb_entry);
181                 r = RB_RIGHT(entry, rb_entry);
182                 v = 0;
183                 if (l != NULL) {
184                         v = MAX(v, l->free_down);
185                         v = MAX(v, entry->start - l->last);
186                 }
187                 if (r != NULL) {
188                         v = MAX(v, r->free_down);
189                         v = MAX(v, r->first - entry->end);
190                 }
191                 MPASS(entry->free_down == v);
192         }
193 }
194 #endif
195
196 static bool
197 iommu_gas_rb_insert(struct iommu_domain *domain, struct iommu_map_entry *entry)
198 {
199         struct iommu_map_entry *found;
200
201         found = RB_INSERT(iommu_gas_entries_tree,
202             &domain->rb_root, entry);
203         return (found == NULL);
204 }
205
206 static void
207 iommu_gas_rb_remove(struct iommu_domain *domain, struct iommu_map_entry *entry)
208 {
209
210         RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry);
211 }
212
213 void
214 iommu_gas_init_domain(struct iommu_domain *domain)
215 {
216         struct iommu_map_entry *begin, *end;
217
218         begin = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK);
219         end = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK);
220
221         IOMMU_DOMAIN_LOCK(domain);
222         KASSERT(domain->entries_cnt == 2, ("dirty domain %p", domain));
223         KASSERT(RB_EMPTY(&domain->rb_root),
224             ("non-empty entries %p", domain));
225
226         begin->start = 0;
227         begin->end = IOMMU_PAGE_SIZE;
228         begin->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED;
229         iommu_gas_rb_insert(domain, begin);
230
231         end->start = domain->end;
232         end->end = domain->end;
233         end->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED;
234         iommu_gas_rb_insert(domain, end);
235
236         domain->first_place = begin;
237         domain->last_place = end;
238         domain->flags |= IOMMU_DOMAIN_GAS_INITED;
239         IOMMU_DOMAIN_UNLOCK(domain);
240 }
241
242 void
243 iommu_gas_fini_domain(struct iommu_domain *domain)
244 {
245         struct iommu_map_entry *entry, *entry1;
246
247         IOMMU_DOMAIN_ASSERT_LOCKED(domain);
248         KASSERT(domain->entries_cnt == 2,
249             ("domain still in use %p", domain));
250
251         entry = RB_MIN(iommu_gas_entries_tree, &domain->rb_root);
252         KASSERT(entry->start == 0, ("start entry start %p", domain));
253         KASSERT(entry->end == IOMMU_PAGE_SIZE, ("start entry end %p", domain));
254         KASSERT(entry->flags == IOMMU_MAP_ENTRY_PLACE,
255             ("start entry flags %p", domain));
256         RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry);
257         iommu_gas_free_entry(domain, entry);
258
259         entry = RB_MAX(iommu_gas_entries_tree, &domain->rb_root);
260         KASSERT(entry->start == domain->end, ("end entry start %p", domain));
261         KASSERT(entry->end == domain->end, ("end entry end %p", domain));
262         KASSERT(entry->flags == IOMMU_MAP_ENTRY_PLACE,
263             ("end entry flags %p", domain));
264         RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root, entry);
265         iommu_gas_free_entry(domain, entry);
266
267         RB_FOREACH_SAFE(entry, iommu_gas_entries_tree, &domain->rb_root,
268             entry1) {
269                 KASSERT((entry->flags & IOMMU_MAP_ENTRY_RMRR) != 0,
270                     ("non-RMRR entry left %p", domain));
271                 RB_REMOVE(iommu_gas_entries_tree, &domain->rb_root,
272                     entry);
273                 iommu_gas_free_entry(domain, entry);
274         }
275 }
276
277 struct iommu_gas_match_args {
278         struct iommu_domain *domain;
279         iommu_gaddr_t size;
280         int offset;
281         const struct bus_dma_tag_common *common;
282         u_int gas_flags;
283         struct iommu_map_entry *entry;
284 };
285
286 /*
287  * The interval [beg, end) is a free interval between two iommu_map_entries.
288  * maxaddr is an upper bound on addresses that can be allocated. Try to
289  * allocate space in the free interval, subject to the conditions expressed
290  * by a, and return 'true' if and only if the allocation attempt succeeds.
291  */
292 static bool
293 iommu_gas_match_one(struct iommu_gas_match_args *a, iommu_gaddr_t beg,
294     iommu_gaddr_t end, iommu_gaddr_t maxaddr)
295 {
296         iommu_gaddr_t bs, start;
297
298         a->entry->start = roundup2(beg + IOMMU_PAGE_SIZE,
299             a->common->alignment);
300         if (a->entry->start + a->size > maxaddr)
301                 return (false);
302
303         /* IOMMU_PAGE_SIZE to create gap after new entry. */
304         if (a->entry->start < beg + IOMMU_PAGE_SIZE ||
305             a->entry->start + a->size + a->offset + IOMMU_PAGE_SIZE > end)
306                 return (false);
307
308         /* No boundary crossing. */
309         if (iommu_test_boundary(a->entry->start + a->offset, a->size,
310             a->common->boundary))
311                 return (true);
312
313         /*
314          * The start + offset to start + offset + size region crosses
315          * the boundary.  Check if there is enough space after the
316          * next boundary after the beg.
317          */
318         bs = rounddown2(a->entry->start + a->offset + a->common->boundary,
319             a->common->boundary);
320         start = roundup2(bs, a->common->alignment);
321         /* IOMMU_PAGE_SIZE to create gap after new entry. */
322         if (start + a->offset + a->size + IOMMU_PAGE_SIZE <= end &&
323             start + a->offset + a->size <= maxaddr &&
324             iommu_test_boundary(start + a->offset, a->size,
325             a->common->boundary)) {
326                 a->entry->start = start;
327                 return (true);
328         }
329
330         /*
331          * Not enough space to align at the requested boundary, or
332          * boundary is smaller than the size, but allowed to split.
333          * We already checked that start + size does not overlap maxaddr.
334          *
335          * XXXKIB. It is possible that bs is exactly at the start of
336          * the next entry, then we do not have gap.  Ignore for now.
337          */
338         if ((a->gas_flags & IOMMU_MF_CANSPLIT) != 0) {
339                 a->size = bs - a->entry->start;
340                 return (true);
341         }
342
343         return (false);
344 }
345
346 static void
347 iommu_gas_match_insert(struct iommu_gas_match_args *a)
348 {
349         bool found;
350
351         /*
352          * The prev->end is always aligned on the page size, which
353          * causes page alignment for the entry->start too.  The size
354          * is checked to be multiple of the page size.
355          *
356          * The page sized gap is created between consequent
357          * allocations to ensure that out-of-bounds accesses fault.
358          */
359         a->entry->end = a->entry->start + a->size;
360
361         found = iommu_gas_rb_insert(a->domain, a->entry);
362         KASSERT(found, ("found dup %p start %jx size %jx",
363             a->domain, (uintmax_t)a->entry->start, (uintmax_t)a->size));
364         a->entry->flags = IOMMU_MAP_ENTRY_MAP;
365 }
366
367 static int
368 iommu_gas_lowermatch(struct iommu_gas_match_args *a, struct iommu_map_entry *entry)
369 {
370         struct iommu_map_entry *child;
371
372         child = RB_RIGHT(entry, rb_entry);
373         if (child != NULL && entry->end < a->common->lowaddr &&
374             iommu_gas_match_one(a, entry->end, child->first,
375             a->common->lowaddr)) {
376                 iommu_gas_match_insert(a);
377                 return (0);
378         }
379         if (entry->free_down < a->size + a->offset + IOMMU_PAGE_SIZE)
380                 return (ENOMEM);
381         if (entry->first >= a->common->lowaddr)
382                 return (ENOMEM);
383         child = RB_LEFT(entry, rb_entry);
384         if (child != NULL && 0 == iommu_gas_lowermatch(a, child))
385                 return (0);
386         if (child != NULL && child->last < a->common->lowaddr &&
387             iommu_gas_match_one(a, child->last, entry->start,
388             a->common->lowaddr)) {
389                 iommu_gas_match_insert(a);
390                 return (0);
391         }
392         child = RB_RIGHT(entry, rb_entry);
393         if (child != NULL && 0 == iommu_gas_lowermatch(a, child))
394                 return (0);
395         return (ENOMEM);
396 }
397
398 static int
399 iommu_gas_uppermatch(struct iommu_gas_match_args *a, struct iommu_map_entry *entry)
400 {
401         struct iommu_map_entry *child;
402
403         if (entry->free_down < a->size + a->offset + IOMMU_PAGE_SIZE)
404                 return (ENOMEM);
405         if (entry->last < a->common->highaddr)
406                 return (ENOMEM);
407         child = RB_LEFT(entry, rb_entry);
408         if (child != NULL && 0 == iommu_gas_uppermatch(a, child))
409                 return (0);
410         if (child != NULL && child->last >= a->common->highaddr &&
411             iommu_gas_match_one(a, child->last, entry->start,
412             a->domain->end)) {
413                 iommu_gas_match_insert(a);
414                 return (0);
415         }
416         child = RB_RIGHT(entry, rb_entry);
417         if (child != NULL && entry->end >= a->common->highaddr &&
418             iommu_gas_match_one(a, entry->end, child->first,
419             a->domain->end)) {
420                 iommu_gas_match_insert(a);
421                 return (0);
422         }
423         if (child != NULL && 0 == iommu_gas_uppermatch(a, child))
424                 return (0);
425         return (ENOMEM);
426 }
427
428 static int
429 iommu_gas_find_space(struct iommu_domain *domain,
430     const struct bus_dma_tag_common *common, iommu_gaddr_t size,
431     int offset, u_int flags, struct iommu_map_entry *entry)
432 {
433         struct iommu_gas_match_args a;
434         int error;
435
436         IOMMU_DOMAIN_ASSERT_LOCKED(domain);
437         KASSERT(entry->flags == 0, ("dirty entry %p %p", domain, entry));
438         KASSERT((size & IOMMU_PAGE_MASK) == 0, ("size %jx", (uintmax_t)size));
439
440         a.domain = domain;
441         a.size = size;
442         a.offset = offset;
443         a.common = common;
444         a.gas_flags = flags;
445         a.entry = entry;
446
447         /* Handle lower region. */
448         if (common->lowaddr > 0) {
449                 error = iommu_gas_lowermatch(&a,
450                     RB_ROOT(&domain->rb_root));
451                 if (error == 0)
452                         return (0);
453                 KASSERT(error == ENOMEM,
454                     ("error %d from iommu_gas_lowermatch", error));
455         }
456         /* Handle upper region. */
457         if (common->highaddr >= domain->end)
458                 return (ENOMEM);
459         error = iommu_gas_uppermatch(&a, RB_ROOT(&domain->rb_root));
460         KASSERT(error == ENOMEM,
461             ("error %d from iommu_gas_uppermatch", error));
462         return (error);
463 }
464
465 static int
466 iommu_gas_alloc_region(struct iommu_domain *domain, struct iommu_map_entry *entry,
467     u_int flags)
468 {
469         struct iommu_map_entry *next, *prev;
470         bool found;
471
472         IOMMU_DOMAIN_ASSERT_LOCKED(domain);
473
474         if ((entry->start & IOMMU_PAGE_MASK) != 0 ||
475             (entry->end & IOMMU_PAGE_MASK) != 0)
476                 return (EINVAL);
477         if (entry->start >= entry->end)
478                 return (EINVAL);
479         if (entry->end >= domain->end)
480                 return (EINVAL);
481
482         next = RB_NFIND(iommu_gas_entries_tree, &domain->rb_root, entry);
483         KASSERT(next != NULL, ("next must be non-null %p %jx", domain,
484             (uintmax_t)entry->start));
485         prev = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, next);
486         /* prev could be NULL */
487
488         /*
489          * Adapt to broken BIOSes which specify overlapping RMRR
490          * entries.
491          *
492          * XXXKIB: this does not handle a case when prev or next
493          * entries are completely covered by the current one, which
494          * extends both ways.
495          */
496         if (prev != NULL && prev->end > entry->start &&
497             (prev->flags & IOMMU_MAP_ENTRY_PLACE) == 0) {
498                 if ((flags & IOMMU_MF_RMRR) == 0 ||
499                     (prev->flags & IOMMU_MAP_ENTRY_RMRR) == 0)
500                         return (EBUSY);
501                 entry->start = prev->end;
502         }
503         if (next->start < entry->end &&
504             (next->flags & IOMMU_MAP_ENTRY_PLACE) == 0) {
505                 if ((flags & IOMMU_MF_RMRR) == 0 ||
506                     (next->flags & IOMMU_MAP_ENTRY_RMRR) == 0)
507                         return (EBUSY);
508                 entry->end = next->start;
509         }
510         if (entry->end == entry->start)
511                 return (0);
512
513         if (prev != NULL && prev->end > entry->start) {
514                 /* This assumes that prev is the placeholder entry. */
515                 iommu_gas_rb_remove(domain, prev);
516                 prev = NULL;
517         }
518         if (next->start < entry->end) {
519                 iommu_gas_rb_remove(domain, next);
520                 next = NULL;
521         }
522
523         found = iommu_gas_rb_insert(domain, entry);
524         KASSERT(found, ("found RMRR dup %p start %jx end %jx",
525             domain, (uintmax_t)entry->start, (uintmax_t)entry->end));
526         if ((flags & IOMMU_MF_RMRR) != 0)
527                 entry->flags = IOMMU_MAP_ENTRY_RMRR;
528
529 #ifdef INVARIANTS
530         struct iommu_map_entry *ip, *in;
531         ip = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, entry);
532         in = RB_NEXT(iommu_gas_entries_tree, &domain->rb_root, entry);
533         KASSERT(prev == NULL || ip == prev,
534             ("RMRR %p (%jx %jx) prev %p (%jx %jx) ins prev %p (%jx %jx)",
535             entry, entry->start, entry->end, prev,
536             prev == NULL ? 0 : prev->start, prev == NULL ? 0 : prev->end,
537             ip, ip == NULL ? 0 : ip->start, ip == NULL ? 0 : ip->end));
538         KASSERT(next == NULL || in == next,
539             ("RMRR %p (%jx %jx) next %p (%jx %jx) ins next %p (%jx %jx)",
540             entry, entry->start, entry->end, next,
541             next == NULL ? 0 : next->start, next == NULL ? 0 : next->end,
542             in, in == NULL ? 0 : in->start, in == NULL ? 0 : in->end));
543 #endif
544
545         return (0);
546 }
547
548 void
549 iommu_gas_free_space(struct iommu_domain *domain, struct iommu_map_entry *entry)
550 {
551
552         IOMMU_DOMAIN_ASSERT_LOCKED(domain);
553         KASSERT((entry->flags & (IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_RMRR |
554             IOMMU_MAP_ENTRY_MAP)) == IOMMU_MAP_ENTRY_MAP,
555             ("permanent entry %p %p", domain, entry));
556
557         iommu_gas_rb_remove(domain, entry);
558         entry->flags &= ~IOMMU_MAP_ENTRY_MAP;
559 #ifdef INVARIANTS
560         if (iommu_check_free)
561                 iommu_gas_check_free(domain);
562 #endif
563 }
564
565 void
566 iommu_gas_free_region(struct iommu_domain *domain, struct iommu_map_entry *entry)
567 {
568         struct iommu_map_entry *next, *prev;
569
570         IOMMU_DOMAIN_ASSERT_LOCKED(domain);
571         KASSERT((entry->flags & (IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_RMRR |
572             IOMMU_MAP_ENTRY_MAP)) == IOMMU_MAP_ENTRY_RMRR,
573             ("non-RMRR entry %p %p", domain, entry));
574
575         prev = RB_PREV(iommu_gas_entries_tree, &domain->rb_root, entry);
576         next = RB_NEXT(iommu_gas_entries_tree, &domain->rb_root, entry);
577         iommu_gas_rb_remove(domain, entry);
578         entry->flags &= ~IOMMU_MAP_ENTRY_RMRR;
579
580         if (prev == NULL)
581                 iommu_gas_rb_insert(domain, domain->first_place);
582         if (next == NULL)
583                 iommu_gas_rb_insert(domain, domain->last_place);
584 }
585
586 int
587 iommu_gas_map(struct iommu_domain *domain,
588     const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset,
589     u_int eflags, u_int flags, vm_page_t *ma, struct iommu_map_entry **res)
590 {
591         struct iommu_map_entry *entry;
592         int error;
593
594         KASSERT((flags & ~(IOMMU_MF_CANWAIT | IOMMU_MF_CANSPLIT)) == 0,
595             ("invalid flags 0x%x", flags));
596
597         entry = iommu_gas_alloc_entry(domain,
598             (flags & IOMMU_MF_CANWAIT) != 0 ?  IOMMU_PGF_WAITOK : 0);
599         if (entry == NULL)
600                 return (ENOMEM);
601         IOMMU_DOMAIN_LOCK(domain);
602         error = iommu_gas_find_space(domain, common, size, offset, flags,
603             entry);
604         if (error == ENOMEM) {
605                 IOMMU_DOMAIN_UNLOCK(domain);
606                 iommu_gas_free_entry(domain, entry);
607                 return (error);
608         }
609 #ifdef INVARIANTS
610         if (iommu_check_free)
611                 iommu_gas_check_free(domain);
612 #endif
613         KASSERT(error == 0,
614             ("unexpected error %d from iommu_gas_find_entry", error));
615         KASSERT(entry->end < domain->end, ("allocated GPA %jx, max GPA %jx",
616             (uintmax_t)entry->end, (uintmax_t)domain->end));
617         entry->flags |= eflags;
618         IOMMU_DOMAIN_UNLOCK(domain);
619
620         error = domain->ops->map(domain, entry->start,
621             entry->end - entry->start, ma, eflags,
622             ((flags & IOMMU_MF_CANWAIT) != 0 ?  IOMMU_PGF_WAITOK : 0));
623         if (error == ENOMEM) {
624                 iommu_domain_unload_entry(entry, true);
625                 return (error);
626         }
627         KASSERT(error == 0,
628             ("unexpected error %d from domain_map_buf", error));
629
630         *res = entry;
631         return (0);
632 }
633
634 int
635 iommu_gas_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry,
636     u_int eflags, u_int flags, vm_page_t *ma)
637 {
638         iommu_gaddr_t start;
639         int error;
640
641         KASSERT(entry->flags == 0, ("used RMRR entry %p %p %x", domain,
642             entry, entry->flags));
643         KASSERT((flags & ~(IOMMU_MF_CANWAIT | IOMMU_MF_RMRR)) == 0,
644             ("invalid flags 0x%x", flags));
645
646         start = entry->start;
647         IOMMU_DOMAIN_LOCK(domain);
648         error = iommu_gas_alloc_region(domain, entry, flags);
649         if (error != 0) {
650                 IOMMU_DOMAIN_UNLOCK(domain);
651                 return (error);
652         }
653         entry->flags |= eflags;
654         IOMMU_DOMAIN_UNLOCK(domain);
655         if (entry->end == entry->start)
656                 return (0);
657
658         error = domain->ops->map(domain, entry->start,
659             entry->end - entry->start, ma + OFF_TO_IDX(start - entry->start),
660             eflags, ((flags & IOMMU_MF_CANWAIT) != 0 ? IOMMU_PGF_WAITOK : 0));
661         if (error == ENOMEM) {
662                 iommu_domain_unload_entry(entry, false);
663                 return (error);
664         }
665         KASSERT(error == 0,
666             ("unexpected error %d from domain_map_buf", error));
667
668         return (0);
669 }
670
671 int
672 iommu_gas_reserve_region(struct iommu_domain *domain, iommu_gaddr_t start,
673     iommu_gaddr_t end)
674 {
675         struct iommu_map_entry *entry;
676         int error;
677
678         entry = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK);
679         entry->start = start;
680         entry->end = end;
681         IOMMU_DOMAIN_LOCK(domain);
682         error = iommu_gas_alloc_region(domain, entry, IOMMU_MF_CANWAIT);
683         if (error == 0)
684                 entry->flags |= IOMMU_MAP_ENTRY_UNMAPPED;
685         IOMMU_DOMAIN_UNLOCK(domain);
686         if (error != 0)
687                 iommu_gas_free_entry(domain, entry);
688         return (error);
689 }
690
691 struct iommu_map_entry *
692 iommu_map_alloc_entry(struct iommu_domain *domain, u_int flags)
693 {
694         struct iommu_map_entry *res;
695
696         res = iommu_gas_alloc_entry(domain, flags);
697
698         return (res);
699 }
700
701 void
702 iommu_map_free_entry(struct iommu_domain *domain, struct iommu_map_entry *entry)
703 {
704
705         iommu_gas_free_entry(domain, entry);
706 }
707
708 int
709 iommu_map(struct iommu_domain *domain,
710     const struct bus_dma_tag_common *common, iommu_gaddr_t size, int offset,
711     u_int eflags, u_int flags, vm_page_t *ma, struct iommu_map_entry **res)
712 {
713         int error;
714
715         error = iommu_gas_map(domain, common, size, offset, eflags, flags,
716             ma, res);
717
718         return (error);
719 }
720
721 int
722 iommu_map_region(struct iommu_domain *domain, struct iommu_map_entry *entry,
723     u_int eflags, u_int flags, vm_page_t *ma)
724 {
725         int error;
726
727         error = iommu_gas_map_region(domain, entry, eflags, flags, ma);
728
729         return (error);
730 }
731
732 SYSCTL_NODE(_hw, OID_AUTO, iommu, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, "");
733
734 #ifdef INVARIANTS
735 SYSCTL_INT(_hw_iommu, OID_AUTO, check_free, CTLFLAG_RWTUN,
736     &iommu_check_free, 0,
737     "Check the GPA RBtree for free_down and free_after validity");
738 #endif