Coherent4.2.10/i386/shm0.c

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

/* $Header: /ker/i386/RCS/shm0.c,v 2.5 93/10/29 00:57:23 nigel Exp Locker: nigel $ */
/*
 * Shared memory - memory management interface
 *
 * $Log:	shm0.c,v $
 * Revision 2.5  93/10/29  00:57:23  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.4  93/08/19  03:40:14  nigel
 * Nigel's R83
 * 
 */

#define	_KERNEL		1

#include <kernel/reg.h>
#include <sys/proc.h>
#include <sys/mmu.h>
#include <sys/shm.h>
#include <sys/seg.h>

SR *	accShm();
void	pdCheck();
void	shmAllDt();
int	shmAtt();
int	shmAttach();
void	shmDetach();
void	shmDetachP();
void	shmFree();


/*
 * Load mmu according to shared memory segments.
 */

#if	__USE_PROTO__
void shmLoad (void)
#else
void
shmLoad ()
#endif
{
	int i;
	SR *srp;
	static SR ushmtab[NSHMSEG];

	/*
	 * Unprogram the currently active segments.
	 * Reset ushmtab.
	 */

	for (i = 0, srp = ushmtab; i < NSHMSEG; i++, srp++) {
		if (srp->sr_segp)
			remove_seg_pages (srp);
		srp->sr_segp = 0;
	}

	/*
	 * Load each segment in the SELF->p_shmsr list into the MMU.
	 * Remember values in ushmtab.
	 */

	for (i = 0, srp = SELF->p_shmsr ; i < NSHMSEG ; i ++, srp ++) {
		if (srp->sr_segp) {
			ushmtab [i] = * srp;
			load_seg_pages (srp);
		}
	}
}


/*
 * shmAlloc()
 *
 * Allocate a segment for shared memory that is `bytes_wanted' bytes long.
 *
 * if successful, return allocated SEG *
 * else, return 0
 *
 * This routine is cloned from smalloc(), from which it differs by
 * (a) NOT linking the new segment into segmq,
 * (b) rounding segment size up to a multiple of 4k bytes.
 *
 * The reference counts s_urefc and s_lrefc for a shm segment are 1
 * at the time of allocation.  Each attachment to a new process and
 * each fork of an already attached process will increment these.
 */

SEG *
shmAlloc (bytes_wanted)
size_t bytes_wanted;
{
	SEG *new_seg = 0;
	unsigned int pages_wanted;

	pages_wanted = btocru (bytes_wanted);

	/* Limit size of any shm segment to SHMMAX bytes. */
	if (bytes_wanted > SHMMAX)
		return NULL;

	/*
	 * Estimate space needed for new segment and its overhead.
	 * Fail if not enough free RAM available.
	 */

	if (countsize (pages_wanted) > allocno ())
		return NULL;

	/*
	 * Allocate a new SEG struct to keep track of the segment, if possible.
	 */

	if ((new_seg = kalloc (sizeof (SEG))) == NULL)
		return NULL;

	if ((new_seg->s_vmem = c_alloc (pages_wanted)) == 0) {
		kfree (new_seg);
		return NULL;
	}

	new_seg->s_urefc = 1;
	new_seg->s_lrefc = 1;
	new_seg->s_size  = ctob(pages_wanted);
	new_seg->s_flags = SFCORE;
	return new_seg;
}


/*
 * shmAtt()
 *
 * Given a pointer "segp" to a SEG which is already allocated, the
 * virtual base address "base" where the segment is to appear, and an
 * index "shm_ix" into p_shmsr[] for a process, set up the
 * SR struct accordingly.
 *
 * Argument "ronflag" is nonzero if segment is to be attached read-only.
 *
 * Return 0 in case of failure, else nonzero.
 */

int
shmAtt(shm_ix, base, segp, shm_readonly)
unsigned int shm_ix;
caddr_t base;
SEG * segp;
int shm_readonly;
{
	int numBytes = segp->s_size;
	return shmAttach(shm_ix, numBytes, base, segp, shm_readonly);
}


