KVM: PPC: book3s_hv: Add support for PPC970-family processors
authorPaul Mackerras <paulus@samba.org>
Wed, 29 Jun 2011 00:40:08 +0000 (00:40 +0000)
committerAvi Kivity <avi@redhat.com>
Tue, 12 Jul 2011 10:16:59 +0000 (13:16 +0300)
This adds support for running KVM guests in supervisor mode on those
PPC970 processors that have a usable hypervisor mode.  Unfortunately,
Apple G5 machines have supervisor mode disabled (MSR[HV] is forced to
1), but the YDL PowerStation does have a usable hypervisor mode.

There are several differences between the PPC970 and POWER7 in how
guests are managed.  These differences are accommodated using the
CPU_FTR_ARCH_201 (PPC970) and CPU_FTR_ARCH_206 (POWER7) CPU feature
bits.  Notably, on PPC970:

* The LPCR, LPID or RMOR registers don't exist, and the functions of
  those registers are provided by bits in HID4 and one bit in HID0.

* External interrupts can be directed to the hypervisor, but unlike
  POWER7 they are masked by MSR[EE] in non-hypervisor modes and use
  SRR0/1 not HSRR0/1.

* There is no virtual RMA (VRMA) mode; the guest must use an RMO
  (real mode offset) area.

* The TLB entries are not tagged with the LPID, so it is necessary to
  flush the whole TLB on partition switch.  Furthermore, when switching
  partitions we have to ensure that no other CPU is executing the tlbie
  or tlbsync instructions in either the old or the new partition,
  otherwise undefined behaviour can occur.

* The PMU has 8 counters (PMC registers) rather than 6.

* The DSCR, PURR, SPURR, AMR, AMOR, UAMOR registers don't exist.

* The SLB has 64 entries rather than 32.

* There is no mediated external interrupt facility, so if we switch to
  a guest that has a virtual external interrupt pending but the guest
  has MSR[EE] = 0, we have to arrange to have an interrupt pending for
  it so that we can get control back once it re-enables interrupts.  We
  do that by sending ourselves an IPI with smp_send_reschedule after
  hard-disabling interrupts.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
14 files changed:
arch/powerpc/include/asm/exception-64s.h
arch/powerpc/include/asm/kvm_book3s_asm.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kvm/Kconfig
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_builtin.c
arch/powerpc/kvm/book3s_hv_interrupts.S
arch/powerpc/kvm/book3s_hv_rm_mmu.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/powerpc.c
arch/powerpc/mm/hash_native_64.c

index 69435da8f2ba254873d7092ec400e7d9588bb256..8057f4f6980ffc55abfa0a20d366cf8e10a65b4b 100644 (file)
@@ -246,6 +246,10 @@ label##_hv:                                                \
        KVMTEST(vec);                                                   \
        _SOFTEN_TEST(EXC_HV)
 
+#define SOFTEN_TEST_HV_201(vec)                                                \
+       KVMTEST(vec);                                                   \
+       _SOFTEN_TEST(EXC_STD)
+
 #define __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra)             \
        HMT_MEDIUM;                                                     \
        SET_SCRATCH0(r13);    /* save r13 */                            \
