xref: /illumos-gate/usr/src/uts/i86pc/io/immu_qinv.c (revision 09a48d4ca0ddda4ad26cc885769745870d989baf)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Portions Copyright (c) 2010, Oracle and/or its affiliates.
23  * All rights reserved.
24  */
25 
26 /*
27  * Copyright (c) 2009, Intel Corporation.
28  * All rights reserved.
29  */
30 
31 #include <sys/ddi.h>
32 #include <sys/archsystm.h>
33 #include <vm/hat_i86.h>
34 #include <sys/types.h>
35 #include <sys/cpu.h>
36 #include <sys/sysmacros.h>
37 #include <sys/immu.h>
38 
39 /* invalidation queue table entry size */
40 #define	QINV_ENTRY_SIZE		0x10
41 
42 /* max value of Queue Size field of Invalidation Queue Address Register */
43 #define	QINV_MAX_QUEUE_SIZE	0x7
44 
45 /* status data size of invalidation wait descriptor */
46 #define	QINV_SYNC_DATA_SIZE	0x4
47 
48 /* invalidation queue head and tail */
49 #define	QINV_IQA_HEAD(QH)	BITX((QH), 18, 4)
50 #define	QINV_IQA_TAIL_SHIFT	4
51 
52 /* invalidation queue entry structure */
53 typedef struct qinv_inv_dsc {
54 	uint64_t	lo;
55 	uint64_t	hi;
56 } qinv_dsc_t;
57 
58 /* physical contigous pages for invalidation queue */
59 typedef struct qinv_mem {
60 	kmutex_t	   qinv_mem_lock;
61 	ddi_dma_handle_t   qinv_mem_dma_hdl;
62 	ddi_acc_handle_t   qinv_mem_acc_hdl;
63 	caddr_t		   qinv_mem_vaddr;
64 	paddr_t		   qinv_mem_paddr;
65 	uint_t		   qinv_mem_size;
66 	uint16_t	   qinv_mem_head;
67 	uint16_t	   qinv_mem_tail;
68 } qinv_mem_t;
69 
70 
71 /*
72  * invalidation queue state
73  *   This structure describes the state information of the
74  *   invalidation queue table and related status memeory for
75  *   invalidation wait descriptor
76  *
77  * qinv_table		- invalidation queue table
78  * qinv_sync		- sync status memory for invalidation wait descriptor
79  */
80 typedef struct qinv {
81 	qinv_mem_t		qinv_table;
82 	qinv_mem_t		qinv_sync;
83 } qinv_t;
84 
85 static void immu_qinv_inv_wait(immu_inv_wait_t *iwp);
86 
87 static struct immu_flushops immu_qinv_flushops = {
88 	immu_qinv_context_fsi,
89 	immu_qinv_context_dsi,
90 	immu_qinv_context_gbl,
91 	immu_qinv_iotlb_psi,
92 	immu_qinv_iotlb_dsi,
93 	immu_qinv_iotlb_gbl,
94 	immu_qinv_inv_wait
95 };
96 
97 /* helper macro for making queue invalidation descriptor */
98 #define	INV_DSC_TYPE(dsc)	((dsc)->lo & 0xF)
99 #define	CC_INV_DSC_HIGH		(0)
100 #define	CC_INV_DSC_LOW(fm, sid, did, g)	(((uint64_t)(fm) << 48) | \
101 	((uint64_t)(sid) << 32) | \
102 	((uint64_t)(did) << 16) | \
103 	((uint64_t)(g) << 4) | \
104 	1)
105 
106 #define	IOTLB_INV_DSC_HIGH(addr, ih, am) (((uint64_t)(addr)) | \
107 	((uint64_t)(ih) << 6) |	\
108 	((uint64_t)(am)))
109 
110 #define	IOTLB_INV_DSC_LOW(did, dr, dw, g) (((uint64_t)(did) << 16) | \
111 	((uint64_t)(dr) << 7) | \
112 	((uint64_t)(dw) << 6) | \
113 	((uint64_t)(g) << 4) | \
114 	2)
115 
116 #define	DEV_IOTLB_INV_DSC_HIGH(addr, s) (((uint64_t)(addr)) | (s))
117 
118 #define	DEV_IOTLB_INV_DSC_LOW(sid, max_invs_pd) ( \
119 	((uint64_t)(sid) << 32) | \
120 	((uint64_t)(max_invs_pd) << 16) | \
121 	3)
122 
123 #define	IEC_INV_DSC_HIGH (0)
124 #define	IEC_INV_DSC_LOW(idx, im, g) (((uint64_t)(idx) << 32) | \
125 	((uint64_t)(im) << 27) | \
126 	((uint64_t)(g) << 4) | \
127 	4)
128 
129 #define	INV_WAIT_DSC_HIGH(saddr) ((uint64_t)(saddr))
130 
131 #define	INV_WAIT_DSC_LOW(sdata, fn, sw, iflag) (((uint64_t)(sdata) << 32) | \
132 	((uint64_t)(fn) << 6) | \
133 	((uint64_t)(sw) << 5) | \
134 	((uint64_t)(iflag) << 4) | \
135 	5)
136 
137 /*
138  * QS field of Invalidation Queue Address Register
139  * the size of invalidation queue is 1 << (qinv_iqa_qs + 8)
140  */
141 static uint_t qinv_iqa_qs = 6;
142 
143 /*
144  * the invalidate desctiptor type of queued invalidation interface
145  */
146 static char *qinv_dsc_type[] = {
147 	"Reserved",
148 	"Context Cache Invalidate Descriptor",
149 	"IOTLB Invalidate Descriptor",
150 	"Device-IOTLB Invalidate Descriptor",
151 	"Interrupt Entry Cache Invalidate Descriptor",
152 	"Invalidation Wait Descriptor",
153 	"Incorrect queue invalidation type"
154 };
155 
156 #define	QINV_MAX_DSC_TYPE	(sizeof (qinv_dsc_type) / sizeof (char *))
157 
158 /*
159  * the queued invalidation interface functions
160  */
161 static void qinv_submit_inv_dsc(immu_t *immu, qinv_dsc_t *dsc);
162 static void qinv_context_common(immu_t *immu, uint8_t function_mask,
163     uint16_t source_id, uint_t domain_id, ctt_inv_g_t type);
164 static void qinv_iotlb_common(immu_t *immu, uint_t domain_id,
165     uint64_t addr, uint_t am, uint_t hint, tlb_inv_g_t type);
166 static void qinv_iec_common(immu_t *immu, uint_t iidx,
167     uint_t im, uint_t g);
168 static void immu_qinv_inv_wait(immu_inv_wait_t *iwp);
169 static void qinv_wait_sync(immu_t *immu, immu_inv_wait_t *iwp);
170 /*LINTED*/
171 static void qinv_dev_iotlb_common(immu_t *immu, uint16_t sid,
172     uint64_t addr, uint_t size, uint_t max_invs_pd);
173 
174 
175 /* submit invalidation request descriptor to invalidation queue */
176 static void
177 qinv_submit_inv_dsc(immu_t *immu, qinv_dsc_t *dsc)
178 {
179 	qinv_t *qinv;
180 	qinv_mem_t *qinv_table;
181 	uint_t tail;
182 #ifdef DEBUG
183 	uint_t count = 0;
184 #endif
185 
186 	qinv = (qinv_t *)immu->immu_qinv;
187 	qinv_table = &(qinv->qinv_table);
188 
189 	mutex_enter(&qinv_table->qinv_mem_lock);
190 	tail = qinv_table->qinv_mem_tail;
191 	qinv_table->qinv_mem_tail++;
192 
193 	if (qinv_table->qinv_mem_tail == qinv_table->qinv_mem_size)
194 		qinv_table->qinv_mem_tail = 0;
195 
196 	while (qinv_table->qinv_mem_head == qinv_table->qinv_mem_tail) {
197 #ifdef DEBUG
198 		count++;
199 #endif
200 		/*
201 		 * inv queue table exhausted, wait hardware to fetch
202 		 * next descriptor
203 		 */
204 		qinv_table->qinv_mem_head = QINV_IQA_HEAD(
205 		    immu_regs_get64(immu, IMMU_REG_INVAL_QH));
206 	}
207 
208 	IMMU_DPROBE3(immu__qinv__sub, uint64_t, dsc->lo, uint64_t, dsc->hi,
209 	    uint_t, count);
210 
211 	bcopy(dsc, qinv_table->qinv_mem_vaddr + tail * QINV_ENTRY_SIZE,
212 	    QINV_ENTRY_SIZE);
213 
214 	immu_regs_put64(immu, IMMU_REG_INVAL_QT,
215 	    qinv_table->qinv_mem_tail << QINV_IQA_TAIL_SHIFT);
216 
217 	mutex_exit(&qinv_table->qinv_mem_lock);
218 }
219 
220 /* queued invalidation interface -- invalidate context cache */
221 static void
222 qinv_context_common(immu_t *immu, uint8_t function_mask,
223     uint16_t source_id, uint_t domain_id, ctt_inv_g_t type)
224 {
225 	qinv_dsc_t dsc;
226 
227 	dsc.lo = CC_INV_DSC_LOW(function_mask, source_id, domain_id, type);
228 	dsc.hi = CC_INV_DSC_HIGH;
229 
230 	qinv_submit_inv_dsc(immu, &dsc);
231 }
232 
233 /* queued invalidation interface -- invalidate iotlb */
234 static void
235 qinv_iotlb_common(immu_t *immu, uint_t domain_id,
236     uint64_t addr, uint_t am, uint_t hint, tlb_inv_g_t type)
237 {
238 	qinv_dsc_t dsc;
239 	uint8_t dr = 0;
240 	uint8_t dw = 0;
241 
242 	if (IMMU_CAP_GET_DRD(immu->immu_regs_cap))
243 		dr = 1;
244 	if (IMMU_CAP_GET_DWD(immu->immu_regs_cap))
245 		dw = 1;
246 
247 	switch (type) {
248 	case TLB_INV_G_PAGE:
249 		if (!IMMU_CAP_GET_PSI(immu->immu_regs_cap) ||
250 		    am > IMMU_CAP_GET_MAMV(immu->immu_regs_cap) ||
251 		    addr & IMMU_PAGEOFFSET) {
252 			type = TLB_INV_G_DOMAIN;
253 			goto qinv_ignore_psi;
254 		}
255 		dsc.lo = IOTLB_INV_DSC_LOW(domain_id, dr, dw, type);
256 		dsc.hi = IOTLB_INV_DSC_HIGH(addr, hint, am);
257 		break;
258 
259 	qinv_ignore_psi:
260 	case TLB_INV_G_DOMAIN:
261 		dsc.lo = IOTLB_INV_DSC_LOW(domain_id, dr, dw, type);
262 		dsc.hi = 0;
263 		break;
264 
265 	case TLB_INV_G_GLOBAL:
266 		dsc.lo = IOTLB_INV_DSC_LOW(0, dr, dw, type);
267 		dsc.hi = 0;
268 		break;
269 	default:
270 		ddi_err(DER_WARN, NULL, "incorrect iotlb flush type");
271 		return;
272 	}
273 
274 	qinv_submit_inv_dsc(immu, &dsc);
275 }
276 
277 /* queued invalidation interface -- invalidate dev_iotlb */
278 static void
279 qinv_dev_iotlb_common(immu_t *immu, uint16_t sid,
280     uint64_t addr, uint_t size, uint_t max_invs_pd)
281 {
282 	qinv_dsc_t dsc;
283 
284 	dsc.lo = DEV_IOTLB_INV_DSC_LOW(sid, max_invs_pd);
285 	dsc.hi = DEV_IOTLB_INV_DSC_HIGH(addr, size);
286 
287 	qinv_submit_inv_dsc(immu, &dsc);
288 }
289 
290 /* queued invalidation interface -- invalidate interrupt entry cache */
291 static void
292 qinv_iec_common(immu_t *immu, uint_t iidx, uint_t im, uint_t g)
293 {
294 	qinv_dsc_t dsc;
295 
296 	dsc.lo = IEC_INV_DSC_LOW(iidx, im, g);
297 	dsc.hi = IEC_INV_DSC_HIGH;
298 
299 	qinv_submit_inv_dsc(immu, &dsc);
300 }
301 
302 /*
303  * queued invalidation interface -- invalidation wait descriptor
304  *   wait until the invalidation request finished
305  */
306 static void
307 qinv_wait_sync(immu_t *immu, immu_inv_wait_t *iwp)
308 {
309 	qinv_dsc_t dsc;
310 	volatile uint32_t *status;
311 	uint64_t paddr;
312 #ifdef DEBUG
313 	uint_t count;
314 #endif
315 
316 	status = &iwp->iwp_vstatus;
317 	paddr = iwp->iwp_pstatus;
318 
319 	*status = IMMU_INV_DATA_PENDING;
320 	membar_producer();
321 
322 	/*
323 	 * sdata = IMMU_INV_DATA_DONE, fence = 1, sw = 1, if = 0
324 	 * indicate the invalidation wait descriptor completion by
325 	 * performing a coherent DWORD write to the status address,
326 	 * not by generating an invalidation completion event
327 	 */
328 	dsc.lo = INV_WAIT_DSC_LOW(IMMU_INV_DATA_DONE, 1, 1, 0);
329 	dsc.hi = INV_WAIT_DSC_HIGH(paddr);
330 
331 	qinv_submit_inv_dsc(immu, &dsc);
332 
333 	if (iwp->iwp_sync) {
334 #ifdef DEBUG
335 		count = 0;
336 		while (*status != IMMU_INV_DATA_DONE) {
337 			count++;
338 			ht_pause();
339 		}
340 		DTRACE_PROBE2(immu__wait__sync, const char *, iwp->iwp_name,
341 		    uint_t, count);
342 #else
343 		while (*status != IMMU_INV_DATA_DONE)
344 			ht_pause();
345 #endif
346 	}
347 }
348 
349 static void
350 immu_qinv_inv_wait(immu_inv_wait_t *iwp)
351 {
352 	volatile uint32_t *status = &iwp->iwp_vstatus;
353 #ifdef DEBUG
354 	uint_t count;
355 
356 	count = 0;
357 	while (*status != IMMU_INV_DATA_DONE) {
358 		count++;
359 		ht_pause();
360 	}
361 	DTRACE_PROBE2(immu__wait__async, const char *, iwp->iwp_name,
362 	    uint_t, count);
363 #else
364 
365 	while (*status != IMMU_INV_DATA_DONE)
366 		ht_pause();
367 #endif
368 }
369 
370 /*
371  * call ddi_dma_mem_alloc to allocate physical contigous
372  * pages for invalidation queue table
373  */
374 static int
375 qinv_setup(immu_t *immu)
376 {
377 	qinv_t *qinv;
378 	size_t size;
379 
380 	ddi_dma_attr_t qinv_dma_attr = {
381 		DMA_ATTR_V0,
382 		0U,
383 		0xffffffffffffffffULL,
384 		0xffffffffU,
385 		MMU_PAGESIZE, /* page aligned */
386 		0x1,
387 		0x1,
388 		0xffffffffU,
389 		0xffffffffffffffffULL,
390 		1,
391 		4,
392 		0
393 	};
394 
395 	ddi_device_acc_attr_t qinv_acc_attr = {
396 		DDI_DEVICE_ATTR_V0,
397 		DDI_NEVERSWAP_ACC,
398 		DDI_STRICTORDER_ACC
399 	};
400 
401 	mutex_init(&(immu->immu_qinv_lock), NULL, MUTEX_DRIVER, NULL);
402 
403 
404 	mutex_enter(&(immu->immu_qinv_lock));
405 
406 	immu->immu_qinv = NULL;
407 	if (!IMMU_ECAP_GET_QI(immu->immu_regs_excap) ||
408 	    immu_qinv_enable == B_FALSE) {
409 		mutex_exit(&(immu->immu_qinv_lock));
410 		return (DDI_SUCCESS);
411 	}
412 
413 	if (qinv_iqa_qs > QINV_MAX_QUEUE_SIZE)
414 		qinv_iqa_qs = QINV_MAX_QUEUE_SIZE;
415 
416 	qinv = kmem_zalloc(sizeof (qinv_t), KM_SLEEP);
417 
418 	if (ddi_dma_alloc_handle(root_devinfo,
419 	    &qinv_dma_attr, DDI_DMA_SLEEP, NULL,
420 	    &(qinv->qinv_table.qinv_mem_dma_hdl)) != DDI_SUCCESS) {
421 		ddi_err(DER_WARN, root_devinfo,
422 		    "alloc invalidation queue table handler failed");
423 		goto queue_table_handle_failed;
424 	}
425 
426 	if (ddi_dma_alloc_handle(root_devinfo,
427 	    &qinv_dma_attr, DDI_DMA_SLEEP, NULL,
428 	    &(qinv->qinv_sync.qinv_mem_dma_hdl)) != DDI_SUCCESS) {
429 		ddi_err(DER_WARN, root_devinfo,
430 		    "alloc invalidation queue sync mem handler failed");
431 		goto sync_table_handle_failed;
432 	}
433 
434 	qinv->qinv_table.qinv_mem_size = (1 << (qinv_iqa_qs + 8));
435 	size = qinv->qinv_table.qinv_mem_size * QINV_ENTRY_SIZE;
436 
437 	/* alloc physical contiguous pages for invalidation queue */
438 	if (ddi_dma_mem_alloc(qinv->qinv_table.qinv_mem_dma_hdl,
439 	    size,
440 	    &qinv_acc_attr,
441 	    DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
442 	    DDI_DMA_SLEEP,
443 	    NULL,
444 	    &(qinv->qinv_table.qinv_mem_vaddr),
445 	    &size,
446 	    &(qinv->qinv_table.qinv_mem_acc_hdl)) != DDI_SUCCESS) {
447 		ddi_err(DER_WARN, root_devinfo,
448 		    "alloc invalidation queue table failed");
449 		goto queue_table_mem_failed;
450 	}
451 
452 	ASSERT(!((uintptr_t)qinv->qinv_table.qinv_mem_vaddr & MMU_PAGEOFFSET));
453 	bzero(qinv->qinv_table.qinv_mem_vaddr, size);
454 
455 	/* get the base physical address of invalidation request queue */
456 	qinv->qinv_table.qinv_mem_paddr = pfn_to_pa(
457 	    hat_getpfnum(kas.a_hat, qinv->qinv_table.qinv_mem_vaddr));
458 
459 	qinv->qinv_table.qinv_mem_head = qinv->qinv_table.qinv_mem_tail = 0;
460 
461 	qinv->qinv_sync.qinv_mem_size = qinv->qinv_table.qinv_mem_size;
462 	size = qinv->qinv_sync.qinv_mem_size * QINV_SYNC_DATA_SIZE;
463 
464 	/* alloc status memory for invalidation wait descriptor */
465 	if (ddi_dma_mem_alloc(qinv->qinv_sync.qinv_mem_dma_hdl,
466 	    size,
467 	    &qinv_acc_attr,
468 	    DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED,
469 	    DDI_DMA_SLEEP,
470 	    NULL,
471 	    &(qinv->qinv_sync.qinv_mem_vaddr),
472 	    &size,
473 	    &(qinv->qinv_sync.qinv_mem_acc_hdl)) != DDI_SUCCESS) {
474 		ddi_err(DER_WARN, root_devinfo,
475 		    "alloc invalidation queue sync mem failed");
476 		goto sync_table_mem_failed;
477 	}
478 
479 	ASSERT(!((uintptr_t)qinv->qinv_sync.qinv_mem_vaddr & MMU_PAGEOFFSET));
480 	bzero(qinv->qinv_sync.qinv_mem_vaddr, size);
481 	qinv->qinv_sync.qinv_mem_paddr = pfn_to_pa(
482 	    hat_getpfnum(kas.a_hat, qinv->qinv_sync.qinv_mem_vaddr));
483 
484 	qinv->qinv_sync.qinv_mem_head = qinv->qinv_sync.qinv_mem_tail = 0;
485 
486 	mutex_init(&(qinv->qinv_table.qinv_mem_lock), NULL, MUTEX_DRIVER, NULL);
487 	mutex_init(&(qinv->qinv_sync.qinv_mem_lock), NULL, MUTEX_DRIVER, NULL);
488 
489 	immu->immu_qinv = qinv;
490 
491 	mutex_exit(&(immu->immu_qinv_lock));
492 
493 	return (DDI_SUCCESS);
494 
495 sync_table_mem_failed:
496 	ddi_dma_mem_free(&(qinv->qinv_table.qinv_mem_acc_hdl));
497 
498 queue_table_mem_failed:
499 	ddi_dma_free_handle(&(qinv->qinv_sync.qinv_mem_dma_hdl));
500 
501 sync_table_handle_failed:
502 	ddi_dma_free_handle(&(qinv->qinv_table.qinv_mem_dma_hdl));
503 
504 queue_table_handle_failed:
505 	kmem_free(qinv, sizeof (qinv_t));
506 
507 	mutex_exit(&(immu->immu_qinv_lock));
508 
509 	return (DDI_FAILURE);
510 }
511 
512 /*
513  * ###########################################################################
514  *
515  * Functions exported by immu_qinv.c
516  *
517  * ###########################################################################
518  */
519 
520 /*
521  * initialize invalidation request queue structure.
522  */
523 int
524 immu_qinv_setup(list_t *listp)
525 {
526 	immu_t *immu;
527 	int nerr;
528 
529 	if (immu_qinv_enable == B_FALSE) {
530 		return (DDI_FAILURE);
531 	}
532 
533 	nerr = 0;
534 	immu = list_head(listp);
535 	for (; immu; immu = list_next(listp, immu)) {
536 		if (qinv_setup(immu) == DDI_SUCCESS) {
537 			immu->immu_qinv_setup = B_TRUE;
538 		} else {
539 			nerr++;
540 			break;
541 		}
542 	}
543 
544 	return (nerr > 0 ? DDI_FAILURE : DDI_SUCCESS);
545 }
546 
547 void
548 immu_qinv_startup(immu_t *immu)
549 {
550 	qinv_t *qinv;
551 	uint64_t qinv_reg_value;
552 
553 	if (immu->immu_qinv_setup == B_FALSE) {
554 		return;
555 	}
556 
557 	qinv = (qinv_t *)immu->immu_qinv;
558 	qinv_reg_value = qinv->qinv_table.qinv_mem_paddr | qinv_iqa_qs;
559 	immu_regs_qinv_enable(immu, qinv_reg_value);
560 	immu->immu_flushops = &immu_qinv_flushops;
561 	immu->immu_qinv_running = B_TRUE;
562 }
563 
564 /*
565  * queued invalidation interface
566  *   function based context cache invalidation
567  */
568 void
569 immu_qinv_context_fsi(immu_t *immu, uint8_t function_mask,
570     uint16_t source_id, uint_t domain_id, immu_inv_wait_t *iwp)
571 {
572 	qinv_context_common(immu, function_mask, source_id,
573 	    domain_id, CTT_INV_G_DEVICE);
574 	qinv_wait_sync(immu, iwp);
575 }
576 
577 /*
578  * queued invalidation interface
579  *   domain based context cache invalidation
580  */
581 void
582 immu_qinv_context_dsi(immu_t *immu, uint_t domain_id, immu_inv_wait_t *iwp)
583 {
584 	qinv_context_common(immu, 0, 0, domain_id, CTT_INV_G_DOMAIN);
585 	qinv_wait_sync(immu, iwp);
586 }
587 
588 /*
589  * queued invalidation interface
590  *   invalidation global context cache
591  */
592 void
593 immu_qinv_context_gbl(immu_t *immu, immu_inv_wait_t *iwp)
594 {
595 	qinv_context_common(immu, 0, 0, 0, CTT_INV_G_GLOBAL);
596 	qinv_wait_sync(immu, iwp);
597 }
598 
599 /*
600  * queued invalidation interface
601  *   paged based iotlb invalidation
602  */
603 void
604 immu_qinv_iotlb_psi(immu_t *immu, uint_t domain_id,
605 	uint64_t dvma, uint_t count, uint_t hint, immu_inv_wait_t *iwp)
606 {
607 	uint_t am = 0;
608 	uint_t max_am;
609 
610 	max_am = IMMU_CAP_GET_MAMV(immu->immu_regs_cap);
611 
612 	/* choose page specified invalidation */
613 	if (IMMU_CAP_GET_PSI(immu->immu_regs_cap)) {
614 		while (am <= max_am) {
615 			if ((ADDR_AM_OFFSET(IMMU_BTOP(dvma), am) + count)
616 			    <= ADDR_AM_MAX(am)) {
617 				qinv_iotlb_common(immu, domain_id,
618 				    dvma, am, hint, TLB_INV_G_PAGE);
619 				break;
620 			}
621 			am++;
622 		}
623 		if (am > max_am) {
624 			qinv_iotlb_common(immu, domain_id,
625 			    dvma, 0, hint, TLB_INV_G_DOMAIN);
626 		}
627 
628 	/* choose domain invalidation */
629 	} else {
630 		qinv_iotlb_common(immu, domain_id, dvma,
631 		    0, hint, TLB_INV_G_DOMAIN);
632 	}
633 
634 	qinv_wait_sync(immu, iwp);
635 }
636 
637 /*
638  * queued invalidation interface
639  *   domain based iotlb invalidation
640  */
641 void
642 immu_qinv_iotlb_dsi(immu_t *immu, uint_t domain_id, immu_inv_wait_t *iwp)
643 {
644 	qinv_iotlb_common(immu, domain_id, 0, 0, 0, TLB_INV_G_DOMAIN);
645 	qinv_wait_sync(immu, iwp);
646 }
647 
648 /*
649  * queued invalidation interface
650  *    global iotlb invalidation
651  */
652 void
653 immu_qinv_iotlb_gbl(immu_t *immu, immu_inv_wait_t *iwp)
654 {
655 	qinv_iotlb_common(immu, 0, 0, 0, 0, TLB_INV_G_GLOBAL);
656 	qinv_wait_sync(immu, iwp);
657 }
658 
659 /* queued invalidation interface -- global invalidate interrupt entry cache */
660 void
661 immu_qinv_intr_global(immu_t *immu, immu_inv_wait_t *iwp)
662 {
663 	qinv_iec_common(immu, 0, 0, IEC_INV_GLOBAL);
664 	qinv_wait_sync(immu, iwp);
665 }
666 
667 /* queued invalidation interface -- invalidate single interrupt entry cache */
668 void
669 immu_qinv_intr_one_cache(immu_t *immu, uint_t iidx, immu_inv_wait_t *iwp)
670 {
671 	qinv_iec_common(immu, iidx, 0, IEC_INV_INDEX);
672 	qinv_wait_sync(immu, iwp);
673 }
674 
675 /* queued invalidation interface -- invalidate interrupt entry caches */
676 void
677 immu_qinv_intr_caches(immu_t *immu, uint_t iidx, uint_t cnt,
678     immu_inv_wait_t *iwp)
679 {
680 	uint_t	i, mask = 0;
681 
682 	ASSERT(cnt != 0);
683 
684 	/* requested interrupt count is not a power of 2 */
685 	if (!ISP2(cnt)) {
686 		for (i = 0; i < cnt; i++) {
687 			qinv_iec_common(immu, iidx + cnt, 0, IEC_INV_INDEX);
688 		}
689 		qinv_wait_sync(immu, iwp);
690 		return;
691 	}
692 
693 	while ((2 << mask) < cnt) {
694 		mask++;
695 	}
696 
697 	if (mask > IMMU_ECAP_GET_MHMV(immu->immu_regs_excap)) {
698 		for (i = 0; i < cnt; i++) {
699 			qinv_iec_common(immu, iidx + cnt, 0, IEC_INV_INDEX);
700 		}
701 		qinv_wait_sync(immu, iwp);
702 		return;
703 	}
704 
705 	qinv_iec_common(immu, iidx, mask, IEC_INV_INDEX);
706 
707 	qinv_wait_sync(immu, iwp);
708 }
709 
710 void
711 immu_qinv_report_fault(immu_t *immu)
712 {
713 	uint16_t head;
714 	qinv_dsc_t *dsc;
715 	qinv_t *qinv;
716 
717 	/* access qinv data */
718 	mutex_enter(&(immu->immu_qinv_lock));
719 
720 	qinv = (qinv_t *)(immu->immu_qinv);
721 
722 	head = QINV_IQA_HEAD(
723 	    immu_regs_get64(immu, IMMU_REG_INVAL_QH));
724 
725 	dsc = (qinv_dsc_t *)(qinv->qinv_table.qinv_mem_vaddr
726 	    + (head * QINV_ENTRY_SIZE));
727 
728 	/* report the error */
729 	ddi_err(DER_WARN, immu->immu_dip,
730 	    "generated a fault when fetching a descriptor from the"
731 	    "\tinvalidation queue, or detects that the fetched"
732 	    "\tdescriptor is invalid. The head register is "
733 	    "0x%" PRIx64
734 	    "\tthe type is %s",
735 	    head,
736 	    qinv_dsc_type[MIN(INV_DSC_TYPE(dsc), QINV_MAX_DSC_TYPE)]);
737 
738 	mutex_exit(&(immu->immu_qinv_lock));
739 }
740