/*
* process.c: handle interruption inject for guests.
* Copyright (c) 2005, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Shaofan Li (Susue Li) <susie.li@intel.com>
* Xiaoyan Feng (Fleming Feng) <fleming.feng@intel.com>
* Xuefei Xu (Anthony Xu) (Anthony.xu@intel.com)
* Xiantao Zhang (xiantao.zhang@intel.com)
*/
#include "vcpu.h"
#include <asm/pal.h>
#include <asm/sal.h>
#include <asm/fpswa.h>
#include <asm/kregs.h>
#include <asm/tlb.h>
fpswa_interface_t *vmm_fpswa_interface;
#define IA64_VHPT_TRANS_VECTOR 0x0000
#define IA64_INST_TLB_VECTOR 0x0400
#define IA64_DATA_TLB_VECTOR 0x0800
#define IA64_ALT_INST_TLB_VECTOR 0x0c00
#define IA64_ALT_DATA_TLB_VECTOR 0x1000
#define IA64_DATA_NESTED_TLB_VECTOR 0x1400
#define IA64_INST_KEY_MISS_VECTOR 0x1800
#define IA64_DATA_KEY_MISS_VECTOR 0x1c00
#define IA64_DIRTY_BIT_VECTOR 0x2000
#define IA64_INST_ACCESS_BIT_VECTOR 0x2400
#define IA64_DATA_ACCESS_BIT_VECTOR 0x2800
#define IA64_BREAK_VECTOR 0x2c00
#define IA64_EXTINT_VECTOR 0x3000
#define IA64_PAGE_NOT_PRESENT_VECTOR 0x5000
#define IA64_KEY_PERMISSION_VECTOR 0x5100
#define IA64_INST_ACCESS_RIGHTS_VECTOR 0x5200
#define IA64_DATA_ACCESS_RIGHTS_VECTOR 0x5300
#define IA64_GENEX_VECTOR 0x5400
#define IA64_DISABLED_FPREG_VECTOR 0x5500
#define IA64_NAT_CONSUMPTION_VECTOR 0x5600
#define IA64_SPECULATION_VECTOR 0x5700 /* UNUSED */
#define IA64_DEBUG_VECTOR 0x5900
#define IA64_UNALIGNED_REF_VECTOR 0x5a00
#define IA64_UNSUPPORTED_DATA_REF_VECTOR 0x5b00
#define IA64_FP_FAULT_VECTOR 0x5c00
#define IA64_FP_TRAP_VECTOR 0x5d00
#define IA64_LOWERPRIV_TRANSFER_TRAP_VECTOR 0x5e00
#define IA64_TAKEN_BRANCH_TRAP_VECTOR 0x5f00
#define IA64_SINGLE_STEP_TRAP_VECTOR 0x6000
/* SDM vol2 5.5 - IVA based interruption handling */
#define INITIAL_PSR_VALUE_AT_INTERRUPTION (IA64_PSR_UP | IA64_PSR_MFL |\
IA64_PSR_MFH | IA64_PSR_PK | IA64_PSR_DT | \
IA64_PSR_RT | IA64_PSR_MC|IA64_PSR_IT)
#define DOMN_PAL_REQUEST 0x110000
#define DOMN_SAL_REQUEST 0x110001
static u64 vec2off[68] = {0x0, 0x400, 0x800, 0xc00, 0x1000, 0x1400, 0x1800,
0x1c00, 0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00,
0x4000, 0x4400, 0x4800, 0x4c00, 0x5000, 0x5100, 0x5200, 0x5300, 0x5400,
0x5500, 0x5600, 0x5700, 0x5800, 0x5900, 0x5a00, 0x5b00, 0x5c00, 0x5d00,
0x5e00, 0x5f00, 0x6000, 0x6100, 0x6200, 0x6300, 0x6400, 0x6500, 0x6600,
0x6700, 0x6800, 0x6900, 0x6a00, 0x6b00, 0x6c00, 0x6d00, 0x6e00, 0x6f00,
0x7000, 0x7100, 0x7200, 0x7300, 0x7400, 0x7500, 0x7600, 0x7700, 0x7800,
0x7900, 0x7a00, 0x7b00, 0x7c00, 0x7d00, 0x7e00, 0x7f00
};
static void collect_interruption(struct kvm_vcpu *vcpu)
{
u64 ipsr;
u64 vdcr;
u64 vifs;
unsigned long vpsr;
struct kvm_pt_regs *regs = vcpu_regs(vcpu);
vpsr = vcpu_get_psr(vcpu);
vcpu_bsw0(vcpu);
if (vpsr & IA64_PSR_IC) {
/* Sync mpsr id/da/dd/ss/ed bits to vipsr
* since after guest do rfi, we still want these bits on in
* mpsr
*/
ipsr = regs->cr_ipsr;
vpsr = vpsr | (ipsr & (IA64_PSR_ID | IA64_PSR_DA
| IA64_PSR_DD | IA64_PSR_SS
| IA64_PSR_ED));
vcpu_set_ipsr(vcpu, vpsr);
/* Currently, for trap, we do not advance IIP to next
* instruction. That's because we assume caller already
* set up IIP correctly
*/
vcpu_set_iip(vcpu , regs->cr_iip);
/* set vifs.v to zero */
vifs = VCPU(vcpu, ifs);
vifs &= ~IA64_IFS_V;
vcpu_set_ifs(vcpu, vifs);
vcpu_set_iipa(vcpu, VMX(vcpu, cr_iipa));
}
vdcr = VCPU(vcpu, dcr);
/* Set guest psr
* up/mfl/mfh/pk/dt/rt/mc/it keeps unchanged
* be: set to the value of dcr.be
* pp: set to the value of dcr.pp
*/
vpsr &= INITIAL_PSR_VALUE_AT_INTERRUPTION;
vpsr |= (vdcr & IA64_DCR_BE);
/* VDCR pp bit position is different from VPSR pp bit */
if (vdcr & IA64_DCR_PP) {
vpsr |= IA64_PSR_PP;
} else {
vpsr &= ~IA64_PSR_PP;
}
vcpu_set_psr(vcpu, vpsr);
}
void inject_guest_interruption(struct kvm_vcpu *vcpu, u64 vec)
{
u64 viva;
struct kvm_pt_regs *regs;
union ia64_isr pt_isr;
regs = vcpu_regs(vcpu);
/* clear cr.isr.ir (incomplete register frame)*/
pt_isr.val = VMX(vcpu, cr_isr);
pt_isr.ir = 0;
VMX(vcpu, cr_isr) = pt_isr.val;
collect_interruption(vcpu);
viva = vcpu_get_iva(vcpu);
regs->cr_iip = viva + vec;
}
static u64 vcpu_get_itir_on_fault(struct kvm_vcpu *vcpu, u64 ifa)
{
union ia64_rr rr, rr1;
rr.val = vcpu_get_rr(vcpu, ifa);
rr1.val = 0