DMA from User Address Space

G.TOMASEVICH 54394gt at hocda.UUCP
Thu Jan 5 01:28:30 AEST 1984


The following code fragments exercise the DR11-W in maintenance mode
on a PDP-11/45 from a user process.  The device registers are accessed
from /dev/mem, and the physical address of the DMA buffer is calculated
from the user segmentation registers.  I have not tried such a scheme
on our VAX.  There are several important notes.
1. The usage is a call to blkdrw(), which returns an indication of success
   or failure.
2. Anything can be read or written from /dev/mem if the permissions are
   relaxed.  Thus one could easily crash the system.  Accessing a non-
   existent DR11-W causes the lseek to hang, according to an abort forced
   with the kill(1) command.
3. The PDP-11/70 would require use of the Unibus Map, which rules out
   such a simple method of getting the physical address for the DR11-W,
   and hence the whole idea of user-space DMA.  I assume the same is true
   for the VAX.
4. If the user swaps out during the DMA, someone else's program gets over-
   written for DMA into memory (CRASH).  Thus this is no method for a busy
   machine, only for debugging.
5. Interrupts can be taken into the user space, too, but there is a trap
   on the 'rti' as the PS restore is attempted from user mode.  Otherwise,
   the interrupt routine works, but is useless because of the trap, unless
   one wants to do a nonlocal jump on every trap (ugh).

#define UDSA	0777660L	/* 1st user data seg adr reg */
unsigned areg[8];		/* buffer for reading seg regs */
int fm;				/* file desc for reading /dev/mem */
extern long lseek(), physadr();

/* device register structures */
struct drwreg {		/* DR11-W registers */
	int drwc;		/* word count */
	int drba;		/* buffer address */
	int drst;		/* status reg */
	int drio;		/* input data reg, output data reg */
};
struct drwreg drtemp[1];	/* mem loc to read/write registers */
#define DRWREG	0772410L	/* adr of 1st DR11-W */

/* Simulation of UNIX kernel driver for DR11-W */

#define loword(X)	(((unsigned*)&X)[1])
#define hiword(X)	(((unsigned*)&X)[0])
#define GO	1	/* start DR11-W */
#define READY	0200	/* set when opn is done */
#define CYCLE	0400	/* do one NPR cycle */
#define MAINT	010000	/* do maintenance mode */

struct buf {	/* physical i/o driver buffer */
	int b_flags;	/* i/o control */
	int b_count;	/* transfer byte count */
	long b_paddr;	/* phys adr of buffer */
} drwbuf[3];		/* 3 DR11-W's */

/* Combined open read/write, physio functions done in UNIX physical i/o.
 * DR11-W read or write is determined by hardware.
 */

blkdrw(d,b,n)	/* blocking i/o */
int d;			/* device number instead of f.d. from open() */
char *b;		/* buffer adr */
int n;			/* byte count */
{
	register struct buf *bp;
	bp = &drwbuf[d];		/* ought to check if legal d */
	bp->b_count = n;
	bp->b_flags = MAINT|CYCLE|GO;	/* maintenance mode */
	bp->b_paddr = physadr(b);	/* physio() does this */
	drwstrat(bp);
	return(drwait(bp));		/* wait for done */
}

drwstrat(bp)	/* simulated strategy routine */
register struct buf *bp;
{
	register com;
	register struct drwreg *dradr = drtemp;
	drrd(bp);			/* read DR11 regs */
	dradr->drwc = -((bp->b_count)>>1);	/* -(word count) */
	dradr->drba = loword(bp->b_paddr);
	com = (hiword(bp->b_paddr) & 03) << 4;
	com |= bp->b_flags;		/* get specific cmd */
	dradr->drst = com;
	drwr(bp);			/* write DR11 regs */
	return;
}

/* KI simulates a keyboard interrupt from nonblocking raw reads.
 * It can set flag.
 */
drwait(bp)	/* wait for DMA to finish */
struct buf *bp;
{
	register struct drwreg *dradr = drtemp;
	do {
		KI();
		if(flag<0)
			return(0);	/*  bail out */
		drrd(bp);
	} while((dradr->drst & READY) == 0);
	return(1);
}

int drwid;		/* selected DR11-W (global for adb(1) ) */

drrd(bp)	/* read DR11-W registers */
struct buf *bp;
{
	long padr;
	drwid = (int)(bp-drwbuf);
	padr = DRWREG + (drwid<<4);
	(void)lseek(fm,padr,0);
	(void)read(fm,(char*)drtemp,sizeof(drtemp[0]));
	return;
}

drwr(bp)	/* write DR11-W registers */
/* same as drrd() but has write() instead of read() */

long physadr(a)	/* calc physical adr from virtual adr */
char *a;
{
	register unsigned d, i;
	long padr;
	d = (unsigned)a;
	(void)lseek(fm,UDSA,0);		/* get to segmentation regs */
	(void)read(fm,(char*)areg,16);
	i = (d>>13)&7;			/* segment */
	padr = (long)areg[i];		/* block */
	padr <<= 6;
	return(padr + (long)(d&017777));
}

	George Tomasevich



More information about the Comp.unix.wizards mailing list