]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
KVM: MIPS/T&E: Implement CP0_EBase register
authorJames Hogan <james.hogan@imgtec.com>
Mon, 14 Nov 2016 23:59:27 +0000 (23:59 +0000)
committerJames Hogan <james.hogan@imgtec.com>
Fri, 3 Feb 2017 15:21:30 +0000 (15:21 +0000)
The CP0_EBase register is a standard feature of MIPS32r2, so we should
always have been implementing it properly. However the register value
was ignored and wasn't exposed to userland.

Fix the emulation of exceptions and interrupts to use the value stored
in guest CP0_EBase, and fix the masks so that the top 3 bits (rather
than the standard 2) are fixed, so that it is always in the guest KSeg0
segment.

Also add CP0_EBASE to the KVM one_reg interface so it can be accessed by
userland, also allowing the CPU number field to be written (which isn't
permitted by the guest).

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: "Radim Krčmář" <rkrcmar@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-mips@linux-mips.org
Cc: kvm@vger.kernel.org
Documentation/virtual/kvm/api.txt
arch/mips/include/asm/kvm_host.h
arch/mips/kvm/emulate.c
arch/mips/kvm/interrupt.c
arch/mips/kvm/trap_emul.c

index 03145b7cafaa36e1906bb35402978fafe94289a1..8d52d0f990ae04bada0d21659dddd64f8220c39e 100644 (file)
@@ -2074,6 +2074,7 @@ registers, find a list below:
   MIPS  | KVM_REG_MIPS_CP0_CAUSE        | 32
   MIPS  | KVM_REG_MIPS_CP0_EPC          | 64
   MIPS  | KVM_REG_MIPS_CP0_PRID         | 32
+  MIPS  | KVM_REG_MIPS_CP0_EBASE        | 64
   MIPS  | KVM_REG_MIPS_CP0_CONFIG       | 32
   MIPS  | KVM_REG_MIPS_CP0_CONFIG1      | 32
   MIPS  | KVM_REG_MIPS_CP0_CONFIG2      | 32
index bc56a312497dd9af6aff11baf1745db183b9c515..420372fa5bbce97c3a1b9ceeeb3dd8cba58e8848 100644 (file)
@@ -88,6 +88,7 @@
 
 #define KVM_GUEST_KUSEG                        0x00000000UL
 #define KVM_GUEST_KSEG0                        0x40000000UL
+#define KVM_GUEST_KSEG1                        0x40000000UL
 #define KVM_GUEST_KSEG23               0x60000000UL
 #define KVM_GUEST_KSEGX(a)             ((_ACAST32_(a)) & 0xe0000000)
 #define KVM_GUEST_CPHYSADDR(a)         ((_ACAST32_(a)) & 0x1fffffff)
@@ -713,6 +714,8 @@ extern enum emulation_result kvm_mips_emulate_inst(u32 cause,
                                                   struct kvm_run *run,
                                                   struct kvm_vcpu *vcpu);
 
