/*
* 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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PCI nexus DVMA relocation routines.
*
* These routines handle the interactions with the HAT layer to
* implement page relocation for page(s) which have active DMA handle
* bindings when DVMA is being used for those handles.
*
* The current modus operandi is as follows:
*
* Object binding: register the appropriate callback for each page
* of the kernel object while obtaining the PFN for the DVMA page.
*
* Object unbinding: unregister the callback for each page of the
* kernel object.
*
* Relocation request:
* 1) Suspend the bus and sync the caches.
* 2) Remap the DVMA object using the new provided PFN.
* 3) Unsuspend the bus.
*
* The relocation code runs with CPUs captured (idling in xc_loop())
* so we can only acquire spinlocks at PIL >= 13 for synchronization
* within those codepaths.
*/
#include <sys/sysmacros.h>
#include <sys/machsystm.h>
#include <sys/ddi_impldefs.h>
/*LINTLIBRARY*/
void
{
int i;
if (!PCI_DMA_CANRELOC(mp))
return;
MP_HAT_CB_COOKIE(mp, 0));
MP_HAT_CB_COOKIE(mp, i));
vaddr += IOMMU_PAGE_SIZE;
}
}
static int
{
int index;
if (flags == HAT_POSTUNSUSPEND) {
ASSERT(pci_reloc_presuspend > 0);
if (--pci_reloc_presuspend == 0) {
}
return (0);
}
ASSERT(pci_reloc_suspend > 0);
} else {
}
return (EIO);
return (EIO);
return (0);
}
/*
* Log a warning message if a callback is still registered on
* a page which is being freed. This is indicative of a driver
* bug -- DMA handles are bound, and the memory is being freed by
* the VM subsystem without an unbind call on the handle first.
*/
static int
{
if (errorcode == HAT_CB_ERR_LEAKED) {
return (0);
}
/* unknown error code, unhandled so panic */
return (EINVAL);
}
/*
* pci DVMA remap entry points
*
* Called in response to a DDI_DMA_REMAP DMA ctlops command.
* Remaps the region specified in the underlying IOMMU. Safe
* to assume that the bus was quiesced and ddi_dma_sync() was
* invoked by the caller before we got to this point.
*/
int
{
int idx;
"pci_dvma_remap: dvma_pg 0x%llx len 0x%llx idx 0x%x\n",
return (DDI_SUCCESS);
}
void
{
int i;
/* make sure we don't exceed reserved boundary */
"%s%d: fdvma remap index(%lx)+pgs(%lx) exceeds limit\n",
return;
}
if (pfn == PFN_INVALID)
goto bad_pfn;
if (i == 0)
/* XXX assumes iommu and mmu has same page size */
}
return;
}
static int
{
int i;
/*
* It isn't safe to do relocation if all of the IOMMU
* mappings haven't yet been established at this index.
*/
for (i = 0; i < mp->dmai_ndvmapages; i++) {
return (0); /* found a valid index */
}
return (EAGAIN);
}
static int
{
int i;
if (flags == HAT_POSTUNSUSPEND) {
if (--pci_reloc_presuspend == 0) {
}
return (0);
}
/*
* This virtual page can have multiple cookies that refer
* to it within the same handle. We must walk the whole
* table for this DMA handle finding all the cookies, and
* update all of them. Sigh.
*/
for (i = 0; i < mp->dmai_ndvmapages; i++) {
int index;
}
}
return (EIO);
return (0);
}
void
{
int i;
for (i = 0; i < npgs && pci_dvma_remap_enabled;
i++, kva += IOMMU_PAGE_SIZE)
}
static int
{
int ret;
if (flags == HAT_PRESUSPEND) {
"dvma-remap-supported"))
return (ENOTSUP);
if (!PCI_DMA_ISMAPPED(mp))
return (EAGAIN);
if (ret != 0)
return (ret);
} else if (!PCI_DMA_ISDVMA(mp))
return (EINVAL);
/*
* Acquire the exclusive right to relocate a PCI DMA page,
* since we later have to pause CPUs which could otherwise
* lead to all sorts of synchronization headaches.
*/
if (pci_reloc_thread != curthread) {
while (pci_reloc_thread != NULL) {
}
ASSERT(pci_reloc_suspend == 0);
}
return (0);
}
return (EIO);
return (EIO);
return (0);
}
/*
* Register two callback types: one for normal DVMA and the
* other for fast DVMA, since each method has a different way
* of tracking the PFNs behind a handle.
*/
void
pci_reloc_init(void)
{
pci_dma_relocerr, 1);
}
void
pci_reloc_fini(void)
{
}