KVM: PPC: e500: Add support for TLBnPS registers
[firefly-linux-kernel-4.4.55.git] / arch / powerpc / kvm / e500_mmu.c
index 5c4475983f7843c0f2b6c80a488c9e8f074ca5eb..a863dc1791eb21c35a0b35043329a05a60fbaf3c 100644 (file)
@@ -596,6 +596,131 @@ int kvmppc_set_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)
        return 0;
 }
 
+int kvmppc_get_one_reg_e500_tlb(struct kvm_vcpu *vcpu, u64 id,
+                               union kvmppc_one_reg *val)
+{
+       int r = 0;
+       long int i;
+
+       switch (id) {
+       case KVM_REG_PPC_MAS0:
+               *val = get_reg_val(id, vcpu->arch.shared->mas0);
+               break;
+       case KVM_REG_PPC_MAS1:
+               *val = get_reg_val(id, vcpu->arch.shared->mas1);
+               break;
+       case KVM_REG_PPC_MAS2:
+               *val = get_reg_val(id, vcpu->arch.shared->mas2);
+               break;
+       case KVM_REG_PPC_MAS7_3:
+               *val = get_reg_val(id, vcpu->arch.shared->mas7_3);
+               break;
+       case KVM_REG_PPC_MAS4:
+               *val = get_reg_val(id, vcpu->arch.shared->mas4);
+               break;
+       case KVM_REG_PPC_MAS6:
+               *val = get_reg_val(id, vcpu->arch.shared->mas6);
+               break;
+       case KVM_REG_PPC_MMUCFG:
+               *val = get_reg_val(id, vcpu->arch.mmucfg);
+               break;
+       case KVM_REG_PPC_TLB0CFG:
+       case KVM_REG_PPC_TLB1CFG:
+       case KVM_REG_PPC_TLB2CFG:
+       case KVM_REG_PPC_TLB3CFG:
+               i = id - KVM_REG_PPC_TLB0CFG;
+               *val = get_reg_val(id, vcpu->arch.tlbcfg[i]);
+               break;
+       case KVM_REG_PPC_TLB0PS:
+       case KVM_REG_PPC_TLB1PS:
+       case KVM_REG_PPC_TLB2PS:
+       case KVM_REG_PPC_TLB3PS:
+               i = id - KVM_REG_PPC_TLB0PS;
+               *val = get_reg_val(id, vcpu->arch.tlbps[i]);
+               break;
+       default:
+               r = -EINVAL;
+               break;
+       }
+
+       return r;
+}
+
+int kvmppc_set_one_reg_e500_tlb(struct kvm_vcpu *vcpu, u64 id,
+                              union kvmppc_one_reg *val)
+{
+       int r = 0;
+       long int i;
+
+       switch (id) {
+       case KVM_REG_PPC_MAS0:
+               vcpu->arch.shared->mas0 = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_MAS1:
+               vcpu->arch.shared->mas1 = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_MAS2:
+               vcpu->arch.shared->mas2 = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_MAS7_3:
+               vcpu->arch.shared->mas7_3 = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_MAS4:
+               vcpu->arch.shared->mas4 = set_reg_val(id, *val);
+               break;
+       case KVM_REG_PPC_MAS6:
+               vcpu->arch.shared->mas6 = set_reg_val(id, *val);
+               break;
+       /* Only allow MMU registers to be set to the config supported by KVM */
+       case KVM_REG_PPC_MMUCFG: {
+               u32 reg = set_reg_val(id, *val);
+               if (reg != vcpu->arch.mmucfg)
+                       r = -EINVAL;
+               break;
+       }
+       case KVM_REG_PPC_TLB0CFG:
+       case KVM_REG_PPC_TLB1CFG:
+       case KVM_REG_PPC_TLB2CFG:
+       case KVM_REG_PPC_TLB3CFG: {
+               /* MMU geometry (N_ENTRY/ASSOC) can be set only using SW_TLB */
+               u32 reg = set_reg_val(id, *val);
+               i = id - KVM_REG_PPC_TLB0CFG;
+               if (reg != vcpu->arch.tlbcfg[i])
+                       r = -EINVAL;
+               break;
+       }
+       case KVM_REG_PPC_TLB0PS:
+       case KVM_REG_PPC_TLB1PS:
+       case KVM_REG_PPC_TLB2PS:
+       case KVM_REG_PPC_TLB3PS: {
+               u32 reg = set_reg_val(id, *val);
+               i = id - KVM_REG_PPC_TLB0PS;
+               if (reg != vcpu->arch.tlbps[i])
+                       r = -EINVAL;
+               break;
+       }
+       default:
+               r = -EINVAL;
+               break;
+       }
+
+       return r;
+}
+
+static int vcpu_mmu_geometry_update(struct kvm_vcpu *vcpu,
+               struct kvm_book3e_206_tlb_params *params)
+{
+       vcpu->arch.tlbcfg[0] &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
+       if (params->tlb_sizes[0] <= 2048)
+               vcpu->arch.tlbcfg[0] |= params->tlb_sizes[0];
+       vcpu->arch.tlbcfg[0] |= params->tlb_ways[0] << TLBnCFG_ASSOC_SHIFT;
+
+       vcpu->arch.tlbcfg[1] &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
+       vcpu->arch.tlbcfg[1] |= params->tlb_sizes[1];
+       vcpu->arch.tlbcfg[1] |= params->tlb_ways[1] << TLBnCFG_ASSOC_SHIFT;
+       return 0;
+}
+
 int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu,
                              struct kvm_config_tlb *cfg)
 {
@@ -692,16 +817,8 @@ int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu,
        vcpu_e500->gtlb_offset[0] = 0;
        vcpu_e500->gtlb_offset[1] = params.tlb_sizes[0];
 
-       vcpu->arch.mmucfg = mfspr(SPRN_MMUCFG) & ~MMUCFG_LPIDSIZE;
-
-       vcpu->arch.tlbcfg[0] &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
-       if (params.tlb_sizes[0] <= 2048)
-               vcpu->arch.tlbcfg[0] |= params.tlb_sizes[0];
-       vcpu->arch.tlbcfg[0] |= params.tlb_ways[0] << TLBnCFG_ASSOC_SHIFT;
-
-       vcpu->arch.tlbcfg[1] &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
-       vcpu->arch.tlbcfg[1] |= params.tlb_sizes[1];
-       vcpu->arch.tlbcfg[1] |= params.tlb_ways[1] << TLBnCFG_ASSOC_SHIFT;
+       /* Update vcpu's MMU geometry based on SW_TLB input */
+       vcpu_mmu_geometry_update(vcpu, &params);
 
        vcpu_e500->shared_tlb_pages = pages;
        vcpu_e500->num_shared_tlb_pages = num_pages;
@@ -737,6 +854,32 @@ int kvm_vcpu_ioctl_dirty_tlb(struct kvm_vcpu *vcpu,
        return 0;
 }
 
+/* Vcpu's MMU default configuration */
+static int vcpu_mmu_init(struct kvm_vcpu *vcpu,
+                      struct kvmppc_e500_tlb_params *params)
+{
+       /* Initialize RASIZE, PIDSIZE, NTLBS and MAVN fields with host values*/
+       vcpu->arch.mmucfg = mfspr(SPRN_MMUCFG) & ~MMUCFG_LPIDSIZE;
+
+       /* Initialize TLBnCFG fields with host values and SW_TLB geometry*/
+       vcpu->arch.tlbcfg[0] = mfspr(SPRN_TLB0CFG) &
+                            ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
+       vcpu->arch.tlbcfg[0] |= params[0].entries;
+       vcpu->arch.tlbcfg[0] |= params[0].ways << TLBnCFG_ASSOC_SHIFT;
+
+       vcpu->arch.tlbcfg[1] = mfspr(SPRN_TLB1CFG) &
+                            ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
+       vcpu->arch.tlbcfg[1] |= params[1].entries;
+       vcpu->arch.tlbcfg[1] |= params[1].ways << TLBnCFG_ASSOC_SHIFT;
+
+       if (has_feature(vcpu, VCPU_FTR_MMU_V2)) {
+               vcpu->arch.tlbps[0] = mfspr(SPRN_TLB0PS);
+               vcpu->arch.tlbps[1] = mfspr(SPRN_TLB1PS);
+       }
+
+       return 0;
+}
+
 int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
 {
        struct kvm_vcpu *vcpu = &vcpu_e500->vcpu;
@@ -781,18 +924,7 @@ int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)
        if (!vcpu_e500->g2h_tlb1_map)
                goto err;
 
-       /* Init TLB configuration register */
-       vcpu->arch.tlbcfg[0] = mfspr(SPRN_TLB0CFG) &
-                            ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
-       vcpu->arch.tlbcfg[0] |= vcpu_e500->gtlb_params[0].entries;
-       vcpu->arch.tlbcfg[0] |=
-               vcpu_e500->gtlb_params[0].ways << TLBnCFG_ASSOC_SHIFT;
-
-       vcpu->arch.tlbcfg[1] = mfspr(SPRN_TLB1CFG) &
-                            ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC);
-       vcpu->arch.tlbcfg[1] |= vcpu_e500->gtlb_params[1].entries;
-       vcpu->arch.tlbcfg[1] |=
-               vcpu_e500->gtlb_params[1].ways << TLBnCFG_ASSOC_SHIFT;
+       vcpu_mmu_init(vcpu, vcpu_e500->gtlb_params);
 
        kvmppc_recalc_tlb1map_range(vcpu_e500);
        return 0;