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"
2013-02-12 12:40:22 +00:00
static void mmio_write_buf ( char * buf , unsigned int len , unsigned long data )
{
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 ) ;
}
static unsigned long mmio_read_buf ( char * buf , unsigned int len )
{
unsigned long data = 0 ;
union {
u16 hword ;
u32 word ;
u64 dword ;
} tmp ;
switch ( len ) {
case 1 :
data = buf [ 0 ] ;
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
* @ vcpu : The VCPU pointer
* @ run : The VCPU run struct containing the mmio data
*
* This should only be called after returning from userspace for MMIO load
* emulation .
*/
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 ;
2013-02-12 12:40:22 +00:00
data = 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 ) ;
* vcpu_reg ( vcpu , vcpu - > arch . mmio_decode . rt ) = data ;
2013-01-20 18:43:58 -05:00
}
return 0 ;
}
static int decode_hsr ( struct kvm_vcpu * vcpu , phys_addr_t fault_ipa ,
struct kvm_exit_mmio * mmio )
{
2013-07-29 20:46:04 -07:00
unsigned long rt ;
int len ;
2013-01-20 18:43:58 -05:00
bool is_write , sign_extend ;
2012-09-18 11:36:16 +01:00
if ( kvm_vcpu_dabt_isextabt ( vcpu ) ) {
2013-01-20 18:43:58 -05:00
/* cache operation on I/O addr, tell guest unsupported */
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 ;
}
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 ;
}
2012-09-18 11:43:30 +01:00
len = kvm_vcpu_dabt_get_as ( vcpu ) ;
if ( unlikely ( len < 0 ) )
return len ;
2013-01-20 18:43:58 -05:00
2012-09-18 11:12:26 +01: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
mmio - > is_write = is_write ;
mmio - > phys_addr = fault_ipa ;
mmio - > len = len ;
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 )
{
struct kvm_exit_mmio mmio ;
2013-02-12 12:40:22 +00:00
unsigned long data ;
2013-01-20 18:43:58 -05:00
unsigned long rt ;
int ret ;
/*
* Prepare MMIO operation . First stash it in a private
* structure that we can use for in - kernel emulation . If the
* kernel can ' t handle it , copy it into run - > mmio and let user
* space do its magic .
*/
2012-09-18 11:06:23 +01:00
if ( kvm_vcpu_dabt_isvalid ( vcpu ) ) {
2013-01-20 18:43:58 -05:00
ret = decode_hsr ( vcpu , fault_ipa , & mmio ) ;
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
data = vcpu_data_guest_to_host ( vcpu , * vcpu_reg ( vcpu , rt ) , mmio . len ) ;
2013-01-20 18:43:58 -05:00
trace_kvm_mmio ( ( mmio . is_write ) ? KVM_TRACE_MMIO_WRITE :
KVM_TRACE_MMIO_READ_UNSATISFIED ,
mmio . len , fault_ipa ,
2013-02-12 12:40:22 +00:00
( mmio . is_write ) ? data : 0 ) ;
2013-01-20 18:43:58 -05:00
if ( mmio . is_write )
2013-02-12 12:40:22 +00:00
mmio_write_buf ( mmio . data , mmio . len , data ) ;
2013-01-20 18:43:58 -05:00
2013-01-21 19:36:12 -05:00
if ( vgic_handle_mmio ( vcpu , run , & mmio ) )
return 1 ;
2013-01-20 18:43:58 -05:00
kvm_prepare_mmio ( run , & mmio ) ;
return 0 ;
}