diff options
Diffstat (limited to 'arch/x86/lguest/boot.c')
-rw-r--r-- | arch/x86/lguest/boot.c | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index d12f554e5f6a..7bc65f0f62c4 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c | |||
@@ -167,6 +167,7 @@ static void lazy_hcall3(unsigned long call, | |||
167 | async_hcall(call, arg1, arg2, arg3, 0); | 167 | async_hcall(call, arg1, arg2, arg3, 0); |
168 | } | 168 | } |
169 | 169 | ||
170 | #ifdef CONFIG_X86_PAE | ||
170 | static void lazy_hcall4(unsigned long call, | 171 | static void lazy_hcall4(unsigned long call, |
171 | unsigned long arg1, | 172 | unsigned long arg1, |
172 | unsigned long arg2, | 173 | unsigned long arg2, |
@@ -178,6 +179,7 @@ static void lazy_hcall4(unsigned long call, | |||
178 | else | 179 | else |
179 | async_hcall(call, arg1, arg2, arg3, arg4); | 180 | async_hcall(call, arg1, arg2, arg3, arg4); |
180 | } | 181 | } |
182 | #endif | ||
181 | 183 | ||
182 | /* When lazy mode is turned off reset the per-cpu lazy mode variable and then | 184 | /* When lazy mode is turned off reset the per-cpu lazy mode variable and then |
183 | * issue the do-nothing hypercall to flush any stored calls. */ | 185 | * issue the do-nothing hypercall to flush any stored calls. */ |
@@ -380,8 +382,8 @@ static void lguest_cpuid(unsigned int *ax, unsigned int *bx, | |||
380 | case 1: /* Basic feature request. */ | 382 | case 1: /* Basic feature request. */ |
381 | /* We only allow kernel to see SSE3, CMPXCHG16B and SSSE3 */ | 383 | /* We only allow kernel to see SSE3, CMPXCHG16B and SSSE3 */ |
382 | *cx &= 0x00002201; | 384 | *cx &= 0x00002201; |
383 | /* SSE, SSE2, FXSR, MMX, CMOV, CMPXCHG8B, TSC, FPU. */ | 385 | /* SSE, SSE2, FXSR, MMX, CMOV, CMPXCHG8B, TSC, FPU, PAE. */ |
384 | *dx &= 0x07808111; | 386 | *dx &= 0x07808151; |
385 | /* The Host can do a nice optimization if it knows that the | 387 | /* The Host can do a nice optimization if it knows that the |
386 | * kernel mappings (addresses above 0xC0000000 or whatever | 388 | * kernel mappings (addresses above 0xC0000000 or whatever |
387 | * PAGE_OFFSET is set to) haven't changed. But Linux calls | 389 | * PAGE_OFFSET is set to) haven't changed. But Linux calls |
@@ -400,6 +402,11 @@ static void lguest_cpuid(unsigned int *ax, unsigned int *bx, | |||
400 | if (*ax > 0x80000008) | 402 | if (*ax > 0x80000008) |
401 | *ax = 0x80000008; | 403 | *ax = 0x80000008; |
402 | break; | 404 | break; |
405 | case 0x80000001: | ||
406 | /* Here we should fix nx cap depending on host. */ | ||
407 | /* For this version of PAE, we just clear NX bit. */ | ||
408 | *dx &= ~(1 << 20); | ||
409 | break; | ||
403 | } | 410 | } |
404 | } | 411 | } |
405 | 412 | ||
@@ -533,7 +540,12 @@ static void lguest_write_cr4(unsigned long val) | |||
533 | static void lguest_pte_update(struct mm_struct *mm, unsigned long addr, | 540 | static void lguest_pte_update(struct mm_struct *mm, unsigned long addr, |
534 | pte_t *ptep) | 541 | pte_t *ptep) |
535 | { | 542 | { |
543 | #ifdef CONFIG_X86_PAE | ||
544 | lazy_hcall4(LHCALL_SET_PTE, __pa(mm->pgd), addr, | ||
545 | ptep->pte_low, ptep->pte_high); | ||
546 | #else | ||
536 | lazy_hcall3(LHCALL_SET_PTE, __pa(mm->pgd), addr, ptep->pte_low); | 547 | lazy_hcall3(LHCALL_SET_PTE, __pa(mm->pgd), addr, ptep->pte_low); |
548 | #endif | ||
537 | } | 549 | } |
538 | 550 | ||
539 | static void lguest_set_pte_at(struct mm_struct *mm, unsigned long addr, | 551 | static void lguest_set_pte_at(struct mm_struct *mm, unsigned long addr, |
@@ -543,15 +555,37 @@ static void lguest_set_pte_at(struct mm_struct *mm, unsigned long addr, | |||
543 | lguest_pte_update(mm, addr, ptep); | 555 | lguest_pte_update(mm, addr, ptep); |
544 | } | 556 | } |
545 | 557 | ||
546 | /* The Guest calls this to set a top-level entry. Again, we set the entry then | 558 | /* The Guest calls lguest_set_pud to set a top-level entry and lguest_set_pmd |
547 | * tell the Host which top-level page we changed, and the index of the entry we | 559 | * to set a middle-level entry when PAE is activated. |
548 | * changed. */ | 560 | * Again, we set the entry then tell the Host which page we changed, |
561 | * and the index of the entry we changed. */ | ||
562 | #ifdef CONFIG_X86_PAE | ||
563 | static void lguest_set_pud(pud_t *pudp, pud_t pudval) | ||
564 | { | ||
565 | native_set_pud(pudp, pudval); | ||
566 | |||
567 | /* 32 bytes aligned pdpt address and the index. */ | ||
568 | lazy_hcall2(LHCALL_SET_PGD, __pa(pudp) & 0xFFFFFFE0, | ||
569 | (__pa(pudp) & 0x1F) / sizeof(pud_t)); | ||
570 | } | ||
571 | |||
572 | static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval) | ||
573 | { | ||
574 | native_set_pmd(pmdp, pmdval); | ||
575 | lazy_hcall2(LHCALL_SET_PMD, __pa(pmdp) & PAGE_MASK, | ||
576 | (__pa(pmdp) & (PAGE_SIZE - 1)) / sizeof(pmd_t)); | ||
577 | } | ||
578 | #else | ||
579 | |||
580 | /* The Guest calls lguest_set_pmd to set a top-level entry when PAE is not | ||
581 | * activated. */ | ||
549 | static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval) | 582 | static void lguest_set_pmd(pmd_t *pmdp, pmd_t pmdval) |
550 | { | 583 | { |
551 | native_set_pmd(pmdp, pmdval); | 584 | native_set_pmd(pmdp, pmdval); |
552 | lazy_hcall2(LHCALL_SET_PGD, __pa(pmdp) & PAGE_MASK, | 585 | lazy_hcall2(LHCALL_SET_PGD, __pa(pmdp) & PAGE_MASK, |
553 | (__pa(pmdp) & (PAGE_SIZE - 1)) / sizeof(pmd_t)); | 586 | (__pa(pmdp) & (PAGE_SIZE - 1)) / sizeof(pmd_t)); |
554 | } | 587 | } |
588 | #endif | ||
555 | 589 | ||
556 | /* There are a couple of legacy places where the kernel sets a PTE, but we | 590 | /* There are a couple of legacy places where the kernel sets a PTE, but we |
557 | * don't know the top level any more. This is useless for us, since we don't | 591 | * don't know the top level any more. This is useless for us, since we don't |
@@ -569,6 +603,26 @@ static void lguest_set_pte(pte_t *ptep, pte_t pteval) | |||
569 | lazy_hcall1(LHCALL_FLUSH_TLB, 1); | 603 | lazy_hcall1(LHCALL_FLUSH_TLB, 1); |
570 | } | 604 | } |
571 | 605 | ||
606 | #ifdef CONFIG_X86_PAE | ||
607 | static void lguest_set_pte_atomic(pte_t *ptep, pte_t pte) | ||
608 | { | ||
609 | native_set_pte_atomic(ptep, pte); | ||
610 | if (cr3_changed) | ||
611 | lazy_hcall1(LHCALL_FLUSH_TLB, 1); | ||
612 | } | ||
613 | |||
614 | void lguest_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) | ||
615 | { | ||
616 | native_pte_clear(mm, addr, ptep); | ||
617 | lguest_pte_update(mm, addr, ptep); | ||
618 | } | ||
619 | |||
620 | void lguest_pmd_clear(pmd_t *pmdp) | ||
621 | { | ||
622 | lguest_set_pmd(pmdp, __pmd(0)); | ||
623 | } | ||
624 | #endif | ||
625 | |||
572 | /* Unfortunately for Lguest, the pv_mmu_ops for page tables were based on | 626 | /* Unfortunately for Lguest, the pv_mmu_ops for page tables were based on |
573 | * native page table operations. On native hardware you can set a new page | 627 | * native page table operations. On native hardware you can set a new page |
574 | * table entry whenever you want, but if you want to remove one you have to do | 628 | * table entry whenever you want, but if you want to remove one you have to do |
@@ -1035,6 +1089,7 @@ __init void lguest_init(void) | |||
1035 | pv_info.name = "lguest"; | 1089 | pv_info.name = "lguest"; |
1036 | pv_info.paravirt_enabled = 1; | 1090 | pv_info.paravirt_enabled = 1; |
1037 | pv_info.kernel_rpl = 1; | 1091 | pv_info.kernel_rpl = 1; |
1092 | pv_info.shared_kernel_pmd = 1; | ||
1038 | 1093 | ||
1039 | /* We set up all the lguest overrides for sensitive operations. These | 1094 | /* We set up all the lguest overrides for sensitive operations. These |
1040 | * are detailed with the operations themselves. */ | 1095 | * are detailed with the operations themselves. */ |
@@ -1080,6 +1135,12 @@ __init void lguest_init(void) | |||
1080 | pv_mmu_ops.set_pte = lguest_set_pte; | 1135 | pv_mmu_ops.set_pte = lguest_set_pte; |
1081 | pv_mmu_ops.set_pte_at = lguest_set_pte_at; | 1136 | pv_mmu_ops.set_pte_at = lguest_set_pte_at; |
1082 | pv_mmu_ops.set_pmd = lguest_set_pmd; | 1137 | pv_mmu_ops.set_pmd = lguest_set_pmd; |
1138 | #ifdef CONFIG_X86_PAE | ||
1139 | pv_mmu_ops.set_pte_atomic = lguest_set_pte_atomic; | ||
1140 | pv_mmu_ops.pte_clear = lguest_pte_clear; | ||
1141 | pv_mmu_ops.pmd_clear = lguest_pmd_clear; | ||
1142 | pv_mmu_ops.set_pud = lguest_set_pud; | ||
1143 | #endif | ||
1083 | pv_mmu_ops.read_cr2 = lguest_read_cr2; | 1144 | pv_mmu_ops.read_cr2 = lguest_read_cr2; |
1084 | pv_mmu_ops.read_cr3 = lguest_read_cr3; | 1145 | pv_mmu_ops.read_cr3 = lguest_read_cr3; |
1085 | pv_mmu_ops.lazy_mode.enter = paravirt_enter_lazy_mmu; | 1146 | pv_mmu_ops.lazy_mode.enter = paravirt_enter_lazy_mmu; |