diff options
author | Alexander Graf <agraf@suse.de> | 2010-07-29 08:48:04 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2010-10-24 04:50:56 -0400 |
commit | 819a63dc792b0888edd3eda306a9e1e049dcbb1c (patch) | |
tree | 70599dcd83eb9892785cd9e7ca5d92534bedcdd7 /arch/powerpc/kernel/kvm.c | |
parent | 92234722ed631f472f1c4d79d35d8e5cf6910002 (diff) |
KVM: PPC: PV mtmsrd L=1
The PowerPC ISA has a special instruction for mtmsr that only changes the EE
and RI bits, namely the L=1 form.
Since that one is reasonably often occuring and simple to implement, let's
go with this first. Writing EE=0 is always just a store. Doing EE=1 also
requires us to check for pending interrupts and if necessary exit back to the
hypervisor.
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/powerpc/kernel/kvm.c')
-rw-r--r-- | arch/powerpc/kernel/kvm.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/kvm.c b/arch/powerpc/kernel/kvm.c index 239a70d750a..717ab0dded2 100644 --- a/arch/powerpc/kernel/kvm.c +++ b/arch/powerpc/kernel/kvm.c | |||
@@ -63,6 +63,7 @@ | |||
63 | #define KVM_INST_MTSPR_DSISR 0x7c1203a6 | 63 | #define KVM_INST_MTSPR_DSISR 0x7c1203a6 |
64 | 64 | ||
65 | #define KVM_INST_TLBSYNC 0x7c00046c | 65 | #define KVM_INST_TLBSYNC 0x7c00046c |
66 | #define KVM_INST_MTMSRD_L1 0x7c010164 | ||
66 | 67 | ||
67 | static bool kvm_patching_worked = true; | 68 | static bool kvm_patching_worked = true; |
68 | static char kvm_tmp[1024 * 1024]; | 69 | static char kvm_tmp[1024 * 1024]; |
@@ -138,6 +139,43 @@ static u32 *kvm_alloc(int len) | |||
138 | return p; | 139 | return p; |
139 | } | 140 | } |
140 | 141 | ||
142 | extern u32 kvm_emulate_mtmsrd_branch_offs; | ||
143 | extern u32 kvm_emulate_mtmsrd_reg_offs; | ||
144 | extern u32 kvm_emulate_mtmsrd_len; | ||
145 | extern u32 kvm_emulate_mtmsrd[]; | ||
146 | |||
147 | static void kvm_patch_ins_mtmsrd(u32 *inst, u32 rt) | ||
148 | { | ||
149 | u32 *p; | ||
150 | int distance_start; | ||
151 | int distance_end; | ||
152 | ulong next_inst; | ||
153 | |||
154 | p = kvm_alloc(kvm_emulate_mtmsrd_len * 4); | ||
155 | if (!p) | ||
156 | return; | ||
157 | |||
158 | /* Find out where we are and put everything there */ | ||
159 | distance_start = (ulong)p - (ulong)inst; | ||
160 | next_inst = ((ulong)inst + 4); | ||
161 | distance_end = next_inst - (ulong)&p[kvm_emulate_mtmsrd_branch_offs]; | ||
162 | |||
163 | /* Make sure we only write valid b instructions */ | ||
164 | if (distance_start > KVM_INST_B_MAX) { | ||
165 | kvm_patching_worked = false; | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | /* Modify the chunk to fit the invocation */ | ||
170 | memcpy(p, kvm_emulate_mtmsrd, kvm_emulate_mtmsrd_len * 4); | ||
171 | p[kvm_emulate_mtmsrd_branch_offs] |= distance_end & KVM_INST_B_MASK; | ||
172 | p[kvm_emulate_mtmsrd_reg_offs] |= rt; | ||
173 | flush_icache_range((ulong)p, (ulong)p + kvm_emulate_mtmsrd_len * 4); | ||
174 | |||
175 | /* Patch the invocation */ | ||
176 | kvm_patch_ins_b(inst, distance_start); | ||
177 | } | ||
178 | |||
141 | static void kvm_map_magic_page(void *data) | 179 | static void kvm_map_magic_page(void *data) |
142 | { | 180 | { |
143 | kvm_hypercall2(KVM_HC_PPC_MAP_MAGIC_PAGE, | 181 | kvm_hypercall2(KVM_HC_PPC_MAP_MAGIC_PAGE, |
@@ -211,6 +249,13 @@ static void kvm_check_ins(u32 *inst) | |||
211 | case KVM_INST_TLBSYNC: | 249 | case KVM_INST_TLBSYNC: |
212 | kvm_patch_ins_nop(inst); | 250 | kvm_patch_ins_nop(inst); |
213 | break; | 251 | break; |
252 | |||
253 | /* Rewrites */ | ||
254 | case KVM_INST_MTMSRD_L1: | ||
255 | /* We use r30 and r31 during the hook */ | ||
256 | if (get_rt(inst_rt) < 30) | ||
257 | kvm_patch_ins_mtmsrd(inst, inst_rt); | ||
258 | break; | ||
214 | } | 259 | } |
215 | 260 | ||
216 | switch (_inst) { | 261 | switch (_inst) { |