diff options
Diffstat (limited to 'arch/ppc/kernel/process.c')
-rw-r--r-- | arch/ppc/kernel/process.c | 430 |
1 files changed, 105 insertions, 325 deletions
diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index f24872063..5e194fedb 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -7,7 +7,7 @@ * Derived from "arch/i386/kernel/process.c" * Copyright (C) 1995 Linus Torvalds * - * Modified by Cort Dougan (cort@cs.nmt.edu) and + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and * Paul Mackerras (paulus@cs.anu.edu.au) * * This program is free software; you can redistribute it and/or @@ -28,22 +28,22 @@ #include <linux/ptrace.h> #include <linux/malloc.h> #include <linux/user.h> -#include <linux/a.out.h> +#include <linux/elf.h> #include <linux/config.h> +#include <linux/elf.h> #include <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> #include <asm/smp_lock.h> +#include <asm/processor.h> -int dump_fpu(void); +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); void switch_to(struct task_struct *, struct task_struct *); -void print_backtrace(unsigned long *); -void show_regs(struct pt_regs * regs); -void inline zero_paged(void); extern unsigned long _get_SP(void); + #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 #undef IDLE_ZERO 1 @@ -59,7 +59,7 @@ task_top(struct task_struct *tsk) { return ((unsigned long)tsk) + sizeof(struct task_struct); } - + static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; @@ -69,9 +69,12 @@ struct mm_struct init_mm = INIT_MM; union task_union init_task_union = { INIT_TASK }; int -dump_fpu(void) +dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { - return (1); + if (last_task_used_math == current) + giveup_fpu(); + memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); + return 1; } /* check to make sure the kernel stack is healthy */ @@ -80,7 +83,6 @@ int check_stack(struct task_struct *tsk) unsigned long stack_top = kernel_stack_top(tsk); unsigned long tsk_top = task_top(tsk); int ret = 0; - unsigned long *i; #if 0 /* check tss magic */ @@ -98,7 +100,7 @@ int check_stack(struct task_struct *tsk) if ( (tsk->tss.ksp > stack_top) || (tsk->tss.ksp < tsk_top) ) { printk("stack out of bounds: %s/%d\n" - " tsk_top %08x ksp %08x stack_top %08x\n", + " tsk_top %08lx ksp %08lx stack_top %08lx\n", tsk->comm,tsk->pid, tsk_top, tsk->tss.ksp, stack_top); ret |= 2; @@ -108,7 +110,7 @@ int check_stack(struct task_struct *tsk) if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) { printk("current stack ptr out of bounds: %s/%d\n" - " tsk_top %08x sp %08x stack_top %08x\n", + " tsk_top %08lx sp %08lx stack_top %08lx\n", current->comm,current->pid, tsk_top, _get_SP(), stack_top); ret |= 4; @@ -143,15 +145,12 @@ switch_to(struct task_struct *prev, struct task_struct *new) { struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); - struct pt_regs *regs = (struct pt_regs *)(new->tss.ksp+STACK_FRAME_OVERHEAD); #if CHECK_STACK check_stack(prev); check_stack(new); #endif - /* turn off fpu for task last to run */ - /*prev->tss.regs->msr &= ~MSR_FP;*/ - + #ifdef SHOW_TASK_SWITCHES printk("%s/%d (%x) -> %s/%d (%x) ctx %x\n", prev->comm,prev->pid,prev->tss.regs->nip, @@ -160,289 +159,29 @@ switch_to(struct task_struct *prev, struct task_struct *new) new_tss = &new->tss; old_tss = ¤t->tss; _switch(old_tss, new_tss, new->mm->context); - /* turn off fpu for task last to run */ _enable_interrupts(s); } - -#include <linux/mc146818rtc.h> -asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) -{ -#if 1 - struct task_struct *p; - printk("sys_debug(): r3 %x r4 %x r5 %x r6 %x\n", a,b,c,d); - printk("last %x\n", last_task_used_math); - printk("cur %x regs %x/%x tss %x/%x\n", - current, current->tss.regs,regs,¤t->tss,current->tss); - for_each_task(p) - { - if ((long)p < KERNELBASE) - { - printk("nip %x lr %x r3 %x\n", regs->nip,regs->link,a); - print_mm_info(); - __cli(); - while(1); - } - } - return regs->gpr[3]; -#endif -#if 0 - /* set the time in the cmos clock */ - unsigned long hwtime, nowtime; - struct rtc_time tm; - - hwtime = get_cmos_time(); - to_tm(hwtime, &tm); - printk("hw: H:M:S M/D/Y %02d:%02d:%02d %d/%d/%d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_mon, - tm.tm_mday, tm.tm_year); - return; -#endif -} -/* - * vars for idle task zero'ing out pages - */ -unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ -unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ -unsigned long zerocount = 0; /* # currently pre-zero'd pages */ -unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ -unsigned long pageptr = 0; /* current page being zero'd */ -unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ - -/* - * Returns a pre-zero'd page from the list otherwise returns - * NULL. - */ -unsigned long get_prezerod_page(void) +asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) { - unsigned long page; - unsigned long s; - - if ( zero_list ) - { - /* atomically remove this page from the list */ - asm ( "101:lwarx %1,0,%2\n" /* reserve zero_list */ - " lwz %0,0(%1)\n" /* get next -- new zero_list */ - " stwcx. %0,0,%2\n" /* update zero_list */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_list), "=&r" (page) - : "r" (&zero_list) - : "cc" ); - /* we can update zerocount after the fact since it is not - * used for anything but control of a loop which doesn't - * matter since it won't effect anything if it zero's one - * less page -- Cort - */ - atomic_inc((atomic_t *)&zeropage_hits); - atomic_dec((atomic_t *)&zerocount); - /* zero out the pointer to next in the page */ - *(unsigned long *)page = 0; - return page; - } return 0; } -/* - * Experimental stuff to zero out pages in the idle task - * to speed up get_free_pages() -- Cort - * Zero's out pages until we need to resched or - * we've reached the limit of zero'd pages. - */ -void inline zero_paged(void) -{ - extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); - unsigned long tmp; - pte_t ptep; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - - sprintf(current->comm, "zero_paged"); - printk("Started zero_paged\n"); - /* want priority over idle task and powerd */ - current->priority = -98; - current->counter = -98; - __sti(); - - while ( zerocount < 128 ) - { - /* - * Mark a page as reserved so we can mess with it - * If we're interrupted we keep this page and our place in it - * since we validly hold it and it's reserved for us. - */ - pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); - if ( !pageptr ) - { - printk("!pageptr in zero_paged\n"); - goto retry; - } - - if ( need_resched ) - schedule(); - - /* - * Make the page no cache so we don't blow our cache with 0's - */ - dir = pgd_offset( init_task.mm, pageptr ); - if (dir) - { - pmd = pmd_offset(dir, pageptr & PAGE_MASK); - if (pmd && pmd_present(*pmd)) - { - pte = pte_offset(pmd, pageptr & PAGE_MASK); - if (pte && pte_present(*pte)) - { - pte_uncache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); - } - } - } - - /* - * Important here to not take time away from real processes. - */ - for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) - { - if ( need_resched ) - schedule(); - *(unsigned long *)(bytecount + pageptr) = 0; - } - - /* - * If we finished zero-ing out a page add this page to - * the zero_list atomically -- we can't use - * down/up since we can't sleep in idle. - * Disabling interrupts is also a bad idea since we would - * steal time away from real processes. - * We can also have several zero_paged's running - * on different processors so we can't interfere with them. - * So we update the list atomically without locking it. - * -- Cort - */ - /* turn cache on for this page */ - pte_cache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); - - /* atomically add this page to the list */ - asm ( "101:lwarx %0,0,%1\n" /* reserve zero_list */ - " stw %0,0(%2)\n" /* update *pageptr */ -#ifdef __SMP__ - " sync\n" /* let store settle */ -#endif - " mr %0,%2\n" /* update zero_list in reg */ - " stwcx. %2,0,%1\n" /* update zero_list in mem */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_list) - : "r" (&zero_list), "r" (pageptr) - : "cc" ); - /* - * This variable is used in the above loop and nowhere - * else so the worst that could happen is we would - * zero out one more or one less page than we want - * per processor on the machine. This is because - * we could add our page to the list but not have - * zerocount updated yet when another processor - * reads it. -- Cort - */ - atomic_inc((atomic_t *)&zerocount); - atomic_inc((atomic_t *)&zerototal); -retry: - schedule(); - } -} - -void powerd(void) -{ - unsigned long msr, hid0; - - sprintf(current->comm, "powerd"); - __sti(); - while (1) - { - /* want priority over idle task 'swapper' -- Cort */ - current->priority = -99; - current->counter = -99; - asm volatile( - /* clear powersaving modes and set nap mode */ - "mfspr %3,1008 \n\t" - "andc %3,%3,%4 \n\t" - "or %3,%3,%5 \n\t" - "mtspr 1008,%3 \n\t" - /* enter the mode */ - "mfmsr %0 \n\t" - "oris %0,%0,%2 \n\t" - "sync \n\t" - "mtmsr %0 \n\t" - "isync \n\t" - : "=&r" (msr) - : "0" (msr), "i" (MSR_POW>>16), - "r" (hid0), - "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), - "r" (HID0_NAP)); - if ( need_resched ) - schedule(); - /* - * The ibm carolina spec says that the eagle memory - * controller will detect the need for a snoop - * and wake up the processor so we don't need to - * check for cache operations that need to be - * snooped. The ppc book says the run signal - * must be asserted while napping for this though. - * -- Cort - */ - } -} - -asmlinkage int sys_idle(void) -{ - int ret = -EPERM; - if (current->pid != 0) - goto out; - -#ifdef IDLE_ZERO - /* - * want one per cpu since it would be nice to have all - * processors who aren't doing anything - * zero-ing pages since this daemon is lock-free - * -- Cort - */ - kernel_thread(zero_paged, NULL, 0); -#endif /* IDLE_ZERO */ - -#ifdef CONFIG_POWERSAVING - /* no powersaving modes on 601 - one per processor */ - if( (_get_PVR()>>16) != 1 ) - kernel_thread(powerd, NULL, 0); -#endif /* CONFIG_POWERSAVING */ - - /* endless loop with no priority at all */ - current->priority = -100; - current->counter = -100; - for (;;) - { - schedule(); - } - ret = 0; -out: - return ret; -} - void show_regs(struct pt_regs * regs) { int i; - printk("NIP: %08X XER: %08X LR: %08X REGS: %08X TRAP: %04x\n", + printk("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx\n", regs->nip, regs->xer, regs->link, regs,regs->trap); - printk("MSR: %08x EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + printk("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, regs->msr&MSR_IR ? 1 : 0, regs->msr&MSR_DR ? 1 : 0); - printk("TASK = %x[%d] '%s' mm->pgd %08X ", + printk("TASK = %p[%d] '%s' mm->pgd %p ", current, current->pid, current->comm, current->mm->pgd); - printk("Last syscall: %d ", current->tss.last_syscall); - printk("\nlast math %08X\n", last_task_used_math); + printk("Last syscall: %ld ", current->tss.last_syscall); + printk("\nlast math %p\n", last_task_used_math); for (i = 0; i < 32; i++) { long r; @@ -451,9 +190,9 @@ void show_regs(struct pt_regs * regs) printk("GPR%02d: ", i); } - if ( get_user(r, &(regs->gpr[i])) ) + if ( __get_user(r, &(regs->gpr[i])) ) goto out; - printk("%08X ", r); + printk("%08lX ", r); if ((i % 8) == 7) { printk("\n"); @@ -480,14 +219,14 @@ release_thread(struct task_struct *t) } /* - * Copy a thread.. - */ + * Copy a thread.. + */ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { - int i; struct pt_regs * childregs; + /* Copy registers */ childregs = ((struct pt_regs *) ((unsigned long)p + sizeof(union task_union) @@ -497,13 +236,13 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, if ((childregs->msr & MSR_PR) == 0) childregs->gpr[2] = (unsigned long) p; /* `current' in new task */ childregs->gpr[3] = 0; /* Result from fork() */ - p->tss.ksp = (unsigned long)(childregs) - STACK_FRAME_OVERHEAD; - p->tss.regs = (struct pt_regs *)(childregs); - if (usp >= (unsigned long)regs) - { /* Stack is in kernel space - must adjust */ - childregs->gpr[1] = (long)(childregs+1); - } else - { /* Provided stack is in user space */ + p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->tss.regs = childregs; + if (usp >= (unsigned long) regs) { + /* Stack is in kernel space - must adjust */ + childregs->gpr[1] = (unsigned long)(childregs + 1); + } else { + /* Provided stack is in user space */ childregs->gpr[1] = usp; } @@ -511,24 +250,75 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, /* * copy fpu info - assume lazy fpu switch now always - * this should really be conditional on whether or - * not the process has used the fpu * -- Cort */ if ( last_task_used_math == current ) giveup_fpu(); - + memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); p->tss.fpscr = current->tss.fpscr; childregs->msr &= ~MSR_FP; - + return 0; } +/* + * XXX ld.so expects the auxiliary table to start on + * a 16-byte boundary, so we have to find it and + * move it up. :-( + */ +static inline void shove_aux_table(unsigned long sp) +{ + int argc; + char *p; + unsigned long e; + unsigned long aux_start, offset; + + if (__get_user(argc, (int *)sp)) + return; + sp += sizeof(int) + (argc + 1) * sizeof(char *); + /* skip over the environment pointers */ + do { + if (__get_user(p, (char **)sp)) + return; + sp += sizeof(char *); + } while (p != NULL); + aux_start = sp; + /* skip to the end of the auxiliary table */ + do { + if (__get_user(e, (unsigned long *)sp)) + return; + sp += 2 * sizeof(unsigned long); + } while (e != AT_NULL); + offset = ((aux_start + 15) & ~15) - aux_start; + if (offset != 0) { + do { + sp -= sizeof(unsigned long); + if (__get_user(e, (unsigned long *)sp) + || __put_user(e, (unsigned long *)(sp + offset))) + return; + } while (sp > aux_start); + } +} + +/* + * Set up a thread for executing a new program + */ +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER; + shove_aux_table(sp); +} + + asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { int ret; + lock_kernel(); ret = do_fork(SIGCHLD, regs->gpr[1], regs); unlock_kernel(); @@ -541,26 +331,26 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, { int error; char * filename; - filename = (int) getname((char *) a0); + + filename = getname((char *) a0); error = PTR_ERR(filename); - if(IS_ERR(filename)) + if (IS_ERR(filename)) goto out; if ( last_task_used_math == current ) last_task_used_math = NULL; error = do_execve(filename, (char **) a1, (char **) a2, regs); - putname(filename); - out: unlock_kernel(); return error; } -asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) { unsigned long clone_flags = p1; int res; - + lock_kernel(); res = do_fork(clone_flags, regs->gpr[1], regs); unlock_kernel(); @@ -571,34 +361,23 @@ void print_backtrace(unsigned long *sp) { int cnt = 0; - int i; + unsigned long i; + printk("Call backtrace: "); - while ( !get_user(i, sp) && i) - { - if ( get_user( i, &sp[1] ) ) - return; - printk("%08X ", i); - if ( get_user( (ulong)sp, sp) ) - return; - if (cnt == 6 ) cnt = 7; /* wraparound early -- Cort */ - if (++cnt == 8) - { + while (sp) { + if (__get_user( i, &sp[1] )) + break; + if (cnt++ % 7 == 0) printk("\n"); - } + printk("%08lX ", i); if (cnt > 32) break; + if (__get_user(sp, (unsigned long **)sp)) + break; } printk("\n"); } -inline void start_thread(struct pt_regs * regs, - unsigned long eip, unsigned long esp) -{ - set_fs(USER_DS); - regs->nip = eip; - regs->gpr[1] = esp; - regs->msr = MSR_USER; -} - +#if 0 /* * Low level print for debugging - Cort */ @@ -651,3 +430,4 @@ void ll_puts(const char *s) orig_x = x; orig_y = y; } +#endif /* CONFIG_PREP */ |