/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * KVM/MIPS: Instruction/Exception emulation * * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. * Authors: Sanjay Lal <sanjayl@kymasys.com>
*/
/* * Compute the return address and do emulate branch simulation, if required. * This function should be called only in branch delay slot active.
*/ staticint kvm_compute_return_epc(struct kvm_vcpu *vcpu, unsignedlong instpc, unsignedlong *out)
{ unsignedint dspcontrol; union mips_instruction insn; struct kvm_vcpu_arch *arch = &vcpu->arch; long epc = instpc; long nextpc; int err;
/* These are unconditional and in j_format. */ case jal_op:
arch->gprs[31] = instpc + 8;
fallthrough; case j_op:
epc += 4;
epc >>= 28;
epc <<= 28;
epc |= (insn.j_format.target << 2);
nextpc = epc; break;
/* These are conditional and in i_format. */ case beq_op: case beql_op: if (arch->gprs[insn.i_format.rs] ==
arch->gprs[insn.i_format.rt])
epc = epc + 4 + (insn.i_format.simmediate << 2); else
epc += 8;
nextpc = epc; break;
case bne_op: case bnel_op: if (arch->gprs[insn.i_format.rs] !=
arch->gprs[insn.i_format.rt])
epc = epc + 4 + (insn.i_format.simmediate << 2); else
epc += 8;
nextpc = epc; break;
case blez_op: /* POP06 */ #ifndef CONFIG_CPU_MIPSR6 case blezl_op: /* removed in R6 */ #endif if (insn.i_format.rt != 0) goto compact_branch; if ((long)arch->gprs[insn.i_format.rs] <= 0)
epc = epc + 4 + (insn.i_format.simmediate << 2); else
epc += 8;
nextpc = epc; break;
case bgtz_op: /* POP07 */ #ifndef CONFIG_CPU_MIPSR6 case bgtzl_op: /* removed in R6 */ #endif if (insn.i_format.rt != 0) goto compact_branch; if ((long)arch->gprs[insn.i_format.rs] > 0)
epc = epc + 4 + (insn.i_format.simmediate << 2); else
epc += 8;
nextpc = epc; break;
/* And now the FPA/cp1 branch instructions. */ case cop1_op:
kvm_err("%s: unsupported cop1_op\n", __func__); return -EINVAL;
#ifdef CONFIG_CPU_MIPSR6 /* R6 added the following compact branches with forbidden slots */ case blezl_op: /* POP26 */ case bgtzl_op: /* POP27 */ /* only rt == 0 isn't compact branch */ if (insn.i_format.rt != 0) goto compact_branch; return -EINVAL; case pop10_op: case pop30_op: /* only rs == rt == 0 is reserved, rest are compact branches */ if (insn.i_format.rs != 0 || insn.i_format.rt != 0) goto compact_branch; return -EINVAL; case pop66_op: case pop76_op: /* only rs == 0 isn't compact branch */ if (insn.i_format.rs != 0) goto compact_branch; return -EINVAL;
compact_branch: /* * If we've hit an exception on the forbidden slot, then * the branch must not have been taken.
*/
epc += 8;
nextpc = epc; break; #else
compact_branch: /* Fall through - Compact branches not supported before R6 */ #endif default: return -EINVAL;
}
*out = nextpc; return 0;
}
enum emulation_result update_pc(struct kvm_vcpu *vcpu, u32 cause)
{ int err;
kvm_debug("update_pc(): New PC: %#lx\n", vcpu->arch.pc);
return EMULATE_DONE;
}
/** * kvm_get_badinstr() - Get bad instruction encoding. * @opc: Guest pointer to faulting instruction. * @vcpu: KVM VCPU information. * * Gets the instruction encoding of the faulting instruction, using the saved * BadInstr register value if it exists, otherwise falling back to reading guest * memory at @opc. * * Returns: The instruction encoding of the faulting instruction.
*/ int kvm_get_badinstr(u32 *opc, struct kvm_vcpu *vcpu, u32 *out)
{ if (cpu_has_badinstr) {
*out = vcpu->arch.host_cp0_badinstr; return 0;
} else {
WARN_ONCE(1, "CPU doesn't have BadInstr register\n"); return -EINVAL;
}
}
/** * kvm_get_badinstrp() - Get bad prior instruction encoding. * @opc: Guest pointer to prior faulting instruction. * @vcpu: KVM VCPU information. * * Gets the instruction encoding of the prior faulting instruction (the branch * containing the delay slot which faulted), using the saved BadInstrP register * value if it exists, otherwise falling back to reading guest memory at @opc. * * Returns: The instruction encoding of the prior faulting instruction.
*/ int kvm_get_badinstrp(u32 *opc, struct kvm_vcpu *vcpu, u32 *out)
{ if (cpu_has_badinstrp) {
*out = vcpu->arch.host_cp0_badinstrp; return 0;
} else {
WARN_ONCE(1, "CPU doesn't have BadInstrp register\n"); return -EINVAL;
}
}
/** * kvm_mips_count_disabled() - Find whether the CP0_Count timer is disabled. * @vcpu: Virtual CPU. * * Returns: 1 if the CP0_Count timer is disabled by either the guest * CP0_Cause.DC bit or the count_ctl.DC bit. * 0 otherwise (in which case CP0_Count timer is running).
*/ int kvm_mips_count_disabled(struct kvm_vcpu *vcpu)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
if (delta >= vcpu->arch.count_period) { /* If delta is out of safe range the bias needs adjusting */
periods = div64_s64(now_ns, vcpu->arch.count_period);
vcpu->arch.count_dyn_bias = -periods * vcpu->arch.count_period; /* Recalculate delta with new bias */
delta = now_ns + vcpu->arch.count_dyn_bias;
}
/* * We've ensured that: * delta < count_period * * Therefore the intermediate delta*count_hz will never overflow since * at the boundary condition: * delta = count_period * delta = NSEC_PER_SEC * 2^32 / count_hz * delta * count_hz = NSEC_PER_SEC * 2^32
*/ return div_u64(delta * vcpu->arch.count_hz, NSEC_PER_SEC);
}
/** * kvm_mips_count_time() - Get effective current time. * @vcpu: Virtual CPU. * * Get effective monotonic ktime. This is usually a straightforward ktime_get(), * except when the master disable bit is set in count_ctl, in which case it is * count_resume, i.e. the time that the count was disabled. * * Returns: Effective monotonic ktime for CP0_Count.
*/ staticinline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu)
{ if (unlikely(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) return vcpu->arch.count_resume;
return ktime_get();
}
/** * kvm_mips_read_count_running() - Read the current count value as if running. * @vcpu: Virtual CPU. * @now: Kernel time to read CP0_Count at. * * Returns the current guest CP0_Count register at time @now and handles if the * timer interrupt is pending and hasn't been handled yet. * * Returns: The current value of the guest CP0_Count register.
*/ static u32 kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
ktime_t expires, threshold;
u32 count, compare; int running;
/* Calculate the biased and scaled guest CP0_Count */
count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
compare = kvm_read_c0_guest_compare(cop0);
/* * Find whether CP0_Count has reached the closest timer interrupt. If * not, we shouldn't inject it.
*/ if ((s32)(count - compare) < 0) return count;
/* * The CP0_Count we're going to return has already reached the closest * timer interrupt. Quickly check if it really is a new interrupt by * looking at whether the interval until the hrtimer expiry time is * less than 1/4 of the timer period.
*/
expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer);
threshold = ktime_add_ns(now, vcpu->arch.count_period / 4); if (ktime_before(expires, threshold)) { /* * Cancel it while we handle it so there's no chance of * interference with the timeout handler.
*/
running = hrtimer_cancel(&vcpu->arch.comparecount_timer);
/* Nothing should be waiting on the timeout */
kvm_mips_callbacks->queue_timer_int(vcpu);
/* * Restart the timer if it was running based on the expiry time * we read, so that we don't push it back 2 periods.
*/ if (running) {
expires = ktime_add_ns(expires,
vcpu->arch.count_period);
hrtimer_start(&vcpu->arch.comparecount_timer, expires,
HRTIMER_MODE_ABS);
}
}
return count;
}
/** * kvm_mips_read_count() - Read the current count value. * @vcpu: Virtual CPU. * * Read the current guest CP0_Count value, taking into account whether the timer * is stopped. * * Returns: The current guest CP0_Count value.
*/
u32 kvm_mips_read_count(struct kvm_vcpu *vcpu)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
/* If count disabled just read static copy of count */ if (kvm_mips_count_disabled(vcpu)) return kvm_read_c0_guest_count(cop0);
/** * kvm_mips_freeze_hrtimer() - Safely stop the hrtimer. * @vcpu: Virtual CPU. * @count: Output pointer for CP0_Count value at point of freeze. * * Freeze the hrtimer safely and return both the ktime and the CP0_Count value * at the point it was frozen. It is guaranteed that any pending interrupts at * the point it was frozen are handled, and none after that point. * * This is useful where the time/CP0_Count is needed in the calculation of the * new parameters. * * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). * * Returns: The ktime at the point of freeze.
*/
ktime_t kvm_mips_freeze_hrtimer(struct kvm_vcpu *vcpu, u32 *count)
{
ktime_t now;
/* stop hrtimer before finding time */
hrtimer_cancel(&vcpu->arch.comparecount_timer);
now = ktime_get();
/* find count at this point and handle pending hrtimer */
*count = kvm_mips_read_count_running(vcpu, now);
return now;
}
/** * kvm_mips_resume_hrtimer() - Resume hrtimer, updating expiry. * @vcpu: Virtual CPU. * @now: ktime at point of resume. * @count: CP0_Count at point of resume. * * Resumes the timer and updates the timer expiry based on @now and @count. * This can be used in conjunction with kvm_mips_freeze_timer() when timer * parameters need to be changed. * * It is guaranteed that a timer interrupt immediately after resume will be * handled, but not if CP_Compare is exactly at @count. That case is already * handled by kvm_mips_freeze_timer(). * * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
*/ staticvoid kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu,
ktime_t now, u32 count)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
u32 compare;
u64 delta;
ktime_t expire;
/* Update hrtimer to use new timeout */
hrtimer_cancel(&vcpu->arch.comparecount_timer);
hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS);
}
/** * kvm_mips_restore_hrtimer() - Restore hrtimer after a gap, updating expiry. * @vcpu: Virtual CPU. * @before: Time before Count was saved, lower bound of drift calculation. * @count: CP0_Count at point of restore. * @min_drift: Minimum amount of drift permitted before correction. * Must be <= 0. * * Restores the timer from a particular @count, accounting for drift. This can * be used in conjunction with kvm_mips_freeze_timer() when a hardware timer is * to be used for a period of time, but the exact ktime corresponding to the * final Count that must be restored is not known. * * It is guaranteed that a timer interrupt immediately after restore will be * handled, but not if CP0_Compare is exactly at @count. That case should * already be handled when the hardware timer state is saved. * * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is not * stopped). * * Returns: Amount of correction to count_bias due to drift.
*/ int kvm_mips_restore_hrtimer(struct kvm_vcpu *vcpu, ktime_t before,
u32 count, int min_drift)
{
ktime_t now, count_time;
u32 now_count, before_count;
u64 delta; int drift, ret = 0;
/* Calculate expected count at before */
before_count = vcpu->arch.count_bias +
kvm_mips_ktime_to_count(vcpu, before);
/* * Detect significantly negative drift, where count is lower than * expected. Some negative drift is expected when hardware counter is * set after kvm_mips_freeze_timer(), and it is harmless to allow the * time to jump forwards a little, within reason. If the drift is too * significant, adjust the bias to avoid a big Guest.CP0_Count jump.
*/
drift = count - before_count; if (drift < min_drift) {
count_time = before;
vcpu->arch.count_bias += drift;
ret = drift; goto resume;
}
/* Calculate expected count right now */
now = ktime_get();
now_count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
/* * Detect positive drift, where count is higher than expected, and * adjust the bias to avoid guest time going backwards.
*/
drift = count - now_count; if (drift > 0) {
count_time = now;
vcpu->arch.count_bias += drift;
ret = drift; goto resume;
}
/* Subtract nanosecond delta to find ktime when count was read */
delta = (u64)(u32)(now_count - count);
delta = div_u64(delta * NSEC_PER_SEC, vcpu->arch.count_hz);
count_time = ktime_sub_ns(now, delta);
resume: /* Resume using the calculated ktime */
kvm_mips_resume_hrtimer(vcpu, count_time, count); return ret;
}
/** * kvm_mips_write_count() - Modify the count and update timer. * @vcpu: Virtual CPU. * @count: Guest CP0_Count value to set. * * Sets the CP0_Count value and updates the timer accordingly.
*/ void kvm_mips_write_count(struct kvm_vcpu *vcpu, u32 count)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
ktime_t now;
if (kvm_mips_count_disabled(vcpu)) /* The timer's disabled, adjust the static count */
kvm_write_c0_guest_count(cop0, count); else /* Update timeout */
kvm_mips_resume_hrtimer(vcpu, now, count);
}
/** * kvm_mips_init_count() - Initialise timer. * @vcpu: Virtual CPU. * @count_hz: Frequency of timer. * * Initialise the timer to the specified frequency, zero it, and set it going if * it's enabled.
*/ void kvm_mips_init_count(struct kvm_vcpu *vcpu, unsignedlong count_hz)
{
vcpu->arch.count_hz = count_hz;
vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz);
vcpu->arch.count_dyn_bias = 0;
/* Starting at 0 */
kvm_mips_write_count(vcpu, 0);
}
/** * kvm_mips_set_count_hz() - Update the frequency of the timer. * @vcpu: Virtual CPU. * @count_hz: Frequency of CP0_Count timer in Hz. * * Change the frequency of the CP0_Count timer. This is done atomically so that * CP0_Count is continuous and no timer interrupt is lost. * * Returns: -EINVAL if @count_hz is out of range. * 0 on success.
*/ int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0; int dc;
ktime_t now;
u32 count;
/* ensure the frequency is in a sensible range... */ if (count_hz <= 0 || count_hz > NSEC_PER_SEC) return -EINVAL; /* ... and has actually changed */ if (vcpu->arch.count_hz == count_hz) return 0;
/* Safely freeze timer so we can keep it continuous */
dc = kvm_mips_count_disabled(vcpu); if (dc) {
now = kvm_mips_count_time(vcpu);
count = kvm_read_c0_guest_count(cop0);
} else {
now = kvm_mips_freeze_hrtimer(vcpu, &count);
}
/* Update the frequency */
vcpu->arch.count_hz = count_hz;
vcpu->arch.count_period = div_u64((u64)NSEC_PER_SEC << 32, count_hz);
vcpu->arch.count_dyn_bias = 0;
/* Calculate adjusted bias so dynamic count is unchanged */
vcpu->arch.count_bias = count - kvm_mips_ktime_to_count(vcpu, now);
/* Update and resume hrtimer */ if (!dc)
kvm_mips_resume_hrtimer(vcpu, now, count); return 0;
}
/** * kvm_mips_write_compare() - Modify compare and update timer. * @vcpu: Virtual CPU. * @compare: New CP0_Compare value. * @ack: Whether to acknowledge timer interrupt. * * Update CP0_Compare to a new value and update the timeout. * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure * any pending timer interrupt is preserved.
*/ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, u32 compare, bool ack)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0; int dc;
u32 old_compare = kvm_read_c0_guest_compare(cop0);
s32 delta = compare - old_compare;
u32 cause;
ktime_t now = ktime_set(0, 0); /* silence bogus GCC warning */
u32 count;
/* if unchanged, must just be an ack */ if (old_compare == compare) { if (!ack) return;
kvm_mips_callbacks->dequeue_timer_int(vcpu);
kvm_write_c0_guest_compare(cop0, compare); return;
}
/* * If guest CP0_Compare moves forward, CP0_GTOffset should be adjusted * too to prevent guest CP0_Count hitting guest CP0_Compare. * * The new GTOffset corresponds to the new value of CP0_Compare, and is * set prior to it being written into the guest context. We disable * preemption until the new value is written to prevent restore of a * GTOffset corresponding to the old CP0_Compare value.
*/ if (delta > 0) {
preempt_disable();
write_c0_gtoffset(compare - read_c0_count());
back_to_back_c0_hazard();
}
/* freeze_hrtimer() takes care of timer interrupts <= count */
dc = kvm_mips_count_disabled(vcpu); if (!dc)
now = kvm_mips_freeze_hrtimer(vcpu, &count);
if (ack)
kvm_mips_callbacks->dequeue_timer_int(vcpu); else /* * With VZ, writing CP0_Compare acks (clears) CP0_Cause.TI, so * preserve guest CP0_Cause.TI if we don't want to ack it.
*/
cause = kvm_read_c0_guest_cause(cop0);
kvm_write_c0_guest_compare(cop0, compare);
if (delta > 0)
preempt_enable();
back_to_back_c0_hazard();
if (!ack && cause & CAUSEF_TI)
kvm_write_c0_guest_cause(cop0, cause);
/* resume_hrtimer() takes care of timer interrupts > count */ if (!dc)
kvm_mips_resume_hrtimer(vcpu, now, count);
/* * If guest CP0_Compare is moving backward, we delay CP0_GTOffset change * until after the new CP0_Compare is written, otherwise new guest * CP0_Count could hit new guest CP0_Compare.
*/ if (delta <= 0)
write_c0_gtoffset(compare - read_c0_count());
}
/** * kvm_mips_count_disable() - Disable count. * @vcpu: Virtual CPU. * * Disable the CP0_Count timer. A timer interrupt on or before the final stop * time will be handled but not after. * * Assumes CP0_Count was previously enabled but now Guest.CP0_Cause.DC or * count_ctl.DC has been set (count disabled). * * Returns: The time that the timer was stopped.
*/ static ktime_t kvm_mips_count_disable(struct kvm_vcpu *vcpu)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
u32 count;
ktime_t now;
/* Set the static count from the dynamic count, handling pending TI */
now = ktime_get();
count = kvm_mips_read_count_running(vcpu, now);
kvm_write_c0_guest_count(cop0, count);
return now;
}
/** * kvm_mips_count_disable_cause() - Disable count using CP0_Cause.DC. * @vcpu: Virtual CPU. * * Disable the CP0_Count timer and set CP0_Cause.DC. A timer interrupt on or * before the final stop time will be handled if the timer isn't disabled by * count_ctl.DC, but not after. * * Assumes CP0_Cause.DC is clear (count enabled).
*/ void kvm_mips_count_disable_cause(struct kvm_vcpu *vcpu)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
kvm_set_c0_guest_cause(cop0, CAUSEF_DC); if (!(vcpu->arch.count_ctl & KVM_REG_MIPS_COUNT_CTL_DC))
kvm_mips_count_disable(vcpu);
}
/** * kvm_mips_count_enable_cause() - Enable count using CP0_Cause.DC. * @vcpu: Virtual CPU. * * Enable the CP0_Count timer and clear CP0_Cause.DC. A timer interrupt after * the start time will be handled if the timer isn't disabled by count_ctl.DC, * potentially before even returning, so the caller should be careful with * ordering of CP0_Cause modifications so as not to lose it. * * Assumes CP0_Cause.DC is set (count disabled).
*/ void kvm_mips_count_enable_cause(struct kvm_vcpu *vcpu)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
u32 count;
kvm_clear_c0_guest_cause(cop0, CAUSEF_DC);
/* * Set the dynamic count to match the static count. * This starts the hrtimer if count_ctl.DC allows it. * Otherwise it conveniently updates the biases.
*/
count = kvm_read_c0_guest_count(cop0);
kvm_mips_write_count(vcpu, count);
}
/** * kvm_mips_set_count_ctl() - Update the count control KVM register. * @vcpu: Virtual CPU. * @count_ctl: Count control register new value. * * Set the count control KVM register. The timer is updated accordingly. * * Returns: -EINVAL if reserved bits are set. * 0 on success.
*/ int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl)
{ struct mips_coproc *cop0 = &vcpu->arch.cop0;
s64 changed = count_ctl ^ vcpu->arch.count_ctl;
s64 delta;
ktime_t expire, now;
u32 count, compare;
/* Only allow defined bits to be changed */ if (changed & ~(s64)(KVM_REG_MIPS_COUNT_CTL_DC)) return -EINVAL;
/* Apply new value */
vcpu->arch.count_ctl = count_ctl;
/* Master CP0_Count disable */ if (changed & KVM_REG_MIPS_COUNT_CTL_DC) { /* Is CP0_Cause.DC already disabling CP0_Count? */ if (kvm_read_c0_guest_cause(cop0) & CAUSEF_DC) { if (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) /* Just record the current time */
vcpu->arch.count_resume = ktime_get();
} elseif (count_ctl & KVM_REG_MIPS_COUNT_CTL_DC) { /* disable timer and record current time */
vcpu->arch.count_resume = kvm_mips_count_disable(vcpu);
} else { /* * Calculate timeout relative to static count at resume * time (wrap 0 to 2^32).
*/
count = kvm_read_c0_guest_count(cop0);
compare = kvm_read_c0_guest_compare(cop0);
delta = (u64)(u32)(compare - count - 1) + 1;
delta = div_u64(delta * NSEC_PER_SEC,
vcpu->arch.count_hz);
expire = ktime_add_ns(vcpu->arch.count_resume, delta);
/* Handle pending interrupt */
now = ktime_get(); if (ktime_compare(now, expire) >= 0) /* Nothing should be waiting on the timeout */
kvm_mips_callbacks->queue_timer_int(vcpu);
/** * kvm_mips_set_count_resume() - Update the count resume KVM register. * @vcpu: Virtual CPU. * @count_resume: Count resume register new value. * * Set the count resume KVM register. * * Returns: -EINVAL if out of valid range (0..now). * 0 on success.
*/ int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume)
{ /* * It doesn't make sense for the resume time to be in the future, as it * would be possible for the next interrupt to be more than a full * period in the future.
*/ if (count_resume < 0 || count_resume > ktime_to_ns(ktime_get())) return -EINVAL;
/** * kvm_mips_count_timeout() - Push timer forward on timeout. * @vcpu: Virtual CPU. * * Handle an hrtimer event by push the hrtimer forward a period. * * Returns: The hrtimer_restart value to return to the hrtimer subsystem.
*/ enum hrtimer_restart kvm_mips_count_timeout(struct kvm_vcpu *vcpu)
{ /* Add the Count period to the current expiry time */
hrtimer_add_expires_ns(&vcpu->arch.comparecount_timer,
vcpu->arch.count_period); return HRTIMER_RESTART;
}
/* * We are runnable, then definitely go off to user space to * check if any I/O interrupts are pending.
*/ if (kvm_arch_vcpu_runnable(vcpu))
vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN;
}
/* * Update PC and hold onto current PC in case there is * an error and we want to rollback the PC
*/
curr_pc = vcpu->arch.pc;
er = update_pc(vcpu, cause); if (er == EMULATE_FAIL) return er;
rt = inst.i_format.rt;
run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
vcpu->arch.host_cp0_badvaddr); if (run->mmio.phys_addr == KVM_INVALID_ADDR) goto out_fail;
/* * Find the resume PC now while we have safe and easy access to the * prior branch instruction, and save it for * kvm_mips_complete_mmio_load() to restore later.
*/
curr_pc = vcpu->arch.pc;
er = update_pc(vcpu, cause); if (er == EMULATE_FAIL) return er;
vcpu->arch.io_pc = vcpu->arch.pc;
vcpu->arch.pc = curr_pc;
vcpu->arch.io_gpr = rt;
run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
vcpu->arch.host_cp0_badvaddr); if (run->mmio.phys_addr == KVM_INVALID_ADDR) return EMULATE_FAIL;
vcpu->mmio_needed = 2; /* signed */ switch (op) { #ifdefined(CONFIG_64BIT) case ld_op:
run->mmio.len = 8; break;
case lwu_op:
vcpu->mmio_needed = 1; /* unsigned */
fallthrough; #endif case lw_op:
run->mmio.len = 4; break;
case lhu_op:
vcpu->mmio_needed = 1; /* unsigned */
fallthrough; case lh_op:
run->mmio.len = 2; break;
case lbu_op:
vcpu->mmio_needed = 1; /* unsigned */
fallthrough; case lb_op:
run->mmio.len = 1; break;
case lwl_op:
run->mmio.phys_addr = kvm_mips_callbacks->gva_to_gpa(
vcpu->arch.host_cp0_badvaddr) & (~0x3);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.