Commit 9fb244a2 authored by Alexander Graf's avatar Alexander Graf Committed by Avi Kivity

KVM: PPC: Fix dcbz emulation

On most systems we need to emulate dcbz when running 32 bit guests. So
far we've been rather slack, not giving correct DSISR values to the guest.

This patch makes the emulation more accurate, introducing a difference
between "page not mapped" and "write protection fault". While at it, it
also speeds up dcbz emulation by an order of magnitude by using kmap.
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent a2b07664
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/highmem.h>
#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
...@@ -369,34 +370,29 @@ void kvmppc_set_pvr(struct kvm_vcpu *vcpu, u32 pvr) ...@@ -369,34 +370,29 @@ void kvmppc_set_pvr(struct kvm_vcpu *vcpu, u32 pvr)
*/ */
static void kvmppc_patch_dcbz(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte) static void kvmppc_patch_dcbz(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte)
{ {
bool touched = false; struct page *hpage;
hva_t hpage; u64 hpage_offset;
u32 *page; u32 *page;
int i; int i;
hpage = gfn_to_hva(vcpu->kvm, pte->raddr >> PAGE_SHIFT); hpage = gfn_to_page(vcpu->kvm, pte->raddr >> PAGE_SHIFT);
if (kvm_is_error_hva(hpage)) if (is_error_page(hpage))
return; return;
hpage |= pte->raddr & ~PAGE_MASK; hpage_offset = pte->raddr & ~PAGE_MASK;
hpage &= ~0xFFFULL; hpage_offset &= ~0xFFFULL;
hpage_offset /= 4;
page = vmalloc(HW_PAGE_SIZE); get_page(hpage);
page = kmap_atomic(hpage, KM_USER0);
if (copy_from_user(page, (void __user *)hpage, HW_PAGE_SIZE)) /* patch dcbz into reserved instruction, so we trap */
goto out; for (i=hpage_offset; i < hpage_offset + (HW_PAGE_SIZE / 4); i++)
if ((page[i] & 0xff0007ff) == INS_DCBZ)
for (i=0; i < HW_PAGE_SIZE / 4; i++) page[i] &= 0xfffffff7;
if ((page[i] & 0xff0007ff) == INS_DCBZ) {
page[i] &= 0xfffffff7; // reserved instruction, so we trap
touched = true;
}
if (touched) kunmap_atomic(page, KM_USER0);
copy_to_user((void __user *)hpage, page, HW_PAGE_SIZE); put_page(hpage);
out:
vfree(page);
} }
static int kvmppc_xlate(struct kvm_vcpu *vcpu, ulong eaddr, bool data, static int kvmppc_xlate(struct kvm_vcpu *vcpu, ulong eaddr, bool data,
...@@ -449,30 +445,21 @@ int kvmppc_st(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr, ...@@ -449,30 +445,21 @@ int kvmppc_st(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
bool data) bool data)
{ {
struct kvmppc_pte pte; struct kvmppc_pte pte;
hva_t hva = *eaddr;
vcpu->stat.st++; vcpu->stat.st++;
if (kvmppc_xlate(vcpu, *eaddr, data, &pte)) if (kvmppc_xlate(vcpu, *eaddr, data, &pte))
goto nopte; return -ENOENT;
*eaddr = pte.raddr; *eaddr = pte.raddr;
hva = kvmppc_pte_to_hva(vcpu, &pte, false); if (!pte.may_write)
if (kvm_is_error_hva(hva)) return -EPERM;
goto mmio;
if (copy_to_user((void __user *)hva, ptr, size)) { if (kvm_write_guest(vcpu->kvm, pte.raddr, ptr, size))
printk(KERN_INFO "kvmppc_st at 0x%lx failed\n", hva); return EMULATE_DO_MMIO;
goto mmio;
}
return EMULATE_DONE; return EMULATE_DONE;
nopte:
return -ENOENT;
mmio:
return EMULATE_DO_MMIO;
} }
int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr, int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
...@@ -787,6 +774,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -787,6 +774,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
* that no guest that needs the dcbz hack does NX. * that no guest that needs the dcbz hack does NX.
*/ */
kvmppc_mmu_pte_flush(vcpu, vcpu->arch.pc, ~0xFFFULL); kvmppc_mmu_pte_flush(vcpu, vcpu->arch.pc, ~0xFFFULL);
r = RESUME_GUEST;
} else { } else {
vcpu->arch.msr |= vcpu->arch.shadow_srr1 & 0x58000000; vcpu->arch.msr |= vcpu->arch.shadow_srr1 & 0x58000000;
kvmppc_book3s_queue_irqprio(vcpu, exit_nr); kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
......
...@@ -189,6 +189,8 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -189,6 +189,8 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
ulong ra = 0; ulong ra = 0;
ulong addr, vaddr; ulong addr, vaddr;
u32 zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; u32 zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
u32 dsisr;
int r;
if (get_ra(inst)) if (get_ra(inst))
ra = kvmppc_get_gpr(vcpu, get_ra(inst)); ra = kvmppc_get_gpr(vcpu, get_ra(inst));
...@@ -198,14 +200,23 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -198,14 +200,23 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
addr &= 0xffffffff; addr &= 0xffffffff;
vaddr = addr; vaddr = addr;
if (kvmppc_st(vcpu, &addr, 32, zeros, true)) { r = kvmppc_st(vcpu, &addr, 32, zeros, true);
if ((r == -ENOENT) || (r == -EPERM)) {
*advance = 0;
vcpu->arch.dear = vaddr; vcpu->arch.dear = vaddr;
vcpu->arch.fault_dear = vaddr; vcpu->arch.fault_dear = vaddr;
to_book3s(vcpu)->dsisr = DSISR_PROTFAULT |
DSISR_ISSTORE; dsisr = DSISR_ISSTORE;
if (r == -ENOENT)
dsisr |= DSISR_NOHPTE;
else if (r == -EPERM)
dsisr |= DSISR_PROTFAULT;
to_book3s(vcpu)->dsisr = dsisr;
vcpu->arch.fault_dsisr = dsisr;
kvmppc_book3s_queue_irqprio(vcpu, kvmppc_book3s_queue_irqprio(vcpu,
BOOK3S_INTERRUPT_DATA_STORAGE); BOOK3S_INTERRUPT_DATA_STORAGE);
kvmppc_mmu_pte_flush(vcpu, vaddr, ~0xFFFULL);
} }
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment