summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/machine/pmap.c206
-rw-r--r--arch/x86/machine/pmap.h36
-rw-r--r--vm/vm_setup.c2
3 files changed, 234 insertions, 10 deletions
diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c
index 556e3cbb..4a32b222 100644
--- a/arch/x86/machine/pmap.c
+++ b/arch/x86/machine/pmap.c
@@ -19,7 +19,10 @@
*/
#include <kern/assert.h>
+#include <kern/error.h>
#include <kern/init.h>
+#include <kern/kmem.h>
+#include <kern/list.h>
#include <kern/macros.h>
#include <kern/panic.h>
#include <kern/param.h>
@@ -68,7 +71,7 @@
* This pool of pure virtual memory can be used to reserve virtual addresses
* before the VM system is initialized.
*/
-#define PMAP_RESERVED_PAGES 2
+#define PMAP_RESERVED_PAGES (2 + PMAP_NR_RPTPS)
/*
* Properties of a page translation level.
@@ -77,14 +80,18 @@ struct pmap_pt_level {
unsigned int bits;
unsigned int shift;
pmap_pte_t *ptes; /* PTEs in the recursive mapping */
+ unsigned int nr_ptes;
pmap_pte_t mask;
};
#ifdef X86_PAE
+
+#define PMAP_PDPT_ALIGN 32
+
/*
* "Hidden" root page table for PAE mode.
*/
-static pmap_pte_t pmap_pdpt[PMAP_NR_RPTPS] __aligned(32);
+static pmap_pte_t pmap_pdpt[PMAP_NR_RPTPS] __aligned(PMAP_PDPT_ALIGN);
#endif /* X86_PAE */
/*
@@ -108,12 +115,12 @@ static unsigned long pmap_kernel_limit;
* This table is only used before paging is enabled.
*/
static struct pmap_pt_level pmap_boot_pt_levels[] __initdata = {
- { PMAP_L1_BITS, PMAP_L1_SHIFT, PMAP_PTEMAP_BASE, PMAP_L1_MASK },
- { PMAP_L2_BITS, PMAP_L2_SHIFT, PMAP_L2_PTEMAP, PMAP_L2_MASK },
+ { PMAP_L1_BITS, PMAP_L1_SHIFT, PMAP_PTEMAP_BASE, PMAP_L2_NR_PTES, PMAP_L1_MASK },
+ { PMAP_L2_BITS, PMAP_L2_SHIFT, PMAP_L2_PTEMAP, PMAP_L2_NR_PTES, PMAP_L2_MASK },
#if PMAP_NR_LEVELS > 2
- { PMAP_L3_BITS, PMAP_L3_SHIFT, PMAP_L3_PTEMAP, PMAP_L3_MASK },
+ { PMAP_L3_BITS, PMAP_L3_SHIFT, PMAP_L3_PTEMAP, PMAP_L3_NR_PTES, PMAP_L3_MASK },
#if PMAP_NR_LEVELS > 3
- { PMAP_L4_BITS, PMAP_L4_SHIFT, PMAP_L4_PTEMAP, PMAP_L4_MASK }
+ { PMAP_L4_BITS, PMAP_L4_SHIFT, PMAP_L4_PTEMAP, PMAP_L4_NR_PTES, PMAP_L4_MASK }
#endif /* PMAP_NR_LEVELS > 3 */
#endif /* PMAP_NR_LEVELS > 2 */
};
@@ -145,6 +152,8 @@ static pmap_pte_t pmap_prot_table[8];
*/
static unsigned long pmap_zero_va;
static struct spinlock pmap_zero_va_lock;
+static unsigned long pmap_pt_va;
+static struct spinlock pmap_pt_va_lock;
/*
* Shared variables used by the inter-processor update functions.
@@ -160,6 +169,18 @@ static struct {
volatile unsigned long count __aligned(CPU_L1_SIZE);
} pmap_nr_updates;
+static struct kmem_cache pmap_cache;
+
+#ifdef X86_PAE
+static struct kmem_cache pmap_pdpt_cache;
+#endif /* X86_PAE */
+
+/*
+ * Global list of physical maps.
+ */
+static struct list pmap_list;
+static struct spinlock pmap_list_lock;
+
static void __init
pmap_boot_enter(pmap_pte_t *root_pt, unsigned long va, phys_addr_t pa)
{
@@ -342,6 +363,8 @@ pmap_bootstrap(void)
pmap_boot_heap_end = pmap_boot_heap + (PMAP_RESERVED_PAGES * PAGE_SIZE);
pmap_zero_va = pmap_bootalloc(1);
spinlock_init(&pmap_zero_va_lock);
+ pmap_pt_va = pmap_bootalloc(PMAP_NR_RPTPS);
+ spinlock_init(&pmap_pt_va_lock);
pmap_kprotect((unsigned long)&_text, (unsigned long)&_rodata,
VM_PROT_READ | VM_PROT_EXECUTE);
@@ -549,3 +572,174 @@ pmap_update_intr(struct trap_frame *frame)
pmap_kupdate_local(pmap_update_start, pmap_update_end);
atomic_add(&pmap_nr_updates.count, -1);
}
+
+#ifdef X86_PAE
+static unsigned long
+pmap_pdpt_alloc(size_t slab_size)
+{
+ struct vm_page *page;
+ unsigned long va, start, end;
+
+ va = vm_kmem_alloc_va(slab_size);
+
+ if (va == 0)
+ return 0;
+
+ for (start = va, end = va + slab_size; start < end; start += PAGE_SIZE) {
+ page = vm_phys_alloc_seg(0, VM_PHYS_SEG_NORMAL);
+
+ if (page == NULL)
+ goto error_page;
+
+ pmap_kenter(start, vm_page_to_pa(page));
+ }
+
+ pmap_kupdate(va, end);
+ return va;
+
+error_page:
+ vm_kmem_free(va, slab_size);
+ return 0;
+}
+#endif /* X86_PAE */
+
+void
+pmap_setup(void)
+{
+ kmem_cache_init(&pmap_cache, "pmap", sizeof(struct pmap),
+ 0, NULL, NULL, NULL, 0);
+
+#ifdef X86_PAE
+ kmem_cache_init(&pmap_pdpt_cache, "pmap_pdpt",
+ PMAP_NR_RPTPS * sizeof(pmap_pte_t), PMAP_PDPT_ALIGN,
+ NULL, pmap_pdpt_alloc, NULL, 0);
+#endif /* X86_PAE */
+
+ list_init(&pmap_list);
+ spinlock_init(&pmap_list_lock);
+}
+
+static void
+pmap_map_pt(phys_addr_t pa)
+{
+ unsigned long va;
+ unsigned int i, offset;
+
+ spinlock_lock(&pmap_pt_va_lock);
+
+ for (i = 0; i < PMAP_NR_RPTPS; i++) {
+ offset = i * PAGE_SIZE;
+ va = pmap_pt_va + offset;
+ pmap_kenter(va, pa + offset);
+ cpu_tlb_flush_va(va);
+ }
+}
+
+static void
+pmap_unmap_pt(void)
+{
+ unsigned int i;
+
+ pmap_kremove(pmap_pt_va, pmap_pt_va + (PMAP_NR_RPTPS * PAGE_SIZE));
+
+ for (i = 0; i < PMAP_NR_RPTPS; i++)
+ cpu_tlb_flush_va(pmap_pt_va + (i * PAGE_SIZE));
+
+ spinlock_unlock(&pmap_pt_va_lock);
+}
+
+int
+pmap_create(struct pmap **pmapp)
+{
+ const struct pmap_pt_level *pt_level;
+ struct vm_page *root_pages;
+ struct pmap *pmap;
+ pmap_pte_t *pt, *kpt;
+ phys_addr_t pa;
+ unsigned long va;
+ unsigned int i, index;
+ int error;
+
+ pmap = kmem_cache_alloc(&pmap_cache);
+
+ if (pmap == NULL) {
+ error = ERROR_NOMEM;
+ goto error_pmap;
+ }
+
+ root_pages = vm_phys_alloc(PMAP_RPTP_ORDER);
+
+ if (root_pages == NULL) {
+ error = ERROR_NOMEM;
+ goto error_pages;
+ }
+
+ pmap->root_pt = vm_page_to_pa(root_pages);
+
+#ifdef X86_PAE
+ pmap->pdpt = kmem_cache_alloc(&pmap_pdpt_cache);
+
+ if (pmap->pdpt == NULL) {
+ error = ERROR_NOMEM;
+ goto error_pdpt;
+ }
+
+ va = (unsigned long)pmap->pdpt;
+ assert(P2ALIGNED(va, PMAP_PDPT_ALIGN));
+
+ for (i = 0; i < PMAP_NR_RPTPS; i++)
+ pmap->pdpt[i] = (pmap->root_pt + (i * PAGE_SIZE)) | PMAP_PTE_P;
+
+ pmap->pdpt_pa = pmap_kextract(va) + (va & PAGE_MASK);
+ assert(pmap->pdpt_pa < VM_PHYS_NORMAL_LIMIT);
+#endif /* X86_PAE */
+
+ pt_level = &pmap_pt_levels[PMAP_NR_LEVELS - 1];
+ pt = (pmap_pte_t *)pmap_pt_va;
+ kpt = pt_level->ptes;
+ index = PMAP_PTEMAP_INDEX(VM_PMAP_PTEMAP_ADDRESS, pt_level->shift);
+
+ pmap_map_pt(pmap->root_pt);
+
+ memset(pt, 0, index * sizeof(pmap_pte_t));
+ index += PMAP_NR_RPTPS;
+ memcpy(&pt[index], &kpt[index], (pt_level->nr_ptes - index)
+ * sizeof(pmap_pte_t));
+
+ for (i = 0; i < PMAP_NR_RPTPS; i++) {
+ va = VM_PMAP_PTEMAP_ADDRESS + (i * (1 << pt_level->shift));
+ index = (va >> pt_level->shift) & ((1 << pt_level->bits) - 1);
+ pa = pmap->root_pt + (i * PAGE_SIZE);
+ pt[index] = (pa | PMAP_PTE_RW | PMAP_PTE_P) & pt_level->mask;
+ }
+
+ pmap_unmap_pt();
+
+ spinlock_init(&pmap->lock);
+
+ spinlock_lock(&pmap_list_lock);
+ list_insert_tail(&pmap_list, &pmap->node);
+ spinlock_unlock(&pmap_list_lock);
+
+ *pmapp = pmap;
+ return 0;
+
+#ifdef X86_PAE
+error_pdpt:
+ vm_phys_free(root_pages, PMAP_RPTP_ORDER);
+#endif /* X86_PAE */
+error_pages:
+ kmem_cache_free(&pmap_cache, pmap);
+error_pmap:
+ return error;
+}
+
+void
+pmap_load(struct pmap *pmap)
+{
+#ifdef X86_PAE
+ cpu_set_cr3(pmap->pdpt_pa);
+#else /* X86_PAE */
+ cpu_set_cr3(pmap->root_pt);
+#endif /* X86_PAE */
+}
diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h
index e082616b..7b9e86c5 100644
--- a/arch/x86/machine/pmap.h
+++ b/arch/x86/machine/pmap.h
@@ -47,7 +47,7 @@
| PMAP_PTE_P)
#ifdef __LP64__
-#define PMAP_NR_RPTPS 1
+#define PMAP_RPTP_ORDER 0
#define PMAP_NR_LEVELS 4
#define PMAP_L1_BITS 9
#define PMAP_L2_BITS 9
@@ -59,14 +59,14 @@
#define PMAP_L4_MASK PMAP_L2_MASK
#else /* __LP64__ */
#ifdef X86_PAE
-#define PMAP_NR_RPTPS 4 /* Assume two levels with a 4-page root table */
+#define PMAP_RPTP_ORDER 2 /* Assume two levels with a 4-page root table */
#define PMAP_NR_LEVELS 2
#define PMAP_L1_BITS 9
#define PMAP_L2_BITS 11
#define PMAP_VA_MASK DECL_CONST(0xffffffff, UL)
#define PMAP_PA_MASK DECL_CONST(0x000ffffffffff000, ULL)
#else /* X86_PAE */
-#define PMAP_NR_RPTPS 1
+#define PMAP_RPTP_ORDER 0
#define PMAP_NR_LEVELS 2
#define PMAP_L1_BITS 10
#define PMAP_L2_BITS 10
@@ -85,8 +85,12 @@
#define PMAP_L3_NR_PTES (1 << PMAP_L3_BITS)
#define PMAP_L4_NR_PTES (1 << PMAP_L4_BITS)
+#define PMAP_NR_RPTPS (1 << PMAP_RPTP_ORDER)
+
#ifndef __ASSEMBLER__
+#include <kern/list.h>
+#include <kern/spinlock.h>
#include <kern/stdint.h>
#include <kern/types.h>
#include <machine/trap.h>
@@ -101,7 +105,13 @@ typedef unsigned long pmap_pte_t;
* Physical address map.
*/
struct pmap {
- int __dummy;
+ struct spinlock lock;
+ struct list node;
+ phys_addr_t root_pt;
+#ifdef X86_PAE
+ pmap_pte_t *pdpt;
+ phys_addr_t pdpt_pa;
+#endif /* X86_PAE */
};
/*
@@ -176,6 +186,24 @@ void pmap_kupdate(unsigned long start, unsigned long end);
*/
void pmap_update_intr(struct trap_frame *frame);
+/*
+ * Set up the pmap module.
+ *
+ * This function should only be called by the VM system, once kernel
+ * allocations can be performed safely.
+ */
+void pmap_setup(void);
+
+/*
+ * Create a pmap for a user task.
+ */
+int pmap_create(struct pmap **pmapp);
+
+/*
+ * Load the given pmap on the current processor.
+ */
+void pmap_load(struct pmap *pmap);
+
#endif /* __ASSEMBLER__ */
#endif /* _X86_PMAP_H */
diff --git a/vm/vm_setup.c b/vm/vm_setup.c
index cca7ada6..72a7db4b 100644
--- a/vm/vm_setup.c
+++ b/vm/vm_setup.c
@@ -16,6 +16,7 @@
*/
#include <kern/kmem.h>
+#include <machine/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_kmem.h>
#include <vm/vm_phys.h>
@@ -28,4 +29,5 @@ vm_setup(void)
vm_phys_setup();
kmem_setup();
vm_map_setup();
+ pmap_setup();
}