+#ifdef CONFIG_X86_PAE
+/* For setting a mid-level, we just throw everything away. It's easy. */
+void guest_set_pmd(struct lguest *lg, unsigned long pmdp, u32 idx)
+{
+ guest_pagetable_clear_all(&lg->cpus[0]);
+}
+#endif
+
+/*H:505
+ * To get through boot, we construct simple identity page mappings (which
+ * set virtual == physical) and linear mappings which will get the Guest far
+ * enough into the boot to create its own. The linear mapping means we
+ * simplify the Guest boot, but it makes assumptions about their PAGE_OFFSET,
+ * as you'll see.
+ *
+ * We lay them out of the way, just below the initrd (which is why we need to
+ * know its size here).
+ */
+static unsigned long setup_pagetables(struct lguest *lg,
+ unsigned long mem,
+ unsigned long initrd_size)
+{
+ pgd_t __user *pgdir;
+ pte_t __user *linear;
+ unsigned long mem_base = (unsigned long)lg->mem_base;
+ unsigned int mapped_pages, i, linear_pages;
+#ifdef CONFIG_X86_PAE
+ pmd_t __user *pmds;
+ unsigned int j;
+ pgd_t pgd;
+ pmd_t pmd;
+#else
+ unsigned int phys_linear;
+#endif
+
+ /*
+ * We have mapped_pages frames to map, so we need linear_pages page
+ * tables to map them.
+ */
+ mapped_pages = mem / PAGE_SIZE;
+ linear_pages = (mapped_pages + PTRS_PER_PTE - 1) / PTRS_PER_PTE;
+
+ /* We put the toplevel page directory page at the top of memory. */
+ pgdir = (pgd_t *)(mem + mem_base - initrd_size - PAGE_SIZE);
+
+ /* Now we use the next linear_pages pages as pte pages */
+ linear = (void *)pgdir - linear_pages * PAGE_SIZE;
+
+#ifdef CONFIG_X86_PAE
+ /*
+ * And the single mid page goes below that. We only use one, but
+ * that's enough to map 1G, which definitely gets us through boot.
+ */
+ pmds = (void *)linear - PAGE_SIZE;
+#endif
+ /*
+ * Linear mapping is easy: put every page's address into the
+ * mapping in order.
+ */
+ for (i = 0; i < mapped_pages; i++) {
+ pte_t pte;
+ pte = pfn_pte(i, __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER));
+ if (copy_to_user(&linear[i], &pte, sizeof(pte)) != 0)
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_X86_PAE
+ /*
+ * Make the Guest PMD entries point to the corresponding place in the
+ * linear mapping (up to one page worth of PMD).
+ */
+ for (i = j = 0; i < mapped_pages && j < PTRS_PER_PMD;
+ i += PTRS_PER_PTE, j++) {
+ pmd = pfn_pmd(((unsigned long)&linear[i] - mem_base)/PAGE_SIZE,
+ __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
+
+ if (copy_to_user(&pmds[j], &pmd, sizeof(pmd)) != 0)
+ return -EFAULT;
+ }
+
+ /* One PGD entry, pointing to that PMD page. */
+ pgd = __pgd(((unsigned long)pmds - mem_base) | _PAGE_PRESENT);
+ /* Copy it in as the first PGD entry (ie. addresses 0-1G). */
+ if (copy_to_user(&pgdir[0], &pgd, sizeof(pgd)) != 0)
+ return -EFAULT;
+ /*
+ * And the other PGD entry to make the linear mapping at PAGE_OFFSET
+ */
+ if (copy_to_user(&pgdir[KERNEL_PGD_BOUNDARY], &pgd, sizeof(pgd)))
+ return -EFAULT;
+#else
+ /*
+ * The top level points to the linear page table pages above.
+ * We setup the identity and linear mappings here.
+ */
+ phys_linear = (unsigned long)linear - mem_base;
+ for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) {
+ pgd_t pgd;
+ /*
+ * Create a PGD entry which points to the right part of the
+ * linear PTE pages.
+ */
+ pgd = __pgd((phys_linear + i * sizeof(pte_t)) |
+ (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER));
+
+ /*
+ * Copy it into the PGD page at 0 and PAGE_OFFSET.
+ */
+ if (copy_to_user(&pgdir[i / PTRS_PER_PTE], &pgd, sizeof(pgd))
+ || copy_to_user(&pgdir[pgd_index(PAGE_OFFSET)
+ + i / PTRS_PER_PTE],
+ &pgd, sizeof(pgd)))
+ return -EFAULT;
+ }
+#endif
+
+ /*
+ * We return the top level (guest-physical) address: we remember where
+ * this is to write it into lguest_data when the Guest initializes.
+ */
+ return (unsigned long)pgdir - mem_base;
+}
+
+/*H:500
+ * (vii) Setting up the page tables initially.