> -----Original Message-----
> From: [EMAIL PROTECTED] 
> [mailto:[EMAIL PROTECTED] On Behalf Of 
> [EMAIL PROTECTED]
> Sent: Tuesday, August 19, 2008 6:37 PM
> To: [email protected]
> Cc: [EMAIL PROTECTED]; [EMAIL PROTECTED]
> Subject: [PATCH 3/6] kvmppc: rewrite guest code - sprg0-3
> 
> From: Christian Ehrhardt <[EMAIL PROTECTED]>
> 
> 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 <[EMAIL PROTECTED]>
> ---
> 
> [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(&current->mm->mmap_sem);
> @@ -219,6 +253,18 @@
>                       vcpu->arch.pvmem_gpaddr >> 
> KVM_PPCPV_MAGIC_PAGE_SHIFT);
>               up_read(&current->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;
> +
> +             down_read(&current->mm->mmap_sem);
> +             pcpage = gfn_to_page(vcpu->kvm,
> +                             guest_pcaddr >> 
> KVM_PPCPV_MAGIC_PAGE_SHIFT);
> +             up_read(&current->mm->mmap_sem);
> +             if (pcpage == bad_page)
> +                     return -EFAULT;
> +
> +             pcmem = kmap_atomic(pcpage, KM_USER0);
> +             if (!pcmem)
> +                     return -EFAULT;
> +             pcaddr = ((unsigned long)pcmem
> +                             | (vcpu->arch.pc & 
> KVM_PPCPV_MAGIC_PAGE_MASK));
> +             BUG_ON(inst != *((u32 *)pcaddr));
> +             create_instruction(pcaddr, newinst);
> +             kunmap_atomic(pcpage, KM_USER0);
> +     }
> +
> +     return err;
> +}
>  
>  /* XXX to do:
>   * lhax
> @@ -260,6 +415,9 @@
>       int dcrn;
>       enum emulation_result emulated = EMULATE_DONE;
>       int advance = 1;
> +
> +     if (kvmppc_has_pvmem(vcpu) && 
> !kvmppc_pvmem_rewrite_instruction(vcpu))
> +             return EMULATE_DONE;
>  
>       switch (get_op(inst)) {
>       case 0:
> diff --git a/include/asm-powerpc/kvm_para.h 
> b/include/asm-powerpc/kvm_para.h
> --- a/include/asm-powerpc/kvm_para.h
> +++ b/include/asm-powerpc/kvm_para.h
> @@ -34,7 +34,16 @@
>   */
>  #define KVM_PPCPV_MAGIC_PAGE_SIZE 4096
>  #define KVM_PPCPV_MAGIC_PAGE_SHIFT 12
> +#define KVM_PPCPV_MAGIC_PAGE_MASK 0xFFF
>  #define KVM_PPCPV_MAGIC_PAGE_FLAGS 0x3f
> +
> +#define KVM_PPCPV_PVMEM_READ 1
> +#define KVM_PPCPV_PVMEM_WRITE 2
> +
> +#define KVM_PPCPV_OFFSET_SPRG0       0x00
> +#define KVM_PPCPV_OFFSET_SPRG1       0x04
> +#define KVM_PPCPV_OFFSET_SPRG2       0x08
> +#define KVM_PPCPV_OFFSET_SPRG3       0x0C

Maintaining copies is painful.
Is there any method to use the sole storage?

For exampe, allocate a page beforehand, and initialize kvm_arch_vcpu on
the page.
After guest request para support, then map this page to guest.

>  
>  static inline int kvm_para_available(void)
>  {
> @@ -55,7 +64,23 @@
>  
>  static inline int kvmppc_has_pvmem(struct kvm_vcpu *vcpu)
>  {
> -     return vcpu->arch.pvmem;
> +     return !!vcpu->arch.pvmem;
> +}
> +
> +static inline u32 kvmppc_get_pvreg(struct kvm_vcpu *vcpu, int offset)
> +{
> +     u32 *pvreg;
> +
> +     pvreg = vcpu->arch.pvmem + offset;
> +     return *pvreg;
> +}
> +
> +static inline void kvmppc_set_pvreg(struct kvm_vcpu *vcpu, 
> int offset, u32 val)
> +{
> +     u32 *pvreg;
> +
> +     pvreg = vcpu->arch.pvmem + offset;
> +     *pvreg = val;
>  }
>  
>  #endif /* __KERNEL__ */
> --
> To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
> the body of a message to [EMAIL PROTECTED]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to