2019-05-30 02:57:35 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-04-16 01:04:10 +04:00
/*
* irqchip . c : Common API for in kernel interrupt controllers
* Copyright ( c ) 2007 , Intel Corporation .
* Copyright 2010 Red Hat , Inc . and / or its affiliates .
* Copyright ( c ) 2013 , Alexander Graf < agraf @ suse . de >
*
* This file is derived from virt / kvm / irq_comm . c .
*
* Authors :
* Yaozu ( Eddie ) Dong < Eddie . dong @ intel . com >
* Alexander Graf < agraf @ suse . de >
*/
# include <linux/kvm_host.h>
# include <linux/slab.h>
2014-01-16 16:44:20 +04:00
# include <linux/srcu.h>
2013-04-16 01:04:10 +04:00
# include <linux/export.h>
# include <trace/events/kvm.h>
# include "irq.h"
2014-06-30 14:51:11 +04:00
int kvm_irq_map_gsi ( struct kvm * kvm ,
struct kvm_kernel_irq_routing_entry * entries , int gsi )
2014-06-30 14:51:10 +04:00
{
2014-06-30 14:51:11 +04:00
struct kvm_irq_routing_table * irq_rt ;
2014-06-30 14:51:10 +04:00
struct kvm_kernel_irq_routing_entry * e ;
int n = 0 ;
2014-06-30 14:51:11 +04:00
irq_rt = srcu_dereference_check ( kvm - > irq_routing , & kvm - > irq_srcu ,
lockdep_is_held ( & kvm - > irq_lock ) ) ;
2016-06-01 15:09:21 +03:00
if ( irq_rt & & gsi < irq_rt - > nr_rt_entries ) {
2014-06-30 14:51:10 +04:00
hlist_for_each_entry ( e , & irq_rt - > map [ gsi ] , link ) {
entries [ n ] = * e ;
+ + n ;
}
}
return n ;
}
2014-06-30 14:51:11 +04:00
int kvm_irq_map_chip_pin ( struct kvm * kvm , unsigned irqchip , unsigned pin )
2014-06-30 14:51:10 +04:00
{
2014-06-30 14:51:11 +04:00
struct kvm_irq_routing_table * irq_rt ;
irq_rt = srcu_dereference ( kvm - > irq_routing , & kvm - > irq_srcu ) ;
2014-06-30 14:51:10 +04:00
return irq_rt - > chip [ irqchip ] [ pin ] ;
}
2013-04-16 01:04:10 +04:00
int kvm_send_userspace_msi ( struct kvm * kvm , struct kvm_msi * msi )
{
struct kvm_kernel_irq_routing_entry route ;
2016-07-22 19:20:39 +03:00
if ( ! irqchip_in_kernel ( kvm ) | | ( msi - > flags & ~ KVM_MSI_VALID_DEVID ) )
2013-04-16 01:04:10 +04:00
return - EINVAL ;
route . msi . address_lo = msi - > address_lo ;
route . msi . address_hi = msi - > address_hi ;
route . msi . data = msi - > data ;
2016-07-22 19:20:39 +03:00
route . msi . flags = msi - > flags ;
route . msi . devid = msi - > devid ;
2013-04-16 01:04:10 +04:00
return kvm_set_msi ( & route , kvm , KVM_USERSPACE_IRQ_SOURCE_ID , 1 , false ) ;
}
/*
* Return value :
* < 0 Interrupt was ignored ( masked or not delivered for other reasons )
* = 0 Interrupt was coalesced ( previous irq is still pending )
* > 0 Number of CPUs interrupt was delivered to
*/
int kvm_set_irq ( struct kvm * kvm , int irq_source_id , u32 irq , int level ,
bool line_status )
{
2014-06-30 14:51:10 +04:00
struct kvm_kernel_irq_routing_entry irq_set [ KVM_NR_IRQCHIPS ] ;
int ret = - 1 , i , idx ;
2013-04-16 01:04:10 +04:00
trace_kvm_set_irq ( irq , level , irq_source_id ) ;
/* Not possible to detect if the guest uses the PIC or the
* IOAPIC . So set the bit in both . The guest will ignore
* writes to the unused one .
*/
2014-01-16 16:44:20 +04:00
idx = srcu_read_lock ( & kvm - > irq_srcu ) ;
2014-06-30 14:51:11 +04:00
i = kvm_irq_map_gsi ( kvm , irq_set , irq ) ;
2014-01-16 16:44:20 +04:00
srcu_read_unlock ( & kvm - > irq_srcu , idx ) ;
2013-04-16 01:04:10 +04:00
2015-02-20 16:21:37 +03:00
while ( i - - ) {
2013-04-16 01:04:10 +04:00
int r ;
r = irq_set [ i ] . set ( & irq_set [ i ] , kvm , irq_source_id , level ,
line_status ) ;
if ( r < 0 )
continue ;
ret = r + ( ( ret < 0 ) ? 0 : ret ) ;
}
return ret ;
}
2015-05-08 15:31:44 +03:00
static void free_irq_routing_table ( struct kvm_irq_routing_table * rt )
{
int i ;
if ( ! rt )
return ;
for ( i = 0 ; i < rt - > nr_rt_entries ; + + i ) {
struct kvm_kernel_irq_routing_entry * e ;
struct hlist_node * n ;
hlist_for_each_entry_safe ( e , n , & rt - > map [ i ] , link ) {
hlist_del ( & e - > link ) ;
kfree ( e ) ;
}
}
kfree ( rt ) ;
}
2013-04-16 01:04:10 +04:00
void kvm_free_irq_routing ( struct kvm * kvm )
{
/* Called only during vm destruction. Nobody can use the pointer
at this stage */
2015-05-08 15:31:44 +03:00
struct kvm_irq_routing_table * rt = rcu_access_pointer ( kvm - > irq_routing ) ;
free_irq_routing_table ( rt ) ;
2013-04-16 01:04:10 +04:00
}
2013-04-16 01:23:21 +04:00
2016-07-12 23:09:26 +03:00
static int setup_routing_entry ( struct kvm * kvm ,
struct kvm_irq_routing_table * rt ,
2013-04-16 01:23:21 +04:00
struct kvm_kernel_irq_routing_entry * e ,
const struct kvm_irq_routing_entry * ue )
{
struct kvm_kernel_irq_routing_entry * ei ;
2017-04-07 11:50:35 +03:00
int r ;
2019-04-11 12:16:47 +03:00
u32 gsi = array_index_nospec ( ue - > gsi , KVM_MAX_IRQ_ROUTES ) ;
2013-04-16 01:23:21 +04:00
/*
* Do not allow GSI to be mapped to the same irqchip more than once .
2015-10-16 10:07:48 +03:00
* Allow only one to one mapping between GSI and non - irqchip routing .
2013-04-16 01:23:21 +04:00
*/
2019-04-11 12:16:47 +03:00
hlist_for_each_entry ( ei , & rt - > map [ gsi ] , link )
2015-10-16 10:07:48 +03:00
if ( ei - > type ! = KVM_IRQ_ROUTING_IRQCHIP | |
ue - > type ! = KVM_IRQ_ROUTING_IRQCHIP | |
2013-04-16 01:23:21 +04:00
ue - > u . irqchip . irqchip = = ei - > irqchip . irqchip )
2017-04-07 11:50:35 +03:00
return - EINVAL ;
2013-04-16 01:23:21 +04:00
2019-04-11 12:16:47 +03:00
e - > gsi = gsi ;
2013-04-16 01:23:21 +04:00
e - > type = ue - > type ;
2016-07-12 23:09:26 +03:00
r = kvm_set_routing_entry ( kvm , e , ue ) ;
2013-04-16 01:23:21 +04:00
if ( r )
2017-04-07 11:50:35 +03:00
return r ;
2014-06-30 14:51:10 +04:00
if ( e - > type = = KVM_IRQ_ROUTING_IRQCHIP )
rt - > chip [ e - > irqchip . irqchip ] [ e - > irqchip . pin ] = e - > gsi ;
2013-04-16 01:23:21 +04:00
hlist_add_head ( & e - > link , & rt - > map [ e - > gsi ] ) ;
2017-04-07 11:50:35 +03:00
return 0 ;
2013-04-16 01:23:21 +04:00
}
2015-11-10 15:36:31 +03:00
void __attribute__ ( ( weak ) ) kvm_arch_irq_routing_update ( struct kvm * kvm )
{
}
2017-04-28 18:06:20 +03:00
bool __weak kvm_arch_can_set_irq_routing ( struct kvm * kvm )
{
return true ;
}
2013-04-16 01:23:21 +04:00
int kvm_set_irq_routing ( struct kvm * kvm ,
const struct kvm_irq_routing_entry * ue ,
unsigned nr ,
unsigned flags )
{
struct kvm_irq_routing_table * new , * old ;
2016-07-22 19:20:42 +03:00
struct kvm_kernel_irq_routing_entry * e ;
2013-04-16 01:23:21 +04:00
u32 i , j , nr_rt_entries = 0 ;
int r ;
for ( i = 0 ; i < nr ; + + i ) {
if ( ue [ i ] . gsi > = KVM_MAX_IRQ_ROUTES )
return - EINVAL ;
nr_rt_entries = max ( nr_rt_entries , ue [ i ] . gsi ) ;
}
nr_rt_entries + = 1 ;
2019-05-31 22:24:53 +03:00
new = kzalloc ( struct_size ( new , map , nr_rt_entries ) , GFP_KERNEL_ACCOUNT ) ;
2013-04-16 01:23:21 +04:00
if ( ! new )
return - ENOMEM ;
new - > nr_rt_entries = nr_rt_entries ;
for ( i = 0 ; i < KVM_NR_IRQCHIPS ; i + + )
for ( j = 0 ; j < KVM_IRQCHIP_NUM_PINS ; j + + )
new - > chip [ i ] [ j ] = - 1 ;
for ( i = 0 ; i < nr ; + + i ) {
2015-05-08 15:31:44 +03:00
r = - ENOMEM ;
2019-02-11 22:02:49 +03:00
e = kzalloc ( sizeof ( * e ) , GFP_KERNEL_ACCOUNT ) ;
2015-05-08 15:31:44 +03:00
if ( ! e )
goto out ;
2013-04-16 01:23:21 +04:00
r = - EINVAL ;
2016-07-22 19:20:42 +03:00
switch ( ue - > type ) {
case KVM_IRQ_ROUTING_MSI :
if ( ue - > flags & ~ KVM_MSI_VALID_DEVID )
goto free_entry ;
break ;
default :
if ( ue - > flags )
goto free_entry ;
break ;
2015-09-02 10:03:53 +03:00
}
2016-07-12 23:09:26 +03:00
r = setup_routing_entry ( kvm , new , e , ue ) ;
2016-07-22 19:20:42 +03:00
if ( r )
goto free_entry ;
2013-04-16 01:23:21 +04:00
+ + ue ;
}
mutex_lock ( & kvm - > irq_lock ) ;
2017-07-06 21:31:11 +03:00
old = rcu_dereference_protected ( kvm - > irq_routing , 1 ) ;
2014-06-30 14:51:11 +04:00
rcu_assign_pointer ( kvm - > irq_routing , new ) ;
kvm_irq_routing_update ( kvm ) ;
2015-11-10 15:36:31 +03:00
kvm_arch_irq_routing_update ( kvm ) ;
2013-04-16 01:23:21 +04:00
mutex_unlock ( & kvm - > irq_lock ) ;
2015-11-10 15:36:31 +03:00
kvm_arch_post_irq_routing_update ( kvm ) ;
2015-07-30 09:32:35 +03:00
2014-01-16 16:44:20 +04:00
synchronize_srcu_expedited ( & kvm - > irq_srcu ) ;
2013-04-16 01:23:21 +04:00
new = old ;
r = 0 ;
2016-07-22 19:20:42 +03:00
goto out ;
2013-04-16 01:23:21 +04:00
2016-07-22 19:20:42 +03:00
free_entry :
kfree ( e ) ;
2013-04-16 01:23:21 +04:00
out :
2015-05-08 15:31:44 +03:00
free_irq_routing_table ( new ) ;
2013-04-16 01:23:21 +04:00
return r ;
}