index 9cfd5436782d7a95a161a4268f1ad9e06ec94b52..ef7b3688c3b6101531c4412d4e15eba3541802fb 100644 (file)
@@ -82,7 +82,7 @@ struct kvmppc_host_state {
        unsigned long xics_phys;
        u64 dabr;
        u64 host_mmcr[3];
-       u32 host_pmc[6];
+       u32 host_pmc[8];
        u64 host_purr;
        u64 host_spurr;
        u64 host_dscr;
index f572d9cc31bd53a4986c583e8afcf24f59ee9ed1..cc22b282d75554a5c2dfc62be49b4408adb3711b 100644 (file)
@@ -353,7 +353,7 @@ struct kvm_vcpu_arch {
        u32 dbsr;
 
        u64 mmcr[3];
-       u32 pmc[6];
+       u32 pmc[8];
 
 #ifdef CONFIG_KVM_EXIT_TIMING
        struct mutex exit_timing_lock;
index f4aba938166bb93c680d45f6d3e3952c93e534d2..54b935f2f5def0b33c0538863fb665143b207c2c 100644 (file)
@@ -128,6 +128,7 @@ int main(void)
        DEFINE(ICACHEL1LINESPERPAGE, offsetof(struct ppc64_caches, ilines_per_page));
        /* paca */
        DEFINE(PACA_SIZE, sizeof(struct paca_struct));
+       DEFINE(PACA_LOCK_TOKEN, offsetof(struct paca_struct, lock_token));
        DEFINE(PACAPACAINDEX, offsetof(struct paca_struct, paca_index));
        DEFINE(PACAPROCSTART, offsetof(struct paca_struct, cpu_start));
        DEFINE(PACAKSAVE, offsetof(struct paca_struct, kstack));
index a5345380bef390e19c3015a6a8f8a4c7952074d6..41b02c792aa387b43c16f8d861564be1482cde01 100644 (file)
@@ -171,7 +171,7 @@ hardware_interrupt_hv:
                KVM_HANDLER(PACA_EXGEN, EXC_HV, 0x502)
        FTR_SECTION_ELSE
                _MASKABLE_EXCEPTION_PSERIES(0x500, hardware_interrupt,
-                                           EXC_STD, SOFTEN_TEST_PR)
+                                           EXC_STD, SOFTEN_TEST_HV_201)
                KVM_HANDLER(PACA_EXGEN, EXC_STD, 0x500)
        ALT_FTR_SECTION_END_IFSET(CPU_FTR_HVMODE | CPU_FTR_ARCH_206)
 
index 5d9b78ebbaa680f0be4fcd5a8a4cac43739bb3c7..eeb42e06f2d7d1a564204e4a2803a161dfac6fb0 100644 (file)
@@ -67,23 +67,20 @@ config KVM_BOOK3S_64
          If unsure, say N.
 
 config KVM_BOOK3S_64_HV
-       bool "KVM support for POWER7 using hypervisor mode in host"
+       bool "KVM support for POWER7 and PPC970 using hypervisor mode in host"
        depends on KVM_BOOK3S_64
        ---help---
          Support running unmodified book3s_64 guest kernels in
-         virtual machines on POWER7 processors that have hypervisor
-         mode available to the host.
+         virtual machines on POWER7 and PPC970 processors that have
+         hypervisor mode available to the host.
 
          If you say Y here, KVM will use the hardware virtualization
          facilities of POWER7 (and later) processors, meaning that
          guest operating systems will run at full hardware speed
          using supervisor and user modes.  However, this also means
          that KVM is not usable under PowerVM (pHyp), is only usable
-         on POWER7 (or later) processors, and can only emulate
-         POWER5+, POWER6 and POWER7 processors.
-
-         This module provides access to the hardware capabilities through
-         a character device node named /dev/kvm.
+         on POWER7 (or later) processors and PPC970-family processors,
+         and cannot emulate a different processor from the host processor.
 
          If unsure, say N.
 
index 212dcd8fc50b302836644f83550e57f3abf3c33e..bc3a2ea94217ef87962c3707b0cb7bd03330c73c 100644 (file)
@@ -42,6 +42,8 @@
 #define VRMA_PAGE_ORDER        24
 #define VRMA_VSID      0x1ffffffUL     /* 1TB VSID reserved for VRMA */
 
+/* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */
+#define MAX_LPID_970   63
 #define NR_LPIDS       (LPID_RSVD + 1)
 unsigned long lpid_inuse[BITS_TO_LONGS(NR_LPIDS)];
 
@@ -69,9 +71,6 @@ long kvmppc_alloc_hpt(struct kvm *kvm)
 
        kvm->arch.sdr1 = __pa(hpt) | (HPT_ORDER - 18);
        kvm->arch.lpid = lpid;
-       kvm->arch.host_sdr1 = mfspr(SPRN_SDR1);
-       kvm->arch.host_lpid = mfspr(SPRN_LPID);
-       kvm->arch.host_lpcr = mfspr(SPRN_LPCR);
 
        pr_info("KVM guest htab at %lx, LPID %lx\n", hpt, lpid);
        return 0;
@@ -128,12 +127,24 @@ void kvmppc_map_vrma(struct kvm *kvm, struct kvm_userspace_memory_region *mem)
 
 int kvmppc_mmu_hv_init(void)
 {
-       if (!cpu_has_feature(CPU_FTR_HVMODE) ||
-           !cpu_has_feature(CPU_FTR_ARCH_206))
+       unsigned long host_lpid, rsvd_lpid;
+
+       if (!cpu_has_feature(CPU_FTR_HVMODE))
                return -EINVAL;
+
        memset(lpid_inuse, 0, sizeof(lpid_inuse));
-       set_bit(mfspr(SPRN_LPID), lpid_inuse);
-       set_bit(LPID_RSVD, lpid_inuse);
+
+       if (cpu_has_feature(CPU_FTR_ARCH_206)) {
+               host_lpid = mfspr(SPRN_LPID);   /* POWER7 */
+               rsvd_lpid = LPID_RSVD;
+       } else {
+               host_lpid = 0;                  /* PPC970 */
+               rsvd_lpid = MAX_LPID_970;
+       }
+
+       set_bit(host_lpid, lpid_inuse);
+       /* rsvd_lpid is reserved for use in partition switching */
+       set_bit(rsvd_lpid, lpid_inuse);
 
        return 0;
 }
@@ -157,7 +168,10 @@ void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu)
 {
        struct kvmppc_mmu *mmu = &vcpu->arch.mmu;
 
-       vcpu->arch.slb_nr = 32;         /* Assume POWER7 for now */
+       if (cpu_has_feature(CPU_FTR_ARCH_206))
+               vcpu->arch.slb_nr = 32;         /* POWER7 */
+       else
+               vcpu->arch.slb_nr = 64;
 
        mmu->xlate = kvmppc_mmu_book3s_64_hv_xlate;
        mmu->reset_msr = kvmppc_mmu_book3s_64_hv_reset_msr;
index dc70e7745ab3ce238e94b2aab95896f29994018c..cc0d7f1b19ab474f62c702192f8fb44ff8ecf216 100644 (file)
@@ -443,8 +443,7 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
 
 int kvmppc_core_check_processor_compat(void)
 {
-       if (cpu_has_feature(CPU_FTR_HVMODE) &&
-           cpu_has_feature(CPU_FTR_ARCH_206))
+       if (cpu_has_feature(CPU_FTR_HVMODE))
                return 0;
        return -EIO;
 }
@@ -731,6 +730,10 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
                return -EINTR;
        }
 
