-----Original Message-----
From: kvm-ppc-owner@xxxxxxxxxxxxxxx
[mailto:kvm-ppc-owner@xxxxxxxxxxxxxxx] On Behalf Of
ehrhardt@xxxxxxxxxxxxxxxxxx
Sent: Tuesday, August 19, 2008 6:37 PM
To: kvm-ppc@xxxxxxxxxxxxxxx
Cc: hollisb@xxxxxxxxxx; ehrhardt@xxxxxxxxxxxxxxxxxx
Subject: [PATCH 3/6] kvmppc: rewrite guest code - sprg0-3
From: Christian Ehrhardt <ehrhardt@xxxxxxxxxxxxxxxxxx>
This patch adds the functionality of rewriting guest code
using the magic page
mechanism. If a paravirtual guest memory (magic page) is
registered the host
rewrites trapping emulations instead of emulating them. This
only works with
instructions that can be rewritten with one instruction.
On registration of the paravirtual memory the values have to
be synced so that
the old emulated values can be found in the magic page now
and are ready to be
read by the guest.
The rewriting of guest code is guest visible (the guest
already agrees on this
by registering a magic page) and is driven by the program
interrupts usually
used to emulate these instructions in the hypervisor.
This patch replaces m[ft]spr SPRG[0-3] instructions with stw
(write) and lwz
(read) instructions.
Signed-off-by: Christian Ehrhardt <ehrhardt@xxxxxxxxxxxxxxxxxx>
---
[diffstat]
arch/powerpc/kvm/booke_guest.c | 17 ++++
arch/powerpc/kvm/emulate.c | 158
+++++++++++++++++++++++++++++++++++++++++
include/asm-powerpc/kvm_para.h | 27 ++++++-
3 files changed, 200 insertions(+), 2 deletions(-)
[diff]
diff --git a/arch/powerpc/kvm/booke_guest.c
b/arch/powerpc/kvm/booke_guest.c
--- a/arch/powerpc/kvm/booke_guest.c
+++ b/arch/powerpc/kvm/booke_guest.c
@@ -339,7 +339,7 @@
gfn_t gfn;
- if (vcpu->arch.pvmem && kvmppc_is_pvmem(vcpu, eaddr)) {
+ if (kvmppc_is_pvmem(vcpu, eaddr)) {
kvmppc_mmu_map(vcpu, eaddr,
vcpu->arch.pvmem_gpaddr >>
KVM_PPCPV_MAGIC_PAGE_SHIFT,
0, KVM_PPCPV_MAGIC_PAGE_FLAGS);
@@ -536,6 +536,13 @@
for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
regs->gpr[i] = vcpu->arch.gpr[i];
+ if (vcpu->arch.pvmem) {
+ regs->sprg0 = kvmppc_get_pvreg(vcpu,
KVM_PPCPV_OFFSET_SPRG0);
+ regs->sprg1 = kvmppc_get_pvreg(vcpu,
KVM_PPCPV_OFFSET_SPRG1);
+ regs->sprg2 = kvmppc_get_pvreg(vcpu,
KVM_PPCPV_OFFSET_SPRG2);
+ regs->sprg3 = kvmppc_get_pvreg(vcpu,
KVM_PPCPV_OFFSET_SPRG3);
+ }
+
return 0;
}
@@ -561,6 +568,14 @@
for (i = 0; i < ARRAY_SIZE(vcpu->arch.gpr); i++)
vcpu->arch.gpr[i] = regs->gpr[i];
+
+ if (vcpu->arch.pvmem) {
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG0,
regs->sprg0);
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG1,
regs->sprg1);
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG2,
regs->sprg2);
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG3,
regs->sprg3);
+ }
+
return 0;
}
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -29,6 +29,7 @@
#include <asm/dcr-regs.h>
#include <asm/time.h>
#include <asm/byteorder.h>
+#include <asm/system.h>
#include <asm/kvm_ppc.h>
#include "44x_tlb.h"
@@ -87,6 +88,37 @@
static inline unsigned int get_d(u32 inst)
{
return inst & 0xffff;
+}
+
+static inline void set_op(u32 op, u32 *inst)
+{
+ *inst |= ((op & 0x3f) << 26);
+ return;
+}
+
+static inline void set_ra(u32 ra, u32 *inst)
+{
+ *inst |= ((ra & 0x1f) << 16);
+ return;
+}
+
+static inline void set_rs(u32 rs, u32 *inst)
+{
+ *inst |= ((rs & 0x1f) << 21);
+ return;
+}
+
+static inline void set_rt(u32 rt, u32 *inst)
+{
+ *inst |= ((rt & 0x1f) << 21);
+ return;
+}
+
+static inline void set_d(s16 d, u32 *inst)
+{
+ /* mask in last 16 signed bits */
+ *inst |= ((u32)d & 0xFFFF);
+ return;
}
static int tlbe_is_host_safe(const struct kvm_vcpu *vcpu,
@@ -212,6 +244,8 @@
switch (vcpu->arch.gpr[0]) {
case KVM_HCALL_RESERVE_MAGICPAGE:
+ /* FIXME assert : we don't support this on smp guests */
+
vcpu->arch.pvmem_gvaddr = vcpu->arch.gpr[3];
vcpu->arch.pvmem_gpaddr = vcpu->arch.gpr[4];
down_read(¤t->mm->mmap_sem);
@@ -219,6 +253,18 @@
vcpu->arch.pvmem_gpaddr >>
KVM_PPCPV_MAGIC_PAGE_SHIFT);
up_read(¤t->mm->mmap_sem);
vcpu->arch.pvmem = kmap(pvmem_page);
+
+ /* on enablement of pvmem support we need to
sync all values
+ * into the pvmem area to be able so correcty
fulfill read
+ * acesses that might occur prior to writes */
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG0,
+ vcpu->arch.sprg0);
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG1,
+ vcpu->arch.sprg1);
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG2,
+ vcpu->arch.sprg2);
+ kvmppc_set_pvreg(vcpu, KVM_PPCPV_OFFSET_SPRG3,
+ vcpu->arch.sprg3);
break;
default:
printk(KERN_ERR "unknown hypercall %d\n",
vcpu->arch.gpr[0]);
@@ -232,6 +278,115 @@
return ret;
}
+int kvmppc_pvmem_rewrite_instruction(struct kvm_vcpu *vcpu)
+{
+ int err = 0;
+ u32 inst = vcpu->arch.last_inst;
+ int sprn;
+ int rw = -1;
+
+ int offset = -1;
+
+ switch (get_op(inst)) {
+ case 31:
+ switch (get_xop(inst)) {
+ case 339:
/* mfspr */
+ sprn = get_sprn(inst);
+ rw = KVM_PPCPV_PVMEM_READ;
+ switch (sprn) {
+ case SPRN_SPRG0:
+ offset = KVM_PPCPV_OFFSET_SPRG0;
+ break;
+ case SPRN_SPRG1:
+ offset = KVM_PPCPV_OFFSET_SPRG1;
+ break;
+ case SPRN_SPRG2:
+ offset = KVM_PPCPV_OFFSET_SPRG2;
+ break;
+ case SPRN_SPRG3:
+ offset = KVM_PPCPV_OFFSET_SPRG3;
+ break;
+ default:
+ err = -EFAULT;
+ }
+ break;
+ case 467:
/* mtspr */
+ sprn = get_sprn(inst);
+ rw = KVM_PPCPV_PVMEM_WRITE;
+ switch (sprn) {
+ case SPRN_SPRG0:
+ offset = KVM_PPCPV_OFFSET_SPRG0;
+ break;
+ case SPRN_SPRG1:
+ offset = KVM_PPCPV_OFFSET_SPRG1;
+ break;
+ case SPRN_SPRG2:
+ offset = KVM_PPCPV_OFFSET_SPRG2;
+ break;
+ case SPRN_SPRG3:
+ offset = KVM_PPCPV_OFFSET_SPRG3;
+ break;
+ default:
+ err = -EFAULT;
+ }
+ break;
+ default:
+ err = -EFAULT;
+ }
+ break;
+ default:
+ err = -EFAULT;
+ }
+
+ if (!err) {
+ u32 newinst = 0;
+ struct page *pcpage;
+ void *pcmem;
+ unsigned long pcaddr;
+ s16 displacement = 0;
+ gpa_t guest_pcaddr;
+ struct tlbe *gtlbe;
+
+ BUG_ON(offset == -1);
+ /* calculate displacement of gvaddr+offset from 0 */
+ displacement += (vcpu->arch.pvmem_gvaddr & 0xFFFF);
+ displacement += offset;
+
+ BUG_ON(rw == -1);
+ if (rw == KVM_PPCPV_PVMEM_READ) {
+ set_op(32, &newinst); /* lwz */
+ set_rt(get_rt(inst), &newinst); /* set
original rt */
+ } else {
+ set_op(36, &newinst); /* stw */
+ set_rs(get_rs(inst), &newinst); /* set
original rs */
+ }
+ set_ra(0, &newinst); /* ra = 0 -> base addr 0
(no reg needed) */
+ set_d(displacement, &newinst); /* set
displacement from 0 */
+
+ gtlbe = kvmppc_44x_itlb_search(vcpu, vcpu->arch.pc);
+ guest_pcaddr = tlb_xlate(gtlbe, vcpu->arch.pc);
+ if (!gtlbe)
+ return -EFAULT;