diff options
Diffstat (limited to 'arch/x86/xen/enlighten.c')
-rw-r--r-- | arch/x86/xen/enlighten.c | 99 |
1 files changed, 84 insertions, 15 deletions
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index c13698faae54..48f1a7eca8b9 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c | |||
@@ -46,7 +46,6 @@ | |||
46 | #include <asm/pgtable.h> | 46 | #include <asm/pgtable.h> |
47 | #include <asm/tlbflush.h> | 47 | #include <asm/tlbflush.h> |
48 | #include <asm/reboot.h> | 48 | #include <asm/reboot.h> |
49 | #include <asm/pgalloc.h> | ||
50 | 49 | ||
51 | #include "xen-ops.h" | 50 | #include "xen-ops.h" |
52 | #include "mmu.h" | 51 | #include "mmu.h" |
@@ -711,29 +710,57 @@ static void set_current_cr3(void *v) | |||
711 | x86_write_percpu(xen_current_cr3, (unsigned long)v); | 710 | x86_write_percpu(xen_current_cr3, (unsigned long)v); |
712 | } | 711 | } |
713 | 712 | ||
714 | static void xen_write_cr3(unsigned long cr3) | 713 | static void __xen_write_cr3(bool kernel, unsigned long cr3) |
715 | { | 714 | { |
716 | struct mmuext_op *op; | 715 | struct mmuext_op *op; |
717 | struct multicall_space mcs; | 716 | struct multicall_space mcs; |
718 | unsigned long mfn = pfn_to_mfn(PFN_DOWN(cr3)); | 717 | unsigned long mfn; |
719 | 718 | ||
720 | BUG_ON(preemptible()); | 719 | if (cr3) |
720 | mfn = pfn_to_mfn(PFN_DOWN(cr3)); | ||
721 | else | ||
722 | mfn = 0; | ||
721 | 723 | ||
722 | mcs = xen_mc_entry(sizeof(*op)); /* disables interrupts */ | 724 | WARN_ON(mfn == 0 && kernel); |
723 | 725 | ||
724 | /* Update while interrupts are disabled, so its atomic with | 726 | mcs = __xen_mc_entry(sizeof(*op)); |
725 | respect to ipis */ | ||
726 | x86_write_percpu(xen_cr3, cr3); | ||
727 | 727 | ||
728 | op = mcs.args; | 728 | op = mcs.args; |
729 | op->cmd = MMUEXT_NEW_BASEPTR; | 729 | op->cmd = kernel ? MMUEXT_NEW_BASEPTR : MMUEXT_NEW_USER_BASEPTR; |
730 | op->arg1.mfn = mfn; | 730 | op->arg1.mfn = mfn; |
731 | 731 | ||
732 | MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); | 732 | MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); |
733 | 733 | ||
734 | /* Update xen_update_cr3 once the batch has actually | 734 | if (kernel) { |
735 | been submitted. */ | 735 | x86_write_percpu(xen_cr3, cr3); |
736 | xen_mc_callback(set_current_cr3, (void *)cr3); | 736 | |
737 | /* Update xen_current_cr3 once the batch has actually | ||
738 | been submitted. */ | ||
739 | xen_mc_callback(set_current_cr3, (void *)cr3); | ||
740 | } | ||
741 | } | ||
742 | |||
743 | static void xen_write_cr3(unsigned long cr3) | ||
744 | { | ||
745 | BUG_ON(preemptible()); | ||
746 | |||
747 | xen_mc_batch(); /* disables interrupts */ | ||
748 | |||
749 | /* Update while interrupts are disabled, so its atomic with | ||
750 | respect to ipis */ | ||
751 | x86_write_percpu(xen_cr3, cr3); | ||
752 | |||
753 | __xen_write_cr3(true, cr3); | ||
754 | |||
755 | #ifdef CONFIG_X86_64 | ||
756 | { | ||
757 | pgd_t *user_pgd = xen_get_user_pgd(__va(cr3)); | ||
758 | if (user_pgd) | ||
759 | __xen_write_cr3(false, __pa(user_pgd)); | ||
760 | else | ||
761 | __xen_write_cr3(false, 0); | ||
762 | } | ||
763 | #endif | ||
737 | 764 | ||
738 | xen_mc_issue(PARAVIRT_LAZY_CPU); /* interrupts restored */ | 765 | xen_mc_issue(PARAVIRT_LAZY_CPU); /* interrupts restored */ |
739 | } | 766 | } |
@@ -794,6 +821,40 @@ static void xen_alloc_pmd(struct mm_struct *mm, u32 pfn) | |||
794 | xen_alloc_ptpage(mm, pfn, PT_PMD); | 821 | xen_alloc_ptpage(mm, pfn, PT_PMD); |
795 | } | 822 | } |
796 | 823 | ||
824 | static int xen_pgd_alloc(struct mm_struct *mm) | ||
825 | { | ||
826 | pgd_t *pgd = mm->pgd; | ||
827 | int ret = 0; | ||
828 | |||
829 | BUG_ON(PagePinned(virt_to_page(pgd))); | ||
830 | |||
831 | #ifdef CONFIG_X86_64 | ||
832 | { | ||
833 | struct page *page = virt_to_page(pgd); | ||
834 | |||
835 | BUG_ON(page->private != 0); | ||
836 | |||
837 | page->private = __get_free_page(GFP_KERNEL | __GFP_ZERO); | ||
838 | if (page->private == 0) | ||
839 | ret = -ENOMEM; | ||
840 | |||
841 | BUG_ON(PagePinned(virt_to_page(xen_get_user_pgd(pgd)))); | ||
842 | } | ||
843 | #endif | ||
844 | |||
845 | return ret; | ||
846 | } | ||
847 | |||
848 | static void xen_pgd_free(struct mm_struct *mm, pgd_t *pgd) | ||
849 | { | ||
850 | #ifdef CONFIG_X86_64 | ||
851 | pgd_t *user_pgd = xen_get_user_pgd(pgd); | ||
852 | |||
853 | if (user_pgd) | ||
854 | free_page((unsigned long)user_pgd); | ||
855 | #endif | ||
856 | } | ||
857 | |||
797 | /* This should never happen until we're OK to use struct page */ | 858 | /* This should never happen until we're OK to use struct page */ |
798 | static void xen_release_ptpage(u32 pfn, unsigned level) | 859 | static void xen_release_ptpage(u32 pfn, unsigned level) |
799 | { | 860 | { |
@@ -1168,8 +1229,8 @@ static const struct pv_mmu_ops xen_mmu_ops __initdata = { | |||
1168 | .pte_update = paravirt_nop, | 1229 | .pte_update = paravirt_nop, |
1169 | .pte_update_defer = paravirt_nop, | 1230 | .pte_update_defer = paravirt_nop, |
1170 | 1231 | ||
1171 | .pgd_alloc = __paravirt_pgd_alloc, | 1232 | .pgd_alloc = xen_pgd_alloc, |
1172 | .pgd_free = paravirt_nop, | 1233 | .pgd_free = xen_pgd_free, |
1173 | 1234 | ||
1174 | .alloc_pte = xen_alloc_pte_init, | 1235 | .alloc_pte = xen_alloc_pte_init, |
1175 | .release_pte = xen_release_pte_init, | 1236 | .release_pte = xen_release_pte_init, |
@@ -1480,7 +1541,15 @@ static __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pf | |||
1480 | 1541 | ||
1481 | /* Switch over */ | 1542 | /* Switch over */ |
1482 | pgd = init_level4_pgt; | 1543 | pgd = init_level4_pgt; |
1483 | xen_write_cr3(__pa(pgd)); | 1544 | |
1545 | /* | ||
1546 | * At this stage there can be no user pgd, and no page | ||
1547 | * structure to attach it to, so make sure we just set kernel | ||
1548 | * pgd. | ||
1549 | */ | ||
1550 | xen_mc_batch(); | ||
1551 | __xen_write_cr3(true, __pa(pgd)); | ||
1552 | xen_mc_issue(PARAVIRT_LAZY_CPU); | ||
1484 | 1553 | ||
1485 | reserve_early(__pa(xen_start_info->pt_base), | 1554 | reserve_early(__pa(xen_start_info->pt_base), |
1486 | __pa(xen_start_info->pt_base + | 1555 | __pa(xen_start_info->pt_base + |