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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
30 * PCI Express nexus DVMA and DMA core routines:
31 * dma_map/dma_bind_handle implementation
32 * bypass and peer-to-peer support
33 * fast track DVMA space allocation
36 #include <sys/types.h>
38 #include <sys/async.h>
39 #include <sys/sysmacros.h>
40 #include <sys/sunddi.h>
41 #include <sys/ddi_impldefs.h>
47 * px_dma_allocmp - Allocate a pci dma implementation structure
49 * An extra ddi_dma_attr structure is bundled with the usual ddi_dma_impl
50 * to hold unmodified device limits. The ddi_dma_attr inside the
51 * ddi_dma_impl structure is augumented with system limits to enhance
52 * DVMA performance at runtime. The unaugumented device limits saved
53 * right after (accessed through (ddi_dma_attr_t *)(mp + 1)) is used
54 * strictly for peer-to-peer transfers which do not obey system limits.
56 * return: DDI_SUCCESS DDI_DMA_NORESOURCES
59 px_dma_allocmp(dev_info_t
*dip
, dev_info_t
*rdip
, int (*waitfp
)(caddr_t
),
62 register ddi_dma_impl_t
*mp
;
63 int sleep
= (waitfp
== DDI_DMA_SLEEP
) ? KM_SLEEP
: KM_NOSLEEP
;
65 /* Caution: we don't use zalloc to enhance performance! */
66 if ((mp
= kmem_alloc(sizeof (px_dma_hdl_t
), sleep
)) == 0) {
67 DBG(DBG_DMA_MAP
, dip
, "can't alloc dma_handle\n");
68 if (waitfp
!= DDI_DMA_DONTWAIT
) {
69 DBG(DBG_DMA_MAP
, dip
, "alloc_mp kmem cb\n");
70 ddi_set_callback(waitfp
, arg
, &px_kmem_clid
);
77 mp
->dmai_pfnlst
= NULL
;
78 mp
->dmai_winlst
= NULL
;
81 * kmem_alloc debug: the following fields are not zero-ed
82 * mp->dmai_mapping = 0;
84 * mp->dmai_offset = 0;
85 * mp->dmai_minxfer = 0;
86 * mp->dmai_burstsizes = 0;
87 * mp->dmai_ndvmapages = 0;
88 * mp->dmai_pool/roffset = 0;
89 * mp->dmai_rflags = 0;
90 * mp->dmai_inuse/flags
92 * mp->dmai_winsize = 0;
93 * mp->dmai_nexus_private/tte = 0;
94 * mp->dmai_iopte/pfnlst
95 * mp->dmai_sbi/pfn0 = 0;
96 * mp->dmai_minfo/winlst/fdvma
98 * bzero(&mp->dmai_object, sizeof (ddi_dma_obj_t));
99 * bzero(&mp->dmai_attr, sizeof (ddi_dma_attr_t));
100 * mp->dmai_cookie = 0;
103 mp
->dmai_attr
.dma_attr_version
= (uint_t
)DMA_ATTR_VERSION
;
104 mp
->dmai_attr
.dma_attr_flags
= (uint_t
)0;
106 mp
->dmai_fault_check
= NULL
;
107 mp
->dmai_fault_notify
= NULL
;
109 mp
->dmai_error
.err_ena
= 0;
110 mp
->dmai_error
.err_status
= DDI_FM_OK
;
111 mp
->dmai_error
.err_expected
= DDI_FM_ERR_UNEXPECTED
;
112 mp
->dmai_error
.err_ontrap
= NULL
;
113 mp
->dmai_error
.err_fep
= NULL
;
114 mp
->dmai_error
.err_cf
= NULL
;
117 * The bdf protection value is set to immediate child
118 * at first. It gets modified by switch/bridge drivers
119 * as the code traverses down the fabric topology.
121 * XXX No IOMMU protection for broken devices.
123 ASSERT((intptr_t)ddi_get_parent_data(rdip
) >> 1 == 0);
124 mp
->dmai_bdf
= ((intptr_t)ddi_get_parent_data(rdip
) == 1) ?
125 PCIE_INVALID_BDF
: pcie_get_bdf_for_dma_xfer(dip
, rdip
);
127 ndi_fmc_insert(rdip
, DMA_HANDLE
, mp
, NULL
);
132 px_dma_freemp(ddi_dma_impl_t
*mp
)
134 ndi_fmc_remove(mp
->dmai_rdip
, DMA_HANDLE
, mp
);
135 if (mp
->dmai_ndvmapages
> 1)
139 kmem_free(mp
, sizeof (px_dma_hdl_t
));
143 px_dma_freepfn(ddi_dma_impl_t
*mp
)
145 void *addr
= mp
->dmai_pfnlst
;
147 size_t npages
= mp
->dmai_ndvmapages
;
149 kmem_free(addr
, npages
* sizeof (px_iopfn_t
));
150 mp
->dmai_pfnlst
= NULL
;
152 mp
->dmai_ndvmapages
= 0;
156 * px_dma_lmts2hdl - alloate a ddi_dma_impl_t, validate practical limits
157 * and convert dmareq->dmar_limits to mp->dmai_attr
159 * ddi_dma_impl_t member modified input
160 * ------------------------------------------------------------------------
161 * mp->dmai_minxfer - dev
162 * mp->dmai_burstsizes - dev
163 * mp->dmai_flags - no limit? peer-to-peer only?
165 * ddi_dma_attr member modified input
166 * ------------------------------------------------------------------------
167 * mp->dmai_attr.dma_attr_addr_lo - dev lo, sys lo
168 * mp->dmai_attr.dma_attr_addr_hi - dev hi, sys hi
169 * mp->dmai_attr.dma_attr_count_max - dev count max, dev/sys lo/hi delta
170 * mp->dmai_attr.dma_attr_seg - 0 (no nocross restriction)
171 * mp->dmai_attr.dma_attr_align - 1 (no alignment restriction)
173 * The dlim_dmaspeed member of dmareq->dmar_limits is ignored.
176 px_dma_lmts2hdl(dev_info_t
*dip
, dev_info_t
*rdip
, px_mmu_t
*mmu_p
,
177 ddi_dma_req_t
*dmareq
)
180 ddi_dma_attr_t
*attr_p
;
181 uint64_t syslo
= mmu_p
->mmu_dvma_base
;
182 uint64_t syshi
= mmu_p
->mmu_dvma_end
;
183 uint64_t fasthi
= mmu_p
->mmu_dvma_fast_end
;
184 ddi_dma_lim_t
*lim_p
= dmareq
->dmar_limits
;
185 uint32_t count_max
= lim_p
->dlim_cntr_max
;
186 uint64_t lo
= lim_p
->dlim_addr_lo
;
187 uint64_t hi
= lim_p
->dlim_addr_hi
;
189 DBG(DBG_DMA_MAP
, dip
, "Bad limits\n");
190 return ((ddi_dma_impl_t
*)DDI_DMA_NOMAPPING
);
195 if (!(mp
= px_dma_allocmp(dip
, rdip
, dmareq
->dmar_fp
,
199 /* store original dev input at the 2nd ddi_dma_attr */
200 attr_p
= PX_DEV_ATTR(mp
);
201 SET_DMAATTR(attr_p
, lo
, hi
, -1, count_max
);
202 SET_DMAALIGN(attr_p
, 1);
207 mp
->dmai_flags
|= PX_DMAI_FLAGS_PEER_ONLY
;
208 count_max
= MIN(count_max
, hi
- lo
);
210 if (PX_DEV_NOSYSLIMIT(lo
, hi
, syslo
, fasthi
, 1))
211 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOFASTLIMIT
|
212 PX_DMAI_FLAGS_NOSYSLIMIT
;
214 if (PX_DEV_NOFASTLIMIT(lo
, hi
, syslo
, syshi
, 1))
215 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOFASTLIMIT
;
217 if (PX_DMA_NOCTX(rdip
))
218 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOCTX
;
220 /* store augumented dev input to mp->dmai_attr */
221 mp
->dmai_burstsizes
= lim_p
->dlim_burstsizes
;
222 attr_p
= &mp
->dmai_attr
;
223 SET_DMAATTR(attr_p
, lo
, hi
, -1, count_max
);
224 SET_DMAALIGN(attr_p
, 1);
229 * Called from px_attach to check for bypass dma support and set
233 px_dma_attach(px_t
*px_p
)
237 if (px_lib_iommu_getbypass(px_p
->px_dip
, 0ull,
238 PCI_MAP_ATTR_WRITE
|PCI_MAP_ATTR_READ
,
239 &baddr
) != DDI_ENOTSUP
)
240 /* ignore all other errors */
241 px_p
->px_dev_caps
|= PX_BYPASS_DMA_ALLOWED
;
243 px_p
->px_dma_sync_opt
= ddi_prop_get_int(DDI_DEV_T_ANY
,
244 px_p
->px_dip
, DDI_PROP_DONTPASS
, "dma-sync-options", 0);
246 if (px_p
->px_dma_sync_opt
!= 0)
247 px_p
->px_dev_caps
|= PX_DMA_SYNC_REQUIRED
;
249 return (DDI_SUCCESS
);
255 * This routine is called from the alloc handle entry point to sanity check the
256 * dma attribute structure.
258 * use by: px_dma_allochdl()
262 * DDI_SUCCESS - on success
263 * DDI_DMA_BADATTR - attribute has invalid version number
264 * or address limits exclude dvma space
267 px_dma_attr2hdl(px_t
*px_p
, ddi_dma_impl_t
*mp
)
269 px_mmu_t
*mmu_p
= px_p
->px_mmu_p
;
270 uint64_t syslo
, syshi
;
272 ddi_dma_attr_t
*attrp
= PX_DEV_ATTR(mp
);
273 uint64_t hi
= attrp
->dma_attr_addr_hi
;
274 uint64_t lo
= attrp
->dma_attr_addr_lo
;
275 uint64_t align
= attrp
->dma_attr_align
;
276 uint64_t nocross
= attrp
->dma_attr_seg
;
277 uint64_t count_max
= attrp
->dma_attr_count_max
;
279 DBG(DBG_DMA_ALLOCH
, px_p
->px_dip
, "attrp=%p cntr_max=%x.%08x\n",
280 attrp
, HI32(count_max
), LO32(count_max
));
281 DBG(DBG_DMA_ALLOCH
, px_p
->px_dip
, "hi=%x.%08x lo=%x.%08x\n",
282 HI32(hi
), LO32(hi
), HI32(lo
), LO32(lo
));
283 DBG(DBG_DMA_ALLOCH
, px_p
->px_dip
, "seg=%x.%08x align=%x.%08x\n",
284 HI32(nocross
), LO32(nocross
), HI32(align
), LO32(align
));
288 if (attrp
->dma_attr_flags
& DDI_DMA_FORCE_PHYSICAL
) { /* BYPASS */
290 DBG(DBG_DMA_ALLOCH
, px_p
->px_dip
, "bypass mode\n");
292 * If Bypass DMA is not supported, return error so that
293 * target driver can fall back to dvma mode of operation
295 if (!(px_p
->px_dev_caps
& PX_BYPASS_DMA_ALLOWED
))
296 return (DDI_DMA_BADATTR
);
297 mp
->dmai_flags
|= PX_DMAI_FLAGS_BYPASSREQ
;
298 if (nocross
!= UINT64_MAX
)
299 return (DDI_DMA_BADATTR
);
300 if (align
&& (align
> MMU_PAGE_SIZE
))
301 return (DDI_DMA_BADATTR
);
302 align
= 1; /* align on 1 page boundary */
304 /* do a range check and get the limits */
305 ret
= px_lib_dma_bypass_rngchk(px_p
->px_dip
, attrp
,
307 if (ret
!= DDI_SUCCESS
)
309 } else { /* MMU_XLATE or PEER_TO_PEER */
310 align
= MAX(align
, MMU_PAGE_SIZE
) - 1;
311 if ((align
& nocross
) != align
) {
312 dev_info_t
*rdip
= mp
->dmai_rdip
;
313 cmn_err(CE_WARN
, "%s%d dma_attr_seg not aligned",
315 return (DDI_DMA_BADATTR
);
317 align
= MMU_BTOP(align
+ 1);
318 syslo
= mmu_p
->mmu_dvma_base
;
319 syshi
= mmu_p
->mmu_dvma_end
;
322 dev_info_t
*rdip
= mp
->dmai_rdip
;
323 cmn_err(CE_WARN
, "%s%d limits out of range", NAMEINST(rdip
));
324 return (DDI_DMA_BADATTR
);
331 DBG(DBG_DMA_ALLOCH
, px_p
->px_dip
, "hi=%x.%08x, lo=%x.%08x\n",
332 HI32(hi
), LO32(hi
), HI32(lo
), LO32(lo
));
335 * If this is an IOMMU bypass access, the caller can't use
336 * the required addresses, so fail it. Otherwise, it's
337 * peer-to-peer; ensure that the caller has no alignment or
338 * segment size restrictions.
340 if ((mp
->dmai_flags
& PX_DMAI_FLAGS_BYPASSREQ
) ||
341 (nocross
< UINT32_MAX
) || (align
> 1))
342 return (DDI_DMA_BADATTR
);
344 mp
->dmai_flags
|= PX_DMAI_FLAGS_PEER_ONLY
;
345 } else /* set practical counter_max value */
346 count_max
= MIN(count_max
, hi
- lo
);
348 if (PX_DEV_NOSYSLIMIT(lo
, hi
, syslo
, syshi
, align
))
349 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOSYSLIMIT
|
350 PX_DMAI_FLAGS_NOFASTLIMIT
;
352 syshi
= mmu_p
->mmu_dvma_fast_end
;
353 if (PX_DEV_NOFASTLIMIT(lo
, hi
, syslo
, syshi
, align
))
354 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOFASTLIMIT
;
356 if (PX_DMA_NOCTX(mp
->dmai_rdip
))
357 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOCTX
;
359 mp
->dmai_burstsizes
= attrp
->dma_attr_burstsizes
;
360 attrp
= &mp
->dmai_attr
;
361 SET_DMAATTR(attrp
, lo
, hi
, nocross
, count_max
);
362 return (DDI_SUCCESS
);
365 #define TGT_PFN_INBETWEEN(pfn, bgn, end) ((pfn >= bgn) && (pfn <= end))
368 * px_dma_type - determine which of the three types DMA (peer-to-peer,
369 * mmu bypass, or mmu translate) we are asked to do.
370 * Also checks pfn0 and rejects any non-peer-to-peer
371 * requests for peer-only devices.
374 * DDI_DMA_NOMAPPING - can't get valid pfn0, or bad dma type
377 * dma handle members affected (set on exit):
378 * mp->dmai_object - dmareq->dmar_object
379 * mp->dmai_rflags - consistent?, nosync?, dmareq->dmar_flags
380 * mp->dmai_flags - DMA type
381 * mp->dmai_pfn0 - 1st page pfn (if va/size pair and not shadow)
382 * mp->dmai_roffset - initialized to starting MMU page offset
383 * mp->dmai_ndvmapages - # of total MMU pages of entire object
386 px_dma_type(px_t
*px_p
, ddi_dma_req_t
*dmareq
, ddi_dma_impl_t
*mp
)
388 dev_info_t
*dip
= px_p
->px_dip
;
389 ddi_dma_obj_t
*dobj_p
= &dmareq
->dmar_object
;
390 px_pec_t
*pec_p
= px_p
->px_pec_p
;
395 mp
->dmai_rflags
= dmareq
->dmar_flags
& DMP_DDIFLAGS
;
397 if (!(px_p
->px_dev_caps
& PX_DMA_SYNC_REQUIRED
))
398 mp
->dmai_rflags
|= DMP_NOSYNC
;
400 switch (dobj_p
->dmao_type
) {
401 case DMA_OTYP_BUFVADDR
:
402 case DMA_OTYP_VADDR
: {
403 page_t
**pplist
= dobj_p
->dmao_obj
.virt_obj
.v_priv
;
404 caddr_t vaddr
= dobj_p
->dmao_obj
.virt_obj
.v_addr
;
406 DBG(DBG_DMA_MAP
, dip
, "vaddr=%p pplist=%p\n", vaddr
, pplist
);
407 offset
= (ulong_t
)vaddr
& MMU_PAGE_OFFSET
;
408 if (pplist
) { /* shadow list */
409 mp
->dmai_flags
|= PX_DMAI_FLAGS_PGPFN
;
410 pfn0
= page_pptonum(*pplist
);
412 struct as
*as_p
= dobj_p
->dmao_obj
.virt_obj
.v_as
;
413 struct hat
*hat_p
= as_p
? as_p
->a_hat
: kas
.a_hat
;
414 pfn0
= hat_getpfnum(hat_p
, vaddr
);
420 offset
= dobj_p
->dmao_obj
.pp_obj
.pp_offset
;
421 mp
->dmai_flags
|= PX_DMAI_FLAGS_PGPFN
;
422 pfn0
= page_pptonum(dobj_p
->dmao_obj
.pp_obj
.pp_pp
);
427 cmn_err(CE_WARN
, "%s%d requested unsupported dma type %x",
428 NAMEINST(mp
->dmai_rdip
), dobj_p
->dmao_type
);
429 return (DDI_DMA_NOMAPPING
);
431 if (pfn0
== PFN_INVALID
) {
432 cmn_err(CE_WARN
, "%s%d: invalid pfn0 for DMA object %p",
433 NAMEINST(dip
), dobj_p
);
434 return (DDI_DMA_NOMAPPING
);
436 if (TGT_PFN_INBETWEEN(pfn0
, pec_p
->pec_base32_pfn
,
437 pec_p
->pec_last32_pfn
)) {
438 mp
->dmai_flags
|= PX_DMAI_FLAGS_PTP
|PX_DMAI_FLAGS_PTP32
;
439 goto done
; /* leave bypass and dvma flag as 0 */
440 } else if (TGT_PFN_INBETWEEN(pfn0
, pec_p
->pec_base64_pfn
,
441 pec_p
->pec_last64_pfn
)) {
442 mp
->dmai_flags
|= PX_DMAI_FLAGS_PTP
|PX_DMAI_FLAGS_PTP64
;
443 goto done
; /* leave bypass and dvma flag as 0 */
445 if (PX_DMA_ISPEERONLY(mp
)) {
446 dev_info_t
*rdip
= mp
->dmai_rdip
;
447 cmn_err(CE_WARN
, "Bad peer-to-peer req %s%d", NAMEINST(rdip
));
448 return (DDI_DMA_NOMAPPING
);
451 redzone
= (mp
->dmai_rflags
& DDI_DMA_REDZONE
) ||
452 (mp
->dmai_flags
& PX_DMAI_FLAGS_MAP_BUFZONE
) ?
453 PX_DMAI_FLAGS_REDZONE
: 0;
455 mp
->dmai_flags
|= (mp
->dmai_flags
& PX_DMAI_FLAGS_BYPASSREQ
) ?
456 PX_DMAI_FLAGS_BYPASS
: (PX_DMAI_FLAGS_DVMA
| redzone
);
458 mp
->dmai_object
= *dobj_p
; /* whole object */
459 mp
->dmai_pfn0
= (void *)pfn0
; /* cache pfn0 */
460 mp
->dmai_roffset
= offset
; /* win0 pg0 offset */
461 mp
->dmai_ndvmapages
= MMU_BTOPR(offset
+ mp
->dmai_object
.dmao_size
);
462 return (DDI_SUCCESS
);
466 * px_dma_pgpfn - set up pfnlst array according to pages
467 * VA/size pair: <shadow IO, bypass, peer-to-peer>, or OTYP_PAGES
471 px_dma_pgpfn(px_t
*px_p
, ddi_dma_impl_t
*mp
, uint_t npages
)
474 dev_info_t
*dip
= px_p
->px_dip
;
476 switch (mp
->dmai_object
.dmao_type
) {
477 case DMA_OTYP_BUFVADDR
:
478 case DMA_OTYP_VADDR
: {
479 page_t
**pplist
= mp
->dmai_object
.dmao_obj
.virt_obj
.v_priv
;
480 DBG(DBG_DMA_MAP
, dip
, "shadow pplist=%p, %x pages, pfns=",
482 for (i
= 1; i
< npages
; i
++) {
483 px_iopfn_t pfn
= page_pptonum(pplist
[i
]);
484 PX_SET_MP_PFN1(mp
, i
, pfn
);
485 DBG(DBG_DMA_MAP
|DBG_CONT
, dip
, "%x ", pfn
);
487 DBG(DBG_DMA_MAP
|DBG_CONT
, dip
, "\n");
491 case DMA_OTYP_PAGES
: {
492 page_t
*pp
= mp
->dmai_object
.dmao_obj
.pp_obj
.pp_pp
->p_next
;
493 DBG(DBG_DMA_MAP
, dip
, "pp=%p pfns=", pp
);
494 for (i
= 1; i
< npages
; i
++, pp
= pp
->p_next
) {
495 px_iopfn_t pfn
= page_pptonum(pp
);
496 PX_SET_MP_PFN1(mp
, i
, pfn
);
497 DBG(DBG_DMA_MAP
|DBG_CONT
, dip
, "%x ", pfn
);
499 DBG(DBG_DMA_MAP
|DBG_CONT
, dip
, "\n");
503 default: /* check is already done by px_dma_type */
507 return (DDI_SUCCESS
);
511 * px_dma_vapfn - set up pfnlst array according to VA
512 * VA/size pair: <normal, bypass, peer-to-peer>
513 * pfn0 is skipped as it is already done.
514 * In this case, the cached pfn0 is used to fill pfnlst[0]
517 px_dma_vapfn(px_t
*px_p
, ddi_dma_impl_t
*mp
, uint_t npages
)
519 dev_info_t
*dip
= px_p
->px_dip
;
521 caddr_t vaddr
= (caddr_t
)mp
->dmai_object
.dmao_obj
.virt_obj
.v_as
;
522 struct hat
*hat_p
= vaddr
? ((struct as
*)vaddr
)->a_hat
: kas
.a_hat
;
524 vaddr
= mp
->dmai_object
.dmao_obj
.virt_obj
.v_addr
+ MMU_PAGE_SIZE
;
525 for (i
= 1; i
< npages
; i
++, vaddr
+= MMU_PAGE_SIZE
) {
526 px_iopfn_t pfn
= hat_getpfnum(hat_p
, vaddr
);
527 if (pfn
== PFN_INVALID
)
529 PX_SET_MP_PFN1(mp
, i
, pfn
);
530 DBG(DBG_DMA_BINDH
, dip
, "px_dma_vapfn: mp=%p pfnlst[%x]=%x\n",
533 return (DDI_SUCCESS
);
535 cmn_err(CE_WARN
, "%s%d: bad page frame vaddr=%p", NAMEINST(dip
), vaddr
);
536 return (DDI_DMA_NOMAPPING
);
540 * px_dma_pfn - Fills pfn list for all pages being DMA-ed.
543 * mp->dmai_ndvmapages - set to total # of dma pages
550 px_dma_pfn(px_t
*px_p
, ddi_dma_req_t
*dmareq
, ddi_dma_impl_t
*mp
)
552 uint32_t npages
= mp
->dmai_ndvmapages
;
553 int (*waitfp
)(caddr_t
) = dmareq
->dmar_fp
;
554 int i
, ret
, peer
= PX_DMA_ISPTP(mp
);
555 int peer32
= PX_DMA_ISPTP32(mp
);
556 dev_info_t
*dip
= px_p
->px_dip
;
558 px_pec_t
*pec_p
= px_p
->px_pec_p
;
559 px_iopfn_t pfn_base
= peer32
? pec_p
->pec_base32_pfn
:
560 pec_p
->pec_base64_pfn
;
561 px_iopfn_t pfn_last
= peer32
? pec_p
->pec_last32_pfn
:
562 pec_p
->pec_last64_pfn
;
563 px_iopfn_t pfn_adj
= peer
? pfn_base
: 0;
565 DBG(DBG_DMA_BINDH
, dip
, "px_dma_pfn: mp=%p pfn0=%x\n",
566 mp
, PX_MP_PFN0(mp
) - pfn_adj
);
567 /* 1 page: no array alloc/fill, no mixed mode check */
569 PX_SET_MP_PFN(mp
, 0, PX_MP_PFN0(mp
) - pfn_adj
);
570 return (DDI_SUCCESS
);
572 /* allocate pfn array */
573 if (!(mp
->dmai_pfnlst
= kmem_alloc(npages
* sizeof (px_iopfn_t
),
574 waitfp
== DDI_DMA_SLEEP
? KM_SLEEP
: KM_NOSLEEP
))) {
575 if (waitfp
!= DDI_DMA_DONTWAIT
)
576 ddi_set_callback(waitfp
, dmareq
->dmar_arg
,
578 return (DDI_DMA_NORESOURCES
);
581 PX_SET_MP_PFN(mp
, 0, PX_MP_PFN0(mp
) - pfn_adj
); /* pfnlst[0] */
582 if ((ret
= PX_DMA_ISPGPFN(mp
) ? px_dma_pgpfn(px_p
, mp
, npages
) :
583 px_dma_vapfn(px_p
, mp
, npages
)) != DDI_SUCCESS
)
586 /* skip pfn0, check mixed mode and adjust peer to peer pfn */
587 for (i
= 1; i
< npages
; i
++) {
588 px_iopfn_t pfn
= PX_GET_MP_PFN1(mp
, i
);
589 if (peer
^ TGT_PFN_INBETWEEN(pfn
, pfn_base
, pfn_last
)) {
590 cmn_err(CE_WARN
, "%s%d mixed mode DMA %lx %lx",
591 NAMEINST(mp
->dmai_rdip
), PX_MP_PFN0(mp
), pfn
);
592 ret
= DDI_DMA_NOMAPPING
; /* mixed mode */
595 DBG(DBG_DMA_MAP
, dip
,
596 "px_dma_pfn: pfnlst[%x]=%x-%x\n", i
, pfn
, pfn_adj
);
598 PX_SET_MP_PFN1(mp
, i
, pfn
- pfn_adj
);
600 return (DDI_SUCCESS
);
607 * px_dvma_win() - trim requested DVMA size down to window size
608 * The 1st window starts from offset and ends at page-aligned boundary.
609 * From the 2nd window on, each window starts and ends at page-aligned
610 * boundary except the last window ends at wherever requested.
612 * accesses the following mp-> members:
613 * mp->dmai_attr.dma_attr_count_max
614 * mp->dmai_attr.dma_attr_seg
615 * mp->dmai_roffset - start offset of 1st window
616 * mp->dmai_rflags (redzone)
617 * mp->dmai_ndvmapages (for 1 page fast path)
619 * sets the following mp-> members:
620 * mp->dmai_size - xfer size, != winsize if 1st/last win (not fixed)
621 * mp->dmai_winsize - window size (no redzone), n * page size (fixed)
622 * mp->dmai_nwin - # of DMA windows of entire object (fixed)
623 * mp->dmai_rflags - remove partial flag if nwin == 1 (fixed)
624 * mp->dmai_winlst - NULL, window objects not used for DVMA (fixed)
626 * fixed - not changed across different DMA windows
630 px_dvma_win(px_t
*px_p
, ddi_dma_req_t
*dmareq
, ddi_dma_impl_t
*mp
)
632 uint32_t redzone_sz
= PX_HAS_REDZONE(mp
) ? MMU_PAGE_SIZE
: 0;
633 size_t obj_sz
= mp
->dmai_object
.dmao_size
;
637 if ((mp
->dmai_ndvmapages
== 1) && !redzone_sz
) {
638 mp
->dmai_rflags
&= ~DDI_DMA_PARTIAL
;
639 mp
->dmai_size
= obj_sz
;
640 mp
->dmai_winsize
= MMU_PAGE_SIZE
;
645 pg_off
= mp
->dmai_roffset
;
646 xfer_sz
= obj_sz
+ redzone_sz
;
648 /* include redzone in nocross check */ {
649 uint64_t nocross
= mp
->dmai_attr
.dma_attr_seg
;
650 if (xfer_sz
+ pg_off
- 1 > nocross
)
651 xfer_sz
= nocross
- pg_off
+ 1;
652 if (redzone_sz
&& (xfer_sz
<= redzone_sz
)) {
653 DBG(DBG_DMA_MAP
, px_p
->px_dip
,
654 "nocross too small: "
655 "%lx(%lx)+%lx+%lx < %llx\n",
656 xfer_sz
, obj_sz
, pg_off
, redzone_sz
, nocross
);
657 return (DDI_DMA_TOOBIG
);
660 xfer_sz
-= redzone_sz
; /* restore transfer size */
661 /* check counter max */ {
662 uint32_t count_max
= mp
->dmai_attr
.dma_attr_count_max
;
663 if (xfer_sz
- 1 > count_max
)
664 xfer_sz
= count_max
+ 1;
666 if (xfer_sz
>= obj_sz
) {
667 mp
->dmai_rflags
&= ~DDI_DMA_PARTIAL
;
668 mp
->dmai_size
= xfer_sz
;
669 mp
->dmai_winsize
= P2ROUNDUP(xfer_sz
+ pg_off
, MMU_PAGE_SIZE
);
673 if (!(dmareq
->dmar_flags
& DDI_DMA_PARTIAL
)) {
674 DBG(DBG_DMA_MAP
, px_p
->px_dip
, "too big: %lx+%lx+%lx > %lx\n",
675 obj_sz
, pg_off
, redzone_sz
, xfer_sz
);
676 return (DDI_DMA_TOOBIG
);
679 xfer_sz
= MMU_PTOB(MMU_BTOP(xfer_sz
+ pg_off
)); /* page align */
680 mp
->dmai_size
= xfer_sz
- pg_off
; /* 1st window xferrable size */
681 mp
->dmai_winsize
= xfer_sz
; /* redzone not in winsize */
682 mp
->dmai_nwin
= (obj_sz
+ pg_off
+ xfer_sz
- 1) / xfer_sz
;
684 mp
->dmai_winlst
= NULL
;
685 px_dump_dma_handle(DBG_DMA_MAP
, px_p
->px_dip
, mp
);
686 return (DDI_SUCCESS
);
690 * fast track cache entry to mmu context, inserts 3 0 bits between
691 * upper 6-bits and lower 3-bits of the 9-bit cache entry
693 #define MMU_FCE_TO_CTX(i) (((i) << 3) | ((i) & 0x7) | 0x38)
696 * px_dvma_map_fast - attempts to map fast trackable DVMA
700 px_dvma_map_fast(px_mmu_t
*mmu_p
, ddi_dma_impl_t
*mp
)
702 uint_t clustsz
= px_dvma_page_cache_clustsz
;
703 uint_t entries
= px_dvma_page_cache_entries
;
704 io_attributes_t attr
= PX_GET_TTE_ATTR(mp
->dmai_rflags
,
705 mp
->dmai_attr
.dma_attr_flags
);
706 int i
= mmu_p
->mmu_dvma_addr_scan_start
;
707 uint8_t *lock_addr
= mmu_p
->mmu_dvma_cache_locks
+ i
;
708 px_dvma_addr_t dvma_pg
;
709 size_t npages
= MMU_BTOP(mp
->dmai_winsize
);
710 dev_info_t
*dip
= mmu_p
->mmu_px_p
->px_dip
;
712 extern uint8_t ldstub(uint8_t *);
713 ASSERT(MMU_PTOB(npages
) == mp
->dmai_winsize
);
714 ASSERT(npages
+ PX_HAS_REDZONE(mp
) <= clustsz
);
716 for (; i
< entries
&& ldstub(lock_addr
); i
++, lock_addr
++)
719 lock_addr
= mmu_p
->mmu_dvma_cache_locks
;
721 for (; i
< entries
&& ldstub(lock_addr
); i
++, lock_addr
++)
726 #endif /* PX_DMA_PROF */
727 return (DDI_DMA_NORESOURCES
);
730 mmu_p
->mmu_dvma_addr_scan_start
= (i
+ 1) & (entries
- 1);
733 dvma_pg
= mmu_p
->dvma_base_pg
+ i
;
735 if (px_lib_iommu_map(dip
, PCI_TSBID(0, i
), npages
,
736 PX_ADD_ATTR_EXTNS(attr
, mp
->dmai_bdf
), (void *)mp
, 0,
737 MMU_MAP_PFN
) != DDI_SUCCESS
) {
738 DBG(DBG_MAP_WIN
, dip
, "px_dvma_map_fast: "
739 "px_lib_iommu_map failed\n");
740 return (DDI_FAILURE
);
743 if (!PX_MAP_BUFZONE(mp
))
746 DBG(DBG_MAP_WIN
, dip
, "px_dvma_map_fast: redzone pg=%x\n", i
+ npages
);
748 ASSERT(PX_HAS_REDZONE(mp
));
750 if (px_lib_iommu_map(dip
, PCI_TSBID(0, i
+ npages
), 1,
751 PX_ADD_ATTR_EXTNS(attr
, mp
->dmai_bdf
), (void *)mp
, npages
- 1,
752 MMU_MAP_PFN
) != DDI_SUCCESS
) {
753 DBG(DBG_MAP_WIN
, dip
, "px_dvma_map_fast: "
754 "mapping REDZONE page failed\n");
756 (void) px_lib_iommu_demap(dip
, PCI_TSBID(0, i
), npages
);
757 return (DDI_FAILURE
);
764 mp
->dmai_mapping
= mp
->dmai_roffset
| MMU_PTOB(dvma_pg
);
766 mp
->dmai_flags
|= PX_DMAI_FLAGS_FASTTRACK
;
767 PX_SAVE_MP_TTE(mp
, attr
); /* save TTE template for unmapping */
768 if (PX_DVMA_DBG_ON(mmu_p
))
769 px_dvma_alloc_debug(mmu_p
, (char *)mp
->dmai_mapping
,
771 return (DDI_SUCCESS
);
775 * px_dvma_map: map non-fasttrack DMA
776 * Use quantum cache if single page DMA.
779 px_dvma_map(ddi_dma_impl_t
*mp
, ddi_dma_req_t
*dmareq
, px_mmu_t
*mmu_p
)
781 uint_t npages
= PX_DMA_WINNPGS(mp
);
782 px_dvma_addr_t dvma_pg
, dvma_pg_index
;
784 io_attributes_t attr
= PX_GET_TTE_ATTR(mp
->dmai_rflags
,
785 mp
->dmai_attr
.dma_attr_flags
);
786 int sleep
= dmareq
->dmar_fp
== DDI_DMA_SLEEP
? VM_SLEEP
: VM_NOSLEEP
;
787 dev_info_t
*dip
= mp
->dmai_rdip
;
788 int ret
= DDI_SUCCESS
;
791 * allocate dvma space resource and map in the first window.
792 * (vmem_t *vmp, size_t size,
793 * size_t align, size_t phase, size_t nocross,
794 * void *minaddr, void *maxaddr, int vmflag)
796 if ((npages
== 1) && !PX_HAS_REDZONE(mp
) && PX_HAS_NOSYSLIMIT(mp
)) {
797 dvma_addr
= vmem_alloc(mmu_p
->mmu_dvma_map
,
798 MMU_PAGE_SIZE
, sleep
);
799 mp
->dmai_flags
|= PX_DMAI_FLAGS_VMEMCACHE
;
801 px_dvma_vmem_alloc
++;
802 #endif /* PX_DMA_PROF */
804 dvma_addr
= vmem_xalloc(mmu_p
->mmu_dvma_map
,
805 MMU_PTOB(npages
+ PX_HAS_REDZONE(mp
)),
806 MAX(mp
->dmai_attr
.dma_attr_align
, MMU_PAGE_SIZE
),
808 mp
->dmai_attr
.dma_attr_seg
+ 1,
809 (void *)mp
->dmai_attr
.dma_attr_addr_lo
,
810 (void *)(mp
->dmai_attr
.dma_attr_addr_hi
+ 1),
813 px_dvma_vmem_xalloc
++;
814 #endif /* PX_DMA_PROF */
816 dvma_pg
= MMU_BTOP((ulong_t
)dvma_addr
);
817 dvma_pg_index
= dvma_pg
- mmu_p
->dvma_base_pg
;
818 DBG(DBG_DMA_MAP
, dip
, "fallback dvma_pages: dvma_pg=%x index=%x\n",
819 dvma_pg
, dvma_pg_index
);
823 mp
->dmai_mapping
= mp
->dmai_roffset
| MMU_PTOB(dvma_pg
);
825 PX_SAVE_MP_TTE(mp
, attr
); /* mp->dmai_tte = tte */
827 if ((ret
= px_mmu_map_pages(mmu_p
,
828 mp
, dvma_pg
, npages
, 0)) != DDI_SUCCESS
) {
829 if (mp
->dmai_flags
& PX_DMAI_FLAGS_VMEMCACHE
) {
830 vmem_free(mmu_p
->mmu_dvma_map
, (void *)dvma_addr
,
834 #endif /* PX_DMA_PROF */
836 vmem_xfree(mmu_p
->mmu_dvma_map
, (void *)dvma_addr
,
837 MMU_PTOB(npages
+ PX_HAS_REDZONE(mp
)));
839 px_dvma_vmem_xfree
++;
840 #endif /* PX_DMA_PROF */
846 if (dmareq
->dmar_fp
!= DDI_DMA_DONTWAIT
) {
847 DBG(DBG_DMA_MAP
, dip
, "dvma_pg 0 - set callback\n");
848 ddi_set_callback(dmareq
->dmar_fp
, dmareq
->dmar_arg
,
849 &mmu_p
->mmu_dvma_clid
);
851 DBG(DBG_DMA_MAP
, dip
, "vmem_xalloc - DDI_DMA_NORESOURCES\n");
852 return (DDI_DMA_NORESOURCES
);
856 px_dvma_unmap(px_mmu_t
*mmu_p
, ddi_dma_impl_t
*mp
)
858 px_dvma_addr_t dvma_addr
= (px_dvma_addr_t
)mp
->dmai_mapping
;
859 px_dvma_addr_t dvma_pg
= MMU_BTOP(dvma_addr
);
860 dvma_addr
= MMU_PTOB(dvma_pg
);
862 if (mp
->dmai_flags
& PX_DMAI_FLAGS_FASTTRACK
) {
863 px_iopfn_t index
= dvma_pg
- mmu_p
->dvma_base_pg
;
864 ASSERT(index
% px_dvma_page_cache_clustsz
== 0);
865 index
/= px_dvma_page_cache_clustsz
;
866 ASSERT(index
< px_dvma_page_cache_entries
);
867 mmu_p
->mmu_dvma_cache_locks
[index
] = 0;
870 #endif /* PX_DMA_PROF */
874 if (mp
->dmai_flags
& PX_DMAI_FLAGS_VMEMCACHE
) {
875 vmem_free(mmu_p
->mmu_dvma_map
, (void *)dvma_addr
,
879 #endif /* PX_DMA_PROF */
881 size_t npages
= MMU_BTOP(mp
->dmai_winsize
) + PX_HAS_REDZONE(mp
);
882 vmem_xfree(mmu_p
->mmu_dvma_map
, (void *)dvma_addr
,
885 px_dvma_vmem_xfree
++;
886 #endif /* PX_DMA_PROF */
891 * DVMA mappings may have multiple windows, but each window always have
895 px_dvma_ctl(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_dma_impl_t
*mp
,
896 enum ddi_dma_ctlops cmd
, off_t
*offp
, size_t *lenp
, caddr_t
*objp
,
901 DBG(DBG_DMA_CTL
, dip
, "unknown command (%x): rdip=%s%d\n",
902 cmd
, ddi_driver_name(rdip
), ddi_get_instance(rdip
));
905 return (DDI_FAILURE
);
909 px_dma_freewin(ddi_dma_impl_t
*mp
)
911 px_dma_win_t
*win_p
= mp
->dmai_winlst
, *win2_p
;
912 for (win2_p
= win_p
; win_p
; win2_p
= win_p
) {
913 win_p
= win2_p
->win_next
;
914 kmem_free(win2_p
, sizeof (px_dma_win_t
) +
915 sizeof (ddi_dma_cookie_t
) * win2_p
->win_ncookies
);
918 mp
->dmai_winlst
= NULL
;
922 * px_dma_newwin - create a dma window object and cookies
924 * After the initial scan in px_dma_physwin(), which identifies
925 * a portion of the pfn array that belongs to a dma window,
926 * we are called to allocate and initialize representing memory
927 * resources. We know from the 1st scan the number of cookies
928 * or dma segment in this window so we can allocate a contiguous
929 * memory array for the dma cookies (The implementation of
930 * ddi_dma_nextcookie(9f) dictates dma cookies be contiguous).
932 * A second round scan is done on the pfn array to identify
933 * each dma segment and initialize its corresponding dma cookie.
934 * We don't need to do all the safety checking and we know they
935 * all belong to the same dma window.
937 * Input: cookie_no - # of cookies identified by the 1st scan
938 * start_idx - subscript of the pfn array for the starting pfn
939 * end_idx - subscript of the last pfn in dma window
940 * win_pp - pointer to win_next member of previous window
941 * Return: DDI_SUCCESS - with **win_pp as newly created window object
942 * DDI_DMA_NORESROUCE - caller frees all previous window objs
943 * Note: Each cookie and window size are all initialized on page
944 * boundary. This is not true for the 1st cookie of the 1st
945 * window and the last cookie of the last window.
946 * We fix that later in upper layer which has access to size
952 px_dma_newwin(dev_info_t
*dip
, ddi_dma_req_t
*dmareq
, ddi_dma_impl_t
*mp
,
953 uint32_t cookie_no
, uint32_t start_idx
, uint32_t end_idx
,
954 px_dma_win_t
**win_pp
, uint64_t count_max
, uint64_t bypass
)
956 int (*waitfp
)(caddr_t
) = dmareq
->dmar_fp
;
957 ddi_dma_cookie_t
*cookie_p
;
959 px_iopfn_t pfn
= PX_GET_MP_PFN(mp
, start_idx
);
960 px_iopfn_t prev_pfn
= pfn
;
961 uint64_t baddr
, seg_pfn0
= pfn
;
962 size_t sz
= cookie_no
* sizeof (ddi_dma_cookie_t
);
963 px_dma_win_t
*win_p
= kmem_zalloc(sizeof (px_dma_win_t
) + sz
,
964 waitfp
== DDI_DMA_SLEEP
? KM_SLEEP
: KM_NOSLEEP
);
965 io_attributes_t attr
= PX_GET_TTE_ATTR(mp
->dmai_rflags
,
966 mp
->dmai_attr
.dma_attr_flags
);
971 win_p
->win_next
= NULL
;
972 win_p
->win_ncookies
= cookie_no
;
973 win_p
->win_curseg
= 0; /* start from segment 0 */
974 win_p
->win_size
= MMU_PTOB(end_idx
- start_idx
+ 1);
975 /* win_p->win_offset is left uninitialized */
977 cookie_p
= (ddi_dma_cookie_t
*)(win_p
+ 1);
979 for (; start_idx
<= end_idx
; start_idx
++, prev_pfn
= pfn
, pfn_no
++) {
980 pfn
= PX_GET_MP_PFN1(mp
, start_idx
);
981 if ((pfn
== prev_pfn
+ 1) &&
982 (MMU_PTOB(pfn_no
+ 1) - 1 <= count_max
))
985 /* close up the cookie up to (including) prev_pfn */
986 baddr
= MMU_PTOB(seg_pfn0
);
988 if (px_lib_iommu_getbypass(dip
, baddr
, attr
, &baddr
)
990 baddr
= px_lib_ro_bypass(dip
, attr
, baddr
);
992 return (DDI_FAILURE
);
995 MAKE_DMA_COOKIE(cookie_p
, baddr
, MMU_PTOB(pfn_no
));
996 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "cookie %p (%x pages)\n",
997 MMU_PTOB(seg_pfn0
), pfn_no
);
999 cookie_p
++; /* advance to next available cookie cell */
1001 seg_pfn0
= pfn
; /* start a new segment from current pfn */
1004 baddr
= MMU_PTOB(seg_pfn0
);
1006 if (px_lib_iommu_getbypass(dip
, baddr
, attr
, &baddr
)
1008 baddr
= px_lib_ro_bypass(dip
, attr
, baddr
);
1010 return (DDI_FAILURE
);
1013 MAKE_DMA_COOKIE(cookie_p
, baddr
, MMU_PTOB(pfn_no
));
1014 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "cookie %p (%x pages) of total %x\n",
1015 MMU_PTOB(seg_pfn0
), pfn_no
, cookie_no
);
1018 ASSERT((cookie_p
- (ddi_dma_cookie_t
*)(win_p
+ 1)) == cookie_no
);
1021 return (DDI_SUCCESS
);
1023 if (waitfp
!= DDI_DMA_DONTWAIT
)
1024 ddi_set_callback(waitfp
, dmareq
->dmar_arg
, &px_kmem_clid
);
1025 return (DDI_DMA_NORESOURCES
);
1029 * px_dma_adjust - adjust 1st and last cookie and window sizes
1030 * remove initial dma page offset from 1st cookie and window size
1031 * remove last dma page remainder from last cookie and window size
1032 * fill win_offset of each dma window according to just fixed up
1034 * px_dma_win_t members modified:
1035 * win_p->win_offset - this window's offset within entire DMA object
1036 * win_p->win_size - xferrable size (in bytes) for this window
1038 * ddi_dma_impl_t members modified:
1039 * mp->dmai_size - 1st window xferrable size
1040 * mp->dmai_offset - 0, which is the dma offset of the 1st window
1042 * ddi_dma_cookie_t members modified:
1043 * cookie_p->dmac_size - 1st and last cookie remove offset or remainder
1044 * cookie_p->dmac_laddress - 1st cookie add page offset
1047 px_dma_adjust(ddi_dma_req_t
*dmareq
, ddi_dma_impl_t
*mp
, px_dma_win_t
*win_p
)
1049 ddi_dma_cookie_t
*cookie_p
= (ddi_dma_cookie_t
*)(win_p
+ 1);
1050 size_t pg_offset
= mp
->dmai_roffset
;
1051 size_t win_offset
= 0;
1053 cookie_p
->dmac_size
-= pg_offset
;
1054 cookie_p
->dmac_laddress
|= pg_offset
;
1055 win_p
->win_size
-= pg_offset
;
1056 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "pg0 adjust %lx\n", pg_offset
);
1058 mp
->dmai_size
= win_p
->win_size
;
1059 mp
->dmai_offset
= 0;
1061 pg_offset
+= mp
->dmai_object
.dmao_size
;
1062 pg_offset
&= MMU_PAGE_OFFSET
;
1064 pg_offset
= MMU_PAGE_SIZE
- pg_offset
;
1065 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "last pg adjust %lx\n", pg_offset
);
1067 for (; win_p
->win_next
; win_p
= win_p
->win_next
) {
1068 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "win off %p\n", win_offset
);
1069 win_p
->win_offset
= win_offset
;
1070 win_offset
+= win_p
->win_size
;
1073 win_p
->win_offset
= win_offset
;
1074 cookie_p
= (ddi_dma_cookie_t
*)(win_p
+ 1);
1075 cookie_p
[win_p
->win_ncookies
- 1].dmac_size
-= pg_offset
;
1076 win_p
->win_size
-= pg_offset
;
1077 ASSERT((win_offset
+ win_p
->win_size
) == mp
->dmai_object
.dmao_size
);
1081 * px_dma_physwin() - carve up dma windows using physical addresses.
1082 * Called to handle mmu bypass and pci peer-to-peer transfers.
1083 * Calls px_dma_newwin() to allocate window objects.
1085 * Dependency: mp->dmai_pfnlst points to an array of pfns
1087 * 1. Each dma window is represented by a px_dma_win_t object.
1088 * The object will be casted to ddi_dma_win_t and returned
1089 * to leaf driver through the DDI interface.
1090 * 2. Each dma window can have several dma segments with each
1091 * segment representing a physically contiguous either memory
1092 * space (if we are doing an mmu bypass transfer) or pci address
1093 * space (if we are doing a peer-to-peer transfer).
1094 * 3. Each segment has a DMA cookie to program the DMA engine.
1095 * The cookies within each DMA window must be located in a
1096 * contiguous array per ddi_dma_nextcookie(9f).
1097 * 4. The number of DMA segments within each DMA window cannot exceed
1098 * mp->dmai_attr.dma_attr_sgllen. If the transfer size is
1099 * too large to fit in the sgllen, the rest needs to be
1100 * relocated to the next dma window.
1101 * 5. Peer-to-peer DMA segment follows device hi, lo, count_max,
1102 * and nocross restrictions while bypass DMA follows the set of
1103 * restrictions with system limits factored in.
1106 * mp->dmai_winlst - points to a link list of px_dma_win_t objects.
1107 * Each px_dma_win_t object on the link list contains
1108 * infomation such as its window size (# of pages),
1109 * starting offset (also see Restriction), an array of
1110 * DMA cookies, and # of cookies in the array.
1111 * mp->dmai_pfnlst - NULL, the pfn list is freed to conserve memory.
1112 * mp->dmai_nwin - # of total DMA windows on mp->dmai_winlst.
1113 * mp->dmai_mapping - starting cookie address
1114 * mp->dmai_rflags - consistent, nosync, no redzone
1115 * mp->dmai_cookie - start of cookie table of the 1st DMA window
1118 * Each px_dma_win_t object can theoratically start from any offset
1119 * since the mmu is not involved. However, this implementation
1120 * always make windows start from page aligned offset (except
1121 * the 1st window, which follows the requested offset) due to the
1122 * fact that we are handed a pfn list. This does require device's
1123 * count_max and attr_seg to be at least MMU_PAGE_SIZE aligned.
1126 px_dma_physwin(px_t
*px_p
, ddi_dma_req_t
*dmareq
, ddi_dma_impl_t
*mp
)
1128 uint_t npages
= mp
->dmai_ndvmapages
;
1129 int ret
, sgllen
= mp
->dmai_attr
.dma_attr_sgllen
;
1130 px_iopfn_t pfn_lo
, pfn_hi
, prev_pfn
;
1131 px_iopfn_t pfn
= PX_GET_MP_PFN(mp
, 0);
1132 uint32_t i
, win_no
= 0, pfn_no
= 1, win_pfn0_index
= 0, cookie_no
= 0;
1133 uint64_t count_max
, bypass_addr
= 0;
1134 px_dma_win_t
**win_pp
= (px_dma_win_t
**)&mp
->dmai_winlst
;
1135 ddi_dma_cookie_t
*cookie0_p
;
1136 io_attributes_t attr
= PX_GET_TTE_ATTR(mp
->dmai_rflags
,
1137 mp
->dmai_attr
.dma_attr_flags
);
1138 dev_info_t
*dip
= px_p
->px_dip
;
1140 ASSERT(PX_DMA_ISPTP(mp
) || PX_DMA_ISBYPASS(mp
));
1141 if (PX_DMA_ISPTP(mp
)) { /* ignore sys limits for peer-to-peer */
1142 ddi_dma_attr_t
*dev_attr_p
= PX_DEV_ATTR(mp
);
1143 uint64_t nocross
= dev_attr_p
->dma_attr_seg
;
1144 px_pec_t
*pec_p
= px_p
->px_pec_p
;
1145 px_iopfn_t pfn_last
= PX_DMA_ISPTP32(mp
) ?
1146 pec_p
->pec_last32_pfn
- pec_p
->pec_base32_pfn
:
1147 pec_p
->pec_last64_pfn
- pec_p
->pec_base64_pfn
;
1149 if (nocross
&& (nocross
< UINT32_MAX
))
1150 return (DDI_DMA_NOMAPPING
);
1151 if (dev_attr_p
->dma_attr_align
> MMU_PAGE_SIZE
)
1152 return (DDI_DMA_NOMAPPING
);
1153 pfn_lo
= MMU_BTOP(dev_attr_p
->dma_attr_addr_lo
);
1154 pfn_hi
= MMU_BTOP(dev_attr_p
->dma_attr_addr_hi
);
1155 pfn_hi
= MIN(pfn_hi
, pfn_last
);
1156 if ((pfn_lo
> pfn_hi
) || (pfn
< pfn_lo
))
1157 return (DDI_DMA_NOMAPPING
);
1159 count_max
= dev_attr_p
->dma_attr_count_max
;
1160 count_max
= MIN(count_max
, nocross
);
1162 * the following count_max trim is not done because we are
1163 * making sure pfn_lo <= pfn <= pfn_hi inside the loop
1164 * count_max=MIN(count_max, MMU_PTOB(pfn_hi - pfn_lo + 1)-1);
1166 } else { /* bypass hi/lo/count_max have been processed by attr2hdl() */
1167 count_max
= mp
->dmai_attr
.dma_attr_count_max
;
1168 pfn_lo
= MMU_BTOP(mp
->dmai_attr
.dma_attr_addr_lo
);
1169 pfn_hi
= MMU_BTOP(mp
->dmai_attr
.dma_attr_addr_hi
);
1171 if (px_lib_iommu_getbypass(dip
, MMU_PTOB(pfn
),
1172 attr
, &bypass_addr
) != DDI_SUCCESS
) {
1173 DBG(DBG_BYPASS
, mp
->dmai_rdip
,
1174 "bypass cookie failure %lx\n", pfn
);
1175 return (DDI_DMA_NOMAPPING
);
1177 pfn
= MMU_BTOP(bypass_addr
);
1180 /* pfn: absolute (bypass mode) or relative (p2p mode) */
1181 for (prev_pfn
= pfn
, i
= 1; i
< npages
;
1182 i
++, prev_pfn
= pfn
, pfn_no
++) {
1183 pfn
= PX_GET_MP_PFN1(mp
, i
);
1185 if (px_lib_iommu_getbypass(dip
, MMU_PTOB(pfn
), attr
,
1186 &bypass_addr
) != DDI_SUCCESS
) {
1187 ret
= DDI_DMA_NOMAPPING
;
1190 pfn
= MMU_BTOP(bypass_addr
);
1192 if ((pfn
== prev_pfn
+ 1) &&
1193 (MMU_PTOB(pfn_no
+ 1) - 1 <= count_max
))
1195 if ((pfn
< pfn_lo
) || (prev_pfn
> pfn_hi
)) {
1196 ret
= DDI_DMA_NOMAPPING
;
1201 if (cookie_no
< sgllen
)
1204 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "newwin pfn[%x-%x] %x cks\n",
1205 win_pfn0_index
, i
- 1, cookie_no
);
1206 if (ret
= px_dma_newwin(dip
, dmareq
, mp
, cookie_no
,
1207 win_pfn0_index
, i
- 1, win_pp
, count_max
, bypass_addr
))
1210 win_pp
= &(*win_pp
)->win_next
; /* win_pp = *(win_pp) */
1216 ret
= DDI_DMA_NOMAPPING
;
1220 DBG(DBG_BYPASS
, mp
->dmai_rdip
, "newwin pfn[%x-%x] %x cks\n",
1221 win_pfn0_index
, i
- 1, cookie_no
);
1222 if (ret
= px_dma_newwin(dip
, dmareq
, mp
, cookie_no
, win_pfn0_index
,
1223 i
- 1, win_pp
, count_max
, bypass_addr
))
1226 px_dma_adjust(dmareq
, mp
, mp
->dmai_winlst
);
1227 mp
->dmai_nwin
= win_no
;
1228 mp
->dmai_rflags
|= DDI_DMA_CONSISTENT
| DMP_NOSYNC
;
1229 mp
->dmai_rflags
&= ~DDI_DMA_REDZONE
;
1230 mp
->dmai_flags
|= PX_DMAI_FLAGS_NOSYNC
;
1231 cookie0_p
= (ddi_dma_cookie_t
*)(PX_WINLST(mp
) + 1);
1232 mp
->dmai_cookie
= PX_WINLST(mp
)->win_ncookies
> 1 ? cookie0_p
+ 1 : 0;
1233 mp
->dmai_mapping
= cookie0_p
->dmac_laddress
;
1236 return (DDI_DMA_MAPPED
);
1243 px_dma_ctl(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_dma_impl_t
*mp
,
1244 enum ddi_dma_ctlops cmd
, off_t
*offp
, size_t *lenp
, caddr_t
*objp
,
1249 DBG(DBG_DMA_CTL
, dip
, "unknown command (%x): rdip=%s%d\n",
1250 cmd
, ddi_driver_name(rdip
), ddi_get_instance(rdip
));
1253 return (DDI_FAILURE
);
1257 px_dvma_debug_init(px_mmu_t
*mmu_p
)
1259 size_t sz
= sizeof (struct px_dvma_rec
) * px_dvma_debug_rec
;
1260 ASSERT(MUTEX_HELD(&mmu_p
->dvma_debug_lock
));
1261 cmn_err(CE_NOTE
, "PCI Express DVMA %p stat ON", mmu_p
);
1263 mmu_p
->dvma_alloc_rec
= kmem_alloc(sz
, KM_SLEEP
);
1264 mmu_p
->dvma_free_rec
= kmem_alloc(sz
, KM_SLEEP
);
1266 mmu_p
->dvma_active_list
= NULL
;
1267 mmu_p
->dvma_alloc_rec_index
= 0;
1268 mmu_p
->dvma_free_rec_index
= 0;
1269 mmu_p
->dvma_active_count
= 0;
1273 px_dvma_debug_fini(px_mmu_t
*mmu_p
)
1275 struct px_dvma_rec
*prev
, *ptr
;
1276 size_t sz
= sizeof (struct px_dvma_rec
) * px_dvma_debug_rec
;
1277 uint64_t mask
= ~(1ull << mmu_p
->mmu_inst
);
1278 cmn_err(CE_NOTE
, "PCI Express DVMA %p stat OFF", mmu_p
);
1280 if (mmu_p
->dvma_alloc_rec
) {
1281 kmem_free(mmu_p
->dvma_alloc_rec
, sz
);
1282 mmu_p
->dvma_alloc_rec
= NULL
;
1284 if (mmu_p
->dvma_free_rec
) {
1285 kmem_free(mmu_p
->dvma_free_rec
, sz
);
1286 mmu_p
->dvma_free_rec
= NULL
;
1289 prev
= mmu_p
->dvma_active_list
;
1292 for (ptr
= prev
->next
; ptr
; prev
= ptr
, ptr
= ptr
->next
)
1293 kmem_free(prev
, sizeof (struct px_dvma_rec
));
1294 kmem_free(prev
, sizeof (struct px_dvma_rec
));
1296 mmu_p
->dvma_active_list
= NULL
;
1297 mmu_p
->dvma_alloc_rec_index
= 0;
1298 mmu_p
->dvma_free_rec_index
= 0;
1299 mmu_p
->dvma_active_count
= 0;
1301 px_dvma_debug_off
&= mask
;
1302 px_dvma_debug_on
&= mask
;
1306 px_dvma_alloc_debug(px_mmu_t
*mmu_p
, char *address
, uint_t len
,
1309 struct px_dvma_rec
*ptr
;
1310 mutex_enter(&mmu_p
->dvma_debug_lock
);
1312 if (!mmu_p
->dvma_alloc_rec
)
1313 px_dvma_debug_init(mmu_p
);
1314 if (PX_DVMA_DBG_OFF(mmu_p
)) {
1315 px_dvma_debug_fini(mmu_p
);
1319 ptr
= &mmu_p
->dvma_alloc_rec
[mmu_p
->dvma_alloc_rec_index
];
1320 ptr
->dvma_addr
= address
;
1323 if (++mmu_p
->dvma_alloc_rec_index
== px_dvma_debug_rec
)
1324 mmu_p
->dvma_alloc_rec_index
= 0;
1326 ptr
= kmem_alloc(sizeof (struct px_dvma_rec
), KM_SLEEP
);
1327 ptr
->dvma_addr
= address
;
1331 ptr
->next
= mmu_p
->dvma_active_list
;
1332 mmu_p
->dvma_active_list
= ptr
;
1333 mmu_p
->dvma_active_count
++;
1335 mutex_exit(&mmu_p
->dvma_debug_lock
);
1339 px_dvma_free_debug(px_mmu_t
*mmu_p
, char *address
, uint_t len
,
1342 struct px_dvma_rec
*ptr
, *ptr_save
;
1343 mutex_enter(&mmu_p
->dvma_debug_lock
);
1345 if (!mmu_p
->dvma_alloc_rec
)
1346 px_dvma_debug_init(mmu_p
);
1347 if (PX_DVMA_DBG_OFF(mmu_p
)) {
1348 px_dvma_debug_fini(mmu_p
);
1352 ptr
= &mmu_p
->dvma_free_rec
[mmu_p
->dvma_free_rec_index
];
1353 ptr
->dvma_addr
= address
;
1356 if (++mmu_p
->dvma_free_rec_index
== px_dvma_debug_rec
)
1357 mmu_p
->dvma_free_rec_index
= 0;
1359 ptr_save
= mmu_p
->dvma_active_list
;
1360 for (ptr
= ptr_save
; ptr
; ptr
= ptr
->next
) {
1361 if ((ptr
->dvma_addr
== address
) && (ptr
->len
= len
))
1366 cmn_err(CE_WARN
, "bad dvma free addr=%lx len=%x",
1367 (long)address
, len
);
1370 if (ptr
== mmu_p
->dvma_active_list
)
1371 mmu_p
->dvma_active_list
= ptr
->next
;
1373 ptr_save
->next
= ptr
->next
;
1374 kmem_free(ptr
, sizeof (struct px_dvma_rec
));
1375 mmu_p
->dvma_active_count
--;
1377 mutex_exit(&mmu_p
->dvma_debug_lock
);
1382 px_dump_dma_handle(uint64_t flag
, dev_info_t
*dip
, ddi_dma_impl_t
*hp
)
1384 DBG(flag
, dip
, "mp(%p): flags=%x mapping=%lx xfer_size=%x\n",
1385 hp
, hp
->dmai_inuse
, hp
->dmai_mapping
, hp
->dmai_size
);
1386 DBG(flag
|DBG_CONT
, dip
, "\tnpages=%x roffset=%x rflags=%x nwin=%x\n",
1387 hp
->dmai_ndvmapages
, hp
->dmai_roffset
, hp
->dmai_rflags
,
1389 DBG(flag
|DBG_CONT
, dip
, "\twinsize=%x tte=%p pfnlst=%p pfn0=%p\n",
1390 hp
->dmai_winsize
, hp
->dmai_tte
, hp
->dmai_pfnlst
, hp
->dmai_pfn0
);
1391 DBG(flag
|DBG_CONT
, dip
, "\twinlst=%x obj=%p attr=%p ckp=%p\n",
1392 hp
->dmai_winlst
, &hp
->dmai_object
, &hp
->dmai_attr
,