|
|
Power 6/32 Unix version 1.2b
/* vm_machdep.c 6.1 83/07/29 */
#include "../machine/pte.h"
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/cmap.h"
#include "../h/mount.h"
#include "../h/vm.h"
#include "../h/text.h"
#include "../machine/mtpr.h"
/*
* Set a red zone in the kernel stack after the u. area.
*/
setredzone(pte, vaddr)
register struct pte *pte;
caddr_t vaddr;
{
pte += (sizeof (struct user) + NBPG - 1) / NBPG;
*(int *)pte &= ~PG_PROT;
*(int *)pte |= PG_URKR;
if (vaddr)
mtpr(vaddr + sizeof (struct user) + NBPG - 1, TBIS);
}
#ifndef mapin
mapin(pte, v, pfnum, count, prot)
struct pte *pte;
u_int v, pfnum;
int count, prot;
{
while (count > 0) {
*(int *)pte++ = pfnum | prot;
mtpr(ptob(v), TBIS);
v++;
pfnum++;
count--;
}
}
#endif
#ifdef notdef
/*ARGSUSED*/
mapout(pte, size)
register struct pte *pte;
int size;
{
panic("mapout");
}
#endif
/*
* Check for valid program size
*/
chksize(ts, ds, ss)
register 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 (ts + ds + ss + LOWPAGES + HIGHPAGES > btoc(USRSTACK)) {
u.u_error = ENOMEM;
return (1);
}
return (0);
}
/*ARGSUSED*/
newptes(pte, v, size)
register struct pte *pte;
u_int v;
register int size;
{
register caddr_t a = ptob(v);
#ifdef lint
pte = pte;
#endif
if (size >= 8) {
mtpr(0, TBIA);
return;
}
while (size > 0) {
mtpr(a, TBIS);
a += NBPG;
size--;
}
}
/*
* Change protection codes of text segment.
* Have to flush translation buffer since this
* affect virtual memory mapping of current process.
*/
chgprot(addr, tprot)
caddr_t addr;
long tprot;
{
unsigned v;
int tp;
register struct pte *pte;
register struct cmap *c;
v = clbase(btop(addr));
if (!isatsv(u.u_procp, v)) {
u.u_error = EFAULT;
return (0);
}
tp = vtotp(u.u_procp, v);
pte = tptopte(u.u_procp, tp);
if (pte->pg_fod == 0 && pte->pg_pfnum) {
c = &cmap[pgtocm(pte->pg_pfnum)];
if (c->c_blkno && c->c_mdev != MSWAPX)
munhash(mount[c->c_mdev].m_dev,
(daddr_t)(u_long)c->c_blkno);
}
*(int *)pte &= ~PG_PROT;
*(int *)pte |= tprot;
distcl(pte);
tbiscl(v);
return (1);
}
settprot(tprot)
long tprot;
{
register int *ptaddr, i;
ptaddr = (int *)mfpr(P0BR);
for (i = 0; i < u.u_tsize; i++) {
ptaddr[i] &= ~PG_PROT;
ptaddr[i] |= tprot;
}
mtpr(0, TBIA);
}
/*
* Rest are machine-dependent
*/
getmemc(addr)
caddr_t addr;
{
register int c;
struct pte savemap;
savemap = mmap[0];
*(int *)mmap = PG_V | PG_KR | btop(addr);
mtpr(vmmap, TBIS);
uncache (&vmmap[(int)addr & PGOFSET]);
c = *(char *)&vmmap[(int)addr & PGOFSET];
mmap[0] = savemap;
mtpr(vmmap, TBIS);
return (c & 0377);
}
putmemc(addr, val)
caddr_t addr;
{
struct pte savemap;
savemap = mmap[0];
*(int *)mmap = PG_V | PG_KW | btop(addr);
mtpr(vmmap, TBIS);
*(char *)&vmmap[(int)addr & PGOFSET] = val;
mtpr (0, PADC);
mtpr (0, PACC);
mmap[0] = savemap;
mtpr(vmmap, TBIS);
}
/*
* 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(from - 0xC0000000)];
tpte = &Sysmap[btop(to - 0xC0000000)];
while (size > 0) {
*tpte++ = *fpte;
*(int *)fpte++ = 0;
mtpr(from, TBIS);
mtpr(to, TBIS);
mtpr(to, P1DC); /* purge !! */
from += NBPG;
to += NBPG;
size -= NBPG;
}
}
/*
* Some code and data key management routines.
* The arrays ckey_cnt and ckey_cache are allways kept in such a way
* that the following invariant holds:
* (ckey_cnt > 0) ==> (ckey_cache == 1)
* meaning as long as a code key is used by at least one process, it's
* marked as being 'in the cache'. Of course, the following invariant
* also holds:
* (ckey_cache==0) ==> (ckey_cnt==0)
* which is just the reciprocal of the 1'st invariant.
* Equivalent invariants hold for the data key arrays.
*/
int dbg_gck,
dbg_gck1,
dbg_gck2,
dbg_gck3,
dbg_gck4,
dbg_gdk,
dbg_gdk1,
dbg_gdk2,
dbg_gdk3;
/*
* ckeyrelease -- release a code key.
*/
ckeyrelease (key)
int key;
{
register int ipl;
ipl = spl8();
if (--ckey_cnt[key] < 0) {
printf ("ckeyrelease: key = %d\n", key);
ckey_cnt[key] = 0;
}
splx (ipl);
}
/*
* dkeyrelease -- release a data key.
*/
dkeyrelease (key)
int key;
{
register int ipl;
ipl = spl8();
if (--dkey_cnt[key] != 0) {
printf ("dkeyrelease: key = %d\n", key);
dkey_cnt[key] = 0;
}
splx (ipl);
}
/*
* getcodekey -- get a code key.
*/
getcodekey()
{
register int i,
ipl,
allocated, /* number of non-zero ckey_cnt's */
shared_key,
return_key;
register struct proc *p;
dbg_gck++;
ipl = spl8();
allocated = 0;
for (i = 1; i <= MAXCKEY; i++) {
if ((int) ckey_cache[i] == 0) { /* Bingo */
ckey_cache[i] = 1;
ckey_cnt[i] = 1;
splx (ipl);
dbg_gck1++;
return (i);
}
if (ckey_cnt[i] != 0)
allocated++;
if (ckey_cnt[i] > 1 && i != MAXCKEY)
shared_key = i;
}
/*
* If we are here, all code keys were marked as being in cache.
* Moreover, we are assured that 'shared_key' has a meaningful value,
* since we know that the 'init' process and the 'shell' are around
* and they have shared text!
*
* Two cases: some of them are free for re-allocation, or all of
* them are currently allocated. In this (second) case, a more
* drastic procedure will follow - i.e. we strip some processes of
* their keys and let them get new ones whenever they need it.
*/
if (allocated < MAXCKEY) {
/*
* This is the easy case.
*/
for (i = 1; i <= MAXCKEY; i++) {
if (ckey_cnt[i] == 0) {
ckey_cache[i] = 0;
return_key = i;
}
}
ckey_cnt[return_key] = 1;
ckey_cache[return_key] = 1;
mtpr (0, PACC);
splx (ipl);
dbg_gck2++;
return (return_key);
}
/*
* Now we have to get nasty.
* Strip some of them of the code key. First time,
* 1) Try hard not to do that to kernel processes !!
* 2) Try hard NOT to strip shared text processes of
* their (shared) key, because then they'll run
* with different keys from now on, i.e. less efficient
* cache utilization.
*/
for (p = proc; p < procNPROC; p++) {
/*
* Look for a meaningful key but not
* used and not shared text.
*/
if (p->p_ckey && p->p_ckey!=MAXCKEY && ckey_cnt[p->p_ckey]<2) {
i = p->p_ckey;
p->p_ckey = 0;
ckey_cnt[i] = 1;
ckey_cache[i] = 1;
mtpr (0, PACC);
splx (ipl);
dbg_gck3++;
return (i);
}
}
/*
* Second time around!
* Highly unlikely situation. It means that all keys are
* allocated AND shared (i.e. we have at least 510 active
* processes).
* Strip some of them. We pick some key (known to be shared
* by several processes) and strip the poor process group.
* At least 2 processes will loose but we gain one key to be reused.
* The way 'shared_key' was produced (above) virtually assures
* us that this key isn't the 'init' group key (1) nor the
* 'shell' group key (2 or 3). It's probably something like 254.
* Could be more straightforward to strip all processes, but it's
* better to invest in one more loop here and keep the cache
* utilization to a maximum.
*/
for (p = proc; p < procNPROC; p++) {
if (p->p_ckey == shared_key) {
p->p_ckey = 0;
ckey_cnt[shared_key]--;
}
}
if (ckey_cnt[shared_key] != 0)
printf("getcodekey: key = %d cnt = %d\n",
shared_key, ckey_cnt[shared_key]);
ckey_cnt[shared_key] = 1;
ckey_cache[shared_key] = 1;
mtpr (0, PACC);
splx (ipl);
dbg_gck4++;
return (shared_key);
}
/*
* getdatakey -- get a data key.
*
* General strategy:
* 1) Try to find a data key that isn't in the cache. Allocate it.
* 2) If all data keys are in the cache, find one which isn't
* allocated. Clear all status and allocate this one.
* 3) If all of them are allocated, pick some process, strip him
* of the data key and allocate it. We (cold-bloodedly) pick
* one process to be the poor looser because that's the
* easiest way to do it and because this extreme situation
* ( >255 active processes ) is expected to be temporary,
* after which 1) or 2) above should be the usual case.
* The poor looser is the first process which has a data key.
* However, we try to spare known kernel processes and daemons
* (fired at bootstrap time), by searching from proc[LOOSER] and on.
*/
getdatakey()
{
register int i,
ipl,
allocated, /* number of non-zero dkey_cnt's */
return_key;
register struct proc *p;
#define LOOSER 20
dbg_gdk++;
ipl = spl8();
allocated = 0;
for (i = 1; i <= MAXDKEY; i++) {
if ((int) dkey_cache[i] == 0) {
/*
* Case 1. The best case.
*/
dkey_cache[i] = 1;
dkey_cnt[i] = 1;
splx (ipl);
dbg_gdk1++;
return (i);
}
if (dkey_cnt[i] > 0)
allocated++;
}
if (allocated < MAXDKEY) {
/*
* Case 2. This is the easy case.
*/
for (i = 1; i <= MAXDKEY; i++) {
if (dkey_cnt[i] == 0) {
dkey_cache[i] = 0;
return_key = i;
}
}
dkey_cnt[return_key] = 1;
dkey_cache[return_key] = 1;
mtpr (0, PADC);
splx (ipl);
dbg_gdk2++;
return (return_key);
}
/*
* Now, we have to take a code from someone.
*/
for (p = &proc[LOOSER]; p < procNPROC; p++) {
if (p->p_dkey != 0) {
i = p->p_dkey;
p->p_dkey = 0;
dkey_cnt[i] = 1;
dkey_cache[i] = 1;
mtpr (0, PADC);
splx (ipl);
dbg_gdk3++;
return (i);
}
}
panic ("getdatakey");
}
/* General (includes system) virtual address to physical */
vtoph(p, v)
register struct proc *p;
register unsigned v;
{
register struct pte *thispte;
thispte = vtopte (p, btop(v));
return ( (thispte->pg_pfnum << PGSHIFT) + (v & PGOFSET));
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.