2019-05-20 20:55:20 +10:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 - 2019 IBM Corporation .
*/
# define __SANE_USERSPACE_TYPES__
# include <sys/types.h>
# include <stdint.h>
# include <malloc.h>
# include <unistd.h>
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
# include <sys/prctl.h>
# include "utils.h"
# include "../pmu/event.h"
extern void pattern_cache_loop ( void ) ;
extern void indirect_branch_loop ( void ) ;
static int do_count_loop ( struct event * events , bool is_p9 , s64 * miss_percent )
{
u64 pred , mpred ;
prctl ( PR_TASK_PERF_EVENTS_ENABLE ) ;
if ( is_p9 )
pattern_cache_loop ( ) ;
else
indirect_branch_loop ( ) ;
prctl ( PR_TASK_PERF_EVENTS_DISABLE ) ;
event_read ( & events [ 0 ] ) ;
event_read ( & events [ 1 ] ) ;
// We could scale all the events by running/enabled but we're lazy
// As long as the PMU is uncontended they should all run
FAIL_IF ( events [ 0 ] . result . running ! = events [ 0 ] . result . enabled ) ;
FAIL_IF ( events [ 1 ] . result . running ! = events [ 1 ] . result . enabled ) ;
pred = events [ 0 ] . result . value ;
mpred = events [ 1 ] . result . value ;
if ( is_p9 ) {
event_read ( & events [ 2 ] ) ;
event_read ( & events [ 3 ] ) ;
FAIL_IF ( events [ 2 ] . result . running ! = events [ 2 ] . result . enabled ) ;
FAIL_IF ( events [ 3 ] . result . running ! = events [ 3 ] . result . enabled ) ;
pred + = events [ 2 ] . result . value ;
mpred + = events [ 3 ] . result . value ;
}
* miss_percent = 100 * mpred / pred ;
return 0 ;
}
static void setup_event ( struct event * e , u64 config , char * name )
{
event_init_named ( e , config , name ) ;
e - > attr . disabled = 1 ;
e - > attr . exclude_kernel = 1 ;
e - > attr . exclude_hv = 1 ;
e - > attr . exclude_idle = 1 ;
}
enum spectre_v2_state {
VULNERABLE = 0 ,
UNKNOWN = 1 , // Works with FAIL_IF()
NOT_AFFECTED ,
BRANCH_SERIALISATION ,
COUNT_CACHE_DISABLED ,
COUNT_CACHE_FLUSH_SW ,
COUNT_CACHE_FLUSH_HW ,
BTB_FLUSH ,
} ;
static enum spectre_v2_state get_sysfs_state ( void )
{
enum spectre_v2_state state = UNKNOWN ;
char buf [ 256 ] ;
int len ;
memset ( buf , 0 , sizeof ( buf ) ) ;
FAIL_IF ( read_sysfs_file ( " devices/system/cpu/vulnerabilities/spectre_v2 " , buf , sizeof ( buf ) ) ) ;
// Make sure it's NULL terminated
buf [ sizeof ( buf ) - 1 ] = ' \0 ' ;
// Trim the trailing newline
len = strlen ( buf ) ;
FAIL_IF ( len < 1 ) ;
buf [ len - 1 ] = ' \0 ' ;
printf ( " sysfs reports: '%s' \n " , buf ) ;
// Order matters
if ( strstr ( buf , " Vulnerable " ) )
state = VULNERABLE ;
else if ( strstr ( buf , " Not affected " ) )
state = NOT_AFFECTED ;
else if ( strstr ( buf , " Indirect branch serialisation (kernel only) " ) )
state = BRANCH_SERIALISATION ;
else if ( strstr ( buf , " Indirect branch cache disabled " ) )
state = COUNT_CACHE_DISABLED ;
else if ( strstr ( buf , " Software count cache flush (hardware accelerated) " ) )
state = COUNT_CACHE_FLUSH_HW ;
else if ( strstr ( buf , " Software count cache flush " ) )
state = COUNT_CACHE_FLUSH_SW ;
else if ( strstr ( buf , " Branch predictor state flush " ) )
state = BTB_FLUSH ;
return state ;
}
# define PM_BR_PRED_CCACHE 0x040a4 // P8 + P9
# define PM_BR_MPRED_CCACHE 0x040ac // P8 + P9
# define PM_BR_PRED_PCACHE 0x048a0 // P9 only
# define PM_BR_MPRED_PCACHE 0x048b0 // P9 only
int spectre_v2_test ( void )
{
enum spectre_v2_state state ;
struct event events [ 4 ] ;
s64 miss_percent ;
bool is_p9 ;
2020-08-19 11:57:24 +10:00
// The PMU events we use only work on Power8 or later
SKIP_IF ( ! have_hwcap2 ( PPC_FEATURE2_ARCH_2_07 ) ) ;
2019-05-20 20:55:20 +10:00
state = get_sysfs_state ( ) ;
if ( state = = UNKNOWN ) {
printf ( " Error: couldn't determine spectre_v2 mitigation state? \n " ) ;
return - 1 ;
}
memset ( events , 0 , sizeof ( events ) ) ;
setup_event ( & events [ 0 ] , PM_BR_PRED_CCACHE , " PM_BR_PRED_CCACHE " ) ;
setup_event ( & events [ 1 ] , PM_BR_MPRED_CCACHE , " PM_BR_MPRED_CCACHE " ) ;
FAIL_IF ( event_open ( & events [ 0 ] ) ) ;
FAIL_IF ( event_open_with_group ( & events [ 1 ] , events [ 0 ] . fd ) = = - 1 ) ;
is_p9 = ( ( mfspr ( SPRN_PVR ) > > 16 ) & 0xFFFF ) = = 0x4e ;
if ( is_p9 ) {
// Count pattern cache too
setup_event ( & events [ 2 ] , PM_BR_PRED_PCACHE , " PM_BR_PRED_PCACHE " ) ;
setup_event ( & events [ 3 ] , PM_BR_MPRED_PCACHE , " PM_BR_MPRED_PCACHE " ) ;
FAIL_IF ( event_open_with_group ( & events [ 2 ] , events [ 0 ] . fd ) = = - 1 ) ;
FAIL_IF ( event_open_with_group ( & events [ 3 ] , events [ 0 ] . fd ) = = - 1 ) ;
}
FAIL_IF ( do_count_loop ( events , is_p9 , & miss_percent ) ) ;
event_report_justified ( & events [ 0 ] , 18 , 10 ) ;
event_report_justified ( & events [ 1 ] , 18 , 10 ) ;
event_close ( & events [ 0 ] ) ;
event_close ( & events [ 1 ] ) ;
if ( is_p9 ) {
event_report_justified ( & events [ 2 ] , 18 , 10 ) ;
event_report_justified ( & events [ 3 ] , 18 , 10 ) ;
event_close ( & events [ 2 ] ) ;
event_close ( & events [ 3 ] ) ;
}
printf ( " Miss percent %lld %% \n " , miss_percent ) ;
switch ( state ) {
case VULNERABLE :
case NOT_AFFECTED :
case COUNT_CACHE_FLUSH_SW :
case COUNT_CACHE_FLUSH_HW :
// These should all not affect userspace branch prediction
if ( miss_percent > 15 ) {
2021-06-08 16:48:09 +10:00
if ( miss_percent > 95 ) {
/*
* Such a mismatch may be caused by a system being unaware
* the count cache is disabled . This may be to enable
* guest migration between hosts with different settings .
* Return skip code to avoid detecting this as an error .
* We are not vulnerable and reporting otherwise , so
* missing such a mismatch is safe .
*/
printf ( " Branch misses > 95%% unexpected in this configuration. \n " ) ;
printf ( " Count cache likely disabled without Linux knowing. \n " ) ;
if ( state = = COUNT_CACHE_FLUSH_SW )
printf ( " WARNING: Kernel performing unnecessary flushes. \n " ) ;
return 4 ;
}
2019-05-20 20:55:20 +10:00
printf ( " Branch misses > 15%% unexpected in this configuration! \n " ) ;
2022-03-19 23:20:25 +00:00
printf ( " Possible mismatch between reported & actual mitigation \n " ) ;
2020-07-28 12:50:39 -03:00
2019-05-20 20:55:20 +10:00
return 1 ;
}
break ;
case BRANCH_SERIALISATION :
// This seems to affect userspace branch prediction a bit?
if ( miss_percent > 25 ) {
printf ( " Branch misses > 25%% unexpected in this configuration! \n " ) ;
2022-03-19 23:20:25 +00:00
printf ( " Possible mismatch between reported & actual mitigation \n " ) ;
2019-05-20 20:55:20 +10:00
return 1 ;
}
break ;
case COUNT_CACHE_DISABLED :
if ( miss_percent < 95 ) {
2021-06-08 15:48:51 +10:00
printf ( " Branch misses < 95%% unexpected in this configuration! \n " ) ;
2022-03-19 23:20:25 +00:00
printf ( " Possible mismatch between reported & actual mitigation \n " ) ;
2019-05-20 20:55:20 +10:00
return 1 ;
}
break ;
case UNKNOWN :
case BTB_FLUSH :
printf ( " Not sure! \n " ) ;
return 1 ;
}
printf ( " OK - Measured branch prediction rates match reported spectre v2 mitigation. \n " ) ;
return 0 ;
}
int main ( int argc , char * argv [ ] )
{
return test_harness ( spectre_v2_test , " spectre_v2 " ) ;
}