This adds the infrastructure for handling PAPR hcalls in the kernel,
either early in the guest exit path while we are still in real mode,
or later once the MMU has been turned back on and we are in the full
kernel context.  The advantage of handling hcalls in real mode if
possible is that we avoid two partition switches -- and this will
become more important when we support SMT4 guests, since a partition
switch means we have to pull all of the threads in the core out of
the guest.  The disadvantage is that we can only access the kernel
linear mapping, not anything vmalloced or ioremapped, since the MMU
is off.

This also adds code to handle the following hcalls in real mode:

H_ENTER       Add an HPTE to the hashed page table
H_REMOVE      Remove an HPTE from the hashed page table
H_READ        Read HPTEs from the hashed page table
H_PROTECT     Change the protection bits in an HPTE
H_BULK_REMOVE Remove up to 4 HPTEs from the hashed page table
H_SET_DABR    Set the data address breakpoint register

Plus code to handle the following hcalls in the kernel:

H_CEDE        Idle the vcpu until an interrupt or H_PROD hcall arrives
H_PROD        Wake up a ceded vcpu
H_REGISTER_VPA Register a virtual processor area (VPA)

Signed-off-by: Paul Mackerras <pau...@samba.org>
---
 arch/powerpc/include/asm/hvcall.h       |    5 +
 arch/powerpc/include/asm/kvm_host.h     |   11 +
 arch/powerpc/include/asm/kvm_ppc.h      |    1 +
 arch/powerpc/kernel/asm-offsets.c       |    2 +
 arch/powerpc/kvm/book3s_64_mmu_hv.c     |  342 +++++++++++++++++++++++++++++++
 arch/powerpc/kvm/book3s_hv.c            |  170 +++++++++++++++-
 arch/powerpc/kvm/book3s_hv_rmhandlers.S |  156 ++++++++++++++-
 arch/powerpc/kvm/powerpc.c              |    2 +-
 8 files changed, 685 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/include/asm/hvcall.h 
b/arch/powerpc/include/asm/hvcall.h
index fd8201d..1c324ff 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -29,6 +29,10 @@
 #define H_LONG_BUSY_ORDER_100_SEC      9905  /* Long busy, hint that 100sec \
                                                 is a good time to retry */
 #define H_LONG_BUSY_END_RANGE          9905  /* End of long busy range */
+
+/* Internal value used in book3s_hv kvm support; not returned to guests */
+#define H_TOO_HARD     9999
+
 #define H_HARDWARE     -1      /* Hardware error */
 #define H_FUNCTION     -2      /* Function not supported */
 #define H_PRIVILEGE    -3      /* Caller not privileged */
@@ -100,6 +104,7 @@
 #define H_PAGE_SET_ACTIVE      H_PAGE_STATE_CHANGE
 #define H_AVPN                 (1UL<<(63-32))  /* An avpn is provided as a 
sanity test */
 #define H_ANDCOND              (1UL<<(63-33))
+#define H_LOCAL                        (1UL<<(63-35))
 #define H_ICACHE_INVALIDATE    (1UL<<(63-40))  /* icbi, etc.  (ignored for IO 
pages) */
 #define H_ICACHE_SYNCHRONIZE   (1UL<<(63-41))  /* dcbst, icbi, etc (ignored 
for IO pages */
 #define H_COALESCE_CAND        (1UL<<(63-42))  /* page is a good candidate for 
coalescing */
diff --git a/arch/powerpc/include/asm/kvm_host.h 
b/arch/powerpc/include/asm/kvm_host.h
index 5645532..359db37 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -59,6 +59,10 @@ struct kvm;
 struct kvm_run;
 struct kvm_vcpu;
 
+struct lppaca;
+struct slb_shadow;
+struct dtl;
+
 struct kvm_vm_stat {
        u32 remote_tlb_flush;
 };
