18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2009 Sunplus Core Technology Co., Ltd. 48c2ecf20Sopenharmony_ci * Chen Liqin <liqin.chen@sunplusct.com> 58c2ecf20Sopenharmony_ci * Lennox Wu <lennox.wu@sunplusct.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Regents of the University of California 78c2ecf20Sopenharmony_ci * Copyright (C) 2017 SiFive 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/cpu.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 148c2ecf20Sopenharmony_ci#include <linux/tick.h> 158c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 168c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/unistd.h> 198c2ecf20Sopenharmony_ci#include <asm/processor.h> 208c2ecf20Sopenharmony_ci#include <asm/csr.h> 218c2ecf20Sopenharmony_ci#include <asm/string.h> 228c2ecf20Sopenharmony_ci#include <asm/switch_to.h> 238c2ecf20Sopenharmony_ci#include <asm/thread_info.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciregister unsigned long gp_in_global __asm__("gp"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifdef CONFIG_STACKPROTECTOR 288c2ecf20Sopenharmony_ci#include <linux/stackprotector.h> 298c2ecf20Sopenharmony_ciunsigned long __stack_chk_guard __read_mostly; 308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__stack_chk_guard); 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciextern asmlinkage void ret_from_fork(void); 348c2ecf20Sopenharmony_ciextern asmlinkage void ret_from_kernel_thread(void); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_civoid arch_cpu_idle(void) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci wait_for_interrupt(); 398c2ecf20Sopenharmony_ci raw_local_irq_enable(); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid show_regs(struct pt_regs *regs) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci show_regs_print_info(KERN_DEFAULT); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci pr_cont("epc: " REG_FMT " ra : " REG_FMT " sp : " REG_FMT "\n", 478c2ecf20Sopenharmony_ci regs->epc, regs->ra, regs->sp); 488c2ecf20Sopenharmony_ci pr_cont(" gp : " REG_FMT " tp : " REG_FMT " t0 : " REG_FMT "\n", 498c2ecf20Sopenharmony_ci regs->gp, regs->tp, regs->t0); 508c2ecf20Sopenharmony_ci pr_cont(" t1 : " REG_FMT " t2 : " REG_FMT " s0 : " REG_FMT "\n", 518c2ecf20Sopenharmony_ci regs->t1, regs->t2, regs->s0); 528c2ecf20Sopenharmony_ci pr_cont(" s1 : " REG_FMT " a0 : " REG_FMT " a1 : " REG_FMT "\n", 538c2ecf20Sopenharmony_ci regs->s1, regs->a0, regs->a1); 548c2ecf20Sopenharmony_ci pr_cont(" a2 : " REG_FMT " a3 : " REG_FMT " a4 : " REG_FMT "\n", 558c2ecf20Sopenharmony_ci regs->a2, regs->a3, regs->a4); 568c2ecf20Sopenharmony_ci pr_cont(" a5 : " REG_FMT " a6 : " REG_FMT " a7 : " REG_FMT "\n", 578c2ecf20Sopenharmony_ci regs->a5, regs->a6, regs->a7); 588c2ecf20Sopenharmony_ci pr_cont(" s2 : " REG_FMT " s3 : " REG_FMT " s4 : " REG_FMT "\n", 598c2ecf20Sopenharmony_ci regs->s2, regs->s3, regs->s4); 608c2ecf20Sopenharmony_ci pr_cont(" s5 : " REG_FMT " s6 : " REG_FMT " s7 : " REG_FMT "\n", 618c2ecf20Sopenharmony_ci regs->s5, regs->s6, regs->s7); 628c2ecf20Sopenharmony_ci pr_cont(" s8 : " REG_FMT " s9 : " REG_FMT " s10: " REG_FMT "\n", 638c2ecf20Sopenharmony_ci regs->s8, regs->s9, regs->s10); 648c2ecf20Sopenharmony_ci pr_cont(" s11: " REG_FMT " t3 : " REG_FMT " t4 : " REG_FMT "\n", 658c2ecf20Sopenharmony_ci regs->s11, regs->t3, regs->t4); 668c2ecf20Sopenharmony_ci pr_cont(" t5 : " REG_FMT " t6 : " REG_FMT "\n", 678c2ecf20Sopenharmony_ci regs->t5, regs->t6); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT "\n", 708c2ecf20Sopenharmony_ci regs->status, regs->badaddr, regs->cause); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_civoid start_thread(struct pt_regs *regs, unsigned long pc, 748c2ecf20Sopenharmony_ci unsigned long sp) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci regs->status = SR_PIE; 778c2ecf20Sopenharmony_ci if (has_fpu) { 788c2ecf20Sopenharmony_ci regs->status |= SR_FS_INITIAL; 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Restore the initial value to the FP register 818c2ecf20Sopenharmony_ci * before starting the user program. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci fstate_restore(current, regs); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci regs->epc = pc; 868c2ecf20Sopenharmony_ci regs->sp = sp; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid flush_thread(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci#ifdef CONFIG_FPU 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * Reset FPU state and context 948c2ecf20Sopenharmony_ci * frm: round to nearest, ties to even (IEEE default) 958c2ecf20Sopenharmony_ci * fflags: accrued exceptions cleared 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci fstate_off(current, task_pt_regs(current)); 988c2ecf20Sopenharmony_ci memset(¤t->thread.fstate, 0, sizeof(current->thread.fstate)); 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciint arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci fstate_save(src, task_pt_regs(src)); 1058c2ecf20Sopenharmony_ci *dst = *src; 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciint copy_thread(unsigned long clone_flags, unsigned long usp, unsigned long arg, 1108c2ecf20Sopenharmony_ci struct task_struct *p, unsigned long tls) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct pt_regs *childregs = task_pt_regs(p); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci memset(&p->thread.s, 0, sizeof(p->thread.s)); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* p->thread holds context to be restored by __switch_to() */ 1178c2ecf20Sopenharmony_ci if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) { 1188c2ecf20Sopenharmony_ci /* Kernel thread */ 1198c2ecf20Sopenharmony_ci memset(childregs, 0, sizeof(struct pt_regs)); 1208c2ecf20Sopenharmony_ci childregs->gp = gp_in_global; 1218c2ecf20Sopenharmony_ci /* Supervisor/Machine, irqs on: */ 1228c2ecf20Sopenharmony_ci childregs->status = SR_PP | SR_PIE; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci p->thread.ra = (unsigned long)ret_from_kernel_thread; 1258c2ecf20Sopenharmony_ci p->thread.s[0] = usp; /* fn */ 1268c2ecf20Sopenharmony_ci p->thread.s[1] = arg; 1278c2ecf20Sopenharmony_ci } else { 1288c2ecf20Sopenharmony_ci *childregs = *(current_pt_regs()); 1298c2ecf20Sopenharmony_ci if (usp) /* User fork */ 1308c2ecf20Sopenharmony_ci childregs->sp = usp; 1318c2ecf20Sopenharmony_ci if (clone_flags & CLONE_SETTLS) 1328c2ecf20Sopenharmony_ci childregs->tp = tls; 1338c2ecf20Sopenharmony_ci childregs->a0 = 0; /* Return value of fork() */ 1348c2ecf20Sopenharmony_ci p->thread.ra = (unsigned long)ret_from_fork; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci p->thread.sp = (unsigned long)childregs; /* kernel sp */ 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 139