diff options
author | Paul Mackerras <paulus@samba.org> | 2013-09-20 00:52:47 -0400 |
---|---|---|
committer | Alexander Graf <agraf@suse.de> | 2013-10-17 08:45:04 -0400 |
commit | 5cd92a9521d35013fff904d3c805323027a33d73 (patch) | |
tree | 94037f1b656b8a029749061660a7575a8941dd5e /arch | |
parent | 03a9c90334d611c3006ac9569579f25f64812bc1 (diff) |
KVM: PPC: Book3S PR: Correct errors in H_ENTER implementation
The implementation of H_ENTER in PR KVM has some errors:
* With H_EXACT not set, if the HPTEG is full, we return H_PTEG_FULL
as the return value of kvmppc_h_pr_enter, but the caller is expecting
one of the EMULATE_* values. The H_PTEG_FULL needs to go in the
guest's R3 instead.
* With H_EXACT set, if the selected HPTE is already valid, the H_ENTER
call should return a H_PTEG_FULL error.
This fixes these errors and also makes it write only the selected HPTE,
not the whole group, since only the selected HPTE has been modified.
This also micro-optimizes the calculations involving pte_index and i.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/kvm/book3s_pr_papr.c | 19 |
1 files changed, 14 insertions, 5 deletions
diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c index da0e0bc268bd..38f189975fe1 100644 --- a/arch/powerpc/kvm/book3s_pr_papr.c +++ b/arch/powerpc/kvm/book3s_pr_papr.c | |||
@@ -21,6 +21,8 @@ | |||
21 | #include <asm/kvm_ppc.h> | 21 | #include <asm/kvm_ppc.h> |
22 | #include <asm/kvm_book3s.h> | 22 | #include <asm/kvm_book3s.h> |
23 | 23 | ||
24 | #define HPTE_SIZE 16 /* bytes per HPT entry */ | ||
25 | |||
24 | static unsigned long get_pteg_addr(struct kvm_vcpu *vcpu, long pte_index) | 26 | static unsigned long get_pteg_addr(struct kvm_vcpu *vcpu, long pte_index) |
25 | { | 27 | { |
26 | struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu); | 28 | struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu); |
@@ -40,32 +42,39 @@ static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu) | |||
40 | long pte_index = kvmppc_get_gpr(vcpu, 5); | 42 | long pte_index = kvmppc_get_gpr(vcpu, 5); |
41 | unsigned long pteg[2 * 8]; | 43 | unsigned long pteg[2 * 8]; |
42 | unsigned long pteg_addr, i, *hpte; | 44 | unsigned long pteg_addr, i, *hpte; |
45 | long int ret; | ||
43 | 46 | ||
47 | i = pte_index & 7; | ||
44 | pte_index &= ~7UL; | 48 | pte_index &= ~7UL; |
45 | pteg_addr = get_pteg_addr(vcpu, pte_index); | 49 | pteg_addr = get_pteg_addr(vcpu, pte_index); |
46 | 50 | ||
47 | copy_from_user(pteg, (void __user *)pteg_addr, sizeof(pteg)); | 51 | copy_from_user(pteg, (void __user *)pteg_addr, sizeof(pteg)); |
48 | hpte = pteg; | 52 | hpte = pteg; |
49 | 53 | ||
54 | ret = H_PTEG_FULL; | ||
50 | if (likely((flags & H_EXACT) == 0)) { | 55 | if (likely((flags & H_EXACT) == 0)) { |
51 | pte_index &= ~7UL; | ||
52 | for (i = 0; ; ++i) { | 56 | for (i = 0; ; ++i) { |
53 | if (i == 8) | 57 | if (i == 8) |
54 | return H_PTEG_FULL; | 58 | goto done; |
55 | if ((*hpte & HPTE_V_VALID) == 0) | 59 | if ((*hpte & HPTE_V_VALID) == 0) |
56 | break; | 60 | break; |
57 | hpte += 2; | 61 | hpte += 2; |
58 | } | 62 | } |
59 | } else { | 63 | } else { |
60 | i = kvmppc_get_gpr(vcpu, 5) & 7UL; | ||
61 | hpte += i * 2; | 64 | hpte += i * 2; |
65 | if (*hpte & HPTE_V_VALID) | ||
66 | goto done; | ||
62 | } | 67 | } |
63 | 68 | ||
64 | hpte[0] = kvmppc_get_gpr(vcpu, 6); | 69 | hpte[0] = kvmppc_get_gpr(vcpu, 6); |
65 | hpte[1] = kvmppc_get_gpr(vcpu, 7); | 70 | hpte[1] = kvmppc_get_gpr(vcpu, 7); |
66 | copy_to_user((void __user *)pteg_addr, pteg, sizeof(pteg)); | 71 | pteg_addr += i * HPTE_SIZE; |
67 | kvmppc_set_gpr(vcpu, 3, H_SUCCESS); | 72 | copy_to_user((void __user *)pteg_addr, hpte, HPTE_SIZE); |
68 | kvmppc_set_gpr(vcpu, 4, pte_index | i); | 73 | kvmppc_set_gpr(vcpu, 4, pte_index | i); |
74 | ret = H_SUCCESS; | ||
75 | |||
76 | done: | ||
77 | kvmppc_set_gpr(vcpu, 3, ret); | ||
69 | 78 | ||
70 | return EMULATE_DONE; | 79 | return EMULATE_DONE; |
71 | } | 80 | } |