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 1009d6775fSSamuel 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 18409d6775fSSamuel Holland #ifdef CONFIG_RISCV_ISA_SUPM 18509d6775fSSamuel Holland if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) 18609d6775fSSamuel Holland envcfg_update_bits(current, ENVCFG_PMM, ENVCFG_PMM_PMLEN_0); 18709d6775fSSamuel 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. */ 193*d863910eSCharlie Jenkins if (has_vector() || has_xtheadvector()) 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 2162e174308SSamuel Holland /* Ensure all threads in this mm have the same pointer masking mode. */ 2172e174308SSamuel Holland if (IS_ENABLED(CONFIG_RISCV_ISA_SUPM) && p->mm && (clone_flags & CLONE_VM)) 2182e174308SSamuel Holland set_bit(MM_CONTEXT_LOCK_PMLEN, &p->mm->context.flags); 2192e174308SSamuel Holland 2206510c784SJisheng Zhang memset(&p->thread.s, 0, sizeof(p->thread.s)); 2216510c784SJisheng Zhang 2227db91e57SPalmer Dabbelt /* p->thread holds context to be restored by __switch_to() */ 2235bd2e97cSEric W. Biederman if (unlikely(args->fn)) { 2247db91e57SPalmer Dabbelt /* Kernel thread */ 2257db91e57SPalmer Dabbelt memset(childregs, 0, sizeof(struct pt_regs)); 226a4c3733dSChristoph Hellwig /* Supervisor/Machine, irqs on: */ 227a4c3733dSChristoph Hellwig childregs->status = SR_PP | SR_PIE; 2287db91e57SPalmer Dabbelt 2295bd2e97cSEric W. Biederman p->thread.s[0] = (unsigned long)args->fn; 2305bd2e97cSEric W. Biederman p->thread.s[1] = (unsigned long)args->fn_arg; 2317db91e57SPalmer Dabbelt } else { 2327db91e57SPalmer Dabbelt *childregs = *(current_pt_regs()); 2333a2df632SGreentime Hu /* Turn off status.VS */ 2343a2df632SGreentime Hu riscv_v_vstate_off(childregs); 2357db91e57SPalmer Dabbelt if (usp) /* User fork */ 2367db91e57SPalmer Dabbelt childregs->sp = usp; 2377db91e57SPalmer Dabbelt if (clone_flags & CLONE_SETTLS) 23820bda4edSAmanieu d'Antras childregs->tp = tls; 2397db91e57SPalmer Dabbelt childregs->a0 = 0; /* Return value of fork() */ 240ab9164daSJisheng Zhang p->thread.s[0] = 0; 2417db91e57SPalmer Dabbelt } 242ecd2ada8SGreentime Hu p->thread.riscv_v_flags = 0; 243*d863910eSCharlie Jenkins if (has_vector() || has_xtheadvector()) 2442080ff94SAndy Chiu riscv_v_thread_alloc(p); 245ab9164daSJisheng Zhang p->thread.ra = (unsigned long)ret_from_fork; 2467db91e57SPalmer Dabbelt p->thread.sp = (unsigned long)childregs; /* kernel sp */ 2477db91e57SPalmer Dabbelt return 0; 2487db91e57SPalmer Dabbelt } 249bd446f5dSAndy Chiu 250bd446f5dSAndy Chiu void __init arch_task_cache_init(void) 251bd446f5dSAndy Chiu { 252bd446f5dSAndy Chiu riscv_v_setup_ctx_cache(); 253bd446f5dSAndy Chiu } 25409d6775fSSamuel Holland 25509d6775fSSamuel Holland #ifdef CONFIG_RISCV_ISA_SUPM 25609d6775fSSamuel Holland enum { 25709d6775fSSamuel Holland PMLEN_0 = 0, 25809d6775fSSamuel Holland PMLEN_7 = 7, 25909d6775fSSamuel Holland PMLEN_16 = 16, 26009d6775fSSamuel Holland }; 26109d6775fSSamuel Holland 26209d6775fSSamuel Holland static bool have_user_pmlen_7; 26309d6775fSSamuel Holland static bool have_user_pmlen_16; 26409d6775fSSamuel Holland 2652e174308SSamuel Holland /* 2662e174308SSamuel Holland * Control the relaxed ABI allowing tagged user addresses into the kernel. 2672e174308SSamuel Holland */ 2682e174308SSamuel Holland static unsigned int tagged_addr_disabled; 2692e174308SSamuel Holland 27009d6775fSSamuel Holland long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) 27109d6775fSSamuel Holland { 2722e174308SSamuel Holland unsigned long valid_mask = PR_PMLEN_MASK | PR_TAGGED_ADDR_ENABLE; 27309d6775fSSamuel Holland struct thread_info *ti = task_thread_info(task); 2742e174308SSamuel Holland struct mm_struct *mm = task->mm; 27509d6775fSSamuel Holland unsigned long pmm; 27609d6775fSSamuel Holland u8 pmlen; 27709d6775fSSamuel Holland 27809d6775fSSamuel Holland if (is_compat_thread(ti)) 27909d6775fSSamuel Holland return -EINVAL; 28009d6775fSSamuel Holland 28109d6775fSSamuel Holland if (arg & ~valid_mask) 28209d6775fSSamuel Holland return -EINVAL; 28309d6775fSSamuel Holland 28409d6775fSSamuel Holland /* 28509d6775fSSamuel Holland * Prefer the smallest PMLEN that satisfies the user's request, 28609d6775fSSamuel Holland * in case choosing a larger PMLEN has a performance impact. 28709d6775fSSamuel Holland */ 28809d6775fSSamuel Holland pmlen = FIELD_GET(PR_PMLEN_MASK, arg); 2892e174308SSamuel Holland if (pmlen == PMLEN_0) { 29009d6775fSSamuel Holland pmm = ENVCFG_PMM_PMLEN_0; 2912e174308SSamuel Holland } else if (pmlen <= PMLEN_7 && have_user_pmlen_7) { 2922e174308SSamuel Holland pmlen = PMLEN_7; 29309d6775fSSamuel Holland pmm = ENVCFG_PMM_PMLEN_7; 2942e174308SSamuel Holland } else if (pmlen <= PMLEN_16 && have_user_pmlen_16) { 2952e174308SSamuel Holland pmlen = PMLEN_16; 29609d6775fSSamuel Holland pmm = ENVCFG_PMM_PMLEN_16; 2972e174308SSamuel Holland } else { 2982e174308SSamuel Holland return -EINVAL; 2992e174308SSamuel Holland } 3002e174308SSamuel Holland 3012e174308SSamuel Holland /* 3022e174308SSamuel Holland * Do not allow the enabling of the tagged address ABI if globally 3032e174308SSamuel Holland * disabled via sysctl abi.tagged_addr_disabled, if pointer masking 3042e174308SSamuel Holland * is disabled for userspace. 3052e174308SSamuel Holland */ 3062e174308SSamuel Holland if (arg & PR_TAGGED_ADDR_ENABLE && (tagged_addr_disabled || !pmlen)) 30709d6775fSSamuel Holland return -EINVAL; 30809d6775fSSamuel Holland 3092e174308SSamuel Holland if (!(arg & PR_TAGGED_ADDR_ENABLE)) 3102e174308SSamuel Holland pmlen = PMLEN_0; 3112e174308SSamuel Holland 3122e174308SSamuel Holland if (mmap_write_lock_killable(mm)) 3132e174308SSamuel Holland return -EINTR; 3142e174308SSamuel Holland 3152e174308SSamuel Holland if (test_bit(MM_CONTEXT_LOCK_PMLEN, &mm->context.flags) && mm->context.pmlen != pmlen) { 3162e174308SSamuel Holland mmap_write_unlock(mm); 3172e174308SSamuel Holland return -EBUSY; 3182e174308SSamuel Holland } 3192e174308SSamuel Holland 32009d6775fSSamuel Holland envcfg_update_bits(task, ENVCFG_PMM, pmm); 3212e174308SSamuel Holland mm->context.pmlen = pmlen; 3222e174308SSamuel Holland 3232e174308SSamuel Holland mmap_write_unlock(mm); 32409d6775fSSamuel Holland 32509d6775fSSamuel Holland return 0; 32609d6775fSSamuel Holland } 32709d6775fSSamuel Holland 32809d6775fSSamuel Holland long get_tagged_addr_ctrl(struct task_struct *task) 32909d6775fSSamuel Holland { 33009d6775fSSamuel Holland struct thread_info *ti = task_thread_info(task); 33109d6775fSSamuel Holland long ret = 0; 33209d6775fSSamuel Holland 33309d6775fSSamuel Holland if (is_compat_thread(ti)) 33409d6775fSSamuel Holland return -EINVAL; 33509d6775fSSamuel Holland 3362e174308SSamuel Holland /* 3372e174308SSamuel Holland * The mm context's pmlen is set only when the tagged address ABI is 3382e174308SSamuel Holland * enabled, so the effective PMLEN must be extracted from envcfg.PMM. 3392e174308SSamuel Holland */ 34009d6775fSSamuel Holland switch (task->thread.envcfg & ENVCFG_PMM) { 34109d6775fSSamuel Holland case ENVCFG_PMM_PMLEN_7: 34209d6775fSSamuel Holland ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_7); 34309d6775fSSamuel Holland break; 34409d6775fSSamuel Holland case ENVCFG_PMM_PMLEN_16: 34509d6775fSSamuel Holland ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_16); 34609d6775fSSamuel Holland break; 34709d6775fSSamuel Holland } 34809d6775fSSamuel Holland 3492e174308SSamuel Holland if (task->mm->context.pmlen) 3502e174308SSamuel Holland ret |= PR_TAGGED_ADDR_ENABLE; 3512e174308SSamuel Holland 35209d6775fSSamuel Holland return ret; 35309d6775fSSamuel Holland } 35409d6775fSSamuel Holland 35509d6775fSSamuel Holland static bool try_to_set_pmm(unsigned long value) 35609d6775fSSamuel Holland { 35709d6775fSSamuel Holland csr_set(CSR_ENVCFG, value); 35809d6775fSSamuel Holland return (csr_read_clear(CSR_ENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value; 35909d6775fSSamuel Holland } 36009d6775fSSamuel Holland 3612e174308SSamuel Holland /* 3622e174308SSamuel Holland * Global sysctl to disable the tagged user addresses support. This control 3632e174308SSamuel Holland * only prevents the tagged address ABI enabling via prctl() and does not 3642e174308SSamuel Holland * disable it for tasks that already opted in to the relaxed ABI. 3652e174308SSamuel Holland */ 3662e174308SSamuel Holland 3672e174308SSamuel Holland static struct ctl_table tagged_addr_sysctl_table[] = { 3682e174308SSamuel Holland { 3692e174308SSamuel Holland .procname = "tagged_addr_disabled", 3702e174308SSamuel Holland .mode = 0644, 3712e174308SSamuel Holland .data = &tagged_addr_disabled, 3722e174308SSamuel Holland .maxlen = sizeof(int), 3732e174308SSamuel Holland .proc_handler = proc_dointvec_minmax, 3742e174308SSamuel Holland .extra1 = SYSCTL_ZERO, 3752e174308SSamuel Holland .extra2 = SYSCTL_ONE, 3762e174308SSamuel Holland }, 3772e174308SSamuel Holland }; 3782e174308SSamuel Holland 37909d6775fSSamuel Holland static int __init tagged_addr_init(void) 38009d6775fSSamuel Holland { 38109d6775fSSamuel Holland if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) 38209d6775fSSamuel Holland return 0; 38309d6775fSSamuel Holland 38409d6775fSSamuel Holland /* 38509d6775fSSamuel Holland * envcfg.PMM is a WARL field. Detect which values are supported. 38609d6775fSSamuel Holland * Assume the supported PMLEN values are the same on all harts. 38709d6775fSSamuel Holland */ 38809d6775fSSamuel Holland csr_clear(CSR_ENVCFG, ENVCFG_PMM); 38909d6775fSSamuel Holland have_user_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7); 39009d6775fSSamuel Holland have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16); 39109d6775fSSamuel Holland 3922e174308SSamuel Holland if (!register_sysctl("abi", tagged_addr_sysctl_table)) 3932e174308SSamuel Holland return -EINVAL; 3942e174308SSamuel Holland 39509d6775fSSamuel Holland return 0; 39609d6775fSSamuel Holland } 39709d6775fSSamuel Holland core_initcall(tagged_addr_init); 39809d6775fSSamuel Holland #endif /* CONFIG_RISCV_ISA_SUPM */ 399