2009-10-30 08:47:14 +03: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 SUSE Linux Products GmbH 2009
*
* Authors : Alexander Graf < agraf @ suse . de >
*/
# include <asm/kvm_ppc.h>
# include <asm/disassemble.h>
# include <asm/kvm_book3s.h>
# include <asm/reg.h>
# define OP_19_XOP_RFID 18
# define OP_19_XOP_RFI 50
# define OP_31_XOP_MFMSR 83
# define OP_31_XOP_MTMSR 146
# define OP_31_XOP_MTMSRD 178
2010-02-19 13:00:37 +03:00
# define OP_31_XOP_MTSR 210
2009-10-30 08:47:14 +03:00
# define OP_31_XOP_MTSRIN 242
# define OP_31_XOP_TLBIEL 274
# define OP_31_XOP_TLBIE 306
# define OP_31_XOP_SLBMTE 402
# define OP_31_XOP_SLBIE 434
# define OP_31_XOP_SLBIA 498
# define OP_31_XOP_MFSRIN 659
# define OP_31_XOP_SLBMFEV 851
# define OP_31_XOP_EIOIO 854
# define OP_31_XOP_SLBMFEE 915
/* DCBZ is actually 1014, but we patch it to 1010 so we get a trap */
# define OP_31_XOP_DCBZ 1010
2010-02-19 13:00:33 +03:00
# define SPRN_GQR0 912
# define SPRN_GQR1 913
# define SPRN_GQR2 914
# define SPRN_GQR3 915
# define SPRN_GQR4 916
# define SPRN_GQR5 917
# define SPRN_GQR6 918
# define SPRN_GQR7 919
2009-10-30 08:47:14 +03:00
int kvmppc_core_emulate_op ( struct kvm_run * run , struct kvm_vcpu * vcpu ,
unsigned int inst , int * advance )
{
int emulated = EMULATE_DONE ;
switch ( get_op ( inst ) ) {
case 19 :
switch ( get_xop ( inst ) ) {
case OP_19_XOP_RFID :
case OP_19_XOP_RFI :
vcpu - > arch . pc = vcpu - > arch . srr0 ;
kvmppc_set_msr ( vcpu , vcpu - > arch . srr1 ) ;
* advance = 0 ;
break ;
default :
emulated = EMULATE_FAIL ;
break ;
}
break ;
case 31 :
switch ( get_xop ( inst ) ) {
case OP_31_XOP_MFMSR :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , get_rt ( inst ) , vcpu - > arch . msr ) ;
2009-10-30 08:47:14 +03:00
break ;
case OP_31_XOP_MTMSRD :
{
2010-01-08 04:58:01 +03:00
ulong rs = kvmppc_get_gpr ( vcpu , get_rs ( inst ) ) ;
2009-10-30 08:47:14 +03:00
if ( inst & 0x10000 ) {
vcpu - > arch . msr & = ~ ( MSR_RI | MSR_EE ) ;
vcpu - > arch . msr | = rs & ( MSR_RI | MSR_EE ) ;
} else
kvmppc_set_msr ( vcpu , rs ) ;
break ;
}
case OP_31_XOP_MTMSR :
2010-01-08 04:58:01 +03:00
kvmppc_set_msr ( vcpu , kvmppc_get_gpr ( vcpu , get_rs ( inst ) ) ) ;
2009-10-30 08:47:14 +03:00
break ;
case OP_31_XOP_MFSRIN :
{
int srnum ;
2010-01-08 04:58:01 +03:00
srnum = ( kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) > > 28 ) & 0xf ;
2009-10-30 08:47:14 +03:00
if ( vcpu - > arch . mmu . mfsrin ) {
u32 sr ;
sr = vcpu - > arch . mmu . mfsrin ( vcpu , srnum ) ;
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , get_rt ( inst ) , sr ) ;
2009-10-30 08:47:14 +03:00
}
break ;
}
2010-02-19 13:00:37 +03:00
case OP_31_XOP_MTSR :
vcpu - > arch . mmu . mtsrin ( vcpu ,
( inst > > 16 ) & 0xf ,
kvmppc_get_gpr ( vcpu , get_rs ( inst ) ) ) ;
break ;
2009-10-30 08:47:14 +03:00
case OP_31_XOP_MTSRIN :
vcpu - > arch . mmu . mtsrin ( vcpu ,
2010-01-08 04:58:01 +03:00
( kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) > > 28 ) & 0xf ,
kvmppc_get_gpr ( vcpu , get_rs ( inst ) ) ) ;
2009-10-30 08:47:14 +03:00
break ;
case OP_31_XOP_TLBIE :
case OP_31_XOP_TLBIEL :
{
bool large = ( inst & 0x00200000 ) ? true : false ;
2010-01-08 04:58:01 +03:00
ulong addr = kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) ;
2009-10-30 08:47:14 +03:00
vcpu - > arch . mmu . tlbie ( vcpu , addr , large ) ;
break ;
}
case OP_31_XOP_EIOIO :
break ;
case OP_31_XOP_SLBMTE :
if ( ! vcpu - > arch . mmu . slbmte )
return EMULATE_FAIL ;
2010-01-08 04:58:01 +03:00
vcpu - > arch . mmu . slbmte ( vcpu ,
kvmppc_get_gpr ( vcpu , get_rs ( inst ) ) ,
kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) ) ;
2009-10-30 08:47:14 +03:00
break ;
case OP_31_XOP_SLBIE :
if ( ! vcpu - > arch . mmu . slbie )
return EMULATE_FAIL ;
2010-01-08 04:58:01 +03:00
vcpu - > arch . mmu . slbie ( vcpu ,
kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) ) ;
2009-10-30 08:47:14 +03:00
break ;
case OP_31_XOP_SLBIA :
if ( ! vcpu - > arch . mmu . slbia )
return EMULATE_FAIL ;
vcpu - > arch . mmu . slbia ( vcpu ) ;
break ;
case OP_31_XOP_SLBMFEE :
if ( ! vcpu - > arch . mmu . slbmfee ) {
emulated = EMULATE_FAIL ;
} else {
ulong t , rb ;
2010-01-08 04:58:01 +03:00
rb = kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) ;
2009-10-30 08:47:14 +03:00
t = vcpu - > arch . mmu . slbmfee ( vcpu , rb ) ;
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , get_rt ( inst ) , t ) ;
2009-10-30 08:47:14 +03:00
}
break ;
case OP_31_XOP_SLBMFEV :
if ( ! vcpu - > arch . mmu . slbmfev ) {
emulated = EMULATE_FAIL ;
} else {
ulong t , rb ;
2010-01-08 04:58:01 +03:00
rb = kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) ;
2009-10-30 08:47:14 +03:00
t = vcpu - > arch . mmu . slbmfev ( vcpu , rb ) ;
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , get_rt ( inst ) , t ) ;
2009-10-30 08:47:14 +03:00
}
break ;
case OP_31_XOP_DCBZ :
{
2010-01-08 04:58:01 +03:00
ulong rb = kvmppc_get_gpr ( vcpu , get_rb ( inst ) ) ;
2009-10-30 08:47:14 +03:00
ulong ra = 0 ;
2010-02-19 13:00:38 +03:00
ulong addr , vaddr ;
2009-10-30 08:47:14 +03:00
u32 zeros [ 8 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
if ( get_ra ( inst ) )
2010-01-08 04:58:01 +03:00
ra = kvmppc_get_gpr ( vcpu , get_ra ( inst ) ) ;
2009-10-30 08:47:14 +03:00
addr = ( ra + rb ) & ~ 31ULL ;
if ( ! ( vcpu - > arch . msr & MSR_SF ) )
addr & = 0xffffffff ;
2010-02-19 13:00:38 +03:00
vaddr = addr ;
2009-10-30 08:47:14 +03:00
2010-02-19 13:00:38 +03:00
if ( kvmppc_st ( vcpu , & addr , 32 , zeros , true ) ) {
vcpu - > arch . dear = vaddr ;
vcpu - > arch . fault_dear = vaddr ;
2009-10-30 08:47:14 +03:00
to_book3s ( vcpu ) - > dsisr = DSISR_PROTFAULT |
DSISR_ISSTORE ;
kvmppc_book3s_queue_irqprio ( vcpu ,
BOOK3S_INTERRUPT_DATA_STORAGE ) ;
2010-02-19 13:00:38 +03:00
kvmppc_mmu_pte_flush ( vcpu , vaddr , ~ 0xFFFULL ) ;
2009-10-30 08:47:14 +03:00
}
break ;
}
default :
emulated = EMULATE_FAIL ;
}
break ;
default :
emulated = EMULATE_FAIL ;
}
return emulated ;
}
2009-11-30 06:02:02 +03:00
void kvmppc_set_bat ( struct kvm_vcpu * vcpu , struct kvmppc_bat * bat , bool upper ,
u32 val )
{
if ( upper ) {
/* Upper BAT */
u32 bl = ( val > > 2 ) & 0x7ff ;
bat - > bepi_mask = ( ~ bl < < 17 ) ;
bat - > bepi = val & 0xfffe0000 ;
bat - > vs = ( val & 2 ) ? 1 : 0 ;
bat - > vp = ( val & 1 ) ? 1 : 0 ;
bat - > raw = ( bat - > raw & 0xffffffff00000000ULL ) | val ;
} else {
/* Lower BAT */
bat - > brpn = val & 0xfffe0000 ;
bat - > wimg = ( val > > 3 ) & 0xf ;
bat - > pp = val & 3 ;
bat - > raw = ( bat - > raw & 0x00000000ffffffffULL ) | ( ( u64 ) val < < 32 ) ;
}
}
static void kvmppc_write_bat ( struct kvm_vcpu * vcpu , int sprn , u32 val )
2009-10-30 08:47:14 +03:00
{
struct kvmppc_vcpu_book3s * vcpu_book3s = to_book3s ( vcpu ) ;
struct kvmppc_bat * bat ;
switch ( sprn ) {
case SPRN_IBAT0U . . . SPRN_IBAT3L :
bat = & vcpu_book3s - > ibat [ ( sprn - SPRN_IBAT0U ) / 2 ] ;
break ;
case SPRN_IBAT4U . . . SPRN_IBAT7L :
2010-02-19 13:00:41 +03:00
bat = & vcpu_book3s - > ibat [ 4 + ( ( sprn - SPRN_IBAT4U ) / 2 ) ] ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_DBAT0U . . . SPRN_DBAT3L :
bat = & vcpu_book3s - > dbat [ ( sprn - SPRN_DBAT0U ) / 2 ] ;
break ;
case SPRN_DBAT4U . . . SPRN_DBAT7L :
2010-02-19 13:00:41 +03:00
bat = & vcpu_book3s - > dbat [ 4 + ( ( sprn - SPRN_DBAT4U ) / 2 ) ] ;
2009-10-30 08:47:14 +03:00
break ;
default :
BUG ( ) ;
}
2009-11-30 06:02:02 +03:00
kvmppc_set_bat ( vcpu , bat , ! ( sprn % 2 ) , val ) ;
2009-10-30 08:47:14 +03:00
}
int kvmppc_core_emulate_mtspr ( struct kvm_vcpu * vcpu , int sprn , int rs )
{
int emulated = EMULATE_DONE ;
2010-01-08 04:58:01 +03:00
ulong spr_val = kvmppc_get_gpr ( vcpu , rs ) ;
2009-10-30 08:47:14 +03:00
switch ( sprn ) {
case SPRN_SDR1 :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > sdr1 = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_DSISR :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > dsisr = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_DAR :
2010-01-08 04:58:01 +03:00
vcpu - > arch . dear = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HIOR :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > hior = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_IBAT0U . . . SPRN_IBAT3L :
case SPRN_IBAT4U . . . SPRN_IBAT7L :
case SPRN_DBAT0U . . . SPRN_DBAT3L :
case SPRN_DBAT4U . . . SPRN_DBAT7L :
2010-01-08 04:58:01 +03:00
kvmppc_write_bat ( vcpu , sprn , ( u32 ) spr_val ) ;
2009-10-30 08:47:14 +03:00
/* BAT writes happen so rarely that we're ok to flush
* everything here */
kvmppc_mmu_pte_flush ( vcpu , 0 , 0 ) ;
break ;
case SPRN_HID0 :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > hid [ 0 ] = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID1 :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > hid [ 1 ] = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID2 :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > hid [ 2 ] = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
2010-02-19 13:00:33 +03:00
case SPRN_HID2_GEKKO :
to_book3s ( vcpu ) - > hid [ 2 ] = spr_val ;
/* HID2.PSE controls paired single on gekko */
switch ( vcpu - > arch . pvr ) {
case 0x00080200 : /* lonestar 2.0 */
case 0x00088202 : /* lonestar 2.2 */
case 0x70000100 : /* gekko 1.0 */
case 0x00080100 : /* gekko 2.0 */
case 0x00083203 : /* gekko 2.3a */
case 0x00083213 : /* gekko 2.3b */
case 0x00083204 : /* gekko 2.4 */
case 0x00083214 : /* gekko 2.4e (8SE) - retail HW2 */
if ( spr_val & ( 1 < < 29 ) ) { /* HID2.PSE */
vcpu - > arch . hflags | = BOOK3S_HFLAG_PAIRED_SINGLE ;
kvmppc_giveup_ext ( vcpu , MSR_FP ) ;
} else {
vcpu - > arch . hflags & = ~ BOOK3S_HFLAG_PAIRED_SINGLE ;
}
break ;
}
break ;
2009-10-30 08:47:14 +03:00
case SPRN_HID4 :
2010-02-19 13:00:33 +03:00
case SPRN_HID4_GEKKO :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > hid [ 4 ] = spr_val ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID5 :
2010-01-08 04:58:01 +03:00
to_book3s ( vcpu ) - > hid [ 5 ] = spr_val ;
2009-10-30 08:47:14 +03:00
/* guest HID5 set can change is_dcbz32 */
if ( vcpu - > arch . mmu . is_dcbz32 ( vcpu ) & &
( mfmsr ( ) & MSR_HV ) )
vcpu - > arch . hflags | = BOOK3S_HFLAG_DCBZ32 ;
break ;
2010-02-19 13:00:33 +03:00
case SPRN_GQR0 :
case SPRN_GQR1 :
case SPRN_GQR2 :
case SPRN_GQR3 :
case SPRN_GQR4 :
case SPRN_GQR5 :
case SPRN_GQR6 :
case SPRN_GQR7 :
to_book3s ( vcpu ) - > gqr [ sprn - SPRN_GQR0 ] = spr_val ;
break ;
2009-10-30 08:47:14 +03:00
case SPRN_ICTC :
case SPRN_THRM1 :
case SPRN_THRM2 :
case SPRN_THRM3 :
case SPRN_CTRLF :
case SPRN_CTRLT :
2010-02-19 13:00:33 +03:00
case SPRN_L2CR :
case SPRN_MMCR0_GEKKO :
case SPRN_MMCR1_GEKKO :
case SPRN_PMC1_GEKKO :
case SPRN_PMC2_GEKKO :
case SPRN_PMC3_GEKKO :
case SPRN_PMC4_GEKKO :
case SPRN_WPAR_GEKKO :
2009-10-30 08:47:14 +03:00
break ;
default :
printk ( KERN_INFO " KVM: invalid SPR write: %d \n " , sprn ) ;
# ifndef DEBUG_SPR
emulated = EMULATE_FAIL ;
# endif
break ;
}
return emulated ;
}
int kvmppc_core_emulate_mfspr ( struct kvm_vcpu * vcpu , int sprn , int rt )
{
int emulated = EMULATE_DONE ;
switch ( sprn ) {
case SPRN_SDR1 :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > sdr1 ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_DSISR :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > dsisr ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_DAR :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , vcpu - > arch . dear ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HIOR :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > hior ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID0 :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > hid [ 0 ] ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID1 :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > hid [ 1 ] ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID2 :
2010-02-19 13:00:33 +03:00
case SPRN_HID2_GEKKO :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > hid [ 2 ] ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID4 :
2010-02-19 13:00:33 +03:00
case SPRN_HID4_GEKKO :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > hid [ 4 ] ) ;
2009-10-30 08:47:14 +03:00
break ;
case SPRN_HID5 :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , to_book3s ( vcpu ) - > hid [ 5 ] ) ;
2009-10-30 08:47:14 +03:00
break ;
2010-02-19 13:00:33 +03:00
case SPRN_GQR0 :
case SPRN_GQR1 :
case SPRN_GQR2 :
case SPRN_GQR3 :
case SPRN_GQR4 :
case SPRN_GQR5 :
case SPRN_GQR6 :
case SPRN_GQR7 :
kvmppc_set_gpr ( vcpu , rt ,
to_book3s ( vcpu ) - > gqr [ sprn - SPRN_GQR0 ] ) ;
break ;
2009-10-30 08:47:14 +03:00
case SPRN_THRM1 :
case SPRN_THRM2 :
case SPRN_THRM3 :
case SPRN_CTRLF :
case SPRN_CTRLT :
2010-02-19 13:00:33 +03:00
case SPRN_L2CR :
case SPRN_MMCR0_GEKKO :
case SPRN_MMCR1_GEKKO :
case SPRN_PMC1_GEKKO :
case SPRN_PMC2_GEKKO :
case SPRN_PMC3_GEKKO :
case SPRN_PMC4_GEKKO :
case SPRN_WPAR_GEKKO :
2010-01-08 04:58:01 +03:00
kvmppc_set_gpr ( vcpu , rt , 0 ) ;
2009-10-30 08:47:14 +03:00
break ;
default :
printk ( KERN_INFO " KVM: invalid SPR read: %d \n " , sprn ) ;
# ifndef DEBUG_SPR
emulated = EMULATE_FAIL ;
# endif
break ;
}
return emulated ;
}