1588cb88cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 27db91e57SPalmer Dabbelt /* 37db91e57SPalmer Dabbelt * Copyright (C) 2009 Sunplus Core Technology Co., Ltd. 47db91e57SPalmer Dabbelt * Chen Liqin <[email protected]> 57db91e57SPalmer Dabbelt * Lennox Wu <[email protected]> 67db91e57SPalmer Dabbelt * Copyright (C) 2012 Regents of the University of California 77db91e57SPalmer Dabbelt * Copyright (C) 2017 SiFive 87db91e57SPalmer Dabbelt */ 97db91e57SPalmer Dabbelt 10*09d6775fSSamuel Holland #include <linux/bitfield.h> 115ed881bcSPaul Walmsley #include <linux/cpu.h> 127db91e57SPalmer Dabbelt #include <linux/kernel.h> 137db91e57SPalmer Dabbelt #include <linux/sched.h> 1486b276c1SNanyong Sun #include <linux/sched/debug.h> 157db91e57SPalmer Dabbelt #include <linux/sched/task_stack.h> 167db91e57SPalmer Dabbelt #include <linux/tick.h> 177db91e57SPalmer Dabbelt #include <linux/ptrace.h> 185e454b54SAl Viro #include <linux/uaccess.h> 19048e2906SYunhui Cui #include <linux/personality.h> 207db91e57SPalmer Dabbelt 217db91e57SPalmer Dabbelt #include <asm/unistd.h> 227db91e57SPalmer Dabbelt #include <asm/processor.h> 237db91e57SPalmer Dabbelt #include <asm/csr.h> 24091b9450SKefeng Wang #include <asm/stacktrace.h> 257db91e57SPalmer Dabbelt #include <asm/string.h> 267db91e57SPalmer Dabbelt #include <asm/switch_to.h> 275ed881bcSPaul Walmsley #include <asm/thread_info.h> 28f6e64b66SAnup Patel #include <asm/cpuidle.h> 293a2df632SGreentime Hu #include <asm/vector.h> 309f23a5d2SClément Léger #include <asm/cpufeature.h> 31048e2906SYunhui Cui #include <asm/exec.h> 327db91e57SPalmer Dabbelt 33fea2fed2SGuo Ren #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK) 34f2c9699fSGuo Ren #include <linux/stackprotector.h> 35f2c9699fSGuo Ren unsigned long __stack_chk_guard __read_mostly; 36f2c9699fSGuo Ren EXPORT_SYMBOL(__stack_chk_guard); 37f2c9699fSGuo Ren #endif 38f2c9699fSGuo Ren 397db91e57SPalmer Dabbelt extern asmlinkage void ret_from_fork(void); 407db91e57SPalmer Dabbelt 418a48ea87SAlexandre Ghiti void noinstr arch_cpu_idle(void) 427db91e57SPalmer Dabbelt { 43f6e64b66SAnup Patel cpu_do_idle(); 447db91e57SPalmer Dabbelt } 457db91e57SPalmer Dabbelt 469f23a5d2SClément Léger int set_unalign_ctl(struct task_struct *tsk, unsigned int val) 479f23a5d2SClément Léger { 489f23a5d2SClément Léger if (!unaligned_ctl_available()) 499f23a5d2SClément Léger return -EINVAL; 509f23a5d2SClément Léger 519f23a5d2SClément Léger tsk->thread.align_ctl = val; 529f23a5d2SClément Léger return 0; 539f23a5d2SClément Léger } 549f23a5d2SClément Léger 559f23a5d2SClément Léger int get_unalign_ctl(struct task_struct *tsk, unsigned long adr) 569f23a5d2SClément Léger { 579f23a5d2SClément Léger if (!unaligned_ctl_available()) 589f23a5d2SClément Léger return -EINVAL; 599f23a5d2SClément Léger 609f23a5d2SClément Léger return put_user(tsk->thread.align_ctl, (unsigned long __user *)adr); 619f23a5d2SClément Léger } 629f23a5d2SClément Léger 63091b9450SKefeng Wang void __show_regs(struct pt_regs *regs) 647db91e57SPalmer Dabbelt { 657db91e57SPalmer Dabbelt show_regs_print_info(KERN_DEFAULT); 667db91e57SPalmer Dabbelt 67da401e89SKefeng Wang if (!user_mode(regs)) { 68da401e89SKefeng Wang pr_cont("epc : %pS\n", (void *)regs->epc); 69da401e89SKefeng Wang pr_cont(" ra : %pS\n", (void *)regs->ra); 70da401e89SKefeng Wang } 71da401e89SKefeng Wang 72a4c3733dSChristoph Hellwig pr_cont("epc : " REG_FMT " ra : " REG_FMT " sp : " REG_FMT "\n", 73a4c3733dSChristoph Hellwig regs->epc, regs->ra, regs->sp); 747db91e57SPalmer Dabbelt pr_cont(" gp : " REG_FMT " tp : " REG_FMT " t0 : " REG_FMT "\n", 757db91e57SPalmer Dabbelt regs->gp, regs->tp, regs->t0); 767db91e57SPalmer Dabbelt pr_cont(" t1 : " REG_FMT " t2 : " REG_FMT " s0 : " REG_FMT "\n", 777db91e57SPalmer Dabbelt regs->t1, regs->t2, regs->s0); 787db91e57SPalmer Dabbelt pr_cont(" s1 : " REG_FMT " a0 : " REG_FMT " a1 : " REG_FMT "\n", 797db91e57SPalmer Dabbelt regs->s1, regs->a0, regs->a1); 807db91e57SPalmer Dabbelt pr_cont(" a2 : " REG_FMT " a3 : " REG_FMT " a4 : " REG_FMT "\n", 817db91e57SPalmer Dabbelt regs->a2, regs->a3, regs->a4); 827db91e57SPalmer Dabbelt pr_cont(" a5 : " REG_FMT " a6 : " REG_FMT " a7 : " REG_FMT "\n", 837db91e57SPalmer Dabbelt regs->a5, regs->a6, regs->a7); 847db91e57SPalmer Dabbelt pr_cont(" s2 : " REG_FMT " s3 : " REG_FMT " s4 : " REG_FMT "\n", 857db91e57SPalmer Dabbelt regs->s2, regs->s3, regs->s4); 867db91e57SPalmer Dabbelt pr_cont(" s5 : " REG_FMT " s6 : " REG_FMT " s7 : " REG_FMT "\n", 877db91e57SPalmer Dabbelt regs->s5, regs->s6, regs->s7); 887db91e57SPalmer Dabbelt pr_cont(" s8 : " REG_FMT " s9 : " REG_FMT " s10: " REG_FMT "\n", 897db91e57SPalmer Dabbelt regs->s8, regs->s9, regs->s10); 907db91e57SPalmer Dabbelt pr_cont(" s11: " REG_FMT " t3 : " REG_FMT " t4 : " REG_FMT "\n", 917db91e57SPalmer Dabbelt regs->s11, regs->t3, regs->t4); 927db91e57SPalmer Dabbelt pr_cont(" t5 : " REG_FMT " t6 : " REG_FMT "\n", 937db91e57SPalmer Dabbelt regs->t5, regs->t6); 947db91e57SPalmer Dabbelt 95a4c3733dSChristoph Hellwig pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT "\n", 96a4c3733dSChristoph Hellwig regs->status, regs->badaddr, regs->cause); 977db91e57SPalmer Dabbelt } 98091b9450SKefeng Wang void show_regs(struct pt_regs *regs) 99091b9450SKefeng Wang { 100091b9450SKefeng Wang __show_regs(regs); 101091b9450SKefeng Wang if (!user_mode(regs)) 102091b9450SKefeng Wang dump_backtrace(regs, NULL, KERN_DEFAULT); 103091b9450SKefeng Wang } 1047db91e57SPalmer Dabbelt 105048e2906SYunhui Cui unsigned long arch_align_stack(unsigned long sp) 106048e2906SYunhui Cui { 107048e2906SYunhui Cui if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) 108048e2906SYunhui Cui sp -= get_random_u32_below(PAGE_SIZE); 109048e2906SYunhui Cui return sp & ~0xf; 110048e2906SYunhui Cui } 111048e2906SYunhui Cui 112f4b395e6SGuo Ren #ifdef CONFIG_COMPAT 113f4b395e6SGuo Ren static bool compat_mode_supported __read_mostly; 114f4b395e6SGuo Ren 115f4b395e6SGuo Ren bool compat_elf_check_arch(Elf32_Ehdr *hdr) 116f4b395e6SGuo Ren { 117f4b395e6SGuo Ren return compat_mode_supported && 118f4b395e6SGuo Ren hdr->e_machine == EM_RISCV && 119f4b395e6SGuo Ren hdr->e_ident[EI_CLASS] == ELFCLASS32; 120f4b395e6SGuo Ren } 121f4b395e6SGuo Ren 122f4b395e6SGuo Ren static int __init compat_mode_detect(void) 123f4b395e6SGuo Ren { 124f4b395e6SGuo Ren unsigned long tmp = csr_read(CSR_STATUS); 125f4b395e6SGuo Ren 126f4b395e6SGuo Ren csr_write(CSR_STATUS, (tmp & ~SR_UXL) | SR_UXL_32); 127f4b395e6SGuo Ren compat_mode_supported = 128f4b395e6SGuo Ren (csr_read(CSR_STATUS) & SR_UXL) == SR_UXL_32; 129f4b395e6SGuo Ren 130f4b395e6SGuo Ren csr_write(CSR_STATUS, tmp); 131f4b395e6SGuo Ren 132f4b395e6SGuo Ren pr_info("riscv: ELF compat mode %s", 133542d353eSJisheng Zhang compat_mode_supported ? "supported" : "unsupported"); 134f4b395e6SGuo Ren 135f4b395e6SGuo Ren return 0; 136f4b395e6SGuo Ren } 137f4b395e6SGuo Ren early_initcall(compat_mode_detect); 138f4b395e6SGuo Ren #endif 139f4b395e6SGuo Ren 1407db91e57SPalmer Dabbelt void start_thread(struct pt_regs *regs, unsigned long pc, 1417db91e57SPalmer Dabbelt unsigned long sp) 1427db91e57SPalmer Dabbelt { 143a4c3733dSChristoph Hellwig regs->status = SR_PIE; 14437a7a2a1SJisheng Zhang if (has_fpu()) { 145a4c3733dSChristoph Hellwig regs->status |= SR_FS_INITIAL; 1468ac71d7eSVincent Chen /* 1478ac71d7eSVincent Chen * Restore the initial value to the FP register 1488ac71d7eSVincent Chen * before starting the user program. 1498ac71d7eSVincent Chen */ 1508ac71d7eSVincent Chen fstate_restore(current, regs); 1518ac71d7eSVincent Chen } 152a4c3733dSChristoph Hellwig regs->epc = pc; 1537db91e57SPalmer Dabbelt regs->sp = sp; 15469d0bf4cSGuo Ren 15569d0bf4cSGuo Ren #ifdef CONFIG_64BIT 15669d0bf4cSGuo Ren regs->status &= ~SR_UXL; 15769d0bf4cSGuo Ren 15869d0bf4cSGuo Ren if (is_compat_task()) 15969d0bf4cSGuo Ren regs->status |= SR_UXL_32; 16069d0bf4cSGuo Ren else 16169d0bf4cSGuo Ren regs->status |= SR_UXL_64; 16269d0bf4cSGuo Ren #endif 1637db91e57SPalmer Dabbelt } 1647db91e57SPalmer Dabbelt 1657db91e57SPalmer Dabbelt void flush_thread(void) 1667db91e57SPalmer Dabbelt { 1679671f706SAlan Kao #ifdef CONFIG_FPU 1687db91e57SPalmer Dabbelt /* 1698ac71d7eSVincent Chen * Reset FPU state and context 1707db91e57SPalmer Dabbelt * frm: round to nearest, ties to even (IEEE default) 1717db91e57SPalmer Dabbelt * fflags: accrued exceptions cleared 1727db91e57SPalmer Dabbelt */ 1738ac71d7eSVincent Chen fstate_off(current, task_pt_regs(current)); 1747db91e57SPalmer Dabbelt memset(¤t->thread.fstate, 0, sizeof(current->thread.fstate)); 1759671f706SAlan Kao #endif 1763a2df632SGreentime Hu #ifdef CONFIG_RISCV_ISA_V 1773a2df632SGreentime Hu /* Reset vector state */ 1781fd96a3eSAndy Chiu riscv_v_vstate_ctrl_init(current); 1793a2df632SGreentime Hu riscv_v_vstate_off(task_pt_regs(current)); 1803a2df632SGreentime Hu kfree(current->thread.vstate.datap); 1813a2df632SGreentime Hu memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); 1827df56cbcSAndy Chiu clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE); 1833a2df632SGreentime Hu #endif 184*09d6775fSSamuel Holland #ifdef CONFIG_RISCV_ISA_SUPM 185*09d6775fSSamuel Holland if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) 186*09d6775fSSamuel Holland envcfg_update_bits(current, ENVCFG_PMM, ENVCFG_PMM_PMLEN_0); 187*09d6775fSSamuel Holland #endif 1883a2df632SGreentime Hu } 1893a2df632SGreentime Hu 1903a2df632SGreentime Hu void arch_release_task_struct(struct task_struct *tsk) 1913a2df632SGreentime Hu { 1923a2df632SGreentime Hu /* Free the vector context of datap. */ 1933a2df632SGreentime Hu if (has_vector()) 194bd446f5dSAndy Chiu riscv_v_thread_free(tsk); 1957db91e57SPalmer Dabbelt } 1967db91e57SPalmer Dabbelt 1977db91e57SPalmer Dabbelt int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) 1987db91e57SPalmer Dabbelt { 1997db91e57SPalmer Dabbelt fstate_save(src, task_pt_regs(src)); 2007db91e57SPalmer Dabbelt *dst = *src; 2013a2df632SGreentime Hu /* clear entire V context, including datap for a new task */ 2023a2df632SGreentime Hu memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); 2032080ff94SAndy Chiu memset(&dst->thread.kernel_vstate, 0, sizeof(struct __riscv_v_ext_state)); 2047df56cbcSAndy Chiu clear_tsk_thread_flag(dst, TIF_RISCV_V_DEFER_RESTORE); 2053a2df632SGreentime Hu 2067db91e57SPalmer Dabbelt return 0; 2077db91e57SPalmer Dabbelt } 2087db91e57SPalmer Dabbelt 209c5febea0SEric W. Biederman int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) 2107db91e57SPalmer Dabbelt { 211c5febea0SEric W. Biederman unsigned long clone_flags = args->flags; 212c5febea0SEric W. Biederman unsigned long usp = args->stack; 213c5febea0SEric W. Biederman unsigned long tls = args->tls; 2147db91e57SPalmer Dabbelt struct pt_regs *childregs = task_pt_regs(p); 2157db91e57SPalmer Dabbelt 2166510c784SJisheng Zhang memset(&p->thread.s, 0, sizeof(p->thread.s)); 2176510c784SJisheng Zhang 2187db91e57SPalmer Dabbelt /* p->thread holds context to be restored by __switch_to() */ 2195bd2e97cSEric W. Biederman if (unlikely(args->fn)) { 2207db91e57SPalmer Dabbelt /* Kernel thread */ 2217db91e57SPalmer Dabbelt memset(childregs, 0, sizeof(struct pt_regs)); 222a4c3733dSChristoph Hellwig /* Supervisor/Machine, irqs on: */ 223a4c3733dSChristoph Hellwig childregs->status = SR_PP | SR_PIE; 2247db91e57SPalmer Dabbelt 2255bd2e97cSEric W. Biederman p->thread.s[0] = (unsigned long)args->fn; 2265bd2e97cSEric W. Biederman p->thread.s[1] = (unsigned long)args->fn_arg; 2277db91e57SPalmer Dabbelt } else { 2287db91e57SPalmer Dabbelt *childregs = *(current_pt_regs()); 2293a2df632SGreentime Hu /* Turn off status.VS */ 2303a2df632SGreentime Hu riscv_v_vstate_off(childregs); 2317db91e57SPalmer Dabbelt if (usp) /* User fork */ 2327db91e57SPalmer Dabbelt childregs->sp = usp; 2337db91e57SPalmer Dabbelt if (clone_flags & CLONE_SETTLS) 23420bda4edSAmanieu d'Antras childregs->tp = tls; 2357db91e57SPalmer Dabbelt childregs->a0 = 0; /* Return value of fork() */ 236ab9164daSJisheng Zhang p->thread.s[0] = 0; 2377db91e57SPalmer Dabbelt } 238ecd2ada8SGreentime Hu p->thread.riscv_v_flags = 0; 2392080ff94SAndy Chiu if (has_vector()) 2402080ff94SAndy Chiu riscv_v_thread_alloc(p); 241ab9164daSJisheng Zhang p->thread.ra = (unsigned long)ret_from_fork; 2427db91e57SPalmer Dabbelt p->thread.sp = (unsigned long)childregs; /* kernel sp */ 2437db91e57SPalmer Dabbelt return 0; 2447db91e57SPalmer Dabbelt } 245bd446f5dSAndy Chiu 246bd446f5dSAndy Chiu void __init arch_task_cache_init(void) 247bd446f5dSAndy Chiu { 248bd446f5dSAndy Chiu riscv_v_setup_ctx_cache(); 249bd446f5dSAndy Chiu } 250*09d6775fSSamuel Holland 251*09d6775fSSamuel Holland #ifdef CONFIG_RISCV_ISA_SUPM 252*09d6775fSSamuel Holland enum { 253*09d6775fSSamuel Holland PMLEN_0 = 0, 254*09d6775fSSamuel Holland PMLEN_7 = 7, 255*09d6775fSSamuel Holland PMLEN_16 = 16, 256*09d6775fSSamuel Holland }; 257*09d6775fSSamuel Holland 258*09d6775fSSamuel Holland static bool have_user_pmlen_7; 259*09d6775fSSamuel Holland static bool have_user_pmlen_16; 260*09d6775fSSamuel Holland 261*09d6775fSSamuel Holland long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) 262*09d6775fSSamuel Holland { 263*09d6775fSSamuel Holland unsigned long valid_mask = PR_PMLEN_MASK; 264*09d6775fSSamuel Holland struct thread_info *ti = task_thread_info(task); 265*09d6775fSSamuel Holland unsigned long pmm; 266*09d6775fSSamuel Holland u8 pmlen; 267*09d6775fSSamuel Holland 268*09d6775fSSamuel Holland if (is_compat_thread(ti)) 269*09d6775fSSamuel Holland return -EINVAL; 270*09d6775fSSamuel Holland 271*09d6775fSSamuel Holland if (arg & ~valid_mask) 272*09d6775fSSamuel Holland return -EINVAL; 273*09d6775fSSamuel Holland 274*09d6775fSSamuel Holland /* 275*09d6775fSSamuel Holland * Prefer the smallest PMLEN that satisfies the user's request, 276*09d6775fSSamuel Holland * in case choosing a larger PMLEN has a performance impact. 277*09d6775fSSamuel Holland */ 278*09d6775fSSamuel Holland pmlen = FIELD_GET(PR_PMLEN_MASK, arg); 279*09d6775fSSamuel Holland if (pmlen == PMLEN_0) 280*09d6775fSSamuel Holland pmm = ENVCFG_PMM_PMLEN_0; 281*09d6775fSSamuel Holland else if (pmlen <= PMLEN_7 && have_user_pmlen_7) 282*09d6775fSSamuel Holland pmm = ENVCFG_PMM_PMLEN_7; 283*09d6775fSSamuel Holland else if (pmlen <= PMLEN_16 && have_user_pmlen_16) 284*09d6775fSSamuel Holland pmm = ENVCFG_PMM_PMLEN_16; 285*09d6775fSSamuel Holland else 286*09d6775fSSamuel Holland return -EINVAL; 287*09d6775fSSamuel Holland 288*09d6775fSSamuel Holland envcfg_update_bits(task, ENVCFG_PMM, pmm); 289*09d6775fSSamuel Holland 290*09d6775fSSamuel Holland return 0; 291*09d6775fSSamuel Holland } 292*09d6775fSSamuel Holland 293*09d6775fSSamuel Holland long get_tagged_addr_ctrl(struct task_struct *task) 294*09d6775fSSamuel Holland { 295*09d6775fSSamuel Holland struct thread_info *ti = task_thread_info(task); 296*09d6775fSSamuel Holland long ret = 0; 297*09d6775fSSamuel Holland 298*09d6775fSSamuel Holland if (is_compat_thread(ti)) 299*09d6775fSSamuel Holland return -EINVAL; 300*09d6775fSSamuel Holland 301*09d6775fSSamuel Holland switch (task->thread.envcfg & ENVCFG_PMM) { 302*09d6775fSSamuel Holland case ENVCFG_PMM_PMLEN_7: 303*09d6775fSSamuel Holland ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_7); 304*09d6775fSSamuel Holland break; 305*09d6775fSSamuel Holland case ENVCFG_PMM_PMLEN_16: 306*09d6775fSSamuel Holland ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_16); 307*09d6775fSSamuel Holland break; 308*09d6775fSSamuel Holland } 309*09d6775fSSamuel Holland 310*09d6775fSSamuel Holland return ret; 311*09d6775fSSamuel Holland } 312*09d6775fSSamuel Holland 313*09d6775fSSamuel Holland static bool try_to_set_pmm(unsigned long value) 314*09d6775fSSamuel Holland { 315*09d6775fSSamuel Holland csr_set(CSR_ENVCFG, value); 316*09d6775fSSamuel Holland return (csr_read_clear(CSR_ENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value; 317*09d6775fSSamuel Holland } 318*09d6775fSSamuel Holland 319*09d6775fSSamuel Holland static int __init tagged_addr_init(void) 320*09d6775fSSamuel Holland { 321*09d6775fSSamuel Holland if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) 322*09d6775fSSamuel Holland return 0; 323*09d6775fSSamuel Holland 324*09d6775fSSamuel Holland /* 325*09d6775fSSamuel Holland * envcfg.PMM is a WARL field. Detect which values are supported. 326*09d6775fSSamuel Holland * Assume the supported PMLEN values are the same on all harts. 327*09d6775fSSamuel Holland */ 328*09d6775fSSamuel Holland csr_clear(CSR_ENVCFG, ENVCFG_PMM); 329*09d6775fSSamuel Holland have_user_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7); 330*09d6775fSSamuel Holland have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16); 331*09d6775fSSamuel Holland 332*09d6775fSSamuel Holland return 0; 333*09d6775fSSamuel Holland } 334*09d6775fSSamuel Holland core_initcall(tagged_addr_init); 335*09d6775fSSamuel Holland #endif /* CONFIG_RISCV_ISA_SUPM */ 336