162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Process creation support for Hexagon 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/sched.h> 962306a36Sopenharmony_ci#include <linux/sched/debug.h> 1062306a36Sopenharmony_ci#include <linux/sched/task.h> 1162306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/tick.h> 1562306a36Sopenharmony_ci#include <linux/uaccess.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/resume_user_mode.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Program thread launch. Often defined as a macro in processor.h, 2162306a36Sopenharmony_ci * but we're shooting for a small footprint and it's not an inner-loop 2262306a36Sopenharmony_ci * performance-critical operation. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * The Hexagon ABI specifies that R28 is zero'ed before program launch, 2562306a36Sopenharmony_ci * so that gets automatically done here. If we ever stop doing that here, 2662306a36Sopenharmony_ci * we'll probably want to define the ELF_PLAT_INIT macro. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_civoid start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci /* We want to zero all data-containing registers. Is this overkill? */ 3162306a36Sopenharmony_ci memset(regs, 0, sizeof(*regs)); 3262306a36Sopenharmony_ci /* We might want to also zero all Processor registers here */ 3362306a36Sopenharmony_ci pt_set_usermode(regs); 3462306a36Sopenharmony_ci pt_set_elr(regs, pc); 3562306a36Sopenharmony_ci pt_set_rte_sp(regs, sp); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * Spin, or better still, do a hardware or VM wait instruction 4062306a36Sopenharmony_ci * If hardware or VM offer wait termination even though interrupts 4162306a36Sopenharmony_ci * are disabled. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_civoid arch_cpu_idle(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci __vmwait(); 4662306a36Sopenharmony_ci /* interrupts wake us up, but irqs are still disabled */ 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * Copy architecture-specific thread state 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ciint copy_thread(struct task_struct *p, const struct kernel_clone_args *args) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned long clone_flags = args->flags; 5562306a36Sopenharmony_ci unsigned long usp = args->stack; 5662306a36Sopenharmony_ci unsigned long tls = args->tls; 5762306a36Sopenharmony_ci struct thread_info *ti = task_thread_info(p); 5862306a36Sopenharmony_ci struct hexagon_switch_stack *ss; 5962306a36Sopenharmony_ci struct pt_regs *childregs; 6062306a36Sopenharmony_ci asmlinkage void ret_from_fork(void); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci childregs = (struct pt_regs *) (((unsigned long) ti + THREAD_SIZE) - 6362306a36Sopenharmony_ci sizeof(*childregs)); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ti->regs = childregs; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* 6862306a36Sopenharmony_ci * Establish kernel stack pointer and initial PC for new thread 6962306a36Sopenharmony_ci * Note that unlike the usual situation, we do not copy the 7062306a36Sopenharmony_ci * parent's callee-saved here; those are in pt_regs and whatever 7162306a36Sopenharmony_ci * we leave here will be overridden on return to userland. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci ss = (struct hexagon_switch_stack *) ((unsigned long) childregs - 7462306a36Sopenharmony_ci sizeof(*ss)); 7562306a36Sopenharmony_ci ss->lr = (unsigned long)ret_from_fork; 7662306a36Sopenharmony_ci p->thread.switch_sp = ss; 7762306a36Sopenharmony_ci if (unlikely(args->fn)) { 7862306a36Sopenharmony_ci memset(childregs, 0, sizeof(struct pt_regs)); 7962306a36Sopenharmony_ci /* r24 <- fn, r25 <- arg */ 8062306a36Sopenharmony_ci ss->r24 = (unsigned long)args->fn; 8162306a36Sopenharmony_ci ss->r25 = (unsigned long)args->fn_arg; 8262306a36Sopenharmony_ci pt_set_kmode(childregs); 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci memcpy(childregs, current_pt_regs(), sizeof(*childregs)); 8662306a36Sopenharmony_ci ss->r2524 = 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (usp) 8962306a36Sopenharmony_ci pt_set_rte_sp(childregs, usp); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Child sees zero return value */ 9262306a36Sopenharmony_ci childregs->r00 = 0; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * The clone syscall has the C signature: 9662306a36Sopenharmony_ci * int [r0] clone(int flags [r0], 9762306a36Sopenharmony_ci * void *child_frame [r1], 9862306a36Sopenharmony_ci * void *parent_tid [r2], 9962306a36Sopenharmony_ci * void *child_tid [r3], 10062306a36Sopenharmony_ci * void *thread_control_block [r4]); 10162306a36Sopenharmony_ci * ugp is used to provide TLS support. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci if (clone_flags & CLONE_SETTLS) 10462306a36Sopenharmony_ci childregs->ugp = tls; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Parent sees new pid -- not necessary, not even possible at 10862306a36Sopenharmony_ci * this point in the fork process 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * Some archs flush debug and FPU info here 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_civoid flush_thread(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * The "wait channel" terminology is archaic, but what we want 12362306a36Sopenharmony_ci * is an identification of the point at which the scheduler 12462306a36Sopenharmony_ci * was invoked by a blocked thread. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ciunsigned long __get_wchan(struct task_struct *p) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci unsigned long fp, pc; 12962306a36Sopenharmony_ci unsigned long stack_page; 13062306a36Sopenharmony_ci int count = 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci stack_page = (unsigned long)task_stack_page(p); 13362306a36Sopenharmony_ci fp = ((struct hexagon_switch_stack *)p->thread.switch_sp)->fp; 13462306a36Sopenharmony_ci do { 13562306a36Sopenharmony_ci if (fp < (stack_page + sizeof(struct thread_info)) || 13662306a36Sopenharmony_ci fp >= (THREAD_SIZE - 8 + stack_page)) 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci pc = ((unsigned long *)fp)[1]; 13962306a36Sopenharmony_ci if (!in_sched_functions(pc)) 14062306a36Sopenharmony_ci return pc; 14162306a36Sopenharmony_ci fp = *(unsigned long *) fp; 14262306a36Sopenharmony_ci } while (count++ < 16); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * Called on the exit path of event entry; see vm_entry.S 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Interrupts will already be disabled. 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Returns 0 if there's no need to re-check for more work. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint do_work_pending(struct pt_regs *regs, u32 thread_info_flags) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (!(thread_info_flags & _TIF_WORK_MASK)) { 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci } /* shortcut -- no work to be done */ 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci local_irq_enable(); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (thread_info_flags & _TIF_NEED_RESCHED) { 16462306a36Sopenharmony_ci schedule(); 16562306a36Sopenharmony_ci return 1; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) { 16962306a36Sopenharmony_ci do_signal(regs); 17062306a36Sopenharmony_ci return 1; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (thread_info_flags & _TIF_NOTIFY_RESUME) { 17462306a36Sopenharmony_ci resume_user_mode_work(regs); 17562306a36Sopenharmony_ci return 1; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Should not even reach here */ 17962306a36Sopenharmony_ci panic("%s: bad thread_info flags 0x%08x\n", __func__, 18062306a36Sopenharmony_ci thread_info_flags); 18162306a36Sopenharmony_ci} 182