+long kvm_mips_guest_exception_base(struct kvm_vcpu *vcpu);
+
 extern enum emulation_result kvm_mips_emulate_syscall(u32 cause,
                                                      u32 *opc,
                                                      struct kvm_run *run,
index f2b054b80bca376a2e67ea0e342ccbff5f5a32d4..d40cfaad45295c8631f306a96fbb0d713e2a864a 100644 (file)
@@ -1200,14 +1200,13 @@ enum emulation_result kvm_mips_emulate_CP0(union mips_instruction inst,
                                er = EMULATE_FAIL;
                                break;
                        }
-#define C0_EBASE_CORE_MASK 0xff
                        if ((rd == MIPS_CP0_PRID) && (sel == 1)) {
-                               /* Preserve CORE number */
-                               kvm_change_c0_guest_ebase(cop0,
-                                                         ~(C0_EBASE_CORE_MASK),
+                               /*
+                                * Preserve core number, and keep the exception
+                                * base in guest KSeg0.
+                                */
+                               kvm_change_c0_guest_ebase(cop0, 0x1ffff000,
                                                          vcpu->arch.gprs[rt]);
-                               kvm_err("MTCz, cop0->reg[EBASE]: %#lx\n",
-                                       kvm_read_c0_guest_ebase(cop0));
                        } else if (rd == MIPS_CP0_TLB_HI && sel == 0) {
                                u32 nasid =
                                        vcpu->arch.gprs[rt] & KVM_ENTRYHI_ASID;
@@ -1917,6 +1916,22 @@ enum emulation_result kvm_mips_emulate_inst(u32 cause, u32 *opc,
        return er;
 }
 
+/**
+ * kvm_mips_guest_exception_base() - Find guest exception vector base address.
+ *
+ * Returns:    The base address of the current guest exception vector, taking
+ *             both Guest.CP0_Status.BEV and Guest.CP0_EBase into account.
+ */
+long kvm_mips_guest_exception_base(struct kvm_vcpu *vcpu)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+
+       if (kvm_read_c0_guest_status(cop0) & ST0_BEV)
+               return KVM_GUEST_CKSEG1ADDR(0x1fc00200);
+       else
+               return kvm_read_c0_guest_ebase(cop0) & MIPS_EBASE_BASE;
+}
+
 enum emulation_result kvm_mips_emulate_syscall(u32 cause,
                                               u32 *opc,
                                               struct kvm_run *run,
@@ -1942,7 +1957,7 @@ enum emulation_result kvm_mips_emulate_syscall(u32 cause,
                                          (EXCCODE_SYS << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver SYSCALL when EXL is already set\n");
@@ -1976,13 +1991,13 @@ enum emulation_result kvm_mips_emulate_tlbmiss_ld(u32 cause,
                          arch->pc);
 
                /* set pc to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x0;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x0;
 
        } else {
                kvm_debug("[EXL == 1] delivering TLB MISS @ pc %#lx\n",
                          arch->pc);
 
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
        }
 
        kvm_change_c0_guest_cause(cop0, (0xff),
@@ -2019,16 +2034,14 @@ enum emulation_result kvm_mips_emulate_tlbinv_ld(u32 cause,
 
                kvm_debug("[EXL == 0] delivering TLB INV @ pc %#lx\n",
                          arch->pc);
-
-               /* set pc to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
-
        } else {
                kvm_debug("[EXL == 1] delivering TLB MISS @ pc %#lx\n",
                          arch->pc);
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
        }
 
+       /* set pc to the exception entry point */
+       arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
+
        kvm_change_c0_guest_cause(cop0, (0xff),
                                  (EXCCODE_TLBL << CAUSEB_EXCCODE));
 
@@ -2064,11 +2077,11 @@ enum emulation_result kvm_mips_emulate_tlbmiss_st(u32 cause,
                          arch->pc);
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x0;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x0;
        } else {
                kvm_debug("[EXL == 1] Delivering TLB MISS @ pc %#lx\n",
                          arch->pc);
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
        }
 
        kvm_change_c0_guest_cause(cop0, (0xff),
@@ -2104,15 +2117,14 @@ enum emulation_result kvm_mips_emulate_tlbinv_st(u32 cause,
 
                kvm_debug("[EXL == 0] Delivering TLB MISS @ pc %#lx\n",
                          arch->pc);
-
-               /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
        } else {
                kvm_debug("[EXL == 1] Delivering TLB MISS @ pc %#lx\n",
                          arch->pc);
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
        }
 
+       /* Set PC to the exception entry point */
+       arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
+
        kvm_change_c0_guest_cause(cop0, (0xff),
                                  (EXCCODE_TLBS << CAUSEB_EXCCODE));
 
@@ -2146,14 +2158,13 @@ enum emulation_result kvm_mips_emulate_tlbmod(u32 cause,
 
                kvm_debug("[EXL == 0] Delivering TLB MOD @ pc %#lx\n",
                          arch->pc);
-
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
        } else {
                kvm_debug("[EXL == 1] Delivering TLB MOD @ pc %#lx\n",
                          arch->pc);
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
        }
 
+       arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
+
        kvm_change_c0_guest_cause(cop0, (0xff),
                                  (EXCCODE_MOD << CAUSEB_EXCCODE));
 
@@ -2185,7 +2196,7 @@ enum emulation_result kvm_mips_emulate_fpu_exc(u32 cause,
 
        }
 
-       arch->pc = KVM_GUEST_KSEG0 + 0x180;
+       arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        kvm_change_c0_guest_cause(cop0, (0xff),
                                  (EXCCODE_CPU << CAUSEB_EXCCODE));
@@ -2219,7 +2230,7 @@ enum emulation_result kvm_mips_emulate_ri_exc(u32 cause,
                                          (EXCCODE_RI << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver RI when EXL is already set\n");
@@ -2254,7 +2265,7 @@ enum emulation_result kvm_mips_emulate_bp_exc(u32 cause,
                                          (EXCCODE_BP << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver BP when EXL is already set\n");
@@ -2289,7 +2300,7 @@ enum emulation_result kvm_mips_emulate_trap_exc(u32 cause,
                                          (EXCCODE_TR << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver TRAP when EXL is already set\n");
@@ -2324,7 +2335,7 @@ enum emulation_result kvm_mips_emulate_msafpe_exc(u32 cause,
                                          (EXCCODE_MSAFPE << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver MSAFPE when EXL is already set\n");
@@ -2359,7 +2370,7 @@ enum emulation_result kvm_mips_emulate_fpe_exc(u32 cause,
                                          (EXCCODE_FPE << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver FPE when EXL is already set\n");
@@ -2394,7 +2405,7 @@ enum emulation_result kvm_mips_emulate_msadis_exc(u32 cause,
                                          (EXCCODE_MSADIS << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
 
        } else {
                kvm_err("Trying to deliver MSADIS when EXL is already set\n");
@@ -2560,7 +2571,7 @@ static enum emulation_result kvm_mips_emulate_exc(u32 cause,
                                          (exccode << CAUSEB_EXCCODE));
 
                /* Set PC to the exception entry point */
-               arch->pc = KVM_GUEST_KSEG0 + 0x180;
+               arch->pc = kvm_mips_guest_exception_base(vcpu) + 0x180;
                kvm_write_c0_guest_badvaddr(cop0, vcpu->arch.host_cp0_badvaddr);
 
                kvm_debug("Delivering EXC %d @ pc %#lx, badVaddr: %#lx\n",
index e88403b3dcdd5d0bf3ecf9d35555bbcb345e50ed..aa0a1a00faf650c5804eb3e9ff5445929d3d5582 100644 (file)
@@ -183,10 +183,11 @@ int kvm_mips_irq_deliver_cb(struct kvm_vcpu *vcpu, unsigned int priority,
                                          (exccode << CAUSEB_EXCCODE));
 
                /* XXXSL Set PC to the interrupt exception entry point */
+               arch->pc = kvm_mips_guest_exception_base(vcpu);
                if (kvm_read_c0_guest_cause(cop0) & CAUSEF_IV)
-                       arch->pc = KVM_GUEST_KSEG0 + 0x200;
+                       arch->pc += 0x200;
                else
-                       arch->pc = KVM_GUEST_KSEG0 + 0x180;
+                       arch->pc += 0x180;
 
                clear_bit(priority, &vcpu->arch.pending_exceptions);
        }
index 08327de4323a1d8546a8b276afee10f83069df1c..80a681f42bf509ab059781555f920cc110f19820 100644 (file)
@@ -653,6 +653,7 @@ static u64 kvm_trap_emul_get_one_regs[] = {
        KVM_REG_MIPS_CP0_CAUSE,
        KVM_REG_MIPS_CP0_EPC,
        KVM_REG_MIPS_CP0_PRID,
+       KVM_REG_MIPS_CP0_EBASE,
        KVM_REG_MIPS_CP0_CONFIG,
        KVM_REG_MIPS_CP0_CONFIG1,
        KVM_REG_MIPS_CP0_CONFIG2,
@@ -735,6 +736,9 @@ static int kvm_trap_emul_get_one_reg(struct kvm_vcpu *vcpu,
        case KVM_REG_MIPS_CP0_PRID:
                *v = (long)kvm_read_c0_guest_prid(cop0);
                break;
+       case KVM_REG_MIPS_CP0_EBASE:
+               *v = (long)kvm_read_c0_guest_ebase(cop0);
+               break;
        case KVM_REG_MIPS_CP0_CONFIG:
                *v = (long)kvm_read_c0_guest_config(cop0);
                break;
@@ -837,6 +841,14 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu,
        case KVM_REG_MIPS_CP0_PRID:
                kvm_write_c0_guest_prid(cop0, v);
                break;
+       case KVM_REG_MIPS_CP0_EBASE:
+               /*
+                * Allow core number to be written, but the exception base must
+                * remain in guest KSeg0.
+                */
+               kvm_change_c0_guest_ebase(cop0, 0x1ffff000 | MIPS_EBASE_CPUNUM,
+                                         v);
+               break;
        case KVM_REG_MIPS_CP0_COUNT:
                kvm_mips_write_count(vcpu, v);
                break;