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