📄 pmap.c
字号:
/* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pmap.c 8.4 (Berkeley) 2/5/94 * * from: $Header: pmap.c,v 1.43 93/10/31 05:34:56 torek Exp $ *//* * SPARC physical map management code. * Does not function on multiprocessors (yet). */#include <sys/param.h>#include <sys/systm.h>#include <sys/device.h>#include <sys/proc.h>#include <sys/malloc.h>#include <vm/vm.h>#include <vm/vm_kern.h>#include <vm/vm_prot.h>#include <vm/vm_page.h>#include <machine/autoconf.h>#include <machine/bsd_openprom.h>#include <machine/cpu.h>#include <machine/ctlreg.h>#include <sparc/sparc/asm.h>#include <sparc/sparc/cache.h>#include <sparc/sparc/vaddrs.h>#ifdef DEBUG#define PTE_BITS "\20\40V\37W\36S\35NC\33IO\32U\31M"#endifextern struct promvec *promvec;/* * The SPARCstation offers us the following challenges: * * 1. A virtual address cache. This is, strictly speaking, not * part of the architecture, but the code below assumes one. * This is a write-through cache on the 4c and a write-back cache * on others. * * 2. An MMU that acts like a cache. There is not enough space * in the MMU to map everything all the time. Instead, we need * to load MMU with the `working set' of translations for each * process. * * 3. Segmented virtual and physical spaces. The upper 12 bits of * a virtual address (the virtual segment) index a segment table, * giving a physical segment. The physical segment selects a * `Page Map Entry Group' (PMEG) and the virtual page number---the * next 5 or 6 bits of the virtual address---select the particular * `Page Map Entry' for the page. We call the latter a PTE and * call each Page Map Entry Group a pmeg (for want of a better name). * * Since there are no valid bits in the segment table, the only way * to have an invalid segment is to make one full pmeg of invalid PTEs. * We use the last one (since the ROM does as well). * * 4. Discontiguous physical pages. The Mach VM expects physical pages * to be in one sequential lump. * * 5. The MMU is always on: it is not possible to disable it. This is * mainly a startup hassle. */struct pmap_stats { int ps_unlink_pvfirst; /* # of pv_unlinks on head */ int ps_unlink_pvsearch; /* # of pv_unlink searches */ int ps_changeprots; /* # of calls to changeprot */ int ps_useless_changeprots; /* # of changeprots for wiring */ int ps_enter_firstpv; /* pv heads entered */ int ps_enter_secondpv; /* pv nonheads entered */ int ps_useless_changewire; /* useless wiring changes */ int ps_npg_prot_all; /* # of active pages protected */ int ps_npg_prot_actual; /* # pages actually affected */} pmap_stats;#ifdef DEBUG#define PDB_CREATE 0x0001#define PDB_DESTROY 0x0002#define PDB_REMOVE 0x0004#define PDB_CHANGEPROT 0x0008#define PDB_ENTER 0x0010#define PDB_MMU_ALLOC 0x0100#define PDB_MMU_STEAL 0x0200#define PDB_CTX_ALLOC 0x0400#define PDB_CTX_STEAL 0x0800int pmapdebug = 0x0;#endif#define splpmap() splimp()/* * First and last managed physical addresses. */#if 0vm_offset_t vm_first_phys, vm_last_phys;#define managed(pa) ((pa) >= vm_first_phys && (pa) < vm_last_phys)#elsevm_offset_t vm_first_phys, vm_num_phys;#define managed(pa) ((unsigned)((pa) - vm_first_phys) < vm_num_phys)#endif/* * For each managed physical page, there is a list of all currently * valid virtual mappings of that page. Since there is usually one * (or zero) mapping per page, the table begins with an initial entry, * rather than a pointer; this head entry is empty iff its pv_pmap * field is NULL. * * Note that these are per machine independent page (so there may be * only one for every two hardware pages, e.g.). Since the virtual * address is aligned on a page boundary, the low order bits are free * for storing flags. Only the head of each list has flags. * * THIS SHOULD BE PART OF THE CORE MAP */struct pvlist { struct pvlist *pv_next; /* next pvlist, if any */ struct pmap *pv_pmap; /* pmap of this va */ int pv_va; /* virtual address */ int pv_flags; /* flags (below) */};/* * Flags in pv_flags. Note that PV_MOD must be 1 and PV_REF must be 2 * since they must line up with the bits in the hardware PTEs (see pte.h). */#define PV_MOD 1 /* page modified */#define PV_REF 2 /* page referenced */#define PV_NC 4 /* page cannot be cached *//*efine PV_ALLF 7 ** all of the above */struct pvlist *pv_table; /* array of entries, one per physical page */#define pvhead(pa) (&pv_table[atop((pa) - vm_first_phys)])/* * Each virtual segment within each pmap is either valid or invalid. * It is valid if pm_npte[VA_VSEG(va)] is not 0. This does not mean * it is in the MMU, however; that is true iff pm_segmap[VA_VSEG(va)] * does not point to the invalid PMEG. * * If a virtual segment is valid and loaded, the correct PTEs appear * in the MMU only. If it is valid and unloaded, the correct PTEs appear * in the pm_pte[VA_VSEG(va)] only. However, some effort is made to keep * the software copies consistent enough with the MMU so that libkvm can * do user address translations. In particular, pv_changepte() and * pmap_enu() maintain consistency, while less critical changes are * not maintained. pm_pte[VA_VSEG(va)] always points to space for those * PTEs, unless this is the kernel pmap, in which case pm_pte[x] is not * used (sigh). * * Each PMEG in the MMU is either free or contains PTEs corresponding to * some pmap and virtual segment. If it contains some PTEs, it also contains * reference and modify bits that belong in the pv_table. If we need * to steal a PMEG from some process (if we need one and none are free) * we must copy the ref and mod bits, and update pm_segmap in the other * pmap to show that its virtual segment is no longer in the MMU. * * There are 128 PMEGs in a small Sun-4, of which only a few dozen are * tied down permanently, leaving `about' 100 to be spread among * running processes. These are managed as an LRU cache. Before * calling the VM paging code for a user page fault, the fault handler * calls mmu_load(pmap, va) to try to get a set of PTEs put into the * MMU. mmu_load will check the validity of the segment and tell whether * it did something. * * Since I hate the name PMEG I call this data structure an `mmu entry'. * Each mmuentry is on exactly one of three `usage' lists: free, LRU, * or locked. The LRU list is for user processes; the locked list is * for kernel entries; both are doubly linked queues headed by `mmuhd's. * The free list is a simple list, headed by a free list pointer. */struct mmuhd { struct mmuentry *mh_next; struct mmuentry *mh_prev;};struct mmuentry { struct mmuentry *me_next; /* queue (MUST BE FIRST) or next free */ struct mmuentry *me_prev; /* queue (MUST BE FIRST) */ struct pmap *me_pmap; /* pmap, if in use */ struct mmuentry *me_pmforw; /* pmap pmeg chain */ struct mmuentry **me_pmback; /* pmap pmeg chain */ u_short me_vseg; /* virtual segment number in pmap */ pmeg_t me_pmeg; /* hardware PMEG number */};struct mmuentry *mmuentry; /* allocated in pmap_bootstrap */struct mmuentry *me_freelist; /* free list (not a queue) */struct mmuhd me_lru = { /* LRU (user) entries */ (struct mmuentry *)&me_lru, (struct mmuentry *)&me_lru};struct mmuhd me_locked = { /* locked (kernel) entries */ (struct mmuentry *)&me_locked, (struct mmuentry *)&me_locked};int seginval; /* the invalid segment number *//* * A context is simply a small number that dictates which set of 4096 * segment map entries the MMU uses. The Sun 4c has eight such sets. * These are alloted in an `almost MRU' fashion. * * Each context is either free or attached to a pmap. * * Since the virtual address cache is tagged by context, when we steal * a context we have to flush (that part of) the cache. */union ctxinfo { union ctxinfo *c_nextfree; /* free list (if free) */ struct pmap *c_pmap; /* pmap (if busy) */};union ctxinfo *ctxinfo; /* allocated at in pmap_bootstrap */int ncontext;union ctxinfo *ctx_freelist; /* context free list */int ctx_kick; /* allocation rover when none free */int ctx_kickdir; /* ctx_kick roves both directions *//* XXX need per-cpu vpage[]s (and vmempage, unless we lock in /dev/mem) */caddr_t vpage[2]; /* two reserved MD virtual pages */caddr_t vmempage; /* one reserved MI vpage for /dev/mem */caddr_t vdumppages; /* 32KB worth of reserved dump pages */struct kpmap kernel_pmap_store; /* the kernel's pmap *//* * We need to know real physical memory ranges (for /dev/mem). */#define MA_SIZE 32 /* size of memory descriptor arrays */struct memarr pmemarr[MA_SIZE];/* physical memory regions */int npmemarr; /* number of entries in pmemarr *//* * The following four global variables are set in pmap_bootstrap * for the vm code to find. This is Wrong. */vm_offset_t avail_start; /* first free physical page number */vm_offset_t avail_end; /* last free physical page number */vm_offset_t virtual_avail; /* first free virtual page number */vm_offset_t virtual_end; /* last free virtual page number *//* * pseudo-functions for mnemonic value#ifdef notyet * NB: setsegmap should be stba for 4c, but stha works and makes the * code right for the Sun-4 as well.#endif */#define getcontext() lduba(AC_CONTEXT, ASI_CONTROL)#define setcontext(c) stba(AC_CONTEXT, ASI_CONTROL, c)#ifdef notyet#define getsegmap(va) lduha(va, ASI_SEGMAP)#define setsegmap(va, pmeg) stha(va, ASI_SEGMAP, pmeg)#else#define getsegmap(va) lduba(va, ASI_SEGMAP)#define setsegmap(va, pmeg) stba(va, ASI_SEGMAP, pmeg)#endif#define getpte(va) lda(va, ASI_PTE)#define setpte(va, pte) sta(va, ASI_PTE, pte)/*----------------------------------------------------------------*/#ifdef sun4c/* * Translations from dense (contiguous) pseudo physical addresses * (fed to the VM code, to keep it happy) to sparse (real, hardware) * physical addresses. We call the former `software' page frame * numbers and the latter `hardware' page frame numbers. The * translation is done on a `per bank' basis. * * The HWTOSW and SWTOHW macros handle the actual translation. * They are defined as no-ops on Sun-4s. * * SHOULD DO atop AND ptoa DIRECTLY IN THESE MACROS SINCE ALL CALLERS * ALWAYS NEED THAT ANYWAY ... CAN JUST PRECOOK THE TABLES (TODO) * * Since we cannot use the memory allocated to the ROM monitor, and * this happens to be just under 64K, I have chosen a bank size of * 64K. This is necessary since all banks must be completely full. * I have also chosen a physical memory limit of 128 MB. The 4c is * architecturally limited to 256 MB, but 128 MB is more than will * fit on present hardware. * * XXX FIX THIS: just make all of each bank available and then * take out the pages reserved to the monitor!! */#define MAXMEM (128 * 1024 * 1024) /* no more than 128 MB phys mem */#define NPGBANK 16 /* 2^4 pages per bank (64K / bank) */#define BSHIFT 4 /* log2(NPGBANK) */#define BOFFSET (NPGBANK - 1)#define BTSIZE (MAXMEM / NBPG / NPGBANK)int pmap_dtos[BTSIZE]; /* dense to sparse */int pmap_stod[BTSIZE]; /* sparse to dense */#define HWTOSW(pg) (pmap_stod[(pg) >> BSHIFT] | ((pg) & BOFFSET))#define SWTOHW(pg) (pmap_dtos[(pg) >> BSHIFT] | ((pg) & BOFFSET))/* * Sort a memory array by address. */static voidsortm(mp, n) register struct memarr *mp; register int n;{ register struct memarr *mpj; register int i, j; register u_int addr, len; /* Insertion sort. This is O(n^2), but so what? */ for (i = 1; i < n; i++) { /* save i'th entry */ addr = mp[i].addr; len = mp[i].len; /* find j such that i'th entry goes before j'th */ for (j = 0, mpj = mp; j < i; j++, mpj++) if (addr < mpj->addr) break; /* slide up any additional entries */ ovbcopy(mpj, mpj + 1, (i - j) * sizeof(*mp)); mpj->addr = addr; mpj->len = len; }}#ifdef DEBUGstruct memarr pmap_ama[MA_SIZE];int pmap_nama;#define ama pmap_ama#endif/* * init_translations sets up pmap_dtos[] and pmap_stod[], and * returns the number of usable physical pages. */intinit_translations(){ register struct memarr *mp; register int n, nmem; register u_int vbank = 0, pbank, v, a; register u_int pages = 0, lost = 0;#ifndef DEBUG struct memarr ama[MA_SIZE]; /* available memory array */#endif nmem = makememarr(ama, MA_SIZE, MEMARR_AVAILPHYS); /* * Open Boot supposedly guarantees at least 3 MB free mem at 0; * this is where the kernel has been loaded (we certainly hope the * kernel is <= 3 MB). We need the memory array to be sorted, and * to start at 0, so that `software page 0' and `hardware page 0' * are the same (otherwise the VM reserves the wrong pages for the * kernel). */ sortm(ama, nmem); if (ama[0].addr != 0) { /* cannot panic here; there's no real kernel yet. */ printf("init_translations: no kernel memory?!\n"); callrom(); }#ifdef DEBUG pmap_nama = nmem;#endif for (mp = ama; --nmem >= 0; mp++) { a = mp->addr >> PGSHIFT; v = mp->len >> PGSHIFT; if ((n = a & BOFFSET) != 0) { /* round up to next bank */ n = NPGBANK - n; if (v < n) { /* not a whole bank: skip it */ lost += v; continue; } lost += n; /* lose n pages from front */ a += n; v -= n; } n = v >> BSHIFT; /* calculate number of banks */ pbank = a >> BSHIFT; /* and the bank itself */ if (pbank + n >= BTSIZE) n = BTSIZE - pbank; pages += n; /* off by a factor of 2^BSHIFT */ lost += v - (n << BSHIFT); while (--n >= 0) { pmap_dtos[vbank] = pbank << BSHIFT; pmap_stod[pbank] = vbank << BSHIFT; pbank++; vbank++; } } /* adjust page count */ pages <<= BSHIFT;#ifdef DEBUG printf("note: lost %d pages in translation\n", lost);#endif return (pages);}#else /* sun4c *//* * Pages are physically contiguous, and hardware PFN == software PFN. * * XXX assumes PAGE_SIZE == NBPG (???) */#define HWTOSW(pg) (pg)#define SWTOHW(pg) (pg)#endif /* sun4c *//* update pv_flags given a valid pte */#define MR(pte) (((pte) >> PG_M_SHIFT) & (PV_MOD | PV_REF))/*----------------------------------------------------------------*//* * Agree with the monitor ROM as to how many MMU entries are * to be reserved, and map all of its segments into all contexts. * * Unfortunately, while the Version 0 PROM had a nice linked list of * taken virtual memory, the Version 2 PROM provides instead a convoluted * description of *free* virtual memory. Rather than invert this, we * resort to two magic constants from the PROM vector description file. */intmmu_reservemon(nmmu) register int nmmu;{ register u_int va, eva; register int mmuseg, i; va = OPENPROM_STARTVADDR; eva = OPENPROM_ENDVADDR; while (va < eva) { mmuseg = getsegmap(va); if (mmuseg < nmmu) nmmu = mmuseg; for (i = ncontext; --i > 0;) (*promvec->pv_setctxt)(i, (caddr_t)va, mmuseg); if (mmuseg == seginval) { va += NBPSG; continue; } /* PROM maps its memory user-accessible: fix it. */ for (i = NPTESG; --i >= 0; va += NBPG) setpte(va, getpte(va) | PG_S); } return (nmmu);}/* * TODO: agree with the ROM on physical pages by taking them away * from the page list, rather than having a dinky BTSIZE above. *//*----------------------------------------------------------------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -