+ 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.
+ *
+ * When a Guest is first created, the Launcher tells us where the toplevel of
+ * its first page table is. We set some things up here:
+ */
+int init_guest_pagetable(struct lguest *lg)
+{
+ u64 mem;
+ u32 initrd_size;
+ struct boot_params __user *boot = (struct boot_params *)lg->mem_base;
+#ifdef CONFIG_X86_PAE
+ pgd_t *pgd;
+ pmd_t *pmd_table;
+#endif
+ /*
+ * Get the Guest memory size and the ramdisk size from the boot header
+ * located at lg->mem_base (Guest address 0).
+ */
+ if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem))
+ || get_user(initrd_size, &boot->hdr.ramdisk_size))
+ return -EFAULT;
+
+ /*
+ * We start on the first shadow page table, and give it a blank PGD
+ * page.
+ */
+ lg->pgdirs[0].gpgdir = setup_pagetables(lg, mem, initrd_size);
+ if (IS_ERR_VALUE(lg->pgdirs[0].gpgdir))
+ return lg->pgdirs[0].gpgdir;
+ lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL);
+ if (!lg->pgdirs[0].pgdir)
+ return -ENOMEM;
+
+#ifdef CONFIG_X86_PAE
+ /* For PAE, we also create the initial mid-level. */
+ pgd = lg->pgdirs[0].pgdir;
+ pmd_table = (pmd_t *) get_zeroed_page(GFP_KERNEL);
+ if (!pmd_table)