diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /kernel | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 22 | ||||
-rw-r--r-- | kernel/dma.c | 95 | ||||
-rw-r--r-- | kernel/exec_domain.c | 2 | ||||
-rw-r--r-- | kernel/exit.c | 47 | ||||
-rw-r--r-- | kernel/fork.c | 175 | ||||
-rw-r--r-- | kernel/itimer.c | 1 | ||||
-rw-r--r-- | kernel/ksyms.c | 183 | ||||
-rw-r--r-- | kernel/ksyms.ver | 194 | ||||
-rw-r--r-- | kernel/module.c | 201 | ||||
-rw-r--r-- | kernel/printk.c | 7 | ||||
-rw-r--r-- | kernel/resource.c | 138 | ||||
-rw-r--r-- | kernel/sched.c | 796 | ||||
-rw-r--r-- | kernel/signal.c | 174 | ||||
-rw-r--r-- | kernel/softirq.c | 55 | ||||
-rw-r--r-- | kernel/sys.c | 114 | ||||
-rw-r--r-- | kernel/time.c | 121 | ||||
-rw-r--r-- | kernel/tqueue.c | 10 | ||||
-rw-r--r-- | kernel/vsprintf.c | 309 |
18 files changed, 1928 insertions, 716 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index 99ced21ae..fd73ad5f0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -12,24 +12,23 @@ .c.s: $(CC) $(CFLAGS) -S $< .s.o: - $(AS) $(ASFLAGS) -o $*.o $< + $(AS) -o $*.o $< .c.o: $(CC) $(CFLAGS) -c $< -OBJS = sched.o entry.o traps.o irq.o dma.o fork.o exec_domain.o \ - panic.o printk.o vsprintf.o sys.o module.o ksyms.o exit.o \ - signal.o ptrace.o ioport.o itimer.o \ - info.o ldt.o time.o tqueue.o vm86.o bios32.o splx.o dummy.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 -all: kernel.o +SYMTAB_OBJS = ksyms.o -kernel.o: $(OBJS) - $(LD) -r -o kernel.o $(OBJS) - sync +all: kernel.o -entry.s: entry.S +include ../versions.mk -entry.o: entry.s +kernel.o: $(SYMTAB_OBJS) $(OBJS) + $(LD) -r -o kernel.o $(SYMTAB_OBJS) $(OBJS) + sync sched.o: sched.c $(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $< @@ -38,6 +37,7 @@ dep: $(CPP) -M *.c > .depend dummy: +modules: # # include a dependency file if one exists diff --git a/kernel/dma.c b/kernel/dma.c index 799439ed6..94b121653 100644 --- a/kernel/dma.c +++ b/kernel/dma.c @@ -1,12 +1,19 @@ -/* $Id: dma.c,v 1.5 1992/11/18 02:49:05 root Exp root $ +/* $Id: dma.c,v 1.7 1994/12/28 03:35:33 root Exp root $ * linux/kernel/dma.c: A DMA channel allocator. Inspired by linux/kernel/irq.c. - * Written by Hennus Bergman, 1992. + * + * 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/system.h> #include <asm/dma.h> +#include <asm/system.h> /* A note on resource allocation: @@ -16,7 +23,7 @@ * * 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. @@ -26,81 +33,52 @@ -/* Channel n is busy iff dma_chan_busy[n] != 0. +/* 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. */ -/* -static volatile unsigned int dma_chan_busy[MAX_DMA_CHANNELS] = { - 0, 0, 0, 0, 1, 0, 0, 0 -}; -*/ -static volatile char * dma_chan_busy[MAX_DMA_CHANNELS] = { - 0, - 0, - 0, - 0, - "cascade", - 0, - 0, - 0 -}; -/* Atomically swap memory location [32 bits] with `newval'. - * This avoid the cli()/sti() junk and related problems. - * [And it's faster too :-)] - * Maybe this should be in include/asm/mutex.h and be used for - * implementing kernel-semaphores as well. - */ -static __inline__ unsigned int mutex_atomic_swap(volatile unsigned int * p, unsigned int newval) -{ - unsigned int semval = newval; - - /* If one of the operands for the XCHG instructions is a memory ref, - * it makes the swap an uninterruptible RMW cycle. - * - * One operand must be in memory, the other in a register, otherwise - * the swap may not be atomic. - */ - -#if 0 - asm __volatile__ ("xchgl %2, %0\n" - : /* outputs: semval */ "=r" (semval) - : /* inputs: newval, p */ "0" (semval), "m" (*p) - ); /* p is a var, containing an address */ -#else - /* - * RB: Try atomic exchange from include/asm/system.h - * This should be portable... - */ - atomic_exchange(p,semval) -#endif - return semval; -} /* mutex_atomic_swap */ +struct dma_chan { + int lock; + char *device_id; +}; +static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 1, "cascade" }, + { 0, 0 }, + { 0, 0 }, + { 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]) { + if (dma_chan_busy[i].lock) { len += sprintf(buf+len, "%2d: %s\n", - i, - dma_chan_busy[i]); + i, + dma_chan_busy[i].device_id); } } return len; -} +} /* get_dma_list */ -int request_dma(unsigned int dmanr, char * deviceID) + +int request_dma(unsigned int dmanr, char * device_id) { if (dmanr >= MAX_DMA_CHANNELS) return -EINVAL; - if (mutex_atomic_swap((unsigned int *) &dma_chan_busy[dmanr], (unsigned int) deviceID) != 0) + if (xchg_u32(&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 */ @@ -113,10 +91,9 @@ void free_dma(unsigned int dmanr) return; } - if (mutex_atomic_swap((unsigned int *) &dma_chan_busy[dmanr], 0) == 0) { + if (xchg_u32(&dma_chan_busy[dmanr].lock, 0) == 0) { printk("Trying to free free DMA%d\n", dmanr); return; } } /* free_dma */ - diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index c80423314..7f0114a46 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -1,7 +1,7 @@ #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); diff --git a/kernel/exit.c b/kernel/exit.c index 3a48c5c23..59c0b075b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -47,7 +47,7 @@ int send_sig(unsigned long sig,struct task_struct * p,int priv) if (!p || sig > 32) return -EINVAL; if (!priv && ((sig != SIGCONT) || (current->session != p->session)) && - (current->euid != p->euid) && (current->uid != p->uid) && !suser()) + (current->euid != p->euid) && (current->euid != p->uid) && !suser()) return -EPERM; if (!sig) return 0; @@ -91,6 +91,7 @@ void release(struct task_struct * p) } for (i=1 ; i<NR_TASKS ; i++) if (task[i] == p) { + nr_tasks--; task[i] = NULL; REMOVE_LINKS(p); if (STACK_MAGIC != *(unsigned long *)p->kernel_stack_page) @@ -354,38 +355,6 @@ static void forget_original_parent(struct task_struct * father) } } -static void exit_mm(void) -{ - struct vm_area_struct * mpnt; - - mpnt = current->mm->mmap; - current->mm->mmap = NULL; - while (mpnt) { - struct vm_area_struct * next = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) - mpnt->vm_ops->close(mpnt); - if (mpnt->vm_inode) - iput(mpnt->vm_inode); - kfree(mpnt); - mpnt = next; - } - -#ifdef __i386__ - /* forget local segments */ - __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0" - : /* no outputs */ - : "r" (0)); - current->tss.ldt = 0; - if (current->ldt) { - void * ldt = current->ldt; - current->ldt = NULL; - vfree(ldt); - } -#endif - - free_page_tables(current); -} - static void exit_files(void) { int i; @@ -412,11 +381,13 @@ NORET_TYPE void do_exit(long code) intr_count = 0; } fake_volatile: - if (current->semundo) - sem_exit(); - exit_mm(); + current->flags |= PF_EXITING; + sem_exit(); + exit_mmap(current); + free_page_tables(current); exit_files(); exit_fs(); + exit_thread(); forget_original_parent(current); /* * Check to see if any process groups have become orphaned @@ -508,7 +479,7 @@ asmlinkage int sys_exit(int error_code) do_exit((error_code&0xff)<<8); } -asmlinkage int sys_wait4(pid_t pid,unsigned long * stat_addr, int options, struct rusage * ru) +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 }; @@ -599,7 +570,7 @@ end_wait4: * sys_waitpid() remains for compatibility. waitpid() should be * implemented by calling sys_wait4() from libc.a. */ -asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) +asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options) { return sys_wait4(pid, stat_addr, options, NULL); } diff --git a/kernel/fork.c b/kernel/fork.c index ceb6e09c1..104ffea96 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -24,13 +24,8 @@ #include <asm/segment.h> #include <asm/system.h> -asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); - -/* These should maybe be in <linux/tasks.h> */ - -#define MAX_TASKS_PER_USER (NR_TASKS/2) -#define MIN_TASKS_LEFT_FOR_ROOT 4 - +int nr_tasks=1; +int nr_running=1; long last_pid=0; static int find_empty_process(void) @@ -59,7 +54,7 @@ repeat: goto repeat; } if (tasks_free <= MIN_TASKS_LEFT_FOR_ROOT || - this_user_tasks > MAX_TASKS_PER_USER) + this_user_tasks > current->rlim[RLIMIT_NPROC].rlim_cur) if (current->uid) return -EAGAIN; return free_task; @@ -95,18 +90,26 @@ static int dup_mmap(struct task_struct * tsk) p = &tsk->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) + if (!tmp) { + exit_mmap(tsk); return -ENOMEM; + } *tmp = *mpnt; tmp->vm_task = tsk; tmp->vm_next = NULL; - if (tmp->vm_inode) + 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 (tmp->vm_ops && tmp->vm_ops->open) tmp->vm_ops->open(tmp); *p = tmp; p = &tmp->vm_next; } + build_mmap_avl(tsk); return 0; } @@ -136,7 +139,6 @@ static void copy_files(unsigned long clone_flags, struct task_struct * p) static int copy_mm(unsigned long clone_flags, struct task_struct * p) { if (clone_flags & COPYVM) { - p->mm->swappable = 1; p->mm->min_flt = p->mm->maj_flt = 0; p->mm->cmin_flt = p->mm->cmaj_flt = 0; if (copy_page_tables(p)) @@ -158,121 +160,25 @@ static void copy_fs(unsigned long clone_flags, struct task_struct * p) } /* - * FIXME: This functions shouldn't be in this file - */ -#if defined (__i386__) - -#define IS_CLONE (regs->orig_eax == __NR_clone) - -static unsigned long -arch_clone(struct task_struct *p, int nr, unsigned long clone_flags - struct pt_regs *regs) -{ - struct pt_regs * childregs; - int i; - - /* - * set up new TSS - */ - p->tss.es = KERNEL_DS; - p->tss.cs = KERNEL_CS; - p->tss.ss = KERNEL_DS; - p->tss.ds = KERNEL_DS; - p->tss.fs = USER_DS; - p->tss.gs = KERNEL_DS; - p->tss.ss0 = KERNEL_DS; - p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE; - p->tss.tr = _TSS(nr); - childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; - p->tss.esp = (unsigned long) childregs; - p->tss.eip = (unsigned long) ret_from_sys_call; - *childregs = *regs; - childregs->eax = 0; - p->tss.back_link = 0; - /* - * iopl is always 0 for a new process - */ - p->tss.eflags = regs->eflags & 0xffffcfff; - if (IS_CLONE) { - if (regs->ebx) - childregs->esp = regs->ebx; - clone_flags = regs->ecx; - if (childregs->esp == regs->esp) - clone_flags |= COPYVM; - } - p->tss.ldt = _LDT(nr); - if (p->ldt) { - p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE); - if (p->ldt != NULL) - memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE); - } - p->tss.bitmap = offsetof(struct tss_struct,io_bitmap); - for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */ - p->tss.io_bitmap[i] = ~0; - if (last_task_used_math == current) - __asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387)); - - return clone_flags; -} -#elif defined (__mips__) - -#include <asm/mipsregs.h> - -#define IS_CLONE (regs->orig_reg2 == __NR_clone) - -static unsigned long -arch_clone(struct task_struct *p, int nr, unsigned long clone_flags, - struct pt_regs *regs) -{ - struct pt_regs * childregs; - - /* - * set up new TSS - */ - p->tss.fs = KERNEL_DS; - p->tss.ksp = p->kernel_stack_page + PAGE_SIZE; - childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; - p->tss.reg29 = (unsigned long) childregs; /* new sp */ - p->tss.cp0_epc = (unsigned long) ret_from_sys_call; - *childregs = *regs; - childregs->reg1 = 0; - /* - * New tasks loose permission to use the fpa. This accelerates task - * switching for non fp programms, which true for the most programms. - */ - p->tss.cp0_status = regs->cp0_status & ~ST0_CU0; - if (IS_CLONE) { - if (regs->reg2) - childregs->reg29 = regs->reg2; - clone_flags = regs->reg3; - if (childregs->reg29 == regs->reg29) - clone_flags |= COPYVM; - } - - return clone_flags; -} -#endif - -/* * 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. */ -asmlinkage int sys_fork(struct pt_regs regs) +int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) { - struct task_struct *p; int nr; - unsigned long clone_flags = COPYVM | SIGCHLD; + unsigned long new_stack; + struct task_struct *p; - /* - * Clone the machine independend part of every process - */ if(!(p = (struct task_struct*)__get_free_page(GFP_KERNEL))) goto bad_fork; + new_stack = get_free_page(GFP_KERNEL); + if (!new_stack) + goto bad_fork_free; nr = find_empty_process(); if (nr < 0) goto bad_fork_free; - task[nr] = p; + *p = *current; if (p->exec_domain && p->exec_domain->use_count) @@ -281,51 +187,36 @@ asmlinkage int sys_fork(struct pt_regs regs) (*p->binfmt->use_count)++; p->did_exec = 0; - p->kernel_stack_page = 0; + p->kernel_stack_page = new_stack; + *(unsigned long *) p->kernel_stack_page = STACK_MAGIC; p->state = TASK_UNINTERRUPTIBLE; p->flags &= ~(PF_PTRACED|PF_TRACESYS); p->pid = last_pid; p->p_pptr = p->p_opptr = current; p->p_cptr = NULL; - SET_LINKS(p); 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; p->leader = 0; /* process leadership doesn't inherit */ + p->tty_old_pgrp = 0; p->utime = p->stime = 0; p->cutime = p->cstime = 0; p->start_time = jiffies; + p->mm->swappable = 0; /* don't try to swap it out before it's set up */ + task[nr] = p; + SET_LINKS(p); + nr_tasks++; - /* - * set up new kernel stack - */ - if (!(p->kernel_stack_page = get_free_page(GFP_KERNEL))) - goto bad_fork_cleanup; - *(unsigned long *)p->kernel_stack_page = STACK_MAGIC; - - /* - * And now let's do the processor dependend things... - */ - - clone_flags = arch_clone(p, nr, clone_flags, ®s); - + /* copy all the process information */ + copy_thread(nr, clone_flags, usp, p, regs); if (copy_mm(clone_flags, p)) goto bad_fork_cleanup; p->semundo = NULL; copy_files(clone_flags, p); copy_fs(clone_flags, p); -#if defined (__i386__) - /* - * May I move this into arch_clone without trouble, Linus??? - */ - set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss)); - if (p->ldt) - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); - else - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); -#endif - + /* ok, now we should be set up.. */ + p->mm->swappable = 1; p->exit_signal = clone_flags & CSIGNAL; p->counter = current->counter >> 1; p->state = TASK_RUNNING; /* do this last, just in case */ @@ -333,10 +224,10 @@ asmlinkage int sys_fork(struct pt_regs regs) bad_fork_cleanup: task[nr] = NULL; REMOVE_LINKS(p); - free_page(p->kernel_stack_page); + nr_tasks--; bad_fork_free: + free_page(new_stack); free_page((long) p); bad_fork: return -EAGAIN; } - diff --git a/kernel/itimer.c b/kernel/itimer.c index 4d5fa0f67..02f7b7ce8 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -11,6 +11,7 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/time.h> +#include <linux/mm.h> #include <asm/segment.h> diff --git a/kernel/ksyms.c b/kernel/ksyms.c index d13e3c4a4..ccb2f2b4c 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -3,11 +3,14 @@ * with dynamically loaded kernel modules. * Jon. * - * Stacked module support and unified symbol table added by - * Bjorn Ekwall <bj0rn@blox.se> + * - 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@blox.se> */ #include <linux/autoconf.h> +#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/blkdev.h> @@ -22,7 +25,6 @@ #include <linux/timer.h> #include <linux/binfmts.h> #include <linux/personality.h> -#include <linux/module.h> #include <linux/termios.h> #include <linux/tqueue.h> #include <linux/tty.h> @@ -30,48 +32,90 @@ #include <linux/locks.h> #include <linux/string.h> #include <linux/delay.h> -#ifdef CONFIG_INET +#include <linux/config.h> + +#ifdef CONFIG_NET #include <linux/net.h> #include <linux/netdevice.h> +#ifdef CONFIG_INET +#include <linux/ip.h> +#include <net/protocol.h> +#include <net/arp.h> +#include <net/tcp.h> +#if defined(CONFIG_PPP) || defined(CONFIG_SLIP) +#include "../drivers/net/slhc.h" +#endif +#endif +#endif +#ifdef CONFIG_PCI +#include <linux/bios32.h> +#include <linux/pci.h> +#endif +#if defined(CONFIG_MSDOS_FS) && !defined(CONFIG_UMSDOS_FS) +#include <linux/msdos_fs.h> #endif #include <asm/irq.h> -extern char floppy_track_buffer[]; + +extern char *floppy_track_buffer; + extern void set_device_ro(int dev,int flag); -#include <linux/delay.h> -#include <linux/locks.h> +extern struct file_operations * get_blkfops(unsigned int); extern void *sys_call_table; -/* must match struct internal_symbol !!! */ -#define X(name) { (void *) &name, "_" #name } - #ifdef CONFIG_FTAPE extern char * ftape_big_buffer; -extern void (*do_floppy)(void); +#endif + +#ifdef CONFIG_SCSI +#include "../drivers/scsi/scsi.h" +#include "../drivers/scsi/hosts.h" +#include "../drivers/scsi/constants.h" #endif extern int sys_tz; extern int request_dma(unsigned int dmanr, char * deviceID); extern void free_dma(unsigned int dmanr); -extern int do_execve(char * filename, char ** argv, char ** envp, - struct pt_regs * regs); -extern int do_signal(unsigned long oldmask, struct pt_regs * regs); - +extern int close_fp(struct file *filp); extern void (* iABI_hook)(struct pt_regs * regs); -struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ - { +struct symbol_table symbol_table = { +#include <linux/symtab_begin.h> +#ifdef CONFIG_MODVERSIONS + { (void *)1 /* Version version :-) */, "_Using_Versions" }, +#endif /* stackable module support */ X(rename_module_symbol), + X(register_symtab), /* system info variables */ + /* These check that they aren't defines (0/1) */ +#ifndef EISA_bus__is_a_macro X(EISA_bus), -#ifdef __i386__ +#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), @@ -97,6 +141,7 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(namei), X(lnamei), X(open_namei), + X(close_fp), X(check_disk_change), X(invalidate_buffers), X(fsync_dev), @@ -107,9 +152,12 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(set_blocksize), X(getblk), X(bread), + X(breada), X(brelse), X(ll_rw_block), X(__wait_on_buffer), + X(dcache_lookup), + X(dcache_add), /* device registration */ X(register_chrdev), @@ -126,17 +174,25 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(block_fsync), X(wait_for_request), X(blksize_size), + X(hardsect_size), X(blk_size), X(blk_dev), X(is_read_only), X(set_device_ro), X(bmap), X(sync_dev), + X(get_blkfops), /* Module creation of serial units */ X(register_serial), X(unregister_serial), + /* tty routines */ + X(tty_hangup), + X(tty_wait_until_sent), + X(tty_check_change), + X(tty_hung_up_p), + /* filesystem registration */ X(register_filesystem), X(unregister_filesystem), @@ -161,6 +217,7 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(del_timer), X(tq_timer), X(tq_immediate), + X(tq_scheduler), X(tq_last), X(timer_active), X(timer_table), @@ -168,6 +225,15 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ /* 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), @@ -198,7 +264,7 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(send_sig), /* Program loader interfaces */ - X(change_ldt), + X(setup_arg_pages), X(copy_strings), X(create_tables), X(do_execve), @@ -208,16 +274,33 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ /* Miscellaneous access points */ X(si_meminfo), - +#ifdef CONFIG_NET /* socket layer registration */ X(sock_register), X(sock_unregister), + /* Internet layer registration */ +#ifdef CONFIG_INET + X(inet_add_protocol), + X(inet_del_protocol), +#if defined(CONFIG_PPP) || defined(CONFIG_SLIP) + /* VJ header compression */ + X(slhc_init), + X(slhc_free), + X(slhc_remember), + X(slhc_compress), + X(slhc_uncompress), +#endif +#endif + /* Device callback registration */ + X(register_netdevice_notifier), + X(unregister_netdevice_notifier), +#endif #ifdef CONFIG_FTAPE /* The next labels are needed for ftape driver. */ X(ftape_big_buffer), - X(do_floppy), #endif + X(floppy_track_buffer), #ifdef CONFIG_INET /* support for loadable net drivers */ X(register_netdev), @@ -226,7 +309,6 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(alloc_skb), X(kfree_skb), X(dev_kfree_skb), - X(snarf_region), X(netif_rx), X(dev_rint), X(dev_tint), @@ -237,6 +319,31 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ X(dev_ioctl), X(dev_queue_xmit), X(dev_base), + X(dev_close), + X(arp_find), + X(n_tty_ioctl), + X(tty_register_ldisc), + X(kill_fasync), +#endif +#ifdef CONFIG_SCSI + /* Supports loadable scsi drivers */ + /* + * in_scan_scsis is a hack, and should go away once the new + * memory allocation code is in the NCR driver + */ + X(in_scan_scsis), + X(scsi_register_module), + X(scsi_unregister_module), + X(scsi_free), + X(scsi_malloc), + X(scsi_register), + X(scsi_unregister), + X(scsicam_bios_param), + X(scsi_init_malloc), + X(scsi_init_free), + X(print_command), + X(print_msg), + X(print_status), #endif /* Added to make file system as module */ X(set_writetime), @@ -251,13 +358,39 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ 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), +#if defined(CONFIG_MSDOS_FS) && !defined(CONFIG_UMSDOS_FS) + /* support for umsdos fs */ + X(msdos_bmap), + X(msdos_create), + X(msdos_file_read), + X(msdos_file_write), + X(msdos_lookup), + X(msdos_mkdir), + X(msdos_mmap), + X(msdos_put_inode), + X(msdos_put_super), + X(msdos_read_inode), + X(msdos_read_super), + X(msdos_readdir), + X(msdos_rename), + X(msdos_rmdir), + X(msdos_smap), + X(msdos_statfs), + X(msdos_truncate), + X(msdos_unlink), + X(msdos_unlink_umsdos), + X(msdos_write_inode), +#endif /******************************************************** * Do not add anything below this line, * as the stacked modules depend on this! */ - { NULL, NULL } /* mark end of table */ - }, - { { NULL, NULL } /* no module refs */ } +#include <linux/symtab_end.h> }; /* diff --git a/kernel/ksyms.ver b/kernel/ksyms.ver new file mode 100644 index 000000000..76537cd4c --- /dev/null +++ b/kernel/ksyms.ver @@ -0,0 +1,194 @@ +/**** This file is generated by genksyms DO NOT EDIT! ****/ +#if defined(CONFIG_MODVERSIONS) && !defined(__GENKSYMS__) +#ifndef _KSYMS_VER_ +#define _KSYMS_VER_ +#define rename_module_symbol _set_ver(rename_module_symbol, b81c73c1) +#define register_symtab _set_ver(register_symtab, e910ea66) +#define EISA_bus _set_ver(EISA_bus, 7e37737c) +#define wp_works_ok _set_ver(wp_works_ok, f37f99e9) +#define verify_area _set_ver(verify_area, 4cfda560) +#define do_mmap _set_ver(do_mmap, 677e7ee1) +#define do_munmap _set_ver(do_munmap, 6221f117) +#define zeromap_page_range _set_ver(zeromap_page_range, 7c395a26) +#define unmap_page_range _set_ver(unmap_page_range, 0110085f) +#define insert_vm_struct _set_ver(insert_vm_struct, 1f4e4882) +#define merge_segments _set_ver(merge_segments, 6854be5a) +#define __get_free_pages _set_ver(__get_free_pages, 5243d78b) +#define free_pages _set_ver(free_pages, 96448859) +#define kmalloc _set_ver(kmalloc, d31fb2cb) +#define kfree_s _set_ver(kfree_s, 1e72eb79) +#define vmalloc _set_ver(vmalloc, 667f3e25) +#define vfree _set_ver(vfree, 6df52add) +#define getname _set_ver(getname, 81487159) +#define putname _set_ver(putname, b19e8126) +#define __iget _set_ver(__iget, ee2b6320) +#define iput _set_ver(iput, 59241ced) +#define namei _set_ver(namei, 00478bcd) +#define lnamei _set_ver(lnamei, fcfddbb1) +#define open_namei _set_ver(open_namei, 414b2b0f) +#define close_fp _set_ver(close_fp, 1d4c15d8) +#define check_disk_change _set_ver(check_disk_change, b66ed457) +#define invalidate_buffers _set_ver(invalidate_buffers, c65255f1) +#define fsync_dev _set_ver(fsync_dev, a221190d) +#define permission _set_ver(permission, 0ebf7474) +#define inode_setattr _set_ver(inode_setattr, 0c80a3c1) +#define inode_change_ok _set_ver(inode_change_ok, 5d1cb326) +#define generic_mmap _set_ver(generic_mmap, d4ff59f3) +#define set_blocksize _set_ver(set_blocksize, f45fda38) +#define getblk _set_ver(getblk, d40228ac) +#define bread _set_ver(bread, c73bf0f0) +#define breada _set_ver(breada, eb8e858c) +#define brelse _set_ver(brelse, 4c27ac3d) +#define ll_rw_block _set_ver(ll_rw_block, f3aa4dd3) +#define __wait_on_buffer _set_ver(__wait_on_buffer, e8fcc968) +#define dcache_lookup _set_ver(dcache_lookup, 83336566) +#define dcache_add _set_ver(dcache_add, fe71f11e) +#define register_chrdev _set_ver(register_chrdev, da99513f) +#define unregister_chrdev _set_ver(unregister_chrdev, 61ea5ee8) +#define register_blkdev _set_ver(register_blkdev, 4699a621) +#define unregister_blkdev _set_ver(unregister_blkdev, d39bbca9) +#define tty_register_driver _set_ver(tty_register_driver, fcc8591c) +#define tty_unregister_driver _set_ver(tty_unregister_driver, c78132a8) +#define tty_std_termios _set_ver(tty_std_termios, cf350678) +#define block_read _set_ver(block_read, a7fe4f51) +#define block_write _set_ver(block_write, 902674c9) +#define block_fsync _set_ver(block_fsync, 182888d8) +#define wait_for_request _set_ver(wait_for_request, 9ca2932e) +#define blksize_size _set_ver(blksize_size, dea1eb55) +#define hardsect_size _set_ver(hardsect_size, ed1ee14f) +#define blk_size _set_ver(blk_size, f60b5398) +#define blk_dev _set_ver(blk_dev, dbf5fdd4) +#define is_read_only _set_ver(is_read_only, b0c5f83e) +#define set_device_ro _set_ver(set_device_ro, 8fb69e13) +#define bmap _set_ver(bmap, 73bb8bdd) +#define sync_dev _set_ver(sync_dev, 9bca536d) +#define get_blkfops _set_ver(get_blkfops, 83827791) +#define register_serial _set_ver(register_serial, 3425f38c) +#define unregister_serial _set_ver(unregister_serial, c013d717) +#define tty_hangup _set_ver(tty_hangup, e3487df0) +#define tty_wait_until_sent _set_ver(tty_wait_until_sent, da85d428) +#define tty_check_change _set_ver(tty_check_change, 705eaab0) +#define tty_hung_up_p _set_ver(tty_hung_up_p, f99ac1e4) +#define register_filesystem _set_ver(register_filesystem, 1c7110ef) +#define unregister_filesystem _set_ver(unregister_filesystem, 5e353af7) +#define register_binfmt _set_ver(register_binfmt, 66ece706) +#define unregister_binfmt _set_ver(unregister_binfmt, 41822618) +#define lookup_exec_domain _set_ver(lookup_exec_domain, 32f10d48) +#define register_exec_domain _set_ver(register_exec_domain, eda4711f) +#define unregister_exec_domain _set_ver(unregister_exec_domain, 78ea447c) +#define request_irq _set_ver(request_irq, 9e81629c) +#define free_irq _set_ver(free_irq, f487dc0c) +#define enable_irq _set_ver(enable_irq, 54e09f5f) +#define disable_irq _set_ver(disable_irq, b4449c1f) +#define bh_active _set_ver(bh_active, 98fb5ca1) +#define bh_mask _set_ver(bh_mask, 1abf3d3f) +#define add_timer _set_ver(add_timer, f13cb728) +#define del_timer _set_ver(del_timer, c7aff713) +#define tq_timer _set_ver(tq_timer, 46cf583e) +#define tq_immediate _set_ver(tq_immediate, 46cf583e) +#define tq_scheduler _set_ver(tq_scheduler, 46cf583e) +#define tq_last _set_ver(tq_last, 457cf547) +#define timer_active _set_ver(timer_active, 5a6747ee) +#define timer_table _set_ver(timer_table, 9e03b650) +#define request_dma _set_ver(request_dma, 2a687646) +#define free_dma _set_ver(free_dma, 5d4b914c) +#define disable_hlt _set_ver(disable_hlt, 794487ee) +#define enable_hlt _set_ver(enable_hlt, 9c7077bd) +#define check_region _set_ver(check_region, b91154fb) +#define request_region _set_ver(request_region, 138b0a1e) +#define release_region _set_ver(release_region, f41d6d31) +#define wake_up _set_ver(wake_up, e8d71419) +#define wake_up_interruptible _set_ver(wake_up_interruptible, 64c8cb92) +#define sleep_on _set_ver(sleep_on, 67a00cee) +#define interruptible_sleep_on _set_ver(interruptible_sleep_on, 6a5fc80d) +#define schedule _set_ver(schedule, 01000e51) +#define current _set_ver(current, fc1cb29b) +#define jiffies _set_ver(jiffies, 2f7c7437) +#define xtime _set_ver(xtime, e70c0be0) +#define loops_per_sec _set_ver(loops_per_sec, 40a14192) +#define need_resched _set_ver(need_resched, dfc016ea) +#define kill_proc _set_ver(kill_proc, 911f760a) +#define kill_pg _set_ver(kill_pg, 0a758a45) +#define kill_sl _set_ver(kill_sl, 49625e94) +#define panic _set_ver(panic, 400c0de3) +#define printk _set_ver(printk, ad1148ba) +#define sprintf _set_ver(sprintf, f9003107) +#define vsprintf _set_ver(vsprintf, e605cb6b) +#define simple_strtoul _set_ver(simple_strtoul, bdb8c1e3) +#define system_utsname _set_ver(system_utsname, 066845bc) +#define sys_call_table _set_ver(sys_call_table, 79fa4011) +#define do_signal _set_ver(do_signal, 86f9bc59) +#define send_sig _set_ver(send_sig, 5cddd8d9) +#define setup_arg_pages _set_ver(setup_arg_pages, fe68d94a) +#define copy_strings _set_ver(copy_strings, 232aee96) +#define create_tables _set_ver(create_tables, ba788fa2) +#define do_execve _set_ver(do_execve, 8c99dc0a) +#define flush_old_exec _set_ver(flush_old_exec, c737e178) +#define open_inode _set_ver(open_inode, 27302cb6) +#define read_exec _set_ver(read_exec, a80a2dd0) +#define si_meminfo _set_ver(si_meminfo, bb05fc9a) +#define sock_register _set_ver(sock_register, d68e1649) +#define sock_unregister _set_ver(sock_unregister, 72c332bd) +#define inet_add_protocol _set_ver(inet_add_protocol, 55292121) +#define inet_del_protocol _set_ver(inet_del_protocol, 73908a1b) +#define slhc_init _set_ver(slhc_init, e490a4b8) +#define slhc_free _set_ver(slhc_free, 39ab902b) +#define slhc_remember _set_ver(slhc_remember, db333be6) +#define slhc_compress _set_ver(slhc_compress, e753e2d2) +#define slhc_uncompress _set_ver(slhc_uncompress, 81cc1144) +#define register_netdevice_notifier _set_ver(register_netdevice_notifier, e7aace7c) +#define unregister_netdevice_notifier _set_ver(unregister_netdevice_notifier, be114416) +#define floppy_track_buffer _set_ver(floppy_track_buffer, c6e3f7c2) +#define register_netdev _set_ver(register_netdev, 0d8d1bb4) +#define unregister_netdev _set_ver(unregister_netdev, 25a99579) +#define ether_setup _set_ver(ether_setup, 4eafef91) +#define alloc_skb _set_ver(alloc_skb, b6b523ba) +#define kfree_skb _set_ver(kfree_skb, 0b938572) +#define dev_kfree_skb _set_ver(dev_kfree_skb, aa1fe7f4) +#define netif_rx _set_ver(netif_rx, d8051cb2) +#define dev_rint _set_ver(dev_rint, 040d3f4b) +#define dev_tint _set_ver(dev_tint, 860b350b) +#define irq2dev_map _set_ver(irq2dev_map, 10bdcd8a) +#define dev_add_pack _set_ver(dev_add_pack, 6d7d9be4) +#define dev_remove_pack _set_ver(dev_remove_pack, 784fa59f) +#define dev_get _set_ver(dev_get, 72ed90fd) +#define dev_ioctl _set_ver(dev_ioctl, 08760203) +#define dev_queue_xmit _set_ver(dev_queue_xmit, 4a478225) +#define dev_base _set_ver(dev_base, 0a8809f0) +#define dev_close _set_ver(dev_close, 9bdad56d) +#define arp_find _set_ver(arp_find, a141bd11) +#define n_tty_ioctl _set_ver(n_tty_ioctl, 538e5fa6) +#define tty_register_ldisc _set_ver(tty_register_ldisc, 8fdde939) +#define kill_fasync _set_ver(kill_fasync, 890501b6) +#define in_scan_scsis _set_ver(in_scan_scsis, 21874a88) +#define scsi_register_module _set_ver(scsi_register_module, 8eff1010) +#define scsi_unregister_module _set_ver(scsi_unregister_module, d913b8f0) +#define scsi_free _set_ver(scsi_free, 475dddfa) +#define scsi_malloc _set_ver(scsi_malloc, 1cce3f92) +#define scsi_register _set_ver(scsi_register, d6e77069) +#define scsi_unregister _set_ver(scsi_unregister, 3b0b616b) +#define scsicam_bios_param _set_ver(scsicam_bios_param, 3d965248) +#define scsi_init_malloc _set_ver(scsi_init_malloc, e5167cbc) +#define scsi_init_free _set_ver(scsi_init_free, 8b2721f8) +#define print_command _set_ver(print_command, 6f14cd75) +#define print_msg _set_ver(print_msg, 0465f877) +#define print_status _set_ver(print_status, 32f84646) +#define set_writetime _set_ver(set_writetime, 52131916) +#define sys_tz _set_ver(sys_tz, aa3c9782) +#define __wait_on_super _set_ver(__wait_on_super, 61a5c00a) +#define file_fsync _set_ver(file_fsync, d30a190f) +#define clear_inode _set_ver(clear_inode, da2b0e9f) +#define refile_buffer _set_ver(refile_buffer, 8c69e123) +#define ___strtok _set_ver(___strtok, 8b55d69c) +#define init_fifo _set_ver(init_fifo, 082629c7) +#define super_blocks _set_ver(super_blocks, e1f1ee99) +#define chrdev_inode_operations _set_ver(chrdev_inode_operations, 6ba1faa3) +#define blkdev_inode_operations _set_ver(blkdev_inode_operations, ed443696) +#define read_ahead _set_ver(read_ahead, bbcd3768) +#define get_hash_table _set_ver(get_hash_table, 3b5f3c55) +#define get_empty_inode _set_ver(get_empty_inode, 554bdc75) +#define insert_inode_hash _set_ver(insert_inode_hash, 59b8c371) +#define event _set_ver(event, a6aac9c1) +#define __down _set_ver(__down, 75aa9e96) +#endif /* _KSYMS_VER_ */ +#endif /* CONFIG_MODVERSIONS !__GENKSYMS__ */ diff --git a/kernel/module.c b/kernel/module.c index eb3ca2417..e29a48ba5 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -23,6 +23,17 @@ * - 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! */ #ifdef DEBUG_MODULE @@ -40,6 +51,7 @@ static struct module *find_module( const char *name); static int get_mod_name( char *user_name, char *buf); static int free_modules( void); +static int module_init_flag = 0; /* Hmm... */ /* * Called at boot time @@ -117,7 +129,7 @@ rename_module_symbol(char *old_name, char *new_name) /* * Allocate space for a module. */ -asmlinkage int +asmlinkage unsigned long sys_create_module(char *module_name, unsigned long size) { struct module *mp; @@ -162,7 +174,7 @@ sys_create_module(char *module_name, unsigned long size) PRINTK(("module `%s' (%lu pages @ 0x%08lx) created\n", mp->name, (unsigned long) mp->size, (unsigned long) mp->addr)); - return (int) addr; + return (unsigned long) addr; } /* @@ -248,7 +260,7 @@ sys_init_module(char *module_name, char *code, unsigned codesize, /* relocate name pointers, index referred from start of table */ for (sym = &(newtab->symbol[0]), i = 0; i < newtab->n_symbols; ++sym, ++i) { - if ((int)sym->name < legal_start || size <= (int)sym->name) { + if ((unsigned long)sym->name < legal_start || size <= (unsigned long)sym->name) { printk("Illegal symbol table! Rejected!\n"); kfree_s(newtab, size); return -EINVAL; @@ -285,8 +297,12 @@ sys_init_module(char *module_name, char *code, unsigned codesize, } } - if ((*rt.init)() != 0) + module_init_flag = 1; /* Hmm... */ + if ((*rt.init)() != 0) { + module_init_flag = 0; /* Hmm... */ return -EBUSY; + } + module_init_flag = 0; /* Hmm... */ mp->state = MOD_RUNNING; return 0; @@ -354,6 +370,9 @@ sys_get_kernel_syms(struct kernel_sym *table) /* 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) { @@ -364,21 +383,22 @@ sys_get_kernel_syms(struct kernel_sym *table) /* copy all module symbols first (always LIFO order) */ for (mp = module_list; mp; mp = mp->next) { - if ((mp->state == MOD_RUNNING) && - (mp->symtab != NULL) && (mp->symtab->n_symbols > 0)) { + 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; - for (i = mp->symtab->n_symbols, - from = mp->symtab->symbol; - i > 0; --i, ++from, ++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); + isym.value = (unsigned long)from->addr; + strncpy(isym.name, from->name, sizeof isym.name); + memcpy_tofs(to, &isym, sizeof isym); + } } } } @@ -582,3 +602,160 @@ int get_ksyms_list(char *buf) return p - buf; } + +/* + * 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 reenterable...) + * + * 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(struct symbol_table *intab) +{ + 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; + } + +#if 1 + if (module_init_flag == 0) { /* Hmm... */ +#else + if (module_list == &kernel_module) { +#endif + /* 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("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... + */ + mp = module_list; /* true when doing init_module! */ + + /* 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 0 + if (oldtab->n_symbols > 0) { + /* Oh dear, I have to drop the old ones... */ + printk("Warning, dropping old symbols\n"); + } +#endif + + 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("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; +} diff --git a/kernel/printk.c b/kernel/printk.c index d92269b30..8b518f6cb 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -19,6 +19,7 @@ #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> +#include <linux/mm.h> #define LOG_BUF_LEN 4096 @@ -129,7 +130,7 @@ asmlinkage int sys_syslog(int type, char * buf, int len) console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; return 0; case 8: - if (len < 0 || len > 8) + if (len < 1 || len > 8) return -EINVAL; console_loglevel = len; return 0; @@ -173,8 +174,10 @@ asmlinkage int printk(const char *fmt, ...) log_buf[(log_start+log_size) & (LOG_BUF_LEN-1)] = *p; if (log_size < LOG_BUF_LEN) log_size++; - else + else { log_start++; + log_start &= LOG_BUF_LEN-1; + } logged_chars++; if (*p == '\n') break; diff --git a/kernel/resource.c b/kernel/resource.c new file mode 100644 index 000000000..5a7999d73 --- /dev/null +++ b/kernel/resource.c @@ -0,0 +1,138 @@ +/* + * linux/kernel/resource.c + * + * Copyright (C) 1995 Linus Torvalds + * David Hinds + * + * Kernel io-region resource management + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/ioport.h> + +#define IOTABLE_SIZE 64 + +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; + } +} + +/* + * This is for compatibility with older drivers. + * It can be removed when all drivers call the new function. + */ +void snarf_region(unsigned int from, unsigned int num) +{ + request_region(from,num,"No name given."); +} + +/* + * 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"); +} diff --git a/kernel/sched.c b/kernel/sched.c new file mode 100644 index 000000000..93003dfc1 --- /dev/null +++ b/kernel/sched.c @@ -0,0 +1,796 @@ +/* + * linux/kernel/sched.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * '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 extracts a field from + * current-task + */ + +#include <linux/config.h> +#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 <asm/system.h> +#include <asm/io.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +#define TIMER_IRQ 0 + +#include <linux/timex.h> + +/* + * kernel variables + */ +long tick = 1000000 / HZ; /* timer interrupt period */ +volatile struct timeval xtime; /* The current time */ +int tickadj = 500/HZ; /* microsecs */ + +DECLARE_TASK_QUEUE(tq_timer); +DECLARE_TASK_QUEUE(tq_immediate); +DECLARE_TASK_QUEUE(tq_scheduler); + +/* + * phase-lock loop variables + */ +int time_status = TIME_BAD; /* clock synchronization status */ +long time_offset = 0; /* time adjustment (us) */ +long time_constant = 0; /* pll time constant */ +long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */ +long time_precision = 1; /* clock precision (us) */ +long time_maxerror = 0x70000000;/* maximum error */ +long time_esterror = 0x70000000;/* estimated error */ +long time_phase = 0; /* phase offset (scaled us) */ +long time_freq = 0; /* 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 long * prof_buffer = NULL; +unsigned long prof_len = 0; + +#define _S(nr) (1<<((nr)-1)) + +extern void mem_use(void); + +extern int timer_interrupt(void); + +unsigned long init_kernel_stack[1024] = { STACK_MAGIC, }; +unsigned long init_user_stack[1024] = { STACK_MAGIC, }; +static struct vm_area_struct init_mmap = INIT_MMAP; +struct task_struct init_task = INIT_TASK; + +unsigned long volatile jiffies=0; + +struct task_struct *current = &init_task; +struct task_struct *last_task_used_math = NULL; + +struct task_struct * task[NR_TASKS] = {&init_task, }; + +struct kernel_stat kstat = { 0 }; + +unsigned long itimer_ticks = 0; +unsigned long itimer_next = ~0; + +/* + * 'schedule()' is the scheduler function. It's a very simple and nice + * scheduler: it's not perfect, but certainly works for most things. + * The one thing you might take a look at is the signal-handler code here. + * + * NOTE!! Task 0 is the 'idle' task, which gets called when no other + * tasks can run. It can not be killed, and it cannot sleep. The 'state' + * information in task[0] is never used. + * + * The "confuse_gcc" goto is used only to get better assembly code.. + * Dijkstra probably hates me. + */ +asmlinkage void schedule(void) +{ + int c; + struct task_struct * p; + struct task_struct * next; + unsigned long ticks; + +/* check alarm, wake up any interruptible tasks that have got a signal */ + + if (intr_count) { + printk("Aiee: scheduling in interrupt\n"); + intr_count = 0; + } + run_task_queue(&tq_scheduler); + cli(); + ticks = itimer_ticks; + itimer_ticks = 0; + itimer_next = ~0; + sti(); + need_resched = 0; + nr_running = 0; + p = &init_task; + for (;;) { + if ((p = p->next_task) == &init_task) + goto confuse_gcc1; + if (ticks && p->it_real_value) { + if (p->it_real_value <= ticks) { + send_sig(SIGALRM, p, 1); + if (!p->it_real_incr) { + p->it_real_value = 0; + goto end_itimer; + } + do { + p->it_real_value += p->it_real_incr; + } while (p->it_real_value <= ticks); + } + p->it_real_value -= ticks; + if (p->it_real_value < itimer_next) + itimer_next = p->it_real_value; + } +end_itimer: + if (p->state != TASK_INTERRUPTIBLE) + continue; + if (p->signal & ~p->blocked) { + p->state = TASK_RUNNING; + continue; + } + if (p->timeout && p->timeout <= jiffies) { + p->timeout = 0; + p->state = TASK_RUNNING; + } + } +confuse_gcc1: + +/* this is the scheduler proper: */ +#if 0 + /* give processes that go to sleep a bit higher priority.. */ + /* This depends on the values for TASK_XXX */ + /* This gives smoother scheduling for some things, but */ + /* can be very unfair under some circumstances, so.. */ + if (TASK_UNINTERRUPTIBLE >= (unsigned) current->state && + current->counter < current->priority*2) { + ++current->counter; + } +#endif + c = -1000; + next = p = &init_task; + for (;;) { + if ((p = p->next_task) == &init_task) + goto confuse_gcc2; + if (p->state == TASK_RUNNING) { + nr_running++; + if (p->counter > c) + c = p->counter, next = p; + } + } +confuse_gcc2: + if (!c) { + for_each_task(p) + p->counter = (p->counter >> 1) + p->priority; + } + if (current == next) + return; + kstat.context_swtch++; + + switch_to(next); +} + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +/* + * wake_up doesn't wake up stopped processes - they have to be awakened + * with signals or similar. + * + * Note that this doesn't need cli-sti pairs: interrupts may not change + * the wait-queue structures directly, but only call wake_up() to wake + * a process. The process itself must remove the queue once it has woken. + */ +void wake_up(struct wait_queue **q) +{ + struct wait_queue *tmp; + struct task_struct * p; + + if (!q || !(tmp = *q)) + return; + do { + if ((p = tmp->task) != NULL) { + if ((p->state == TASK_UNINTERRUPTIBLE) || + (p->state == TASK_INTERRUPTIBLE)) { + p->state = TASK_RUNNING; + if (p->counter > current->counter + 3) + need_resched = 1; + } + } + if (!tmp->next) { + printk("wait_queue is bad (eip = %p)\n", + __builtin_return_address(0)); + printk(" q = %p\n",q); + printk(" *q = %p\n",*q); + printk(" tmp = %p\n",tmp); + break; + } + tmp = tmp->next; + } while (tmp != *q); +} + +void wake_up_interruptible(struct wait_queue **q) +{ + struct wait_queue *tmp; + struct task_struct * p; + + if (!q || !(tmp = *q)) + return; + do { + if ((p = tmp->task) != NULL) { + if (p->state == TASK_INTERRUPTIBLE) { + p->state = TASK_RUNNING; + if (p->counter > current->counter + 3) + need_resched = 1; + } + } + if (!tmp->next) { + printk("wait_queue is bad (eip = %p)\n", + __builtin_return_address(0)); + printk(" q = %p\n",q); + printk(" *q = %p\n",*q); + printk(" tmp = %p\n",tmp); + break; + } + tmp = tmp->next; + } while (tmp != *q); +} + +void __down(struct semaphore * sem) +{ + struct wait_queue wait = { current, NULL }; + add_wait_queue(&sem->wait, &wait); + current->state = TASK_UNINTERRUPTIBLE; + while (sem->count <= 0) { + schedule(); + current->state = TASK_UNINTERRUPTIBLE; + } + current->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); +} + +static inline void __sleep_on(struct wait_queue **p, int state) +{ + unsigned long flags; + struct wait_queue wait = { current, NULL }; + + if (!p) + return; + if (current == task[0]) + panic("task[0] trying to sleep"); + current->state = state; + add_wait_queue(p, &wait); + save_flags(flags); + sti(); + schedule(); + remove_wait_queue(p, &wait); + restore_flags(flags); +} + +void interruptible_sleep_on(struct wait_queue **p) +{ + __sleep_on(p,TASK_INTERRUPTIBLE); +} + +void sleep_on(struct wait_queue **p) +{ + __sleep_on(p,TASK_UNINTERRUPTIBLE); +} + +/* + * The head for the timer-list has a "expires" field of MAX_UINT, + * and the sorting routine counts on this.. + */ +static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL }; +#define SLOW_BUT_DEBUGGING_TIMERS 1 + +void add_timer(struct timer_list * timer) +{ + unsigned long flags; + struct timer_list *p; + +#if SLOW_BUT_DEBUGGING_TIMERS + if (timer->next || timer->prev) { + printk("add_timer() called with non-zero list from %p\n", + __builtin_return_address(0)); + return; + } +#endif + p = &timer_head; + timer->expires += jiffies; + save_flags(flags); + cli(); + do { + p = p->next; + } while (timer->expires > p->expires); + timer->next = p; + timer->prev = p->prev; + p->prev = timer; + timer->prev->next = timer; + restore_flags(flags); +} + +int del_timer(struct timer_list * timer) +{ + unsigned long flags; +#if SLOW_BUT_DEBUGGING_TIMERS + struct timer_list * p; + + p = &timer_head; + save_flags(flags); + cli(); + while ((p = p->next) != &timer_head) { + if (p == timer) { + timer->next->prev = timer->prev; + timer->prev->next = timer->next; + timer->next = timer->prev = NULL; + restore_flags(flags); + timer->expires -= jiffies; + return 1; + } + } + if (timer->next || timer->prev) + printk("del_timer() called from %p with timer not initialized\n", + __builtin_return_address(0)); + restore_flags(flags); + return 0; +#else + save_flags(flags); + cli(); + if (timer->next) { + timer->next->prev = timer->prev; + timer->prev->next = timer->next; + timer->next = timer->prev = NULL; + restore_flags(flags); + timer->expires -= jiffies; + return 1; + } + restore_flags(flags); + return 0; +#endif +} + +unsigned long timer_active = 0; +struct timer_struct timer_table[32]; + +/* + * Hmm.. Changed this, as the GNU make sources (load.c) seems to + * imply that avenrun[] is the standard name for this kind of thing. + * Nothing else seems to be standardized: the fractional size etc + * all seem to differ on different machines. + */ +unsigned long avenrun[3] = { 0,0,0 }; + +/* + * Nr of active tasks - counted in fixed-point numbers + */ +static unsigned long count_active_tasks(void) +{ + struct task_struct **p; + unsigned long nr = 0; + + for(p = &LAST_TASK; p > &FIRST_TASK; --p) + if (*p && ((*p)->state == TASK_RUNNING || + (*p)->state == TASK_UNINTERRUPTIBLE || + (*p)->state == TASK_SWAPPING)) + nr += FIXED_1; + return nr; +} + +static inline void calc_load(void) +{ + unsigned long active_tasks; /* fixed-point */ + static int count = LOAD_FREQ; + + if (count-- > 0) + return; + count = LOAD_FREQ; + active_tasks = count_active_tasks(); + CALC_LOAD(avenrun[0], EXP_1, active_tasks); + CALC_LOAD(avenrun[1], EXP_5, active_tasks); + CALC_LOAD(avenrun[2], EXP_15, active_tasks); +} + +/* + * this routine handles the overflow of the microsecond field + * + * The tricky bits of code to handle the accurate clock support + * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame. + * They were originally developed for SUN and DEC kernels. + * All the kudos should go to Dave for this stuff. + * + * These were ported to Linux by Philip Gladstone. + */ +static void second_overflow(void) +{ + long ltemp; + + /* Bump the maxerror field */ + time_maxerror = (0x70000000-time_maxerror < time_tolerance) ? + 0x70000000 : (time_maxerror + time_tolerance); + + /* Run the PLL */ + if (time_offset < 0) { + ltemp = (-(time_offset+1) >> (SHIFT_KG + time_constant)) + 1; + time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); + time_offset += (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE); + time_adj = - time_adj; + } else if (time_offset > 0) { + ltemp = ((time_offset-1) >> (SHIFT_KG + time_constant)) + 1; + time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); + time_offset -= (time_adj * HZ) >> (SHIFT_SCALE - SHIFT_UPDATE); + } else { + time_adj = 0; + } + + time_adj += (time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE)) + + FINETUNE; + + /* Handle the leap second stuff */ + switch (time_status) { + case TIME_INS: + /* ugly divide should be replaced */ + if (xtime.tv_sec % 86400 == 0) { + xtime.tv_sec--; /* !! */ + time_status = TIME_OOP; + printk("Clock: inserting leap second 23:59:60 UTC\n"); + } + break; + + case TIME_DEL: + /* ugly divide should be replaced */ + if (xtime.tv_sec % 86400 == 86399) { + xtime.tv_sec++; + time_status = TIME_OK; + printk("Clock: deleting leap second 23:59:59 UTC\n"); + } + break; + + case TIME_OOP: + time_status = TIME_OK; + break; + } +} + +/* + * disregard lost ticks for now.. We don't care enough. + */ +static void timer_bh(void * unused) +{ + unsigned long mask; + struct timer_struct *tp; + struct timer_list * timer; + + cli(); + while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) { + void (*fn)(unsigned long) = timer->function; + unsigned long data = timer->data; + timer->next->prev = timer->prev; + timer->prev->next = timer->next; + timer->next = timer->prev = NULL; + sti(); + fn(data); + cli(); + } + sti(); + + for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { + if (mask > timer_active) + break; + if (!(mask & timer_active)) + continue; + if (tp->expires > jiffies) + continue; + timer_active &= ~mask; + tp->fn(); + sti(); + } +} + +void tqueue_bh(void * unused) +{ + run_task_queue(&tq_timer); +} + +void immediate_bh(void * unused) +{ + run_task_queue(&tq_immediate); +} + +/* + * The int argument is really a (struct pt_regs *), in case the + * interrupt wants to know from where it was called. The timer + * irq uses this to decide if it should update the user or system + * times. + */ +static void do_timer(int irq, struct pt_regs * regs) +{ + unsigned long mask; + struct timer_struct *tp; + /* last time the cmos clock got updated */ + static long last_rtc_update=0; + extern int set_rtc_mmss(unsigned long); + + long ltemp, psecs; + + /* Advance the phase, once it gets to one microsecond, then + * advance the tick more. + */ + time_phase += time_adj; + if (time_phase < -FINEUSEC) { + ltemp = -time_phase >> SHIFT_SCALE; + time_phase += ltemp << SHIFT_SCALE; + xtime.tv_usec += tick + time_adjust_step - ltemp; + } + else if (time_phase > FINEUSEC) { + ltemp = time_phase >> SHIFT_SCALE; + time_phase -= ltemp << SHIFT_SCALE; + xtime.tv_usec += tick + time_adjust_step + ltemp; + } else + xtime.tv_usec += tick + time_adjust_step; + + if (time_adjust) + { + /* We are doing an adjtime thing. + * + * Modify the value of the tick for next time. + * Note that a positive delta means we want the clock + * to run fast. This means that the tick should be bigger + * + * Limit the amount of the step for *next* tick to be + * in the range -tickadj .. +tickadj + */ + if (time_adjust > tickadj) + time_adjust_step = tickadj; + else if (time_adjust < -tickadj) + time_adjust_step = -tickadj; + else + time_adjust_step = time_adjust; + + /* Reduce by this step the amount of time left */ + time_adjust -= time_adjust_step; + } + else + time_adjust_step = 0; + + if (xtime.tv_usec >= 1000000) { + xtime.tv_usec -= 1000000; + xtime.tv_sec++; + second_overflow(); + } + + /* If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if (time_status != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1)) + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + + jiffies++; + calc_load(); + if (user_mode(regs)) { + current->utime++; + if (current != task[0]) { + if (current->priority < 15) + kstat.cpu_nice++; + else + kstat.cpu_user++; + } + /* Update ITIMER_VIRT for current task if not in a system call */ + if (current->it_virt_value && !(--current->it_virt_value)) { + current->it_virt_value = current->it_virt_incr; + send_sig(SIGVTALRM,current,1); + } + } else { + current->stime++; + if(current != task[0]) + kstat.cpu_system++; +#ifdef CONFIG_PROFILE + if (prof_buffer && current != task[0]) { + extern int _stext; + unsigned long eip = regs->eip - (unsigned long) &_stext; + eip >>= CONFIG_PROFILE_SHIFT; + if (eip < prof_len) + prof_buffer[eip]++; + } +#endif + } + /* + * check the cpu time limit on the process. + */ + if ((current->rlim[RLIMIT_CPU].rlim_max != RLIM_INFINITY) && + (((current->stime + current->utime) / HZ) >= current->rlim[RLIMIT_CPU].rlim_max)) + send_sig(SIGKILL, current, 1); + if ((current->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) && + (((current->stime + current->utime) % HZ) == 0)) { + psecs = (current->stime + current->utime) / HZ; + /* send when equal */ + if (psecs == current->rlim[RLIMIT_CPU].rlim_cur) + send_sig(SIGXCPU, current, 1); + /* and every five seconds thereafter. */ + else if ((psecs > current->rlim[RLIMIT_CPU].rlim_cur) && + ((psecs - current->rlim[RLIMIT_CPU].rlim_cur) % 5) == 0) + send_sig(SIGXCPU, current, 1); + } + + if (current != task[0] && 0 > --current->counter) { + current->counter = 0; + need_resched = 1; + } + /* Update ITIMER_PROF for the current task */ + if (current->it_prof_value && !(--current->it_prof_value)) { + current->it_prof_value = current->it_prof_incr; + send_sig(SIGPROF,current,1); + } + for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) { + if (mask > timer_active) + break; + if (!(mask & timer_active)) + continue; + if (tp->expires > jiffies) + continue; + mark_bh(TIMER_BH); + } + cli(); + itimer_ticks++; + if (itimer_ticks > itimer_next) + need_resched = 1; + if (timer_head.next->expires < jiffies) + mark_bh(TIMER_BH); + if (tq_timer != &tq_last) + mark_bh(TQUEUE_BH); + sti(); +} + +asmlinkage int sys_alarm(long seconds) +{ + struct itimerval it_new, it_old; + + it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; + it_new.it_value.tv_sec = seconds; + it_new.it_value.tv_usec = 0; + _setitimer(ITIMER_REAL, &it_new, &it_old); + return(it_old.it_value.tv_sec + (it_old.it_value.tv_usec / 1000000)); +} + +asmlinkage int sys_getpid(void) +{ + return current->pid; +} + +asmlinkage int sys_getppid(void) +{ + return current->p_opptr->pid; +} + +asmlinkage int sys_getuid(void) +{ + return current->uid; +} + +asmlinkage int sys_geteuid(void) +{ + return current->euid; +} + +asmlinkage int sys_getgid(void) +{ + return current->gid; +} + +asmlinkage int sys_getegid(void) +{ + return current->egid; +} + +asmlinkage int sys_nice(long increment) +{ + int newprio; + + if (increment < 0 && !suser()) + return -EPERM; + newprio = current->priority - increment; + if (newprio < 1) + newprio = 1; + if (newprio > 35) + newprio = 35; + current->priority = newprio; + return 0; +} + +static void show_task(int nr,struct task_struct * p) +{ + unsigned long free; + static char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" }; + + printk("%-8s %3d ", p->comm, (p == current) ? -nr : nr); + if (((unsigned) p->state) < sizeof(stat_nam)/sizeof(char *)) + printk(stat_nam[p->state]); + else + printk(" "); +#ifdef __i386__ + if (p == current) + printk(" current "); + else + printk(" %08lX ", ((unsigned long *)p->tss.esp)[3]); +#elif defined (__mips__) + if (p == current) + printk(" current "); + else + printk(" "); +#endif + for (free = 1; free < 1024 ; free++) { + if (((unsigned long *)p->kernel_stack_page)[free]) + break; + } + printk("%5lu %5d %6d ", free << 2, p->pid, p->p_pptr->pid); + if (p->p_cptr) + printk("%5d ", p->p_cptr->pid); + else + printk(" "); + if (p->p_ysptr) + printk("%7d", p->p_ysptr->pid); + else + printk(" "); + if (p->p_osptr) + printk(" %5d\n", p->p_osptr->pid); + else + printk("\n"); +} + +void show_state(void) +{ + int i; + + printk(" free sibling\n"); + printk(" task PC stack pid father child younger older\n"); + for (i=0 ; i<NR_TASKS ; i++) + if (task[i]) + show_task(i,task[i]); +} + +void sched_init(void) +{ + bh_base[TIMER_BH].routine = timer_bh; + bh_base[TQUEUE_BH].routine = tqueue_bh; + bh_base[IMMEDIATE_BH].routine = immediate_bh; + if (request_irq(TIMER_IRQ, do_timer, 0, "timer") != 0) + panic("Could not allocate timer IRQ!"); + enable_bh(TIMER_BH); + enable_bh(TQUEUE_BH); + enable_bh(IMMEDIATE_BH); +} diff --git a/kernel/signal.c b/kernel/signal.c new file mode 100644 index 000000000..f21d7a2c9 --- /dev/null +++ b/kernel/signal.c @@ -0,0 +1,174 @@ +/* + * linux/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/mm.h> + +#include <asm/segment.h> + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset) +{ + sigset_t new_set, old_set = current->blocked; + int error; + + if (set) { + error = verify_area(VERIFY_READ, set, sizeof(sigset_t)); + if (error) + return error; + new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE; + switch (how) { + case SIG_BLOCK: + current->blocked |= new_set; + break; + case SIG_UNBLOCK: + current->blocked &= ~new_set; + break; + case SIG_SETMASK: + current->blocked = new_set; + break; + default: + return -EINVAL; + } + } + if (oset) { + error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t)); + if (error) + return error; + put_fs_long(old_set, (unsigned long *) oset); + } + return 0; +} + +asmlinkage int sys_sgetmask(void) +{ + return current->blocked; +} + +asmlinkage int sys_ssetmask(int newmask) +{ + int old=current->blocked; + + current->blocked = newmask & _BLOCKABLE; + return old; +} + +asmlinkage int sys_sigpending(sigset_t *set) +{ + int error; + /* fill in "set" with signals pending but blocked. */ + error = verify_area(VERIFY_WRITE, set, 4); + if (!error) + put_fs_long(current->blocked & current->signal, (unsigned long *)set); + return error; +} + +/* + * POSIX 3.3.1.3: + * "Setting a signal action to SIG_IGN for a signal that is pending + * shall cause the pending signal to be discarded, whether or not + * it is blocked" (but SIGCHLD is unspecified: linux leaves it alone). + * + * "Setting a signal action to SIG_DFL for a signal that is pending + * and whose default action is to ignore the signal (for example, + * SIGCHLD), shall cause the pending signal to be discarded, whether + * or not it is blocked" + * + * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal + * isn't actually ignored, but does automatic child reaping, while + * SIG_DFL is explicitly said by POSIX to force the signal to be ignored.. + */ +static void check_pending(int signum) +{ + struct sigaction *p; + + p = signum - 1 + current->sigaction; + if (p->sa_handler == SIG_IGN) { + if (signum == SIGCHLD) + return; + current->signal &= ~_S(signum); + return; + } + if (p->sa_handler == SIG_DFL) { + if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH) + return; + current->signal &= ~_S(signum); + return; + } +} + +asmlinkage unsigned long sys_signal(int signum, void (*handler)(int)) +{ + int err; + struct sigaction tmp; + + if (signum<1 || signum>32) + return -EINVAL; + if (signum==SIGKILL || signum==SIGSTOP) + return -EINVAL; + if (handler != SIG_DFL && handler != SIG_IGN) { + err = verify_area(VERIFY_READ, handler, 1); + if (err) + return err; + } + tmp.sa_handler = handler; + tmp.sa_mask = 0; + tmp.sa_flags = SA_ONESHOT | SA_NOMASK; + tmp.sa_restorer = NULL; + handler = current->sigaction[signum-1].sa_handler; + current->sigaction[signum-1] = tmp; + check_pending(signum); + return (unsigned long) handler; +} + +asmlinkage int sys_sigaction(int signum, const struct sigaction * action, + struct sigaction * oldaction) +{ + struct sigaction new_sa, *p; + + if (signum<1 || signum>32) + return -EINVAL; + if (signum==SIGKILL || signum==SIGSTOP) + return -EINVAL; + p = signum - 1 + current->sigaction; + if (action) { + int err = verify_area(VERIFY_READ, action, sizeof(*action)); + if (err) + return err; + memcpy_fromfs(&new_sa, action, sizeof(struct sigaction)); + if (new_sa.sa_flags & SA_NOMASK) + new_sa.sa_mask = 0; + else { + new_sa.sa_mask |= _S(signum); + new_sa.sa_mask &= _BLOCKABLE; + } + if (new_sa.sa_handler != SIG_DFL && new_sa.sa_handler != SIG_IGN) { + err = verify_area(VERIFY_READ, new_sa.sa_handler, 1); + if (err) + return err; + } + } + if (oldaction) { + int err = verify_area(VERIFY_WRITE, oldaction, sizeof(*oldaction)); + if (err) + return err; + memcpy_tofs(oldaction, p, sizeof(struct sigaction)); + } + if (action) { + *p = new_sa; + check_pending(signum); + } + return 0; +} diff --git a/kernel/softirq.c b/kernel/softirq.c new file mode 100644 index 000000000..7d919272b --- /dev/null +++ b/kernel/softirq.c @@ -0,0 +1,55 @@ +/* + * linux/kernel/softirq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * do_bottom_half() runs at normal kernel priority: all interrupts + * enabled. do_bottom_half() is atomic with respect to itself: a + * bottom_half handler need not be re-entrant. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> + +#define INCLUDE_INLINE_FUNCS +#include <linux/tqueue.h> + +unsigned long intr_count = 0; + +unsigned long bh_active = 0; +unsigned long bh_mask = 0; +struct bh_struct bh_base[32]; + + +asmlinkage void do_bottom_half(void) +{ + unsigned long active; + unsigned long mask, left; + struct bh_struct *bh; + + bh = bh_base; + active = bh_active & bh_mask; + for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) { + if (mask & active) { + void (*fn)(void *); + bh_active &= ~mask; + fn = bh->routine; + if (!fn) + goto bad_bh; + fn(bh->data); + } + } + return; +bad_bh: + printk ("irq.c:bad bottom half entry %08lx\n", mask); +} diff --git a/kernel/sys.c b/kernel/sys.c index 706e3d66e..171d2411c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -17,6 +16,7 @@ #include <linux/ptrace.h> #include <linux/stat.h> #include <linux/mman.h> +#include <linux/mm.h> #include <asm/segment.h> #include <asm/io.h> @@ -32,38 +32,7 @@ extern void adjust_clock(void); asmlinkage int sys_ni_syscall(void) { - return -EINVAL; -} - -asmlinkage int sys_idle(void) -{ - int i; - - if (current->pid != 0) - return -EPERM; - -#ifdef __i386__ - /* Map out the low memory: it's no longer needed */ - for (i = 0 ; i < 768 ; i++) - swapper_pg_dir[i] = 0; -#endif - - /* endless idle loop with no priority at all */ - current->counter = -100; - for (;;) { -#if defined (__i386__) - if (hlt_works_ok && !need_resched) - __asm__("hlt"); -#elif defined (__mips__) - /* - * R4[26]00 have wait, the R4000 doesn't. - * Dunno about the R4400... - */ - if (!need_resched) - __asm__("wait"); -#endif - schedule(); - } + return -ENOSYS; } static int proc_sel(struct task_struct *p, int which, int who) @@ -163,6 +132,7 @@ asmlinkage int sys_prof(void) } extern void hard_reset_now(void); +extern asmlinkage sys_kill(int, int); /* * Reboot system call: for obvious reasons only root may call it, @@ -184,7 +154,11 @@ asmlinkage int sys_reboot(int magic, int magic_too, int flag) C_A_D = 1; else if (!flag) C_A_D = 0; - else + else if (flag == 0xCDEF0123) { + printk(KERN_EMERG "System halted\n"); + sys_kill(-1, SIGKILL); + do_exit(0); + } else return -EINVAL; return (0); } @@ -402,12 +376,11 @@ asmlinkage int sys_times(struct tms * tbuf) return jiffies; } -asmlinkage int sys_brk(unsigned long brk) +asmlinkage unsigned long sys_brk(unsigned long brk) { int freepages; unsigned long rlim; unsigned long newbrk, oldbrk; - struct vm_area_struct * vma; if (brk < current->mm->end_code) return current->mm->brk; @@ -430,18 +403,13 @@ asmlinkage int sys_brk(unsigned long brk) rlim = current->rlim[RLIMIT_DATA].rlim_cur; if (rlim >= RLIM_INFINITY) rlim = ~0; - if (brk - current->mm->end_code > rlim || - brk >= current->mm->start_stack - 16384) + if (brk - current->mm->end_code > rlim) return current->mm->brk; /* * Check against existing mmap mappings. */ - for (vma = current->mm->mmap; vma; vma = vma->vm_next) { - if (newbrk <= vma->vm_start) - break; - if (oldbrk < vma->vm_end) - return current->mm->brk; - } + if (find_vma_intersection(current, oldbrk, newbrk+PAGE_SIZE)) + return current->mm->brk; /* * stupid algorithm to decide if we have enough memory: while * simple, it hopefully works in most obvious cases.. Easy to @@ -450,7 +418,14 @@ asmlinkage int sys_brk(unsigned long brk) freepages = buffermem >> 12; freepages += nr_free_pages; freepages += nr_swap_pages; +#if 0 + /* + * This assumes a PCish memory architecture... + */ freepages -= (high_memory - 0x100000) >> 16; +#else + freepages -= (high_memory - KSEG0) >> 16; +#endif freepages -= (newbrk-oldbrk) >> 12; if (freepages < 0) return current->mm->brk; @@ -548,6 +523,7 @@ asmlinkage int sys_setsid(void) current->leader = 1; current->session = current->pgrp = current->pid; current->tty = NULL; + current->tty_old_pgrp = 0; return current->pgrp; } @@ -557,18 +533,20 @@ asmlinkage int sys_setsid(void) asmlinkage int sys_getgroups(int gidsetsize, gid_t *grouplist) { int i; + int * groups; if (gidsetsize) { i = verify_area(VERIFY_WRITE, grouplist, sizeof(gid_t) * gidsetsize); if (i) return i; } - for (i = 0 ; (i < NGROUPS) && (current->groups[i] != NOGROUP) ; i++) { + groups = current->groups; + for (i = 0 ; (i < NGROUPS) && (*groups != NOGROUP) ; i++, groups++) { if (!gidsetsize) continue; if (i >= gidsetsize) break; - put_fs_word(current->groups[i], (short *) grouplist); + put_user(*groups, grouplist); grouplist++; } return(i); @@ -660,22 +638,35 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) return 0; } -/* - * Only sethostname; gethostname can be implemented by calling uname() - */ asmlinkage int sys_sethostname(char *name, int len) { - int i; - + int error; + if (!suser()) return -EPERM; - if (len > __NEW_UTS_LEN) + if (len < 0 || len > __NEW_UTS_LEN) return -EINVAL; - for (i=0; i < len; i++) { - if ((system_utsname.nodename[i] = get_fs_byte(name+i)) == 0) - return 0; - } - system_utsname.nodename[i] = 0; + error = verify_area(VERIFY_READ, name, len); + if (error) + return error; + memcpy_fromfs(system_utsname.nodename, name, len); + system_utsname.nodename[len] = 0; + return 0; +} + +asmlinkage int sys_gethostname(char *name, int len) +{ + int i; + + if (len < 0) + return -EINVAL; + i = verify_area(VERIFY_WRITE, name, len); + if (i) + return i; + i = 1+strlen(system_utsname.nodename); + if (i > len) + i = len; + memcpy_tofs(name, system_utsname.nodename, i); return 0; } @@ -708,10 +699,7 @@ asmlinkage int sys_getrlimit(unsigned int resource, struct rlimit *rlim) error = verify_area(VERIFY_WRITE,rlim,sizeof *rlim); if (error) return error; - put_fs_long(current->rlim[resource].rlim_cur, - (unsigned long *) rlim); - put_fs_long(current->rlim[resource].rlim_max, - ((unsigned long *) rlim)+1); + memcpy_tofs(rlim, current->rlim + resource, sizeof(*rlim)); return 0; } @@ -731,6 +719,10 @@ asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim) (new_rlim.rlim_max > old_rlim->rlim_max)) && !suser()) return -EPERM; + if (resource == RLIMIT_NOFILE) { + if (new_rlim.rlim_cur > NR_OPEN || new_rlim.rlim_max > NR_OPEN) + return -EPERM; + } *old_rlim = new_rlim; return 0; } diff --git a/kernel/time.c b/kernel/time.c index 1a25d43ef..0424b2eaa 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -10,39 +10,48 @@ /* * Modification history kernel/time.c * - * 02 Sep 93 Philip Gladstone + * 1993-09-02 Philip Gladstone * Created file with time related functions from sched.c and adjtimex() - * 08 Oct 93 Torsten Duwe + * 1993-10-08 Torsten Duwe * adjtime interface update and CMOS clock write code - * 02 Jul 94 Alan Modra + * 1994-07-02 Alan Modra * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1995-03-26 Markus Kuhn + * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 + * precision CMOS clock update + * + * to do: adjtimex() has to be updated to recent (1994-12-13) revision + * of David Mill's kernel clock model. For more information, check + * <ftp://louie.udel.edu/pub/ntp/kernel.tar.Z>. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/param.h> #include <linux/string.h> +#include <linux/mm.h> #include <asm/segment.h> #include <asm/io.h> #include <linux/mc146818rtc.h> -#define RTC_ALWAYS_BCD 1 - #include <linux/timex.h> -/* converts date to days since 1/1/1970 - * assumes year,mon,day in normal date format - * ie. 1/1/1970 => year=1970, mon=1, day=1 +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. * - * For the Julian calendar (which was used in Russia before 1917, + * [For the Julian calendar (which was used in Russia before 1917, * Britain & colonies before 1752, anywhere else before 1582, * and is still in use by some communities) leave out the - * -year/100+year/400 terms, and add 10. + * -year/100+year/400 terms, and add 10.] * * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) */ static inline unsigned long mktime(unsigned int year, unsigned int mon, unsigned int day, unsigned int hour, @@ -65,16 +74,16 @@ void time_init(void) unsigned int year, mon, day, hour, min, sec; int i; - /* checking for Update-In-Progress could be done more elegantly - * (using the "update finished"-interrupt for example), but that - * would require excessive testing. promise I'll do that when I find - * the time. - Torsten + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. */ /* read RTC exactly on falling edge of update flag */ for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) break; - for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms*/ + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) break; do { /* Isn't this overkill ? UIP above should guarantee consistency */ @@ -98,7 +107,14 @@ void time_init(void) year += 100; xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_usec = 0; +printk("Year : %d\n", year); +printk("Mon : %d\n", mon); +printk("Day : %d\n", day); +printk("Hour : %d\n", hour); +printk("Min : %d\n", min); +printk("Sec : %d\n", sec); } + /* * The timezone where the local system is located. Used as a default by some * programs who obtain this value by using gettimeofday. @@ -148,25 +164,25 @@ asmlinkage int sys_stime(unsigned long * tptr) * counter rather than 11932! This has an adverse impact on * do_gettimeoffset() -- it stops working! What is also not * good is that the interval that our timer function gets called - * is no longer 10.0002 msecs, but 9.9767 msec. To get around this + * is no longer 10.0002 ms, but 9.9767 ms. To get around this * would require using a different timing source. Maybe someone * could use the RTC - I know that this can interrupt at frequencies * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix * it so that at startup, the timer code in sched.c would select * using either the RTC or the 8253 timer. The decision would be * based on whether there was any other device around that needed - * to trample on the 8253. I'd set up the RTC to interrupt at 1024Hz, + * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz, * and then do some jiggery to have a version of do_timer that - * advanced the clock by 1/1024 sec. Every time that reached over 1/100 + * advanced the clock by 1/1024 s. Every time that reached over 1/100 * of a second, then do all the old code. If the time was kept correct * then do_gettimeoffset could just return 0 - there is no low order * divider that can be accessed. * * Ideally, you would be able to use the RTC for the speaker driver, * but it appears that the speaker driver really needs interrupt more - * often than every 120us or so. + * often than every 120 us or so. * - * Anyway, this needs more thought.... pjsg (28 Aug 93) + * Anyway, this needs more thought.... pjsg (1993-08-28) * * If you are really that interested, you should be reading * comp.protocols.time.ntp! @@ -198,22 +214,21 @@ static inline unsigned long do_gettimeoffset(void) /* * This version of gettimeofday has near microsecond resolution. */ -static inline void do_gettimeofday(struct timeval *tv) +void do_gettimeofday(struct timeval *tv) { -#if defined (__i386__) || defined (__mips__) + unsigned long flags; + + save_flags(flags); cli(); *tv = xtime; +#if defined (__i386__) || defined (__mips__) tv->tv_usec += do_gettimeoffset(); if (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } - sti(); -#else /* not __i386__ */ - cli(); - *tv = xtime; - sti(); -#endif /* not __i386__ */ +#endif /* !defined (__i386__) && !defined (__mips__) */ + restore_flags(flags); } asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz) @@ -226,21 +241,19 @@ asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz) if (error) return error; do_gettimeofday(&ktv); - put_fs_long(ktv.tv_sec, (unsigned long *) &tv->tv_sec); - put_fs_long(ktv.tv_usec, (unsigned long *) &tv->tv_usec); + memcpy_tofs(tv, &ktv, sizeof(ktv)); } if (tz) { error = verify_area(VERIFY_WRITE, tz, sizeof *tz); if (error) return error; - put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz); - put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1); + memcpy_tofs(tz, &sys_tz, sizeof(sys_tz)); } return 0; } /* - * Adjust the time obtained from the CMOS to be GMT time instead of + * Adjust the time obtained from the CMOS to be UTC time instead of * local time. * * This is ugly, but preferable to the alternatives. Otherwise we @@ -249,11 +262,11 @@ asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz) * hard to make the program warp the clock precisely n hours) or * compile in the timezone information into the kernel. Bad, bad.... * - * XXX Currently does not adjust for daylight savings time. May not - * need to do anything, depending on how smart (dumb?) the BIOS - * is. Blast it all.... the best thing to do not depend on the CMOS - * clock at all, but get the time via NTP or timed if you're on a - * network.... - TYT, 1/1/92 + * - TYT, 1992-01-01 + * + * The best thing to do is to keep the CMOS clock in universal time (UTC) + * as real UNIX machines always do it. This avoids all headaches about + * daylight saving times and warping kernel clocks. */ inline static void warp_clock(void) { @@ -263,12 +276,13 @@ inline static void warp_clock(void) } /* - * The first time we set the timezone, we will warp the clock so that - * it is ticking GMT time instead of local time. Presumably, - * if someone is setting the timezone then we are running in an - * environment where the programs understand about timezones. - * This should be done at boot time in the /etc/rc script, as - * soon as possible, so that the clock can be set right. Otherwise, + * In case for some reason the CMOS clock has not already been running + * in UTC, but in some local time: The first time we set the timezone, + * we will warp the clock so that it is ticking UTC time instead of + * local time. Presumably, if someone is setting the timezone then we + * are running in an environment where the programs understand about + * timezones. This should be done at boot time in the /etc/rc script, + * as soon as possible, so that the clock can be set right. Otherwise, * various programs will get confused when the clock gets warped. */ asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz) @@ -441,6 +455,13 @@ asmlinkage int sys_adjtimex(struct timex *txc_p) return time_status; } +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be + * called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. + */ int set_rtc_mmss(unsigned long nowtime) { int retval = 0; @@ -481,7 +502,15 @@ int set_rtc_mmss(unsigned long nowtime) else retval = -1; - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + return retval; } diff --git a/kernel/tqueue.c b/kernel/tqueue.c deleted file mode 100644 index 440709611..000000000 --- a/kernel/tqueue.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * tqueue.c --- task queue handling for Linux. - * - * This routine merely draws in the static portion of the task queue - * inline functions. Look in tqueue.h for the relevant functions. - */ - -#define INCLUDE_INLINE_FUNCS - -#include <linux/tqueue.h> diff --git a/kernel/vsprintf.c b/kernel/vsprintf.c deleted file mode 100644 index b85f78420..000000000 --- a/kernel/vsprintf.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * linux/kernel/vsprintf.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ -/* - * Wirzenius wrote this portably, Torvalds fucked it up :-) - */ - -#include <stdarg.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/ctype.h> - -unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) -{ - unsigned long result = 0,value; - - if (!base) { - base = 10; - if (*cp == '0') { - base = 8; - cp++; - if ((*cp == 'x') && isxdigit(cp[1])) { - cp++; - base = 16; - } - } - } - while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) - ? toupper(*cp) : *cp)-'A'+10) < base) { - result = result*base + value; - cp++; - } - if (endp) - *endp = (char *)cp; - return result; -} - -/* we use this so that we can do without the ctype library */ -#define is_digit(c) ((c) >= '0' && (c) <= '9') - -static int skip_atoi(const char **s) -{ - int i=0; - - while (is_digit(**s)) - i = i*10 + *((*s)++) - '0'; - return i; -} - -#define ZEROPAD 1 /* pad with zero */ -#define SIGN 2 /* unsigned/signed long */ -#define PLUS 4 /* show plus */ -#define SPACE 8 /* space if plus */ -#define LEFT 16 /* left justified */ -#define SPECIAL 32 /* 0x */ -#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ - -#define do_div(n,base) ({ \ -int __res; \ -__res = ((unsigned long) n) % (unsigned) base; \ -n = ((unsigned long) n) / (unsigned) base; \ -__res; }) - -static char * number(char * str, long num, int base, int size, int precision - ,int type) -{ - char c,sign,tmp[36]; - const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; - int i; - - if (type & LARGE) - digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - if (type & LEFT) - type &= ~ZEROPAD; - if (base < 2 || base > 36) - return 0; - c = (type & ZEROPAD) ? '0' : ' '; - sign = 0; - if (type & SIGN) { - if (num < 0) { - sign = '-'; - num = -num; - size--; - } else if (type & PLUS) { - sign = '+'; - size--; - } else if (type & SPACE) { - sign = ' '; - size--; - } - } - if (type & SPECIAL) { - if (base == 16) - size -= 2; - else if (base == 8) - size--; - } - i = 0; - if (num == 0) - tmp[i++]='0'; - else while (num != 0) - tmp[i++] = digits[do_div(num,base)]; - if (i > precision) - precision = i; - size -= precision; - if (!(type&(ZEROPAD+LEFT))) - while(size-->0) - *str++ = ' '; - if (sign) - *str++ = sign; - if (type & SPECIAL) - if (base==8) - *str++ = '0'; - else if (base==16) { - *str++ = '0'; - *str++ = digits[33]; - } - if (!(type & LEFT)) - while (size-- > 0) - *str++ = c; - while (i < precision--) - *str++ = '0'; - while (i-- > 0) - *str++ = tmp[i]; - while (size-- > 0) - *str++ = ' '; - return str; -} - -int vsprintf(char *buf, const char *fmt, va_list args) -{ - int len; - unsigned long num; - int i, base; - char * str; - char *s; - - int flags; /* flags to number() */ - - int field_width; /* width of output field */ - int precision; /* min. # of digits for integers; max - number of chars for from string */ - int qualifier; /* 'h', 'l', or 'L' for integer fields */ - - for (str=buf ; *fmt ; ++fmt) { - if (*fmt != '%') { - *str++ = *fmt; - continue; - } - - /* process flags */ - flags = 0; - repeat: - ++fmt; /* this also skips first '%' */ - switch (*fmt) { - case '-': flags |= LEFT; goto repeat; - case '+': flags |= PLUS; goto repeat; - case ' ': flags |= SPACE; goto repeat; - case '#': flags |= SPECIAL; goto repeat; - case '0': flags |= ZEROPAD; goto repeat; - } - - /* get field width */ - field_width = -1; - if (is_digit(*fmt)) - field_width = skip_atoi(&fmt); - else if (*fmt == '*') { - ++fmt; - /* it's the next argument */ - field_width = va_arg(args, int); - if (field_width < 0) { - field_width = -field_width; - flags |= LEFT; - } - } - - /* get the precision */ - precision = -1; - if (*fmt == '.') { - ++fmt; - if (is_digit(*fmt)) - precision = skip_atoi(&fmt); - else if (*fmt == '*') { - ++fmt; - /* it's the next argument */ - precision = va_arg(args, int); - } - if (precision < 0) - precision = 0; - } - - /* get the conversion qualifier */ - qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { - qualifier = *fmt; - ++fmt; - } - - /* default base */ - base = 10; - - switch (*fmt) { - case 'c': - if (!(flags & LEFT)) - while (--field_width > 0) - *str++ = ' '; - *str++ = (unsigned char) va_arg(args, int); - while (--field_width > 0) - *str++ = ' '; - continue; - - case 's': - s = va_arg(args, char *); - if (!s) - s = "<NULL>"; - len = strlen(s); - if (precision < 0) - precision = len; - else if (len > precision) - len = precision; - - if (!(flags & LEFT)) - while (len < field_width--) - *str++ = ' '; - for (i = 0; i < len; ++i) - *str++ = *s++; - while (len < field_width--) - *str++ = ' '; - continue; - - case 'p': - if (field_width == -1) { - field_width = 2*sizeof(void *); - flags |= ZEROPAD; - } - str = number(str, - (unsigned long) va_arg(args, void *), 16, - field_width, precision, flags); - continue; - - - case 'n': - if (qualifier == 'l') { - long * ip = va_arg(args, long *); - *ip = (str - buf); - } else { - int * ip = va_arg(args, int *); - *ip = (str - buf); - } - continue; - - /* integer number formats - set up the flags and "break" */ - case 'o': - base = 8; - break; - - case 'X': - flags |= LARGE; - case 'x': - base = 16; - break; - - case 'd': - case 'i': - flags |= SIGN; - case 'u': - break; - - default: - if (*fmt != '%') - *str++ = '%'; - if (*fmt) - *str++ = *fmt; - else - --fmt; - continue; - } - if (qualifier == 'l') - num = va_arg(args, unsigned long); - else if (qualifier == 'h') - if (flags & SIGN) - num = va_arg(args, short); - else - num = va_arg(args, unsigned short); - else if (flags & SIGN) - num = va_arg(args, int); - else - num = va_arg(args, unsigned int); - str = number(str, num, base, field_width, precision, flags); - } - *str = '\0'; - return str-buf; -} - -int sprintf(char * buf, const char *fmt, ...) -{ - va_list args; - int i; - - va_start(args, fmt); - i=vsprintf(buf,fmt,args); - va_end(args); - return i; -} - |