Merge tag 'kvm-s390-next-4.14-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorRadim Krčmář <rkrcmar@redhat.com>
Thu, 7 Sep 2017 14:46:46 +0000 (16:46 +0200)
committerRadim Krčmář <rkrcmar@redhat.com>
Thu, 7 Sep 2017 14:46:46 +0000 (16:46 +0200)
KVM: s390: Fixes and features for 4.14

- merge of topic branch tlb-flushing from the s390 tree to get the
  no-dat base features
- merge of kvm/master to avoid conflicts with additional sthyi fixes
- wire up the no-dat enhancements in KVM
- multiple epoch facility (z14 feature)
- Configuration z/Architecture Mode
- more sthyi fixes
- gdb server range checking fix
- small code cleanups

1  2 
arch/s390/kvm/kvm-s390.c
arch/s390/mm/pgtable.c

diff --combined arch/s390/kvm/kvm-s390.c
index 9f23a9e81a91d9006f6f52790d0875125914a732,39115f5a38df2b6cd52463ff9f0557427395d88f..40d0a1a97889b04f5e0bf1beb9d509721e1d5ff8
@@@ -130,6 -130,12 +130,12 @@@ struct kvm_stats_debugfs_item debugfs_e
        { NULL }
  };
  
+ struct kvm_s390_tod_clock_ext {
+       __u8 epoch_idx;
+       __u64 tod;
+       __u8 reserved[7];
+ } __packed;
  /* allow nested virtualization in KVM (if enabled by user space) */
  static int nested;
  module_param(nested, int, S_IRUGO);
@@@ -874,6 -880,26 +880,26 @@@ static int kvm_s390_vm_get_migration(st
        return 0;
  }
  