+       /* On PPC970, check that we have an RMA region */
+       if (!vcpu->kvm->arch.rma && cpu_has_feature(CPU_FTR_ARCH_201))
+               return -EPERM;
+
        kvm_run->exit_reason = 0;
        vcpu->arch.ret = RESUME_GUEST;
        vcpu->arch.trap = 0;
@@ -920,12 +923,14 @@ fail:
 }
 
 /* Work out RMLS (real mode limit selector) field value for a given RMA size.
-   Assumes POWER7. */
+   Assumes POWER7 or PPC970. */
 static inline int lpcr_rmls(unsigned long rma_size)
 {
        switch (rma_size) {
        case 32ul << 20:        /* 32 MB */
-               return 8;
+               if (cpu_has_feature(CPU_FTR_ARCH_206))
+                       return 8;       /* only supported on POWER7 */
+               return -1;
        case 64ul << 20:        /* 64 MB */
                return 3;
        case 128ul << 20:       /* 128 MB */
@@ -1059,6 +1064,10 @@ int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                    mem->userspace_addr == vma->vm_start)
                        ri = vma->vm_file->private_data;
                up_read(&current->mm->mmap_sem);
+               if (!ri && cpu_has_feature(CPU_FTR_ARCH_201)) {
+                       pr_err("CPU requires an RMO\n");
+                       return -EINVAL;
+               }
        }
 
        if (ri) {
@@ -1077,10 +1086,25 @@ int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                atomic_inc(&ri->use_count);
                kvm->arch.rma = ri;
                kvm->arch.n_rma_pages = rma_size >> porder;
-               lpcr = kvm->arch.lpcr & ~(LPCR_VPM0 | LPCR_VRMA_L);
-               lpcr |= rmls << LPCR_RMLS_SH;
+
+               /* Update LPCR and RMOR */
+               lpcr = kvm->arch.lpcr;
+               if (cpu_has_feature(CPU_FTR_ARCH_201)) {
+                       /* PPC970; insert RMLS value (split field) in HID4 */
+                       lpcr &= ~((1ul << HID4_RMLS0_SH) |
+                                 (3ul << HID4_RMLS2_SH));
+                       lpcr |= ((rmls >> 2) << HID4_RMLS0_SH) |
+                               ((rmls & 3) << HID4_RMLS2_SH);
+                       /* RMOR is also in HID4 */
+                       lpcr |= ((ri->base_pfn >> (26 - PAGE_SHIFT)) & 0xffff)
+                               << HID4_RMOR_SH;
+               } else {
+                       /* POWER7 */
+                       lpcr &= ~(LPCR_VPM0 | LPCR_VRMA_L);
+                       lpcr |= rmls << LPCR_RMLS_SH;
+                       kvm->arch.rmor = kvm->arch.rma->base_pfn << PAGE_SHIFT;
+               }
                kvm->arch.lpcr = lpcr;
-               kvm->arch.rmor = kvm->arch.rma->base_pfn << PAGE_SHIFT;
                pr_info("Using RMO at %lx size %lx (LPCR = %lx)\n",
                        ri->base_pfn << PAGE_SHIFT, rma_size, lpcr);
        }
@@ -1151,11 +1175,25 @@ int kvmppc_core_init_vm(struct kvm *kvm)
        kvm->arch.rma = NULL;
        kvm->arch.n_rma_pages = 0;
 
