/* Pointers to last VCPU loaded on each physical CPU */ staticstruct kvm_vcpu *last_vcpu[NR_CPUS]; /* Pointers to last VCPU executed on each physical CPU */ staticstruct kvm_vcpu *last_exec_vcpu[NR_CPUS];
switch (priority) { case MIPS_EXC_INT_TIMER:
set_gc0_cause(C_TI); break;
case MIPS_EXC_INT_IO_1: case MIPS_EXC_INT_IO_2: case MIPS_EXC_INT_IPI_1: case MIPS_EXC_INT_IPI_2: if (cpu_has_guestctl2)
set_c0_guestctl2(irq); else
set_gc0_cause(irq); break;
switch (priority) { case MIPS_EXC_INT_TIMER: /* *ExplicitlyclearirqassociatedwithCause.IP[IPTI] *ifGuestCtl2virtualinterruptregisternot *supportedorifnotusingGuestCtl2HardwareClear.
*/ if (cpu_has_guestctl2) { if (!(read_c0_guestctl2() & (irq << 14)))
clear_c0_guestctl2(irq);
} else {
clear_gc0_cause(irq);
} break;
case MIPS_EXC_INT_IO_1: case MIPS_EXC_INT_IO_2: case MIPS_EXC_INT_IPI_1: case MIPS_EXC_INT_IPI_2: /* Clear GuestCtl2.VIP irq if not using Hardware Clear */ if (cpu_has_guestctl2) { if (!(read_c0_guestctl2() & (irq << 14)))
clear_c0_guestctl2(irq);
} else {
clear_gc0_cause(irq);
} break;
switch (inst.spec3_format.func) { case lwle_op: case lwre_op: case cachee_op: case sbe_op: case she_op: case sce_op: case swe_op: case swle_op: case swre_op: case prefe_op: case lbue_op: case lhue_op: case lbe_op: case lhe_op: case lle_op: case lwe_op: returntrue; default: returnfalse;
}
}
if (read_gc0_pagegrain() & PG_ELPA)
mask |= 0x00ffffff00000000ull; if (cpu_guest_has_mvh)
mask |= MIPS_MAAR_VH;
/* Set or clear VH */ if (op == mtc_op) { /* clear VH */
val &= ~MIPS_MAAR_VH;
} elseif (op == dmtc_op) { /* set VH to match VL */
val &= ~MIPS_MAAR_VH; if (val & MIPS_MAAR_VL)
val |= MIPS_MAAR_VH;
}
default:
er = EMULATE_FAIL; break;
}
} /* Rollback PC only if emulation was unsuccessful */ if (er == EMULATE_FAIL) {
kvm_err("[%#lx]%s: unsupported cop0 instruction 0x%08x\n",
curr_pc, __func__, inst.word);
/* Secondary or tirtiary cache ops ignored */ if (cache != Cache_I && cache != Cache_D) return EMULATE_DONE;
switch (op_inst) { case Index_Invalidate_I:
flush_icache_line_indexed(va); return EMULATE_DONE; case Index_Writeback_Inv_D:
flush_dcache_line_indexed(va); return EMULATE_DONE; case Hit_Invalidate_I: case Hit_Invalidate_D: case Hit_Writeback_Inv_D: if (boot_cpu_type() == CPU_CAVIUM_OCTEON3) { /* We can just flush entire icache */
local_flush_icache_range(0, 0); return EMULATE_DONE;
}
/* So far, other platforms support guest hit cache ops */ break; default: break;
}
switch (vcpu->arch.gprs[rs]) { case LOONGSON_CFG0:
vcpu->arch.gprs[rd] = 0x14c000; break; case LOONGSON_CFG1:
hostcfg &= (LOONGSON_CFG1_FP | LOONGSON_CFG1_MMI |
LOONGSON_CFG1_MSA1 | LOONGSON_CFG1_MSA2 |
LOONGSON_CFG1_SFBP);
vcpu->arch.gprs[rd] = hostcfg; break; case LOONGSON_CFG2:
hostcfg &= (LOONGSON_CFG2_LEXT1 | LOONGSON_CFG2_LEXT2 |
LOONGSON_CFG2_LEXT3 | LOONGSON_CFG2_LSPW);
vcpu->arch.gprs[rd] = hostcfg; break; case LOONGSON_CFG3:
vcpu->arch.gprs[rd] = hostcfg; break; default: /* Don't export any other advanced features to guest */
vcpu->arch.gprs[rd] = 0; break;
} break;
default:
kvm_err("lwc2 emulate not impl %d rs %lx @%lx\n",
inst.loongson3_lscsr_format.fr, vcpu->arch.gprs[rs], curr_pc);
er = EMULATE_FAIL; break;
}
/* Rollback PC only if emulation was unsuccessful */ if (er == EMULATE_FAIL) {
kvm_err("[%#lx]%s: unsupported lwc2 instruction 0x%08x 0x%08x\n",
curr_pc, __func__, inst.word, inst.loongson3_lscsr_format.fr);
vcpu->arch.pc = curr_pc;
}
return er;
} #endif
staticenum emulation_result kvm_trap_vz_handle_gpsi(u32 cause, u32 *opc, struct kvm_vcpu *vcpu)
{ enum emulation_result er = EMULATE_DONE; struct kvm_vcpu_arch *arch = &vcpu->arch; union mips_instruction inst; int rd, rt, sel; int err;
/* *Fetchtheinstruction.
*/ if (cause & CAUSEF_BD)
opc += 1;
err = kvm_get_badinstr(opc, vcpu, &inst.word); if (err) return EMULATE_FAIL;
switch (inst.r_format.opcode) { case cop0_op:
er = kvm_vz_gpsi_cop0(inst, opc, cause, vcpu); break; #ifndef CONFIG_CPU_MIPSR6 case cache_op:
trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE);
er = kvm_vz_gpsi_cache(inst, opc, cause, vcpu); break; #endif #ifdef CONFIG_CPU_LOONGSON64 case lwc2_op:
er = kvm_vz_gpsi_lwc2(inst, opc, cause, vcpu); break; #endif case spec3_op: switch (inst.spec3_format.func) { #ifdef CONFIG_CPU_MIPSR6 case cache6_op:
trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE);
er = kvm_vz_gpsi_cache(inst, opc, cause, vcpu); break; #endif case rdhwr_op: if (inst.r_format.rs || (inst.r_format.re >> 3)) goto unknown;
default:
kvm_err("GPSI exception not supported (%p/%#x)\n",
opc, inst.word);
kvm_arch_vcpu_dump_regs(vcpu);
er = EMULATE_FAIL; break;
}
return er;
}
staticenum emulation_result kvm_trap_vz_handle_gsfc(u32 cause, u32 *opc, struct kvm_vcpu *vcpu)
{ enum emulation_result er = EMULATE_DONE; struct kvm_vcpu_arch *arch = &vcpu->arch; union mips_instruction inst; int err;
/* *Fetchtheinstruction.
*/ if (cause & CAUSEF_BD)
opc += 1;
err = kvm_get_badinstr(opc, vcpu, &inst.word); if (err) return EMULATE_FAIL;
/* complete MTC0 on behalf of guest and advance EPC */ if (inst.c0r_format.opcode == cop0_op &&
inst.c0r_format.rs == mtc_op &&
inst.c0r_format.z == 0) { int rt = inst.c0r_format.rt; int rd = inst.c0r_format.rd; int sel = inst.c0r_format.sel; unsignedint val = arch->gprs[rt]; unsignedint old_val, change;
if ((rd == MIPS_CP0_STATUS) && (sel == 0)) { /* FR bit should read as zero if no FPU */ if (!kvm_mips_guest_has_fpu(&vcpu->arch))
val &= ~(ST0_CU1 | ST0_FR);
/* *Alsodon'tallowFRtobesetifhostdoesn'tsupport *it.
*/ if (!(boot_cpu_data.fpu_id & MIPS_FPIR_F64))
val &= ~ST0_FR;
old_val = read_gc0_status();
change = val ^ old_val;
staticint kvm_trap_vz_handle_tlb_ld_miss(struct kvm_vcpu *vcpu)
{ struct kvm_run *run = vcpu->run;
u32 *opc = (u32 *) vcpu->arch.pc;
u32 cause = vcpu->arch.host_cp0_cause;
ulong badvaddr = vcpu->arch.host_cp0_badvaddr; union mips_instruction inst; enum emulation_result er = EMULATE_DONE; int err, ret = RESUME_GUEST;
if (kvm_mips_handle_vz_root_tlb_fault(badvaddr, vcpu, false)) { /* A code fetch fault doesn't count as an MMIO */ if (kvm_is_ifetch_fault(&vcpu->arch)) {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR; return RESUME_HOST;
}
/* Fetch the instruction */ if (cause & CAUSEF_BD)
opc += 1;
err = kvm_get_badinstr(opc, vcpu, &inst.word); if (err) {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR; return RESUME_HOST;
}
/* Treat as MMIO */
er = kvm_mips_emulate_load(inst, cause, vcpu); if (er == EMULATE_FAIL) {
kvm_err("Guest Emulate Load from MMIO space failed: PC: %p, BadVaddr: %#lx\n",
opc, badvaddr);
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
}
}
if (er == EMULATE_DONE) {
ret = RESUME_GUEST;
} elseif (er == EMULATE_DO_MMIO) {
run->exit_reason = KVM_EXIT_MMIO;
ret = RESUME_HOST;
} else {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
ret = RESUME_HOST;
} return ret;
}
staticint kvm_trap_vz_handle_tlb_st_miss(struct kvm_vcpu *vcpu)
{ struct kvm_run *run = vcpu->run;
u32 *opc = (u32 *) vcpu->arch.pc;
u32 cause = vcpu->arch.host_cp0_cause;
ulong badvaddr = vcpu->arch.host_cp0_badvaddr; union mips_instruction inst; enum emulation_result er = EMULATE_DONE; int err; int ret = RESUME_GUEST;
/* Just try the access again if we couldn't do the translation */ if (kvm_vz_badvaddr_to_gpa(vcpu, badvaddr, &badvaddr)) return RESUME_GUEST;
vcpu->arch.host_cp0_badvaddr = badvaddr;
if (kvm_mips_handle_vz_root_tlb_fault(badvaddr, vcpu, true)) { /* Fetch the instruction */ if (cause & CAUSEF_BD)
opc += 1;
err = kvm_get_badinstr(opc, vcpu, &inst.word); if (err) {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR; return RESUME_HOST;
}
/* Treat as MMIO */
er = kvm_mips_emulate_store(inst, cause, vcpu); if (er == EMULATE_FAIL) {
kvm_err("Guest Emulate Store to MMIO space failed: PC: %p, BadVaddr: %#lx\n",
opc, badvaddr);
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
}
}
if (er == EMULATE_DONE) {
ret = RESUME_GUEST;
} elseif (er == EMULATE_DO_MMIO) {
run->exit_reason = KVM_EXIT_MMIO;
ret = RESUME_HOST;
} else {
run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
ret = RESUME_HOST;
} return ret;
}
ret = ARRAY_SIZE(kvm_vz_get_one_regs); if (cpu_guest_has_userlocal)
++ret; if (cpu_guest_has_badinstr)
++ret; if (cpu_guest_has_badinstrp)
++ret; if (cpu_guest_has_contextconfig)
ret += ARRAY_SIZE(kvm_vz_get_one_regs_contextconfig); if (cpu_guest_has_segments)
ret += ARRAY_SIZE(kvm_vz_get_one_regs_segments); if (cpu_guest_has_htw || cpu_guest_has_ldpte)
ret += ARRAY_SIZE(kvm_vz_get_one_regs_htw); if (cpu_guest_has_maar && !cpu_guest_has_dyn_maar)
ret += 1 + ARRAY_SIZE(vcpu->arch.maar);
ret += __arch_hweight8(cpu_data[0].guest.kscratch_mask);
if (copy_to_user(indices, kvm_vz_get_one_regs, sizeof(kvm_vz_get_one_regs))) return -EFAULT;
indices += ARRAY_SIZE(kvm_vz_get_one_regs);
if (cpu_guest_has_userlocal) {
index = KVM_REG_MIPS_CP0_USERLOCAL; if (copy_to_user(indices, &index, sizeof(index))) return -EFAULT;
++indices;
} if (cpu_guest_has_badinstr) {
index = KVM_REG_MIPS_CP0_BADINSTR; if (copy_to_user(indices, &index, sizeof(index))) return -EFAULT;
++indices;
} if (cpu_guest_has_badinstrp) {
index = KVM_REG_MIPS_CP0_BADINSTRP; if (copy_to_user(indices, &index, sizeof(index))) return -EFAULT;
++indices;
} if (cpu_guest_has_contextconfig) { if (copy_to_user(indices, kvm_vz_get_one_regs_contextconfig, sizeof(kvm_vz_get_one_regs_contextconfig))) return -EFAULT;
indices += ARRAY_SIZE(kvm_vz_get_one_regs_contextconfig);
} if (cpu_guest_has_segments) { if (copy_to_user(indices, kvm_vz_get_one_regs_segments, sizeof(kvm_vz_get_one_regs_segments))) return -EFAULT;
indices += ARRAY_SIZE(kvm_vz_get_one_regs_segments);
} if (cpu_guest_has_htw || cpu_guest_has_ldpte) { if (copy_to_user(indices, kvm_vz_get_one_regs_htw, sizeof(kvm_vz_get_one_regs_htw))) return -EFAULT;
indices += ARRAY_SIZE(kvm_vz_get_one_regs_htw);
} if (cpu_guest_has_maar && !cpu_guest_has_dyn_maar) { for (i = 0; i < ARRAY_SIZE(vcpu->arch.maar); ++i) {
index = KVM_REG_MIPS_CP0_MAAR(i); if (copy_to_user(indices, &index, sizeof(index))) return -EFAULT;
++indices;
}
index = KVM_REG_MIPS_CP0_MAARI; if (copy_to_user(indices, &index, sizeof(index))) return -EFAULT;
++indices;
} for (i = 0; i < 6; ++i) { if (!cpu_guest_has_kscr(i + 2)) continue;
if (copy_to_user(indices, &kvm_vz_get_one_regs_kscratch[i], sizeof(kvm_vz_get_one_regs_kscratch[i]))) return -EFAULT;
++indices;
}
return0;
}
staticinline s64 entrylo_kvm_to_user(unsignedlong v)
{
s64 mask, ret = v;
if (BITS_PER_LONG == 32) { /* *KVMAPIexposes64-bitversionoftheregister,somovethe *RI/XIbitsupintoplace.
*/
mask = MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI;
ret &= ~mask;
ret |= ((s64)v & mask) << 32;
} return ret;
}
staticinlineunsignedlong entrylo_user_to_kvm(s64 v)
{ unsignedlong mask, ret = v;
if (BITS_PER_LONG == 32) { /* *KVMAPIexposes64-bitversionooftheregister,somovethe *RI/XIbitsdownintoplace.
*/
mask = MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI;
ret &= ~mask;
ret |= (v >> 32) & mask;
} return ret;
}
switch (reg->id) { case KVM_REG_MIPS_CP0_INDEX:
write_gc0_index(v); break; case KVM_REG_MIPS_CP0_ENTRYLO0:
write_gc0_entrylo0(entrylo_user_to_kvm(v)); break; case KVM_REG_MIPS_CP0_ENTRYLO1:
write_gc0_entrylo1(entrylo_user_to_kvm(v)); break; case KVM_REG_MIPS_CP0_CONTEXT:
write_gc0_context(v); break; case KVM_REG_MIPS_CP0_CONTEXTCONFIG: if (!cpu_guest_has_contextconfig) return -EINVAL;
write_gc0_contextconfig(v); break; case KVM_REG_MIPS_CP0_USERLOCAL: if (!cpu_guest_has_userlocal) return -EINVAL;
write_gc0_userlocal(v); break; #ifdef CONFIG_64BIT case KVM_REG_MIPS_CP0_XCONTEXTCONFIG: if (!cpu_guest_has_contextconfig) return -EINVAL;
write_gc0_xcontextconfig(v); break; #endif case KVM_REG_MIPS_CP0_PAGEMASK:
write_gc0_pagemask(v); break; case KVM_REG_MIPS_CP0_PAGEGRAIN:
write_gc0_pagegrain(v); break; case KVM_REG_MIPS_CP0_SEGCTL0: if (!cpu_guest_has_segments) return -EINVAL;
write_gc0_segctl0(v); break; case KVM_REG_MIPS_CP0_SEGCTL1: if (!cpu_guest_has_segments) return -EINVAL;
write_gc0_segctl1(v); break; case KVM_REG_MIPS_CP0_SEGCTL2: if (!cpu_guest_has_segments) return -EINVAL;
write_gc0_segctl2(v); break; case KVM_REG_MIPS_CP0_PWBASE: if (!cpu_guest_has_htw && !cpu_guest_has_ldpte) return -EINVAL;
write_gc0_pwbase(v); break; case KVM_REG_MIPS_CP0_PWFIELD: if (!cpu_guest_has_htw && !cpu_guest_has_ldpte) return -EINVAL;
write_gc0_pwfield(v); break; case KVM_REG_MIPS_CP0_PWSIZE: if (!cpu_guest_has_htw && !cpu_guest_has_ldpte) return -EINVAL;
write_gc0_pwsize(v); break; case KVM_REG_MIPS_CP0_WIRED:
change_gc0_wired(MIPSR6_WIRED_WIRED, v); break; case KVM_REG_MIPS_CP0_PWCTL: if (!cpu_guest_has_htw && !cpu_guest_has_ldpte) return -EINVAL;
write_gc0_pwctl(v); break; case KVM_REG_MIPS_CP0_HWRENA:
write_gc0_hwrena(v); break; case KVM_REG_MIPS_CP0_BADVADDR:
write_gc0_badvaddr(v); break; case KVM_REG_MIPS_CP0_BADINSTR: if (!cpu_guest_has_badinstr) return -EINVAL;
write_gc0_badinstr(v); break; case KVM_REG_MIPS_CP0_BADINSTRP: if (!cpu_guest_has_badinstrp) return -EINVAL;
write_gc0_badinstrp(v); break; case KVM_REG_MIPS_CP0_COUNT:
kvm_mips_write_count(vcpu, v); break; case KVM_REG_MIPS_CP0_ENTRYHI:
write_gc0_entryhi(v); break; case KVM_REG_MIPS_CP0_COMPARE:
kvm_mips_write_compare(vcpu, v, false); break; case KVM_REG_MIPS_CP0_STATUS:
write_gc0_status(v); break; case KVM_REG_MIPS_CP0_INTCTL:
write_gc0_intctl(v); break; case KVM_REG_MIPS_CP0_CAUSE: /* *Ifthetimerisstoppedorstarted(DCbit)itmustlook *atomicwithchangestothetimerinterruptpendingbit(TI). *Atimerinterruptshouldnothappeninbetween.
*/ if ((read_gc0_cause() ^ v) & CAUSEF_DC) { if (v & CAUSEF_DC) { /* disable timer first */
kvm_mips_count_disable_cause(vcpu);
change_gc0_cause((u32)~CAUSEF_DC, v);
} else { /* enable timer last */
change_gc0_cause((u32)~CAUSEF_DC, v);
kvm_mips_count_enable_cause(vcpu);
}
} else {
write_gc0_cause(v);
} break; case KVM_REG_MIPS_CP0_EPC:
write_gc0_epc(v); break; case KVM_REG_MIPS_CP0_PRID: switch (boot_cpu_type()) { case CPU_CAVIUM_OCTEON3: /* Octeon III has a guest.PRid, but its read-only */ break; default:
kvm_write_c0_guest_prid(cop0, v); break;
} break; case KVM_REG_MIPS_CP0_EBASE:
kvm_vz_write_gc0_ebase(v); break; case KVM_REG_MIPS_CP0_CONFIG:
cur = read_gc0_config();
change = (cur ^ v) & kvm_vz_config_user_wrmask(vcpu); if (change) {
v = cur ^ change;
write_gc0_config(v);
} break; case KVM_REG_MIPS_CP0_CONFIG1: if (!cpu_guest_has_conf1) break;
cur = read_gc0_config1();
change = (cur ^ v) & kvm_vz_config1_user_wrmask(vcpu); if (change) {
v = cur ^ change;
write_gc0_config1(v);
} break; case KVM_REG_MIPS_CP0_CONFIG2: if (!cpu_guest_has_conf2) break;
cur = read_gc0_config2();
change = (cur ^ v) & kvm_vz_config2_user_wrmask(vcpu); if (change) {
v = cur ^ change;
write_gc0_config2(v);
} break; case KVM_REG_MIPS_CP0_CONFIG3: if (!cpu_guest_has_conf3) break;
cur = read_gc0_config3();
change = (cur ^ v) & kvm_vz_config3_user_wrmask(vcpu); if (change) {
v = cur ^ change;
write_gc0_config3(v);
} break; case KVM_REG_MIPS_CP0_CONFIG4: if (!cpu_guest_has_conf4) break;
cur = read_gc0_config4();
change = (cur ^ v) & kvm_vz_config4_user_wrmask(vcpu); if (change) {
v = cur ^ change;
write_gc0_config4(v);
} break; case KVM_REG_MIPS_CP0_CONFIG5: if (!cpu_guest_has_conf5) break;
cur = read_gc0_config5();
change = (cur ^ v) & kvm_vz_config5_user_wrmask(vcpu); if (change) {
v = cur ^ change;
write_gc0_config5(v);
} break; case KVM_REG_MIPS_CP0_CONFIG6:
cur = kvm_read_sw_gc0_config6(cop0);
change = (cur ^ v) & kvm_vz_config6_user_wrmask(vcpu); if (change) {
v = cur ^ change;
kvm_write_sw_gc0_config6(cop0, (int)v);
} break; case KVM_REG_MIPS_CP0_MAAR(0) ... KVM_REG_MIPS_CP0_MAAR(0x3f): if (!cpu_guest_has_maar || cpu_guest_has_dyn_maar) return -EINVAL;
idx = reg->id - KVM_REG_MIPS_CP0_MAAR(0); if (idx >= ARRAY_SIZE(vcpu->arch.maar)) return -EINVAL;
vcpu->arch.maar[idx] = mips_process_maar(dmtc_op, v); break; case KVM_REG_MIPS_CP0_MAARI: if (!cpu_guest_has_maar || cpu_guest_has_dyn_maar) return -EINVAL;
kvm_write_maari(vcpu, v); break; #ifdef CONFIG_64BIT case KVM_REG_MIPS_CP0_XCONTEXT:
write_gc0_xcontext(v); break; #endif case KVM_REG_MIPS_CP0_ERROREPC:
write_gc0_errorepc(v); break; case KVM_REG_MIPS_CP0_KSCRATCH1 ... KVM_REG_MIPS_CP0_KSCRATCH6:
idx = reg->id - KVM_REG_MIPS_CP0_KSCRATCH1 + 2; if (!cpu_guest_has_kscr(idx)) return -EINVAL; switch (idx) { case2:
write_gc0_kscratch1(v); break; case3:
write_gc0_kscratch2(v); break; case4:
write_gc0_kscratch3(v); break; case5:
write_gc0_kscratch4(v); break; case6:
write_gc0_kscratch5(v); break; case7:
write_gc0_kscratch6(v); break;
} break; case KVM_REG_MIPS_COUNT_CTL:
ret = kvm_mips_set_count_ctl(vcpu, v); break; case KVM_REG_MIPS_COUNT_RESUME:
ret = kvm_mips_set_count_resume(vcpu, v); break; case KVM_REG_MIPS_COUNT_HZ:
ret = kvm_mips_set_count_hz(vcpu, v); break; default: return -EINVAL;
} return ret;
}
if (!(++guestid & GUESTID_MASK)) { if (cpu_has_vtag_icache)
flush_icache_all();
if (!guestid) /* fix version if needed */
guestid = GUESTID_FIRST_VERSION;
++guestid; /* guestid 0 reserved for root */
/* start new guestid cycle */
kvm_vz_local_flush_roottlb_all_guests();
kvm_vz_local_flush_guesttlb_all();
}
guestid_cache(cpu) = guestid;
}
/* Returns 1 if the guest TLB may be clobbered */ staticint kvm_vz_check_requests(struct kvm_vcpu *vcpu, int cpu)
{ int ret = 0; int i;
if (!kvm_request_pending(vcpu)) return0;
if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu)) { if (cpu_has_guestid) { /* Drop all GuestIDs for this VCPU */
for_each_possible_cpu(i)
vcpu->arch.vzguestid[i] = 0; /* This will clobber guest TLB contents too */
ret = 1;
} /* *ForRootASIDDealias(RAD)wedon'tdoanythinghere,butwe *stillneedtherequesttoensurewerecheckasid_flush_mask. *Wecanstillreturn0asonlytherootTLBwillbeaffected *byarootASIDflush.
*/
}
/* Expand the wired TLB array if necessary */
wired &= MIPSR6_WIRED_WIRED; if (wired > vcpu->arch.wired_tlb_limit) {
tlbs = krealloc(vcpu->arch.wired_tlb, wired * sizeof(*vcpu->arch.wired_tlb), GFP_ATOMIC); if (WARN_ON(!tlbs)) { /* Save whatever we can */
wired = vcpu->arch.wired_tlb_limit;
} else {
vcpu->arch.wired_tlb = tlbs;
vcpu->arch.wired_tlb_limit = wired;
}
}
if (wired) /* Save wired entries from the guest TLB */
kvm_vz_save_guesttlb(vcpu->arch.wired_tlb, 0, wired); /* Invalidate any dropped entries since last time */ for (i = wired; i < vcpu->arch.wired_tlb_used; ++i) {
vcpu->arch.wired_tlb[i].tlb_hi = UNIQUE_GUEST_ENTRYHI(i);
vcpu->arch.wired_tlb[i].tlb_lo[0] = 0;
vcpu->arch.wired_tlb[i].tlb_lo[1] = 0;
vcpu->arch.wired_tlb[i].tlb_mask = 0;
}
vcpu->arch.wired_tlb_used = wired;
}
staticvoid kvm_vz_vcpu_load_wired(struct kvm_vcpu *vcpu)
{ /* Load wired entries into the guest TLB */ if (vcpu->arch.wired_tlb)
kvm_vz_load_guesttlb(vcpu->arch.wired_tlb, 0,
vcpu->arch.wired_tlb_used);
}
/* Set MC bit if we want to trace guest mode changes */ if (kvm_trace_guest_mode_change)
set_c0_guestctl0(MIPS_GCTL0_MC); else
clear_c0_guestctl0(MIPS_GCTL0_MC);
/* Don't bother restoring registers multiple times unless necessary */ if (!all) return0;
/* *Restoreconfigregistersfirst,assomeimplementationsrestrict *writestootherregisterswhenthecorrespondingfeaturebitsaren't *set.ForexampleStatus.CU1cannotbesetunlessConfig1.FPisset.
*/
kvm_restore_gc0_config(cop0); if (cpu_guest_has_conf1)
kvm_restore_gc0_config1(cop0); if (cpu_guest_has_conf2)
kvm_restore_gc0_config2(cop0); if (cpu_guest_has_conf3)
kvm_restore_gc0_config3(cop0); if (cpu_guest_has_conf4)
kvm_restore_gc0_config4(cop0); if (cpu_guest_has_conf5)
kvm_restore_gc0_config5(cop0); if (cpu_guest_has_conf6)
kvm_restore_gc0_config6(cop0); if (cpu_guest_has_conf7)
kvm_restore_gc0_config7(cop0);
kvm_restore_gc0_index(cop0);
kvm_restore_gc0_entrylo0(cop0);
kvm_restore_gc0_entrylo1(cop0);
kvm_restore_gc0_context(cop0); if (cpu_guest_has_contextconfig)
kvm_restore_gc0_contextconfig(cop0); #ifdef CONFIG_64BIT
kvm_restore_gc0_xcontext(cop0); if (cpu_guest_has_contextconfig)
kvm_restore_gc0_xcontextconfig(cop0); #endif
kvm_restore_gc0_pagemask(cop0);
kvm_restore_gc0_pagegrain(cop0);
kvm_restore_gc0_hwrena(cop0);
kvm_restore_gc0_badvaddr(cop0);
kvm_restore_gc0_entryhi(cop0);
kvm_restore_gc0_status(cop0);
kvm_restore_gc0_intctl(cop0);
kvm_restore_gc0_epc(cop0);
kvm_vz_write_gc0_ebase(kvm_read_sw_gc0_ebase(cop0)); if (cpu_guest_has_userlocal)
kvm_restore_gc0_userlocal(cop0);
kvm_restore_gc0_errorepc(cop0);
/* restore KScratch registers if enabled in guest */ if (cpu_guest_has_conf4) { if (cpu_guest_has_kscr(2))
kvm_restore_gc0_kscratch1(cop0); if (cpu_guest_has_kscr(3))
kvm_restore_gc0_kscratch2(cop0); if (cpu_guest_has_kscr(4))
kvm_restore_gc0_kscratch3(cop0); if (cpu_guest_has_kscr(5))
kvm_restore_gc0_kscratch4(cop0); if (cpu_guest_has_kscr(6))
kvm_restore_gc0_kscratch5(cop0); if (cpu_guest_has_kscr(7))
kvm_restore_gc0_kscratch6(cop0);
}
if (cpu_guest_has_badinstr)
kvm_restore_gc0_badinstr(cop0); if (cpu_guest_has_badinstrp)
kvm_restore_gc0_badinstrp(cop0);
if (cpu_guest_has_segments) {
kvm_restore_gc0_segctl0(cop0);
kvm_restore_gc0_segctl1(cop0);
kvm_restore_gc0_segctl2(cop0);
}
if (current->flags & PF_VCPU)
kvm_vz_vcpu_save_wired(vcpu);
kvm_lose_fpu(vcpu);
kvm_save_gc0_index(cop0);
kvm_save_gc0_entrylo0(cop0);
kvm_save_gc0_entrylo1(cop0);
kvm_save_gc0_context(cop0); if (cpu_guest_has_contextconfig)
kvm_save_gc0_contextconfig(cop0); #ifdef CONFIG_64BIT
kvm_save_gc0_xcontext(cop0); if (cpu_guest_has_contextconfig)
kvm_save_gc0_xcontextconfig(cop0); #endif
kvm_save_gc0_pagemask(cop0);
kvm_save_gc0_pagegrain(cop0);
kvm_save_gc0_wired(cop0); /* allow wired TLB entries to be overwritten */
clear_gc0_wired(MIPSR6_WIRED_WIRED);
kvm_save_gc0_hwrena(cop0);
kvm_save_gc0_badvaddr(cop0);
kvm_save_gc0_entryhi(cop0);
kvm_save_gc0_status(cop0);
kvm_save_gc0_intctl(cop0);
kvm_save_gc0_epc(cop0);
kvm_write_sw_gc0_ebase(cop0, kvm_vz_read_gc0_ebase()); if (cpu_guest_has_userlocal)
kvm_save_gc0_userlocal(cop0);
/* only save implemented config registers */
kvm_save_gc0_config(cop0); if (cpu_guest_has_conf1)
kvm_save_gc0_config1(cop0); if (cpu_guest_has_conf2)
kvm_save_gc0_config2(cop0); if (cpu_guest_has_conf3)
kvm_save_gc0_config3(cop0); if (cpu_guest_has_conf4)
kvm_save_gc0_config4(cop0); if (cpu_guest_has_conf5)
kvm_save_gc0_config5(cop0); if (cpu_guest_has_conf6)
kvm_save_gc0_config6(cop0); if (cpu_guest_has_conf7)
kvm_save_gc0_config7(cop0);
kvm_save_gc0_errorepc(cop0);
/* save KScratch registers if enabled in guest */ if (cpu_guest_has_conf4) { if (cpu_guest_has_kscr(2))
kvm_save_gc0_kscratch1(cop0); if (cpu_guest_has_kscr(3))
kvm_save_gc0_kscratch2(cop0); if (cpu_guest_has_kscr(4))
kvm_save_gc0_kscratch3(cop0); if (cpu_guest_has_kscr(5))
kvm_save_gc0_kscratch4(cop0); if (cpu_guest_has_kscr(6))
kvm_save_gc0_kscratch5(cop0); if (cpu_guest_has_kscr(7))
kvm_save_gc0_kscratch6(cop0);
}
if (cpu_guest_has_badinstr)
kvm_save_gc0_badinstr(cop0); if (cpu_guest_has_badinstrp)
kvm_save_gc0_badinstrp(cop0);
if (cpu_guest_has_segments) {
kvm_save_gc0_segctl0(cop0);
kvm_save_gc0_segctl1(cop0);
kvm_save_gc0_segctl2(cop0);
}
/* save HTW registers if enabled in guest */ if (cpu_guest_has_ldpte || (cpu_guest_has_htw &&
kvm_read_sw_gc0_config3(cop0) & MIPS_CONF3_PW)) {
kvm_save_gc0_pwbase(cop0);
kvm_save_gc0_pwfield(cop0);
kvm_save_gc0_pwsize(cop0);
kvm_save_gc0_pwctl(cop0);
}
kvm_vz_save_timer(vcpu);
/* save Root.GuestCtl2 in unused Guest guestctl2 register */ if (cpu_has_guestctl2)
cop0->reg[MIPS_CP0_GUESTCTL2][MIPS_CP0_GUESTCTL2_SEL] =
read_c0_guestctl2();
/* Flush moved entries in new (root) context */
local_flush_tlb_all(); break;
}
if (cpu_has_guestid) {
write_c0_guestctl1(0);
kvm_vz_local_flush_roottlb_all_guests();
}
}
staticint kvm_vz_check_extension(struct kvm *kvm, long ext)
{ int r;
switch (ext) { case KVM_CAP_MIPS_VZ: /* we wouldn't be here unless cpu_has_vz */
r = 1; break; #ifdef CONFIG_64BIT case KVM_CAP_MIPS_64BIT: /* We support 64-bit registers/operations and addresses */
r = 2; break; #endif case KVM_CAP_IOEVENTFD:
r = 1; break; default:
r = 0; break;
}
return r;
}
staticint kvm_vz_vcpu_init(struct kvm_vcpu *vcpu)
{ int i;
/* FIXME: Get rid of the callbacks now that trap-and-emulate is gone. */ conststruct kvm_mips_callbacks * const kvm_mips_callbacks = &kvm_vz_callbacks;
int kvm_mips_emulation_init(void)
{ if (!cpu_has_vz) return -ENODEV;
/* *VZrequiresatleast2KScratchregisters,soitshouldhavebeen *possibletoallocatepgd_reg.
*/ if (WARN(pgd_reg == -1, "pgd_reg not allocated even though cpu_has_vz\n")) return -ENODEV;
pr_info("Starting KVM with MIPS VZ extensions\n"); return0;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.84 Sekunden
(vorverarbeitet am 2026-09-28)
¤