2008-11-05 09:36:16 -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
*
* Authors : Hollis Blanchard < hollisb @ us . ibm . com >
*/
# include <asm/kvm_ppc.h>
# include <asm/dcr.h>
# include <asm/dcr-regs.h>
# include <asm/disassemble.h>
2008-11-10 14:57:36 -06:00
# include <asm/kvm_44x.h>
2008-12-02 15:51:57 -06:00
# include "timing.h"
2008-11-05 09:36:16 -06:00
# include "booke.h"
# include "44x_tlb.h"
# define XOP_MFDCR 323
# define XOP_MTDCR 451
# define XOP_TLBSX 914
# define XOP_ICCCI 966
# define XOP_TLBWE 978
int kvmppc_core_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 dcrn = get_dcrn ( inst ) ;
int ra = get_ra ( inst ) ;
int rb = get_rb ( inst ) ;
int rc = get_rc ( inst ) ;
int rs = get_rs ( inst ) ;
int rt = get_rt ( inst ) ;
int ws = get_ws ( inst ) ;
2008-11-05 09:36:16 -06:00
switch ( get_op ( inst ) ) {
case 31 :
switch ( get_xop ( inst ) ) {
case XOP_MFDCR :
/* The guest may access CPR0 registers to determine the timebase
* frequency , and it must know the real host frequency because it
* can directly access the timebase registers .
*
* It would be possible to emulate those accesses in userspace ,
* but userspace can really only figure out the end frequency .
* We could decompose that into the factors that compute it , but
* that ' s tricky math , and it ' s easier to just report the real
* CPR0 values .
*/
switch ( dcrn ) {
case DCRN_CPR0_CONFIG_ADDR :
2010-01-08 02:58:01 +01:00
kvmppc_set_gpr ( vcpu , rt , vcpu - > arch . cpr0_cfgaddr ) ;
2008-11-05 09:36:16 -06:00
break ;
case DCRN_CPR0_CONFIG_DATA :
local_irq_disable ( ) ;
mtdcr ( DCRN_CPR0_CONFIG_ADDR ,
vcpu - > arch . cpr0_cfgaddr ) ;
2010-01-08 02:58:01 +01:00
kvmppc_set_gpr ( vcpu , rt ,
mfdcr ( DCRN_CPR0_CONFIG_DATA ) ) ;
2008-11-05 09:36:16 -06:00
local_irq_enable ( ) ;
break ;
default :
run - > dcr . dcrn = dcrn ;
run - > dcr . data = 0 ;
run - > dcr . is_write = 0 ;
vcpu - > arch . io_gpr = rt ;
vcpu - > arch . dcr_needed = 1 ;
2008-12-02 15:51:58 -06:00
kvmppc_account_exit ( vcpu , DCR_EXITS ) ;
2008-11-05 09:36:16 -06:00
emulated = EMULATE_DO_DCR ;
}
break ;
case XOP_MTDCR :
/* emulate some access in kernel */
switch ( dcrn ) {
case DCRN_CPR0_CONFIG_ADDR :
2010-01-08 02:58:01 +01:00
vcpu - > arch . cpr0_cfgaddr = kvmppc_get_gpr ( vcpu , rs ) ;
2008-11-05 09:36:16 -06:00
break ;
default :
run - > dcr . dcrn = dcrn ;
2010-01-08 02:58:01 +01:00
run - > dcr . data = kvmppc_get_gpr ( vcpu , rs ) ;
2008-11-05 09:36:16 -06:00
run - > dcr . is_write = 1 ;
vcpu - > arch . dcr_needed = 1 ;
2008-12-02 15:51:58 -06:00
kvmppc_account_exit ( vcpu , DCR_EXITS ) ;
2008-11-05 09:36:16 -06:00
emulated = EMULATE_DO_DCR ;
}
break ;
case XOP_TLBWE :
emulated = kvmppc_44x_emul_tlbwe ( vcpu , ra , rs , ws ) ;
break ;
case XOP_TLBSX :
emulated = kvmppc_44x_emul_tlbsx ( vcpu , rt , ra , rb , rc ) ;
break ;
case XOP_ICCCI :
break ;
default :
emulated = EMULATE_FAIL ;
}
break ;
default :
emulated = EMULATE_FAIL ;
}
2009-01-03 16:23:06 -06:00
if ( emulated = = EMULATE_FAIL )
emulated = kvmppc_booke_emulate_op ( run , vcpu , inst , advance ) ;
2008-11-05 09:36:16 -06:00
return emulated ;
}
2012-05-04 14:55:12 +02:00
int kvmppc_core_emulate_mtspr ( struct kvm_vcpu * vcpu , int sprn , ulong spr_val )
2008-11-05 09:36:16 -06:00
{
2009-01-03 16:23:06 -06:00
int emulated = EMULATE_DONE ;
2008-11-05 09:36:16 -06:00
switch ( sprn ) {
case SPRN_PID :
2012-05-04 14:55:12 +02:00
kvmppc_set_pid ( vcpu , spr_val ) ; break ;
2009-01-03 16:23:06 -06:00
case SPRN_MMUCR :
2012-05-04 14:55:12 +02:00
vcpu - > arch . mmucr = spr_val ; break ;
2008-11-05 09:36:16 -06:00
case SPRN_CCR0 :
2012-05-04 14:55:12 +02:00
vcpu - > arch . ccr0 = spr_val ; break ;
2008-11-05 09:36:16 -06:00
case SPRN_CCR1 :
2012-05-04 14:55:12 +02:00
vcpu - > arch . ccr1 = spr_val ; break ;
2008-11-05 09:36:16 -06:00
default :
2012-05-04 14:55:12 +02:00
emulated = kvmppc_booke_emulate_mtspr ( vcpu , sprn , spr_val ) ;
2008-11-05 09:36:16 -06:00
}
2009-01-03 16:23:06 -06:00
return emulated ;
2008-11-05 09:36:16 -06:00
}
2012-05-04 14:55:12 +02:00
int kvmppc_core_emulate_mfspr ( struct kvm_vcpu * vcpu , int sprn , ulong * spr_val )
2008-11-05 09:36:16 -06:00
{
2009-01-03 16:23:06 -06:00
int emulated = EMULATE_DONE ;
2008-11-05 09:36:16 -06:00
switch ( sprn ) {
2009-01-03 16:23:06 -06:00
case SPRN_PID :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . pid ; break ;
2008-11-05 09:36:16 -06:00
case SPRN_MMUCR :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . mmucr ; break ;
2008-11-05 09:36:16 -06:00
case SPRN_CCR0 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ccr0 ; break ;
2008-11-05 09:36:16 -06:00
case SPRN_CCR1 :
2012-05-04 14:55:12 +02:00
* spr_val = vcpu - > arch . ccr1 ; break ;
2008-11-05 09:36:16 -06:00
default :
2012-05-04 14:55:12 +02:00
emulated = kvmppc_booke_emulate_mfspr ( vcpu , sprn , spr_val ) ;
2008-11-05 09:36:16 -06:00
}
2009-01-03 16:23:06 -06:00
return emulated ;
2008-11-05 09:36:16 -06:00
}