Quelle process.c
Sprache: C
/*
* arch / xtensa / kernel / process . c
*
* Xtensa Processor version .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2001 - 2005 Tensilica Inc .
*
* Joe Taylor < joe @ tensilica . com , joetylr @ yahoo . com >
* Chris Zankel < chris @ zankel . net >
* Marc Gauthier < marc @ tensilica . com , marc @ alumni . uwaterloo . ca >
* Kevin Chea
*/
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/sched/task_stack.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/elf.h>
#include <linux/hw_breakpoint.h>
#include <linux/init.h>
#include <linux/prctl.h>
#include <linux/init_task.h>
#include <linux/module.h>
#include <linux/mqueue.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
#include <linux/uaccess.h>
#include <asm /io.h>
#include <asm /processor.h>
#include <asm /platform.h>
#include <asm /mmu.h>
#include <asm /irq.h>
#include <linux/atomic.h>
#include <asm /asm -offsets.h>
#include <asm /regs.h>
#include <asm /hw_breakpoint.h>
#include <asm /sections.h>
#include <asm /traps.h>
extern void ret_from_fork(void );
extern void ret_from_kernel_thread(void );
void (*pm_power_off)(void ) = NULL;
EXPORT_SYMBOL(pm_power_off);
#ifdef CONFIG_STACKPROTECTOR
#include <linux/stackprotector.h>
unsigned long __stack_chk_guard __read_mostly;
EXPORT_SYMBOL(__stack_chk_guard);
#endif
#if XTENSA_HAVE_COPROCESSORS
void local_coprocessors_flush_release_all(void )
{
struct thread_info **coprocessor_owner;
struct thread_info *unique_owner[XCHAL_CP_MAX];
int n = 0 ;
int i, j;
coprocessor_owner = this_cpu_ptr(&exc_table)->coprocessor_owner;
xtensa_set_sr(XCHAL_CP_MASK, cpenable);
for (i = 0 ; i < XCHAL_CP_MAX; i++) {
struct thread_info *ti = coprocessor_owner[i];
if (ti) {
coprocessor_flush(ti, i);
for (j = 0 ; j < n; j++)
if (unique_owner[j] == ti)
break ;
if (j == n)
unique_owner[n++] = ti;
coprocessor_owner[i] = NULL;
}
}
for (i = 0 ; i < n; i++) {
/* pairs with memw (1) in fast_coprocessor and memw in switch_to */
smp_wmb();
unique_owner[i]->cpenable = 0 ;
}
xtensa_set_sr(0 , cpenable);
}
static void local_coprocessor_release_all(void *info)
{
struct thread_info *ti = info;
struct thread_info **coprocessor_owner;
int i;
coprocessor_owner = this_cpu_ptr(&exc_table)->coprocessor_owner;
/* Walk through all cp owners and release it for the requested one. */
for (i = 0 ; i < XCHAL_CP_MAX; i++) {
if (coprocessor_owner[i] == ti)
coprocessor_owner[i] = NULL;
}
/* pairs with memw (1) in fast_coprocessor and memw in switch_to */
smp_wmb();
ti->cpenable = 0 ;
if (ti == current_thread_info())
xtensa_set_sr(0 , cpenable);
}
void coprocessor_release_all(struct thread_info *ti)
{
if (ti->cpenable) {
/* pairs with memw (2) in fast_coprocessor */
smp_rmb();
smp_call_function_single(ti->cp_owner_cpu,
local_coprocessor_release_all,
ti, true );
}
}
static void local_coprocessor_flush_all(void *info)
{
struct thread_info *ti = info;
struct thread_info **coprocessor_owner;
unsigned long old_cpenable;
int i;
coprocessor_owner = this_cpu_ptr(&exc_table)->coprocessor_owner;
old_cpenable = xtensa_xsr(ti->cpenable, cpenable);
for (i = 0 ; i < XCHAL_CP_MAX; i++) {
if (coprocessor_owner[i] == ti)
coprocessor_flush(ti, i);
}
xtensa_set_sr(old_cpenable, cpenable);
}
void coprocessor_flush_all(struct thread_info *ti)
{
if (ti->cpenable) {
/* pairs with memw (2) in fast_coprocessor */
smp_rmb();
smp_call_function_single(ti->cp_owner_cpu,
local_coprocessor_flush_all,
ti, true );
}
}
static void local_coprocessor_flush_release_all(void *info)
{
local_coprocessor_flush_all(info);
local_coprocessor_release_all(info);
}
void coprocessor_flush_release_all(struct thread_info *ti)
{
if (ti->cpenable) {
/* pairs with memw (2) in fast_coprocessor */
smp_rmb();
smp_call_function_single(ti->cp_owner_cpu,
local_coprocessor_flush_release_all,
ti, true );
}
}
#endif
/*
* Powermanagement idle function , if any is provided by the platform .
*/
void arch_cpu_idle(void )
{
platform_idle();
raw_local_irq_disable();
}
/*
* This is called when the thread calls exit ( ) .
*/
void exit_thread(struct task_struct *tsk)
{
#if XTENSA_HAVE_COPROCESSORS
coprocessor_release_all(task_thread_info(tsk));
#endif
}
/*
* Flush thread state . This is called when a thread does an execve ( )
* Note that we flush coprocessor registers for the case execve fails .
*/
void flush_thread(void )
{
#if XTENSA_HAVE_COPROCESSORS
struct thread_info *ti = current_thread_info();
coprocessor_flush_release_all(ti);
#endif
flush_ptrace_hw_breakpoint(current);
}
/*
* this gets called so that we can store coprocessor state into memory and
* copy the current task into the new thread .
*/
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
#if XTENSA_HAVE_COPROCESSORS
coprocessor_flush_all(task_thread_info(src));
#endif
*dst = *src;
return 0 ;
}
/*
* Copy thread .
*
* There are two modes in which this function is called :
* 1 ) Userspace thread creation ,
* regs ! = NULL , usp_thread_fn is userspace stack pointer .
* It is expected to copy parent regs ( in case CLONE_VM is not set
* in the clone_flags ) and set up passed usp in the childregs .
* 2 ) Kernel thread creation ,
* regs = = NULL , usp_thread_fn is the function to run in the new thread
* and thread_fn_arg is its parameter .
* childregs are not used for the kernel threads .
*
* The stack layout for the new thread looks like this :
*
* + - - - - - - - - - - - - - - - - - - - - - - - - +
* | childregs |
* + - - - - - - - - - - - - - - - - - - - - - - - - + < - thread . sp = sp in dummy - frame
* | dummy - frame | ( saved in dummy - frame spill - area )
* + - - - - - - - - - - - - - - - - - - - - - - - - +
*
* We create a dummy frame to return to either ret_from_fork or
* ret_from_kernel_thread :
* a0 points to ret_from_fork / ret_from_kernel_thread ( simulating a call4 )
* sp points to itself ( thread . sp )
* a2 , a3 are unused for userspace threads ,
* a2 points to thread_fn , a3 holds thread_fn arg for kernel threads .
*
* Note : This is a pristine frame , so we don ' t need any spill region on top of
* childregs .
*
* The fun part : if we ' re keeping the same VM ( i . e . cloning a thread ,
* not an entire process ) , we ' re normally given a new usp , and we CANNOT share
* any live address register windows . If we just copy those live frames over ,
* the two threads ( parent and child ) will overflow the same frames onto the
* parent stack at different times , likely corrupting the parent stack ( esp .
* if the parent returns from functions that called clone ( ) and calls new
* ones , before the child overflows its now old copies of its parent windows ) .
* One solution is to spill windows to the parent stack , but that ' s fairly
* involved . Much simpler to just not copy those live frames across .
*/
int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
{
u64 clone_flags = args->flags;
unsigned long usp_thread_fn = args->stack;
unsigned long tls = args->tls;
struct pt_regs *childregs = task_pt_regs(p);
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
struct thread_info *ti;
#endif
#if defined (__XTENSA_WINDOWED_ABI__)
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
SPILL_SLOT(childregs, 1 ) = (unsigned long )childregs;
SPILL_SLOT(childregs, 0 ) = 0 ;
p->thread.sp = (unsigned long )childregs;
#elif defined (__XTENSA_CALL0_ABI__)
/* Reserve 16 bytes for the _switch_to stack frame. */
p->thread.sp = (unsigned long )childregs - 16 ;
#else
#error Unsupported Xtensa ABI
#endif
if (!args->fn) {
struct pt_regs *regs = current_pt_regs();
unsigned long usp = usp_thread_fn ?
usp_thread_fn : regs->areg[1 ];
p->thread.ra = MAKE_RA_FOR_CALL(
(unsigned long )ret_from_fork, 0 x1);
*childregs = *regs;
childregs->areg[1 ] = usp;
childregs->areg[2 ] = 0 ;
/* When sharing memory with the parent thread, the child
usually starts on a pristine stack , so we have to reset
windowbase , windowstart and wmask .
( Note that such a new thread is required to always create
an initial call4 frame )
The exception is vfork , where the new thread continues to
run on the parent ' s stack until it calls execve . This could
be a call8 or call12 , which requires a legal stack frame
of the previous caller for the overflow handlers to work .
( Note that it ' s always legal to overflow live registers ) .
In this case , ensure to spill at least the stack pointer
of that frame. */
if (clone_flags & CLONE_VM) {
/* check that caller window is live and same stack */
int len = childregs->wmask & ~0 xf;
if (regs->areg[1 ] == usp && len != 0 ) {
int callinc = (regs->areg[0 ] >> 30 ) & 3 ;
int caller_ars = XCHAL_NUM_AREGS - callinc * 4 ;
put_user(regs->areg[caller_ars+1 ],
(unsigned __user*)(usp - 12 ));
}
childregs->wmask = 1 ;
childregs->windowstart = 1 ;
childregs->windowbase = 0 ;
}
if (clone_flags & CLONE_SETTLS)
childregs->threadptr = tls;
} else {
p->thread.ra = MAKE_RA_FOR_CALL(
(unsigned long )ret_from_kernel_thread, 1 );
/* pass parameters to ret_from_kernel_thread: */
#if defined (__XTENSA_WINDOWED_ABI__)
/*
* a2 = thread_fn , a3 = thread_fn arg .
* Window underflow will load registers from the
* spill slots on the stack on return from _ switch_to .
*/
SPILL_SLOT(childregs, 2 ) = (unsigned long )args->fn;
SPILL_SLOT(childregs, 3 ) = (unsigned long )args->fn_arg;
#elif defined (__XTENSA_CALL0_ABI__)
/*
* a12 = thread_fn , a13 = thread_fn arg .
* _ switch_to epilogue will load registers from the stack .
*/
((unsigned long *)p->thread.sp)[0 ] = (unsigned long )args->fn;
((unsigned long *)p->thread.sp)[1 ] = (unsigned long )args->fn_arg;
#else
#error Unsupported Xtensa ABI
#endif
/* Childregs are only used when we're going to userspace
* in which case start_thread will set them up .
*/
}
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
ti = task_thread_info(p);
ti->cpenable = 0 ;
#endif
clear_ptrace_hw_breakpoint(p);
return 0 ;
}
/*
* These bracket the sleeping functions . .
*/
unsigned long __get_wchan(struct task_struct *p)
{
unsigned long sp, pc;
unsigned long stack_page = (unsigned long ) task_stack_page(p);
int count = 0 ;
sp = p->thread.sp;
pc = MAKE_PC_FROM_RA(p->thread.ra, _text);
do {
if (sp < stack_page + sizeof (struct task_struct) ||
sp >= (stack_page + THREAD_SIZE) ||
pc == 0 )
return 0 ;
if (!in_sched_functions(pc))
return pc;
/* Stack layout: sp-4: ra, sp-3: sp' */
pc = MAKE_PC_FROM_RA(SPILL_SLOT(sp, 0 ), _text);
sp = SPILL_SLOT(sp, 1 );
} while (count++ < 16 );
return 0 ;
}
Messung V0.5 in Prozent C=95 H=76 G=85
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1897691