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"
/**
* 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 )
{
2012-10-03 11:17:02 +01:00
unsigned long * dest ;
2013-01-20 18:43:58 -05:00
unsigned int len ;
int mask ;
if ( ! run - > mmio . is_write ) {
dest = vcpu_reg ( vcpu , vcpu - > arch . mmio_decode . rt ) ;
2013-03-05 02:43:23 +00:00
* dest = 0 ;
2013-01-20 18:43:58 -05:00
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 ;
memcpy ( dest , run - > mmio . data , len ) ;
trace_kvm_mmio ( KVM_TRACE_MMIO_READ , len , run - > mmio . phys_addr ,
* ( ( u64 * ) run - > mmio . data ) ) ;
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 ) ;
* dest = ( * dest ^ mask ) - mask ;
}
}
return 0 ;
}
static int decode_hsr ( struct kvm_vcpu * vcpu , phys_addr_t fault_ipa ,
struct kvm_exit_mmio * mmio )
{
unsigned long rt , len ;
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
if ( kvm_vcpu_reg_is_pc ( vcpu , rt ) ) {
/* IO memory trying to read/write pc */
2012-09-17 19:27:09 +01:00
kvm_inject_pabt ( vcpu , kvm_vcpu_get_hfar ( vcpu ) ) ;
2013-01-20 18:43:58 -05:00
return 1 ;
}
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 ;
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 ;
trace_kvm_mmio ( ( mmio . is_write ) ? KVM_TRACE_MMIO_WRITE :
KVM_TRACE_MMIO_READ_UNSATISFIED ,
mmio . len , fault_ipa ,
( mmio . is_write ) ? * vcpu_reg ( vcpu , rt ) : 0 ) ;
if ( mmio . is_write )
memcpy ( mmio . data , vcpu_reg ( vcpu , rt ) , mmio . len ) ;
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 ;
}