2012-11-21 18:34:16 -08:00
/*
2014-06-26 12:11:34 -07:00
* 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 : Binary Patching for privileged instructions , reduces traps .
*
* Copyright ( C ) 2012 MIPS Technologies , Inc . All rights reserved .
* Authors : Sanjay Lal < sanjayl @ kymasys . com >
*/
2012-11-21 18:34:16 -08:00
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/kvm_host.h>
# include <linux/module.h>
# include <linux/vmalloc.h>
# include <linux/fs.h>
# include <linux/bootmem.h>
2014-05-29 10:16:25 +01:00
# include <asm/cacheflush.h>
2012-11-21 18:34:16 -08:00
2014-06-26 12:11:38 -07:00
# include "commpage.h"
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:46 +01:00
/**
* kvm_mips_trans_replace ( ) - Replace trapping instruction in guest memory .
* @ vcpu : Virtual CPU .
* @ opc : PC of instruction to replace .
* @ replace : Instruction to write
*/
2016-06-15 19:29:47 +01:00
static int kvm_mips_trans_replace ( struct kvm_vcpu * vcpu , u32 * opc ,
union mips_instruction replace )
2016-06-15 19:29:46 +01:00
{
unsigned long kseg0_opc , flags ;
if ( KVM_GUEST_KSEGX ( opc ) = = KVM_GUEST_KSEG0 ) {
kseg0_opc =
CKSEG0ADDR ( kvm_mips_translate_guest_kseg0_to_hpa
( vcpu , ( unsigned long ) opc ) ) ;
memcpy ( ( void * ) kseg0_opc , ( void * ) & replace , sizeof ( u32 ) ) ;
local_flush_icache_range ( kseg0_opc , kseg0_opc + 32 ) ;
} else if ( KVM_GUEST_KSEGX ( ( unsigned long ) opc ) = = KVM_GUEST_KSEG23 ) {
local_irq_save ( flags ) ;
memcpy ( ( void * ) opc , ( void * ) & replace , sizeof ( u32 ) ) ;
local_flush_icache_range ( ( unsigned long ) opc ,
( unsigned long ) opc + 32 ) ;
local_irq_restore ( flags ) ;
} else {
kvm_err ( " %s: Invalid address: %p \n " , __func__ , opc ) ;
return - EFAULT ;
}
return 0 ;
}
2016-06-15 19:29:47 +01:00
int kvm_mips_trans_cache_index ( union mips_instruction inst , u32 * opc ,
2014-06-26 12:11:34 -07:00
struct kvm_vcpu * vcpu )
2012-11-21 18:34:16 -08:00
{
2016-06-15 19:29:47 +01:00
union mips_instruction nop_inst = { 0 } ;
2012-11-21 18:34:16 -08:00
/* Replace the CACHE instruction, with a NOP */
2016-06-15 19:29:47 +01:00
return kvm_mips_trans_replace ( vcpu , opc , nop_inst ) ;
2012-11-21 18:34:16 -08:00
}
/*
2014-06-26 12:11:34 -07:00
* Address based CACHE instructions are transformed into synci ( s ) . A little
* heavy for just D - cache invalidates , but avoids an expensive trap
2012-11-21 18:34:16 -08:00
*/
2016-06-15 19:29:47 +01:00
int kvm_mips_trans_cache_va ( union mips_instruction inst , u32 * opc ,
2014-06-26 12:11:34 -07:00
struct kvm_vcpu * vcpu )
2012-11-21 18:34:16 -08:00
{
2016-06-15 19:29:47 +01:00
union mips_instruction synci_inst = { 0 } ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:47 +01:00
synci_inst . i_format . opcode = bcond_op ;
synci_inst . i_format . rs = inst . i_format . rs ;
synci_inst . i_format . rt = synci_op ;
2016-07-04 19:35:13 +01:00
if ( cpu_has_mips_r6 )
synci_inst . i_format . simmediate = inst . spec3_format . simmediate ;
else
synci_inst . i_format . simmediate = inst . i_format . simmediate ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:46 +01:00
return kvm_mips_trans_replace ( vcpu , opc , synci_inst ) ;
2012-11-21 18:34:16 -08:00
}
2016-06-15 19:29:47 +01:00
int kvm_mips_trans_mfc0 ( union mips_instruction inst , u32 * opc ,
struct kvm_vcpu * vcpu )
2012-11-21 18:34:16 -08:00
{
2016-06-15 19:29:47 +01:00
union mips_instruction mfc0_inst = { 0 } ;
u32 rd , sel ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:47 +01:00
rd = inst . c0r_format . rd ;
sel = inst . c0r_format . sel ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:47 +01:00
if ( rd = = MIPS_CP0_ERRCTL & & sel = = 0 ) {
mfc0_inst . r_format . opcode = spec_op ;
mfc0_inst . r_format . rd = inst . c0r_format . rt ;
mfc0_inst . r_format . func = add_op ;
2012-11-21 18:34:16 -08:00
} else {
2016-06-15 19:29:47 +01:00
mfc0_inst . i_format . opcode = lw_op ;
mfc0_inst . i_format . rt = inst . c0r_format . rt ;
2016-06-15 19:29:57 +01:00
mfc0_inst . i_format . simmediate = KVM_GUEST_COMMPAGE_ADDR |
2016-06-15 19:29:47 +01:00
offsetof ( struct kvm_mips_commpage , cop0 . reg [ rd ] [ sel ] ) ;
2012-11-21 18:34:16 -08:00
}
2016-06-15 19:29:46 +01:00
return kvm_mips_trans_replace ( vcpu , opc , mfc0_inst ) ;
2012-11-21 18:34:16 -08:00
}
2016-06-15 19:29:47 +01:00
int kvm_mips_trans_mtc0 ( union mips_instruction inst , u32 * opc ,
struct kvm_vcpu * vcpu )
2012-11-21 18:34:16 -08:00
{
2016-06-15 19:29:47 +01:00
union mips_instruction mtc0_inst = { 0 } ;
u32 rd , sel ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:47 +01:00
rd = inst . c0r_format . rd ;
sel = inst . c0r_format . sel ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:47 +01:00
mtc0_inst . i_format . opcode = sw_op ;
mtc0_inst . i_format . rt = inst . c0r_format . rt ;
2016-06-15 19:29:57 +01:00
mtc0_inst . i_format . simmediate = KVM_GUEST_COMMPAGE_ADDR |
2016-06-15 19:29:47 +01:00
offsetof ( struct kvm_mips_commpage , cop0 . reg [ rd ] [ sel ] ) ;
2012-11-21 18:34:16 -08:00
2016-06-15 19:29:46 +01:00
return kvm_mips_trans_replace ( vcpu , opc , mtc0_inst ) ;
2012-11-21 18:34:16 -08:00
}