@@ -337,7 +341,14 @@ struct kvm_vcpu_arch {
        u64 dec_expires;
        unsigned long pending_exceptions;
        u16 last_cpu;
+       u8 ceded;
+       u8 prodded;
        u32 last_inst;
+
+       struct lppaca *vpa;
+       struct slb_shadow *slb_shadow;
+       struct dtl *dtl;
+       struct dtl *dtl_end;
        int trap;
        struct kvm_vcpu_arch_shared *shared;
        unsigned long magic_page_pa; /* phys addr to map the magic page to */
diff --git a/arch/powerpc/include/asm/kvm_ppc.h 
b/arch/powerpc/include/asm/kvm_ppc.h
index 2a10e82..d809e58 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -117,6 +117,7 @@ extern long kvmppc_prepare_vrma(struct kvm *kvm,
                                struct kvm_userspace_memory_region *mem);
 extern void kvmppc_map_vrma(struct kvm *kvm,
                            struct kvm_userspace_memory_region *mem);
+extern int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu);
 extern int kvmppc_core_init_vm(struct kvm *kvm);
 extern void kvmppc_core_destroy_vm(struct kvm *kvm);
 extern int kvmppc_core_prepare_memory_region(struct kvm *kvm,
diff --git a/arch/powerpc/kernel/asm-offsets.c 
b/arch/powerpc/kernel/asm-offsets.c
index a2f540b..d29f19a 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -189,6 +189,7 @@ int main(void)
        DEFINE(LPPACADECRINT, offsetof(struct lppaca, 
int_dword.fields.decr_int));
        DEFINE(LPPACA_PMCINUSE, offsetof(struct lppaca, pmcregs_in_use));
        DEFINE(LPPACA_DTLIDX, offsetof(struct lppaca, dtl_idx));
+       DEFINE(LPPACA_YIELDCOUNT, offsetof(struct lppaca, yield_count));
        DEFINE(PACA_DTL_RIDX, offsetof(struct paca_struct, dtl_ridx));
 #endif /* CONFIG_PPC_STD_MMU_64 */
        DEFINE(PACAEMERGSP, offsetof(struct paca_struct, emergency_sp));
@@ -458,6 +459,7 @@ int main(void)
        DEFINE(VCPU_DEC, offsetof(struct kvm_vcpu, arch.dec));
        DEFINE(VCPU_DEC_EXPIRES, offsetof(struct kvm_vcpu, arch.dec_expires));
        DEFINE(VCPU_LPCR, offsetof(struct kvm_vcpu, arch.lpcr));
+       DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa));
        DEFINE(VCPU_MMCR, offsetof(struct kvm_vcpu, arch.mmcr));
        DEFINE(VCPU_PMC, offsetof(struct kvm_vcpu, arch.pmc));
        DEFINE(VCPU_SLB, offsetof(struct kvm_vcpu, arch.slb));
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c 
b/arch/powerpc/kvm/book3s_64_mmu_hv.c
index 4a4fbec..f6fc4e5 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -219,6 +219,348 @@ void kvmppc_map_vrma(struct kvm *kvm, struct 
kvm_userspace_memory_region *mem)
        }
 }
 
+#define HPTE_V_HVLOCK  0x40UL
+
+static inline long lock_hpte(unsigned long *hpte, unsigned long bits)
+{
+       unsigned long tmp, old;
+
+       asm volatile("  ldarx   %0,0,%2\n"
+                    "  and.    %1,%0,%3\n"
+                    "  bne     2f\n"
+                    "  ori     %0,%0,%4\n"
+                    "  stdcx.  %0,0,%2\n"
+                    "  beq+    2f\n"
+                    "  li      %1,%3\n"
+                    "2:        isync"
+                    : "=&r" (tmp), "=&r" (old)
+                    : "r" (hpte), "r" (bits), "i" (HPTE_V_HVLOCK)
+                    : "cc", "memory");
+       return old == 0;
+}
+
+long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
+                   long pte_index, unsigned long pteh, unsigned long ptel)
+{
+       unsigned long porder;
+       struct kvm *kvm = vcpu->kvm;
+       unsigned long i, lpn, pa;
+       unsigned long *hpte;
+
+       /* only handle 4k, 64k and 16M pages for now */
+       porder = 12;
+       if (pteh & HPTE_V_LARGE) {
+               if ((ptel & 0xf000) == 0x1000) {
+                       /* 64k page */
+                       porder = 16;
+               } else if ((ptel & 0xff000) == 0) {
+                       /* 16M page */
+                       porder = 24;
+                       /* lowest AVA bit must be 0 for 16M pages */
+                       if (pteh & 0x80)
+                               return H_PARAMETER;
+               } else
+                       return H_PARAMETER;
+       }
+       lpn = (ptel & HPTE_R_RPN) >> kvm->arch.ram_porder;
+       if (lpn >= kvm->arch.ram_npages || porder > kvm->arch.ram_porder)
+               return H_PARAMETER;
+       pa = kvm->arch.ram_pginfo[lpn].pfn << PAGE_SHIFT;
+       if (!pa)
+               return H_PARAMETER;
+       /* Check WIMG */
+       if ((ptel & HPTE_R_WIMG) != HPTE_R_M &&
+           (ptel & HPTE_R_WIMG) != (HPTE_R_W | HPTE_R_I | HPTE_R_M))
+               return H_PARAMETER;
+       pteh &= ~0x60UL;
+       ptel &= ~(HPTE_R_PP0 - kvm->arch.ram_psize);
+       ptel |= pa;
+       if (pte_index >= (HPT_NPTEG << 3))
+               return H_PARAMETER;
+       if (likely((flags & H_EXACT) == 0)) {
+               pte_index &= ~7UL;
+               hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
+               for (i = 0; ; ++i) {
+                       if (i == 8)
+                               return H_PTEG_FULL;
+                       if ((*hpte & HPTE_V_VALID) == 0 &&
+                           lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID))
+                               break;
+                       hpte += 2;
+               }
+       } else {
+               i = 0;
+               hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
+               if (!lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID))
+                       return H_PTEG_FULL;
+       }
+       hpte[1] = ptel;
+       eieio();
+       hpte[0] = pteh;
+       asm volatile("ptesync" : : : "memory");
+       atomic_inc(&kvm->arch.ram_pginfo[lpn].refcnt);
+       vcpu->arch.gpr[4] = pte_index + i;
+       return H_SUCCESS;
+}
+
+static unsigned long compute_tlbie_rb(unsigned long v, unsigned long r,
+                                     unsigned long pte_index)
+{
+       unsigned long rb, va_low;
+
+       rb = (v & ~0x7fUL) << 16;               /* AVA field */
+       va_low = pte_index >> 3;
+       if (v & HPTE_V_SECONDARY)
+               va_low = ~va_low;
+       /* xor vsid from AVA */
+       if (!(v & HPTE_V_1TB_SEG))
+               va_low ^= v >> 12;
+       else
+               va_low ^= v >> 24;
+       va_low &= 0x7ff;
+       if (v & HPTE_V_LARGE) {
+               rb |= 1;                        /* L field */
+               if (r & 0xff000) {
+                       /* non-16MB large page, must be 64k */
+                       /* (masks depend on page size) */
+                       rb |= 0x1000;           /* page encoding in LP field */
+                       rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP 
field */
+                       rb |= (va_low & 0xfe);  /* AVAL field (P7 doesn't seem 
to care) */
+               }
+       } else {
+               /* 4kB page */
+               rb |= (va_low & 0x7ff) << 12;   /* remaining 11b of VA */
+       }
+       rb |= (v >> 54) & 0x300;                /* B field */
+       return rb;
+}
+
+#define LOCK_TOKEN     (*(u32 *)(&get_paca()->lock_token))
+
+static inline int try_lock_tlbie(unsigned int *lock)
+{
+       unsigned int tmp, old;
+       unsigned int token = LOCK_TOKEN;
+
+       asm volatile("1:lwarx   %1,0,%2\n"
+                    "  cmpwi   cr0,%1,0\n"
+                    "  bne     2f\n"
+                    "  stwcx.  %3,0,%2\n"
+                    "  bne-    1b\n"
+                    "  isync\n"
+                    "2:"
+                    : "=&r" (tmp), "=&r" (old)
+                    : "r" (lock), "r" (token)
+                    : "cc", "memory");
+       return old == 0;
+}
+
+long kvmppc_h_remove(struct kvm_vcpu *vcpu, unsigned long flags,
+                    unsigned long pte_index, unsigned long avpn,
+                    unsigned long va)
+{
+       struct kvm *kvm = vcpu->kvm;
+       unsigned long *hpte;
+       unsigned long v, r, rb;
+
+       if (pte_index >= (HPT_NPTEG << 3))
+               return H_PARAMETER;
+       hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
+       while (!lock_hpte(hpte, HPTE_V_HVLOCK))
+               cpu_relax();
+       if ((hpte[0] & HPTE_V_VALID) == 0 ||
+           ((flags & H_AVPN) && (hpte[0] & ~0x7fUL) != avpn) ||
+           ((flags & H_ANDCOND) && (hpte[0] & avpn) != 0)) {
+               hpte[0] &= ~HPTE_V_HVLOCK;
+               return H_NOT_FOUND;
+       }
+       if (atomic_read(&kvm->online_vcpus) == 1)
+               flags |= H_LOCAL;
+       vcpu->arch.gpr[4] = v = hpte[0] & ~HPTE_V_HVLOCK;
+       vcpu->arch.gpr[5] = r = hpte[1];
+       rb = compute_tlbie_rb(v, r, pte_index);
+       hpte[0] = 0;
+       if (!(flags & H_LOCAL)) {
+               while(!try_lock_tlbie(&kvm->arch.tlbie_lock))
+                       cpu_relax();
+               asm volatile("ptesync" : : : "memory");
+               asm volatile(PPC_TLBIE(%1,%0)"; eieio; tlbsync"
+                            : : "r" (rb), "r" (kvm->arch.lpid));
+               asm volatile("ptesync" : : : "memory");
+               kvm->arch.tlbie_lock = 0;
+       } else {
+               asm volatile("ptesync" : : : "memory");
+               asm volatile("tlbiel %0" : : "r" (rb));
+               asm volatile("ptesync" : : : "memory");
+       }
+       return H_SUCCESS;
+}
+
+long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu)
+{
+       struct kvm *kvm = vcpu->kvm;
+       unsigned long *args = &vcpu->arch.gpr[4];
+       unsigned long *hp, tlbrb[4];
+       long int i, found;
+       long int n_inval = 0;
+       unsigned long flags, req, pte_index;
+       long int local = 0;
+       long int ret = H_SUCCESS;
+
+       if (atomic_read(&kvm->online_vcpus) == 1)
+               local = 1;
+       for (i = 0; i < 4; ++i) {
+               pte_index = args[i * 2];
+               flags = pte_index >> 56;
+               pte_index &= ((1ul << 56) - 1);
+               req = flags >> 6;
+               flags &= 3;
+               if (req == 3)
+                       break;
+               if (req != 1 || flags == 3 ||
+                   pte_index >= (HPT_NPTEG << 3)) {
+                       /* parameter error */
+                       args[i * 2] = ((0xa0 | flags) << 56) + pte_index;
+                       ret = H_PARAMETER;
+                       break;
+               }
+               hp = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
+               while (!lock_hpte(hp, HPTE_V_HVLOCK))
+                       cpu_relax();
+               found = 0;
+               if (hp[0] & HPTE_V_VALID) {
+                       switch (flags & 3) {
+                       case 0:         /* absolute */
+                               found = 1;
+                               break;
+                       case 1:         /* andcond */
+                               if (!(hp[0] & args[i * 2 + 1]))
+                                       found = 1;
+                               break;
+                       case 2:         /* AVPN */
+                               if ((hp[0] & ~0x7fUL) == args[i * 2 + 1])
+                                       found = 1;
+                               break;
+                       }
+               }
+               if (!found) {
+                       hp[0] &= ~HPTE_V_HVLOCK;
+                       args[i * 2] = ((0x90 | flags) << 56) + pte_index;
+                       continue;
+               }
+               /* insert R and C bits from PTE */
+               flags |= (hp[1] >> 5) & 0x0c;
+               args[i * 2] = ((0x80 | flags) << 56) + pte_index;
+               tlbrb[n_inval++] = compute_tlbie_rb(hp[0], hp[1], pte_index);
+               hp[0] = 0;
+       }
+       if (n_inval == 0)
+               return ret;
+
+       if (!local) {
+               while(!try_lock_tlbie(&kvm->arch.tlbie_lock))
+                       cpu_relax();
+               asm volatile("ptesync" : : : "memory");
+               for (i = 0; i < n_inval; ++i)
+                       asm volatile(PPC_TLBIE(%1,%0)
+                                    : : "r" (tlbrb[i]), "r" (kvm->arch.lpid));
+               asm volatile("eieio; tlbsync; ptesync" : : : "memory");
+               kvm->arch.tlbie_lock = 0;
+       } else {
+               asm volatile("ptesync" : : : "memory");
+               for (i = 0; i < n_inval; ++i)
+                       asm volatile("tlbiel %0" : : "r" (tlbrb[i]));
+               asm volatile("ptesync" : : : "memory");
+       }
+       return ret;
+}
+
+long kvmppc_h_protect(struct kvm_vcpu *vcpu, unsigned long flags,
+                     unsigned long pte_index, unsigned long avpn,
+                     unsigned long va)
+{
+       struct kvm *kvm = vcpu->kvm;
+       unsigned long *hpte;
+       unsigned long v, r, rb;
+
+       if (pte_index >= (HPT_NPTEG << 3))
+               return H_PARAMETER;
+       hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
+       while (!lock_hpte(hpte, HPTE_V_HVLOCK))
+               cpu_relax();
+       if ((hpte[0] & HPTE_V_VALID) == 0 ||
+           ((flags & H_AVPN) && (hpte[0] & ~0x7fUL) != avpn)) {
+               hpte[0] &= ~HPTE_V_HVLOCK;
+               return H_NOT_FOUND;
+       }
+       if (atomic_read(&kvm->online_vcpus) == 1)
+               flags |= H_LOCAL;
+       v = hpte[0];
+       r = hpte[1] & ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N |
+                       HPTE_R_KEY_HI | HPTE_R_KEY_LO);
+       r |= (flags << 55) & HPTE_R_PP0;
+       r |= (flags << 48) & HPTE_R_KEY_HI;
+       r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO);
+       rb = compute_tlbie_rb(v, r, pte_index);
+       hpte[0] = v & ~HPTE_V_VALID;
+       if (!(flags & H_LOCAL)) {
+               while(!try_lock_tlbie(&kvm->arch.tlbie_lock))
+                       cpu_relax();
+               asm volatile("ptesync" : : : "memory");
+               asm volatile(PPC_TLBIE(%1,%0)"; eieio; tlbsync"
+                            : : "r" (rb), "r" (kvm->arch.lpid));
+               asm volatile("ptesync" : : : "memory");
+               kvm->arch.tlbie_lock = 0;
+       } else {
+               asm volatile("ptesync" : : : "memory");
+               asm volatile("tlbiel %0" : : "r" (rb));
+               asm volatile("ptesync" : : : "memory");
+       }
+       hpte[1] = r;
+       eieio();
+       hpte[0] = v & ~HPTE_V_HVLOCK;
+       asm volatile("ptesync" : : : "memory");
+       return H_SUCCESS;
+}
+
+static unsigned long reverse_xlate(struct kvm *kvm, unsigned long realaddr)
+{
+       long int i;
+       unsigned long offset, rpn;
+
+       offset = realaddr & (kvm->arch.ram_psize - 1);
+       rpn = (realaddr - offset) >> PAGE_SHIFT;
+       for (i = 0; i < kvm->arch.ram_npages; ++i)
+               if (rpn == kvm->arch.ram_pginfo[i].pfn)
+                       return (i << PAGE_SHIFT) + offset;
+       return HPTE_R_RPN;      /* all 1s in the RPN field */
+}
+
+long kvmppc_h_read(struct kvm_vcpu *vcpu, unsigned long flags,
+                  unsigned long pte_index)
+{
+       struct kvm *kvm = vcpu->kvm;
+       unsigned long *hpte, r;
+       int i, n = 1;
+
+       if (pte_index >= (HPT_NPTEG << 3))
+               return H_PARAMETER;
+       if (flags & H_READ_4) {
+               pte_index &= ~3;
+               n = 4;
+       }
+       for (i = 0; i < n; ++i, ++pte_index) {
+               hpte = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
+               r = hpte[1];
+               if ((flags & H_R_XLATE) && (hpte[0] & HPTE_V_VALID))
+                       r = reverse_xlate(kvm, r & HPTE_R_RPN) |
+                               (r & ~HPTE_R_RPN);
+               vcpu->arch.gpr[4 + i * 2] = hpte[0];
+               vcpu->arch.gpr[5 + i * 2] = r;
+       }
+       return H_SUCCESS;
+}
+
 int kvmppc_mmu_hv_init(void)
 {
        if (!cpu_has_feature(CPU_FTR_HVMODE_206))
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index 0d812c5..d565a9f 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -123,6 +123,158 @@ void kvmppc_dump_regs(struct kvm_vcpu *vcpu)
               vcpu->arch.last_inst);
 }
 
+struct kvm_vcpu *kvmppc_find_vcpu(struct kvm *kvm, int id)
+{
+       int r;
+       struct kvm_vcpu *v, *ret = NULL;
+
+       mutex_lock(&kvm->lock);
+       kvm_for_each_vcpu(r, v, kvm) {
+               if (v->vcpu_id == id) {
+                       ret = v;
+                       break;
+               }
+       }
+       mutex_unlock(&kvm->lock);
+       return ret;
+}
+
+static void init_vpa(struct kvm_vcpu *vcpu, struct lppaca *vpa)
+{
+       vpa->shared_proc = 1;
+       vpa->yield_count = 1;
+}
+
+static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu,
+                                      unsigned long flags,
+                                      unsigned long vcpuid, unsigned long vpa)
+{
+       struct kvm *kvm = vcpu->kvm;
+       unsigned long pg_index, ra, len;
+       unsigned long pg_offset;
+       void *va;
+       struct kvm_vcpu *tvcpu;
+
+       tvcpu = kvmppc_find_vcpu(kvm, vcpuid);
+       if (!tvcpu)
+               return H_PARAMETER;
+
+       flags >>= 63 - 18;
+       flags &= 7;
+       if (flags == 0 || flags == 4)
+               return H_PARAMETER;
+       if (flags < 4) {
+               if (vpa & 0x7f)
+                       return H_PARAMETER;
+               /* registering new area; convert logical addr to real */
+               pg_index = vpa >> kvm->arch.ram_porder;
+               pg_offset = vpa & (kvm->arch.ram_psize - 1);
+               if (pg_index >= kvm->arch.ram_npages)
+                       return H_PARAMETER;
+               if (kvm->arch.ram_pginfo[pg_index].pfn == 0)
+                       return H_PARAMETER;
+               ra = kvm->arch.ram_pginfo[pg_index].pfn << PAGE_SHIFT;
+               ra |= pg_offset;
+               va = __va(ra);
+               if (flags <= 1)
+                       len = *(unsigned short *)(va + 4);
+               else
+                       len = *(unsigned int *)(va + 4);
+               if (pg_offset + len > kvm->arch.ram_psize)
+                       return H_PARAMETER;
+               switch (flags) {
+               case 1:         /* register VPA */
+                       if (len < 640)
+                               return H_PARAMETER;
+                       tvcpu->arch.vpa = va;
+                       init_vpa(vcpu, va);
+                       break;
+               case 2:         /* register DTL */
+                       if (len < 48)
+                               return H_PARAMETER;
+                       if (!tvcpu->arch.vpa)
+                               return H_RESOURCE;
+                       len -= len % 48;
+                       tvcpu->arch.dtl = va;
+                       tvcpu->arch.dtl_end = va + len;
+                       break;
+               case 3:         /* register SLB shadow buffer */
+                       if (len < 8)
+                               return H_PARAMETER;
+                       if (!tvcpu->arch.vpa)
+                               return H_RESOURCE;
+                       tvcpu->arch.slb_shadow = va;
+                       len = (len - 16) / 16;
+                       tvcpu->arch.slb_shadow = va;
+                       break;
+               }
+       } else {
+               switch (flags) {
+               case 5:         /* unregister VPA */
+                       if (tvcpu->arch.slb_shadow || tvcpu->arch.dtl)
+                               return H_RESOURCE;
+                       tvcpu->arch.vpa = NULL;
+                       break;
+               case 6:         /* unregister DTL */
+                       tvcpu->arch.dtl = NULL;
+                       break;
+               case 7:         /* unregister SLB shadow buffer */
+                       tvcpu->arch.slb_shadow = NULL;
+                       break;
+               }
+       }
+       return H_SUCCESS;
+}
+
+int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
+{
+       unsigned long req = kvmppc_get_gpr(vcpu, 3);
+       unsigned long target, ret = H_SUCCESS;
+       struct kvm_vcpu *tvcpu;
+
+       switch (req) {
+       case H_CEDE:
+               vcpu->arch.shregs.msr |= MSR_EE;
+               vcpu->arch.ceded = 1;
+               smp_mb();
+               if (!vcpu->arch.prodded)
+                       kvmppc_vcpu_block(vcpu);
+               else
+                       vcpu->arch.prodded = 0;
+               smp_mb();
+               vcpu->arch.ceded = 0;
+               break;
+       case H_PROD:
+               target = kvmppc_get_gpr(vcpu, 4);
+               tvcpu = kvmppc_find_vcpu(vcpu->kvm, target);
+               if (!tvcpu) {
+                       ret = H_PARAMETER;
+                       break;
+               }
+               tvcpu->arch.prodded = 1;
+               smp_mb();
+               if (vcpu->arch.ceded) {
+                       if (waitqueue_active(&vcpu->wq)) {
+                               wake_up_interruptible(&vcpu->wq);
+                               vcpu->stat.halt_wakeup++;
+                       }
+               }
+               break;
+       case H_CONFER:
+               break;
+       case H_REGISTER_VPA:
+               ret = do_h_register_vpa(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                       kvmppc_get_gpr(vcpu, 5),
+                                       kvmppc_get_gpr(vcpu, 6));
+               break;
+       default:
+               return RESUME_HOST;
+       }
+       kvmppc_set_gpr(vcpu, 3, ret);
+       vcpu->arch.hcall_needed = 0;
+       return RESUME_GUEST;
+}
+
 static int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                              struct task_struct *tsk)
 {
@@ -324,7 +476,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
 
 extern int __kvmppc_vcore_entry(struct kvm_run *kvm_run, struct kvm_vcpu 
*vcpu);
 
-int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu)
+static int kvmppc_run_vcpu(struct kvm_run *run, struct kvm_vcpu *vcpu)
 {
        u64 now;
 
@@ -355,6 +507,22 @@ int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu 
*vcpu)
        return kvmppc_handle_exit(run, vcpu, current);
 }
 
+int kvmppc_vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu)
+{
+       int r;
+
+       do {
+               r = kvmppc_run_vcpu(run, vcpu);
+
+               if (run->exit_reason == KVM_EXIT_PAPR_HCALL &&
+                   !(vcpu->arch.shregs.msr & MSR_PR)) {
+                       r = kvmppc_pseries_do_hcall(vcpu);
+                       kvmppc_core_deliver_interrupts(vcpu);
+               }
+       } while (r == RESUME_GUEST);
+       return r;
+}
+
 int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                                struct kvm_userspace_memory_region *mem)
 {
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S 
b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index f0dc0ff..e0643b3 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -196,6 +196,14 @@ kvmppc_handler_trampoline_enter:
        /* Save R1 in the PACA */
        std     r1, HSTATE_HOST_R1(r13)
 
+       /* Increment yield count if they have a VPA */
+       ld      r3, VCPU_VPA(r4)
+       cmpdi   r3, 0
+       beq     25f
+       lwz     r5, LPPACA_YIELDCOUNT(r3)
+       addi    r5, r5, 1
+       stw     r5, LPPACA_YIELDCOUNT(r3)
+25:
        /* Load up DAR and DSISR */
        ld      r5, VCPU_DAR(r4)
        lwz     r6, VCPU_DSISR(r4)
@@ -433,6 +441,10 @@ kvmppc_interrupt:
        cmpwi   r3,0
        bge     ignore_hdec
 2:
+       /* See if this is something we can handle in real mode */
+       cmpwi   r12,BOOK3S_INTERRUPT_SYSCALL
+       beq     hcall_try_real_mode
+hcall_real_cont:
 
        /* Check for mediated interrupts (could be done earlier really ...) */
        cmpwi   r12,BOOK3S_INTERRUPT_EXTERNAL
@@ -611,13 +623,28 @@ hdec_soon:
        std     r5, VCPU_SPRG2(r9)
        std     r6, VCPU_SPRG3(r9)
 
-       /* Save PMU registers */
+       /* Increment yield count if they have a VPA */
+       ld      r8, VCPU_VPA(r9)        /* do they have a VPA? */
+       cmpdi   r8, 0
+       beq     25f
+       lwz     r3, LPPACA_YIELDCOUNT(r8)
+       addi    r3, r3, 1
+       stw     r3, LPPACA_YIELDCOUNT(r8)
+25:
+       /* Save PMU registers if requested */
+       /* r8 and cr0.eq are live here */
        li      r3, 1
        sldi    r3, r3, 31              /* MMCR0_FC (freeze counters) bit */
        mfspr   r4, SPRN_MMCR0          /* save MMCR0 */
        mtspr   SPRN_MMCR0, r3          /* freeze all counters, disable ints */
        isync
-       mfspr   r5, SPRN_MMCR1
+       beq     21f                     /* if no VPA, save PMU stuff anyway */
+       lbz     r7, LPPACA_PMCINUSE(r8)
+       cmpwi   r7, 0                   /* did they ask for PMU stuff to be 
saved? */
+       bne     21f
+       std     r3, VCPU_MMCR(r9)       /* if not, set saved MMCR0 to FC */
+       b       22f
+21:    mfspr   r5, SPRN_MMCR1
        mfspr   r6, SPRN_MMCRA
        std     r4, VCPU_MMCR(r9)
        std     r5, VCPU_MMCR + 8(r9)
@@ -654,6 +681,125 @@ kvmppc_handler_trampoline_exit_end:
        mfspr   r7,SPRN_HDSISR
        b       7b
 
+/*
+ * Try to handle an hcall in real mode.
+ * Returns to the guest if we handle it, or continues on up to
+ * the kernel if we can't (i.e. if we don't have a handler for
+ * it, or if the handler returns H_TOO_HARD).
+ */
+       .globl  hcall_try_real_mode
+hcall_try_real_mode:
+       ld      r3,VCPU_GPR(r3)(r9)
+       andi.   r0,r11,MSR_PR
+       bne     hcall_real_cont
+       clrrdi  r3,r3,2
+       cmpldi  r3,hcall_real_table_end - hcall_real_table
+       bge     hcall_real_cont
+       LOAD_REG_ADDR(r4, hcall_real_table)
+       lwzx    r3,r3,r4
+       cmpwi   r3,0
+       beq     hcall_real_cont
+       add     r3,r3,r4
+       mtctr   r3
+       mr      r3,r9           /* get vcpu pointer */
+       ld      r4,VCPU_GPR(r4)(r9)
+       bctrl
+       cmpdi   r3,H_TOO_HARD
+       beq     hcall_real_fallback
+       ld      r4,HSTATE_KVM_VCPU(r13)
+       std     r3,VCPU_GPR(r3)(r4)
+       ld      r10,VCPU_PC(r4)
+       ld      r11,VCPU_MSR(r4)
+       b       fast_guest_return
+
+       /* We've attempted a real mode hcall, but it's punted it back
+        * to userspace.  We need to restore some clobbered volatiles
+        * before resuming the pass-it-to-qemu path */
+hcall_real_fallback:
+       li      r12,BOOK3S_INTERRUPT_SYSCALL
+       ld      r9, HSTATE_KVM_VCPU(r13)
+       ld      r11, VCPU_MSR(r9)
+
+       b       hcall_real_cont
+
+       .globl  hcall_real_table
+hcall_real_table:
+       .long   0               /* 0 - unused */
+       .long   .kvmppc_h_remove - hcall_real_table
+       .long   .kvmppc_h_enter - hcall_real_table
+       .long   .kvmppc_h_read - hcall_real_table
+       .long   0               /* 0x10 - H_CLEAR_MOD */
+       .long   0               /* 0x14 - H_CLEAR_REF */
+       .long   .kvmppc_h_protect - hcall_real_table
+       .long   0               /* 0x1c - H_GET_TCE */
+       .long   0               /* 0x20 - H_SET_TCE */
+       .long   0               /* 0x24 - H_SET_SPRG0 */
+       .long   .kvmppc_h_set_dabr - hcall_real_table
+       .long   0               /* 0x2c */
+       .long   0               /* 0x30 */
+       .long   0               /* 0x34 */
+       .long   0               /* 0x38 */
+       .long   0               /* 0x3c */
+       .long   0               /* 0x40 */
+       .long   0               /* 0x44 */
+       .long   0               /* 0x48 */
+       .long   0               /* 0x4c */
+       .long   0               /* 0x50 */
+       .long   0               /* 0x54 */
+       .long   0               /* 0x58 */
+       .long   0               /* 0x5c */
+       .long   0               /* 0x60 */
+       .long   0               /* 0x64 */
+       .long   0               /* 0x68 */
+       .long   0               /* 0x6c */
+       .long   0               /* 0x70 */
+       .long   0               /* 0x74 */
+       .long   0               /* 0x78 */
+       .long   0               /* 0x7c */
+       .long   0               /* 0x80 */
+       .long   0               /* 0x84 */
+       .long   0               /* 0x88 */
+       .long   0               /* 0x8c */
+       .long   0               /* 0x90 */
+       .long   0               /* 0x94 */
+       .long   0               /* 0x98 */
+       .long   0               /* 0x9c */
+       .long   0               /* 0xa0 */
+       .long   0               /* 0xa4 */
+       .long   0               /* 0xa8 */
+       .long   0               /* 0xac */
+       .long   0               /* 0xb0 */
+       .long   0               /* 0xb4 */
+       .long   0               /* 0xb8 */
+       .long   0               /* 0xbc */
+       .long   0               /* 0xc0 */
+       .long   0               /* 0xc4 */
+       .long   0               /* 0xc8 */
+       .long   0               /* 0xcc */
+       .long   0               /* 0xd0 */
+       .long   0               /* 0xd4 */
+       .long   0               /* 0xd8 */
+       .long   0               /* 0xdc */
+       .long   0               /* 0xe0 */
+       .long   0               /* 0xe4 */
+       .long   0               /* 0xe8 */
+       .long   0               /* 0xec */
+       .long   0               /* 0xf0 */
+       .long   0               /* 0xf4 */
+       .long   0               /* 0xf8 */
+       .long   0               /* 0xfc */
+       .long   0               /* 0x100 */
+       .long   0               /* 0x104 */
+       .long   0               /* 0x108 */
+       .long   0               /* 0x10c */
+       .long   0               /* 0x110 */
+       .long   0               /* 0x114 */
+       .long   0               /* 0x118 */
+       .long   0               /* 0x11c */
+       .long   0               /* 0x120 */
+       .long   .kvmppc_h_bulk_remove - hcall_real_table
+hcall_real_table_end:
+
 ignore_hdec:
        mr      r4,r9
        b       fast_guest_return
@@ -665,3 +811,9 @@ bounce_ext_interrupt:
        li      r10,BOOK3S_INTERRUPT_EXTERNAL
        LOAD_REG_IMMEDIATE(r11,MSR_SF | MSR_ME);
        b       fast_guest_return
+
+_GLOBAL(kvmppc_h_set_dabr)
+       std     r4,VCPU_DABR(r3)
+       mtspr   SPRN_DABR,r4
+       li      r3,0
+       blr
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index a5635c9..985ee72 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -42,7 +42,7 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
        return !(v->arch.shared->msr & MSR_WE) ||
               !!(v->arch.pending_exceptions);
 #else
-       return 1;
+       return !(v->arch.ceded) || !!(v->arch.pending_exceptions);
 #endif
 }
 
-- 
1.7.5.3

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to