Merge branch 'drm-atmel-hlcdc-devel' of git://github.com/bbrezillon/linux-at91 into...
[cascardo/linux.git] / drivers / lguest / page_tables.c
index e8b55c3..e3abebc 100644 (file)
@@ -250,6 +250,16 @@ static void release_pte(pte_t pte)
 }
 /*:*/
 
+static bool gpte_in_iomem(struct lg_cpu *cpu, pte_t gpte)
+{
+       /* We don't handle large pages. */
+       if (pte_flags(gpte) & _PAGE_PSE)
+               return false;
+
+       return (pte_pfn(gpte) >= cpu->lg->pfn_limit
+               && pte_pfn(gpte) < cpu->lg->device_limit);
+}
+
 static bool check_gpte(struct lg_cpu *cpu, pte_t gpte)
 {
        if ((pte_flags(gpte) & _PAGE_PSE) ||
@@ -374,8 +384,14 @@ static pte_t *find_spte(struct lg_cpu *cpu, unsigned long vaddr, bool allocate,
  *
  * If we fixed up the fault (ie. we mapped the address), this routine returns
  * true.  Otherwise, it was a real fault and we need to tell the Guest.
+ *
+ * There's a corner case: they're trying to access memory between
+ * pfn_limit and device_limit, which is I/O memory.  In this case, we
+ * return false and set @iomem to the physical address, so the the
+ * Launcher can handle the instruction manually.
  */
-bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
+bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode,
+                unsigned long *iomem)
 {
        unsigned long gpte_ptr;
        pte_t gpte;
@@ -383,6 +399,8 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
        pmd_t gpmd;
        pgd_t gpgd;
 
+       *iomem = 0;
+
        /* We never demand page the Switcher, so trying is a mistake. */
        if (vaddr >= switcher_addr)
                return false;
@@ -459,6 +477,12 @@ bool demand_page(struct lg_cpu *cpu, unsigned long vaddr, int errcode)
        if ((errcode & 4) && !(pte_flags(gpte) & _PAGE_USER))
                return false;
 
+       /* If they're accessing io memory, we expect a fault. */
+       if (gpte_in_iomem(cpu, gpte)) {
+               *iomem = (pte_pfn(gpte) << PAGE_SHIFT) | (vaddr & ~PAGE_MASK);
+               return false;
+       }
+
        /*
         * Check that the Guest PTE flags are OK, and the page number is below
         * the pfn_limit (ie. not mapping the Launcher binary).
@@ -553,7 +577,9 @@ static bool page_writable(struct lg_cpu *cpu, unsigned long vaddr)
  */
 void pin_page(struct lg_cpu *cpu, unsigned long vaddr)
 {
-       if (!page_writable(cpu, vaddr) && !demand_page(cpu, vaddr, 2))
+       unsigned long iomem;
+
+       if (!page_writable(cpu, vaddr) && !demand_page(cpu, vaddr, 2, &iomem))
                kill_guest(cpu, "bad stack page %#lx", vaddr);
 }
 /*:*/
@@ -647,7 +673,7 @@ void guest_pagetable_flush_user(struct lg_cpu *cpu)
 /*:*/
 
 /* We walk down the guest page tables to get a guest-physical address */
-unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
+bool __guest_pa(struct lg_cpu *cpu, unsigned long vaddr, unsigned long *paddr)
 {
        pgd_t gpgd;
        pte_t gpte;
@@ -656,31 +682,47 @@ unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
 #endif
 
        /* Still not set up?  Just map 1:1. */
-       if (unlikely(cpu->linear_pages))
-               return vaddr;
+       if (unlikely(cpu->linear_pages)) {
+               *paddr = vaddr;
+               return true;
+       }
 
        /* First step: get the top-level Guest page table entry. */
        gpgd = lgread(cpu, gpgd_addr(cpu, vaddr), pgd_t);
        /* Toplevel not present?  We can't map it in. */
-       if (!(pgd_flags(gpgd) & _PAGE_PRESENT)) {
-               kill_guest(cpu, "Bad address %#lx", vaddr);
-               return -1UL;
-       }
+       if (!(pgd_flags(gpgd) & _PAGE_PRESENT))
+               goto fail;
 
 #ifdef CONFIG_X86_PAE
        gpmd = lgread(cpu, gpmd_addr(gpgd, vaddr), pmd_t);
-       if (!(pmd_flags(gpmd) & _PAGE_PRESENT)) {
-               kill_guest(cpu, "Bad address %#lx", vaddr);
-               return -1UL;
-       }
+       if (!(pmd_flags(gpmd) & _PAGE_PRESENT))
+               goto fail;
        gpte = lgread(cpu, gpte_addr(cpu, gpmd, vaddr), pte_t);
 #else
        gpte = lgread(cpu, gpte_addr(cpu, gpgd, vaddr), pte_t);
 #endif
        if (!(pte_flags(gpte) & _PAGE_PRESENT))
-               kill_guest(cpu, "Bad address %#lx", vaddr);
+               goto fail;
+
+       *paddr = pte_pfn(gpte) * PAGE_SIZE | (vaddr & ~PAGE_MASK);
+       return true;
+
+fail:
+       *paddr = -1UL;
+       return false;
+}
 
-       return pte_pfn(gpte) * PAGE_SIZE | (vaddr & ~PAGE_MASK);
+/*
+ * This is the version we normally use: kills the Guest if it uses a
+ * bad address
+ */
+unsigned long guest_pa(struct lg_cpu *cpu, unsigned long vaddr)
+{
+       unsigned long paddr;
+
+       if (!__guest_pa(cpu, vaddr, &paddr))
+               kill_guest(cpu, "Bad address %#lx", vaddr);
+       return paddr;
 }
 
 /*
@@ -912,7 +954,8 @@ static void __guest_set_pte(struct lg_cpu *cpu, int idx,
                         * now.  This shaves 10% off a copy-on-write
                         * micro-benchmark.
                         */
-                       if (pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED)) {
+                       if ((pte_flags(gpte) & (_PAGE_DIRTY | _PAGE_ACCESSED))
+                           && !gpte_in_iomem(cpu, gpte)) {
                                if (!check_gpte(cpu, gpte))
                                        return;
                                set_pte(spte,