⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pmap.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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 + -