[PATCH 11/13] kvm/powerpc: Handle some PAPR hcalls in the kernel
Alexander Graf
agraf at suse.de
Tue May 17 17:54:35 EST 2011
On 11.05.2011, at 12:45, Paul Mackerras wrote:
> 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 <paulus at 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 | 150 +++++++++++++-
> arch/powerpc/kvm/powerpc.c | 2 +-
> 8 files changed, 679 insertions(+), 4 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
> index 8edec71..a226002 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 */
> +
> +/* Hacked in for HV-aware kvm support */
> +#define H_TOO_HARD 9999
Not sure I like the name - when is it used? :)
Also, if it's not in the PAPR, the guest should never receive it, right?
> +
> #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_ZERO_PAGE (1UL<<(63-48)) /* zero the page before mapping (ignored for IO pages) */
> diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
> index ec62365..af6703e 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;
> };
> @@ -341,7 +345,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 cd9ad96..b4ee11a 100644
> --- a/arch/powerpc/include/asm/kvm_ppc.h
> +++ b/arch/powerpc/include/asm/kvm_ppc.h
> @@ -116,6 +116,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 49e97fd..fd56f14 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));
> @@ -467,6 +468,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 52d1be1..623caae 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 f6b7cd1..377a35a 100644
> --- a/arch/powerpc/kvm/book3s_hv.c
> +++ b/arch/powerpc/kvm/book3s_hv.c
> @@ -126,6 +126,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.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)
> {
> @@ -307,7 +459,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;
>
> @@ -338,6 +490,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.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 813b01c..e8a8f3c 100644
> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> @@ -195,6 +195,14 @@ kvmppc_handler_trampoline_enter:
> /* Save R1 in the PACA */
> std r1, PACA_KVM_SVCPU + SVCPU_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)
> @@ -432,6 +440,10 @@ kvmppc_interrupt:
> cmpwi r3,0
> bge ignore_hdec
> 2:
> + /* See if this is something we can handle in real mode */
> + cmpwi r12,0xc00
use the define please
> + beq hcall_real_mode
This is simply a hcall helper, as the name suggests. So the comment is slightly misleading - it should rather read like "Try to handle hypercalls in real mode".
> +hcall_real_cont:
>
> /* Check for mediated interrupts (could be done earlier really ...) */
> cmpwi r12,0x500
> @@ -607,13 +619,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)
> @@ -650,6 +677,119 @@ kvmppc_handler_trampoline_exit_end:
> mfspr r7,SPRN_HDSISR
> b 7b
>
> + .globl hcall_real_mode
> +hcall_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
Ah, very good. Please mark the constant as "for internal use only" then, as that's certainly fine :).
> + ld r4,PACA_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,0xc00
use the define please :)
Alex
More information about the Linuxppc-dev
mailing list