/*
 * shmAttach()
 *
 * Given a pointer "segp" to a SEG which is already allocated, the number
 * "numBytes" of bytes in the segment visible in this reference, the
 * virtual base address "base" where the segment is to appear, and an
 * index "shm_ix" into p_shmsr[] for a process, set up the
 * SR struct accordingly.
 *
 * Argument "ronflag" is nonzero if segment is to be attached read-only.
 *
 * Return 0 in case of failure, else nonzero.
 */

int
shmAttach(shm_ix, numBytes, base, segp, shm_readonly)
unsigned int shm_ix;
size_t numBytes;
caddr_t base;
SEG * segp;
int shm_readonly;
{
	SR * srp;

	/* sanity checks */
	if (shm_ix >= NSHMSEG || numBytes > segp->s_size)
		return 0;

	/*
	 * You may find that the base address requested is not
	 * supported in the page directory.  Since a shm segment
	 * may straddle a 4 Mb boundary, there is the possibility
	 * of two missing page directory entries.  Check for both.
	 */

	pdCheck(base);
	pdCheck(base + segp->s_size - 1);

	srp = SELF->p_shmsr + shm_ix;
	srp->sr_base = base;
	srp->sr_flag = (SRFDUMP | SRFDATA);
	if (shm_readonly)
		srp->sr_flag |= SRFRODT;
	srp->sr_size = numBytes;
	srp->sr_segp = segp;

	segp->s_urefc ++;
	segp->s_lrefc ++;

	shmLoad ();
	mmuupd ();
	return 1;
}


/*
 * shmDetachP()
 *
 * Given an index "shm_ix", into the p_shmsr[] for a process,
 * and a PROC *, detach the indicated shared memory segment.
 */

void
shmDetachP(shm_ix, pp)
unsigned int shm_ix;
PROC *pp;
{
	SR * srp;
	SEG * segp;

	if (shm_ix >= NSHMSEG)
		return;

	srp = pp->p_shmsr + shm_ix;
	segp = srp->sr_segp;

	if (segp) {
		segp->s_urefc --;
		segp->s_lrefc --;

		/* We have to set detach time and decrement attachment
		 * count.
		 */
		shmSetDs (segp);	/* shm1.c */

		/* If it was last attachment and segment was marked to be
		 * removed, remove it.
		 */
		if ((segp->s_flags & SRFBERM) != 0 &&
		    segp->s_urefc == 1 && segp->s_lrefc == 1)
			shmFree (segp);
	}
	srp->sr_base = 0;
	srp->sr_flag = 0;
	srp->sr_size = 0;
	srp->sr_segp = 0;

	if (pp == SELF) {
		shmLoad ();
		mmuupd ();
	}
}


/*
 * shmDetach()
 *
 * Given an index "shm_ix", into the p_shmsr[] for a process,
 * detach the indicated shared memory segment.
 */

void
shmDetach(shm_ix)
unsigned int shm_ix;
{
	shmDetachP (shm_ix, SELF);
}


/*
 * Scan shared memory for the range of addresses from
 * "base" up to but not including "base" + "count".
 *
 * If any shared memory segment contains the range of addresses, return
 * its SR pointer, otherwise return zero.
 *
 * This routine is used by iomapvp() and sysio().
 */

SR *
accShm(base, numBytes)
caddr_t base;
size_t numBytes;
{
	SR * srp;
	int i;

	for (i = 0; i < NSHMSEG; i ++) {
		srp = SELF->p_shmsr + i;
		if (srp->sr_segp && base >= srp->sr_base &&
		    base + numBytes <= srp->sr_base + srp->sr_size)
			return srp;
	}
	return 0;
}


/*
 * shmFree()
 *
 * Given a non-null SEG pointer "segp" to a shared memory segment,
 * deallocate the RAM used by that segment.
 *
 * The s_urefc field must be 1 when this routine is called, i.e., there
 * must be no pending attachments to the segment.
 */

void
shmFree(segp)
SEG * segp;
{
	if (segp == NULL) {
		printf("shmFree err: NULL argument\n");
		return;
	}

	if (segp->s_urefc != 1 || segp->s_lrefc != 1) {
		printf("shmFree err: segp=%x count=%d\n", segp, segp->s_urefc);
		return;
	}

	c_free (segp->s_vmem, btocru (segp->s_size));

	kfree (segp);
}


