LOGIN   :::   RECOVER PASS   :::   GET ACCOUNT    
Browse
  • Projects
  • Code (CVS)
  • Forums
  • News
  • Articles
  • Polls
  •  
    OpenCores
  • FAQ
  • CVS HowTo
  • Mission
  • Media
  • Tools
  • Advertise
  • Mirrors
  • Logos
  • Contact us
  • Job Opportunity
  •  
    Tools
  • Search
      
  • Download Cores (CVSGet)
  •  
    More
  • Wishbone
  • Perlilog
  • EDA tools
  • OpenTech CD
  •  
    Navigation: All forums > Cvs-checkins > Message List > Message Post

    Message

    Reply | Reply all
    Date Prev | Date Next | Thread Prev | Thread Next Date Index | Thread Index

    From: cvs at opencores.org<cvs@o...>
    Date: Tue Dec 20 12:46:59 CET 2005
    Subject: [cvs-checkins] MODIFIED: or1k ...
    Top
    Date: 00/05/12 20:12:46

    Added: or1k/rc203soc/sw/uClinux/kernel Makefile dma.c exec_domain.c
    exit.c fork.c info.c itimer.c ksyms.c module.c
    panic.c printk.c resource.c sched.c signal.c
    softirq.c sys.c sysctl.c time.c
    Log:
    First Import of RC20x uClinux


    Revision Changes Path
    1.1 or1k/rc203soc/sw/uClinux/kernel/Makefile

    http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/Makefile?rev=1.1&content-type=text/x-cvsweb-markup

    Index: Makefile
    ===================================================================
    #
    # Makefile for the linux kernel.
    #
    # Note! Dependencies are done automagically by 'make dep', which also
    # removes any old dependencies. DON'T put your own dependencies here
    # unless it's something special (ie not a .c file).
    #
    # Note 2! The CFLAGS definitions are now in the main makefile...

    .S.s:
    $(CPP) -traditional $< -o $*.s

    O_TARGET := kernel.o
    O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \
    module.o exit.o signal.o itimer.o info.o time.o softirq.o \
    resource.o sysctl.o

    ifeq ($(CONFIG_MODULES),y)
    OX_OBJS = ksyms.o
    endif

    include $(TOPDIR)/Rules.make

    sched.o: sched.c
    $(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $<



    1.1 or1k/rc203soc/sw/uClinux/kernel/dma.c

    http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/dma.c?rev=1.1&content-type=text/x-cvsweb-markup

    Index: dma.c
    ===================================================================
    /* $Id: dma.c,v 1.1 2005/12/20 11:46:56 jcastillo Exp $
    * linux/kernel/dma.c: A DMA channel allocator. Inspired by linux/kernel/irq.c.
    *
    * Written by Hennus Bergman, 1992.
    *
    * 1994/12/26: Changes by Alex Nash to fix a minor bug in /proc/dma.
    * In the previous version the reported device could end up being wrong,
    * if a device requested a DMA channel that was already in use.
    * [It also happened to remove the sizeof(char *) == sizeof(int)
    * assumption introduced because of those /proc/dma patches. -- Hennus]
    */

    #include <linux/kernel.h>
    #include <linux/errno.h>
    #include <asm/dma.h>
    #include <asm/system.h>


    /* A note on resource allocation:
    *
    * All drivers needing DMA channels, should allocate and release them
    * through the public routines `request_dma()' and `free_dma()'.
    *
    * In order to avoid problems, all processes should allocate resources in
    * the same sequence and release them in the reverse order.
    *
    * So, when allocating DMAs and IRQs, first allocate the IRQ, then the DMA.
    * When releasing them, first release the DMA, then release the IRQ.
    * If you don't, you may cause allocation requests to fail unnecessarily.
    * This doesn't really matter now, but it will once we get real semaphores
    * in the kernel.
    */



    /* Channel n is busy iff dma_chan_busy[n].lock != 0.
    * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
    * DMA4 is reserved for cascading.
    */

    struct dma_chan {
    int lock;
    const char *device_id;
    };

    static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = {
    { 0, 0 },
    #ifdef CONFIG_M5307
    { 0, 0 },
    { 0, 0 },
    #endif
    #ifndef CONFIG_UCLINUX { 0, 0 }, { 0, 0 }, { 1, "cascade" }, { 0, 0 }, { 0, 0 }, #endif { 0, 0 } }; int get_dma_list(char *buf) { int i, len = 0; for (i = 0 ; i < MAX_DMA_CHANNELS ; i++) { if (dma_chan_busy[i].lock) { len += sprintf(buf+len, "%2d: %s\n", i, dma_chan_busy[i].device_id); } } return len; } /* get_dma_list */ int request_dma(unsigned int dmanr, const char * device_id) { if (dmanr >= MAX_DMA_CHANNELS) return -EINVAL; if (xchg(&dma_chan_busy[dmanr].lock, 1) != 0) return -EBUSY; dma_chan_busy[dmanr].device_id = device_id; /* old flag was 0, now contains 1 to indicate busy */ return 0; } /* request_dma */ void free_dma(unsigned int dmanr) { if (dmanr >= MAX_DMA_CHANNELS) { printk("Trying to free DMA%d\n", dmanr); return; } if (xchg(&dma_chan_busy[dmanr].lock, 0) == 0) { printk("Trying to free free DMA%d\n", dmanr); return; } } /* free_dma */ 1.1 or1k/rc203soc/sw/uClinux/kernel/exec_domain.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/exec_domain.c?rev=1.1&content-type=text/x-cvsweb-markup Index: exec_domain.c =================================================================== #include <linux/personality.h> #include <linux/ptrace.h> #include <linux/sched.h> #include <linux/mm.h> static asmlinkage void no_lcall7(struct pt_regs * regs); static unsigned long ident_map[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; struct exec_domain default_exec_domain = { "Linux", /* name */ no_lcall7, /* lcall7 causes a seg fault. */ 0, 0xff, /* All personalities. */ ident_map, /* Identity map signals. */ ident_map, /* - both ways. */ NULL, /* No usage counter. */ NULL /* Nothing after this in the list. */ }; static struct exec_domain *exec_domains = &default_exec_domain; static asmlinkage void no_lcall7(struct pt_regs * regs) { /* * This may have been a static linked SVr4 binary, so we would have the * personality set incorrectly. Check to see whether SVr4 is available, * and use it, otherwise give the user a SEGV. */ if (current->exec_domain && current->exec_domain->use_count) (*current->exec_domain->use_count)--; current->personality = PER_SVR4; current->exec_domain = lookup_exec_domain(current->personality); if (current->exec_domain && current->exec_domain->use_count) (*current->exec_domain->use_count)++; if (current->exec_domain && current->exec_domain->handler && current->exec_domain->handler != no_lcall7) { current->exec_domain->handler(regs); return; } send_sig(SIGSEGV, current, 1); } struct exec_domain *lookup_exec_domain(unsigned long personality) { unsigned long pers = personality & PER_MASK; struct exec_domain *it; for (it=exec_domains; it; it=it->next) if (pers >= it->pers_low && pers <= it->pers_high) return it; /* Should never get this far. */ printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers); return NULL; } int register_exec_domain(struct exec_domain *it) { struct exec_domain *tmp; if (!it) return -EINVAL; if (it->next) return -EBUSY; for (tmp=exec_domains; tmp; tmp=tmp->next) if (tmp == it) return -EBUSY; it->next = exec_domains; exec_domains = it; return 0; } int unregister_exec_domain(struct exec_domain *it) { struct exec_domain ** tmp; tmp = &exec_domains; while (*tmp) { if (it == *tmp) { *tmp = it->next; it->next = NULL; return 0; } tmp = &(*tmp)->next; } return -EINVAL; } asmlinkage int sys_personality(unsigned long personality) { struct exec_domain *it; unsigned long old_personality; if (personality == 0xffffffff) return current->personality; it = lookup_exec_domain(personality); if (!it) return -EINVAL; old_personality = current->personality; if (current->exec_domain && current->exec_domain->use_count) (*current->exec_domain->use_count)--; current->personality = personality; current->exec_domain = it; if (current->exec_domain->use_count) (*current->exec_domain->use_count)++; return old_personality; } 1.1 or1k/rc203soc/sw/uClinux/kernel/exit.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/exit.c?rev=1.1&content-type=text/x-cvsweb-markup Index: exit.c =================================================================== /* * linux/kernel/exit.c * * Copyright (C) 1991, 1992 Linus Torvalds */ /* * uClinux revisions for NO_MM * Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>, * The Silver Hammer Group, Ltd. */ #undef DEBUG_PROC_TREE #include <linux/wait.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/resource.h> #include <linux/mm.h> #include <linux/tty.h> #include <linux/malloc.h> #include <linux/interrupt.h> #include <asm/segment.h> #include <asm/pgtable.h> extern void sem_exit (void); extern int acct_process (long exitcode); extern void kerneld_exit(void); int getrusage(struct task_struct *, int, struct rusage *); static inline void generate(unsigned long sig, struct task_struct * p) { unsigned long flags; unsigned long mask = 1 << (sig-1); struct sigaction * sa = sig + p->sig->action - 1; /* * Optimize away the signal, if it's a signal that can * be handled immediately (ie non-blocked and untraced) * and that is ignored (either explicitly or by default) */ save_flags(flags); cli(); if (!(mask & p->blocked) && !(p->flags & PF_PTRACED)) { /* don't bother with ignored signals (but SIGCHLD is special) */ if (sa->sa_handler == SIG_IGN && sig != SIGCHLD) { restore_flags(flags); return; } /* some signals are ignored by default.. (but SIGCONT already did its deed) */ if ((sa->sa_handler == SIG_DFL) && (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH || sig == SIGURG)) { restore_flags(flags); return; } } p->signal |= mask; if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked)) wake_up_process(p); restore_flags(flags); } /* * Force a signal that the process can't ignore: if necessary * we unblock the signal and change any SIG_IGN to SIG_DFL. */ void force_sig(unsigned long sig, struct task_struct * p) { sig--; if (p->sig) { unsigned long flags; unsigned long mask = 1UL << sig; struct sigaction *sa = p->sig->action + sig; save_flags(flags); cli(); p->signal |= mask; p->blocked &= ~mask; if (sa->sa_handler == SIG_IGN) sa->sa_handler = SIG_DFL; if (p->state == TASK_INTERRUPTIBLE) wake_up_process(p); restore_flags(flags); } } int send_sig(unsigned long sig,struct task_struct * p,int priv) { unsigned long flags; if (!p || sig > 32) return -EINVAL; if (!priv && ((sig != SIGCONT) || (current->session != p->session)) && (current->euid ^ p->suid) && (current->euid ^ p->uid) && (current->uid ^ p->suid) && (current->uid ^ p->uid) && !suser()) return -EPERM; if (!sig) return 0; /* * Forget it if the process is already zombie'd. */ if (!p->sig) return 0; save_flags(flags); cli(); if ((sig == SIGKILL) || (sig == SIGCONT)) { if (p->state == TASK_STOPPED) wake_up_process(p); p->exit_code = 0; p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) | (1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) ); } if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) p->signal &= ~(1<<(SIGCONT-1)); restore_flags(flags); /* Actually generate the signal */ generate(sig,p); return 0; } void notify_parent(struct task_struct * tsk, int signal) { send_sig(signal, tsk->p_pptr, 1); wake_up_interruptible(&tsk->p_pptr->wait_chldexit); } void release(struct task_struct * p) { int i; if (!p) return; if (p == current) { printk("task releasing itself\n"); return; } for (i=1 ; i<NR_TASKS ; i++) if (task[i] == p) { nr_tasks--; task[i] = NULL; REMOVE_LINKS(p); release_thread(p); if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page) printk(KERN_ALERT "release: %s kernel stack corruption. Aiee\n", p->comm); free_kernel_stack(p->kernel_stack_page); current->cmin_flt += p->min_flt + p->cmin_flt; current->cmaj_flt += p->maj_flt + p->cmaj_flt; current->cnswap += p->nswap + p->cnswap; kfree(p); return; } panic("trying to release non-existent task"); } #ifdef DEBUG_PROC_TREE /* * Check to see if a task_struct pointer is present in the task[] array * Return 0 if found, and 1 if not found. */ int bad_task_ptr(struct task_struct *p) { int i; if (!p) return 0; for (i=0 ; i<NR_TASKS ; i++) if (task[i] == p) return 0; return 1; } /* * This routine scans the pid tree and makes sure the rep invariant still * holds. Used for debugging only, since it's very slow.... * * It looks a lot scarier than it really is.... we're doing nothing more * than verifying the doubly-linked list found in p_ysptr and p_osptr, * and checking it corresponds with the process tree defined by p_cptr and * p_pptr; */ void audit_ptree(void) { int i; for (i=1 ; i<NR_TASKS ; i++) { if (!task[i]) continue; if (bad_task_ptr(task[i]->p_pptr)) printk("Warning, pid %d's parent link is bad\n", task[i]->pid); if (bad_task_ptr(task[i]->p_cptr)) printk("Warning, pid %d's child link is bad\n", task[i]->pid); if (bad_task_ptr(task[i]->p_ysptr)) printk("Warning, pid %d's ys link is bad\n", task[i]->pid); if (bad_task_ptr(task[i]->p_osptr)) printk("Warning, pid %d's os link is bad\n", task[i]->pid); if (task[i]->p_pptr == task[i]) printk("Warning, pid %d parent link points to self\n", task[i]->pid); if (task[i]->p_cptr == task[i]) printk("Warning, pid %d child link points to self\n", task[i]->pid); if (task[i]->p_ysptr == task[i]) printk("Warning, pid %d ys link points to self\n", task[i]->pid); if (task[i]->p_osptr == task[i]) printk("Warning, pid %d os link points to self\n", task[i]->pid); if (task[i]->p_osptr) { if (task[i]->p_pptr != task[i]->p_osptr->p_pptr) printk( "Warning, pid %d older sibling %d parent is %d\n", task[i]->pid, task[i]->p_osptr->pid, task[i]->p_osptr->p_pptr->pid); if (task[i]->p_osptr->p_ysptr != task[i]) printk( "Warning, pid %d older sibling %d has mismatched ys link\n", task[i]->pid, task[i]->p_osptr->pid); } if (task[i]->p_ysptr) { if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr) printk( "Warning, pid %d younger sibling %d parent is %d\n", task[i]->pid, task[i]->p_osptr->pid, task[i]->p_osptr->p_pptr->pid); if (task[i]->p_ysptr->p_osptr != task[i]) printk( "Warning, pid %d younger sibling %d has mismatched os link\n", task[i]->pid, task[i]->p_ysptr->pid); } if (task[i]->p_cptr) { if (task[i]->p_cptr->p_pptr != task[i]) printk( "Warning, pid %d youngest child %d has mismatched parent link\n", task[i]->pid, task[i]->p_cptr->pid); if (task[i]->p_cptr->p_ysptr) printk( "Warning, pid %d youngest child %d has non-NULL ys link\n", task[i]->pid, task[i]->p_cptr->pid); } } } #endif /* DEBUG_PROC_TREE */ /* * This checks not only the pgrp, but falls back on the pid if no * satisfactory pgrp is found. I dunno - gdb doesn't work correctly * without this... */ int session_of_pgrp(int pgrp) { struct task_struct *p; int fallback; fallback = -1; for_each_task(p) { if (p->session <= 0) continue; if (p->pgrp == pgrp) return p->session; if (p->pid == pgrp) fallback = p->session; } return fallback; } /* * kill_pg() sends a signal to a process group: this is what the tty * control characters do (^C, ^Z etc) */ int kill_pg(int pgrp, int sig, int priv) { struct task_struct *p; int err,retval = -ESRCH; int found = 0; if (sig<0 || sig>32 || pgrp<=0) return -EINVAL; for_each_task(p) { if (p->pgrp == pgrp) { if ((err = send_sig(sig,p,priv)) != 0) retval = err; else found++; } } return(found ? 0 : retval); } /* * kill_sl() sends a signal to the session leader: this is used * to send SIGHUP to the controlling process of a terminal when * the connection is lost. */ int kill_sl(int sess, int sig, int priv) { struct task_struct *p; int err,retval = -ESRCH; int found = 0; if (sig<0 || sig>32 || sess<=0) return -EINVAL; for_each_task(p) { if (p->session == sess && p->leader) { if ((err = send_sig(sig,p,priv)) != 0) retval = err; else found++; } } return(found ? 0 : retval); } int kill_proc(int pid, int sig, int priv) { struct task_struct *p; if (sig<0 || sig>32) return -EINVAL; for_each_task(p) { if (p && p->pid == pid) return send_sig(sig,p,priv); } return(-ESRCH); } /* * POSIX specifies that kill(-1,sig) is unspecified, but what we have * is probably wrong. Should make it like BSD or SYSV. */ asmlinkage int sys_kill(int pid,int sig) { int err, retval = 0, count = 0; if (!pid) return(kill_pg(current->pgrp,sig,0)); if (pid == -1) { struct task_struct * p; for_each_task(p) { if (p->pid > 1 && p != current) { ++count; if ((err = send_sig(sig,p,0)) != -EPERM) retval = err; } } return(count ? retval : -ESRCH); } if (pid < 0) return(kill_pg(-pid,sig,0)); /* Normal kill */ return(kill_proc(pid,sig,0)); } /* * Determine if a process group is "orphaned", according to the POSIX * definition in 2.2.2.52. Orphaned process groups are not to be affected * by terminal-generated stop signals. Newly orphaned process groups are * to receive a SIGHUP and a SIGCONT. * * "I ask you, have you ever known what it is to be an orphan?" */ static int will_become_orphaned_pgrp(int pgrp, struct task_struct * ignored_task) { struct task_struct *p; for_each_task(p) { if ((p == ignored_task) || (p->pgrp != pgrp) || (p->state == TASK_ZOMBIE) || (p->p_pptr->pid == 1)) continue; if ((p->p_pptr->pgrp != pgrp) && (p->p_pptr->session == p->session)) return 0; } return(1); /* (sighing) "Often!" */ } int is_orphaned_pgrp(int pgrp) { return will_become_orphaned_pgrp(pgrp, 0); } static inline int has_stopped_jobs(int pgrp) { struct task_struct * p; for_each_task(p) { if (p->pgrp != pgrp) continue; if (p->state == TASK_STOPPED) return(1); } return(0); } static inline void forget_original_parent(struct task_struct * father) { struct task_struct * p; for_each_task(p) { if (p->p_opptr == father) { p->exit_signal = SIGCHLD; p->p_opptr = task[smp_num_cpus] ? : task[0]; /* init */ } } } static inline void close_files(struct files_struct * files) { int i, j; j = 0; for (;;) { unsigned long set = files->open_fds.fds_bits[j]; i = j * __NFDBITS; j++; if (i >= NR_OPEN) break; while (set) { if (set & 1) { struct file * file = files->fd[i]; if (file) { files->fd[i] = NULL; close_fp(file); } } i++; set >>= 1; } } } static inline void __exit_files(struct task_struct *tsk) { struct files_struct * files = tsk->files; if (files) { tsk->files = NULL; if (!--files->count) { close_files(files); kfree(files); } } } void exit_files(struct task_struct *tsk) { __exit_files(tsk); } static inline void __exit_fs(struct task_struct *tsk) { struct fs_struct * fs = tsk->fs; if (fs) { tsk->fs = NULL; if (!--fs->count) { iput(fs->root); iput(fs->pwd); kfree(fs); } } } void exit_fs(struct task_struct *tsk) { __exit_fs(tsk); } static inline void __exit_sighand(struct task_struct *tsk) { struct signal_struct * sig = tsk->sig; if (sig) { tsk->sig = NULL; if (!--sig->count) { kfree(sig); } } } void exit_sighand(struct task_struct *tsk) { __exit_sighand(tsk); } static inline void __exit_mm(struct task_struct * tsk) { #ifndef NO_MM struct mm_struct * mm = tsk->mm; /* Set us up to use the kernel mm state */ if (mm != &init_mm) { flush_cache_mm(mm); flush_tlb_mm(mm); tsk->mm = &init_mm; tsk->swappable = 0; SET_PAGE_DIR(tsk, swapper_pg_dir); /* free the old state - not used any more */ if (!--mm->count) { exit_mmap(mm); free_page_tables(mm); kfree(mm); } } #else /* NO_MM */ struct mm_struct * mm = tsk->mm; /* Set us up to use the kernel mm state */ if (mm != &init_mm) { /* Wake up parent that vforked me */ wake_up(&tsk->p_opptr->mm->vforkwait); tsk->mm = &init_mm; tsk->swappable = 0; /* free the old state - not used any more */ if (!--mm->count) { exit_mmap(mm); kfree(mm); } } #endif /* NO_MM */ } void exit_mm(struct task_struct *tsk) { __exit_mm(tsk); } /* * Send signals to all our closest relatives so that they know * to properly mourn us.. */ static void exit_notify(void) { struct task_struct * p; forget_original_parent(current); /* * Check to see if any process groups have become orphaned * as a result of our exiting, and if they have any stopped * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) * * Case i: Our father is in a different pgrp than we are * and we were the only connection outside, so our pgrp * is about to become orphaned. */ if ((current->p_pptr->pgrp != current->pgrp) && (current->p_pptr->session == current->session) && will_become_orphaned_pgrp(current->pgrp, current) && has_stopped_jobs(current->pgrp)) { kill_pg(current->pgrp,SIGHUP,1); kill_pg(current->pgrp,SIGCONT,1); } /* Let father know we died */ notify_parent(current, current->exit_signal); /* * This loop does two things: * * A. Make init inherit all the child processes * B. Check to see if any process groups have become orphaned * as a result of our exiting, and if they have any stopped * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) */ while ((p = current->p_cptr) != NULL) { current->p_cptr = p->p_osptr; p->p_ysptr = NULL; p->flags &= ~(PF_PTRACED|PF_TRACESYS); p->p_pptr = p->p_opptr; p->p_osptr = p->p_pptr->p_cptr; if (p->p_osptr) p->p_osptr->p_ysptr = p; p->p_pptr->p_cptr = p; if (p->state == TASK_ZOMBIE) notify_parent(p, p->exit_signal); /* * process group orphan check * Case ii: Our child is in a different pgrp * than we are, and it was the only connection * outside, so the child pgrp is now orphaned. */ if ((p->pgrp != current->pgrp) && (p->session == current->session) && is_orphaned_pgrp(p->pgrp) && has_stopped_jobs(p->pgrp)) { kill_pg(p->pgrp,SIGHUP,1); kill_pg(p->pgrp,SIGCONT,1); } } if (current->leader) disassociate_ctty(1); } NORET_TYPE void do_exit(long code) { if (intr_count) { printk("Aiee, killing interrupt handler\n"); intr_count = 0; } fake_volatile: acct_process(code); current->flags |= PF_EXITING; del_timer(&current->real_timer); sem_exit(); kerneld_exit(); __exit_mm(current); __exit_files(current); __exit_fs(current); __exit_sighand(current); exit_thread(); current->state = TASK_ZOMBIE; current->exit_code = code; exit_notify(); #ifdef DEBUG_PROC_TREE audit_ptree(); #endif if (current->exec_domain && current->exec_domain->use_count) (*current->exec_domain->use_count)--; if (current->binfmt && current->binfmt->use_count) (*current->binfmt->use_count)--; schedule(); /* * In order to get rid of the "volatile function does return" message * I did this little loop that confuses gcc to think do_exit really * is volatile. In fact it's schedule() that is volatile in some * circumstances: when current->state = ZOMBIE, schedule() never * returns. * * In fact the natural way to do all this is to have the label and the * goto right after each other, but I put the fake_volatile label at * the start of the function just in case something /really/ bad * happens, and the schedule returns. This way we can try again. I'm * not paranoid: it's just that everybody is out to get me. */ goto fake_volatile; } asmlinkage int sys_exit(int error_code) { do_exit((error_code&0xff)<<8); } asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru) { int flag, retval; struct wait_queue wait = { current, NULL }; struct task_struct *p; if (stat_addr) { flag = verify_area(VERIFY_WRITE, stat_addr, sizeof(*stat_addr)); if (flag) return flag; } if (ru) { flag = verify_area(VERIFY_WRITE, ru, sizeof(*ru)); if (flag) return flag; } if (options & ~(WNOHANG|WUNTRACED|__WCLONE)) return -EINVAL; add_wait_queue(&current->wait_chldexit,&wait); repeat: flag=0; for (p = current->p_cptr ; p ; p = p->p_osptr) { if (pid>0) { if (p->pid != pid) continue; } else if (!pid) { if (p->pgrp != current->pgrp) continue; } else if (pid != -1) { if (p->pgrp != -pid) continue; } /* If you are tracing a process, then you don't need to get the * WCLONE bit right -- useful for strace and gdb */ if (!(p->flags & (PF_PTRACED|PF_TRACESYS))) { /* wait for cloned processes iff the __WCLONE flag is set */ if ((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0)) continue; } flag = 1; switch (p->state) { case TASK_STOPPED: if (!p->exit_code) continue; if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED)) continue; if (ru != NULL) getrusage(p, RUSAGE_BOTH, ru); if (stat_addr) put_user((p->exit_code << 8) | 0x7f, stat_addr); p->exit_code = 0; retval = p->pid; goto end_wait4; case TASK_ZOMBIE: current->cutime += p->utime + p->cutime; current->cstime += p->stime + p->cstime; if (ru != NULL) getrusage(p, RUSAGE_BOTH, ru); if (stat_addr) put_user(p->exit_code, stat_addr); retval = p->pid; if (p->p_opptr != p->p_pptr) { REMOVE_LINKS(p); p->p_pptr = p->p_opptr; SET_LINKS(p); notify_parent(p, p->exit_signal); } else release(p); #ifdef DEBUG_PROC_TREE audit_ptree(); #endif goto end_wait4; default: continue; } } if (flag) { retval = 0; if (options & WNOHANG) goto end_wait4; retval = -ERESTARTSYS; if (current->signal & ~current->blocked) goto end_wait4; current->state=TASK_INTERRUPTIBLE; schedule(); goto repeat; } retval = -ECHILD; end_wait4: remove_wait_queue(&current->wait_chldexit,&wait); return retval; } #ifndef __alpha__ /* * sys_waitpid() remains for compatibility. waitpid() should be * implemented by calling sys_wait4() from libc.a. */ asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options) { return sys_wait4(pid, stat_addr, options, NULL); } #endif 1.1 or1k/rc203soc/sw/uClinux/kernel/fork.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/fork.c?rev=1.1&content-type=text/x-cvsweb-markup Index: fork.c =================================================================== /* * linux/kernel/fork.c * * Copyright (C) 1991, 1992 Linus Torvalds */ /* * 'fork.c' contains the help-routines for the 'fork' system call * (see also system_call.s). * Fork is rather simple, once you get the hang of it, but the memory * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' */ /* * uClinux revisions for NO_MM * Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>, * The Silver Hammer Group, Ltd. */ #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> #include <linux/malloc.h> #include <linux/ldt.h> #include <linux/smp.h> #include <asm/segment.h> #include <asm/system.h> #include <asm/pgtable.h> int nr_tasks=1; int nr_running=1; unsigned long int total_forks=0; /* Handle normal Linux uptimes. */ int last_pid=0; static inline int find_empty_process(void) { int i; if (nr_tasks >= NR_TASKS - MIN_TASKS_LEFT_FOR_ROOT) { if (current->uid) return -EAGAIN; } if (current->uid) { long max_tasks = current->rlim[RLIMIT_NPROC].rlim_cur; max_tasks--; /* count the new process.. */ if (max_tasks < nr_tasks) { struct task_struct *p; for_each_task (p) { if (p->uid == current->uid) if (--max_tasks < 0) return -EAGAIN; } } } for (i = 0 ; i < NR_TASKS ; i++) { if (!task[i]) return i; } return -EAGAIN; } static int get_pid(unsigned long flags) { struct task_struct *p; if (flags & CLONE_PID) return current->pid; repeat: if ((++last_pid) & 0xffff8000) last_pid=1; for_each_task (p) { if (p->pid == last_pid || p->pgrp == last_pid || p->session == last_pid) goto repeat; } return last_pid; } #ifndef NO_MM static inline int dup_mmap(struct mm_struct * mm) { struct vm_area_struct * mpnt, **p, *tmp; mm->mmap = NULL; p = &mm->mmap; for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); if (!tmp) { /* exit_mmap is called by the caller */ return -ENOMEM; } *tmp = *mpnt; tmp->vm_flags &= ~VM_LOCKED; tmp->vm_mm = mm; tmp->vm_next = NULL; if (tmp->vm_inode) { tmp->vm_inode->i_count++; /* insert tmp into the share list, just after mpnt */ tmp->vm_next_share->vm_prev_share = tmp; mpnt->vm_next_share = tmp; tmp->vm_prev_share = mpnt; } if (copy_page_range(mm, current->mm, tmp)) { /* link into the linked list for exit_mmap */ *p = tmp; p = &tmp->vm_next; /* exit_mmap is called by the caller */ return -ENOMEM; } if (tmp->vm_ops && tmp->vm_ops->open) tmp->vm_ops->open(tmp); *p = tmp; p = &tmp->vm_next; } build_mmap_avl(mm); flush_tlb_mm(current->mm); return 0; } static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) { if (!(clone_flags & CLONE_VM)) { struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL); if (!mm) return -ENOMEM; *mm = *current->mm; mm->count = 1; mm->def_flags = 0; mm->mmap_sem = MUTEX; tsk->mm = mm; tsk->min_flt = tsk->maj_flt = 0; tsk->cmin_flt = tsk->cmaj_flt = 0; tsk->nswap = tsk->cnswap = 0; if (new_page_tables(tsk)) { tsk->mm = NULL; exit_mmap(mm); goto free_mm; } down(&mm->mmap_sem); if (dup_mmap(mm)) { up(&mm->mmap_sem); tsk->mm = NULL; exit_mmap(mm); free_page_tables(mm); free_mm: kfree(mm); return -ENOMEM; } up(&mm->mmap_sem); return 0; } SET_PAGE_DIR(tsk, current->mm->pgd); current->mm->count++; return 0; } #else /* NO_MM */ static inline int dup_mmap(struct mm_struct * mm) { struct mm_tblock_struct * tmp = &current->mm->tblock; struct mm_tblock_struct * newtmp = &mm->tblock; /*unsigned long flags;*/ extern long realalloc, askedalloc; if (!mm) return -1; mm->tblock.rblock = 0; mm->tblock.next = 0; /*save_flags(flags); cli();*/ while((tmp = tmp->next)) { newtmp->next = kmalloc(sizeof(struct mm_tblock_struct), GFP_KERNEL); if (!newtmp->next) { /*restore_flags(flags);*/ return -ENOMEM; } realalloc += ksize(newtmp->next); askedalloc += sizeof(struct mm_tblock_struct); newtmp->next->rblock = tmp->rblock; if (tmp->rblock) tmp->rblock->refcount++; newtmp->next->next = 0; newtmp = newtmp->next; } /*restore_flags(flags);*/ return 0; } static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) { if (!(clone_flags & CLONE_VM)) { struct mm_struct * mm = kmalloc(sizeof(*tsk->mm), GFP_KERNEL); if (!mm) return -ENOMEM; *mm = *current->mm; mm->count = 1; mm->def_flags = 0; mm->vforkwait = 0; tsk->mm = mm; if (tsk->mm->executable) tsk->mm->executable->i_count++; tsk->min_flt = tsk->maj_flt = 0; tsk->cmin_flt = tsk->cmaj_flt = 0; tsk->nswap = tsk->cnswap = 0; if (dup_mmap(mm)) { tsk->mm = NULL; exit_mmap(mm); kfree(mm); return -ENOMEM; } return 0; } current->mm->count++; return 0; } #endif /* NO_MM */ static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_FS) { current->fs->count++; return 0; } tsk->fs = kmalloc(sizeof(*tsk->fs), GFP_KERNEL); if (!tsk->fs) return -1; tsk->fs->count = 1; tsk->fs->umask = current->fs->umask; if ((tsk->fs->root = current->fs->root)) tsk->fs->root->i_count++; if ((tsk->fs->pwd = current->fs->pwd)) tsk->fs->pwd->i_count++; return 0; } static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk) { int i; struct files_struct *oldf, *newf; struct file **old_fds, **new_fds; oldf = current->files; if (clone_flags & CLONE_FILES) { oldf->count++; return 0; } newf = kmalloc(sizeof(*newf), GFP_KERNEL); tsk->files = newf; if (!newf) return -1; newf->count = 1; newf->close_on_exec = oldf->close_on_exec; newf->open_fds = oldf->open_fds; old_fds = oldf->fd; new_fds = newf->fd; for (i = NR_OPEN; i != 0; i--) { struct file * f = *old_fds; old_fds++; *new_fds = f; new_fds++; if (f) f->f_count++; } return 0; } static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk) { if (clone_flags & CLONE_SIGHAND) { current->sig->count++; return 0; } tsk->sig = kmalloc(sizeof(*tsk->sig), GFP_KERNEL); if (!tsk->sig) return -1; tsk->sig->count = 1; memcpy(tsk->sig->action, current->sig->action, sizeof(tsk->sig->action)); return 0; } /* * Ok, this is the main fork-routine. It copies the system process * information (task[nr]) and sets up the necessary registers. It * also copies the data segment in its entirety. */ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) { int nr; int i; int error = -ENOMEM; unsigned long new_stack; struct task_struct *p; p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL); if (!p) goto bad_fork; new_stack = alloc_kernel_stack(); if (!new_stack) goto bad_fork_free_p; error = -EAGAIN; nr = find_empty_process(); if (nr < 0) goto bad_fork_free_stack; *p = *current; if (p->exec_domain && p->exec_domain->use_count) (*p->exec_domain->use_count)++; if (p->binfmt && p->binfmt->use_count) (*p->binfmt->use_count)++; p->did_exec = 0; p->swappable = 0; p->kernel_stack_page = new_stack; *(unsigned long *) p->kernel_stack_page = STACK_MAGIC; for(i=1;i<(PAGE_SIZE/sizeof(long));i++) ((unsigned long*)p->kernel_stack_page)[i] = STACK_UNTOUCHED_MAGIC; p->state = TASK_UNINTERRUPTIBLE; p->flags &= ~(PF_PTRACED|PF_TRACESYS|PF_SUPERPRIV); p->flags |= PF_FORKNOEXEC; p->pid = get_pid(clone_flags); p->next_run = NULL; p->prev_run = NULL; p->p_pptr = p->p_opptr = current; p->p_cptr = NULL; init_waitqueue(&p->wait_chldexit); p->signal = 0; 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->utime = p->stime = 0; p->cutime = p->cstime = 0; #ifdef __SMP__ p->processor = NO_PROC_ID; p->lock_depth = 1; #endif p->start_time = jiffies; task[nr] = p; SET_LINKS(p); nr_tasks++; error = -ENOMEM; /* 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; if (copy_mm(clone_flags, p)) goto bad_fork_cleanup_sighand; copy_thread(nr, clone_flags, usp, p, regs); p->semundo = NULL; /* ok, now we should be set up.. */ p->swappable = 1; p->exit_signal = clone_flags & CSIGNAL; p->counter = (current->counter >>= 1); wake_up_process(p); /* do this last, just in case */ ++total_forks; #ifdef NO_MM if (clone_flags & CLONE_WAIT) { sleep_on(&current->mm->vforkwait); } #endif /*NO_MM*/ return p->pid; bad_fork_cleanup_sighand: exit_sighand(p); bad_fork_cleanup_fs: exit_fs(p); bad_fork_cleanup_files: exit_files(p); bad_fork_cleanup: if (p->exec_domain && p->exec_domain->use_count) (*p->exec_domain->use_count)--; if (p->binfmt && p->binfmt->use_count) (*p->binfmt->use_count)--; task[nr] = NULL; REMOVE_LINKS(p); nr_tasks--; bad_fork_free_stack: free_kernel_stack(new_stack); bad_fork_free_p: kfree(p); bad_fork: return error; } 1.1 or1k/rc203soc/sw/uClinux/kernel/info.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/info.c?rev=1.1&content-type=text/x-cvsweb-markup Index: info.c =================================================================== /* * linux/kernel/info.c * * Copyright (C) 1992 Darren Senn */ /* This implements the sysinfo() system call */ #include <asm/segment.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/unistd.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/swap.h> asmlinkage int sys_sysinfo(struct sysinfo *info) { int error; struct sysinfo val; error = verify_area(VERIFY_WRITE, info, sizeof(struct sysinfo)); if (error) return error; memset((char *)&val, 0, sizeof(struct sysinfo)); val.uptime = jiffies / HZ; val.loads[0] = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT); val.loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT); val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT); val.procs = nr_tasks-1; si_meminfo(&val); si_swapinfo(&val); memcpy_tofs(info, &val, sizeof(struct sysinfo)); return 0; } 1.1 or1k/rc203soc/sw/uClinux/kernel/itimer.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/itimer.c?rev=1.1&content-type=text/x-cvsweb-markup Index: itimer.c =================================================================== /* * linux/kernel/itimer.c * * Copyright (C) 1992 Darren Senn */ /* These are all the functions necessary to implement itimers */ #include <linux/signal.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/time.h> #include <linux/mm.h> #include <asm/segment.h> /* * change timeval to jiffies, trying to avoid the * most obvious overflows.. * * The tv_*sec values are signed, but nothing seems to * indicate whether we really should use them as signed values * when doing itimers. POSIX doesn't mention this (but if * alarm() uses itimers without checking, we have to use unsigned * arithmetic). */ static unsigned long tvtojiffies(struct timeval *value) { unsigned long sec = (unsigned) value->tv_sec; unsigned long usec = (unsigned) value->tv_usec; if (sec > (ULONG_MAX / HZ)) return ULONG_MAX; usec += 1000000 / HZ - 1; usec /= 1000000 / HZ; return HZ*sec+usec; } static void jiffiestotv(unsigned long jiffies, struct timeval *value) { value->tv_usec = (jiffies % HZ) * (1000000 / HZ); value->tv_sec = jiffies / HZ; return; } static int _getitimer(int which, struct itimerval *value) { register unsigned long val, interval; switch (which) { case ITIMER_REAL: interval = current->it_real_incr; val = 0; if (del_timer(&current->real_timer)) { unsigned long now = jiffies; val = current->real_timer.expires; add_timer(&current->real_timer); /* look out for negative/zero itimer.. */ if (val <= now) val = now+1; val -= now; } break; case ITIMER_VIRTUAL: val = current->it_virt_value; interval = current->it_virt_incr; break; case ITIMER_PROF: val = current->it_prof_value; interval = current->it_prof_incr; break; default: return(-EINVAL); } jiffiestotv(val, &value->it_value); jiffiestotv(interval, &value->it_interval); return 0; } asmlinkage int sys_getitimer(int which, struct itimerval *value) { int error; struct itimerval get_buffer; if (!value) return -EFAULT; error = _getitimer(which, &get_buffer); if (error) return error; error = verify_area(VERIFY_WRITE, value, sizeof(struct itimerval)); if (error) return error; memcpy_tofs(value, &get_buffer, sizeof(get_buffer)); return 0; } void it_real_fn(unsigned long __data) { struct task_struct * p = (struct task_struct *) __data; unsigned long interval; send_sig(SIGALRM, p, 1); interval = p->it_real_incr; if (interval) { unsigned long timeout = jiffies + interval; /* check for overflow */ if (timeout < interval) timeout = ULONG_MAX; p->real_timer.expires = timeout; add_timer(&p->real_timer); } } int _setitimer(int which, struct itimerval *value, struct itimerval *ovalue) { register unsigned long i, j; int k; i = tvtojiffies(&value->it_interval); j = tvtojiffies(&value->it_value); if (ovalue && (k = _getitimer(which, ovalue)) < 0) return k; switch (which) { case ITIMER_REAL: del_timer(&current->real_timer); current->it_real_value = j; current->it_real_incr = i; if (!j) break; i = j + jiffies; /* check for overflow.. */ if (i < j) i = ULONG_MAX; current->real_timer.expires = i; add_timer(&current->real_timer); break; case ITIMER_VIRTUAL: if (j) j++; current->it_virt_value = j; current->it_virt_incr = i; break; case ITIMER_PROF: if (j) j++; current->it_prof_value = j; current->it_prof_incr = i; break; default: return -EINVAL; } return 0; } asmlinkage int sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) { int error; struct itimerval set_buffer, get_buffer; if (value) { error = verify_area(VERIFY_READ, value, sizeof(*value)); if (error) return error; memcpy_fromfs(&set_buffer, value, sizeof(set_buffer)); } else memset((char *) &set_buffer, 0, sizeof(set_buffer)); if (ovalue) { error = verify_area(VERIFY_WRITE, ovalue, sizeof(struct itimerval)); if (error) return error; } error = _setitimer(which, &set_buffer, ovalue ? &get_buffer : 0); if (error || !ovalue) return error; memcpy_tofs(ovalue, &get_buffer, sizeof(get_buffer)); return error; } 1.1 or1k/rc203soc/sw/uClinux/kernel/ksyms.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/ksyms.c?rev=1.1&content-type=text/x-cvsweb-markup Index: ksyms.c =================================================================== /* * Herein lies all the functions/variables that are "exported" for linkage * with dynamically loaded kernel modules. * Jon. * * - Stacked module support and unified symbol table added (June 1994) * - External symbol table support added (December 1994) * - Versions on symbols added (December 1994) * by Bjorn Ekwall <bj0rn@b...> */ #include <linux/module.h> #include <linux/config.h> #include <linux/kernel.h> #include <linux/smp.h> #include <linux/fs.h> #include <linux/blkdev.h> #include <linux/sched.h> #include <linux/kernel_stat.h> #include <linux/mm.h> #include <linux/malloc.h> #include <linux/ptrace.h> #include <linux/sys.h> #include <linux/utsname.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timer.h> #include <linux/binfmts.h> #include <linux/personality.h> #include <linux/termios.h> #include <linux/tqueue.h> #include <linux/tty.h> #include <linux/serial.h> #include <linux/locks.h> #include <linux/string.h> #include <linux/delay.h> #include <linux/sem.h> #include <linux/minix_fs.h> #include <linux/ext2_fs.h> #include <linux/random.h> #include <linux/mount.h> #include <linux/pagemap.h> #include <linux/sysctl.h> #include <linux/hdreg.h> #include <linux/skbuff.h> #include <linux/genhd.h> #include <linux/swap.h> #include <linux/ctype.h> #include <linux/file.h> extern unsigned char aux_device_present, kbd_read_mask; #ifdef CONFIG_PCI #include <linux/bios32.h> #include <linux/pci.h> #endif #if defined(CONFIG_PROC_FS) #include <linux/proc_fs.h> #endif #ifdef CONFIG_KERNELD #include <linux/kerneld.h> #endif #include <asm/irq.h> #ifdef __SMP__ #include <linux/smp.h> #endif extern char *get_options(char *str, int *ints); extern void set_device_ro(kdev_t dev,int flag); extern struct file_operations * get_blkfops(unsigned int); extern void blkdev_release(struct inode * inode); extern void *sys_call_table; extern struct timezone sys_tz; extern int request_dma(unsigned int dmanr, char * deviceID); extern void free_dma(unsigned int dmanr); extern void hard_reset_now(void); extern void select_free_wait(select_table * p); extern int select_check(int flag, select_table * wait, struct file * file); struct symbol_table symbol_table = { #include <linux/symtab_begin.h> #ifdef MODVERSIONS { (void *)1 /* Version version :-) */, SYMBOL_NAME_STR (Using_Versions) }, #endif /* stackable module support */ X(register_symtab_from), X(get_module_symbol), #ifdef CONFIG_KERNELD X(kerneld_send), #endif X(get_options), /* system info variables */ /* These check that they aren't defines (0/1) */ #ifndef EISA_bus__is_a_macro X(EISA_bus), #endif #ifndef MCA_bus__is_a_macro X(MCA_bus), #endif #ifndef wp_works_ok__is_a_macro X(wp_works_ok), #endif #ifdef CONFIG_PCI /* PCI BIOS support */ X(pcibios_present), X(pcibios_find_class), X(pcibios_find_device), X(pcibios_read_config_byte), X(pcibios_read_config_word), X(pcibios_read_config_dword), X(pcibios_strerror), X(pcibios_write_config_byte), X(pcibios_write_config_word), X(pcibios_write_config_dword), #endif /* process memory management */ X(verify_area), X(do_mmap), X(do_munmap), X(exit_mm), /* internal kernel memory management */ X(__get_free_pages), X(free_pages), X(kmalloc), X(kfree), X(vmalloc), X(vremap), X(vfree), X(mem_map), X(remap_page_range), X(high_memory), X(update_vm_cache), /* filesystem internal functions */ X(getname), X(putname), X(__iget), X(iput), X(namei), X(lnamei), X(open_namei), X(sys_close), X(close_fp), X(check_disk_change), X(invalidate_buffers), X(invalidate_inodes), X(invalidate_inode_pages), X(fsync_dev), X(permission), X(inode_setattr), X(inode_change_ok), X(set_blocksize), X(getblk), X(bread), X(breada), X(select_check), X(select_free_wait), X(__brelse), X(__bforget), X(ll_rw_block), X(brw_page), X(__wait_on_buffer), X(mark_buffer_uptodate), X(unlock_buffer), X(dcache_lookup), X(dcache_add), X(add_blkdev_randomness), X(generic_file_read), X(generic_file_mmap), X(generic_readpage), X(__fput), X(make_bad_inode), /* device registration */ X(register_chrdev), X(unregister_chrdev), X(register_blkdev), X(unregister_blkdev), X(tty_register_driver), X(tty_unregister_driver), X(tty_std_termios), /* block device driver support */ X(block_read), X(block_write), X(block_fsync), X(wait_for_request), X(blksize_size), X(hardsect_size), X(blk_size), X(blk_dev), X(max_sectors), X(max_segments), X(is_read_only), X(set_device_ro), X(bmap), X(sync_dev), X(get_blkfops), X(blkdev_open), X(blkdev_release), X(gendisk_head), X(resetup_one_dev), X(unplug_device), X(make_request), X(tq_disk), #ifdef CONFIG_SERIAL /* Module creation of serial units */ X(register_serial), X(unregister_serial), #endif /* tty routines */ X(tty_hangup), X(tty_wait_until_sent), X(tty_check_change), X(tty_hung_up_p), X(do_SAK), X(console_print), /* filesystem registration */ X(register_filesystem), X(unregister_filesystem), /* executable format registration */ X(register_binfmt), X(unregister_binfmt), X(search_binary_handler), X(prepare_binprm), X(remove_arg_zero), /* execution environment registration */ X(lookup_exec_domain), X(register_exec_domain), X(unregister_exec_domain), /* sysctl table registration */ X(register_sysctl_table), X(unregister_sysctl_table), X(sysctl_string), X(sysctl_intvec), X(proc_dostring), X(proc_dointvec), X(proc_dointvec_minmax), /* interrupt handling */ X(request_irq), X(free_irq), X(enable_irq), X(disable_irq), X(probe_irq_on), X(probe_irq_off), X(bh_active), X(bh_mask), X(bh_mask_count), X(bh_base), X(add_timer), X(del_timer), X(tq_timer), X(tq_immediate), X(tq_scheduler), X(timer_active), X(timer_table), X(intr_count), /* autoirq from drivers/net/auto_irq.c */ #ifdef CONFIG_NET X(autoirq_setup), X(autoirq_report), #endif /* dma handling */ X(request_dma), X(free_dma), #ifdef HAVE_DISABLE_HLT X(disable_hlt), X(enable_hlt), #endif /* IO port handling */ X(check_region), X(request_region), X(release_region), /* process management */ X(wake_up), X(wake_up_interruptible), X(sleep_on), X(interruptible_sleep_on), X(schedule), X(current_set), X(jiffies), X(xtime), X(do_gettimeofday), X(loops_per_sec), X(need_resched), X(kstat), X(kill_proc), X(kill_pg), X(kill_sl), X(force_sig), /* misc */ X(panic), X(printk), X(sprintf), X(vsprintf), X(kdevname), X(simple_strtoul), X(system_utsname), X(sys_call_table), X(hard_reset_now), X(_ctype), X(_ctmp), X(get_random_bytes), /* Signal interfaces */ X(send_sig), /* Program loader interfaces */ X(setup_arg_pages), X(copy_strings), X(do_execve), X(flush_old_exec), X(open_inode), X(read_exec), /* Miscellaneous access points */ X(si_meminfo), /* Added to make file system as module */ X(set_writetime), X(sys_tz), X(__wait_on_super), X(file_fsync), X(clear_inode), X(refile_buffer), X(nr_async_pages), X(___strtok), X(init_fifo), X(super_blocks), X(fifo_inode_operations), X(chrdev_inode_operations), X(blkdev_inode_operations), X(read_ahead), X(get_hash_table), X(get_empty_inode), X(insert_inode_hash), X(event), X(__down), X(__up), X(securelevel), /* all busmice */ X(add_mouse_randomness), X(fasync_helper), #ifndef __mc68000__ /* psaux mouse */ X(aux_device_present), X(kbd_read_mask), #endif #ifdef CONFIG_BLK_DEV_IDE_PCMCIA X(ide_register), X(ide_unregister), #endif #ifdef CONFIG_BLK_DEV_MD X(disk_name), /* for md.c */ #endif /* binfmt_aout */ X(get_write_access), X(put_write_access), #ifdef CONFIG_PROC_FS X(proc_dir_inode_operations), #endif /* Modular sound */ X(sys_open), X(sys_read), /******************************************************** * Do not add anything below this line, * as the stacked modules depend on this! */ #include <linux/symtab_end.h> }; /* int symbol_table_size = sizeof (symbol_table) / sizeof (symbol_table[0]); */ 1.1 or1k/rc203soc/sw/uClinux/kernel/module.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/module.c?rev=1.1&content-type=text/x-cvsweb-markup Index: module.c =================================================================== #include <linux/errno.h> #include <linux/kernel.h> #include <asm/segment.h> #include <linux/mm.h> /* defines GFP_KERNEL */ #include <linux/string.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/malloc.h> #include <linux/config.h> #include <asm/pgtable.h> /* * Originally by Anonymous (as far as I know...) * Linux version by Bas Laarhoven <bas@v...> * 0.99.14 version by Jon Tombs <jon@g...>, * * Heavily modified by Bjorn Ekwall <bj0rn@b...> May 1994 (C) * This source is covered by the GNU GPL, the same as all kernel sources. * * Features: * - Supports stacked modules (removable only of there are no dependents). * - Supports table of symbols defined by the modules. * - Supports /proc/ksyms, showing value, name and owner of all * the symbols defined by all modules (in stack order). * - Added module dependencies information into /proc/modules * - Supports redefines of all symbols, for streams-like behaviour. * - Compatible with older versions of insmod. * * New addition in December 1994: (Bjorn Ekwall, idea from Jacques Gelinas) * - Externally callable function: * * "int register_symtab(struct symbol_table *)" * * This function can be called from within the kernel, * and ALSO from loadable modules. * The goal is to assist in modularizing the kernel even more, * and finally: reducing the number of entries in ksyms.c * since every subsystem should now be able to decide and * control exactly what symbols it wants to export, locally! * * On 1-Aug-95: <Matti.Aarnio@u...> altered code to use same style as * do /proc/net/XXX "files". Namely allow more than 4kB * (or what the block size is) output. * * - Use dummy syscall functions for users who disable all * module support. Similar to kernel/sys.c (Paul Gortmaker) */ #ifdef CONFIG_MODULES /* a *big* #ifdef block... */ static struct module kernel_module; static struct module *module_list = &kernel_module; static int freeing_modules; /* true if some modules are marked for deletion */ static struct module *find_module( const char *name); static int get_mod_name( char *user_name, char *buf); static int free_modules( void); extern struct symbol_table symbol_table; /* in kernel/ksyms.c */ /* * Called at boot time */ void init_modules(void) { struct internal_symbol *sym; int i; for (i = 0, sym = symbol_table.symbol; sym->name; ++sym, ++i) ; symbol_table.n_symbols = i; kernel_module.symtab = &symbol_table; kernel_module.state = MOD_RUNNING; /* Hah! */ kernel_module.name = ""; } /* * Allocate space for a module. */ asmlinkage unsigned long sys_create_module(char *module_name, unsigned long size) { struct module *mp; void* addr; int error; int npages; int sspace = sizeof(struct module) + MOD_MAX_NAME; char name[MOD_MAX_NAME]; if (!suser() || securelevel > 0) return -EPERM; if (module_name == NULL || size == 0) return -EINVAL; if ((error = get_mod_name(module_name, name)) != 0) return error; if (find_module(name) != NULL) { return -EEXIST; } if ((mp = (struct module*) kmalloc(sspace, GFP_KERNEL)) == NULL) { return -ENOMEM; } strcpy((char *)(mp + 1), name); /* why not? */ npages = (size + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE; if ((addr = vmalloc(npages * PAGE_SIZE)) == 0) { kfree_s(mp, sspace); return -ENOMEM; } mp->next = module_list; mp->ref = NULL; mp->symtab = NULL; mp->name = (char *)(mp + 1); mp->size = npages; mp->addr = addr; mp->state = MOD_UNINITIALIZED; mp->cleanup = NULL; * (long *) addr = 0; /* set use count to zero */ module_list = mp; /* link it in */ pr_debug("module `%s' (%lu pages @ 0x%08lx) created\n", mp->name, (unsigned long) mp->size, (unsigned long) mp->addr); return (unsigned long) addr; } /* * Initialize a module. */ asmlinkage int sys_init_module(char *module_name, char *code, unsigned codesize, struct mod_routines *routines, struct symbol_table *symtab) { struct module *mp; struct symbol_table *newtab; char name[MOD_MAX_NAME]; int error; struct mod_routines rt; if (!suser() || securelevel > 0) return -EPERM; #ifdef __i386__ /* A little bit of protection... we "know" where the user stack is... */ if (symtab && ((unsigned long)symtab > 0xb0000000)) { printk(KERN_WARNING "warning: you are using an old insmod, no symbols will be inserted!\n"); symtab = NULL; } #endif if ((error = get_mod_name(module_name, name)) != 0) return error; pr_debug("initializing module `%s', %d (0x%x) bytes\n", name, codesize, codesize); memcpy_fromfs(&rt, routines, sizeof rt); if ((mp = find_module(name)) == NULL) return -ENOENT; if (codesize & MOD_AUTOCLEAN) { /* * set autoclean marker from codesize... * set usage count to "zero" */ codesize &= ~MOD_AUTOCLEAN; GET_USE_COUNT(mp) = MOD_AUTOCLEAN; } if ((codesize + sizeof (long) + PAGE_SIZE - 1) / PAGE_SIZE > mp->size) return -EINVAL; memcpy_fromfs((char *)mp->addr + sizeof (long), code, codesize); memset((char *)mp->addr + sizeof (long) + codesize, 0, mp->size * PAGE_SIZE - (codesize + sizeof (long))); pr_debug("module init entry = 0x%08lx, cleanup entry = 0x%08lx\n", (unsigned long) rt.init, (unsigned long) rt.cleanup); mp->cleanup = rt.cleanup; /* update kernel symbol table */ if (symtab) { /* symtab == NULL means no new entries to handle */ struct internal_symbol *sym; struct module_ref *ref; int size; int i; int legal_start; if ((error = verify_area(VERIFY_READ, &symtab->size, sizeof(symtab->size)))) return error; size = get_user(&symtab->size); if ((newtab = (struct symbol_table*) kmalloc(size, GFP_KERNEL)) == NULL) { return -ENOMEM; } if ((error = verify_area(VERIFY_READ, symtab, size))) { kfree_s(newtab, size); return error; } memcpy_fromfs((char *)(newtab), symtab, size); /* sanity check */ legal_start = sizeof(struct symbol_table) + newtab->n_symbols * sizeof(struct internal_symbol) + newtab->n_refs * sizeof(struct module_ref); if ((newtab->n_symbols < 0) || (newtab->n_refs < 0) || (legal_start > size)) { printk(KERN_WARNING "Rejecting illegal symbol table (n_symbols=%d,n_refs=%d)\n", newtab->n_symbols, newtab->n_refs); kfree_s(newtab, size); return -EINVAL; } /* relocate name pointers, index referred from start of table */ for (sym = &(newtab->symbol[0]), i = 0; i < newtab->n_symbols; ++sym, ++i) { if ((unsigned long)sym->name < legal_start || size <= (unsigned long)sym->name) { printk(KERN_WARNING "Rejecting illegal symbol table\n"); kfree_s(newtab, size); return -EINVAL; } /* else */ sym->name += (long)newtab; } mp->symtab = newtab; /* Update module references. * On entry, from "insmod", ref->module points to * the referenced module! * Now it will point to the current module instead! * The ref structure becomes the first link in the linked * list of references to the referenced module. * Also, "sym" from above, points to the first ref entry!!! */ for (ref = (struct module_ref *)sym, i = 0; i < newtab->n_refs; ++ref, ++i) { /* Check for valid reference */ struct module *link = module_list; while (link && (ref->module != link)) link = link->next; if (link == (struct module *)0) { printk(KERN_WARNING "Non-module reference! Rejected!\n"); return -EINVAL; } ref->next = ref->module->ref; ref->module->ref = ref; ref->module = mp; } } flush_pages_to_ram((unsigned long)mp->addr, (codesize+sizeof(long)+PAGE_SIZE-1)/PAGE_SIZE); GET_USE_COUNT(mp) += 1; if ((*rt.init)() != 0) { GET_USE_COUNT(mp) = 0; return -EBUSY; } GET_USE_COUNT(mp) -= 1; mp->state = MOD_RUNNING; return 0; } asmlinkage int sys_delete_module(char *module_name) { struct module *mp; char name[MOD_MAX_NAME]; int error; if (!suser() || securelevel > 0) return -EPERM; /* else */ if (module_name != NULL) { if ((error = get_mod_name(module_name, name)) != 0) return error; if ((mp = find_module(name)) == NULL) return -ENOENT; if ((mp->ref != NULL) || ((GET_USE_COUNT(mp) & ~(MOD_AUTOCLEAN | MOD_VISITED)) != 0)) return -EBUSY; GET_USE_COUNT(mp) &= ~(MOD_AUTOCLEAN | MOD_VISITED); if (mp->state == MOD_RUNNING) (*mp->cleanup)(); mp->state = MOD_DELETED; free_modules(); } /* for automatic reaping */ else { struct module *mp_next; for (mp = module_list; mp != &kernel_module; mp = mp_next) { mp_next = mp->next; if ((mp->ref == NULL) && (mp->state == MOD_RUNNING) && ((GET_USE_COUNT(mp) & ~MOD_VISITED) == MOD_AUTOCLEAN)) { if ((GET_USE_COUNT(mp) & MOD_VISITED)) { /* Don't reap until one "cycle" after last _use_ */ GET_USE_COUNT(mp) &= ~MOD_VISITED; } else { GET_USE_COUNT(mp) &= ~(MOD_AUTOCLEAN | MOD_VISITED); (*mp->cleanup)(); mp->state = MOD_DELETED; free_modules(); } } } } return 0; } /* * Copy the kernel symbol table to user space. If the argument is null, * just return the size of the table. * * Note that the transient module symbols are copied _first_, * in lifo order!!! * * The symbols to "insmod" are according to the "old" format: struct kernel_sym, * which is actually quite handy for this purpose. * Note that insmod inserts a struct symbol_table later on... * (as that format is quite handy for the kernel...) * * For every module, the first (pseudo)symbol copied is the module name * and the address of the module struct. * This lets "insmod" keep track of references, and build the array of * struct module_refs in the symbol table. * The format of the module name is "#module", so that "insmod" can easily * notice when a module name comes along. Also, this will make it possible * to use old versions of "insmod", albeit with reduced functionality... * The "kernel" module has an empty name. */ asmlinkage int sys_get_kernel_syms(struct kernel_sym *table) { struct internal_symbol *from; struct kernel_sym isym; struct kernel_sym *to; struct module *mp = module_list; int i; int nmodsyms = 0; for (mp = module_list; mp; mp = mp->next) { if (mp->symtab && mp->symtab->n_symbols) { /* include the count for the module name! */ nmodsyms += mp->symtab->n_symbols + 1; } else /* include the count for the module name! */ nmodsyms += 1; /* return modules without symbols too */ } if (table != NULL) { to = table; if ((i = verify_area(VERIFY_WRITE, to, nmodsyms * sizeof(*table)))) return i; /* copy all module symbols first (always LIFO order) */ for (mp = module_list; mp; mp = mp->next) { if (mp->state == MOD_RUNNING) { /* magic: write module info as a pseudo symbol */ isym.value = (unsigned long)mp; sprintf(isym.name, "#%s", mp->name); memcpy_tofs(to, &isym, sizeof isym); ++to; if (mp->symtab != NULL) { for (i = mp->symtab->n_symbols, from = mp->symtab->symbol; i > 0; --i, ++from, ++to) { isym.value = (unsigned long)from->addr; strncpy(isym.name, from->name, sizeof isym.name); memcpy_tofs(to, &isym, sizeof isym); } } } } } return nmodsyms; } /* * Copy the name of a module from user space. */ int get_mod_name(char *user_name, char *buf) { int i; i = 0; for (i = 0 ; (buf[i] = get_user(user_name + i)) != '\0' ; ) { if (++i >= MOD_MAX_NAME) return -E2BIG; } return 0; } /* * Look for a module by name, ignoring modules marked for deletion. */ struct module * find_module( const char *name) { struct module *mp; for (mp = module_list ; mp ; mp = mp->next) { if (mp->state == MOD_DELETED) continue; if (!strcmp(mp->name, name)) break; } return mp; } static void drop_refs(struct module *mp) { struct module *step; struct module_ref *prev; struct module_ref *ref; for (step = module_list; step; step = step->next) { for (prev = ref = step->ref; ref; ref = prev->next) { if (ref->module == mp) { if (ref == step->ref) step->ref = ref->next; else prev->next = ref->next; break; /* every module only references once! */ } else prev = ref; } } } /* * Try to free modules which have been marked for deletion. Returns nonzero * if a module was actually freed. */ int free_modules( void) { struct module *mp; struct module **mpp; int did_deletion; did_deletion = 0; freeing_modules = 0; mpp = &module_list; while ((mp = *mpp) != NULL) { if (mp->state != MOD_DELETED) { mpp = &mp->next; } else { if ((GET_USE_COUNT(mp) != 0) || (mp->ref != NULL)) { freeing_modules = 1; mpp = &mp->next; } else { /* delete it */ *mpp = mp->next; if (mp->symtab) { if (mp->symtab->n_refs) drop_refs(mp); if (mp->symtab->size) kfree_s(mp->symtab, mp->symtab->size); } vfree(mp->addr); kfree_s(mp, sizeof(struct module) + MOD_MAX_NAME); did_deletion = 1; } } } return did_deletion; } /* * Called by the /proc file system to return a current list of modules. */ int get_module_list(char *buf) { char *p; const char *q; int i; struct module *mp; struct module_ref *ref; char size[32]; p = buf; /* Do not show the kernel pseudo module */ for (mp = module_list ; mp && mp->next; mp = mp->next) { if (p - buf > 4096 - 100) break; /* avoid overflowing buffer */ q = mp->name; if (*q == '\0' && mp->size == 0 && mp->ref == NULL) continue; /* don't list modules for kernel syms */ i = 20; while (*q) { *p++ = *q++; i--; } sprintf(size, "%d", mp->size); i -= strlen(size); if (i <= 0) i = 1; while (--i >= 0) *p++ = ' '; q = size; while (*q) *p++ = *q++; if (mp->state == MOD_UNINITIALIZED) q = " (uninitialized)"; else if (mp->state == MOD_RUNNING) q = ""; else if (mp->state == MOD_DELETED) q = " (deleted)"; else q = " (bad state)"; while (*q) *p++ = *q++; *p++ = '\t'; if ((ref = mp->ref) != NULL) { *p++ = '['; for (; ref; ref = ref->next) { q = ref->module->name; while (*q) *p++ = *q++; if (ref->next) *p++ = ' '; } *p++ = ']'; } if (mp->state == MOD_RUNNING) { sprintf(size,"\t%ld%s", GET_USE_COUNT(mp) & ~(MOD_AUTOCLEAN | MOD_VISITED), ((GET_USE_COUNT(mp) & MOD_AUTOCLEAN)? " (autoclean)":"")); q = size; while (*q) *p++ = *q++; } *p++ = '\n'; } return p - buf; } /* * Called by the /proc file system to return a current list of ksyms. */ int get_ksyms_list(char *buf, char **start, off_t offset, int length) { struct module *mp; struct internal_symbol *sym; int i; char *p = buf; int len = 0; /* code from net/ipv4/proc.c */ off_t pos = 0; off_t begin = 0; for (mp = module_list; mp; mp = mp->next) { if ((mp->state == MOD_RUNNING) && (mp->symtab != NULL) && (mp->symtab->n_symbols > 0)) { for (i = mp->symtab->n_symbols, sym = mp->symtab->symbol; i > 0; --i, ++sym) { p = buf + len; if (mp->name[0]) { len += sprintf(p, "%08lx %s\t[%s]\n", (long)sym->addr, sym->name, mp->name); } else { len += sprintf(p, "%08lx %s\n", (long)sym->addr, sym->name); } pos = begin + len; if (pos < offset) { len = 0; begin = pos; } pos = begin + len; if (pos > offset+length) goto leave_the_loop; } } } leave_the_loop: *start = buf + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len; } /* * Gets the address for a symbol in the given module. If modname is * NULL, it looks for the name in any registered symbol table. If the * modname is an empty string, it looks for the symbol in kernel exported * symbol tables. */ void *get_module_symbol(char *modname, char *symname) { struct module *mp; struct internal_symbol *sym; int i; for (mp = module_list; mp; mp = mp->next) { if (((modname == NULL) || (strcmp(mp->name, modname) == 0)) && (mp->state == MOD_RUNNING) && (mp->symtab != NULL) && (mp->symtab->n_symbols > 0)) { for (i = mp->symtab->n_symbols, sym = mp->symtab->symbol; i > 0; --i, ++sym) { if (strcmp(sym->name, symname) == 0) { return sym->addr; } } } } return NULL; } /* * Rules: * - The new symbol table should be statically allocated, or else you _have_ * to set the "size" field of the struct to the number of bytes allocated. * * - The strings that name the symbols will not be copied, maybe the pointers * * - For a loadable module, the function should only be called in the * context of init_module * * Those are the only restrictions! (apart from not being reentrant...) * * If you want to remove a symbol table for a loadable module, * the call looks like: "register_symtab(0)". * * The look of the code is mostly dictated by the format of * the frozen struct symbol_table, due to compatibility demands. */ #define INTSIZ sizeof(struct internal_symbol) #define REFSIZ sizeof(struct module_ref) #define SYMSIZ sizeof(struct symbol_table) #define MODSIZ sizeof(struct module) static struct symbol_table nulltab; int register_symtab_from(struct symbol_table *intab, long *from) { struct module *mp; struct module *link; struct symbol_table *oldtab; struct symbol_table *newtab; struct module_ref *newref; int size; if (intab && (intab->n_symbols == 0)) { struct internal_symbol *sym; /* How many symbols, really? */ for (sym = intab->symbol; sym->name; ++sym) intab->n_symbols +=1; } for (mp = module_list; mp != &kernel_module; mp = mp->next) { /* * "from" points to "mod_use_count_" (== start of module) * or is == 0 if called from a non-module */ if ((unsigned long)(mp->addr) == (unsigned long)from) break; } if (mp == &kernel_module) { /* Aha! Called from an "internal" module */ if (!intab) return 0; /* or -ESILLY_PROGRAMMER :-) */ /* create a pseudo module! */ if (!(mp = (struct module*) kmalloc(MODSIZ, GFP_KERNEL))) { /* panic time! */ printk(KERN_ERR "Out of memory for new symbol table!\n"); return -ENOMEM; } /* else OK */ memset(mp, 0, MODSIZ); mp->state = MOD_RUNNING; /* Since it is resident... */ mp->name = ""; /* This is still the "kernel" symbol table! */ mp->symtab = intab; /* link it in _after_ the resident symbol table */ mp->next = kernel_module.next; kernel_module.next = mp; return 0; } /* else ******** Called from a loadable module **********/ /* * This call should _only_ be done in the context of the * call to init_module i.e. when loading the module!! * Or else... */ /* Any table there before? */ if ((oldtab = mp->symtab) == (struct symbol_table*)0) { /* No, just insert it! */ mp->symtab = intab; return 0; } /* else ****** we have to replace the module symbol table ******/ if (oldtab->n_refs == 0) { /* no problems! */ mp->symtab = intab; /* if the old table was kmalloc-ed, drop it */ if (oldtab->size > 0) kfree_s(oldtab, oldtab->size); return 0; } /* else */ /***** The module references other modules... insmod said so! *****/ /* We have to allocate a new symbol table, or we lose them! */ if (intab == (struct symbol_table*)0) intab = &nulltab; /* easier code with zeroes in place */ /* the input symbol table space does not include the string table */ /* (it does for symbol tables that insmod creates) */ if (!(newtab = (struct symbol_table*)kmalloc( size = SYMSIZ + intab->n_symbols * INTSIZ + oldtab->n_refs * REFSIZ, GFP_KERNEL))) { /* panic time! */ printk(KERN_ERR "Out of memory for new symbol table!\n"); return -ENOMEM; } /* copy up to, and including, the new symbols */ memcpy(newtab, intab, SYMSIZ + intab->n_symbols * INTSIZ); newtab->size = size; newtab->n_refs = oldtab->n_refs; /* copy references */ memcpy( ((char *)newtab) + SYMSIZ + intab->n_symbols * INTSIZ, ((char *)oldtab) + SYMSIZ + oldtab->n_symbols * INTSIZ, oldtab->n_refs * REFSIZ); /* relink references from the old table to the new one */ /* pointer to the first reference entry in newtab! Really! */ newref = (struct module_ref*) &(newtab->symbol[newtab->n_symbols]); /* check for reference links from previous modules */ for ( link = module_list; link && (link != &kernel_module); link = link->next) { if (link->ref && (link->ref->module == mp)) link->ref = newref++; } mp->symtab = newtab; /* all references (if any) have been handled */ /* if the old table was kmalloc-ed, drop it */ if (oldtab->size > 0) kfree_s(oldtab, oldtab->size); return 0; } #else /* CONFIG_MODULES */ /* Dummy syscalls for people who don't want modules */ asmlinkage unsigned long sys_create_module(void) { return -ENOSYS; } asmlinkage int sys_init_module(void) { return -ENOSYS; } asmlinkage int sys_delete_module(void) { return -ENOSYS; } asmlinkage int sys_get_kernel_syms(void) { return -ENOSYS; } int register_symtab_from(struct symbol_table *intab, long *from) { return 0; } #endif /* CONFIG_MODULES */ 1.1 or1k/rc203soc/sw/uClinux/kernel/panic.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/panic.c?rev=1.1&content-type=text/x-cvsweb-markup Index: panic.c =================================================================== /* * linux/kernel/panic.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Revisions for CONFIG_CONSOLE by Kenneth Albanowski * Copyright (c) 1997, 1998 The Silver Hammer Group, Ltd. * */ /* * This function is used through-out the kernel (including mm and fs) * to indicate a major problem. */ #include <stdarg.h> #include <linux/config.h> /* CONFIG_SCSI_GDTH */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/delay.h> #ifdef CONFIG_NETtel #include <asm/coldfire.h> #include <asm/mcfsim.h> #include <asm/nettel.h> #endif asmlinkage void sys_sync(void); /* it's really int */ extern void hard_reset_now(void); extern void do_unblank_screen(void); extern void DAC960_Finalize(void); extern void gdth_halt(void); extern int C_A_D; int panic_timeout = 0; void panic_setup(char *str, int *ints) { if (ints[0] == 1) panic_timeout = ints[1]; } NORET_TYPE void panic(const char * fmt, ...) { static char buf[1024]; va_list args; int i; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); printk(KERN_EMERG "Kernel panic: %s\n",buf); if (current == task[0]) printk(KERN_EMERG "In swapper task - not syncing\n"); else sys_sync(); #ifdef CONFIG_CONSOLE do_unblank_screen(); #endif /* CONFIG_CONSOLE */ if (panic_timeout > 0) { /* * Delay timeout seconds before rebooting the machine. * We can't use the "normal" timers since we just panicked.. */ printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout); for(i = 0; i < (panic_timeout*1000); i++) udelay(1000); #ifdef CONFIG_BLK_DEV_DAC960 DAC960_Finalize(); #endif #ifdef CONFIG_SCSI_GDTH gdth_halt(); #endif hard_reset_now(); } #ifdef CONFIG_NETtel nettel_panic(); #endif for(;;); } /* * GCC 2.5.8 doesn't always optimize correctly; see include/asm/segment.h */ int bad_user_access_length(void) { panic("bad_user_access_length executed (not cool, dude)"); } 1.1 or1k/rc203soc/sw/uClinux/kernel/printk.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/printk.c?rev=1.1&content-type=text/x-cvsweb-markup Index: printk.c =================================================================== /* * linux/kernel/printk.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Modified to make sys_syslog() more flexible: added commands to * return the last 4k of kernel messages, regardless of whether * they've been read or not. Added option to suppress kernel printk's * to the console. Added hook for sending the console messages * elsewhere, in preparation for a serial line console (someday). * Ted Ts'o, 2/11/93. */ #include <stdarg.h> #include <asm/segment.h> #include <asm/system.h> #include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> #include <linux/tty.h> #include <linux/tty_driver.h> #if defined(CONFIG_REDUCED_MEMORY) && !defined(CONFIG_DUMPTOFLASH) #define LOG_BUF_LEN 1024 /* Originally: 8192 */ #else /* !CONFIG_REDUCED_MEMORY */ #define LOG_BUF_LEN 8192 #endif /* !CONFIG_REDUCED_MEMORY */ static char buf[1024]; extern void console_print(const char *); /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */ /* We show everything that is MORE important than this.. */ #define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */ #define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */ unsigned long log_size = 0; struct wait_queue * log_wait = NULL; int console_loglevel = 8;/*DEFAULT_CONSOLE_LOGLEVEL;*/ static void (*console_print_proc)(const char *) = 0; static char log_buf[LOG_BUF_LEN]; static unsigned long log_start = 0; static unsigned long logged_chars = 0; /* * Commands to sys_syslog: * * 0 -- Close the log. Currently a NOP. * 1 -- Open the log. Currently a NOP. * 2 -- Read from the log. * 3 -- Read up to the last 4k of messages in the ring buffer. * 4 -- Read and clear last 4k of messages in the ring buffer * 5 -- Clear ring buffer. * 6 -- Disable printk's to console * 7 -- Enable printk's to console * 8 -- Set level of messages printed to console */ asmlinkage int sys_syslog(int type, char * buf, int len) { unsigned long i, j, count; int do_clear = 0; char c; int error; if ((type != 3) && !suser()) return -EPERM; switch (type) { case 0: /* Close log */ return 0; case 1: /* Open log */ return 0; case 2: /* Read from log */ if (!buf || len < 0) return -EINVAL; if (!len) return 0; error = verify_area(VERIFY_WRITE,buf,len); if (error) return error; cli(); while (!log_size) { if (current->signal & ~current->blocked) { sti(); return -ERESTARTSYS; } interruptible_sleep_on(&log_wait); } i = 0; while (log_size && i < len) { c = *((char *) log_buf+log_start); log_start++; log_size--; log_start &= LOG_BUF_LEN-1; sti(); put_user(c,buf); buf++; i++; cli(); } sti(); return i; case 4: /* Read/clear last kernel messages */ do_clear = 1; /* FALL THRU */ case 3: /* Read last kernel messages */ if (!buf || len < 0) return -EINVAL; if (!len) return 0; error = verify_area(VERIFY_WRITE,buf,len); if (error) return error; count = len; if (count > LOG_BUF_LEN) count = LOG_BUF_LEN; if (count > logged_chars) count = logged_chars; j = log_start + log_size - count; for (i = 0; i < count; i++) { c = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1))); put_user(c, buf++); } if (do_clear) logged_chars = 0; return i; case 5: /* Clear ring buffer */ logged_chars = 0; return 0; case 6: /* Disable logging to console */ console_loglevel = MINIMUM_CONSOLE_LOGLEVEL; return 0; case 7: /* Enable logging to console */ console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; return 0; case 8: if (len < 1 || len > 8) return -EINVAL; if (len < MINIMUM_CONSOLE_LOGLEVEL) len = MINIMUM_CONSOLE_LOGLEVEL; console_loglevel = len; return 0; } return -EINVAL; } asmlinkage int printk(const char *fmt, ...) { va_list args; int i; char *msg, *p, *buf_end; static char msg_level = -1; long flags; save_flags(flags); cli(); va_start(args, fmt); i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */ buf_end = buf + 3 + i; va_end(args); for (p = buf + 3; p < buf_end; p++) { msg = p; if (msg_level < 0) { if ( p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>' ) { p -= 3; p[0] = '<'; p[1] = DEFAULT_MESSAGE_LOGLEVEL + '0'; p[2] = '>'; } else msg += 3; msg_level = p[1] - '0'; } for (; p < buf_end; p++) { log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p; if (log_size < LOG_BUF_LEN) log_size++; else { log_start++; log_start &= LOG_BUF_LEN-1; } logged_chars++; if (*p == '\n') break; } if (msg_level < console_loglevel && console_print_proc) { char tmp = p[1]; p[1] = '\0'; (*console_print_proc)(msg); p[1] = tmp; } if (*p == '\n') msg_level = -1; } restore_flags(flags); wake_up_interruptible(&log_wait); return i; } /* * The console driver calls this routine during kernel initialization * to register the console printing procedure with printk() and to * print any messages that were printed by the kernel before the * console driver was initialized. */ void register_console(void (*proc)(const char *)) { int i,j; int p = log_start; char buf[16]; char msg_level = -1; char *q; console_print_proc = proc; for (i=0,j=0; i < log_size; i++) { buf[j++] = log_buf[p]; p++; p &= LOG_BUF_LEN-1; if (buf[j-1] != '\n' && i < log_size - 1 && j < sizeof(buf)-1) continue; buf[j] = 0; q = buf; if (msg_level < 0) { msg_level = buf[1] - '0'; q = buf + 3; } if (msg_level < console_loglevel) (*proc)(q); if (buf[j-1] == '\n') msg_level = -1; j = 0; } } /* * Return log buffer address and size. */ unsigned long sys_getlog(char **bp) { *bp = &log_buf[0]; return(log_size); } void sys_resetlog(void) { log_start = 0; log_size = 0; logged_chars = 0; } /* * Write a message to a certain tty, not just the console. This is used for * messages that need to be redirected to a specific tty. * We don't put it into the syslog queue right now maybe in the future if * really needed. */ void tty_write_message(struct tty_struct *tty, char *msg) { if (tty && tty->driver.write) tty->driver.write(tty, 0, msg, strlen(msg)); return; } 1.1 or1k/rc203soc/sw/uClinux/kernel/resource.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/resource.c?rev=1.1&content-type=text/x-cvsweb-markup Index: resource.c =================================================================== /* * linux/kernel/resource.c * * Copyright (C) 1995 Linus Torvalds * David Hinds * * Kernel io-region resource management */ /* * Revisions for CONFIG_REDUCED_MEMORY by Kenneth Albanowski <kjahds@k...>, * Copyright (C) 1997, 1998 The Silver Hammer Group, Ltd. */ #include <linux/sched.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/ioport.h> #ifdef CONFIG_REDUCED_MEMORY #define IOTABLE_SIZE 1 /* Originally 128 */ #else /* !CONFIG_REDUCED_MEMORY */ #define IOTABLE_SIZE 128 #endif /* !CONFIG_REDUCED_MEMORY */ typedef struct resource_entry_t { u_long from, num; const char *name; struct resource_entry_t *next; } resource_entry_t; static resource_entry_t iolist = { 0, 0, "", NULL }; static resource_entry_t iotable[IOTABLE_SIZE]; /* * This generates the report for /proc/ioports */ int get_ioport_list(char *buf) { resource_entry_t *p; int len = 0; for (p = iolist.next; (p) && (len < 4000); p = p->next) len += sprintf(buf+len, "%04lx-%04lx : %s\n", p->from, p->from+p->num-1, p->name); if (p) len += sprintf(buf+len, "4K limit reached!\n"); return len; } /* * The workhorse function: find where to put a new entry */ static resource_entry_t *find_gap(resource_entry_t *root, u_long from, u_long num) { unsigned long flags; resource_entry_t *p; if (from > from+num-1) return NULL; save_flags(flags); cli(); for (p = root; ; p = p->next) { if ((p != root) && (p->from+p->num-1 >= from)) { p = NULL; break; } if ((p->next == NULL) || (p->next->from > from+num-1)) break; } restore_flags(flags); return p; } /* * Call this from the device driver to register the ioport region. */ void request_region(unsigned int from, unsigned int num, const char *name) { resource_entry_t *p; int i; for (i = 0; i < IOTABLE_SIZE; i++) if (iotable[i].num == 0) break; if (i == IOTABLE_SIZE) printk("warning: ioport table is full\n"); else { p = find_gap(&iolist, from, num); if (p == NULL) return; iotable[i].name = name; iotable[i].from = from; iotable[i].num = num; iotable[i].next = p->next; p->next = &iotable[i]; return; } } /* * Call this when the device driver is unloaded */ void release_region(unsigned int from, unsigned int num) { resource_entry_t *p, *q; for (p = &iolist; ; p = q) { q = p->next; if (q == NULL) break; if ((q->from == from) && (q->num == num)) { q->num = 0; p->next = q->next; return; } } } /* * Call this to check the ioport region before probing */ int check_region(unsigned int from, unsigned int num) { return (find_gap(&iolist, from, num) == NULL) ? -EBUSY : 0; } /* Called from init/main.c to reserve IO ports. */ void reserve_setup(char *str, int *ints) { int i; for (i = 1; i < ints[0]; i += 2) request_region(ints[i], ints[i+1], "reserved"); } 1.1 or1k/rc203soc/sw/uClinux/kernel/sched.c http://www.opencores.org/cvsweb.shtml/or1k/rc203soc/sw/uClinux/kernel/sched.c?rev=1.1&content-type=text/x-cvsweb-markup Index: sched.c =================================================================== /* * linux/kernel/sched.c * * Copyright (C) 1991, 1992 Linus Torvalds * * 1996-04-21 Modified by Ulrich Windl to make NTP work * 1996-12-23 Modified by Dave Grothe to fix bugs in semaphores and * make semaphores SMP safe * 1997-01-28 Modified by Finn Arne Gangstad to make timers scale better. * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills */ /* * 'sched.c' is the main kernel file. It contains scheduling primitives * (sleep_on, wakeup, schedule etc) as well as a number of simple system * call functions (type getpid()), which just extract a field from * current-task */ /* * uClinux revisions for NO_MM * Copyright (C) 1998 Kenneth Albanowski <kjahds@k...>, * The Silver Hammer Group, Ltd. */ #include <linux/signal.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/kernel.h> #include <linux/kernel_stat.h> #include <linux/fdreg.h> #include <linux/errno.h> #include <linux/time.h> #include <linux/ptrace.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/tqueue.h> #include <linux/resource.h> #include <linux/mm.h> #include <linux/smp.h> #include <asm/system.h> #include <asm/io.h> #include <asm/segment.h> #include <asm/pgtable.h> #include <asm/mmu_context.h> #include <linux/timex.h> /* SIMON - I don't know why the fuck this prototype can't be in header but it won't work */ extern void switch_to(struct task_struct *prev, struct task_struct *next); /* * kernel variables */ int securelevel = 0; /* system security level */ long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */ volatile struct timeval xtime; /* The current time */ int tickadj = 500/HZ ? 500/HZ : 1; /* microsecs */ DECLARE_TASK_QUEUE(tq_timer); DECLARE_TASK_QUEUE(tq_immediate); DECLARE_TASK_QUEUE(tq_scheduler); /* * phase-lock loop variables */ /* TIME_ERROR prevents overwriting the CMOS clock */ int time_state = TIME_ERROR; /* clock synchronization status */ int time_status = STA_UNSYNC; /* clock status bits */ long time_offset = 0; /* time adjustment (us) */ long time_constant = 2; /* pll time constant */ long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ long time_precision = 1; /* clock precision (us) */ long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */ long time_esterror = NTP_PHASE_LIMIT; /* estimated error (us) */ long time_phase = 0; /* phase offset (scaled us) */ long time_freq = ((1000000 + HZ/2) % HZ - HZ/2) << SHIFT_USEC; /* frequency offset (scaled ppm) */ long time_adj = 0; /* tick adjust (scaled 1 / HZ) */ long time_reftime = 0; /* time at last adjustment (s) */ long time_adjust = 0; long time_adjust_step = 0; int need_resched = 0; unsigned long event = 0; extern int _setitimer(int, struct itimerval *, struct itimerval *); unsigned int * prof_buffer = NULL; unsigned long prof_len = 0; unsigned long prof_shift = 0; #define _S(nr) (1<<((nr)-1)) extern void mem_use(void); unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; #ifndef NO_MM unsigned long init_user_stack[1024] = { STACK_MAGIC, }; static struct vm_area_struct init_mmap = INIT_MMAP; #endif /* !NO_MM */ static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; struct mm_struct init_mm = INIT_MM; struct task_struct init_task = INIT_TASK; unsigned long volatile jiffies=0; struct task_struct *current_set[NR_CPUS]; struct task_struct *last_task_used_math = NULL; struct task_struct * task[NR_TASKS] = {&init_task, }; struct kernel_stat kstat = { 0 }; static inline void add_to_runqueue(struct task_struct * p) { #ifdef __SMP__ int cpu=smp_processor_id(); #endif #if 1 /* sanity tests */ if (p->next_run || p->prev_run) { printk("task already on run-queue\n"); return; } #endif if (p->policy != SCHED_OTHER || p->counter > current->counter + 3) need_resched = 1; nr_running++; (p->prev_run = init_task.prev_run)->next_run = p; p->next_run = &init_task; init_task.prev_run = p; #ifdef __SMP__ /* this is safe only if called with cli()*/ while(set_bit(31,&smp_process_available)) { while(test_bit(31,&smp_process_available)) { if(clear_bit(cpu,&smp_invalidate_needed)) { local_flush_tlb(); set_bit(cpu,&cpu_callin_map[0]); } } } smp_process_available++; clear_bit(31,&smp_process_available); if ((0!=p->pid) && smp_threads_ready) { int i; for (i=0;i<smp_num_cpus;i++) { if (0==current_set[cpu_logical_map[i]]->pid) { smp_message_pass(cpu_logical_map[i], MSG_RESCHEDULE, 0L, 0); break; } } } #endif } static inline void del_from_runqueue(struct task_struct * p) { struct task_struct *next = p->next_run; struct task_struct *prev = p->prev_run; #if 1 /* sanity tests */ if (!next || !prev) { printk("task not on run-queue\n"); return; } #endif if (p == &init_task) { static int nr = 0; if (nr < 5) { nr++; printk("idle task may not sleep\n"); } return; } nr_running--; next->prev_run = prev; prev->next_run = next; p->next_run = NULL; p->prev_run = NULL; } static inline void move_last_runqueue(struct task_struct * p) { struct task_struct *next = p->next_run; struct task_struct *prev = p->prev_run; /* remove from list */ next->prev_run = prev; prev->next_run = next; /* add back to list */ p->next_run = &init_task; prev = init_task.prev_run; init_task.prev_run = p; p->prev_run = prev; prev->next_run = p; } /* * Wake up a process. Put it on the run-queue if it's not * already there. The "current" process is always on the * run-queue (except when the actual re-schedule is in * progress), and as such you're allowed to do the simpler * "current->state = TASK_RUNNING" to mark yourself runnable * without the overhead of this. */ inline void wake_up_process(struct task_struct * p) { unsigned long flags; save_flags(flags); cli(); p->state = TASK_RUNNING; if (!p->next_run) add_to_runqueue(p); restore_flags(flags); } static void process_timeout(unsigned long __data) { struct task_struct * p = (struct task_struct *) __data; p->timeout = 0; wake_up_process(p); } /* * This is the function that decides how desirable a process is.. * You can weigh different processes against each other depending * on what CPU they've run on lately etc to try to handle cache * and TLB miss penalties. * * Return values: * -1000: never select this * 0: out of time, recalculate counters (but it might still be * selected) * +ve: "goodness" value (the larger, the better) * +1000: realtime process, select this. */ static inline int goodness(struct task_struct * p, struct task_struct * prev, int this_cpu) { int weight; #ifdef __SMP__ /* We are not permitted to run a task someone else is running */ if (p->processor != NO_PROC_ID) return -1000; #ifdef PAST_2_0 /* This process is locked to a processor group */ if (p->processor_mask && !(p->processor_mask & (1<<this_cpu)) return -1000; #endif #endif /* * Realtime process, select the first one on the * runqueue (taking priorities within processes * into account). */ if (p->policy != SCHED_OTHER) return 1000 + p->rt_priority; /* * Give the process a first-approximation goodness value * according to the number of clock-ticks it has left. * * Don't do any other calculations if the time slice is * over.. */ weight = p->counter; if (weight) { #ifdef __SMP__ /* Give a largish advantage to the same processor... */ /* (this is equivalent to penalizing other processors) */ if (p->last_processor == this_cpu) weight += PROC_CHANGE_PENALTY; #endif /* .. and a slight advantage to the current process */ if (p == prev) weight += 1; } return weight; } /* The following allow_interrupts function is used to workaround a rare but nasty deadlock situation that is possible for 2.0.x Intel SMP because it uses a single kernel lock and interrupts are only routed to the boot CPU. There are two deadlock scenarios this code protects against. The first scenario is that if a CPU other than the boot CPU holds the kernel lock and needs to wait for an operation to complete that itself requires an interrupt, there is a deadlock since the boot CPU may be able to accept the interrupt but will not be able to acquire the kernel lock to process it. The workaround for this deadlock requires adding calls to allow_interrupts to places where this deadlock is possible. These places are known to be present in buffer.c and keyboard.c. It is also possible that there are other such places which have not been identified yet. In order to break the deadlock, the code in allow_interrupts temporarily yields the kernel lock directly to the boot CPU to allow the interrupt to be processed. The boot CPU interrupt entry code indicates that it is spinning waiting for the kernel lock by setting the smp_blocked_interrupt_pending variable. This code notices that and manipulates the active_kernel_processor variable to yield the kernel lock without ever clearing it. When the interrupt has been processed, the saved_active_kernel_processor variable contains the value for the interrupt exit code to restore, either the APICID of the CPU that granted it the kernel lock, or NO_PROC_ID in the normal case where no yielding occurred. Restoring active_kernel_processor from saved_active_kernel_processor returns the kernel lock back to the CPU that yielded it. The second form of deadlock is even more insidious. Suppose the boot CPU takes a page fault and then the previous scenario ensues. In this case, the boot CPU would spin with interrupts disabled waiting to acquire the kernel lock. To resolve this deadlock, the kernel lock acquisition code must enable interrupts briefly so that the pending interrupt can be handled as in the case above. An additional form of deadlock is where kernel code running on a non-boot CPU waits for the jiffies variable to be incremented. This deadlock is avoided by having the spin loops in ENTER_KERNEL increment jiffies appro