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>
# 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>
# 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-02-27 11:07:28 -08:00
unsigned long gp_in_global __asm__ ( " gp " ) ;
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 ( ) ;
local_irq_enable ( ) ;
}
void show_regs ( struct pt_regs * regs )
{
show_regs_print_info ( KERN_DEFAULT ) ;
2019-10-28 13:10:32 +01:00
pr_cont ( " epc: " REG_FMT " ra : " REG_FMT " sp : " REG_FMT " \n " ,
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
}
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 ;
2019-08-14 16:23:52 +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 ;
set_fs ( USER_DS ) ;
}
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-01-02 18:24:11 +01:00
int copy_thread_tls ( 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() */
if ( unlikely ( p - > flags & PF_KTHREAD ) ) {
/* 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 ;
}