|
|
researchv9-SUN3
#ifndef lint
static char sccsid[] = "@(#)vm_machdep.c 1.1 86/02/03 Copyr 1985 Sun Micro";
#endif
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
/*
* Machine dependent virtual memory support.
* Context and segment and page map support for the Sun-3.
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/vm.h"
#include "../h/cmap.h"
#include "../h/text.h"
#include "../h/mount.h"
#include "../machine/pte.h"
#include "../machine/mmu.h"
#include "../machine/cpu.h"
#include "../machine/reg.h"
#include "../machine/buserr.h"
#include "../machine/scb.h"
#include "../machine/mbvar.h"
u_char getsegmap();
long getpgmap();
struct context context[NCONTEXT]; /* contexts */
int ctxtime = 0; /* pseudo-time for ctx lru */
struct pmeg pmeg[NPMEG]; /* all the pmegs in the world */
struct pmeg pmeghead; /* pmeg free list */
int kernpmeg = 0; /* how many pmegs the kernel has */
/*
* Initialize pmeg allocation list.
*/
pmeginit()
{
register int i;
pmeghead.pm_forw = pmeghead.pm_back = &pmeghead;
for (i = 0; i < NPMEG; i++) /* add all entries to queue */
insque(&pmeg[i], &pmeghead);
}
/*
* Take care of common pmeg release code for pmegalloc() and pmegallocres(),
* called only when pmp->pm_procp is non-zero.
*/
pmegrelease(pmp)
register struct pmeg *pmp;
{
register struct context *cp;
int ctx;
pmegunload(pmp);
cp = pmp->pm_procp->p_ctx;
cp->ctx_pmeg[pmp->pm_seg] = 0;
ctx = getcontext();
setcontext((int)cp->ctx_context);
setsegmap((u_int)pmp->pm_seg, (u_char)SEGINV);
setcontext(ctx);
}
/*
* Allocate a page map entry group.
*/
u_char
pmegalloc(p)
register struct proc *p; /* process to allocate it for */
{
register struct pmeg *pmp;
extern int potime;
pmp = pmeghead.pm_forw; /* get pmeg off head of chain */
if (pmp->pm_procp)
pmegrelease(pmp);
remque(pmp);
pmp->pm_procp = p; /* set process pointer */
pmp->pm_count = 0; /* no valid pages yet */
pmp->pm_seg = -1; /* no segments yet */
pmp->pm_time = potime - 1; /* reset time */
if (p)
insque(pmp, pmeghead.pm_back); /* put back on queue */
else
pmp->pm_forw = pmp->pm_back = 0;/* wanted by kernel */
return ((u_char)(pmp - pmeg)); /* return index to pmeg in pmeg list */
}
/*
* Free a pmeg.
*/
pmegfree(pmx)
u_char pmx; /* index of pmeg in pmeg list */
{
register struct pmeg *pmp = &pmeg[pmx];
if (pmp->pm_procp) /* is there a process with this one? */
remque(pmp); /* take if off the queue its on */
pmp->pm_procp = 0; /* reset process number */
insque(pmp, &pmeghead); /* put back on free list */
}
/*
* Reserve named pmeg for use by kernel and monitor.
*/
pmegreserve(n)
u_char n;
{
register struct pmeg *pmp = &pmeg[n];
remque(pmp);
pmp->pm_forw = pmp->pm_back = 0;
pmp->pm_count = NPAGSEG;
pmp->pm_procp = (struct proc *)0;
pmp->pm_seg = 0;
kernpmeg++;
}
/*
* Allocate and reserve kernel pmeg
*/
u_char
pmegallocres()
{
struct pmeg *pmp;
u_char pm;
pmp = pmeghead.pm_forw;
if (pmp->pm_procp)
pmegrelease(pmp);
pm = (u_char)(pmp - pmeg);
pmegreserve(pm);
return (pm);
}
/*
* Load all hardware page map entries for the specified
* pmeg from the software page table entries.
*/
pmegload(seg, need)
int seg; /* segment we are loading */
int need; /* need a segment */
{
register struct pte *pte;
register struct pmeg *pmp;
register int v, num, k, new = 0;
register int i;
register struct proc *p = u.u_procp;
int pm, last, tse, dse, sss;
struct context *cp = p->p_ctx; /* process context number */
u_char *pmxp = &cp->ctx_pmeg[seg]; /* which segments pmeg */
int s = splimp();
if (getcontext() == KCONTEXT) {
printf("NO CONTEXT IN PMEGLOAD\n");
(void) splx(s);
return;
}
if (*pmxp == 0) { /* is there a pmeg already */
if (!need) {
setsegmap((u_int)seg, (u_char)SEGINV);
(void) splx(s);
return;
}
*pmxp = pmegalloc(p); /* get one */
pmeg[*pmxp].pm_seg = seg; /* assign it in context */
new = 1;
}
pmp = &pmeg[*pmxp]; /* get pointer to pmeg */
if (pmp->pm_procp != p) /* better be same process */
panic("dup alloc pmeg");
if (pmp->pm_seg != seg) /* better be same segment */
panic("pmeg changed seg");
pm = pmp - pmeg; /* make pmeg index based on pmeg addr */
v = seg * NPAGSEG; /* make index based on segment number */
/*
* Decide which of the text|data|stack
* segments the virtual segment seg is in.
*
* Lots of assumptions about the layout
* of virtual memory here. Should be
* more parameterized.
*/
last = ptos(btop(USRSTACK)); /* find bottom of user stack seg */
/* find end of text segment */
tse = p->p_tsize ? ptos(tptov(p, 0) + p->p_tsize - 1) : 0;
/* find end of data segment */
dse = ptos(dptov(p, 0) + p->p_dsize - 1);
/* compute start of stack segment */
sss = last - ptos(p->p_ssize + NPAGSEG - 1);
setsegmap((u_int)seg, (u_char)pm); /* set the seg map */
if (seg <= tse) {
if (seg == 0) {
setpgmap((caddr_t)0, (long)0);/* first page invalid */
v = LOWPAGES; /* set addr in map */
num = MIN((p->p_tsize ? p->p_tsize : p->p_dsize),
NPAGSEG - LOWPAGES);
i = 0;
} else {
i = v - tptov(p, 0); /* compute index in text */
num = MIN(p->p_tsize - i, NPAGSEG);
}
pte = tptopte(p, i); /* get pointer ptes */
pmp->pm_pte = pte;
pmp->pm_count = num;
for (k = num; k--; v++, pte++)
loadpgmap((u_int)v, pte, new);
for (k = NPAGSEG - num - ((seg == 0)? LOWPAGES : 0); k--; v++)
setpgmap((caddr_t)ctob(v), (long)0);
if (seg > cp->ctx_tdmax)
cp->ctx_tdmax = seg;
} else if (seg > tse && seg <= dse) {
i = v - dptov(p, 0); /* compute index in data */
pte = dptopte(p, i); /* get pointer ptes */
num = MIN(p->p_dsize - i, NPAGSEG);
pmp->pm_pte = pte;
pmp->pm_count = num;
for (k = num; k--; v++, pte++)
loadpgmap((u_int)v, pte, new);
for (k = NPAGSEG - num; k--; v++)
setpgmap((caddr_t)ctob(v), (long)0);
if (seg > cp->ctx_tdmax)
cp->ctx_tdmax = seg;
} else if (seg >= sss && seg < last) {
i = btop(USRSTACK)-NPAGSEG - v; /* compute index in stack */
pte = sptopte(p, i); /* get pointer ptes */
num = MIN(p->p_ssize - i, NPAGSEG);
pte -= num - 1;
pmp->pm_pte = pte;
pmp->pm_count = -num;
for (k = NPAGSEG - num; k--; v++)
setpgmap((caddr_t)ctob(v), (long)0);
for (k = num; k--; v++, pte++)
loadpgmap((u_int)v, pte, new);
if (seg < cp->ctx_smin)
cp->ctx_smin = seg;
} else {
if (need)
panic("need pmeg in hole");
pmegfree(*pmxp);
*pmxp = 0;
setsegmap((u_int)seg, (u_char)SEGINV);
}
(void) splx(s);
}
/*
* Unload bits for specified pmeg.
*/
pmegunload(pmp)
register struct pmeg *pmp; /* pointer to pmeg */
{
register struct pte *pte;
register int num, k, v;
if (pmp->pm_procp == 0)
panic("pmegunload");
setsegmap(CSEG, (u_char)(pmp - pmeg));
v = NPAGSEG * CSEG;
num = pmp->pm_count;
pte = pmp->pm_pte;
if (num < 0) {
num = -num;
v += NPAGSEG - num;
}
if (pmp->pm_seg == 0) /* special case seg zero */
v += LOWPAGES;
for (k = num; k--; v++, pte++)
unloadpgmap((u_int)v, pte);
setsegmap(CSEG, (u_char)SEGINV);
}
/*
* Get referenced and modified bits for
* the pmeg containing page v. Called
* only by pageout.
*/
ptesync(p, v)
register struct proc *p;
register unsigned v;
{
register struct context *cp;
register struct pmeg *pmp;
register int pm, s;
s = splimp();
if ((cp = p->p_ctx) == NULL)
goto out;
if ((pm = cp->ctx_pmeg[ptos(v)]) == 0)
goto out;
pmp = &pmeg[pm];
if (pmp->pm_procp != p)
panic("ptesync procp");
if (pmp->pm_time != potime) { /* check mod time, done? */
pmegunload(pmp);
pmp->pm_time = potime;
}
out:
(void) splx(s);
}
/*
* get a kernel page map entry given an address
*/
getkpgmap(addr)
caddr_t addr;
{
return ((int)getpgmap(addr));
}
/*
* Initialize the context structures.
*/
ctxinit()
{
register int i;
for (i = 0; i < NCONTEXT; i++) {
if (i == KCONTEXT)
continue;
context[i].ctx_context = i;
}
}
/*
* Allocate a context and corresponding
* page map entries for the current process.
* If no free context must take one away
* from someone.
*/
ctxalloc()
{
register struct proc *p = u.u_procp; /* process this is for */
register struct context *cp, *scp = 0;
register int ct, i;
/* find a free context or an old one */
for (cp = context; cp < &context[NCONTEXT]; cp++) {
if (cp == &context[KCONTEXT])
continue;
if (cp->ctx_procp == 0) /* if no process use this one */
goto found;
if (scp == 0) { /* otherwise find the oldest */
scp = cp;
ct = cp->ctx_time;
} else if (cp->ctx_time <= ct) {
scp = cp;
ct = cp->ctx_time;
}
}
cp = scp; /* reset pointer to save context pointer */
if (cp->ctx_procp) /* if in use free it up before using */
ctxfree(cp->ctx_procp);
found:
p->p_ctx = cp; /* set context pointer in proc entry */
cp->ctx_procp = p; /* set proc pointer in context table */
cp->ctx_time = ctxtime++;
setcontext((int)cp->ctx_context);
for (i = 0; i <= cp->ctx_tdmax; i++)
setsegmap((u_int)i, (u_char)SEGINV);
for (i = cp->ctx_smin; i < ptos(btop(USRSTACK)); i++)
setsegmap((u_int)i, (u_char)SEGINV);
cp->ctx_tdmax = 0;
cp->ctx_smin = (ptos(btop(USRSTACK)));
p->p_flag &= ~SPTECHG;
}
/*
* Free the context and page map entries
* of the specified process.
*/
ctxfree(p)
register struct proc *p;
{
register struct context *cp;
register u_char *pmxp;
register int s;
if ((cp = p->p_ctx) == 0) /* no context */
return;
if (p != cp->ctx_procp) /* not the same process */
panic("ctxfree");
if ((p->p_flag&SWEXIT) == 0) /* don't bother if dieing */
ctxunload(p);
s = splimp();
/* free the pmegs for this context */
for (pmxp = cp->ctx_pmeg; pmxp < &cp->ctx_pmeg[ptos(btop(USRSTACK))];
pmxp++) {
if (*pmxp) { /* is there a pmeg for this segment */
pmegfree(*pmxp);
*pmxp = 0;
}
}
(void) splx(s); /* back to normal */
setcontext(KCONTEXT); /* paranoid */
cp->ctx_procp = 0; /* reset proc pointer */
p->p_ctx = 0; /* reset context pointer */
}
/*
* Set up the segment and page map entries for
* the current process.
*/
ctxsetup()
{
register int i;
register int last = ptos(btop(USRSTACK)); /* last segment */
register struct context *cp = u.u_procp->p_ctx;
/*
* Initialize all segments.
*/
for (i = 0; i <= cp->ctx_tdmax; i++) /* load pmegs for text/data */
pmegload(i, 0);
for (i = cp->ctx_smin; i < last; i++) /* load pmegs for stack */
pmegload(i, 0);
u.u_procp->p_flag &= ~SPTECHG;
}
/*
* Unload the referenced and modified bits
* for the specified process.
*/
ctxunload(p)
struct proc *p;
{
register int last = ptos(btop(USRSTACK)); /* last segment */
register int i, s = splimp();
register u_char *pmxp;
/*
* Unload bits from all allocated pmegs.
*/
pmxp = p->p_ctx->ctx_pmeg; /* get pointer to pte's */
for (i = 0; i < last; i++, pmxp++)
if (*pmxp) /* is it set */
pmegunload(&pmeg[*pmxp]);
(void) splx(s);
}
/*
* Pass all resources associated with a context
* from process p to process q. Used by vfork.
*/
ctxpass(p, q)
register struct proc *p, *q;
{
register struct context *cp = p->p_ctx;
register u_char *pmxp;
register int last = ptos(btop(USRSTACK)); /* last segment */
register int i;
if (cp == 0)
return;
/*
* Pass the context from p to q.
*/
q->p_ctx = cp; /* q gets p's context */
p->p_ctx = 0; /* p loses the context */
cp->ctx_procp = q; /* context get q's proc id */
q->p_flag |= SPTECHG; /* conservative */
setcontext(KCONTEXT); /* paranoid */
/*
* Change all pmegs to refer to q.
*/
pmxp = cp->ctx_pmeg;
for (i = 0; i < last; i++, pmxp++)
if (*pmxp)
pmeg[*pmxp].pm_procp = q;
}
/*
* Handle a page fault on a 68020.
*/
pagefault(accaddr)
register int accaddr;
{
register struct proc *p = u.u_procp;
register int v = btop(accaddr);
struct pte *addrtopte();
int i, seg;
int s;
/*
* If user has no context, allocate one for him.
*/
if (getcontext() == KCONTEXT) {
usetup();
return (1);
}
if (addrtopte((caddr_t)accaddr, 1) == NULL)
return (0);
seg = ptos(v);
if (p->p_ctx->ctx_pmeg[seg]) {
if (getpgmap((caddr_t)accaddr) & PG_V)
return (0);
i = u.u_error;
pagein((u_int)accaddr, &u, 0);
u.u_error = i;
}
s = splimp();
if (p->p_ctx && p->p_ctx->ctx_pmeg[seg] == 0)
pmegload(seg, 1);
(void) splx(s);
return (1);
}
/*
* Set up everything the user program might need.
* If we need a context, allocate it. If we need
* to set up hardware segment and page maps, do it.
*/
usetup()
{
register struct proc *p = u.u_procp;
if (p->p_ctx == 0) /* do we need a context */
ctxalloc();
else {
p->p_ctx->ctx_time = ctxtime++; /* update time */
setcontext((int)p->p_ctx->ctx_context); /* set to user */
if (p->p_flag & SPTECHG) /* are we changing? */
ctxsetup();
}
}
/*
* Set a red zone below the kernel stack.
* NO LONGER USED, startup() SETS THE REDZONE.
*/
/*ARGSUSED*/
setredzone(pte, vaddr)
struct pte *pte;
caddr_t vaddr;
{
}
/*
* Map a physical address range into kernel virtual addresses.
* Since the kernel appears in all contexts any new pmegs are
* mapped in all contexts.
*/
mapin(ppte, v, paddr, size, access)
register struct pte *ppte; /* pointer to pte's */
u_int v; /* page number to map in */
register u_int paddr; /* physical address */
register int size, access; /* size in pages and access rights */
{
register caddr_t vaddr = (caddr_t)ctob(v);
register u_char pm;
register int c, i;
int s = splimp();
while (size--) {
if ((pm = getsegmap((u_int)ptos(v))) == SEGINV) {
caddr_t va, vs;
pm = pmegalloc((struct proc *)0);
kernpmeg++;
c = getcontext();
/* need to map in new seg across all contexts */
for (i = 0; i < NCONTEXT; i++) {
setcontext(i);
setsegmap(ptos(v), pm);
}
setcontext(c);
vs = (caddr_t)(ptos(v)<<SGSHIFT);
for (va = vs; va < vs + NBSG; va += NBPG)
setpgmap(va, (long)0);
}
/*
* Increment count of number of pme's used in this pmeg.
* Allow it to go one past the number of pme's in a pmeg;
* this indicates someone is doing a mapin without
* corresponding mapout's and will be noticed in mapout
* who will prevent the reference count from changing.
*/
if (pmeg[pm].pm_count <= NPAGSEG)
pmeg[pm].pm_count++;
*((int *)ppte) = (paddr & PG_PFNUM) | access;
setpgmap(vaddr, *(long *)ppte);
ppte++;
paddr++;
v++;
vaddr += NBPG;
}
(void) splx(s);
}
/*
* Release mapping for kernel.
* This frees pmegs, which are the most critical resource.
* Since the kernel appears in all contexts the pmeg has to be mapped
* out for all contexts. Assumes that ppte is a pointer
* to a pte within Sysmap.
*/
mapout(ppte, size)
register struct pte *ppte;
{
register int vaddr = ctob(ppte - Sysmap) + KERNELBASE;
register u_char pm;
register int c, i;
int s = splimp();
while (size--) {
if (!ppte->pg_v)
panic("mapout: invalid pte");
ppte->pg_v = 0;
if ((pm = getsegmap((u_int)ptos(btop(vaddr)))) == SEGINV)
panic("mapout: invalid segment");
if ((getpgmap((caddr_t)vaddr)&PG_V) == 0)
panic("mapout: invalid page");
if (pmeg[pm].pm_count <= 0)
panic("mapout: pmeg count");
setpgmap((caddr_t)vaddr, (long)0);
if (pmeg[pm].pm_count <= NPAGSEG)
if (--pmeg[pm].pm_count == 0) { /* done with all ptes */
c = getcontext();
/* need to map out seg across all contexts */
for (i = 0; i < NCONTEXT; i++) {
setcontext(i);
setsegmap((u_int)ptos(btop(vaddr)),
(u_char)SEGINV);
}
setcontext(c); /* reset context */
pmegfree(pm);
kernpmeg--;
}
ppte++; /* do next pte */
vaddr += NBPG;
}
(void) splx(s);
}
/*
* Check user accessibility to a given address.
*/
useracc(vaddr, count, access)
caddr_t vaddr;
u_int count;
int access;
{
register struct pte *pte;
struct pte *addrtopte();
pte = addrtopte(vaddr, count);
if (pte == NULL)
return (0);
count = btop((int)(vaddr + count - 1)) - btop((int)vaddr) + 1;
access = access == B_READ ? 0 : PG_W;
while (count--) {
if (((*(int *)pte) & PG_S) ||
(((*(int *)pte) & PG_W)) < access)
return (0);
pte++;
}
return (1);
}
/*
* Check kernel accessibility to a given address.
* Unlike the vax, vaddr is checked against the range of Sysmap only!
*/
kernacc(vaddr, count, access)
caddr_t vaddr;
u_int count;
int access;
{
register struct pte *ppte = &Sysmap[btop((int)vaddr - KERNELBASE)];
extern struct pte ESysmap[];
count = btoc((int)vaddr + count) - btop(vaddr);
if (ppte + count > ESysmap || ppte < Sysmap)
return (0);
access = access == B_READ ? 0 : PG_W;
while (count--) {
if (!ppte->pg_v || ((((*(int *)ppte) & PG_W)) < access))
return (0);
ppte++;
}
return (1);
}
/*
* Check for valid program size
*/
chksize(ts, ds, ss)
unsigned ts, ds, ss;
{
static int maxdmap = 0;
if (ts > MAXTSIZ || ds > MAXDSIZ || ss > MAXSSIZ) {
u.u_error = ENOMEM;
return (1);
}
/* check for swap map overflow */
if (maxdmap == 0) {
register int i, blk;
blk = DMMIN;
for (i = 0; i < NDMAP; i++) {
maxdmap += blk;
if (blk < DMMAX)
blk *= 2;
}
}
if (ctod(ts) > NXDAD*DMTEXT ||
ctod(ds) > maxdmap || ctod(ss) > maxdmap) {
u.u_error = ENOMEM;
return (1);
}
/*
* Make sure the process isn't bigger than our
* virtual memory limit.
*
* THERE SHOULD BE A CONSTANT FOR THIS.
*/
if (ctos(ts + LOWPAGES) + ctos(ds) + ctos(ss + HIGHPAGES) >
ctos(btop(USRSTACK))) {
u.u_error = ENOMEM;
return (1);
}
return (0);
}
/*
* Change the translation for the current proc
* to reflect the change made in software ptes
* starting at ppte for size ptes.
*/
newptes(ppte, v, size)
register struct pte *ppte;
u_int v;
int size;
{
register int i, fs, ls, need;
if (getcontext() == KCONTEXT) {
if (u.u_procp->p_ctx)
usetup();
else
return;
}
fs = ptos(v); /* convert page pointer for 1st seg */
ls = ptos(v + size - 1); /* convert page pointer for last seg */
need = ppte->pg_v;
for (i = fs; i <= ls; i++) /* go through list and set ptes */
pmegload(i, need);
}
/*
* Move pages from one kernel virtual address to another.
* Both addresses are assumed to reside in the Sysmap,
* and size must be a multiple of CLSIZE.
*/
pagemove(from, to, size)
register caddr_t from, to;
int size;
{
register struct pte *fpte, *tpte;
if (size % CLBYTES)
panic("pagemove");
fpte = &Sysmap[btop((int)from - KERNELBASE)];
tpte = &Sysmap[btop((int)to - KERNELBASE)];
while (size > 0) {
*tpte++ = *fpte;
setpgmap(to, *(long *)fpte);
*(int *)fpte++ = 0;
setpgmap(from, (long)0);
from += NBPG;
to += NBPG;
size -= NBPG;
}
}
/*
* Check the validity of a user address range and return NULL
* on error or a pointer to the first pte for these addresses.
*/
struct pte *
addrtopte(vaddr, count)
caddr_t vaddr;
u_int count;
{
register struct proc *p = u.u_procp;
register int fv, lv;
int tss, dss, sss;
fv = btop((int)vaddr);
lv = btop((int)(vaddr + count - 1));
if (lv < fv || fv < btop(USRTEXT) || lv >= btop(USRSTACK))
return (NULL);
/*
* Check that the request was within the
* user's valid address space. Can't use
* isa[tds]sv because they don't check the holes.
*/
tss = tptov(p, 0);
dss = dptov(p, 0);
sss = sptov(p, p->p_ssize - 1);
if (fv >= tss && lv < tss + p->p_tsize)
return (tptopte(p, vtotp(p, fv)));
else if (fv >= dss && lv < dss + p->p_dsize)
return (dptopte(p, vtodp(p, fv)));
else if (fv >= sss && lv < sss + p->p_ssize)
return (sptopte(p, vtosp(p, fv)));
return (NULL);
}
#define ONBPG 2048 /* old page size */
#define ONBSG 32768 /* old segment size */
/*
* Routine used to check to see if an a.out can be executed
* by the current machine/architecture.
*/
chkaout()
{
if ((u.u_exdata.ux_mach == M_68010) ||
(u.u_exdata.ux_mach == M_68020))
return (0);
else
return (ENOEXEC);
}
/*
* The following functions return information about an a.out
* which is used when a program is executed.
*/
/*
* Return the size of the text segment adjusted for the type of a.out.
*/
size_t
getts()
{
return (clrnd(btoc(u.u_exdata.ux_tsize)));
}
/*
* Return the size of the data segment depending on the type of a.out.
* For the case of an old a.out we need to allow for the old segment
* alignment and the fact that the text segment starts at 32k and not 8k.
* To do this we calculate the size of the text segment and round
* it to the next old Sun-2 segment boundary.
*/
size_t
getds()
{
return (clrnd(btoc(u.u_exdata.ux_dsize + u.u_exdata.ux_bsize)));
}
/*
* Return the load memory address for the data segment.
*/
caddr_t
getdmem()
{
return ((caddr_t)ctob(dptov(u.u_procp, 0)));
}
/*
* Return the starting disk address for the data segment.
*/
getdfile()
{
if (u.u_exdata.ux_mag == ZMAGIC)
return (u.u_exdata.ux_tsize);
else
return (sizeof (u.u_exdata) + u.u_exdata.ux_tsize);
}
/*
* Return the load memory address for the text segment.
*/
caddr_t
gettmem(up)
struct user *up;
{
return ((caddr_t)USRTEXT);
}
/*
* Return the file byte offset for the text segment.
*/
gettfile(up)
struct user *up;
{
if (up->u_exdata.ux_mag == ZMAGIC)
return (0);
else
return (sizeof (u.u_exdata));
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.