Coherent4.2.10/coh.386/seg.c
/* $Header: /ker/coh.386/RCS/seg.c,v 2.6 93/10/29 00:55:34 nigel Exp Locker: nigel $ */
/* (lgl-
* The information contained herein is a trade secret of Mark Williams
* Company, and is confidential information. It is provided under a
* license agreement, and may be copied or disclosed only under the
* terms of that agreement. Any reproduction or disclosure of this
* material without the express written authorization of Mark Williams
* Company or persuant to the license agreement is unlawful.
*
* COHERENT Version 2.3.37
* Copyright (c) 1982, 1983, 1984.
* An unpublished work by Mark Williams Company, Chicago.
* All rights reserved.
-lgl) */
/*
* Coherent.
* Segment manipulation.
*
* $Log: seg.c,v $
* Revision 2.6 93/10/29 00:55:34 nigel
* R98 (aka 4.2 Beta) prior to removing System Global memory
*
* Revision 2.5 93/09/02 18:08:01 nigel
* Prepare for DDI/DKI merge
*
* Revision 2.4 93/08/19 03:26:45 nigel
* Nigel's r83 (Stylistic cleanup)
*/
#include <sys/errno.h>
#include <stddef.h>
#define _KERNEL 1
#include <kernel/alloc.h>
#include <kernel/trace.h>
#include <kernel/reg.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/mmu.h>
#include <sys/buf.h>
#include <sys/ino.h>
#include <sys/inode.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/seg.h>
#include <a.out.h>
/*
* NIGEL: Wrap up the garbage before we take it out.
*/
__DUMB_GATE __seglink = __GATE_DECLARE ("segment list");
#define __LOCK_SEGMENT_LIST(where) \
(__GATE_LOCK (__seglink, "lock : seglink " where))
#define __UNLOCK_SEGMENT_LIST() \
(__GATE_UNLOCK (__seglink))
SEG segmq; /* seg.h */
#define min(a, b) ((a) < (b) ? (a) : (b))
/*
* Initialisation code.
*/
void
seginit()
{
/*
* Create empty circular-list of memory segments.
*/
segmq.s_forw = & segmq;
segmq.s_back = & segmq;
}
/*
* Given an inode, `ip', and flags, `ff', describing a segment associated
* with the inode, see if the segment already exists and if so, return a
* copy. If the segment does not exist, allocate the segment having size
* `ss', and read the segment using the inode at seek offset `dq' with a
* size of `ds'.
*/
SEG *
ssalloc(ip, ff, ss)
INODE *ip;
int ff;
int ss;
{
SEG *sp;
int f;
__LOCK_SEGMENT_LIST ("ssalloc ()");
f = ff & (SFSHRX | SFTEXT);
/*
* Look for the segment in the memory queue.
*/
for (sp = segmq.s_forw ; sp != & segmq ; sp = sp->s_forw) {
if (sp->s_ip == ip &&
(sp->s_flags & (SFSHRX | SFTEXT)) == f) {
__UNLOCK_SEGMENT_LIST ();
return segdupl (sp);
}
}
__UNLOCK_SEGMENT_LIST ();
/*
* Allocate and create the segment.
*/
return salloc (__ROUND_UP_TO_MULTIPLE (ss, NBPC), ff);
}
/*
* Free the given segment pointer.
*/
void
sfree (sp)
SEG *sp;
{
INODE *ip;
if (sp->s_urefc != 1) {
sp->s_urefc --;
sp->s_lrefc --;
return;
}
__LOCK_SEGMENT_LIST ("sfree ()");
-- sp->s_lrefc;
sp->s_back->s_forw = sp->s_forw;
sp->s_forw->s_back = sp->s_back;
c_free (sp->s_vmem, btocru (sp->s_size));
__UNLOCK_SEGMENT_LIST ();
if (sp->s_lrefc)
panic ("Bad segment count");
/*
* Check if inode is ilocked, in order to allow the process
* to exec itself (file with the same inode as parent). Vlad.
*/
if ((ip = sp->s_ip) != NULL && ! ilocked (ip))
ldetach (ip);
kfree (sp);
}
/*
* Given a pointer to a newly created process, copy all of our segments
* into the given process.
*
* Return nonzero if successful.
*/
int
segadup (cpp)
PROC *cpp;
{
SEG * sp;
int n;
cpp->p_flags |= PFSWIO;
for (n = 0 ; n < NUSEG ; n ++) {
cpp->p_segl [n] = SELF->p_segl [n];
if ((sp = SELF->p_segl [n].sr_segp) == NULL)
continue;
if ((sp = segdupl (sp)) == NULL)
break;
cpp->p_segl [n].sr_segp = sp;
if ((sp->s_flags & SFCORE) == 0)
cpp->p_flags &= ~ PFCORE;
}
/*
* One of the calls to segdupl() failed.
* Undo any that succeeded.
*/
if (n < NUSEG) {
/*
* If segdupl () fails on first segment, sr_segp needs
* to be cleared or relproc () would cause parent to
* nuke itself.
*/
if (n == 0)
cpp->p_segl [0].sr_segp = NULL;
while (n > 0) {
if ((sp = cpp->p_segl [-- n].sr_segp) != NULL) {
cpp->p_segl [n].sr_segp = NULL;
sfree (sp);
}
}
}
cpp->p_flags &= ~ PFSWIO;
return n;
}
/*
* Duplicate a segment.
*/
SEG *
segdupl(sp)
SEG *sp;
{
SEG *sp1;
if (sp->s_flags & SFSHRX) {
sp->s_urefc ++;
sp->s_lrefc ++;
return sp;
}
if ((sp->s_flags & SFCORE) == 0)
panic("Cannot duplicate non shared swapped segment");
if ((sp1 = salloc (sp->s_size,
sp->s_flags | SFNSWP | SFNCLR)) == NULL)
return NULL;
sp1->s_flags = sp->s_flags;
dmacopy (btocru (sp->s_size), sp->s_vmem, sp1->s_vmem);
return sp1;
}
/*
* Allocate a segment `bytes_wanted' bytes long.
* `flags' contains some pseudo flags.
*/
SEG *
salloc (bytes_wanted, flags)
int bytes_wanted, flags;
{
SEG *sp;
if ((sp = smalloc (bytes_wanted)) == NULL)
return NULL;
sp->s_flags = (flags & (SFSYST | SFTEXT | SFSHRX | SFDOWN)) | SFCORE;
if ((flags & SFNCLR) == 0)
dmaclear (sp->s_size, MAPIO (sp->s_vmem, 0));
return sp;
}
/*
* Make the segment descriptor pointed to by `sp1' have the attributes
* of `sp2' including it's position in the segment queue and release
* `sp2'. `seglink' must be locked when this routine is called.
*/
void
satcopy(sp1, sp2)
SEG *sp1;
SEG *sp2;
{
sp1->s_back->s_forw = sp1->s_forw;
sp1->s_forw->s_back = sp1->s_back;
sp2->s_back->s_forw = sp1;
sp1->s_back = sp2->s_back;
sp2->s_forw->s_back = sp1;
sp1->s_forw = sp2->s_forw;
sp1->s_daddr = sp2->s_daddr;
sp1->s_size = sp2->s_size;
sp1->s_vmem = sp2->s_vmem;
kfree (sp2);
}
/*
* Grow or shrink the segment `sp' so that it has size `new_bytes' bytes.
* Return 1 on success, 0 on failure.
*
* WARNING: Downward growing segments (like user stack) not done yet!
*/
int
seggrow(sp, new_bytes)
SEG *sp;
unsigned int new_bytes;
{
SEG *sp1;
int dowflag;
unsigned int old_bytes, common_pages;
dowflag = sp->s_flags & SFDOWN;
old_bytes = sp->s_size;
/* Get rid of degenerate case. */
if (new_bytes == old_bytes)
return 1;
/*
* If we want a larger segment AND c_grow() succeeds
* boost segment size to new_bytes
*/
if (new_bytes >= old_bytes && c_grow (sp, new_bytes) == 0) {
T_HAL(0x100, printf("c_grow(%d) ", new_bytes));
sp->s_size = new_bytes;
dmaclear (new_bytes - old_bytes,
MAPIO (sp->s_vmem, old_bytes));
return 1;
}
if ((sp1 = salloc (new_bytes,
sp->s_flags | SFNSWP | SFNCLR)) != NULL) {
T_HAL(0x100, printf("salloc(%d) ", new_bytes));
if (dowflag == 0) {
common_pages = btocru (min (new_bytes, old_bytes));
dmacopy (common_pages, sp->s_vmem, sp1->s_vmem);
if (new_bytes > old_bytes)
dmaclear (new_bytes - old_bytes,
MAPIO (sp1->s_vmem, old_bytes));
} else
panic ("downflag");
__LOCK_SEGMENT_LIST ("seggrow ()");
c_free (sp->s_vmem, btocru (old_bytes));
satcopy (sp, sp1);
__UNLOCK_SEGMENT_LIST ();
return 1;
}
return 0;
}
/*
* Given a segment pointer, `sp' and a segment size, grow the given segment
* to the given size.
*/
void
segsize(sp, s2)
SEG *sp;
caddr_t s2;
{
caddr_t s1;
s1 = (caddr_t) sp->s_size;
if (s2 == 0 || seggrow (sp, (off_t) s2) == 0) {
SET_U_ERROR (ENOMEM, "can not grow segment");
return;
}
if (sproto (0) == 0) {
if (seggrow (sp, (off_t) s1) == 0 || sproto (0) == 0) {
T_PIGGY (0x2000000, printf("auto SEGV\n"));
sendsig (SIGSEGV, SELF);
}
}
segload ();
}
/*
* Allocate a segment in memory that is `bytes_wanted' bytes long.
* The `seglink' gate should be locked before this routine is called.
*
* if successful, return allocated SEG * else, return 0
*
* NIGEL: This routine is actually only called from salloc (), whose callers
* expect a completely initialized structure (or so it seems). Let's do that
* initialization rather than expecting kalloc () to have accidentally done
* the job. Furthermore, this routine is specially set up to only work for the
* _I386 version of the data structures.
*/
SEG *
smalloc (bytes_wanted)
off_t bytes_wanted;
{
SEG *sp1;
SEG *new_seg;
unsigned pages_wanted;
pages_wanted = btocru (bytes_wanted);
/*
* Estimate space needed for new segment and its overhead.
* Fail if not enough free RAM available.
*/
if (countsize (pages_wanted) > allocno ())
return 0;
/*
* Allocate a new SEG struct to keep track of the segment, if possible.
*/
if ((new_seg = kalloc (sizeof (SEG))) == NULL)
return 0;
if ((new_seg->s_vmem = c_alloc (pages_wanted)) == NULL) {
kfree (new_seg);
return 0;
}
/* link new_seg in at start of segmq */
__LOCK_SEGMENT_LIST ("smalloc ()");
sp1 = segmq.s_forw;
sp1->s_back->s_forw = new_seg;
new_seg->s_back = sp1->s_back;
sp1->s_back = new_seg;
new_seg->s_forw = sp1;
new_seg->s_urefc = 1;
new_seg->s_lrefc = 1;
new_seg->s_size = bytes_wanted;
new_seg->s_ip = NULL;
new_seg->s_daddr = 0;
__UNLOCK_SEGMENT_LIST ();
return new_seg;
}
/*
* Set up `SR' structure in user area from segments descriptors in
* process structure. Also set up the user segmentation registers.
*/
int
sproto (xhp)
struct xechdr *xhp;
{
int n;
SEG *sp;
for (n = 0 ; n < NUSEG ; n ++) {
SELF->p_segl [n].sr_flag = SELF->p_segl [n].sr_size = 0;
if ((sp = SELF->p_segl [n].sr_segp) == NULL)
continue;
if (n == SIUSERP)
SELF->p_segl [n].sr_base = (caddr_t) & u;
else {
if (xhp)
SELF->p_segl [n].sr_base = (caddr_t) xhp->segs [n].mbase;
SELF->p_segl [n].sr_flag |= SRFPMAP;
}
if (n != SIUSERP && n != SISTEXT)
SELF->p_segl [n].sr_flag |= (SRFDATA | SRFDUMP);
SELF->p_segl [n].sr_size = sp->s_size;
}
return mproto ();
}
/*
* Search for a busy text inode.
*/
int
sbusy(ip)
struct inode *ip;
{
SEG *sp;
__LOCK_SEGMENT_LIST ("sbusy ()");
/*
* Look for the segment in the memory queue.
*/
for (sp = segmq.s_forw ; sp != & segmq ; sp = sp->s_forw) {
if (sp->s_ip == ip &&
(sp->s_flags & (SFSHRX | SFTEXT)) == (SFSHRX | SFTEXT)) {
__UNLOCK_SEGMENT_LIST ();
return 1;
}
}
__UNLOCK_SEGMENT_LIST ();
return 0;
}