void free_task(struct task_struct *tsk) { account_kernel_stack(tsk->stack, -1); free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); free_task_struct(tsk); }
void free_task(struct task_struct *tsk) { prop_local_destroy_single(&tsk->dirties); free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); free_task_struct(tsk); }
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; unsigned long *stackend; int node = tsk_fork_get_node(orig); int err; tsk = alloc_task_struct_node(node); if (!tsk){ printk("[%d:%s] fork fail at alloc_tsk_node, please check kmem_cache_alloc_node()\n", current->pid, current->comm); return NULL; } ti = alloc_thread_info_node(tsk, node); if (!ti) { printk("[%d:%s] fork fail at alloc_t_info_node, please check alloc_pages_node()\n", current->pid, current->comm); goto free_tsk; } err = arch_dup_task_struct(tsk, orig); if (err){ printk("[%d:%s] fork fail at arch_dup_task_struct, err:%d \n", current->pid, current->comm, err); goto free_ti; } tsk->stack = ti; setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); stackend = end_of_stack(tsk); *stackend = STACK_END_MAGIC; /* for overflow detection */ #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif /* * One for us, one for whoever does the "release_task()" (usually * parent) */ atomic_set(&tsk->usage, 2); #ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; #endif tsk->splice_pipe = NULL; tsk->task_frag.page = NULL; account_kernel_stack(ti, 1); return tsk; free_ti: free_thread_info(ti); free_tsk: free_task_struct(tsk); return NULL; }
void free_task(struct task_struct *tsk) { prop_local_destroy_single(&tsk->dirties); account_kernel_stack(tsk->stack, -1); free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); put_seccomp_filter(tsk); free_task_struct(tsk); }
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; int err; prepare_to_copy(orig); tsk = alloc_task_struct(); if (!tsk) return NULL; ti = alloc_thread_info(tsk); if (!ti) { free_task_struct(tsk); return NULL; } *tsk = *orig; tsk->stack = ti; err = prop_local_init_single(&tsk->dirties); if (err) { free_thread_info(ti); free_task_struct(tsk); return NULL; } setup_thread_stack(tsk, orig); #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif /* One for us, one for whoever does the "release_task()" (usually parent) */ atomic_set(&tsk->usage,2); atomic_set(&tsk->fs_excl, 0); #ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; #endif tsk->splice_pipe = NULL; return tsk; }
void free_task(struct task_struct *tsk) { account_kernel_stack(tsk->stack, -1); arch_release_thread_info(tsk->stack); free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); put_seccomp_filter(tsk); arch_release_task_struct(tsk); free_task_struct(tsk); }
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; unsigned long *stackend; int node = tsk_fork_get_node(orig); int err; tsk = alloc_task_struct_node(node); if (!tsk) return NULL; ti = alloc_thread_info_node(tsk, node); if (!ti) goto free_tsk; err = arch_dup_task_struct(tsk, orig); if (err) goto free_ti; tsk->stack = ti; setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); stackend = end_of_stack(tsk); *stackend = STACK_END_MAGIC; /* for overflow detection */ #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif /* * One for us, one for whoever does the "release_task()" (usually * parent) */ atomic_set(&tsk->usage, 2); #ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; #endif tsk->splice_pipe = NULL; account_kernel_stack(ti, 1); memset(&(tsk->vemu), 0, sizeof(vemu_state)); return tsk; free_ti: free_thread_info(ti); free_tsk: free_task_struct(tsk); return NULL; }
void free_task(struct task_struct *tsk) { prop_local_destroy_single(&tsk->dirties); #ifndef KSTACK_MEM_OPT_64KPAGE account_kernel_stack(tsk->stack, -1); #endif free_thread_info(tsk->stack); rt_mutex_debug_task_free(tsk); ftrace_graph_exit_task(tsk); free_task_struct(tsk); }
static void release_task(struct task_struct * p) { if (p != current) { #ifdef CONFIG_SMP /* * Wait to make sure the process isn't on the * runqueue (active on some other CPU still) */ for (;;) { task_lock(p); if (!task_has_cpu(p)) break; task_unlock(p); do { cpu_relax(); barrier(); } while (task_has_cpu(p)); } task_unlock(p); #endif atomic_dec(&p->user->processes); security_task_free(p); free_uid(p->user); unhash_process(p); release_thread(p); current->cmin_flt += p->min_flt + p->cmin_flt; current->cmaj_flt += p->maj_flt + p->cmaj_flt; current->cnswap += p->nswap + p->cnswap; /* * Potentially available timeslices are retrieved * here - this way the parent does not get penalized * for creating too many processes. * * (this cannot be used to artificially 'generate' * timeslices, because any timeslice recovered here * was given away by the parent in the first place.) */ current->counter += p->counter; if (current->counter >= MAX_COUNTER) current->counter = MAX_COUNTER; p->pid = 0; free_task_struct(p); } else { printk("task releasing itself\n"); } }
static void release_task(struct task_struct * p) { if (p == current) BUG(); #ifdef CONFIG_SMP wait_task_inactive(p); #endif atomic_dec(&p->user->processes); free_uid(p->user); unhash_process(p); release_thread(p); current->cmin_flt += p->min_flt + p->cmin_flt; current->cmaj_flt += p->maj_flt + p->cmaj_flt; current->cnswap += p->nswap + p->cnswap; sched_exit(p); p->pid = 0; free_task_struct(p); }
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; prepare_to_copy(orig); /* * 为新进程获取进程描述符 */ tsk = alloc_task_struct(); if (!tsk) return NULL; /* * 获取一片空闲区域4k或者8k, 用来存放新进程的thread_info结构和内核栈 */ ti = alloc_thread_info(tsk); if (!ti) { free_task_struct(tsk); return NULL; } /* * 将current进程描述符的内容复制到tsk所指向的task_struct结构中 */ *ti = *orig->thread_info; /* * 把current进程的thread_info描述符的内容复制到ti中 */ *tsk = *orig; /* * 下面两行将新进程的task_struct与thread_info绑定 */ tsk->thread_info = ti; ti->task = tsk; /* One for us, one for whoever does the "release_task()" (usually parent) */ atomic_set(&tsk->usage,2); return tsk; }
void release(struct task_struct * p) { if (p != current) { #ifdef __SMP__ /* * Wait to make sure the process isn't active on any * other CPU */ for (;;) { int has_cpu; spin_lock_irq(&runqueue_lock); has_cpu = p->has_cpu; spin_unlock_irq(&runqueue_lock); if (!has_cpu) break; do { barrier(); } while (p->has_cpu); } #endif free_uid(p); add_free_taskslot(p->tarray_ptr); write_lock_irq(&tasklist_lock); nr_tasks--; unhash_pid(p); REMOVE_LINKS(p); write_unlock_irq(&tasklist_lock); release_thread(p); current->cmin_flt += p->min_flt + p->cmin_flt; current->cmaj_flt += p->maj_flt + p->cmaj_flt; current->cnswap += p->nswap + p->cnswap; free_task_struct(p); } else { printk("task releasing itself\n"); } }
asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret = -EPERM; unsigned long tmp; int copied; ptrace_area parea; lock_kernel(); if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; // printk("child=%lX child->flags=%lX",child,child->flags); /* I added child!=current line so we can get the */ /* ieee_instruction_pointer from the user structure DJB */ if(child!=current) { if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; } switch (request) { /* If I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: copied = access_process_vm(child,ADDR_BITS_REMOVE(addr), &tmp, sizeof(tmp), 0); ret = -EIO; if (copied != sizeof(tmp)) break; ret = put_user(tmp,(unsigned long *) data); break; /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: ret=copy_user(child,addr,data,sizeof(unsigned long),1,0); break; /* If I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child,ADDR_BITS_REMOVE(addr), &data, sizeof(data), 1) == sizeof(data)) break; ret = -EIO; break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1); break; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: /* restart after signal. */ ret = -EIO; if ((unsigned long) data >= _NSIG) break; if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); ret = 0; break; /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; clear_single_step(child); wake_up_process(child); /* make sure the single step bit is not set. */ break; case PTRACE_SINGLESTEP: /* set the trap flag. */ ret = -EIO; if ((unsigned long) data >= _NSIG) break; child->ptrace &= ~PT_TRACESYS; child->exit_code = data; set_single_step(child); /* give it a chance to run. */ wake_up_process(child); ret = 0; break; case PTRACE_DETACH: /* detach a process that was attached. */ ret = ptrace_detach(child, data); break; case PTRACE_PEEKUSR_AREA: case PTRACE_POKEUSR_AREA: if(copy_from_user(&parea,(void *)addr,sizeof(parea))==0) ret=copy_user(child,parea.kernel_addr,parea.process_addr, parea.len,1,(request==PTRACE_POKEUSR_AREA)); else ret = -EFAULT; break; default: ret = -EIO; break; } out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; }
asmlinkage int exe$creprc(unsigned int *pidadr, void *image, void *input, void *output, void *error, struct _generic_64 *prvadr, unsigned int *quota, void*prcnam, unsigned int baspri, unsigned int uic, unsigned short int mbxunt, unsigned int stsflg,...) { unsigned long stack_here; struct _pcb * p, * cur; int retval; struct dsc$descriptor * imd = image, * ind = input, * oud = output, * erd = error; unsigned long clone_flags=CLONE_VFORK; //check pidadr ctl$gl_creprc_flags = stsflg; // check for PRC$M_NOUAF sometime if (stsflg&PRC$M_DETACH) { } if (uic) { } //setipl(IPL$_ASTDEL);//postpone this? cur=ctl$gl_pcb; vmslock(&SPIN_SCHED, IPL$_SCHED); vmslock(&SPIN_MMG, IPL$_MMG); p = alloc_task_struct(); //bzero(p,sizeof(struct _pcb));//not wise? memset(p,0,sizeof(struct _pcb)); // check more // compensate for no struct clone/copy p->sigmask_lock = SPIN_LOCK_UNLOCKED; p->alloc_lock = SPIN_LOCK_UNLOCKED; qhead_init(&p->pcb$l_astqfl); // and enable ast del to all modes p->pcb$b_type = DYN$C_PCB; p->pcb$b_asten=15; p->phd$b_astlvl=4; p->pr_astlvl=4; p->psl=0; p->pslindex=0; qhead_init(&p->pcb$l_lockqfl); // set capabilities p->pcb$l_permanent_capability = sch$gl_default_process_cap; p->pcb$l_capability = p->pcb$l_permanent_capability; // set affinity // set default fileprot // set arb // set mbx stuff // from setprn: if (prcnam) { struct dsc$descriptor *s=prcnam; strncpy(p->pcb$t_lname,s->dsc$a_pointer,s->dsc$w_length); } // set priv p->pcb$l_priv=ctl$gl_pcb->pcb$l_priv; // set pris p->pcb$b_prib=31-baspri; p->pcb$b_pri=31-baspri-6; // if (p->pcb$b_pri<16) p->pcb$b_pri=16; p->pcb$w_quant=-QUANTUM; // set uic p->pcb$l_uic=ctl$gl_pcb->pcb$l_uic; // set vms pid // check process name // do something with pqb p->pcb$l_pqb=kmalloc(sizeof(struct _pqb),GFP_KERNEL); memset(p->pcb$l_pqb,0,sizeof(struct _pqb)); struct _pqb * pqb = p->pcb$l_pqb; pqb->pqb$q_prvmsk = ctl$gq_procpriv; if (imd) memcpy(pqb->pqb$t_image,imd->dsc$a_pointer,imd->dsc$w_length); if (ind) memcpy(pqb->pqb$t_input,ind->dsc$a_pointer,ind->dsc$w_length); if (oud) memcpy(pqb->pqb$t_output,oud->dsc$a_pointer,oud->dsc$w_length); if (erd) memcpy(pqb->pqb$t_error,erd->dsc$a_pointer,erd->dsc$w_length); if (oud) // temp measure memcpy(p->pcb$t_terminal,oud->dsc$a_pointer,oud->dsc$w_length); // translate some logicals // copy security clearance // copy msg // copy flags // set jib // do quotas // process itmlst // set pcb$l_pqb #if 0 setipl(IPL$_MMG); vmslock(&SPIN_SCHED,-1); // find vacant slot in pcb vector // and store it #endif // make ipid and epid p->pcb$l_pid=alloc_ipid(); { unsigned long *vec=sch$gl_pcbvec; vec[p->pcb$l_pid&0xffff]=p; } p->pcb$l_epid=exe$ipid_to_epid(p->pcb$l_pid); // should invoke sch$chse, put this at bottom? // setipl(0) and return // now lots of things from fork retval = -EAGAIN; /* * Check if we are over our maximum process limit, but be sure to * exclude root. This is needed to make it possible for login and * friends to set the per-user process limit to something lower * than the amount of processes root is running. -- Rik */ #if 0 if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur && !capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE)) goto bad_fork_free; atomic_inc(&p->user->__count); atomic_inc(&p->user->processes); #endif /* * Counter increases are protected by * the kernel lock so nr_threads can't * increase under us (but it may decrease). */ get_exec_domain(p->exec_domain); if (p->binfmt && p->binfmt->module) __MOD_INC_USE_COUNT(p->binfmt->module); p->did_exec = 0; p->swappable = 0; p->state = TASK_UNINTERRUPTIBLE; //copy_flags(clone_flags, p); // not here? p->pcb$l_pid = alloc_ipid(); p->run_list.next = NULL; p->run_list.prev = NULL; p->p_cptr = NULL; init_waitqueue_head(&p->wait_chldexit); p->vfork_done = NULL; spin_lock_init(&p->alloc_lock); p->sigpending = 0; init_sigpending(&p->pending); p->it_real_value = p->it_virt_value = p->it_prof_value = 0; p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0; init_timer(&p->real_timer); p->real_timer.data = (unsigned long) p; p->leader = 0; /* session leadership doesn't inherit */ p->tty_old_pgrp = 0; p->times.tms_utime = p->times.tms_stime = 0; p->times.tms_cutime = p->times.tms_cstime = 0; p->lock_depth = -1; /* -1 = no lock */ p->start_time = jiffies; INIT_LIST_HEAD(&p->local_pages); p->files = current->files; p->fs = current->fs; p->sig = current->sig; /* copy all the process information */ if (copy_files(clone_flags, p)) goto bad_fork_cleanup; if (copy_fs(clone_flags, p)) goto bad_fork_cleanup_files; if (copy_sighand(clone_flags, p)) goto bad_fork_cleanup_fs; bad_fork_cleanup: bad_fork_cleanup_files: bad_fork_cleanup_fs: // now a hole // now more from fork /* ok, now we should be set up.. */ p->swappable = 1; p->exit_signal = 0; p->pdeath_signal = 0; /* * "share" dynamic priority between parent and child, thus the * total amount of dynamic priorities in the system doesnt change, * more scheduling fairness. This is only important in the first * timeslice, on the long run the scheduling behaviour is unchanged. */ /* * Ok, add it to the run-queues and make it * visible to the rest of the system. * * Let it rip! */ retval = p->pcb$l_epid; INIT_LIST_HEAD(&p->thread_group); /* Need tasklist lock for parent etc handling! */ write_lock_irq(&tasklist_lock); /* CLONE_PARENT and CLONE_THREAD re-use the old parent */ p->p_opptr = current->p_opptr; p->p_pptr = current->p_pptr; p->p_opptr = current /*->p_opptr*/; p->p_pptr = current /*->p_pptr*/; SET_LINKS(p); nr_threads++; write_unlock_irq(&tasklist_lock); // printk("fork befwak\n"); //wake_up_process(p); /* do this last */ // wake_up_process2(p,PRI$_TICOM); /* do this last */ //goto fork_out;//?? // now something from exec // wait, better do execve itself memcpy(p->rlim, current->rlim, sizeof(p->rlim)); qhead_init(&p->pcb$l_sqfl); struct mm_struct * mm = mm_alloc(); p->mm = mm; p->active_mm = mm; p->user = INIT_USER; spin_lock(&mmlist_lock); #if 0 list_add(&mm->mmlist, &p->p_pptr->mm->mmlist); #endif mmlist_nr++; spin_unlock(&mmlist_lock); // Now we are getting into the area that is really the swappers // To be moved to shell.c and swp$shelinit later p->pcb$l_phd=kmalloc(sizeof(struct _phd),GFP_KERNEL); init_phd(p->pcb$l_phd); init_fork_p1pp(p,p->pcb$l_phd,ctl$gl_pcb,ctl$gl_pcb->pcb$l_phd); #ifdef __x86_64__ shell_init_other(p,ctl$gl_pcb,0x7ff80000-0x1000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ff80000-0x2000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ff90000-0x1000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ff90000-0x2000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ffa0000-0x1000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ffa0000-0x2000,0x7fffe000); #else shell_init_other(p,ctl$gl_pcb,0x7ff80000-0x1000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ff80000-0x2000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ff90000-0x1000,0x7fffe000); shell_init_other(p,ctl$gl_pcb,0x7ff90000-0x2000,0x7fffe000); #endif int exe$procstrt(struct _pcb * p); struct pt_regs * regs = &pidadr; //printk("newthread %x\n",p), retval = new_thread(0, clone_flags, 0, 0, p, 0); int eip=0,esp=0; // start_thread(regs,eip,esp); sch$chse(p, PRI$_TICOM); vmsunlock(&SPIN_MMG,-1); vmsunlock(&SPIN_SCHED,0); return SS$_NORMAL; #if 0 return sys_execve(((struct dsc$descriptor *)image)->dsc$a_pointer,0,0); return SS$_NORMAL; #endif #if 0 { char * filename=((struct dsc$descriptor *)image)->dsc$a_pointer; char ** argv=0; char ** envp=0; struct pt_regs * regs=0; struct linux_binprm bprm; struct file *file; int retval; int i; file = open_exec(filename); retval = PTR_ERR(file); if (IS_ERR(file)) return retval; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); bprm.file = file; bprm.filename = filename; bprm.sh_bang = 0; bprm.loader = 0; bprm.exec = 0; if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) { allow_write_access(file); fput(file); //printk("here 7 %x\n",bprm.argc); return bprm.argc; } if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) { allow_write_access(file); fput(file); //printk("here 6\n"); return bprm.envc; } retval = prepare_binprm(&bprm); //printk("here 4\n"); if (retval < 0) goto out; retval = copy_strings_kernel(1, &bprm.filename, &bprm); //printk("here 3\n"); if (retval < 0) goto out; bprm.exec = bprm.p; retval = copy_strings(bprm.envc, envp, &bprm); //printk("here 2\n"); if (retval < 0) goto out; retval = copy_strings(bprm.argc, argv, &bprm); //printk("here 1\n"); if (retval < 0) goto out; retval = search_binary_handler(&bprm,regs); if (retval >= 0) /* execve success */ return retval; out: /* Something went wrong, return the inode and free the argument pages*/ allow_write_access(bprm.file); if (bprm.file) fput(bprm.file); for (i = 0 ; i < MAX_ARG_PAGES ; i++) { struct page * page = bprm.page[i]; if (page) __free_page(page); } return retval; } #endif fork_out: return retval; bad_fork_free: free_task_struct(p); goto fork_out; }
void free_task(struct task_struct *tsk) { free_thread_info(tsk->thread_info); rt_mutex_debug_task_free(tsk); free_task_struct(tsk); }
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; unsigned long *stackend; int node = tsk_fork_get_node(orig); int err; prepare_to_copy(orig); tsk = alloc_task_struct_node(node); if (!tsk) return NULL; ti = alloc_thread_info_node(tsk, node); if (!ti) { free_task_struct(tsk); return NULL; } err = arch_dup_task_struct(tsk, orig); if (err) goto out; tsk->stack = ti; #ifdef CONFIG_SECCOMP /* * We must handle setting up seccomp filters once we're under * the sighand lock in case orig has changed between now and * then. Until then, filter must be NULL to avoid messing up * the usage counts on the error path calling free_task. */ tsk->seccomp.filter = NULL; #endif setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); stackend = end_of_stack(tsk); *stackend = STACK_END_MAGIC; /* for overflow detection */ #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif /* * One for us, one for whoever does the "release_task()" (usually * parent) */ atomic_set(&tsk->usage, 2); #ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; #endif tsk->splice_pipe = NULL; account_kernel_stack(ti, 1); return tsk; out: free_thread_info(ti); free_task_struct(tsk); return NULL; }
static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; unsigned long *stackend; int err; prepare_to_copy(orig); tsk = alloc_task_struct(); if (!tsk) return NULL; ti = alloc_thread_info(tsk); if (!ti) { free_task_struct(tsk); return NULL; } err = arch_dup_task_struct(tsk, orig); if (err) goto out; tsk->stack = ti; err = prop_local_init_single(&tsk->dirties); if (err) goto out; setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); stackend = end_of_stack(tsk); *stackend = STACK_END_MAGIC; /* for overflow detection */ #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif /* One for us, one for whoever does the "release_task()" (usually parent) */ atomic_set(&tsk->usage,2); atomic_set(&tsk->fs_excl, 0); #ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; #endif tsk->splice_pipe = NULL; #ifdef CONFIG_AMP tsk->session_pipe_info = NULL; #endif #ifndef KSTACK_MEM_OPT_64KPAGE account_kernel_stack(ti, 1); #endif return tsk; out: free_thread_info(ti); free_task_struct(tsk); return NULL; }
/* * 由do_fork()调用,为新的子进程创建task_struct, thread_info, 内核栈等信息 * 创建完成之后和父进程状态完全相同 */ static struct task_struct *dup_task_struct(struct task_struct *orig) { struct task_struct *tsk; struct thread_info *ti; unsigned long *stackend; int err; prepare_to_copy(orig); /* 申请task_struct内存空间 */ tsk = alloc_task_struct(); if (!tsk) return NULL; /* 申请thread_info内存空间 */ ti = alloc_thread_info(tsk); if (!ti) { /* 失败需要释放前面已经成功申请的task_struct内存空间,防止内存泄漏 */ free_task_struct(tsk); return NULL; } /* 复制orig进程的信息到tsk进程中,复制完成后地址空间地址相同 */ err = arch_dup_task_struct(tsk, orig); if (err) goto out; tsk->stack = ti; /* 清空新进程的部分标志 */ err = prop_local_init_single(&tsk->dirties); if (err) goto out; /* 新进程的进程栈信息 */ setup_thread_stack(tsk, orig); /* 清除进程栈的部分信息 */ clear_user_return_notifier(tsk); clear_tsk_need_resched(tsk); stackend = end_of_stack(tsk); *stackend = STACK_END_MAGIC; /* 用作内存越界检测 */ #ifdef CONFIG_CC_STACKPROTECTOR tsk->stack_canary = get_random_int(); #endif /* One for us, one for whoever does the "release_task()" (usually parent) */ atomic_set(&tsk->usage,2); atomic_set(&tsk->fs_excl, 0); #ifdef CONFIG_BLK_DEV_IO_TRACE tsk->btrace_seq = 0; #endif tsk->splice_pipe = NULL; /* 内核栈??? 干啥的??? */ account_kernel_stack(ti, 1); return tsk; out: free_thread_info(ti); free_task_struct(tsk); return NULL; }
asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret; lock_kernel(); ret = -EPERM; if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; int copied; copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); ret = -EIO; if (copied != sizeof(tmp)) break; ret = put_user(tmp,(unsigned long *) data); break; } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) break; tmp = get_reg(child, addr >> 2); ret = put_user(tmp, (unsigned long *)data); break; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) break; ret = -EIO; break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) break; addr >>= 2; if (addr == PT_DCCR) { /* don't allow the tracing process to change stuff like * interrupt enable, kernel/user bit, dma enables etc. */ data &= DCCR_MASK; data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; } if (put_reg(child, addr, data)) break; ret = 0; break; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: /* restart after signal. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; /* TODO: make sure any pending breakpoint is killed */ wake_up_process(child); ret = 0; break; /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* TODO: make sure any pending breakpoint is killed */ wake_up_process(child); break; case PTRACE_SINGLESTEP: /* set the trap flag. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; child->ptrace &= ~PT_TRACESYS; /* TODO: set some clever breakpoint mechanism... */ child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); ret = 0; break; case PTRACE_DETACH: ret = ptrace_detach(child, data); break; case PTRACE_GETREGS: { /* Get all gp regs from the child. */ int i; unsigned long tmp; for (i = 0; i <= PT_MAX; i++) { tmp = get_reg(child, i); if (put_user(tmp, (unsigned long *) data)) { ret = -EFAULT; break; } data += sizeof(long); } ret = 0; break; } case PTRACE_SETREGS: { /* Set all gp regs in the child. */ int i; unsigned long tmp; for (i = 0; i <= PT_MAX; i++) { if (get_user(tmp, (unsigned long *) data)) { ret = -EFAULT; break; } if (i == PT_DCCR) { tmp &= DCCR_MASK; tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; } put_reg(child, i, tmp); data += sizeof(long); } ret = 0; break; } default: ret = -EIO; break; } out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; }
asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret; lock_kernel(); ret = -EPERM; if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; switch (request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; ret = -EIO; if (access_process_vm(child, addr, &tmp, sizeof(tmp), 0) != sizeof(tmp)) break; ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); if (!ret) put_user(tmp, (unsigned long *) data); break ; } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) ret = -EIO; ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); if (ret) break ; tmp = 0; /* Default return condition */ addr = addr >> 2; /* temporary hack. */ if (addr < H8300_REGS_NO) tmp = h8300_get_reg(child, addr); else { switch(addr) { case 49: tmp = child->mm->start_code; break ; case 50: tmp = child->mm->start_data; break ; case 51: tmp = child->mm->end_code; break ; case 52: tmp = child->mm->end_data; break ; default: ret = -EIO; } } if (!ret) put_user(tmp,(unsigned long *) data); break ; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) break; ret = -EIO; break; case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) { ret = -EIO; break ; } addr = addr >> 2; /* temporary hack. */ if (addr == PT_ORIG_ER0) { ret = -EIO; break ; } if (addr < H8300_REGS_NO) { ret = h8300_put_reg(child, addr, data); break ; } ret = -EIO; break ; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ ret = -EIO; if ((unsigned long) data >= _NSIG) break ; if (request == PTRACE_SYSCALL) child->flags |= PT_TRACESYS; else child->flags &= ~PT_TRACESYS; child->exit_code = data; wake_up_process(child); /* make sure the single step bit is not set. */ h8300_disable_trace(child); ret = 0; } /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: { ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; h8300_disable_trace(child); wake_up_process(child); break; } case PTRACE_SINGLESTEP: { /* set the trap flag. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; child->ptrace &= ~PT_TRACESYS; child->exit_code = data; h8300_enable_trace(child); wake_up_process(child); ret = 0; break; } case PTRACE_DETACH: /* detach a process that was attached. */ ret = ptrace_detach(child, data); break; case PTRACE_GETREGS: { /* Get all gp regs from the child. */ int i; unsigned long tmp; for (i = 0; i < H8300_REGS_NO; i++) { tmp = h8300_get_reg(child, i); if (put_user(tmp, (unsigned long *) data)) { ret = -EFAULT; break; } data += sizeof(long); } ret = 0; break; } case PTRACE_SETREGS: { /* Set all gp regs in the child. */ int i; unsigned long tmp; for (i = 0; i < H8300_REGS_NO; i++) { if (get_user(tmp, (unsigned long *) data)) { ret = -EFAULT; break; } h8300_put_reg(child, i, tmp); data += sizeof(long); } ret = 0; break; } default: ret = -EIO; break; } out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; }
int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret = -EPERM; lock_kernel(); if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_tsk; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; int copied; copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); ret = -EIO; if (copied != sizeof(tmp)) break; ret = put_user(tmp,(unsigned long *) data); break; } /* read the word at location addr in the USER area. */ /* XXX this will need fixing for 64-bit */ case PTRACE_PEEKUSR: { unsigned long index, tmp; ret = -EIO; /* convert to index and check */ index = (unsigned long) addr >> 2; if ((addr & 3) || index > PT_FPSCR) break; if (index < PT_FPR0) { tmp = get_reg(child, (int) index); } else { if (child->thread.regs != NULL && child->thread.regs->msr & MSR_FP) giveup_fpu(child); tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; } ret = put_user(tmp,(unsigned long *) data); break; } /* If I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) break; ret = -EIO; break; /* write the word at location addr in the USER area */ /* XXX this will need fixing for 64-bit */ case PTRACE_POKEUSR: { unsigned long index; ret = -EIO; /* convert to index and check */ index = (unsigned long) addr >> 2; if ((addr & 3) || index > PT_FPSCR) break; if (index == PT_ORIG_R3) break; if (index < PT_FPR0) { ret = put_reg(child, index, data); } else { if (child->thread.regs != NULL && child->thread.regs->msr & MSR_FP) giveup_fpu(child); ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; ret = 0; } break; } case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); ret = 0; break; } /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: { ret = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ clear_single_step(child); wake_up_process(child); break; } case PTRACE_SINGLESTEP: { /* set the trap flag. */ ret = -EIO; if ((unsigned long) data > _NSIG) break; child->ptrace &= ~PT_TRACESYS; set_single_step(child); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); ret = 0; break; } case PTRACE_DETACH: ret = ptrace_detach(child, data); break; #ifdef CONFIG_ALTIVEC case PTRACE_GETVRREGS: /* Get the child altivec register state. */ if (child->thread.regs->msr & MSR_VEC) giveup_altivec(child); ret = get_vrregs((unsigned long *)data, child); break; case PTRACE_SETVRREGS: /* Set the child altivec register state. */ /* this is to clear the MSR_VEC bit to force a reload * of register state from memory */ if (child->thread.regs->msr & MSR_VEC) giveup_altivec(child); ret = set_vrregs(child, (unsigned long *)data); break; #endif default: ret = -EIO; break; } out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; }
long sys_ptrace(long request, pid_t pid, long addr, long data) { struct task_struct *child; long ret; lock_kernel(); ret = -EPERM; if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; ret = -EPERM; if (pid == 1) /* no messing around with init! */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = -ESRCH; if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; switch (request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp; int copied; copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); ret = -EIO; if (copied != sizeof(tmp)) goto out_tsk; ret = put_user(tmp,(unsigned long *) data); goto out_tsk; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) goto out_tsk; ret = -EIO; goto out_tsk; /* Read the word at location addr in the USER area. This will need to change when the kernel no longer saves all regs on a syscall. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs)) goto out_tsk; tmp = *(unsigned long *) ((char *) task_regs(child) + addr); ret = put_user(tmp, (unsigned long *) data); goto out_tsk; } /* Write the word at location addr in the USER area. This will need to change when the kernel no longer saves all regs on a syscall. FIXME. There is a problem at the moment in that r3-r18 are only saved if the process is ptraced on syscall entry, and even then those values are overwritten by actual register values on syscall exit. */ case PTRACE_POKEUSR: ret = -EIO; if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs)) goto out_tsk; /* XXX This test probably needs adjusting. We probably want to * allow writes to some bits of PSW, and may want to block writes * to (some) space registers. Some register values written here * may be ignored in entry.S:syscall_restore_rfi; e.g. iaoq is * written with r31/r31+4, and not with the values in pt_regs. */ /* Allow writing of gr1-gr31, fr*, sr*, iasq*, iaoq*, sar */ if (addr == PT_PSW || (addr > PT_IAOQ1 && addr != PT_SAR)) goto out_tsk; *(unsigned long *) ((char *) task_regs(child) + addr) = data; ret = 0; goto out_tsk; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_SINGLESTEP|PT_BLOCKSTEP); if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; goto out_wake_notrap; case PTRACE_KILL: /* * make the child exit. Best I can do is send it a * sigkill. perhaps it should be put in the status * that it wants to exit. */ if (child->state == TASK_ZOMBIE) /* already dead */ goto out_tsk; child->exit_code = SIGKILL; goto out_wake_notrap; case PTRACE_SINGLEBLOCK: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_TRACESYS|PT_SINGLESTEP); child->ptrace |= PT_BLOCKSTEP; child->exit_code = data; /* Enable taken branch trap. */ pa_psw(child)->r = 0; pa_psw(child)->t = 1; pa_psw(child)->h = 0; pa_psw(child)->l = 0; goto out_wake; case PTRACE_SINGLESTEP: ret = -EIO; if ((unsigned long) data > _NSIG) goto out_tsk; child->ptrace &= ~(PT_TRACESYS|PT_BLOCKSTEP); child->ptrace |= PT_SINGLESTEP; child->exit_code = data; if (pa_psw(child)->n) { struct siginfo si; /* Nullified, just crank over the queue. */ task_regs(child)->iaoq[0] = task_regs(child)->iaoq[1]; task_regs(child)->iasq[0] = task_regs(child)->iasq[1]; task_regs(child)->iaoq[1] = task_regs(child)->iaoq[0] + 4; pa_psw(child)->n = 0; pa_psw(child)->x = 0; pa_psw(child)->y = 0; pa_psw(child)->z = 0; pa_psw(child)->b = 0; pa_psw(child)->r = 0; pa_psw(child)->t = 0; pa_psw(child)->h = 0; pa_psw(child)->l = 0; /* Don't wake up the child, but let the parent know something happened. */ si.si_code = TRAP_TRACE; si.si_addr = (void *) (task_regs(child)->iaoq[0] & ~3); si.si_signo = SIGTRAP; si.si_errno = 0; force_sig_info(SIGTRAP, &si, child); //notify_parent(child, SIGCHLD); //ret = 0; goto out_wake; } /* Enable recovery counter traps. The recovery counter * itself will be set to zero on a task switch. If the * task is suspended on a syscall then the syscall return * path will overwrite the recovery counter with a suitable * value such that it traps once back in user space. We * disable interrupts in the childs PSW here also, to avoid * interrupts while the recovery counter is decrementing. */ pa_psw(child)->r = 1; pa_psw(child)->t = 0; pa_psw(child)->h = 0; pa_psw(child)->l = 0; /* give it a chance to run. */ goto out_wake; case PTRACE_DETACH: ret = ptrace_detach(child, data); goto out_tsk; default: ret = -EIO; goto out_tsk; } out_wake_notrap: /* make sure the trap bits are not set */ pa_psw(child)->r = 0; pa_psw(child)->t = 0; pa_psw(child)->h = 0; pa_psw(child)->l = 0; out_wake: wake_up_process(child); ret = 0; out_tsk: free_task_struct(child); out: unlock_kernel(); return ret; }
void free_task(struct task_struct *tsk) { free_thread_info(tsk->thread_info); free_task_struct(tsk); }
int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int rval; lock_kernel(); /* printk("sys_ptrace - PID:%u ADDR:%08x DATA:%08x\n",pid,addr,data); */ if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) { rval = -EPERM; goto out; } /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; rval = 0; goto out; } rval = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); if (!child) goto out; rval = -EPERM; if (pid == 1) /* you may not mess with init */ goto out; if (request == PTRACE_ATTACH) { rval = ptrace_attach(child); goto out_tsk; } rval = -ESRCH; if (!(child->ptrace & PT_PTRACED)) goto out_tsk; if (child->state != TASK_STOPPED) { if (request != PTRACE_KILL) goto out_tsk; } if (child->p_pptr != current) goto out_tsk; switch (request) { unsigned long val, copied; case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: /* printk("PEEKTEXT/PEEKDATA at %08X\n",addr); */ copied = access_process_vm(child, addr, &val, sizeof(val), 0); rval = -EIO; if (copied != sizeof(val)) break; rval = put_user(val, (unsigned long *)data); goto out; case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: /* printk("POKETEXT/POKEDATA to %08X\n",addr); */ rval = 0; if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) break; rval = -EIO; goto out; /* Read/write the word at location ADDR in the registers. */ case PTRACE_PEEKUSR: case PTRACE_POKEUSR: rval = 0; if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) { /* Special requests that don't actually correspond to offsets in struct pt_regs. */ if (addr == PT_TEXT_ADDR) { val = child->mm->start_code; } else if (addr == PT_DATA_ADDR) { val = child->mm->start_data; } else if (addr == PT_TEXT_LEN) { val = child->mm->end_code - child->mm->start_code; } else if (addr == PT_DATA_LEN) { val = child->mm->end_data - child->mm->start_data; } else { rval = -EIO; } } else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) { microblaze_reg_t *reg_addr = reg_save_addr(addr, child); if (request == PTRACE_PEEKUSR) { val = *reg_addr; } else { *reg_addr = data; } } else rval = -EIO; if (rval == 0 && request == PTRACE_PEEKUSR) { rval = put_user (val, (unsigned long *)data); } goto out; /* Continue and stop at next (return from) syscall */ case PTRACE_SYSCALL: /* Restart after a signal. */ case PTRACE_CONT: /* printk("PTRACE_CONT\n"); */ /* Execute a single instruction. */ case PTRACE_SINGLESTEP: /* printk("PTRACE_SINGLESTEP\n"); */ rval = -EIO; if ((unsigned long) data > _NSIG) break; /* Turn CHILD's single-step flag on or off. */ /* printk("calling set_single_step\n"); */ if (! set_single_step (child, request == PTRACE_SINGLESTEP)) break; if (request == PTRACE_SYSCALL) child->ptrace |= PT_TRACESYS; else child->ptrace &= ~PT_TRACESYS; child->exit_code = data; /* printk("wakeup_process\n"); */ wake_up_process(child); rval = 0; break; /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: /* printk("PTRACE_KILL\n"); */ rval = 0; if (child->state == TASK_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; wake_up_process(child); break; case PTRACE_DETACH: /* detach a process that was attached. */ /* printk("PTRACE_DETACH\n"); */ set_single_step (child, 0); /* Clear single-step flag */ rval = ptrace_detach(child, data); break; default: rval = -EIO; goto out; } out_tsk: free_task_struct(child); out: unlock_kernel(); return rval; }