+ static int kvm_s390_set_tod_ext(struct kvm *kvm, struct kvm_device_attr *attr)
+ {
+       struct kvm_s390_vm_tod_clock gtod;
+       if (copy_from_user(&gtod, (void __user *)attr->addr, sizeof(gtod)))
+               return -EFAULT;
+       if (test_kvm_facility(kvm, 139))
+               kvm_s390_set_tod_clock_ext(kvm, &gtod);
+       else if (gtod.epoch_idx == 0)
+               kvm_s390_set_tod_clock(kvm, gtod.tod);
+       else
+               return -EINVAL;
+       VM_EVENT(kvm, 3, "SET: TOD extension: 0x%x, TOD base: 0x%llx",
+               gtod.epoch_idx, gtod.tod);
+       return 0;
+ }
  static int kvm_s390_set_tod_high(struct kvm *kvm, struct kvm_device_attr *attr)
  {
        u8 gtod_high;
@@@ -909,6 -935,9 +935,9 @@@ static int kvm_s390_set_tod(struct kvm 
                return -EINVAL;
  
        switch (attr->attr) {
+       case KVM_S390_VM_TOD_EXT:
+               ret = kvm_s390_set_tod_ext(kvm, attr);
+               break;
        case KVM_S390_VM_TOD_HIGH:
                ret = kvm_s390_set_tod_high(kvm, attr);
                break;
        return ret;
  }
  
+ static void kvm_s390_get_tod_clock_ext(struct kvm *kvm,
+                                       struct kvm_s390_vm_tod_clock *gtod)
+ {
+       struct kvm_s390_tod_clock_ext htod;
+       preempt_disable();
+       get_tod_clock_ext((char *)&htod);
+       gtod->tod = htod.tod + kvm->arch.epoch;
+       gtod->epoch_idx = htod.epoch_idx + kvm->arch.epdx;
+       if (gtod->tod < htod.tod)
+               gtod->epoch_idx += 1;
+       preempt_enable();
+ }
+ static int kvm_s390_get_tod_ext(struct kvm *kvm, struct kvm_device_attr *attr)
+ {
+       struct kvm_s390_vm_tod_clock gtod;
+       memset(&gtod, 0, sizeof(gtod));
+       if (test_kvm_facility(kvm, 139))
+               kvm_s390_get_tod_clock_ext(kvm, &gtod);
+       else
+               gtod.tod = kvm_s390_get_tod_clock_fast(kvm);
+       if (copy_to_user((void __user *)attr->addr, &gtod, sizeof(gtod)))
+               return -EFAULT;
+       VM_EVENT(kvm, 3, "QUERY: TOD extension: 0x%x, TOD base: 0x%llx",
+               gtod.epoch_idx, gtod.tod);
+       return 0;
+ }
  static int kvm_s390_get_tod_high(struct kvm *kvm, struct kvm_device_attr *attr)
  {
        u8 gtod_high = 0;
@@@ -954,6 -1020,9 +1020,9 @@@ static int kvm_s390_get_tod(struct kvm 
                return -EINVAL;
  
        switch (attr->attr) {
+       case KVM_S390_VM_TOD_EXT:
+               ret = kvm_s390_get_tod_ext(kvm, attr);
+               break;
        case KVM_S390_VM_TOD_HIGH:
                ret = kvm_s390_get_tod_high(kvm, attr);
                break;
@@@ -1505,7 -1574,7 +1574,7 @@@ static int kvm_s390_get_cmma_bits(struc
                if (r < 0)
                        pgstev = 0;
                /* save the value */
-               res[i++] = (pgstev >> 24) & 0x3;
+               res[i++] = (pgstev >> 24) & 0x43;
                /*
                 * if the next bit is too far away, stop.
                 * if we reached the previous "next", find the next one
@@@ -1583,7 -1652,7 +1652,7 @@@ static int kvm_s390_set_cmma_bits(struc
  
                pgstev = bits[i];
                pgstev = pgstev << 24;
-               mask &= _PGSTE_GPS_USAGE_MASK;
+               mask &= _PGSTE_GPS_USAGE_MASK | _PGSTE_GPS_NODAT;
                set_pgste_bits(kvm->mm, hva, mask, pgstev);
        }
        srcu_read_unlock(&kvm->srcu, srcu_idx);
@@@ -1858,8 -1927,16 +1927,16 @@@ int kvm_arch_init_vm(struct kvm *kvm, u
        memcpy(kvm->arch.model.fac_list, kvm->arch.model.fac_mask,
               S390_ARCH_FAC_LIST_SIZE_BYTE);
  
+       /* we are always in czam mode - even on pre z14 machines */
+       set_kvm_facility(kvm->arch.model.fac_mask, 138);
+       set_kvm_facility(kvm->arch.model.fac_list, 138);
+       /* we emulate STHYI in kvm */
        set_kvm_facility(kvm->arch.model.fac_mask, 74);
        set_kvm_facility(kvm->arch.model.fac_list, 74);
+       if (MACHINE_HAS_TLB_GUEST) {
+               set_kvm_facility(kvm->arch.model.fac_mask, 147);
+               set_kvm_facility(kvm->arch.model.fac_list, 147);
+       }
  
        kvm->arch.model.cpuid = kvm_s390_get_initial_cpuid();
        kvm->arch.model.ibc = sclp.ibc & 0x0fff;
@@@ -2369,6 -2446,9 +2446,9 @@@ int kvm_arch_vcpu_setup(struct kvm_vcp
                vcpu->arch.sie_block->eca |= ECA_VX;
                vcpu->arch.sie_block->ecd |= ECD_HOSTREGMGMT;
        }
+       if (test_kvm_facility(vcpu->kvm, 139))
+               vcpu->arch.sie_block->ecd |= ECD_MEF;
        vcpu->arch.sie_block->sdnxo = ((unsigned long) &vcpu->run->s.regs.sdnx)
                                        | SDNXC;
        vcpu->arch.sie_block->riccbd = (unsigned long) &vcpu->run->s.regs.riccb;
@@@ -2447,11 -2527,6 +2527,11 @@@ int kvm_arch_vcpu_runnable(struct kvm_v
        return kvm_s390_vcpu_has_irq(vcpu, 0);
  }
  
 +bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
 +{
 +      return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE);
 +}
 +
  void kvm_s390_vcpu_block(struct kvm_vcpu *vcpu)
  {
        atomic_or(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20);
@@@ -2860,6 -2935,35 +2940,35 @@@ retry
        return 0;
  }
  
+ void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
+                                const struct kvm_s390_vm_tod_clock *gtod)
+ {
+       struct kvm_vcpu *vcpu;
+       struct kvm_s390_tod_clock_ext htod;
+       int i;
+       mutex_lock(&kvm->lock);
+       preempt_disable();
+       get_tod_clock_ext((char *)&htod);
+       kvm->arch.epoch = gtod->tod - htod.tod;
+       kvm->arch.epdx = gtod->epoch_idx - htod.epoch_idx;
+       if (kvm->arch.epoch > gtod->tod)
+               kvm->arch.epdx -= 1;
+       kvm_s390_vcpu_block_all(kvm);
+       kvm_for_each_vcpu(i, vcpu, kvm) {
+               vcpu->arch.sie_block->epoch = kvm->arch.epoch;
+               vcpu->arch.sie_block->epdx  = kvm->arch.epdx;
+       }
+       kvm_s390_vcpu_unblock_all(kvm);
+       preempt_enable();
+       mutex_unlock(&kvm->lock);
+ }
  void kvm_s390_set_tod_clock(struct kvm *kvm, u64 tod)
  {
        struct kvm_vcpu *vcpu;
diff --combined arch/s390/mm/pgtable.c
index 4a1f7366b17aeffacb6c766ab891227e2609f1b9,459716de5318a76c44853a94a951dca9dc660f8b..ae677f814bc07a406f7f996a81ed2db65718f5ff
  #include <asm/mmu_context.h>
  #include <asm/page-states.h>
  
+ static inline void ptep_ipte_local(struct mm_struct *mm, unsigned long addr,
+                                  pte_t *ptep, int nodat)
+ {
+       unsigned long opt, asce;
+       if (MACHINE_HAS_TLB_GUEST) {
+               opt = 0;
+               asce = READ_ONCE(mm->context.gmap_asce);
+               if (asce == 0UL || nodat)
+                       opt |= IPTE_NODAT;
+               if (asce != -1UL) {
+                       asce = asce ? : mm->context.asce;
+                       opt |= IPTE_GUEST_ASCE;
+               }
+               __ptep_ipte(addr, ptep, opt, asce, IPTE_LOCAL);
+       } else {
+               __ptep_ipte(addr, ptep, 0, 0, IPTE_LOCAL);
+       }
+ }
+ static inline void ptep_ipte_global(struct mm_struct *mm, unsigned long addr,
+                                   pte_t *ptep, int nodat)
+ {
+       unsigned long opt, asce;
+       if (MACHINE_HAS_TLB_GUEST) {
+               opt = 0;
+               asce = READ_ONCE(mm->context.gmap_asce);
+               if (asce == 0UL || nodat)
+                       opt |= IPTE_NODAT;
+               if (asce != -1UL) {
+                       asce = asce ? : mm->context.asce;
+                       opt |= IPTE_GUEST_ASCE;
+               }
+               __ptep_ipte(addr, ptep, opt, asce, IPTE_GLOBAL);
+       } else {
+               __ptep_ipte(addr, ptep, 0, 0, IPTE_GLOBAL);
+       }
+ }
  static inline pte_t ptep_flush_direct(struct mm_struct *mm,
-                                     unsigned long addr, pte_t *ptep)
+                                     unsigned long addr, pte_t *ptep,
+                                     int nodat)
  {
        pte_t old;
  
        atomic_inc(&mm->context.flush_count);
        if (MACHINE_HAS_TLB_LC &&
            cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
-               __ptep_ipte(addr, ptep, IPTE_LOCAL);
+               ptep_ipte_local(mm, addr, ptep, nodat);
        else
-               __ptep_ipte(addr, ptep, IPTE_GLOBAL);
+               ptep_ipte_global(mm, addr, ptep, nodat);
        atomic_dec(&mm->context.flush_count);
        return old;
  }
  
  static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
-                                   unsigned long addr, pte_t *ptep)
+                                   unsigned long addr, pte_t *ptep,
+                                   int nodat)
  {
        pte_t old;
  
@@@ -57,7 -99,7 +99,7 @@@
                pte_val(*ptep) |= _PAGE_INVALID;
                mm->context.flush_mm = 1;
        } else
-               __ptep_ipte(addr, ptep, IPTE_GLOBAL);
+               ptep_ipte_global(mm, addr, ptep, nodat);
        atomic_dec(&mm->context.flush_count);
        return old;
  }
@@@ -229,10 -271,12 +271,12 @@@ pte_t ptep_xchg_direct(struct mm_struc
  {
        pgste_t pgste;
        pte_t old;
+       int nodat;
  
        preempt_disable();
        pgste = ptep_xchg_start(mm, addr, ptep);
-       old = ptep_flush_direct(mm, addr, ptep);
+       nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT);
+       old = ptep_flush_direct(mm, addr, ptep, nodat);
        old = ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
        preempt_enable();
        return old;
@@@ -244,10 -288,12 +288,12 @@@ pte_t ptep_xchg_lazy(struct mm_struct *
  {
        pgste_t pgste;
        pte_t old;
+       int nodat;
  
        preempt_disable();
        pgste = ptep_xchg_start(mm, addr, ptep);
-       old = ptep_flush_lazy(mm, addr, ptep);
+       nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT);
+       old = ptep_flush_lazy(mm, addr, ptep, nodat);
        old = ptep_xchg_commit(mm, addr, ptep, pgste, old, new);
        preempt_enable();
        return old;
@@@ -259,10 -305,12 +305,12 @@@ pte_t ptep_modify_prot_start(struct mm_
  {
        pgste_t pgste;
        pte_t old;
+       int nodat;
  
        preempt_disable();
        pgste = ptep_xchg_start(mm, addr, ptep);
-       old = ptep_flush_lazy(mm, addr, ptep);
+       nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT);
+       old = ptep_flush_lazy(mm, addr, ptep, nodat);
        if (mm_has_pgste(mm)) {
                pgste = pgste_update_all(old, pgste, mm);
                pgste_set(ptep, pgste);
@@@ -290,6 -338,28 +338,28 @@@ void ptep_modify_prot_commit(struct mm_
  }
  EXPORT_SYMBOL(ptep_modify_prot_commit);
  
+ static inline void pmdp_idte_local(struct mm_struct *mm,
+                                  unsigned long addr, pmd_t *pmdp)
+ {
+       if (MACHINE_HAS_TLB_GUEST)
+               __pmdp_idte(addr, pmdp, IDTE_NODAT | IDTE_GUEST_ASCE,
+                           mm->context.asce, IDTE_LOCAL);
+       else
+               __pmdp_idte(addr, pmdp, 0, 0, IDTE_LOCAL);
+ }
+ static inline void pmdp_idte_global(struct mm_struct *mm,
+                                   unsigned long addr, pmd_t *pmdp)
+ {
+       if (MACHINE_HAS_TLB_GUEST)
+               __pmdp_idte(addr, pmdp, IDTE_NODAT | IDTE_GUEST_ASCE,
+                           mm->context.asce, IDTE_GLOBAL);
+       else if (MACHINE_HAS_IDTE)
+               __pmdp_idte(addr, pmdp, 0, 0, IDTE_GLOBAL);
+       else
+               __pmdp_csp(pmdp);
+ }
  static inline pmd_t pmdp_flush_direct(struct mm_struct *mm,
                                      unsigned long addr, pmd_t *pmdp)
  {
        old = *pmdp;
        if (pmd_val(old) & _SEGMENT_ENTRY_INVALID)
                return old;
-       if (!MACHINE_HAS_IDTE) {
-               __pmdp_csp(pmdp);
-               return old;
-       }
        atomic_inc(&mm->context.flush_count);
        if (MACHINE_HAS_TLB_LC &&
            cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
-               __pmdp_idte(addr, pmdp, IDTE_LOCAL);
+               pmdp_idte_local(mm, addr, pmdp);
        else
-               __pmdp_idte(addr, pmdp, IDTE_GLOBAL);
+               pmdp_idte_global(mm, addr, pmdp);
        atomic_dec(&mm->context.flush_count);
        return old;
  }
@@@ -325,10 -391,9 +391,9 @@@ static inline pmd_t pmdp_flush_lazy(str
                          cpumask_of(smp_processor_id()))) {
                pmd_val(*pmdp) |= _SEGMENT_ENTRY_INVALID;
                mm->context.flush_mm = 1;
-       } else if (MACHINE_HAS_IDTE)
-               __pmdp_idte(addr, pmdp, IDTE_GLOBAL);
-       else
-               __pmdp_csp(pmdp);
+       } else {
+               pmdp_idte_global(mm, addr, pmdp);
+       }
        atomic_dec(&mm->context.flush_count);
        return old;
  }
@@@ -359,28 -424,46 +424,46 @@@ pmd_t pmdp_xchg_lazy(struct mm_struct *
  }
  EXPORT_SYMBOL(pmdp_xchg_lazy);
  
- static inline pud_t pudp_flush_direct(struct mm_struct *mm,
-                                     unsigned long addr, pud_t *pudp)
+ static inline void pudp_idte_local(struct mm_struct *mm,
+                                  unsigned long addr, pud_t *pudp)
  {
-       pud_t old;
+       if (MACHINE_HAS_TLB_GUEST)
+               __pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE,
+                           mm->context.asce, IDTE_LOCAL);
+       else
+               __pudp_idte(addr, pudp, 0, 0, IDTE_LOCAL);
+ }
  
-       old = *pudp;
-       if (pud_val(old) & _REGION_ENTRY_INVALID)
-               return old;
-       if (!MACHINE_HAS_IDTE) {
+ static inline void pudp_idte_global(struct mm_struct *mm,
+                                   unsigned long addr, pud_t *pudp)
+ {
+       if (MACHINE_HAS_TLB_GUEST)
+               __pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE,
+                           mm->context.asce, IDTE_GLOBAL);
+       else if (MACHINE_HAS_IDTE)
+               __pudp_idte(addr, pudp, 0, 0, IDTE_GLOBAL);
+       else
                /*
                 * Invalid bit position is the same for pmd and pud, so we can
                 * re-use _pmd_csp() here
                 */
                __pmdp_csp((pmd_t *) pudp);
+ }
+ static inline pud_t pudp_flush_direct(struct mm_struct *mm,
+                                     unsigned long addr, pud_t *pudp)
+ {
+       pud_t old;
+       old = *pudp;
+       if (pud_val(old) & _REGION_ENTRY_INVALID)
                return old;
-       }
        atomic_inc(&mm->context.flush_count);
        if (MACHINE_HAS_TLB_LC &&
            cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
-               __pudp_idte(addr, pudp, IDTE_LOCAL);
+               pudp_idte_local(mm, addr, pudp);
        else
-               __pudp_idte(addr, pudp, IDTE_GLOBAL);
+               pudp_idte_global(mm, addr, pudp);
        atomic_dec(&mm->context.flush_count);
        return old;
  }
@@@ -482,7 -565,7 +565,7 @@@ int ptep_force_prot(struct mm_struct *m
  {
        pte_t entry;
        pgste_t pgste;
-       int pte_i, pte_p;
+       int pte_i, pte_p, nodat;
  
        pgste = pgste_get_lock(ptep);
        entry = *ptep;
                return -EAGAIN;
        }
        /* Change access rights and set pgste bit */
+       nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT);
        if (prot == PROT_NONE && !pte_i) {
-               ptep_flush_direct(mm, addr, ptep);
+               ptep_flush_direct(mm, addr, ptep, nodat);
                pgste = pgste_update_all(entry, pgste, mm);
                pte_val(entry) |= _PAGE_INVALID;
        }
        if (prot == PROT_READ && !pte_p) {
-               ptep_flush_direct(mm, addr, ptep);
+               ptep_flush_direct(mm, addr, ptep, nodat);
                pte_val(entry) &= ~_PAGE_INVALID;
                pte_val(entry) |= _PAGE_PROTECT;
        }
@@@ -541,10 -625,12 +625,12 @@@ int ptep_shadow_pte(struct mm_struct *m
  void ptep_unshadow_pte(struct mm_struct *mm, unsigned long saddr, pte_t *ptep)
  {
        pgste_t pgste;
+       int nodat;
  
        pgste = pgste_get_lock(ptep);
        /* notifier is called by the caller */
-       ptep_flush_direct(mm, saddr, ptep);
+       nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT);
+       ptep_flush_direct(mm, saddr, ptep, nodat);
        /* don't touch the storage key - it belongs to parent pgste */
        pgste = pgste_set_pte(ptep, pgste, __pte(_PAGE_INVALID));
        pgste_set_unlock(ptep, pgste);
@@@ -591,11 -677,11 +677,11 @@@ void ptep_zap_key(struct mm_struct *mm
        unsigned long ptev;
        pgste_t pgste;
  
 -      /* Clear storage key */
 +      /* Clear storage key ACC and F, but set R/C */
        preempt_disable();
        pgste = pgste_get_lock(ptep);
 -      pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT |
 -                            PGSTE_GR_BIT | PGSTE_GC_BIT);
 +      pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT);
 +      pgste_val(pgste) |= PGSTE_GR_BIT | PGSTE_GC_BIT;
        ptev = pte_val(*ptep);
        if (!(ptev & _PAGE_INVALID) && (ptev & _PAGE_WRITE))
                page_set_storage_key(ptev & PAGE_MASK, PAGE_DEFAULT_KEY, 1);
@@@ -617,6 -703,7 +703,7 @@@ bool test_and_clear_guest_dirty(struct 
        pte_t *ptep;
        pte_t pte;
        bool dirty;
+       int nodat;
  
        pgd = pgd_offset(mm, addr);
        p4d = p4d_alloc(mm, pgd, addr);
        pte = *ptep;
        if (dirty && (pte_val(pte) & _PAGE_PRESENT)) {
                pgste = pgste_pte_notify(mm, addr, ptep, pgste);
-               __ptep_ipte(addr, ptep, IPTE_GLOBAL);
+               nodat = !!(pgste_val(pgste) & _PGSTE_GPS_NODAT);
+               ptep_ipte_global(mm, addr, ptep, nodat);
                if (MACHINE_HAS_ESOP || !(pte_val(pte) & _PAGE_WRITE))
                        pte_val(pte) |= _PAGE_PROTECT;
                else
@@@ -831,7 -919,7 +919,7 @@@ int pgste_perform_essa(struct mm_struc
        case ESSA_GET_STATE:
                break;
        case ESSA_SET_STABLE:
-               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               pgstev &= ~(_PGSTE_GPS_USAGE_MASK | _PGSTE_GPS_NODAT);
                pgstev |= _PGSTE_GPS_USAGE_STABLE;
                break;
        case ESSA_SET_UNUSED:
                        pgstev |= _PGSTE_GPS_USAGE_STABLE;
                }
                break;
+       case ESSA_SET_STABLE_NODAT:
+               pgstev &= ~_PGSTE_GPS_USAGE_MASK;
+               pgstev |= _PGSTE_GPS_USAGE_STABLE | _PGSTE_GPS_NODAT;
+               break;
        default:
                /* we should never get here! */
                break;