-       lpcr = kvm->arch.host_lpcr & (LPCR_PECE | LPCR_LPES);
-       lpcr |= (4UL << LPCR_DPFD_SH) | LPCR_HDICE |
-               LPCR_VPM0 | LPCR_VRMA_L;
-       kvm->arch.lpcr = lpcr;
+       kvm->arch.host_sdr1 = mfspr(SPRN_SDR1);
 
+       if (cpu_has_feature(CPU_FTR_ARCH_201)) {
+               /* PPC970; HID4 is effectively the LPCR */
+               unsigned long lpid = kvm->arch.lpid;
+               kvm->arch.host_lpid = 0;
+               kvm->arch.host_lpcr = lpcr = mfspr(SPRN_HID4);
+               lpcr &= ~((3 << HID4_LPID1_SH) | (0xful << HID4_LPID5_SH));
+               lpcr |= ((lpid >> 4) << HID4_LPID1_SH) |
+                       ((lpid & 0xf) << HID4_LPID5_SH);
+       } else {
+               /* POWER7; init LPCR for virtual RMA mode */
+               kvm->arch.host_lpid = mfspr(SPRN_LPID);
+               kvm->arch.host_lpcr = lpcr = mfspr(SPRN_LPCR);
+               lpcr &= LPCR_PECE | LPCR_LPES;
+               lpcr |= (4UL << LPCR_DPFD_SH) | LPCR_HDICE |
+                       LPCR_VPM0 | LPCR_VRMA_L;
+       }
+       kvm->arch.lpcr = lpcr;
 
        return 0;
 
