summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/vm/i86_mmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/i86pc/vm/i86_mmu.c')
-rw-r--r--usr/src/uts/i86pc/vm/i86_mmu.c117
1 files changed, 111 insertions, 6 deletions
diff --git a/usr/src/uts/i86pc/vm/i86_mmu.c b/usr/src/uts/i86pc/vm/i86_mmu.c
index 7bf91af9b5..069411ae23 100644
--- a/usr/src/uts/i86pc/vm/i86_mmu.c
+++ b/usr/src/uts/i86pc/vm/i86_mmu.c
@@ -59,6 +59,10 @@
#include <sys/bootinfo.h>
#include <vm/kboot_mmu.h>
+#ifdef __xpv
+#include <sys/hypervisor.h>
+#endif
+
caddr_t
i86devmap(pfn_t pf, pgcnt_t pgcnt, uint_t prot)
{
@@ -195,6 +199,8 @@ hat_kmap_init(uintptr_t base, size_t len)
/*
* We have to map in an area that matches an entire page table.
+ * The PTEs are large page aligned to avoid spurious pagefaults
+ * on the hypervisor.
*/
map_addr = base & LEVEL_MASK(1);
map_eaddr = (base + len + LEVEL_SIZE(1) - 1) & LEVEL_MASK(1);
@@ -224,7 +230,11 @@ hat_kmap_init(uintptr_t base, size_t len)
hat_devload(kas.a_hat, ptes + i * MMU_PAGESIZE,
MMU_PAGESIZE, ht->ht_pfn,
+#ifdef __xpv
+ PROT_READ | HAT_NOSYNC | HAT_UNORDERED_OK,
+#else
PROT_READ | PROT_WRITE | HAT_NOSYNC | HAT_UNORDERED_OK,
+#endif
HAT_LOAD | HAT_LOAD_NOCONSIST);
}
@@ -239,6 +249,55 @@ hat_kmap_init(uintptr_t base, size_t len)
extern caddr_t kpm_vbase;
extern size_t kpm_size;
+#ifdef __xpv
+/*
+ * Create the initial segkpm mappings for the hypervisor. To avoid having
+ * to deal with page tables being read only, we make all mappings
+ * read only at first.
+ */
+static void
+xen_kpm_create(paddr_t paddr, level_t lvl)
+{
+ ulong_t pg_off;
+
+ for (pg_off = 0; pg_off < LEVEL_SIZE(lvl); pg_off += MMU_PAGESIZE) {
+ kbm_map((uintptr_t)kpm_vbase + paddr, (paddr_t)0, 0, 1);
+ kbm_read_only((uintptr_t)kpm_vbase + paddr + pg_off,
+ paddr + pg_off);
+ }
+}
+
+/*
+ * Try to make all kpm mappings writable. Failures are ok, as those
+ * are just pagetable, GDT, etc. pages.
+ */
+static void
+xen_kpm_finish_init(void)
+{
+ pfn_t gdtpfn = mmu_btop(CPU->cpu_m.mcpu_gdtpa);
+ pfn_t pfn;
+ page_t *pp;
+
+ for (pfn = 0; pfn < mfn_count; ++pfn) {
+ /*
+ * skip gdt
+ */
+ if (pfn == gdtpfn)
+ continue;
+
+ /*
+ * p_index is a hint that this is a pagetable
+ */
+ pp = page_numtopp_nolock(pfn);
+ if (pp && pp->p_index) {
+ pp->p_index = 0;
+ continue;
+ }
+ (void) xen_kpm_page(pfn, PT_VALID | PT_WRITABLE);
+ }
+}
+#endif
+
/*
* Routine to pre-allocate data structures for hat_kern_setup(). It computes
* how many pagetables it needs by walking the boot loader's page tables.
@@ -263,11 +322,13 @@ hat_kern_alloc(
level_t lpagel = mmu.max_page_level;
uint64_t paddr;
int64_t psize;
-
+ int nwindows;
if (kpm_size > 0) {
/*
- * Create the kpm page tables.
+ * Create the kpm page tables. When running on the
+ * hypervisor these are made read/only at first.
+ * Later we'll add write permission where possible.
*/
for (pmem = phys_install; pmem; pmem = pmem->next) {
paddr = pmem->address;
@@ -278,20 +339,45 @@ hat_kern_alloc(
l = lpagel;
else
l = 0;
+#if defined(__xpv)
+ /*
+ * Create read/only mappings to avoid
+ * conflicting with pagetable usage
+ */
+ xen_kpm_create(paddr, l);
+#else
kbm_map((uintptr_t)kpm_vbase + paddr, paddr,
l, 1);
+#endif
paddr += LEVEL_SIZE(l);
psize -= LEVEL_SIZE(l);
}
}
- } else {
+ }
+
+ /*
+ * If this machine doesn't have a kpm segment, we need to allocate
+ * a small number of 'windows' which can be used to map pagetables.
+ */
+ nwindows = (kpm_size == 0) ? 2 * NCPU : 0;
+
+#if defined(__xpv)
+ /*
+ * On a hypervisor, these windows are also used by the xpv_panic
+ * code, where we need one window for each level of the pagetable
+ * hierarchy.
+ */
+ nwindows = MAX(nwindows, mmu.max_level);
+#endif
+
+ if (nwindows != 0) {
/*
* Create the page windows and 1 page of VA in
* which we map the PTEs of those windows.
*/
- mmu.pwin_base = vmem_xalloc(heap_arena, 2 * NCPU * MMU_PAGESIZE,
+ mmu.pwin_base = vmem_xalloc(heap_arena, nwindows * MMU_PAGESIZE,
LEVEL_SIZE(1), 0, 0, NULL, NULL, VM_SLEEP);
- ASSERT(NCPU * 2 <= MMU_PAGESIZE / mmu.pte_size);
+ ASSERT(nwindows <= MMU_PAGESIZE / mmu.pte_size);
mmu.pwin_pte_va = vmem_xalloc(heap_arena, MMU_PAGESIZE,
MMU_PAGESIZE, 0, 0, NULL, NULL, VM_SLEEP);
@@ -303,7 +389,12 @@ hat_kern_alloc(
ASSERT(paddr != 0);
ASSERT((paddr & MMU_PAGEOFFSET) == 0);
mmu.pwin_pte_pa = paddr;
+#ifdef __xpv
+ (void) find_pte((uintptr_t)mmu.pwin_pte_va, NULL, 0, 0);
+ kbm_read_only((uintptr_t)mmu.pwin_pte_va, mmu.pwin_pte_pa);
+#else
kbm_map((uintptr_t)mmu.pwin_pte_va, mmu.pwin_pte_pa, 0, 1);
+#endif
}
/*
@@ -391,13 +482,27 @@ hat_kern_setup(void)
/*
* Attach htables to the existing pagetables
*/
+ /* BEGIN CSTYLED */
htable_attach(kas.a_hat, 0, mmu.max_level, NULL,
+#ifdef __xpv
+ mmu_btop(xen_info->pt_base - ONE_GIG));
+#else
mmu_btop(getcr3()));
+#endif
+ /* END CSTYLED */
-#if defined(__i386)
+#if defined(__i386) && !defined(__xpv)
CPU->cpu_tss->tss_cr3 = dftss0.tss_cr3 = getcr3();
#endif /* __i386 */
+#if defined(__xpv) && defined(__amd64)
+ /*
+ * Try to make the kpm mappings r/w. Failures here are OK, as
+ * it's probably just a pagetable
+ */
+ xen_kpm_finish_init();
+#endif
+
/*
* The kernel HAT is now officially open for business.
*/