[comp.unix.i386] How to map physical memory into a process's context

plocher%sally@Sun.COM (John Plocher) (06/07/89)

THIS CODE IS PROVIDED "AS IS" AND SUN MICROSYSTEMS WILL NOT SUPPORT IT.

(Whee, now that that is out of the way...)

Sorry this took so long, but here is what is needed to do things like
accessing graphics MEMORY from a user program without the overhead of a
system call for each access.  (It does not address access to I/O ports)

This info is specific to System V versions 3.0 and later. (it may be quite
similar for Vr2, but I can't test it)

(Microport combined all this with their V/286 2.3/2.4 releases into a
program called shmcreate.  That program does things much the same way as I
am describing here.  There was never something like this for the 386)
(Gfx was simply a back door hook into the console driver...)

First, the sample program.  This is what plays with physical memory.  It
is a user level program - only needing access to the device which your
driver implements.  It initializes things and ends up with a pointer which
it can use to muck with the memory.

Second, the sample "driver".  This is what is used (once) to map a chunk
of physical memory into a shared memory segment.  This example talks to a
Cornerstone CVC1 framebuffer; it is not complete, but the framework is
there (i.e., you *really* don't care about register settings for linear
access native mode, do you?) It gets a kernel pointer to the physical
memory, allocates a shared memory segment, stuffs in the physical address,
and makes the shm "key" available to program using the driver.

Lastly comes the framework for the actual shared memory access routines.
This is what really makes this all work.  Because of many factors it is
simply not possible to provide the source code to this level.  If you have
ATT Source code, these routines are simply the shared memory access
routines with the error assignments to u.u_error removed.

    -John Plocher


****								****
	   I can NOT provide the source code to kshm()
****								****

Usage would be:

1) write your driver using the framework shown here
2) make a Driver.o which contains your code and the kshm module
   which you will have to provide. (See V/386 3.2 Driver Docs...)
3) build a kernel with this new driver - make device node(s)...
4) reboot with this new kernel

5) write user program, compile, and run it.


Reference: (Strongly recommended!) Some of the examples are taken from

Portable NeWS(tm) 1.1 Porting Manual
Part Number 800-2250-10
Revision A of 5 February 1988
Appendix C - System V Platform
Appendix D - Device Driver Notes
Sun Microsystems, Inc
2550 Garcia Avenue
Mountain View, CA
94043
(415) 960-1300


---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
		Sample program which shows off the driver
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----

main() 
{
	char *cvc1mem;

	cvc1mem = openFB( "/dev/cvc1" );
}

/*
**
** openFB(name)
** open Cornerstone board & return a pointer to it's memory
**
** returns:
**   Address of shared memory which maps to physical memory
**   -or- NULL if could not open
**
** The fd of the framebuffer isn't stored because we don't
** care in this simple demo.  In real life there would be a
** close entry in the driver and we would use it at the
** appropriate time .....
*/

char *
openFB(name)
char	*name;
{
	int  fd;
	char *mmap_addr;
	key_t key;
	int shmid;


	if ((fd = open(name, 2)) < 0) {		/* open the device */
	    Fatal_Internal("open of FRAMEBUFFER failed.");
	    return (NULL);
	}

	if (ioctl(fd, CVC1GETKEY, &key) < 0) {	/* ask it for the shm key */
	    Fatal_Internal("ioctl CVC1GETKEY failed.");
	    return(NULL);
	}

	/* standard shared memory stuff here */

	if ((shmid = shmget(key, length, IPC_ALLOC)) < 0) {
	    Fatal_Internal("shget failed.");
	    return(NULL);
	}

        if ((int) (mmap_addr = (char *)shmat(shmid, 0, 0))  == -1) {
	    Fatal_Internal("shmat failed.");
	    return(NULL);
	}

	if (ioctl(fd, CVC1SETGM) == -1) {	/* go into graphic mode */
	    Fatal_Internal("ioctl CVCSETGM failed.");
	    return(NULL);
	}

	return (mmap_addr);
}

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
	Sample device driver to demo direct physical memory access
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----

