Module Name: src
Committed By: scole
Date: Sat Apr 8 18:08:33 UTC 2017
Modified Files:
src/sys/arch/ia64/ia64: pmap.c
src/sys/arch/ia64/include: pmap.h
Log Message:
Attempted port over from FreeBSD with suggestions from <chs>. Still
more work needed, but at least now the ski simulator and hardware die
at the same place.
To generate a diff of this commit:
cvs rdiff -u -r1.34 -r1.35 src/sys/arch/ia64/ia64/pmap.c
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/ia64/include/pmap.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/ia64/ia64/pmap.c
diff -u src/sys/arch/ia64/ia64/pmap.c:1.34 src/sys/arch/ia64/ia64/pmap.c:1.35
--- src/sys/arch/ia64/ia64/pmap.c:1.34 Fri Dec 23 17:26:43 2016
+++ src/sys/arch/ia64/ia64/pmap.c Sat Apr 8 18:08:33 2017
@@ -1,5 +1,4 @@
-/* $NetBSD: pmap.c,v 1.34 2016/12/23 17:26:43 scole Exp $ */
-
+/* $NetBSD: pmap.c,v 1.35 2017/04/08 18:08:33 scole Exp $ */
/*-
* Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -80,18 +79,18 @@
/* __FBSDID("$FreeBSD: src/sys/ia64/ia64/pmap.c,v 1.172 2005/11/20 06:09:48 alc Exp $"); */
-
-/* XXX: This module is a mess. Need to clean up Locking, list traversal. etc....... */
-
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.34 2016/12/23 17:26:43 scole Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.35 2017/04/08 18:08:33 scole Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/lock.h>
+#include <sys/pool.h>
+#include <sys/sched.h>
+#include <sys/bitops.h>
#include <uvm/uvm.h>
#include <uvm/uvm_physseg.h>
@@ -99,47 +98,158 @@ __KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.3
#include <machine/pal.h>
#include <machine/atomic.h>
#include <machine/pte.h>
-#include <sys/sched.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
+#include <machine/vmparam.h>
+
+/*
+ * Manages physical address maps.
+ *
+ * Since the information managed by this module is
+ * also stored by the logical address mapping module,
+ * this module may throw away valid virtual-to-physical
+ * mappings at almost any time. However, invalidations
+ * of virtual-to-physical mappings must be done as
+ * requested.
+ *
+ * In order to cope with hardware architectures which
+ * make virtual-to-physical map invalidates expensive,
+ * this module may delay invalidate or reduced protection
+ * operations until such time as they are actually
+ * necessary. This module is given full information as
+ * to which processors are currently using which maps,
+ * and to when physical maps must be made correct.
+ */
+
+/*
+ * Following the Linux model, region IDs are allocated in groups of
+ * eight so that a single region ID can be used for as many RRs as we
+ * want by encoding the RR number into the low bits of the ID.
+ *
+ * We reserve region ID 0 for the kernel and allocate the remaining
+ * IDs for user pmaps.
+ *
+ * Region 0-3: User virtually mapped
+ * Region 4: PBVM and special mappings
+ * Region 5: Kernel virtual memory
+ * Region 6: Direct-mapped uncacheable
+ * Region 7: Direct-mapped cacheable
+ */
+
+#if !defined(DIAGNOSTIC)
+#define PMAP_INLINE __inline
+#else
+#define PMAP_INLINE
+#endif
+
+#ifdef PV_STATS
+#define PV_STAT(x) do { x ; } while (0)
+#else
+#define PV_STAT(x) do { } while (0)
+#endif
+
+#define pmap_accessed(lpte) ((lpte)->pte & PTE_ACCESSED)
+#define pmap_dirty(lpte) ((lpte)->pte & PTE_DIRTY)
+#define pmap_exec(lpte) ((lpte)->pte & PTE_AR_RX)
+#define pmap_managed(lpte) ((lpte)->pte & PTE_MANAGED)
+#define pmap_ppn(lpte) ((lpte)->pte & PTE_PPN_MASK)
+#define pmap_present(lpte) ((lpte)->pte & PTE_PRESENT)
+#define pmap_prot(lpte) (((lpte)->pte & PTE_PROT_MASK) >> 56)
+#define pmap_wired(lpte) ((lpte)->pte & PTE_WIRED)
+
+#define pmap_clear_accessed(lpte) (lpte)->pte &= ~PTE_ACCESSED
+#define pmap_clear_dirty(lpte) (lpte)->pte &= ~PTE_DIRTY
+#define pmap_clear_present(lpte) (lpte)->pte &= ~PTE_PRESENT
+#define pmap_clear_wired(lpte) (lpte)->pte &= ~PTE_WIRED
+
+#define pmap_set_wired(lpte) (lpte)->pte |= PTE_WIRED
+
+/*
+ * Individual PV entries are stored in per-pmap chunks. This saves
+ * space by eliminating the need to record the pmap within every PV
+ * entry.
+ */
+#if PAGE_SIZE == 8192
+#define _NPCM 6
+#define _NPCPV 337
+#define _NPCS 2
+#elif PAGE_SIZE == 16384
+#define _NPCM 11
+#define _NPCPV 677
+#define _NPCS 1
+#else
+#error "invalid page size"
+#endif
+
+struct pv_chunk {
+ pmap_t pc_pmap;
+ TAILQ_ENTRY(pv_chunk) pc_list;
+ u_long pc_map[_NPCM]; /* bitmap; 1 = free */
+ TAILQ_ENTRY(pv_chunk) pc_lru;
+ u_long pc_spare[_NPCS];
+ struct pv_entry pc_pventry[_NPCPV];
+};
+
+/*
+ * The VHPT bucket head structure.
+ */
+struct ia64_bucket {
+ uint64_t chain;
+ kmutex_t mutex;
+ u_int length;
+};
+
+/*
+ * Statically allocated kernel pmap
+ */
+static struct pmap kernel_pmap_store;/* the kernel's pmap (proc0) */
+struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
+vaddr_t virtual_avail; /* VA of first avail page (after kernel bss) */
+vaddr_t virtual_end; /* VA of last avail page (end of kernel AS) */
+
+/* XXX freebsd, needs to be sorted out */
+#define kernel_pmap pmap_kernel()
+#define critical_enter() kpreempt_disable()
+#define critical_exit() kpreempt_enable()
+/* flags the entire page as dirty */
+#define vm_page_dirty(page) (page->flags &= ~PG_CLEAN)
+#define vm_page_is_managed(page) (pmap_initialized && uvm_pageismanaged(VM_PAGE_TO_PHYS(page)))
/*
* Kernel virtual memory management.
*/
static int nkpt;
-struct ia64_lpte **ia64_kptdir;
-#define KPTE_DIR_INDEX(va) \
- ((va >> (2*PAGE_SHIFT-5)) & ((1<<(PAGE_SHIFT-3))-1))
+
+extern struct ia64_lpte ***ia64_kptdir;
+
+#define KPTE_DIR0_INDEX(va) \
+ (((va) >> (3*PAGE_SHIFT-8)) & ((1<<(PAGE_SHIFT-3))-1))
+#define KPTE_DIR1_INDEX(va) \
+ (((va) >> (2*PAGE_SHIFT-5)) & ((1<<(PAGE_SHIFT-3))-1))
#define KPTE_PTE_INDEX(va) \
- ((va >> PAGE_SHIFT) & ((1<<(PAGE_SHIFT-5))-1))
+ (((va) >> PAGE_SHIFT) & ((1<<(PAGE_SHIFT-5))-1))
+
#define NKPTEPG (PAGE_SIZE / sizeof(struct ia64_lpte))
+vaddr_t kernel_vm_end;
-/* Values for ptc.e. XXX values for SKI. */
+/* Defaults for ptc.e. */
+/*
+static uint64_t pmap_ptc_e_base = 0;
+static uint32_t pmap_ptc_e_count1 = 1;
+static uint32_t pmap_ptc_e_count2 = 1;
+static uint32_t pmap_ptc_e_stride1 = 0;
+static uint32_t pmap_ptc_e_stride2 = 0;
+*/
+/* Values for ptc.e. XXX values for SKI, add SKI kernel option methinks */
static uint64_t pmap_ptc_e_base = 0x100000000;
static uint64_t pmap_ptc_e_count1 = 3;
static uint64_t pmap_ptc_e_count2 = 2;
static uint64_t pmap_ptc_e_stride1 = 0x2000;
static uint64_t pmap_ptc_e_stride2 = 0x100000000;
-kmutex_t pmap_ptc_lock; /* Global PTC lock */
-
-/* VHPT Base */
-
-vaddr_t vhpt_base;
-vaddr_t pmap_vhpt_log2size;
-
-struct ia64_bucket *pmap_vhpt_bucket;
-int pmap_vhpt_nbuckets;
-kmutex_t pmap_vhptlock; /* VHPT collision chain lock */
-
-int pmap_vhpt_inserts;
-int pmap_vhpt_resident;
-int pmap_vhpt_collisions;
-#ifdef DEBUG
-static void dump_vhpt(void);
-#endif
+kmutex_t pmap_ptc_mutex;
/*
* Data for the RID allocator
@@ -149,1080 +259,1280 @@ static int pmap_rididx;
static int pmap_ridmapsz;
static int pmap_ridmax;
static uint64_t *pmap_ridmap;
-kmutex_t pmap_rid_lock; /* RID allocator lock */
-
-
-bool pmap_initialized; /* Has pmap_init completed? */
-u_long pmap_pages_stolen; /* instrumentation */
+kmutex_t pmap_ridmutex;
-static struct pmap kernel_pmap_store; /* the kernel's pmap (proc0) */
-struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
+static krwlock_t pvh_global_lock __attribute__ ((aligned (128)));
-static vaddr_t kernel_vm_end; /* VA of last avail page ( end of kernel Address Space ) */
+static pool_cache_t pmap_pool_cache;
/*
- * This variable contains the number of CPU IDs we need to allocate
- * space for when allocating the pmap structure. It is used to
- * size a per-CPU array of ASN and ASN Generation number.
+ * Data for the pv entry allocation mechanism
*/
-u_long pmap_ncpuids;
-
-#ifndef PMAP_PV_LOWAT
-#define PMAP_PV_LOWAT 16
-#endif
-int pmap_pv_lowat = PMAP_PV_LOWAT;
+static TAILQ_HEAD(pch, pv_chunk) pv_chunks = TAILQ_HEAD_INITIALIZER(pv_chunks);
+static int pv_entry_count;
/*
- * PV table management functions.
+ * Data for allocating PTEs for user processes.
*/
-void *pmap_pv_page_alloc(struct pool *, int);
-void pmap_pv_page_free(struct pool *, void *);
+static pool_cache_t pte_pool_cache;
-struct pool_allocator pmap_pv_page_allocator = {
- pmap_pv_page_alloc, pmap_pv_page_free, 0,
-};
+struct ia64_bucket *pmap_vhpt_bucket;
-bool pmap_poolpage_alloc(paddr_t *);
-void pmap_poolpage_free(paddr_t);
+/* XXX For freebsd, these are sysctl variables */
+static uint64_t pmap_vhpt_nbuckets = 0;
+uint64_t pmap_vhpt_log2size = 0;
+static uint64_t pmap_vhpt_inserts = 0;
-/*
- * List of all pmaps, used to update them when e.g. additional kernel
- * page tables are allocated. This list is kept LRU-ordered by
- * pmap_activate(). XXX: Check on this.....
- */
-TAILQ_HEAD(, pmap) pmap_all_pmaps;
+static bool pmap_initialized = false; /* Has pmap_init completed? */
+static uint64_t pmap_pages_stolen = 0; /* instrumentation */
-/*
- * The pools from which pmap structures and sub-structures are allocated.
- */
-struct pool pmap_pmap_pool;
-struct pool pmap_ia64_lpte_pool;
-struct pool pmap_pv_pool;
+static struct ia64_lpte *pmap_find_vhpt(vaddr_t va);
-kmutex_t pmap_main_lock;
-kmutex_t pmap_all_pmaps_slock;
+static void free_pv_chunk(struct pv_chunk *pc);
+static void free_pv_entry(pmap_t pmap, pv_entry_t pv);
+static pv_entry_t get_pv_entry(pmap_t pmap, bool try);
+static struct vm_page *pmap_pv_reclaim(pmap_t locked_pmap);
-#if defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
-/* XXX(kochi) need to use only spin lock? */
-#define PMAP_MAP_TO_HEAD_LOCK() \
- spinlockmgr(&pmap_main_lock, LK_SHARED, NULL)
-#define PMAP_MAP_TO_HEAD_UNLOCK() \
- spinlockmgr(&pmap_main_lock, LK_RELEASE, NULL)
-#define PMAP_HEAD_TO_MAP_LOCK() \
- spinlockmgr(&pmap_main_lock, LK_EXCLUSIVE, NULL)
-#define PMAP_HEAD_TO_MAP_UNLOCK() \
- spinlockmgr(&pmap_main_lock, LK_RELEASE, NULL)
-#else
-#define PMAP_MAP_TO_HEAD_LOCK() /* nothing */
-#define PMAP_MAP_TO_HEAD_UNLOCK() /* nothing */
-#define PMAP_HEAD_TO_MAP_LOCK() /* nothing */
-#define PMAP_HEAD_TO_MAP_UNLOCK() /* nothing */
-#endif /* MULTIPROCESSOR || LOCKDEBUG */
-
-
-#define pmap_accessed(lpte) ((lpte)->pte & PTE_ACCESSED)
-#define pmap_dirty(lpte) ((lpte)->pte & PTE_DIRTY)
-#define pmap_managed(lpte) ((lpte)->pte & PTE_MANAGED)
-#define pmap_ppn(lpte) ((lpte)->pte & PTE_PPN_MASK)
-#define pmap_present(lpte) ((lpte)->pte & PTE_PRESENT)
-#define pmap_prot(lpte) (((lpte)->pte & PTE_PROT_MASK) >> 56)
-#define pmap_wired(lpte) ((lpte)->pte & PTE_WIRED)
-
-#define pmap_clear_accessed(lpte) (lpte)->pte &= ~PTE_ACCESSED
-#define pmap_clear_dirty(lpte) (lpte)->pte &= ~PTE_DIRTY
-#define pmap_clear_present(lpte) (lpte)->pte &= ~PTE_PRESENT
-#define pmap_clear_wired(lpte) (lpte)->pte &= ~PTE_WIRED
+static void pmap_free_pte(struct ia64_lpte *pte, vaddr_t va);
+static int pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte,
+ vaddr_t va, pv_entry_t pv, int freepte);
+static int pmap_remove_vhpt(vaddr_t va);
-#define pmap_set_wired(lpte) (lpte)->pte |= PTE_WIRED
+static vaddr_t pmap_steal_vhpt_memory(vsize_t);
+static struct vm_page *vm_page_alloc1(void);
+static void vm_page_free1(struct vm_page *pg);
-/*
- * The VHPT bucket head structure.
- */
-struct ia64_bucket {
- uint64_t chain;
- kmutex_t lock;
- u_int length;
-};
+static vm_memattr_t pmap_flags_to_memattr(u_int flags);
+#if DEBUG
+static void pmap_testout(void);
+#endif
-/* Local Helper functions */
+static void
+pmap_initialize_vhpt(vaddr_t vhpt)
+{
+ struct ia64_lpte *pte;
+ u_int i;
-static void pmap_invalidate_all(pmap_t);
-static void pmap_invalidate_page(pmap_t, vaddr_t);
+ pte = (struct ia64_lpte *)vhpt;
+ for (i = 0; i < pmap_vhpt_nbuckets; i++) {
+ pte[i].pte = 0;
+ pte[i].itir = 0;
+ pte[i].tag = 1UL << 63; /* Invalid tag */
+ pte[i].chain = (uintptr_t)(pmap_vhpt_bucket + i);
+ }
+}
-static pmap_t pmap_switch(pmap_t pm);
-static pmap_t pmap_install(pmap_t);
+#ifdef MULTIPROCESSOR
+vaddr_t
+pmap_alloc_vhpt(void)
+{
+ vaddr_t vhpt;
+ struct vm_page *m;
+ vsize_t size;
-static struct ia64_lpte *pmap_find_kpte(vaddr_t);
+ size = 1UL << pmap_vhpt_log2size;
+ m = vm_page_alloc_contig(NULL, 0, VM_ALLOC_SYSTEM | VM_ALLOC_NOOBJ |
+ VM_ALLOC_WIRED, atop(size), 0UL, ~0UL, size, 0UL,
+ VM_MEMATTR_DEFAULT);
+ if (m != NULL) {
+ vhpt = IA64_PHYS_TO_RR7(VM_PAGE_TO_PHYS(m));
+ pmap_initialize_vhpt(vhpt);
+ return (vhpt);
+ }
+ return (0);
+}
+#endif
-static void
-pmap_set_pte(struct ia64_lpte *, vaddr_t, vaddr_t, bool, bool);
-static void
-pmap_free_pte(struct ia64_lpte *pte, vaddr_t va);
+/*
+ * Bootstrap the system enough to run with virtual memory.
+ */
+void
+pmap_bootstrap(void)
+{
+ struct ia64_pal_result res;
+ vaddr_t base;
+ size_t size;
+ vsize_t bufsz;
+ int i, ridbits;
-static __inline void
-pmap_pte_prot(pmap_t pm, struct ia64_lpte *pte, vm_prot_t prot);
-static int
-pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte, vaddr_t va,
- pv_entry_t pv, int freepte);
+ /*
+ * Query the PAL Code to find the loop parameters for the
+ * ptc.e instruction.
+ */
+ res = ia64_call_pal_static(PAL_PTCE_INFO, 0, 0, 0);
+ if (res.pal_status != 0)
+ panic("Can't configure ptc.e parameters");
+ pmap_ptc_e_base = res.pal_result[0];
+ pmap_ptc_e_count1 = res.pal_result[1] >> 32;
+ pmap_ptc_e_count2 = res.pal_result[1] & ((1L<<32) - 1);
+ pmap_ptc_e_stride1 = res.pal_result[2] >> 32;
+ pmap_ptc_e_stride2 = res.pal_result[2] & ((1L<<32) - 1);
+ if (bootverbose)
+ printf("ptc.e base=0x%lx, count1=%ld, count2=%ld, "
+ "stride1=0x%lx, stride2=0x%lx\n",
+ pmap_ptc_e_base,
+ pmap_ptc_e_count1,
+ pmap_ptc_e_count2,
+ pmap_ptc_e_stride1,
+ pmap_ptc_e_stride2);
-static struct ia64_lpte *
-pmap_find_pte(vaddr_t va);
-static int
-pmap_remove_entry(pmap_t pmap, struct vm_page * pg, vaddr_t va, pv_entry_t pv);
-static void
-pmap_insert_entry(pmap_t pmap, vaddr_t va, struct vm_page *pg);
+ mutex_init(&pmap_ptc_mutex, MUTEX_DEFAULT, IPL_VM);
-static void
-pmap_enter_vhpt(struct ia64_lpte *, vaddr_t);
-static int pmap_remove_vhpt(vaddr_t);
-static struct ia64_lpte *
-pmap_find_vhpt(vaddr_t);
-void
-pmap_page_purge(struct vm_page * pg);
-static void
-pmap_remove_page(pmap_t pmap, vaddr_t va);
+ /*
+ * Setup RIDs. RIDs 0..7 are reserved for the kernel.
+ *
+ * We currently need at least 19 bits in the RID because PID_MAX
+ * can only be encoded in 17 bits and we need RIDs for 4 regions
+ * per process. With PID_MAX equalling 99999 this means that we
+ * need to be able to encode 399996 (=4*PID_MAX).
+ * The Itanium processor only has 18 bits and the architected
+ * minimum is exactly that. So, we cannot use a PID based scheme
+ * in those cases. Enter pmap_ridmap...
+ * We should avoid the map when running on a processor that has
+ * implemented enough bits. This means that we should pass the
+ * process/thread ID to pmap. This we currently don't do, so we
+ * use the map anyway. However, we don't want to allocate a map
+ * that is large enough to cover the range dictated by the number
+ * of bits in the RID, because that may result in a RID map of
+ * 2MB in size for a 24-bit RID. A 64KB map is enough.
+ * The bottomline: we create a 32KB map when the processor only
+ * implements 18 bits (or when we can't figure it out). Otherwise
+ * we create a 64KB map.
+ */
+ res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0);
+ if (res.pal_status != 0) {
+ if (bootverbose)
+ printf("Can't read VM Summary - assuming 18 Region ID bits\n");
+ ridbits = 18; /* guaranteed minimum */
+ } else {
+ ridbits = (res.pal_result[1] >> 8) & 0xff;
+ if (bootverbose)
+ printf("Processor supports %d Region ID bits\n",
+ ridbits);
+ }
+ if (ridbits > 19)
+ ridbits = 19;
+ pmap_ridmax = (1 << ridbits);
+ pmap_ridmapsz = pmap_ridmax / 64;
+ pmap_ridmap = (uint64_t *)uvm_pageboot_alloc(pmap_ridmax / 8);
+ pmap_ridmap[0] |= 0xff;
+ pmap_rididx = 0;
+ pmap_ridcount = 8;
+ mutex_init(&pmap_ridmutex, MUTEX_DEFAULT, IPL_VM);
-static uint32_t pmap_allocate_rid(void);
-static void pmap_free_rid(uint32_t rid);
+ /*
+ * Compute the number of pages kmem_map will have.
+ */
+ kmeminit_nkmempages();
-static vaddr_t
-pmap_steal_vhpt_memory(vsize_t);
+ /*
+ * Figure out how many initial PTE's are necessary to map the
+ * kernel. We also reserve space for kmem_alloc_pageable()
+ * for vm_fork().
+ */
-/*
- * pmap_steal_memory: [ INTERFACE ]
- *
- * Bootstrap memory allocator (alternative to uvm_pageboot_alloc()).
- * This function allows for early dynamic memory allocation until the
- * virtual memory system has been bootstrapped. After that point, either
- * kmem_alloc or malloc should be used. This function works by stealing
- * pages from the (to be) managed page pool, then implicitly mapping the
- * pages (by using their RR7 addresses) and zeroing them.
- *
- * It may be used once the physical memory segments have been pre-loaded
- * into the vm_physmem[] array. Early memory allocation MUST use this
- * interface! This cannot be used after uvm_page_init(), and will
- * generate a panic if tried.
- *
- * Note that this memory will never be freed, and in essence it is wired
- * down.
- *
- * We must adjust *vstartp and/or *vendp iff we use address space
- * from the kernel virtual address range defined by pmap_virtual_space().
- *
- * Note: no locking is necessary in this function.
- */
-vaddr_t
-pmap_steal_memory(vsize_t size, vaddr_t *vstartp, vaddr_t *vendp)
-{
- int npgs;
- uvm_physseg_t upm;
- vaddr_t va;
- paddr_t pa;
+ /* Get size of buffer cache and set an upper limit */
+ bufsz = buf_memcalc();
+ buf_setvalimit(bufsz);
- size = round_page(size);
- npgs = atop(size);
+ nkpt = (((ubc_nwins << ubc_winshift) + uvm_emap_size +
+ bufsz + 16 * NCARGS + pager_map_size) / PAGE_SIZE +
+ USRIOSIZE + (maxproc * UPAGES) + nkmempages) / NKPTEPG;
- for (upm = uvm_physseg_get_first();
- uvm_physseg_valid_p(upm);
- upm = uvm_physseg_get_next(upm)) {
- if (uvm.page_init_done == true)
- panic("pmap_steal_memory: called _after_ bootstrap");
+ /*
+ * Allocate some memory for initial kernel 'page tables'.
+ */
+ ia64_kptdir = (void *)uvm_pageboot_alloc(PAGE_SIZE);
+ memset((void *)ia64_kptdir, 0, PAGE_SIZE);
+ nkpt = 0;
+ kernel_vm_end = VM_INIT_KERNEL_ADDRESS;
+
+ /*
+ * Determine a valid (mappable) VHPT size.
+ */
+ if (pmap_vhpt_log2size == 0)
+ pmap_vhpt_log2size = 20;
+ else if (pmap_vhpt_log2size < 16)
+ pmap_vhpt_log2size = 16;
+ else if (pmap_vhpt_log2size > 28)
+ pmap_vhpt_log2size = 28;
+ if (pmap_vhpt_log2size & 1)
+ pmap_vhpt_log2size--;
- if (uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm) ||
- uvm_physseg_get_avail_start(upm) >= uvm_physseg_get_avail_end(upm))
- continue;
+ size = 1UL << pmap_vhpt_log2size;
+ /* XXX add some retries here */
+ base = pmap_steal_vhpt_memory(size);
+
+ curcpu()->ci_vhpt = base;
- if ((uvm_physseg_get_avail_end(upm) - uvm_physseg_get_avail_start(upm))
- < npgs)
- continue;
+ if (base == 0)
+ panic("Unable to allocate VHPT");
- /*
- * There are enough pages here; steal them!
- */
- pa = ptoa(uvm_physseg_get_start(upm));
- uvm_physseg_unplug(atop(pa), npgs);
+ if (bootverbose)
+ printf("VHPT: address=%#lx, size=%#lx\n", base, size);
- va = IA64_PHYS_TO_RR7(pa);
- memset((void *)va, 0, size);
- pmap_pages_stolen += npgs;
- return va;
+ pmap_vhpt_nbuckets = size / sizeof(struct ia64_lpte);
+ pmap_vhpt_bucket = (void *)uvm_pageboot_alloc(pmap_vhpt_nbuckets *
+ sizeof(struct ia64_bucket));
+ for (i = 0; i < pmap_vhpt_nbuckets; i++) {
+ /* Stolen memory is zeroed. */
+ mutex_init(&pmap_vhpt_bucket[i].mutex, MUTEX_DEFAULT, IPL_VM);
}
+ pmap_initialize_vhpt(base);
+ map_vhpt(base);
+ ia64_set_pta(base + (1 << 8) + (pmap_vhpt_log2size << 2) + 1);
+ ia64_srlz_i();
+
+ /* XXX
+ virtual_avail = VM_INIT_KERNEL_ADDRESS;
+ virtual_end = VM_MAX_KERNEL_ADDRESS;
+ */
+
/*
- * If we got here, this was no memory left.
+ * Initialize the kernel pmap (which is statically allocated).
*/
- panic("pmap_steal_memory: no memory to steal");
+ PMAP_LOCK_INIT(kernel_pmap);
+ for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
+ kernel_pmap->pm_rid[i] = 0;
+ TAILQ_INIT(&kernel_pmap->pm_pvchunk);
+
+ bzero(&kernel_pmap->pm_stats, sizeof kernel_pmap->pm_stats);
+ kernel_pmap->pm_refcount = 1;
+
+ curcpu()->ci_pmap = kernel_pmap;
+
+ /*
+ * Initialize the global pv list lock.
+ */
+ rw_init(&pvh_global_lock);
+
+ /* Region 5 is mapped via the VHPT. */
+ ia64_set_rr(IA64_RR_BASE(5), (5 << 8) | (PAGE_SHIFT << 2) | 1);
+
+ /*
+ * Clear out any random TLB entries left over from booting.
+ */
+ pmap_invalidate_all();
+
+ map_gateway_page();
}
-/*
- * pmap_steal_vhpt_memory: Derived from alpha/pmap.c:pmap_steal_memory()
- * Note: This function is not visible outside the pmap module.
- * Based on pmap_steal_memory();
- * Assumptions: size is always a power of 2.
- * Returns: Allocated memory at a naturally aligned address
- */
-static vaddr_t
-pmap_steal_vhpt_memory(vsize_t size)
+vaddr_t
+pmap_page_to_va(struct vm_page *m)
{
- int npgs;
- uvm_physseg_t upm;
+ paddr_t pa;
vaddr_t va;
- paddr_t pa = 0;
- paddr_t vhpt_start = 0, start1, start2, end1, end2;
- size = round_page(size);
- npgs = atop(size);
-
- for (upm = uvm_physseg_get_first();
- uvm_physseg_valid_p(upm);
- upm = uvm_physseg_get_next(upm)) {
- if (uvm.page_init_done == true)
- panic("pmap_vhpt_steal_memory: called _after_ bootstrap");
-
- if (uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm) || /* XXX: ??? */
- uvm_physseg_get_avail_start(upm) >= uvm_physseg_get_avail_end(upm))
- continue;
+ pa = VM_PAGE_TO_PHYS(m);
+ va = (m->mdpage.memattr == VM_MEMATTR_UNCACHEABLE) ? IA64_PHYS_TO_RR6(pa) :
+ IA64_PHYS_TO_RR7(pa);
+ return (va);
+}
- /* Break off a VHPT sized, aligned chunk off this segment. */
+/***************************************************
+ * Manipulate TLBs for a pmap
+ ***************************************************/
- start1 = uvm_physseg_get_avail_start(upm);
+static void
+pmap_invalidate_page(vaddr_t va)
+{
+ struct ia64_lpte *pte;
+ //struct pcpu *pc;
+ uint64_t tag;
+ u_int vhpt_ofs;
+ struct cpu_info *ci;
+ CPU_INFO_ITERATOR cii;
+
+ critical_enter();
- /* Align requested start address on requested size boundary */
- end1 = vhpt_start = roundup(start1, npgs);
+ vhpt_ofs = ia64_thash(va) - curcpu()->ci_vhpt;
+
+ tag = ia64_ttag(va);
- start2 = vhpt_start + npgs;
- end2 = uvm_physseg_get_avail_end(upm);
+ for (CPU_INFO_FOREACH(cii,ci)) {
+ pte = (struct ia64_lpte *)(ci->ci_vhpt + vhpt_ofs);
+ atomic_cmpset_64(&pte->tag, tag, 1UL << 63);
+ }
+
+ mutex_spin_enter(&pmap_ptc_mutex);
- /* Case 1: Doesn't fit. skip this segment */
+ ia64_ptc_ga(va, PAGE_SHIFT << 2);
+ ia64_mf();
+ ia64_srlz_i();
- if (start2 > end2) {
- vhpt_start = 0;
- continue;
- }
+ mutex_spin_exit(&pmap_ptc_mutex);
+
+ ia64_invala();
- /* For all cases of fit:
- * - Remove segment.
- * - Re-insert fragments via uvm_page_physload();
- */
+ critical_exit();
+}
- /*
- * We _fail_ on a vhpt request which exhausts memory.
- */
- if (start1 == end1 &&
- start2 == end2 &&
- uvm_physseg_get_first() == uvm_physseg_get_last() /* single segment */) {
-#ifdef DEBUG
- printf("pmap_vhpt_steal_memory: out of memory!");
-#endif
- return -1;
- }
+void
+pmap_invalidate_all(void)
+{
+ uint64_t addr;
+ int i, j;
- /* Remove this segment from the list. */
- if (uvm_physseg_unplug(uvm_physseg_get_start(upm),
- uvm_physseg_get_end(upm) - uvm_physseg_get_start(upm)) == false) {
- panic("%s: uvm_physseg_unplug(%"PRIxPADDR", %"PRIxPADDR") failed\n",
- __func__, uvm_physseg_get_start(upm),
- uvm_physseg_get_end(upm) - uvm_physseg_get_start(upm));
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+
+ addr = pmap_ptc_e_base;
+ for (i = 0; i < pmap_ptc_e_count1; i++) {
+ for (j = 0; j < pmap_ptc_e_count2; j++) {
+ ia64_ptc_e(addr);
+ addr += pmap_ptc_e_stride2;
}
-
- /* Case 2: Perfect fit - skip segment reload. */
-
- if (start1 == end1 && start2 == end2) break;
-
- /* Case 3: Left unfit - reload it.
- */
-
- if (start1 != end1)
- uvm_page_physload(start1, end1, start1, end1,
- VM_FREELIST_DEFAULT);
-
- /* Case 4: Right unfit - reload it. */
-
- if (start2 != end2)
- uvm_page_physload(start2, end2, start2, end2,
- VM_FREELIST_DEFAULT);
-
- /* Case 5: Both unfit - Redundant, isn't it ? */
- break;
- }
-
- /*
- * If we got here, we couldn't find a fit.
- */
- if (vhpt_start == 0) {
-#ifdef DEBUG
- printf("pmap_steal_vhpt_memory: no VHPT aligned fit found.");
-#endif
- return -1;
+ addr += pmap_ptc_e_stride1;
}
-
- /*
- * There are enough pages here; steal them!
- */
- pa = ptoa(vhpt_start);
- va = IA64_PHYS_TO_RR7(pa);
- memset((void *)va, 0, size);
- pmap_pages_stolen += npgs;
- return va;
+ ia64_srlz_i();
}
-/*
- * pmap_bootstrap:
- *
- * Bootstrap the system to run with virtual memory.
- *
- * Note: no locking is necessary in this function.
- */
-void
-pmap_bootstrap(void)
+static uint32_t
+pmap_allocate_rid(void)
{
- struct ia64_pal_result res;
- vaddr_t base, limit;
- size_t size;
- vsize_t bufsz;
-
- int i, ridbits;
+ uint64_t bit, bits;
+ int rid;
- /*
- * Query the PAL Code to find the loop parameters for the
- * ptc.e instruction.
- */
- res = ia64_call_pal_static(PAL_PTCE_INFO, 0, 0, 0);
- if (res.pal_status != 0)
- panic("Can't configure ptc.e parameters");
- pmap_ptc_e_base = res.pal_result[0];
- pmap_ptc_e_count1 = res.pal_result[1] >> 32;
- pmap_ptc_e_count2 = res.pal_result[1] & ((1L<<32) - 1);
- pmap_ptc_e_stride1 = res.pal_result[2] >> 32;
- pmap_ptc_e_stride2 = res.pal_result[2] & ((1L<<32) - 1);
- if (bootverbose)
- printf("ptc.e base=0x%lx, count1=%ld, count2=%ld, "
- "stride1=0x%lx, stride2=0x%lx\n",
- pmap_ptc_e_base,
- pmap_ptc_e_count1,
- pmap_ptc_e_count2,
- pmap_ptc_e_stride1,
- pmap_ptc_e_stride2);
- mutex_init(&pmap_ptc_lock, MUTEX_DEFAULT, IPL_VM);
+ mutex_enter(&pmap_ridmutex);
+
+ if (pmap_ridcount == pmap_ridmax)
+ panic("pmap_allocate_rid: All Region IDs used");
- /*
- * Setup RIDs. RIDs 0..7 are reserved for the kernel.
- *
- * We currently need at least 19 bits in the RID because PID_MAX
- * can only be encoded in 17 bits and we need RIDs for 5 regions
- * per process. With PID_MAX equalling 99999 this means that we
- * need to be able to encode 499995 (=5*PID_MAX).
- * The Itanium processor only has 18 bits and the architected
- * minimum is exactly that. So, we cannot use a PID based scheme
- * in those cases. Enter pmap_ridmap...
- * We should avoid the map when running on a processor that has
- * implemented enough bits. This means that we should pass the
- * process/thread ID to pmap. This we currently don't do, so we
- * use the map anyway. However, we don't want to allocate a map
- * that is large enough to cover the range dictated by the number
- * of bits in the RID, because that may result in a RID map of
- * 2MB in size for a 24-bit RID. A 64KB map is enough.
- * The bottomline: we create a 32KB map when the processor only
- * implements 18 bits (or when we can't figure it out). Otherwise
- * we create a 64KB map.
- */
- res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0);
- if (res.pal_status != 0) {
- if (bootverbose)
- printf("Can't read VM Summary - assuming 18 Region ID bits\n");
- ridbits = 18; /* guaranteed minimum */
- } else {
- ridbits = (res.pal_result[1] >> 8) & 0xff;
- if (bootverbose)
- printf("Processor supports %d Region ID bits\n",
- ridbits);
+ /* Find an index with a free bit. */
+ while ((bits = pmap_ridmap[pmap_rididx]) == ~0UL) {
+ pmap_rididx++;
+ if (pmap_rididx == pmap_ridmapsz)
+ pmap_rididx = 0;
}
- if (ridbits > 19)
- ridbits = 19;
+ rid = pmap_rididx * 64;
- pmap_ridmax = (1 << ridbits);
- pmap_ridmapsz = pmap_ridmax / 64;
- pmap_ridmap = (uint64_t *)uvm_pageboot_alloc(pmap_ridmax / 8);
- pmap_ridmap[0] |= 0xff;
- pmap_rididx = 0;
- pmap_ridcount = 8;
+ /* Find a free bit. */
+ bit = 1UL;
+ while (bits & bit) {
+ rid++;
+ bit <<= 1;
+ }
- /* XXX: The FreeBSD pmap.c defines initialises this like this:
- * mtx_init(&pmap_ridmutex, "RID allocator lock", NULL, MTX_DEF);
- * MTX_DEF can *sleep*.
- */
- mutex_init(&pmap_rid_lock, MUTEX_DEFAULT, IPL_VM);
+ pmap_ridmap[pmap_rididx] |= bit;
+ pmap_ridcount++;
- /*
- * Compute the number of pages kmem_map will have.
- */
- kmeminit_nkmempages();
+ mutex_exit(&pmap_ridmutex);
+
+ return rid;
+}
- /*
- * Figure out how many initial PTE's are necessary to map the
- * kernel. We also reserve space for kmem_alloc_pageable()
- * for vm_fork().
- */
+static void
+pmap_free_rid(uint32_t rid)
+{
+ uint64_t bit;
+ int idx;
- /* Get size of buffer cache and set an upper limit */
- bufsz = buf_memcalc();
- buf_setvalimit(bufsz);
+ idx = rid / 64;
+ bit = ~(1UL << (rid & 63));
- nkpt = (((ubc_nwins << ubc_winshift) + uvm_emap_size +
- bufsz + 16 * NCARGS + pager_map_size) / PAGE_SIZE +
- USRIOSIZE + (maxproc * UPAGES) + nkmempages) / NKPTEPG;
+ mutex_enter(&pmap_ridmutex);
+ pmap_ridmap[idx] &= bit;
+ pmap_ridcount--;
- /*
- * Allocate some memory for initial kernel 'page tables'.
- */
- ia64_kptdir = (void *)uvm_pageboot_alloc((nkpt + 1) * PAGE_SIZE);
- for (i = 0; i < nkpt; i++)
- ia64_kptdir[i] =
- (void*)((vaddr_t)ia64_kptdir + PAGE_SIZE * (i + 1));
+ mutex_exit(&pmap_ridmutex);
+}
- kernel_vm_end = nkpt * PAGE_SIZE * NKPTEPG + VM_MIN_KERNEL_ADDRESS -
- VM_GATEWAY_SIZE;
+/***************************************************
+ * Page table page management routines.....
+ ***************************************************/
+CTASSERT(sizeof(struct pv_chunk) == PAGE_SIZE);
- /*
- * Initialize the pmap pools and list.
- */
- pmap_ncpuids = pmap_ridmax;
- pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
- &pool_allocator_nointr, IPL_NONE); /* This may block. */
+static __inline struct pv_chunk *
+pv_to_chunk(pv_entry_t pv)
+{
+ return ((struct pv_chunk *)((uintptr_t)pv & ~(uintptr_t)PAGE_MASK));
+}
- /* XXX: Need to convert ia64_kptdir[][] to a pool. ????*/
+#define PV_PMAP(pv) (pv_to_chunk(pv)->pc_pmap)
- /* The default pool allocator uses uvm_km_alloc & friends.
- * XXX: We should be using regular vm_alloced mem for regular,
- * non-kernel ptesl
- */
+#define PC_FREE_FULL 0xfffffffffffffffful
+#define PC_FREE_PARTIAL \
+ ((1UL << (_NPCPV - sizeof(u_long) * 8 * (_NPCM - 1))) - 1)
- pool_init(&pmap_ia64_lpte_pool, sizeof (struct ia64_lpte),
- sizeof(void *), 0, 0, "ptpl", NULL, IPL_NONE);
+#if PAGE_SIZE == 8192
+static const u_long pc_freemask[_NPCM] = {
+ PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
+ PC_FREE_FULL, PC_FREE_FULL, PC_FREE_PARTIAL
+};
+#elif PAGE_SIZE == 16384
+static const u_long pc_freemask[_NPCM] = {
+ PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
+ PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
+ PC_FREE_FULL, PC_FREE_FULL, PC_FREE_FULL,
+ PC_FREE_FULL, PC_FREE_PARTIAL
+};
+#endif
- pool_init(&pmap_pv_pool, sizeof (struct pv_entry), sizeof(void *),
- 0, 0, "pvpl", &pmap_pv_page_allocator, IPL_NONE);
+#ifdef PV_STATS
+static int pc_chunk_count, pc_chunk_allocs, pc_chunk_frees, pc_chunk_tryfail;
+static long pv_entry_frees, pv_entry_allocs;
+static int pv_entry_spare;
+#endif
- TAILQ_INIT(&pmap_all_pmaps);
+/*
+ * We are in a serious low memory condition. Resort to
+ * drastic measures to free some pages so we can allocate
+ * another pv entry chunk.
+ */
+static struct vm_page
+*pmap_pv_reclaim(pmap_t locked_pmap)
+{
+ struct pch newtail;
+ struct pv_chunk *pc;
+ struct ia64_lpte *pte;
+ pmap_t pmap;
+ pv_entry_t pv;
+ vaddr_t va;
+ struct vm_page *m;
+ struct vm_page *m_pc;
+ u_long inuse;
+ int bit, field, freed, idx;
+
+ PMAP_LOCK_ASSERT(locked_pmap);
+ pmap = NULL;
+ m_pc = NULL;
+ TAILQ_INIT(&newtail);
+ while ((pc = TAILQ_FIRST(&pv_chunks)) != NULL) {
+ TAILQ_REMOVE(&pv_chunks, pc, pc_lru);
+ if (pmap != pc->pc_pmap) {
+ if (pmap != NULL) {
+ if (pmap != locked_pmap) {
+ pmap_switch(locked_pmap);
+ PMAP_UNLOCK(pmap);
+ }
+ }
+ pmap = pc->pc_pmap;
+ /* Avoid deadlock and lock recursion. */
+ if (pmap > locked_pmap)
+ PMAP_LOCK(pmap);
+ else if (pmap != locked_pmap && !PMAP_TRYLOCK(pmap)) {
+ pmap = NULL;
+ TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
+ continue;
+ }
+ pmap_switch(pmap);
+ }
- /*
- * Figure out a useful size for the VHPT, based on the size of
- * physical memory and try to locate a region which is large
- * enough to contain the VHPT (which must be a power of two in
- * size and aligned to a natural boundary).
- * We silently bump up the VHPT size to the minimum size if the
- * user has set the tunable too small. Likewise, the VHPT size
- * is silently capped to the maximum allowed.
- */
+ /*
+ * Destroy every non-wired, 8 KB page mapping in the chunk.
+ */
+ freed = 0;
+ for (field = 0; field < _NPCM; field++) {
+ for (inuse = ~pc->pc_map[field] & pc_freemask[field];
+ inuse != 0; inuse &= ~(1UL << bit)) {
+ bit = ffs64(inuse) - 1;
+ idx = field * sizeof(inuse) * NBBY + bit;
+ pv = &pc->pc_pventry[idx];
+ va = pv->pv_va;
+ pte = pmap_find_vhpt(va);
+ KASSERTMSG(pte != NULL, "pte");
+ if (pmap_wired(pte))
+ continue;
+ pmap_remove_vhpt(va);
+ pmap_invalidate_page(va);
+ m = PHYS_TO_VM_PAGE(pmap_ppn(pte));
+ if (pmap_dirty(pte))
+ vm_page_dirty(m);
+ pmap_free_pte(pte, va);
+ TAILQ_REMOVE(&m->mdpage.pv_list, pv, pv_list);
+ /* XXX
+ if (TAILQ_EMPTY(&m->mdpage.pv_list))
+ //vm_page_aflag_clear(m, PGA_WRITEABLE);
+ m->flags |= PG_RDONLY;
+ */
+ pc->pc_map[field] |= 1UL << bit;
+ freed++;
+ }
+ }
+ if (freed == 0) {
+ TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
+ continue;
+ }
+ /* Every freed mapping is for a 8 KB page. */
+ pmap->pm_stats.resident_count -= freed;
+ PV_STAT(pv_entry_frees += freed);
+ PV_STAT(pv_entry_spare += freed);
+ pv_entry_count -= freed;
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ for (field = 0; field < _NPCM; field++)
+ if (pc->pc_map[field] != pc_freemask[field]) {
+ TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc,
+ pc_list);
+ TAILQ_INSERT_TAIL(&newtail, pc, pc_lru);
+
+ /*
+ * One freed pv entry in locked_pmap is
+ * sufficient.
+ */
+ if (pmap == locked_pmap)
+ goto out;
+ break;
+ }
+ if (field == _NPCM) {
+ PV_STAT(pv_entry_spare -= _NPCPV);
+ PV_STAT(pc_chunk_count--);
+ PV_STAT(pc_chunk_frees++);
+ /* Entire chunk is free; return it. */
+ m_pc = PHYS_TO_VM_PAGE(IA64_RR_MASK((vaddr_t)pc));
+ break;
+ }
+ }
+out:
+ TAILQ_CONCAT(&pv_chunks, &newtail, pc_lru);
+ if (pmap != NULL) {
+ if (pmap != locked_pmap) {
+ pmap_switch(locked_pmap);
+ PMAP_UNLOCK(pmap);
+ }
+ }
+ return (m_pc);
+}
- pmap_vhpt_log2size = PMAP_VHPT_LOG2SIZE;
+/*
+ * free the pv_entry back to the free list
+ */
+static void
+free_pv_entry(pmap_t pmap, pv_entry_t pv)
+{
+ struct pv_chunk *pc;
+ int bit, field, idx;
- if (pmap_vhpt_log2size == 0) {
- pmap_vhpt_log2size = 15;
- size = 1UL << pmap_vhpt_log2size;
- while (size < physmem * 32) {
- pmap_vhpt_log2size++;
- size <<= 1;
+ KASSERT(rw_write_held(&pvh_global_lock));
+ PMAP_LOCK_ASSERT(pmap);
+ PV_STAT(pv_entry_frees++);
+ PV_STAT(pv_entry_spare++);
+ pv_entry_count--;
+ pc = pv_to_chunk(pv);
+ idx = pv - &pc->pc_pventry[0];
+ field = idx / (sizeof(u_long) * NBBY);
+ bit = idx % (sizeof(u_long) * NBBY);
+ pc->pc_map[field] |= 1ul << bit;
+ for (idx = 0; idx < _NPCM; idx++)
+ if (pc->pc_map[idx] != pc_freemask[idx]) {
+ /*
+ * 98% of the time, pc is already at the head of the
+ * list. If it isn't already, move it to the head.
+ */
+ if (__predict_false(TAILQ_FIRST(&pmap->pm_pvchunk) !=
+ pc)) {
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc,
+ pc_list);
+ }
+ return;
}
- } else
- if (pmap_vhpt_log2size < 15)
- pmap_vhpt_log2size = 15;
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ free_pv_chunk(pc);
+}
- if (pmap_vhpt_log2size > 61) pmap_vhpt_log2size = 61;
+static void
+free_pv_chunk(struct pv_chunk *pc)
+{
+ struct vm_page *m;
- vhpt_base = 0;
- base = limit = 0;
- size = 1UL << pmap_vhpt_log2size;
- while (vhpt_base == 0 && size) {
- if (bootverbose)
- printf("Trying VHPT size 0x%lx\n", size);
+ TAILQ_REMOVE(&pv_chunks, pc, pc_lru);
+ PV_STAT(pv_entry_spare -= _NPCPV);
+ PV_STAT(pc_chunk_count--);
+ PV_STAT(pc_chunk_frees++);
+ /* entire chunk is free, return it */
+ m = PHYS_TO_VM_PAGE(IA64_RR_MASK((vaddr_t)pc));
+#if 0
+ /* XXX freebsd these move pages around in queue */
+ vm_page_unwire(m, 0); // releases one wiring and moves page back active/inactive queue
+ vm_page_free(m); // moves page to "free" queue & disassociate with object
+
+ /* XXX might to need locks/other checks here, uvm_unwire, pmap_kremove... */
+ uvm_pagefree(m);
+#endif
+ vm_page_free1(m);
+}
- /* allocate size bytes aligned at size */
- /* #ifdef MULTIPROCESSOR, then (size * MAXCPU) bytes */
- base = pmap_steal_vhpt_memory(size);
-
- if (!base) {
- /* Can't fit, try next smaller size. */
- pmap_vhpt_log2size--;
- size >>= 1;
- } else
- vhpt_base = IA64_PHYS_TO_RR7(base);
- }
- if (pmap_vhpt_log2size < 15)
- panic("Can't find space for VHPT");
+/*
+ * get a new pv_entry, allocating a block from the system
+ * when needed.
+ */
+static pv_entry_t
+get_pv_entry(pmap_t pmap, bool try)
+{
+ struct pv_chunk *pc;
+ pv_entry_t pv;
+ struct vm_page *m;
+ int bit, field, idx;
- if (bootverbose)
- printf("Putting VHPT at 0x%lx\n", base);
+ KASSERT(rw_write_held(&pvh_global_lock));
+ PMAP_LOCK_ASSERT(pmap);
+ PV_STAT(pv_entry_allocs++);
+ pv_entry_count++;
+retry:
+ pc = TAILQ_FIRST(&pmap->pm_pvchunk);
+ if (pc != NULL) {
+ for (field = 0; field < _NPCM; field++) {
+ if (pc->pc_map[field]) {
+ bit = ffs64(pc->pc_map[field]) - 1;
+ break;
+ }
+ }
+ if (field < _NPCM) {
+ idx = field * sizeof(pc->pc_map[field]) * NBBY + bit;
+ pv = &pc->pc_pventry[idx];
+ pc->pc_map[field] &= ~(1ul << bit);
+ /* If this was the last item, move it to tail */
+ for (field = 0; field < _NPCM; field++)
+ if (pc->pc_map[field] != 0) {
+ PV_STAT(pv_entry_spare--);
+ return (pv); /* not full, return */
+ }
+ TAILQ_REMOVE(&pmap->pm_pvchunk, pc, pc_list);
+ TAILQ_INSERT_TAIL(&pmap->pm_pvchunk, pc, pc_list);
+ PV_STAT(pv_entry_spare--);
+ return (pv);
+ }
+ }
- mutex_init(&pmap_vhptlock, MUTEX_DEFAULT, IPL_VM);
+ /* No free items, allocate another chunk */
+ m = vm_page_alloc1();
+
+ if (m == NULL) {
+ if (try) {
+ pv_entry_count--;
+ PV_STAT(pc_chunk_tryfail++);
+ return (NULL);
+ }
+ m = pmap_pv_reclaim(pmap);
+ if (m == NULL)
+ goto retry;
+ }
+
+ PV_STAT(pc_chunk_count++);
+ PV_STAT(pc_chunk_allocs++);
+ pc = (struct pv_chunk *)IA64_PHYS_TO_RR7(VM_PAGE_TO_PHYS(m));
+ pc->pc_pmap = pmap;
+ pc->pc_map[0] = pc_freemask[0] & ~1ul; /* preallocated bit 0 */
+ for (field = 1; field < _NPCM; field++)
+ pc->pc_map[field] = pc_freemask[field];
+ TAILQ_INSERT_TAIL(&pv_chunks, pc, pc_lru);
+ pv = &pc->pc_pventry[0];
+ TAILQ_INSERT_HEAD(&pmap->pm_pvchunk, pc, pc_list);
+ PV_STAT(pv_entry_spare += _NPCPV - 1);
+ return (pv);
+}
- __asm __volatile("mov cr.pta=%0;; srlz.i;;" ::
- "r" (vhpt_base + (1<<8) + (pmap_vhpt_log2size<<2) + 1));
+/*
+ * Conditionally create a pv entry.
+ */
+#if 0
+static bool
+pmap_try_insert_pv_entry(pmap_t pmap, vaddr_t va, struct vm_page *m)
+{
+ pv_entry_t pv;
-#ifdef DEBUG
- dump_vhpt();
+ PMAP_LOCK_ASSERT(pmap);
+ KASSERT(rw_write_held(&pvh_global_lock));
+ if ((pv = get_pv_entry(pmap, true)) != NULL) {
+ pv->pv_va = va;
+ TAILQ_INSERT_TAIL(&m->mdpage.pv_list, pv, pv_list);
+ return (true);
+ } else
+ return (false);
+}
#endif
- /*
- * Initialise vhpt pte entries.
- */
-
- pmap_vhpt_nbuckets = size / sizeof(struct ia64_lpte);
-
- pmap_vhpt_bucket = (void *)uvm_pageboot_alloc(pmap_vhpt_nbuckets *
- sizeof(struct ia64_bucket));
+/*
+ * Add an ia64_lpte to the VHPT.
+ */
+static void
+pmap_enter_vhpt(struct ia64_lpte *pte, vaddr_t va)
+{
+ struct ia64_bucket *bckt;
+ struct ia64_lpte *vhpte;
+ uint64_t pte_pa;
- struct ia64_lpte *pte;
+ /* Can fault, so get it out of the way. */
+ pte_pa = ia64_tpa((vaddr_t)pte);
- pte = (struct ia64_lpte *)vhpt_base;
- for (i = 0; i < pmap_vhpt_nbuckets; i++) {
- pte[i].pte = 0;
- pte[i].itir = 0;
- pte[i].tag = 1UL << 63; /* Invalid tag */
- pte[i].chain = (uintptr_t)(pmap_vhpt_bucket + i);
- /* Stolen memory is zeroed! */
- mutex_init(&pmap_vhpt_bucket[i].lock, MUTEX_DEFAULT, IPL_VM);
- }
+ vhpte = (struct ia64_lpte *)ia64_thash(va);
+ bckt = (struct ia64_bucket *)vhpte->chain;
- /*
- * Initialize the locks.
- */
- mutex_init(&pmap_main_lock, MUTEX_DEFAULT, IPL_VM);
- mutex_init(&pmap_all_pmaps_slock, MUTEX_DEFAULT, IPL_VM);
+ mutex_spin_enter(&bckt->mutex);
+ pte->chain = bckt->chain;
+ ia64_mf();
+ bckt->chain = pte_pa;
- /*
- * Initialize the kernel pmap (which is statically allocated).
- */
- memset(pmap_kernel(), 0, sizeof(struct pmap));
+ pmap_vhpt_inserts++;
+ bckt->length++;
+ mutex_spin_exit(&bckt->mutex);
+}
- mutex_init(&pmap_kernel()->pm_slock, MUTEX_DEFAULT, IPL_VM);
- for (i = 0; i < 5; i++)
- pmap_kernel()->pm_rid[i] = 0;
- pmap_kernel()->pm_active = 1;
- TAILQ_INIT(&pmap_kernel()->pm_pvlist);
+/*
+ * Remove the ia64_lpte matching va from the VHPT. Return zero if it
+ * worked or an appropriate error code otherwise.
+ */
+static int
+pmap_remove_vhpt(vaddr_t va)
+{
+ struct ia64_bucket *bckt;
+ struct ia64_lpte *pte;
+ struct ia64_lpte *lpte;
+ struct ia64_lpte *vhpte;
+ uint64_t chain, tag;
- TAILQ_INSERT_TAIL(&pmap_all_pmaps, pmap_kernel(), pm_list);
+ tag = ia64_ttag(va);
+ vhpte = (struct ia64_lpte *)ia64_thash(va);
+ bckt = (struct ia64_bucket *)vhpte->chain;
- /*
- * Region 5 is mapped via the vhpt.
- */
- ia64_set_rr(IA64_RR_BASE(5), (5 << 8) | (PAGE_SHIFT << 2) | 1);
+ lpte = NULL;
+ mutex_spin_enter(&bckt->mutex);
+ chain = bckt->chain;
+ pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
+ while (chain != 0 && pte->tag != tag) {
+ lpte = pte;
+ chain = pte->chain;
+ pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
+ }
+ if (chain == 0) {
+ mutex_spin_exit(&bckt->mutex);
+ return (ENOENT);
+ }
- /*
- * Region 6 is direct mapped UC and region 7 is direct mapped
- * WC. The details of this is controlled by the Alt {I,D}TLB
- * handlers. Here we just make sure that they have the largest
- * possible page size to minimise TLB usage.
- */
- ia64_set_rr(IA64_RR_BASE(6), (6 << 8) | (IA64_ID_PAGE_SHIFT << 2));
- ia64_set_rr(IA64_RR_BASE(7), (7 << 8) | (IA64_ID_PAGE_SHIFT << 2));
- ia64_srlz_d();
+ /* Snip this pv_entry out of the collision chain. */
+ if (lpte == NULL)
+ bckt->chain = pte->chain;
+ else
+ lpte->chain = pte->chain;
+ ia64_mf();
- /*
- * Clear out any random TLB entries left over from booting.
- */
- pmap_invalidate_all(pmap_kernel());
+ bckt->length--;
- map_gateway_page();
+ mutex_spin_exit(&bckt->mutex);
+ return (0);
}
/*
- * pmap_init: [ INTERFACE ]
- *
- * Initialize the pmap module. Called by vm_init(), to initialize any
- * structures that the pmap system needs to map virtual memory.
- *
- * Note: no locking is necessary in this function.
+ * Find the ia64_lpte for the given va, if any.
*/
-void
-pmap_init(void)
+static struct ia64_lpte *
+pmap_find_vhpt(vaddr_t va)
{
+ struct ia64_bucket *bckt;
+ struct ia64_lpte *pte;
+ uint64_t chain, tag;
- /*
- * Set a low water mark on the pv_entry pool, so that we are
- * more likely to have these around even in extreme memory
- * starvation.
- */
- pool_setlowat(&pmap_pv_pool, pmap_pv_lowat);
+ tag = ia64_ttag(va);
+ pte = (struct ia64_lpte *)ia64_thash(va);
+ bckt = (struct ia64_bucket *)pte->chain;
- /*
- * Now it is safe to enable pv entry recording.
- */
- pmap_initialized = true;
+ mutex_spin_enter(&bckt->mutex);
+ chain = bckt->chain;
+ pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
+ while (chain != 0 && pte->tag != tag) {
+ chain = pte->chain;
+ pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
+ }
+ mutex_spin_exit(&bckt->mutex);
+ return ((chain != 0) ? pte : NULL);
}
/*
- * vtophys: virtual address to physical address. For use by
- * machine-dependent code only.
+ * Remove an entry from the list of managed mappings.
*/
-
-paddr_t
-vtophys(vaddr_t va)
+static int
+pmap_remove_entry(pmap_t pmap, struct vm_page *m, vaddr_t va, pv_entry_t pv)
{
- paddr_t pa;
- if (pmap_extract(pmap_kernel(), va, &pa) == true)
- return pa;
- return 0;
+ KASSERT(rw_write_held(&pvh_global_lock));
+ if (!pv) {
+ TAILQ_FOREACH(pv, &m->mdpage.pv_list, pv_list) {
+ if (pmap == PV_PMAP(pv) && va == pv->pv_va)
+ break;
+ }
+ }
+
+ if (pv) {
+ TAILQ_REMOVE(&m->mdpage.pv_list, pv, pv_list);
+ /* XXX
+ if (TAILQ_FIRST(&m->mdpage.pv_list) == NULL)
+ //vm_page_aflag_clear(m, PGA_WRITEABLE);
+ m->flags |= PG_RDONLY;
+ */
+ free_pv_entry(pmap, pv);
+ return 0;
+ } else {
+ return ENOENT;
+ }
}
/*
- * pmap_virtual_space: [ INTERFACE ]
- *
- * Define the initial bounds of the kernel virtual address space.
+ * Create a pv entry for page at pa for
+ * (pmap, va).
*/
-void
-pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
+static void
+pmap_insert_entry(pmap_t pmap, vaddr_t va, struct vm_page *m)
{
+ pv_entry_t pv;
- *vstartp = VM_MIN_KERNEL_ADDRESS;
- *vendp = VM_MAX_KERNEL_ADDRESS;
+ KASSERT(rw_write_held(&pvh_global_lock));
+ pv = get_pv_entry(pmap, false);
+ pv->pv_va = va;
+ TAILQ_INSERT_TAIL(&m->mdpage.pv_list, pv, pv_list);
}
+/***************************************************
+ * Low level mapping routines.....
+ ***************************************************/
+
/*
- * pmap_remove_all: [ INTERFACE ]
- *
- * This function is a hint to the pmap implementation that all
- * entries in pmap will be removed before any more entries are
- * entered.
+ * Find the kernel lpte for mapping the given virtual address, which
+ * must be in the part of region 5 which we can cover with our kernel
+ * 'page tables'.
*/
-void
-pmap_remove_all(pmap_t pmap)
+static struct ia64_lpte *
+pmap_find_kpte(vaddr_t va)
{
- /* Nothing Yet */
+ struct ia64_lpte **dir1;
+ struct ia64_lpte *leaf;
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(va=%p)", va, 0, 0, 0);
+
+ KASSERTMSG((va >> 61) == 5, "kernel mapping 0x%lx not in region 5", va);
+
+ KASSERTMSG(va < kernel_vm_end, "kernel mapping 0x%lx out of range", va);
+
+ dir1 = ia64_kptdir[KPTE_DIR0_INDEX(va)];
+ leaf = dir1[KPTE_DIR1_INDEX(va)];
+
+ UVMHIST_LOG(maphist, "(kpte_dir0=%#lx, kpte_dir1=%#lx, kpte_pte=%#lx)",
+ KPTE_DIR0_INDEX(va), KPTE_DIR1_INDEX(va), KPTE_PTE_INDEX(va), 0);
+ UVMHIST_LOG(maphist, "(dir1=%p, leaf=%p ret=%p)",
+ dir1, leaf, &leaf[KPTE_PTE_INDEX(va)], 0);
+
+ return (&leaf[KPTE_PTE_INDEX(va)]);
}
/*
- * pmap_remove: [ INTERFACE ]
- *
- * Remove the given range of addresses from the specified map.
- *
- * It is assumed that the start and end are properly
- * rounded to the page size.
+ * Find a pte suitable for mapping a user-space address. If one exists
+ * in the VHPT, that one will be returned, otherwise a new pte is
+ * allocated.
*/
-void
-pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
+static struct ia64_lpte *
+pmap_find_pte(vaddr_t va)
{
- pmap_t oldpmap;
- vaddr_t va;
- pv_entry_t pv;
struct ia64_lpte *pte;
- if (pmap->pm_stats.resident_count == 0)
- return;
-
- PMAP_MAP_TO_HEAD_LOCK();
- PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
-
- /*
- * special handling of removing one page. a very
- * common operation and easy to short circuit some
- * code.
- */
- if (sva + PAGE_SIZE == eva) {
- pmap_remove_page(pmap, sva);
- goto out;
- }
-
- if (pmap->pm_stats.resident_count < ((eva - sva) >> PAGE_SHIFT)) {
- TAILQ_FOREACH(pv, &pmap->pm_pvlist, pv_plist) {
- va = pv->pv_va;
- if (va >= sva && va < eva) {
- pte = pmap_find_vhpt(va);
- KASSERT(pte != NULL);
- pmap_remove_pte(pmap, pte, va, pv, 1);
- pmap_invalidate_page(pmap, va);
- }
- }
+ if (va >= VM_MAXUSER_ADDRESS)
+ return pmap_find_kpte(va);
- } else {
- for (va = sva; va < eva; va += PAGE_SIZE) {
- pte = pmap_find_vhpt(va);
- if (pte != NULL) {
- pmap_remove_pte(pmap, pte, va, 0, 1);
- pmap_invalidate_page(pmap, va);
- }
+ pte = pmap_find_vhpt(va);
+ if (pte == NULL) {
+ pte = pool_cache_get(pte_pool_cache, PR_NOWAIT);
+ if (pte != NULL) {
+ memset((void *)pte, 0, sizeof(struct ia64_lpte));
+ pte->tag = 1UL << 63;
}
}
-out:
- pmap_install(oldpmap);
- PMAP_UNLOCK(pmap);
- PMAP_MAP_TO_HEAD_UNLOCK();
-
+ return (pte);
}
/*
- * pmap_zero_page: [ INTERFACE ]
- *
- * Zero the specified (machine independent) page by mapping the page
- * into virtual memory and clear its contents, one machine dependent
- * page at a time.
- *
- * Note: no locking is necessary in this function.
+ * Free a pte which is now unused. This simply returns it to the zone
+ * allocator if it is a user mapping. For kernel mappings, clear the
+ * valid bit to make it clear that the mapping is not currently used.
*/
-void
-pmap_zero_page(paddr_t phys)
+static void
+pmap_free_pte(struct ia64_lpte *pte, vaddr_t va)
{
- vaddr_t va = IA64_PHYS_TO_RR7(phys);
-
- memset((void *) va, 0, PAGE_SIZE);
+ if (va < VM_MAXUSER_ADDRESS)
+ pool_cache_put(pte_pool_cache, pte);
+ else
+ pmap_clear_present(pte);
}
-/*
- * pmap_copy_page: [ INTERFACE ]
- *
- * Copy the specified (machine independent) page by mapping the page
- * into virtual memory and using memcpy to copy the page, one machine
- * dependent page at a time.
- *
- * Note: no locking is necessary in this function.
- */
-void
-pmap_copy_page(paddr_t psrc, paddr_t pdst)
+static PMAP_INLINE void
+pmap_pte_prot(pmap_t pm, struct ia64_lpte *pte, vm_prot_t prot)
{
- vaddr_t vsrc = IA64_PHYS_TO_RR7(psrc);
- vaddr_t vdst = IA64_PHYS_TO_RR7(pdst);
+ static long prot2ar[4] = {
+ PTE_AR_R, /* VM_PROT_NONE */
+ PTE_AR_RW, /* VM_PROT_WRITE */
+ PTE_AR_RX|PTE_ED, /* VM_PROT_EXECUTE */
+ PTE_AR_RWX|PTE_ED /* VM_PROT_WRITE|VM_PROT_EXECUTE */
+ };
+
+ pte->pte &= ~(PTE_PROT_MASK | PTE_PL_MASK | PTE_AR_MASK | PTE_ED);
+ pte->pte |= (uint64_t)(prot & VM_PROT_ALL) << 56;
+ pte->pte |= (prot == VM_PROT_NONE || pm == kernel_pmap)
+ ? PTE_PL_KERN : PTE_PL_USER;
+ pte->pte |= prot2ar[(prot & VM_PROT_ALL) >> 1];
+}
- memcpy((void *) vdst, (void *) vsrc, PAGE_SIZE);
+static PMAP_INLINE void
+pmap_pte_attr(struct ia64_lpte *pte, vm_memattr_t ma)
+{
+ pte->pte &= ~PTE_MA_MASK;
+ pte->pte |= (ma & PTE_MA_MASK);
}
/*
- * pmap_unwire: [ INTERFACE ]
- *
- * Clear the wired attribute for a map/virtual-address pair.
- *
- * The mapping must already exist in the pmap.
+ * Set a pte to contain a valid mapping and enter it in the VHPT. If
+ * the pte was orginally valid, then its assumed to already be in the
+ * VHPT.
+ * This functions does not set the protection bits. It's expected
+ * that those have been set correctly prior to calling this function.
*/
-void
-pmap_unwire(pmap_t pmap, vaddr_t va)
+static void
+pmap_set_pte(struct ia64_lpte *pte, vaddr_t va, vaddr_t pa,
+ bool wired, bool managed)
{
- pmap_t oldpmap;
- struct ia64_lpte *pte;
+ pte->pte &= PTE_PROT_MASK | PTE_MA_MASK | PTE_PL_MASK |
+ PTE_AR_MASK | PTE_ED;
+ pte->pte |= PTE_PRESENT;
+ pte->pte |= (managed) ? PTE_MANAGED : (PTE_DIRTY | PTE_ACCESSED);
+ pte->pte |= (wired) ? PTE_WIRED : 0;
+ pte->pte |= pa & PTE_PPN_MASK;
- if (pmap == NULL)
- return;
+ pte->itir = PAGE_SHIFT << 2;
- PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
+ ia64_mf();
- pte = pmap_find_vhpt(va);
+ pte->tag = ia64_ttag(va);
+}
- KASSERT(pte != NULL);
+/*
+ * Remove the (possibly managed) mapping represented by pte from the
+ * given pmap.
+ */
+static int
+pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte, vaddr_t va,
+ pv_entry_t pv, int freepte)
+{
+ int error;
+ struct vm_page *m;
/*
- * If wiring actually changed (always?) clear the wire bit and
- * update the wire count. Note that wiring is not a hardware
- * characteristic so there is no need to invalidate the TLB.
+ * First remove from the VHPT.
*/
+ error = pmap_remove_vhpt(va);
+ KASSERTMSG(error == 0, "%s: pmap_remove_vhpt returned %d",__func__, error);
+
+ pmap_invalidate_page(va);
- if (pmap_wired(pte)) {
- pmap->pm_stats.wired_count--;
- pmap_clear_wired(pte);
- }
-#ifdef DIAGNOSTIC
- else {
- printf("pmap_unwire: wiring for pmap %p va 0x%lx "
- "didn't change!\n", pmap, va);
+ if (pmap_wired(pte))
+ pmap->pm_stats.wired_count -= 1;
+
+ pmap->pm_stats.resident_count -= 1;
+ if (pmap_managed(pte)) {
+ m = PHYS_TO_VM_PAGE(pmap_ppn(pte));
+ if (pmap_dirty(pte))
+ vm_page_dirty(m);
+
+ error = pmap_remove_entry(pmap, m, va, pv);
}
-#endif
- pmap_install(oldpmap);
- PMAP_UNLOCK(pmap);
+ if (freepte)
+ pmap_free_pte(pte, va);
+
+ return (error);
}
/*
- * pmap_kenter_pa: [ INTERFACE ]
+ * pmap_init: [ INTERFACE ]
*
- * Enter a va -> pa mapping into the kernel pmap without any
- * physical->virtual tracking.
+ * Initialize the pmap module. Called by vm_init(), to initialize any
+ * structures that the pmap system needs to map virtual memory.
*
* Note: no locking is necessary in this function.
*/
void
-pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
+pmap_init(void)
{
- struct ia64_lpte *pte;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+
+ pmap_pool_cache = pool_cache_init(sizeof(struct pmap), 0, 0, 0,
+ "pmap_pool_cache", NULL, IPL_VM,
+ NULL, NULL, NULL);
+ if (pmap_pool_cache == NULL)
+ panic("%s cannot allocate pmap pool", __func__);
+
+ pte_pool_cache = pool_cache_init(sizeof(struct ia64_lpte), 0, 0, 0,
+ "pte_pool_cache", NULL, IPL_VM,
+ NULL, NULL, NULL);
+ if (pte_pool_cache == NULL)
+ panic("%s cannot allocate pte pool", __func__);
- pte = pmap_find_kpte(va);
- if (pmap_present(pte))
- pmap_invalidate_page(pmap_kernel(), va);
- else
- pmap_enter_vhpt(pte, va);
- pmap_pte_prot(pmap_kernel(), pte, prot);
- pmap_set_pte(pte, va, pa, false, false);
+
+ pmap_initialized = true;
+
+#if DEBUG
+ if (0) pmap_testout();
+#endif
}
/*
- * pmap_kremove: [ INTERFACE ]
+ * pmap_virtual_space: [ INTERFACE ]
*
- * Remove a mapping entered with pmap_kenter_pa() starting at va,
- * for size bytes (assumed to be page rounded).
+ * Define the initial bounds of the kernel virtual address space.
*/
void
-pmap_kremove(vaddr_t va, vsize_t size)
+pmap_virtual_space(vaddr_t *vstartp, vaddr_t *vendp)
{
- struct ia64_lpte *pte;
-
- while (size > 0) {
- pte = pmap_find_kpte(va);
- if (pmap_present(pte)) {
- pmap_remove_vhpt(va);
- pmap_invalidate_page(pmap_kernel(), va);
- pmap_clear_present(pte);
- }
- va += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
+ *vstartp = VM_MIN_KERNEL_ADDRESS;
+ *vendp = VM_MAX_KERNEL_ADDRESS;
}
/*
- * pmap_create: [ INTERFACE ]
+ * pmap_steal_memory: [ INTERFACE ]
*
- * Create and return a physical map.
+ * Bootstrap memory allocator (alternative to uvm_pageboot_alloc()).
+ * This function allows for early dynamic memory allocation until the
+ * virtual memory system has been bootstrapped. After that point, either
+ * kmem_alloc or malloc should be used. This function works by stealing
+ * pages from the (to be) managed page pool, then implicitly mapping the
+ * pages (by using their RR7 addresses) and zeroing them.
+ *
+ * It may be used once the physical memory segments have been pre-loaded
+ * into the vm_physmem[] array. Early memory allocation MUST use this
+ * interface! This cannot be used after uvm_page_init(), and will
+ * generate a panic if tried.
+ *
+ * Note that this memory will never be freed, and in essence it is wired
+ * down.
+ *
+ * We must adjust *vstartp and/or *vendp iff we use address space
+ * from the kernel virtual address range defined by pmap_virtual_space().
*
* Note: no locking is necessary in this function.
*/
-pmap_t
-pmap_create(void)
+vaddr_t
+pmap_steal_memory(vsize_t size, vaddr_t *vstartp, vaddr_t *vendp)
{
- pmap_t pmap;
- int i;
+ int npgs;
+ uvm_physseg_t upm;
+ vaddr_t va;
+ paddr_t pa;
-#ifdef DEBUG
- printf("pmap_create()\n");
-#endif
+ size = round_page(size);
+ npgs = atop(size);
- pmap = pool_get(&pmap_pmap_pool, PR_WAITOK);
- memset(pmap, 0, sizeof(*pmap));
+ for (upm = uvm_physseg_get_first();
+ uvm_physseg_valid_p(upm);
+ upm = uvm_physseg_get_next(upm)) {
+ if (uvm.page_init_done == true)
+ panic("pmap_steal_memory: called _after_ bootstrap");
- for (i = 0; i < 5; i++)
- pmap->pm_rid[i] = pmap_allocate_rid();
- pmap->pm_active = 0;
- TAILQ_INIT(&pmap->pm_pvlist);
- memset(&pmap->pm_stats, 0, sizeof (pmap->pm_stats) );
-
- mutex_init(&pmap->pm_slock, MUTEX_DEFAULT, IPL_VM);
-
- mutex_enter(&pmap_all_pmaps_slock);
- TAILQ_INSERT_TAIL(&pmap_all_pmaps, pmap, pm_list);
- mutex_exit(&pmap_all_pmaps_slock);
+ if (uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm) ||
+ uvm_physseg_get_avail_start(upm) >= uvm_physseg_get_avail_end(upm))
+ continue;
- return pmap;
-}
+ if ((uvm_physseg_get_avail_end(upm) - uvm_physseg_get_avail_start(upm))
+ < npgs)
+ continue;
-/*
- * pmap_destroy: [ INTERFACE ]
- *
- * Drop the reference count on the specified pmap, releasing
- * all resources if the reference count drops to zero.
- */
-void
-pmap_destroy(pmap_t pmap)
-{
- int i;
+ /*
+ * There are enough pages here; steal them!
+ */
+ pa = ptoa(uvm_physseg_get_start(upm));
+ uvm_physseg_unplug(atop(pa), npgs);
-#ifdef DEBUG
- printf("pmap_destroy(%p)\n", pmap);
-#endif
+ va = IA64_PHYS_TO_RR7(pa);
+ memset((void *)va, 0, size);
+ pmap_pages_stolen += npgs;
+ return va;
+ }
- for (i = 0; i < 5; i++)
- if (pmap->pm_rid[i])
- pmap_free_rid(pmap->pm_rid[i]);
/*
- * Remove it from the global list of all pmaps.
+ * If we got here, this was no memory left.
*/
- mutex_enter(&pmap_all_pmaps_slock);
- TAILQ_REMOVE(&pmap_all_pmaps, pmap, pm_list);
- mutex_exit(&pmap_all_pmaps_slock);
-
- pool_put(&pmap_pmap_pool, pmap);
-
+ panic("pmap_steal_memory: no memory to steal");
}
/*
- * pmap_activate: [ INTERFACE ]
- *
- * Activate the pmap used by the specified process. This includes
- * reloading the MMU context if the current process, and marking
- * the pmap in use by the processor.
- *
- * Note: We may use only spin locks here, since we are called
- * by a critical section in cpu_switch()!
+ * pmap_steal_vhpt_memory: Derived from alpha/pmap.c:pmap_steal_memory()
+ * Note: This function is not visible outside the pmap module.
+ * Based on pmap_steal_memory();
+ * Assumptions: size is always a power of 2.
+ * Returns: Allocated memory at a naturally aligned address
*/
-void
-pmap_activate(struct lwp *l)
+static vaddr_t
+pmap_steal_vhpt_memory(vsize_t size)
{
+ int npgs;
+ uvm_physseg_t upm;
+ vaddr_t va;
+ paddr_t pa = 0;
+ paddr_t vhpt_start = 0, start1, start2, end1, end2;
- pmap_install(vm_map_pmap(&l->l_proc->p_vmspace->vm_map));
-}
+ size = round_page(size);
+ npgs = atop(size);
-/*
- * pmap_deactivate: [ INTERFACE ]
- *
- * Mark that the pmap used by the specified process is no longer
- * in use by the processor.
- *
- */
+ for (upm = uvm_physseg_get_first();
+ uvm_physseg_valid_p(upm);
+ upm = uvm_physseg_get_next(upm)) {
+ if (uvm.page_init_done == true)
+ panic("pmap_vhpt_steal_memory: called _after_ bootstrap");
-void
-pmap_deactivate(struct lwp *l)
-{
-}
+ if (uvm_physseg_get_avail_start(upm) != uvm_physseg_get_start(upm) || /* XXX: ??? */
+ uvm_physseg_get_avail_start(upm) >= uvm_physseg_get_avail_end(upm))
+ continue;
+
+ /* Break off a VHPT sized, aligned chunk off this segment. */
-/*
- * pmap_protect: [ INTERFACE ]
- *
- * Set the physical protection on the specified range of this map
- * as requested.
- */
-/*
- * Set the physical protection on the
- * specified range of this map as requested.
- */
-void
-pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
-{
- pmap_t oldpmap;
- struct ia64_lpte *pte;
+ start1 = uvm_physseg_get_avail_start(upm);
- if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
- pmap_remove(pmap, sva, eva);
- return;
- }
+ /* Align requested start address on requested size boundary */
+ end1 = vhpt_start = roundup(start1, npgs);
+
+ start2 = vhpt_start + npgs;
+ end2 = uvm_physseg_get_avail_end(upm);
+
+ /* Case 1: Doesn't fit. skip this segment */
+
+ if (start2 > end2) {
+ vhpt_start = 0;
+ continue;
+ }
+
+ /* For all cases of fit:
+ * - Remove segment.
+ * - Re-insert fragments via uvm_page_physload();
+ */
+
+ /*
+ * We _fail_ on a vhpt request which exhausts memory.
+ */
+ if (start1 == end1 &&
+ start2 == end2 &&
+ uvm_physseg_get_first() == uvm_physseg_get_last() /* single segment */) {
+#ifdef DEBUG
+ printf("pmap_vhpt_steal_memory: out of memory!");
+#endif
+ return -1;
+ }
+
+ /* Remove this segment from the list. */
+ if (uvm_physseg_unplug(uvm_physseg_get_start(upm),
+ uvm_physseg_get_end(upm) - uvm_physseg_get_start(upm)) == false) {
+ panic("%s: uvm_physseg_unplug(%"PRIxPADDR", %"PRIxPADDR") failed\n",
+ __func__, uvm_physseg_get_start(upm),
+ uvm_physseg_get_end(upm) - uvm_physseg_get_start(upm));
+ }
- if (prot & VM_PROT_WRITE)
- return;
+ /* Case 2: Perfect fit - skip segment reload. */
- if ((sva & PAGE_MASK) || (eva & PAGE_MASK))
- panic("pmap_protect: unaligned addresses");
+ if (start1 == end1 && start2 == end2) break;
- PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
- while (sva < eva) {
- /*
- * If page is invalid, skip this page
+ /* Case 3: Left unfit - reload it.
*/
- pte = pmap_find_vhpt(sva);
- if (pte == NULL) {
- sva += PAGE_SIZE;
- continue;
- }
- if (pmap_prot(pte) != prot) {
- if (pmap_managed(pte)) {
- if (pmap_dirty(pte))
- pmap_clear_dirty(pte);
- if (pmap_accessed(pte)) {
- pmap_clear_accessed(pte);
- }
- }
- pmap_pte_prot(pmap, pte, prot);
- pmap_invalidate_page(pmap, sva);
- }
+ if (start1 != end1)
+ uvm_page_physload(start1, end1, start1, end1,
+ VM_FREELIST_DEFAULT);
- sva += PAGE_SIZE;
- }
- pmap_install(oldpmap);
- PMAP_UNLOCK(pmap);
-}
+ /* Case 4: Right unfit - reload it. */
-/*
- * pmap_extract: [ INTERFACE ]
- *
- * Extract the physical address associated with the given
- * pmap/virtual address pair.
- */
-bool
-pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
-{
- struct ia64_lpte *pte;
- pmap_t oldpmap;
+ if (start2 != end2)
+ uvm_page_physload(start2, end2, start2, end2,
+ VM_FREELIST_DEFAULT);
- mutex_enter(&pmap->pm_slock);
- oldpmap = pmap_install(pmap); /*XXX: isn't this a little inefficient ?*/
- pte = pmap_find_vhpt(va);
- if (pte != NULL && pmap_present(pte)) {
- if (pap != NULL)
- *pap = pmap_ppn(pte);
- } else {
- mutex_exit(&pmap->pm_slock);
- return false;
+ /* Case 5: Both unfit - Redundant, isn't it ? */
+ break;
+ }
+
+ /*
+ * If we got here, we couldn't find a fit.
+ */
+ if (vhpt_start == 0) {
+#ifdef DEBUG
+ printf("pmap_steal_vhpt_memory: no VHPT aligned fit found.");
+#endif
+ return -1;
}
- pmap_install(oldpmap);
- mutex_exit(&pmap->pm_slock);
- return true;
+ /*
+ * There are enough pages here; steal them!
+ */
+ pa = ptoa(vhpt_start);
+ va = IA64_PHYS_TO_RR7(pa);
+ memset((void *)va, 0, size);
+ pmap_pages_stolen += npgs;
+ return va;
}
/*
- * pmap_clear_modify: [ INTERFACE ]
+ * pmap_create: [ INTERFACE ]
*
- * Clear the modify bits on the specified physical page.
+ * Create and return a physical map.
+ *
+ * Note: no locking is necessary in this function.
*/
-bool
-pmap_clear_modify(struct vm_page *pg)
+pmap_t
+pmap_create(void)
{
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
- bool rv = false;
- struct ia64_lpte *pte;
- pmap_t oldpmap;
- pv_entry_t pv;
+ pmap_t pmap;
+ int i;
- TAILQ_FOREACH(pv, &md->pv_list, pv_list) {
- PMAP_LOCK(pv->pv_pmap);
- oldpmap = pmap_install(pv->pv_pmap);
- pte = pmap_find_vhpt(pv->pv_va);
- KASSERT(pte != NULL);
- if (pmap_dirty(pte)) {
- rv = true;
- pmap_clear_dirty(pte);
- pmap_invalidate_page(pv->pv_pmap, pv->pv_va);
- }
- pmap_install(oldpmap);
- PMAP_UNLOCK(pv->pv_pmap);
- }
- return rv;
+ pmap = pool_cache_get(pmap_pool_cache, PR_WAITOK);
+
+ if (pmap == NULL)
+ panic("%s no pool", __func__);
+
+ PMAP_LOCK_INIT(pmap);
+
+ for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
+ pmap->pm_rid[i] = pmap_allocate_rid();
+
+ TAILQ_INIT(&pmap->pm_pvchunk);
+
+ pmap->pm_stats.resident_count = 0;
+ pmap->pm_stats.wired_count = 0;
+
+ pmap->pm_refcount = 1;
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p)", pmap, 0, 0, 0);
+
+ return pmap;
}
/*
- * pmap_page_protect: [ INTERFACE ]
+ * pmap_destroy: [ INTERFACE ]
*
- * Lower the permission for all mappings to a given page to
- * the permissions specified.
+ * Drop the reference count on the specified pmap, releasing
+ * all resources if the reference count drops to zero.
*/
void
-pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
+pmap_destroy(pmap_t pmap)
{
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
- struct ia64_lpte *pte;
- pmap_t oldpmap, pmap;
- pv_entry_t pv;
+ int i;
- if ((prot & VM_PROT_WRITE) != 0)
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p)", pmap, 0, 0, 0);
+
+ if (atomic_dec_64_nv(&pmap->pm_refcount) > 0)
return;
- if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) {
- if (pg->flags & PG_RDONLY)
- return;
- TAILQ_FOREACH(pv, &md->pv_list, pv_list) {
- pmap = pv->pv_pmap;
- PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
- pte = pmap_find_vhpt(pv->pv_va);
- KASSERT(pte != NULL);
- pmap_pte_prot(pmap, pte, prot);
- pmap_invalidate_page(pmap, pv->pv_va);
- pmap_install(oldpmap);
- PMAP_UNLOCK(pmap);
- }
- pg->flags |= PG_RDONLY;
- } else
- pmap_page_purge(pg);
+ KASSERT(pmap->pm_stats.resident_count == 0);
+ KASSERT(pmap->pm_stats.wired_count == 0);
+
+ KASSERT(TAILQ_EMPTY(&pmap->pm_pvchunk)); /* XXX hmmm */
+ KASSERT(!PMAP_LOCKED(pmap)); /* XXX hmmm */
+ /*PMAP_LOCK(pmap); */ /* XXX overkill */
+
+ for (i = 0; i < IA64_VM_MINKERN_REGION; i++)
+ if (pmap->pm_rid[i])
+ pmap_free_rid(pmap->pm_rid[i]);
+
+ /*PMAP_UNLOCK(pmap);*/ /* XXX hmm */
+ PMAP_LOCK_DESTROY(pmap);
+
+ pool_cache_put(pmap_pool_cache, pmap);
}
/*
@@ -1233,98 +1543,165 @@ pmap_page_protect(struct vm_page *pg, vm
void
pmap_reference(pmap_t pmap)
{
-
-#ifdef DEBUG
- printf("pmap_reference(%p)\n", pmap);
-#endif
-
- PMAP_LOCK(pmap);
- pmap->pm_count++;
- PMAP_UNLOCK(pmap);
+ atomic_inc_64(&pmap->pm_refcount);
}
/*
- * pmap_clear_reference: [ INTERFACE ]
+ * pmap_resident_count: [ INTERFACE ]
*
- * Clear the reference bit on the specified physical page.
+ * Query the ``resident pages'' statistic for pmap.
*/
-bool
-pmap_clear_reference(struct vm_page *pg)
+long
+pmap_resident_count(pmap_t pmap)
{
-
- return false;
+ return (pmap->pm_stats.resident_count);
}
/*
- * pmap_phys_address: [ INTERFACE ]
+ * pmap_wired_count: [ INTERFACE ]
*
- * Return the physical address corresponding to the specified
- * cookie. Used by the device pager to decode a device driver's
- * mmap entry point return value.
+ * Query the ``wired pages'' statistic for pmap.
*
- * Note: no locking is necessary in this function.
*/
-paddr_t
-pmap_phys_address(paddr_t ppn)
+long
+pmap_wired_count(pmap_t pmap)
{
+ return (pmap->pm_stats.wired_count);
+}
- return ia64_ptob(ppn);
+/*
+ * pmap_growkernel: [ INTERFACE ]
+ *
+ */
+vaddr_t
+pmap_growkernel(vaddr_t maxkvaddr)
+{
+ struct ia64_lpte **dir1;
+ struct ia64_lpte *leaf;
+ struct vm_page *pg;
+#if 0
+ struct vm_page *nkpg;
+ paddr_t pa;
+ vaddr_t va;
+#endif
+
+ /* XXX this function may still need serious fixin' */
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(va=%#lx, nkpt=%ld, kvm_end=%#lx before)", maxkvaddr, nkpt, kernel_vm_end, 0);
+
+ vaddr_t addr = maxkvaddr;
+
+ /* XXX use uvm_pageboot_alloc if not done? */
+ if (!uvm.page_init_done)
+ panic("uvm_page init not done");
+
+ while (kernel_vm_end <= addr) {
+ if (nkpt == PAGE_SIZE/8 + PAGE_SIZE*PAGE_SIZE/64)
+ panic("%s: out of kernel address space", __func__);
+ dir1 = ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)];
+
+ if (dir1 == NULL) {
+#if 0
+ /* FreeBSD does it this way... */
+ nkpg = vm_page_alloc(NULL, nkpt++, VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED);
+ pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
+#endif
+ pg = vm_page_alloc1();
+ if (!pg)
+ panic("%s: cannot add dir1 page", __func__);
+ nkpt++;
+
+#if 0
+ dir1 = (struct ia64_lpte **)pmap_page_to_va(nkpg);
+ bzero(dir1, PAGE_SIZE);
+#endif
+ dir1 = (struct ia64_lpte **)pmap_page_to_va(pg);
+
+ ia64_kptdir[KPTE_DIR0_INDEX(kernel_vm_end)] = dir1;
+ }
+
+#if 0
+ nkpg = vm_page_alloc(NULL, nkpt++, VM_ALLOC_NOOBJ|VM_ALLOC_INTERRUPT|VM_ALLOC_WIRED);
+ pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
+#endif
+ pg = vm_page_alloc1();
+ if (!pg)
+ panic("%s: cannot add PTE page", __func__);
+ nkpt++;
+#if 0
+ leaf = (struct ia64_lpte *)pmap_page_to_va(nkpg);
+ bzero(leaf, PAGE_SIZE);
+#endif
+ leaf = (struct ia64_lpte *)pmap_page_to_va(pg);
+
+ dir1[KPTE_DIR1_INDEX(kernel_vm_end)] = leaf;
+
+ kernel_vm_end += PAGE_SIZE * NKPTEPG;
+ }
+
+ UVMHIST_LOG(maphist, "(va=%#lx, nkpt=%ld, kvm_end=%#lx after)", maxkvaddr, nkpt, kernel_vm_end, 0);
+
+ /* XXX fix */
+ return kernel_vm_end;
}
/*
* pmap_enter: [ INTERFACE ]
*
- * Insert the given physical page (p) at
- * the specified virtual address (v) in the
- * target physical map with the protection requested.
- *
- * If specified, the page will be wired down, meaning
- * that the related pte can not be reclaimed.
- *
- * Note: This is the only routine which MAY NOT lazy-evaluate
- * or lose information. That is, this routine must actually
- * insert this page into the given map NOW.
+ * Create a mapping in physical map pmap for the physical
+ * address pa at the virtual address va with protection speci-
+ * fied by bits in prot.
*/
int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
pmap_t oldpmap;
+ struct vm_page *m;
vaddr_t opa;
struct ia64_lpte origpte;
struct ia64_lpte *pte;
- bool managed, wired;
- struct vm_page *pg;
- int error = 0;
+ bool icache_inval, managed, wired, canfail;
+ /* vm_memattr_t ma; */
- PMAP_MAP_TO_HEAD_LOCK();
+ /* XXX this needs work */
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p, va=%#lx, pa=%p, prot=%#x)", pmap, va, pa, prot);
+
+ /* wired = (flags & PMAP_ENTER_WIRED) != 0; */
+ wired = (flags & PMAP_WIRED) != 0;
+ canfail = (flags & PMAP_CANFAIL) != 0;
+
+ /* ma = pmap_flags_to_memattr(flags); */
+
+ rw_enter(&pvh_global_lock, RW_WRITER);
PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
+ oldpmap = pmap_switch(pmap);
va &= ~PAGE_MASK;
+ KASSERTMSG(va <= VM_MAX_KERNEL_ADDRESS, "%s: toobig", __func__);
- managed = false;
-
- wired = (flags & PMAP_WIRED) !=0;
-
- pg = PHYS_TO_VM_PAGE(pa);
-
-#ifdef DIAGNOSTIC
- if (va > VM_MAX_KERNEL_ADDRESS)
- panic("pmap_enter: toobig");
-#endif
+ /* XXX
+ if ((m->oflags & VPO_UNMANAGED) == 0 && !vm_page_xbusied(m))
+ VM_OBJECT_ASSERT_LOCKED(m->object);
+ */
/*
* Find (or create) a pte for the given mapping.
*/
- while ((pte = pmap_find_pte(va)) == NULL) {
- pmap_install(oldpmap);
+ pte = pmap_find_pte(va);
+
+ if (pte == NULL) {
+ pmap_switch(oldpmap);
PMAP_UNLOCK(pmap);
- PMAP_MAP_TO_HEAD_UNLOCK();
- uvm_kick_pdaemon();
- PMAP_MAP_TO_HEAD_LOCK();
- PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
+ rw_exit(&pvh_global_lock);
+
+ if (canfail)
+ return (ENOMEM);
+ else
+ panic("%s: no pte available", __func__);
}
+
origpte = *pte;
if (!pmap_present(pte)) {
opa = ~0UL;
@@ -1332,6 +1709,18 @@ pmap_enter(pmap_t pmap, vaddr_t va, padd
} else
opa = pmap_ppn(pte);
+ managed = false;
+ /* XXX hmm
+ pa = VM_PAGE_TO_PHYS(m);
+ */
+
+ m = PHYS_TO_VM_PAGE(pa);
+ if (m == NULL) {
+ /* implies page not managed? */
+ panic("%s: new page needed", __func__);
+ }
+ icache_inval = (prot & VM_PROT_EXECUTE) ? true : false;
+
/*
* Mapping has not changed, must be protection or wiring change.
*/
@@ -1349,15 +1738,18 @@ pmap_enter(pmap_t pmap, vaddr_t va, padd
managed = (pmap_managed(&origpte)) ? true : false;
-
/*
* We might be turning off write access to the page,
- * so we go ahead and sense modify status.
+ * so we go ahead and sense modify status. Otherwise,
+ * we can avoid I-cache invalidation if the page
+ * already allowed execution.
*/
if (managed && pmap_dirty(&origpte))
- pg->flags &= ~PG_CLEAN;
+ vm_page_dirty(m);
+ else if (pmap_exec(&origpte))
+ icache_inval = false;
- pmap_invalidate_page(pmap, va);
+ pmap_invalidate_page(va);
goto validate;
}
@@ -1373,9 +1765,12 @@ pmap_enter(pmap_t pmap, vaddr_t va, padd
/*
* Enter on the PV list if part of our managed memory.
*/
-
- if (pg != NULL) {
- pmap_insert_entry(pmap, va, pg);
+ if (vm_page_is_managed(m)) {
+#if 0
+ KASSERTMSG(va < kmi.clean_sva || va >= kmi.clean_eva,
+ ("pmap_enter: managed mapping within the clean submap"));
+#endif
+ pmap_insert_entry(pmap, va, m);
managed = true;
}
@@ -1393,672 +1788,1128 @@ validate:
* adds the pte to the VHPT if necessary.
*/
pmap_pte_prot(pmap, pte, prot);
+ pmap_pte_attr(pte, m->mdpage.memattr);
pmap_set_pte(pte, va, pa, wired, managed);
- PMAP_MAP_TO_HEAD_UNLOCK();
- pmap_install(oldpmap);
+ /* Invalidate the I-cache when needed. */
+ if (icache_inval)
+ ia64_sync_icache(va, PAGE_SIZE);
+
+ /* XXX
+ if ((prot & VM_PROT_WRITE) != 0 && managed)
+ //vm_page_aflag_set(m, PGA_WRITEABLE);
+ m->flags &= ~PG_RDONLY;
+ */
+ rw_exit(&pvh_global_lock);
+ pmap_switch(oldpmap);
PMAP_UNLOCK(pmap);
-
- return error; /* XXX: Look into this. */
+ return (0);
}
/*
- * Routine: pmap_page_purge
- * Function:
- * Removes this physical page from
- * all physical maps in which it resides.
- * Reflects back modify bits to the pager.
+ * pmap_remove: [ INTERFACE ]
*
- * Notes:
- * Original versions of this routine were very
- * inefficient because they iteratively called
- * pmap_remove (slow...)
+ * Remove the given range of addresses from the specified map.
+ *
+ * It is assumed that the start and end are properly
+ * rounded to the page size.
+ *
+ * Sparsely used ranges are inefficiently removed. The VHPT is
+ * probed for every page within the range. XXX
*/
-
void
-pmap_page_purge(struct vm_page *pg)
+pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
pmap_t oldpmap;
- pv_entry_t pv;
+ vaddr_t va;
+ struct ia64_lpte *pte;
- while ((pv = TAILQ_FIRST(&md->pv_list)) != NULL) {
- struct ia64_lpte *pte;
- pmap_t pmap = pv->pv_pmap;
- vaddr_t va = pv->pv_va;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p, sva=%#lx, eva=%#lx)", pmap, sva, eva, 0);
- PMAP_LOCK(pmap);
- oldpmap = pmap_install(pmap);
+ /*
+ * Perform an unsynchronized read. This is, however, safe.
+ */
+ if (pmap->pm_stats.resident_count == 0)
+ return;
+
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ for (va = sva; va < eva; va += PAGE_SIZE) {
pte = pmap_find_vhpt(va);
- KASSERT(pte != NULL);
- if (pmap_ppn(pte) != VM_PAGE_TO_PHYS(pg))
- panic("pmap_remove_all:"
- "pv_table for %lx is inconsistent",
- VM_PAGE_TO_PHYS(pg));
- pmap_remove_pte(pmap, pte, va, pv, 1);
- pmap_install(oldpmap);
- PMAP_UNLOCK(pmap);
+ if (pte != NULL)
+ pmap_remove_pte(pmap, pte, va, 0, 1);
}
- pg->flags |= PG_RDONLY;
-
+ rw_exit(&pvh_global_lock);
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
}
-pmap_t
-pmap_switch(pmap_t pm)
+/*
+ * pmap_remove_all: [ INTERFACE ]
+ *
+ * This function is a hint to the pmap implementation that all
+ * entries in pmap will be removed before any more entries are
+ * entered.
+ */
+void
+pmap_remove_all(pmap_t pmap)
{
- pmap_t prevpm;
- int i;
-
- prevpm = curcpu()->ci_pmap;
- if (prevpm == pm)
- return prevpm;
- if (pm == NULL)
- for (i = 0; i < 5; i++)
- ia64_set_rr(IA64_RR_BASE(i),
- (i << 8)|(PAGE_SHIFT << 2)|1);
- else
- for (i = 0; i < 5; i++)
- ia64_set_rr(IA64_RR_BASE(i),
- (pm->pm_rid[i] << 8)|(PAGE_SHIFT << 2)|1);
- curcpu()->ci_pmap = pm;
- ia64_srlz_d();
- return prevpm;
+ /* XXX do nothing */
}
-static pmap_t
-pmap_install(pmap_t pm)
+/*
+ * pmap_protect: [ INTERFACE ]
+ *
+ * Set the physical protection on the specified range of this map
+ * as requested.
+ */
+void
+pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
- pmap_t prevpm;
- int splsched;
+ pmap_t oldpmap;
+ struct ia64_lpte *pte;
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p, sva=%#lx, eva=%#lx, prot=%#x)",
+ pmap, sva, eva, prot);
+ UVMHIST_LOG(maphist, "(VM_PROT_READ=%u, VM_PROT_WRITE=%u, VM_PROT_EXECUTE=%u)",
+ (prot & VM_PROT_READ) != 0, (prot & VM_PROT_WRITE) != 0,
+ (prot & VM_PROT_EXECUTE) != 0, 0);
+
+ if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
+ pmap_remove(pmap, sva, eva);
+ return;
+ }
+
+ if ((prot & (VM_PROT_WRITE|VM_PROT_EXECUTE)) ==
+ (VM_PROT_WRITE|VM_PROT_EXECUTE))
+ return;
+
+ if ((sva & PAGE_MASK) || (eva & PAGE_MASK))
+ panic("pmap_protect: unaligned addresses");
+ sva = trunc_page(sva);
+ eva = round_page(eva) - 1;
+
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ for ( ; sva < eva; sva += PAGE_SIZE) {
+ /* If page is invalid, skip this page */
+ pte = pmap_find_vhpt(sva);
+ if (pte == NULL)
+ continue;
+
+ /* If there's no change, skip it too */
+ if (pmap_prot(pte) == prot)
+ continue;
+
+ /* wired pages unaffected by prot changes */
+ if (pmap_wired(pte))
+ continue;
+
+ if ((prot & VM_PROT_WRITE) == 0 &&
+ pmap_managed(pte) && pmap_dirty(pte)) {
+ paddr_t pa = pmap_ppn(pte);
+ struct vm_page *m = PHYS_TO_VM_PAGE(pa);
+
+ vm_page_dirty(m);
+ pmap_clear_dirty(pte);
+ }
+
+ if (prot & VM_PROT_EXECUTE)
+ ia64_sync_icache(sva, PAGE_SIZE);
- splsched = splsched();
- prevpm = pmap_switch(pm);
- splx(splsched);
- return prevpm;
+ pmap_pte_prot(pmap, pte, prot);
+ pmap_invalidate_page(sva);
+ }
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
}
-static uint32_t
-pmap_allocate_rid(void)
+/*
+ * pmap_unwire: [ INTERFACE ]
+ *
+ * Clear the wired attribute for a map/virtual-address pair.
+ *
+ * The wired attribute of the page table entry is not a hardware feature,
+ * so there is no need to invalidate any TLB entries.
+ *
+ * The mapping must already exist in the pmap.
+ */
+void
+pmap_unwire(pmap_t pmap, vaddr_t va)
{
- uint64_t bit, bits;
- int rid;
-
- mutex_enter(&pmap_rid_lock);
- if (pmap_ridcount == pmap_ridmax)
- panic("pmap_allocate_rid: All Region IDs used");
+ pmap_t oldpmap;
+ struct ia64_lpte *pte;
- /* Find an index with a free bit. */
- while ((bits = pmap_ridmap[pmap_rididx]) == ~0UL) {
- pmap_rididx++;
- if (pmap_rididx == pmap_ridmapsz)
- pmap_rididx = 0;
- }
- rid = pmap_rididx * 64;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p, va=%#x)", pmap, va, 0, 0);
- /* Find a free bit. */
- bit = 1UL;
- while (bits & bit) {
- rid++;
- bit <<= 1;
- }
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
- pmap_ridmap[pmap_rididx] |= bit;
- pmap_ridcount++;
- mutex_exit(&pmap_rid_lock);
+ pte = pmap_find_vhpt(va);
- return rid;
+ /* XXX panic if no pte or not wired? */
+ if (pte == NULL)
+ panic("pmap_unwire: %lx not found in vhpt", va);
+
+ if (!pmap_wired(pte))
+ panic("pmap_unwire: pte %p isn't wired", pte);
+
+ pmap->pm_stats.wired_count--;
+ pmap_clear_wired(pte);
+
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
}
-static void
-pmap_free_rid(uint32_t rid)
+/*
+ * pmap_extract: [ INTERFACE ]
+ *
+ * Extract the physical address associated with the given
+ * pmap/virtual address pair.
+ */
+bool
+pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
- uint64_t bit;
- int idx;
+ struct ia64_lpte *pte;
+ pmap_t oldpmap;
+ paddr_t pa;
- idx = rid / 64;
- bit = ~(1UL << (rid & 63));
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p, va=%#lx, pap=%#lx)", pmap, va, pap, 0);
- mutex_enter(&pmap_rid_lock);
- pmap_ridmap[idx] &= bit;
- pmap_ridcount--;
- mutex_exit(&pmap_rid_lock);
-}
+ pa = 0;
-/***************************************************
- * Manipulate TLBs for a pmap
- ***************************************************/
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ pte = pmap_find_vhpt(va);
+ if (pte != NULL && pmap_present(pte))
+ pa = pmap_ppn(pte);
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
-static void
-pmap_invalidate_page(pmap_t pmap, vaddr_t va)
-{
+ if (pa && (pap != NULL))
+ *pap = pa;
- KASSERT((pmap == pmap_kernel() || pmap == curcpu()->ci_pmap));
- ia64_ptc_g(va, PAGE_SHIFT << 2);
+ UVMHIST_LOG(maphist, "(pa=%#lx)", pa, 0, 0, 0);
+
+ return (pa != 0);
}
-
-static void
-pmap_invalidate_all_1(void *arg)
+/*
+ * Extract the physical page address associated with a kernel
+ * virtual address.
+ */
+paddr_t
+pmap_kextract(vaddr_t va)
{
- uint64_t addr;
- int i, j;
- register_t psr;
+ struct ia64_lpte *pte;
+ /*uint64_t *pbvm_pgtbl;*/
+ paddr_t pa;
+ /*u_int idx;*/
- psr = intr_disable();
- addr = pmap_ptc_e_base;
- for (i = 0; i < pmap_ptc_e_count1; i++) {
- for (j = 0; j < pmap_ptc_e_count2; j++) {
- ia64_ptc_e(addr);
- addr += pmap_ptc_e_stride2;
- }
- addr += pmap_ptc_e_stride1;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(va=%#lx)", va, 0, 0, 0);
+
+ KASSERTMSG(va >= VM_MAXUSER_ADDRESS, "Must be kernel VA");
+
+ /* Regions 6 and 7 are direct mapped. */
+ if (va >= IA64_RR_BASE(6)) {
+ pa = IA64_RR_MASK(va);
+ goto out;
}
- intr_restore(psr);
-}
-
-static void
-pmap_invalidate_all(pmap_t pmap)
-{
- KASSERT(pmap == pmap_kernel() || pmap == curcpu()->ci_pmap);
+ /* Region 5 is our KVA. Bail out if the VA is beyond our limits. */
+ if (va >= kernel_vm_end)
+ goto err_out;
+ if (va >= VM_INIT_KERNEL_ADDRESS) {
+ pte = pmap_find_kpte(va);
+ pa = pmap_present(pte) ? pmap_ppn(pte) | (va & PAGE_MASK) : 0;
+ goto out;
+ }
+#if 0 /* XXX fix */
+ /* The PBVM page table. */
+ if (va >= IA64_PBVM_PGTBL + bootinfo->bi_pbvm_pgtblsz)
+ goto err_out;
+ if (va >= IA64_PBVM_PGTBL) {
+ pa = (va - IA64_PBVM_PGTBL) + bootinfo->bi_pbvm_pgtbl;
+ goto out;
+ }
-#ifdef MULTIPROCESSOR
- smp_rendezvous(0, pmap_invalidate_all_1, 0, 0);
-#else
- pmap_invalidate_all_1(0);
+ /* The PBVM itself. */
+ if (va >= IA64_PBVM_BASE) {
+ pbvm_pgtbl = (void *)IA64_PBVM_PGTBL;
+ idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT;
+ if (idx >= (bootinfo->bi_pbvm_pgtblsz >> 3))
+ goto err_out;
+ if ((pbvm_pgtbl[idx] & PTE_PRESENT) == 0)
+ goto err_out;
+ pa = (pbvm_pgtbl[idx] & PTE_PPN_MASK) +
+ (va & IA64_PBVM_PAGE_MASK);
+ goto out;
+ }
#endif
-}
-/***************************************************
- * Low level mapping routines.....
- ***************************************************/
+ err_out:
+ KASSERT(1);
+ printf("XXX: %s: va=%#lx is invalid\n", __func__, va);
+ pa = 0;
+ /* FALLTHROUGH */
+
+ out:
+ UVMHIST_LOG(maphist, "(pa=%#lx)", pa, 0, 0, 0);
+ return (pa);
+}
/*
- * Find the kernel lpte for mapping the given virtual address, which
- * must be in the part of region 5 which we can cover with our kernel
- * 'page tables'.
+ * pmap_kenter_pa: [ INTERFACE ]
+ *
+ * Enter a va -> pa mapping into the kernel pmap without any
+ * physical->virtual tracking.
+ *
+ * Note: no locking is necessary in this function.
*/
-static struct ia64_lpte *
-pmap_find_kpte(vaddr_t va)
+void
+pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
+ struct ia64_lpte *pte;
+ vm_memattr_t attr;
+ const bool managed = false; /* don't gather ref/mod info */
+ const bool wired = true; /* pmap_kenter_pa always wired */
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(va=%#lx, pa=%#lx, prot=%p, flags=%p)", va, pa, prot, flags);
+
+ KASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS);
+
+ attr = pmap_flags_to_memattr(flags);
- KASSERT((va >> 61) == 5);
- KASSERT(IA64_RR_MASK(va) < (nkpt * PAGE_SIZE * NKPTEPG));
- return &ia64_kptdir[KPTE_DIR_INDEX(va)][KPTE_PTE_INDEX(va)];
-}
-
+ pte = pmap_find_kpte(va);
+ if (pmap_present(pte))
+ pmap_invalidate_page(va);
+ else
+ pmap_enter_vhpt(pte, va);
-/***************************************************
- * Low level helper routines.....
- ***************************************************/
+ pmap_pte_prot(kernel_pmap, pte, VM_PROT_ALL);
+ pmap_pte_attr(pte, attr);
+ pmap_set_pte(pte, va, pa, wired, managed);
+}
/*
- * Find a pte suitable for mapping a user-space address. If one exists
- * in the VHPT, that one will be returned, otherwise a new pte is
- * allocated.
+ * pmap_kremove: [ INTERFACE ]
+ *
+ * Remove all mappings starting at virtual address va for size
+ * bytes from the kernel physical map. All mappings that are
+ * removed must be the ``unmanaged'' type created with
+ * pmap_kenter_pa(). The implementation may assert this.
*/
-static struct ia64_lpte *
-pmap_find_pte(vaddr_t va)
+void
+pmap_kremove(vaddr_t va, vsize_t size)
{
struct ia64_lpte *pte;
- if (va >= VM_MAXUSER_ADDRESS)
- return pmap_find_kpte(va);
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(va=%#lx)", va, 0, 0, 0);
- pte = pmap_find_vhpt(va);
- if (pte == NULL) {
- pte = pool_get(&pmap_ia64_lpte_pool, PR_NOWAIT);
- pte->tag = 1UL << 63;
+ while (size > 0) {
+ pte = pmap_find_kpte(va);
+ if (pmap_present(pte)) {
+ KASSERT(pmap_managed(pte) != 0);
+ pmap_remove_vhpt(va);
+ pmap_invalidate_page(va);
+ pmap_clear_present(pte);
+ }
+ va += PAGE_SIZE;
+ size -= PAGE_SIZE;
}
-
- return pte;
}
-static __inline void
-pmap_pte_prot(pmap_t pm, struct ia64_lpte *pte, vm_prot_t prot)
+/*
+ * pmap_copy: [ INTERFACE ]
+ *
+ * This function copies the mappings starting at src_addr in
+ * src_map for len bytes into dst_map starting at dst_addr.
+ *
+ * Note that while this function is required to be provided by
+ * a pmap implementation, it is not actually required to do
+ * anything. pmap_copy() is merely advisory (it is used in
+ * the fork(2) path to ``pre-fault'' the child's address
+ * space).
+ */
+void
+pmap_copy(pmap_t dst_map, pmap_t src_map, vaddr_t dst_addr, vsize_t len,
+ vaddr_t src_addr)
{
- static int prot2ar[4] = {
- PTE_AR_R, /* VM_PROT_NONE */
- PTE_AR_RW, /* VM_PROT_WRITE */
- PTE_AR_RX, /* VM_PROT_EXECUTE */
- PTE_AR_RWX /* VM_PROT_WRITE|VM_PROT_EXECUTE */
- };
-
- pte->pte &= ~(PTE_PROT_MASK | PTE_PL_MASK | PTE_AR_MASK);
- pte->pte |= (uint64_t)(prot & VM_PROT_ALL) << 56;
- pte->pte |= (prot == VM_PROT_NONE || pm == pmap_kernel())
- ? PTE_PL_KERN : PTE_PL_USER;
- pte->pte |= prot2ar[(prot & VM_PROT_ALL) >> 1];
+ /* nothing required */
}
-
-
/*
- * Set a pte to contain a valid mapping and enter it in the VHPT. If
- * the pte was orginally valid, then its assumed to already be in the
- * VHPT.
- * This functions does not set the protection bits. It's expected
- * that those have been set correctly prior to calling this function.
+ * pmap_update: [ INTERFACE ]
+ *
+ * If a pmap implementation does not delay virtual-to-physical
+ * mapping updates, pmap_update() has no operation.
*/
-static void
-pmap_set_pte(struct ia64_lpte *pte, vaddr_t va, vaddr_t pa,
- bool wired, bool managed)
+void
+pmap_update(pmap_t pmap)
{
-
- pte->pte &= PTE_PROT_MASK | PTE_PL_MASK | PTE_AR_MASK;
- pte->pte |= PTE_PRESENT | PTE_MA_WB;
- pte->pte |= (managed) ? PTE_MANAGED : (PTE_DIRTY | PTE_ACCESSED);
- pte->pte |= (wired) ? PTE_WIRED : 0;
- pte->pte |= pa & PTE_PPN_MASK;
-
- pte->itir = PAGE_SHIFT << 2;
-
- pte->tag = ia64_ttag(va);
+ /* nothing required */
}
/*
- * Remove the (possibly managed) mapping represented by pte from the
- * given pmap.
+ * pmap_activate: [ INTERFACE ]
+ *
+ * Activate the pmap used by the specified process. This includes
+ * reloading the MMU context if the current process, and marking
+ * the pmap in use by the processor.
+ *
+ * Note: We may use only spin locks here, since we are called
+ * by a critical section in cpu_switch()!
*/
-static int
-pmap_remove_pte(pmap_t pmap, struct ia64_lpte *pte, vaddr_t va,
- pv_entry_t pv, int freepte)
+void
+pmap_activate(struct lwp *l)
{
- int error;
- struct vm_page *pg;
-
- KASSERT(pmap == pmap_kernel() || pmap == curcpu()->ci_pmap);
-
- /*
- * First remove from the VHPT.
- */
- error = pmap_remove_vhpt(va);
- if (error)
- return error;
-
- pmap_invalidate_page(pmap, va);
-
- if (pmap_wired(pte))
- pmap->pm_stats.wired_count -= 1;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(lwp=%p)", l, 0, 0, 0);
+ KASSERT(l == curlwp);
+#if 0
+ /* pmap_switch(vmspace_pmap(td->td_proc->p_vmspace)); */
+ pmap_switch(vm_map_pmap(&l->l_proc->p_vmspace->vm_map));
+#else
+ struct pmap *pmap = l->l_proc->p_vmspace->vm_map.pmap;
- pmap->pm_stats.resident_count -= 1;
- if (pmap_managed(pte)) {
- pg = PHYS_TO_VM_PAGE(pmap_ppn(pte));
- if (pmap_dirty(pte))
- pg->flags &= ~(PG_CLEAN);
- if (pmap_accessed(pte))
- pg->flags &= ~PG_CLEAN; /* XXX: Do we need this ? */
+ if (pmap == pmap_kernel()) {
+ return;
+ }
- if (freepte)
- pmap_free_pte(pte, va);
+ if (l != curlwp) {
+ return;
+ }
- error = pmap_remove_entry(pmap, pg, va, pv);
+ pmap_switch(pmap);
+#endif
+}
- }
- if (freepte)
- pmap_free_pte(pte, va);
- return 0;
+/*
+ * pmap_deactivate: [ INTERFACE ]
+ *
+ * Deactivate the physical map used by the process behind lwp
+ * l. It is generally used in conjunction with
+ * pmap_activate(). Like pmap_activate(), pmap_deactivate()
+ * may not always be called when l is the current lwp.
+ *
+ */
+void
+pmap_deactivate(struct lwp *l)
+{
+ /* XXX ? */
}
/*
- * Free a pte which is now unused. This simply returns it to the zone
- * allocator if it is a user mapping. For kernel mappings, clear the
- * valid bit to make it clear that the mapping is not currently used.
+ * pmap_zero_page: [ INTERFACE ]
+ *
+ * Zero the specified (machine independent) page by mapping the page
+ * into virtual memory and clear its contents, one machine dependent
+ * page at a time.
+ *
+ * Note: no locking is necessary in this function.
*/
-static void
-pmap_free_pte(struct ia64_lpte *pte, vaddr_t va)
+void
+pmap_zero_page(paddr_t phys)
{
+ struct vm_page *m;
+ vaddr_t va;
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pa=%p)", phys, 0, 0, 0);
- if (va >= VM_MAXUSER_ADDRESS)
- pmap_clear_present(pte);
-}
+ m = PHYS_TO_VM_PAGE(phys);
+ KASSERT(m != NULL);
+ va = pmap_page_to_va(m);
+ KASSERT(trunc_page(va) == va);
-/***************************************************
- * page management routines.
- ***************************************************/
+ UVMHIST_LOG(maphist, "(pa=%p, va=%p)", phys, va, 0, 0);
+ memset((void *)va, 0, PAGE_SIZE);
+}
/*
- * get a new pv_entry, allocating a block from the system
- * when needed.
- * the memory allocation is performed bypassing the malloc code
- * because of the possibility of allocations at interrupt time.
- */
-/*
- * get a new pv_entry, allocating a block from the system
- * when needed.
+ * pmap_copy_page: [ INTERFACE ]
+ *
+ * Copy the specified (machine independent) page by mapping the page
+ * into virtual memory and using memcpy to copy the page, one machine
+ * dependent page at a time.
+ *
+ * Note: no locking is necessary in this function.
*/
-static pv_entry_t
-get_pv_entry(pmap_t locked_pmap)
+void
+pmap_copy_page(paddr_t psrc, paddr_t pdst)
{
- pv_entry_t allocated_pv;
+ struct vm_page *md, *ms;
+ vaddr_t dst_va, src_va;
- allocated_pv = pool_get(&pmap_pv_pool, PR_NOWAIT);
- return allocated_pv;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(sp=%p, dp=%p)", psrc, pdst, 0, 0);
- /* XXX: Nice to have all this stuff later:
- * Reclaim pv entries: At first, destroy mappings to inactive
- * pages. After that, if a pv entry is still needed, destroy
- * mappings to active pages.
- */
+ md = PHYS_TO_VM_PAGE(pdst);
+ ms = PHYS_TO_VM_PAGE(psrc);
+ KASSERT(md != NULL && ms != NULL);
+
+ dst_va = pmap_page_to_va(md);
+ src_va = pmap_page_to_va(ms);
+ KASSERT(trunc_page(dst_va) == dst_va && trunc_page(src_va) == src_va);
+
+ memcpy((void *)dst_va, (void *)src_va, PAGE_SIZE);
}
/*
- * free the pv_entry back to the free list
+ * pmap_page_protect: [ INTERFACE ]
+ *
+ * Lower the permissions for all mappings of the page pg to
+ * prot. This function is used by the virtual memory system
+ * to implement copy-on-write (called with VM_PROT_READ set in
+ * prot) and to revoke all mappings when cleaning a page
+ * (called with no bits set in prot). Access permissions must
+ * never be added to a page as a result of this call.
*/
-static __inline void
-free_pv_entry(pv_entry_t pv)
+void
+pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
+ //struct ia64_lpte *pte;
+ pmap_t pmap;
+ pv_entry_t pv;
+ vaddr_t va;
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(m=%p, prot=%p)", pg, prot, 0, 0);
- pool_put(&pmap_pv_pool, pv);
+ if ((prot & VM_PROT_WRITE) != 0)
+ return;
+ if (prot & (VM_PROT_READ | VM_PROT_EXECUTE)) {
+ /* XXX FreeBSD
+ if ((pg->mdpage.aflags & PGA_WRITEABLE) == 0)
+ return;
+ */
+ if (pg->flags & PG_RDONLY)
+ return;
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
+ pmap = PV_PMAP(pv);
+ va = pv->pv_va;
+ // locking of pmap done in pmap_protect
+ pmap_protect(pmap, va, va + PAGE_SIZE, prot);
+ }
+ /* XXX
+ vm_page_aflag_clear(pg, PGA_WRITEABLE);
+ */
+ pg->flags |= PG_RDONLY;
+
+ rw_exit(&pvh_global_lock);
+ } else {
+ pmap_remove_all_phys(pg);
+ }
}
/*
- * Add an ia64_lpte to the VHPT.
+ * pmap_clear_modify: [ INTERFACE ]
+ *
+ * Clear the modify bits on the specified physical page.
*/
-static void
-pmap_enter_vhpt(struct ia64_lpte *pte, vaddr_t va)
+bool
+pmap_clear_modify(struct vm_page *pg)
{
- struct ia64_bucket *bckt;
- struct ia64_lpte *vhpte;
- uint64_t pte_pa;
+ struct ia64_lpte *pte;
+ pmap_t oldpmap, pmap;
+ pv_entry_t pv;
+ bool rv;
- /* Can fault, so get it out of the way. */
- pte_pa = ia64_tpa((vaddr_t)pte);
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);
- vhpte = (struct ia64_lpte *)ia64_thash(va);
- bckt = (struct ia64_bucket *)vhpte->chain;
- /* XXX: fixme */
- mutex_enter(&bckt->lock);
- pte->chain = bckt->chain;
- ia64_mf();
- bckt->chain = pte_pa;
+ KASSERTMSG(vm_page_is_managed(pg), "%s : page %p not managed",
+ __func__, pg);
- pmap_vhpt_inserts++;
- bckt->length++;
- /*XXX : fixme */
- mutex_exit(&bckt->lock);
+ rv = false;
+
+ //VM_OBJECT_ASSERT_WLOCKED(m->object);
+ //KASSERT(!vm_page_xbusied(m),
+ // ("pmap_clear_modify: page %p is exclusive busied", m));
+
+ /*
+ * If the page is not PGA_WRITEABLE, then no PTEs can be modified.
+ * If the object containing the page is locked and the page is not
+ * exclusive busied, then PGA_WRITEABLE cannot be concurrently set.
+ */
+
+#if 0 /* XXX freebsd does this, looks faster but not required */
+ if ((m->aflags & PGA_WRITEABLE) == 0)
+ return;
+ if (pg->flags & PG_RDONLY)
+ return (rv);
+#endif
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ pte = pmap_find_vhpt(pv->pv_va);
+ KASSERTMSG(pte != NULL, "pte");
+ if (pmap_dirty(pte)) {
+ pmap_clear_dirty(pte);
+ pmap_invalidate_page(pv->pv_va);
+ rv = true;
+ }
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
+ }
+
+ rw_exit(&pvh_global_lock);
+
+ return (rv);
}
/*
- * Remove the ia64_lpte matching va from the VHPT. Return zero if it
- * worked or an appropriate error code otherwise.
+ * pmap_clear_reference: [ INTERFACE ]
+ *
+ * Clear the reference bit on the specified physical page.
+ *
+ * returns true or false
+ * indicating whether or not the ``referenced'' attribute was
+ * set on the page before it was cleared
*/
-static int
-pmap_remove_vhpt(vaddr_t va)
+bool
+pmap_clear_reference(struct vm_page *pg)
{
- struct ia64_bucket *bckt;
struct ia64_lpte *pte;
- struct ia64_lpte *lpte;
- struct ia64_lpte *vhpte;
- uint64_t chain, tag;
+ pmap_t oldpmap, pmap;
+ pv_entry_t pv;
+ bool rv;
- tag = ia64_ttag(va);
- vhpte = (struct ia64_lpte *)ia64_thash(va);
- bckt = (struct ia64_bucket *)vhpte->chain;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);
- lpte = NULL;
- mutex_enter(&bckt->lock);
+ KASSERTMSG(vm_page_is_managed(pg), "%s: page %p is not managed",
+ __func__, pg);
- chain = bckt->chain;
- pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
- while (chain != 0 && pte->tag != tag) {
- lpte = pte;
- chain = pte->chain;
- pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
- }
- if (chain == 0) {
- mutex_exit(&bckt->lock);
- return ENOENT;
+ rv = false;
+
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ pte = pmap_find_vhpt(pv->pv_va);
+ KASSERTMSG(pte != NULL, "pte");
+ if (pmap_accessed(pte)) {
+ rv = true;
+ pmap_clear_accessed(pte);
+ pmap_invalidate_page(pv->pv_va);
+ }
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
}
- /* Snip this pv_entry out of the collision chain. */
- if (lpte == NULL)
- bckt->chain = pte->chain;
- else
- lpte->chain = pte->chain;
- ia64_mf();
-
- bckt->length--;
- mutex_exit(&bckt->lock);
- return 0;
+ rw_exit(&pvh_global_lock);
+ return (rv);
}
/*
- * Find the ia64_lpte for the given va, if any.
+ * pmap_is_modified: [ INTERFACE ]
+ *
*/
-static struct ia64_lpte *
-pmap_find_vhpt(vaddr_t va)
+bool
+pmap_is_modified(struct vm_page *pg)
{
- struct ia64_bucket *bckt;
struct ia64_lpte *pte;
- uint64_t chain, tag;
+ pmap_t oldpmap, pmap;
+ pv_entry_t pv;
+ bool rv;
- tag = ia64_ttag(va);
- pte = (struct ia64_lpte *)ia64_thash(va);
- bckt = (struct ia64_bucket *)pte->chain;
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);
- mutex_enter(&bckt->lock);
- chain = bckt->chain;
- pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
- while (chain != 0 && pte->tag != tag) {
- chain = pte->chain;
- pte = (struct ia64_lpte *)IA64_PHYS_TO_RR7(chain);
+ KASSERTMSG(vm_page_is_managed(pg), "%s: page %p is not managed",
+ __func__, pg);
+ rv = false;
+
+ /*
+ * If the page is not exclusive busied, then PGA_WRITEABLE cannot be
+ * concurrently set while the object is locked. Thus, if PGA_WRITEABLE
+ * is clear, no PTEs can be dirty.
+ */
+ /* XXX freebsd
+ VM_OBJECT_ASSERT_WLOCKED(m->object);
+ if (!vm_page_xbusied(m) && (m->aflags & PGA_WRITEABLE) == 0)
+ return (rv);
+ */
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ pte = pmap_find_vhpt(pv->pv_va);
+ pmap_switch(oldpmap);
+ KASSERTMSG(pte != NULL, "pte");
+
+ rv = pmap_dirty(pte) ? true : false;
+ PMAP_UNLOCK(pmap);
+ if (rv)
+ break;
}
- mutex_exit(&bckt->lock);
- return (chain != 0) ? pte : NULL;
+
+ rw_exit(&pvh_global_lock);
+ return (rv);
}
/*
- * Remove an entry from the list of managed mappings.
+ * pmap_is_referenced: [ INTERFACE ]
+ *
+ * Test whether or not the ``referenced'' attribute is set on
+ * page pg.
+ *
*/
-static int
-pmap_remove_entry(pmap_t pmap, struct vm_page *pg, vaddr_t va, pv_entry_t pv)
+bool
+pmap_is_referenced(struct vm_page *pg)
{
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
+ struct ia64_lpte *pte;
+ pmap_t oldpmap, pmap;
+ pv_entry_t pv;
+ bool rv;
- if (!pv) {
- if (md->pv_list_count < pmap->pm_stats.resident_count) {
- TAILQ_FOREACH(pv, &md->pv_list, pv_list)
- if (pmap == pv->pv_pmap && va == pv->pv_va)
- break;
- } else {
- TAILQ_FOREACH(pv, &pmap->pm_pvlist, pv_plist)
- if (va == pv->pv_va)
- break;
- }
- }
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(m=%p)", pg, 0, 0, 0);
- if (pv) {
- TAILQ_REMOVE(&md->pv_list, pv, pv_list);
- md->pv_list_count--;
- if (TAILQ_FIRST(&md->pv_list) == NULL)
- pg->flags |= PG_RDONLY;
+ KASSERTMSG(vm_page_is_managed(pg), "%s: page %p is not managed",
+ __func__, pg);
+
+ rv = false;
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ TAILQ_FOREACH(pv, &pg->mdpage.pv_list, pv_list) {
+ pmap = PV_PMAP(pv);
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ pte = pmap_find_vhpt(pv->pv_va);
+ KASSERTMSG(pte != NULL, "pte");
+ rv = pmap_accessed(pte) ? true : false;
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
+ if (rv)
+ break;
+ }
- TAILQ_REMOVE(&pmap->pm_pvlist, pv, pv_plist);
- free_pv_entry(pv);
- return 0;
- } else
- return ENOENT;
+ rw_exit(&pvh_global_lock);
+ return (rv);
}
/*
- * Create a pv entry for page at pa for
- * (pmap, va).
+ * pmap_phys_address: [ INTERFACE ]
+ *
+ * Return the physical address corresponding to the specified
+ * cookie. Used by the device pager to decode a device driver's
+ * mmap entry point return value.
+ *
+ * Note: no locking is necessary in this function.
*/
-static void
-pmap_insert_entry(pmap_t pmap, vaddr_t va, struct vm_page *pg)
+paddr_t
+pmap_phys_address(paddr_t ppn)
{
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
- pv_entry_t pv;
-
- pv = get_pv_entry(pmap);
- pv->pv_pmap = pmap;
- pv->pv_va = va;
-
- TAILQ_INSERT_TAIL(&pmap->pm_pvlist, pv, pv_plist);
- TAILQ_INSERT_TAIL(&md->pv_list, pv, pv_list);
- md->pv_list_count++;
+ return ia64_ptob(ppn);
}
-/*
- * Remove a single page from a process address space
- */
-static void
-pmap_remove_page(pmap_t pmap, vaddr_t va)
+pmap_t
+pmap_switch(pmap_t pm)
{
- struct ia64_lpte *pte;
+ pmap_t prevpm;
+ int i;
- KASSERT(pmap == pmap_kernel() || pmap == curcpu()->ci_pmap);
+ critical_enter();
- pte = pmap_find_vhpt(va);
- if (pte) {
- pmap_remove_pte(pmap, pte, va, 0, 1);
- pmap_invalidate_page(pmap, va);
+ prevpm = curcpu()->ci_pmap;
+ if (prevpm == pm)
+ goto out;
+ if (pm == NULL) {
+ for (i = 0; i < IA64_VM_MINKERN_REGION; i++) {
+ ia64_set_rr(IA64_RR_BASE(i),
+ (i << 8)|(PAGE_SHIFT << 2)|1);
+ }
+ } else {
+ for (i = 0; i < IA64_VM_MINKERN_REGION; i++) {
+ ia64_set_rr(IA64_RR_BASE(i),
+ (pm->pm_rid[i] << 8)|(PAGE_SHIFT << 2)|1);
+ }
}
- return;
+
+ curcpu()->ci_pmap = pm;
+ ia64_srlz_d();
+
+out:
+ critical_exit();
+ return (prevpm);
}
+
/*
- * pmap_pv_page_alloc:
- *
- * Allocate a page for the pv_entry pool.
+ * Synchronize CPU instruction caches of the specified range.
+ * Same as freebsd
+ * void pmap_sync_icache(pmap_t pm, vaddr_t va, vsize_t sz)
*/
-void *
-pmap_pv_page_alloc(struct pool *pp, int flags)
+void
+pmap_procwr(struct proc *p, vaddr_t va, vsize_t sz)
{
- paddr_t pg;
+ pmap_t oldpm;
+ struct ia64_lpte *pte;
+ vaddr_t lim;
+ vsize_t len;
- if (pmap_poolpage_alloc(&pg))
- return (void *)IA64_PHYS_TO_RR7(pg);
- return NULL;
+ struct pmap * const pm = p->p_vmspace->vm_map.pmap;
+
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(pm=%p, va=%#lx, sz=%#lx)", pm, va, sz, 0);
+
+ sz += va & 31;
+ va &= ~31;
+ sz = (sz + 31) & ~31;
+
+ PMAP_LOCK(pm);
+ oldpm = pmap_switch(pm);
+ while (sz > 0) {
+ lim = round_page(va);
+ len = MIN(lim - va, sz);
+ pte = pmap_find_vhpt(va);
+ if (pte != NULL && pmap_present(pte))
+ ia64_sync_icache(va, len);
+ va += len;
+ sz -= len;
+ }
+ pmap_switch(oldpm);
+ PMAP_UNLOCK(pm);
}
/*
- * pmap_pv_page_free:
+ * Routine: pmap_remove_all_phys
+ * Function:
+ * Removes this physical page from
+ * all physical maps in which it resides.
+ * Reflects back modify bits to the pager.
*
- * Free a pv_entry pool page.
+ * Notes:
+ * Original versions of this routine were very
+ * inefficient because they iteratively called
+ * pmap_remove (slow...)
*/
void
-pmap_pv_page_free(struct pool *pp, void *v)
+pmap_remove_all_phys(struct vm_page *m)
{
+ pmap_t oldpmap;
+ pv_entry_t pv;
- pmap_poolpage_free(IA64_RR_MASK((vaddr_t)v));
-}
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(m=%p)", m, 0, 0, 0);
+
+ KASSERTMSG(vm_page_is_managed(m), "%s: page %p is not managed",
+ __func__, m);
+
+ rw_enter(&pvh_global_lock, RW_WRITER);
+ while ((pv = TAILQ_FIRST(&m->mdpage.pv_list)) != NULL) {
+ struct ia64_lpte *pte;
+ pmap_t pmap = PV_PMAP(pv);
+ vaddr_t va = pv->pv_va;
-/******************** misc. functions ********************/
+ PMAP_LOCK(pmap);
+ oldpmap = pmap_switch(pmap);
+ pte = pmap_find_vhpt(va);
+ KASSERTMSG(pte != NULL, "pte");
+ if (pmap_ppn(pte) != VM_PAGE_TO_PHYS(m))
+ panic("%s: pv_table for %lx is inconsistent",
+ __func__, VM_PAGE_TO_PHYS(m));
+ pmap_remove_pte(pmap, pte, va, pv, 1);
+ pmap_switch(oldpmap);
+ PMAP_UNLOCK(pmap);
+ }
+ /* XXX freebsd
+ vm_page_aflag_clear(m, PGA_WRITEABLE);
+ */
+ m->flags |= PG_RDONLY;
+
+ rw_exit(&pvh_global_lock);
+}
/*
- * pmap_poolpage_alloc: based on alpha/pmap_physpage_alloc
+ * vm_page_alloc1:
*
- * Allocate a single page from the VM system and return the
- * physical address for that page.
+ * Allocate and return a memory cell with no associated object.
*/
-bool
-pmap_poolpage_alloc(paddr_t *pap)
+static struct vm_page
+*vm_page_alloc1(void)
{
struct vm_page *pg;
- paddr_t pa;
-
+
pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE|UVM_PGA_ZERO);
- if (pg != NULL) {
- pa = VM_PAGE_TO_PHYS(pg);
-
-#ifdef DEBUG
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
- mutex_enter(&md->pv_mutex);
- if (pg->wire_count != 0) {
- printf("pmap_physpage_alloc: page 0x%lx has "
- "%d references\n", pa, pg->wire_count);
- panic("pmap_physpage_alloc");
- }
- mutex_exit(&md->pv_mutex);
-#endif
- *pap = pa;
- return true;
+ if (pg) {
+ pg->wire_count = 1; /* no mappings yet */
+ pg->flags &= ~PG_BUSY; /* never busy */
}
- return false;
+ return pg;
}
/*
- * pmap_poolpage_free: based on alpha/pmap_physpage_free:
+ * vm_page_free1:
+ *
+ * Returns the given page to the free list,
+ * disassociating it with any VM object.
*
- * Free the single page table page at the specified physical address.
+ * Object and page must be locked prior to entry.
*/
-void
-pmap_poolpage_free(paddr_t pa)
+static void
+vm_page_free1(struct vm_page *pg)
{
- struct vm_page *pg;
-
- if ((pg = PHYS_TO_VM_PAGE(pa)) == NULL)
- panic("pmap_physpage_free: bogus physical page address");
-
-#ifdef DEBUG
- struct vm_page_md * const md = VM_PAGE_TO_MD(pg);
- mutex_enter(&md->pv_mutex);
- if (pg->wire_count != 0)
- panic("pmap_physpage_free: page still has references");
- mutex_exit(&md->pv_mutex);
-#endif
+ KASSERT(pg->flags != (PG_CLEAN|PG_FAKE)); /* Freeing invalid page */
+ pg->flags |= PG_BUSY;
+ pg->wire_count = 0;
uvm_pagefree(pg);
}
-#ifdef DEBUG
-
-static void dump_vhpt(void)
+/*
+ * pmap_flag_to_attr
+ *
+ * Convert pmap_enter/pmap_kenter_pa flags to memory attributes
+ */
+static vm_memattr_t
+pmap_flags_to_memattr(u_int flags)
{
+ u_int cacheflags = flags & PMAP_CACHE_MASK;
- vaddr_t base;
- vsize_t size, i;
- struct ia64_lpte *pte;
-
- __asm __volatile("mov %0=cr.pta;; srlz.i;;" : "=r" (base));
-
-#define VHPTBASE(x) ( (x) & (~0x7fffUL) )
-#define VHPTSIZE(x) ( (vsize_t) (1 << (((x) & 0x7cUL) >> 2)))
-
- size = VHPTSIZE(base);
- base = VHPTBASE(base);
-
- pte = (void *) base;
-
- printf("vhpt base = %lx \n", base);
- printf("vhpt size = %lx \n", size);
-
- for(i = 0; i < size/sizeof(struct ia64_lpte);i++ )
- if(pte[i].pte & PTE_PRESENT) {
- printf("PTE_PRESENT ");
+#if 0
+ UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
+ UVMHIST_LOG(maphist, "(PMAP_NOCACHE=%u, PMAP_WRITE_COMBINE=%u, "
+ "PMAP_WRITE_BACK=%u, PMAP_NOCACHE_OVR=%u)",
+ (flags & PMAP_NOCACHE) != 0, (flags & PMAP_WRITE_COMBINE) != 0,
+ (flags & PMAP_WRITE_BACK) != 0, (flags & PMAP_NOCACHE_OVR) != 0);
+#endif
+ switch (cacheflags) {
+ case PMAP_NOCACHE:
+ return VM_MEMATTR_UNCACHEABLE;
+ case PMAP_WRITE_COMBINE:
+ /* XXX implement if possible */
+ KASSERT(1);
+ return VM_MEMATTR_WRITE_COMBINING;
+ case PMAP_WRITE_BACK:
+ return VM_MEMATTR_WRITE_BACK;
+ case PMAP_NOCACHE_OVR:
+ default:
+ return VM_MEMATTR_DEFAULT;
+ }
+}
- if (pte[i].pte & PTE_MA_MASK) printf("MA: ");
- if (pte[i].pte & PTE_MA_WB) printf("WB ");
- if (pte[i].pte & PTE_MA_UC) printf("UC ");
- if (pte[i].pte & PTE_MA_UCE) printf("UCE ");
- if (pte[i].pte & PTE_MA_WC) printf("WC ");
- if (pte[i].pte & PTE_MA_NATPAGE)printf("NATPAGE ");
+#ifdef DEBUG
+/*
+ * Test ref/modify handling.
+ */
- if (pte[i].pte & PTE_ACCESSED) printf("PTE_ACCESSED ");
- if (pte[i].pte & PTE_DIRTY) printf("PTE_DIRTY ");
+static void
+pmap_testout(void)
+{
+ vaddr_t va;
+ volatile int *loc;
+ int val = 0;
+ paddr_t pa;
+ struct vm_page *pg;
+ int ref, mod;
+ bool extracted;
+
+ /* Allocate a page */
+ va = (vaddr_t)uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_WIRED | UVM_KMF_ZERO);
+ KASSERT(va != 0);
+ loc = (int*)va;
+
+ extracted = pmap_extract(pmap_kernel(), va, &pa);
+ printf("va %p pa %lx extracted %u\n", (void *)(u_long)va, pa, extracted);
+ printf("kextract %lx\n", pmap_kextract(va));
- if (pte[i].pte & PTE_PL_MASK) printf("PL: ");
- if (pte[i].pte & PTE_PL_KERN) printf("KERN");
- if (pte[i].pte & PTE_PL_USER) printf("USER");
+ pg = PHYS_TO_VM_PAGE(pa);
+ pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
+ pmap_update(pmap_kernel());
- if (pte[i].pte & PTE_AR_MASK) printf("AR: ");
- if (pte[i].pte & PTE_AR_R) printf("R ");
- if (pte[i].pte & PTE_AR_RX) printf("RX ");
- if (pte[i].pte & PTE_AR_RWX) printf("RWX ");
- if (pte[i].pte & PTE_AR_R_RW) printf("R RW ");
- if (pte[i].pte & PTE_AR_RX_RWX) printf("RX RWX ");
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
- printf("ppn = %lx", (pte[i].pte & PTE_PPN_MASK) >> 12);
+ printf("Entered page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa, ref, mod);
- if (pte[i].pte & PTE_ED) printf("ED ");
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+ /* Check it's properly cleared */
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Checking cleared page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Reference page */
+ val = *loc;
+
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Referenced page: ref %d, mod %d val %x\n",
+ ref, mod, val);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+ /* Modify page */
+ *loc = 1;
+
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Modified page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+ /* Check it's properly cleared */
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Checking cleared page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Modify page */
+ *loc = 1;
+
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Modified page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Check pmap_protect() */
+ pmap_protect(pmap_kernel(), va, va+1, VM_PROT_READ);
+ pmap_update(pmap_kernel());
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("pmap_protect(VM_PROT_READ): ref %d, mod %d\n",
+ ref, mod);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+ /* Modify page */
+ pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
+ pmap_update(pmap_kernel());
+ *loc = 1;
+
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Modified page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Check pmap_protect() */
+ pmap_protect(pmap_kernel(), va, va+1, VM_PROT_NONE);
+ pmap_update(pmap_kernel());
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("pmap_protect(VM_PROT_READ): ref %d, mod %d\n",
+ ref, mod);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+ /* Modify page */
+ pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
+ pmap_update(pmap_kernel());
+ *loc = 1;
+
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Modified page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Check pmap_pag_protect() */
+ pmap_page_protect(pg, VM_PROT_READ);
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("pmap_protect(): ref %d, mod %d\n",
+ ref, mod);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+
+ /* Modify page */
+ pmap_enter(pmap_kernel(), va, pa, VM_PROT_ALL, 0);
+ pmap_update(pmap_kernel());
+ *loc = 1;
+
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Modified page: ref %d, mod %d\n",
+ ref, mod);
+
+ /* Check pmap_pag_protect() */
+ pmap_page_protect(pg, VM_PROT_NONE);
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("pmap_protect(): ref %d, mod %d\n",
+ ref, mod);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa,
+ ref, mod);
+
+ /* Unmap page */
+ pmap_remove(pmap_kernel(), va, va+1);
+ pmap_update(pmap_kernel());
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Unmapped page: ref %d, mod %d\n", ref, mod);
+
+ /* Now clear reference and modify */
+ ref = pmap_clear_reference(pg);
+ mod = pmap_clear_modify(pg);
+ printf("Clearing page va %p pa %lx: ref %d, mod %d\n",
+ (void *)(u_long)va, (long)pa, ref, mod);
+
+ /* Check it's properly cleared */
+ ref = pmap_is_referenced(pg);
+ mod = pmap_is_modified(pg);
+ printf("Checking cleared page: ref %d, mod %d\n",
+ ref, mod);
+
+ pmap_remove(pmap_kernel(), va, va+1);
+ pmap_update(pmap_kernel());
+ //pmap_free_page(pa, cpus_active);
+ uvm_km_free(kernel_map, (vaddr_t)va, PAGE_SIZE,
+ UVM_KMF_WIRED | UVM_KMF_ZERO);
- if (pte[i].pte & PTE_IG_MASK) printf("OS: ");
- if (pte[i].pte & PTE_WIRED) printf("WIRED ");
- if (pte[i].pte & PTE_MANAGED) printf("MANAGED ");
- printf("\n");
- }
+ panic("end of testout");
}
#endif
Index: src/sys/arch/ia64/include/pmap.h
diff -u src/sys/arch/ia64/include/pmap.h:1.7 src/sys/arch/ia64/include/pmap.h:1.8
--- src/sys/arch/ia64/include/pmap.h:1.7 Sun Nov 14 13:33:22 2010
+++ src/sys/arch/ia64/include/pmap.h Sat Apr 8 18:08:33 2017
@@ -69,67 +69,93 @@
* from: hp300: @(#)pmap.h 7.2 (Berkeley) 12/16/90
* from: @(#)pmap.h 7.4 (Berkeley) 5/12/91
* from: i386 pmap.h,v 1.54 1997/11/20 19:30:35 bde Exp
- * $FreeBSD: src/sys/ia64/include/pmap.h,v 1.25 2005/09/03 23:53:50 marcel Exp $
+ * $FreeBSD: releng/10.1/sys/ia64/include/pmap.h 268201 2014-07-02 23:57:55Z marcel $
*/
-#ifndef _PMAP_MACHINE_
-#define _PMAP_MACHINE_
+#ifndef _IA64_PMAP_H_
+#define _IA64_PMAP_H_
+#include <sys/types.h>
+#include <sys/queue.h>
#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <machine/pte.h>
+#include <machine/vmparam.h>
+
+#include <machine/md_var.h>
+
+typedef char vm_memattr_t;
-paddr_t vtophys(vaddr_t);
+#define VM_MEMATTR_WRITE_BACK ((vm_memattr_t)PTE_MA_WB)
+#define VM_MEMATTR_UNCACHEABLE ((vm_memattr_t)PTE_MA_UC)
+#define VM_MEMATTR_UNCACHEABLE_EXPORTED ((vm_memattr_t)PTE_MA_UCE)
+#define VM_MEMATTR_WRITE_COMBINING ((vm_memattr_t)PTE_MA_WC)
+#define VM_MEMATTR_NATPAGE ((vm_memattr_t)PTE_MA_NATPAGE)
+#define VM_MEMATTR_DEFAULT VM_MEMATTR_WRITE_BACK
-struct pv_entry; /* Forward declaration. */
+#ifdef _KERNEL
+
+#define MAXKPT (PAGE_SIZE/sizeof(vaddr_t))
+
+#define vtophys(va) pmap_kextract((vaddr_t)(va))
+
+#endif /* _KERNEL */
+
+/*
+ * Pmap stuff
+ */
+struct pv_entry;
+struct pv_chunk;
struct pmap {
- TAILQ_ENTRY(pmap) pm_list; /* list of all pmaps */
- TAILQ_HEAD(,pv_entry) pm_pvlist; /* list of mappings in pmap */
- int pm_count; /* pmap reference count */
- kmutex_t pm_slock; /* lock on pmap */
- uint32_t pm_rid[5]; /* base RID for pmap */
- int pm_active; /* active flag */
+ kmutex_t pm_mtx;
+ TAILQ_HEAD(,pv_chunk) pm_pvchunk; /* list of mappings in pmap */
+ uint32_t pm_rid[IA64_VM_MINKERN_REGION];
struct pmap_statistics pm_stats; /* pmap statistics */
- unsigned long pm_cpus; /* mask of CPUs using pmap */
-
+ uint64_t pm_refcount; /* pmap reference count, atomic */
};
+typedef struct pmap *pmap_t;
+
+#ifdef _KERNEL
+
+#define PMAP_LOCK(pmap) mutex_enter(&(pmap)->pm_mtx)
+#define PMAP_LOCK_ASSERT(pmap) KASSERT(mutex_owned(&(pmap)->pm_mtx))
+#define PMAP_LOCK_DESTROY(pmap) mutex_destroy(&(pmap)->pm_mtx)
+#define PMAP_LOCK_INIT(pmap) mutex_init(&(pmap)->pm_mtx, MUTEX_DEFAULT, IPL_NONE)
+#define PMAP_LOCKED(pmap) mutex_owned(&(pmap)->pm_mtx)
+#define PMAP_MTX(pmap) (&(pmap)->pm_mtx)
+#define PMAP_TRYLOCK(pmap) mutex_tryenter(&(pmap)->pm_mtx)
+#define PMAP_UNLOCK(pmap) mutex_exit(&(pmap)->pm_mtx)
+
+#endif
+
/*
* For each vm_page_t, there is a list of all currently valid virtual
- * mappings of that page. An entry is a pv_entry_t, the list is pv_pvlist.
+ * mappings of that page. An entry is a pv_entry_t, the list is pv_list.
*/
typedef struct pv_entry {
- pmap_t pv_pmap; /* pmap where mapping lies */
- vaddr_t pv_va; /* virtual address for mapping */
+ vaddr_t pv_va; /* virtual address for mapping */
TAILQ_ENTRY(pv_entry) pv_list;
- TAILQ_ENTRY(pv_entry) pv_plist;
} *pv_entry_t;
-/* pvh_attrs */
-#define PGA_MODIFIED 0x01 /* modified */
-#define PGA_REFERENCED 0x02 /* referenced */
-
-
-#define pmap_resident_count(pmap) ((pmap)->pm_stats.resident_count)
-#define pmap_wired_count(pmap) ((pmap)->pm_stats.wired_count)
-
-#define pmap_copy(dp, sp, da, l, sa) /* nothing */
-#define pmap_update(pmap) /* nothing (yet) */
-
void pmap_bootstrap(void);
-#define pmap_is_referenced(pg) \
- (((pg)->mdpage.pvh_attrs & PGA_REFERENCED) != 0)
-#define pmap_is_modified(pg) \
- (((pg)->mdpage.pvh_attrs & PGA_MODIFIED) != 0)
-
+/* optional pmap API functions, according to pmap(9) */
+#define PMAP_STEAL_MEMORY
+#define PMAP_GROWKERNEL
-#define PMAP_STEAL_MEMORY /* enable pmap_steal_memory() */
+#define PMAP_NEED_PROCWR
+void pmap_procwr(struct proc *, vaddr_t, vsize_t);
/*
* Alternate mapping hooks for pool pages. Avoids thrashing the TLB.
*/
+/* XXX
#define PMAP_MAP_POOLPAGE(pa) IA64_PHYS_TO_RR7((pa))
#define PMAP_UNMAP_POOLPAGE(va) IA64_RR_MASK((va))
+*/
/*
* Macros for locking pmap structures.
@@ -139,30 +165,46 @@ void pmap_bootstrap(void);
* operations, locking the kernel pmap is not necessary. Therefore,
* it is not necessary to block interrupts when locking pmap strucutres.
*/
+/* XXX
#define PMAP_LOCK(pmap) mutex_enter(&(pmap)->pm_slock)
#define PMAP_UNLOCK(pmap) mutex_exit(&(pmap)->pm_slock)
+*/
-
-#define PMAP_VHPT_LOG2SIZE 16
-
-
-#include <sys/queue.h>
-#include <sys/mutex.h>
/*
* pmap-specific data store in the vm_page structure.
*/
#define __HAVE_VM_PAGE_MD
+
struct vm_page_md {
- TAILQ_HEAD(,pv_entry) pv_list; /* pv_entry list */
- int pv_list_count;
- kmutex_t pv_mutex; /* lock on this head */
- int pvh_attrs; /* page attributes */
+ TAILQ_HEAD(,pv_entry) pv_list;
+ vm_memattr_t memattr;
+#if 0 /* XXX freebsd */
+ uint8_t pv_flags;
+ uint8_t aflags;
+#endif
};
#define VM_MDPAGE_INIT(pg) \
do { \
TAILQ_INIT(&(pg)->mdpage.pv_list); \
- mutex_init(&(pg)->mdpage.pv_mutex, MUTEX_DEFAULT, IPL_NONE); \
+ (pg)->mdpage.memattr = VM_MEMATTR_DEFAULT; \
} while (/*CONSTCOND*/0)
-#endif /* _PMAP_MACHINE_ */
+#ifdef _KERNEL
+
+extern uint64_t pmap_vhpt_base[];
+extern uint64_t pmap_vhpt_log2size;
+
+vaddr_t pmap_page_to_va(struct vm_page*);
+
+/* Machine-architecture private */
+vaddr_t pmap_alloc_vhpt(void);
+void pmap_bootstrap(void);
+void pmap_invalidate_all(void);
+paddr_t pmap_kextract(vaddr_t va);
+struct pmap *pmap_switch(struct pmap *pmap);
+void pmap_remove_all_phys(struct vm_page*); /* used in only pmap_page_protect */
+
+#endif /* _KERNEL */
+
+#endif /* _IA64_PMAP_H_ */