2019-05-23 11:14:57 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-07-10 18:04:30 -07:00
/*
* Copyright ( C ) 2009 Sunplus Core Technology Co . , Ltd .
* Chen Liqin < liqin . chen @ sunplusct . com >
* Lennox Wu < lennox . wu @ sunplusct . com >
* Copyright ( C ) 2012 Regents of the University of California
* Copyright ( C ) 2017 SiFive
*/
2019-10-17 15:21:28 -07:00
# include <linux/cpu.h>
2017-07-10 18:04:30 -07:00
# include <linux/kernel.h>
# include <linux/sched.h>
2021-03-05 19:33:30 +08:00
# include <linux/sched/debug.h>
2017-07-10 18:04:30 -07:00
# include <linux/sched/task_stack.h>
# include <linux/tick.h>
# include <linux/ptrace.h>
2017-12-04 18:01:01 -05:00
# include <linux/uaccess.h>
2017-07-10 18:04:30 -07:00
# include <asm/unistd.h>
# include <asm/processor.h>
# include <asm/csr.h>
2021-01-11 20:40:12 +08:00
# include <asm/stacktrace.h>
2017-07-10 18:04:30 -07:00
# include <asm/string.h>
# include <asm/switch_to.h>
2019-10-17 15:21:28 -07:00
# include <asm/thread_info.h>
2017-07-10 18:04:30 -07:00
2020-05-21 13:28:26 -07:00
register unsigned long gp_in_global __asm__ ( " gp " ) ;
2020-02-27 11:07:28 -08:00
riscv: Enable per-task stack canaries
This enables the use of per-task stack canary values if GCC has
support for emitting the stack canary reference relative to the
value of tp, which holds the task struct pointer in the riscv
kernel.
After compare arm64 and x86 implementations, seems arm64's is more
flexible and readable. The key point is how gcc get the offset of
stack_canary from gs/el0_sp.
x86: Use a fix offset from gs, not flexible.
struct fixed_percpu_data {
/*
* GCC hardcodes the stack canary as %gs:40. Since the
* irq_stack is the object at %gs:0, we reserve the bottom
* 48 bytes of the irq stack for the canary.
*/
char gs_base[40]; // :(
unsigned long stack_canary;
};
arm64: Use -mstack-protector-guard-offset & guard-reg
gcc options:
-mstack-protector-guard=sysreg
-mstack-protector-guard-reg=sp_el0
-mstack-protector-guard-offset=xxx
riscv: Use -mstack-protector-guard-offset & guard-reg
gcc options:
-mstack-protector-guard=tls
-mstack-protector-guard-reg=tp
-mstack-protector-guard-offset=xxx
GCC's implementation has been merged:
commit c931e8d5a96463427040b0d11f9c4352ac22b2b0
Author: Cooper Qu <cooper.qu@linux.alibaba.com>
Date: Mon Jul 13 16:15:08 2020 +0800
RISC-V: Add support for TLS stack protector canary access
In the end, these codes are inserted by gcc before return:
* 0xffffffe00020b396 <+120>: ld a5,1008(tp) # 0x3f0
* 0xffffffe00020b39a <+124>: xor a5,a5,a4
* 0xffffffe00020b39c <+126>: mv a0,s5
* 0xffffffe00020b39e <+128>: bnez a5,0xffffffe00020b61c <_do_fork+766>
0xffffffe00020b3a2 <+132>: ld ra,136(sp)
0xffffffe00020b3a4 <+134>: ld s0,128(sp)
0xffffffe00020b3a6 <+136>: ld s1,120(sp)
0xffffffe00020b3a8 <+138>: ld s2,112(sp)
0xffffffe00020b3aa <+140>: ld s3,104(sp)
0xffffffe00020b3ac <+142>: ld s4,96(sp)
0xffffffe00020b3ae <+144>: ld s5,88(sp)
0xffffffe00020b3b0 <+146>: ld s6,80(sp)
0xffffffe00020b3b2 <+148>: ld s7,72(sp)
0xffffffe00020b3b4 <+150>: addi sp,sp,144
0xffffffe00020b3b6 <+152>: ret
...
* 0xffffffe00020b61c <+766>: auipc ra,0x7f8
* 0xffffffe00020b620 <+770>: jalr -1764(ra) # 0xffffffe000a02f38 <__stack_chk_fail>
Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Signed-off-by: Cooper Qu <cooper.qu@linux.alibaba.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>
2020-12-17 16:29:18 +00:00
# if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
2020-07-10 16:19:57 +00:00
# include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly ;
EXPORT_SYMBOL ( __stack_chk_guard ) ;
# endif
2017-07-10 18:04:30 -07:00
extern asmlinkage void ret_from_fork ( void ) ;
extern asmlinkage void ret_from_kernel_thread ( void ) ;
void arch_cpu_idle ( void )
{
wait_for_interrupt ( ) ;
2020-11-20 11:50:35 +01:00
raw_local_irq_enable ( ) ;
2017-07-10 18:04:30 -07:00
}
2021-01-11 20:40:12 +08:00
void __show_regs ( struct pt_regs * regs )
2017-07-10 18:04:30 -07:00
{
show_regs_print_info ( KERN_DEFAULT ) ;
2021-01-11 20:40:13 +08:00
if ( ! user_mode ( regs ) ) {
pr_cont ( " epc : %pS \n " , ( void * ) regs - > epc ) ;
pr_cont ( " ra : %pS \n " , ( void * ) regs - > ra ) ;
}
pr_cont ( " epc : " REG_FMT " ra : " REG_FMT " sp : " REG_FMT " \n " ,
2019-10-28 13:10:32 +01:00
regs - > epc , regs - > ra , regs - > sp ) ;
2017-07-10 18:04:30 -07:00
pr_cont ( " gp : " REG_FMT " tp : " REG_FMT " t0 : " REG_FMT " \n " ,
regs - > gp , regs - > tp , regs - > t0 ) ;
pr_cont ( " t1 : " REG_FMT " t2 : " REG_FMT " s0 : " REG_FMT " \n " ,
regs - > t1 , regs - > t2 , regs - > s0 ) ;
pr_cont ( " s1 : " REG_FMT " a0 : " REG_FMT " a1 : " REG_FMT " \n " ,
regs - > s1 , regs - > a0 , regs - > a1 ) ;
pr_cont ( " a2 : " REG_FMT " a3 : " REG_FMT " a4 : " REG_FMT " \n " ,
regs - > a2 , regs - > a3 , regs - > a4 ) ;
pr_cont ( " a5 : " REG_FMT " a6 : " REG_FMT " a7 : " REG_FMT " \n " ,
regs - > a5 , regs - > a6 , regs - > a7 ) ;
pr_cont ( " s2 : " REG_FMT " s3 : " REG_FMT " s4 : " REG_FMT " \n " ,
regs - > s2 , regs - > s3 , regs - > s4 ) ;
pr_cont ( " s5 : " REG_FMT " s6 : " REG_FMT " s7 : " REG_FMT " \n " ,
regs - > s5 , regs - > s6 , regs - > s7 ) ;
pr_cont ( " s8 : " REG_FMT " s9 : " REG_FMT " s10: " REG_FMT " \n " ,
regs - > s8 , regs - > s9 , regs - > s10 ) ;
pr_cont ( " s11: " REG_FMT " t3 : " REG_FMT " t4 : " REG_FMT " \n " ,
regs - > s11 , regs - > t3 , regs - > t4 ) ;
pr_cont ( " t5 : " REG_FMT " t6 : " REG_FMT " \n " ,
regs - > t5 , regs - > t6 ) ;
2019-10-28 13:10:32 +01:00
pr_cont ( " status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT " \n " ,
regs - > status , regs - > badaddr , regs - > cause ) ;
2017-07-10 18:04:30 -07:00
}
2021-01-11 20:40:12 +08:00
void show_regs ( struct pt_regs * regs )
{
__show_regs ( regs ) ;
if ( ! user_mode ( regs ) )
dump_backtrace ( regs , NULL , KERN_DEFAULT ) ;
}
2017-07-10 18:04:30 -07:00
void start_thread ( struct pt_regs * regs , unsigned long pc ,
unsigned long sp )
{
2019-10-28 13:10:32 +01:00
regs - > status = SR_PIE ;
2021-05-12 22:55:45 +08:00
if ( has_fpu ( ) ) {
2019-10-28 13:10:32 +01:00
regs - > status | = SR_FS_INITIAL ;
2019-08-14 16:23:52 +08:00
/*
* Restore the initial value to the FP register
* before starting the user program .
*/
fstate_restore ( current , regs ) ;
}
2019-10-28 13:10:32 +01:00
regs - > epc = pc ;
2017-07-10 18:04:30 -07:00
regs - > sp = sp ;
}
void flush_thread ( void )
{
2018-10-09 10:18:33 +08:00
# ifdef CONFIG_FPU
2017-07-10 18:04:30 -07:00
/*
2019-08-14 16:23:52 +08:00
* Reset FPU state and context
2017-07-10 18:04:30 -07:00
* frm : round to nearest , ties to even ( IEEE default )
* fflags : accrued exceptions cleared
*/
2019-08-14 16:23:52 +08:00
fstate_off ( current , task_pt_regs ( current ) ) ;
2017-07-10 18:04:30 -07:00
memset ( & current - > thread . fstate , 0 , sizeof ( current - > thread . fstate ) ) ;
2018-10-09 10:18:33 +08:00
# endif
2017-07-10 18:04:30 -07:00
}
int arch_dup_task_struct ( struct task_struct * dst , struct task_struct * src )
{
fstate_save ( src , task_pt_regs ( src ) ) ;
* dst = * src ;
return 0 ;
}
2020-06-11 11:04:15 +02:00
int copy_thread ( unsigned long clone_flags , unsigned long usp , unsigned long arg ,
struct task_struct * p , unsigned long tls )
2017-07-10 18:04:30 -07:00
{
struct pt_regs * childregs = task_pt_regs ( p ) ;
/* p->thread holds context to be restored by __switch_to() */
2021-02-17 08:48:00 -07:00
if ( unlikely ( p - > flags & ( PF_KTHREAD | PF_IO_WORKER ) ) ) {
2017-07-10 18:04:30 -07:00
/* Kernel thread */
memset ( childregs , 0 , sizeof ( struct pt_regs ) ) ;
2020-02-27 11:07:28 -08:00
childregs - > gp = gp_in_global ;
2019-10-28 13:10:32 +01:00
/* Supervisor/Machine, irqs on: */
childregs - > status = SR_PP | SR_PIE ;
2017-07-10 18:04:30 -07:00
p - > thread . ra = ( unsigned long ) ret_from_kernel_thread ;
p - > thread . s [ 0 ] = usp ; /* fn */
p - > thread . s [ 1 ] = arg ;
} else {
* childregs = * ( current_pt_regs ( ) ) ;
if ( usp ) /* User fork */
childregs - > sp = usp ;
if ( clone_flags & CLONE_SETTLS )
2020-01-02 18:24:11 +01:00
childregs - > tp = tls ;
2017-07-10 18:04:30 -07:00
childregs - > a0 = 0 ; /* Return value of fork() */
p - > thread . ra = ( unsigned long ) ret_from_fork ;
}
p - > thread . sp = ( unsigned long ) childregs ; /* kernel sp */
return 0 ;
}