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>
2016-07-08 11:53:22 +01:00
# include <linux/highmem.h>
2012-11-21 18:34:16 -08:00
# include <linux/kvm_host.h>
2016-08-19 15:27:22 +01:00
# include <linux/uaccess.h>
2012-11-21 18:34:16 -08:00
# include <linux/vmalloc.h>
# include <linux/fs.h>
2018-10-30 15:09:49 -07:00
# include <linux/memblock.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
{
2016-08-19 15:27:22 +01:00
unsigned long vaddr = ( unsigned long ) opc ;
int err ;
2016-06-15 19:29:46 +01:00
2016-11-28 23:13:38 +00:00
retry :
/* The GVA page table is still active so use the Linux TLB handlers */
kvm_trap_emul_gva_lockless_begin ( vcpu ) ;
2016-08-19 15:27:22 +01:00
err = put_user ( replace . word , opc ) ;
2016-11-28 23:13:38 +00:00
kvm_trap_emul_gva_lockless_end ( vcpu ) ;
2016-08-19 15:27:22 +01:00
if ( unlikely ( err ) ) {
2016-11-28 23:13:38 +00:00
/*
* We write protect clean pages in GVA page table so normal
* Linux TLB mod handler doesn ' t silently dirty the page .
* Its also possible we raced with a GVA invalidation .
* Try to force the page to become dirty .
*/
err = kvm_trap_emul_gva_fault ( vcpu , vaddr , true ) ;
if ( unlikely ( err ) ) {
kvm_info ( " %s: Address unwriteable: %p \n " ,
__func__ , opc ) ;
return - EFAULT ;
}
/*
* Try again . This will likely trigger a TLB refill , which will
* fetch the new dirty entry from the GVA page table , which
* should then succeed .
*/
goto retry ;
2016-06-15 19:29:46 +01:00
}
2016-08-19 15:27:22 +01:00
__local_flush_icache_user_range ( vaddr , vaddr + 4 ) ;
2016-06-15 19:29:46 +01:00
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 ] ) ;
2016-07-08 11:53:27 +01:00
# ifdef CONFIG_CPU_BIG_ENDIAN
if ( sizeof ( vcpu - > arch . cop0 - > reg [ 0 ] [ 0 ] ) = = 8 )
mfc0_inst . i_format . simmediate | = 4 ;
# endif
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 ] ) ;
2016-07-08 11:53:27 +01:00
# ifdef CONFIG_CPU_BIG_ENDIAN
if ( sizeof ( vcpu - > arch . cop0 - > reg [ 0 ] [ 0 ] ) = = 8 )
mtc0_inst . i_format . simmediate | = 4 ;
# endif
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
}