[PATCH v5 07/12] target-mips: kvm: Add main KVM support for MIPS

James Hogan
2014-06-17T22:14:14Z

From: Sanjay Lal <sanjayl@kymasys.com>

Implement the main KVM arch API for MIPS.

Signed-off-by: Sanjay Lal <sanjayl@kymasys.com>
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: Gleb Natapov <gleb@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Andreas Färber <afaerber@suse.de>
Cc: Peter Maydell <peter.maydell@linaro.org>
---
Changes in v5:
- Rename kvm_arch_reset_vcpu to kvm_mips_reset_vcpu based on commit
50a2c6e55fa2 (kvm: reset state from the CPU's reset method).
- Rename kvm_mips_te_{put,get}_cp0_registers() functions to drop the
"te_" since they're not really specific to T&E.
- Pass level through from kvm_arch_put_registers() to
kvm_mips_put_cp0_registers() rather than hard coding it to
KVM_PUT_FULL_STATE.
- Fix KVM_REG_MIPS_CP0_* definitions to set KVM_REG_MIPS and
KVM_REG_SIZE_U32/KVM_REG_SIZE_U64 (using a macro).
- Remove unused KVM_REG_MIPS_CP0_* definitions for now.
- Correct type of kvm_mips_{get,put}_one_{,ul}reg() reg_id argument to
uint64_t. Various high bits must be set to disambiguate the
architecture and register size.
- Add register accessors for always-64-bit registers (rather than ulong
registers). These are needed for virtual KVM registers for
controlling the KVM Compare/Count timer.
- Simplify register access functions slightly.
- Save and restore KVM timer state with the rest of the state, and also
when VM clock is started or stopped. When the KVM timer state is
restored (or VM clock restarted) it is resumed with the stored count
at the monotonic time when the VM clock was last stopped. If the VM
clock hasn't been stopped it resumes from the monotonic time when the
state was saved (i.e. as if the timer was never stopped).
Changes since RFC patch on kernel KVM thread "[PATCH v2 00/23] MIPS:
KVM: Fixes and guest timer rewrite"):
- Simplified, removing extra state for storing VM time of
save/restore, at the cost of losing/gaining time when VM gets
stopped and started (Paolo Bonzini).
- Save and restore the UserLocal and HWREna CP0 registers.
- Improve get/put KVM register error handling with DPRINTFs and fall
through so that getting/putting of all the registers is attempted
even if one of them fails due to being unimplemented in the kernel.

Changes in v4:
(No functional changes, assembly output unchanged)
- Use int32_t instead of int32 (which is for softfloat) in kvm register
accessors (Andreas Färber).
- Use uint64_t instead of __u64 (which is really just for kernel
headers) in the kvm register accessors (Andreas Färber).
- Cast pointer to uintptr_t rather than target_ulong in kvm register
accessors.
- Remove some redundant casts in kvm register accessors.

Changes in v3:
- s/dprintf/DPRINTF/ (Andreas Färber).
- Use "cs" rather than "cpu" or "env" for CPUState variable names
(Andreas Färber).
- Use CPUMIPSState rather than CPUArchState (Andreas Färber).
- Pass MIPSCPU to cpu_mips_io_interrupts_pending() rather than
CPUMIPSState (Andreas Färber).
- Remove spurious parentheses around cpu_mips_io_interrupts_pending()
call (Andreas Färber).
- Pass MIPSCPU to kvm_mips_set_[ipi_]interrupt (Andreas Färber).
- Make use of error_report (Andreas Färber) and clean up error messages
a little to include __func__.
- Remove inline kvm_mips_{put,get}_one_[ul]reg() declarations from
kvm_mips.h. They're only used in target-mips/kvm.c anyway.
- Make kvm_arch_{put,get}_registers static within target-mips/kvm.c and
remove from kvm_mips.h.
- Set sigmask length to 16 from kvm_arch_init() since MIPS Linux has
128 signals. This is better than cluttering kvm_all.c with TARGET_*
ifdefs (Peter Maydell).

Changes in v2:
- Expand commit message
- Checkpatch cleanups.
- Some interrupt bug fixes from Yann Le Du <ledu@kymasys.com>
- Add get/set register functionality from Yann Le Du <ledu@kymasys.com>
- Use new 64 bit compatible ABI from Cavium from Sanjay Lal
<sanjayl@kymasys.com>
- Add dummy kvm_arch_init_irq_routing()
The common KVM code insists on calling kvm_arch_init_irq_routing() as
soon as it sees kernel header support for it (regardless of whether
QEMU supports it). Provide a dummy function to satisfy this.
- Remove request_interrupt_window code (Peter Maydell)
---
target-mips/kvm.c | 683 +++++++++++++++++++++++++++++++++++++++++++++++++
target-mips/kvm_mips.h | 26 ++
2 files changed, 709 insertions(+)
create mode 100644 target-mips/kvm.c
create mode 100644 target-mips/kvm_mips.h

diff --git a/target-mips/kvm.c b/target-mips/kvm.c
new file mode 100644
index 000000000000..844e5bbe5f92
--- /dev/null
+++ b/target-mips/kvm.c
@@ -0,0 +1,683 @@
+/*
+ * 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: MIPS specific KVM APIs
+ *
+ * Copyright (C) 2012-2014 Imagination Technologies Ltd.
+ * Authors: Sanjay Lal <sanjayl@kymasys.com>
+*/
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "cpu.h"
+#include "sysemu/cpus.h"
+#include "kvm_mips.h"
+
+#define DEBUG_KVM 0
+
+#define DPRINTF(fmt, ...) \
+ do { if (DEBUG_KVM) { fprintf(stderr, fmt, ## __VA_ARGS__); } } while (0)
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+static void kvm_mips_update_state(void *opaque, int running, RunState state);
+
+unsigned long kvm_arch_vcpu_id(CPUState *cs)
+{
+ return cs->cpu_index;
+}
+
+int kvm_arch_init(KVMState *s)
+{
+ /* MIPS has 128 signals */
+ kvm_set_sigmask_len(s, 16);
+
+ DPRINTF("%s\n", __func__);
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ int ret = 0;
+
+ qemu_add_vm_change_state_handler(kvm_mips_update_state, cs);
+
+ DPRINTF("%s\n", __func__);
+ return ret;
+}
+
+void kvm_mips_reset_vcpu(MIPSCPU *cpu)
+{
+ DPRINTF("%s\n", __func__);
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+ DPRINTF("%s\n", __func__);
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
+{
+ DPRINTF("%s\n", __func__);
+ return 0;
+}
+
+static inline int cpu_mips_io_interrupts_pending(MIPSCPU *cpu)
+{
+ CPUMIPSState *env = &cpu->env;
+
+ DPRINTF("%s: %#x\n", __func__, env->CP0_Cause & (1 << (2 + CP0Ca_IP)));
+ return env->CP0_Cause & (0x1 << (2 + CP0Ca_IP));
+}
+
+
+void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ int r;
+ struct kvm_mips_interrupt intr;
+
+ if ((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
+ cpu_mips_io_interrupts_pending(cpu)) {
+ intr.cpu = -1;
+ intr.irq = 2;
+ r = kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr);
+ if (r < 0) {
+ error_report("%s: cpu %d: failed to inject IRQ %x",
+ __func__, cs->cpu_index, intr.irq);
+ }
+ }
+}
+
+void kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
+{
+ DPRINTF("%s\n", __func__);
+}
+
+int kvm_arch_process_async_events(CPUState *cs)
+{
+ return cs->halted;
+}
+
+int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
+{
+ int ret;
+
+ DPRINTF("%s\n", __func__);
+ switch (run->exit_reason) {
+ default:
+ error_report("%s: unknown exit reason %d",
+ __func__, run->exit_reason);
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUState *cs)
+{
+ DPRINTF("%s\n", __func__);
+ return true;
+}
+
+int kvm_arch_on_sigbus_vcpu(CPUState *cs, int code, void *addr)
+{
+ DPRINTF("%s\n", __func__);
+ return 1;
+}
+
+int kvm_arch_on_sigbus(int code, void *addr)
+{
+ DPRINTF("%s\n", __func__);
+ return 1;
+}
+
+void kvm_arch_init_irq_routing(KVMState *s)
+{
+}
+
+int kvm_mips_set_interrupt(MIPSCPU *cpu, int irq, int level)
+{
+ CPUState *cs = CPU(cpu);
+ struct kvm_mips_interrupt intr;
+
+ if (!kvm_enabled()) {
+ return 0;
+ }
+
+ intr.cpu = -1;
+
+ if (level) {
+ intr.irq = irq;
+ } else {
+ intr.irq = -irq;
+ }
+
+ kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr);
+
+ return 0;
+}
+
+int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level)
+{
+ CPUState *cs = current_cpu;
+ CPUState *dest_cs = CPU(cpu);
+ struct kvm_mips_interrupt intr;
+
+ if (!kvm_enabled()) {
+ return 0;
+ }
+
+ intr.cpu = dest_cs->cpu_index;
+
+ if (level) {
+ intr.irq = irq;
+ } else {
+ intr.irq = -irq;
+ }
+
+ DPRINTF("%s: CPU %d, IRQ: %d\n", __func__, intr.cpu, intr.irq);
+
+ kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr);
+
+ return 0;
+}
+
+#define MIPS_CP0_32(_R, _S) \
+ (KVM_REG_MIPS | KVM_REG_SIZE_U32 | 0x10000 | (8 * (_R) + (_S)))
+
+#define MIPS_CP0_64(_R, _S) \
+ (KVM_REG_MIPS | KVM_REG_SIZE_U64 | 0x10000 | (8 * (_R) + (_S)))
+
+#define KVM_REG_MIPS_CP0_INDEX MIPS_CP0_32(0, 0)
+#define KVM_REG_MIPS_CP0_CONTEXT MIPS_CP0_64(4, 0)
+#define KVM_REG_MIPS_CP0_USERLOCAL MIPS_CP0_64(4, 2)
+#define KVM_REG_MIPS_CP0_PAGEMASK MIPS_CP0_32(5, 0)
+#define KVM_REG_MIPS_CP0_WIRED MIPS_CP0_32(6, 0)
+#define KVM_REG_MIPS_CP0_HWRENA MIPS_CP0_32(7, 0)
+#define KVM_REG_MIPS_CP0_BADVADDR MIPS_CP0_64(8, 0)
+#define KVM_REG_MIPS_CP0_COUNT MIPS_CP0_32(9, 0)
+#define KVM_REG_MIPS_CP0_ENTRYHI MIPS_CP0_64(10, 0)
+#define KVM_REG_MIPS_CP0_COMPARE MIPS_CP0_32(11, 0)
+#define KVM_REG_MIPS_CP0_STATUS MIPS_CP0_32(12, 0)
+#define KVM_REG_MIPS_CP0_CAUSE MIPS_CP0_32(13, 0)
+#define KVM_REG_MIPS_CP0_EPC MIPS_CP0_64(14, 0)
+#define KVM_REG_MIPS_CP0_ERROREPC MIPS_CP0_64(30, 0)
+
+/* CP0_Count control */
+#define KVM_REG_MIPS_COUNT_CTL (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
+ 0x20000 | 0)
+#define KVM_REG_MIPS_COUNT_CTL_DC 0x00000001 /* master disable */
+/* CP0_Count resume monotonic nanoseconds */
+#define KVM_REG_MIPS_COUNT_RESUME (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
+ 0x20000 | 1)
+/* CP0_Count rate in Hz */
+#define KVM_REG_MIPS_COUNT_HZ (KVM_REG_MIPS | KVM_REG_SIZE_U64 | \
+ 0x20000 | 2)
+
+static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id,
+ int32_t *addr)
+{
+ uint64_t val64 = *addr;
+ struct kvm_one_reg cp0reg = {
+ .id = reg_id,
+ .addr = (uintptr_t)&val64
+ };
+
+ return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);
+}
+
+static inline int kvm_mips_put_one_ulreg(CPUState *cs, uint64_t reg_id,
+ target_ulong *addr)
+{
+ uint64_t val64 = *addr;
+ struct kvm_one_reg cp0reg = {
+ .id = reg_id,
+ .addr = (uintptr_t)&val64
+ };
+
+ return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);
+}
+
+static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id,
+ uint64_t *addr)
+{
+ struct kvm_one_reg cp0reg = {
+ .id = reg_id,
+ .addr = (uintptr_t)addr
+ };
+
+ return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg);
+}
+
+static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id,
+ int32_t *addr)
+{
+ int ret;
+ uint64_t val64 = 0;
+ struct kvm_one_reg cp0reg = {
+ .id = reg_id,
+ .addr = (uintptr_t)&val64
+ };
+
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
+ if (ret >= 0) {
+ *addr = val64;
+ }
+ return ret;
+}
+
+static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id,
+ target_ulong *addr)
+{
+ int ret;
+ uint64_t val64 = 0;
+ struct kvm_one_reg cp0reg = {
+ .id = reg_id,
+ .addr = (uintptr_t)&val64
+ };
+
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
+ if (ret >= 0) {
+ *addr = val64;
+ }
+ return ret;
+}
+
+static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64 reg_id,
+ uint64_t *addr)
+{
+ struct kvm_one_reg cp0reg = {
+ .id = reg_id,
+ .addr = (uintptr_t)addr
+ };
+
+ return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
+}
+
+/*
+ * We freeze the KVM timer when either the VM clock is stopped or the state is
+ * saved (the state is dirty).
+ */
+
+/*
+ * Save the state of the KVM timer when VM clock is stopped or state is synced
+ * to QEMU.
+ */
+static int kvm_mips_save_count(CPUState *cs)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ uint64_t count_ctl;
+ int err, ret = 0;
+
+ /* freeze KVM timer */
+ err = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get COUNT_CTL (%d)\n", __func__, err);
+ ret = err;
+ } else if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) {
+ count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC;
+ err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
+ if (err < 0) {
+ DPRINTF("%s: Failed to set COUNT_CTL.DC=1 (%d)\n", __func__, err);
+ ret = err;
+ }
+ }
+
+ /* read CP0_Cause */
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_CAUSE (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ /* read CP0_Count */
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_COUNT (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ return ret;
+}
+
+/*
+ * Restore the state of the KVM timer when VM clock is restarted or state is
+ * synced to KVM.
+ */
+static int kvm_mips_restore_count(CPUState *cs)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ uint64_t count_ctl;
+ int err_dc, err, ret = 0;
+
+ /* check the timer is frozen */
+ err_dc = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
+ if (err_dc < 0) {
+ DPRINTF("%s: Failed to get COUNT_CTL (%d)\n", __func__, err_dc);
+ ret = err_dc;
+ } else if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) {
+ /* freeze timer (sets COUNT_RESUME for us) */
+ count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC;
+ err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
+ if (err < 0) {
+ DPRINTF("%s: Failed to set COUNT_CTL.DC=1 (%d)\n", __func__, err);
+ ret = err;
+ }
+ }
+
+ /* load CP0_Cause */
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_CAUSE (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ /* load CP0_Count */
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_COUNT (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ /* resume KVM timer */
+ if (err_dc >= 0) {
+ count_ctl &= ~KVM_REG_MIPS_COUNT_CTL_DC;
+ err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl);
+ if (err < 0) {
+ DPRINTF("%s: Failed to set COUNT_CTL.DC=0 (%d)\n", __func__, err);
+ ret = err;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Handle the VM clock being started or stopped
+ */
+static void kvm_mips_update_state(void *opaque, int running, RunState state)
+{
+ CPUState *cs = opaque;
+ int ret;
+ uint64_t count_resume;
+
+ /*
+ * If state is already dirty (synced to QEMU) then the KVM timer state is
+ * already saved and can be restored when it is synced back to KVM.
+ */
+ if (!running) {
+ if (!cs->kvm_vcpu_dirty) {
+ ret = kvm_mips_save_count(cs);
+ if (ret < 0) {
+ fprintf(stderr, "Failed saving count\n");
+ }
+ }
+ } else {
+ /* Set clock restore time to now */
+ count_resume = get_clock();
+ ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME,
+ &count_resume);
+ if (ret < 0) {
+ fprintf(stderr, "Failed setting COUNT_RESUME\n");
+ return;
+ }
+
+ if (!cs->kvm_vcpu_dirty) {
+ ret = kvm_mips_restore_count(cs);
+ if (ret < 0) {
+ fprintf(stderr, "Failed restoring count\n");
+ }
+ }
+ }
+}
+
+static int kvm_mips_put_cp0_registers(CPUState *cs, int level)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ int err, ret = 0;
+
+ (void)level;
+
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_INDEX, &env->CP0_Index);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_INDEX (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_CONTEXT,
+ &env->CP0_Context);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_CONTEXT (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_USERLOCAL,
+ &env->active_tc.CP0_UserLocal);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_USERLOCAL (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_PAGEMASK,
+ &env->CP0_PageMask);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_PAGEMASK (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_WIRED, &env->CP0_Wired);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_WIRED (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_HWRENA, &env->CP0_HWREna);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_HWRENA (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_BADVADDR,
+ &env->CP0_BadVAddr);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_BADVADDR (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ /* If VM clock stopped then state will be restored when it is restarted */
+ if (runstate_is_running()) {
+ err = kvm_mips_restore_count(cs);
+ if (err < 0) {
+ ret = err;
+ }
+ }
+
+ err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI,
+ &env->CP0_EntryHi);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_ENTRYHI (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COMPARE,
+ &env->CP0_Compare);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_COMPARE (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_STATUS, &env->CP0_Status);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_STATUS (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_EPC, &env->CP0_EPC);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_EPC (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ERROREPC,
+ &env->CP0_ErrorEPC);
+ if (err < 0) {
+ DPRINTF("%s: Failed to put CP0_ERROREPC (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ return ret;
+}
+
+static int kvm_mips_get_cp0_registers(CPUState *cs)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ int err, ret = 0;
+
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_INDEX, &env->CP0_Index);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_INDEX (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_CONTEXT,
+ &env->CP0_Context);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_CONTEXT (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_USERLOCAL,
+ &env->active_tc.CP0_UserLocal);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_USERLOCAL (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_PAGEMASK,
+ &env->CP0_PageMask);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_PAGEMASK (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_WIRED, &env->CP0_Wired);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_WIRED (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_HWRENA, &env->CP0_HWREna);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_HWRENA (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_BADVADDR,
+ &env->CP0_BadVAddr);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_BADVADDR (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI,
+ &env->CP0_EntryHi);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_ENTRYHI (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COMPARE,
+ &env->CP0_Compare);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_COMPARE (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_STATUS, &env->CP0_Status);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_STATUS (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ /* If VM clock stopped then state was already saved when it was stopped */
+ if (runstate_is_running()) {
+ err = kvm_mips_save_count(cs);
+ if (err < 0) {
+ ret = err;
+ }
+ }
+
+ err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_EPC, &env->CP0_EPC);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_EPC (%d)\n", __func__, err);
+ ret = err;
+ }
+ err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ERROREPC,
+ &env->CP0_ErrorEPC);
+ if (err < 0) {
+ DPRINTF("%s: Failed to get CP0_ERROREPC (%d)\n", __func__, err);
+ ret = err;
+ }
+
+ return ret;
+}
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ struct kvm_regs regs;
+ int ret;
+ int i;
+
+ /* Set the registers based on QEMU's view of things */
+ for (i = 0; i < 32; i++) {
+ regs.gpr[i] = env->active_tc.gpr[i];
+ }
+
+ regs.hi = env->active_tc.HI[0];
+ regs.lo = env->active_tc.LO[0];
+ regs.pc = env->active_tc.PC;
+
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, &regs);
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = kvm_mips_put_cp0_registers(cs, level);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ MIPSCPU *cpu = MIPS_CPU(cs);
+ CPUMIPSState *env = &cpu->env;
+ int ret = 0;
+ struct kvm_regs regs;
+ int i;
+
+ /* Get the current register set as KVM seems it */
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, &regs);
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < 32; i++) {
+ env->active_tc.gpr[i] = regs.gpr[i];
+ }
+
+ env->active_tc.HI[0] = regs.hi;
+ env->active_tc.LO[0] = regs.lo;
+ env->active_tc.PC = regs.pc;
+
+ kvm_mips_get_cp0_registers(cs);
+
+ return ret;
+}
diff --git a/target-mips/kvm_mips.h b/target-mips/kvm_mips.h
new file mode 100644
index 000000000000..54f59656efb3
--- /dev/null
+++ b/target-mips/kvm_mips.h
@@ -0,0 +1,26 @@
+/*
+ * 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: MIPS specific KVM APIs
+ *
+ * Copyright (C) 2012-2014 Imagination Technologies Ltd.
+ * Authors: Sanjay Lal <sanjayl@kymasys.com>
+*/
+
+#ifndef __KVM_MIPS_H__
+#define __KVM_MIPS_H__
+
+/**
+ * kvm_mips_reset_vcpu:
+ * @cpu: MIPSCPU
+ *
+ * Called at reset time to set kernel registers to their initial values.
+ */
+void kvm_mips_reset_vcpu(MIPSCPU *cpu);
+
+int kvm_mips_set_interrupt(MIPSCPU *cpu, int irq, int level);
+int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level);
+
+#endif /* __KVM_MIPS_H__ */
--
1.9.3