2007-07-18 13:03:39 +04:00
/*
* Copyright ( C ) 2001 MandrakeSoft S . A .
*
* MandrakeSoft S . A .
* 43 , rue d ' Aboukir
* 75002 Paris - France
* http : //www.linux-mandrake.com/
* http : //www.mandrakesoft.com/
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Yunhong Jiang < yunhong . jiang @ intel . com >
* Yaozu ( Eddie ) Dong < eddie . dong @ intel . com >
* Based on Xen 3.1 code .
*/
2007-12-16 12:02:48 +03:00
# include <linux/kvm_host.h>
2007-07-18 13:03:39 +04:00
# include <linux/kvm.h>
# include <linux/mm.h>
# include <linux/highmem.h>
# include <linux/smp.h>
# include <linux/hrtimer.h>
# include <linux/io.h>
# include <asm/processor.h>
# include <asm/page.h>
# include <asm/current.h>
2007-12-17 08:59:56 +03:00
# include "ioapic.h"
# include "lapic.h"
2008-07-27 00:01:00 +04:00
# include "irq.h"
2007-12-17 08:59:56 +03:00
2007-10-12 13:01:59 +04:00
#if 0
# define ioapic_debug(fmt,arg...) printk(KERN_WARNING fmt,##arg)
# else
2007-07-18 13:03:39 +04:00
# define ioapic_debug(fmt, arg...)
2007-10-12 13:01:59 +04:00
# endif
2008-06-05 07:08:11 +04:00
static int ioapic_deliver ( struct kvm_ioapic * vioapic , int irq ) ;
2007-07-18 13:03:39 +04:00
static unsigned long ioapic_read_indirect ( struct kvm_ioapic * ioapic ,
unsigned long addr ,
unsigned long length )
{
unsigned long result = 0 ;
switch ( ioapic - > ioregsel ) {
case IOAPIC_REG_VERSION :
result = ( ( ( ( IOAPIC_NUM_PINS - 1 ) & 0xff ) < < 16 )
| ( IOAPIC_VERSION_ID & 0xff ) ) ;
break ;
case IOAPIC_REG_APIC_ID :
case IOAPIC_REG_ARB_ID :
result = ( ( ioapic - > id & 0xf ) < < 24 ) ;
break ;
default :
{
u32 redir_index = ( ioapic - > ioregsel - 0x10 ) > > 1 ;
u64 redir_content ;
ASSERT ( redir_index < IOAPIC_NUM_PINS ) ;
redir_content = ioapic - > redirtbl [ redir_index ] . bits ;
result = ( ioapic - > ioregsel & 0x1 ) ?
( redir_content > > 32 ) & 0xffffffff :
redir_content & 0xffffffff ;
break ;
}
}
return result ;
}
2009-02-04 18:28:14 +03:00
static int ioapic_service ( struct kvm_ioapic * ioapic , unsigned int idx )
2007-07-18 13:03:39 +04:00
{
2009-02-11 11:03:36 +03:00
union kvm_ioapic_redirect_entry * pent ;
2009-02-04 18:28:14 +03:00
int injected = - 1 ;
2007-07-18 13:03:39 +04:00
pent = & ioapic - > redirtbl [ idx ] ;
if ( ! pent - > fields . mask ) {
2009-02-04 18:28:14 +03:00
injected = ioapic_deliver ( ioapic , idx ) ;
2008-06-05 07:08:11 +04:00
if ( injected & & pent - > fields . trig_mode = = IOAPIC_LEVEL_TRIG )
2007-07-18 13:03:39 +04:00
pent - > fields . remote_irr = 1 ;
}
2009-02-04 18:28:14 +03:00
return injected ;
2007-07-18 13:03:39 +04:00
}
static void ioapic_write_indirect ( struct kvm_ioapic * ioapic , u32 val )
{
unsigned index ;
2009-01-04 18:10:50 +03:00
bool mask_before , mask_after ;
2007-07-18 13:03:39 +04:00
switch ( ioapic - > ioregsel ) {
case IOAPIC_REG_VERSION :
/* Writes are ignored. */
break ;
case IOAPIC_REG_APIC_ID :
ioapic - > id = ( val > > 24 ) & 0xf ;
break ;
case IOAPIC_REG_ARB_ID :
break ;
default :
index = ( ioapic - > ioregsel - 0x10 ) > > 1 ;
2007-10-12 13:01:59 +04:00
ioapic_debug ( " change redir index %x val %x \n " , index , val ) ;
2007-07-18 13:03:39 +04:00
if ( index > = IOAPIC_NUM_PINS )
return ;
2009-01-04 18:10:50 +03:00
mask_before = ioapic - > redirtbl [ index ] . fields . mask ;
2007-07-18 13:03:39 +04:00
if ( ioapic - > ioregsel & 1 ) {
ioapic - > redirtbl [ index ] . bits & = 0xffffffff ;
ioapic - > redirtbl [ index ] . bits | = ( u64 ) val < < 32 ;
} else {
ioapic - > redirtbl [ index ] . bits & = ~ 0xffffffffULL ;
ioapic - > redirtbl [ index ] . bits | = ( u32 ) val ;
ioapic - > redirtbl [ index ] . fields . remote_irr = 0 ;
}
2009-01-04 18:10:50 +03:00
mask_after = ioapic - > redirtbl [ index ] . fields . mask ;
if ( mask_before ! = mask_after )
kvm_fire_mask_notifiers ( ioapic - > kvm , index , mask_after ) ;
2009-07-05 19:48:11 +04:00
if ( ioapic - > redirtbl [ index ] . fields . trig_mode = = IOAPIC_LEVEL_TRIG
& & ioapic - > irr & ( 1 < < index ) )
2007-07-18 13:03:39 +04:00
ioapic_service ( ioapic , index ) ;
break ;
}
}
2009-03-05 17:34:49 +03:00
static int ioapic_deliver ( struct kvm_ioapic * ioapic , int irq )
{
2009-03-05 17:35:04 +03:00
union kvm_ioapic_redirect_entry * entry = & ioapic - > redirtbl [ irq ] ;
struct kvm_lapic_irq irqe ;
2009-03-05 17:34:49 +03:00
ioapic_debug ( " dest=%x dest_mode=%x delivery_mode=%x "
" vector=%x trig_mode=%x \n " ,
2009-03-05 17:35:04 +03:00
entry - > fields . dest , entry - > fields . dest_mode ,
entry - > fields . delivery_mode , entry - > fields . vector ,
entry - > fields . trig_mode ) ;
irqe . dest_id = entry - > fields . dest_id ;
irqe . vector = entry - > fields . vector ;
irqe . dest_mode = entry - > fields . dest_mode ;
irqe . trig_mode = entry - > fields . trig_mode ;
irqe . delivery_mode = entry - > fields . delivery_mode < < 8 ;
irqe . level = 1 ;
irqe . shorthand = 0 ;
2009-03-05 17:34:49 +03:00
# ifdef CONFIG_X86
/* Always delivery PIT interrupt to vcpu 0 */
if ( irq = = 0 ) {
2009-03-05 17:35:04 +03:00
irqe . dest_mode = 0 ; /* Physical mode. */
irqe . dest_id = ioapic - > kvm - > vcpus [ 0 ] - > vcpu_id ;
2009-03-05 17:34:49 +03:00
}
# endif
2009-03-05 17:35:04 +03:00
return kvm_irq_delivery_to_apic ( ioapic - > kvm , NULL , & irqe ) ;
2009-03-05 17:34:49 +03:00
}
2009-02-04 18:28:14 +03:00
int kvm_ioapic_set_irq ( struct kvm_ioapic * ioapic , int irq , int level )
2007-07-18 13:03:39 +04:00
{
u32 old_irr = ioapic - > irr ;
u32 mask = 1 < < irq ;
2009-02-11 11:03:36 +03:00
union kvm_ioapic_redirect_entry entry ;
2009-02-04 18:28:14 +03:00
int ret = 1 ;
2007-07-18 13:03:39 +04:00
if ( irq > = 0 & & irq < IOAPIC_NUM_PINS ) {
entry = ioapic - > redirtbl [ irq ] ;
level ^ = entry . fields . polarity ;
if ( ! level )
ioapic - > irr & = ~ mask ;
else {
2009-07-05 19:48:11 +04:00
int edge = ( entry . fields . trig_mode = = IOAPIC_EDGE_TRIG ) ;
2007-07-18 13:03:39 +04:00
ioapic - > irr | = mask ;
2009-07-05 19:48:11 +04:00
if ( ( edge & & old_irr ! = ioapic - > irr ) | |
( ! edge & & ! entry . fields . remote_irr ) )
2009-02-04 18:28:14 +03:00
ret = ioapic_service ( ioapic , irq ) ;
2007-07-18 13:03:39 +04:00
}
}
2009-02-04 18:28:14 +03:00
return ret ;
2007-07-18 13:03:39 +04:00
}
2009-01-27 20:12:38 +03:00
static void __kvm_ioapic_update_eoi ( struct kvm_ioapic * ioapic , int pin ,
2008-07-27 00:01:00 +04:00
int trigger_mode )
2007-07-18 13:03:39 +04:00
{
2009-02-11 11:03:36 +03:00
union kvm_ioapic_redirect_entry * ent ;
2007-07-18 13:03:39 +04:00
2009-01-27 20:12:38 +03:00
ent = & ioapic - > redirtbl [ pin ] ;
2007-07-18 13:03:39 +04:00
2009-01-27 20:12:38 +03:00
kvm_notify_acked_irq ( ioapic - > kvm , KVM_IRQCHIP_IOAPIC , pin ) ;
2008-07-27 00:01:00 +04:00
if ( trigger_mode = = IOAPIC_LEVEL_TRIG ) {
ASSERT ( ent - > fields . trig_mode = = IOAPIC_LEVEL_TRIG ) ;
ent - > fields . remote_irr = 0 ;
2009-01-27 20:12:38 +03:00
if ( ! ent - > fields . mask & & ( ioapic - > irr & ( 1 < < pin ) ) )
ioapic_service ( ioapic , pin ) ;
2008-07-27 00:01:00 +04:00
}
2007-07-18 13:03:39 +04:00
}
2008-07-27 00:01:00 +04:00
void kvm_ioapic_update_eoi ( struct kvm * kvm , int vector , int trigger_mode )
2008-06-18 02:36:36 +04:00
{
struct kvm_ioapic * ioapic = kvm - > arch . vioapic ;
int i ;
for ( i = 0 ; i < IOAPIC_NUM_PINS ; i + + )
if ( ioapic - > redirtbl [ i ] . fields . vector = = vector )
2008-07-27 00:01:00 +04:00
__kvm_ioapic_update_eoi ( ioapic , i , trigger_mode ) ;
2008-06-18 02:36:36 +04:00
}
2008-05-30 18:05:53 +04:00
static int ioapic_in_range ( struct kvm_io_device * this , gpa_t addr ,
int len , int is_write )
2007-07-18 13:03:39 +04:00
{
struct kvm_ioapic * ioapic = ( struct kvm_ioapic * ) this - > private ;
return ( ( addr > = ioapic - > base_address & &
( addr < ioapic - > base_address + IOAPIC_MEM_LENGTH ) ) ) ;
}
static void ioapic_mmio_read ( struct kvm_io_device * this , gpa_t addr , int len ,
void * val )
{
struct kvm_ioapic * ioapic = ( struct kvm_ioapic * ) this - > private ;
u32 result ;
2007-10-12 13:01:59 +04:00
ioapic_debug ( " addr %lx \n " , ( unsigned long ) addr ) ;
2007-07-18 13:03:39 +04:00
ASSERT ( ! ( addr & 0xf ) ) ; /* check alignment */
addr & = 0xff ;
switch ( addr ) {
case IOAPIC_REG_SELECT :
result = ioapic - > ioregsel ;
break ;
case IOAPIC_REG_WINDOW :
result = ioapic_read_indirect ( ioapic , addr , len ) ;
break ;
default :
result = 0 ;
break ;
}
switch ( len ) {
case 8 :
* ( u64 * ) val = result ;
break ;
case 1 :
case 2 :
case 4 :
memcpy ( val , ( char * ) & result , len ) ;
break ;
default :
printk ( KERN_WARNING " ioapic: wrong length %d \n " , len ) ;
}
}
static void ioapic_mmio_write ( struct kvm_io_device * this , gpa_t addr , int len ,
const void * val )
{
struct kvm_ioapic * ioapic = ( struct kvm_ioapic * ) this - > private ;
u32 data ;
2007-10-12 13:01:59 +04:00
ioapic_debug ( " ioapic_mmio_write addr=%p len=%d val=%p \n " ,
( void * ) addr , len , val ) ;
2007-07-18 13:03:39 +04:00
ASSERT ( ! ( addr & 0xf ) ) ; /* check alignment */
if ( len = = 4 | | len = = 8 )
data = * ( u32 * ) val ;
else {
printk ( KERN_WARNING " ioapic: Unsupported size %d \n " , len ) ;
return ;
}
addr & = 0xff ;
switch ( addr ) {
case IOAPIC_REG_SELECT :
ioapic - > ioregsel = data ;
break ;
case IOAPIC_REG_WINDOW :
ioapic_write_indirect ( ioapic , data ) ;
break ;
2007-12-02 17:53:07 +03:00
# ifdef CONFIG_IA64
case IOAPIC_REG_EOI :
2008-08-19 16:48:03 +04:00
kvm_ioapic_update_eoi ( ioapic - > kvm , data , IOAPIC_LEVEL_TRIG ) ;
2007-12-02 17:53:07 +03:00
break ;
# endif
2007-07-18 13:03:39 +04:00
default :
break ;
}
}
2007-10-10 14:15:54 +04:00
void kvm_ioapic_reset ( struct kvm_ioapic * ioapic )
{
int i ;
for ( i = 0 ; i < IOAPIC_NUM_PINS ; i + + )
ioapic - > redirtbl [ i ] . fields . mask = 1 ;
ioapic - > base_address = IOAPIC_DEFAULT_BASE_ADDRESS ;
ioapic - > ioregsel = 0 ;
ioapic - > irr = 0 ;
ioapic - > id = 0 ;
}
2007-07-18 13:03:39 +04:00
int kvm_ioapic_init ( struct kvm * kvm )
{
struct kvm_ioapic * ioapic ;
ioapic = kzalloc ( sizeof ( struct kvm_ioapic ) , GFP_KERNEL ) ;
if ( ! ioapic )
return - ENOMEM ;
2007-12-14 05:17:34 +03:00
kvm - > arch . vioapic = ioapic ;
2007-10-10 14:15:54 +04:00
kvm_ioapic_reset ( ioapic ) ;
2007-07-18 13:03:39 +04:00
ioapic - > dev . read = ioapic_mmio_read ;
ioapic - > dev . write = ioapic_mmio_write ;
ioapic - > dev . in_range = ioapic_in_range ;
ioapic - > dev . private = ioapic ;
ioapic - > kvm = kvm ;
kvm_io_bus_register_dev ( & kvm - > mmio_bus , & ioapic - > dev ) ;
return 0 ;
}
2009-01-04 18:10:50 +03:00