4.4BSD/usr/src/sys/tahoe/vba/vba.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1987, 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)vba.c	7.6 (Berkeley) 12/16/90
 */

/*
 * Tahoe VERSAbus adapator support routines.
 */

#include "sys/param.h"
#include "sys/buf.h"
#include "sys/cmap.h"
#include "sys/conf.h"
#include "dk.h"
#include "sys/map.h"
#include "sys/systm.h"
#include "sys/user.h"
#include "sys/vmparam.h"
#include "sys/vmmac.h"
#include "sys/proc.h"
#include "sys/syslog.h"
#include "sys/malloc.h"

#include "../include/mtpr.h"
#include "../include/pte.h"

#include "../vba/vbavar.h"

/*
 * Allocate private page map and intermediate buffer
 * for a VERSAbus device, large enough for maximum transfer size.
 * Intermediate buffer 
 * Make intermediate buffer uncacheable.
 */
vbainit(vb, xsize, flags)
	register struct vb_buf *vb;
	int xsize, flags;
{
	register struct pte *pte;
	register n;

	vb->vb_flags = flags;
	if (vbmapalloc((int)btoc(xsize) + 1, &vb->vb_map, &vb->vb_utl) == 0) {
		printf("vbmap exhausted\n");
		return (0);
	}
	n = roundup(xsize, NBPG);
	vb->vb_bufsize = n;
	if (vb->vb_rawbuf == 0)
		vb->vb_rawbuf = (caddr_t)malloc((u_long)n, M_DEVBUF, M_NOWAIT);
	if (vb->vb_rawbuf == 0) {
		printf("no memory for device buffer\n");
		return (0);
	}
	if ((int)vb->vb_rawbuf & PGOFSET)
		panic("vbinit pgoff");
	vb->vb_physbuf = (u_long)kvtophys(vb->vb_rawbuf);
	if (flags & VB_20BIT)
		vb->vb_maxphys = btoc(VB_MAXADDR20);
	else if (flags & VB_24BIT)
		vb->vb_maxphys = btoc(VB_MAXADDR24);
	else
		vb->vb_maxphys = btoc(VB_MAXADDR32);
	if (btoc(vb->vb_physbuf + n) > vb->vb_maxphys)
		panic("vbinit physbuf");
	
	/*
	 * Make raw buffer pages uncacheable.
	 */
	pte = kvtopte(vb->vb_rawbuf);
	for (n = btoc(n); n--; pte++)
		pte->pg_nc = 1;
	mtpr(TBIA, 0);
	return (1);
}

/*
 * Due to unknown hardware or software errors, some sites have problems
 * with strange crashes or corruption of text images when DMA is attempted
 * to kernel addresses spanning a page boundary, or to user addresses
 * (even if the buffer is physically contiguous).  To avoid this behavior,
 * the following toggles inhibit such transfers when set.
 *	vba_copyk: copy transfers to kernel address that span a page boundary
 *	vba_copyu: copy transfers to user addresses
 */
#ifndef VBA_TRICKY
int vba_copyk = 1;
int vba_copyu = 1;
#else
int vba_copyk = 0;
int vba_copyu = 0;
#endif

/*
 * Check a transfer to see whether it can be done directly
 * to the destination buffer, or whether it must be copied.
 * On Tahoe, the lack of a bus I/O map forces data to be copied
 * to a physically-contiguous buffer whenever one of the following is true:
 *	1) The data length is not a multiple of sector size.
 *	   (The swapping code does this, unfortunately.)
 *	2) The buffer is not physically contiguous and the controller
 *	   does not support scatter-gather operations.
 *	3) The physical address for I/O is higher than addressible
 *	   by the device.
 * This routine is called by the start routine.
 * If copying is necessary, the intermediate buffer is mapped;
 * if the operation is a write, the data is copied into the buffer.
 * It returns the physical address of the first byte for DMA, to
 * be presented to the controller.
 */
u_long
vbasetup(bp, vb, sectsize)
	register struct buf *bp;
	register struct vb_buf *vb;
	int sectsize;
{
	register struct pte *spte, *dpte;
	register int p, i;
	int npf, o, v;

	o = (int)bp->b_un.b_addr & PGOFSET;
	npf = btoc(bp->b_bcount + o);
	vb->vb_iskernel = (((int)bp->b_un.b_addr & KERNBASE) == KERNBASE);
	if (vb->vb_iskernel) {
		spte = kvtopte(bp->b_un.b_addr);
if (vba_copyk && (o != 0 || npf > 1)) goto copy;
	} else {
		spte = vtopte((bp->b_flags&B_DIRTY) ? &proc[2] : bp->b_proc,
		    btop(bp->b_un.b_addr));
if (vba_copyu) goto copy;
	}
	if (bp->b_bcount % sectsize != 0 || (o & (sizeof(long) - 1)) != 0)
		goto copy;
	else if ((vb->vb_flags & VB_SCATTER) == 0 ||
	    vb->vb_maxphys != btoc(VB_MAXADDR32)) {
		dpte = spte;
		p = (dpte++)->pg_pfnum;
		for (i = npf; --i > 0; dpte++) {
			if ((v = dpte->pg_pfnum) != p + CLSIZE &&
			    (vb->vb_flags & VB_SCATTER) == 0)
				goto copy;
			if (p >= vb->vb_maxphys)
				goto copy;
			p = v;
		}
		if (p >= vb->vb_maxphys)
			goto copy;
	}
	vb->vb_copy = 0;
	if (vb->vb_iskernel)
		vbastat.k_raw++;
	else
		vbastat.u_raw++;
	return ((spte->pg_pfnum << PGSHIFT) + o);

copy:
	vb->vb_copy = 1;
	if (bp->b_bcount > vb->vb_bufsize)
		panic("vba xfer too large");
	if (vb->vb_iskernel) {
		if ((bp->b_flags & B_READ) == 0)
			bcopy(bp->b_un.b_addr, vb->vb_rawbuf,
			    (unsigned)bp->b_bcount);
		vbastat.k_copy++;
	} else  {
		dpte = vb->vb_map;
		for (i = npf, p = (int)vb->vb_utl; i--; p += NBPG) {
			*(int *)dpte++ = (spte++)->pg_pfnum |
			    PG_V | PG_KW | PG_N;
			mtpr(TBIS, p);
		}
		if ((bp->b_flags & B_READ) == 0)
			bcopy(vb->vb_utl + o, vb->vb_rawbuf,
			    (unsigned)bp->b_bcount);
		vbastat.u_copy++;
	}
	return (vb->vb_physbuf);
}

/*
 * Called by the driver's interrupt routine, after DMA is completed.
 * If the operation was a read, copy data to final buffer if necessary
 * or invalidate data cache for cacheable direct buffers.
 * Similar to the vbastart routine, but in the reverse direction.
 */
vbadone(bp, vb)
	register struct buf *bp;
	register struct vb_buf *vb;
{
	register npf;
	register caddr_t v;
	int o;

	if (bp->b_flags & B_READ) {
		o = (int)bp->b_un.b_addr & PGOFSET;
		if (vb->vb_copy) {
			if (vb->vb_iskernel)
				bcopy(vb->vb_rawbuf, bp->b_un.b_addr,
				    (unsigned)(bp->b_bcount - bp->b_resid));
			else {
				bcopy(vb->vb_rawbuf, vb->vb_utl + o,
				    (unsigned)(bp->b_bcount - bp->b_resid));
				dkeyinval(bp->b_proc);
			}
		} else {
			if (vb->vb_iskernel) {
				npf = btoc(bp->b_bcount + o);
				for (v = bp->b_un.b_addr; npf--; v += NBPG)
					mtpr(P1DC, (int)v);
			} else
				dkeyinval(bp->b_proc);
		}
	}
}

/*
 * Set up a scatter-gather operation for SMD/E controller.
 * This code belongs half-way between {hd,vd}.c and this file.
 */

#include "dk.h"
#if NVD > 0
#include "vdreg.h"

vd_sgsetup(bp, vb, sg)
	register struct buf *bp;
	struct vb_buf *vb;
	struct trsg *sg;
{
	register struct pte *spte;
	register struct addr_chain *adr;
	register int i;
	int o;

	vb->vb_iskernel = (((int)bp->b_un.b_addr & KERNBASE) == KERNBASE);
	vb->vb_copy = 0;
	if (vb->vb_iskernel) {
		spte = kvtopte(bp->b_un.b_addr);
		vbastat.k_sg++;
	} else {
		spte = vtopte((bp->b_flags&B_DIRTY) ? &proc[2] : bp->b_proc,
		    btop(bp->b_un.b_addr));
		vbastat.u_sg++;
	}

	o = (int)bp->b_un.b_addr & PGOFSET;
	i = min(NBPG - o, bp->b_bcount);
	sg->start_addr.wcount = i >> 1;
	sg->start_addr.memadr = ((spte++)->pg_pfnum << PGSHIFT) + o;
	i = bp->b_bcount - i;
	if (i > VDMAXPAGES * NBPG)
		panic("vba xfer too large");
	i = i >> 1;
	for (adr = sg->addr_chain; i > 0; adr++, i -= NBPG / 2) {
		adr->nxt_addr = (spte++)->pg_pfnum << PGSHIFT;
		adr->nxt_len = imin(i, NBPG / 2);
	}
	adr->nxt_addr = 0;
	adr++->nxt_len = 0;
	return ((adr - sg->addr_chain) * sizeof(*adr) / sizeof(long));
}
#endif

#include "hd.h"
#if NHD > 0
#include "hdreg.h"

hd_sgsetup(bp, vb, sg)
	register struct buf *bp;
	struct vb_buf *vb;
	struct chain *sg;
{
	register struct pte *spte;
	register struct addr_chain *adr;
	register int i, cnt;
	int o;

	if (bp->b_bcount > HDC_MAXBC ||
	    bp->b_bcount % sizeof(long) - 1 ||
	    (u_int)bp->b_un.b_addr % sizeof(long) - 1)
		return(0);

	vb->vb_iskernel = (((int)bp->b_un.b_addr & KERNBASE) == KERNBASE);
	vb->vb_copy = 0;
	if (vb->vb_iskernel) {
		spte = kvtopte(bp->b_un.b_addr);
		vbastat.k_sg++;
	} else {
		spte = vtopte((bp->b_flags&B_DIRTY) ? &proc[2] : bp->b_proc,
		    btop(bp->b_un.b_addr));
		vbastat.u_sg++;
	}

	o = (int)bp->b_un.b_addr & PGOFSET;
	i = min(NBPG - o, bp->b_bcount);
	sg->wcount = i >> 2;
	sg->memadr = ((spte++)->pg_pfnum << PGSHIFT) + o;
	cnt = 0;
	for (i = (bp->b_bcount - i) >> 2; i > 0; i -= NBPG / sizeof(long)) {
		if (++cnt == HDC_MAXCHAIN)
			return(0);
		sg->wcount |= LWC_DATA_CHAIN;
		++sg;
		sg->wcount = imin(i, NBPG / sizeof(long));
		sg->memadr = (spte++)->pg_pfnum << PGSHIFT;
	}
	return(1);
}
#endif