2013-01-20 18:43:58 -05:00
/*
* Copyright ( C ) 2012 - Virtual Open Systems and Columbia University
* Author : Christoffer Dall < c . dall @ virtualopensystems . com >
*
* 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 .
*/
# include <linux/kvm_host.h>
# include <asm/kvm_mmio.h>
# include <asm/kvm_emulate.h>
# include <trace/events/kvm.h>
# include "trace.h"
2016-04-24 21:41:36 +02:00
void kvm_mmio_write_buf ( void * buf , unsigned int len , unsigned long data )
2013-02-12 12:40:22 +00:00
{
void * datap = NULL ;
union {
u8 byte ;
u16 hword ;
u32 word ;
u64 dword ;
} tmp ;
switch ( len ) {
case 1 :
tmp . byte = data ;
datap = & tmp . byte ;
break ;
case 2 :
tmp . hword = data ;
datap = & tmp . hword ;
break ;
case 4 :
tmp . word = data ;
datap = & tmp . word ;
break ;
case 8 :
tmp . dword = data ;
datap = & tmp . dword ;
break ;
}
memcpy ( buf , datap , len ) ;
}
2016-04-24 21:41:36 +02:00
unsigned long kvm_mmio_read_buf ( const void * buf , unsigned int len )
2013-02-12 12:40:22 +00:00
{
unsigned long data = 0 ;
union {
u16 hword ;
u32 word ;
u64 dword ;
} tmp ;
switch ( len ) {
case 1 :
2016-04-24 21:41:36 +02:00
data = * ( u8 * ) buf ;
2013-02-12 12:40:22 +00:00
break ;
case 2 :
memcpy ( & tmp . hword , buf , len ) ;
data = tmp . hword ;
break ;
case 4 :
memcpy ( & tmp . word , buf , len ) ;
data = tmp . word ;
break ;
case 8 :
memcpy ( & tmp . dword , buf , len ) ;
data = tmp . dword ;
break ;
}
return data ;
}
2013-01-20 18:43:58 -05:00
/**
* kvm_handle_mmio_return - - Handle MMIO loads after user space emulation
2016-03-29 14:29:28 +02:00
* or in - kernel IO emulation
*
2013-01-20 18:43:58 -05:00
* @ vcpu : The VCPU pointer
* @ run : The VCPU run struct containing the mmio data
*/
int kvm_handle_mmio_return ( struct kvm_vcpu * vcpu , struct kvm_run * run )
{
2013-02-12 12:40:22 +00:00
unsigned long data ;
2013-01-20 18:43:58 -05:00
unsigned int len ;
int mask ;
if ( ! run - > mmio . is_write ) {
len = run - > mmio . len ;
2013-03-05 02:43:23 +00:00
if ( len > sizeof ( unsigned long ) )
2013-01-20 18:43:58 -05:00
return - EINVAL ;
2016-04-24 21:41:36 +02:00
data = kvm_mmio_read_buf ( run - > mmio . data , len ) ;
2013-01-20 18:43:58 -05:00
2013-03-05 02:43:23 +00:00
if ( vcpu - > arch . mmio_decode . sign_extend & &
len < sizeof ( unsigned long ) ) {
2013-01-20 18:43:58 -05:00
mask = 1U < < ( ( len * 8 ) - 1 ) ;
2013-02-12 12:40:22 +00:00
data = ( data ^ mask ) - mask ;
2013-01-20 18:43:58 -05:00
}
2013-02-12 12:40:22 +00:00
trace_kvm_mmio ( KVM_TRACE_MMIO_READ , len , run - > mmio . phys_addr ,
data ) ;
data = vcpu_data_host_to_guest ( vcpu , data , len ) ;
2015-12-04 15:03:11 +03:00
vcpu_set_reg ( vcpu , vcpu - > arch . mmio_decode . rt , data ) ;
2013-01-20 18:43:58 -05:00
}
return 0 ;
}
2015-03-28 01:13:13 +00:00
static int decode_hsr ( struct kvm_vcpu * vcpu , bool * is_write , int * len )
2013-01-20 18:43:58 -05:00
{
2013-07-29 20:46:04 -07:00
unsigned long rt ;
2015-03-28 01:13:13 +00:00
int access_size ;
bool sign_extend ;
2013-01-20 18:43:58 -05:00
2012-09-18 11:37:28 +01:00
if ( kvm_vcpu_dabt_iss1tw ( vcpu ) ) {
2013-01-20 18:43:58 -05:00
/* page table accesses IO mem: tell guest to fix its TTBR */
2012-09-17 19:27:09 +01:00
kvm_inject_dabt ( vcpu , kvm_vcpu_get_hfar ( vcpu ) ) ;
2013-01-20 18:43:58 -05:00
return 1 ;
}
2015-03-28 01:13:13 +00:00
access_size = kvm_vcpu_dabt_get_as ( vcpu ) ;
if ( unlikely ( access_size < 0 ) )
return access_size ;
2013-01-20 18:43:58 -05:00
2015-03-28 01:13:13 +00:00
* is_write = kvm_vcpu_dabt_iswrite ( vcpu ) ;
2012-09-18 11:23:02 +01:00
sign_extend = kvm_vcpu_dabt_issext ( vcpu ) ;
2012-09-18 11:28:57 +01:00
rt = kvm_vcpu_dabt_get_rd ( vcpu ) ;
2013-01-20 18:43:58 -05:00
2015-03-28 01:13:13 +00:00
* len = access_size ;
2013-01-20 18:43:58 -05:00
vcpu - > arch . mmio_decode . sign_extend = sign_extend ;
vcpu - > arch . mmio_decode . rt = rt ;
/*
* The MMIO instruction is emulated and should not be re - executed
* in the guest .
*/
2012-09-18 12:07:06 +01:00
kvm_skip_instr ( vcpu , kvm_vcpu_trap_il_is32bit ( vcpu ) ) ;
2013-01-20 18:43:58 -05:00
return 0 ;
}
int io_mem_abort ( struct kvm_vcpu * vcpu , struct kvm_run * run ,
phys_addr_t fault_ipa )
{
2013-02-12 12:40:22 +00:00
unsigned long data ;
2013-01-20 18:43:58 -05:00
unsigned long rt ;
int ret ;
2015-03-28 01:13:13 +00:00
bool is_write ;
int len ;
u8 data_buf [ 8 ] ;
2013-01-20 18:43:58 -05:00
/*
2015-03-28 01:13:13 +00:00
* Prepare MMIO operation . First decode the syndrome data we get
* from the CPU . Then try if some in - kernel emulation feels
* responsible , otherwise let user space do its magic .
2013-01-20 18:43:58 -05:00
*/
2012-09-18 11:06:23 +01:00
if ( kvm_vcpu_dabt_isvalid ( vcpu ) ) {
2015-03-28 01:13:13 +00:00
ret = decode_hsr ( vcpu , & is_write , & len ) ;
2013-01-20 18:43:58 -05:00
if ( ret )
return ret ;
} else {
kvm_err ( " load/store instruction decoding not implemented \n " ) ;
return - ENOSYS ;
}
rt = vcpu - > arch . mmio_decode . rt ;
2013-02-12 12:40:22 +00:00
2015-03-28 01:13:13 +00:00
if ( is_write ) {
2015-12-04 15:03:11 +03:00
data = vcpu_data_guest_to_host ( vcpu , vcpu_get_reg ( vcpu , rt ) ,
len ) ;
2015-03-28 01:13:13 +00:00
trace_kvm_mmio ( KVM_TRACE_MMIO_WRITE , len , fault_ipa , data ) ;
2016-04-24 21:41:36 +02:00
kvm_mmio_write_buf ( data_buf , len , data ) ;
2013-01-20 18:43:58 -05:00
2015-03-28 01:13:13 +00:00
ret = kvm_io_bus_write ( vcpu , KVM_MMIO_BUS , fault_ipa , len ,
data_buf ) ;
2014-11-06 12:11:45 +00:00
} else {
2015-03-28 01:13:13 +00:00
trace_kvm_mmio ( KVM_TRACE_MMIO_READ_UNSATISFIED , len ,
2014-11-06 12:11:45 +00:00
fault_ipa , 0 ) ;
2015-03-28 01:13:13 +00:00
ret = kvm_io_bus_read ( vcpu , KVM_MMIO_BUS , fault_ipa , len ,
data_buf ) ;
2014-11-06 12:11:45 +00:00
}
2013-01-20 18:43:58 -05:00
2015-03-28 01:13:13 +00:00
/* Now prepare kvm_run for the potential return to userland. */
run - > mmio . is_write = is_write ;
run - > mmio . phys_addr = fault_ipa ;
run - > mmio . len = len ;
if ( ! ret ) {
/* We handled the access successfully in the kernel. */
2016-03-29 14:29:28 +02:00
if ( ! is_write )
memcpy ( run - > mmio . data , data_buf , len ) ;
2015-11-26 10:09:43 +00:00
vcpu - > stat . mmio_exit_kernel + + ;
2015-03-28 01:13:13 +00:00
kvm_handle_mmio_return ( vcpu , run ) ;
2013-01-21 19:36:12 -05:00
return 1 ;
2015-03-28 01:13:13 +00:00
}
2013-01-21 19:36:12 -05:00
2016-03-29 14:29:28 +02:00
if ( is_write )
memcpy ( run - > mmio . data , data_buf , len ) ;
vcpu - > stat . mmio_exit_user + + ;
2015-03-28 01:13:13 +00:00
run - > exit_reason = KVM_EXIT_MMIO ;
2013-01-20 18:43:58 -05:00
return 0 ;
}