Coherent4.2.10/i386/mmu.c
/* $Header: /ker/i386/RCS/mmu.c,v 2.5 93/10/29 00:57:12 nigel Exp Locker: nigel $ */
/*
* MMU dependent code for Coherent 386
*
* Copyright (c) Ciaran O'Donnell, Bievres (FRANCE), 1991
*
* $Log: mmu.c,v $
* Revision 2.5 93/10/29 00:57:12 nigel
* R98 (aka 4.2 Beta) prior to removing System Global memory
*
* Revision 2.4 93/09/02 18:11:38 nigel
* Minor edits to prepare for DDI/DKI integration
*
* Revision 2.3 93/08/19 03:40:05 nigel
* Nigel's R83
*/
#include <common/ccompat.h>
#include <common/_tricks.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include <coh/misc.h>
#include <limits.h>
#define _KERNEL 1
#include <kernel/trace.h>
#include <kernel/alloc.h>
#include <kernel/param.h>
#include <kernel/reg.h>
#include <sys/mmu.h>
#include <sys/proc.h>
#include <sys/clist.h>
#include <sys/inode.h>
#include <sys/seg.h>
#include <sys/buf.h>
#include <sys/filsys.h>
#include <l.out.h>
#include <ieeefp.h>
#define PTABLE0_P 0x00001 /* Page directory physical address. */
#define PPTABLE1_V 0xFFFFC /* Virtual address of the page table
* for the virtual page table.
*/
#define INSERT2(p, pp) ((void) ((p)->forw = (pp), (p)->back = (pp)->back, \
(pp)->back->forw = (pp)->back = (p)))
#define DELETE2(p) ((p)->forw->back = (p)->back, \
(p)->back->forw = (p)->forw, \
(p)->forw = (p)->back = (p))
#define INIT2(lp) ((lp)->forw = (lp)->back = (lp))
extern unsigned int total_mem; /* Total physical memory in bytes. */
#define BUDDY(addr,size) ((addr) ^ (1 << (size)))
#define SPLASH 3
#define NDATA 4 /* process data segments */
#define BLKSZ 2 /* log2 sizeof(BLOCKLIST)/sizeof(cseg_t) */
/* These defines belong somewhere else: */
#define LOMEM 0x15 /* CMOS address of size in K of memory below 1MB. */
#define EXTMEM 0x17 /* CMOS address of size in K of memory above 1MB. */
#define ONE_K 1024
#define ONE_MEG 1048576
#define USE_NDATA 1
/*
* End of kernel (i.e., end of BSS).
*/
extern char __end [];
/*
* For 0 < i < 64, buddysize [i] is log (base 2) of nearest power of two
* which is greater than or equal to i.
*/
char buddysize [64] = {
-1, 0, 1, 2, 2, 3, 3, 3,
3, 4, 4, 4, 4, 4, 4, 4,
4, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
5, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6 };
#define min(a, b) ((a) < (b) ? (a) : (b))
/*
* Is 'p' a valid physical page address?
*/
#define pvalid(p) ((p) >= sysmem.lo && (p) < sysmem.hi)
/*
* Functions.
* Import.
* Export.
* Local.
*/
void areacheck ();
void areafree ();
void areainit ();
struct __blocklist * arealloc ();
int areasize ();
cseg_t * c_extend ();
int c_grow ();
int countsize ();
char * getPhysMem ();
void physMemInit ();
SR * loaded ();
void sunload ();
void valloc ();
/* Read a 16 byte number from the CMOS. */
#if __USE_PROTO__
unsigned int read16_cmos (unsigned int addr)
#else
unsigned int
read16_cmos (addr)
unsigned int addr;
#endif
{
unsigned char read_cmos ();
return (read_cmos (addr + 1) << 8) + read_cmos (addr);
}
/*
* Load a segment's page entries into the page table.
*/
#if __USE_PROTO__
void load_seg_pages (SR * srp)
#else
void
load_seg_pages (srp)
SR * srp;
#endif
{
int n;
cseg_t * pp;
int base1;
int akey;
pp = srp->sr_segp->s_vmem;
base1 = btocrd (srp->sr_base);
n = btocru (srp->sr_size);
/*
* we load all pages
*/
/* a shm segment ref may be Read-Write or Read-Only */
if (srp->sr_flag & SRFRODT)
akey = SEG_RO;
else {
switch (srp->sr_segp->s_flags & (SFSYST | SFTEXT)) {
case SFTEXT:
akey = SEG_RO;
break;
case SFSYST:
akey = SEG_SRW;
break;
default:
akey = SEG_RW;
break;
}
}
do
ptable1_v [base1 ++] = (* pp ++ & ~ SEG_NPL) | akey;
while (-- n);
}
/*
* Mark all the pages that are occupied by a segment as being invalid in the
* active page table.
*/
#if __USE_PROTO__
void remove_seg_pages (SR * srp)
#else
void
remove_seg_pages (srp)
SR * srp;
#endif
{
int n;
int base1;
base1 = btocrd (srp->sr_base);
n = btocru (srp->sr_size);
do {
ptable1_v [base1 ++] = SEG_ILL;
} while (-- n);
}
/*
* Load pages from a segment into the page table, and request a paging TLB
* flush.
*/
#if __USE_PROTO__
void doload (SR * srp)
#else
void
doload (srp)
SR * srp;
#endif
{
load_seg_pages (srp);
mmuupd ();
}
/*
* Remove the pages that are occupied by a segment from the active page table,
* and request a paging TLB flush.
*/
#if __USE_PROTO__
void unload (SR * srp)
#else
void
unload (srp)
SR * srp;
#endif
{
remove_seg_pages (srp);
mmuupd ();
}
/*
* Allocate 'pages_wanted' pages of core space.
* Returns physical segment descriptor if success, else NULL.
* The physical segment descriptor is a table of page table entries
* suitable for insertion into a page table.
*/
cseg_t *
c_alloc (pages_wanted)
unsigned pages_wanted;
{
unsigned pno;
cseg_t * pp;
register cseg_t * qp;
/* Do we have enough free physical pages for this request? */
if (pages_wanted > allocno ())
return NULL;
/* Allocate some space for the table to return. */
if ((pp = (cseg_t *) arealloc (pages_wanted)) == NULL)
return NULL;
qp = pp;
/* fill in entries in the requested table */
do {
pno = * -- sysmem.pfree;
if (! pvalid (pno))
panic ("c_alloc");
* qp ++ = (ctob (pno) & ~ SEG_BITS) | SEG_PRE;
} while (-- pages_wanted);
return pp;
}
/*
* Given an array "pp" containing "numPages" page descriptors,
* if "pp" is the page list for a user segment currently loaded
* invalidate page entries for "pp" in the current page table
* return each page in "pp" to the sysmem pool, if it came from there.
* return the array "pp" to the buddy pool.
*/
void
c_free (pp, numPages)
cseg_t * pp;
unsigned numPages;
{
unsigned pno;
register cseg_t * qp;
register int sz;
SR * srp;
if ((srp = loaded (pp)) != NULL) {
unload (srp);
srp->sr_segp = 0;
}
sz = numPages;
if (& sysmem.pfree [sz] > sysmem.efree)
panic ("c_free - nalloc");
qp = pp;
do {
if ((* qp & SEG_NPL) == 0) {
pno = btocrd (* qp);
if (! pvalid (pno))
panic ("c_free");
* sysmem.pfree ++ = pno;
} else {
T_HAL (0x40000, printf ("c_free NPL %x ", * qp));
}
qp ++;
} while (-- sz);
areafree ((struct __blocklist *) pp, numPages);
}
MAKESR (physMem, _physMem);
extern int PHYS_MEM; /* Number of bytes of contiguous RAM needed */
/*
* A block of contiguous physical memory has been allocated for special
* i / o devices.
* Problem: pages of physical memory are in reverse order in the
* page table.
* This routine reverses the page table entries for the pages
* involved. It relies * heavily * on all pages having virtual addresses
* in the FFCx xxxx segment.
*
* If all goes well, assign physAvailStart to the virtual address of
* the beginning of the region, and physAvailBytes to the number of bytes
* in the region. Otherwise, leave physAvailStart and physAvailBytes at 0.
*
* As memory is allocated, physAvailStart advances to point to the next
* available byte of contiguous memory, physAvailBytes is decremented,
* and physPoolStart remains set to the virtual address of the start of
* the contiguous pool.
*/
static int physPoolStart; /* start of contiguous memory area */
static int physAvailStart; /* next free byte in contiguous memory area */
static int physAvailBytes; /* number of bytes in contiguous memory area */
void
physMemInit ()
{
int m;
int err = 0, num_pages = btocru (PHYS_MEM);
int prevPaddr, paddr;
/*
* Going half way into page table for physMem
* If entry and its complementary entry aren't both in top segment
* Error exit (no phys mem will be available).
* Get page table entries and swap them.
*/
for (m = 0; m < num_pages / 2; m ++) {
int m2 = num_pages - 1 - m; /* complementary index */
/* compute virtual addresses */
int lo_addr = physMem.sr_base + ctob (m);
int hi_addr = physMem.sr_base + ctob (m2);
/* compute indices into page table (ptable1_v) */
int lo_p1ix = btocrd (lo_addr);
int hi_p1ix = btocrd (hi_addr);
/* fetch physical addresses from page table */
int lo_paddr = ptable1_v [lo_p1ix];
int hi_paddr = ptable1_v [hi_p1ix];
/* abort if either address is not in top segment */
if (btosrd (lo_addr) != 0x3FF) {
err = 1;
break;
}
if (btosrd (hi_addr) != 0x3FF) {
err = 1;
break;
}
/* exchange page table entries */
ptable1_v [lo_p1ix] = hi_paddr;
ptable1_v [hi_p1ix] = lo_paddr;
}
/*
* Final sanity check.
* In case someone gets creative with startup code, check
* again here that the memory is actually contiguous.
*/
prevPaddr = __coh_vtop (physMem.sr_base);
for (m = 0; m < num_pages - 1; m ++) {
paddr = __coh_vtop (physMem.sr_base + ctob (m + 1));
if (paddr - prevPaddr != NBPC) {
err = 1;
break;
}
prevPaddr = paddr;
}
if (! err) {
physPoolStart = physAvailStart = physMem.sr_base;
physAvailBytes = PHYS_MEM;
}
}
/*
* Return virtual address of block of contiguous physical memory.
* If request cannot be granted, return 0.
*
* Expect physMem resource to be granted during load routine of device
* drivers. Once allocated, memory is not returned to the physMem pool.
*/
char *
getPhysMem (numBytes)
unsigned numBytes;
{
char * ret;
if (numBytes > physAvailBytes) {
printf ("getPhysMem failed - %d additional bytes "
"PHYS_MEM needed\n", physAvailBytes - numBytes);
return NULL;
}
ret = (char *) physAvailStart;
physAvailStart += numBytes;
physAvailBytes -= numBytes;
return ret;
}
/*
* Return virtual address of aligned block of contiguous physical memory.
* Mainly for devices using the stupid Intel DMA hardware without
* scatter/gather.
* If request cannot be granted, return 0.
*
* Argument "align" says what physical boundary we need alignment on.
* It must be a power of 2.
* For 4k alignment, align = 4k, etc.
* Sorry, but will throw away memory to get to the next acceptable address.
*
* Once allocated, memory is not returned to the physMem pool.
*/
char *
getDmaMem (numBytes, align)
unsigned numBytes;
unsigned align;
{
int wastedBytes;
if (align == 0) {
printf ("getDmaMem (0) (?)\n");
return NULL;
}
if (! __IS_POWER_OF_TWO (align)) {
printf ("getDmaMem (%x) (?)\n", align);
return NULL;
}
/*
* Waste RAM from bottom of pool up to physical
* address with desired alignment.
*/
wastedBytes = align - (__coh_vtop (physAvailStart) % align);
if (getPhysMem (wastedBytes) == NULL)
return NULL;
return getPhysMem (numBytes);
}
/*
* Check whether a range of physical addresses lies within the
* pool of contiguous physical memory.
*/
#if __USE_PROTO__
int physValid (unsigned int base, unsigned int numBytes)
#else
int
physValid (base, numBytes)
unsigned int base, numBytes;
#endif
{
int vpool;
int ret = 0;
if (PHYS_MEM) {
vpool = __coh_vtop (physPoolStart);
T_HAL (0x40000, printf ("PHYS_MEM phys addrs %x to %x ",
vpool, vpool + PHYS_MEM));
if (base >= vpool && (base + numBytes) <= (vpool + PHYS_MEM))
ret = 1;
} else {
T_HAL (0x40000, printf ("No PHYS_MEM "));
}
T_HAL (0x40000, printf ("physValid (%x, %x) = %d ", base, numBytes, ret));
return ret;
}
/*
* Given a user virtual address, a physical address, and a byte
* count, map the specified virtual address into the user data
* page table for the current process.
*
* This is meant to be called from the console ioctl, KDMAPDISP.
* The user virtual address must be page aligned.
* The range of physical addresses must lie outside installed RAM
* or within the "PHYS_MEM" pool.
*
*
* Return 1 on success, else 0.
*/
#if __USE_PROTO__
int mapPhysUser (__caddr_t virtAddr, int physAddr, int numBytes)
#else
int
mapPhysUser (virtAddr, physAddr, numBytes)
__caddr_t virtAddr;
int physAddr;
int numBytes;
#endif
{
int ret = 0;
SR * srp = SELF->p_segl + SIPDATA;
SEG * sp = srp->sr_segp;
cseg_t * pp = sp->s_vmem, * qp;
int pno, pageOffset, numPages, i;
/* Check alignment. */
if (((int)virtAddr & (NBPC - 1)) || (physAddr & (NBPC - 1))) {
T_HAL (0x40000, printf ("mPU: failed alignment "));
goto mPUdone;
}
/*
* If "numBytes" is not a multiple of page size,
* round it up before proceeding.
*/
numBytes = __ROUND_UP_TO_MULTIPLE(numBytes, NBPC);
/* Check validity of range of virtual addresses. */
if (virtAddr < srp->sr_base ||
virtAddr + numBytes >= srp->sr_base + srp->sr_size) {
T_HAL (0x40000, printf ("mPU: bad vaddr "));
goto mPUdone;
}
/* Check validity of range of physical addresses. */
/* if not in PHYS_MEM pool... */
if (! physValid (physAddr, numBytes)) {
/* get installed RAM physical addresses */
unsigned int physLow = ctob ((read16_cmos (LOMEM) + 3) >> 2);
unsigned int physHigh = ctob ((read16_cmos (EXTMEM) + 3) >> 2)
+ ONE_MEG;
T_HAL (0x40000, printf ("physLow =%x physHigh =%x ",
physLow, physHigh));
/* Fail if physical range overlaps installed base RAM. */
if (physAddr < physLow) {
T_HAL (0x40000, printf ("mPU: overlap base RAM "));
goto mPUdone;
}
/* Fail if physical range overlaps installed extended RAM. */
if (physAddr < physHigh && physAddr + numBytes >= ONE_MEG) {
T_HAL (0x40000, printf ("mPU: overlap extended RAM "));
goto mPUdone;
}
}
/*
* For each page in user data segment which is to be remapped
* if current page was taken from sysmem pool
* return current page to sysmem pool
* write new physical address into current page entry
* mark current page as not coming from sysmem pool
* map current page into page table
*/
/* NIGEL: cast to long to make GCC compile this */
pageOffset = btocrd (virtAddr - (long) srp->sr_base);
numPages = numBytes >> BPCSHIFT;
for (qp = pp + pageOffset, i = 0; i < numPages; i ++, qp ++) {
if ((* qp & SEG_NPL) == 0) {
pno = btocrd (* qp);
if (! pvalid (pno)) {
T_HAL (0x40000, printf ("mPU: bad release "));
} else {
* sysmem.pfree ++ = pno;
T_HAL (0x40000,
printf ("mPU: freeing virtual page %x ",
virtAddr + ctob (i)));
}
} else {
T_HAL (0x40000,
printf ("mPU: rewriting virtual NPL page %x ",
virtAddr + ctob (i)));
}
* qp = (physAddr + ctob (i)) | (SEG_RW | SEG_NPL);
ptable1_v [btocrd (virtAddr) + i] = * qp;
}
mmuupd ();
ret = 1;
mPUdone:
return ret;
}
/*
* Add a page to a segment.
* Enlarge buddy table for segment, if needed.
*
* Arguments:
* pp points to segment reference table (segp->s_vmem, e.g.)
* osz is old segment size, in pages
*
* Return pointer to enlarged segment reference table, or NULL if failed.
*/
cseg_t *
c_extend (pp, osz)
register cseg_t * pp;
int osz;
{
register cseg_t * pp1;
register unsigned pno;
register int i;
SR * srp;
/* Fail if no more free pages available. */
if (sysmem.pfree < & sysmem.tfree [1])
goto no_c_extend;
/* Don't grow segment beyond hardware segment size (4 megabytes). */
if (osz >= (NBPS / NBPC))
goto no_c_extend;
if (srp = loaded (pp)) {
unload (srp);
srp->sr_segp = 0;
}
/*
* If the old size was a power of 2, it has used up an entire
* buddy area, so we will need to allocate more space.
*/
if (__IS_POWER_OF_TWO (osz)) {
if ((pp1 = (cseg_t *) arealloc (osz + 1))== 0)
goto no_c_extend;
for (i = 0; i < osz; i ++)
pp1 [i] = pp [i];
areafree (pp, osz);
pp = pp1;
}
for (i = osz; -- i >= 0;)
pp [i + 1] = pp [i];
pno = * -- sysmem.pfree;
if (! pvalid (pno))
panic ("c_extend");
pp [0] = ctob (pno) | SEG_RW;
return pp;
no_c_extend:
return 0;
}
/*
* Given segment size in bytes, estimate total space needed
* to keep track of the segment (I think - hws).
*
* return value is num_bytes plus some overhead...
*/
int
countsize (num_bytes)
int num_bytes;
{
int ret;
if (num_bytes <= NBPC / sizeof (long))
ret = num_bytes + 1;
else
ret = num_bytes
+ ((num_bytes + NBPC / sizeof (long) - 1) >> BPC1SHIFT) + 1;
return ret;
}
/*
* buddy allocation
*/
/*
* Deallocate a segment descriptor area.
* "sp" is not really a struct __blocklist *, rather a cseg_t *.
* "numPages" is the number of pages referenced in the area.
*/
void
areafree (sp, numPages)
struct __blocklist * sp;
int numPages;
{
int n; /* adresse du buddy, taille du reste */
int ix, nx;
struct __blocklist * buddy;
areacheck (2, sp);
/*
* Pointer "sp" points to an element in the sysmem table of
* free pages.
* Integer "ix" is the index of "sp" into that table.
* Will use "ix" to index into one or more buddy tables.
*/
ix = sp - sysmem.u.budtab;
n = areasize (numPages);
do {
/* "nx" is index of buddy element to the one at "ix". */
nx = BUDDY (ix, n);
if (sysmem.budfree [nx >> WSHIFT] & 1 <<(nx &(WCOUNT-1))) {
/* coalesce two buddies */
buddy = sysmem.u.budtab + nx;
if (buddy->kval != n)
break;
sysmem.budfree [nx >> WSHIFT] &= ~ (1 <<(nx & (WCOUNT-1)));
DELETE2(buddy);
if (nx < ix)
ix = nx;
} else
break;
} while (++ n < NBUDDY);
sysmem.budfree [ix >> WSHIFT] |= 1 << (ix & (WCOUNT-1));
buddy = sysmem.u.budtab + ix;
INSERT2 (buddy, & sysmem.bfree [n]);
buddy->kval = n;
areacheck (3, buddy);
}
/*
* arealloc ()
*
* Given size in "pages" of a segment to manage,
* return pointer to an array of enough descriptors.
* If not enough free descriptors available, return 0.
*/
struct __blocklist *
arealloc (pages)
int pages;
{
struct __blocklist * sp;
struct __blocklist * p, * q;
int size;
struct __blocklist * rsp;
int nx;
areacheck (0, 0);
size = areasize (pages);
/*
* 1. Find little end, bloc p, free >= size
*/
for (q = p = sysmem.bfree + size;p->forw == p; size ++, p ++)
if (p >= sysmem.bfree + NBUDDY - 1) {
return 0; /* y en a pas */
}
rsp = p->forw;
DELETE2(rsp);
nx = rsp - sysmem.u.budtab;
sysmem.budfree [nx >> WSHIFT] &= ~(1 << (nx & (WCOUNT-1)));
size = 1 << size;
sp = rsp + size; /* buddy address */
while (p-- != q) {
/*
* 2.1 The block is too big, uncouple & free buddy
*/
sp -= (size >>= 1);
nx = sp - sysmem.u.budtab;
sysmem.budfree [nx >> WSHIFT] |= 1 << (nx & (WCOUNT-1));
INSERT2 (sp, p);
sp->kval = p - sysmem.bfree;
}
areacheck (1, rsp);
return rsp;
}
void
areainit (n)
{
register int i;
for (i = 0; i < (1 <<(NBUDDY-WSHIFT)); i ++)
sysmem.budfree [i] = 0;
for (i = 0; i < NBUDDY; i ++)
INIT2(& sysmem.bfree [i]);
sysmem.u.budtab = (struct __blocklist *) __end;
n /= sizeof (struct __blocklist);
if (n > (1 << NBUDDY))
panic ("areainit");
for (i = 0; i < n; i ++)
areafree (& sysmem.u.budtab [i],
sizeof (struct __blocklist) / sizeof (long));
}
/*
* areasize ()
*
* Do a log (base 2) calculation on n.
* If n is zero, return -1.
*
* Else, consider the nearest power of two which is greater than or
* equal to n
* p/2 < n <= p
* Then set p = 4 * (2 ** x). Note BLKSZ is 2.
* Return max (x, 0).
*
* If n is too large (more than 3F00), we will go beyond the limits of
* table buddysize [].
*
* In practice, n is the total number of pages needed in a segment,
* and the return value will be used to access a buddy system list.
*
* The buddy system tracks memory in 4-page chunks.
* areasize(pagecount) returns the log base 2 of the number of chunks,
* which gives an index into the buddy list tracking regions just large
* enough to accommodate pagecount.
*/
int
areasize (n)
register unsigned int n;
{
register int m;
#ifdef FROTZ
int ret, oldn = n;
#endif
if (n > 0x3F00)
panic ("areasize");
n = (n + (1 << BLKSZ) - 1) >> BLKSZ;
m = n & 0x3F;
#ifdef FROTZ
if ((n >>= 6) == 0)
ret = buddysize [m];
else {
int index;
index = n;
if (m)
index ++;
ret = buddysize [index] + 6;
}
return ret;
#else
if ((n >>= 6) == 0)
return buddysize [m];
return buddysize [n + ((m!= 0)?1:0)] + 6;
#endif
}
#define MAXBUDDY 2048
#define CHECK(p) ((p>=& sysmem.bfree [0] && p <& sysmem.bfree [NBUDDY]) || \
(p >= sysmem.u.budtab && p <& sysmem.u.budtab [1 << NBUDDY]))
void
areacheck (flag, sp)
struct __blocklist * sp;
{
struct __blocklist * next, * start;
int i, nx;
if (sp) {
if (& sysmem.u.budtab [sp-sysmem.u.budtab] != sp)
printf ("* check * %d %x %x\n", flag, sp, sysmem.u.budtab);
}
for (i = 0; i < NBUDDY; i ++) {
start = next = & sysmem.bfree [i];
do {
next = next->forw;
if (! CHECK (next))
printf ("next = %x (%d)\n", next, flag);
if (next->back != start)
printf ("%x->forw->back != %x\n", next, start);
if (next != & sysmem.bfree [i]) {
if (next->kval != i)
printf ("bad kval %x, %d (%d)\n",
next, next->kval, flag);
nx = next - sysmem.u.budtab;
if ((sysmem.budfree [nx >> WSHIFT] & (1 << (nx & (WCOUNT-1)))) == 0)
printf ("in bfree but not budfree %x (%d)\n", next, flag);
}
start = next;
} while (next != & sysmem.bfree [i]);
}
}
/*
* Load up segmentation registers.
*/
SR ugmtab [NUSEG];
void
segload ()
{
int i;
SR * start;
/*
* unprogram the currently active UGM user segments
*/
for (start = ugmtab + 1; start < ugmtab + NUSEG ; start ++) {
if (start->sr_segp != NULL)
remove_seg_pages (start);
start->sr_segp = NULL;
}
/*
* Load each segment in the p->p_region list into the MMU
* Remember values in ugmtab.
*/
start = ugmtab + 1;
for (i = 1; i < NUSEG; i ++) {
if (SELF->p_segl [i].sr_segp == NULL)
continue;
* start = SELF->p_segl [i];
switch (i) {
case SIPDATA:
if (SELF->p_segl [SISTACK].sr_base == 0)
break;
start->sr_size = min (start->sr_size,
(long) SELF->p_segl [SISTACK].sr_base -
SELF->p_segl [SISTACK].sr_size);
break;
case SISTACK:
start->sr_base -= start->sr_size;
break;
}
load_seg_pages (start);
start ++;
}
/*
* Update shm segment information, then flush the paging TLB.
*/
shmLoad ();
mmuupd ();
}
SR *
loaded (pp)
cseg_t * pp;
{
SR * start;
for (start = ugmtab; start < ugmtab + NUSEG; start ++)
if (start->sr_segp && start->sr_segp->s_vmem == pp)
return start;
return NULL;
}
MAKESR (r0stk, _r0stk);
extern int tss_sp0;
/*
* General initialization
*/
void
i8086()
{
unsigned csize, isize, allsize;
caddr_t base;
unsigned int calc_mem, boost;
extern caddr_t clistp;
extern SR blockp;
extern SR allocp;
/* This is the first C code executed after paging is turned on. */
workPoolInit ();
/*
* Allocate contiguous physical memory if PHYS_MEM is patched
* to a nonzero value.
*/
if (PHYS_MEM) {
physMem.sr_size = (PHYS_MEM + NBPC - 1) & ~ (NBPC - 1);
valloc (& physMem);
physMemInit ();
}
/*
* Allocate a page for ring 0 stack.
*/
r0stk.sr_size = NBPC;
valloc (& r0stk);
tss_sp0 = r0stk.sr_base + NBPC;
/*
* calc_mem is used for autosizing buffer cache and kalloc pool.
* It is total_mem, limited below by 1 meg and above by 12 meg.
* The upper limit is a temporary move to allow booting on 16 Meg
* systems.
*
* "boost" is used in autosizing buffer cache and kalloc pool.
* It is the number of megabytes of calc_mem above 1 meg, i.e.,
* a number between 0 and 11.
*/
if (total_mem < ONE_MEG)
calc_mem = ONE_MEG;
else if (total_mem > 12 * ONE_MEG)
calc_mem = 12 * ONE_MEG;
else
calc_mem = total_mem;
boost = (calc_mem - ONE_MEG) / ONE_MEG;
/*
* If the number of cache buffers was not explicitly set (i.e., ! 0)
* then calculate the number of buffers using the simple heuristic:
* 128 minimum + 400 per MB of available RAM (i.e., after 1MB)
*/
if (NBUF == 0)
NBUF = 128 + (400 * boost);
/*
* Calculate NHASH as the next lower prime number from NBUF.
*/
NHASH = nlp (NBUF);
/*
* If the amount of kalloc () space was not explicitly set (i.e., ! 0)
* then calculate using the simple heuristic:
* 64k minimum + 32k per MB of available RAM (i.e., after 1MB)
*/
if (ALLSIZE == 0)
ALLSIZE = 65536 + (32768 * boost);
blockp.sr_size = NBUF * BSIZE;
valloc (& blockp);
allocp.sr_size = allsize = NBUF * sizeof (BUF) + ALLSIZE;
allocp.sr_size += isize = NINODE * sizeof (INODE);
allocp.sr_size += csize = NCLIST * sizeof (CLIST);
valloc (& allocp);
base = allocp.sr_base;
KMEM_INIT (base, allsize);
base += allsize;
inode_table = (struct inode *) base;
base += isize;
clistp = base;
}
/*
* Allocate srp->sr_size bytes of physical memory, and map it into
* virtual memory space. At the end, the struct at srp will describe
* the new segment.
*/
void
valloc (srp)
SR * srp;
{
register int npage;
/*
* If we've run out of virtual memory space, panic ().
*
* A more graceful solution is needed, but valloc () does
* not provide a return value.
*/
if (sysmem.vaddre + srp->sr_size > MAX_VADDR) {
panic ("valloc: out of virtual memory space");
}
npage = btocru (srp->sr_size);
srp->sr_base = sysmem.vaddre;
srp->sr_segp->s_size = srp->sr_size;
srp->sr_segp->s_vmem = c_alloc (npage);
srp->sr_segp->s_flags = SFSYST | SFCORE;
doload (srp);
sysmem.vaddre += ctob (npage);
}
/*
* See if the given process may fit in core.
*/
int
testcore (pp)
PROC * pp;
{
return 1;
}
/*
* Calculate segmentation for a
* new program. If there is a stack segment
* present merge it into the data segment and
* relocate the argument list.
* Make sure that the changes are reflected in the SELF->p_segl array
* which sproto sets up.
*/
int
mproto ()
{
return 1;
}
#if __USE_PROTO__
int accdata (__caddr_t base, size_t count)
#else
int
accdata (base, count)
__caddr_t base;
size_t count;
#endif
{
SR * srp;
srp = & SELF->p_segl [SIPDATA];
return base >= srp->sr_base
&& base + count <= srp->sr_base + srp->sr_size;
}
#if __USE_PROTO__
int accstack (__caddr_t base, size_t count)
#else
int
accstack (base, count)
__caddr_t base;
size_t count;
#endif
{
SR * srp;
srp = & SELF->p_segl [SISTACK];
return base >= srp->sr_base - srp->sr_size
&& base + count <= srp->sr_base;
}
#if __USE_PROTO__
int acctext (__caddr_t base, size_t count)
#else
int
acctext (base, count)
__caddr_t base;
size_t count;
#endif
{
SR * srp;
srp = & SELF->p_segl [SISTEXT];
return base >= srp->sr_base
&& base + count <= srp->sr_base + srp->sr_size;
}
/*
* Grow a segment - increase its size to the desired new length in bytes.
* Return 0 on success, -1 on failure.
* Possible failures:
* attempt to grow a segment to smaller than its present size
* not enough pages available in free pool
* can't allocate a new descriptor vector
*/
int
c_grow (sp, new_bytes)
SEG * sp;
int new_bytes;
{
register int i;
register cseg_t * pp;
int new_pages, pno, nsize, old_pages;
SR * srp;
T_PIGGY (0x8000000, printf ("c_grow (sp: %x, new: %x)", sp, new_bytes));
new_pages = btocru (new_bytes);
old_pages = btocru (sp->s_size);
if (new_pages == old_pages)
return 0;
if (new_pages < old_pages) {
printf ("%s:can't contract segment\n", SELF->p_comm);
return -1;
}
if (new_pages - old_pages > allocno ())
return -1;
T_PIGGY (0x8000000, printf ("nc: %x, oc: %x,", new_pages, old_pages));
/*
* Allocate a new descriptor vector if necessary.
* pp is the element corresponding to the virtual address
* "0"(sr_base)
*/
pp = sp->s_vmem;
nsize = areasize (new_pages);
if (nsize != areasize (old_pages) &&
(pp = (cseg_t *) arealloc (new_pages)) == NULL) {
T_PIGGY (0x8000000,
printf ("Can not allocate new descriptor."));
return -1;
}
T_PIGGY (0x8000000, printf ("new pp: %x", pp));
if ((srp = loaded (sp->s_vmem)) != NULL) {
T_PIGGY (0x8000000, printf ("unloading srp: %x, ", srp));
unload (srp);
srp->sr_segp = 0;
}
/*
* Allocate new descriptors.
*/
T_PIGGY (0x8000000, printf ("new desc: ["));
for (i = old_pages; i < new_pages; i ++) {
pno = * -- sysmem.pfree;
pp [i] = ctob (pno) | SEG_RW;
T_PIGGY (0x8000000, printf ("%x, ", pp [i]));
}
T_PIGGY (0x8000000, printf ("]"));
/*
* Copy unchanged descriptors and free old vector if necessary.
*/
if (pp != sp->s_vmem) {
T_PIGGY (0x8000000, printf ("old desc: ["));
for (i = 0; i < old_pages; i ++) {
pp [i] = sp->s_vmem [i];
T_PIGGY (0x8000000, printf ("%x, ", pp [i]));
}
T_PIGGY (0x8000000, printf ("]"));
areafree ((struct __blocklist *) sp->s_vmem, old_pages);
}
sp->s_vmem = pp;
/*
* clear the added pages
*
* MAPIO macro - convert array of page descriptors, offset
* into system global address.
*/
T_PIGGY (0x8000000, printf ("dmaclear (%x, %x, 0)",
ctob (new_pages - old_pages),
MAPIO (sp->s_vmem, ctob (old_pages))
)); /* T_PIGGY () */
dmaclear (ctob (new_pages - old_pages),
MAPIO (sp->s_vmem, ctob (old_pages)));
return 0;
}