@ -32,14 +32,13 @@
# include "qapi/error.h"
# include "qom/object.h"
# define MAX_APICS 255
# define MAX_APIC_WORDS 8
# define SYNC_FROM_VAPIC 0x1
# define SYNC_TO_VAPIC 0x2
# define SYNC_ISR_IRR_TO_VAPIC 0x4
static APICCommonState * local_apics [ MAX_APICS + 1 ] ;
static APICCommonState * * local_apics ;
static uint32_t max_apics ;
static uint32_t max_apic_words ;
# define TYPE_APIC "apic"
/*This is reusing the APICCommonState typedef from APIC_COMMON */
@ -49,7 +48,19 @@ DECLARE_INSTANCE_CHECKER(APICCommonState, APIC,
static void apic_set_irq ( APICCommonState * s , int vector_num , int trigger_mode ) ;
static void apic_update_irq ( APICCommonState * s ) ;
static void apic_get_delivery_bitmask ( uint32_t * deliver_bitmask ,
uint8_t dest , uint8_t dest_mode ) ;
uint32_t dest , uint8_t dest_mode ) ;
void apic_set_max_apic_id ( uint32_t max_apic_id )
{
int word_size = 32 ;
/* round up the max apic id to next multiple of words */
max_apics = ( max_apic_id + word_size - 1 ) & ~ ( word_size - 1 ) ;
local_apics = g_malloc0 ( sizeof ( * local_apics ) * max_apics ) ;
max_apic_words = max_apics > > 5 ;
}
/* Find first bit starting from msb */
static int apic_fls_bit ( uint32_t value )
@ -199,10 +210,10 @@ static void apic_external_nmi(APICCommonState *s)
# define foreach_apic(apic, deliver_bitmask, code) \
{ \
int __i , __j ; \
for ( __i = 0 ; __i < MAX_APIC_WORDS ; __i + + ) { \
for ( __i = 0 ; __i < max_apic_words ; __i + + ) { \
uint32_t __mask = deliver_bitmask [ __i ] ; \
if ( __mask ) { \
for ( __j = 0 ; __j < 32 ; __j + + ) { \
for ( __j = 0 ; __j < 32 ; __j + + ) { \
if ( __mask & ( 1U < < __j ) ) { \
apic = local_apics [ __i * 32 + __j ] ; \
if ( apic ) { \
@ -226,7 +237,7 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask,
{
int i , d ;
d = - 1 ;
for ( i = 0 ; i < MAX_APIC_WORDS ; i + + ) {
for ( i = 0 ; i < max_apic_words ; i + + ) {
if ( deliver_bitmask [ i ] ) {
d = i * 32 + apic_ffs_bit ( deliver_bitmask [ i ] ) ;
break ;
@ -276,16 +287,18 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask,
apic_set_irq ( apic_iter , vector_num , trigger_mode ) ) ;
}
void apic_deliver_irq ( uint8_t dest , uint8_t dest_mode , uint8_t delivery_mode ,
uint8_t vector_num , uint8_t trigger_mode )
static void apic_deliver_irq ( uint32_t dest , uint8_t dest_mode ,
uint8_t delivery_mode , uint8_t vector_num ,
uint8_t trigger_mode )
{
uint32_t deliver_bitmask [ MAX_APIC_WORDS ] ;
uint32_t * deliver_bitmask = g_malloc ( max_apic_words * sizeof ( uint32_t ) ) ;
trace_apic_deliver_irq ( dest , dest_mode , delivery_mode , vector_num ,
trigger_mode ) ;
apic_get_delivery_bitmask ( deliver_bitmask , dest , dest_mode ) ;
apic_bus_deliver ( deliver_bitmask , delivery_mode , vector_num , trigger_mode ) ;
g_free ( deliver_bitmask ) ;
}
bool is_x2apic_mode ( DeviceState * dev )
@ -442,57 +455,123 @@ static void apic_eoi(APICCommonState *s)
apic_update_irq ( s ) ;
}
static int apic_find_dest ( uint8 _t dest )
static bool apic_match_dest ( APICCommonState * apic , uint32 _t dest )
{
APICCommonState * apic = local_apics [ dest ] ;
int i ;
if ( is_x2apic_mode ( & apic - > parent_obj ) ) {
return apic - > initial_apic_id = = dest ;
} else {
return apic - > id = = ( uint8_t ) dest ;
}
}
if ( apic & & apic - > id = = dest )
return dest ; /* shortcut in case apic->id == local_apics[dest]->id */
static void apic_find_dest ( uint32_t * deliver_bitmask , uint32_t dest )
{
APICCommonState * apic = NULL ;
int i ;
for ( i = 0 ; i < MAX_APICS ; i + + ) {
for ( i = 0 ; i < max_apics ; i + + ) {
apic = local_apics [ i ] ;
if ( apic & & apic - > id = = dest )
return i ;
if ( ! apic )
break ;
if ( apic & & apic_match_dest ( apic , dest ) ) {
apic_set_bit ( deliver_bitmask , i ) ;
}
}
}
return - 1 ;
/*
* Deliver interrupt to x2APIC CPUs if it is x2APIC broadcast .
* Otherwise , deliver interrupt to xAPIC CPUs if it is xAPIC
* broadcast .
*/
static void apic_get_broadcast_bitmask ( uint32_t * deliver_bitmask ,
bool is_x2apic_broadcast )
{
int i ;
APICCommonState * apic_iter ;
for ( i = 0 ; i < max_apics ; i + + ) {
apic_iter = local_apics [ i ] ;
if ( apic_iter ) {
bool apic_in_x2apic = is_x2apic_mode ( & apic_iter - > parent_obj ) ;
if ( is_x2apic_broadcast & & apic_in_x2apic ) {
apic_set_bit ( deliver_bitmask , i ) ;
} else if ( ! is_x2apic_broadcast & & ! apic_in_x2apic ) {
apic_set_bit ( deliver_bitmask , i ) ;
}
}
}
}
static void apic_get_delivery_bitmask ( uint32_t * deliver_bitmask ,
uint8_t dest , uint8_t dest_mode )
uint32 _t dest , uint8_t dest_mode )
{
APICCommonState * apic_iter ;
APICCommonState * apic ;
int i ;
if ( dest_mode = = 0 ) {
if ( dest = = 0xff ) {
memset ( deliver_bitmask , 0xff , MAX_APIC_WORDS * sizeof ( uint32_t ) ) ;
memset ( deliver_bitmask , 0x00 , max_apic_words * sizeof ( uint32_t ) ) ;
/*
* x2APIC broadcast is delivered to all x2APIC CPUs regardless of
* destination mode . In case the destination mode is physical , it is
* broadcasted to all xAPIC CPUs too . Otherwise , if the destination
* mode is logical , we need to continue checking if xAPIC CPUs accepts
* the interrupt .
*/
if ( dest = = 0xffffffff ) {
if ( dest_mode = = APIC_DESTMODE_PHYSICAL ) {
memset ( deliver_bitmask , 0xff , max_apic_words * sizeof ( uint32_t ) ) ;
return ;
} else {
int idx = apic_find_dest ( dest ) ;
memset ( deliver_bitmask , 0x00 , MAX_APIC_WORDS * sizeof ( uint32_t ) ) ;
if ( idx > = 0 )
apic_set_bit ( deliver_bitmask , idx ) ;
apic_get_broadcast_bitmask ( deliver_bitmask , true ) ;
}
}
if ( dest_mode = = APIC_DESTMODE_PHYSICAL ) {
apic_find_dest ( deliver_bitmask , dest ) ;
/* Any APIC in xAPIC mode will interpret 0xFF as broadcast */
if ( dest = = 0xff ) {
apic_get_broadcast_bitmask ( deliver_bitmask , false ) ;
}
} else {
/* XXX: cluster mode */
memset ( deliver_bitmask , 0x00 , MAX_APIC_WORDS * sizeof ( uint32_t ) ) ;
for ( i = 0 ; i < MAX_APICS ; i + + ) {
apic_iter = local_apics [ i ] ;
if ( apic_iter ) {
if ( apic_iter - > dest_mode = = 0xf ) {
if ( dest & apic_iter - > log_dest )
apic_set_bit ( deliver_bitmask , i ) ;
} else if ( apic_iter - > dest_mode = = 0x0 ) {
if ( ( dest & 0xf0 ) = = ( apic_iter - > log_dest & 0xf0 ) & &
( dest & apic_iter - > log_dest & 0x0f ) ) {
/* XXX: logical mode */
for ( i = 0 ; i < max_apics ; i + + ) {
apic = local_apics [ i ] ;
if ( apic ) {
/* x2APIC logical mode */
if ( apic - > apicbase & MSR_IA32_APICBASE_EXTD ) {
if ( ( dest > > 16 ) = = ( apic - > extended_log_dest > > 16 ) & &
( dest & apic - > extended_log_dest & 0xffff ) ) {
apic_set_bit ( deliver_bitmask , i ) ;
}
continue ;
}
} else {
break ;
/* xAPIC logical mode */
dest = ( uint8_t ) dest ;
if ( apic - > dest_mode = = APIC_DESTMODE_LOGICAL_FLAT ) {
if ( dest & apic - > log_dest ) {
apic_set_bit ( deliver_bitmask , i ) ;
}
} else if ( apic - > dest_mode = = APIC_DESTMODE_LOGICAL_CLUSTER ) {
/*
* In cluster model of xAPIC logical mode IPI , 4 higher
* bits are used as cluster address , 4 lower bits are
* the bitmask for local APICs in the cluster . The IPI
* is delivered to an APIC if the cluster address
* matches and the APIC ' s address bit in the cluster is
* set in bitmask of destination ID in IPI .
*
* The cluster address ranges from 0 - 14 , the cluster
* address 15 ( 0xf ) is the broadcast address to all
* clusters .
*/
if ( ( dest & 0xf0 ) = = 0xf0 | |
( dest & 0xf0 ) = = ( apic - > log_dest & 0xf0 ) ) {
if ( dest & apic - > log_dest & 0x0f ) {
apic_set_bit ( deliver_bitmask , i ) ;
}
}
}
}
}
}
@ -516,29 +595,36 @@ void apic_sipi(DeviceState *dev)
s - > wait_for_sipi = 0 ;
}
static void apic_deliver ( DeviceState * dev , uint8 _t dest , uint8_t dest_mode ,
static void apic_deliver ( DeviceState * dev , uint32 _t dest , uint8_t dest_mode ,
uint8_t delivery_mode , uint8_t vector_num ,
uint8_t trigger_mode )
uint8_t trigger_mode , uint8_t dest_shorthand )
{
APICCommonState * s = APIC ( dev ) ;
uint32_t deliver_bitmask [ MAX_APIC_WORDS ] ;
int dest_shorthand = ( s - > icr [ 0 ] > > 18 ) & 3 ;
APICCommonState * apic_iter ;
uint32_t deliver_bitmask_size = max_apic_words * sizeof ( uint32_t ) ;
uint32_t * deliver_bitmask = g_malloc ( deliver_bitmask_size ) ;
uint32_t current_apic_id ;
if ( is_x2apic_mode ( dev ) ) {
current_apic_id = s - > initial_apic_id ;
} else {
current_apic_id = s - > id ;
}
switch ( dest_shorthand ) {
case 0 :
apic_get_delivery_bitmask ( deliver_bitmask , dest , dest_mode ) ;
break ;
case 1 :
memset ( deliver_bitmask , 0x00 , sizeof ( deliver_bitmask ) ) ;
apic_set_bit ( deliver_bitmask , s - > id ) ;
memset ( deliver_bitmask , 0x00 , deliver_bitmask_size ) ;
apic_set_bit ( deliver_bitmask , current_apic_ id) ;
break ;
case 2 :
memset ( deliver_bitmask , 0xff , sizeof ( deliver_bitmask ) ) ;
memset ( deliver_bitmask , 0xff , deliver_bitmask_size ) ;
break ;
case 3 :
memset ( deliver_bitmask , 0xff , sizeof ( deliver_bitmask ) ) ;
apic_reset_bit ( deliver_bitmask , s - > id ) ;
memset ( deliver_bitmask , 0xff , deliver_bitmask_size ) ;
apic_reset_bit ( deliver_bitmask , current_apic_ id) ;
break ;
}
@ -562,6 +648,7 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
}
apic_bus_deliver ( deliver_bitmask , delivery_mode , vector_num , trigger_mode ) ;
g_free ( deliver_bitmask ) ;
}
static bool apic_check_pic ( APICCommonState * s )
@ -658,7 +745,11 @@ static int apic_register_read(int index, uint64_t *value)
switch ( index ) {
case 0x02 : /* id */
val = s - > id < < 24 ;
if ( is_x2apic_mode ( dev ) ) {
val = s - > initial_apic_id ;
} else {
val = s - > id < < 24 ;
}
break ;
case 0x03 : /* version */
val = s - > version | ( ( APIC_LVT_NB - 1 ) < < 16 ) ;
@ -681,10 +772,19 @@ static int apic_register_read(int index, uint64_t *value)
val = 0 ;
break ;
case 0x0d :
val = s - > log_dest < < 24 ;
if ( is_x2apic_mode ( dev ) ) {
val = s - > extended_log_dest ;
} else {
val = s - > log_dest < < 24 ;
}
break ;
case 0x0e :
val = ( s - > dest_mode < < 28 ) | 0xfffffff ;
if ( is_x2apic_mode ( dev ) ) {
val = 0 ;
ret = - 1 ;
} else {
val = ( s - > dest_mode < < 28 ) | 0xfffffff ;
}
break ;
case 0x0f :
val = s - > spurious_vec ;
@ -764,7 +864,12 @@ static void apic_send_msi(MSIMessage *msi)
{
uint64_t addr = msi - > address ;
uint32_t data = msi - > data ;
uint8_t dest = ( addr & MSI_ADDR_DEST_ID_MASK ) > > MSI_ADDR_DEST_ID_SHIFT ;
uint32_t dest = ( addr & MSI_ADDR_DEST_ID_MASK ) > > MSI_ADDR_DEST_ID_SHIFT ;
/*
* The higher 3 bytes of destination id is stored in higher word of
* msi address . See x86_iommu_irq_to_msi_message ( )
*/
dest = dest | ( addr > > 32 ) ;
uint8_t vector = ( data & MSI_DATA_VECTOR_MASK ) > > MSI_DATA_VECTOR_SHIFT ;
uint8_t dest_mode = ( addr > > MSI_ADDR_DEST_MODE_SHIFT ) & 0x1 ;
uint8_t trigger_mode = ( data > > MSI_DATA_TRIGGER_SHIFT ) & 0x1 ;
@ -788,6 +893,10 @@ static int apic_register_write(int index, uint64_t val)
switch ( index ) {
case 0x02 :
if ( is_x2apic_mode ( dev ) ) {
return - 1 ;
}
s - > id = ( val > > 24 ) ;
break ;
case 0x03 :
@ -807,9 +916,17 @@ static int apic_register_write(int index, uint64_t val)
apic_eoi ( s ) ;
break ;
case 0x0d :
if ( is_x2apic_mode ( dev ) ) {
return - 1 ;
}
s - > log_dest = val > > 24 ;
break ;
case 0x0e :
if ( is_x2apic_mode ( dev ) ) {
return - 1 ;
}
s - > dest_mode = val > > 28 ;
break ;
case 0x0f :
@ -821,13 +938,27 @@ static int apic_register_write(int index, uint64_t val)
case 0x20 . . . 0x27 :
case 0x28 :
break ;
case 0x30 :
case 0x30 : {
uint32_t dest ;
s - > icr [ 0 ] = val ;
apic_deliver ( dev , ( s - > icr [ 1 ] > > 24 ) & 0xff , ( s - > icr [ 0 ] > > 11 ) & 1 ,
if ( is_x2apic_mode ( dev ) ) {
s - > icr [ 1 ] = val > > 32 ;
dest = s - > icr [ 1 ] ;
} else {
dest = ( s - > icr [ 1 ] > > 24 ) & 0xff ;
}
apic_deliver ( dev , dest , ( s - > icr [ 0 ] > > 11 ) & 1 ,
( s - > icr [ 0 ] > > 8 ) & 7 , ( s - > icr [ 0 ] & 0xff ) ,
( s - > icr [ 0 ] > > 15 ) & 1 ) ;
( s - > icr [ 0 ] > > 15 ) & 1 , ( s - > icr [ 0 ] > > 18 ) & 3 ) ;
break ;
}
case 0x31 :
if ( is_x2apic_mode ( dev ) ) {
return - 1 ;
}
s - > icr [ 1 ] = val ;
break ;
case 0x32 . . . 0x37 :
@ -856,6 +987,23 @@ static int apic_register_write(int index, uint64_t val)
s - > count_shift = ( v + 1 ) & 7 ;
}
break ;
case 0x3f : {
int vector = val & 0xff ;
if ( ! is_x2apic_mode ( dev ) ) {
return - 1 ;
}
/*
* Self IPI is identical to IPI with
* - Destination shorthand : 1 ( Self )
* - Trigger mode : 0 ( Edge )
* - Delivery mode : 0 ( Fixed )
*/
apic_deliver ( dev , 0 , 0 , APIC_DM_FIXED , vector , 0 , 1 ) ;
break ;
}
default :
s - > esr | = APIC_ESR_ILLEGAL_ADDRESS ;
return - 1 ;
@ -933,12 +1081,6 @@ static void apic_realize(DeviceState *dev, Error **errp)
{
APICCommonState * s = APIC ( dev ) ;
if ( s - > id > = MAX_APICS ) {
error_setg ( errp , " %s initialization failed. APIC ID %d is invalid " ,
object_get_typename ( OBJECT ( dev ) ) , s - > id ) ;
return ;
}
if ( kvm_enabled ( ) ) {
warn_report ( " Userspace local APIC is deprecated for KVM. " ) ;
warn_report ( " Do not use kernel-irqchip except for the -M isapc machine type. " ) ;
@ -955,7 +1097,16 @@ static void apic_realize(DeviceState *dev, Error **errp)
s - > io_memory . disable_reentrancy_guard = true ;
s - > timer = timer_new_ns ( QEMU_CLOCK_VIRTUAL , apic_timer , s ) ;
local_apics [ s - > id ] = s ;
/*
* The - - machine none does not call apic_set_max_apic_id before creating
* apic , so we need to call it here and set it to 1 which is the max cpus
* in machine none .
*/
if ( ! local_apics ) {
apic_set_max_apic_id ( 1 ) ;
}
local_apics [ s - > initial_apic_id ] = s ;
msi_nonbroken = true ;
}
@ -965,7 +1116,7 @@ static void apic_unrealize(DeviceState *dev)
APICCommonState * s = APIC ( dev ) ;
timer_free ( s - > timer ) ;
local_apics [ s - > id ] = NULL ;
local_apics [ s - > initial_apic_i d ] = NULL ;
}
static void apic_class_init ( ObjectClass * klass , void * data )