index 7315ec6e817798fc5f06bf0138112b241547aa93..d43120355eec15b8d61a0859a7575b2d8e1e6713 100644 (file)
@@ -55,12 +55,14 @@ static LIST_HEAD(free_rmas);
 static DEFINE_SPINLOCK(rma_lock);
 
 /* Work out RMLS (real mode limit selector) field value for a given RMA size.
-   Assumes POWER7. */
+   Assumes POWER7 or PPC970. */
 static inline int lpcr_rmls(unsigned long rma_size)
 {
        switch (rma_size) {
        case 32ul << 20:        /* 32 MB */
-               return 8;
+               if (cpu_has_feature(CPU_FTR_ARCH_206))
+                       return 8;       /* only supported on POWER7 */
+               return -1;
        case 64ul << 20:        /* 64 MB */
                return 3;
        case 128ul << 20:       /* 128 MB */
@@ -90,8 +92,9 @@ void kvm_rma_init(void)
        void *rma;
        struct page *pg;
 
-       /* Only do this in HV mode */
-       if (!cpu_has_feature(CPU_FTR_HVMODE))
+       /* Only do this on PPC970 in HV mode */
+       if (!cpu_has_feature(CPU_FTR_HVMODE) ||
+           !cpu_has_feature(CPU_FTR_ARCH_201))
                return;
 
        if (!kvm_rma_size || !kvm_rma_count)
index 532afaf19841918e8b4efbb83b860d7b482adc6f..3f7b674dd4bf4843ab37e649527149fd6143cf23 100644 (file)
@@ -50,8 +50,10 @@ _GLOBAL(__kvmppc_vcore_entry)
        SAVE_NVGPRS(r1)
 
        /* Save host DSCR */
+BEGIN_FTR_SECTION
        mfspr   r3, SPRN_DSCR
        std     r3, HSTATE_DSCR(r13)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /* Save host DABR */
        mfspr   r3, SPRN_DABR
@@ -86,12 +88,20 @@ _GLOBAL(__kvmppc_vcore_entry)
        mfspr   r7, SPRN_PMC4
        mfspr   r8, SPRN_PMC5
        mfspr   r9, SPRN_PMC6
+BEGIN_FTR_SECTION
+       mfspr   r10, SPRN_PMC7
+       mfspr   r11, SPRN_PMC8
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        stw     r3, HSTATE_PMC(r13)
        stw     r5, HSTATE_PMC + 4(r13)
        stw     r6, HSTATE_PMC + 8(r13)
        stw     r7, HSTATE_PMC + 12(r13)
        stw     r8, HSTATE_PMC + 16(r13)
        stw     r9, HSTATE_PMC + 20(r13)
+BEGIN_FTR_SECTION
+       stw     r10, HSTATE_PMC + 24(r13)
+       stw     r11, HSTATE_PMC + 28(r13)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
 31:
 
        /*
@@ -105,6 +115,26 @@ _GLOBAL(__kvmppc_vcore_entry)
        add     r8,r8,r7
        std     r8,HSTATE_DECEXP(r13)
 
+       /*
+        * On PPC970, if the guest vcpu has an external interrupt pending,
+        * send ourselves an IPI so as to interrupt the guest once it
+        * enables interrupts.  (It must have interrupts disabled,
+        * otherwise we would already have delivered the interrupt.)
+        */
+BEGIN_FTR_SECTION
+       ld      r0, VCPU_PENDING_EXC(r4)
+       li      r7, (1 << BOOK3S_IRQPRIO_EXTERNAL)
+       oris    r7, r7, (1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
+       and.    r0, r0, r7
+       beq     32f
+       mr      r31, r4
+       lhz     r3, PACAPACAINDEX(r13)
+       bl      smp_send_reschedule
+       nop
+       mr      r4, r31
+32:
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+
        /* Jump to partition switch code */
        bl      .kvmppc_hv_entry_trampoline
        nop
index edb0aae901a3e7b6c8f8dadeae063c2f3c2e781d..fcfe6b0555587b3afca7991fc66935deac69248e 100644 (file)
@@ -56,7 +56,8 @@ long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
        /* only handle 4k, 64k and 16M pages for now */
        porder = 12;
        if (pteh & HPTE_V_LARGE) {
-               if ((ptel & 0xf000) == 0x1000) {
+               if (cpu_has_feature(CPU_FTR_ARCH_206) &&
+                   (ptel & 0xf000) == 0x1000) {
                        /* 64k page */
                        porder = 16;
                } else if ((ptel & 0xff000) == 0) {
@@ -126,7 +127,8 @@ static unsigned long compute_tlbie_rb(unsigned long v, unsigned long r,
        va_low &= 0x7ff;
        if (v & HPTE_V_LARGE) {
                rb |= 1;                        /* L field */
-               if (r & 0xff000) {
+               if (cpu_has_feature(CPU_FTR_ARCH_206) &&
+                   (r & 0xff000)) {
                        /* non-16MB large page, must be 64k */
                        /* (masks depend on page size) */
                        rb |= 0x1000;           /* page encoding in LP field */
index 9ee223c35285c3c15b34394499323b96d5b47f32..6dd33581a22814afc30f0168ebd7ea21ae53e911 100644 (file)
@@ -148,12 +148,20 @@ kvmppc_hv_entry:
        lwz     r7, VCPU_PMC + 12(r4)
        lwz     r8, VCPU_PMC + 16(r4)
        lwz     r9, VCPU_PMC + 20(r4)
+BEGIN_FTR_SECTION
+       lwz     r10, VCPU_PMC + 24(r4)
+       lwz     r11, VCPU_PMC + 28(r4)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        mtspr   SPRN_PMC1, r3
        mtspr   SPRN_PMC2, r5
        mtspr   SPRN_PMC3, r6
        mtspr   SPRN_PMC4, r7
        mtspr   SPRN_PMC5, r8
        mtspr   SPRN_PMC6, r9
+BEGIN_FTR_SECTION
+       mtspr   SPRN_PMC7, r10
+       mtspr   SPRN_PMC8, r11
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        ld      r3, VCPU_MMCR(r4)
        ld      r5, VCPU_MMCR + 8(r4)
        ld      r6, VCPU_MMCR + 16(r4)
@@ -165,9 +173,11 @@ kvmppc_hv_entry:
        /* Load up FP, VMX and VSX registers */
        bl      kvmppc_load_fp
 
+BEGIN_FTR_SECTION
        /* Switch DSCR to guest value */
        ld      r5, VCPU_DSCR(r4)
        mtspr   SPRN_DSCR, r5
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /*
         * Set the decrementer to the guest decrementer.
@@ -210,6 +220,7 @@ kvmppc_hv_entry:
        mtspr   SPRN_DABRX,r5
        mtspr   SPRN_DABR,r6
 
+BEGIN_FTR_SECTION
        /* Restore AMR and UAMOR, set AMOR to all 1s */
        ld      r5,VCPU_AMR(r4)
        ld      r6,VCPU_UAMOR(r4)
@@ -217,6 +228,7 @@ kvmppc_hv_entry:
        mtspr   SPRN_AMR,r5
        mtspr   SPRN_UAMOR,r6
        mtspr   SPRN_AMOR,r7
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /* Clear out SLB */
        li      r6,0
@@ -224,6 +236,14 @@ kvmppc_hv_entry:
        slbia
        ptesync
 
+BEGIN_FTR_SECTION
+       b       30f
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+       /*
+        * POWER7 host -> guest partition switch code.
+        * We don't have to lock against concurrent tlbies,
+        * but we do have to coordinate across hardware threads.
+        */
        /* Increment entry count iff exit count is zero. */
        ld      r5,HSTATE_KVM_VCORE(r13)
        addi    r9,r5,VCORE_ENTRY_EXIT
@@ -315,9 +335,94 @@ kvmppc_hv_entry:
        ld      r8,VCPU_SPURR(r4)
        mtspr   SPRN_PURR,r7
        mtspr   SPRN_SPURR,r8
+       b       31f
+
+       /*
+        * PPC970 host -> guest partition switch code.
+        * We have to lock against concurrent tlbies,
+        * using native_tlbie_lock to lock against host tlbies
+        * and kvm->arch.tlbie_lock to lock against guest tlbies.
+        * We also have to invalidate the TLB since its
+        * entries aren't tagged with the LPID.
+        */
+30:    ld      r9,VCPU_KVM(r4)         /* pointer to struct kvm */
+
+       /* first take native_tlbie_lock */
+       .section ".toc","aw"
+toc_tlbie_lock:
+       .tc     native_tlbie_lock[TC],native_tlbie_lock
+       .previous
+       ld      r3,toc_tlbie_lock@toc(2)
+       lwz     r8,PACA_LOCK_TOKEN(r13)
+24:    lwarx   r0,0,r3
+       cmpwi   r0,0
+       bne     24b
+       stwcx.  r8,0,r3
+       bne     24b
+       isync
+
+       ld      r7,KVM_LPCR(r9)         /* use kvm->arch.lpcr to store HID4 */
+       li      r0,0x18f
+       rotldi  r0,r0,HID4_LPID5_SH     /* all lpid bits in HID4 = 1 */
+       or      r0,r7,r0
+       ptesync
+       sync
+       mtspr   SPRN_HID4,r0            /* switch to reserved LPID */
+       isync
+       li      r0,0
+       stw     r0,0(r3)                /* drop native_tlbie_lock */
+
+       /* invalidate the whole TLB */
+       li      r0,256
+       mtctr   r0
+       li      r6,0
+25:    tlbiel  r6
+       addi    r6,r6,0x1000
+       bdnz    25b
+       ptesync
+
+       /* Take the guest's tlbie_lock */
+       addi    r3,r9,KVM_TLBIE_LOCK
+24:    lwarx   r0,0,r3
+       cmpwi   r0,0
+       bne     24b
+       stwcx.  r8,0,r3
+       bne     24b
+       isync
+       ld      r6,KVM_SDR1(r9)
+       mtspr   SPRN_SDR1,r6            /* switch to partition page table */
+
+       /* Set up HID4 with the guest's LPID etc. */
+       sync
+       mtspr   SPRN_HID4,r7
+       isync
+
+       /* drop the guest's tlbie_lock */
+       li      r0,0
+       stw     r0,0(r3)
+
+       /* Check if HDEC expires soon */
+       mfspr   r3,SPRN_HDEC
+       cmpwi   r3,10
+       li      r12,BOOK3S_INTERRUPT_HV_DECREMENTER
+       mr      r9,r4
+       blt     hdec_soon
+
+       /* Enable HDEC interrupts */
+       mfspr   r0,SPRN_HID0
+       li      r3,1
+       rldimi  r0,r3, HID0_HDICE_SH, 64-HID0_HDICE_SH-1
+       sync
+       mtspr   SPRN_HID0,r0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
 
        /* Load up guest SLB entries */
-       lwz     r5,VCPU_SLB_MAX(r4)
+31:    lwz     r5,VCPU_SLB_MAX(r4)
        cmpwi   r5,0
        beq     9f
        mtctr   r5
@@ -472,6 +577,7 @@ kvmppc_interrupt:
 hcall_real_cont:
 
        /* Check for mediated interrupts (could be done earlier really ...) */
+BEGIN_FTR_SECTION
        cmpwi   r12,BOOK3S_INTERRUPT_EXTERNAL
        bne+    1f
        ld      r5,VCPU_KVM(r9)
@@ -481,6 +587,7 @@ hcall_real_cont:
        andi.   r0,r5,LPCR_MER
        bne     bounce_ext_interrupt
 1:
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /* Save DEC */
        mfspr   r5,SPRN_DEC
@@ -492,9 +599,11 @@ hcall_real_cont:
        /* Save HEIR (HV emulation assist reg) in last_inst
           if this is an HEI (HV emulation interrupt, e40) */
        li      r3,-1
+BEGIN_FTR_SECTION
        cmpwi   r12,BOOK3S_INTERRUPT_H_EMUL_ASSIST
        bne     11f
        mfspr   r3,SPRN_HEIR
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 11:    stw     r3,VCPU_LAST_INST(r9)
 
        /* Save more register state  */
@@ -508,8 +617,10 @@ hcall_real_cont:
        stw     r7, VCPU_DSISR(r9)
        std     r8, VCPU_CTR(r9)
        /* grab HDAR & HDSISR if HV data storage interrupt (HDSI) */
+BEGIN_FTR_SECTION
        cmpwi   r12,BOOK3S_INTERRUPT_H_DATA_STORAGE
        beq     6f
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 7:     std     r6, VCPU_FAULT_DAR(r9)
        stw     r7, VCPU_FAULT_DSISR(r9)
 
@@ -543,6 +654,7 @@ hcall_real_cont:
        /*
         * Save the guest PURR/SPURR
         */
+BEGIN_FTR_SECTION
        mfspr   r5,SPRN_PURR
        mfspr   r6,SPRN_SPURR
        ld      r7,VCPU_PURR(r9)
@@ -562,6 +674,7 @@ hcall_real_cont:
        add     r4,r4,r6
        mtspr   SPRN_PURR,r3
        mtspr   SPRN_SPURR,r4
+END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_201)
 
        /* Clear out SLB */
        li      r5,0
@@ -570,6 +683,14 @@ hcall_real_cont:
        ptesync
 
 hdec_soon:
+BEGIN_FTR_SECTION
+       b       32f
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+       /*
+        * POWER7 guest -> host partition switch code.
+        * We don't have to lock against tlbies but we do
+        * have to coordinate the hardware threads.
+        */
        /* Increment the threads-exiting-guest count in the 0xff00
           bits of vcore->entry_exit_count */
        lwsync
@@ -640,9 +761,82 @@ hdec_soon:
 16:    ld      r8,KVM_HOST_LPCR(r4)
        mtspr   SPRN_LPCR,r8
        isync
+       b       33f
+
+       /*
+        * PPC970 guest -> host partition switch code.
+        * We have to lock against concurrent tlbies, and
+        * we have to flush the whole TLB.
+        */
+32:    ld      r4,VCPU_KVM(r9)         /* pointer to struct kvm */
+
+       /* Take the guest's tlbie_lock */
+       lwz     r8,PACA_LOCK_TOKEN(r13)
+       addi    r3,r4,KVM_TLBIE_LOCK
+24:    lwarx   r0,0,r3
+       cmpwi   r0,0
+       bne     24b
+       stwcx.  r8,0,r3
+       bne     24b
+       isync
+
+       ld      r7,KVM_HOST_LPCR(r4)    /* use kvm->arch.host_lpcr for HID4 */
+       li      r0,0x18f
+       rotldi  r0,r0,HID4_LPID5_SH     /* all lpid bits in HID4 = 1 */
+       or      r0,r7,r0
+       ptesync
+       sync
+       mtspr   SPRN_HID4,r0            /* switch to reserved LPID */
+       isync
+       li      r0,0
+       stw     r0,0(r3)                /* drop guest tlbie_lock */
+
+       /* invalidate the whole TLB */
+       li      r0,256
+       mtctr   r0
+       li      r6,0
+25:    tlbiel  r6
+       addi    r6,r6,0x1000
+       bdnz    25b
+       ptesync
+
+       /* take native_tlbie_lock */
+       ld      r3,toc_tlbie_lock@toc(2)
+24:    lwarx   r0,0,r3
+       cmpwi   r0,0
+       bne     24b
+       stwcx.  r8,0,r3
+       bne     24b
+       isync
+
+       ld      r6,KVM_HOST_SDR1(r4)
+       mtspr   SPRN_SDR1,r6            /* switch to host page table */
+
+       /* Set up host HID4 value */
+       sync
+       mtspr   SPRN_HID4,r7
+       isync
+       li      r0,0
+       stw     r0,0(r3)                /* drop native_tlbie_lock */
+
+       lis     r8,0x7fff               /* MAX_INT@h */
+       mtspr   SPRN_HDEC,r8
+
+       /* Disable HDEC interrupts */
+       mfspr   r0,SPRN_HID0
+       li      r3,0
+       rldimi  r0,r3, HID0_HDICE_SH, 64-HID0_HDICE_SH-1
+       sync
+       mtspr   SPRN_HID0,r0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
+       mfspr   r0,SPRN_HID0
 
        /* load host SLB entries */
-       ld      r8,PACA_SLBSHADOWPTR(r13)
+33:    ld      r8,PACA_SLBSHADOWPTR(r13)
 
        .rept   SLB_NUM_BOLTED
        ld      r5,SLBSHADOW_SAVEAREA(r8)
@@ -654,12 +848,14 @@ hdec_soon:
        .endr
 
        /* Save and reset AMR and UAMOR before turning on the MMU */
+BEGIN_FTR_SECTION
        mfspr   r5,SPRN_AMR
        mfspr   r6,SPRN_UAMOR
        std     r5,VCPU_AMR(r9)
        std     r6,VCPU_UAMOR(r9)
        li      r6,0
        mtspr   SPRN_AMR,r6
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /* Restore host DABR and DABRX */
        ld      r5,HSTATE_DABR(r13)
@@ -668,10 +864,12 @@ hdec_soon:
        mtspr   SPRN_DABRX,r6
 
        /* Switch DSCR back to host value */
+BEGIN_FTR_SECTION
        mfspr   r8, SPRN_DSCR
        ld      r7, HSTATE_DSCR(r13)
        std     r8, VCPU_DSCR(r7)
        mtspr   SPRN_DSCR, r7
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /* Save non-volatile GPRs */
        std     r14, VCPU_GPR(r14)(r9)
@@ -735,21 +933,31 @@ hdec_soon:
        mfspr   r6, SPRN_PMC4
        mfspr   r7, SPRN_PMC5
        mfspr   r8, SPRN_PMC6
+BEGIN_FTR_SECTION
+       mfspr   r10, SPRN_PMC7
+       mfspr   r11, SPRN_PMC8
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        stw     r3, VCPU_PMC(r9)
        stw     r4, VCPU_PMC + 4(r9)
        stw     r5, VCPU_PMC + 8(r9)
        stw     r6, VCPU_PMC + 12(r9)
        stw     r7, VCPU_PMC + 16(r9)
        stw     r8, VCPU_PMC + 20(r9)
+BEGIN_FTR_SECTION
+       stw     r10, VCPU_PMC + 24(r9)
+       stw     r11, VCPU_PMC + 28(r9)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
 22:
        /* save FP state */
        mr      r3, r9
        bl      .kvmppc_save_fp
 
-       /* Secondary threads go off to take a nap */
+       /* Secondary threads go off to take a nap on POWER7 */
+BEGIN_FTR_SECTION
        lwz     r0,VCPU_PTID(r3)
        cmpwi   r0,0
        bne     secondary_nap
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
 
        /*
         * Reload DEC.  HDEC interrupts were disabled when
@@ -771,12 +979,20 @@ hdec_soon:
        lwz     r6, HSTATE_PMC + 12(r13)
        lwz     r8, HSTATE_PMC + 16(r13)
        lwz     r9, HSTATE_PMC + 20(r13)
+BEGIN_FTR_SECTION
+       lwz     r10, HSTATE_PMC + 24(r13)
+       lwz     r11, HSTATE_PMC + 28(r13)
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        mtspr   SPRN_PMC1, r3
        mtspr   SPRN_PMC2, r4
        mtspr   SPRN_PMC3, r5
        mtspr   SPRN_PMC4, r6
        mtspr   SPRN_PMC5, r8
        mtspr   SPRN_PMC6, r9
+BEGIN_FTR_SECTION
+       mtspr   SPRN_PMC7, r10
+       mtspr   SPRN_PMC8, r11
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
        ld      r3, HSTATE_MMCR(r13)
        ld      r4, HSTATE_MMCR + 8(r13)
        ld      r5, HSTATE_MMCR + 16(r13)
@@ -802,7 +1018,7 @@ hdec_soon:
        cmpwi   r12, BOOK3S_INTERRUPT_MACHINE_CHECK
 
        /* RFI into the highmem handler, or branch to interrupt handler */
-       mfmsr   r6
+12:    mfmsr   r6
        mtctr   r12
        li      r0, MSR_RI
        andc    r6, r6, r0
@@ -812,7 +1028,11 @@ hdec_soon:
        beqctr
        RFI
 
-11:    mtspr   SPRN_HSRR0, r8
+11:
+BEGIN_FTR_SECTION
+       b       12b
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201)
+       mtspr   SPRN_HSRR0, r8
        mtspr   SPRN_HSRR1, r7
        ba      0x500
 
index 72c506505fa4a3fe4c53075373f1ac6ec9d20c82..a107c9be0fb1c01a9cdbe5488706f91ef35732c9 100644 (file)
@@ -213,6 +213,9 @@ int kvm_dev_ioctl_check_extension(long ext)
                break;
        case KVM_CAP_PPC_RMA:
                r = 1;
+               /* PPC970 requires an RMA */
+               if (cpu_has_feature(CPU_FTR_ARCH_201))
+                       r = 2;
                break;
 #endif
        default:
index b44f5f803052e6db19ad74a3e2c5222f5eca0ec4..90039bc64119902c5bf85b79b4dd8bab31fd0e10 100644 (file)
@@ -37,7 +37,7 @@
 
 #define HPTE_LOCK_BIT 3
 
-static DEFINE_RAW_SPINLOCK(native_tlbie_lock);
+DEFINE_RAW_SPINLOCK(native_tlbie_lock);
 
 static inline void __tlbie(unsigned long va, int psize, int ssize)
 {