2018-01-26 14:46:47 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/module.h>
2018-03-21 11:38:21 +03:00
# include <linux/device.h>
2019-04-12 23:39:31 +03:00
# include <linux/cpu.h>
2018-01-26 14:46:47 +03:00
# include <asm/nospec-branch.h>
2018-03-23 19:09:39 +03:00
static int __init nobp_setup_early ( char * str )
{
bool enabled ;
int rc ;
rc = kstrtobool ( str , & enabled ) ;
if ( rc )
return rc ;
2018-03-23 15:04:49 +03:00
if ( enabled & & test_facility ( 82 ) ) {
/*
2023-06-28 17:23:20 +03:00
* The user explicitly requested nobp = 1 , enable it and
2018-03-23 15:04:49 +03:00
* disable the expoline support .
*/
2021-05-05 23:01:10 +03:00
__set_facility ( 82 , alt_stfle_fac_list ) ;
2018-03-23 15:04:49 +03:00
if ( IS_ENABLED ( CONFIG_EXPOLINE ) )
nospec_disable = 1 ;
} else {
2021-05-05 23:01:10 +03:00
__clear_facility ( 82 , alt_stfle_fac_list ) ;
2018-03-23 15:04:49 +03:00
}
2018-03-23 19:09:39 +03:00
return 0 ;
}
early_param ( " nobp " , nobp_setup_early ) ;
static int __init nospec_setup_early ( char * str )
{
2021-05-05 23:01:10 +03:00
__clear_facility ( 82 , alt_stfle_fac_list ) ;
2018-03-23 19:09:39 +03:00
return 0 ;
}
early_param ( " nospec " , nospec_setup_early ) ;
2018-03-20 18:33:43 +03:00
static int __init nospec_report ( void )
{
2018-05-08 16:14:48 +03:00
if ( test_facility ( 156 ) )
pr_info ( " Spectre V2 mitigation: etokens \n " ) ;
2021-10-04 09:51:06 +03:00
if ( nospec_uses_trampoline ( ) )
2018-05-09 10:47:53 +03:00
pr_info ( " Spectre V2 mitigation: execute trampolines \n " ) ;
2021-05-05 23:01:10 +03:00
if ( __test_facility ( 82 , alt_stfle_fac_list ) )
2018-05-09 10:47:53 +03:00
pr_info ( " Spectre V2 mitigation: limited branch prediction \n " ) ;
2018-03-20 18:33:43 +03:00
return 0 ;
}
arch_initcall ( nospec_report ) ;
2018-03-23 19:09:39 +03:00
# ifdef CONFIG_EXPOLINE
2018-03-23 15:04:49 +03:00
int nospec_disable = IS_ENABLED ( CONFIG_EXPOLINE_OFF ) ;
2018-01-26 14:46:47 +03:00
static int __init nospectre_v2_setup_early ( char * str )
{
2018-03-23 15:04:49 +03:00
nospec_disable = 1 ;
2018-01-26 14:46:47 +03:00
return 0 ;
}
early_param ( " nospectre_v2 " , nospectre_v2_setup_early ) ;
2018-04-11 09:35:23 +03:00
void __init nospec_auto_detect ( void )
2018-03-23 15:04:49 +03:00
{
2019-04-12 23:39:31 +03:00
if ( test_facility ( 156 ) | | cpu_mitigations_off ( ) ) {
2018-05-08 16:14:48 +03:00
/*
* The machine supports etokens .
* Disable expolines and disable nobp .
*/
2019-04-09 19:33:12 +03:00
if ( __is_defined ( CC_USING_EXPOLINE ) )
2018-05-08 16:14:48 +03:00
nospec_disable = 1 ;
2021-05-05 23:01:10 +03:00
__clear_facility ( 82 , alt_stfle_fac_list ) ;
2019-04-09 19:33:12 +03:00
} else if ( __is_defined ( CC_USING_EXPOLINE ) ) {
2018-03-23 15:04:49 +03:00
/*
* The kernel has been compiled with expolines .
* Keep expolines enabled and disable nobp .
*/
nospec_disable = 0 ;
2021-05-05 23:01:10 +03:00
__clear_facility ( 82 , alt_stfle_fac_list ) ;
2018-03-23 15:04:49 +03:00
}
/*
* If the kernel has not been compiled with expolines the
* nobp setting decides what is done , this depends on the
* CONFIG_KERNEL_NP option and the nobp / nospec parameters .
*/
}
2018-01-26 14:46:47 +03:00
static int __init spectre_v2_setup_early ( char * str )
{
if ( str & & ! strncmp ( str , " on " , 2 ) ) {
2018-03-23 15:04:49 +03:00
nospec_disable = 0 ;
2021-05-05 23:01:10 +03:00
__clear_facility ( 82 , alt_stfle_fac_list ) ;
2018-01-26 14:46:47 +03:00
}
2018-03-23 15:04:49 +03:00
if ( str & & ! strncmp ( str , " off " , 3 ) )
nospec_disable = 1 ;
if ( str & & ! strncmp ( str , " auto " , 4 ) )
2018-04-11 09:35:23 +03:00
nospec_auto_detect ( ) ;
2018-01-26 14:46:47 +03:00
return 0 ;
}
early_param ( " spectre_v2 " , spectre_v2_setup_early ) ;
static void __init_or_module __nospec_revert ( s32 * start , s32 * end )
{
enum { BRCL_EXPOLINE , BRASL_EXPOLINE } type ;
2021-06-16 23:18:23 +03:00
static const u8 branch [ ] = { 0x47 , 0x00 , 0x07 , 0x00 } ;
2018-01-26 14:46:47 +03:00
u8 * instr , * thunk , * br ;
u8 insnbuf [ 6 ] ;
s32 * epo ;
/* Second part of the instruction replace is always a nop */
2022-02-24 18:43:23 +03:00
memcpy ( insnbuf + 2 , branch , sizeof ( branch ) ) ;
2018-01-26 14:46:47 +03:00
for ( epo = start ; epo < end ; epo + + ) {
instr = ( u8 * ) epo + * epo ;
if ( instr [ 0 ] = = 0xc0 & & ( instr [ 1 ] & 0x0f ) = = 0x04 )
type = BRCL_EXPOLINE ; /* brcl instruction */
else if ( instr [ 0 ] = = 0xc0 & & ( instr [ 1 ] & 0x0f ) = = 0x05 )
type = BRASL_EXPOLINE ; /* brasl instruction */
else
continue ;
thunk = instr + ( * ( int * ) ( instr + 2 ) ) * 2 ;
if ( thunk [ 0 ] = = 0xc6 & & thunk [ 1 ] = = 0x00 )
/* exrl %r0,<target-br> */
br = thunk + ( * ( int * ) ( thunk + 2 ) ) * 2 ;
else
continue ;
2022-02-24 18:43:23 +03:00
if ( br [ 0 ] ! = 0x07 | | ( br [ 1 ] & 0xf0 ) ! = 0xf0 )
2018-01-26 14:46:47 +03:00
continue ;
switch ( type ) {
case BRCL_EXPOLINE :
2022-02-24 18:43:23 +03:00
/* brcl to thunk, replace with br + nop */
2018-01-26 14:46:47 +03:00
insnbuf [ 0 ] = br [ 0 ] ;
insnbuf [ 1 ] = ( instr [ 1 ] & 0xf0 ) | ( br [ 1 ] & 0x0f ) ;
break ;
case BRASL_EXPOLINE :
2022-02-24 18:43:23 +03:00
/* brasl to thunk, replace with basr + nop */
insnbuf [ 0 ] = 0x0d ;
2018-01-26 14:46:47 +03:00
insnbuf [ 1 ] = ( instr [ 1 ] & 0xf0 ) | ( br [ 1 ] & 0x0f ) ;
break ;
}
s390_kernel_write ( instr , insnbuf , 6 ) ;
}
}
2018-03-23 15:04:49 +03:00
void __init_or_module nospec_revert ( s32 * start , s32 * end )
2018-01-26 14:46:47 +03:00
{
2018-03-23 15:04:49 +03:00
if ( nospec_disable )
2018-01-26 14:46:47 +03:00
__nospec_revert ( start , end ) ;
}
extern s32 __nospec_call_start [ ] , __nospec_call_end [ ] ;
extern s32 __nospec_return_start [ ] , __nospec_return_end [ ] ;
void __init nospec_init_branches ( void )
{
2018-03-23 15:04:49 +03:00
nospec_revert ( __nospec_call_start , __nospec_call_end ) ;
nospec_revert ( __nospec_return_start , __nospec_return_end ) ;
2018-01-26 14:46:47 +03:00
}
2018-03-23 19:09:39 +03:00
# endif /* CONFIG_EXPOLINE */