/*
 * Given a PROC pointer "pp", detach ALL shared memory segments from
 * the process.  Done during exec and exit.
 */

void
shmAllDt()
{
	int i;

	for (i = 0; i < NSHMSEG; i ++)
		shmDetach (i);
}


/*
 * Given a PROC pointer "cpp" (e.g. child-of-current-process),
 * duplicate ALL shared memory segments for the process, and update
 * reference counts.  Done during fork.
 */

void
shmDup (cpp)
PROC * cpp;
{
	int i;
	PROC * pp = SELF;
	SR * srp;

	for (i = 0, srp = pp->p_shmsr; i < NSHMSEG; i ++, srp ++) {
		cpp->p_shmsr[i] = * srp;
		if (srp->sr_segp) {
			srp->sr_segp->s_urefc ++;
			srp->sr_segp->s_lrefc ++;
		}
	}
}


/*
 * Given a virtual address "base", check the page directory.  If the page
 * directory can't access "base", allocate a 4k page table for the segment and
 * point the page directory at the new page table.
 *
 * This routine is really tricky, so here is a picture of virtual memory:
 *
 * +--------------------+
 * |  U area, etc	|
 * |--------------------|	0xFFFF_F000
 * |  Page directory	|
 * |--------------------|	0xFFFF_E000
 * |       ...		|
 * |  Kernel text, data |
 * |--------------------|	0xFFC0_0000
 * |      -----		| <- 4k page table that maps ptable1_v[] (unmapped!)
 * |       ...		|
 * |      -----		| <- 4k page table that maps base (basePTvadd[])
 * |  Page tables	|
 * |   (ptable1_v[])	|
 * |--------------------|	0xFF80_0000
 * |       ...		|
 * |       base		|
 * |       ...		|
 * +--------------------+	0x0000_0000
 *
 * In comments below, "segment number" is a value in range 0..0x3FF.
 */

void
pdCheck(base)
int base;
{
	int baseSeg;		/* Segment number of base */
	int ptable1_vSeg;  	/* Segment number of ptable1_v */

	int tabPadd;		/* Physical address of new 4k page table */
	int basePTvadd;		/* Virtual address where we want the new page
				   table page (in ptable1_v[]). */

	int ptable1_vPTpadd;	/* Physical address of page table covering
				   segment ptable1_v[]. */
	int w;			/* Temporary virtual page number for
				 * ptable1_vPTpadd. */
	int basePTindex;	/* Offset of entry for page table for "base" 
				   within its page table. */
	int *unmapped;

	baseSeg = btosrd (base);

	/* If there is already a page table for "base", nothing to do. */
	if (ptable0_v [baseSeg] & SEG_PRE)
		return;

	/* Get a free page. */
	tabPadd = btocru (* -- sysmem.pfree);

	/* Point the page directory at the new page. */
	ptable0_v [baseSeg] = tabPadd | SEG_RW;

	/* Now update the page tables so we can access the new page. */

	/* Get physical address for the page table for segment ptable1_v[]. */
	ptable1_vSeg = btosrd (ptable1_v) & 0x3ff;
	ptable1_vPTpadd = ptable0_v [ptable1_vSeg] & ~ SEG_BITS;

	/* Map the page at ptable1_vPTpadd into virtual memory somewhere. */
	w = workAlloc ();
	ptable1_v [w] = ptable1_vPTpadd | SEG_SRW;
#if	_NIGEL_MMU_HACK
	mmuupd();
#endif

	/* Point page table at new page table page. */
	basePTvadd = (int) (ptable1_v + btocrd (base));

	basePTindex = btocrd (basePTvadd) & 0x3FF;

	unmapped = (int *) ctob (w) + basePTindex;

	* unmapped = tabPadd | SEG_SRW;
	mmuupd ();

	/* Release the temporary page of virtual space. */
	workFree (w);

	/* Now we can write to the new page table.  Initialize it empty. */
	memset (basePTvadd, 0, NBPC);
}