1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <sys/types.h>
30 #include <sys/cmn_err.h>
31 #include <sys/mman.h>
32 #include <sys/sunddi.h>
33 #include <sys/tnf_probe.h>
34 #include <vm/hat_sfmmu.h>
35 #include <vm/as.h>
36 #include <vm/xhat.h>
37 #include <vm/xhat_sfmmu.h>
38 #include <sys/zulu_hat.h>
39 #include <sys/zulumod.h>
40
41 /*
42 * This file contains the implementation of zulu_hat: an XHAT provider
43 * to support the MMU for the XVR-4000 graphics accelerator (code name zulu).
44 *
45 * The zulu hat is linked into the kernel misc module zuluvm.
46 * zuluvm provides services that the zulu device driver module requires
47 * that are not part of the standard ddi. See PSARC 2002/231.
48 *
49 * The zulu driver is delivered by the graphics consolidation.
50 * zuluvm is in ON workspace.
51 *
52 * There are two types of interfaces provided by zulu_hat
53 * 1. The set of functions and data structures used by zuluvm to obtain
54 * tte entries for the zulu MMU and to manage the association between
55 * user process's address spaces and zulu graphics contexts.
56 *
57 * 2. The entry points required for an XHAT provider: zulu_hat_ops
58 */
59
60 /*
61 * zulu_ctx_tab contains an array of pointers to the zulu_hats.
62 *
63 * During zulu graphics context switch, the zulu MMU's current context register
64 * is set to the index of the process's zulu hat's location in the array
65 * zulu_ctx_tab.
66 *
67 * This allows the TL=1 TLB miss handler to quickly find the zulu hat and
68 * lookup a tte in the zulu hat's TSB.
69 *
70 * To synchronize with the trap handler we use bit zero of
71 * the pointer as a lock bit. See the function zulu_ctx_tsb_lock_enter().
72 *
73 * If the trap handler finds the ctx locked it doesn't wait, it
74 * posts a soft interrupt which is handled at TL=0.
75 */
76
77 #define ZULU_HAT_MAX_CTX 32
78 struct zulu_hat *zulu_ctx_tab[ZULU_HAT_MAX_CTX];
79
80 /*
81 * To avoid searching through the whole zulu_ctx_tab for a free slot,
82 * we maintain the value of zulu_ctx_search_start.
83 *
84 * This value is a guess as to where a free slot in the context table might be.
85 * All slots < zulu_ctx_search_start are definitely occupied.
86 */
87 static int zulu_ctx_search_start = 0;
88
89
90 /*
91 * this mutex protects the zulu_ctx_tab and zulu_ctx_search_start
92 */
93 static kmutex_t zulu_ctx_lock;
94
95
96 uint64_t zulu_tsb_hit = 0; /* assembly code increments this */
97 static uint64_t zulu_tsb_miss = 0;
98 static uint64_t zulu_as_fault = 0;
99
100 /*
101 * The zulu device has two zulu data mmus.
102 * We use the base pagesize for one of them and the and 4M for the other.
103 */
104 extern int zuluvm_base_pgsize;
105
106
107
108 /*
109 * call zuluvm to remove translations for a page
110 */
111 static void
zulu_hat_demap_page(struct zulu_hat * zhat,caddr_t vaddr,int size)112 zulu_hat_demap_page(struct zulu_hat *zhat, caddr_t vaddr, int size)
113 {
114 if (zhat->zulu_ctx < 0) {
115 /* context has been stolen, so page is already demapped */
116 return;
117 }
118 zuluvm_demap_page(zhat->zdev, NULL, zhat->zulu_ctx, vaddr, size);
119 }
120
121 static void
zulu_hat_demap_ctx(void * zdev,int zulu_ctx)122 zulu_hat_demap_ctx(void *zdev, int zulu_ctx)
123 {
124 if (zulu_ctx < 0) {
125 /* context has been stolen */
126 return;
127 }
128 zuluvm_demap_ctx(zdev, zulu_ctx);
129 }
130
131
132 /*
133 * steal the least recently used context slot.
134 */
135 static int
zulu_hat_steal_ctx()136 zulu_hat_steal_ctx()
137 {
138 int ctx;
139 hrtime_t delta = INT64_MAX;
140 struct zulu_hat *zhat_oldest = NULL;
141
142 ASSERT(mutex_owned(&zulu_ctx_lock));
143
144 for (ctx = 0; ctx < ZULU_HAT_MAX_CTX; ctx++) {
145 struct zulu_hat *zhat = ZULU_CTX_GET_HAT(ctx);
146
147 /*
148 * we shouldn't be here unless all slots are occupied
149 */
150 ASSERT(zhat != NULL);
151
152 TNF_PROBE_3(steal_ctx_loop, "zulu_hat", /* CSTYLED */,
153 tnf_int, ctx, ctx,
154 tnf_long, last_used, zhat->last_used,
155 tnf_long, oldest, delta);
156
157 if (zhat->last_used < delta) {
158 zhat_oldest = zhat;
159 delta = zhat->last_used;
160 }
161 }
162
163 ASSERT(zhat_oldest != NULL);
164
165 mutex_enter(&zhat_oldest->lock);
166
167 /* Nobody should have the tsb lock bit set here */
168 ASSERT(((uint64_t)zulu_ctx_tab[zhat_oldest->zulu_ctx] & ZULU_CTX_LOCK)
169 == 0);
170
171 ctx = zhat_oldest->zulu_ctx;
172 zhat_oldest->zulu_ctx = -1;
173
174 ZULU_CTX_SET_HAT(ctx, NULL);
175
176 zulu_hat_demap_ctx(zhat_oldest->zdev, ctx);
177
178 mutex_exit(&zhat_oldest->lock);
179
180 TNF_PROBE_1(zulu_hat_steal_ctx, "zulu_hat", /* CSTYLED */,
181 tnf_int, ctx, ctx);
182
183 return (ctx);
184 }
185
186 /*
187 * find a slot in the context table for a zulu_hat
188 */
189 static void
zulu_hat_ctx_alloc(struct zulu_hat * zhat)190 zulu_hat_ctx_alloc(struct zulu_hat *zhat)
191 {
192 int ctx;
193
194 mutex_enter(&zulu_ctx_lock);
195
196 for (ctx = zulu_ctx_search_start; ctx < ZULU_HAT_MAX_CTX; ctx++) {
197 if (ZULU_CTX_IS_FREE(ctx)) {
198 zulu_ctx_search_start = ctx + 1;
199 break;
200 }
201 }
202
203 if (ctx == ZULU_HAT_MAX_CTX) {
204 /* table is full need to steal an entry */
205 zulu_ctx_search_start = ZULU_HAT_MAX_CTX;
206 ctx = zulu_hat_steal_ctx();
207 }
208
209 mutex_enter(&zhat->lock);
210
211 ZULU_CTX_SET_HAT(ctx, zhat);
212 zhat->zulu_ctx = ctx;
213
214 mutex_exit(&zhat->lock);
215
216 mutex_exit(&zulu_ctx_lock);
217
218 TNF_PROBE_2(zulu_hat_ctx_alloc, "zulu_hat", /* CSTYLED */,
219 tnf_opaque, zhat, zhat, tnf_int, ctx, ctx);
220 }
221
222 /*
223 * zulu_hat_validate_ctx: Called before the graphics context associated
224 * with a given zulu hat becomes the current zulu graphics context.
225 * Make sure that the hat has a slot in zulu_ctx_tab.
226 */
227 void
zulu_hat_validate_ctx(struct zulu_hat * zhat)228 zulu_hat_validate_ctx(struct zulu_hat *zhat)
229 {
230 if (zhat->zulu_ctx < 0) {
231 zulu_hat_ctx_alloc(zhat);
232 }
233 zhat->last_used = gethrtime();
234 }
235
236
237 static void
zulu_hat_ctx_free(struct zulu_hat * zhat)238 zulu_hat_ctx_free(struct zulu_hat *zhat)
239 {
240 TNF_PROBE_1(zulu_hat_ctx_free, "zulu_hat", /* CSTYLED */,
241 tnf_int, ctx, zhat->zulu_ctx);
242
243 mutex_enter(&zulu_ctx_lock);
244
245 mutex_enter(&zhat->lock);
246 if (zhat->zulu_ctx >= 0) {
247 ZULU_CTX_SET_HAT(zhat->zulu_ctx, NULL);
248
249 if (zulu_ctx_search_start > zhat->zulu_ctx) {
250 zulu_ctx_search_start = zhat->zulu_ctx;
251 }
252 }
253 mutex_exit(&zhat->lock);
254 mutex_exit(&zulu_ctx_lock);
255 }
256
257 /*
258 * Lock the zulu tsb for a given zulu_hat.
259 *
260 * We're just protecting against the TLB trap handler here. Other operations
261 * on the zulu_hat require entering the zhat's lock.
262 */
263 static void
zulu_ctx_tsb_lock_enter(struct zulu_hat * zhat)264 zulu_ctx_tsb_lock_enter(struct zulu_hat *zhat)
265 {
266 uint64_t lck;
267 uint64_t *plck;
268
269 ASSERT(mutex_owned(&zhat->lock));
270
271 if (zhat->zulu_ctx < 0) {
272 return;
273 }
274 plck = (uint64_t *)&zulu_ctx_tab[zhat->zulu_ctx];
275
276 for (; ; ) {
277 lck = *plck;
278 if (!(lck & ZULU_CTX_LOCK)) {
279 uint64_t old_lck, new_lck;
280
281 new_lck = lck | ZULU_CTX_LOCK;
282
283 old_lck = cas64(plck, lck, new_lck);
284
285 if (old_lck == lck) {
286 /*
287 * success
288 */
289 break;
290 }
291 }
292 }
293 }
294
295 static void
zulu_ctx_tsb_lock_exit(struct zulu_hat * zhat)296 zulu_ctx_tsb_lock_exit(struct zulu_hat *zhat)
297 {
298 uint64_t lck;
299 int zulu_ctx = zhat->zulu_ctx;
300
301 if (zulu_ctx < 0) {
302 return;
303 }
304 lck = (uint64_t)zulu_ctx_tab[zulu_ctx];
305 ASSERT(lck & ZULU_CTX_LOCK);
306 lck &= ~ZULU_CTX_LOCK;
307 zulu_ctx_tab[zulu_ctx] = (struct zulu_hat *)lck;
308 }
309
310 /*
311 * Each zulu hat has a "shadow tree" which is a table of 4MB address regions
312 * for which the zhat has mappings.
313 *
314 * This table is maintained in an avl tree.
315 * Nodes in the tree are called shadow blocks (or sblks)
316 *
317 * This data structure allows unload operations by (address, range) to be
318 * much more efficent.
319 *
320 * We get called a lot for address ranges that have never been supplied
321 * to zulu.
322 */
323
324 /*
325 * compare the base address of two nodes in the shadow tree
326 */
327 static int
zulu_shadow_tree_compare(const void * a,const void * b)328 zulu_shadow_tree_compare(const void *a, const void *b)
329 {
330 struct zulu_shadow_blk *zba = (struct zulu_shadow_blk *)a;
331 struct zulu_shadow_blk *zbb = (struct zulu_shadow_blk *)b;
332 uint64_t addr_a = zba->ivaddr;
333 uint64_t addr_b = zbb->ivaddr;
334
335 TNF_PROBE_2(zulu_shadow_tree_compare, "zulu_shadow_tree", /* CSTYLED */,
336 tnf_opaque, addr_a, addr_a, tnf_opaque, addr_b, addr_b);
337
338 if (addr_a < addr_b) {
339 return (-1);
340 } else if (addr_a > addr_b) {
341 return (1);
342 } else {
343 return (0);
344 }
345 }
346
347 /*
348 * lookup the entry in the shadow tree for a given virtual address
349 */
350 static struct zulu_shadow_blk *
zulu_shadow_tree_lookup(struct zulu_hat * zhat,uint64_t ivaddr,avl_index_t * where)351 zulu_shadow_tree_lookup(struct zulu_hat *zhat, uint64_t ivaddr,
352 avl_index_t *where)
353 {
354 struct zulu_shadow_blk proto;
355 struct zulu_shadow_blk *sblk;
356
357 proto.ivaddr = ivaddr & ZULU_SHADOW_BLK_MASK;
358
359 /*
360 * pages typically fault in in order so we cache the last shadow
361 * block that was referenced so we usually get to reduce calls to
362 * avl_find.
363 */
364 if ((zhat->sblk_last != NULL) &&
365 (proto.ivaddr == zhat->sblk_last->ivaddr)) {
366 sblk = zhat->sblk_last;
367 } else {
368 sblk = (struct zulu_shadow_blk *)avl_find(&zhat->shadow_tree,
369 &proto, where);
370 zhat->sblk_last = sblk;
371 }
372
373 TNF_PROBE_2(zulu_shadow_tree_lookup, "zulu_shadow_tree", /* CSTYLED */,
374 tnf_opaque, ivaddr, proto.ivaddr,
375 tnf_opaque, where, where ? *where : ~0);
376
377 return (sblk);
378 }
379
380 /*
381 * insert a sblk into the shadow tree for a given zblk.
382 * If a sblk already exists, just increment it's refcount.
383 */
384 static void
zulu_shadow_tree_insert(struct zulu_hat * zhat,struct zulu_hat_blk * zblk)385 zulu_shadow_tree_insert(struct zulu_hat *zhat, struct zulu_hat_blk *zblk)
386 {
387 avl_index_t where;
388 struct zulu_shadow_blk *sblk = NULL;
389 uint64_t ivaddr;
390 uint64_t end;
391
392 ivaddr = zblk->zulu_hat_blk_vaddr & ZULU_SHADOW_BLK_MASK;
393
394 end = zblk->zulu_hat_blk_vaddr + ZULU_HAT_PGSZ(zblk->zulu_hat_blk_size);
395
396 sblk = zulu_shadow_tree_lookup(zhat, ivaddr, &where);
397 if (sblk != NULL) {
398 sblk->ref_count++;
399
400 end = zblk->zulu_hat_blk_vaddr +
401 ZULU_HAT_PGSZ(zblk->zulu_hat_blk_size);
402 if (zblk->zulu_hat_blk_vaddr < sblk->min_addr) {
403 sblk->min_addr = zblk->zulu_hat_blk_vaddr;
404 }
405 /*
406 * a blk can set both the minimum and maximum when it
407 * is the first zblk added to a previously emptied sblk
408 */
409 if (end > sblk->max_addr) {
410 sblk->max_addr = end;
411 }
412 } else {
413 sblk = kmem_zalloc(sizeof (*sblk), KM_SLEEP);
414 sblk->ref_count = 1;
415 sblk->ivaddr = ivaddr;
416 sblk->min_addr = zblk->zulu_hat_blk_vaddr;
417 sblk->max_addr = end;
418 zhat->sblk_last = sblk;
419
420 avl_insert(&zhat->shadow_tree, sblk, where);
421 }
422 zblk->zulu_shadow_blk = sblk;
423 TNF_PROBE_2(zulu_shadow_tree_insert, "zulu_shadow_tree", /* CSTYLED */,
424 tnf_opaque, vaddr, ivaddr,
425 tnf_opaque, ref_count, sblk->ref_count);
426 }
427
428 /*
429 * decrement the ref_count for the sblk that corresponds to a given zblk.
430 * When the ref_count goes to zero remove the sblk from the tree and free it.
431 */
432
433 static void
zulu_shadow_tree_delete(struct zulu_hat * zhat,struct zulu_hat_blk * zblk)434 zulu_shadow_tree_delete(struct zulu_hat *zhat, struct zulu_hat_blk *zblk)
435 {
436 struct zulu_shadow_blk *sblk;
437
438 ASSERT(zblk->zulu_shadow_blk != NULL);
439
440 sblk = zblk->zulu_shadow_blk;
441
442 TNF_PROBE_2(zulu_shadow_tree_delete, "zulu_shadow_tree", /* CSTYLED */,
443 tnf_opaque, vaddr, sblk->ivaddr,
444 tnf_opaque, ref_count, sblk->ref_count-1);
445
446 if (--sblk->ref_count == 0) {
447 if (zhat->sblk_last == sblk) {
448 zhat->sblk_last = NULL;
449 }
450 sblk->min_addr = sblk->ivaddr + ZULU_SHADOW_BLK_RANGE;
451 sblk->max_addr = sblk->ivaddr;
452 } else {
453 /*
454 * Update the high and low water marks for this sblk.
455 * These are estimates, because we don't know if the previous
456 * or next region are actually occupied, but we can tell
457 * whether the previous values have become invalid.
458 *
459 * In the most often applied case a segment is being
460 * unloaded, and the min_addr will be kept up to date as
461 * the zblks are deleted in order.
462 */
463 uint64_t end = zblk->zulu_hat_blk_vaddr +
464 ZULU_HAT_PGSZ(zblk->zulu_hat_blk_size);
465
466 if (zblk->zulu_hat_blk_vaddr == sblk->min_addr) {
467 sblk->min_addr = end;
468 }
469 if (end == sblk->max_addr) {
470 sblk->max_addr = zblk->zulu_hat_blk_vaddr;
471 }
472 }
473
474 zblk->zulu_shadow_blk = NULL;
475 }
476
477 static void
zulu_shadow_tree_destroy(struct zulu_hat * zhat)478 zulu_shadow_tree_destroy(struct zulu_hat *zhat)
479 {
480 struct zulu_shadow_blk *sblk;
481 void *cookie = NULL;
482
483 while ((sblk = (struct zulu_shadow_blk *)avl_destroy_nodes(
484 &zhat->shadow_tree, &cookie)) != NULL) {
485 TNF_PROBE_2(shadow_tree_destroy, "zulu_hat", /* CSTYLED */,
486 tnf_opaque, vaddr, sblk->ivaddr,
487 tnf_opaque, ref_count, sblk->ref_count);
488 kmem_free(sblk, sizeof (*sblk));
489 }
490 avl_destroy(&zhat->shadow_tree);
491 }
492
493 /*
494 * zulu_hat_insert_map:
495 *
496 * Add a zulu_hat_blk to the a zhat's mappings list.
497 *
498 * Several data stuctures are used
499 * tsb: for simple fast lookups by the trap handler
500 * hash table: for efficent lookups by address, range
501 * An shadow tree of 4MB ranges with mappings for unloading big regions.
502 */
503 static void
zulu_hat_insert_map(struct zulu_hat * zhat,struct zulu_hat_blk * zblk)504 zulu_hat_insert_map(struct zulu_hat *zhat, struct zulu_hat_blk *zblk)
505 {
506 int tsb_hash;
507
508 tsb_hash = ZULU_TSB_HASH(zblk->zulu_hat_blk_vaddr,
509 zblk->zulu_hat_blk_size, zhat->zulu_tsb_size);
510
511 TNF_PROBE_3(zulu_hat_insert_map, "zulu_hat", /* CSTYLED */,
512 tnf_opaque, zblkp, zblk,
513 tnf_opaque, vaddr, zblk->zulu_hat_blk_vaddr,
514 tnf_opaque, hash, tsb_hash);
515
516 ASSERT(tsb_hash < zhat->zulu_tsb_size);
517
518 zulu_shadow_tree_insert(zhat, zblk);
519
520 /*
521 * The hash table is an array of buckets. Each bucket is the
522 * head of a linked list of mappings who's address hashess to the bucket
523 * New entries go to the head of the list.
524 */
525 zblk->zulu_hash_prev = NULL;
526 zblk->zulu_hash_next = ZULU_MAP_HASH_HEAD(zhat,
527 zblk->zulu_hat_blk_vaddr, zblk->zulu_hat_blk_size);
528 if (zblk->zulu_hash_next) {
529 zblk->zulu_hash_next->zulu_hash_prev = zblk;
530 }
531 ZULU_MAP_HASH_HEAD(zhat, zblk->zulu_hat_blk_vaddr,
532 zblk->zulu_hat_blk_size) = zblk;
533
534 zulu_ctx_tsb_lock_enter(zhat);
535 zhat->zulu_tsb[tsb_hash] = zblk->zulu_hat_blk_tte;
536 zulu_ctx_tsb_lock_exit(zhat);
537 }
538
539 /*
540 * remove a block from a zhat
541 */
542 static void
zulu_hat_remove_map(struct zulu_hat * zhat,struct zulu_hat_blk * zblk)543 zulu_hat_remove_map(struct zulu_hat *zhat, struct zulu_hat_blk *zblk)
544 {
545 int tsb_hash = ZULU_TSB_HASH(zblk->zulu_hat_blk_vaddr,
546 zblk->zulu_hat_blk_size, zhat->zulu_tsb_size);
547
548 TNF_PROBE_2(zulu_hat_remove_map, "zulu_hat", /* CSTYLED */,
549 tnf_opaque, vaddr, zblk->zulu_hat_blk_vaddr,
550 tnf_opaque, hash, tsb_hash);
551
552 ASSERT(tsb_hash < zhat->zulu_tsb_size);
553 ASSERT(mutex_owned(&zhat->lock));
554
555 zulu_shadow_tree_delete(zhat, zblk);
556
557 /*
558 * first remove zblk from hash table
559 */
560 if (zblk->zulu_hash_prev) {
561 zblk->zulu_hash_prev->zulu_hash_next = zblk->zulu_hash_next;
562 } else {
563 ZULU_MAP_HASH_HEAD(zhat, zblk->zulu_hat_blk_vaddr,
564 zblk->zulu_hat_blk_size) = NULL;
565 }
566 if (zblk->zulu_hash_next) {
567 zblk->zulu_hash_next->zulu_hash_prev = zblk->zulu_hash_prev;
568 }
569 zblk->zulu_hash_next = NULL;
570 zblk->zulu_hash_prev = NULL;
571
572 /*
573 * then remove the tsb entry
574 */
575 zulu_ctx_tsb_lock_enter(zhat);
576 if (zhat->zulu_tsb[tsb_hash].un.zulu_tte_addr ==
577 zblk->zulu_hat_blk_vaddr) {
578 zhat->zulu_tsb[tsb_hash].zulu_tte_valid = 0;
579 }
580 zulu_ctx_tsb_lock_exit(zhat);
581 }
582
583 /*
584 * look for a mapping to a given vaddr and page size
585 */
586 static struct zulu_hat_blk *
zulu_lookup_map_bysize(struct zulu_hat * zhat,caddr_t vaddr,int page_sz)587 zulu_lookup_map_bysize(struct zulu_hat *zhat, caddr_t vaddr, int page_sz)
588 {
589 struct zulu_hat_blk *zblkp;
590 uint64_t ivaddr = (uint64_t)vaddr;
591 int blks_checked = 0;
592
593 ASSERT(mutex_owned(&zhat->lock));
594
595 for (zblkp = ZULU_MAP_HASH_HEAD(zhat, ivaddr, page_sz); zblkp != NULL;
596 zblkp = zblkp->zulu_hash_next) {
597 uint64_t size;
598 uint64_t iaddr;
599
600 blks_checked++;
601
602 size = ZULU_HAT_PGSZ(zblkp->zulu_hat_blk_size);
603 iaddr = ZULU_VADDR((uint64_t)zblkp->zulu_hat_blk_vaddr);
604
605 if (iaddr <= ivaddr && (iaddr + size) > ivaddr) {
606 int tsb_hash;
607
608 tsb_hash = ZULU_TSB_HASH(zblkp->zulu_hat_blk_vaddr,
609 zblkp->zulu_hat_blk_size,
610 zhat->zulu_tsb_size);
611 ASSERT(tsb_hash < zhat->zulu_tsb_size);
612
613 zulu_ctx_tsb_lock_enter(zhat);
614 zhat->zulu_tsb[tsb_hash] = zblkp->zulu_hat_blk_tte;
615 zulu_ctx_tsb_lock_exit(zhat);
616 break;
617 }
618
619 }
620
621 TNF_PROBE_3(zulu_hat_lookup_map_bysz, "zulu_hat", /* CSTYLED */,
622 tnf_opaque, zblkp, zblkp,
623 tnf_int, blks_checked, blks_checked,
624 tnf_int, page_sz, page_sz);
625
626 return (zblkp);
627 }
628
629 /*
630 * Lookup a zblk for a given virtual address.
631 */
632 static struct zulu_hat_blk *
zulu_lookup_map(struct zulu_hat * zhat,caddr_t vaddr)633 zulu_lookup_map(struct zulu_hat *zhat, caddr_t vaddr)
634 {
635 struct zulu_hat_blk *zblkp = NULL;
636
637 /*
638 * if the hat is using 4M pages, look first for a 4M page
639 */
640 if (zhat->map4m) {
641 zblkp = zulu_lookup_map_bysize(zhat, vaddr, ZULU_TTE4M);
642 if (zblkp != NULL) {
643 return (zblkp);
644 }
645 }
646 /*
647 * Otherwise look for a 8k page
648 * Note: if base pagesize gets increased to 64K remove this test
649 */
650 if (zhat->map8k) {
651 zblkp = zulu_lookup_map_bysize(zhat, vaddr, ZULU_TTE8K);
652 if (zblkp != NULL) {
653 return (zblkp);
654 }
655 }
656 /*
657 * only if the page isn't found in the sizes that match the zulu mmus
658 * look for the inefficient 64K or 512K page sizes
659 */
660 if (zhat->map64k) {
661 zblkp = zulu_lookup_map_bysize(zhat, vaddr, ZULU_TTE64K);
662 if (zblkp != NULL) {
663 return (zblkp);
664 }
665 }
666 if (zhat->map512k) {
667 zblkp = zulu_lookup_map_bysize(zhat, vaddr, ZULU_TTE512K);
668 }
669
670 return (zblkp);
671 }
672
673 /*
674 * zulu_hat_load: Load translation for given vaddr
675 */
676 int
zulu_hat_load(struct zulu_hat * zhat,caddr_t vaddr,enum seg_rw rw,int * ppg_size)677 zulu_hat_load(struct zulu_hat *zhat, caddr_t vaddr,
678 enum seg_rw rw, int *ppg_size)
679 {
680 faultcode_t as_err;
681 struct zulu_hat_blk *zblkp;
682 int rval;
683 uint64_t flags_pfn;
684 struct zulu_tte tte;
685
686 TNF_PROBE_2(zulu_hat_load, "zulu_hat", /* CSTYLED */,
687 tnf_int, zulu_ctx, zhat->zulu_ctx,
688 tnf_opaque, vaddr, vaddr);
689
690 mutex_enter(&zhat->lock);
691 ASSERT(zhat->zulu_ctx >= 0);
692 /*
693 * lookup in our tsb first
694 */
695 zulu_ctx_tsb_lock_enter(zhat);
696 flags_pfn = zulu_hat_tsb_lookup_tl0(zhat, vaddr);
697 zulu_ctx_tsb_lock_exit(zhat);
698
699 if (flags_pfn) {
700 uint64_t *p = (uint64_t *)&tte;
701
702 p++; /* ignore the tag */
703 *p = flags_pfn; /* load the flags */
704
705 zuluvm_load_tte(zhat, vaddr, flags_pfn, tte.zulu_tte_perm,
706 tte.zulu_tte_size);
707 if (ppg_size != NULL) {
708 *ppg_size = tte.zulu_tte_size;
709 }
710
711 zulu_tsb_hit++;
712 mutex_exit(&zhat->lock);
713 return (0);
714 }
715
716 zulu_tsb_miss++;
717
718 zblkp = zulu_lookup_map(zhat, vaddr);
719 if (zblkp) {
720 tte = zblkp->zulu_hat_blk_tte;
721 tte.zulu_tte_pfn = ZULU_HAT_ADJ_PFN((&tte), vaddr);
722 zuluvm_load_tte(zhat, vaddr, tte.zulu_tte_pfn,
723 tte.zulu_tte_perm, tte.zulu_tte_size);
724 if (ppg_size != NULL) {
725 *ppg_size = tte.zulu_tte_size;
726 }
727 mutex_exit(&zhat->lock);
728 return (0);
729 }
730
731 /*
732 * Set a flag indicating that we're processing a fault.
733 * See comments in zulu_hat_unload_region.
734 */
735 zhat->in_fault = 1;
736 mutex_exit(&zhat->lock);
737
738 zulu_as_fault++;
739 TNF_PROBE_0(calling_as_fault, "zulu_hat", /* CSTYLED */);
740
741 as_err = as_fault((struct hat *)zhat, zhat->zulu_xhat.xhat_as,
742 (caddr_t)(ZULU_VADDR((uint64_t)vaddr) & PAGEMASK),
743 PAGESIZE, F_INVAL, rw);
744
745 mutex_enter(&zhat->lock);
746 zhat->in_fault = 0;
747 if (ppg_size != NULL) {
748 /*
749 * caller wants to know the page size (used by preload)
750 */
751 zblkp = zulu_lookup_map(zhat, vaddr);
752 if (zblkp != NULL) {
753 *ppg_size = zblkp->zulu_hat_blk_size;
754 } else {
755 *ppg_size = -1;
756 }
757 }
758 mutex_exit(&zhat->lock);
759
760 TNF_PROBE_1(as_fault_returned, "zulu_hat", /* CSTYLED */,
761 tnf_int, as_err, as_err);
762
763 if (as_err != 0) {
764 printf("as_fault returned %d\n", as_err);
765 rval = as_err;
766 } else if (zhat->freed) {
767 rval = -1;
768 } else {
769 rval = 0;
770 }
771
772 return (rval);
773 }
774
775 static struct xhat *
zulu_hat_alloc(void * arg)776 zulu_hat_alloc(void *arg)
777 {
778 struct zulu_hat *zhat = kmem_zalloc(sizeof (struct zulu_hat), KM_SLEEP);
779
780 (void) arg;
781
782 zulu_hat_ctx_alloc(zhat);
783
784 mutex_init(&zhat->lock, NULL, MUTEX_DEFAULT, NULL);
785
786 zhat->zulu_tsb = kmem_zalloc(ZULU_TSB_SZ, KM_SLEEP);
787 zhat->zulu_tsb_size = ZULU_TSB_NUM;
788 zhat->hash_tbl = kmem_zalloc(ZULU_HASH_TBL_SZ, KM_SLEEP);
789 avl_create(&zhat->shadow_tree, zulu_shadow_tree_compare,
790 sizeof (zhat->shadow_tree), ZULU_SHADOW_BLK_LINK_OFFSET);
791 /*
792 * The zulu hat has a few opaque data structs embedded in it.
793 * This tag makes finding the our data easier with a debugger.
794 */
795 zhat->magic = 0x42;
796
797 zhat->freed = 0;
798 TNF_PROBE_1(zulu_hat_alloc, "zulu_hat", /* CSTYLED */,
799 tnf_int, zulu_ctx, zhat->zulu_ctx);
800 return ((struct xhat *)zhat);
801 }
802
803 static void
zulu_hat_free(struct xhat * xhat)804 zulu_hat_free(struct xhat *xhat)
805 {
806 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
807
808 TNF_PROBE_1(zulu_hat_free, "zulu_hat", /* CSTYLED */,
809 tnf_int, zulu_ctx, zhat->zulu_ctx);
810
811 zulu_shadow_tree_destroy(zhat);
812 kmem_free(zhat->hash_tbl, ZULU_HASH_TBL_SZ);
813 kmem_free(zhat->zulu_tsb, ZULU_TSB_SZ);
814 mutex_destroy(&zhat->lock);
815 kmem_free(xhat, sizeof (struct zulu_hat));
816 }
817
818 static void
zulu_hat_free_start(struct xhat * xhat)819 zulu_hat_free_start(struct xhat *xhat)
820 {
821 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
822
823 TNF_PROBE_1(zulu_hat_free_start, "zulu_hat", /* CSTYLED */,
824 tnf_int, zulu_ctx, zhat->zulu_ctx);
825 (void) xhat;
826 }
827
828 /*
829 * zulu_hat_memload: This is the callback where the vm system gives us our
830 * translations
831 */
832 static void
zulu_do_hat_memload(struct xhat * xhat,caddr_t vaddr,struct page * page,uint_t attr,uint_t flags,int use_pszc)833 zulu_do_hat_memload(struct xhat *xhat, caddr_t vaddr, struct page *page,
834 uint_t attr, uint_t flags, int use_pszc)
835 {
836 void *blk;
837 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
838 struct zulu_hat_blk *zblk;
839 pfn_t pfn;
840
841 TNF_PROBE_4(zulu_hat_memload, "zulu_hat", /* CSTYLED */,
842 tnf_int, zulu_ctx, zhat->zulu_ctx,
843 tnf_opaque, vaddr, vaddr, tnf_opaque, attr, attr,
844 tnf_opaque, flags, flags);
845
846 /*
847 * keep track of the highest address that this zhat has had
848 * a mapping for.
849 * We use this in unload to avoid searching for regions that
850 * we've never seen.
851 *
852 * This is particularly useful avoiding repeated searches for
853 * for the process's mappings to the zulu hardware. These mappings
854 * are explicitly unloaded at each graphics context switch..
855 *
856 * This takes advantage of the fact that the device addresses
857 * are always above than the heap where most DMA data is stored.
858 */
859 if (vaddr > zhat->vaddr_max) {
860 zhat->vaddr_max = vaddr;
861 }
862
863 pfn = xhat_insert_xhatblk(page, xhat, &blk);
864 zblk = (struct zulu_hat_blk *)blk;
865 zblk->zulu_hat_blk_vaddr = (uintptr_t)vaddr;
866 zblk->zulu_hat_blk_pfn = (uint_t)pfn;
867 /*
868 * The perm bit is actually in the tte which gets copied to the TSB
869 */
870 zblk->zulu_hat_blk_perm = (attr & PROT_WRITE) ? 1 : 0;
871 zblk->zulu_hat_blk_size = use_pszc ? page->p_szc : 0;
872 zblk->zulu_hat_blk_valid = 1;
873
874 switch (zblk->zulu_hat_blk_size) {
875 case ZULU_TTE8K:
876 zhat->map8k = 1;
877 break;
878 case ZULU_TTE64K:
879 zhat->map64k = 1;
880 break;
881 case ZULU_TTE512K:
882 zhat->map512k = 1;
883 break;
884 case ZULU_TTE4M:
885 zhat->map4m = 1;
886 break;
887 default:
888 panic("zulu_hat illegal page size\n");
889 }
890
891 mutex_enter(&zhat->lock);
892
893 zulu_hat_insert_map(zhat, zblk);
894 if (!zhat->freed) {
895 zuluvm_load_tte(zhat, vaddr, zblk->zulu_hat_blk_pfn,
896 zblk->zulu_hat_blk_perm, zblk->zulu_hat_blk_size);
897 }
898 zhat->fault_ivaddr_last =
899 ZULU_VADDR((uint64_t)zblk->zulu_hat_blk_vaddr);
900
901 mutex_exit(&zhat->lock);
902 }
903
904 static void
zulu_hat_memload(struct xhat * xhat,caddr_t vaddr,struct page * page,uint_t attr,uint_t flags)905 zulu_hat_memload(struct xhat *xhat, caddr_t vaddr, struct page *page,
906 uint_t attr, uint_t flags)
907 {
908 zulu_do_hat_memload(xhat, vaddr, page, attr, flags, 0);
909 }
910
911 static void
zulu_hat_devload(struct xhat * xhat,caddr_t vaddr,size_t size,pfn_t pfn,uint_t attr,int flags)912 zulu_hat_devload(struct xhat *xhat, caddr_t vaddr, size_t size, pfn_t pfn,
913 uint_t attr, int flags)
914 {
915 struct page *pp = page_numtopp_nolock(pfn);
916 (void) size;
917 zulu_do_hat_memload(xhat, vaddr, pp, attr, (uint_t)flags, 1);
918 }
919
920 static void
zulu_hat_memload_array(struct xhat * xhat,caddr_t addr,size_t len,struct page ** gen_pps,uint_t attr,uint_t flags)921 zulu_hat_memload_array(struct xhat *xhat, caddr_t addr, size_t len,
922 struct page **gen_pps, uint_t attr, uint_t flags)
923 {
924 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
925
926 TNF_PROBE_3(zulu_hat_memload_array, "zulu_hat", /* CSTYLED */,
927 tnf_int, zulu_ctx, zhat->zulu_ctx,
928 tnf_opaque, addr, addr,
929 tnf_opaque, len, len);
930
931 for (; len > 0; len -= ZULU_HAT_PGSZ((*gen_pps)->p_szc),
932 gen_pps += ZULU_HAT_NUM_PGS((*gen_pps)->p_szc)) {
933 zulu_do_hat_memload(xhat, addr, *gen_pps, attr, flags, 1);
934
935 addr += ZULU_HAT_PGSZ((*gen_pps)->p_szc);
936 }
937 }
938
939 static void
free_zblks(struct zulu_hat_blk * free_list)940 free_zblks(struct zulu_hat_blk *free_list)
941 {
942 struct zulu_hat_blk *zblkp;
943 struct zulu_hat_blk *next;
944
945 for (zblkp = free_list; zblkp != NULL; zblkp = next) {
946 next = zblkp->zulu_hash_next;
947 (void) xhat_delete_xhatblk((struct xhat_hme_blk *)zblkp, 0);
948 }
949 }
950
951 static void
add_to_free_list(struct zulu_hat_blk ** pfree_list,struct zulu_hat_blk * zblk)952 add_to_free_list(struct zulu_hat_blk **pfree_list, struct zulu_hat_blk *zblk)
953 {
954 zblk->zulu_hash_next = *pfree_list;
955 *pfree_list = zblk;
956 }
957
958 static void
zulu_hat_unload_region(struct zulu_hat * zhat,uint64_t ivaddr,size_t size,struct zulu_shadow_blk * sblk,struct zulu_hat_blk ** pfree_list)959 zulu_hat_unload_region(struct zulu_hat *zhat, uint64_t ivaddr, size_t size,
960 struct zulu_shadow_blk *sblk, struct zulu_hat_blk **pfree_list)
961 {
962 uint64_t end = ivaddr + size;
963 int found = 0;
964
965 TNF_PROBE_2(zulu_hat_unload_region, "zulu_hat", /* CSTYLED */,
966 tnf_opaque, vaddr, ivaddr, tnf_opaque, size, size);
967
968 /*
969 * check address against the low and highwater marks for mappings
970 * in this sblk
971 */
972 if (ivaddr < sblk->min_addr) {
973 ivaddr = sblk->min_addr;
974 TNF_PROBE_1(zulu_hat_unload_skip, "zulu_hat", /* CSTYLED */,
975 tnf_opaque, ivaddr, ivaddr);
976 }
977 if (end > sblk->max_addr) {
978 end = sblk->max_addr;
979 TNF_PROBE_1(zulu_hat_unload_reg_skip, "zulu_hat", /* CSTYLED */,
980 tnf_opaque, end, end);
981 }
982 /*
983 * REMIND: It's not safe to touch the sblk after we enter this loop
984 * because it may get deleted.
985 */
986
987 while (ivaddr < end) {
988 uint64_t iaddr;
989 size_t pg_sz;
990 struct zulu_hat_blk *zblkp;
991
992 zblkp = zulu_lookup_map(zhat, (caddr_t)ivaddr);
993 if (zblkp == NULL) {
994 ivaddr += PAGESIZE;
995 continue;
996 }
997
998 iaddr = ZULU_VADDR((uint64_t)zblkp->zulu_hat_blk_vaddr);
999 pg_sz = ZULU_HAT_PGSZ(zblkp->zulu_hat_blk_size);
1000
1001 found++;
1002
1003 zulu_hat_remove_map(zhat, zblkp);
1004 /*
1005 * skip demap page if as_free has already been entered
1006 * zuluvm demapped the context already
1007 */
1008 if (!zhat->freed) {
1009 if ((zhat->in_fault) &&
1010 (iaddr == zhat->fault_ivaddr_last)) {
1011 /*
1012 * We're being called from within as_fault to
1013 * unload the last translation we loaded.
1014 *
1015 * This is probably due to watchpoint handling.
1016 * Delay the demap for a millisecond
1017 * to allow zulu to make some progress.
1018 */
1019 drv_usecwait(1000);
1020 zhat->fault_ivaddr_last = 0;
1021 }
1022 zulu_hat_demap_page(zhat, (caddr_t)iaddr,
1023 zblkp->zulu_hat_blk_size);
1024 }
1025
1026 add_to_free_list(pfree_list, zblkp);
1027
1028 if ((iaddr + pg_sz) >= end) {
1029 break;
1030 }
1031
1032 ivaddr += pg_sz;
1033 }
1034 TNF_PROBE_1(zulu_hat_unload_region_done, "zulu_hat", /* CSTYLED */,
1035 tnf_opaque, found, found);
1036 }
1037
1038 static void
zulu_hat_unload(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags)1039 zulu_hat_unload(struct xhat *xhat, caddr_t vaddr, size_t size, uint_t flags)
1040 {
1041 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1042 uint64_t ivaddr;
1043 uint64_t end;
1044 int found = 0;
1045 struct zulu_hat_blk *free_list = NULL;
1046
1047 (void) flags;
1048
1049 TNF_PROBE_4(zulu_hat_unload, "zulu_hat", /* CSTYLED */,
1050 tnf_int, zulu_ctx, zhat->zulu_ctx,
1051 tnf_opaque, vaddr, vaddr,
1052 tnf_opaque, vaddr_max, zhat->vaddr_max,
1053 tnf_opaque, size, size);
1054
1055 mutex_enter(&zhat->lock);
1056
1057 /*
1058 * The following test prevents us from searching for the user's
1059 * mappings to the zulu device registers. Those mappings get unloaded
1060 * every time a graphics context switch away from a given context
1061 * occurs.
1062 *
1063 * Since the heap is located at smaller virtual addresses than the
1064 * registers, this simple test avoids quite a bit of useless work.
1065 */
1066 if (vaddr > zhat->vaddr_max) {
1067 /*
1068 * all existing mappings have lower addresses than vaddr
1069 * no need to search further.
1070 */
1071 mutex_exit(&zhat->lock);
1072 return;
1073 }
1074
1075 ivaddr = (uint64_t)vaddr;
1076 end = ivaddr + size;
1077
1078 do {
1079 struct zulu_shadow_blk *sblk;
1080
1081 sblk = zulu_shadow_tree_lookup(zhat, ivaddr, NULL);
1082 if (sblk != NULL) {
1083 uint64_t sblk_end;
1084 size_t region_size;
1085
1086 found++;
1087
1088 sblk_end = (ivaddr + ZULU_SHADOW_BLK_RANGE) &
1089 ZULU_SHADOW_BLK_MASK;
1090
1091 if (sblk_end < end) {
1092 region_size = sblk_end - ivaddr;
1093 } else {
1094 region_size = end - ivaddr;
1095 }
1096 zulu_hat_unload_region(zhat, ivaddr, region_size, sblk,
1097 &free_list);
1098
1099 }
1100 ivaddr += ZULU_SHADOW_BLK_RANGE;
1101 } while (ivaddr < end);
1102
1103 mutex_exit(&zhat->lock);
1104
1105 free_zblks(free_list);
1106
1107 TNF_PROBE_1(zulu_hat_unload_done, "zulu_hat", /* CSTYLED */,
1108 tnf_int, found, found);
1109 }
1110
1111 static void
zulu_hat_unload_callback(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags,hat_callback_t * pcb)1112 zulu_hat_unload_callback(struct xhat *xhat, caddr_t vaddr, size_t size,
1113 uint_t flags, hat_callback_t *pcb)
1114 {
1115 (void) size;
1116 (void) pcb;
1117 zulu_hat_unload(xhat, vaddr, size, flags);
1118 }
1119
1120
1121 /*
1122 * unload one page
1123 */
1124 static int
zulu_hat_pageunload(struct xhat * xhat,struct page * pp,uint_t flags,void * xblk)1125 zulu_hat_pageunload(struct xhat *xhat, struct page *pp, uint_t flags,
1126 void *xblk)
1127 {
1128 struct zulu_hat_blk *zblk = (struct zulu_hat_blk *)xblk;
1129 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1130 int do_delete;
1131
1132 (void) pp;
1133 (void) flags;
1134
1135 TNF_PROBE_3(zulu_hat_pageunload, "zulu_hat", /* CSTYLED */,
1136 tnf_int, zulu_ctx, zhat->zulu_ctx,
1137 tnf_opaque, vaddr, zblk->zulu_hat_blk_vaddr,
1138 tnf_int, pg_size, zblk->zulu_hat_blk_size);
1139
1140 mutex_enter(&zhat->lock);
1141 if (zblk->zulu_shadow_blk != NULL) {
1142
1143 do_delete = 1;
1144
1145 zulu_hat_remove_map(zhat, zblk);
1146
1147 /*
1148 * now that the entry is removed from the TSB, remove the
1149 * translation from the zulu hardware.
1150 *
1151 * Skip the demap if this as is in the process of being freed.
1152 * The zuluvm as callback has demapped the whole context.
1153 */
1154 if (!zhat->freed) {
1155 zulu_hat_demap_page(zhat,
1156 (caddr_t)(zblk->zulu_hat_blk_page << ZULU_HAT_BP_SHIFT),
1157 zblk->zulu_hat_blk_size);
1158 }
1159 } else {
1160 /*
1161 * This block has already been removed from the zulu_hat,
1162 * it's on a free list waiting for our thread to release
1163 * a mutex so it can be freed
1164 */
1165 do_delete = 0;
1166
1167 TNF_PROBE_0(zulu_hat_pageunload_skip, "zulu_hat",
1168 /* CSTYLED */);
1169 }
1170 mutex_exit(&zhat->lock);
1171
1172 if (do_delete) {
1173 (void) xhat_delete_xhatblk(xblk, 1);
1174 }
1175
1176 return (0);
1177 }
1178
1179 static void
zulu_hat_swapout(struct xhat * xhat)1180 zulu_hat_swapout(struct xhat *xhat)
1181 {
1182 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1183 struct zulu_hat_blk *zblk;
1184 struct zulu_hat_blk *free_list = NULL;
1185 int i;
1186 int nblks = 0;
1187
1188 TNF_PROBE_1(zulu_hat_swapout, "zulu_hat", /* CSTYLED */,
1189 tnf_int, zulu_ctx, zhat->zulu_ctx);
1190
1191 mutex_enter(&zhat->lock);
1192
1193 /*
1194 * real swapout calls are rare so we don't do anything in
1195 * particular to optimize them.
1196 *
1197 * Just loop over all buckets in the hash table and free each
1198 * zblk.
1199 */
1200 for (i = 0; i < ZULU_HASH_TBL_NUM; i++) {
1201 struct zulu_hat_blk *next;
1202 for (zblk = zhat->hash_tbl[i]; zblk != NULL; zblk = next) {
1203 next = zblk->zulu_hash_next;
1204 zulu_hat_remove_map(zhat, zblk);
1205 add_to_free_list(&free_list, zblk);
1206 nblks++;
1207 }
1208 }
1209
1210 /*
1211 * remove all mappings for this context from zulu hardware.
1212 */
1213 zulu_hat_demap_ctx(zhat->zdev, zhat->zulu_ctx);
1214
1215 mutex_exit(&zhat->lock);
1216
1217 free_zblks(free_list);
1218
1219 TNF_PROBE_1(zulu_hat_swapout_done, "zulu_hat", /* CSTYLED */,
1220 tnf_int, nblks, nblks);
1221 }
1222
1223
1224 static void
zulu_hat_unshare(struct xhat * xhat,caddr_t vaddr,size_t size)1225 zulu_hat_unshare(struct xhat *xhat, caddr_t vaddr, size_t size)
1226 {
1227 TNF_PROBE_0(zulu_hat_unshare, "zulu_hat", /* CSTYLED */);
1228
1229 zulu_hat_unload(xhat, vaddr, size, 0);
1230 }
1231
1232 /*
1233 * Functions to manage changes in protections for mappings.
1234 *
1235 * These are rarely called in normal operation so for now just unload
1236 * the region.
1237 * If the mapping is still needed, it will fault in later with the new
1238 * attrributes.
1239 */
1240 typedef enum {
1241 ZULU_HAT_CHGATTR,
1242 ZULU_HAT_SETATTR,
1243 ZULU_HAT_CLRATTR
1244 } zulu_hat_prot_op;
1245
1246 static void
zulu_hat_update_attr(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags,zulu_hat_prot_op op)1247 zulu_hat_update_attr(struct xhat *xhat, caddr_t vaddr, size_t size,
1248 uint_t flags, zulu_hat_prot_op op)
1249 {
1250 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1251
1252 TNF_PROBE_5(zulu_hat_changeprot, "zulu_hat", /* CSTYLED */,
1253 tnf_int, ctx, zhat->zulu_ctx,
1254 tnf_opaque, vaddr, vaddr, tnf_opaque, size, size,
1255 tnf_uint, flags, flags, tnf_int, op, op);
1256
1257 zulu_hat_unload(xhat, vaddr, size, 0);
1258 }
1259
1260 static void
zulu_hat_chgprot(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags)1261 zulu_hat_chgprot(struct xhat *xhat, caddr_t vaddr, size_t size, uint_t flags)
1262 {
1263 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1264 #ifdef DEBUG
1265 printf("zulu_hat_chgprot: ctx: %d addr: %lx, size: %lx flags: %x\n",
1266 zhat->zulu_ctx, (uint64_t)vaddr, size, flags);
1267 #endif
1268 zulu_hat_update_attr(xhat, vaddr, size, flags, ZULU_HAT_CHGATTR);
1269 }
1270
1271
1272 static void
zulu_hat_setattr(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags)1273 zulu_hat_setattr(struct xhat *xhat, caddr_t vaddr, size_t size, uint_t flags)
1274 {
1275 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1276 #ifdef DEBUG
1277 printf("zulu_hat_setattr: ctx: %d addr: %lx, size: %lx flags: %x\n",
1278 zhat->zulu_ctx, (uint64_t)vaddr, size, flags);
1279 #endif
1280 zulu_hat_update_attr(xhat, vaddr, size, flags, ZULU_HAT_SETATTR);
1281 }
1282
1283 static void
zulu_hat_clrattr(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags)1284 zulu_hat_clrattr(struct xhat *xhat, caddr_t vaddr, size_t size, uint_t flags)
1285 {
1286 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1287 #ifdef DEBUG
1288 printf("zulu_hat_clrattr: ctx: %d addr: %lx, size: %lx flags: %x\n",
1289 zhat->zulu_ctx, (uint64_t)vaddr, size, flags);
1290 #endif
1291 zulu_hat_update_attr(xhat, vaddr, size, flags, ZULU_HAT_CLRATTR);
1292 }
1293
1294 static void
zulu_hat_chgattr(struct xhat * xhat,caddr_t vaddr,size_t size,uint_t flags)1295 zulu_hat_chgattr(struct xhat *xhat, caddr_t vaddr, size_t size, uint_t flags)
1296 {
1297 struct zulu_hat *zhat = (struct zulu_hat *)xhat;
1298 TNF_PROBE_3(zulu_hat_chgattr, "zulu_hat", /* CSTYLED */,
1299 tnf_int, ctx, zhat->zulu_ctx,
1300 tnf_opaque, vaddr, vaddr,
1301 tnf_opaque, flags, flags);
1302 #ifdef DEBUG
1303 printf("zulu_hat_chgattr: ctx: %d addr: %lx, size: %lx flags: %x\n",
1304 zhat->zulu_ctx, (uint64_t)vaddr, size, flags);
1305 #endif
1306 zulu_hat_update_attr(xhat, vaddr, size, flags, ZULU_HAT_CHGATTR);
1307 }
1308
1309
1310 struct xhat_ops zulu_hat_ops = {
1311 zulu_hat_alloc, /* xhat_alloc */
1312 zulu_hat_free, /* xhat_free */
1313 zulu_hat_free_start, /* xhat_free_start */
1314 NULL, /* xhat_free_end */
1315 NULL, /* xhat_dup */
1316 NULL, /* xhat_swapin */
1317 zulu_hat_swapout, /* xhat_swapout */
1318 zulu_hat_memload, /* xhat_memload */
1319 zulu_hat_memload_array, /* xhat_memload_array */
1320 zulu_hat_devload, /* xhat_devload */
1321 zulu_hat_unload, /* xhat_unload */
1322 zulu_hat_unload_callback, /* xhat_unload_callback */
1323 zulu_hat_setattr, /* xhat_setattr */
1324 zulu_hat_clrattr, /* xhat_clrattr */
1325 zulu_hat_chgattr, /* xhat_chgattr */
1326 zulu_hat_unshare, /* xhat_unshare */
1327 zulu_hat_chgprot, /* xhat_chgprot */
1328 zulu_hat_pageunload, /* xhat_pageunload */
1329 };
1330
1331 xblk_cache_t zulu_xblk_cache = {
1332 NULL,
1333 NULL,
1334 NULL,
1335 xhat_xblkcache_reclaim
1336 };
1337
1338 xhat_provider_t zulu_hat_provider = {
1339 XHAT_PROVIDER_VERSION,
1340 0,
1341 NULL,
1342 NULL,
1343 "zulu_hat_provider",
1344 &zulu_xblk_cache,
1345 &zulu_hat_ops,
1346 sizeof (struct zulu_hat_blk) + sizeof (struct xhat_hme_blk)
1347 };
1348
1349 /*
1350 * The following functions are the entry points that zuluvm uses.
1351 */
1352
1353 /*
1354 * initialize this module. Called from zuluvm's _init function
1355 */
1356 int
zulu_hat_init()1357 zulu_hat_init()
1358 {
1359 int c;
1360 int rval;
1361 mutex_init(&zulu_ctx_lock, NULL, MUTEX_DEFAULT, NULL);
1362
1363 for (c = 0; c < ZULU_HAT_MAX_CTX; c++) {
1364 ZULU_CTX_LOCK_INIT(c);
1365 }
1366 zulu_ctx_search_start = 0;
1367 rval = xhat_provider_register(&zulu_hat_provider);
1368 if (rval != 0) {
1369 mutex_destroy(&zulu_ctx_lock);
1370 }
1371 return (rval);
1372 }
1373
1374 /*
1375 * un-initialize this module. Called from zuluvm's _fini function
1376 */
1377 int
zulu_hat_destroy()1378 zulu_hat_destroy()
1379 {
1380 if (xhat_provider_unregister(&zulu_hat_provider) != 0) {
1381 return (-1);
1382 }
1383 mutex_destroy(&zulu_ctx_lock);
1384 return (0);
1385 }
1386
1387 int
zulu_hat_attach(void * arg)1388 zulu_hat_attach(void *arg)
1389 {
1390 (void) arg;
1391 return (0);
1392 }
1393
1394 int
zulu_hat_detach(void * arg)1395 zulu_hat_detach(void *arg)
1396 {
1397 (void) arg;
1398 return (0);
1399 }
1400
1401 /*
1402 * create a zulu hat for this address space.
1403 */
1404 struct zulu_hat *
zulu_hat_proc_attach(struct as * as,void * zdev)1405 zulu_hat_proc_attach(struct as *as, void *zdev)
1406 {
1407 struct zulu_hat *zhat;
1408 int xhat_rval;
1409
1410 xhat_rval = xhat_attach_xhat(&zulu_hat_provider, as,
1411 (struct xhat **)&zhat, NULL);
1412 if ((xhat_rval == 0) && (zhat != NULL)) {
1413 mutex_enter(&zhat->lock);
1414 ZULU_HAT2AS(zhat) = as;
1415 zhat->zdev = zdev;
1416 mutex_exit(&zhat->lock);
1417 }
1418
1419 TNF_PROBE_3(zulu_hat_proc_attach, "zulu_hat", /* CSTYLED */,
1420 tnf_int, xhat_rval, xhat_rval, tnf_opaque, as, as,
1421 tnf_opaque, zhat, zhat);
1422
1423 return (zhat);
1424 }
1425
1426 void
zulu_hat_proc_detach(struct zulu_hat * zhat)1427 zulu_hat_proc_detach(struct zulu_hat *zhat)
1428 {
1429 struct as *as = ZULU_HAT2AS(zhat);
1430
1431 zulu_hat_ctx_free(zhat);
1432
1433 (void) xhat_detach_xhat(&zulu_hat_provider, ZULU_HAT2AS(zhat));
1434
1435 TNF_PROBE_1(zulu_hat_proc_detach, "zulu_hat", /* CSTYLED */,
1436 tnf_opaque, as, as);
1437 }
1438
1439 /*
1440 * zulu_hat_terminate
1441 *
1442 * Disables any further TLB miss processing for this hat
1443 * Called by zuluvm's as_free callback. The primary purpose of this
1444 * function is to cause any pending zulu DMA to abort quickly.
1445 */
1446 void
zulu_hat_terminate(struct zulu_hat * zhat)1447 zulu_hat_terminate(struct zulu_hat *zhat)
1448 {
1449 int ctx = zhat->zulu_ctx;
1450
1451 TNF_PROBE_1(zulu_hat_terminate, "zulu_hat", /* CSTYLED */,
1452 tnf_int, ctx, ctx);
1453
1454 mutex_enter(&zhat->lock);
1455
1456 zhat->freed = 1;
1457
1458 zulu_ctx_tsb_lock_enter(zhat);
1459 /*
1460 * zap the tsb
1461 */
1462 bzero(zhat->zulu_tsb, ZULU_TSB_SZ);
1463 zulu_ctx_tsb_lock_exit(zhat);
1464
1465 zulu_hat_demap_ctx(zhat->zdev, zhat->zulu_ctx);
1466
1467 mutex_exit(&zhat->lock);
1468
1469 TNF_PROBE_0(zulu_hat_terminate_done, "zulu_hat", /* CSTYLED */);
1470 }
1471