2009-01-03 16:23:06 -06:00
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License , version 2 , as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that 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 , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* Copyright IBM Corp . 2008
2011-11-17 12:39:59 +00:00
* Copyright 2011 Freescale Semiconductor , Inc .
2009-01-03 16:23:06 -06:00
*
* Authors : Hollis Blanchard < hollisb @ us . ibm . com >
*/
# include <linux/kvm_host.h>
# include <asm/disassemble.h>
# include "booke.h"
# define OP_19_XOP_RFI 50
2012-06-27 19:37:31 +00:00
# define OP_19_XOP_RFCI 51
2014-08-06 12:08:52 +05:30
# define OP_19_XOP_RFDI 39
2009-01-03 16:23:06 -06:00
# define OP_31_XOP_MFMSR 83
# define OP_31_XOP_WRTEE 131
# define OP_31_XOP_MTMSR 146
# define OP_31_XOP_WRTEEI 163
static void kvmppc_emul_rfi ( struct kvm_vcpu * vcpu )
{
2010-07-29 14:47:46 +02:00
vcpu - > arch . pc = vcpu - > arch . shared - > srr0 ;
kvmppc_set_msr ( vcpu , vcpu - > arch . shared - > srr1 ) ;
2009-01-03 16:23:06 -06:00
}
2014-08-06 12:08:52 +05:30
static void kvmppc_emul_rfdi ( struct kvm_vcpu * vcpu )
{
vcpu - > arch . pc = vcpu - > arch . dsrr0 ;
kvmppc_set_msr ( vcpu , vcpu - > arch . dsrr1 ) ;
}
2012-06-27 19:37:31 +00:00
static void kvmppc_emul_rfci ( struct kvm_vcpu * vcpu )
{
vcpu - > arch . pc = vcpu - > arch . csrr0 ;
kvmppc_set_msr ( vcpu , vcpu - > arch . csrr1 ) ;
}
2009-01-03 16:23:06 -06:00
int kvmppc_booke_emulate_op ( struct kvm_run * run , struct kvm_vcpu * vcpu ,
unsigned int inst , int * advance )
{
int emulated = EMULATE_DONE ;
2012-05-04 14:01:33 +02:00
int rs = get_rs ( inst ) ;
int rt = get_rt ( inst ) ;
2009-01-03 16:23:06 -06:00
switch ( get_op ( inst ) ) {
case 19 :
switch ( get_xop ( inst ) ) {
case OP_19_XOP_RFI :
kvmppc_emul_rfi ( vcpu ) ;
kvmppc_set_exit_type ( vcpu , EMULATED_RFI_EXITS ) ;
* advance = 0 ;
break ;
2012-06-27 19:37:31 +00:00
case OP_19_XOP_RFCI :
kvmppc_emul_rfci ( vcpu ) ;
kvmppc_set_exit_type ( vcpu , EMULATED_RFCI_EXITS ) ;
* advance = 0 ;
break ;
2014-08-06 12:08:52 +05:30
case OP_19_XOP_RFDI :
kvmppc_emul_rfdi ( vcpu ) ;
kvmppc_set_exit_type ( vcpu , EMULATED_RFDI_EXITS ) ;
* advance = 0 ;
break ;
2009-01-03 16:23:06 -06:00
default :
emulated = EMULATE_FAIL ;
break ;
}
break ;
case 31 :
switch ( get_xop ( inst ) ) {
case OP_31_XOP_MFMSR :
2010-07-29 14:47:43 +02:00
kvmppc_set_gpr ( vcpu , rt , vcpu - > arch . shared - > msr ) ;
2009-01-03 16:23:06 -06:00
kvmppc_set_exit_type ( vcpu , EMULATED_MFMSR_EXITS ) ;
break ;
case OP_31_XOP_MTMSR :
kvmppc_set_exit_type ( vcpu , EMULATED_MTMSR_EXITS ) ;
2010-01-08 02:58:01 +01:00
kvmppc_set_msr ( vcpu , kvmppc_get_gpr ( vcpu , rs ) ) ;
2009-01-03 16:23:06 -06:00
break ;
case OP_31_XOP_WRTEE :
2010-07-29 14:47:43 +02:00
vcpu - > arch . shared - > msr = ( vcpu - > arch . shared - > msr & ~ MSR_EE )
2010-01-08 02:58:01 +01:00
| ( kvmppc_get_gpr ( vcpu , rs ) & MSR_EE ) ;
2009-01-03 16:23:06 -06:00
kvmppc_set_exit_type ( vcpu , EMULATED_WRTEE_EXITS ) ;
break ;
case OP_31_XOP_WRTEEI :
2010-07-29 14:47:43 +02:00
vcpu - > arch . shared - > msr = ( vcpu - > arch . shared - > msr & ~ MSR_EE )
2009-01-03 16:23:06 -06:00
| ( inst & MSR_EE ) ;
kvmppc_set_exit_type ( vcpu , EMULATED_WRTEE_EXITS ) ;
break ;
default :
emulated = EMULATE_FAIL ;
}
break ;
default :
emulated = EMULATE_FAIL ;
}
return emulated ;
}
2011-12-20 15:34:43 +00:00
/*
* NOTE : some of these registers are not emulated on BOOKE_HV ( GS - mode ) .
* Their backing store is in real registers , and these functions
* will return the wrong result if called for them in another context
* ( such as debugging ) .
*/
2012-05-04 14:55:12 +02:00
int kvmppc_booke_emulate_mtspr ( struct kvm_vcpu * vcpu , int sprn , ulong spr_val )
2009-01-03 16:23:06 -06:00
{
int emulated = EMULATE_DONE ;
switch ( sprn ) {
case SPRN_DEAR :
2012-05-04 14:55:12 +02:00
vcpu - > arch . shared - > dar = spr_val ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_ESR :
2012-05-04 14:55:12 +02:00
vcpu - > arch . shared - > esr = spr_val ;
break ;
2012-06-27 19:37:31 +00:00
case SPRN_CSRR0 :
vcpu - > arch . csrr0 = spr_val ;
break ;
case SPRN_CSRR1 :
vcpu - > arch . csrr1 = spr_val ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_DBCR0 :
2012-08-08 21:17:55 +00:00
vcpu - > arch . dbg_reg . dbcr0 = spr_val ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:06 -06:00
case SPRN_DBCR1 :
2012-08-08 21:17:55 +00:00
vcpu - > arch . dbg_reg . dbcr1 = spr_val ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:07 -06:00
case SPRN_DBSR :
2012-05-04 14:55:12 +02:00
vcpu - > arch . dbsr & = ~ spr_val ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_TSR :
2011-11-17 12:39:59 +00:00
kvmppc_clr_tsr_bits ( vcpu , spr_val ) ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_TCR :
2012-08-08 20:38:19 +00:00
/*
* WRC is a 2 - bit field that is supposed to preserve its
* value once written to non - zero .
*/
if ( vcpu - > arch . tcr & TCR_WRC_MASK ) {
spr_val & = ~ TCR_WRC_MASK ;
spr_val | = vcpu - > arch . tcr & TCR_WRC_MASK ;
}
2011-11-17 12:39:59 +00:00
kvmppc_set_tcr ( vcpu , spr_val ) ;
2009-01-03 16:23:06 -06:00
break ;
2012-05-20 23:21:23 +00:00
case SPRN_DECAR :
vcpu - > arch . decar = spr_val ;
break ;
2011-12-20 15:34:43 +00:00
/*
* Note : SPRG4 - 7 are user - readable .
* These values are loaded into the real SPRGs when resuming the
* guest ( PR - mode only ) .
*/
2009-01-03 16:23:06 -06:00
case SPRN_SPRG4 :
2014-07-17 17:01:39 +05:30
kvmppc_set_sprg4 ( vcpu , spr_val ) ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:06 -06:00
case SPRN_SPRG5 :
2014-07-17 17:01:39 +05:30
kvmppc_set_sprg5 ( vcpu , spr_val ) ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:06 -06:00
case SPRN_SPRG6 :
2014-07-17 17:01:39 +05:30
kvmppc_set_sprg6 ( vcpu , spr_val ) ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:06 -06:00
case SPRN_SPRG7 :
2014-07-17 17:01:39 +05:30
kvmppc_set_sprg7 ( vcpu , spr_val ) ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:06 -06:00
case SPRN_IVPR :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivpr = spr_val ;
2011-12-20 15:34:43 +00:00
# ifdef CONFIG_KVM_BOOKE_HV
mtspr ( SPRN_GIVPR , spr_val ) ;
# endif
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR0 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_CRITICAL ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR1 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_MACHINE_CHECK ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR2 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_DATA_STORAGE ] = spr_val ;
2011-12-20 15:34:43 +00:00
# ifdef CONFIG_KVM_BOOKE_HV
mtspr ( SPRN_GIVOR2 , spr_val ) ;
# endif
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR3 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_INST_STORAGE ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR4 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_EXTERNAL ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR5 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_ALIGNMENT ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR6 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_PROGRAM ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR7 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_FP_UNAVAIL ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR8 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_SYSCALL ] = spr_val ;
2011-12-20 15:34:43 +00:00
# ifdef CONFIG_KVM_BOOKE_HV
mtspr ( SPRN_GIVOR8 , spr_val ) ;
# endif
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR9 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_AP_UNAVAIL ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR10 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_DECREMENTER ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR11 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_FIT ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR12 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_WATCHDOG ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR13 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_DTLB_MISS ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR14 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_ITLB_MISS ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR15 :
2010-01-08 02:58:01 +01:00
vcpu - > arch . ivor [ BOOKE_IRQPRIO_DEBUG ] = spr_val ;
2009-01-03 16:23:06 -06:00
break ;
2012-08-13 14:50:54 +02:00
case SPRN_MCSR :
vcpu - > arch . mcsr & = ~ spr_val ;
break ;
2012-10-11 06:13:27 +00:00
# if defined(CONFIG_64BIT)
case SPRN_EPCR :
kvmppc_set_epcr ( vcpu , spr_val ) ;
# ifdef CONFIG_KVM_BOOKE_HV
mtspr ( SPRN_EPCR , vcpu - > arch . shadow_epcr ) ;
# endif
break ;
# endif
2009-01-03 16:23:06 -06:00
default :
emulated = EMULATE_FAIL ;
}
return emulated ;
}
2012-05-04 14:55:12 +02:00
int kvmppc_booke_emulate_mfspr ( struct kvm_vcpu * vcpu , int sprn , ulong * spr_val )
2009-01-03 16:23:06 -06:00
{
int emulated = EMULATE_DONE ;
switch ( sprn ) {
case SPRN_IVPR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivpr ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_DEAR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . shared - > dar ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_ESR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . shared - > esr ;
break ;
2013-01-04 18:02:14 +01:00
case SPRN_EPR :
* spr_val = vcpu - > arch . epr ;
break ;
2012-06-27 19:37:31 +00:00
case SPRN_CSRR0 :
* spr_val = vcpu - > arch . csrr0 ;
break ;
case SPRN_CSRR1 :
* spr_val = vcpu - > arch . csrr1 ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_DBCR0 :
2012-08-08 21:17:55 +00:00
* spr_val = vcpu - > arch . dbg_reg . dbcr0 ;
2014-08-06 12:08:55 +05:30
if ( vcpu - > guest_debug )
* spr_val = * spr_val | DBCR0_EDM ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:06 -06:00
case SPRN_DBCR1 :
2012-08-08 21:17:55 +00:00
* spr_val = vcpu - > arch . dbg_reg . dbcr1 ;
2012-05-04 14:55:12 +02:00
break ;
2009-01-03 16:23:07 -06:00
case SPRN_DBSR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . dbsr ;
break ;
2011-11-17 12:39:59 +00:00
case SPRN_TSR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . tsr ;
break ;
2011-11-17 12:39:59 +00:00
case SPRN_TCR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . tcr ;
break ;
2009-01-03 16:23:06 -06:00
case SPRN_IVOR0 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_CRITICAL ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR1 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_MACHINE_CHECK ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR2 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_DATA_STORAGE ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR3 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_INST_STORAGE ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR4 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_EXTERNAL ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR5 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_ALIGNMENT ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR6 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_PROGRAM ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR7 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_FP_UNAVAIL ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR8 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_SYSCALL ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR9 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_AP_UNAVAIL ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR10 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_DECREMENTER ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR11 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_FIT ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR12 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_WATCHDOG ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR13 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_DTLB_MISS ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR14 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_ITLB_MISS ] ;
2009-01-03 16:23:06 -06:00
break ;
case SPRN_IVOR15 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ivor [ BOOKE_IRQPRIO_DEBUG ] ;
2009-01-03 16:23:06 -06:00
break ;
2012-08-13 14:50:54 +02:00
case SPRN_MCSR :
* spr_val = vcpu - > arch . mcsr ;
break ;
2012-10-11 06:13:27 +00:00
# if defined(CONFIG_64BIT)
case SPRN_EPCR :
* spr_val = vcpu - > arch . epcr ;
break ;
# endif
2009-01-03 16:23:06 -06:00
default :
emulated = EMULATE_FAIL ;
}
return emulated ;
}