Mercurial > illumos > fmac
changeset 7613:e49de7ec7617 onnv_99
PSARC 2008/561 AMD IOMMU
6747701 Integrate DDI hooks for AMD IOMMU
6748625 Intel IOMMU putback broke dom0 boot
author | Vikram Hegde <Vikram.Hegde@Sun.COM> |
---|---|
date | Mon, 15 Sep 2008 21:50:24 -0700 |
parents | c832bb3d8947 |
children | fca06b3ad595 |
files | usr/src/pkgdefs/SUNWckr/prototype_i386 usr/src/pkgdefs/SUNWhea/prototype_i386 usr/src/uts/common/os/devcfg.c usr/src/uts/common/sys/ddi_impldefs.h usr/src/uts/common/sys/ddi_implfuncs.h usr/src/uts/i86pc/io/rootnex.c usr/src/uts/i86pc/sys/rootnex.h usr/src/uts/intel/Makefile.files usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/io/iommulib.c usr/src/uts/intel/iommulib/Makefile usr/src/uts/intel/sys/Makefile usr/src/uts/intel/sys/iommulib.h |
diffstat | 13 files changed, 1772 insertions(+), 54 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386 Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/pkgdefs/SUNWckr/prototype_i386 Mon Sep 15 21:50:24 2008 -0700 @@ -204,6 +204,7 @@ f none kernel/misc/hook 755 root sys f none kernel/misc/hpcsvc 755 root sys f none kernel/misc/idmap 755 root sys +f none kernel/misc/iommulib 755 root sys f none kernel/misc/ipc 755 root sys f none kernel/misc/kbtrans 755 root sys f none kernel/misc/kcf 755 root sys @@ -415,6 +416,7 @@ f none kernel/misc/amd64/hook 755 root sys f none kernel/misc/amd64/hpcsvc 755 root sys f none kernel/misc/amd64/idmap 755 root sys +f none kernel/misc/amd64/iommulib 755 root sys f none kernel/misc/amd64/ipc 755 root sys f none kernel/misc/amd64/kbtrans 755 root sys f none kernel/misc/amd64/kcf 755 root sys
--- a/usr/src/pkgdefs/SUNWhea/prototype_i386 Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/pkgdefs/SUNWhea/prototype_i386 Mon Sep 15 21:50:24 2008 -0700 @@ -19,10 +19,9 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" # # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package @@ -89,6 +88,7 @@ f none usr/include/sys/fp.h 644 root bin f none usr/include/sys/hypervisor.h 644 root bin f none usr/include/sys/i8272A.h 644 root bin +f none usr/include/sys/iommulib.h 644 root bin f none usr/include/sys/kd.h 644 root bin f none usr/include/sys/mc.h 644 root bin f none usr/include/sys/mc_amd.h 644 root bin
--- a/usr/src/uts/common/os/devcfg.c Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/common/os/devcfg.c Mon Sep 15 21:50:24 2008 -0700 @@ -3617,6 +3617,39 @@ } +#define PCI_EX_CLASS "pciexclass" +#define PCI_EX "pciex" +#define PCI_CLASS "pciclass" +#define PCI "pci" + +int +ddi_is_pci_dip(dev_info_t *dip) +{ + char *prop = NULL; + + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "compatible", &prop) == DDI_PROP_SUCCESS) { + ASSERT(prop); + if (strncmp(prop, PCI_EX_CLASS, sizeof (PCI_EX_CLASS) - 1) + == 0 || + strncmp(prop, PCI_EX, sizeof (PCI_EX)- 1) + == 0 || + strncmp(prop, PCI_CLASS, sizeof (PCI_CLASS) - 1) + == 0 || + strncmp(prop, PCI, sizeof (PCI) - 1) + == 0) { + ddi_prop_free(prop); + return (1); + } + } + + if (prop != NULL) { + ddi_prop_free(prop); + } + + return (0); +} + /* * Given the pathname of a device, fill in the dev_info_t value and/or the * dev_t value and/or the spectype, depending on which parameters are non-NULL. @@ -3650,9 +3683,9 @@ * the ioc, look for minor node dhcp. If not found, pass ":dhcp" * to ioc's bus_config entry point. */ -int -resolve_pathname(char *pathname, - dev_info_t **dipp, dev_t *devtp, int *spectypep) +static int +parse_pathname(char *pathname, + dev_info_t **dipp, dev_t *devtp, int *spectypep, dev_info_t **pci_dipp) { int error; dev_info_t *parent, *child; @@ -3665,6 +3698,9 @@ struct ddi_minor_data *dmn; int circ; + if (pci_dipp) + *pci_dipp = NULL; + if (*pathname != '/') return (EINVAL); parent = ddi_root_node(); /* Begin at the top of the tree */ @@ -3707,6 +3743,10 @@ pn_free(&pn); kmem_free(component, MAXNAMELEN); kmem_free(config_name, MAXNAMELEN); + if (pci_dipp && *pci_dipp) { + ndi_rele_devi(*pci_dipp); + *pci_dipp = NULL; + } return (-1); } @@ -3714,6 +3754,15 @@ ndi_rele_devi(parent); parent = child; pn_skipslash(&pn); + if (pci_dipp) { + if (ddi_is_pci_dip(child)) { + ndi_hold_devi(child); + if (*pci_dipp != NULL) { + ndi_rele_devi(*pci_dipp); + } + *pci_dipp = child; + } + } } /* @@ -3731,6 +3780,10 @@ kmem_free(config_name, MAXNAMELEN); NDI_CONFIG_DEBUG((CE_NOTE, "%s: minor node not found\n", pathname)); + if (pci_dipp && *pci_dipp) { + ndi_rele_devi(*pci_dipp); + *pci_dipp = NULL; + } return (-1); } minorname = NULL; /* look for default minor */ @@ -3789,7 +3842,7 @@ */ if (dipp != NULL) *dipp = parent; - else { + else if (pci_dipp == NULL) { /* * We should really keep the ref count to keep the node from * detaching but ddi_pathname_to_dev_t() specifies a NULL dipp, @@ -3804,6 +3857,10 @@ * it, and all references, with a call that specifies a dipp. * In addition, the callers of this new interfaces would then * need to call ndi_rele_devi when the reference is complete. + * + * NOTE: If pci_dipp is non-NULL we are only interested + * in the PCI parent which is returned held. No need to hold + * the leaf dip. */ (void) ddi_prop_update_int(DDI_DEV_T_NONE, parent, DDI_NO_AUTODETACH, 1); @@ -3813,6 +3870,19 @@ return (0); } +int +resolve_pathname(char *pathname, + dev_info_t **dipp, dev_t *devtp, int *spectypep) +{ + return (parse_pathname(pathname, dipp, devtp, spectypep, NULL)); +} + +int +ddi_find_pci_parent(char *pathname, dev_info_t **pci_dipp) +{ + return (parse_pathname(pathname, NULL, NULL, NULL, pci_dipp)); +} + /* * Given the pathname of a device, return the dev_t of the corresponding * device. Returns NODEV on failure.
--- a/usr/src/uts/common/sys/ddi_impldefs.h Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/common/sys/ddi_impldefs.h Mon Sep 15 21:50:24 2008 -0700 @@ -88,6 +88,9 @@ devi_port_t port_down; } devi_bus_priv_t; +struct iommulib_unit; +typedef struct iommulib_unit *iommulib_handle_t; + struct dev_info { struct dev_info *devi_parent; /* my parent node in tree */ @@ -224,6 +227,9 @@ /* For intel iommu support */ void *devi_iommu_private; + + /* IOMMU handle */ + iommulib_handle_t devi_iommulib_handle; }; #define DEVI(dev_info_type) ((struct dev_info *)(dev_info_type))
--- a/usr/src/uts/common/sys/ddi_implfuncs.h Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/common/sys/ddi_implfuncs.h Mon Sep 15 21:50:24 2008 -0700 @@ -27,8 +27,6 @@ #ifndef _SYS_DDI_IMPLFUNCS_H #define _SYS_DDI_IMPLFUNCS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -249,6 +247,8 @@ int i_ddi_attach_hw_nodes(char *); int i_ddi_devs_attached(major_t); int i_ddi_minor_node_count(dev_info_t *, const char *); +int ddi_find_pci_parent(char *pathname, dev_info_t **pci_dipp); +int ddi_is_pci_dip(dev_info_t *dip); /* non-DDI functions: wrapper around mod_hold/rele_dev_by_major() */ struct dev_ops *ddi_hold_driver(major_t);
--- a/usr/src/uts/i86pc/io/rootnex.c Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/i86pc/io/rootnex.c Mon Sep 15 21:50:24 2008 -0700 @@ -67,14 +67,10 @@ #include <sys/hypervisor.h> #include <sys/bootconf.h> #include <vm/kboot_mmu.h> -#endif - +#else #include <sys/intel_iommu.h> - -/* - * add to support dmar fault interrupt, will change soon - */ -char _depends_on[] = "mach/pcplusmp"; +#endif + /* * enable/disable extra checking of function parameters. Useful for debugging @@ -157,6 +153,9 @@ typedef paddr_t rootnex_addr_t; #endif +#if !defined(__xpv) +char _depends_on[] = "mach/pcplusmp misc/iommulib"; +#endif static struct cb_ops rootnex_cb_ops = { nodev, /* open */ @@ -211,6 +210,30 @@ static int rootnex_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); +static int rootnex_coredma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep); +static int rootnex_coredma_freehdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle); +static int rootnex_coredma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); +static int rootnex_coredma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle); +static void rootnex_coredma_reset_cookies(dev_info_t *dip, + ddi_dma_handle_t handle); +static int rootnex_coredma_get_cookies(dev_info_t *dip, ddi_dma_handle_t handle, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); +static int rootnex_coredma_sync(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, off_t off, size_t len, uint_t cache_flags); +static int rootnex_coredma_win(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, uint_t win, off_t *offp, size_t *lenp, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); +static int rootnex_coredma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep); +static int rootnex_coredma_mctl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, enum ddi_dma_ctlops request, off_t *offp, + size_t *lenp, caddr_t *objpp, uint_t cache_flags); static struct bus_ops rootnex_bus_ops = { BUSO_REV, @@ -272,6 +295,21 @@ NULL }; +static iommulib_nexops_t iommulib_nexops = { + IOMMU_NEXOPS_VERSION, + "Rootnex IOMMU ops Vers 1.1", + NULL, + rootnex_coredma_allochdl, + rootnex_coredma_freehdl, + rootnex_coredma_bindhdl, + rootnex_coredma_unbindhdl, + rootnex_coredma_reset_cookies, + rootnex_coredma_get_cookies, + rootnex_coredma_sync, + rootnex_coredma_win, + rootnex_coredma_map, + rootnex_coredma_mctl +}; /* * extern hacks @@ -434,6 +472,7 @@ /* Initialize rootnex event handle */ i_ddi_rootnex_init_events(dip); +#if !defined(__xpv) #if defined(__amd64) /* probe intel iommu */ intel_iommu_probe_and_parse(); @@ -448,6 +487,12 @@ } #endif + e = iommulib_nexus_register(dip, &iommulib_nexops, + &rootnex_state->r_iommulib_handle); + + ASSERT(e == DDI_SUCCESS); +#endif + return (DDI_SUCCESS); } @@ -1543,14 +1588,11 @@ * ****************** */ -/* - * rootnex_dma_allochdl() - * called from ddi_dma_alloc_handle(). - */ /*ARGSUSED*/ static int -rootnex_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr, - int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) +rootnex_coredma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep) { uint64_t maxsegmentsize_ll; uint_t maxsegmentsize; @@ -1687,12 +1729,42 @@ /* - * rootnex_dma_freehdl() - * called from ddi_dma_free_handle(). + * rootnex_dma_allochdl() + * called from ddi_dma_alloc_handle(). */ +static int +rootnex_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr, + int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) +{ +#if !defined(__xpv) + uint_t error = ENOTSUP; + int retval; + + retval = iommulib_nex_open(rdip, &error); + + if (retval != DDI_SUCCESS && error == ENOTSUP) { + /* No IOMMU */ + return (rootnex_coredma_allochdl(dip, rdip, attr, waitfp, arg, + handlep)); + } else if (retval != DDI_SUCCESS) { + return (DDI_FAILURE); + } + + ASSERT(IOMMU_USED(rdip)); + + /* has an IOMMU */ + return (iommulib_nexdma_allochdl(dip, rdip, attr, + waitfp, arg, handlep)); +#else + return (rootnex_coredma_allochdl(dip, rdip, attr, waitfp, arg, + handlep)); +#endif +} + /*ARGSUSED*/ static int -rootnex_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) +rootnex_coredma_freehdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle) { ddi_dma_impl_t *hp; rootnex_dma_t *dma; @@ -1717,15 +1789,27 @@ return (DDI_SUCCESS); } - /* - * rootnex_dma_bindhdl() - * called from ddi_dma_addr_bind_handle() and ddi_dma_buf_bind_handle(). + * rootnex_dma_freehdl() + * called from ddi_dma_free_handle(). */ +static int +rootnex_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_freehdl(dip, rdip, handle)); + } +#endif + return (rootnex_coredma_freehdl(dip, rdip, handle)); +} + + /*ARGSUSED*/ static int -rootnex_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, - struct ddi_dma_req *dmareq, ddi_dma_cookie_t *cookiep, uint_t *ccountp) +rootnex_coredma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) { rootnex_sglinfo_t *sinfo; ddi_dma_attr_t *attr; @@ -1779,6 +1863,7 @@ /* save away the original bind info */ dma->dp_dma = dmareq->dmar_object; +#if !defined(__xpv) if (rootnex_state->r_intel_iommu_enabled) { e = intel_iommu_map_sgl(handle, dmareq, rootnex_state->r_prealloc_cookies); @@ -1805,6 +1890,7 @@ return (DDI_DMA_NORESOURCES); } } +#endif rootnex_sgl_start: /* @@ -1970,19 +2056,33 @@ /* - * rootnex_dma_unbindhdl() - * called from ddi_dma_unbind_handle() + * rootnex_dma_bindhdl() + * called from ddi_dma_addr_bind_handle() and ddi_dma_buf_bind_handle(). */ +static int +rootnex_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_bindhdl(dip, rdip, handle, dmareq, + cookiep, ccountp)); + } +#endif + return (rootnex_coredma_bindhdl(dip, rdip, handle, dmareq, + cookiep, ccountp)); +} + /*ARGSUSED*/ static int -rootnex_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, +rootnex_coredma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle) { ddi_dma_impl_t *hp; rootnex_dma_t *dma; int e; - hp = (ddi_dma_impl_t *)handle; dma = (rootnex_dma_t *)hp->dmai_private; @@ -2020,12 +2120,14 @@ rootnex_teardown_copybuf(dma); rootnex_teardown_windows(dma); +#if !defined(__xpv) /* * If intel iommu enabled, clean up the page tables and free the dvma */ if (rootnex_state->r_intel_iommu_enabled) { intel_iommu_unmap_sgl(handle); } +#endif /* * If we had to allocate space to for the worse case sgl (it didn't @@ -2051,6 +2153,56 @@ return (DDI_SUCCESS); } +/* + * rootnex_dma_unbindhdl() + * called from ddi_dma_unbind_handle() + */ +/*ARGSUSED*/ +static int +rootnex_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_unbindhdl(dip, rdip, handle)); + } +#endif + return (rootnex_coredma_unbindhdl(dip, rdip, handle)); +} + +/*ARGSUSED*/ +static void +rootnex_coredma_reset_cookies(dev_info_t *dip, ddi_dma_handle_t handle) +{ + ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle; + rootnex_dma_t *dma = (rootnex_dma_t *)hp->dmai_private; + + hp->dmai_cookie = &dma->dp_cookies[0]; + hp->dmai_cookie++; +} + +/*ARGSUSED*/ +static int +rootnex_coredma_get_cookies(dev_info_t *dip, ddi_dma_handle_t handle, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ + ddi_dma_impl_t *hp = (ddi_dma_impl_t *)handle; + rootnex_dma_t *dma = (rootnex_dma_t *)hp->dmai_private; + + + if (hp->dmai_rflags & DDI_DMA_PARTIAL) { + *ccountp = dma->dp_window[dma->dp_current_win].wd_cookie_cnt; + } else { + *ccountp = dma->dp_sglinfo.si_sgl_size; + } + *cookiep = dma->dp_cookies[0]; + + /* reset the cookies */ + hp->dmai_cookie = &dma->dp_cookies[0]; + hp->dmai_cookie++; + + return (DDI_SUCCESS); +} /* * rootnex_verify_buffer() @@ -3794,15 +3946,9 @@ } -/* - * rootnex_dma_sync() - * called from ddi_dma_sync() if DMP_NOSYNC is not set in hp->dmai_rflags. - * We set DMP_NOSYNC if we're not using the copy buffer. If DMP_NOSYNC - * is set, ddi_dma_sync() returns immediately passing back success. - */ /*ARGSUSED*/ static int -rootnex_dma_sync(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, +rootnex_coredma_sync(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, off_t off, size_t len, uint_t cache_flags) { rootnex_sglinfo_t *sinfo; @@ -3935,6 +4081,26 @@ return (DDI_SUCCESS); } +/* + * rootnex_dma_sync() + * called from ddi_dma_sync() if DMP_NOSYNC is not set in hp->dmai_rflags. + * We set DMP_NOSYNC if we're not using the copy buffer. If DMP_NOSYNC + * is set, ddi_dma_sync() returns immediately passing back success. + */ +/*ARGSUSED*/ +static int +rootnex_dma_sync(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, + off_t off, size_t len, uint_t cache_flags) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_sync(dip, rdip, handle, off, len, + cache_flags)); + } +#endif + return (rootnex_coredma_sync(dip, rdip, handle, off, len, + cache_flags)); +} /* * rootnex_valid_sync_parms() @@ -3987,13 +4153,9 @@ } -/* - * rootnex_dma_win() - * called from ddi_dma_getwin() - */ /*ARGSUSED*/ static int -rootnex_dma_win(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, +rootnex_coredma_win(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, uint_t win, off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep, uint_t *ccountp) { @@ -4207,7 +4369,26 @@ return (DDI_SUCCESS); } - +/* + * rootnex_dma_win() + * called from ddi_dma_getwin() + */ +/*ARGSUSED*/ +static int +rootnex_dma_win(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, + uint_t win, off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep, + uint_t *ccountp) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_win(dip, rdip, handle, win, offp, lenp, + cookiep, ccountp)); + } +#endif + + return (rootnex_coredma_win(dip, rdip, handle, win, offp, lenp, + cookiep, ccountp)); +} /* * ************************ @@ -4215,14 +4396,10 @@ * ************************ */ -/* - * rootnex_dma_map() - * called from ddi_dma_setup() - */ /* ARGSUSED */ static int -rootnex_dma_map(dev_info_t *dip, dev_info_t *rdip, struct ddi_dma_req *dmareq, - ddi_dma_handle_t *handlep) +rootnex_coredma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep) { #if defined(__amd64) /* @@ -4296,6 +4473,22 @@ #endif /* defined(__amd64) */ } +/* + * rootnex_dma_map() + * called from ddi_dma_setup() + */ +/* ARGSUSED */ +static int +rootnex_dma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_map(dip, rdip, dmareq, handlep)); + } +#endif + return (rootnex_coredma_map(dip, rdip, dmareq, handlep)); +} /* * rootnex_dma_mctl() @@ -4303,7 +4496,7 @@ */ /* ARGSUSED */ static int -rootnex_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, +rootnex_coredma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, enum ddi_dma_ctlops request, off_t *offp, size_t *lenp, caddr_t *objpp, uint_t cache_flags) { @@ -4488,6 +4681,26 @@ #endif /* defined(__amd64) */ } +/* + * rootnex_dma_mctl() + * + */ +/* ARGSUSED */ +static int +rootnex_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, + enum ddi_dma_ctlops request, off_t *offp, size_t *lenp, caddr_t *objpp, + uint_t cache_flags) +{ +#if !defined(__xpv) + if (IOMMU_USED(rdip)) { + return (iommulib_nexdma_mctl(dip, rdip, handle, request, offp, + lenp, objpp, cache_flags)); + } +#endif + + return (rootnex_coredma_mctl(dip, rdip, handle, request, offp, + lenp, objpp, cache_flags)); +} /* * *********
--- a/usr/src/uts/i86pc/sys/rootnex.h Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/i86pc/sys/rootnex.h Mon Sep 15 21:50:24 2008 -0700 @@ -34,6 +34,7 @@ #include <sys/conf.h> #include <sys/modctl.h> #include <sys/sunddi.h> +#include <sys/iommulib.h> #ifdef __cplusplus extern "C" { @@ -340,6 +341,7 @@ boolean_t r_reserved_msg_printed; uint64_t r_counters[ROOTNEX_CNT_LAST]; boolean_t r_intel_iommu_enabled; + iommulib_nexhandle_t r_iommulib_handle; } rootnex_state_t;
--- a/usr/src/uts/intel/Makefile.files Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/intel/Makefile.files Mon Sep 15 21:50:24 2008 -0700 @@ -252,6 +252,11 @@ AMR_OBJS = amr.o # +# IOMMULIB module +# +IOMMULIB_OBJS = iommulib.o + +# # Brand modules # SN1_BRAND_OBJS = sn1_brand.o sn1_brand_asm.o
--- a/usr/src/uts/intel/Makefile.intel.shared Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/intel/Makefile.intel.shared Mon Sep 15 21:50:24 2008 -0700 @@ -550,6 +550,7 @@ MISC_KMODS += ibmf MISC_KMODS += ibtl MISC_KMODS += idmap +MISC_KMODS += iommulib MISC_KMODS += ipc MISC_KMODS += kbtrans MISC_KMODS += kcf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/io/iommulib.c Mon Sep 15 21:50:24 2008 -0700 @@ -0,0 +1,1071 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "@(#)iommulib.c 1.6 08/09/07 SMI" + +#include <sys/sunddi.h> +#include <sys/sunndi.h> +#include <sys/errno.h> +#include <sys/modctl.h> +#include <sys/iommulib.h> + +/* ******** Type definitions private to this file ********************** */ + +/* 1 per IOMMU unit. There may be more than one per dip */ +typedef struct iommulib_unit { + kmutex_t ilu_lock; + uint64_t ilu_ref; + uint32_t ilu_unitid; + dev_info_t *ilu_dip; + iommulib_ops_t *ilu_ops; + void* ilu_data; + struct iommulib_unit *ilu_next; + struct iommulib_unit *ilu_prev; +} iommulib_unit_t; + +typedef struct iommulib_cache { + dev_info_t *cache_rdip; + iommulib_unit_t *cache_unit; + struct iommulib_cache *cache_next; + struct iommulib_cache *cache_prev; +} iommulib_cache_t; + +typedef struct iommulib_nex { + dev_info_t *nex_dip; + iommulib_nexops_t nex_ops; + struct iommulib_nex *nex_next; + struct iommulib_nex *nex_prev; +} iommulib_nex_t; + +/* ********* Function prototypes ********************* */ +static int lookup_cache(dev_info_t *rdip, iommulib_unit_t **unitpp); +static void insert_cache(dev_info_t *rdip, iommulib_unit_t *unitp); + + +/* ********* Globals ************************ */ + +/* IOMMU side: Following data protected by lock */ +static kmutex_t iommulib_lock; +static iommulib_unit_t *iommulib_list; +static uint64_t iommulib_unit_ids = 0; +static uint64_t iommulib_num_units = 0; + +/* rootnex side data */ + +static kmutex_t iommulib_nexus_lock; +static iommulib_nex_t *iommulib_nexus_list; + +#define IOMMULIB_CACHE_SIZE 256 +static kmutex_t iommulib_cache_lock; +static iommulib_cache_t **iommulib_cache; + +/* tunable via /etc/system */ +static uint_t iommulib_cache_size = IOMMULIB_CACHE_SIZE; + +/* can be set atomically without lock */ +static volatile uint32_t iommulib_fini; + +/* debug flag */ +static int iommulib_debug; + +/* + * Module linkage information for the kernel. + */ +static struct modlmisc modlmisc = { + &mod_miscops, "IOMMU library module" +}; + +static struct modlinkage modlinkage = { + MODREV_1, (void *)&modlmisc, NULL +}; + +int +_init(void) +{ + /* + * static mutexes automagically initialized + * by being allocated in zeroed memory + */ + mutex_enter(&iommulib_cache_lock); + iommulib_cache = kmem_zalloc( + sizeof (iommulib_cache_t *) * iommulib_cache_size, KM_SLEEP); + mutex_exit(&iommulib_cache_lock); + + return (mod_install(&modlinkage)); +} + +int +_fini(void) +{ + mutex_enter(&iommulib_lock); + if (iommulib_list != NULL || iommulib_nexus_list != NULL) { + mutex_exit(&iommulib_lock); + return (EBUSY); + } + iommulib_fini = 1; + + mutex_enter(&iommulib_cache_lock); + kmem_free(iommulib_cache, + sizeof (iommulib_cache_t *) * iommulib_cache_size); + iommulib_cache = NULL; + mutex_exit(&iommulib_cache_lock); + + mutex_exit(&iommulib_lock); + return (mod_remove(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* + * Routines with iommulib_iommu_* are invoked from the + * IOMMU driver. + * Routines with iommulib_nex* are invoked from the + * nexus driver (typically rootnex) + */ + +int +iommulib_nexus_register(dev_info_t *dip, iommulib_nexops_t *nexops, + iommulib_nexhandle_t *handle) +{ + iommulib_nex_t *nexp; + int instance = ddi_get_instance(dip); + const char *driver = ddi_driver_name(dip); + dev_info_t *pdip = ddi_get_parent(dip); + const char *f = "iommulib_nexus_register"; + + ASSERT(nexops); + ASSERT(handle); + + *handle = NULL; + + /* + * Root node is never busy held + */ + if (dip != ddi_root_node() && (i_ddi_node_state(dip) < DS_PROBED || + !DEVI_BUSY_OWNED(pdip))) { + cmn_err(CE_WARN, "%s: NEXUS devinfo node not in DS_PROBED " + "or busy held for nexops vector (%p). Failing registration", + f, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_vers != IOMMU_NEXOPS_VERSION) { + cmn_err(CE_WARN, "%s: %s%d: Invalid IOMMULIB nexops version " + "in nexops vector (%p). Failing NEXUS registration", + f, driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + ASSERT(nexops->nops_data == NULL); + + if (nexops->nops_id == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL ID field. " + "Failing registration for nexops vector: %p", + f, driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_allochdl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_allochdl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_freehdl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_freehdl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_bindhdl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_bindhdl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_sync == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_sync op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + + if (nexops->nops_dma_reset_cookies == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_reset_cookies op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_get_cookies == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_get_cookies op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_win == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL nops_dma_win op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + /* Check for legacy ops */ + if (nexops->nops_dma_map == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL legacy nops_dma_map op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + if (nexops->nops_dma_mctl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL legacy nops_dma_mctl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)nexops); + return (DDI_FAILURE); + } + + nexp = kmem_zalloc(sizeof (iommulib_nex_t), KM_SLEEP); + + mutex_enter(&iommulib_lock); + if (iommulib_fini == 1) { + mutex_exit(&iommulib_lock); + cmn_err(CE_WARN, "%s: IOMMULIB unloading. " + "Failing NEXUS register.", f); + kmem_free(nexp, sizeof (iommulib_nex_t)); + return (DDI_FAILURE); + } + + /* + * fini/register race conditions have been handled. Now create the + * nexus struct + */ + ndi_hold_devi(dip); + nexp->nex_dip = dip; + nexp->nex_ops = *nexops; + + mutex_enter(&iommulib_nexus_lock); + nexp->nex_next = iommulib_nexus_list; + iommulib_nexus_list = nexp; + nexp->nex_prev = NULL; + + if (nexp->nex_next != NULL) + nexp->nex_next->nex_prev = nexp; + + mutex_exit(&iommulib_nexus_lock); + mutex_exit(&iommulib_lock); + + cmn_err(CE_NOTE, "!%s: %s%d: Succesfully registered NEXUS %s " + "nexops=%p", f, driver, instance, ddi_node_name(dip), + (void *)nexops); + + *handle = nexp; + + return (DDI_SUCCESS); +} + +int +iommulib_nexus_unregister(iommulib_nexhandle_t handle) +{ + dev_info_t *dip; + int instance; + const char *driver; + iommulib_nex_t *nexp = (iommulib_nex_t *)handle; + const char *f = "iommulib_nexus_unregister"; + + ASSERT(nexp); + + mutex_enter(&iommulib_nexus_lock); + + dip = nexp->nex_dip; + driver = ddi_driver_name(dip); + instance = ddi_get_instance(dip); + + /* A future enhancement would be to add ref-counts */ + + if (nexp->nex_prev == NULL) { + iommulib_nexus_list = nexp->nex_next; + } else { + nexp->nex_prev->nex_next = nexp->nex_next; + } + + if (nexp->nex_next != NULL) + nexp->nex_next->nex_prev = nexp->nex_prev; + + mutex_exit(&iommulib_nexus_lock); + + kmem_free(nexp, sizeof (iommulib_nex_t)); + + cmn_err(CE_WARN, "%s: %s%d: NEXUS (%s) handle successfully " + "unregistered from IOMMULIB", f, driver, instance, + ddi_node_name(dip)); + + ndi_rele_devi(dip); + + return (DDI_SUCCESS); +} + +static iommulib_nexops_t * +lookup_nexops(dev_info_t *dip) +{ + iommulib_nex_t *nexp; + + mutex_enter(&iommulib_nexus_lock); + nexp = iommulib_nexus_list; + while (nexp) { + if (nexp->nex_dip == dip) + break; + nexp = nexp->nex_next; + } + mutex_exit(&iommulib_nexus_lock); + + return (nexp ? &nexp->nex_ops : NULL); +} + +int +iommulib_iommu_register(dev_info_t *dip, iommulib_ops_t *ops, + iommulib_handle_t *handle) +{ + const char *vendor; + iommulib_unit_t *unitp; + int instance = ddi_get_instance(dip); + const char *driver = ddi_driver_name(dip); + dev_info_t *pdip = ddi_get_parent(dip); + const char *f = "iommulib_register"; + + ASSERT(ops); + ASSERT(handle); + + if (i_ddi_node_state(dip) < DS_PROBED || !DEVI_BUSY_OWNED(pdip)) { + cmn_err(CE_WARN, "%s: devinfo node not in DS_PROBED or " + "busy held for ops vector (%p). Failing registration", + f, (void *)ops); + return (DDI_FAILURE); + } + + + if (ops->ilops_vers != IOMMU_OPS_VERSION) { + cmn_err(CE_WARN, "%s: %s%d: Invalid IOMMULIB ops version " + "in ops vector (%p). Failing registration", f, driver, + instance, (void *)ops); + return (DDI_FAILURE); + } + + switch (ops->ilops_vendor) { + case AMD_IOMMU: + vendor = "AMD"; + break; + case INTEL_IOMMU: + vendor = "Intel"; + break; + case INVALID_VENDOR: + cmn_err(CE_WARN, "%s: %s%d: vendor field (%x) not initialized. " + "Failing registration for ops vector: %p", f, + driver, instance, ops->ilops_vendor, (void *)ops); + return (DDI_FAILURE); + default: + cmn_err(CE_WARN, "%s: %s%d: Invalid vendor field (%x). " + "Failing registration for ops vector: %p", f, + driver, instance, ops->ilops_vendor, (void *)ops); + return (DDI_FAILURE); + } + + cmn_err(CE_NOTE, "%s: %s%d: Detected IOMMU registration from vendor %s", + f, driver, instance, vendor); + + if (ops->ilops_data == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL IOMMU data field. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_id == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL ID field. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_probe == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL probe op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_dma_allochdl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL dma_allochdl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_dma_freehdl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL dma_freehdl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_dma_bindhdl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL dma_bindhdl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_dma_sync == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL dma_sync op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_dma_win == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL dma_win op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + /* Check for legacy ops */ + if (ops->ilops_dma_map == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL legacy dma_map op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + if (ops->ilops_dma_mctl == NULL) { + cmn_err(CE_WARN, "%s: %s%d: NULL legacy dma_mctl op. " + "Failing registration for ops vector: %p", f, + driver, instance, (void *)ops); + return (DDI_FAILURE); + } + + unitp = kmem_zalloc(sizeof (iommulib_unit_t), KM_SLEEP); + mutex_enter(&iommulib_lock); + if (iommulib_fini == 1) { + mutex_exit(&iommulib_lock); + cmn_err(CE_WARN, "%s: IOMMULIB unloading. Failing register.", + f); + kmem_free(unitp, sizeof (iommulib_unit_t)); + return (DDI_FAILURE); + } + + /* + * fini/register race conditions have been handled. Now create the + * IOMMU unit + */ + mutex_init(&unitp->ilu_lock, NULL, MUTEX_DEFAULT, NULL); + + mutex_enter(&unitp->ilu_lock); + unitp->ilu_unitid = ++iommulib_unit_ids; + unitp->ilu_ref = 0; + ndi_hold_devi(dip); + unitp->ilu_dip = dip; + unitp->ilu_ops = ops; + unitp->ilu_data = ops->ilops_data; + + unitp->ilu_next = iommulib_list; + unitp->ilu_prev = NULL; + iommulib_list->ilu_prev = unitp; + iommulib_list = unitp; + + mutex_exit(&unitp->ilu_lock); + + iommulib_num_units++; + + *handle = unitp; + + mutex_exit(&iommulib_lock); + + cmn_err(CE_NOTE, "%s: %s%d: Succesfully registered IOMMU unit " + "from vendor=%s, ops=%p, data=%p, IOMMULIB unitid=%u", + f, driver, instance, vendor, (void *)ops, (void *)unitp->ilu_data, + unitp->ilu_unitid); + + return (DDI_SUCCESS); +} + +int +iommulib_iommu_unregister(iommulib_handle_t handle) +{ + uint32_t unitid; + dev_info_t *dip; + int instance; + const char *driver; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + const char *f = "iommulib_unregister"; + + ASSERT(unitp); + + mutex_enter(&iommulib_lock); + mutex_enter(&unitp->ilu_lock); + + unitid = unitp->ilu_unitid; + dip = unitp->ilu_dip; + driver = ddi_driver_name(dip); + instance = ddi_get_instance(dip); + + if (unitp->ilu_ref != 0) { + mutex_exit(&unitp->ilu_lock); + mutex_exit(&iommulib_lock); + cmn_err(CE_WARN, "%s: %s%d: IOMMULIB handle is busy. Cannot " + "unregister IOMMULIB unitid %u", + f, driver, instance, unitid); + return (DDI_FAILURE); + } + unitp->ilu_unitid = 0; + ASSERT(unitp->ilu_ref == 0); + + if (unitp->ilu_prev == NULL) { + iommulib_list = unitp->ilu_next; + unitp->ilu_next->ilu_prev = NULL; + } else { + unitp->ilu_prev->ilu_next = unitp->ilu_next; + unitp->ilu_next->ilu_prev = unitp->ilu_prev; + } + + iommulib_num_units--; + + mutex_exit(&unitp->ilu_lock); + + mutex_destroy(&unitp->ilu_lock); + kmem_free(unitp, sizeof (iommulib_unit_t)); + + mutex_exit(&iommulib_lock); + + cmn_err(CE_WARN, "%s: %s%d: IOMMULIB handle (unitid=%u) successfully " + "unregistered", f, driver, instance, unitid); + + ndi_rele_devi(dip); + + return (DDI_SUCCESS); +} + +int +iommulib_nex_open(dev_info_t *rdip, uint_t *errorp) +{ + iommulib_unit_t *unitp; + int instance = ddi_get_instance(rdip); + const char *driver = ddi_driver_name(rdip); + const char *f = "iommulib_nex_open"; + + *errorp = 0; + DEVI(rdip)->devi_iommulib_handle = NULL; + + /* prevent use of IOMMU for AMD IOMMU's DMA */ + if (strcmp(driver, "amd_iommu") == 0) { + *errorp = ENOTSUP; + return (DDI_FAILURE); + } + + if (lookup_cache(rdip, &unitp) == DDI_SUCCESS) { + DEVI(rdip)->devi_iommulib_handle = + (iommulib_handle_t)unitp; + return (DDI_SUCCESS); + } + + + /* + * Ok this dip is not in the cache. Use the probe entry point + * to determine in a hardware specific manner whether this + * dip is controlled by an IOMMU. If yes, insert it into the + * cache and return the handle corresponding to the IOMMU unit. + */ + + mutex_enter(&iommulib_lock); + for (unitp = iommulib_list; unitp; unitp = unitp->ilu_next) { + if (unitp->ilu_ops->ilops_probe(rdip) == DDI_SUCCESS) + break; + } + + if (unitp == NULL) { + mutex_exit(&iommulib_lock); + if (iommulib_debug) { + char buf[MAXPATHLEN]; + cmn_err(CE_WARN, "%s: %s%d: devinfo node (%p): is not " + "controlled by an IOMMU: path=%s", f, driver, + instance, (void *)rdip, ddi_pathname(rdip, buf)); + } + *errorp = ENOTSUP; + return (DDI_FAILURE); + } + + mutex_enter(&unitp->ilu_lock); + unitp->ilu_ref++; + mutex_exit(&unitp->ilu_lock); + mutex_exit(&iommulib_lock); + + insert_cache(rdip, unitp); + + DEVI(rdip)->devi_iommulib_handle = unitp; + + return (DDI_SUCCESS); +} + +void +iommulib_nex_close(dev_info_t *rdip) +{ + char buf[MAXPATHLEN]; + iommulib_unit_t *unitp; + const char *driver; + int instance; + uint32_t unitid; + const char *f = "iommulib_nex_close"; + + unitp = (iommulib_unit_t *)DEVI(rdip)->devi_iommulib_handle; + if (unitp == NULL) + return; + + DEVI(rdip)->devi_iommulib_handle = NULL; + + /* + * Assume we don't support DR of IOMMUs. The mapping of + * dips to IOMMU units should not change. Let the mapping + * persist in the cache. + */ + + mutex_enter(&iommulib_lock); + mutex_enter(&unitp->ilu_lock); + unitid = unitp->ilu_unitid; + driver = ddi_driver_name(unitp->ilu_dip); + instance = ddi_get_instance(unitp->ilu_dip); + (void) ddi_pathname(rdip, buf); + unitp->ilu_ref--; + mutex_exit(&unitp->ilu_lock); + mutex_exit(&iommulib_lock); + + if (iommulib_debug) { + cmn_err(CE_NOTE, "%s: %s%d: closing IOMMU for dip (%p), " + "unitid=%u rdip path = %s", f, driver, instance, + (void *)rdip, unitid, buf); + } +} + +int +iommulib_nexdma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), + caddr_t arg, ddi_dma_handle_t *dma_handlep) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_allochdl(handle, dip, rdip, + attr, waitfp, arg, dma_handlep)); +} + +int +iommulib_nexdma_freehdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle) +{ + int error; + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + error = unitp->ilu_ops->ilops_dma_freehdl(handle, dip, + rdip, dma_handle); + + iommulib_nex_close(rdip); + + return (error); +} + +int +iommulib_nexdma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_bindhdl(handle, dip, rdip, dma_handle, + dmareq, cookiep, ccountp)); +} + +int +iommulib_nexdma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_unbindhdl(handle, dip, rdip, + dma_handle)); +} + +int +iommulib_nexdma_sync(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, off_t off, size_t len, + uint_t cache_flags) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_sync(handle, dip, rdip, dma_handle, + off, len, cache_flags)); +} + +int +iommulib_nexdma_win(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, uint_t win, off_t *offp, size_t *lenp, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_win(handle, dip, rdip, dma_handle, + win, offp, lenp, cookiep, ccountp)); +} + +/* Obsolete DMA routines */ + +int +iommulib_nexdma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *dma_handle) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_map(handle, dip, rdip, dmareq, + dma_handle)); +} + +int +iommulib_nexdma_mctl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, enum ddi_dma_ctlops request, + off_t *offp, size_t *lenp, caddr_t *objpp, uint_t cache_flags) +{ + iommulib_handle_t handle = DEVI(rdip)->devi_iommulib_handle; + iommulib_unit_t *unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + /* No need to grab lock - the handle is reference counted */ + return (unitp->ilu_ops->ilops_dma_mctl(handle, dip, rdip, dma_handle, + request, offp, lenp, objpp, cache_flags)); +} + +/* Utility routines invoked by IOMMU drivers */ +int +iommulib_iommu_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_allochdl(dip, rdip, attr, waitfp, arg, + handlep)); +} + +int +iommulib_iommu_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_freehdl(dip, rdip, handle)); +} + +int +iommulib_iommu_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_bindhdl(dip, rdip, handle, dmareq, + cookiep, ccountp)); +} + +int +iommulib_iommu_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_unbindhdl(dip, rdip, handle)); +} + +void +iommulib_iommu_dma_reset_cookies(dev_info_t *dip, ddi_dma_handle_t handle) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + nexops->nops_dma_reset_cookies(dip, handle); +} + +int +iommulib_iommu_dma_get_cookies(dev_info_t *dip, ddi_dma_handle_t handle, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_get_cookies(dip, handle, cookiep, ccountp)); +} + +int +iommulib_iommu_dma_sync(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, off_t off, size_t len, uint_t cache_flags) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_sync(dip, rdip, handle, off, len, + cache_flags)); +} + +int +iommulib_iommu_dma_win(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, uint_t win, off_t *offp, size_t *lenp, + ddi_dma_cookie_t *cookiep, uint_t *ccountp) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_win(dip, rdip, handle, win, offp, lenp, + cookiep, ccountp)); +} + +int +iommulib_iommu_dma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_map(dip, rdip, dmareq, handlep)); +} + +int +iommulib_iommu_dma_mctl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, enum ddi_dma_ctlops request, off_t *offp, + size_t *lenp, caddr_t *objpp, uint_t cache_flags) +{ + iommulib_nexops_t *nexops = lookup_nexops(dip); + if (nexops == NULL) + return (DDI_FAILURE); + return (nexops->nops_dma_mctl(dip, rdip, handle, request, offp, lenp, + objpp, cache_flags)); +} + +int +iommulib_iommu_getunitid(iommulib_handle_t handle, uint64_t *unitidp) +{ + iommulib_unit_t *unitp; + uint64_t unitid; + + unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + ASSERT(unitidp); + + mutex_enter(&unitp->ilu_lock); + unitid = unitp->ilu_unitid; + mutex_exit(&unitp->ilu_lock); + + ASSERT(unitid > 0); + *unitidp = (uint64_t)unitid; + + return (DDI_SUCCESS); +} + +dev_info_t * +iommulib_iommu_getdip(iommulib_handle_t handle) +{ + iommulib_unit_t *unitp; + dev_info_t *dip; + + unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + mutex_enter(&unitp->ilu_lock); + dip = unitp->ilu_dip; + ASSERT(dip); + ndi_hold_devi(dip); + mutex_exit(&unitp->ilu_lock); + + return (dip); +} + +iommulib_ops_t * +iommulib_iommu_getops(iommulib_handle_t handle) +{ + iommulib_unit_t *unitp; + iommulib_ops_t *ops; + + unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + mutex_enter(&unitp->ilu_lock); + ops = unitp->ilu_ops; + mutex_exit(&unitp->ilu_lock); + + ASSERT(ops); + + return (ops); +} + +void * +iommulib_iommu_getdata(iommulib_handle_t handle) +{ + iommulib_unit_t *unitp; + void *data; + + unitp = (iommulib_unit_t *)handle; + + ASSERT(unitp); + + mutex_enter(&unitp->ilu_lock); + data = unitp->ilu_data; + mutex_exit(&unitp->ilu_lock); + + ASSERT(data); + + return (data); +} + +/* + * Internal routines + */ + +static uint32_t +hashfn(uint64_t ptr) +{ + return (ptr % iommulib_cache_size); +} + +static int +lookup_cache(dev_info_t *rdip, iommulib_unit_t **unitpp) +{ + uint64_t idx; + iommulib_cache_t *cachep; + iommulib_unit_t *unitp; + int retval = DDI_FAILURE; + + *unitpp = NULL; + + mutex_enter(&iommulib_lock); + mutex_enter(&iommulib_cache_lock); + + ASSERT(iommulib_cache); + + idx = hashfn((uint64_t)(uintptr_t)rdip); + + ASSERT(idx < iommulib_cache_size); + + for (cachep = iommulib_cache[idx]; cachep; + cachep = cachep->cache_next) { + if (cachep->cache_rdip == rdip) + break; + } + + if (cachep != NULL) { + unitp = cachep->cache_unit; + mutex_enter(&unitp->ilu_lock); + unitp->ilu_ref++; + mutex_exit(&unitp->ilu_lock); + *unitpp = unitp; + retval = DDI_SUCCESS; + } + + mutex_exit(&iommulib_cache_lock); + mutex_exit(&iommulib_lock); + return (retval); +} + +static void +insert_cache(dev_info_t *rdip, iommulib_unit_t *unitp) +{ + uint32_t idx; + iommulib_cache_t *cachep; + + mutex_enter(&iommulib_lock); + mutex_enter(&iommulib_cache_lock); + + ASSERT(iommulib_cache); + + idx = hashfn((uint64_t)(uintptr_t)rdip); + + ASSERT(idx < iommulib_cache_size); + + for (cachep = iommulib_cache[idx]; cachep; + cachep = cachep->cache_next) { + if (cachep->cache_rdip == rdip) + break; + } + + if (cachep == NULL) { + cachep = kmem_zalloc(sizeof (iommulib_cache_t), KM_SLEEP); + cachep->cache_rdip = rdip; + cachep->cache_unit = unitp; /* ref-count set by caller */ + cachep->cache_prev = NULL; + cachep->cache_next = iommulib_cache[idx]; + if (cachep->cache_next) + cachep->cache_next->cache_prev = cachep; + iommulib_cache[idx] = cachep; + } + + mutex_exit(&iommulib_cache_lock); + mutex_exit(&iommulib_lock); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/iommulib/Makefile Mon Sep 15 21:50:24 2008 -0700 @@ -0,0 +1,98 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "@(#)Makefile 1.8 07/01/10 SMI" +# +# This makefile drives the production of the IOMMULIB misc +# kernel module. +# +# intel architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = iommulib +OBJECTS = $(IOMMULIB_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(IOMMULIB_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) +INC_PATH += -I$(UTSBASE)/intel +INC_PATH += -I$(UTSBASE)/i86pc + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(CONFMOD) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Overrides. +# +DEBUG_DEFS += $(DEBUG_FLGS) + +# +# lint pass one non-enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# 3rd party code is not lint clean +# +LINTFLAGS += -errwarn=%all + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/intel/sys/Makefile Mon Sep 15 22:45:56 2008 -0700 +++ b/usr/src/uts/intel/sys/Makefile Mon Sep 15 21:50:24 2008 -0700 @@ -22,7 +22,6 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" # include ../../../Makefile.master @@ -47,6 +46,7 @@ fp.h \ frame.h \ inline.h \ + iommulib.h \ hypervisor.h \ kd.h \ kdi_machimpl.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/sys/iommulib.h Mon Sep 15 21:50:24 2008 -0700 @@ -0,0 +1,250 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_IOMMULIB_H +#define _SYS_IOMMULIB_H + +#pragma ident "@(#)iommulib.h 1.3 08/08/31 SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/ddi_impldefs.h> + +#ifdef _KERNEL + +typedef enum { + INVALID_VENDOR = 0, + AMD_IOMMU, + INTEL_IOMMU +} iommulib_vendor_t; + +typedef enum { + IOMMU_OPS_VERSION_INVALID = 0, + IOMMU_OPS_VERSION_1 = 1 +} iommulib_opsversion_t; + +#define IOMMU_OPS_VERSION IOMMU_OPS_VERSION_1 + +typedef struct iommulib_ops { + iommulib_opsversion_t ilops_vers; + iommulib_vendor_t ilops_vendor; + char *ilops_id; + void *ilops_data; + + int (*ilops_probe)(dev_info_t *rdip); + + int (*ilops_dma_allochdl)(iommulib_handle_t handle, + dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *attr, + int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *dma_handlep); + + int (*ilops_dma_freehdl)(iommulib_handle_t handle, + dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle); + + int (*ilops_dma_bindhdl)(iommulib_handle_t handle, dev_info_t *dip, + dev_info_t *rdip, ddi_dma_handle_t dma_handle, + struct ddi_dma_req *dmareq, ddi_dma_cookie_t *cookiep, + uint_t *ccountp); + + int (*ilops_dma_unbindhdl)(iommulib_handle_t handle, + dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t dma_handle); + + int (*ilops_dma_sync)(iommulib_handle_t handle, dev_info_t *dip, + dev_info_t *rdip, ddi_dma_handle_t dma_handle, off_t off, + size_t len, uint_t cache_flags); + + int (*ilops_dma_win)(iommulib_handle_t handle, dev_info_t *dip, + dev_info_t *rdip, ddi_dma_handle_t dma_handle, uint_t win, + off_t *offp, size_t *lenp, ddi_dma_cookie_t *cookiep, + uint_t *ccountp); + + + /* Obsolete DMA routines */ + + int (*ilops_dma_map)(iommulib_handle_t handle, dev_info_t *dip, + dev_info_t *rdip, struct ddi_dma_req *dmareq, + ddi_dma_handle_t *dma_handle); + + int (*ilops_dma_mctl)(iommulib_handle_t handle, dev_info_t *dip, + dev_info_t *rdip, ddi_dma_handle_t dma_handle, + enum ddi_dma_ctlops request, off_t *offp, size_t *lenp, + caddr_t *objpp, uint_t cache_flags); + +} iommulib_ops_t; + +#define IOMMU_USED(dip) (DEVI(dip)->devi_iommulib_handle != NULL) + +typedef enum { + IOMMU_NEXOPS_VERSION_INVALID = 0, + IOMMU_NEXOPS_VERSION_1 = 1 +} iommulib_nexops_version_t; + +#define IOMMU_NEXOPS_VERSION IOMMU_NEXOPS_VERSION_1 + +typedef struct iommulib_nexops { + iommulib_nexops_version_t nops_vers; + char *nops_id; + void *nops_data; + + int (*nops_dma_allochdl)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep); + + int (*nops_dma_freehdl)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle); + + int (*nops_dma_bindhdl)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + + int (*nops_dma_unbindhdl)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle); + + void (*nops_dma_reset_cookies)(dev_info_t *dip, + ddi_dma_handle_t handle); + + int (*nops_dma_get_cookies)(dev_info_t *dip, ddi_dma_handle_t handle, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + + int (*nops_dma_sync)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, off_t off, size_t len, uint_t cache_flags); + + int (*nops_dma_win)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, uint_t win, off_t *offp, size_t *lenp, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + + int (*nops_dma_map)(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep); + + int (*nops_dma_mctl)(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, enum ddi_dma_ctlops request, off_t *offp, + size_t *lenp, caddr_t *objpp, uint_t cache_flags); +} iommulib_nexops_t; + +struct iommulib_nex; +typedef struct iommulib_nex *iommulib_nexhandle_t; + +/* + * Interfaces for nexus drivers - typically rootnex + */ + +int iommulib_nexus_register(dev_info_t *dip, iommulib_nexops_t *nexops, + iommulib_nexhandle_t *handle); + +int iommulib_nexus_unregister(iommulib_nexhandle_t handle); + +int iommulib_nex_open(dev_info_t *rdip, uint_t *errorp); +void iommulib_nex_close(dev_info_t *rdip); + +int iommulib_nexdma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), + caddr_t arg, ddi_dma_handle_t *dma_handlep); + +int iommulib_nexdma_freehdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle); + +int iommulib_nexdma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + +int iommulib_nexdma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle); + +int iommulib_nexdma_sync(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, off_t off, size_t len, + uint_t cache_flags); + +int iommulib_nexdma_win(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, uint_t win, off_t *offp, size_t *lenp, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + +int iommulib_nexdma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *dma_handle); + +int iommulib_nexdma_mctl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t dma_handle, enum ddi_dma_ctlops request, + off_t *offp, size_t *lenp, caddr_t *objpp, uint_t cache_flags); + +/* + * Interfaces for IOMMU drivers provided by IOMMULIB + */ + +int iommulib_iommu_register(dev_info_t *dip, iommulib_ops_t *ops, + iommulib_handle_t *handle); + +int iommulib_iommu_unregister(iommulib_handle_t handle); + +int iommulib_iommu_getunitid(iommulib_handle_t handle, uint64_t *unitidp); + +dev_info_t *iommulib_iommu_getdip(iommulib_handle_t handle); + +iommulib_ops_t *iommulib_iommu_getops(iommulib_handle_t handle); + +void *iommulib_iommu_getdata(iommulib_handle_t handle); + + +/* Interfaces for IOMMU drivers provided by NEXUS drivers (typically rootnex) */ + +int iommulib_iommu_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_attr_t *attr, int (*waitfp)(caddr_t), caddr_t arg, + ddi_dma_handle_t *handlep); + +int iommulib_iommu_dma_freehdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle); + +int iommulib_iommu_dma_bindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, struct ddi_dma_req *dmareq, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + +int iommulib_iommu_dma_unbindhdl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle); + +void iommulib_iommu_dma_reset_cookies(dev_info_t *dip, ddi_dma_handle_t handle); + +int iommulib_iommu_dma_get_cookies(dev_info_t *dip, ddi_dma_handle_t handle, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + +int iommulib_iommu_dma_sync(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, off_t off, size_t len, uint_t cache_flags); + +int iommulib_iommu_dma_win(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, uint_t win, off_t *offp, size_t *lenp, + ddi_dma_cookie_t *cookiep, uint_t *ccountp); + +int iommulib_iommu_dma_map(dev_info_t *dip, dev_info_t *rdip, + struct ddi_dma_req *dmareq, ddi_dma_handle_t *handlep); + +int iommulib_iommu_dma_mctl(dev_info_t *dip, dev_info_t *rdip, + ddi_dma_handle_t handle, enum ddi_dma_ctlops request, off_t *offp, + size_t *lenp, caddr_t *objpp, uint_t cache_flags); + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_IOMMULIB_H */