/*
 * Sample "Physical Memory" device driver interface to a CVC1 graphics board
 * Ioctls are provided to:
 *	Set graphics mode
 *	Reset to power up state
 *	Map physical frame buffer memory into a shared memory segment
 *
 */

/* include everything that looks neat in <sys/...> */

#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/immu.h>
#include <sys/region.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <sys/tss.h>
#include <sys/ipc.h>
#include <sys/systm.h>
#include <sys/shm.h>
#include <sys/debug.h>
#include <sys/tuneable.h>
#include <sys/cmn_err.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/conf.h>


#define KSHMFAILED	-1
#define KSHMOKAY	 0

#define CVC1KEY	((('C'<<24)|('V'<<16)|('C'<<8)|'1')|'c') /* or whatever */
#define MEMSIZE 0x80000			/* 1600x1280 */

key_t	cvc1key = CVC1KEY
char *vbase;				/* virtual  address of frame buffer */
long	cvc1membase = 0x00c00000;	/* physical address of frame buffer */
short	cvc1iobase  = 0x390;		/* base of io addresses */

cvc1ioctl(dev, cmd, arg)
int dev, cmd;
faddr_t arg;
{
	switch (cmd) {
	case CVC1SETGM:		/* set graphics mode */
		cvc1setgm();
		break;
	case CVC1RESET:		/* reset to power up state */
		cvc1reset();
		break;
	case CVC1GETKEY:	/* setup shm seg & return unique key */
				/* Reference: p132 "Portable NeWS 1.1
				 * Porting Manual"  Appendix D
				 * Device Driver Notes
				 * Sun P/N 800-2250-10
				 */
		if (kshm(cvc1key, CVC1SIZE, vbase) == KSHMFAILED)
			u.u_error = ENOENT;
		else
		    copyout((key_t)&cvc1key, arg, sizeof(cvc1key));
		break;
	default:
		u.u_error = EINVAL;
	}
}

cvc1init()
{
	printf("CVC1 Driver Installed\n");

	/*
	 * Get the page tables set up for the memory at physical address
	 * cvc1membase. Lock this memory in core (don't want the framebuffer
	 * swapped, also this will be a shared memory segment so it should be
	 * locked down), make it read/write by the user and set the page
	 * status to valid.
	 */
	vbase = (char *)sptalloc(btoc(MEMSIZE)+1,
			         PG_P | PG_RW | PG_US | PG_LOCK,
				 btoc(cvc1membase),
				 NOSLEEP);
}

cvc1setgm()
{
	unchar saved_mode;
	saved_mode = inb(cvc1iobase + CVC1_MODE);	/* examples */
	outb(cvc1iobase + CVC1_MODE, saved_mode);
}

cvc1reset()
{
}

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
		Shared Memory Interface Routine
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----


/*
 * kshm()
 *
 * Note:   This driver will only work with SystemV.3 as supplied by ATT for
 *	   the 386 (AT based). It assumes internal system data structures.
 *	   If the operating system region code, memory mapping code or the
 *	   shared memory code is different from the above system the
 *	   driver will need to be modified.
 *
 * Set up an shmid_ds structure for a user process to attach to. It
 * finds the slot based on key and allocates a page table for the
 * given size. It then fills in the page table with the page table
 * pointers taken from the kernel virtual address vaddr. It is
 * safe to call this routine more than once with the same key (it
 * will notice that the segment is already allocated and return).
 * Returns KSHMOKAY on success (allocating a segment or finding
 * one) and KSHMFAILED on failure (if the shmid_ds can not be filled
 * in).
 *
 * Duplicates the routines shmget() and shmat() but does not
 * set the user error codes since this will be called from the
 * kernel. It also does not allocate any physical memory for the
 * shared segment since the kernel should have already allocated,
 * locked down, and set up the appropriate protections on the memory
 * that it wants used (vaddr).
 */
static
kshm(key, size, vaddr) 
key_t   key;		/* shared memory KEY to use */
int	size;		/* what size the sharedmem is */
int	vaddr;		/* virtual address of sharedmem */
{
	/* Sorry, y'know how these thing are .... */
}

---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
			End of posting
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----