zuluvm.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* zuluvm module
*
* Provides services required by the XVR-4000 graphics accelerator (zulu)
* that are not provided by the ddi. See PSARC 2002/231.
*
* Zulu has 2 dma engines with built in MMUs. zuluvm provides TLB miss
* interrupt support obtaining virtual to physical address translations
* using the XHAT interface PSARC/2003/517.
*
* The module has 3 components. This file, sun4u/vm/zulu_hat.c, and the
* assembly language routines in sun4u/ml/zulu_asm.s and
*
* The interrupt handler is a data bearing mondo interrupt handled at TL=1
* If no translation is found in the zulu hat's tsb, or if the tsb is locked by
* C code, the handler posts a soft interrupt which wakes up a parked
* thread belonging to zuludaemon(1M).
*/
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_subrdefs.h>
#include <sys/machsystm.h>
#include <sys/tnf_probe.h>
#include <vm/seg_enum.h>
#include <vm/faultcode.h>
#include <sys/zulu_hat.h>
#define ZULUVM_GET_PAGE(val) \
#define ZULUVM_SET_STATE(_z, b, c) \
#define ZULUVM_GET_STATE(_z) \
#define ZULUVM_SET_IDLE(_z) \
(ZULUVM_INO_MASK & (_n))
static void zuluvm_stop(zuluvm_state_t *, int, char *);
static int zuluvm_driver_attach(zuluvm_state_t *);
static int zuluvm_driver_detach(zuluvm_state_t *);
static void zuluvm_retarget_intr(void *arg);
extern const unsigned int _mmu_pageshift;
extern int zuluvm_base_pgsize;
int zuluvm_fast_tlb = 1;
#ifdef DEBUG
int zuluvm_debug_state = 0;
#endif
unsigned long zuluvm_ctx_locked = 0;
/*
* Module linkage information for the kernel.
*/
extern struct mod_ops mod_miscops;
"sun4u support " ZULUVM_MOD_VERSION
};
static struct modlinkage modlinkage = {
(void *)&modlmisc,
};
int
_init(void)
{
if (zulu_hat_init() != 0) {
return (ZULUVM_ERROR);
}
return (mod_install(&modlinkage));
}
int
_fini(void)
{
(void) zulu_hat_destroy();
return (mod_remove(&modlinkage));
}
int
{
}
/*
* currently the kernel driver makes the following assumptions:
* - there is only one TLB miss per zulu device handled at
* any given time
* ==> we only need local data storage per device, not per DMA
* ==> a page fault will block the DMA engine until the fault
* is resolved
* ==> a pagefault will not trigger a zulu DMA context switch
*
* If we want to implement asynnchronous zulu page fault, then we
* need to keep track of outstanding faults while zulu DMA runs
* in a different context.
*/
static int
{
int error;
(void) addr;
if (tag == 0) { /* not coming from preload */
if (state != ZULUVM_STATE_INTR_PENDING) {
return (ZULUVM_MISS_CANCELED);
}
}
if (!(tlbtype & ZULUVM_ITLB_FLAG) &&
t_size != zuluvm_base_pgsize &&
t_size != ZULU_TTE4M) {
}
/*
* if the caller is zuluvm_preload, then we need to pass
* back the page size so it can add the right offset.
*/
if (size)
return (error);
}
static void
{
while (state != ZULUVM_STATE_STOPPED) {
#ifdef DEBUG
if (zuluvm_debug_state)
#endif
}
}
/*
* Executed with the context of the parked zulu deamon thread,
* uses zulu_hat_load to resolve the miss.
* The tte is loaded and miss done called by the function zuluvm_load_tte
* which is called from zulu_hat
*
* This function is synchronized with the zuluvm_as_free.
* zuluvm_as_free will block until miss servicing is complete.
*
* There is a race condition between as_free and the zulu tlb miss
* soft interrupt:
* - queue zulu interrupt
* - process dies, as_free runs
* - interrupt gets scheduled and runs as_fault on the
* already freed as.
* This is solved by keeping track of current zulu dma processes
* and invalidating them in zuluvm_as_free.
*/
{
int error;
int flag = 0;
int wait = 0;
int tlbtype;
void *arg;
/*
* select the correct dma engine and remember the
* the as_free synchronization flags.
*/
switch (tlbtype) {
case ZULUVM_ITLB1:
case ZULUVM_DMA1:
flag |= ZULUVM_DO_INTR1;
break;
case ZULUVM_ITLB2:
case ZULUVM_DMA2:
flag |= ZULUVM_DO_INTR2;
break;
}
#ifdef DEBUG
if (zuluvm_debug_state)
#endif
if (state != ZULUVM_STATE_INTR_PENDING &&
return (1);
}
/*
* block the as_free callback in case it comes in
*/
/*
* check if this as is still valid
*/
/*
* we are on our way out, wake up the as_free
* callback if it is waiting for us
*/
if (state != ZULUVM_STATE_INTR_PENDING) {
}
return (1);
}
switch (error) {
case ZULUVM_CTX_LOCKED:
/*
* trap handler found that zulu_hat had the lock bit set
* rather than block in the fast trap handler, it punts
* in this rare instance
*/
/*FALLTHROUGH*/
case ZULUVM_TTE_DELAY:
/*
* fast tlb handler was skipped, see zuluvm_fast_tlb flag
*/
/*FALLTHROUGH*/
case ZULUVM_NO_TTE:
/*
* no TSB entry and TTE in the hash
*/
if (error) {
} else {
return (1);
}
default:
/*
* error case, fall through and tell zulu driver to abort DMA
*/
break;
}
if (error != ZULUVM_MISS_CANCELED) {
if (state != ZULUVM_STATE_WRITE_TTE) {
}
}
/*
* synchronize with as_free callback
* It will set the wait flag, in that case we send
* a wake up.
*/
return (1);
}
void
{
int error;
int flag = 0;
int wait = 0;
if (error != ZULUVM_MISS_CANCELED) {
if (state != ZULUVM_STATE_WRITE_TTE) {
}
}
/*
* synchronize with as_free callback
* It will set the wait flag, in that case we send
* a wake up.
*/
switch (tlbtype) {
case ZULUVM_ITLB1:
case ZULUVM_DMA1:
break;
case ZULUVM_ITLB2:
case ZULUVM_DMA2:
break;
}
} else {
}
}
/*
* This function provides the faulting thread for zulu page faults
* It is call from the device driver in response to an ioctl issued
* by a zuludaemon thread.
* It sits in cv_wait_sig until it gets woken up by a signal or
* zulu tlb miss soft interrupt.
*/
int
{
int rval;
for (;;) {
if (rval == 0)
break;
}
return (rval);
}
/*
* zulu soft interrupt handler, just triggers the parked zulu fault
* thread
*/
/*ARGSUSED*/
{
int tlbtype;
void *arg;
} else {
}
return (1);
}
/* ***** public interface for process mapping events (hat layer) ***** */
/*
* If the page size matches the Zulu page sizes then just pass
* it thru. If not then emulate the page demap with demaps of
* smaller page size.
*/
/* ARGSUSED */
void
{
void *ddarg;
return;
if (size != zuluvm_base_pgsize &&
size != ZULU_TTE4M) {
int i;
for (i = 0; i < cnt; i++) {
i << ZULU_HAT_BP_SHIFT;
}
} else {
}
} else {
}
}
/*
* An entire context has gone away, just pass it thru
*/
void
{
void *ddarg;
return;
}
static int
{
int i;
for (i = 0; i < ZULUVM_MAX_DEV; i++) {
if (zuluvm_devtab[i] == NULL) {
zuluvm_devtab[i] = zdev;
break;
}
}
if (i >= ZULUVM_MAX_DEV)
return (ZULUVM_ERROR);
if (zulu_hat_attach((void *)zdev) != 0) {
return (ZULUVM_ERROR);
}
#ifdef ZULUVM_STATS
#endif
for (i = 0; i < 50; i++)
return (ZULUVM_SUCCESS);
}
static int
{
int i;
for (i = 0; i < ZULUVM_MAX_DEV; i++) {
if (zuluvm_devtab[i] == zdev) {
zuluvm_devtab[i] = NULL;
break;
}
}
if (zulu_hat_detach((void *)zdev) == 0) {
return (ZULUVM_SUCCESS);
} else {
return (ZULUVM_ERROR);
}
}
/*
* init the zulu kernel driver (variables, locks, etc)
*/
int
{
int error = ZULUVM_SUCCESS;
int i;
return (ZULUVM_VERSION_MISMATCH);
zuluvm_dops = ops;
zuluvm_pagesizes[i] = size++;
}
zuluvm_pagesizes[i] = -1;
return (error);
}
/*
* cleanup afterwards
*/
int
zuluvm_fini(void)
{
zuluvm_dops = NULL;
return (ZULUVM_SUCCESS);
}
/*
* allocate a zulu kernel driver instance for this zulu device
*/
int
{
int error = ZULUVM_SUCCESS;
if (error != ZULUVM_SUCCESS) {
return (ZULUVM_NO_DEV);
}
if (error != ZULUVM_SUCCESS) {
if (error != ZULUVM_SUCCESS)
return (error);
return (ZULUVM_NO_DEV);
}
(void *)zdev);
(void *)zdev));
(void *)zdev));
return (ZULUVM_SUCCESS);
}
/*
* free a zulu kernel driver instance
*/
int
{
int error;
return (ZULUVM_NO_DEV);
return (ZULUVM_NO_DEV);
}
if (error != ZULUVM_SUCCESS)
return (error);
return (ZULUVM_SUCCESS);
}
/*
* find the as in the list of active zulu processes
* The caller has to hold zdev->proc_lck
*/
static zuluvm_proc_t *
{
zuluvm_proc_t *p;
return (p);
}
}
return (NULL);
}
void
{
int wait = 0;
int flag = 0;
int valid;
(void) events;
/*
* if this entry is still valid, then we need to sync
* with zuluvm_tlb_handler rountine.
*/
if (valid) {
wait |= ZULUVM_DO_INTR1;
}
wait |= ZULUVM_DO_INTR2;
}
if (flag) {
/*
* wait until the tlb miss is resloved
*/
}
}
}
/*
* prevent any further tlb miss processing for this hat
*/
}
/*
* decrement the ref count and do the appropriate
* if it drops to zero.
*/
}
/*
* notify zulu vm driver about a new process going to
* use zulu DMA. Create a zulu_hat.
*/
int
{
int refcnt;
return (ZULUVM_ERROR);
}
} else {
return (ZULUVM_ERROR);
}
}
return (ZULUVM_SUCCESS);
}
void
{
}
/*
* decrement ref count and free data if it drops to zero
*/
static int
{
int refcnt;
if (refcnt == 0) {
else
}
return (refcnt);
}
/*
* this process is not longer using DMA, all entries
* have been removed from the TLB.
*/
int
{
int refcnt;
}
}
return (ZULUVM_SUCCESS);
}
/*
* barrier sync for device driver
* blocks until zuluvm_tlbmiss_tl1 function is done
*/
void
{
int state;
int cnt = 0;
do {
cnt++;
} while (state == ZULUVM_STATE_TLB_PENDING);
}
/*
* setup DMA handling for this handle
*/
int
{
int error = ZULUVM_NO_DEV;
return (ZULUVM_NO_HAT);
}
*tsbreg = 0;
#ifdef DEBUG
if (zuluvm_debug_state)
#endif
while (state != ZULUVM_STATE_IDLE) {
#ifdef DEBUG
if (zuluvm_debug_state)
" state %d\n", state);
#endif
if (state != ZULUVM_STATE_IDLE)
delay(1);
}
}
}
switch (dma) {
case ZULUVM_DMA1:
break;
case ZULUVM_DMA2:
break;
default:
}
if (error == ZULUVM_SUCCESS) {
} else {
printf("invalid context value: %d\n",
}
} else {
}
}
return (error);
}
/*
* preload TLB
* this will try to pre-set the zulu tlb, mainly used for dma engine 2,
* video read-back.
*/
int
{
int i;
int error = ZULUVM_SUCCESS;
switch (dma) {
case ZULUVM_DMA1:
break;
case ZULUVM_DMA2:
break;
}
return (ZULUVM_NO_HAT);
}
/*
* need to release this to avoid recursive enter in zuluvm_load_tte
* which gets called from zulu_hat_memload()
*/
for (i = 0; i < num; i++) {
int pg_size;
int res;
int first = 1;
while (size > 0) {
break;
}
&pg_size);
break;
}
if (first) {
first = 0;
pg_size);
} else {
}
}
}
return (ZULUVM_SUCCESS);
}
/*
* destroy DMA handling for this handle
*/
int
{
int error = ZULUVM_NO_DEV;
#ifdef DEBUG
if (zuluvm_debug_state)
#endif
int doit = 1;
while (doit) {
switch (state) {
case ZULUVM_STATE_CANCELED:
case ZULUVM_STATE_STOPPED:
doit = 0;
break;
case ZULUVM_STATE_IDLE:
break;
default:
}
#ifdef DEBUG
if (zuluvm_debug_state)
" state %d\n", state);
#endif
}
}
while (state != ZULUVM_STATE_STOPPED) {
#ifdef DEBUG
if (zuluvm_debug_state)
state);
#endif
if (state != ZULUVM_STATE_STOPPED)
delay(1);
}
switch (dma) {
case ZULUVM_DMA1:
break;
case ZULUVM_DMA2:
break;
default:
}
if (proc) {
}
} else {
}
return (error);
}
static void
{
int i, idx;
for (i = 0; i < ZULUVM_MAX_INTR; i++) {
cpu = intr_dist_cpuid();
else
}
}
}
static void
zuluvm_retarget_intr(void *arg)
{
}
int
{
return (ZULUVM_NO_DEV);
}
!= DDI_SUCCESS) {
return (ZULUVM_ERROR);
}
return (ZULUVM_SUCCESS);
}
int
{
return (ZULUVM_NO_DEV);
}
/* remove from distributin list */
return (ZULUVM_SUCCESS);
}
int
{
return (ZULUVM_NO_DEV);
}
return (ZULUVM_BAD_IDX);
}
return (ZULUVM_SUCCESS);
}
int
{
return (ZULUVM_NO_DEV);
}
return (ZULUVM_BAD_IDX);
}
return (ZULUVM_SUCCESS);
}
static int
{
int *intr;
int i;
"portid", -1);
return (ZULUVM_ERROR);
}
for (i = 0; i < ZULUVM_MAX_INTR; i++) {
}
if (nintr == 0) {
return (ZULUVM_ERROR);
}
if (nintr >= ZULUVM_MAX_INTR) {
return (ZULUVM_ERROR);
}
for (i = 0; i < nintr; i++) {
}
} else {
}
return (ZULUVM_SUCCESS);
}
/* *** enf of zulu *** */