From 44d816da94062ff9392998d914164d9eb078b7c4 Mon Sep 17 00:00:00 2001 From: CSRG Date: Thu, 16 Dec 1982 19:17:22 -0800 Subject: [PATCH] BSD 4_1c_2 development Work on file a/sys/sys/vm_pt.c Synthesized-from: CSRG/cd1/4.1c.2 --- a/sys/sys/vm_pt.c | 554 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 a/sys/sys/vm_pt.c diff --git a/a/sys/sys/vm_pt.c b/a/sys/sys/vm_pt.c new file mode 100644 index 0000000000..6e3ab0f733 --- /dev/null +++ b/a/sys/sys/vm_pt.c @@ -0,0 +1,554 @@ +/* %M% %I% %E% */ + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/proc.h" +#include "../h/map.h" +#include "../h/cmap.h" +#include "../h/vm.h" +#include "../h/buf.h" +#include "../h/text.h" +#include "../h/mount.h" +#include "../h/inode.h" +#include "../h/kernel.h" + +#include "../machine/pte.h" + +#ifdef vax +#include "../vax/mtpr.h" +#endif + +/* + * Get page tables for process p. Allocator + * for memory is argument; process must be locked + * from swapping if vmemall is used; if memall is + * used, call will return w/o waiting for memory. + * In any case an error return results if no user + * page table space is available. + */ +vgetpt(p, pmemall) + register struct proc *p; + int (*pmemall)(); +{ + register long a; + register int i; + + if (p->p_szpt == 0) + panic("vgetpt"); + /* + * Allocate space in the kernel map for this process. + * Then allocate page table pages, and initialize the + * process' p0br and addr pointer to be the kernel + * virtual addresses of the base of the page tables and + * the pte for the process pcb (at the base of the u.). + */ + a = rmalloc(kernelmap, (long)p->p_szpt); + if (a == 0) + return (0); + if ((*pmemall)(&Usrptmap[a], p->p_szpt, p, CSYS) == 0) { + rmfree(kernelmap, (long)p->p_szpt, a); + return (0); + } + p->p_p0br = kmxtob(a); + p->p_addr = uaddr(p); + /* + * Now validate the system page table entries for the + * user page table pages, flushing old translations + * for these kernel virtual addresses. Clear the new + * page table pages for clean post-mortems. + */ + vmaccess(&Usrptmap[a], (caddr_t)p->p_p0br, p->p_szpt); + for (i = 0; i < p->p_szpt; i++) + clearseg(Usrptmap[a + i].pg_pfnum); + return (1); +} + +/* + * Initialize text portion of page table. + */ +vinitpt(p) + struct proc *p; +{ + register struct text *xp; + register struct proc *q; + register struct pte *pte; + register int i; + struct pte proto; + + xp = p->p_textp; + if (xp == 0) + return; + pte = tptopte(p, 0); + /* + * If there is another instance of same text in core + * then just copy page tables from other process. + */ + if (q = xp->x_caddr) { + bcopy((caddr_t)tptopte(q, 0), (caddr_t)pte, + (unsigned) (sizeof(struct pte) * xp->x_size)); + goto done; + } + /* + * Initialize text page tables, zfod if we are loading + * the text now; unless the process is demand loaded, + * this will suffice as the text will henceforth either be + * read from a file or demand paged in. + */ + *(int *)&proto = PG_URKR; + if (xp->x_flag & XLOAD) { + proto.pg_fod = 1; + ((struct fpte *)&proto)->pg_fileno = PG_FZERO; + } + for (i = 0; i < xp->x_size; i++) + *pte++ = proto; + if ((xp->x_flag & XPAGI) == 0) + goto done; + /* + * Text is demand loaded. If process is not loaded (i.e. being + * swapped in) then retrieve page tables from swap area. Otherwise + * this is the first time and we must initialize the page tables + * from the blocks in the file system. + */ + if (xp->x_flag & XLOAD) + vinifod((struct fpte *)tptopte(p, 0), PG_FTEXT, xp->x_iptr, + (daddr_t)1, xp->x_size); + else + swap(p, xp->x_ptdaddr, (caddr_t)tptopte(p, 0), + xp->x_size * sizeof (struct pte), B_READ, + B_PAGET, swapdev, 0); +done: + /* + * In the case where we are overlaying ourself with new page + * table entries, old user-space translations should be flushed. + */ + if (p == u.u_procp) + newptes(tptopte(p, 0), tptov(p, 0), xp->x_size); + else + p->p_flag |= SPTECHG; +} + +/* + * Update the page tables of all processes linked + * to a particular text segment, by distributing + * dpte to the the text page at virtual frame v. + * + * Note that invalidation in the translation buffer for + * the current process is the responsibility of the caller. + */ +distpte(xp, tp, dpte) + struct text *xp; + register size_t tp; + register struct pte *dpte; +{ + register struct proc *p; + register struct pte *pte; + register int i; + + for (p = xp->x_caddr; p; p = p->p_xlink) { + pte = tptopte(p, tp); + p->p_flag |= SPTECHG; + if (pte != dpte) + for (i = 0; i < CLSIZE; i++) + pte[i] = dpte[i]; + } +} + +/* + * Release page tables of process p. + */ +vrelpt(p) + register struct proc *p; +{ + register int a; + + if (p->p_szpt == 0) + return; + a = btokmx(p->p_p0br); + (void) vmemfree(&Usrptmap[a], p->p_szpt); + rmfree(kernelmap, (long)p->p_szpt, (long)a); +} + +#define Xu(a) t = up->u_pcb.a; up->u_pcb.a = uq ->u_pcb.a; uq->u_pcb.a = t; +#define Xup(a) tp = up->u_pcb.a; up->u_pcb.a = uq ->u_pcb.a; uq->u_pcb.a = tp; +#define Xp(a) t = p->a; p->a = q->a; q->a = t; +#define Xpp(a) tp = p->a; p->a = q->a; q->a = tp; + +/* + * Pass the page tables of process p to process q. + * Used during vfork(). P and q are not symmetric; + * p is the giver and q the receiver; after calling vpasspt + * p will be ``cleaned out''. Thus before vfork() we call vpasspt + * with the child as q and give it our resources; after vfork() we + * call vpasspt with the child as p to steal our resources back. + * We are cognizant of whether we are p or q because we have to + * be careful to keep our u. area and restore the other u. area from + * umap after we temporarily put our u. area in both p and q's page tables. + */ +vpasspt(p, q, up, uq, umap) + register struct proc *p, *q; + register struct user *up, *uq; + struct pte *umap; +{ + int t; + int s; + struct pte *tp; + register int i; + + s = spl7(); /* conservative, and slightly paranoid */ + Xu(pcb_szpt); Xu(pcb_p0lr); Xu(pcb_p1lr); + Xup(pcb_p0br); Xup(pcb_p1br); + + /* + * The u. area is contained in the process' p1 region. + * Thus we map the current u. area into the process virtual space + * of both sets of page tables we will deal with so that it + * will stay with us as we rearrange memory management. + */ + for (i = 0; i < UPAGES; i++) + if (up == &u) + q->p_addr[i] = p->p_addr[i]; + else + p->p_addr[i] = q->p_addr[i]; +#ifdef vax + mtpr(TBIA, 0); +#endif + /* + * Now have u. double mapped, and have flushed + * any stale translations to new u. area. + * Switch the page tables. + */ + Xpp(p_p0br); Xp(p_szpt); Xpp(p_addr); +#ifdef vax + mtpr(P0BR, u.u_pcb.pcb_p0br); + mtpr(P1BR, u.u_pcb.pcb_p1br); + mtpr(P0LR, u.u_pcb.pcb_p0lr &~ AST_CLR); + mtpr(P1LR, u.u_pcb.pcb_p1lr); +#endif + /* + * Now running on the ``other'' set of page tables. + * Flush translation to insure that we get correct u. + * Resurrect the u. for the other process in the other + * (our old) set of page tables. Thus the other u. has moved + * from its old (our current) set of page tables to our old + * (its current) set of page tables, while we have kept our + * u. by mapping it into the other page table and then keeping + * the other page table. + */ +#ifdef vax + mtpr(TBIA, 0); +#endif + for (i = 0; i < UPAGES; i++) { + int pf; + struct pte *pte; + if (up == &u) { + pf = umap[i].pg_pfnum; + pte = &q->p_addr[i]; + pte->pg_pfnum = pf; + } else { + pf = umap[i].pg_pfnum; + pte = &p->p_addr[i]; + pte->pg_pfnum = pf; + } + } +#ifdef vax + mtpr(TBIA, 0); +#endif + splx(s); +} + +/* + * Compute number of pages to be allocated to the u. area + * and data and stack area page tables, which are stored on the + * disk immediately after the u. area. + */ +/*ARGSUSED*/ +vusize(p, utl) + register struct proc *p; + struct user *utl; +{ + register int tsz = p->p_tsize / NPTEPG; + + /* + * We do not need page table space on the disk for page + * table pages wholly containing text. This is well + * understood in the code in vmswap.c. + */ + return (clrnd(UPAGES + + clrnd(ctopt(p->p_tsize+p->p_dsize+p->p_ssize+UPAGES)) - tsz)); +} + +/* + * Get u area for process p. If a old u area is given, + * then copy the new area from the old, else + * swap in as specified in the proc structure. + * + * Since argument map/newu is potentially shared + * when an old u. is provided we have to be careful not + * to block after beginning to use them in this case. + * (This is not true when called from swapin() with no old u.) + */ +vgetu(p, palloc, map, newu, oldu) + register struct proc *p; + int (*palloc)(); + register struct pte *map; + register struct user *newu; + struct user *oldu; +{ + register int i; + + if ((*palloc)(p->p_addr, clrnd(UPAGES), p, CSYS) == 0) + return (0); + /* + * New u. pages are to be accessible in map/newu as well + * as in process p's virtual memory. + */ + for (i = 0; i < UPAGES; i++) { + map[i] = p->p_addr[i]; + *(int *)(p->p_addr + i) |= PG_URKW | PG_V; + } + setredzone(p->p_addr, (caddr_t)0); + vmaccess(map, (caddr_t)newu, UPAGES); + /* + * New u.'s come from forking or inswap. + */ + if (oldu) { + bcopy((caddr_t)oldu, (caddr_t)newu, UPAGES * NBPG); + newu->u_procp = p; + } else { + swap(p, p->p_swaddr, (caddr_t)0, ctob(UPAGES), + B_READ, B_UAREA, swapdev, 0); + if ( +#ifdef vax + newu->u_pcb.pcb_ssp != -1 || newu->u_pcb.pcb_esp != -1 || +#endif + newu->u_tsize != p->p_tsize || newu->u_dsize != p->p_dsize || + newu->u_ssize != p->p_ssize || newu->u_procp != p) + panic("vgetu"); + } + /* + * Initialize the pcb copies of the p0 and p1 region bases and + * software page table size from the information in the proc structure. + */ + newu->u_pcb.pcb_p0br = p->p_p0br; + newu->u_pcb.pcb_p1br = initp1br(p->p_p0br + p->p_szpt * NPTEPG); + newu->u_pcb.pcb_szpt = p->p_szpt; + return (1); +} + +/* + * Release swap space for a u. area. + */ +vrelswu(p, utl) + struct proc *p; + struct user *utl; +{ + + rmfree(swapmap, (long)ctod(vusize(p, utl)), p->p_swaddr); + /* p->p_swaddr = 0; */ /* leave for post-mortems */ +} + +/* + * Get swap space for a u. area. + */ +vgetswu(p, utl) + struct proc *p; + struct user *utl; +{ + + p->p_swaddr = rmalloc(swapmap, (long)ctod(vusize(p, utl))); + return (p->p_swaddr); +} + +/* + * Release u. area, swapping it out if desired. + * + * Note: we run on the old u. after it is released into swtch(), + * and are safe because nothing can happen at interrupt time. + */ +vrelu(p, swapu) + register struct proc *p; +{ + register int i; + struct pte uu[UPAGES]; + + if (swapu) + swap(p, p->p_swaddr, (caddr_t)0, ctob(UPAGES), + B_WRITE, B_UAREA, swapdev, 0); + for (i = 0; i < UPAGES; i++) + uu[i] = p->p_addr[i]; + (void) vmemfree(uu, clrnd(UPAGES)); +} + +#ifdef unneeded +int ptforceswap; +#endif +/* + * Expand a page table, assigning new kernel virtual + * space and copying the page table entries over both + * in the system map and as necessary in the user page table space. + */ +ptexpand(change) + register int change; +{ + register struct pte *p1, *p2; + register int i; + register int spages, ss = P1PAGES - u.u_pcb.pcb_p1lr; + register int kold = btokmx(u.u_pcb.pcb_p0br); + int knew, tdpages; + int szpt = u.u_pcb.pcb_szpt; + int s; + + if (change <= 0 || change % CLSIZE) + panic("ptexpand"); + /* + * Change is the number of new page table pages needed. + * Kold is the old index in the kernelmap of the page tables. + * Allocate a new kernel map segment of size szpt+change for + * the page tables, and the new page table pages in the + * middle of this new region. + */ +top: +#ifdef unneeded + if (ptforceswap) + goto bad; +#endif + if ((knew=rmalloc(kernelmap, (long)(szpt+change))) == 0) + goto bad; + spages = ss/NPTEPG; + tdpages = szpt - spages; + if (memall(&Usrptmap[knew+tdpages], change, u.u_procp, CSYS) == 0) { + rmfree(kernelmap, (long)(szpt+change), (long)knew); + goto bad; + } + + /* + * Spages pages of u.+stack page tables go over unchanged. + * Tdpages of text+data page table may contain a few stack + * pages which need to go in one of the newly allocated pages; + * this is a rough cut. + */ + kmcopy(knew, kold, tdpages); + kmcopy(knew+tdpages+change, kold+tdpages, spages); + + /* + * Validate and clear the newly allocated page table pages in the + * center of the new region of the kernelmap. + */ + i = knew + tdpages; + p1 = &Usrptmap[i]; + p2 = p1 + change; + while (p1 < p2) { +#ifndef NEW + *(int *)p1 |= PG_V | PG_KW; +#else NEW + mapin(p1, tptov(u.u_procp, i), p1->pg_pfnum, 1, PG_V|PG_KW); +#endif NEW + clearseg(p1->pg_pfnum); + p1++; + i++; + } +#ifdef vax + mtpr(TBIA, 0); +#endif + + /* + * Move the stack or u. pte's which are before the newly + * allocated pages into the last of the newly allocated pages. + * They are taken from the end of the current p1 region, + * and moved to the end of the new p1 region. There are + * ss % NPTEPG such pte's. + */ + p1 = u.u_pcb.pcb_p1br + u.u_pcb.pcb_p1lr; + p2 = kmxtob(knew+szpt+change) - ss; + for (i = ss - NPTEPG*spages; i != 0; i--) + *p2++ = *p1++; + + /* + * Now switch to the new page tables. + */ +#ifdef vax + mtpr(TBIA, 0); /* paranoid */ +#endif + s = spl7(); /* conservative */ + u.u_procp->p_p0br = kmxtob(knew); + setp0br(u.u_procp->p_p0br); + u.u_pcb.pcb_p1br = kmxtob(knew+szpt+change) - P1PAGES; + u.u_pcb.pcb_p1br = initp1br(kmxtob(knew+szpt+change)); + setp1br(u.u_pcb.pcb_p1br); + u.u_pcb.pcb_szpt += change; + u.u_procp->p_szpt += change; + u.u_procp->p_addr = uaddr(u.u_procp); +#ifdef vax + mtpr(TBIA, 0); +#endif + splx(s); + + /* + * Finally, free old kernelmap. + */ + if (szpt) { +#ifdef sun + mapout(&Usrptmap[kold], szpt); +#endif + rmfree(kernelmap, (long)szpt, (long)kold); + } + return; + +bad: + /* + * Swap out the process so that the unavailable + * resource will be allocated upon swapin. + * + * When resume is executed for the process, + * here is where it will resume. + */ + resume(pcbb(u.u_procp)); + if (setjmp(&u.u_ssave)) + return; + if (swapout(u.u_procp, + (size_t)((u.u_pcb.pcb_p0lr &~ AST_CLR) - u.u_tsize), + ss - HIGHPAGES) == 0) { + /* + * No space to swap... it is inconvenient to try + * to exit, so just wait a bit and hope something + * turns up. Could deadlock here. + * + * SOMEDAY REFLECT ERROR BACK THROUGH expand TO CALLERS + * (grow, sbreak) SO CAN'T DEADLOCK HERE. + */ + sleep((caddr_t)&lbolt, PRIBIO); + goto top; + } + /* + * Set SSWAP bit, so that when process is swapped back in + * swapin will set u.u_pcb.pcb_sswap to u_sswap and force a + * return from the setjmp() above. + */ + u.u_procp->p_flag |= SSWAP; + swtch(); + /* no return */ +} + +kmcopy(to, from, count) + register int to; + int from; + register int count; +{ + register struct pte *tp = &Usrptmap[to]; + register struct pte *fp = &Usrptmap[from]; + + while (count != 0) { +#ifndef NEW + *tp++ = *fp++; +#else NEW + mapin(tp, tptov(u.u_procp, to), + fp->pg_pfnum, 1, *((int *)fp) & (PG_V|PG_PROT)); + tp++; + fp++; +#endif NEW + to++; + count--; + } +} -- 2.20.1