2018-05-29 15:11:14 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2018 ARM Ltd , All Rights Reserved .
*/
2018-08-07 15:47:06 +03:00
# include <linux/compat.h>
2018-05-29 15:11:14 +03:00
# include <linux/errno.h>
2019-06-17 16:22:21 +03:00
# include <linux/prctl.h>
2018-05-29 15:11:14 +03:00
# include <linux/sched.h>
2018-08-07 15:47:06 +03:00
# include <linux/sched/task_stack.h>
2018-05-29 15:11:14 +03:00
# include <linux/thread_info.h>
# include <asm/cpufeature.h>
2018-08-07 15:47:06 +03:00
static void ssbd_ssbs_enable ( struct task_struct * task )
{
u64 val = is_compat_thread ( task_thread_info ( task ) ) ?
PSR_AA32_SSBS_BIT : PSR_SSBS_BIT ;
task_pt_regs ( task ) - > pstate | = val ;
}
static void ssbd_ssbs_disable ( struct task_struct * task )
{
u64 val = is_compat_thread ( task_thread_info ( task ) ) ?
PSR_AA32_SSBS_BIT : PSR_SSBS_BIT ;
task_pt_regs ( task ) - > pstate & = ~ val ;
}
2018-05-29 15:11:14 +03:00
/*
* prctl interface for SSBD
*/
static int ssbd_prctl_set ( struct task_struct * task , unsigned long ctrl )
{
int state = arm64_get_ssbd_state ( ) ;
/* Unsupported */
if ( state = = ARM64_SSBD_UNKNOWN )
2019-12-17 01:53:47 +03:00
return - ENODEV ;
2018-05-29 15:11:14 +03:00
/* Treat the unaffected/mitigated state separately */
if ( state = = ARM64_SSBD_MITIGATED ) {
switch ( ctrl ) {
case PR_SPEC_ENABLE :
return - EPERM ;
case PR_SPEC_DISABLE :
case PR_SPEC_FORCE_DISABLE :
return 0 ;
}
}
/*
* Things are a bit backward here : the arm64 internal API
* * enables the mitigation * when the userspace API * disables
* speculation * . So much fun .
*/
switch ( ctrl ) {
case PR_SPEC_ENABLE :
/* If speculation is force disabled, enable is not allowed */
if ( state = = ARM64_SSBD_FORCE_ENABLE | |
task_spec_ssb_force_disable ( task ) )
return - EPERM ;
task_clear_spec_ssb_disable ( task ) ;
clear_tsk_thread_flag ( task , TIF_SSBD ) ;
2018-08-07 15:47:06 +03:00
ssbd_ssbs_enable ( task ) ;
2018-05-29 15:11:14 +03:00
break ;
case PR_SPEC_DISABLE :
if ( state = = ARM64_SSBD_FORCE_DISABLE )
return - EPERM ;
task_set_spec_ssb_disable ( task ) ;
set_tsk_thread_flag ( task , TIF_SSBD ) ;
2018-08-07 15:47:06 +03:00
ssbd_ssbs_disable ( task ) ;
2018-05-29 15:11:14 +03:00
break ;
case PR_SPEC_FORCE_DISABLE :
if ( state = = ARM64_SSBD_FORCE_DISABLE )
return - EPERM ;
task_set_spec_ssb_disable ( task ) ;
task_set_spec_ssb_force_disable ( task ) ;
set_tsk_thread_flag ( task , TIF_SSBD ) ;
2018-08-07 15:47:06 +03:00
ssbd_ssbs_disable ( task ) ;
2018-05-29 15:11:14 +03:00
break ;
default :
return - ERANGE ;
}
return 0 ;
}
int arch_prctl_spec_ctrl_set ( struct task_struct * task , unsigned long which ,
unsigned long ctrl )
{
switch ( which ) {
case PR_SPEC_STORE_BYPASS :
return ssbd_prctl_set ( task , ctrl ) ;
default :
return - ENODEV ;
}
}
static int ssbd_prctl_get ( struct task_struct * task )
{
switch ( arm64_get_ssbd_state ( ) ) {
case ARM64_SSBD_UNKNOWN :
2019-12-17 01:53:47 +03:00
return - ENODEV ;
2018-05-29 15:11:14 +03:00
case ARM64_SSBD_FORCE_ENABLE :
return PR_SPEC_DISABLE ;
case ARM64_SSBD_KERNEL :
if ( task_spec_ssb_force_disable ( task ) )
return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE ;
if ( task_spec_ssb_disable ( task ) )
return PR_SPEC_PRCTL | PR_SPEC_DISABLE ;
return PR_SPEC_PRCTL | PR_SPEC_ENABLE ;
case ARM64_SSBD_FORCE_DISABLE :
return PR_SPEC_ENABLE ;
default :
return PR_SPEC_NOT_AFFECTED ;
}
}
int arch_prctl_spec_ctrl_get ( struct task_struct * task , unsigned long which )
{
switch ( which ) {
case PR_SPEC_STORE_BYPASS :
return ssbd_prctl_get ( task ) ;
default :
return - ENODEV ;
}
}