diff options
Diffstat (limited to 'drivers/lguest/page_tables.c')
| -rw-r--r-- | drivers/lguest/page_tables.c | 72 |
1 files changed, 70 insertions, 2 deletions
diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 81d0c6053447..576a8318221c 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/percpu.h> | 14 | #include <linux/percpu.h> |
| 15 | #include <asm/tlbflush.h> | 15 | #include <asm/tlbflush.h> |
| 16 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
| 17 | #include <asm/bootparam.h> | ||
| 17 | #include "lg.h" | 18 | #include "lg.h" |
| 18 | 19 | ||
| 19 | /*M:008 We hold reference to pages, which prevents them from being swapped. | 20 | /*M:008 We hold reference to pages, which prevents them from being swapped. |
| @@ -581,15 +582,82 @@ void guest_set_pmd(struct lguest *lg, unsigned long gpgdir, u32 idx) | |||
| 581 | release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx); | 582 | release_pgd(lg, lg->pgdirs[pgdir].pgdir + idx); |
| 582 | } | 583 | } |
| 583 | 584 | ||
| 585 | /* Once we know how much memory we have we can construct simple identity | ||
| 586 | * (which set virtual == physical) and linear mappings | ||
| 587 | * which will get the Guest far enough into the boot to create its own. | ||
| 588 | * | ||
| 589 | * We lay them out of the way, just below the initrd (which is why we need to | ||
| 590 | * know its size here). */ | ||
| 591 | static unsigned long setup_pagetables(struct lguest *lg, | ||
| 592 | unsigned long mem, | ||
| 593 | unsigned long initrd_size) | ||
| 594 | { | ||
| 595 | pgd_t __user *pgdir; | ||
| 596 | pte_t __user *linear; | ||
| 597 | unsigned int mapped_pages, i, linear_pages, phys_linear; | ||
| 598 | unsigned long mem_base = (unsigned long)lg->mem_base; | ||
| 599 | |||
| 600 | /* We have mapped_pages frames to map, so we need | ||
| 601 | * linear_pages page tables to map them. */ | ||
| 602 | mapped_pages = mem / PAGE_SIZE; | ||
| 603 | linear_pages = (mapped_pages + PTRS_PER_PTE - 1) / PTRS_PER_PTE; | ||
| 604 | |||
| 605 | /* We put the toplevel page directory page at the top of memory. */ | ||
| 606 | pgdir = (pgd_t *)(mem + mem_base - initrd_size - PAGE_SIZE); | ||
| 607 | |||
| 608 | /* Now we use the next linear_pages pages as pte pages */ | ||
| 609 | linear = (void *)pgdir - linear_pages * PAGE_SIZE; | ||
| 610 | |||
| 611 | /* Linear mapping is easy: put every page's address into the | ||
| 612 | * mapping in order. */ | ||
| 613 | for (i = 0; i < mapped_pages; i++) { | ||
| 614 | pte_t pte; | ||
| 615 | pte = pfn_pte(i, __pgprot(_PAGE_PRESENT|_PAGE_RW|_PAGE_USER)); | ||
| 616 | if (copy_to_user(&linear[i], &pte, sizeof(pte)) != 0) | ||
| 617 | return -EFAULT; | ||
| 618 | } | ||
| 619 | |||
| 620 | /* The top level points to the linear page table pages above. | ||
| 621 | * We setup the identity and linear mappings here. */ | ||
| 622 | phys_linear = (unsigned long)linear - mem_base; | ||
| 623 | for (i = 0; i < mapped_pages; i += PTRS_PER_PTE) { | ||
| 624 | pgd_t pgd; | ||
| 625 | pgd = __pgd((phys_linear + i * sizeof(pte_t)) | | ||
| 626 | (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER)); | ||
| 627 | |||
| 628 | if (copy_to_user(&pgdir[i / PTRS_PER_PTE], &pgd, sizeof(pgd)) | ||
| 629 | || copy_to_user(&pgdir[pgd_index(PAGE_OFFSET) | ||
| 630 | + i / PTRS_PER_PTE], | ||
| 631 | &pgd, sizeof(pgd))) | ||
| 632 | return -EFAULT; | ||
| 633 | } | ||
| 634 | |||
| 635 | /* We return the top level (guest-physical) address: remember where | ||
| 636 | * this is. */ | ||
| 637 | return (unsigned long)pgdir - mem_base; | ||
| 638 | } | ||
| 639 | |||
| 584 | /*H:500 (vii) Setting up the page tables initially. | 640 | /*H:500 (vii) Setting up the page tables initially. |
| 585 | * | 641 | * |
| 586 | * When a Guest is first created, the Launcher tells us where the toplevel of | 642 | * When a Guest is first created, the Launcher tells us where the toplevel of |
| 587 | * its first page table is. We set some things up here: */ | 643 | * its first page table is. We set some things up here: */ |
| 588 | int init_guest_pagetable(struct lguest *lg, unsigned long pgtable) | 644 | int init_guest_pagetable(struct lguest *lg) |
| 589 | { | 645 | { |
| 646 | u64 mem; | ||
| 647 | u32 initrd_size; | ||
| 648 | struct boot_params __user *boot = (struct boot_params *)lg->mem_base; | ||
| 649 | |||
| 650 | /* Get the Guest memory size and the ramdisk size from the boot header | ||
| 651 | * located at lg->mem_base (Guest address 0). */ | ||
| 652 | if (copy_from_user(&mem, &boot->e820_map[0].size, sizeof(mem)) | ||
| 653 | || get_user(initrd_size, &boot->hdr.ramdisk_size)) | ||
| 654 | return -EFAULT; | ||
| 655 | |||
| 590 | /* We start on the first shadow page table, and give it a blank PGD | 656 | /* We start on the first shadow page table, and give it a blank PGD |
| 591 | * page. */ | 657 | * page. */ |
| 592 | lg->pgdirs[0].gpgdir = pgtable; | 658 | lg->pgdirs[0].gpgdir = setup_pagetables(lg, mem, initrd_size); |
| 659 | if (IS_ERR_VALUE(lg->pgdirs[0].gpgdir)) | ||
| 660 | return lg->pgdirs[0].gpgdir; | ||
| 593 | lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL); | 661 | lg->pgdirs[0].pgdir = (pgd_t *)get_zeroed_page(GFP_KERNEL); |
| 594 | if (!lg->pgdirs[0].pgdir) | 662 | if (!lg->pgdirs[0].pgdir) |
| 595 | return -ENOMEM; | 663 | return -ENOMEM; |
