qemu-patch-raspberry4/target-mips/kvm.c
James Hogan bee62662a3 mips/kvm: Support MSA in MIPS KVM guests
Support the new KVM_CAP_MIPS_MSA capability, which allows MIPS SIMD
Architecture (MSA) to be exposed to the KVM guest.

The capability is enabled if the guest core has MSA according to its
Config3 register. Various config bits are now writeable so that KVM is
aware of the configuration (Config3.MSAP) and so that QEMU can
save/restore the guest modifiable bits (Config5.MSAEn). The MSACSR/MSAIR
registers and the MSA vector registers are now saved/restored. Since the
FP registers are a subset of the vector registers, they are omitted if
the guest has MSA.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Leon Alrae <leon.alrae@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
2016-02-26 08:59:17 +00:00

1051 lines
31 KiB
C

/*
* 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 "qemu/osdep.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"
#include "exec/memattrs.h"
#define DEBUG_KVM 0
#define DPRINTF(fmt, ...) \
do { if (DEBUG_KVM) { fprintf(stderr, fmt, ## __VA_ARGS__); } } while (0)
static int kvm_mips_fpu_cap;
static int kvm_mips_msa_cap;
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(MachineState *ms, KVMState *s)
{
/* MIPS has 128 signals */
kvm_set_sigmask_len(s, 16);
kvm_mips_fpu_cap = kvm_check_extension(s, KVM_CAP_MIPS_FPU);
kvm_mips_msa_cap = kvm_check_extension(s, KVM_CAP_MIPS_MSA);
DPRINTF("%s\n", __func__);
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
int ret = 0;
qemu_add_vm_change_state_handler(kvm_mips_update_state, cs);
if (kvm_mips_fpu_cap && env->CP0_Config1 & (1 << CP0C1_FP)) {
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_MIPS_FPU, 0, 0);
if (ret < 0) {
/* mark unsupported so it gets disabled on reset */
kvm_mips_fpu_cap = 0;
ret = 0;
}
}
if (kvm_mips_msa_cap && env->CP0_Config3 & (1 << CP0C3_MSAP)) {
ret = kvm_vcpu_enable_cap(cs, KVM_CAP_MIPS_MSA, 0, 0);
if (ret < 0) {
/* mark unsupported so it gets disabled on reset */
kvm_mips_msa_cap = 0;
ret = 0;
}
}
DPRINTF("%s\n", __func__);
return ret;
}
void kvm_mips_reset_vcpu(MIPSCPU *cpu)
{
CPUMIPSState *env = &cpu->env;
if (!kvm_mips_fpu_cap && env->CP0_Config1 & (1 << CP0C1_FP)) {
fprintf(stderr, "Warning: KVM does not support FPU, disabling\n");
env->CP0_Config1 &= ~(1 << CP0C1_FP);
}
if (!kvm_mips_msa_cap && env->CP0_Config3 & (1 << CP0C3_MSAP)) {
fprintf(stderr, "Warning: KVM does not support MSA, disabling\n");
env->CP0_Config3 &= ~(1 << CP0C3_MSAP);
}
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;
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;
qemu_mutex_lock_iothread();
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);
}
}
qemu_mutex_unlock_iothread();
}
MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
{
return MEMTXATTRS_UNSPECIFIED;
}
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_CP0 | KVM_REG_SIZE_U32 | (8 * (_R) + (_S)))
#define MIPS_CP0_64(_R, _S) \
(KVM_REG_MIPS_CP0 | KVM_REG_SIZE_U64 | (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_PRID MIPS_CP0_32(15, 0)
#define KVM_REG_MIPS_CP0_CONFIG MIPS_CP0_32(16, 0)
#define KVM_REG_MIPS_CP0_CONFIG1 MIPS_CP0_32(16, 1)
#define KVM_REG_MIPS_CP0_CONFIG2 MIPS_CP0_32(16, 2)
#define KVM_REG_MIPS_CP0_CONFIG3 MIPS_CP0_32(16, 3)
#define KVM_REG_MIPS_CP0_CONFIG4 MIPS_CP0_32(16, 4)
#define KVM_REG_MIPS_CP0_CONFIG5 MIPS_CP0_32(16, 5)
#define KVM_REG_MIPS_CP0_ERROREPC MIPS_CP0_64(30, 0)
static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id,
int32_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_put_one_ureg(CPUState *cs, uint64_t reg_id,
uint32_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_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,
int64_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_put_one_ureg64(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)
{
struct kvm_one_reg cp0reg = {
.id = reg_id,
.addr = (uintptr_t)addr
};
return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
}
static inline int kvm_mips_get_one_ureg(CPUState *cs, uint64_t reg_id,
uint32_t *addr)
{
struct kvm_one_reg cp0reg = {
.id = reg_id,
.addr = (uintptr_t)addr
};
return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
}
static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64_t 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_t reg_id,
int64_t *addr)
{
struct kvm_one_reg cp0reg = {
.id = reg_id,
.addr = (uintptr_t)addr
};
return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg);
}
static inline int kvm_mips_get_one_ureg64(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_GET_ONE_REG, &cp0reg);
}
#define KVM_REG_MIPS_CP0_CONFIG_MASK (1U << CP0C0_M)
#define KVM_REG_MIPS_CP0_CONFIG1_MASK ((1U << CP0C1_M) | \
(1U << CP0C1_FP))
#define KVM_REG_MIPS_CP0_CONFIG2_MASK (1U << CP0C2_M)
#define KVM_REG_MIPS_CP0_CONFIG3_MASK ((1U << CP0C3_M) | \
(1U << CP0C3_MSAP))
#define KVM_REG_MIPS_CP0_CONFIG4_MASK (1U << CP0C4_M)
#define KVM_REG_MIPS_CP0_CONFIG5_MASK ((1U << CP0C5_MSAEn) | \
(1U << CP0C5_UFE) | \
(1U << CP0C5_FRE) | \
(1U << CP0C5_UFR))
static inline int kvm_mips_change_one_reg(CPUState *cs, uint64_t reg_id,
int32_t *addr, int32_t mask)
{
int err;
int32_t tmp, change;
err = kvm_mips_get_one_reg(cs, reg_id, &tmp);
if (err < 0) {
return err;
}
/* only change bits in mask */
change = (*addr ^ tmp) & mask;
if (!change) {
return 0;
}
tmp = tmp ^ change;
return kvm_mips_put_one_reg(cs, reg_id, &tmp);
}
/*
* 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_ureg64(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_ureg64(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_ureg64(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_ureg64(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_ureg64(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 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
ret = kvm_mips_put_one_ureg64(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_fpu_registers(CPUState *cs, int level)
{
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
int err, ret = 0;
unsigned int i;
/* Only put FPU state if we're emulating a CPU with an FPU */
if (env->CP0_Config1 & (1 << CP0C1_FP)) {
/* FPU Control Registers */
if (level == KVM_PUT_FULL_STATE) {
err = kvm_mips_put_one_ureg(cs, KVM_REG_MIPS_FCR_IR,
&env->active_fpu.fcr0);
if (err < 0) {
DPRINTF("%s: Failed to put FCR_IR (%d)\n", __func__, err);
ret = err;
}
}
err = kvm_mips_put_one_ureg(cs, KVM_REG_MIPS_FCR_CSR,
&env->active_fpu.fcr31);
if (err < 0) {
DPRINTF("%s: Failed to put FCR_CSR (%d)\n", __func__, err);
ret = err;
}
/*
* FPU register state is a subset of MSA vector state, so don't put FPU
* registers if we're emulating a CPU with MSA.
*/
if (!(env->CP0_Config3 & (1 << CP0C3_MSAP))) {
/* Floating point registers */
for (i = 0; i < 32; ++i) {
if (env->CP0_Status & (1 << CP0St_FR)) {
err = kvm_mips_put_one_ureg64(cs, KVM_REG_MIPS_FPR_64(i),
&env->active_fpu.fpr[i].d);
} else {
err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FPR_32(i),
&env->active_fpu.fpr[i].w[FP_ENDIAN_IDX]);
}
if (err < 0) {
DPRINTF("%s: Failed to put FPR%u (%d)\n", __func__, i, err);
ret = err;
}
}
}
}
/* Only put MSA state if we're emulating a CPU with MSA */
if (env->CP0_Config3 & (1 << CP0C3_MSAP)) {
/* MSA Control Registers */
if (level == KVM_PUT_FULL_STATE) {
err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_MSA_IR,
&env->msair);
if (err < 0) {
DPRINTF("%s: Failed to put MSA_IR (%d)\n", __func__, err);
ret = err;
}
}
err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_MSA_CSR,
&env->active_tc.msacsr);
if (err < 0) {
DPRINTF("%s: Failed to put MSA_CSR (%d)\n", __func__, err);
ret = err;
}
/* Vector registers (includes FP registers) */
for (i = 0; i < 32; ++i) {
/* Big endian MSA not supported by QEMU yet anyway */
err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_VEC_128(i),
env->active_fpu.fpr[i].wr.d);
if (err < 0) {
DPRINTF("%s: Failed to put VEC%u (%d)\n", __func__, i, err);
ret = err;
}
}
}
return ret;
}
static int kvm_mips_get_fpu_registers(CPUState *cs)
{
MIPSCPU *cpu = MIPS_CPU(cs);
CPUMIPSState *env = &cpu->env;
int err, ret = 0;
unsigned int i;
/* Only get FPU state if we're emulating a CPU with an FPU */
if (env->CP0_Config1 & (1 << CP0C1_FP)) {
/* FPU Control Registers */
err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FCR_IR,
&env->active_fpu.fcr0);
if (err < 0) {
DPRINTF("%s: Failed to get FCR_IR (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FCR_CSR,
&env->active_fpu.fcr31);
if (err < 0) {
DPRINTF("%s: Failed to get FCR_CSR (%d)\n", __func__, err);
ret = err;
} else {
restore_fp_status(env);
}
/*
* FPU register state is a subset of MSA vector state, so don't save FPU
* registers if we're emulating a CPU with MSA.
*/
if (!(env->CP0_Config3 & (1 << CP0C3_MSAP))) {
/* Floating point registers */
for (i = 0; i < 32; ++i) {
if (env->CP0_Status & (1 << CP0St_FR)) {
err = kvm_mips_get_one_ureg64(cs, KVM_REG_MIPS_FPR_64(i),
&env->active_fpu.fpr[i].d);
} else {
err = kvm_mips_get_one_ureg(cs, KVM_REG_MIPS_FPR_32(i),
&env->active_fpu.fpr[i].w[FP_ENDIAN_IDX]);
}
if (err < 0) {
DPRINTF("%s: Failed to get FPR%u (%d)\n", __func__, i, err);
ret = err;
}
}
}
}
/* Only get MSA state if we're emulating a CPU with MSA */
if (env->CP0_Config3 & (1 << CP0C3_MSAP)) {
/* MSA Control Registers */
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_MSA_IR,
&env->msair);
if (err < 0) {
DPRINTF("%s: Failed to get MSA_IR (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_MSA_CSR,
&env->active_tc.msacsr);
if (err < 0) {
DPRINTF("%s: Failed to get MSA_CSR (%d)\n", __func__, err);
ret = err;
} else {
restore_msa_fp_status(env);
}
/* Vector registers (includes FP registers) */
for (i = 0; i < 32; ++i) {
/* Big endian MSA not supported by QEMU yet anyway */
err = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_VEC_128(i),
env->active_fpu.fpr[i].wr.d);
if (err < 0) {
DPRINTF("%s: Failed to get VEC%u (%d)\n", __func__, i, err);
ret = err;
}
}
}
return ret;
}
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_reg(cs, KVM_REG_MIPS_CP0_PRID, &env->CP0_PRid);
if (err < 0) {
DPRINTF("%s: Failed to put CP0_PRID (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG,
&env->CP0_Config0,
KVM_REG_MIPS_CP0_CONFIG_MASK);
if (err < 0) {
DPRINTF("%s: Failed to change CP0_CONFIG (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG1,
&env->CP0_Config1,
KVM_REG_MIPS_CP0_CONFIG1_MASK);
if (err < 0) {
DPRINTF("%s: Failed to change CP0_CONFIG1 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG2,
&env->CP0_Config2,
KVM_REG_MIPS_CP0_CONFIG2_MASK);
if (err < 0) {
DPRINTF("%s: Failed to change CP0_CONFIG2 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG3,
&env->CP0_Config3,
KVM_REG_MIPS_CP0_CONFIG3_MASK);
if (err < 0) {
DPRINTF("%s: Failed to change CP0_CONFIG3 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG4,
&env->CP0_Config4,
KVM_REG_MIPS_CP0_CONFIG4_MASK);
if (err < 0) {
DPRINTF("%s: Failed to change CP0_CONFIG4 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_change_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG5,
&env->CP0_Config5,
KVM_REG_MIPS_CP0_CONFIG5_MASK);
if (err < 0) {
DPRINTF("%s: Failed to change CP0_CONFIG5 (%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_reg(cs, KVM_REG_MIPS_CP0_PRID, &env->CP0_PRid);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_PRID (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG, &env->CP0_Config0);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_CONFIG (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG1, &env->CP0_Config1);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_CONFIG1 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG2, &env->CP0_Config2);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_CONFIG2 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG3, &env->CP0_Config3);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_CONFIG3 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG4, &env->CP0_Config4);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_CONFIG4 (%d)\n", __func__, err);
ret = err;
}
err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CONFIG5, &env->CP0_Config5);
if (err < 0) {
DPRINTF("%s: Failed to get CP0_CONFIG5 (%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] = (int64_t)(target_long)env->active_tc.gpr[i];
}
regs.hi = (int64_t)(target_long)env->active_tc.HI[0];
regs.lo = (int64_t)(target_long)env->active_tc.LO[0];
regs.pc = (int64_t)(target_long)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;
}
ret = kvm_mips_put_fpu_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);
kvm_mips_get_fpu_registers(cs);
return ret;
}
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
uint64_t address, uint32_t data, PCIDevice *dev)
{
return 0;
}
int kvm_arch_msi_data_to_gsi(uint32_t data)
{
abort();
}