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 /arch/mips | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'arch/mips')
65 files changed, 9345 insertions, 7238 deletions
diff --git a/arch/mips/.gdbinit b/arch/mips/.gdbinit new file mode 100644 index 000000000..8bdcdae68 --- /dev/null +++ b/arch/mips/.gdbinit @@ -0,0 +1,7 @@ +echo Setting up the environment for debugging vmlinux...\n +echo set remotedebug 0 \n +set remotedebug 0 +echo cd arch/mips/kernel \n +cd arch/mips/kernel +echo target remote /dev/ttyS0 \n +target remote /dev/ttyS0 diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 0dc133749..baec00d70 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -1,71 +1,101 @@ # -# Makefile.mips +# arch/mips/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture # # This file is subject to the terms and conditions of the GNU General Public # License. See the file "COPYING" in the main directory of this archive # for more details. # -# Copyright (C) 1994 by Waldorf GMBH, +# Copyright (C) 1994, 1995 by Waldorf Electronics, # written by Ralf Baechle # -AS = mips-linux-as -ASFLAGS = -mips3 -mcpu=r4000 -LD = mips-linux-ld -HOSTCC = gcc -CC = mips-linux-gcc -V 2.5.8 -Wa,-mips3 -mcpu=r4000 -D__KERNEL__ -I$(TOPDIR)/include -#CC = mips-linux-gcc -V 2.6.2 -Wa,-mips3 -mcpu=r4600 -D__KERNEL__ -I$(TOPDIR)/include -MAKE = make -CPP = $(CC) -E -AR = mips-linux-ar -RANLIB = mips-linux-ranlib -STRIP = strip -KERNELHDRS = /home/ralf/src/linux +ifdef CONFIG_ELF_COMPILER +ifdef CONFIG_CPU_LITTLE_ENDIAN +prefix = mipsel-linuxelf- +else +prefix = mips-linuxelf- +endif +else +ifdef CONFIG_CPU_LITTLE_ENDIAN +prefix = mipsel-linux- +else +prefix = mips-linux- +endif +endif + +AS = $(prefix)as +LD = $(prefix)ld +LINKFLAGS = -N -Ttext 0x80000000 +#HOSTCC = gcc +CC = $(prefix)gcc -D__KERNEL__ -I$(TOPDIR)/include +CPP = $(CC) -E $(CFLAGS) +AR = $(prefix)ar +RANLIB = $(prefix)ranlib +STRIP = $(prefix)strip +NM = $(prefix)nm + +# +# The new ELF GCC uses -G0 -mabicalls -fpic as default. We don't need PIC +# code in the kernel since it only slows down the whole thing. For the +# old GCC these options are just the defaults. At some point we might +# make use of global pointer optimaztions. +# +ifdef CONFIG_OBJECT_ELF +CFLAGS := $(CFLAGS) -G0 -mno-abicalls -fno-pic #-pipe +endif + +ifdef CONFIG_REMOTE_DEBUG +CFLAGS := $(CFLAGS) -g +endif + +ifdef CONFIG_CPU_R3000 +CFLAGS := $(CFLAGS) -mcpu=r3000 -mips1 +ASFLAGS := $(ASFLAGS) -mcpu=r3000 -mips1 +endif +ifdef CONFIG_CPU_R6000 +CFLAGS := $(CFLAGS) -mcpu=r6000 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r6000 -mips2 +endif +ifdef CONFIG_CPU_R4X00 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r4400 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r4400 -mips2 +endif +ifdef CONFIG_CPU_R4600 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r4600 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r4600 -mips2 +endif +ifdef CONFIG_CPU_R8000 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r8000 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r8000 -mips2 +endif +ifdef CONFIG_CPU_R10000 +CFLAGS := $(CFLAGS) -D__R4000__ -mcpu=r8000 -mips2 +ASFLAGS := $(ASFLAGS) -mcpu=r8000 -mips2 +endif + +HEAD := arch/mips/kernel/head.o -zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem - $(MAKE) -C zBoot +SUBDIRS := $(SUBDIRS) arch/mips/kernel arch/mips/mm arch/mips/lib +ARCHIVES := arch/mips/kernel/kernel.o arch/mips/mm/mm.o $(ARCHIVES) +LIBS := arch/mips/lib/lib.a $(LIBS) arch/mips/lib/lib.a -zImage: $(CONFIGURE) tools/zSystem - cp tools/System zImage - sync +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot -#zImage: $(CONFIGURE) zBoot/zSystem tools/build -# tools/build zBoot/zSystem $(ROOT_DEV) > zImage -# sync +zImage: vmlinux + @$(MAKEBOOT) zImage -zdisk: zImage - mcopy -n zImage a:vmlinux +compressed: zImage -tools/zSystem: boot/head.o init/main.o init/init.o tools/version.o linuxsubdirs - $(LD) $(LOWLDFLAGS) boot/head.o init/main.o init/init.o \ - tools/version.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(LIBS) \ - -N -Ttext 0x80000000 \ - -o tools/System - nm tools/System | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ - sort > System.map +zdisk: vmlinux + @$(MAKEBOOT) zdisk -#tools/system: boot/head.o init/main.o init/init.o tools/version.o linuxsubdirs -# $(LD) $(LOWLDFLAGS) boot/head.o init/main.o tools/version.o \ -# $(ARCHIVES) \ -# $(FILESYSTEMS) \ -# $(DRIVERS) \ -# $(LIBS) \ -# -N -Ttext 0x80000000 \ -# -o tools/system -# nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ -# sort > System.map +archclean: + @$(MAKEBOOT) clean -#tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs -# $(LD) $(HIGHLDFLAGS) boot/head.o init/main.o tools/version.o \ -# $(ARCHIVES) \ -# $(FILESYSTEMS) \ -# $(DRIVERS) \ -# $(LIBS) \ -# -N -Ttext 0x80600000 \ -# -o tools/zSystem -# nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \ -# sort > zSystem.map +archdep: + @$(MAKEBOOT) dep diff --git a/arch/mips/TODO b/arch/mips/TODO new file mode 100644 index 000000000..29afd71f7 --- /dev/null +++ b/arch/mips/TODO @@ -0,0 +1,10 @@ + - Check the definitions for F_EXLCK and F_SHLCK in include/asm-mips/fcntl.h + for correctness and compatibility with MIPS ABI. + - Check the definitions for O_NDELAY in include/asm-mips/fcntl.h for + correctness and compatibility with MIPS ABI. + - What are the fields l_sysid and pad in struct flock supposed to contain? + Do we need to handle them in the kernel? + - Check the resource limits defines in asm-mips/resource.h + - Recheck struct stat in asm-mips/stat.h + - Use timestruc_t in struct stat in asm/stat.h instead of time_t + - cacheflush should check for illegal flags diff --git a/arch/mips/bios32.c b/arch/mips/bios32.c deleted file mode 100644 index bb011a852..000000000 --- a/arch/mips/bios32.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * bios32.c - BIOS32, PCI BIOS functions. - * - * Copyright (C) 1994 by Waldorf GMBH, - * written by Ralf Baechle - * - * Just nothing for a MIPS board... - */ diff --git a/arch/mips/boot/Makefile b/arch/mips/boot/Makefile new file mode 100644 index 000000000..93784137d --- /dev/null +++ b/arch/mips/boot/Makefile @@ -0,0 +1,47 @@ +# +# arch/mips/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Ralf Baechle +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = milo.o a.out.o + +# +# Fake compressed boot +# +zImage: $(CONFIGURE) $(TOPDIR)/vmlinux + cp $(TOPDIR)/vmlinux $@ + $(STRIP) --discard-all $@ + +zdisk: zImage + mcopy -n zImage a:vmlinux + +dep: + $(CPP) -M *.[cS] > .depend + +clean: + rm -f zImage + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/arch/mips/boot/a.out.c b/arch/mips/boot/a.out.c new file mode 100644 index 000000000..15515b219 --- /dev/null +++ b/arch/mips/boot/a.out.c @@ -0,0 +1,254 @@ +/* + * arch/mips/boot/milo.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1994, 1995 by Ralf Baechle + * Copyright (C) 1993 by Hamish Macdonald + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> + +#include "loader.h" + +static int kfd; +static struct exec kexec; +static off_t filesize; +static struct nlist *syms; +static char *strs; +static long strsize, numsyms; + +/* + * Read a symbol from the kernel executable + */ +static struct nlist *aout_get_nlist(char *symbol) +{ + struct nlist *nl, *p = NULL; + + for (nl = syms; nl < syms + numsyms; nl++) { + /* + * We accept only extern visible .text, .data and .bss symbols. + */ + if (strcmp (symbol, strs + nl->n_un.n_strx) == 0 + && ((nl->n_type == N_TEXT | N_EXT) || + (nl->n_type == N_DATA | N_EXT) || + (nl->n_type == N_BSS | N_EXT))) { + p = (struct nlist *)malloc (sizeof (struct nlist) + + strlen(strs + nl->n_un.n_strx) + 1); + if (!p) + break; + *p = *nl; + p->n_un.n_name = (char *)(p+1); + strcpy (p->n_un.n_name, strs + nl->n_un.n_strx); + } + } + return p; +} + +/* + * Return a pointer to the internal symbol + */ +static char *aout_ld_isymbol(char *symbol) +{ + static char isymbol[64]; + + strcpy(isymbol, STR(C_LABEL_PREFIX)); + strcat(isymbol, symbol); + + return isymbol; +} + +/* + * Return base address for the loaded kernel + */ +static u_long aout_get_kbase(void) +{ + return (u_long) kexec.a_entry; +} + +/* + * Return size of kernel code + data + */ +static u_long aout_get_ksize(void) +{ + return (u_long) (kexec.a_text + kexec.a_data); +} + +/* + * Load a.out kernel into memory + */ +static int aout_load_kernel(void *mem) +{ + if (lseek (kfd, N_TXTOFF(kexec), L_SET) == -1) + { + fprintf (stderr, "Failed to seek to text\n\r"); + exit (EXIT_FAILURE); + } + if (read (kfd, mem, kexec.a_text) != kexec.a_text) + { + fprintf (stderr, "Failed to read text\n\r"); + exit (EXIT_FAILURE); + } + if (lseek (kfd, N_DATOFF(kexec), L_SET) == -1) + { + fprintf (stderr, "Failed to seek to data\n\r"); + exit (EXIT_FAILURE); + } + if (read (kfd, mem + kexec.a_text, kexec.a_data) != kexec.a_data) + { + fprintf (stderr, "Failed to read data\n\r"); + exit (EXIT_FAILURE); + } + close (kfd); + + return 0; +} + +/* + * Print some statistics about the kernel + */ +static void aout_print_stats(void) +{ + printf("Kernel text at 0x%08lx, size %d bytes\n\r", + kexec.a_entry + N_TXTADDR(kexec), kexec.a_text); + printf("Kernel data at 0x%08lx, size %d bytes\n\r", + kexec.a_entry + N_DATADDR(kexec), kexec.a_data ); + printf("Kernel bss at 0x%08lx, size %d bytes\n\r", + kexec.a_entry + N_BSSADDR(kexec), kexec.a_bss ); +} + +static int aout_open_kernel(char *kernel) +{ + int sfd; + + /* + * open kernel executable and read exec header + */ + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Open kernel file\r\n"); + fflush(stdout); + } + if ((kfd = open (kernel, O_RDONLY)) == -1) + { + printf ("Unable to open kernel file %s\r\n", kernel); + exit (EXIT_FAILURE); + } + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Reading exec header\r\n"); + fflush(stdout); + } + if (read (kfd, (void *)&kexec, sizeof(kexec)) != sizeof(kexec)) + { + printf ("Unable to read exec header from %s\n\r", kernel); + exit (EXIT_FAILURE); + } + + /* + * Is this really a kernel??? + * (My Mips docs mention SMAGIC, too, but don't tell about what + * a SMAGIC file exactly is. If someone knows, please tell me -Ralf) + * + * FIXME: QMAGIC doesn't work yet. + */ + if(N_MAGIC(kexec) != OMAGIC && + N_MAGIC(kexec) != NMAGIC && + N_MAGIC(kexec) != ZMAGIC && + N_MAGIC(kexec) != QMAGIC) + { + fprintf(stderr, "Kernel %s is no MIPS binary.\r\n", kernel); + exit (EXIT_FAILURE); + } + if(N_MACHTYPE(kexec) != M_MIPS1 && + N_MACHTYPE(kexec) != M_MIPS2) + { + fprintf(stderr, "Kernel %s is no MIPS binary.\r\n", kernel); + exit (EXIT_FAILURE); + } + + /* + * Read file's symbol table + */ + /* + * Open kernel executable and read exec header - we need to + * use a second open file since the ARC standard doesn't + * support reverse seeking on files which might run us in + * trouble later on. + */ + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Open kernel file\r\n"); + fflush(stdout); + } + if ((sfd = open (kernel, O_RDONLY)) == -1) + { + printf ("Unable to open kernel %s for reading symbols.\r\n", kernel); + exit (EXIT_FAILURE); + } + syms = (struct nlist *)malloc (kexec.a_syms); + if (!syms) + { + return 0; + } + + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Seeking to symbol table\r\n"); + fflush(stdout); + } + lseek (sfd, N_SYMOFF(kexec), L_SET); + if (debuglevel >= 2) + { + printf("aout_open_kernel(): Reading symbol table\r\n"); + fflush(stdout); + } + read (sfd, syms, kexec.a_syms); + numsyms = kexec.a_syms / sizeof (struct nlist); + filesize = lseek (sfd, 0L, L_XTND); + strsize = filesize - N_STROFF(kexec); + strs = (char *)malloc (strsize); + + if (!strs) + { + free (syms); + syms = NULL; + return 0; + } + + lseek (sfd, N_STROFF(kexec), L_SET); + read (sfd, strs, strsize); + close(sfd); + + return 0; +} + +static void aout_close_kernel(void) +{ + if (strs) + { + free (strs); + strs = NULL; + } + if (syms) + { + free (syms); + syms = NULL; + } + close(kfd); +} + +struct loader loader_aout = { + aout_get_nlist, + aout_ld_isymbol, + aout_get_kbase, + aout_get_ksize, + aout_load_kernel, + aout_print_stats, + aout_open_kernel, + aout_close_kernel +}; diff --git a/arch/mips/boot/compressed/Makefile b/arch/mips/boot/compressed/Makefile new file mode 100644 index 000000000..228ee83bc --- /dev/null +++ b/arch/mips/boot/compressed/Makefile @@ -0,0 +1,45 @@ +# +# linux/arch/i386/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +HEAD = head.o +SYSTEM = $(TOPDIR)/vmlinux + +OBJECTS = $(HEAD) inflate.o unzip.o misc.o + +CFLAGS = -O2 -DSTDC_HEADERS + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +all: vmlinux + +vmlinux: piggy.o $(OBJECTS) + $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJECTS) piggy.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/linux/tasks.h + $(CPP) -traditional head.S -o head.s + +piggy.o: $(SYSTEM) xtract piggyback + ./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.o + +xtract: xtract.c + $(HOSTCC) $(CFLAGS) -o xtract xtract.c + +piggyback: piggyback.c + $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c + +clean: + rm -f xtract piggyback vmlinux diff --git a/arch/mips/boot/compressed/cache.S b/arch/mips/boot/compressed/cache.S new file mode 100644 index 000000000..8e7fff0c1 --- /dev/null +++ b/arch/mips/boot/compressed/cache.S @@ -0,0 +1,162 @@ +/* + * arch/mips/boot/compressed/cache.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle + * + * Flush instruction/data caches + * + * Parameters: a0 - starting address to flush + * a1 - size of area to be flushed + * a2 - which caches to be flushed + * + * FIXME: - ignores parameters in a0/a1 + * - doesn't know about second level caches + */ +#include <linux/autoconf.h> + +#include <asm/asm.h> +#include <asm/cachectl.h> +#include <asm/mipsregs.h> +#include <asm/segment.h> + +#ifdef __R4000__ + +/* + * Some bits in the config register + */ +#define CONFIG_IB (1<<5) +#define CONFIG_DB (1<<4) + + /* + * Flush instruction/data caches + * + * Parameters: a0 - starting address to flush + * a1 - size of area to be flushed + * a2 - which caches to be flushed + * + */ + + .text + + .set noreorder + LEAF(cacheflush) + andi t1,a2,DCACHE + beqz t1,do_icache + li t0,KSEG0 # delay slot + + /* + * Writeback data cache, even lines + */ + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,0(t0) + cache Index_Writeback_Inv_D,32(t0) + cache Index_Writeback_Inv_D,64(t0) + cache Index_Writeback_Inv_D,96(t0) + cache Index_Writeback_Inv_D,128(t0) + cache Index_Writeback_Inv_D,160(t0) + cache Index_Writeback_Inv_D,192(t0) + cache Index_Writeback_Inv_D,224(t0) + cache Index_Writeback_Inv_D,256(t0) + cache Index_Writeback_Inv_D,288(t0) + cache Index_Writeback_Inv_D,320(t0) + cache Index_Writeback_Inv_D,352(t0) + cache Index_Writeback_Inv_D,384(t0) + cache Index_Writeback_Inv_D,416(t0) + cache Index_Writeback_Inv_D,448(t0) + cache Index_Writeback_Inv_D,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Writeback data cache, odd lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_IB + bnez t1,do_icache + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,16(t0) + cache Index_Writeback_Inv_D,48(t0) + cache Index_Writeback_Inv_D,80(t0) + cache Index_Writeback_Inv_D,112(t0) + cache Index_Writeback_Inv_D,144(t0) + cache Index_Writeback_Inv_D,176(t0) + cache Index_Writeback_Inv_D,208(t0) + cache Index_Writeback_Inv_D,240(t0) + cache Index_Writeback_Inv_D,272(t0) + cache Index_Writeback_Inv_D,304(t0) + cache Index_Writeback_Inv_D,336(t0) + cache Index_Writeback_Inv_D,368(t0) + cache Index_Writeback_Inv_D,400(t0) + cache Index_Writeback_Inv_D,432(t0) + cache Index_Writeback_Inv_D,464(t0) + cache Index_Writeback_Inv_D,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +do_icache: andi t1,a2,ICACHE + beqz t1,done + + /* + * Flush instruction cache, even lines + */ + lui t0,0x8000 + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,0(t0) + cache Index_Invalidate_I,32(t0) + cache Index_Invalidate_I,64(t0) + cache Index_Invalidate_I,96(t0) + cache Index_Invalidate_I,128(t0) + cache Index_Invalidate_I,160(t0) + cache Index_Invalidate_I,192(t0) + cache Index_Invalidate_I,224(t0) + cache Index_Invalidate_I,256(t0) + cache Index_Invalidate_I,288(t0) + cache Index_Invalidate_I,320(t0) + cache Index_Invalidate_I,352(t0) + cache Index_Invalidate_I,384(t0) + cache Index_Invalidate_I,416(t0) + cache Index_Invalidate_I,448(t0) + cache Index_Invalidate_I,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Flush instruction cache, even lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_DB + bnez t1,done + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,16(t0) + cache Index_Invalidate_I,48(t0) + cache Index_Invalidate_I,80(t0) + cache Index_Invalidate_I,112(t0) + cache Index_Invalidate_I,144(t0) + cache Index_Invalidate_I,176(t0) + cache Index_Invalidate_I,208(t0) + cache Index_Invalidate_I,240(t0) + cache Index_Invalidate_I,272(t0) + cache Index_Invalidate_I,304(t0) + cache Index_Invalidate_I,336(t0) + cache Index_Invalidate_I,368(t0) + cache Index_Invalidate_I,400(t0) + cache Index_Invalidate_I,432(t0) + cache Index_Invalidate_I,464(t0) + cache Index_Invalidate_I,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +done: j ra + nop + END(sys_cacheflush) + +#else /* !defined (__R4000__) */ +#error "No R3000 cacheflushing implemented yet!" +#endif /* !defined (__R4000__) */ diff --git a/arch/mips/boot/compressed/crypt.h b/arch/mips/boot/compressed/crypt.h new file mode 100644 index 000000000..2a4c203ca --- /dev/null +++ b/arch/mips/boot/compressed/crypt.h @@ -0,0 +1,12 @@ +/* crypt.h (dummy version) -- do not perform encryption + * Hardly worth copyrighting :-) + */ + +#ifdef CRYPT +# undef CRYPT /* dummy version */ +#endif + +#define RAND_HEAD_LEN 12 /* length of encryption random header */ + +#define zencode +#define zdecode diff --git a/arch/mips/boot/compressed/gzip.h b/arch/mips/boot/compressed/gzip.h new file mode 100644 index 000000000..2f738b945 --- /dev/null +++ b/arch/mips/boot/compressed/gzip.h @@ -0,0 +1,284 @@ +/* gzip.h -- common declarations for all gzip modules + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if defined(__STDC__) || defined(PROTO) +# define OF(args) args +#else +# define OF(args) () +#endif + +#ifdef __STDC__ + typedef void *voidp; +#else + typedef char *voidp; +#endif + +/* I don't like nested includes, but the string functions are used too often */ +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +# include <string.h> +# define memzero(s, n) memset ((s), 0, (n)) +#else +# include <strings.h> +# define strchr index +# define strrchr rindex +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) +# define memzero(s, n) bzero((s), (n)) +#endif + +#if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) +# include <memory.h> +#endif + +#ifndef RETSIGTYPE +# define RETSIGTYPE void +#endif + +#define local static + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +/* Return codes from gzip */ +#define OK 0 +#define ERROR 1 +#define WARNING 2 + +/* Compression methods (see algorithm.doc) */ +#define STORED 0 +#define COMPRESSED 1 +#define PACKED 2 +/* methods 3 to 7 reserved */ +#define DEFLATED 8 +extern int method; /* compression method */ + +/* To save memory for 16 bit systems, some arrays are overlayed between + * the various modules: + * deflate: prev+head window d_buf l_buf outbuf + * unlzw: tab_prefix tab_suffix stack inbuf outbuf + * inflate: window inbuf + * unpack: window inbuf + * For compression, input is done in window[]. For decompression, output + * is done in window except for unlzw. + */ + +#ifndef INBUFSIZ +# define INBUFSIZ 0x8000 /* input buffer size */ +#endif +#define INBUF_EXTRA 64 /* required by unlzw() */ + +#ifndef OUTBUFSIZ +# define OUTBUFSIZ 16384 /* output buffer size */ +#endif +#define OUTBUF_EXTRA 2048 /* required by unlzw() */ + +#define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */ + +#ifdef DYN_ALLOC +# define EXTERN(type, array) extern type * near array +# define DECLARE(type, array, size) type * near array +# define ALLOC(type, array, size) { \ + array = (type*)fcalloc((unsigned)(((size)+1L)/2), 2*sizeof(type)); \ + if (array == NULL) error("insufficient memory"); \ + } +# define FREE(array) {if (array != NULL) fcfree(array), array=NULL;} +#else +# define EXTERN(type, array) extern type array[] +# define DECLARE(type, array, size) type array[size] +# define ALLOC(type, array, size) +# define FREE(array) +#endif + +EXTERN(uch, inbuf); /* input buffer */ +EXTERN(uch, outbuf); /* output buffer */ +EXTERN(ush, d_buf); /* buffer for distances, see trees.c */ +EXTERN(uch, window); /* Sliding window and suffix table (unlzw) */ +#define tab_suffix window +#ifndef MAXSEG_64K +# define tab_prefix prev /* hash link (see deflate.c) */ +# define head (prev+WSIZE) /* hash head (see deflate.c) */ + EXTERN(ush, tab_prefix); /* prefix code (see unlzw.c) */ +#else +# define tab_prefix0 prev +# define head tab_prefix1 + EXTERN(ush, tab_prefix0); /* prefix for even codes */ + EXTERN(ush, tab_prefix1); /* prefix for odd codes */ +#endif + +extern unsigned insize; /* valid bytes in inbuf */ +extern unsigned inptr; /* index of next byte to be processed in inbuf */ +extern unsigned outcnt; /* bytes in output buffer */ + +extern long bytes_in; /* number of input bytes */ +extern long bytes_out; /* number of output bytes */ +extern long overhead; /* number of bytes in gzip header */ + +#define isize bytes_in +/* for compatibility with old zip sources (to be cleaned) */ + +extern int ifd; /* input file descriptor */ +extern int ofd; /* output file descriptor */ +extern char ifname[]; /* input filename or "stdin" */ +extern char ofname[]; /* output filename or "stdout" */ + +extern ulg time_stamp; /* original time stamp (modification time) */ +extern long ifile_size; /* input file size, -1 for devices (debug only) */ + +extern int exit_code; /* program exit code */ + +typedef int file_t; /* Do not use stdio */ +#define NO_FILE (-1) /* in memory compression */ + + +#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */ +#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */ +#define PKZIP_MAGIC "PK\003\004" /* Magic header for pkzip files */ +#define PACK_MAGIC "\037\036" /* Magic header for packed files */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* internal file attribute */ +#define UNKNOWN (-1) +#define BINARY 0 +#define ASCII 1 + +#ifndef WSIZE +# define WSIZE 0x8000 /* window size--must be a power of two, and */ +#endif /* at least 32K for zip's deflate method */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +extern int decrypt; /* flag to turn on decryption */ +extern int save_orig_name; /* set if original name must be saved */ +extern int verbose; /* be verbose (-v) */ +extern int level; /* compression level */ +extern int test; /* check .z file integrity */ +extern int to_stdout; /* output to stdout (-c) */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* put_byte is used for the compressed output, put_char for the + * uncompressed output. However unlzw() uses window for its + * suffix table instead of its output buffer, so it does not use put_char. + * (to be cleaned up). + */ +#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\ + flush_outbuf();} +#define put_char(c) {window[outcnt++]=(uch)(c); if (outcnt==WSIZE)\ + flush_window();} + +/* Output a 16 bit value, lsb first */ +#define put_short(w) \ +{ if (outcnt < OUTBUFSIZ-2) { \ + outbuf[outcnt++] = (uch) ((w) & 0xff); \ + outbuf[outcnt++] = (uch) ((ush)(w) >> 8); \ + } else { \ + put_byte((uch)((w) & 0xff)); \ + put_byte((uch)((ush)(w) >> 8)); \ + } \ +} + +/* Output a 32 bit value to the bit stream, lsb first */ +#define put_long(n) { \ + put_short((n) & 0xffff); \ + put_short(((ulg)(n)) >> 16); \ +} + +#define seekable() 0 /* force sequential output */ +#define translate_eol 0 /* no option -a yet */ + +#define tolow(c) (isupper(c) ? (c)-'A'+'a' : (c)) /* force to lower case */ + +/* Macros for getting two-byte and four-byte header values */ +#define SH(p) ((ush)(uch)((p)[0]) | ((ush)(uch)((p)[1]) << 8)) +#define LG(p) ((ulg)(SH(p)) | ((ulg)(SH((p)+2)) << 16)) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + /* in zip.c: */ +extern void zip OF((int in, int out)); +extern int file_read OF((char *buf, unsigned size)); + + /* in unzip.c */ +extern void unzip OF((int in, int out)); +extern int check_zipfile OF((int in)); + + /* in unpack.c */ +extern void unpack OF((int in, int out)); + + /* in gzip.c */ +RETSIGTYPE abort_gzip OF((void)); + + /* in deflate.c */ +void lm_init OF((int pack_level, ush *flags)); +ulg deflate OF((void)); + + /* in trees.c */ +void ct_init OF((ush *attr, int *method)); +int ct_tally OF((int dist, int lc)); +ulg flush_block OF((char *buf, ulg stored_len, int eof)); + + /* in bits.c */ +void bi_init OF((file_t zipfile)); +void send_bits OF((int value, int length)); +unsigned bi_reverse OF((unsigned value, int length)); +void bi_windup OF((void)); +void copy_block OF((char *buf, unsigned len, int header)); +extern int (*read_buf) OF((char *buf, unsigned size)); + + /* in util.c: */ +extern ulg updcrc OF((uch *s, unsigned n)); +extern void clear_bufs OF((void)); +extern int fill_inbuf OF((void)); +extern void flush_outbuf OF((void)); +extern void flush_window OF((void)); +extern char *strlwr OF((char *s)); +extern char *basename OF((char *fname)); +extern char *add_envopt OF((int *argcp, char ***argvp, char *env)); +extern void error OF((char *m)); +extern void warn OF((char *a, char *b)); +extern void read_error OF((void)); +extern void write_error OF((void)); +extern void display_ratio OF((long num, long den)); +extern voidp xmalloc OF((unsigned int size)); + + /* in inflate.c */ +extern int inflate OF((void)); diff --git a/arch/mips/boot/compressed/head.S b/arch/mips/boot/compressed/head.S new file mode 100644 index 000000000..0ca599563 --- /dev/null +++ b/arch/mips/boot/compressed/head.S @@ -0,0 +1,52 @@ +/* + * arch/mips/boot/compressed/head.S + * + * Copyright (C) 1995 by Ralf Baechle + * + * Head.S contains the MIPS exception handler and startup code. + */ + +#include <asm/asm.h> + + .text + .set noreorder +/* + * Compressed kernel entry + */ + NESTED(kernel_entry, 16, sp) + /* + * Set EXL in c0_status. The results in the lowest two + * gigabytes identity mapped. + */ + mfc0 t0,CP0_STATUS + ori t0,4 + mtc0 t0,CP0_STATUS + + /* + * Clear BSS first so that there are no surprises... + */ + la t0,_edata + la t1,_end + sw zero,(t0) +1: addiu t0,4 + bnel t0,t1,1b + sw zero,(t0) + END(kernel_entry) + + /* + * Do the decompression, and jump to the new kernel.. + */ + jal C_LABEL(decompress_kernel) + nop + + /* + * Flush caches + */ + jal C_LABEL(cacheflush) + + /* + * Jump into the decompressed kernel + */ + la t0,KSEG0 + jr t0 + nop diff --git a/arch/mips/boot/compressed/inflate.c b/arch/mips/boot/compressed/inflate.c new file mode 100644 index 000000000..848fef6ae --- /dev/null +++ b/arch/mips/boot/compressed/inflate.c @@ -0,0 +1,810 @@ +#define DEBG(x) +#define DEBG1(x) +/* inflate.c -- Not copyrighted 1992 by Mark Adler + version c10p1, 10 January 1993 */ + +/* + * Adapted for booting Linux by Hannu Savolainen 1993 + * based on gzip-1.0.3 + */ + +#ifndef lint +static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $"; +#endif + +#include "gzip.h" +#define slide window + +#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H) +# include <sys/types.h> +# include <stdlib.h> +#endif + +struct huft { + uch e; /* number of extra bits or operation */ + uch b; /* number of bits in this code or subcode */ + union { + ush n; /* literal, length base, or distance base */ + struct huft *t; /* pointer to next level of table */ + } v; +}; + + +/* Function prototypes */ +int huft_build OF((unsigned *, unsigned, unsigned, ush *, ush *, + struct huft **, int *)); +int huft_free OF((struct huft *)); +int inflate_codes OF((struct huft *, struct huft *, int, int)); +int inflate_stored OF((void)); +int inflate_fixed OF((void)); +int inflate_dynamic OF((void)); +int inflate_block OF((int *)); +int inflate OF((void)); + + +#define wp outcnt +#define flush_output(w) (wp=(w),flush_window()) + +/* Tables for deflate from PKZIP's appnote.txt. */ +static unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; +static ush cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* note: see note #13 above about the 258 in this list. */ +static ush cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ +static ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +static ush cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + +ulg bb; /* bit buffer */ +unsigned bk; /* bits in bit buffer */ + +ush mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +#ifdef CRYPT + uch cc; +# define NEXTBYTE() \ + (decrypt ? (cc = get_byte(), zdecode(cc), cc) : get_byte()) +#else +# define NEXTBYTE() (uch)get_byte() +#endif +#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<<k;k+=8;}} +#define DUMPBITS(n) {b>>=(n);k-=(n);} + +int lbits = 9; /* bits in base literal/length lookup table */ +int dbits = 6; /* bits in base distance lookup table */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +#define BMAX 16 /* maximum bit length of any code (16 for explode) */ +#define N_MAX 288 /* maximum number of codes in any set */ + + +unsigned hufts; /* track memory usage */ + + +int huft_build(b, n, s, d, e, t, m) +unsigned *b; /* code lengths in bits (all assumed <= BMAX) */ +unsigned n; /* number of codes (assumed <= N_MAX) */ +unsigned s; /* number of simple-valued codes (0..s-1) */ +ush *d; /* list of base values for non-simple codes */ +ush *e; /* list of extra bits for non-simple codes */ +struct huft **t; /* result: starting table */ +int *m; /* maximum lookup bits, returns actual */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. */ +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX+1]; /* bit length count table */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register unsigned i; /* counter, current code */ + register unsigned j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register unsigned *p; /* pointer into c[], b[], or v[] */ + register struct huft *q; /* points to current table */ + struct huft r; /* table entry for structure assignment */ + struct huft *u[BMAX]; /* table stack */ + unsigned v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + unsigned x[BMAX+1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + +DEBG("huft1 "); + + /* Generate counts for each bit length */ + memzero(c, sizeof(c)); + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (struct huft *)NULL; + *m = 0; + return 0; + } + +DEBG("huft2 "); + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((unsigned)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((unsigned)l > i) + l = i; + *m = l; + +DEBG("huft3 "); + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return 2; /* bad input: more codes than bits */ + if ((y -= c[i]) < 0) + return 2; + c[i] += y; + +DEBG("huft4 "); + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + +DEBG("huft5 "); + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + +DEBG("h6 "); + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (struct huft *)NULL; /* just to keep compilers happy */ + q = (struct huft *)NULL; /* ditto */ + z = 0; /* ditto */ +DEBG("h6a "); + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { +DEBG("h6b "); + a = c[k]; + while (a--) + { +DEBG("h6b1 "); + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { +DEBG1("1 "); + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ +DEBG1("2 "); + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } +DEBG1("3 "); + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + q = (struct huft *)malloc((z + 1)*sizeof(struct huft)); +DEBG1("4 "); + hufts += z + 1; /* track memory usage */ + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->v.t)) = (struct huft *)NULL; + u[h] = ++q; /* table starts after link */ + +DEBG1("5 "); + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.b = (uch)l; /* bits to dump before this table */ + r.e = (uch)(16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } +DEBG1("6 "); + } +DEBG("h6c "); + + /* set up table entry in r */ + r.b = (uch)(k - w); + if (p >= v + n) + r.e = 99; /* out of values--invalid code */ + else if (*p < s) + { + r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = *p++; /* simple code is just the value */ + } + else + { + r.e = (uch)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } +DEBG("h6d "); + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } +DEBG("h6e "); + } +DEBG("h6f "); + } + +DEBG("huft7 "); + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + + + +int huft_free(t) +struct huft *t; /* table to free */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register struct huft *p, *q; + + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != (struct huft *)NULL) + { + q = (--p)->v.t; + free(p); + p = q; + } + return 0; +} + + +int inflate_codes(tl, td, bl, bd) +struct huft *tl, *td; /* literal/length and distance decoder tables */ +int bl, bd; /* number of bits decoded by tl[] and td[] */ +/* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ +{ + register unsigned e; /* table entry flag/number of extra bits */ + unsigned n, d; /* length and index for copy */ + unsigned w; /* current window position */ + struct huft *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) /* do until end of block */ + { + NEEDBITS((unsigned)bl) + if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + if (e == 16) /* then it's a literal */ + { + slide[w++] = (uch)t->v.n; + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if (e == 15) + break; + + /* get length of block to copy */ + NEEDBITS(e) + n = t->v.n + ((unsigned)b & mask_bits[e]); + DUMPBITS(e); + + /* decode distance of block to copy */ + NEEDBITS((unsigned)bd) + if ((e = (t = td + ((unsigned)b & md))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + NEEDBITS(e) + d = w - t->v.n - ((unsigned)b & mask_bits[e]); + DUMPBITS(e) + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) /* (this test assumes unsigned comparison) */ + { + memcpy(slide + w, slide + d, e); + w += e; + d += e; + } + else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + slide[w++] = slide[d++]; + } while (--e); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } while (n); + } + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + + + +int inflate_stored() +/* "decompress" an inflated type 0 (stored) block. */ +{ + unsigned n; /* number of bytes in block */ + unsigned w; /* current window position */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + +DEBG("<stor"); + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + + /* go to byte boundary */ + n = k & 7; + DUMPBITS(n); + + + /* get the length and its complement */ + NEEDBITS(16) + n = ((unsigned)b & 0xffff); + DUMPBITS(16) + NEEDBITS(16) + if (n != (unsigned)((~b) & 0xffff)) + return 1; /* error in compressed data */ + DUMPBITS(16) + + + /* read and output the compressed data */ + while (n--) + { + NEEDBITS(8) + slide[w++] = (uch)b; + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + DUMPBITS(8) + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + DEBG(">"); + return 0; +} + + + +int inflate_fixed() +/* decompress an inflated type 1 (fixed Huffman codes) block. We should + either replace this with a custom decoder, or at least precompute the + Huffman tables. */ +{ + int i; /* temporary variable */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned l[288]; /* length list for huft_build */ + +DEBG("<fix"); + + /* set up literal table */ + for (i = 0; i < 144; i++) + l[i] = 8; + for (; i < 256; i++) + l[i] = 9; + for (; i < 280; i++) + l[i] = 7; + for (; i < 288; i++) /* make a complete, but wrong code set */ + l[i] = 8; + bl = 7; + if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) + return i; + + + /* set up distance table */ + for (i = 0; i < 30; i++) /* make an incomplete code set */ + l[i] = 5; + bd = 5; + if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) + { + huft_free(tl); + + DEBG(">"); + return i; + } + + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; +} + + + +int inflate_dynamic() +/* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ +#ifdef PKZIP_BUG_WORKAROUND + unsigned ll[288+32]; /* literal/length and distance code lengths */ +#else + unsigned ll[286+30]; /* literal/length and distance code lengths */ +#endif + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + +DEBG("<dyn"); + + /* make local bit buffer */ + b = bb; + k = bk; + + + /* read in table lengths */ + NEEDBITS(5) + nl = 257 + ((unsigned)b & 0x1f); /* number of literal/length codes */ + DUMPBITS(5) + NEEDBITS(5) + nd = 1 + ((unsigned)b & 0x1f); /* number of distance codes */ + DUMPBITS(5) + NEEDBITS(4) + nb = 4 + ((unsigned)b & 0xf); /* number of bit length codes */ + DUMPBITS(4) +#ifdef PKZIP_BUG_WORKAROUND + if (nl > 288 || nd > 32) +#else + if (nl > 286 || nd > 30) +#endif + return 1; /* bad lengths */ + +DEBG("dyn1 "); + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) + { + NEEDBITS(3) + ll[border[j]] = (unsigned)b & 7; + DUMPBITS(3) + } + for (; j < 19; j++) + ll[border[j]] = 0; + +DEBG("dyn2 "); + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) + { + if (i == 1) + huft_free(tl); + return i; /* incomplete code set */ + } + +DEBG("dyn3 "); + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned)i < n) + { + NEEDBITS((unsigned)bl) + j = (td = tl + ((unsigned)b & m))->b; + DUMPBITS(j) + j = td->v.n; + if (j < 16) /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + else if (j == 16) /* repeat last length 3 to 6 times */ + { + NEEDBITS(2) + j = 3 + ((unsigned)b & 3); + DUMPBITS(2) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = l; + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + NEEDBITS(3) + j = 3 + ((unsigned)b & 7); + DUMPBITS(3) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + NEEDBITS(7) + j = 11 + ((unsigned)b & 0x7f); + DUMPBITS(7) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + } + +DEBG("dyn4 "); + + /* free decoding table for trees */ + huft_free(tl); + +DEBG("dyn5 "); + + /* restore the global bit buffer */ + bb = b; + bk = k; + +DEBG("dyn5a "); + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) + { +DEBG("dyn5b "); + if (i == 1) { + error(" incomplete literal tree\n"); + huft_free(tl); + } + return i; /* incomplete code set */ + } +DEBG("dyn5c "); + bd = dbits; + if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) + { +DEBG("dyn5d "); + if (i == 1) { + error(" incomplete distance tree\n"); +#ifdef PKZIP_BUG_WORKAROUND + i = 0; + } +#else + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ +#endif + } + +DEBG("dyn6 "); + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + +DEBG("dyn7 "); + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + + DEBG(">"); + return 0; +} + + + +int inflate_block(e) +int *e; /* last block flag */ +/* decompress an inflated block */ +{ + unsigned t; /* block type */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + DEBG("<blk"); + + /* make local bit buffer */ + b = bb; + k = bk; + + + /* read in last block bit */ + NEEDBITS(1) + *e = (int)b & 1; + DUMPBITS(1) + + + /* read in block type */ + NEEDBITS(2) + t = (unsigned)b & 3; + DUMPBITS(2) + + + /* restore the global bit buffer */ + bb = b; + bk = k; + + /* inflate that block type */ + if (t == 2) + return inflate_dynamic(); + if (t == 0) + return inflate_stored(); + if (t == 1) + return inflate_fixed(); + + DEBG(">"); + + /* bad block type */ + return 2; +} + + + +int inflate() +/* decompress an inflated entry */ +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h; /* maximum struct huft's malloc'ed */ + + + /* initialize window, bit buffer */ + wp = 0; + bk = 0; + bb = 0; + + + /* decompress until the last block */ + h = 0; + do { + hufts = 0; + if ((r = inflate_block(&e)) != 0) + return r; + if (hufts > h) + h = hufts; + } while (!e); + + /* Undo too much lookahead. The next read will be byte aligned so we + * can discard unused bits in the last meaningful byte. + */ + while (bk >= 8) { + bk -= 8; + inptr--; + } + + /* flush out slide */ + flush_output(wp); + + + /* return success */ +#ifdef DEBUG + fprintf(stderr, "<%u> ", h); +#endif /* DEBUG */ + return 0; +} diff --git a/arch/mips/boot/compressed/lzw.h b/arch/mips/boot/compressed/lzw.h new file mode 100644 index 000000000..4e640f5a2 --- /dev/null +++ b/arch/mips/boot/compressed/lzw.h @@ -0,0 +1,42 @@ +/* lzw.h -- define the lzw functions. + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +#if !defined(OF) && defined(lint) +# include "gzip.h" +#endif + +#ifndef BITS +# define BITS 16 +#endif +#define INIT_BITS 9 /* Initial number of bits per code */ + +#define LZW_MAGIC "\037\235" /* Magic header for lzw files, 1F 9D */ + +#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */ +/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free. + * It's a pity that old uncompress does not check bit 0x20. That makes + * extension of the format actually undesirable because old compress + * would just crash on the new format instead of giving a meaningful + * error message. It does check the number of bits, but it's more + * helpful to say "unsupported format, get a new version" than + * "can only handle 16 bits". + */ + +#define BLOCK_MODE 0x80 +/* Block compression: if table is full and compression rate is dropping, + * clear the dictionary. + */ + +#define LZW_RESERVED 0x60 /* reserved bits */ + +#define CLEAR 256 /* flush the dictionary */ +#define FIRST (CLEAR+1) /* first free entry */ + +extern int maxbits; /* max bits per code for LZW */ +extern int block_mode; /* block compress mode -C compatible with 2.0 */ + +extern void lzw OF((int in, int out)); +extern void unlzw OF((int in, int out)); diff --git a/arch/mips/boot/compressed/misc.c b/arch/mips/boot/compressed/misc.c new file mode 100644 index 000000000..1e3bb5f82 --- /dev/null +++ b/arch/mips/boot/compressed/misc.c @@ -0,0 +1,438 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * Modified for Linux/MIPS 1995 by Ralf Baechle + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993 + */ + +#include "gzip.h" +#include "lzw.h" + +#include <asm/segment.h> + +/* + * These are set up by the setup-routine at boot-time: + */ + +struct screen_info { + unsigned char orig_x; + unsigned char orig_y; + unsigned char unused1[2]; + unsigned short orig_video_page; + unsigned char orig_video_mode; + unsigned char orig_video_cols; + unsigned short orig_video_ega_ax; + unsigned short orig_video_ega_bx; + unsigned short orig_video_ega_cx; + unsigned char orig_video_lines; +}; + +/* + * This is set up by the setup-routine at boot-time + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) +#define DRIVE_INFO (*(struct drive_info *)0x90080) +#define SCREEN_INFO (*(struct screen_info *)0x90000) +#define RAMDISK_SIZE (*(unsigned short *)0x901F8) +#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) +#define AUX_DEVICE_INFO (*(unsigned char *)0x901FF) + +#define EOF -1 + +DECLARE(uch, inbuf, INBUFSIZ); +DECLARE(uch, outbuf, OUTBUFSIZ+OUTBUF_EXTRA); +DECLARE(uch, window, WSIZE); + +unsigned outcnt; +unsigned insize; +unsigned inptr; +int graphmode; + +extern char input_data[]; +extern int input_len; + +int input_ptr; + +int method, exit_code, part_nb, last_member; +int test = 0; +int force = 0; +int verbose = 1; +long bytes_in, bytes_out; + +char *output_data; +unsigned long output_ptr; + +extern int end; +long free_mem_ptr = (long)&end; + +int to_stdout = 0; +int hard_math = 0; + +void (*work)(int inf, int outf); +void makecrc(void); + +local int get_method(int); + +char *vidmem = (char *)0xb8000; +int lines, cols; + +static void puts(const char *); + +void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error\n"); + if (free_mem_ptr <= 0) error("Memory error\n"); + + while(1) { + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + /* + * The part of the compressed kernel which has already been expanded + * is no longer needed. Therefore we can reuse it for malloc. + * With bigger kernels, this is necessary. + */ + + if (free_mem_ptr < (long)&end) { + if (free_mem_ptr > (long)&input_data[input_ptr]) + error("\nOut of memory\n"); + + return p; + } + if (free_mem_ptr < 0x90000) + return p; + puts("large kernel, low 1M tight..."); + free_mem_ptr = (long)input_data; + } +} + +void free(void *where) +{ /* Don't care */ +} + +static void scroll() +{ + int i; + + memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 ); + for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 ) + vidmem[i] = ' '; +} + +static void puts(const char *s) +{ + int x,y; + char c; + + if (graphmode) + { + /* + * No support for graphic console. We'd need a font + * and that would render the compression somewhat senseless... + */ + return; + } + + x = SCREEN_INFO.orig_x; + y = SCREEN_INFO.orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + } + + SCREEN_INFO.orig_x = x; + SCREEN_INFO.orig_y = y; +} + +__ptr_t memset(__ptr_t s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i<n;i++) ss[i] = c; +} + +__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src, + size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++) d[i] = s[i]; +} + +extern ulg crc_32_tab[]; /* crc table, defined below */ + +/* =========================================================================== + * Run a set of bytes through the crc shift register. If s is a NULL + * pointer, then initialize the crc shift register contents instead. + * Return the current crc in either case. + */ +ulg updcrc(s, n) + uch *s; /* pointer to bytes to pump through */ + unsigned n; /* number of bytes in s[] */ +{ + register ulg c; /* temporary variable */ + + static ulg crc = (ulg)0xffffffffL; /* shift register contents */ + + if (s == NULL) { + c = 0xffffffffL; + } else { + c = crc; + while (n--) { + c = crc_32_tab[((int)c ^ (*s++)) & 0xff] ^ (c >> 8); + } + } + crc = c; + return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */ +} + +/* =========================================================================== + * Clear input and output buffers + */ +void clear_bufs() +{ + outcnt = 0; + insize = inptr = 0; + bytes_in = bytes_out = 0L; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +int fill_inbuf() +{ + int len, i; + + /* Read as much as possible */ + insize = 0; + do { + len = INBUFSIZ-insize; + if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1; + if (len == 0 || len == EOF) break; + + for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i]; + insize += len; + input_ptr += len; + } while (insize < INBUFSIZ); + + if (insize == 0) { + error("unable to fill buffer\n"); + } + bytes_in += (ulg)insize; + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +void flush_window() +{ + if (outcnt == 0) return; + updcrc(window, outcnt); + + memcpy(&output_data[output_ptr], (char *)window, outcnt); + + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +/* + * Code to compute the CRC-32 table. Borrowed from + * gzip-1.0.3/makecrc.c. + */ + +ulg crc_32_tab[256]; + +void +makecrc(void) +{ +/* Not copyrighted 1990 Mark Adler */ + + unsigned long c; /* crc shift register */ + unsigned long e; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial */ + e = 0; + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + crc_32_tab[0] = 0; + + for (i = 1; i < 256; i++) + { + c = 0; + for (k = i | 256; k != 1; k >>= 1) + { + c = c & 1 ? (c >> 1) ^ e : c >> 1; + if (k & 1) + c ^= e; + } + crc_32_tab[i] = c; + } +} + +void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +#define STACK_SIZE 4096UL + +long user_stack [STACK_SIZE]; + +void decompress_kernel(void) +{ + if (boot_info.machtype == MACH_MIPS_MAGNUM_4000) + { + /* + * We don't have a font for graphic console so this means silence... + */ + graphmode = 1; + } + else if (boot_info.machtype = MACH_ACER_PICA_61) + { + vidmem = boot_info.vram_base; + vidmem = vidmem + 0x8000; + } + else + { + if (SCREEN_INFO.orig_video_mode == 7) + vidmem = SLOTSPACE + 0xb0000; + else + vidmem = SLOTSPACE + 0xb8000; + } + + lines = SCREEN_INFO.orig_video_lines; + cols = SCREEN_INFO.orig_video_cols; + + if (EXT_MEM_K < 1024) error("<2M of mem\n"); + + output_data = (char *)0x100000; /* Points to 1M */ + output_ptr = 0; + + exit_code = 0; + test = 0; + input_ptr = 0; + part_nb = 0; + + clear_bufs(); + makecrc(); + + puts("Uncompressing Linux..."); + + method = get_method(0); + + work(0, 0); + + puts("done.\n"); + + puts("Now booting the kernel\n"); +} + +/* ======================================================================== + * Check the magic number of the input file and update ofname if an + * original name was given and to_stdout is not set. + * Return the compression method, -1 for error, -2 for warning. + * Set inptr to the offset of the next byte to be processed. + * This function may be called repeatedly for an input file consisting + * of several contiguous gzip'ed members. + * IN assertions: there is at least one remaining compressed member. + * If the member is a zip file, it must be the only one. + */ +local int get_method(int in) /* input file descriptor */ +{ + uch flags; + char magic[2]; /* magic header */ + + magic[0] = (char)get_byte(); + magic[1] = (char)get_byte(); + + method = -1; /* unknown yet */ + part_nb++; /* number of parts in gzip file */ + last_member = 0; + /* assume multiple members in gzip file except for record oriented I/O */ + + if (memcmp(magic, GZIP_MAGIC, 2) == 0 + || memcmp(magic, OLD_GZIP_MAGIC, 2) == 0) { + + work = unzip; + method = (int)get_byte(); + flags = (uch)get_byte(); + if ((flags & ENCRYPTED) != 0) + error("Input is encrypted\n"); + if ((flags & CONTINUATION) != 0) + error("Multi part input\n"); + if ((flags & RESERVED) != 0) { + error("Input has invalid flags\n"); + exit_code = ERROR; + if (force <= 1) return -1; + } + (ulg)get_byte(); /* Get timestamp */ + ((ulg)get_byte()) << 8; + ((ulg)get_byte()) << 16; + ((ulg)get_byte()) << 24; + + (void)get_byte(); /* Ignore extra flags for the moment */ + (void)get_byte(); /* Ignore OS type for the moment */ + + if ((flags & EXTRA_FIELD) != 0) { + unsigned len = (unsigned)get_byte(); + len |= ((unsigned)get_byte())<<8; + while (len--) (void)get_byte(); + } + + /* Get original file name if it was truncated */ + if ((flags & ORIG_NAME) != 0) { + if (to_stdout || part_nb > 1) { + /* Discard the old name */ + while (get_byte() != 0) /* null */ ; + } else { + } /* to_stdout */ + } /* orig_name */ + + /* Discard file comment if any */ + if ((flags & COMMENT) != 0) { + while (get_byte() != 0) /* null */ ; + } + } else + error("unknown compression method"); + return method; +} diff --git a/arch/mips/boot/compressed/piggyback.c b/arch/mips/boot/compressed/piggyback.c new file mode 100644 index 000000000..40284118b --- /dev/null +++ b/arch/mips/boot/compressed/piggyback.c @@ -0,0 +1,81 @@ +/* + * linux/zBoot/piggyback.c + * + * (C) 1993 Hannu Savolainen + */ + +/* + * This program reads the compressed system image from stdin and + * encapsulates it into an object file written to the stdout. + */ + +#include <stdio.h> +#include <unistd.h> +#include <a.out.h> + +int main(int argc, char *argv[]) +{ + int c, n=0, len=0; + char tmp_buf[512*1024]; + + struct exec obj = {0x00640107}; /* object header */ + char string_names[] = {"_input_data\0_input_len\0"}; + + struct nlist var_names[2] = /* Symbol table */ + { + { /* _input_data */ + (char *)4, 7, 0, 0, 0 + }, + { /* _input_len */ + (char *)16, 7, 0, 0, 0 + } + }; + + + len = 0; + while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0) + len += n; + + if (n==-1) + { + perror("stdin"); + exit(-1); + } + + if (len >= sizeof(tmp_buf)) + { + fprintf(stderr, "%s: Input too large\n", argv[0]); + exit(-1); + } + + fprintf(stderr, "Compressed size %d.\n", len); + +/* + * Output object header + */ + obj.a_data = len + sizeof(long); + obj.a_syms = sizeof(var_names); + write(1, (char *)&obj, sizeof(obj)); + +/* + * Output data segment (compressed system & len) + */ + write(1, tmp_buf, len); + write(1, (char *)&len, sizeof(len)); + +/* + * Output symbol table + */ + var_names[1].n_value = len; + write(1, (char *)&var_names, sizeof(var_names)); + +/* + * Output string table + */ + len = sizeof(string_names) + sizeof(len); + write(1, (char *)&len, sizeof(len)); + write(1, string_names, sizeof(string_names)); + + exit(0); + +} diff --git a/arch/mips/boot/compressed/unzip.c b/arch/mips/boot/compressed/unzip.c new file mode 100644 index 000000000..d4a6617cd --- /dev/null +++ b/arch/mips/boot/compressed/unzip.c @@ -0,0 +1,180 @@ +/* unzip.c -- decompress files in gzip or pkzip format. + * Copyright (C) 1992-1993 Jean-loup Gailly + * + * Adapted for Linux booting by Hannu Savolainen 1993 + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + * + * The code in this file is derived from the file funzip.c written + * and put in the public domain by Mark Adler. + */ + +/* + This version can extract files in gzip or pkzip format. + For the latter, only the first entry is extracted, and it has to be + either deflated or stored. + */ + +#ifndef lint +static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $"; +#endif + +#include "gzip.h" +#include "crypt.h" + +#include <stdio.h> + +/* PKZIP header definitions */ +#define LOCSIG 0x04034b50L /* four-byte lead-in (lsb first) */ +#define LOCFLG 6 /* offset of bit flag */ +#define CRPFLG 1 /* bit for encrypted entry */ +#define EXTFLG 8 /* bit for extended local header */ +#define LOCHOW 8 /* offset of compression method */ +#define LOCTIM 10 /* file mod time (for decryption) */ +#define LOCCRC 14 /* offset of crc */ +#define LOCSIZ 18 /* offset of compressed size */ +#define LOCLEN 22 /* offset of uncompressed length */ +#define LOCFIL 26 /* offset of file name field length */ +#define LOCEXT 28 /* offset of extra field length */ +#define LOCHDR 30 /* size of local header, including sig */ +#define EXTHDR 16 /* size of extended local header, inc sig */ + + +/* Globals */ + +int decrypt; /* flag to turn on decryption */ +char *key; /* not used--needed to link crypt.c */ +int pkzip = 0; /* set for a pkzip file */ +int extended = 0; /* set if extended local header */ + +/* =========================================================================== + * Check zip file and advance inptr to the start of the compressed data. + * Get ofname from the local header if necessary. + */ +int check_zipfile(in) + int in; /* input file descriptors */ +{ + uch *h = inbuf + inptr; /* first local header */ + + /* ifd = in; */ + + /* Check validity of local header, and skip name and extra fields */ + inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT); + + if (inptr > insize || LG(h) != LOCSIG) { + error("input not a zip"); + } + method = h[LOCHOW]; + if (method != STORED && method != DEFLATED) { + error("first entry not deflated or stored--can't extract"); + } + + /* If entry encrypted, decrypt and validate encryption header */ + if ((decrypt = h[LOCFLG] & CRPFLG) != 0) { + error("encrypted file\n"); + exit_code = ERROR; + return -1; + } + + /* Save flags for unzip() */ + extended = (h[LOCFLG] & EXTFLG) != 0; + pkzip = 1; + + /* Get ofname and time stamp from local header (to be done) */ + return 0; +} + +/* =========================================================================== + * Unzip in to out. This routine works on both gzip and pkzip files. + * + * IN assertions: the buffer inbuf contains already the beginning of + * the compressed data, from offsets inptr to insize-1 included. + * The magic header has already been checked. The output buffer is cleared. + */ +void unzip(in, out) + int in, out; /* input and output file descriptors */ +{ + ulg orig_crc = 0; /* original crc */ + ulg orig_len = 0; /* original uncompressed length */ + int n; + uch buf[EXTHDR]; /* extended local header */ + + /* ifd = in; + ofd = out; */ + + updcrc(NULL, 0); /* initialize crc */ + + if (pkzip && !extended) { /* crc and length at the end otherwise */ + orig_crc = LG(inbuf + LOCCRC); + orig_len = LG(inbuf + LOCLEN); + } + + /* Decompress */ + if (method == DEFLATED) { + + int res = inflate(); + + if (res == 3) { + error("out of memory"); + } else if (res != 0) { + error("invalid compressed format"); + } + + } else if (pkzip && method == STORED) { + + register ulg n = LG(inbuf + LOCLEN); + + if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) { + + error("length mismatch"); + } + while (n--) { + uch c = (uch)get_byte(); +#ifdef CRYPT + if (decrypt) zdecode(c); +#endif + if (!test) put_char(c); + } + } else { + error("internal error, invalid method"); + } + + /* Get the crc and original length */ + if (!pkzip) { + /* crc32 (see algorithm.doc) + * uncompressed input size modulo 2^32 + */ + for (n = 0; n < 8; n++) { + buf[n] = (uch)get_byte(); /* may cause an error if EOF */ + } + orig_crc = LG(buf); + orig_len = LG(buf+4); + + } else if (extended) { /* If extended header, check it */ + /* signature - 4bytes: 0x50 0x4b 0x07 0x08 + * CRC-32 value + * compressed size 4-bytes + * uncompressed size 4-bytes + */ + for (n = 0; n < EXTHDR; n++) { + buf[n] = (uch)get_byte(); /* may cause an error if EOF */ + } + orig_crc = LG(buf+4); + orig_len = LG(buf+12); + } + + /* Validate decompression */ + if (orig_crc != updcrc(outbuf, 0)) { + error("crc error"); + } + if (orig_len != bytes_out) { + error("length error"); + } + + /* Check if there are more entries in a pkzip file */ + if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) { + error("zip file has more than one entry"); + } + extended = pkzip = 0; /* for next file */ +} diff --git a/arch/mips/boot/compressed/xtract.c b/arch/mips/boot/compressed/xtract.c new file mode 100644 index 000000000..91de49ca7 --- /dev/null +++ b/arch/mips/boot/compressed/xtract.c @@ -0,0 +1,86 @@ +/* + * linux/zBoot/xtract.c + * + * Copyright (C) 1993 Hannu Savolainen + * + * Extracts the system image and writes it to the stdout. + * based on tools/build.c by Linus Torvalds + */ + +#include <stdio.h> /* fprintf */ +#include <string.h> +#include <stdlib.h> /* contains exit */ +#include <sys/types.h> /* unistd.h needs this */ +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> /* contains read/write */ +#include <fcntl.h> +#include <a.out.h> + +#define N_MAGIC_OFFSET 1024 + +static int GCC_HEADER = sizeof(struct exec); + +#define STRINGIFY(x) #x + +void die(char * str) +{ + fprintf(stderr,"%s\n",str); + exit(1); +} + +void usage(void) +{ + die("Usage: xtract system [ | gzip | piggyback > piggy.s]"); +} + +int main(int argc, char ** argv) +{ + int i,c,id, sz; + char buf[1024]; + char major_root, minor_root; + struct stat sb; + + struct exec *ex = (struct exec *)buf; + + if (argc != 2) + usage(); + + if ((id=open(argv[1],O_RDONLY,0))<0) + die("Unable to open 'system'"); + if (read(id,buf,GCC_HEADER) != GCC_HEADER) + die("Unable to read header of 'system'"); + if (N_MAGIC(*ex) == ZMAGIC) { + GCC_HEADER = N_MAGIC_OFFSET; + lseek(id, GCC_HEADER, SEEK_SET); + } else if (N_MAGIC(*ex) != QMAGIC) + die("Non-GCC header of 'system'"); + + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; /* +4 to get the same result than tools/build */ + + fprintf(stderr, "System size is %d\n", sz); + + while (sz) + { + int l, n; + + l = sz; + if (l > sizeof(buf)) l = sizeof(buf); + + if ((n=read(id, buf, l)) !=l) + { + if (n == -1) + perror(argv[1]); + else + fprintf(stderr, "Unexpected EOF\n"); + + die("Can't read system"); + } + + write(1, buf, l); + sz -= l; + } + + close(id); + return(0); +} diff --git a/arch/mips/boot/head.S b/arch/mips/boot/head.S deleted file mode 100644 index 73e9d2cf9..000000000 --- a/arch/mips/boot/head.S +++ /dev/null @@ -1,387 +0,0 @@ -/* - * mips/head.S - * - * Copyright (C) 1994 Ralf Baechle - * - * Head.S contains the MIPS 32-bit startup code. - */ - -/* - * prevent prototypes from being imported - */ -#define __ASSEMBLY__ - -#include <asm/segment.h> -#include <asm/cachectl.h> -#include <asm/mipsregs.h> -#include <asm/mipsconfig.h> -#include <asm/stackframe.h> -#include <asm/regdef.h> -#include <linux/tasks.h> - - - .globl _empty_bad_page - .globl _empty_bad_page_table - .globl _invalid_pg_table - .globl _empty_zero_page - .globl _tmp_floppy_area - .globl _floppy_track_buffer - .globl _swapper_pg_dir - - .set noreorder - - .text -/* - * This is space for the interrupt handlers. - * They are located at virtual address 0x80000000 (physical 0x0) - */ - /* - * TLB refill, EXL == 0 - */ -except_vec0: - .set noreorder - .set noat - /* - * This TLB-refill handler is supposed never to cause - * another tlb-refill exception. Unmapped pages will - * cause another type of exception. - */ - dmfc0 k0,CP0_CONTEXT - dsrl k0,k0,1 - lwu k0,(k1) - lwu k1,4(k1) - dmtc0 k0,CP0_ENTRYLO0 - dmtc0 k0,CP0_ENTRYLO1 - tlbwr - eret - - /* - * XTLB refill, EXL == 0 (X == 64-bit TLB) - */ - .org except_vec0+0x80 -except_vec1: - /* - * Not used yet... - */ - eret - - /* - * Cache Error - */ - .org except_vec1+0x80 -except_vec2: - /* - * Not used yet... - */ - eret - - /* - * General exception vector. - */ - .org except_vec2+0x80 -except_vec3: - SAVE_ALL - mfc0 t0,CP0_STATUS - ori t0,t0,0x1f - xori t0,t0,0x1f - mtc0 t0,CP0_STATUS - .set at - la k0,_exception_handlers - mfc0 k1,CP0_CAUSE - andi k1,k1,0x7c - addu k0,k0,k1 - lw k0,(k0) - FILL_LDS - jr k0 - nop - - -/******************************************************************************/ - - /* - * The following data is expected to be at certain absolute - * addresses, which are hardwired in - * include/asm-mips/mipsconfig.h - * If the following offset is to short, the assembler will - * break with an assertion failure. You then will have to - * increase it and to fix the address in - * include/asm-mips/mipsconfig.h - */ - - .org except_vec3+0x100 - .globl _kernelsp -_kernelsp: .word 0 - -kernel_entry: - -/* - * Flush the TLB - */ - dmtc0 zero,CP0_ENTRYHI - dmtc0 zero,CP0_ENTRYLO0 - dmtc0 zero,CP0_ENTRYLO1 - li t0,NUMBER_OF_TLB_ENTRIES-1 -1: mtc0 t0,CP0_INDEX - tlbwi - bne zero,t0,1b - subu t0,t0,1 - -/* - * Initialize memory management. - * Wire mapping for port i/o space 0xe0000000 -> 0x9000000900000000 - */ - li t0,3 - mtc0 t0,CP0_WIRED - li t0,PM_64K - mtc0 t0,CP0_PAGEMASK - la t3,map0 - ld t1,0(t3) - ld t2,8(t3) - mtc0 zero,CP0_INDEX - dmtc0 t1,CP0_ENTRYHI - dmtc0 t2,CP0_ENTRYLO0 - dmtc0 zero,CP0_ENTRYLO1 /* Invalid page */ - tlbwi - li t0,PM_1M - mtc0 t0,CP0_PAGEMASK - ld t1,16(t3) - ld t2,24(t3) - li t0,1 - mtc0 t0,CP0_INDEX - dmtc0 t1,CP0_ENTRYHI - dmtc0 t2,CP0_ENTRYLO0 - tlbwi - ld t1,32(t3) - ld t2,40(t3) - li t0,2 - mtc0 t0,CP0_INDEX - dmtc0 t1,CP0_ENTRYHI - dmtc0 t2,CP0_ENTRYLO0 - tlbwi - -/* - * We always use 4k pages. Therefore the PageMask register - * is expected to be setup for 4k pages. - */ - li t0,PM_4K - mtc0 t0,CP0_PAGEMASK - -/* - * Clear BSS first so that there are no surprises... - */ - la t0,__edata - la t1,__end - sw zero,(t0) -1: addiu t0,t0,4 - bnel t0,t1,1b - sw zero,(t0) - -/* - * Copy bootup parameters out of the way. First 2kB of - * _empty_zero_page is for boot parameters, second 2kB - * is for the command line. - */ -#if 0 - movl $0x90000,%esi - movl $_empty_zero_page,%edi - movl $512,%ecx - cld - rep - movsl - xorl %eax,%eax - movl $512,%ecx - rep - stosl - cmpw $(CL_MAGIC),CL_MAGIC_ADDR - jne 1f - movl $_empty_zero_page+2048,%edi - movzwl CL_OFFSET,%esi - addl $(CL_BASE_ADDR),%esi - movl $2048,%ecx - rep - movsb -#endif - - /* - * Preliminary stack... - */ - la sp,0x80700000 - sw sp,_kernelsp -6: - jal _start_kernel - nop - j 6b # main should never return here, but - # just in case, we know what happens. - -#if 0 -/* This is the default interrupt "handler" :-) */ -int_msg: - .asciz "Unknown interrupt\n" -.align 2 -ignore_int: - cld - pushl %eax - pushl %ecx - pushl %edx - push %ds - push %es - push %fs - movl $(KERNEL_DS),%eax - mov %ax,%ds - mov %ax,%es - mov %ax,%fs - pushl $int_msg - call _printk - popl %eax - pop %fs - pop %es - pop %ds - popl %edx - popl %ecx - popl %eax - iret -#endif - -#define CACHELINES 512 /* number of cachelines */ - -/* - * Flush instruction/data caches - * - * Parameters: a0 - starting address to flush - * a1 - size of area to be flushed - * a2 - which caches to be flushed - * - * FIXME: - ignores parameters - * - doesn't know about second level caches - */ - - .set noreorder - .globl _cacheflush - .text -_cacheflush: - /* - * Flush the instruction cache - */ - lui t0,0x8000 - li t1,CACHELINES-1 -1: cache 0,0(t0) - cache 0,32(t0) - cache 0,64(t0) - cache 0,96(t0) - cache 0,128(t0) - cache 0,160(t0) - cache 0,192(t0) - cache 0,224(t0) - cache 0,256(t0) - cache 0,288(t0) - cache 0,320(t0) - cache 0,352(t0) - cache 0,384(t0) - cache 0,416(t0) - cache 0,448(t0) - cache 0,480(t0) - addiu t0,t0,512 - bne zero,t1,1b - subu t1,t1,1 - /* - * Flush the data cache - */ - lui t0,0x8000 - li t1,CACHELINES-1 -1: cache 1,0(t0) - cache 1,32(t0) - cache 1,64(t0) - cache 1,96(t0) - cache 1,128(t0) - cache 1,160(t0) - cache 1,192(t0) - cache 1,224(t0) - cache 1,256(t0) - cache 1,288(t0) - cache 1,320(t0) - cache 1,352(t0) - cache 1,384(t0) - cache 1,416(t0) - cache 1,448(t0) - cache 1,480(t0) - addiu t0,t0,512 - bne zero,t1,1b - subu t1,t1,1 - - j ra - nop - - .globl _beep -_beep: lbu t0,0xe0000061 - xori t0,t0,3 - sb t0,0xe0000061 - jr ra - nop - -/* - * Instead of Intel's strage and unportable segment descriptor magic - * we difference user and kernel space by their address. - * Kernel space (== physical memory) is mapped at 0x80000000, - * User space is mapped at 0x0. - */ - - .data - - .globl _segment_fs - /* - * Inital wired mappings - */ -map0: .quad 0xc00000ffe0000000,0x24000017 - .quad 0xc00000ffe1000000,0x04000017 - .quad 0xc00000ffe2000000,0x04020017 - -/* - * page 0 is made non-existent, so that kernel NULL pointer references get - * caught. Thus the swapper page directory has been moved to 0x1000 - * - * XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte, - * with the introduction of the compressed boot code. Theoretically, - * the original design of overlaying the startup code with the swapper - * page directory is still possible --- it would reduce the size of the kernel - * by 2-3k. This would be a good thing to do at some point..... - */ - .text - - .org 0x1000 -_swapper_pg_dir: -/* - * The page tables are initialized to only 4MB here - the final page - * tables are set up later depending on memory size. - */ - .org 0x2000 -_pg0: - - .org 0x3000 -_empty_bad_page: - - .org 0x4000 -_empty_bad_page_table: - - .org 0x5000 -_invalid_pg_table: - - .org 0x6000 -_empty_zero_page: - - .org 0x7000 - -/* - * tmp_floppy_area is used by the floppy-driver when DMA cannot - * reach to a buffer-block. It needs to be aligned, so that it isn't - * on a 64kB border. - */ -_tmp_floppy_area: .fill 1024,1,0 -/* - * floppy_track_buffer is used to buffer one track of floppy data: it - * has to be separate from the tmp_floppy area, as otherwise a single- - * sector read/write can mess it up. It can contain one full cylinder (sic) of - * data (36*2*512 bytes). - */ -_floppy_track_buffer: .fill 512*2*36,1,0 - -_segment_fs: .word KERNEL_DS diff --git a/arch/mips/boot/loader.h b/arch/mips/boot/loader.h new file mode 100644 index 000000000..610111b9a --- /dev/null +++ b/arch/mips/boot/loader.h @@ -0,0 +1,24 @@ +/* + * Defines for Linux/MIPS executable loaders + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995 by Ralf Baechle + */ +#ifndef __STAND_LOADER +#define __STAND_LOADER + +struct loader { + struct nlist *(*ld_get_nlist)(char *symbol); + char *(*ld_isymbol)(char *); + u_long (*ld_get_kbase)(void); + u_long (*ld_get_ksize)(void); + int (*ld_load_kernel)(void *mem); + void (*ld_print_stats)(void); + int (*ld_open_kernel)(char *kernel); + void (*ld_close_kernel)(void); +}; + +#endif /* __STAND_LOADER */ diff --git a/arch/mips/boot/milo.c b/arch/mips/boot/milo.c new file mode 100644 index 000000000..a8a25dd91 --- /dev/null +++ b/arch/mips/boot/milo.c @@ -0,0 +1,316 @@ +/* + * Load and launch Linux/MIPS kernel. + * + * Copyright (C) 1994, 1995 by Waldorf Electronics, + * written by Ralf Baechle and Andreas Busse + * + * Loosly based on bootstrap.c for Linux/68k, + * Copyright (C) 1993 by Hamish Macdonald, Greg Harp + * + * This file is subject to the terms and conditions of the + * GNU General Public License. See the file COPYING in the + * main directory of this archive for more details. + */ +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/file.h> +#include <sys/types.h> + +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/a.out.h> + +/* + * Prototypes + */ +void usage(void); + +/* + * Defaults, may be overiden by option or environment. + */ +static char *kernel_name = KERNEL_NAME; +static char *ramdisk_name = NULL; +int option_debuglevel = 0; +int option_verbose = 1; +int behaviour = BEHAVE_MILO; + +extern char *optarg; + +extern volatile void launch(char *kptr, char *rdptr, + u_long kernel_size, u_long rd_size); + +static char *kptr; /* kernel will be loaded there */ +static char *rdptr; /* ramdisk will be loaded there */ + +u_long loadaddr = LOADADDR; /* mallocinit() needs that */ +u_long kernel_base = KERNELBASE; /* whereever that is... */ +u_long start_mem; /* always true on an ARC system */ +u_long mem_size; +u_long rd_size; +u_long kernel_entry; +u_long kernel_size; +u_long kernel_bss_end; + +struct bootinfo bi; /* Linux boot info */ +struct screen_info si; /* Linux screen info */ +struct DisplayInfo *di; /* ARC display info */ + + +/* + * For now we just use the aout loader + */ +extern struct loader loader_aout; +struct loader *kld = &loader_aout; + +/* + * main() + */ +int main(int argc, char *argv[]) +{ + int ch; + + /* + * Print the greet message + */ + printf("Linux/MIPS ARC Standalone Shell "); + printf("V" STR(VERSION) "\r\n"); + printf("Copyright (C) Waldorf Electronics and others 1994, 1995\r\n\r\n"); + + /* + * Analyse arguments + */ + if(argc > 1) + { + while ((ch = getopt(argc, argv, "dik:r:v")) != EOF) + switch (ch) + { + case 'd': + option_debuglevel++; + option_verbose = 1; + break; + case 'i': + interactive = 1; + break; + case 'k': + kernel_name = optarg; + break; + case 'r': + ramdisk_name = optarg; + break; + case 'v': + option_verbose = 1; + break; + case '?': + default: + usage(); + } + } +} + +/* + * Do the actual boot + */ +int do_boot(char *kernel_name, char *ramdisk_name) +{ + int kfd, rfd = -1, i; + u_long memreq; + struct nlist *nl; + u_long kbi_offset, ksi_offset; + + /* + * Verify that there is enough RAM + */ + mem_size = bi.memupper - bi.memlower; + if (mem_size < 0x800000) + { + fprintf(stderr, + "Insufficient Memory to load Linux/MIPS, aborting\n\r"); + return(5); + } + + if (behaviour == BEHAVE_ARCDB) + { + printf("%s: kernel file is `%s'\r\n", NAMEOF_ARCDB, kernel_name); + if (ramdisk_name) + printf("%s: ramdisk file is `%s'\r\n", NAMEOF_ARCDB, ramdisk_name); + } + + /* + * Open kernel and gather some data from the executable + */ + if (kld->ld_open_kernel(kernel_name) < 0) + { + fprintf(stderr, "Error opening kernel file %s.\n\r", kernel_name); + return 5; + } + kernel_base = kld->ld_get_kbase(); + kernel_size = kld->ld_get_ksize(); + + bi.ramdisk_size = 0; /* default: no ramdisk */ + if (ramdisk_name) + { + if ((rfd = open (ramdisk_name, O_RDONLY)) == -1) + { + fprintf (stderr, + "Unable to open ramdisk file %s\n\r", ramdisk_name); + } + else + { + /* + * record ramdisk size + */ + bi.ramdisk_size = (lseek (rfd, 0, L_XTND) + 1023) >> 10; + + rd_size = lseek (rfd, 0, L_XTND); + if (rd_size & ((1<<10)-1)) + { + /* + * Most probably the file is no image at all or has been + * corrupted, so print a warning message. + */ + printf("Warning: Ramdisk size is not multiple of 1024 bytes.\r\n"); + } + bi.ramdisk_size = rd_size >> 10; + } + } + bi.ramdisk_base = (u_long)start_mem + mem_size - rd_size; + + /* + * find offset to boot_info structure + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("boot_info")))) + { + perror("get_nlist1"); + return 1; + } + else + { + kbi_offset = nl->n_value - kernel_base; + free(nl); + } + + /* + * Find offset to screen_info structure + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("screen_info")))) + { + perror("get_nlist2"); + return 1; + } + else + { + ksi_offset = nl->n_value - kernel_base; + free(nl); + } + + /* + * Find kernel entry point + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("kernel_entry")))) + { + perror("get_nlist3"); + return 1; + } + else + { + kernel_entry = nl->n_value; + free(nl); + } + + /* + * End of bss segment - ramdisk will be placed there + */ + if (!(nl = kld->ld_get_nlist (kld->ld_isymbol("_end")))) + { + perror("get_nlist3"); + return 1; + } + else + { + kernel_bss_end = nl->n_value; + free(nl); + } + + /* + * allocate buffers for kernel and ramdisk + */ + if (!(kptr = (char *) malloc(kernel_size))) + { + fprintf (stderr, "Unable to allocate %d bytes of memory\n\r", memreq); + return 1; + } + memset (kptr, 0, kernel_size); + + if (rd_size) + { + if (!(rdptr = (char *) malloc(rd_size))) + { + fprintf (stderr, + "Unable to allocate %d bytes of memory\n\r", memreq); + return 1; + } + memset (rdptr, 0, rd_size); + } + + /* + * Should check whether kernel & RAMdisk moving doesn't overwrite + * the bootstraper during startup. + */ + if (option_verbose) + { + /* + * The following text should be printed by the loader module + */ + printf ("\r\n"); + kld->ld_print_stats(); + printf ("Kernel entry at 0x%08lx\n\r", kernel_entry); + } + + /* + * Now do the real loading + */ + if (kld->ld_load_kernel(kptr) < 0) + { + fprintf (stderr, "Failed to load kernel executable\r\n"); + free(kptr); + exit (EXIT_FAILURE); + } + kld->ld_close_kernel(); + + /* + * copy the boot and screen info structures to the kernel image, + * then execute the copy-and-go code + */ + memcpy ((void *)(kptr + kbi_offset), &bi, sizeof(bi)); + memcpy ((void *)(kptr + ksi_offset), &si, sizeof(si)); + + /* + * Write the manipulated kernel back + */ + + /* + * Success... + */ + return 0; +} + +/* + * usage() + * + * Options: + * -d - enable debugging (default is no debugging) + * -i - interactive (default is noninteractive) + * -k - kernel executable (default multi(0)disk(0)fdisk(0)\VMLINUX) + * -r - ramdisk image (default is no ramdisk) + * -v - verbose output + */ +void usage(void) +{ + fprintf (stderr, "Usage:\r\n" + "\tmilo [-d] [-i] [-k kernel_executable] [-r ramdisk_file] [-v]" + " [option...]\r\n"); + exit (EXIT_FAILURE); +} diff --git a/arch/mips/config.in b/arch/mips/config.in index 24f0c13f9..6ecc5e698 100644 --- a/arch/mips/config.in +++ b/arch/mips/config.in @@ -3,28 +3,84 @@ # see the Configure script. # +comment 'Machine setup' + +bool 'Support for Acer PICA 1 chipset' CONFIG_ACER_PICA_61 y +bool 'Support for DECstation' CONFIG_DECSTATION n +bool 'Support for Deskstation RPC44' CONFIG_DESKSTATION_RPC44 n +bool 'Support for Deskstation Tyne' CONFIG_DESKSTATION_TYNE n +bool 'Support for Mips Magnum 3000' CONFIG_MIPS_MAGNUM_3000 n +bool 'Support for Mips Magnum 4000' CONFIG_MIPS_MAGNUM_4000 y +if [ "$CONFIG_ACER_PICA_61" = "y" -o \ + "$CONFIG_MIPS_MAGNUM_4000" = "y" -o \ + "$CONFIG_OLIVETTI_M700" = "y" ]; then + echo "#define CONFIG_MIPS_JAZZ" >> $CONFIG_H + echo "CONFIG_MIPS_JAZZ=y" >> $CONFIG + CONFIG_MIPS_JAZZ=y +fi + +comment 'CPU selection' + +bool 'Generate code for R3000' CONFIG_CPU_R3000 n +#bool 'Generate code for R6000' CONFIG_CPU_R6000 n +bool 'Generate code for R4x00' CONFIG_CPU_R4X00 y +bool 'Generate code for R4600' CONFIG_CPU_R4600 n +bool 'Generate code for R8000' CONFIG_CPU_R8000 n +bool 'Generate code for R10000' CONFIG_CPU_R10000 n +bool 'Generate little endian code' CONFIG_CPU_LITTLE_ENDIAN y +bool 'Compile the kernel into the ELF object format' CONFIG_MIPS_ELF n +if [ "$CONFIG_MIPS_ELF" = "y" ]; then + bool 'Is your normal Linux/MIPS compiler the ELF compiler' CONFIG_ELF_COMPILER n +fi + comment 'General setup' -bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD n -bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n +bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y +bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 n +if [ "$CONFIG_ST506" = "y" ]; then + comment 'Please see block/drivers/README.ide for help/info on IDE drives' + bool ' Use old (reliable) disk-only driver for primary i/f' CONFIG_BLK_DEV_HD y + if [ "$CONFIG_BLK_DEV_HD" = "y" ]; then + bool ' Include new IDE driver for secondary i/f support' CONFIG_BLK_DEV_IDE n + else + bool ' Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE n + fi + if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then + bool ' Include support for IDE CDROM (ATAPI)' CONFIG_BLK_DEV_IDECD n + fi +fi + bool 'XT harddisk support' CONFIG_BLK_DEV_XD n -bool 'Networking support' CONFIG_NET n +bool 'Networking support' CONFIG_NET y bool 'System V IPC' CONFIG_SYSVIPC n bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF n if [ "$CONFIG_NET" = "y" ]; then comment 'Networking options' -bool 'TCP/IP networking' CONFIG_INET n -if [ "$CONFIG_INET" "=" "y" ]; then -bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD y +bool 'TCP/IP networking' CONFIG_INET y +if [ "$CONFIG_INET" = "y" ]; then +bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD n +bool 'IP: multicasting' CONFIG_IP_MULTICAST n +bool 'IP: firewalling' CONFIG_IP_FIREWALL n +bool 'IP: accounting' CONFIG_IP_ACCT n +bool 'IP: tunneling' CONFIG_NET_IPIP n +if [ "$CONFIG_IP_FORWARD" = "y" -a "$CONFIG_IP_FIREWALL" = "y" ]; then + bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE y + bool 'IP: masquerading (ALPHA)' CONFIG_IP_MASQUERADE n +fi comment '(it is safe to leave these untouched)' -bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n -bool 'Reverse ARP' CONFIG_INET_RARP n -bool 'Assume subnets are local' CONFIG_INET_SNARL y -bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP n +bool 'IP: Reverse ARP' CONFIG_INET_RARP n +bool 'IP: Assume subnets are local' CONFIG_INET_SNARL y +bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +bool 'IP: Drop source routed frames' CONFIG_IP_NOSR y fi bool 'The IPX protocol' CONFIG_IPX n -#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n +bool 'Appletalk DDP' CONFIG_ATALK n +bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n +if [ "$CONFIG_AX25" = "y" ]; then + bool 'Amateur Radio NET/ROM' CONFIG_NETROM n +fi fi comment 'SCSI support' @@ -39,20 +95,28 @@ else comment 'SCSI support type (disk, tape, CDrom)' -bool 'Scsi disk support' CONFIG_BLK_DEV_SD y -bool 'Scsi tape support' CONFIG_CHR_DEV_ST y -bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR y -bool 'Scsi generic support' CONFIG_CHR_DEV_SG y +bool 'SCSI disk support' CONFIG_BLK_DEV_SD y +bool 'SCSI tape support' CONFIG_CHR_DEV_ST y +bool 'SCSI CDROM support' CONFIG_BLK_DEV_SR y +bool 'SCSI generic support' CONFIG_CHR_DEV_SG n + +comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' + +bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN n comment 'SCSI low-level drivers' -bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n -bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y -bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n +bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X y +bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n +bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n +bool 'EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support' CONFIG_SCSI_EATA_DMA n +bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n -bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +if [ "$CONFIG_PCI" = "y" ]; then + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +fi bool 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 n bool 'PAS16 SCSI support' CONFIG_SCSI_PAS16 n bool 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC n @@ -60,7 +124,7 @@ bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n -bool 'EISA EATA support' CONFIG_SCSI_EATA n +#bool 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n #bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n fi @@ -75,22 +139,30 @@ if [ "$CONFIG_NETDEVICES" = "n" ]; then comment 'Skipping network driver configuration options...' else -bool 'Dummy net driver support' CONFIG_DUMMY n +bool 'Dummy net driver support' CONFIG_DUMMY y bool 'SLIP (serial line) support' CONFIG_SLIP n if [ "$CONFIG_SLIP" = "y" ]; then - bool ' CSLIP compressed headers' SL_COMPRESSED y -# bool ' SLIP debugging on' SL_DUMP y + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y + bool ' 16 channels instead of 4' SL_SLIP_LOTS n fi bool 'PPP (point-to-point) support' CONFIG_PPP n +if [ "$CONFIG_PPP" = "y" ]; then + bool ' 16 channels instead of 4' CONFIG_PPP_LOTS n +fi +if [ "$CONFIG_AX25" = "y" ]; then + bool 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC y +fi bool 'PLIP (parallel port) support' CONFIG_PLIP n +bool 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n -bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC y +bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then - bool 'WD80*3 support' CONFIG_WD80x3 y + bool 'WD80*3 support' CONFIG_WD80x3 n bool 'SMC Ultra support' CONFIG_ULTRA n fi -bool '3COM cards' CONFIG_NET_VENDOR_3COM y +bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n +bool '3COM cards' CONFIG_NET_VENDOR_3COM n if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then bool '3c501 support' CONFIG_EL1 n bool '3c503 support' CONFIG_EL2 n @@ -102,32 +174,54 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then fi bool 'Other ISA cards' CONFIG_NET_ISA n if [ "$CONFIG_NET_ISA" = "y" ]; then - bool 'AT1500 and NE2100 (LANCE and PCnet-ISA) support' CONFIG_LANCE n - bool 'Cabletron E21xx support (not recommended)' CONFIG_E2100 n + bool 'Arcnet support' CONFIG_ARCNET n + bool 'Cabletron E21xx support' CONFIG_E2100 n bool 'DEPCA support' CONFIG_DEPCA n bool 'EtherWorks 3 support' CONFIG_EWRK3 n if [ "$CONFIG_NET_ALPHA" = "y" ]; then - bool 'EtherExpress support' CONFIG_EEXPRESS n bool 'AT1700 support' CONFIG_AT1700 n +# bool 'EtherExpressPro support' CONFIG_EEXPRESS_PRO n + bool 'EtherExpress support' CONFIG_EEXPRESS n + bool 'NI5210 support' CONFIG_NI52 n + bool 'NI6510 support' CONFIG_NI65 n + bool 'WaveLAN support' CONFIG_WAVELAN n fi - bool 'HP PCLAN support' CONFIG_HPLAN n - bool 'HP PCLAN PLUS support' CONFIG_HPLAN_PLUS n + bool 'HP PCLAN+ (27247B and 27252A) support' CONFIG_HPLAN_PLUS n + bool 'HP PCLAN (27245 and other 27xxx series) support' CONFIG_HPLAN n bool 'NE2000/NE1000 support' CONFIG_NE2000 y + if [ "$CONFIG_AX25" = "y" ]; then + bool 'Ottawa PI and PI/2 support' CONFIG_PI y + fi bool 'SK_G16 support' CONFIG_SK_G16 n fi -bool 'EISA and on board controllers' CONFIG_NET_EISA n +bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA n +if [ "$CONFIG_NET_EISA" = "y" ]; then if [ "$CONFIG_NET_ALPHA" = "y" ]; then bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n fi bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n -#bool 'NI52EE support' CONFIG_NI52 n -#bool 'NI65EE support' CONFIG_NI65 n +# bool 'DEC 21040 PCI support' CONFIG_DEC_ELCP n +# bool 'LPL T100V 100Mbs support' CONFIG_LPL_T100 n +# bool 'PCnet32 (32 bit VLB and PCI LANCE) support' CONFIG_PCNET32 n +# bool 'Zenith Z-Note support' CONFIG_ZNET n +fi + +if [ "$CONFIG_MIPS_JAZZ" = "y" ]; then + bool 'MIPS JAZZ onboard SONIC ethernet support' CONFIG_MIPS_JAZZ_SONIC y +fi + bool 'Pocket and portable adaptors' CONFIG_NET_POCKET n if [ "$CONFIG_NET_POCKET" = "y" ]; then + bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n bool 'D-Link DE600 pocket adaptor support' CONFIG_DE600 n bool 'D-Link DE620 pocket adaptor support' CONFIG_DE620 n - bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP n - bool 'Zenith Z-Note support' CONFIG_ZNET n +# bool 'Silicom pocket adaptor support' CONFIG_SILICOM_PEA n +# bool 'WaveLAN PCMCIA support' CONFIG_WaveLAN n +# bool '3 Com 3c589 PCMCIA support' CONFIG_3C589 n +fi +bool 'Token Ring driver support' CONFIG_TR n +if [ "$CONFIG_TR" = "y" ]; then + bool 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR y fi fi fi @@ -149,17 +243,18 @@ fi comment 'Filesystems' -bool 'Standard (minix) fs support' CONFIG_MINIX_FS y +bool 'Standard (minix) fs support' CONFIG_MINIX_FS n bool 'Extended fs support' CONFIG_EXT_FS n -bool 'Second extended fs support' CONFIG_EXT2_FS n +bool 'Second extended fs support' CONFIG_EXT2_FS y bool 'xiafs filesystem support' CONFIG_XIA_FS n bool 'msdos fs support' CONFIG_MSDOS_FS n if [ "$CONFIG_MSDOS_FS" = "y" ]; then -bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n +#bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n +comment 'Umsdos is not supported in 1.3.0: wait for 1.3.1' fi bool '/proc filesystem support' CONFIG_PROC_FS n if [ "$CONFIG_INET" = "y" ]; then -bool 'NFS filesystem support' CONFIG_NFS_FS y +bool 'NFS filesystem support' CONFIG_NFS_FS n fi if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" ]; then bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y @@ -171,7 +266,9 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n comment 'character devices' -bool 'Parallel printer support' CONFIG_PRINTER n +bool 'Parallel printer support' CONFIG_PRINTER y +bool 'Standard serial device support' CONFIG_SERIAL y +bool 'Cyclades async mux support' CONFIG_CYCLADES n bool 'Logitech busmouse support' CONFIG_BUSMOUSE n bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n if [ "$CONFIG_PSMOUSE" = "y" ]; then @@ -179,8 +276,7 @@ bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE n fi bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n -bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n - + bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n if [ "$CONFIG_QIC02_TAPE" = "y" ]; then bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF n @@ -207,8 +303,12 @@ bool 'Sound card support' CONFIG_SOUND n comment 'Kernel hacking' +bool 'Remote kernel debugging support' CONFIG_REMOTE_DEBUG n #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n bool 'Kernel profiling support' CONFIG_PROFILE n +if [ "$CONFIG_PROFILE" = "y" ]; then + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 +fi if [ "$CONFIG_SCSI" = "y" ]; then -bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y +bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS y fi diff --git a/arch/mips/dummy.c b/arch/mips/dummy.c deleted file mode 100644 index b85a1d71e..000000000 --- a/arch/mips/dummy.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * This file handles Systemcalls not available for all CPUs. - * - * Written by Ralf Baechle, - * Copyright (C) 1994 by Waldorf GMBH - */ - -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) -{ - printk("clone_page_tables\n"); - return start_mem; -} - -void fake_keyboard_interrupt(void) -{ -/* printk("fake_keyboard_interrupt\n"); */ -} diff --git a/arch/mips/entry.S b/arch/mips/entry.S deleted file mode 100644 index ebf2c1d9c..000000000 --- a/arch/mips/entry.S +++ /dev/null @@ -1,665 +0,0 @@ -/* - * linux/kernel/mips/sys_call.S - * - * Copyright (C) 1994 Waldorf GMBH - * written by Ralf Baechle - */ - -/* - * sys_call.S contains the system-call and fault low-level handling routines. - * This also contains the timer-interrupt handler, as well as all interrupts - * and faults that can result in a task-switch. - */ - -#define __ASSEMBLY__ - -#include <linux/sys.h> -#include <asm/segment.h> -#include <asm/mipsregs.h> -#include <asm/mipsconfig.h> -#include <asm/stackframe.h> -#include <asm/regdef.h> - -/* - * These are offsets into the task-struct. - */ -state = 0 -counter = 4 -priority = 8 -signal = 12 -blocked = 16 -flags = 20 -errno = 24 #/* MIPS OK */ -exec_domain = 60 #/* ??? */ - -ENOSYS = 38 - - .globl _system_call - .globl _lcall7 - .globl _device_not_available - .globl _coprocessor_error - .globl _divide_error - .globl _debug - .globl _nmi - .globl _int3 - .globl _overflow - .globl _bounds - .globl _invalid_op - .globl _double_fault - .globl _coprocessor_segment_overrun - .globl _invalid_TSS - .globl _segment_not_present - .globl _stack_segment - .globl _general_protection - .globl _reserved - .globl _alignment_check - .globl _page_fault - .globl ret_from_sys_call - .globl _sys_call_table - - .text - .set noreorder - .align 4 -handle_bottom_half: - lw s0,_intr_count - addiu s1,s0,1 - sw s1,_intr_count - mfc0 t0,CP0_STATUS # Enable IRQs - ori t0,t0,7 - xori t0,t0,6 - jal _do_bottom_half - mtc0 t0,CP0_STATUS - j 9f - sw s1,_intr_count - - .set reorder - .align 4 -reschedule: - la ra,ret_from_sys_call - j _schedule - - .set noreorder - .align 4 -_system_call: - li t1,NR_syscalls - bge t0,t1,ret_from_sys_call - .set nomacro - li t2,-ENOSYS # must be single instruction! - .set macro - lui t1,_sys_call_table - sll t0,t0,2 - addu t1,t0,t1 - lw t0,_sys_call_table(t1) - lw s0,_current - - beq zero,t0,ret_from_sys_call - lw t0,flags(s0) - sll t0,t0,2 # PF_TRACESYS - bltz t0,1f - sw zero,errno(s0) # delay slot - - jal t0 # do the real work - nop # fillme: delay slot - - sw v0,FR_REG2(sp) # save the return value - lw v0,errno(s0) - beq zero,v0,ret_from_sys_call - subu v0,zero,v0 # v0 = -v0 - # fixme: indicate error - j ret_from_sys_call - sw v0,FR_REG2(sp) - - .align 4 -1: jal _syscall_trace - nop -#if 0 - movl ORIG_EAX(%esp),%eax - call _sys_call_table(,%eax,4) - movl %eax,EAX(%esp) # save the return value - movl _current,%eax - movl errno(%eax),%edx - negl %edx - je 1f - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error -#endif -1: jal _syscall_trace - nop - - .align 4 -ret_from_sys_call: - lw t0,_intr_count # bottom half - bne zero,t0,2f - - lw t0,_bh_mask - lw t1,_bh_active - and t0,t0,t1 - bne zero,t0,handle_bottom_half -9: - mfc0 t0,CP0_STATUS # returning to supervisor ? - andi t0,t0,30 - subu t0,t0,6 - bltz t0,2f - -1: -#if 0 -/* - * Try whether this is needed or not... - */ - mfc0 t0,CP0_STATUS # enable irqs - ori t0,t0,0x7 - xori t0,t0,0x6 - mtc0 t0,CP0_STATUS -#endif - - lw t0,_need_resched - bne zero,t0,reschedule - - lw t0,_current - la t1,_task # task[0] cannot have signals - lw t2,state(s0) # state - beq t0,t1,2f - lw t0,counter(s0) # counter - beq zero,t2,reschedule # state == 0 ? - lw a0,blocked(s0) - # save blocked in a0 for - # signal handling - beq zero,t0,reschedule # counter == 0 ? - lw t0,signal(s0) - nor t1,zero,t0 - and t1,a0,t1 - beq zero,t1,skip_signal_return - nop -2: - jal _do_signal - move a1,sp - -skip_signal_return: - .set noreorder - .set noat -return: RESTORE_ALL - .set at - -/* - * Assumptions for _handle_int: - * - only bank a or b are possible interrupt sources - */ - .globl _handle_int -_handle_int: - .set noreorder - .text - la s0,PORT_BASE - li t1,0x0f - sb t1,0x20(s0) # poll command - lb t1,0x20(s0) # read result - FILL_LDS - bgtz t1,poll_second - andi t1,t1,7 - /* - * Acknowledge first pic - */ - lb t2,0x21(s0) - li s1,1 - sllv s1,s1,t1 - lb t4,_cache_21 - or t4,t4,s1 - sb t4,_cache_21 - sb t4,0x21(s0) - li t4,0x20 - sb t4,0x20(s0) - lw t0,_intr_count - addiu t0,t0,1 - sw t0,_intr_count - /* - * Now call the real handler - */ - la t0,_IRQ_vectors - sll t2,t1,2 - addu t0,t0,t2 - lw t0,(t0) - FILL_LDS - jalr t0 - nop - lw t0,_intr_count - subu t0,t0,1 - sw t0,_intr_count - /* - * Unblock first pic - */ -test1: lbu t1,0x21(s0) # tlbl exception?!? - lb t1,_cache_21 - nor s1,zero,s1 - and t1,t1,s1 - sb t1,_cache_21 - jr v0 - sb t1,0x21(s0) # delay slot - - .set at -poll_second: - li t1,0x0f - sb t1,0xa0(s0) # poll command - lb t1,0xa0(s0) # read result - FILL_LDS - bgtz t1,spurious_interrupt - andi t1,t1,7 - /* - * Acknowledge second pic - */ - lbu t2,0xa1(s0) - lbu t3,_cache_A1 - li s1,1 - sllv s1,s1,t1 - or t3,t3,s1 - sb t3,_cache_A1 - sb t3,0xa1(s0) - li t3,0x20 - sb t3,0xa0(s0) - lw t0,_intr_count - sb t3,0x20(s0) - addiu t0,t0,1 - sw t0,_intr_count - /* - * Now call the real handler - */ - la t0,_IRQ_vectors - sll t2,t1,2 - addu t0,t0,t2 - lw t0,32(t0) - FILL_LDS - jalr t0 - nop - lw t0,_intr_count - subu t0,t0,1 - sw t0,_intr_count - /* - * Unblock second pic - */ - lbu t1,0xa1(s0) - lb t1,_cache_A1 - nor s1,zero,s1 - and t1,t1,s1 - sb t1,_cache_A1 - jr v0 - sb t1,0xa1(s0) # delay slot - - .set at -spurious_interrupt: - /* - * Nothing happend... (whistle) - */ - lw t0,_spurious_count - la v0,return - addiu t0,t0,1 - sw t0,_spurious_count - jr ra - nop - - .globl _IRQ -_IRQ: move s2,ra - mfc0 t0,CP0_STATUS - ori t0,t0,0x1f - xori t0,t0,0x1e - mtc0 t0,CP0_STATUS - move a1,sp - jal _do_IRQ - move a0,t1 # Delay slot - mfc0 t0,CP0_STATUS - ori t0,t0,1 - xori t0,t0,1 - la v0,ret_from_sys_call - jr s2 - mtc0 t0,CP0_STATUS # Delay slot - - .globl _fast_IRQ -_fast_IRQ: move s2,ra - move a1,sp - jal _do_fast_IRQ - move a0,t1 # Delay slot - la v0,return - jr s2 - nop - - .globl _bad_IRQ -_bad_IRQ: - /* - * Don't return & unblock the pic - */ - j return - nop - - .bss - .globl _IRQ_vectors - -_IRQ_vectors: - .fill 16,4,0 - -/* - * Dummy handlers - */ - .text - .set noreorder - .set at - - .globl _handle_mod -_handle_mod: - la a0,mod_text - j _panic - nop - - .globl _handle_tlbl -_handle_tlbl: - la a0,badvaddr - mfc0 a1,CP0_BADVADDR - jal _printk - nop - la a0,status - lw a1,FR_STATUS(sp) - jal _printk - nop - la a0,eszero - move a1,s0 - jal _printk - nop - la a0,espe - move a1,sp - jal _printk - nop - la a0,jifftext - lw a1,_jiffies - jal _printk - nop - la a0,inttext - lw a1,_intr_count - jal _printk - nop - la a0,tlbl_msg - mfc0 a1,CP0_EPC - jal _printk - nop - la a0,tlbl_text - j _panic - nop - - .data -tlbl_msg: .asciz "tlbl exception at %x\n" -badvaddr: .asciz "accessing %x\n" -status: .asciz "cp0_status %x\n" -eszero: .asciz "s0 %x\n" -espe: .asciz "sp %x\n" -jifftext: .asciz "jiffies %d\n" -inttext: .asciz "IntNest: %d\n" - - .text - .globl _handle_tlbs -_handle_tlbs: - la a0,tlbs_text - j _panic - nop - - .globl _handle_adel -_handle_adel: - la v0,adel_text - jal _printk - nop - j _handle_tlbl - la a0,adel_text - j _panic - nop - - .globl _handle_ades -_handle_ades: - la a0,ades_text - j _panic - nop - - .globl _handle_ibe -_handle_ibe: - la a0,ibe_text - j _panic - nop - - .globl _handle_dbe -_handle_dbe: - la a0,dbe_text - j _panic - nop - - .globl _handle_sys -_handle_sys: - la a0,sys_text - j _panic - nop - - .globl _handle_bp -_handle_bp: - la a0,bp_text - j _panic - nop - - .globl _handle_ri -_handle_ri: - la a0,ri_text - j _panic - nop - - .globl _handle_cpu -_handle_cpu: - la a0,cpu_text - j _panic - nop - - .globl _handle_ov -_handle_ov: - la a0,ov_text - j _panic - nop - - .globl _handle_tr -_handle_tr: - la a0,tr_text - j _panic - nop - - .globl _handle_reserved -_handle_reserved: - la a0,reserved_text - j _panic - nop - - .globl _handle_fpe -_handle_fpe: - la a0,fpe_text - j _panic - nop - - .data -spurious_text: .asciz "Spurious interrupt" -fpe_text: .asciz "fpe exception" -reserved_text: .asciz "reserved exception" -tr_text: .asciz "tr exception" -ov_text: .asciz "ov exception" -cpu_text: .asciz "cpu exception" -ri_text: .asciz "ri exception" -bp_text: .asciz "bp exception" -sys_text: .asciz "sys exception" -dbe_text: .asciz "dbe exception" -ibe_text: .asciz "ibe exception" -ades_text: .asciz "ades exception" -adel_text: .asciz "adel exception" -tlbs_text: .asciz "tlbs exception" -mod_text: .asciz "mod exception" -tlbl_text: .asciz "tlbl exception" - -/* - * Exception handler table, 256 entries. - */ - .data - .globl _exception_handlers -_exception_handlers: - .word _handle_int /* 0 */ - .word _handle_mod - .word _handle_tlbl - .word _handle_tlbs - .word _handle_adel - .word _handle_ades - .word _handle_ibe - .word _handle_dbe - .word _handle_sys - .word _handle_bp - .word _handle_ri - .word _handle_cpu - .word _handle_ov - .word _handle_tr - .word _handle_reserved - .word _handle_fpe /* 15 */ -#if 0 - .fill 240,4,_handle_reserved -#endif - -/* - * Table of syscalls - */ - .data -_sys_call_table: - .word _sys_setup /* 0 */ - .word _sys_exit - .word _sys_fork - .word _sys_read - .word _sys_write - .word _sys_open /* 5 */ - .word _sys_close - .word _sys_waitpid - .word _sys_creat - .word _sys_link - .word _sys_unlink /* 10 */ - .word _sys_execve - .word _sys_chdir - .word _sys_time - .word _sys_mknod - .word _sys_chmod /* 15 */ - .word _sys_chown - .word _sys_break - .word _sys_stat - .word _sys_lseek - .word _sys_getpid /* 20 */ - .word _sys_mount - .word _sys_umount - .word _sys_setuid - .word _sys_getuid - .word _sys_stime /* 25 */ - .word _sys_ptrace - .word _sys_alarm - .word _sys_fstat - .word _sys_pause - .word _sys_utime /* 30 */ - .word _sys_stty - .word _sys_gtty - .word _sys_access - .word _sys_nice - .word _sys_ftime /* 35 */ - .word _sys_sync - .word _sys_kill - .word _sys_rename - .word _sys_mkdir - .word _sys_rmdir /* 40 */ - .word _sys_dup - .word _sys_pipe - .word _sys_times - .word _sys_prof - .word _sys_brk /* 45 */ - .word _sys_setgid - .word _sys_getgid - .word _sys_signal - .word _sys_geteuid - .word _sys_getegid /* 50 */ - .word _sys_acct - .word _sys_phys - .word _sys_lock - .word _sys_ioctl - .word _sys_fcntl /* 55 */ - .word _sys_mpx - .word _sys_setpgid - .word _sys_ulimit - .word _sys_olduname - .word _sys_umask /* 60 */ - .word _sys_chroot - .word _sys_ustat - .word _sys_dup2 - .word _sys_getppid - .word _sys_getpgrp /* 65 */ - .word _sys_setsid - .word _sys_sigaction - .word _sys_sgetmask - .word _sys_ssetmask - .word _sys_setreuid /* 70 */ - .word _sys_setregid - .word _sys_sigsuspend - .word _sys_sigpending - .word _sys_sethostname - .word _sys_setrlimit /* 75 */ - .word _sys_getrlimit - .word _sys_getrusage - .word _sys_gettimeofday - .word _sys_settimeofday - .word _sys_getgroups /* 80 */ - .word _sys_setgroups - .word _sys_select - .word _sys_symlink - .word _sys_lstat - .word _sys_readlink /* 85 */ - .word _sys_uselib - .word _sys_swapon - .word _sys_reboot - .word _sys_readdir - .word _sys_mmap /* 90 */ - .word _sys_munmap - .word _sys_truncate - .word _sys_ftruncate - .word _sys_fchmod - .word _sys_fchown /* 95 */ - .word _sys_getpriority - .word _sys_setpriority - .word _sys_profil - .word _sys_statfs - .word _sys_fstatfs /* 100 */ - .word _sys_ioperm - .word _sys_socketcall - .word _sys_syslog - .word _sys_setitimer - .word _sys_getitimer /* 105 */ - .word _sys_newstat - .word _sys_newlstat - .word _sys_newfstat - .word _sys_uname - .word _sys_iopl /* 110 */ - .word _sys_vhangup - .word _sys_idle - .word _sys_vm86 - .word _sys_wait4 - .word _sys_swapoff /* 115 */ - .word _sys_sysinfo - .word _sys_ipc - .word _sys_fsync - .word _sys_sigreturn - .word _sys_clone /* 120 */ - .word _sys_setdomainname - .word _sys_newuname - .word _sys_modify_ldt - .word _sys_adjtimex - .word _sys_mprotect /* 125 */ - .word _sys_sigprocmask - .word _sys_create_module - .word _sys_init_module - .word _sys_delete_module - .word _sys_get_kernel_syms /* 130 */ - .word _sys_quotactl - .word _sys_getpgid - .word _sys_fchdir - .word _sys_bdflush - .word _sys_sysfs /* 135 */ - .word _sys_personality - .word 0 /* for afs_syscall */ - .word _sys_setfsuid - .word _sys_setfsgid - .word _sys_llseek /* 140 */ - .space (NR_syscalls-140)*4 diff --git a/arch/mips/ioport.c b/arch/mips/ioport.c deleted file mode 100644 index ee3352410..000000000 --- a/arch/mips/ioport.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/arch/mips/ioport.c - * - * Functions not implemented for Linux/MIPS - */ -#include <linux/linkage.h> -#include <linux/errno.h> - -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) -{ - return -ENOSYS; -} - -asmlinkage int sys_iopl(long ebx,long ecx,long edx, - long esi, long edi, long ebp, long eax, long ds, - long es, long fs, long gs, long orig_eax, - long eip,long cs,long eflags,long esp,long ss) -{ - return -ENOSYS; -} diff --git a/arch/mips/irq.S b/arch/mips/irq.S deleted file mode 100644 index 129c2843f..000000000 --- a/arch/mips/irq.S +++ /dev/null @@ -1,642 +0,0 @@ -/* - * linux/kernel/mips/sys_call.S - * - * Copyright (C) 1994 Waldorf GMBH - * written by Ralf Baechle - */ - -/* - * All code below must be relocatable! - */ - -/* - * sys_call.S contains the system-call and fault low-level handling routines. - * This also contains the timer-interrupt handler, as well as all interrupts - * and faults that can result in a task-switch. - * - * NOTE: This code handles signal-recognition, which happens every time - * after a timer-interrupt and after each system call. - * - * I changed all the .align's to 4 (16 byte alignment), as that's faster - * on a 486. - * - * Stack layout in 'ret_from_system_call': - * ptrace needs to have all regs on the stack. - * if the order here is changed, it needs to be - * updated in fork.c:copy_process, signal.c:do_signal, - * ptrace.c and ptrace.h - * - * 0(%esp) - %ebx - * 4(%esp) - %ecx - * 8(%esp) - %edx - * C(%esp) - %esi - * 10(%esp) - %edi - * 14(%esp) - %ebp - * 18(%esp) - %eax - * 1C(%esp) - %ds - * 20(%esp) - %es - * 24(%esp) - %fs - * 28(%esp) - %gs - * 2C(%esp) - orig_eax - * 30(%esp) - %eip - * 34(%esp) - %cs - * 38(%esp) - %eflags - * 3C(%esp) - %oldesp - * 40(%esp) - %oldss - */ - -#include <linux/segment.h> -#include <linux/sys.h> - -/* - * Offsets into the Interrupt stackframe. - */ -FR_REG1 = 0 -FR_REG2 = 4 -FR_REG3 = 8 -FR_REG4 = 12 -FR_REG5 = 16 -FR_REG6 = 20 -FR_REG7 = 24 -FR_REG8 = 28 -FR_REG9 = 32 -FR_REG10 = 36 -FR_REG11 = 40 -FR_REG12 = 44 -FR_REG13 = 48 -FR_REG14 = 52 -FR_REG15 = 56 -FR_REG16 = 60 -FR_REG17 = 64 -FR_REG18 = 68 -FR_REG19 = 72 -FR_REG20 = 76 -FR_REG21 = 80 -FR_REG22 = 84 -FR_REG23 = 88 -FR_REG24 = 92 -FR_REG25 = 96 -/* $26 and $27 not saved */ -FR_REG28 = 100 -FR_REG29 = 104 -FR_REG30 = 108 -FR_REG31 = 112 -/* - * Saved cp0 registers follow - */ -FR_STATUS = 116 -FR_EPC = 120 -FR_ERROREPC = 124 -FR_SIZE = 120 /* Size of stack frame */ - -/* - * These are offsets into the task-struct. - */ -state = 0 -counter = 4 -priority = 8 -signal = 12 -blocked = 16 -flags = 20 -errno = 24 -dbgreg6 = 52 -dbgreg7 = 56 -exec_domain = 60 - -ENOSYS = 38 - - .globl _system_call,_lcall7 - .globl _device_not_available, _coprocessor_error - .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds, - .globl _invalid_op,_double_fault,_coprocessor_segment_overrun - .globl _invalid_TSS,_segment_not_present,_stack_segment - .globl _general_protection,_reserved - .globl _alignment_check,_page_fault - .globl ret_from_sys_call, _sys_call_table - -#define SAVE_ALL(which_pc) \ - .set noreorder \ - .set noat \ - lui k0,0x8000 \ - move k1,$sp \ - lw sp,_kernelsp-except_vec0(k0) \ - subu sp,$sp,FR_SIZE \ - sw sp,_kernelsp-except_vec0(k0) \ /* Kernel SP */ - mfc0 v0,CP0_STATUS \ - sw v0,FR_STATUS(sp) \ - mfc0 v0,CP0_EPC \ - sw v0,FR_EPC \ - mfc0 v0,CP0_ERROREPC \ - sw v0,FR_ERROREPC \ - sw k1,FR_R27(sp) \ - sw $2,FR_R1(sp) \ - sw $2,FR_R2(sp) \ - sw $3,FR_R3(sp) \ - sw $4,FR_R4(sp) \ - sw $5,FR_R5(sp) \ - sw $6,FR_R6(sp) \ - sw $7,FR_R7(sp) \ - sw $8,FR_R8(sp) \ - sw $9,FR_R9(sp) \ - sw $10,FR_R10(sp) \ - sw $11,FR_R11(sp) \ - sw $12,FR_R12(sp) \ - sw $13,FR_R13(sp) \ - sw $14,FR_R14(sp) \ - sw $15,FR_R15(sp) \ - sw $16,FR_R16(sp) \ - sw $17,FR_R17(sp) \ - sw $18,FR_R18(sp) \ - sw $19,FR_R19(sp) \ - sw $20,FR_R20(sp) \ - sw $21,FR_R21(sp) \ - sw $22,FR_R22(sp) \ - sw $23,FR_R23(sp) \ - sw $24,FR_R24(sp) \ - sw $25,FR_R25(sp) \ - sw $28,FR_R28(sp) \ - sw $30,FR_R30(sp) \ - sw $31,FR_R31(sp) - - -#define RESTORE_ALL \ - lui k1,0x8000 \ - move k0,$sp \ - lw v0,FR_ERROREPC(k0) \ - lw v1,FR_EPC(k0) \ - mtc0 v0,CP0_ERROREPC(k0) \ - mtc0 v1,CP0_EPC(k0) \ - lw $31,FR_R31(k0) \ - lw $30,FR_R30(k0) \ - lw $28,FR_R28(k0) \ - lw $25,FR_R25(k0) \ - lw $24,FR_R24(k0) \ - lw $23,FR_R23(k0) \ - lw $22,FR_R22(k0) \ - lw $21,FR_R21(k0) \ - lw $20,FR_R20(k0) \ - lw $19,FR_R19(k0) \ - lw $18,FR_R18(k0) \ - lw $17,FR_R17(k0) \ - lw $16,FR_R16(k0) \ - lw $15,FR_R15(k0) \ - lw $14,FR_R14(k0) \ - lw $13,FR_R13(k0) \ - lw $12,FR_R12(k0) \ - lw $11,FR_R11(k0) \ - lw $10,FR_R10(k0) \ - lw $9,FR_R9(k0) \ - lw $8,FR_R8(k0) \ - lw $7,FR_R7(k0) \ - lw $6,FR_R6(k0) \ - lw $5,FR_R5(k0) \ - lw $4,FR_R4(k0) \ - lw $3,FR_R3(k0) \ - lw $2,FR_R2(k0) \ - lw $1,FR_R1(k0) \ - addiu k0,k0,FR_SIZE \ - sw k0,_kernelsp-except_vec0(k1) \ /* Kernel SP */ - eret - - .align 4 -handle_bottom_half: - pushfl - incl _intr_count - mtc0 zero,CP0_STATUS - call _do_bottom_half - popfl - decl _intr_count - j 9f - nop - - .align 4 -reschedule: - pushl $ret_from_sys_call - j _schedule - nop - - .align 4 -_system_call: - pushl %eax # save orig_eax - SAVE_ALL - movl $-ENOSYS,EAX(%esp) - cmpl $(NR_syscalls),%eax - jae ret_from_sys_call - movl _sys_call_table(,%eax,4),%eax - testl %eax,%eax - je ret_from_sys_call - movl _current,%ebx - andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors - movl $0,errno(%ebx) - movl %db6,%edx - movl %edx,dbgreg6(%ebx) # save current hardware debugging status - testb $0x20,flags(%ebx) # PF_TRACESYS - jne 1f - call *%eax - movl %eax,EAX(%esp) # save the return value - movl errno(%ebx),%edx - negl %edx - je ret_from_sys_call - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error - j ret_from_sys_call - nop - - .align 4 -1: call _syscall_trace - movl ORIG_EAX(%esp),%eax - call _sys_call_table(,%eax,4) - movl %eax,EAX(%esp) # save the return value - movl _current,%eax - movl errno(%eax),%edx - negl %edx - je 1f - movl %edx,EAX(%esp) - orl $(CF_MASK),EFLAGS(%esp) # set carry to indicate error -1: call _syscall_trace - - .align 4,0x90 -ret_from_sys_call: - cmpl $0,_intr_count - jne 2f - movl _bh_mask,%eax - andl _bh_active,%eax - jne handle_bottom_half -9: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are - testl $(VM_MASK),%eax # different then - jne 1f - cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ? - je 2f -1: sti - orl $(IF_MASK),%eax # these just try to make sure - andl $~NT_MASK,%eax # the program doesn't do anything - movl %eax,EFLAGS(%esp) # stupid - cmpl $0,_need_resched - jne reschedule - movl _current,%eax - cmpl _task,%eax # task[0] cannot have signals - je 2f - cmpl $0,state(%eax) # state - jne reschedule - cmpl $0,counter(%eax) # counter - je reschedule - movl blocked(%eax),%ecx - movl %ecx,%ebx # save blocked in %ebx for - # signal handling - notl %ecx - andl signal(%eax),%ecx - jne signal_return -2: RESTORE_ALL - - .align 4 -signal_return: - movl %esp,%ecx - pushl %ecx - testl $(VM_MASK),EFLAGS(%ecx) - jne v86_signal_return - pushl %ebx - call _do_signal - popl %ebx - popl %ebx - RESTORE_ALL - - .align 4 -v86_signal_return: - call _save_v86_state - movl %eax,%esp - pushl %eax - pushl %ebx - call _do_signal - popl %ebx - popl %ebx - RESTORE_ALL - - .align 4 -_divide_error: - move $a1,zero # no error code - la $t0,$_do_divide_error - .align 4,0x90 -error_code: - push %fs - push %es - push %ds - pushl %eax - pushl %ebp - pushl %edi - pushl %esi - pushl %edx - pushl %ecx - pushl %ebx - cld - movl $-1, %eax - xchgl %eax, ORIG_EAX(%esp) # orig_eax (get the error code. ) - xorl %ebx,%ebx # zero ebx - mov %gs,%bx # get the lower order bits of gs - xchgl %ebx, GS(%esp) # get the address and save gs. - pushl %eax # push the error code - lea 4(%esp),%edx - pushl %edx - movl $(KERNEL_DS),%edx - mov %dx,%ds - mov %dx,%es - movl $(USER_DS),%edx - mov %dx,%fs - jal t0 # call handler - addl $8,%esp - j ret_from_sys_call - - .align 4 -_coprocessor_error: - move a1,zero - la t0,_do_coprocessor_error - j error_code - - .align 4 -_device_not_available: - pushl $-1 # mark this as an int - SAVE_ALL - pushl $ret_from_sys_call - movl %cr0,%eax - testl $0x4,%eax # EM (math emulation bit) - je _math_state_restore - pushl $0 # temporary storage for ORIG_EIP - call _math_emulate - addl $4,%esp - ret - - .set reorder - - .align 4 -_debug: - move a1,zero - la t0,_do_debug - j error_code - - .align 4 -_nmi: - move a1,zero - la t0,_do_nmi - j error_code - - .align 4 -_int3: - move a1,zero - la t0,_do_int3 - j error_code - - .align 4 -_overflow: - move a1,zero - la t0,_do_overflow - j error_code - - .align 4 -_bounds: - move a1,zero - la t0,_do_bounds - j error_code - - .align 4 -_invalid_op: - move a1,zero - la t0,_do_invalid_op - j error_code - - .align 4 -_segment_not_present: - la t0,_do_segment_not_present - j error_code - - .align 4 -_stack_segment: - la t0,_do_stack_segment - j error_code - - .align 4 -_general_protection: - la t0,_do_general_protection - j error_code - - .align 4 -_page_fault: - la t0,_do_page_fault - j error_code -/* - * TLB Refill exception entry point - * - * The mm data is stored in the context register and - */ - .text - .set noreorder - .set noat - dmfc0 k0,CP0_CONTEXT - dsrl k0,k0,2 - lw k0,(k0) # Level 1 descriptor - dmfc0 k1,CP0_BADVADDR - srl k1,k1,10 - andi k1,k1,0xffc - addu k1,k1,k1 - lwu k0,(k1) # 2 Level 2 entries - lwu k1,4(k1) - dmtc0 k0,CP0_ENTRYLO0 - dmtc0 k0,CP0_ENTRYLO1 - tlbwr - /* - * Now compute the return address. Since this is extremly - * timecritical the code is inlined - */ - mfc0 k0,CP0_CAUSE - bgtz k0,1f - - /* - * Damn - a branch delay slot. Compute new PC - */ - - /* - * That's it boys - back to work! - */ -1: eret - - - - - lui t0,>_exception_handlers - mfc0 t1,CP0_CAUSE - andi t1,t1,0x3fc - addu t0,t0,t1 - lw t0,<_exception_handlers(t0) - sw /* fill delay slot */ - jalr t0 - sw /* fill delay slot */ - - -/* - * Exception handler table, 256 entries. - */ - .data - .align 4 -_exception_handlers: - .word _handle_int /* 0 */ - .word _handle_mod - .word _handle_tlbl - .word _handle_tlbs - .word _handle_adel - .word _handle_ades - .word _handle_ibe - .word _handle_dbe - .word _handle_sys - .word _handle_bp - .word _handle_ri - .word _handle_cpu - .word _handle_ov - .word _handle_tr - .word _handle_reserved - .word _handle_fpe - .fill 240,4,_handle_reserved /* 16 */ - -/* - * Table of syscalls - */ - .data - .align 4 -_sys_call_table: - .word _sys_setup /* 0 */ - .word _sys_exit - .word _sys_fork - .word _sys_read - .word _sys_write - .word _sys_open /* 5 */ - .word _sys_close - .word _sys_wordpid - .word _sys_creat - .word _sys_link - .word _sys_unlink /* 10 */ - .word _sys_execve - .word _sys_chdir - .word _sys_time - .word _sys_mknod - .word _sys_chmod /* 15 */ - .word _sys_chown - .word _sys_break - .word _sys_stat - .word _sys_lseek - .word _sys_getpid /* 20 */ - .word _sys_mount - .word _sys_umount - .word _sys_setuid - .word _sys_getuid - .word _sys_stime /* 25 */ - .word _sys_ptrace - .word _sys_alarm - .word _sys_fstat - .word _sys_pause - .word _sys_utime /* 30 */ - .word _sys_stty - .word _sys_gtty - .word _sys_access - .word _sys_nice - .word _sys_ftime /* 35 */ - .word _sys_sync - .word _sys_kill - .word _sys_rename - .word _sys_mkdir - .word _sys_rmdir /* 40 */ - .word _sys_dup - .word _sys_pipe - .word _sys_times - .word _sys_prof - .word _sys_brk /* 45 */ - .word _sys_setgid - .word _sys_getgid - .word _sys_signal - .word _sys_geteuid - .word _sys_getegid /* 50 */ - .word _sys_acct - .word _sys_phys - .word _sys_lock - .word _sys_ioctl - .word _sys_fcntl /* 55 */ - .word _sys_mpx - .word _sys_setpgid - .word _sys_ulimit - .word _sys_olduname - .word _sys_umask /* 60 */ - .word _sys_chroot - .word _sys_ustat - .word _sys_dup2 - .word _sys_getppid - .word _sys_getpgrp /* 65 */ - .word _sys_setsid - .word _sys_sigaction - .word _sys_sgetmask - .word _sys_ssetmask - .word _sys_setreuid /* 70 */ - .word _sys_setregid - .word _sys_sigsuspend - .word _sys_sigpending - .word _sys_sethostname - .word _sys_setrlimit /* 75 */ - .word _sys_getrlimit - .word _sys_getrusage - .word _sys_gettimeofday - .word _sys_settimeofday - .word _sys_getgroups /* 80 */ - .word _sys_setgroups - .word _sys_select - .word _sys_symlink - .word _sys_lstat - .word _sys_readlink /* 85 */ - .word _sys_uselib - .word _sys_swapon - .word _sys_reboot - .word _sys_readdir - .word _sys_mmap /* 90 */ - .word _sys_munmap - .word _sys_truncate - .word _sys_ftruncate - .word _sys_fchmod - .word _sys_fchown /* 95 */ - .word _sys_getpriority - .word _sys_setpriority - .word _sys_profil - .word _sys_statfs - .word _sys_fstatfs /* 100 */ - .word _sys_ioperm - .word _sys_socketcall - .word _sys_syslog - .word _sys_setitimer - .word _sys_getitimer /* 105 */ - .word _sys_newstat - .word _sys_newlstat - .word _sys_newfstat - .word _sys_uname - .word _sys_iopl /* 110 */ - .word _sys_vhangup - .word _sys_idle - .word _sys_vm86 - .word _sys_word4 - .word _sys_swapoff /* 115 */ - .word _sys_sysinfo - .word _sys_ipc - .word _sys_fsync - .word _sys_sigreturn - .word _sys_clone /* 120 */ - .word _sys_setdomainname - .word _sys_newuname - .word _sys_modify_ldt - .word _sys_adjtimex - .word _sys_mprotect /* 125 */ - .word _sys_sigprocmask - .word _sys_create_module - .word _sys_init_module - .word _sys_delete_module - .word _sys_get_kernel_syms /* 130 */ - .word _sys_quotactl - .word _sys_getpgid - .word _sys_fchdir - .word _sys_bdflush - .word _sys_sysfs /* 135 */ - .word _sys_personality - .word 0 /* for afs_syscall */ - - .space (NR_syscalls-137)*4 diff --git a/arch/mips/irq.c b/arch/mips/irq.c deleted file mode 100644 index 1bce3d07a..000000000 --- a/arch/mips/irq.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * linux/kernel/irq.c - * - * Copyright (C) 1992 Linus Torvalds - * - * This file contains the code used by various IRQ handling routines: - * asking for different IRQ's should be done through these routines - * instead of just grabbing them. Thus setups with different IRQ numbers - * shouldn't result in any weird surprises, and installing new handlers - * should be easier. - */ - -/* - * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * The same sigaction struct is used, and with similar semantics (ie there - * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there - * are similarities. - * - * sa_handler(int irq_NR) is the default function called (0 if no). - * sa_mask is horribly ugly (I won't even mention it) - * sa_flags contains various info: SA_INTERRUPT etc - * sa_restorer is the unused - */ - -#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 <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> - -#define CR0_NE 32 - -unsigned char cache_21 = 0xff; -unsigned char cache_A1 = 0xff; - -unsigned long intr_count = 0; -unsigned long spurious_count = 0; -unsigned long bh_active = 0; -unsigned long bh_mask = 0xFFFFFFFF; -struct bh_struct bh_base[32]; - -void disable_irq(unsigned int irq_nr) -{ - unsigned long flags; - unsigned char mask; - - mask = 1 << (irq_nr & 7); - save_flags(flags); - if (irq_nr < 8) { - cli(); - cache_21 |= mask; - outb(cache_21,0x21); - restore_flags(flags); - return; - } - cli(); - cache_A1 |= mask; - outb(cache_A1,0xA1); - restore_flags(flags); -} - -void enable_irq(unsigned int irq_nr) -{ - unsigned long flags; - unsigned char mask; - - mask = ~(1 << (irq_nr & 7)); - save_flags(flags); - if (irq_nr < 8) { - cli(); - cache_21 &= mask; - outb(cache_21,0x21); - restore_flags(flags); - return; - } - cli(); - cache_A1 &= mask; - outb(cache_A1,0xA1); - restore_flags(flags); -} - -/* - * 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. - */ -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\n"); -} - -/* - * Pointers to the low-level handlers: first the general ones, then the - * fast ones, then the bad ones. - */ -extern void IRQ(void); -extern void fast_IRQ(void); -extern void bad_IRQ(void); - -/* - * Initial irq handlers. - */ -static struct sigaction irq_sigaction[16] = { - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, - { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } -}; - -int get_irq_list(char *buf) -{ - int i, len = 0; - struct sigaction * sa = irq_sigaction; - - for (i = 0 ; i < 16 ; i++, sa++) { - if (!sa->sa_handler) - continue; - len += sprintf(buf+len, "%2d: %8d %c %s\n", - i, kstat.interrupts[i], - (sa->sa_flags & SA_INTERRUPT) ? '+' : ' ', - (char *) sa->sa_mask); - } - return len; -} - -/* - * do_IRQ handles IRQ's that have been installed without the - * SA_INTERRUPT flag: it uses the full signal-handling return - * and runs with other interrupts enabled. All relatively slow - * IRQ's should use this format: notably the keyboard/timer - * routines. - */ -asmlinkage void do_IRQ(int irq, struct pt_regs * regs) -{ - struct sigaction * sa = irq + irq_sigaction; - - kstat.interrupts[irq]++; - sa->sa_handler((int) regs); -} - -/* - * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return - * stuff - the handler is also running with interrupts disabled unless - * it explicitly enables them later. - */ -asmlinkage void do_fast_IRQ(int irq) -{ - struct sigaction * sa = irq + irq_sigaction; - - kstat.interrupts[irq]++; - sa->sa_handler(irq); -} - -/* - * Using "struct sigaction" is slightly silly, but there - * are historical reasons and it works well, so.. - */ -static int irqaction(unsigned int irq, struct sigaction * new_sa) -{ - struct sigaction * sa; - unsigned long flags; - - if (irq > 15) - return -EINVAL; - sa = irq + irq_sigaction; - if (sa->sa_handler) - return -EBUSY; - if (!new_sa->sa_handler) - return -EINVAL; - save_flags(flags); - cli(); - *sa = *new_sa; - /* - * FIXME: Does the SA_INTERRUPT flag make any sense on the MIPS??? - */ - if (sa->sa_flags & SA_INTERRUPT) - set_intr_gate(irq,fast_IRQ); - else - set_intr_gate(irq,IRQ); - if (irq < 8) { - cache_21 &= ~(1<<irq); - outb(cache_21,0x21); - } else { - cache_21 &= ~(1<<2); - cache_A1 &= ~(1<<(irq-8)); - outb(cache_21,0x21); - outb(cache_A1,0xA1); - } - restore_flags(flags); - return 0; -} - -int request_irq(unsigned int irq, void (*handler)(int), - unsigned long flags, const char * devname) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sa.sa_flags = flags; - sa.sa_mask = (unsigned long) devname; - sa.sa_restorer = NULL; - return irqaction(irq,&sa); -} - -void free_irq(unsigned int irq) -{ - struct sigaction * sa = irq + irq_sigaction; - unsigned long flags; - - if (irq > 15) { - printk("Trying to free IRQ%d\n",irq); - return; - } - if (!sa->sa_handler) { - printk("Trying to free free IRQ%d\n",irq); - return; - } - save_flags(flags); - cli(); - if (irq < 8) { - cache_21 |= 1 << irq; - outb(cache_21,0x21); - } else { - cache_A1 |= 1 << (irq-8); - outb(cache_A1,0xA1); - } - set_intr_gate(irq,bad_IRQ); - sa->sa_handler = NULL; - sa->sa_flags = 0; - sa->sa_mask = 0; - sa->sa_restorer = NULL; - restore_flags(flags); -} - -#if 0 -/* - * handle fpa errors - */ -static void math_error_irq(int cpl) -{ - if (!hard_math) - return; - handle_fpe(); -} -#endif - -static void no_action(int cpl) { } - -void init_IRQ(void) -{ - int i; - - for (i = 0; i < 16 ; i++) - set_intr_gate(i, bad_IRQ[i]); - if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) - printk("Unable to get IRQ2 for cascade\n"); - - /* initialize the bottom half routines. */ - for (i = 0; i < 32; i++) { - bh_base[i].routine = NULL; - bh_base[i].data = NULL; - } - bh_active = 0; - intr_count = 0; -} diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile new file mode 100644 index 000000000..0086f60cf --- /dev/null +++ b/arch/mips/kernel/Makefile @@ -0,0 +1,94 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = process.o signal.o entry.o traps.o irq.o ptrace.o vm86.o ioport.o \ + setup.o bios32.o tynedma.o + +include ../../../.config + +# +# Kernel debugging +# + +ifdef CONFIG_REMOTE_DEBUG +OBJS += gdb-low.o gdb-stub.o +endif + +# +# Board specific code +# + +ifdef CONFIG_MIPS_JAZZ +OBJS += jazzdma.o +endif + +ifdef CONFIG_ACER_PICA_61 +OBJS += pica.o +endif + +ifdef CONFIG_DESKSTATION_TYNE +OBJS += tyne.o +endif + +ifdef CONFIG_MIPS_MAGNUM_4000 +OBJS += magnum4000.o +endif + +# +# CPU model specific code +# +ifdef CONFIG_CPU_R4X00 +OBJS += r4xx0.o +endif + +ifdef CONFIG_CPU_R4600 +OBJS += r4xx0.o +endif + +all: kernel.o head.o + +entry.o: entry.S + +head.o: head.S + +magnum4000.o: magnum4000.S + +pica.o: pica.S + +r4xx0.o: r4xx0.S + +tyne.o: tyne.S + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.[cS] > .depend + +modules: + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/arch/mips/kernel/bios32.c b/arch/mips/kernel/bios32.c new file mode 100644 index 000000000..1fe61faa0 --- /dev/null +++ b/arch/mips/kernel/bios32.c @@ -0,0 +1,7 @@ +/* + * bios 32 replacement + */ +unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +{ + return memory_start; +} diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S new file mode 100644 index 000000000..787e2bbf4 --- /dev/null +++ b/arch/mips/kernel/entry.S @@ -0,0 +1,625 @@ +/* + * arch/mips/kernel/entry.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all interrupts + * and faults that can result in a task-switch. The ISA dependend TLB + * code is in arch/mips/kernel/tlb.S + */ + +#include <linux/sys.h> + +#include <asm/asm.h> +#include <asm/errno.h> +#include <asm/segment.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/stackframe.h> +#include <asm/processor.h> + +/* + * These are offsets into the task-struct. + */ +state = 0 +counter = 4 +priority = 8 +signal = 12 +blocked = 16 +flags = 20 +errno = 24 +exec_domain = 60 + + .text + .set noreorder + .align 4 +handle_bottom_half: + lui s0,%hi(intr_count) + lw s1,%lo(intr_count)(s0) + mfc0 s3,CP0_STATUS # Enable IRQs + addiu s2,s1,1 + sw s2,%lo(intr_count)(s0) + ori t0,s3,0x1f + xori t0,0x1e + jal do_bottom_half + mtc0 t0,CP0_STATUS # delay slot + mtc0 s3,CP0_STATUS # Restore old IRQ state + j 9f + sw s1,%lo(intr_count)(s0) # delay slot + +reschedule: + lui ra,%hi(ret_from_sys_call) + j schedule + addiu ra,%lo(ret_from_sys_call) # delay slot + + .align 5 + NESTED(handle_sys, FR_SIZE, sp) + .set noat + SAVE_ALL + STI + .set at + /* + * Compute return address. We assume that syscalls never + * appear in delay slots. For the Linux/MIPS libc this + * assumption is always true. + */ + lw t3,FR_EPC(sp) + lw s1,FR_REG2(sp) + li t0,-ENOSYS + addiu t3,4 + sw t3,FR_EPC(sp) + li t2,NR_syscalls + bge s1,t2,ret_from_sys_call + sw t0,FR_REG2(sp) # delay slot + sll s1,PTRLOG + lw s1,sys_call_table(s1) + lw s0,current + + beqz s1,ret_from_sys_call + lw t0,flags(s0) # delay slot + sll t0,26 # PF_TRACESYS + bltz t0,1f + sw zero,errno(s0) # delay slot + +#if 0 + lw t0,FR_ORIG_REG2(sp) + beq t0,4,1f + nop + la t0,sys_call_names + lw t1,FR_ORIG_REG2(sp) + sll t1,2 + addu t0,t1 + lw a1,(t0) + PRINT("%s(") + lw a1,FR_REG4(sp) + lw a2,FR_REG5(sp) + lw a3,FR_REG6(sp) + PRINT("%08lx, %08lx, %08lx, ") + lw a1,FR_REG7(sp) + lw a2,FR_EPC(sp) + lw a3,FR_REG31(sp) + PRINT("%08lx) epc %08lx ra %08lx ") +1: +#endif + lw a0,FR_REG4(sp) + lw a1,FR_REG5(sp) + lw a2,FR_REG6(sp) + lw a3,FR_REG7(sp) + lw t0,FR_REG3(sp) + jalr s1 # do the real work + sw t0,PTRSIZE*4(sp) # delay slot + +#if 0 + lw t0,FR_ORIG_REG2(sp) + beq t0,4,1f + nop + sw v0,xxx + lw a1,xxx + PRINT("res %08lx\n") + lw v0,xxx + .data +xxx: .word 0 + .text +1: +#endif + + lw t0,errno(s0) + sw v0,FR_REG2(sp) # save return value + subu t0,zero,t0 + beqz t0,ret_from_sys_call + nop # delay slot + /* + * Fixme: should set error flag + */ + j ret_from_sys_call + sw t0,FR_REG2(sp) # delay slot + + .align 4 +1: jal syscall_trace + nop # delay slot + + lw a0,FR_REG4(sp) + lw a1,FR_REG5(sp) + lw a2,FR_REG6(sp) + lw a3,FR_REG7(sp) + lw t0,FR_REG3(sp) + jalr s1 # do the real work + sw t0,PTRSIZE*4(sp) # delay slot + + lw t0,errno(s0) + sw v0,FR_REG2(sp) + subu t0,zero,t0 # delay slot + beqz t0,1f + nop # delay slot + /* + * Fixme: should set error flag + */ +1: jal syscall_trace + sw t0,FR_REG2(sp) # delay slot + + .align 4 + .globl ret_from_sys_call +ret_from_sys_call: + lw t0,intr_count # bottom half + bnez t0,return +9: + lw t0,bh_mask # delay slot + lw t1,bh_active # unused delay slot + and t0,t1 + bnez t0,handle_bottom_half + + lw t0,FR_STATUS(sp) # returning to kernel mode? + andi t1,t0,0x10 + beqz t1,return # -> yes + + mfc0 t0,CP0_STATUS # delay slot + lw t1,need_resched + ori t0,0x1f # enable irqs + xori t0,0x1e + bnez t1,reschedule + mtc0 t0,CP0_STATUS # delay slot + + lw s0,current + lw t0,task + lw t1,state(s0) # state + beq s0,t0,return # task[0] cannot have signals + lw t0,counter(s0) # counter + bnez t1,reschedule # state == 0 ? + lw a0,blocked(s0) + # save blocked in a0 for + # signal handling + beqz t0,reschedule # counter == 0 ? + lw t0,signal(s0) + nor t1,zero,a0 + and t1,t0,t1 + beqz t1,return + nop + + jal do_signal + move a1,sp # delay slot + + .set noat + .globl return +return: RESTORE_ALL + ERET + .set at + END(handle_sys) + +/* + * Beware: interrupt, fast_interrupt and bad_interrupt have unusal + * calling conventions! + * + * t1 - interrupt number + * s2 - destroyed + * return values: + * v0 - return routine + */ + .text + .set at + .align 5 + NESTED(interrupt, FR_SIZE, sp) + move s2,ra + mfc0 t0,CP0_STATUS # enable IRQs + ori t0,0x1f + xori t0,0x1e + mtc0 t0,CP0_STATUS + move a0,t1 + jal do_IRQ + move a1,sp # delay slot + mfc0 t0,CP0_STATUS # disable IRQs + ori t0,1 + xori t0,1 + la v0,ret_from_sys_call + jr s2 + mtc0 t0,CP0_STATUS # delay slot + END(interrupt) + + .align 5 + NESTED(fast_interrupt, FR_SIZE, sp) + move s2,ra + move a0,t1 + jal do_fast_IRQ + move a1,sp # delay slot + lui v0,%hi(return) + jr s2 + addiu v0,%lo(return) # delay slot + END(fast_interrupt) + + LEAF(bad_interrupt) + /* + * Don't return & unblock the pic + */ + j return + nop + END(bad_interrupt) + + .align 5 + LEAF(spurious_interrupt) + /* + * Nothing happened... (whistle) + */ + lui t1,%hi(spurious_count) + lw t0,%lo(spurious_count)(t1) + la v0,return + addiu t0,1 + jr ra + sw t0,%lo(spurious_count)(t1) + END(spurious_interrupt) + + + +/* + * Build a default exception handler for the other R4x00 exceptions + */ +#define BUILD_HANDLER(exception) \ + .align 5; \ + NESTED(handle_##exception, FR_SIZE, sp); \ + .set noat; \ + SAVE_ALL; \ + STI; \ + .set at; \ + la a1,8f; \ + TEXT (#exception); \ + lw a2,FR_EPC(sp); \ + PRINT("Got %s at %08x.\n"); \ + li a0,0; \ + li t0,-1; /* not a sys call */ \ + sw t0,FR_ORIG_REG2(sp); \ + jal do_##exception; \ + move a0,sp; /* delay slot */ \ + j ret_from_sys_call; \ + nop; /* delay slot */ \ + END(handle_##exception) + + BUILD_HANDLER(adel) + BUILD_HANDLER(ades) + BUILD_HANDLER(ibe) + BUILD_HANDLER(dbe) + BUILD_HANDLER(ov) + BUILD_HANDLER(fpe) + BUILD_HANDLER(bp) + BUILD_HANDLER(tr) + BUILD_HANDLER(ri) + BUILD_HANDLER(cpu) + BUILD_HANDLER(vcei) + BUILD_HANDLER(vced) + BUILD_HANDLER(watch) + BUILD_HANDLER(reserved) + + +/* + * Exception handler table with 32 entries. + * This might be extended to handle software exceptions + */ + .bss + .align 2 + EXPORT(exception_handlers) + .fill 32,4,0 + +/* + * Table of syscalls + */ + .data + EXPORT(sys_call_table) + PTR sys_setup /* 0 */ + PTR sys_exit + PTR sys_fork + PTR sys_read + PTR sys_write + PTR sys_open /* 5 */ + PTR sys_close + PTR sys_waitpid + PTR sys_creat + PTR sys_link + PTR sys_unlink /* 10 */ + PTR sys_execve + PTR sys_chdir + PTR sys_time + PTR sys_mknod + PTR sys_chmod /* 15 */ + PTR sys_chown + PTR sys_break + PTR sys_stat + PTR sys_lseek + PTR sys_getpid /* 20 */ + PTR sys_mount + PTR sys_umount + PTR sys_setuid + PTR sys_getuid + PTR sys_stime /* 25 */ + PTR sys_ptrace + PTR sys_alarm + PTR sys_fstat + PTR sys_pause + PTR sys_utime /* 30 */ + PTR sys_stty + PTR sys_gtty + PTR sys_access + PTR sys_nice + PTR sys_ftime /* 35 */ + PTR sys_sync + PTR sys_kill + PTR sys_rename + PTR sys_mkdir + PTR sys_rmdir /* 40 */ + PTR sys_dup + PTR sys_pipe + PTR sys_times + PTR sys_prof + PTR sys_brk /* 45 */ + PTR sys_setgid + PTR sys_getgid + PTR sys_signal + PTR sys_geteuid + PTR sys_getegid /* 50 */ + PTR sys_acct + PTR sys_phys + PTR sys_lock + PTR sys_ioctl + PTR sys_fcntl /* 55 */ + PTR sys_mpx + PTR sys_setpgid + PTR sys_ulimit + PTR sys_olduname + PTR sys_umask /* 60 */ + PTR sys_chroot + PTR sys_ustat + PTR sys_dup2 + PTR sys_getppid + PTR sys_getpgrp /* 65 */ + PTR sys_setsid + PTR sys_sigaction + PTR sys_sgetmask + PTR sys_ssetmask + PTR sys_setreuid /* 70 */ + PTR sys_setregid + PTR sys_sigsuspend + PTR sys_sigpending + PTR sys_sethostname + PTR sys_setrlimit /* 75 */ + PTR sys_getrlimit + PTR sys_getrusage + PTR sys_gettimeofday + PTR sys_settimeofday + PTR sys_getgroups /* 80 */ + PTR sys_setgroups + PTR sys_select + PTR sys_symlink + PTR sys_lstat + PTR sys_readlink /* 85 */ + PTR sys_uselib + PTR sys_swapon + PTR sys_reboot + PTR old_readdir + PTR sys_mmap /* 90 */ + PTR sys_munmap + PTR sys_truncate + PTR sys_ftruncate + PTR sys_fchmod + PTR sys_fchown /* 95 */ + PTR sys_getpriority + PTR sys_setpriority + PTR sys_profil + PTR sys_statfs + PTR sys_fstatfs /* 100 */ + PTR sys_ioperm + PTR sys_socketcall + PTR sys_syslog + PTR sys_setitimer + PTR sys_getitimer /* 105 */ + PTR sys_newstat + PTR sys_newlstat + PTR sys_newfstat + PTR sys_uname + PTR sys_iopl /* 110 */ + PTR sys_vhangup + PTR sys_idle + PTR sys_vm86 + PTR sys_wait4 + PTR sys_swapoff /* 115 */ + PTR sys_sysinfo + PTR sys_ipc + PTR sys_fsync + PTR sys_sigreturn + PTR sys_clone /* 120 */ + PTR sys_setdomainname + PTR sys_newuname + PTR 0 #sys_modify_ldt + PTR sys_adjtimex + PTR sys_mprotect /* 125 */ + PTR sys_sigprocmask + PTR sys_create_module + PTR sys_init_module + PTR sys_delete_module + PTR sys_get_kernel_syms /* 130 */ + PTR sys_quotactl + PTR sys_getpgid + PTR sys_fchdir + PTR sys_bdflush + PTR sys_sysfs /* 135 */ + PTR sys_personality + PTR 0 /* for afs_syscall */ + PTR sys_setfsuid + PTR sys_setfsgid + PTR sys_llseek /* 140 */ + PTR sys_getdents + PTR sys_select + PTR sys_flock + .space (NR_syscalls-140)*4 + + .bss + EXPORT(IRQ_vectors) + .fill 16,4,0 + + .text +sys_call_names: + TTABLE ("setup") + TTABLE ("exit") + TTABLE ("fork") + TTABLE ("read") + TTABLE ("write") + TTABLE ("open") + TTABLE ("close") + TTABLE ("waitpid") + TTABLE ("creat") + TTABLE ("link") + TTABLE ("unlink") + TTABLE ("execve") + TTABLE ("chdir") + TTABLE ("time") + TTABLE ("mknod") + TTABLE ("chmod") + TTABLE ("chown") + TTABLE ("break") + TTABLE ("stat") + TTABLE ("lseek") + TTABLE ("getpid") + TTABLE ("mount") + TTABLE ("umount") + TTABLE ("setuid") + TTABLE ("getuid") + TTABLE ("stime") + TTABLE ("ptrace") + TTABLE ("alarm") + TTABLE ("fstat") + TTABLE ("pause") + TTABLE ("utime") + TTABLE ("stty") + TTABLE ("gtty") + TTABLE ("access") + TTABLE ("nice") + TTABLE ("ftime") + TTABLE ("sync") + TTABLE ("kill") + TTABLE ("rename") + TTABLE ("mkdir") + TTABLE ("rmdir") + TTABLE ("dup") + TTABLE ("pipe") + TTABLE ("times") + TTABLE ("prof") + TTABLE ("brk") + TTABLE ("setgid") + TTABLE ("getgid") + TTABLE ("signal") + TTABLE ("geteuid") + TTABLE ("getegid") + TTABLE ("acct") + TTABLE ("phys") + TTABLE ("lock") + TTABLE ("ioctl") + TTABLE ("fcntl") + TTABLE ("mpx") + TTABLE ("setpgid") + TTABLE ("ulimit") + TTABLE ("olduname") + TTABLE ("umask") + TTABLE ("chroot") + TTABLE ("ustat") + TTABLE ("dup2") + TTABLE ("getppid") + TTABLE ("getpgrp") + TTABLE ("setsid") + TTABLE ("sigaction") + TTABLE ("sgetmask") + TTABLE ("ssetmask") + TTABLE ("setreuid") + TTABLE ("setregid") + TTABLE ("sigsuspend") + TTABLE ("sigpending") + TTABLE ("sethostname") + TTABLE ("setrlimit") + TTABLE ("getrlimit") + TTABLE ("getrusage") + TTABLE ("gettimeofday") + TTABLE ("settimeofday") + TTABLE ("getgroups") + TTABLE ("setgroups") + TTABLE ("select") + TTABLE ("symlink") + TTABLE ("lstat") + TTABLE ("readlink") + TTABLE ("uselib") + TTABLE ("swapon") + TTABLE ("reboot") + TTABLE ("readdir") + TTABLE ("mmap") + TTABLE ("munmap") + TTABLE ("truncate") + TTABLE ("ftruncate") + TTABLE ("fchmod") + TTABLE ("fchown") + TTABLE ("getpriority") + TTABLE ("setpriority") + TTABLE ("profil") + TTABLE ("statfs") + TTABLE ("fstatfs") + TTABLE ("ioperm") + TTABLE ("socketcall") + TTABLE ("syslog") + TTABLE ("setitimer") + TTABLE ("getitimer") + TTABLE ("newstat") + TTABLE ("newlstat") + TTABLE ("newfstat") + TTABLE ("uname") + TTABLE ("iopl") + TTABLE ("vhangup") + TTABLE ("idle") + TTABLE ("vm86") + TTABLE ("wait4") + TTABLE ("swapoff") + TTABLE ("sysinfo") + TTABLE ("ipc") + TTABLE ("fsync") + TTABLE ("sigreturn") + TTABLE ("clone") + TTABLE ("setdomainname") + TTABLE ("newuname") + TTABLE ("modify_ldt (unused)") + TTABLE ("adjtimex") + TTABLE ("mprotect") + TTABLE ("sigprocmask") + TTABLE ("create_module") + TTABLE ("init_module") + TTABLE ("delete_module") + TTABLE ("get_kernel_syms") + TTABLE ("quotactl") + TTABLE ("getpgid") + TTABLE ("fchdir") + TTABLE ("bdflush") + TTABLE ("sysfs") + TTABLE ("personality") + TTABLE ("afs_syscall") /* for afs_syscall */ + TTABLE ("setfsuid") + TTABLE ("setfsgid") + TTABLE ("llseek") + TTABLE ("sys_getdents") + TTABLE ("sys_select") + TTABLE ("sys_flock") diff --git a/arch/mips/kernel/gdb-low.S b/arch/mips/kernel/gdb-low.S new file mode 100644 index 000000000..ea775e732 --- /dev/null +++ b/arch/mips/kernel/gdb-low.S @@ -0,0 +1,300 @@ +/* + * arch/mips/kernel/gdb-low.S + * + * gdb-low.S contains the low-level trap handler for the GDB stub. + * + * Copyright (C) 1995 Andreas Busse + */ + +#include <linux/sys.h> + +#include <asm/asm.h> +#include <asm/segment.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/stackframe.h> +#include <asm/gdb-stub.h> + +/* + * The low level trap handler + */ + .align 5 + NESTED(trap_low, GDB_FR_SIZE, sp) + .set noat + .set noreorder + + mfc0 k0,CP0_STATUS + sll k0,3 /* extract cu0 bit */ + bltz k0,1f + move k1,sp + + /* + * Called from user mode, new stack + */ + lui k1,%hi(kernelsp) + lw k1,%lo(kernelsp)(k1) +1: move k0,sp + subu sp,k1,GDB_FR_SIZE + sw k0,GDB_FR_REG29(sp) + sw v0,GDB_FR_REG2(sp) + +/* + * first save the CP0 and special registers + */ + + mfc0 v0,CP0_STATUS + sw v0,GDB_FR_STATUS(sp) + mfc0 v0,CP0_CAUSE + sw v0,GDB_FR_CAUSE(sp) + mfc0 v0,CP0_EPC + sw v0,GDB_FR_EPC(sp) + mfc0 v0,CP0_BADVADDR + sw v0,GDB_FR_BADVADDR(sp) + mfhi v0 + sw v0,GDB_FR_HI(sp) + mflo v0 + sw v0,GDB_FR_LO(sp) + +/* + * Now the integer registers + */ + + sw zero,GDB_FR_REG0(sp) /* I know... */ + sw $1,GDB_FR_REG1(sp) + /* v0 already saved */ + sw v1,GDB_FR_REG3(sp) + sw a0,GDB_FR_REG4(sp) + sw a1,GDB_FR_REG5(sp) + sw a2,GDB_FR_REG6(sp) + sw a3,GDB_FR_REG7(sp) + sw t0,GDB_FR_REG8(sp) + sw t1,GDB_FR_REG9(sp) + sw t2,GDB_FR_REG10(sp) + sw t3,GDB_FR_REG11(sp) + sw t4,GDB_FR_REG12(sp) + sw t5,GDB_FR_REG13(sp) + sw t6,GDB_FR_REG14(sp) + sw t7,GDB_FR_REG15(sp) + sw s0,GDB_FR_REG16(sp) + sw s1,GDB_FR_REG17(sp) + sw s2,GDB_FR_REG18(sp) + sw s3,GDB_FR_REG19(sp) + sw s4,GDB_FR_REG20(sp) + sw s5,GDB_FR_REG21(sp) + sw s6,GDB_FR_REG22(sp) + sw s7,GDB_FR_REG23(sp) + sw t8,GDB_FR_REG24(sp) + sw t9,GDB_FR_REG25(sp) + sw k0,GDB_FR_REG26(sp) + sw k1,GDB_FR_REG27(sp) + sw gp,GDB_FR_REG28(sp) + /* sp already saved */ + sw fp,GDB_FR_REG30(sp) + sw ra,GDB_FR_REG31(sp) + + STI /* disable interrupts */ + +/* + * Followed by the floating point registers + */ + mfc0 v0,CP0_STATUS /* check if the FPU is enabled */ + srl v0,v0,16 + andi v0,v0,(ST0_CU1 >> 16) + beqz v0,2f /* disabled, skip */ + nop + + swc1 $0,GDB_FR_FPR0(sp) + swc1 $1,GDB_FR_FPR1(sp) + swc1 $2,GDB_FR_FPR2(sp) + swc1 $3,GDB_FR_FPR3(sp) + swc1 $4,GDB_FR_FPR4(sp) + swc1 $5,GDB_FR_FPR5(sp) + swc1 $6,GDB_FR_FPR6(sp) + swc1 $7,GDB_FR_FPR7(sp) + swc1 $8,GDB_FR_FPR8(sp) + swc1 $9,GDB_FR_FPR9(sp) + swc1 $10,GDB_FR_FPR10(sp) + swc1 $11,GDB_FR_FPR11(sp) + swc1 $12,GDB_FR_FPR12(sp) + swc1 $13,GDB_FR_FPR13(sp) + swc1 $14,GDB_FR_FPR14(sp) + swc1 $15,GDB_FR_FPR15(sp) + swc1 $16,GDB_FR_FPR16(sp) + swc1 $17,GDB_FR_FPR17(sp) + swc1 $18,GDB_FR_FPR18(sp) + swc1 $19,GDB_FR_FPR19(sp) + swc1 $20,GDB_FR_FPR20(sp) + swc1 $21,GDB_FR_FPR21(sp) + swc1 $22,GDB_FR_FPR22(sp) + swc1 $23,GDB_FR_FPR23(sp) + swc1 $24,GDB_FR_FPR24(sp) + swc1 $25,GDB_FR_FPR25(sp) + swc1 $26,GDB_FR_FPR26(sp) + swc1 $27,GDB_FR_FPR27(sp) + swc1 $28,GDB_FR_FPR28(sp) + swc1 $29,GDB_FR_FPR29(sp) + swc1 $30,GDB_FR_FPR30(sp) + swc1 $31,GDB_FR_FPR31(sp) + +/* + * FPU control registers + */ + + mfc1 v0,CP1_STATUS + sw v0,GDB_FR_FSR(sp) + mfc1 v0,CP1_REVISION + sw v0,GDB_FR_FIR(sp) + +/* + * current stack frame ptr + */ + +2: sw sp,GDB_FR_FRP(sp) + +/* + * CP0 registers (R4000/R4400 unused registers skipped) + */ + + mfc0 v0,CP0_INDEX + sw v0,GDB_FR_CP0_INDEX(sp) + mfc0 v0,CP0_RANDOM + sw v0,GDB_FR_CP0_RANDOM(sp) + mfc0 v0,CP0_ENTRYLO0 + sw v0,GDB_FR_CP0_ENTRYLO0(sp) + mfc0 v0,CP0_ENTRYLO1 + sw v0,GDB_FR_CP0_ENTRYLO1(sp) + mfc0 v0,CP0_PAGEMASK + sw v0,GDB_FR_CP0_PAGEMASK(sp) + mfc0 v0,CP0_WIRED + sw v0,GDB_FR_CP0_WIRED(sp) + mfc0 v0,CP0_ENTRYHI + sw v0,GDB_FR_CP0_ENTRYHI(sp) + mfc0 v0,CP0_PRID + sw v0,GDB_FR_CP0_PRID(sp) + + .set at + +/* + * continue with the higher level handler + */ + + move a0,sp + jal handle_exception + nop + +/* + * restore all writable registers, in reverse order + */ + + .set noat + + lw v0,GDB_FR_CP0_ENTRYHI(sp) + lw v1,GDB_FR_CP0_WIRED(sp) + mtc0 v0,CP0_ENTRYHI + mtc0 v1,CP0_WIRED + lw v0,GDB_FR_CP0_PAGEMASK(sp) + lw v1,GDB_FR_CP0_ENTRYLO1(sp) + mtc0 v0,CP0_PAGEMASK + mtc0 v1,CP0_ENTRYLO1 + lw v0,GDB_FR_CP0_ENTRYLO0(sp) + lw v1,GDB_FR_CP0_INDEX(sp) + mtc0 v0,CP0_ENTRYLO0 + mtc0 v1,CP0_INDEX + +/* + * Next, the floating point registers + */ + mfc0 v0,CP0_STATUS /* check if the FPU is enabled */ + srl v0,v0,16 + andi v0,v0,(ST0_CU1 >> 16) + beqz v0,3f /* disabled, skip */ + nop + + lwc1 $31,GDB_FR_FPR31(sp) + lwc1 $30,GDB_FR_FPR30(sp) + lwc1 $29,GDB_FR_FPR29(sp) + lwc1 $28,GDB_FR_FPR28(sp) + lwc1 $27,GDB_FR_FPR27(sp) + lwc1 $26,GDB_FR_FPR26(sp) + lwc1 $25,GDB_FR_FPR25(sp) + lwc1 $24,GDB_FR_FPR24(sp) + lwc1 $23,GDB_FR_FPR23(sp) + lwc1 $22,GDB_FR_FPR22(sp) + lwc1 $21,GDB_FR_FPR21(sp) + lwc1 $20,GDB_FR_FPR20(sp) + lwc1 $19,GDB_FR_FPR19(sp) + lwc1 $18,GDB_FR_FPR18(sp) + lwc1 $17,GDB_FR_FPR17(sp) + lwc1 $16,GDB_FR_FPR16(sp) + lwc1 $15,GDB_FR_FPR15(sp) + lwc1 $14,GDB_FR_FPR14(sp) + lwc1 $13,GDB_FR_FPR13(sp) + lwc1 $12,GDB_FR_FPR12(sp) + lwc1 $11,GDB_FR_FPR11(sp) + lwc1 $10,GDB_FR_FPR10(sp) + lwc1 $9,GDB_FR_FPR9(sp) + lwc1 $8,GDB_FR_FPR8(sp) + lwc1 $7,GDB_FR_FPR7(sp) + lwc1 $6,GDB_FR_FPR6(sp) + lwc1 $5,GDB_FR_FPR5(sp) + lwc1 $4,GDB_FR_FPR4(sp) + lwc1 $3,GDB_FR_FPR3(sp) + lwc1 $2,GDB_FR_FPR2(sp) + lwc1 $1,GDB_FR_FPR1(sp) + lwc1 $0,GDB_FR_FPR0(sp) + +/* + * Now the CP0 and integer registers + */ + +3: mfc0 t0,CP0_STATUS + ori t0,0x1f + xori t0,0x1f + mtc0 t0,CP0_STATUS + + lw v0,GDB_FR_STATUS(sp) + lw v1,GDB_FR_EPC(sp) + mtc0 v0,CP0_STATUS + mtc0 v1,CP0_EPC + lw v0,GDB_FR_HI(sp) + lw v1,GDB_FR_LO(sp) + mthi v0 + mtlo v0 + lw ra,GDB_FR_REG31(sp) + lw fp,GDB_FR_REG30(sp) + lw gp,GDB_FR_REG28(sp) + lw k1,GDB_FR_REG27(sp) + lw k0,GDB_FR_REG26(sp) + lw t9,GDB_FR_REG25(sp) + lw t8,GDB_FR_REG24(sp) + lw s7,GDB_FR_REG23(sp) + lw s6,GDB_FR_REG22(sp) + lw s5,GDB_FR_REG21(sp) + lw s4,GDB_FR_REG20(sp) + lw s3,GDB_FR_REG19(sp) + lw s2,GDB_FR_REG18(sp) + lw s1,GDB_FR_REG17(sp) + lw s0,GDB_FR_REG16(sp) + lw t7,GDB_FR_REG15(sp) + lw t6,GDB_FR_REG14(sp) + lw t5,GDB_FR_REG13(sp) + lw t4,GDB_FR_REG12(sp) + lw t3,GDB_FR_REG11(sp) + lw t2,GDB_FR_REG10(sp) + lw t1,GDB_FR_REG9(sp) + lw t0,GDB_FR_REG8(sp) + lw a3,GDB_FR_REG7(sp) + lw a2,GDB_FR_REG6(sp) + lw a1,GDB_FR_REG5(sp) + lw a0,GDB_FR_REG4(sp) + lw v1,GDB_FR_REG3(sp) + lw v0,GDB_FR_REG2(sp) + lw $1,GDB_FR_REG1(sp) + lw sp,GDB_FR_REG29(sp) /* Deallocate stack */ + + ERET + .set at + .set reorder + END(trap_low) + +/* end of file gdb-low.S */ diff --git a/arch/mips/kernel/gdb-stub.c b/arch/mips/kernel/gdb-stub.c new file mode 100644 index 000000000..7708feac0 --- /dev/null +++ b/arch/mips/kernel/gdb-stub.c @@ -0,0 +1,748 @@ +/* + * arch/mips/kernel/gdb-stub.c + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/kernel.h> + +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/segment.h> +#include <asm/cachectl.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void fltr_set_mem_err(void); +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); +extern void adel(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static void set_mem_fault_trap(int enable); +static int computeSignal(int tt); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); +void handle_exception(struct gdb_regs *regs); +static void show_gdbregs(struct gdb_regs *regs); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static int initialized = 0; /* !0 means we've been initialized */ +static const char hexchars[]="0123456789abcdef"; + + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + +/* + * Indicate to caller of mem2hex or hex2mem that there + * has been an error. + */ +static volatile int mem_err = 0; + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + while (count-- > 0) { + ch = *(mem++); + if (mem_err) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + +/* set_mem_fault_trap(0); */ + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +static char *hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + for (i=0; i<count; i++) + { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *(mem++) = ch; + if (mem_err) + return 0; + } + +/* set_mem_fault_trap(0); */ + + return mem; +} + +/* + * This table contains the mapping between SPARC hardware trap types, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static struct hard_trap_info +{ + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 4, SIGBUS }, /* address error (load) */ + { 5, SIGBUS }, /* address error (store) */ + { 6, SIGBUS }, /* instruction bus error */ + { 7, SIGBUS }, /* data bus error */ + { 9, SIGTRAP }, /* break */ + { 10, SIGILL }, /* reserved instruction */ +/* { 11, SIGILL }, */ /* cpu unusable */ + { 12, SIGFPE }, /* overflow */ + { 13, SIGTRAP }, /* trap */ + { 14, SIGSEGV }, /* virtual instruction cache coherency */ + { 15, SIGFPE }, /* floating point exception */ + { 23, SIGSEGV }, /* watch */ + { 31, SIGSEGV }, /* virtual data cache coherency */ + { 0, 0} /* Must be last */ +}; + + +/* + * Set up exception handlers for tracing and breakpoints + */ +void set_debug_traps(void) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + set_except_vector(ht->tt, trap_low); + + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + + putDebugChar ('+'); + initialized = 1; + + breakpoint(); +} + + +/* + * Trap handler for memory errors. This just sets mem_err to be non-zero. It + * assumes that %l1 is non-zero. This should be safe, as it is doubtful that + * 0 would ever contain code that could mem fault. This routine will skip + * past the faulting instruction after setting mem_err. + */ +extern void fltr_set_mem_err(void) +{ + /* FIXME: Needs to be written... */ +} + + +static void set_mem_fault_trap(int enable) +{ + mem_err = 0; + +#if 0 + if (enable) + exceptionHandler(9, fltr_set_mem_err); + else + exceptionHandler(9, trap_low); +#endif +} + +/* + * Convert the MIPS hardware trap type code to a unix signal number. + */ +static int computeSignal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +/* + * This function does all command procesing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +void handle_exception (struct gdb_regs *regs) +{ + int trap; /* Trap type */ + int sigval; + int addr; + int length; + char *ptr; + unsigned long *stack; + +#if 0 + printk("in handle_exception()\n"); + show_gdbregs(regs); +#endif + + /* + * First check trap type. If this is CPU_UNUSABLE and CPU_ID is 1, + * the simply switch the FPU on and return since this is no error + * condition. kernel/traps.c does the same. + * FIXME: This doesn't work yet, so we don't catch CPU_UNUSABLE + * traps for now. + */ + trap = (regs->cp0_cause & 0x7c) >> 2; +/* printk("trap=%d\n",trap); */ + if (trap == 11) { + if (((regs->cp0_cause >> CAUSEB_CE) & 3) == 1) { + regs->cp0_status |= ST0_CU1; + return; + } + } + + /* + * If we're in breakpoint() increment the PC + */ + if (trap == 9 && regs->cp0_epc == (unsigned long)breakinst) + regs->cp0_epc += 4; + + stack = (long *)regs->reg29; /* stack ptr */ + sigval = computeSignal(trap); + + /* + * reply to host that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[REG_EPC >> 4]; + *ptr++ = hexchars[REG_EPC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->cp0_epc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[REG_FP >> 4]; + *ptr++ = hexchars[REG_FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg30, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + *ptr++ = hexchars[REG_SP >> 4]; + *ptr++ = hexchars[REG_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->reg29, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + ptr = output_buffer; + ptr = mem2hex((char *)®s->reg0, ptr, 32*4, 0); /* r0...r31 */ + ptr = mem2hex((char *)®s->cp0_status, ptr, 6*4, 0); /* cp0 */ + ptr = mem2hex((char *)®s->fpr0, ptr, 32*4, 0); /* f0...31 */ + ptr = mem2hex((char *)®s->cp1_fsr, ptr, 2*4, 0); /* cp1 */ + ptr = mem2hex((char *)®s->frame_ptr, ptr, 2*4, 0); /* frp */ + ptr = mem2hex((char *)®s->cp0_index, ptr, 16*4, 0); /* cp0 */ + break; + + /* + * set the value of the CPU registers - return OK + * FIXME: Needs to be written + */ + case 'G': + { +#if 0 + unsigned long *newsp, psr; + + ptr = &input_buffer[1]; + hex2mem(ptr, (char *)registers, 16 * 4, 0); /* G & O regs */ + + /* + * See if the stack pointer has moved. If so, then copy the + * saved locals and ins to the new location. + */ + + newsp = (unsigned long *)registers[SP]; + if (sp != newsp) + sp = memcpy(newsp, sp, 16 * 4); + +#endif + strcpy(output_buffer,"OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->cp0_epc = addr; + + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + + sys_cacheflush((void *)KSEG0,KSEG1-KSEG0,BCACHE); + return; + /* NOTREACHED */ + break; + + + /* + * kill the program + */ + case 'k' : + break; /* do nothing */ + + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + + /* + * Step to next instruction + * FIXME: Needs to be written + */ + case 's': + strcpy (output_buffer, "S01"); + break; + + /* + * Set baud rate (bBB) + * FIXME: Needs to be written + */ + case 'b': + { +#if 0 + int baudrate; + extern void set_timer_3(); + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) + { + strcpy(output_buffer,"B01"); + break; + } + + /* Convert baud rate to uart clock divider */ + + switch (baudrate) + { + case 38400: + baudrate = 16; + break; + case 19200: + baudrate = 33; + break; + case 9600: + baudrate = 65; + break; + default: + baudrate = 0; + strcpy(output_buffer,"B02"); + goto x1; + } + + if (baudrate) { + putpacket("OK"); /* Ack before changing speed */ + set_timer_3(baudrate); /* Set it */ + } +#endif + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!initialized) + return; + + __asm__ __volatile__(" + .globl breakinst + .set noreorder + nop +breakinst: break + nop + .set reorder + "); +} + +void adel(void) +{ + __asm__ __volatile__(" + .globl adel + la $8,0x80000001 + lw $9,0($8) + "); +} + +/* + * Print registers (on target console) + * Used only to debug the stub... + */ +void show_gdbregs(struct gdb_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg0, regs->reg1, regs->reg2, regs->reg3, + regs->reg4, regs->reg5, regs->reg6, regs->reg7); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg8, regs->reg9, regs->reg10, regs->reg11, + regs->reg12, regs->reg13, regs->reg14, regs->reg15); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg16, regs->reg17, regs->reg18, regs->reg19, + regs->reg20, regs->reg21, regs->reg22, regs->reg23); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg24, regs->reg25, regs->reg26, regs->reg27, + regs->reg28, regs->reg29, regs->reg30, regs->reg31); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", + regs->cp0_epc, regs->cp0_status, regs->cp0_cause); +} diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S new file mode 100644 index 000000000..a2cb43de3 --- /dev/null +++ b/arch/mips/kernel/head.S @@ -0,0 +1,412 @@ +/* + * arch/mips/kernel/head.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle and Andreas Busse + * + * Head.S contains the MIPS exception handler and startup code. + */ +#include <linux/tasks.h> + +#include <asm/asm.h> +#include <asm/segment.h> +#include <asm/cachectl.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/stackframe.h> +#include <asm/bootinfo.h> + +#define PAGE_SIZE 0x1000 + +#define MODE_GLOBAL 0x0001 /* shared for all processes */ +#define MODE_ALIAS 0x0016 /* uncachable */ + + .text + .set mips3 +/* + * This is space for the interrupt handlers. + * They are located at virtual address KSEG[01] (physical 0x0) + */ + /* + * TLB refill, EXL == 0 + */ + .set noreorder + .set noat + LEAF(except_vec0) + dmfc0 k1,CP0_CONTEXT + dsra k1,1 + lwu k0,(k1) # May cause another exception + lwu k1,4(k1) + dsrl k0,6 # Convert to EntryLo format + dsrl k1,6 # Convert to EntryLo format + dmtc0 k0,CP0_ENTRYLO0 + dmtc0 k1,CP0_ENTRYLO1 + nop # Needed for R4[04]00 pipeline + tlbwr + nop # Needed for R4[04]00 pipeline + nop + nop + eret + /* + * Workaround for R4000 bug. For explanation see MIPS + * docs. Note that this that obscure that it wont almost + * never happen. Well, but Mips writes about it's bugs. + */ + nop + eret + END(except_vec0) + + /* + * XTLB refill, EXL == 0 + * Should never be reached + */ + .org except_vec0+0x80 + LEAF(except_vec1) + PANIC("XTLB Refill exception.\n") +1: j 1b + nop + END(except_vec1) + + /* + * Cache Error + */ + .org except_vec1+0x80 + LEAF(except_vec2) + /* + * Famous last words: unreached + */ + mfc0 a1,CP0_ERROREPC + PRINT("Cache error exception: c0_errorepc == %08x\n") +1: j 1b + nop + END(except_vec2) + + /* + * General exception vector. + */ + .org except_vec2+0x80 + NESTED(except_vec3, 0, sp) + .set noat + /* + * Register saving is delayed as long as we don't know + * which registers really need to be saved. + */ + mfc0 k1,CP0_CAUSE + la k0,exception_handlers + /* + * Next lines assumes that the used CPU type has max. + * 32 different types of exceptions. We might use this + * to implement software exceptions in the future. + */ + andi k1,0x7c + addu k0,k1 + lw k0,(k0) + NOP + jr k0 + nop + END(except_vec3) + .set at + +/******************************************************************************/ + +/* + * Kernel entry + */ + .set noreorder + NESTED(kernel_entry, 16, sp) + /* + * Clear BSS first so that there are no surprises... + */ + la t0,_edata + la t1,_end + sw zero,(t0) +1: addiu t0,4 + bnel t0,t1,1b + sw zero,(t0) + + /* + * Initialize low level part of memory management + */ + jal tlbflush + mtc0 zero,CP0_WIRED # delay slot + jal wire_mappings + nop + jal tlbflush + nop + + /* + * Stack for kernel and init + */ + la sp,init_user_stack+PAGE_SIZE-24 + la t0,init_kernel_stack+PAGE_SIZE + sw t0,kernelsp + + /* + * Disable coprocessors + */ + mfc0 t0,CP0_STATUS + li t1,~(ST0_CU0|ST0_CU1|ST0_CU2|ST0_CU3) + and t0,t1 + mtc0 t0,CP0_STATUS + +1: jal start_kernel + nop # delay slot + /* + * Main should never return here, but + * just in case, we know what happens. + */ + b 1b + nop # delay slot + END(kernel_entry) + +/* + * wire_mappings - used to map hardware registers + */ + LEAF(wire_mappings) + /* + * Get base address of map0 table for the + * the board we're running on + */ + la t0,boot_info + lw t1,OFFSET_BOOTINFO_MACHTYPE(t0) + la t0,map0table + sll t1,PTRLOG # machtype used as index + addu t0,t1 + lw t0,(t0) # get base address + + /* + * Get number of wired TLB entries and + * loop over selected map0 table. + */ + lw t1,(t0) # number of wired TLB entries + move t2,zero # TLB entry counter + addiu t3,t1,1 # wire one additional entry + beqz t1,2f # null, exit + mtc0 t3,CP0_WIRED # delay slot + addiu t0,8 +1: lw t4,24(t0) # PageMask + ld t5,0(t0) # entryHi + ld t6,8(t0) # entryLo0 + ld t7,16(t0) # entryLo1 + addiu t2,1 # increment ctr + mtc0 t2,CP0_INDEX # set TLB entry + mtc0 t4,CP0_PAGEMASK + dmtc0 t5,CP0_ENTRYHI + dmtc0 t6,CP0_ENTRYLO0 + dmtc0 t7,CP0_ENTRYLO1 + addiu t0,32 + bne t1,t2,1b # next TLB entry + tlbwi # delay slot + + /* + * We use only 4k pages. Therefore the PageMask register + * is expected to be setup for 4k pages. + */ +2: li t0,PM_4K + mtc0 t0,CP0_PAGEMASK + + /* + * Now map the pagetables + */ + mtc0 zero,CP0_INDEX + la t0,TLB_ROOT + dmtc0 t0,CP0_ENTRYHI + la t0,swapper_pg_dir-KSEG1 + srl t0,6 + ori t0,(MODE_ALIAS|MODE_GLOBAL) # uncachable, dirty, valid + dmtc0 t0,CP0_ENTRYLO0 + li t0,MODE_GLOBAL + dmtc0 t0,CP0_ENTRYLO1 + nop + tlbwi # delayed + + /* + * Load the context register with a value that allows + * it to be used as fast as possible in tlb exceptions. + * It is expected that this register's content will + * NEVER be changed. + */ + li t0,TLBMAP + dsll t0,1 + dmtc0 t0,CP0_CONTEXT + jr ra # delay slot + nop + END(wire_mappings) + +/* + * Just for debugging... + */ + .set noreorder + LEAF(beep) + lw t0,beepflag + nop + bnez t0,1f + lbu t0,0xe2000061 + xori t0,3 + sb t0,0xe2000061 + li t0,1 + sw t0,beepflag +1: jr ra + nop + END(beep) + + .bss +beepflag: .word 0 + .text + +/* + * Compute kernel code checksum to check kernel code against corruption + */ + LEAF(csum) + jal sys_cacheflush + move t8,ra # delay slot + li t0,KSEG1 + la t1,final + li t2,KSEG1 + or t0,t2 + or t1,t2 + move v0,zero +1: lw t2,(t0) + addiu t0,4 + bne t0,t1,1b + xor v0,t2 + jr t8 + nop + END(csum) +final: + + .data +/* + * Build an entry for table of wired entries + */ +#define MAPDATA(q1,q2,q3,w1) \ + .quad q1; \ + .quad q2; \ + .quad q3; \ + .word w1; \ + .word 0 + +/* + * Initial mapping tables for supported Mips boards. + * First item is always the number of wired TLB entries, + * following by EntryHi/EntryLo pairs and page mask. + * Since everything must be quad-aligned (8) we insert + * some dummy zeros. + */ + +/* + * Address table of mapping tables for supported Mips boards. + * Add your own stuff here but don't forget to define your + * target system in bootinfo.h + */ + +map0table: PTR map0_dummy # machtype = unknown + PTR map0_rpc # Deskstation rPC44 + PTR map0_tyne # Deskstation Tyne + PTR map0_pica61 # Acer Pica-61 + PTR map0_magnum4000 # MIPS Magnum 4000PC (RC4030) + +map0_dummy: .word 0 # 0 entries + + .align 3 +/* + * Initial mappings for Deskstation rPC boards. + * RB: Untested goodie - I don't have such a board. + */ +map0_rpc: .word 2 # no. of wired TLB entries + .word 0 # pad for alignment + +MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000001, PM_1M) # VESA DMA cache +MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memory space + +/* + * Initial mappings for Deskstation Tyne boards. + */ +map0_tyne: .word 2 # no. of wired TLB entries + .word 0 # pad for alignment + +MAPDATA(0xffffffffe0000000, 0x04020017, 0x00000001, PM_1M) # VESA DMA cache +MAPDATA(0xffffffffe2000000, 0x24000017, 0x04000017, PM_16M) # VESA I/O and memory space + +/* + * Initial mapping for ACER PICA-61 boards. + * FIXME: These are rather preliminary since many drivers, such as serial, + * parallel, scsi and ethernet need some changes to distinguish between "local" + * (built-in) and "optional" (ISA/PCI) I/O hardware. Local video ram is mapped + * to the same location as the bios maps it to. Console driver has been changed + * accordingly (new video type: VIDEO_TYPE_PICA_S3). + * FIXME: Remove or merge some of the mappings. + */ +map0_pica61: .word 7 # no. wired TLB entries + .word 0 # dummy + +MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000001, PM_64K) # Local I/O space +MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000001, PM_4K) # Interrupt source register +MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, PM_1M) # Local video control +MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, PM_1M) # Extended video control +MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, PM_4M) # Local video memory (BIOS mapping) +MAPDATA(0xffffffffe2000000, 0x02400017, 0x02440017, PM_16M) # ISA I/O and ISA memory space (both 16M) +MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, PM_4K) # PCR (???) + +/* + * Initial mapping for Mips Magnum 4000PC systems. + * Do you believe me now that the Acer and Mips boxes are nearly the same ? :-) + * FIXME: Remove or merge some of the mappings. + */ + +map0_magnum4000: + .word 8 # no. wired TLB entries + .word 0 # dummy + +MAPDATA(0xffffffffe1000000, 0x03ffc013, 0x00000001, 0x7e000) # 0 +MAPDATA(0xffffffffe0000000, 0x02000017, 0x00000001, 0x1e000) # 1 local I/O +MAPDATA(0xffffffffe0100000, 0x03c00017, 0x00000001, 0) # 2 IRQ source +MAPDATA(0xffffffffe0200000, 0x01800017, 0x01804017, 0x1fe000) # 3 local video ctrl +MAPDATA(0xffffffffe0400000, 0x01808017, 0x0180c017, 0x1fe000) # 4 ext. video ctrl +MAPDATA(0xffffffffe0800000, 0x01000017, 0x01010017, 0x7fe000) # 5 local video mem. +MAPDATA(0xffffffffe2000000, 0x02400017, 0x02440017, 0x1ffe000) # 6 ISA I/O and mem. +MAPDATA(0xffffffffffffe000, 0x00000001, 0x0001ffd7, 0) # 7 PCR + + + .text + + .org 0x1000 + .globl swapper_pg_dir +swapper_pg_dir = . + (KSEG1-KSEG0) + +/* + * The page tables are initialized to only 4MB here - the final page + * tables are set up later depending on memory size. + */ + .org 0x2000 + EXPORT(pg0) + + .org 0x3000 + EXPORT(empty_bad_page) + + .org 0x4000 + EXPORT(empty_bad_page_table) + + .org 0x5000 + EXPORT(empty_zero_page) + + .org 0x6000 + EXPORT(invalid_pte_table) + + .org 0x7000 + +/* + * floppy_track_buffer is used to buffer one track of floppy data: it + * has to be separate from the tmp_floppy area, as otherwise a single- + * sector read/write can mess it up. It can contain one full cylinder (sic) of + * data (36*2*512 bytes). + */ + EXPORT(floppy_track_buffer) + .fill 512*2*36,1,0 + + EXPORT(cache_error_buffer) + .fill 32*4,1,0 + + .data + EXPORT(kernelsp) + PTR 0 diff --git a/arch/mips/kernel/ioport.c b/arch/mips/kernel/ioport.c new file mode 100644 index 000000000..ff6c0d518 --- /dev/null +++ b/arch/mips/kernel/ioport.c @@ -0,0 +1,36 @@ +/* + * linux/arch/mips/kernel/ioport.c + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/ioport.h> + +/* + * This changes the io permissions bitmap in the current task. + */ +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) +{ + return -ENOSYS; +} + +unsigned int *stack; + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ +asmlinkage int sys_iopl(long ebx,long ecx,long edx, + long esi, long edi, long ebp, long eax, long ds, + long es, long fs, long gs, long orig_eax, + long eip,long cs,long eflags,long esp,long ss) +{ + return -ENOSYS; +} diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c new file mode 100644 index 000000000..608a0b431 --- /dev/null +++ b/arch/mips/kernel/irq.c @@ -0,0 +1,334 @@ +/* + * linux/arch/mips/kernel/irq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +/* + * Mips support by Ralf Baechle and Andreas Busse + * + * The Deskstation Tyne is almost completely like an IBM compatible PC with + * another type of microprocessor. Therefore this code is almost completely + * the same. More work needs to be done to support Acer PICA and other + * machines. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/timex.h> + +#include <asm/bitops.h> +#include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/jazz.h> +#include <asm/system.h> + +unsigned char cache_21 = 0xff; +unsigned char cache_A1 = 0xff; + +unsigned long spurious_count = 0; + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char mask; + + mask = 1 << (irq_nr & 7); + save_flags(flags); + if (irq_nr < 8) { + cli(); + cache_21 |= mask; + outb(cache_21,0x21); + restore_flags(flags); + return; + } + cli(); + cache_A1 |= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char mask; + + mask = ~(1 << (irq_nr & 7)); + save_flags(flags); + if (irq_nr < 8) { + cli(); + cache_21 &= mask; + outb(cache_21,0x21); + restore_flags(flags); + return; + } + cli(); + cache_A1 &= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +/* + * Pointers to the low-level handlers: first the general ones, then the + * fast ones, then the bad ones. + */ +extern void interrupt(void); +extern void fast_interrupt(void); +extern void bad_interrupt(void); + +/* + * Initial irq handlers. + */ +struct irqaction { + void (*handler)(int, struct pt_regs *); + unsigned long flags; + unsigned long mask; + const char *name; +}; + +static struct irqaction irq_action[16] = { + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action = irq_action; + + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s\n", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + } + return len; +} + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts enabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction * action = irq + irq_action; +#if 0 +if (irq > 0) { + printk("in do_IRQ with irq=%d\n",irq); +} +#endif + kstat.interrupts[irq]++; + action->handler(irq, regs); +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +asmlinkage void do_fast_IRQ(int irq) +{ + struct irqaction * action = irq + irq_action; + + kstat.interrupts[irq]++; + action->handler(irq, NULL); +} + +#define SA_PROBE SA_ONESHOT + +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) +{ + struct irqaction * action; + unsigned long flags; + + if (irq > 15) + return -EINVAL; + action = irq + irq_action; + if (action->handler) + return -EBUSY; + if (!handler) + return -EINVAL; + save_flags(flags); + cli(); + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ + /* + * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS??? + */ + if (action->flags & SA_INTERRUPT) + set_int_vector(irq,fast_interrupt); + else + set_int_vector(irq,interrupt); + } + if (irq < 8) { + cache_21 &= ~(1<<irq); + outb(cache_21,0x21); + } else { + cache_21 &= ~(1<<2); + cache_A1 &= ~(1<<(irq-8)); + outb(cache_21,0x21); + outb(cache_A1,0xA1); + } + restore_flags(flags); + return 0; +} + +void free_irq(unsigned int irq) +{ + struct irqaction * action = irq + irq_action; + unsigned long flags; + + if (irq > 15) { + printk("Trying to free IRQ%d\n",irq); + return; + } + if (!action->handler) { + printk("Trying to free free IRQ%d\n",irq); + return; + } + save_flags(flags); + cli(); + if (irq < 8) { + cache_21 |= 1 << irq; + outb(cache_21,0x21); + } else { + cache_A1 |= 1 << (irq-8); + outb(cache_A1,0xA1); + } + set_int_vector(irq,bad_interrupt); + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; + restore_flags(flags); +} + +static void no_action(int cpl, struct pt_regs * regs) { } + +unsigned int probe_irq_on (void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + /* first, snaffle up any unassigned irqs */ + for (i = 15; i > 0; i--) { + if (!request_irq(i, no_action, SA_PROBE, "probe")) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ + + /* now filter out any obviously spurious interrupts */ + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + for (i = 15; i > 0; i--) { + if (irqs & (1 << i) & irqmask) { + irqs ^= (1 << i); + free_irq(i); + } + } +#ifdef DEBUG + printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); +#endif + return irqs; +} + +int probe_irq_off (unsigned int irqs) +{ + unsigned int i, irqmask; + + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + for (i = 15; i > 0; i--) { + if (irqs & (1 << i)) { + free_irq(i); + } + } +#ifdef DEBUG + printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); +#endif + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (irqs & (1 << i))) + i = -i; + return i; +} + +void init_IRQ(void) +{ + int i; + + switch (boot_info.machtype) { + case MACH_MIPS_MAGNUM_4000: + case MACH_ACER_PICA_61: + r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, + JAZZ_IE_ETHERNET | + JAZZ_IE_SERIAL1 | + JAZZ_IE_SERIAL2 | + JAZZ_IE_PARALLEL | + JAZZ_IE_FLOPPY); + r4030_read_reg16(JAZZ_IO_IRQ_SOURCE); /* clear pending IRQs */ + set_cp0_status(ST0_IM, IE_IRQ4 | IE_IRQ1); + /* set the clock to 100 Hz */ + r4030_write_reg32(JAZZ_TIMER_INTERVAL, 9); + break; + case MACH_DESKSTATION_TYNE: + /* set the clock to 100 Hz */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ + + if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) + printk("Unable to get IRQ2 for cascade\n"); + break; + default: + panic("Unknown machtype in init_IRQ"); + } + + for (i = 0; i < 16 ; i++) + set_int_vector(i, bad_interrupt); + + /* initialize the bottom half routines. */ + for (i = 0; i < 32; i++) { + bh_base[i].routine = NULL; + bh_base[i].data = NULL; + } + bh_active = 0; + intr_count = 0; +} diff --git a/arch/mips/kernel/jazzdma.c b/arch/mips/kernel/jazzdma.c new file mode 100644 index 000000000..1d535e716 --- /dev/null +++ b/arch/mips/kernel/jazzdma.c @@ -0,0 +1,518 @@ +/* + * jazzdma.c + * + * Mips Jazz DMA controller support + * (C) 1995 Andreas Busse + * + * NOTE: Some of the argument checkings could be removed when + * things have settled down. Also, instead of returning 0xffffffff + * on failure of vdma_alloc() one could leave page #0 unused + * and return the more usual NULL pointer as logical address. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <asm/mipsregs.h> +#include <asm/mipsconfig.h> +#include <asm/jazz.h> +#include <asm/io.h> +#include <asm/segment.h> +#include <asm/dma.h> +#include <asm/jazzdma.h> + + +static unsigned long vdma_pagetable_start = 0; +static unsigned long vdma_pagetable_end = 0; + +/* + * Debug stuff + */ + +#define DEBUG_VDMA 0 +#define vdma_debug ((DEBUG_VDMA) ? debuglvl : 0) + +static int debuglvl = 3; + +/* + * Local prototypes + */ + +static void vdma_pgtbl_init(void); + + +/* + * Initialize the Jazz R4030 dma controller + */ + +unsigned long vdma_init(unsigned long memory_start, unsigned long memory_end) +{ + + /* + * Allocate 32k of memory for DMA page tables. + * This needs to be page aligned and should be + * uncached to avoid cache flushing after every + * update. + */ + + vdma_pagetable_start = KSEG1ADDR((memory_start + 4095) & ~ 4095); + vdma_pagetable_end = vdma_pagetable_start + VDMA_PGTBL_SIZE; + + + /* + * Clear the R4030 translation table + */ + + vdma_pgtbl_init(); + + r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE,PHYSADDR(vdma_pagetable_start)); + r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM,VDMA_PGTBL_SIZE); + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); + + printk("VDMA: R4030 DMA pagetables initialized.\n"); + return(KSEG0ADDR(vdma_pagetable_end)); +} + +/* + * Allocate DMA pagetables using a simple first-fit algorithm + */ + +unsigned long vdma_alloc(unsigned long paddr, unsigned long size) +{ + VDMA_PGTBL_ENTRY *entry = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + int first; + int last; + int pages; + unsigned int frame; + unsigned long laddr; + int i; + + /* check arguments */ + + if (paddr > 0x1fffffff) { + if (vdma_debug) + printk("vdma_alloc: Invalid physical address: %08lx\n",paddr); + return (VDMA_ERROR); /* invalid physical address */ + } + if (size > 0x400000 || size == 0) { + if (vdma_debug) + printk("vdma_alloc: Invalid size: %08lx\n",size); + return (VDMA_ERROR); /* invalid physical address */ + } + + /* find free chunk */ + + pages = (size + 4095) >> 12; /* no. of pages to allocate */ + first = 0; + while (1) { + while (entry[first].owner != VDMA_PAGE_EMPTY && first < VDMA_PGTBL_ENTRIES) + first++; + if (first+pages > VDMA_PGTBL_ENTRIES) /* nothing free */ + return (VDMA_ERROR); + + last = first+1; + while (entry[last].owner == VDMA_PAGE_EMPTY && last-first < pages) + last++; + + if (last-first == pages) + break; /* found */ + } + + /* mark pages as allocated */ + + laddr = (first << 12) + (paddr & (VDMA_PAGESIZE-1)); + frame = paddr & ~(VDMA_PAGESIZE-1); + + for (i=first; i<last; i++) { + entry[i].frame = frame; + entry[i].owner = laddr; + frame += VDMA_PAGESIZE; + } + + /* + * update translation table and + * return logical start address + */ + + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); + + if (vdma_debug > 1) + printk("vdma_alloc: Allocated %d pages starting from %08lx\n", + pages,laddr); + + if (vdma_debug > 2) { + printk("LADDR: "); + for (i=first; i<last; i++) + printk("%08x ",i<<12); + printk("\nPADDR: "); + for (i=first; i<last; i++) + printk("%08x ",entry[i].frame); + printk("\nOWNER: "); + for (i=first; i<last; i++) + printk("%08x ",entry[i].owner); + printk("\n"); + } + + return(laddr); +} + + +/* + * Free previously allocated dma translation pages + * Note that this does NOT change the translation table, + * it just marks the free'd pages as unused! + */ + +int vdma_free(unsigned long laddr) +{ + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + int i; + + i = laddr >> 12; + + if (pgtbl[i].owner != laddr) { + printk("vdma_free: trying to free other's dma pages, laddr=%8lx\n",laddr); + return -1; + } + + while (pgtbl[i].owner == laddr && i < VDMA_PGTBL_ENTRIES) { + pgtbl[i].owner = VDMA_PAGE_EMPTY; + i++; + } + + if (vdma_debug > 1) + printk("vdma_free: freed %ld pages starting from %08lx\n", + i-(laddr>>12),laddr); + + return 0; +} + +/* + * Map certain page(s) to another physical address. + * Caller must have allocated the page(s) before. + */ + +int vdma_remap(unsigned long laddr, unsigned long paddr, unsigned long size) +{ + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + int first; + int pages; + + if (laddr > 0xffffff) { + if (vdma_debug) + printk("vdma_map: Invalid logical address: %08lx\n",laddr); + return -EINVAL; /* invalid logical address */ + } + if (paddr > 0x1fffffff) { + if (vdma_debug) + printk("vdma_map: Invalid physical address: %08lx\n",paddr); + return -EINVAL; /* invalid physical address */ + } + + pages = (((paddr & (VDMA_PAGESIZE-1)) + size) >> 12) + 1; + first = laddr >> 12; + if (vdma_debug) + printk("vdma_remap: first=%x, pages=%x\n",first,pages); + if (first+pages > VDMA_PGTBL_ENTRIES) { + if (vdma_debug) + printk("vdma_alloc: Invalid size: %08lx\n",size); + return -EINVAL; + } + + paddr &= ~(VDMA_PAGESIZE-1); + while (pages > 0 && first < VDMA_PGTBL_ENTRIES) { + if (pgtbl[first].owner != laddr) { + if (vdma_debug) + printk("Trying to remap other's pages.\n"); + return -EPERM; /* not owner */ + } + pgtbl[first].frame = paddr; + paddr += VDMA_PAGESIZE; + first++; + pages--; + } + + /* update translation table */ + + r4030_write_reg32(JAZZ_R4030_TRSTBL_INV,0); + + + if (vdma_debug > 2) { + int i; + pages = (((paddr & (VDMA_PAGESIZE-1)) + size) >> 12) + 1; + first = laddr >> 12; + printk("LADDR: "); + for (i=first; i<first+pages; i++) + printk("%08x ",i<<12); + printk("\nPADDR: "); + for (i=first; i<first+pages; i++) + printk("%08x ",pgtbl[i].frame); + printk("\nOWNER: "); + for (i=first; i<first+pages; i++) + printk("%08x ",pgtbl[i].owner); + printk("\n"); + } + + return 0; +} + +/* + * Translate a physical address to a logical address. + * This will return the logical address of the first + * match. + */ + +unsigned long vdma_phys2log(unsigned long paddr) +{ + int i; + int frame; + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + + frame = paddr & ~(VDMA_PAGESIZE-1); + + for (i=0; i<VDMA_PGTBL_ENTRIES; i++) { + if (pgtbl[i].frame == frame) + break; + } + + if (i == VDMA_PGTBL_ENTRIES) + return(0xffffffff); + + return((i<<12) + (paddr & (VDMA_PAGESIZE-1))); +} + +/* + * Translate a logical DMA address to a physical address + */ +unsigned long vdma_log2phys(unsigned long laddr) +{ + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + + return(pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE-1))); +} + + +/* + * initialize the pagetable with a one-to-one mapping of + * the first 16 Mbytes of main memory and declare all + * entries to be unused. Using this method will at least + * allow some early device driver operations to work. + */ + +static void vdma_pgtbl_init(void) +{ + int i; + unsigned long paddr = 0; + VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *)vdma_pagetable_start; + + for (i=0; i<VDMA_PGTBL_ENTRIES; i++) { + pgtbl[i].frame = paddr; + pgtbl[i].owner = VDMA_PAGE_EMPTY; + paddr += VDMA_PAGESIZE; + } + +/* vdma_stats(); */ +} + +/* + * Print DMA statistics + */ + +void vdma_stats(void) +{ + int i; + + printk("vdma_stats: CONFIG: %08x\n", + r4030_read_reg32(JAZZ_R4030_CONFIG)); + printk("R4030 translation table base: %08x\n", + r4030_read_reg32(JAZZ_R4030_TRSTBL_BASE)); + printk("R4030 translation table limit: %08x\n", + r4030_read_reg32(JAZZ_R4030_TRSTBL_LIM)); + printk("vdma_stats: INV_ADDR: %08x\n", + r4030_read_reg32(JAZZ_R4030_INV_ADDR)); + printk("vdma_stats: R_FAIL_ADDR: %08x\n", + r4030_read_reg32(JAZZ_R4030_R_FAIL_ADDR)); + printk("vdma_stats: M_FAIL_ADDR: %08x\n", + r4030_read_reg32(JAZZ_R4030_M_FAIL_ADDR)); + printk("vdma_stats: IRQ_SOURCE: %08x\n", + r4030_read_reg32(JAZZ_R4030_IRQ_SOURCE)); + printk("vdma_stats: I386_ERROR: %08x\n", + r4030_read_reg32(JAZZ_R4030_I386_ERROR)); + printk("vdma_chnl_modes: "); + for (i=0; i<8; i++) + printk("%04x ",(unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_MODE+(i<<5))); + printk("\n"); + printk("vdma_chnl_enables: "); + for (i=0; i<8; i++) + printk("%04x ",(unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(i<<5))); + printk("\n"); +} + + +/* + * DMA transfer functions + */ + +/* + * Enable a DMA channel. Also clear any error conditions. + */ +void vdma_enable(int channel) +{ + int status; + + if (vdma_debug) + printk("vdma_enable: channel %d\n",channel); + + /* + * Check error conditions first + */ + status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)); + if (status & 0x400) + printk("VDMA: Channel %d: Address error!\n",channel); + if (status & 0x200) + printk("VDMA: Channel %d: Memory error!\n",channel); + + /* + * Clear all interrupt flags + */ + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + R4030_TC_INTR | R4030_MEM_INTR | R4030_ADDR_INTR); + + /* + * Enable the desired channel + */ + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) | + R4030_CHNL_ENABLE); +} + +/* + * Disable a DMA channel + */ +void vdma_disable(int channel) +{ + if (vdma_debug) + { + int status = r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)); + + printk("vdma_disable: channel %d\n",channel); + printk("VDMA: channel %d status: %04x (%s) mode: %02x addr: %06x count: %06x\n", + channel,status,((status & 0x600) ? "ERROR" : "OK"), + (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5)), + (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_ADDR+(channel<<5)), + (unsigned)r4030_read_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5))); + } + + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) & + ~R4030_CHNL_ENABLE); + + /* + * After disabling a DMA channel a remote bus register should be + * read to ensure that the current DMA acknowledge cycle is completed. + */ + + *((volatile unsigned int *)JAZZ_DUMMY_DEVICE); +} + +/* + * Set DMA mode. This function accepts the mode values used + * to set a PC-style DMA controller. For the SCSI and FDC + * channels, we also set the default modes each time we're + * called. + * NOTE: The FAST and BURST dma modes are supported by the + * R4030 Rev. 2 and PICA chipsets only. I leave them disabled + * for now. + */ +void vdma_set_mode(int channel, int mode) +{ + if (vdma_debug) + printk("vdma_set_mode: channel %d, mode 0x%x\n",channel,mode); + + switch(channel) + { + case JAZZ_SCSI_DMA: /* scsi */ + r4030_write_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5), +/* R4030_MODE_FAST | */ +/* R4030_MODE_BURST | */ + R4030_MODE_INTR_EN | + R4030_MODE_WIDTH_16 | + R4030_MODE_ATIME_80); + break; + + case JAZZ_FLOPPY_DMA: /* floppy */ + r4030_write_reg32(JAZZ_R4030_CHNL_MODE+(channel<<5), +/* R4030_MODE_FAST | */ +/* R4030_MODE_BURST | */ + R4030_MODE_INTR_EN | + R4030_MODE_WIDTH_8 | + R4030_MODE_ATIME_120); + break; + + case JAZZ_AUDIOL_DMA: + case JAZZ_AUDIOR_DMA: + printk("VDMA: Audio DMA not supported yet.\n"); + break; + + default: + printk("VDMA: vdma_set_mode() called with unsupported channel %d!\n",channel); + } + + switch(mode) + { + case DMA_MODE_READ: + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) & + ~R4030_CHNL_WRITE); + break; + + case DMA_MODE_WRITE: + r4030_write_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5), + r4030_read_reg32(JAZZ_R4030_CHNL_ENABLE+(channel<<5)) | + R4030_CHNL_WRITE); + break; + + default: + printk("VDMA: vdma_set_mode() called with unknown dma mode 0x%x\n",mode); + } +} + +/* + * Set Transfer Address + */ +void vdma_set_addr(int channel, long addr) +{ + if (vdma_debug) + printk("vdma_set_addr: channel %d, addr %lx\n",channel,addr); + + r4030_write_reg32(JAZZ_R4030_CHNL_ADDR+(channel<<5),addr); +} + +/* + * Set Transfer Count + */ +void vdma_set_count(int channel, int count) +{ + if (vdma_debug) + printk("vdma_set_count: channel %d, count %08x\n",channel,(unsigned)count); + + r4030_write_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5),count); +} + +/* + * Get Residual + */ +int vdma_get_residue(int channel) +{ + int residual; + + residual = r4030_read_reg32(JAZZ_R4030_CHNL_COUNT+(channel<<5)); + + if (vdma_debug) + printk("vdma_get_residual: channel %d: residual=%d\n",channel,residual); + + return(residual); +} + + +/* end of file jazzdma.h */ diff --git a/arch/mips/kernel/magnum4000.S b/arch/mips/kernel/magnum4000.S new file mode 100644 index 000000000..45e62d88d --- /dev/null +++ b/arch/mips/kernel/magnum4000.S @@ -0,0 +1,261 @@ +/* + * arch/mips/kernel/magnum4000.S + * + * Copyright (C) 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + */ +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/jazz.h> +#include <asm/stackframe.h> + +/* + * mips_magnum_4000_handle_int: Interrupt handler for Mips Magnum 4000 + */ + .set noreorder + + NESTED(mips_magnum_4000_handle_int, FR_SIZE, ra) + .set noat + SAVE_ALL + CLI + .set at + + /* + * Get pending interrupts + */ + mfc0 t0,CP0_CAUSE # get pending interrupts + mfc0 t1,CP0_STATUS # get enabled interrupts + and t0,t1 # isolate allowed ones + andi t0,0xff00 # isolate pending bits + beqz t0,spurious_interrupt + sll t0,16 # delay slot + + /* + * Find irq with highest priority + * FIXME: This is slow + */ + la t1,ll_vectors +1: bltz t0,2f # found pending irq + sll t0,1 + b 1b + subu t1,PTRSIZE # delay slot + + /* + * Do the low-level stuff + */ +2: lw t0,(t1) + jr t0 + nop # delay slot + END(mips_magnum_4000_handle_int) + +/* + * Used for keyboard driver's fake_keyboard_interrupt() + */ +ll_sw0: li s1,~IE_SW0 + mfc0 t0,CP0_CAUSE + and t0,s1 + mtc0 t0,CP0_CAUSE + PRINT("sw0 received...\n") + li t1,1 + b call_real + li t3,PTRSIZE # delay slot, re-map to irq level 1 + +ll_sw1: li s1,~IE_SW1 + PANIC("Unimplemented sw1 handler") + +ll_local_dma: li s1,~IE_IRQ0 + PANIC("Unimplemented local_dma handler") + +ll_local_dev: lbu t0,JAZZ_IO_IRQ_SOURCE +#if __mips == 3 + dsll t0,1 + ld t0,local_vector(t0) +#else /* 32 bit */ + lw t0,local_vector(t0) +#endif + jr t0 + nop + + +loc_no_irq: PANIC("Unimplemented loc_no_irq handler") +loc_sound: PANIC("Unimplemented loc_sound handler") +loc_video: PANIC("Unimplemented loc_video handler") +loc_scsi: PANIC("Unimplemented loc_scsi handler") + +/* + * Keyboard interrupt handler + */ +loc_keyboard: li s1,~JAZZ_IE_KEYBOARD + li t1,JAZZ_KEYBOARD_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # delay slot + +/* + * Ethernet interrupt handler, remapped to level 2 + */ +loc_ethernet: /* PRINT ("ethernet IRQ\n"); */ + li s1,~JAZZ_IE_ETHERNET + li t1,JAZZ_ETHERNET_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot + + +loc_mouse: PANIC("Unimplemented loc_mouse handler") + +/* + * Serial port 1 IRQ, remapped to level 3 + */ +loc_serial1: li s1,~JAZZ_IE_SERIAL1 + li t1,JAZZ_SERIAL1_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot + +/* + * Serial port 2 IRQ, remapped to level 4 + */ +loc_serial2: li s1,~JAZZ_IE_SERIAL2 + li t1,JAZZ_SERIAL2_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot + +/* + * Parallel port IRQ, remapped to level 5 + */ +loc_parallel: li s1,~JAZZ_IE_PARALLEL + li t1,JAZZ_PARALLEL_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot + +/* + * Floppy IRQ, remapped to level 6 + */ +loc_floppy: li s1,~JAZZ_IE_FLOPPY + li t1,JAZZ_FLOPPY_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot + +/* + * Now call the real handler + */ +loc_call: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors # delay slot + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * Temporarily disable interrupt source + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + addu t0,t3 # make ptr to IRQ handler + lw t0,(t0) + and t2,s1 # delay slot + sh t2,JAZZ_IO_IRQ_ENABLE + jalr t0 # call IRQ handler + nor s1,zero,s1 # delay slot + + /* + * Reenable interrupt + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + lw t1,%lo(intr_count)(s3) # delay slot + or t2,s1 + sh t2,JAZZ_IO_IRQ_ENABLE + + subu t1,1 + jr v0 + sw t1,%lo(intr_count)(s3) + +ll_eisa_irq: li s1,~IE_IRQ2 + PANIC("Unimplemented eisa_irq handler") + +ll_eisa_nmi: li s1,~IE_IRQ3 + PANIC("Unimplemented eisa_nmi handler") + +/* + * Timer IRQ + * We remap the timer irq to be more similar to a IBM compatible + */ +ll_timer: lw t0,JAZZ_TIMER_REGISTER # timer irq cleared on read + li s1,~IE_IRQ4 + li t1,0 + b call_real + li t3,0 # delay slot, re-map to irq level 0 + +/* + * CPU count/compare IRQ (unused) + */ +ll_count: j return + mtc0 zero,CP0_COMPARE + +/* + * Now call the real handler + */ +call_real: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors # delay slot + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * temporarily disable interrupt + */ + mfc0 t2,CP0_STATUS + and t2,s1 + + addu t0,t3 + lw t0,(t0) + mtc0 t2,CP0_STATUS # delay slot + jalr t0 + nor s1,zero,s1 # delay slot + + /* + * reenable interrupt + */ + mfc0 t2,CP0_STATUS + or t2,s1 + mtc0 t2,CP0_STATUS + + lw t2,%lo(intr_count)(s3) + subu t2,1 + + jr v0 + sw t2,%lo(intr_count)(s3) + +/* + * Just for debugging... + */ + LEAF(drawline) + li t1,0xffffffff + li t2,0x100 +1: sw t1,(a0) + addiu a0,a0,4 + addiu t2,t2,-1 + bnez t2,1b + nop + jr ra + nop + END(drawline) + + + .data + PTR ll_sw0 # SW0 + PTR ll_sw1 # SW1 + PTR ll_local_dma # Local DMA + PTR ll_local_dev # Local devices + PTR ll_eisa_irq # EISA IRQ + PTR ll_eisa_nmi # EISA NMI + PTR ll_timer # Timer +ll_vectors: PTR ll_count # Count/Compare IRQ + +local_vector: PTR loc_no_irq + PTR loc_parallel + PTR loc_floppy + PTR loc_sound + PTR loc_video + PTR loc_ethernet + PTR loc_scsi + PTR loc_keyboard + PTR loc_mouse + PTR loc_serial1 + PTR loc_serial2 diff --git a/arch/mips/kernel/pica.S b/arch/mips/kernel/pica.S new file mode 100644 index 000000000..036aa3139 --- /dev/null +++ b/arch/mips/kernel/pica.S @@ -0,0 +1,252 @@ +/* + * arch/mips/kernel/pica.S + * + * Copyright (C) 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + * + * Acer PICA 61 specific stuff + */ +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/jazz.h> +#include <asm/pica.h> +#include <asm/stackframe.h> + +/* + * acer_pica_61_handle_int: Interrupt handler for the ACER Pica-61 boards + * FIXME: this is *very* experimental! + */ + .set noreorder + + NESTED(acer_pica_61_handle_int, FR_SIZE, ra) + .set noat + SAVE_ALL + CLI + .set at + + /* + * Get pending interrupts + */ + mfc0 t0,CP0_CAUSE # get pending interrupts + mfc0 t1,CP0_STATUS # get enabled interrupts + and t0,t1 # isolate allowed ones + andi t0,0xff00 # isolate pending bits + beqz t0,spurious_interrupt + sll t0,16 # delay slot + + /* + * Find irq with highest priority + * FIXME: This is slow - use binary search + */ + la t1,ll_vectors +1: bltz t0,2f # found pending irq + sll t0,1 + b 1b + subu t1,PTRSIZE # delay slot + + /* + * Do the low-level stuff + */ +2: lw t0,(t1) + jr t0 + nop # delay slot + END(acer_pica_61_handle_int) + +/* + * Used for keyboard driver's fake_keyboard_interrupt() + */ +ll_sw0: li s1,~IE_SW0 + mfc0 t0,CP0_CAUSE + and t0,s1 + mtc0 t0,CP0_CAUSE + PRINT("sw0 received...\n") + li t1,1 + b call_real + li t3,PTRSIZE # delay slot, re-map to irq level 1 + +ll_sw1: li s1,~IE_SW1 + PANIC("Unimplemented sw1 handler") + +ll_local_dma: li s1,~IE_IRQ0 + PANIC("Unimplemented local_dma handler") + +ll_local_dev: lbu t0,JAZZ_IO_IRQ_SOURCE +#if __mips == 3 + dsll t0,1 + ld t0,local_vector(t0) +#else /* 32 bit */ + lw t0,local_vector(t0) +#endif + jr t0 + nop + + +loc_no_irq: PANIC("Unimplemented loc_no_irq handler") +/* + * Parallel port IRQ, remapped to level 5 + */ +loc_parallel: li s1,~JAZZ_IE_PARALLEL + li t1,JAZZ_PARALLEL_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_PARALLEL_IRQ # delay slot + +/* + * Floppy IRQ, remapped to level 6 + */ +loc_floppy: li s1,~JAZZ_IE_FLOPPY + li t1,JAZZ_FLOPPY_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_FLOPPY_IRQ # delay slot + +/* + * Now call the real handler + */ +loc_call: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors # delay slot + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * Temporarily disable interrupt source + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + addu t0,t3 # make ptr to IRQ handler + lw t0,(t0) + and t2,s1 # delay slot + sh t2,JAZZ_IO_IRQ_ENABLE + jalr t0 # call IRQ handler + nor s1,zero,s1 # delay slot + + /* + * Reenable interrupt + */ + lhu t2,JAZZ_IO_IRQ_ENABLE + lw t1,%lo(intr_count)(s3) # delay slot + or t2,s1 + sh t2,JAZZ_IO_IRQ_ENABLE + + subu t1,1 + jr v0 + sw t1,%lo(intr_count)(s3) # delay slot + +ll_isa_irq: li s1,~IE_IRQ2 + PANIC("Unimplemented isa_irq handler") + +ll_isa_nmi: li s1,~IE_IRQ3 + PANIC("Unimplemented isa_nmi handler") + +/* + * Timer IRQ + * We remap the timer irq to be more similar to an IBM compatible + */ +ll_timer: lw zero,JAZZ_TIMER_REGISTER # timer irq cleared on read + li s1,~IE_IRQ4 + li t1,0 + b call_real + li t3,0 # delay slot, re-map to irq level 0 + +/* + * CPU count/compare IRQ (unused) + */ +ll_count: j return + mtc0 zero,CP0_COMPARE + +/* + * Now call the real handler + */ +call_real: lui s3,%hi(intr_count) + lw t2,%lo(intr_count)(s3) + la t0,IRQ_vectors + addiu t2,1 + sw t2,%lo(intr_count)(s3) + + /* + * temporarily disable interrupt + */ + mfc0 t2,CP0_STATUS + and t2,s1 + + addu t0,t3 + lw t0,(t0) + mtc0 t2,CP0_STATUS # delay slot + jalr t0 + nor s1,zero,s1 # delay slot + + /* + * reenable interrupt + */ + mfc0 t2,CP0_STATUS + or t2,s1 + mtc0 t2,CP0_STATUS + + lw t2,%lo(intr_count)(s3) + subu t2,1 + + jr v0 + sw t2,%lo(intr_count)(s3) + + .data + PTR ll_sw0 # SW0 + PTR ll_sw1 # SW1 + PTR ll_local_dma # Local DMA + PTR ll_local_dev # Local devices + PTR ll_isa_irq # ISA IRQ + PTR ll_isa_nmi # ISA NMI + PTR ll_timer # Timer +ll_vectors: PTR ll_count # Count/Compare IRQ + + +/* + * Sound? What sound hardware (whistle) ??? + */ +loc_sound: PANIC("Unimplemented loc_sound handler") +loc_video: PANIC("Unimplemented loc_video handler") + +/* + * Ethernet interrupt handler, remapped to level 2 + */ +loc_ethernet: li s1,~JAZZ_IE_ETHERNET + li t1,JAZZ_ETHERNET_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_ETHERNET_IRQ # delay slot + +loc_scsi: PANIC("Unimplemented loc_scsi handler") + +/* + * Keyboard interrupt handler + */ +loc_keyboard: li s1,~JAZZ_IE_KEYBOARD + li t1,JAZZ_KEYBOARD_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_KEYBOARD_IRQ # re-map to irq level 1 + +loc_mouse: PANIC("Unimplemented loc_mouse handler") + +/* + * Serial port 1 IRQ, remapped to level 3 + */ +loc_serial1: li s1,~JAZZ_IE_SERIAL1 + li t1,JAZZ_SERIAL1_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL1_IRQ # delay slot + +/* + * Serial port 2 IRQ, remapped to level 4 + */ +loc_serial2: li s1,~JAZZ_IE_SERIAL2 + li t1,JAZZ_SERIAL2_IRQ + b loc_call + li t3,PTRSIZE*JAZZ_SERIAL2_IRQ # delay slot + +local_vector: PTR loc_no_irq + PTR loc_parallel + PTR loc_floppy + PTR loc_sound + PTR loc_video + PTR loc_ethernet + PTR loc_scsi + PTR loc_keyboard + PTR loc_mouse + PTR loc_serial1 + PTR loc_serial2 diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c new file mode 100644 index 000000000..dd69c3208 --- /dev/null +++ b/arch/mips/kernel/process.c @@ -0,0 +1,216 @@ +/* + * linux/arch/mips/kernel/process.c + * + * Copyright (C) 1995 Ralf Baechle + * written by Ralf Baechle + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/ldt.h> +#include <linux/user.h> +#include <linux/a.out.h> + +#include <asm/bootinfo.h> +#include <asm/segment.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/mipsregs.h> +#include <asm/processor.h> +#include <asm/stackframe.h> +#include <asm/io.h> + +asmlinkage void ret_from_sys_call(void); + +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = verify_area(VERIFY_WRITE,fildes,8); + if (error) + return error; + error = do_pipe(fd); + if (error) + return error; + put_fs_long(fd[0],0+fildes); + put_fs_long(fd[1],1+fildes); + return 0; +} + +asmlinkage int sys_idle(void) +{ + if (current->pid != 0) + return -EPERM; + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + /* + * R4[26]00 have wait, R4[04]00 don't. + */ + if (wait_available && !need_resched) + __asm__(".set\tmips3\n\t" + "wait\n\t" + ".set\tmips0\n\t"); + schedule(); + } +} + +/* + * This routine reboots the machine by asking the keyboard + * controller to pulse the reset-line low. We try that for a while, + * and if it doesn't work, we do some other stupid things. + * Should be ok for Deskstation Tynes. Reseting others needs to be + * investigated... + */ +static inline void kb_wait(void) +{ + int i; + + for (i=0; i<0x10000; i++) + if ((inb_p(0x64) & 0x02) == 0) + break; +} + +/* + * Hard reset for Deskstation Tyne + * No hint how this works on Pica boards. + */ +void hard_reset_now(void) +{ + int i, j; + + sti(); + for (;;) { + for (i=0; i<100; i++) { + kb_wait(); + for(j = 0; j < 100000 ; j++) + /* nothing */; + outb(0xfe,0x64); /* pulse reset low */ + } + } +} + +void show_regs(struct pt_regs * regs) +{ + /* + * Saved main processor registers + */ + printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + 0, regs->reg1, regs->reg2, regs->reg3, + regs->reg4, regs->reg5, regs->reg6, regs->reg7); + printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg8, regs->reg9, regs->reg10, regs->reg11, + regs->reg12, regs->reg13, regs->reg14, regs->reg15); + printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg16, regs->reg17, regs->reg18, regs->reg19, + regs->reg20, regs->reg21, regs->reg22, regs->reg23); + printk("$24: %08lx %08lx %08lx %08lx %08lx %08lx\n", + regs->reg24, regs->reg25, regs->reg28, regs->reg29, + regs->reg30, regs->reg31); + + /* + * Saved cp0 registers + */ + printk("epc : %08lx\nStatus: %08lx\nCause : %08lx\n", + regs->cp0_epc, regs->cp0_status, regs->cp0_cause); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* + * Nothing to do + */ +} + +void flush_thread(void) +{ + /* + * Nothing to do + */ +} + +void copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + + /* + * set up new TSS + */ + childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1; + *childregs = *regs; + childregs->reg2 = 0; + regs->reg2 = p->pid; + childregs->reg29 = usp; + p->tss.ksp = (p->kernel_stack_page + PAGE_SIZE - 8); + p->tss.reg29 = (unsigned long) childregs; /* new sp */ + p->tss.reg31 = (unsigned long) ret_from_sys_call; + + /* + * New tasks loose permission to use the fpu. This accelerates context + * switching for most programs since they don't use the fpu. + */ + p->tss.cp0_status = read_32bit_cp0_register(CP0_STATUS) & + ~(ST0_CU3|ST0_CU2|ST0_CU1|ST0_KSU|ST0_ERL|ST0_EXL); + childregs->cp0_status &= ~(ST0_CU3|ST0_CU2|ST0_CU1); +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + /* + * To do... + */ +} + +asmlinkage int sys_fork(struct pt_regs regs) +{ + return do_fork(COPYVM | SIGCHLD, regs.reg29, ®s); +} + +asmlinkage int sys_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + + newsp = regs.reg4; + clone_flags = regs.reg5; + if (!newsp) + newsp = regs.reg29; + if (newsp == regs.reg29) + clone_flags |= COPYVM; + return do_fork(clone_flags, newsp, ®s); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + error = getname((char *) regs.reg4, &filename); + if (error) + return error; + error = do_execve(filename, (char **) regs.reg5, (char **) regs.reg6, ®s); + putname(filename); + return error; +} diff --git a/arch/mips/ptrace.c b/arch/mips/kernel/ptrace.c index 0a42b9c38..6f35ceb67 100644 --- a/arch/mips/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -11,6 +11,7 @@ #include <linux/user.h> #include <asm/segment.h> +#include <asm/pgtable.h> #include <asm/system.h> #if 0 @@ -84,23 +85,30 @@ static inline int put_stack_long(struct task_struct *task, int offset, */ static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr) { + pgd_t * pgdir; + pte_t * pgtable; unsigned long page; repeat: - page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - page += PAGE_PTR(addr); - page = *((unsigned long *) page); + pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr); + if (pgd_none(*pgdir)) { + do_no_page(vma, addr, 0); + goto repeat; + } + if (pgd_bad(*pgdir)) { + printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return 0; } - if (!(page & PAGE_PRESENT)) { + pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir)); + if (!pte_present(*pgtable)) { do_no_page(vma, addr, 0); goto repeat; } + page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ if (page >= high_memory) return 0; - page &= PAGE_MASK; page += addr & ~PAGE_MASK; return *(unsigned long *) page; } @@ -117,52 +125,50 @@ repeat: static void put_long(struct vm_area_struct * vma, unsigned long addr, unsigned long data) { - unsigned long page, pte = 0; - int readonly = 0; + pgd_t *pgdir; + pte_t *pgtable; + unsigned long page; repeat: - page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr); - if (page & PAGE_PRESENT) { - page &= PAGE_MASK; - page += PAGE_PTR(addr); - pte = page; - page = *((unsigned long *) page); + pgdir = PAGE_DIR_OFFSET(vma->vm_task, addr); + if (!pgd_present(*pgdir)) { + do_no_page(vma, addr, 1); + goto repeat; + } + if (pgd_bad(*pgdir)) { + printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); + pgd_clear(pgdir); + return; } - if (!(page & PAGE_PRESENT)) { - do_no_page(vma, addr, 0 /* PAGE_RW */); + pgtable = (pte_t *) (PAGE_PTR(addr) + pgd_page(*pgdir)); + if (!pte_present(*pgtable)) { + do_no_page(vma, addr, 1); goto repeat; } - if (!(page & PAGE_RW)) { - if (!(page & PAGE_COW)) - readonly = 1; - do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT); + page = pte_page(*pgtable); + if (!pte_write(*pgtable)) { + do_wp_page(vma, addr, 1); goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (page >= high_memory) - return; + if (page < high_memory) { + page += addr & ~PAGE_MASK; + *(unsigned long *) page = data; + } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ - *(unsigned long *) pte |= (PAGE_DIRTY|PAGE_COW); - page &= PAGE_MASK; - page += addr & ~PAGE_MASK; - *(unsigned long *) page = data; - if (readonly) { - *(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW); - invalidate(); - } +/* this should also re-instate whatever read-only mode there was before */ + *pgtable = pte_mkdirty(mk_pte(page, vma->vm_page_prot)); + invalidate(); } -static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr) +static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) { struct vm_area_struct * vma; addr &= PAGE_MASK; - for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return NULL; - if (vma->vm_end > addr) - break; - } + vma = find_vma(tsk, addr); + if (!vma) + return NULL; if (vma->vm_start <= addr) return vma; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -181,7 +187,7 @@ static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long static int read_long(struct task_struct * tsk, unsigned long addr, unsigned long * result) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; @@ -223,7 +229,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr, static int write_long(struct task_struct * tsk, unsigned long addr, unsigned long data) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; diff --git a/arch/mips/kernel/r4xx0.S b/arch/mips/kernel/r4xx0.S new file mode 100644 index 000000000..a68b32243 --- /dev/null +++ b/arch/mips/kernel/r4xx0.S @@ -0,0 +1,732 @@ +/* + * arch/mips/kernel/r4xx0.S + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * Written by Ralf Baechle and Andreas Busse + * + * This file contains most of the R4xx0 specific routines. + * + * This code is evil magic. Read appendix f (coprozessor 0 hazards) of + * all R4xx0 manuals and think about that MIPS means "Microprocessor without + * Interlocked Pipeline Stages" before you even think about changing this code! + */ +#include <linux/autoconf.h> + +#include <asm/asm.h> +#include <asm/bootinfo.h> +#include <asm/cachectl.h> +#include <asm/mipsconfig.h> +#include <asm/mipsregs.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/mipsregs.h> +#include <asm/segment.h> +#include <asm/stackframe.h> + +MODE_ALIAS = 0x0016 # uncachable + + .text + .set mips3 + .set noreorder + + .align 5 + NESTED(handle_tlbl, FR_SIZE, sp) + .set noat + /* + * Check whether this is a refill or an invalid exception + * + * NOTE: Some MIPS manuals say that the R4x00 sets the + * BadVAddr only when EXL == 0. This is wrong - BadVAddr + * is being set for all Reload, Invalid and Modified + * exceptions. + */ + mfc0 k0,CP0_BADVADDR + mfc0 k1,CP0_ENTRYHI + ori k0,0x1fff + xori k0,0x1fff + andi k1,0xff + or k0,k1 + mfc0 k1,CP0_ENTRYHI + mtc0 k0,CP0_ENTRYHI + nop # for R4[04]00 pipeline + nop + nop + tlbp + nop # for R4[04]00 pipeline + nop + mfc0 k0,CP0_INDEX + bgez k0,invalid_tlbl # bad addr in c0_badvaddr + mtc0 k1,CP0_ENTRYHI # delay slot + /* + * Damn... The next nop is required on my R4400PC V5.0, but + * I don't know why - at least there is no documented + * reason as for the others :-( + */ + nop + +#ifdef CONFIG_DEBUG_TLB + /* + * OK, this is a double fault. Let's see whether this is + * due to an invalid entry in the page_table. + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + andi k1,(_PAGE_PRESENT|_PAGE_ACCESSED) + bnez k1,reload_pgd_entries + nop # delay slot + + .set noat + SAVE_ALL + .set at + PRINT("Double fault caused by invalid entries in pgd:\n") + dmfc0 a1,CP0_BADVADDR + PRINT("Double fault address : %08lx\n") + dmfc0 a1,CP0_EPC + PRINT("c0_epc : %08lx\n") + jal show_regs + move a0,sp + .set noat + STI + .set at + PANIC("Corrupted pagedir") + .set noat + +reload_pgd_entries: +#endif /* CONFIG_DEBUG_TLB */ + + /* + * Load missing pair of entries from the pgd and return. + */ + dmfc0 k1,CP0_CONTEXT + dsra k1,1 + lwu k0,(k1) # Never causes nested exception + lwu k1,4(k1) + dsrl k0,6 # Convert to EntryLo format + dsrl k1,6 # Convert to EntryLo format + dmtc0 k0,CP0_ENTRYLO0 + dmtc0 k1,CP0_ENTRYLO1 + nop # for R4[04]00 pipeline + tlbwr + nop # for R4[04]00 pipeline + nop + nop + /* + * We don't know whether the original access was read or + * write, so return and see what happens... + */ + eret + + /* + * Handle invalid exception + * + * There are two possible causes for an invalid (tlbl) + * exception: + * 1) pages with present bit set but the valid bit clear + * 2) nonexistant pages + * Case one needs fast handling, therefore don't save + * registers yet. + * + * k0 contains c0_index. + */ +invalid_tlbl: /* + * Remove entry so we don't need to care later + * For sake of the R4000 V2.2 pipeline the tlbwi insn + * has been moved down. Moving it around is juggling with + * explosives... + */ + lui k1,0x0008 + or k0,k1 + dsll k0,13 + dmtc0 k0,CP0_ENTRYHI + dmtc0 zero,CP0_ENTRYLO0 + dmtc0 zero,CP0_ENTRYLO1 + /* + * Test present bit in entry + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + tlbwi # do not move! + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + andi k1,(_PAGE_PRESENT|_PAGE_READ) + xori k1,(_PAGE_PRESENT|_PAGE_READ) + bnez k1,nopage_tlbl + /* + * Present and read bits are set -> set valid and accessed bits + */ + lw k1,(k0) # delay slot + ori k1,(_PAGE_VALID|_PAGE_ACCESSED) + sw k1,(k0) + eret + + /* + * Page doesn't exist. Lots of work which is less important + * for speed needs to be done, so hand it all over to the + * kernel memory management routines. + */ +nopage_tlbl: SAVE_ALL + STI + .set at + /* + * a0 (struct pt_regs *) regs + * a1 (unsigned long) 0 for read access + */ + move a0,sp + jal do_page_fault + li a1,0 # delay slot + j ret_from_sys_call + nop # delay slot + END(handle_tlbl) + + .text + .align 5 + NESTED(handle_tlbs, FR_SIZE, sp) + .set noat + /* + * It is impossible that is a nested reload exception. + * Therefore this must be a invalid exception. + * Two possible cases: + * 1) Page exists but not dirty. + * 2) Page doesn't exist yet. Hand over to the kernel. + * + * Test whether present bit in entry is set + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + tlbp # find faulting entry + andi k1,(_PAGE_PRESENT|_PAGE_WRITE) + xori k1,(_PAGE_PRESENT|_PAGE_WRITE) + bnez k1,nopage_tlbs + /* + * Present and writable bits set: set accessed and dirty bits. + */ + lw k1,(k0) # delay slot + ori k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \ + _PAGE_VALID|_PAGE_DIRTY) + sw k1,(k0) + /* + * Now reload the entry into the TLB + */ + ori k0,0x0004 + xori k0,0x0004 + lw k1,4(k0) + lw k0,(k0) + srl k1,6 + srl k0,6 + dmtc0 k1,CP0_ENTRYLO1 + dmtc0 k0,CP0_ENTRYLO0 + nop # for R4[04]00 pipeline + tlbwi + nop # for R4[04]00 pipeline + nop + nop + eret + + /* + * Page doesn't exist. Lots of work which is less important + * for speed needs to be done, so hand it all over to the + * kernel memory management routines. + */ +nopage_tlbs: +#if 0 + .set mips3 + SAVE_ALL + .set mips0 + PRINT("nopage_tlbs\n") + .set mips3 + RESTORE_ALL + .set mips3 + j 1f + nop +#endif +nowrite_mod: +#if 0 + .set mips3 + SAVE_ALL + .set mips0 + PRINT("nopage_mod\n") + .set mips3 + RESTORE_ALL + .set mips3 + j 1f + nop +1: +#endif + /* + * Remove entry so we don't need to care later + */ + mfc0 k0,CP0_INDEX +#ifdef CONFIG_DEBUG_TLB + bgez k0,2f + nop + /* + * We got a tlbs exception but found no matching entry in + * the tlb. This should never happen. Paranoia makes us + * check it, though. + */ + SAVE_ALL + jal show_regs + move a0,sp + .set at + mfc0 a1,CP0_BADVADDR + PRINT("c0_badvaddr == %08lx\n") + mfc0 a1,CP0_INDEX + PRINT("c0_index == %08x\n") + mfc0 a1,CP0_ENTRYHI + PRINT("c0_entryhi == %08x\n") + jal dump_tlb_nonwired + nop + .set noat + STI + .set at + PANIC("Tlbs or tlbm exception with no matching entry in tlb") +1: j 1b + nop +2: +#endif /* CONFIG_DEBUG_TLB */ + lui k1,0x0008 + or k0,k1 + dsll k0,13 + dmtc0 k0,CP0_ENTRYHI + dmtc0 zero,CP0_ENTRYLO0 + dmtc0 zero,CP0_ENTRYLO1 + nop # for R4[04]00 pipeline + nop # R4000 V2.2 requires 4 NOPs + nop + nop + tlbwi + .set noat + SAVE_ALL + STI + .set at + /* + * a0 (struct pt_regs *) regs + * a1 (unsigned long) 1 for write access + */ + move a0,sp + jal do_page_fault + li a1,1 # delay slot + j ret_from_sys_call + nop # delay slot + END(handle_tlbs) + + .align 5 + NESTED(handle_mod, FR_SIZE, sp) + .set noat + /* + * Two possible cases: + * 1) Page is writable but not dirty -> set dirty and return + * 2) Page is not writable -> call C handler + */ + dmfc0 k0,CP0_BADVADDR + srl k0,12 + sll k0,2 + lui k1,%HI(TLBMAP) + addu k0,k1 + lw k1,(k0) + tlbp # find faulting entry + andi k1,_PAGE_WRITE + beqz k1,nowrite_mod + /* + * Present and writable bits set: set accessed and dirty bits. + */ + lw k1,(k0) # delay slot + ori k1,(_PAGE_ACCESSED|_PAGE_DIRTY) + sw k1,(k0) + /* + * Now reload the entry into the tlb + */ + ori k0,0x0004 + xori k0,0x0004 + lw k1,4(k0) + lw k0,(k0) + srl k1,6 + srl k0,6 + dmtc0 k1,CP0_ENTRYLO1 + dmtc0 k0,CP0_ENTRYLO0 + nop # for R4[04]00 pipeline + nop + nop + tlbwi + nop # for R4[04]00 pipeline + nop + nop + eret + END(handle_mod) + .set at + + .set noreorder + LEAF(tlbflush) + li t0,PM_4K + mtc0 t0,CP0_PAGEMASK + la t0,boot_info + lw t0,OFFSET_BOOTINFO_TLB_ENTRIES(t0) + dmtc0 zero,CP0_ENTRYLO0 + dmtc0 zero,CP0_ENTRYLO1 + mfc0 t2,CP0_WIRED +1: subu t0,1 + mtc0 t0,CP0_INDEX + lui t1,0x0008 + or t1,t0,t1 + dsll t1,13 + dmtc0 t1,CP0_ENTRYHI + bne t2,t0,1b + tlbwi # delay slot + jr ra + nop + END(tlbflush) + + /* + * Code necessary to switch tasks on an Linux/MIPS machine. + */ + .align 5 + LEAF(resume) + /* + * Current task's task_struct + */ + lui t5,%hi(current) + lw t0,%lo(current)(t5) + + /* + * Save status register + */ + mfc0 t1,CP0_STATUS + addu t0,a1 # Add tss offset + sw t1,TOFF_CP0_STATUS(t0) + + /* + * Disable interrupts + */ + ori t2,t1,0x1f + xori t2,0x1e + mtc0 t2,CP0_STATUS + + /* + * Save non-scratch registers + * All other registers have been saved on the kernel stack + */ + sw s0,TOFF_REG16(t0) + sw s1,TOFF_REG17(t0) + sw s2,TOFF_REG18(t0) + sw s3,TOFF_REG19(t0) + sw s4,TOFF_REG20(t0) + sw s5,TOFF_REG21(t0) + sw s6,TOFF_REG22(t0) + sw s7,TOFF_REG23(t0) + sw gp,TOFF_REG28(t0) + sw sp,TOFF_REG29(t0) + sw fp,TOFF_REG30(t0) + + /* + * Save floating point state + */ + sll t2,t1,2 + bgez t2,2f + sw ra,TOFF_REG31(t0) # delay slot + sll t2,t1,5 + bgez t2,1f + sdc1 $f0,(TOFF_FPU+0)(t0) # delay slot + /* + * Store the 16 odd double precision registers + */ + sdc1 $f1,(TOFF_FPU+8)(t0) + sdc1 $f3,(TOFF_FPU+24)(t0) + sdc1 $f5,(TOFF_FPU+40)(t0) + sdc1 $f7,(TOFF_FPU+56)(t0) + sdc1 $f9,(TOFF_FPU+72)(t0) + sdc1 $f11,(TOFF_FPU+88)(t0) + sdc1 $f13,(TOFF_FPU+104)(t0) + sdc1 $f15,(TOFF_FPU+120)(t0) + sdc1 $f17,(TOFF_FPU+136)(t0) + sdc1 $f19,(TOFF_FPU+152)(t0) + sdc1 $f21,(TOFF_FPU+168)(t0) + sdc1 $f23,(TOFF_FPU+184)(t0) + sdc1 $f25,(TOFF_FPU+200)(t0) + sdc1 $f27,(TOFF_FPU+216)(t0) + sdc1 $f29,(TOFF_FPU+232)(t0) + sdc1 $f31,(TOFF_FPU+248)(t0) + + /* + * Store the 16 even double precision registers + */ +1: cfc1 t1,$31 + sdc1 $f2,(TOFF_FPU+16)(t0) + sdc1 $f4,(TOFF_FPU+32)(t0) + sdc1 $f6,(TOFF_FPU+48)(t0) + sdc1 $f8,(TOFF_FPU+64)(t0) + sdc1 $f10,(TOFF_FPU+80)(t0) + sdc1 $f12,(TOFF_FPU+96)(t0) + sdc1 $f14,(TOFF_FPU+112)(t0) + sdc1 $f16,(TOFF_FPU+128)(t0) + sdc1 $f18,(TOFF_FPU+144)(t0) + sdc1 $f20,(TOFF_FPU+160)(t0) + sdc1 $f22,(TOFF_FPU+176)(t0) + sdc1 $f24,(TOFF_FPU+192)(t0) + sdc1 $f26,(TOFF_FPU+208)(t0) + sdc1 $f28,(TOFF_FPU+224)(t0) + sdc1 $f30,(TOFF_FPU+240)(t0) + sw t1,(TOFF_FPU+256)(t0) + + /* + * Switch current task + */ +2: sw a0,%lo(current)(t5) + addu a0,a1 # Add tss offset + + /* + * Switch address space + */ + + /* + * (Choose new ASID for process) + * This isn't really required, but would speed up + * context switching. + */ + + /* + * Switch the root pointer + */ + lw t0,TOFF_PG_DIR(a0) + li t1,TLB_ROOT + mtc0 t1,CP0_ENTRYHI + mtc0 zero,CP0_INDEX + srl t0,6 + ori t0,MODE_ALIAS + mtc0 t0,CP0_ENTRYLO0 + mtc0 zero,CP0_ENTRYLO1 + lw a2,TOFF_CP0_STATUS(a0) + + /* + * Flush tlb + * (probably not needed, doesn't clobber a0-a3) + */ + jal tlbflush + tlbwi # delay slot + + /* + * Restore fpu state: + * - cp0 status register bits + * - fp gp registers + * - cp1 status/control register + */ + ori t1,a2,1 # pipeline magic + xori t1,1 + mtc0 t1,CP0_STATUS + sll t0,a2,2 + bgez t0,2f + sll t0,a2,5 # delay slot + bgez t0,1f + ldc1 $f0,(TOFF_FPU+0)(a0) # delay slot + /* + * Restore the 16 odd double precision registers only + * when enabled in the cp0 status register. + */ + ldc1 $f1,(TOFF_FPU+8)(a0) + ldc1 $f3,(TOFF_FPU+24)(a0) + ldc1 $f5,(TOFF_FPU+40)(a0) + ldc1 $f7,(TOFF_FPU+56)(a0) + ldc1 $f9,(TOFF_FPU+72)(a0) + ldc1 $f11,(TOFF_FPU+88)(a0) + ldc1 $f13,(TOFF_FPU+104)(a0) + ldc1 $f15,(TOFF_FPU+120)(a0) + ldc1 $f17,(TOFF_FPU+136)(a0) + ldc1 $f19,(TOFF_FPU+152)(a0) + ldc1 $f21,(TOFF_FPU+168)(a0) + ldc1 $f23,(TOFF_FPU+184)(a0) + ldc1 $f25,(TOFF_FPU+200)(a0) + ldc1 $f27,(TOFF_FPU+216)(a0) + ldc1 $f29,(TOFF_FPU+232)(a0) + ldc1 $f31,(TOFF_FPU+248)(a0) + + /* + * Restore the 16 even double precision registers + * when cp1 was enabled in the cp0 status register. + */ +1: lw t0,(TOFF_FPU+256)(a0) + ldc1 $f2,(TOFF_FPU+16)(a0) + ldc1 $f4,(TOFF_FPU+32)(a0) + ldc1 $f6,(TOFF_FPU+48)(a0) + ldc1 $f8,(TOFF_FPU+64)(a0) + ldc1 $f10,(TOFF_FPU+80)(a0) + ldc1 $f12,(TOFF_FPU+96)(a0) + ldc1 $f14,(TOFF_FPU+112)(a0) + ldc1 $f16,(TOFF_FPU+128)(a0) + ldc1 $f18,(TOFF_FPU+144)(a0) + ldc1 $f20,(TOFF_FPU+160)(a0) + ldc1 $f22,(TOFF_FPU+176)(a0) + ldc1 $f24,(TOFF_FPU+192)(a0) + ldc1 $f26,(TOFF_FPU+208)(a0) + ldc1 $f28,(TOFF_FPU+224)(a0) + ldc1 $f30,(TOFF_FPU+240)(a0) + ctc1 t0,$31 + + /* + * Restore non-scratch registers + */ +2: lw s0,TOFF_REG16(a0) + lw s1,TOFF_REG17(a0) + lw s2,TOFF_REG18(a0) + lw s3,TOFF_REG19(a0) + lw s4,TOFF_REG20(a0) + lw s5,TOFF_REG21(a0) + lw s6,TOFF_REG22(a0) + lw s7,TOFF_REG23(a0) + lw gp,TOFF_REG28(a0) + lw sp,TOFF_REG29(a0) + lw fp,TOFF_REG30(a0) + lw ra,TOFF_REG31(a0) + + /* + * Restore status register + */ + lw t0,TOFF_KSP(a0) + sw t0,kernelsp + + jr ra + mtc0 a2,CP0_STATUS # delay slot + END(resume) + +/* + * Some bits in the config register + */ +#define CONFIG_DB (1<<4) +#define CONFIG_IB (1<<5) + +/* + * Flush instruction/data caches + * + * Parameters: a0 - starting address to flush + * a1 - size of area to be flushed + * a2 - which caches to be flushed + * + * FIXME: - ignores parameters in a0/a1 + * - doesn't know about second level caches + */ + .set noreorder + LEAF(sys_cacheflush) + andi t1,a2,DCACHE + beqz t1,do_icache + li t0,KSEG0 # delay slot + + /* + * Writeback data cache, even lines + */ + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,0(t0) + cache Index_Writeback_Inv_D,32(t0) + cache Index_Writeback_Inv_D,64(t0) + cache Index_Writeback_Inv_D,96(t0) + cache Index_Writeback_Inv_D,128(t0) + cache Index_Writeback_Inv_D,160(t0) + cache Index_Writeback_Inv_D,192(t0) + cache Index_Writeback_Inv_D,224(t0) + cache Index_Writeback_Inv_D,256(t0) + cache Index_Writeback_Inv_D,288(t0) + cache Index_Writeback_Inv_D,320(t0) + cache Index_Writeback_Inv_D,352(t0) + cache Index_Writeback_Inv_D,384(t0) + cache Index_Writeback_Inv_D,416(t0) + cache Index_Writeback_Inv_D,448(t0) + cache Index_Writeback_Inv_D,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Writeback data cache, odd lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_DB + bnez t1,do_icache + li t1,CACHELINES-1 +1: cache Index_Writeback_Inv_D,16(t0) + cache Index_Writeback_Inv_D,48(t0) + cache Index_Writeback_Inv_D,80(t0) + cache Index_Writeback_Inv_D,112(t0) + cache Index_Writeback_Inv_D,144(t0) + cache Index_Writeback_Inv_D,176(t0) + cache Index_Writeback_Inv_D,208(t0) + cache Index_Writeback_Inv_D,240(t0) + cache Index_Writeback_Inv_D,272(t0) + cache Index_Writeback_Inv_D,304(t0) + cache Index_Writeback_Inv_D,336(t0) + cache Index_Writeback_Inv_D,368(t0) + cache Index_Writeback_Inv_D,400(t0) + cache Index_Writeback_Inv_D,432(t0) + cache Index_Writeback_Inv_D,464(t0) + cache Index_Writeback_Inv_D,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +do_icache: andi t1,a2,ICACHE + beqz t1,done + + /* + * Flush instruction cache, even lines + */ + lui t0,0x8000 + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,0(t0) + cache Index_Invalidate_I,32(t0) + cache Index_Invalidate_I,64(t0) + cache Index_Invalidate_I,96(t0) + cache Index_Invalidate_I,128(t0) + cache Index_Invalidate_I,160(t0) + cache Index_Invalidate_I,192(t0) + cache Index_Invalidate_I,224(t0) + cache Index_Invalidate_I,256(t0) + cache Index_Invalidate_I,288(t0) + cache Index_Invalidate_I,320(t0) + cache Index_Invalidate_I,352(t0) + cache Index_Invalidate_I,384(t0) + cache Index_Invalidate_I,416(t0) + cache Index_Invalidate_I,448(t0) + cache Index_Invalidate_I,480(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + + /* + * Flush instruction cache, even lines + * Only needed for 16 byte line size + */ + mfc0 t1,CP0_CONFIG + andi t1,CONFIG_IB + bnez t1,done + li t1,CACHELINES-1 +1: cache Index_Invalidate_I,16(t0) + cache Index_Invalidate_I,48(t0) + cache Index_Invalidate_I,80(t0) + cache Index_Invalidate_I,112(t0) + cache Index_Invalidate_I,144(t0) + cache Index_Invalidate_I,176(t0) + cache Index_Invalidate_I,208(t0) + cache Index_Invalidate_I,240(t0) + cache Index_Invalidate_I,272(t0) + cache Index_Invalidate_I,304(t0) + cache Index_Invalidate_I,336(t0) + cache Index_Invalidate_I,368(t0) + cache Index_Invalidate_I,400(t0) + cache Index_Invalidate_I,432(t0) + cache Index_Invalidate_I,464(t0) + cache Index_Invalidate_I,496(t0) + addiu t0,512 + bnez t1,1b + subu t1,1 + +done: j ra + nop + END(sys_cacheflush) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c new file mode 100644 index 000000000..f5037fbd7 --- /dev/null +++ b/arch/mips/kernel/setup.c @@ -0,0 +1,207 @@ +/* + * linux/arch/mips/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 1995 Ralf Baechle + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/string.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/ldt.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> + +#include <asm/asm.h> +#include <asm/bootinfo.h> +#include <asm/vector.h> +#include <asm/segment.h> +#include <asm/stackframe.h> +#include <asm/system.h> + +/* + * How to handle the machine's features + */ +struct feature *feature; + +#ifdef CONFIG_ACER_PICA_61 +void acer_pica_61_handle_int(void); +struct feature acer_pica_61_feature = { + acer_pica_61_handle_int +}; +#endif +#ifdef CONFIG_DECSTATION +void decstation_handle_handle_int(void); +struct feature decstation_feature = { + decstation_handle_handle_int +}; +#endif +#ifdef CONFIG_DESKSTATION_RPC44 +void deskstation_rpc44_handle_int(void); +struct feature deskstation_rpc44_feature = { + deskstation_rpc44_handle_int +}; +#endif +#ifdef CONFIG_DESKSTATION_TYNE +void deskstation_tyne_handle_int(void); +struct feature deskstation_tyne_feature = { + deskstation_tyne_handle_int +}; +#endif +#ifdef CONFIG_MIPS_MAGNUM_4000 +void mips_magnum_4000_handle_int(void); +struct feature mips_magnum_4000_feature = { + mips_magnum_4000_handle_int +}; +#endif + +/* + * Tell us the machine setup.. + */ +char wait_available; /* set if the "wait" instruction available */ + +/* + * Bus types .. + */ +int EISA_bus = 0; + +/* + * Setup options + */ +struct drive_info_struct drive_info; +struct screen_info screen_info = SCREEN_INFO; + +unsigned char aux_device_present; +extern int ramdisk_size; +extern int root_mountflags; +extern int _end; + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * Initialise this structure so that it will be placed in the + * .data section of the object file + */ +struct bootinfo boot_info = BOOT_INFO; + +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM empty_zero_page +#if 0 +#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) +#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) +#endif + +static char command_line[CL_SIZE] = { 0, }; + +#if 0 +/* + * Code for easy access to new style bootinfo + * + * Parameter: tag -- taglist entry + * + * returns : (tag *) -- pointer to taglist entry, NULL for not found + */ +tag * +bi_TagFind(enum bi_tag tag) +{ + /* TBD */ + return 0; +} + +/* + * Only for taglist creators (bootloaders) + * + * Parameter: tag -- (enum bi_tag) taglist entry + * + * returns : 1 -- success + * 0 -- failure + */ +int +bi_TagAdd(enum bi_tag tag, unsigned long size, void *tagdata) +{ + /* TBD */ + return 0; +} +#endif /* 0 */ + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + unsigned long memory_start, memory_end; + + switch(boot_info.machtype) + { +#ifdef CONFIG_ACER_PICA_61 + case MACH_ACER_PICA_61: + feature = &acer_pica_61_feature; + break; +#endif +#ifdef CONFIG_DECSTATION + case MACH_DECSTATION: + feature = &decstation_feature; + break; +#endif +#ifdef CONFIG_DESKSTATION_RPC + case MACH_DESKSTATION_RPC: + feature = &deskstation_rpc44_feature; + break; +#endif +#ifdef CONFIG_DESKSTATION_TYNE + case MACH_DESKSTATION_TYNE: + feature = &deskstation_tyne_feature; + break; +#endif +#ifdef CONFIG_MIPS_MAGNUM_4000 + case MACH_MIPS_MAGNUM_4000: + feature = &mips_magnum_4000_feature; + break; +#endif + default: + panic("Unsupported architecture"); + } + +#if 0 + ROOT_DEV = ORIG_ROOT_DEV; +#else + ROOT_DEV = 0x021c; /* fd0H1440 */ +/* ROOT_DEV = 0x0101; */ /* ram */ +/* ROOT_DEV = 0x00ff; */ /* NFS */ +#endif + memcpy(&drive_info, &boot_info.drive_info, sizeof(drive_info)); +#if 0 + aux_device_present = AUX_DEVICE_INFO; +#endif + memory_end = boot_info.memupper; + memory_end &= PAGE_MASK; + ramdisk_size = boot_info.ramdisk_size; + if (boot_info.mount_root_rdonly) + root_mountflags |= MS_RDONLY; + + memory_start = (unsigned long) &_end; + memory_start += (ramdisk_size << 10); + + *cmdline_p = command_line; + *memory_start_p = memory_start; + *memory_end_p = memory_end; + +#if 0 + /* + * Check that struct pt_regs is defined properly + * (Should be optimized away, but gcc 2.6.3 is too bad..) + */ + if (FR_SIZE != sizeof(struct pt_regs) || + FR_SIZE & 7) + { + panic("Check_definition_of_struct_pt_regs\n"); + } +#endif +} diff --git a/arch/mips/signal.c b/arch/mips/kernel/signal.c index ef3246ee0..ea00551a9 100644 --- a/arch/mips/signal.c +++ b/arch/mips/kernel/signal.c @@ -1,10 +1,11 @@ /* - * linux/kernel/signal.c + * linux/arch/mips/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds */ #include <linux/sched.h> +#include <linux/mm.h> #include <linux/kernel.h> #include <linux/signal.h> #include <linux/errno.h> @@ -19,63 +20,7 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) -asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs); - -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; -} +asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); /* * atomically swap in the new signal mask, and wait for a signal. @@ -87,11 +32,7 @@ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long mask = current->blocked; current->blocked = set & _BLOCKABLE; -#if defined (__i386__) - regs->eax = -EINTR; -#elif defined (__mips__) regs->reg2 = -EINTR; -#endif while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); @@ -101,99 +42,6 @@ asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long } /* - * 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 int sys_signal(int signum, unsigned long handler) -{ - struct sigaction tmp; - - if (signum<1 || signum>32) - return -EINVAL; - if (signum==SIGKILL || signum==SIGSTOP) - return -EINVAL; - if (handler >= TASK_SIZE) - return -EFAULT; - tmp.sa_handler = (void (*)(int)) handler; - tmp.sa_mask = 0; - tmp.sa_flags = SA_ONESHOT | SA_NOMASK; - tmp.sa_restorer = NULL; - handler = (long) current->sigaction[signum-1].sa_handler; - current->sigaction[signum-1] = tmp; - check_pending(signum); - return 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 (TASK_SIZE <= (unsigned long) new_sa.sa_handler) - return -EFAULT; - } - 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; -} - -asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); - -/* * This sets regs->reg29 even though we don't actually use sigstacks yet.. */ asmlinkage int sys_sigreturn(unsigned long __unused) @@ -221,9 +69,25 @@ asmlinkage int sys_sigreturn(unsigned long __unused) regs->reg13 = context.sc_t5; regs->reg14 = context.sc_t6; regs->reg15 = context.sc_t7; + regs->reg16 = context.sc_s0; + regs->reg17 = context.sc_s1; + regs->reg18 = context.sc_s2; + regs->reg19 = context.sc_s3; + regs->reg20 = context.sc_s4; + regs->reg21 = context.sc_s5; + regs->reg22 = context.sc_s6; + regs->reg23 = context.sc_s7; regs->reg24 = context.sc_t8; regs->reg25 = context.sc_t9; + /* + * Skip k0/k1 + */ + regs->reg28 = context.sc_gp; regs->reg29 = context.sc_sp; + regs->reg30 = context.sc_fp; + regs->reg31 = context.sc_ra; + regs->cp0_epc = context.sc_epc; + regs->cp0_cause = context.sc_cause; /* * disable syscall checks @@ -244,11 +108,11 @@ static void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long * frame; frame = *fp; - frame -= 21; + frame -= 32; if (verify_area(VERIFY_WRITE,frame,21*4)) do_exit(SIGSEGV); /* - * set up the "normal" stack seen by the signal handler (iBCS2) + * set up the "normal" stack seen by the signal handler */ put_fs_long(regs->reg1 , frame ); put_fs_long(regs->reg2 , frame+ 1); @@ -258,34 +122,49 @@ static void setup_frame(struct sigaction * sa, unsigned long ** fp, put_fs_long(regs->reg6 , frame+ 5); put_fs_long(regs->reg7 , frame+ 6); put_fs_long(regs->reg8 , frame+ 7); - put_fs_long(regs->reg10, frame+ 8); - put_fs_long(regs->reg11, frame+ 9); - put_fs_long(regs->reg12, frame+10); - put_fs_long(regs->reg13, frame+11); - put_fs_long(regs->reg14, frame+12); - put_fs_long(regs->reg15, frame+13); - put_fs_long(regs->reg16, frame+14); - put_fs_long(regs->reg17, frame+15); - put_fs_long(regs->reg24, frame+16); - put_fs_long(regs->reg25, frame+17); - put_fs_long(regs->reg29, frame+18); - put_fs_long(pc , frame+19); - put_fs_long(oldmask , frame+20); + put_fs_long(regs->reg9 , frame+ 8); + put_fs_long(regs->reg10, frame+ 9); + put_fs_long(regs->reg11, frame+10); + put_fs_long(regs->reg12, frame+11); + put_fs_long(regs->reg13, frame+12); + put_fs_long(regs->reg14, frame+13); + put_fs_long(regs->reg15, frame+14); + put_fs_long(regs->reg16, frame+15); + put_fs_long(regs->reg17, frame+16); + put_fs_long(regs->reg18, frame+17); + put_fs_long(regs->reg19, frame+18); + put_fs_long(regs->reg20, frame+19); + put_fs_long(regs->reg21, frame+20); + put_fs_long(regs->reg22, frame+21); + put_fs_long(regs->reg23, frame+22); + put_fs_long(regs->reg24, frame+23); + put_fs_long(regs->reg25, frame+24); + /* + * Don't copy k0/k1 + */ + put_fs_long(regs->reg28, frame+25); + put_fs_long(regs->reg29, frame+26); + put_fs_long(regs->reg30, frame+27); + put_fs_long(regs->reg31, frame+28); + put_fs_long(pc , frame+29); + put_fs_long(oldmask , frame+30); /* * set up the return code... * + * .set noreorder * .set noat * syscall - * li $1,__NR_sigreturn + * li v0,__NR_sigreturn * .set at + * .set reorder */ - put_fs_long(0x24010077, frame+20); /* li $1,119 */ - put_fs_long(0x000000c0, frame+21); /* syscall */ + put_fs_long(0x24020077, frame+31); /* li $2,119 */ + put_fs_long(0x000000c0, frame+32); /* syscall */ *fp = frame; /* * Flush caches so the instructions will be correctly executed. */ - cacheflush(frame, 21*4, BCACHE); + sys_cacheflush(frame, 32*4, BCACHE); } /* @@ -307,30 +186,8 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) struct sigaction * sa; while ((signr = current->signal & mask)) { -#if defined (__i386__) - __asm__("bsf %2,%1\n\t" - "btrl %1,%0" - :"=m" (current->signal),"=r" (signr) - :"1" (signr)); -#elif defined (__mips__) - __asm__(".set\tnoreorder\n\t" - ".set\tnoat\n\t" - "li\t%1,1\n" - "1:\tand\t$1,%2,%1\n\t" - "beq\t$0,$1,2f\n\t" - "sll\t%2,%2,1\n\t" - "bne\t$0,%2,1b\n\t" - "add\t%0,%0,1\n" - "2:\tli\t%2,-2\n\t" - "sllv\t%2,%2,%0\n\t" - "and\t%1,%1,%2\n\t" - ".set\tat\n\t" - ".set\treorder\n" - "2:\n\t" - :"=r" (signr),"=r" (current->signal),"=r" (mask) - :"0" (signr),"1" (current->signal) - :"$1"); -#endif + signr = ffz(~signr); + clear_bit(signr, ¤t->signal); sa = current->sigaction + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { @@ -376,7 +233,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) continue; case SIGQUIT: case SIGILL: case SIGTRAP: - case SIGIOT: case SIGFPE: case SIGSEGV: + case SIGIOT: case SIGFPE: case SIGSEGV: case SIGBUS: if (current->binfmt && current->binfmt->core_dump) { if (current->binfmt->core_dump(signr, regs)) signr |= 0x80; @@ -404,7 +261,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) regs->reg2 == -ERESTARTSYS || regs->reg2 == -ERESTARTNOINTR)) { regs->reg2 = regs->orig_reg2; - regs->cp0_epc -= 2; + regs->cp0_epc -= 4; } if (!handler_signal) /* no handler will be called - return 0 */ return 0; @@ -425,16 +282,14 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) * force a kernel-mode page-in of the signal * handler to reduce races */ - __asm__(".set\tnoat\n\t" - "lwu\t$1,(%0)\n\t" - ".set\tat\n\t" - : - :"r" ((char *) pc) - :"$1"); + __asm__("lw\t$0,(%0)" + : /* no output */ + :"r" ((char *) pc)); current->blocked |= sa->sa_mask; oldmask |= sa->sa_mask; } regs->reg29 = (unsigned long) frame; regs->cp0_epc = pc; /* "return" to the first handler */ + return 1; } diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c new file mode 100644 index 000000000..36e8a31e8 --- /dev/null +++ b/arch/mips/kernel/traps.c @@ -0,0 +1,438 @@ +/* + * arch/mips/kernel/traps.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* + * 'traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. Currently mostly a debugging-aid, will be extended + * to mainly kill the offending process (probably by giving it a signal, + * but possibly by killing it outright if necessary). + * + * FIXME: This is the place for a fpu emulator. + */ +#include <linux/head.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/config.h> +#include <linux/timer.h> +#include <linux/mm.h> + +#include <asm/vector.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/mipsregs.h> +#include <asm/bootinfo.h> + +static inline void console_verbose(void) +{ + extern int console_loglevel; + console_loglevel = 15; +} + +/* + * Machine specific interrupt handlers + */ +extern asmlinkage void acer_pica_61_handle_int(void); +extern asmlinkage void decstation_handle_int(void); +extern asmlinkage void deskstation_rpc44_handle_int(void); +extern asmlinkage void deskstation_tyne_handle_int(void); +extern asmlinkage void mips_magnum_4000_handle_int(void); + +extern asmlinkage void handle_mod(void); +extern asmlinkage void handle_tlbl(void); +extern asmlinkage void handle_tlbs(void); +extern asmlinkage void handle_adel(void); +extern asmlinkage void handle_ades(void); +extern asmlinkage void handle_ibe(void); +extern asmlinkage void handle_dbe(void); +extern asmlinkage void handle_sys(void); +extern asmlinkage void handle_bp(void); +extern asmlinkage void handle_ri(void); +extern asmlinkage void handle_cpu(void); +extern asmlinkage void handle_ov(void); +extern asmlinkage void handle_tr(void); +extern asmlinkage void handle_vcei(void); +extern asmlinkage void handle_fpe(void); +extern asmlinkage void handle_vced(void); +extern asmlinkage void handle_watch(void); +extern asmlinkage void handle_reserved(void); + +static char *cpu_names[] = CPU_NAMES; + +/* + * Fix address errors. This is slow, so try not to use it. This is + * disabled by default, anyway. + */ +int fix_ade_enabled = 0; +unsigned long page_colour_mask; + +int kstack_depth_to_print = 24; + +/* + * These constant is for searching for possible module text segments. + * MODULE_RANGE is a guess of how much space is likely to be vmalloced. + */ +#define MODULE_RANGE (8*1024*1024) + +void die_if_kernel(char * str, struct pt_regs * regs, long err) +{ + int i; + int *stack; + u32 *sp, *pc, addr, module_start, module_end; + extern char start_kernel, _etext; + + if ((regs->cp0_status & (ST0_ERL|ST0_EXL)) == 0) + return; + + sp = (u32 *)regs->reg29; + pc = (u32 *)regs->cp0_epc; + + console_verbose(); + printk("%s: %08lx\n", str, err ); + + show_regs(regs); + + /* + * Some goodies... + */ + printk("Int : %ld\n", regs->interrupt); + + /* + * Dump the stack + */ + if (STACK_MAGIC != *(u32 *)current->kernel_stack_page) + printk("Corrupted stack page\n"); + printk("Process %s (pid: %d, stackpage=%08lx)\nStack: ", + current->comm, current->pid, current->kernel_stack_page); + for(i=0;i<5;i++) + printk("%08x ", *sp++); + stack = (int *) sp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((u32) stack & (PAGE_SIZE -1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", get_user_long(stack++)); + } + printk("\nCall Trace: "); + stack = (int *)sp; + i = 1; + module_start = VMALLOC_START; + module_end = module_start + MODULE_RANGE; + while (((u32)stack & (PAGE_SIZE -1)) != 0) { + addr = get_user_long(stack++); + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (u32) &start_kernel) && + (addr <= (u32) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08x ", addr); + i++; + } + } + + printk("\nCode : "); + for(i=0;i<5;i++) + printk("%08x ", *pc++); + printk("\n"); + do_exit(SIGSEGV); +} + +static void +fix_ade(struct pt_regs *regs, int write) +{ + printk("Received address error (ade%c)\n", write ? 's' : 'l'); + panic("Fixing address errors not implemented yet"); +} + +void do_adel(struct pt_regs *regs) +{ + unsigned long pc = regs->cp0_epc; + int i; + + if(fix_ade_enabled) + { + fix_ade(regs, 0); + return; + } +#if 0 + for(i=0; i<NR_TASKS;i++) + if(task[i] && task[i]->pid >= 2) + { + printk("Process %d\n", task[i]->pid); + dump_list_process(task[i], pc); + } +#endif + show_regs(regs); +while(1); + dump_tlb_nonwired(); + send_sig(SIGSEGV, current, 1); +} + +void do_ades(struct pt_regs *regs) +{ + unsigned long pc = regs->cp0_epc; + int i; + + if(fix_ade_enabled) + { + fix_ade(regs, 1); + return; + } +while(1); + for(i=0; i<NR_TASKS;i++) + if(task[i] && task[i]->pid >= 2) + { + printk("Process %d\n", task[i]->pid); + dump_list_process(task[i], pc); + } + show_regs(regs); + dump_tlb_nonwired(); + send_sig(SIGSEGV, current, 1); +} + +/* + * The ibe/dbe exceptions are signaled by onboard hardware and should get + * a board specific handlers to get maximum available information. Bus + * errors are always symptom of hardware malfunction or a kernel error. + * + * FIXME: Linux/68k sends a SIGSEGV for a buserror which seems to be wrong. + * This is certainly wrong. Actually, all hardware errors (ades,adel,ibe,dbe) + * are bus errors and therefor should send a SIGBUS! (Andy) + */ +void do_ibe(struct pt_regs *regs) +{ +while(1); + send_sig(SIGBUS, current, 1); +} + +void do_dbe(struct pt_regs *regs) +{ +while(1); + send_sig(SIGBUS, current, 1); +} + +void do_ov(struct pt_regs *regs) +{ +while(1); + send_sig(SIGFPE, current, 1); +} + +void do_fpe(struct pt_regs *regs) +{ +while(1); + send_sig(SIGFPE, current, 1); +} + +void do_bp(struct pt_regs *regs) +{ +while(1); + send_sig(SIGILL, current, 1); +} + +void do_tr(struct pt_regs *regs) +{ +while(1); + send_sig(SIGILL, current, 1); +} + +void do_ri(struct pt_regs *regs) +{ + int i; + + for(i=0; i<NR_TASKS;i++) + if(task[i] && task[i]->pid >= 2) + { + printk("Process %d\n", task[i]->pid); + dump_list_process(task[i], 0x7ffff000); + } + show_regs(regs); +while(1); + send_sig(SIGILL, current, 1); +} + +void do_cpu(struct pt_regs *regs) +{ + unsigned int cpid; + + cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; + switch(cpid) + { + case 1: + regs->cp0_status |= ST0_CU1; + break; + case 3: + /* + * This is a guess how to handle MIPS IV - + * I don't have a manual. + */ + if((boot_info.cputype == CPU_R8000) || + (boot_info.cputype == CPU_R10000)) + { + regs->cp0_status |= ST0_CU3; + break; + } + case 0: + /* + * CPU for cp0 can only happen in user mode + */ + case 2: + send_sig(SIGILL, current, 1); + break; + } +} + +void do_vcei(struct pt_regs *regs) +{ + /* + * Only possible on R4[04]00[SM]C. No handler because + * I don't have such a cpu. + */ + panic("Caught VCEI exception - can't handle yet\n"); +} + +void do_vced(struct pt_regs *regs) +{ + /* + * Only possible on R4[04]00[SM]C. No handler because + * I don't have such a cpu. + */ + panic("Caught VCED exception - can't handle yet\n"); +} + +void do_watch(struct pt_regs *regs) +{ + panic("Caught WATCH exception - can't handle yet\n"); +} + +void do_reserved(struct pt_regs *regs) +{ + /* + * Game over - no way to handle this if it ever occurs. + * Most probably caused by a new unknown cpu type or + * after another deadly hard/software error. + */ + panic("Caught reserved exception - can't handle.\n"); +} + +void trap_init(void) +{ + unsigned long i; + + if(boot_info.machtype == MACH_MIPS_MAGNUM_4000) + EISA_bus = 1; + + /* + * Setup default vectors + */ + for (i=0;i<=31;i++) + set_except_vector(i, handle_reserved); + + /* + * Handling the following exceptions depends mostly of the cpu type + */ + switch(boot_info.cputype) { + case CPU_R4000MC: + case CPU_R4400MC: + case CPU_R4000SC: + case CPU_R4400SC: + /* + * Handlers not implemented yet. If should every be used + * it's a bug in the Linux/MIPS kernel, anyway. + */ + set_except_vector(14, handle_vcei); + set_except_vector(31, handle_vced); + case CPU_R4000PC: + case CPU_R4400PC: + case CPU_R4200: + /* case CPU_R4300: */ + /* + * Use watch exception to trap on access to address zero + */ + set_except_vector(23, handle_watch); + watch_set(KSEG0, 3); + case CPU_R4600: + set_except_vector(1, handle_mod); + set_except_vector(2, handle_tlbl); + set_except_vector(3, handle_tlbs); + set_except_vector(4, handle_adel); + set_except_vector(5, handle_ades); + /* + * The following two are signaled by onboard hardware and + * should get board specific handlers to get maximum + * available information. + */ + set_except_vector(6, handle_ibe); + set_except_vector(7, handle_dbe); + + set_except_vector(8, handle_sys); + set_except_vector(9, handle_bp); + set_except_vector(10, handle_ri); + set_except_vector(11, handle_cpu); + set_except_vector(12, handle_ov); + set_except_vector(13, handle_tr); + set_except_vector(15, handle_fpe); + + /* + * Compute mask for page_colour(). This is based on the + * size of the data cache. Does the size of the icache + * need to be accounted for? + */ + i = read_32bit_cp0_register(CP0_CONFIG); + i = (i >> 26) & 7; + page_colour_mask = 1 << (12 + i); + break; + case CPU_R2000: + case CPU_R3000: + case CPU_R3000A: + case CPU_R3041: + case CPU_R3051: + case CPU_R3052: + case CPU_R3081: + case CPU_R3081E: + case CPU_R6000: + case CPU_R6000A: + case CPU_R8000: + printk("Detected unsupported CPU type %s.\n", + cpu_names[boot_info.cputype]); + panic("Can't handle CPU\n"); + break; + + /* + * The R10000 is in most aspects similar to the R4400. It however + * should get some special optimizations. + */ + case CPU_R10000: + write_32bit_cp0_register(CP0_FRAMEMASK, 0); + panic("CPU too expensive - making holiday in the ANDES!"); + break; + case CPU_UNKNOWN: + default: + panic("Unknown CPU type"); + } + + /* + * The interrupt handler depends most of the board type. + */ + set_except_vector(0, feature->handle_int); +} diff --git a/arch/mips/kernel/tyne.S b/arch/mips/kernel/tyne.S new file mode 100644 index 000000000..912f6d414 --- /dev/null +++ b/arch/mips/kernel/tyne.S @@ -0,0 +1,114 @@ +/* + * arch/mips/kernel/tyne.S + * + * Deskstation Tyne specific Assembler code + * + * Copyright (C) 1994, 1995 Waldorf Electronics + * written by Ralf Baechle and Andreas Busse + */ +#include <asm/asm.h> +#include <asm/mipsconfig.h> +#include <asm/mipsregs.h> +#include <asm/stackframe.h> + +/* + * Deskstation Tyne interrupt handler + */ + .text + .set noreorder + .set noat + .align 5 + NESTED(deskstation_tyne_handle_int, FR_SIZE, sp) + SAVE_ALL + CLI + .set at + lui s0,%hi(PORT_BASE) + li t1,0x0f + sb t1,%lo(PORT_BASE+0x20)(s0) # poll command + lb t1,%lo(PORT_BASE+0x20)(s0) # read result + li s1,1 + bgtz t1,Lpoll_second + andi t1,t1,7 + /* + * Acknowledge first pic + */ + lb t2,%lo(PORT_BASE+0x21)(s0) + lui s4,%hi(cache_21) + lb t0,%lo(cache_21)(s4) + sllv s1,s1,t1 + or t0,t0,s1 + sb t0,%lo(cache_21)(s4) + sb t0,%lo(PORT_BASE+0x21)(s0) + lui s3,%hi(intr_count) + lw t0,%lo(intr_count)(s3) + li t2,0x20 + sb t2,%lo(PORT_BASE+0x20)(s0) + /* + * Now call the real handler + */ + la t3,IRQ_vectors + sll t2,t1,2 + addu t3,t3,t2 + lw t3,(t3) + addiu t0,t0,1 + jalr t3 + sw t0,%lo(intr_count)(s3) # delay slot + lw t0,%lo(intr_count)(s3) + /* + * Unblock first pic + */ + lbu t1,%lo(PORT_BASE+0x21)(s0) + lb t1,%lo(cache_21)(s4) + subu t0,t0,1 + sw t0,%lo(intr_count)(s3) + nor s1,zero,s1 + and t1,t1,s1 + sb t1,%lo(cache_21)(s4) + jr v0 + sb t1,%lo(PORT_BASE+0x21)(s0) # delay slot + + .align 5 +Lpoll_second: li t1,0x0f + sb t1,%lo(PORT_BASE+0xa0)(s0) # poll command + lb t1,%lo(PORT_BASE+0xa0)(s0) # read result + lui s4,%hi(cache_A1) + bgtz t1,spurious_interrupt + andi t1,t1,7 + /* + * Acknowledge second pic + */ + lbu t2,%lo(PORT_BASE+0xa1)(s0) + lb t3,%lo(cache_A1)(s4) + sllv s1,s1,t1 + or t3,t3,s1 + sb t3,%lo(cache_A1)(s4) + sb t3,%lo(PORT_BASE+0xa1)(s0) + li t3,0x20 + sb t3,%lo(PORT_BASE+0xa0)(s0) + lui s3,%hi(intr_count) + lw t0,%lo(intr_count)(s3) + sb t3,%lo(PORT_BASE+0x20)(s0) + /* + * Now call the real handler + */ + la t0,IRQ_vectors + sll t2,t1,2 + addu t0,t0,t2 + lw t0,32(t0) + addiu t0,t0,1 + jalr t0 + sw t0,%lo(intr_count)(s3) # delay slot + lw t0,%lo(intr_count)(s3) + /* + * Unblock second pic + */ + lb t1,%lo(PORT_BASE+0xa1)(s0) + lb t1,%lo(cache_A1)(s4) + subu t0,t0,1 + lw t0,%lo(intr_count)(s3) + nor s1,zero,s1 + and t1,t1,s1 + sb t1,%lo(cache_A1)(s4) + jr v0 + sb t1,%lo(PORT_BASE+0xa1)(s0) # delay slot + END(deskstation_tyne_handle_int) diff --git a/arch/mips/kernel/tynedma.c b/arch/mips/kernel/tynedma.c new file mode 100644 index 000000000..04846cddd --- /dev/null +++ b/arch/mips/kernel/tynedma.c @@ -0,0 +1,36 @@ +/* + * Tiny Tyne DMA buffer allocator + * + * Copyright (C) 1995 Ralf Baechle + */ +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/bootinfo.h> + +#ifdef CONFIG_DESKSTATION_TYNE + +static unsigned long allocated; + +/* + * Not very sophisticated, but should suffice for now... + */ +unsigned long deskstation_tyne_dma_alloc(size_t size) +{ + unsigned long ret = allocated; + allocated += size; + if (allocated > boot_info.dma_cache_size) + ret = -1; + return ret; +} + +void deskstation_tyne_dma_init(void) +{ + if (boot_info.machtype != MACH_DESKSTATION_TYNE) + return; + allocated = 0; + printk ("Deskstation Tyne DMA (%luk) buffer initialized.\n", + boot_info.dma_cache_size >> 10); +} + +#endif /* CONFIG_DESKSTATION_TYNE */ diff --git a/arch/mips/vm86.c b/arch/mips/kernel/vm86.c index 454b35fe0..454b35fe0 100644 --- a/arch/mips/vm86.c +++ b/arch/mips/kernel/vm86.c diff --git a/arch/mips/ldt.c b/arch/mips/ldt.c deleted file mode 100644 index 089605cee..000000000 --- a/arch/mips/ldt.c +++ /dev/null @@ -1,13 +0,0 @@ -/* - * arch/mips/ldt.c - * - * Copyright (C) 1994 by Waldorf GMBH, - * written by Ralf Baechle - */ -#include <linux/linkage.h> -#include <linux/errno.h> - -asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount) -{ - return -ENOSYS; -} diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile new file mode 100644 index 000000000..56279fb3e --- /dev/null +++ b/arch/mips/lib/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for MIPS-specific library files.. +# + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) $(ASFLAGS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) $(CFLAGS) $< -o $*.s +.S.o: + $(CC) $(CFLAGS) -c $< -o $*.o + +OBJS = dump_tlb.o tinycon.o watch.o + +include ../../../.config + +lib.a: $(OBJS) + $(AR) rcs lib.a $(OBJS) + sync + +dep: + $(CPP) -M *.[cS] > .depend + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + diff --git a/arch/mips/lib/dump_tlb.c b/arch/mips/lib/dump_tlb.c new file mode 100644 index 000000000..f730b9fce --- /dev/null +++ b/arch/mips/lib/dump_tlb.c @@ -0,0 +1,161 @@ +/* + * Dump R4x00 TLB for debugging purposes. + * + * Copyright (C) 1994, 1995 by Waldorf Electronics, + * written by Ralf Baechle. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> + +#include <asm/bootinfo.h> +#include <asm/cachectl.h> +#include <asm/mipsregs.h> + +static void +dump_tlb(int first, int last) +{ + int i; + int wired; + unsigned int pagemask; + unsigned long long entryhi, entrylo0, entrylo1; + + wired = read_32bit_cp0_register(CP0_WIRED); + printk("Wired: %d", wired); + + for(i=first;i<last;i++) + { + write_32bit_cp0_register(CP0_INDEX, i); + __asm__ __volatile__( + ".set\tmips3\n\t" + ".set\tnoreorder\n\t" + "nop;nop;nop;nop\n\t" + "tlbr\n\t" + "nop;nop;nop;nop\n\t" + ".set\treorder\n\t" + ".set\tmips0\n\t"); + pagemask = read_32bit_cp0_register(CP0_PAGEMASK); + entryhi = read_64bit_cp0_register(CP0_ENTRYHI); + entrylo0 = read_64bit_cp0_register(CP0_ENTRYLO0); + entrylo1 = read_64bit_cp0_register(CP0_ENTRYLO1); + + if((entrylo0|entrylo1) & 2) + { + /* + * Only print entries in use + */ + printk("\nIndex: %2d %08x", i, pagemask); + + printk(" %08x %08x", (unsigned int)(entryhi >> 32), + (unsigned int) entryhi); + printk(" %08x %08x", (unsigned int)(entrylo0 >> 32), + (unsigned int) entrylo0); + printk(" %08x %08x", (unsigned int)(entrylo1 >> 32), + (unsigned int) entrylo1); + } + } + printk("\n"); +} + +void +dump_tlb_all(void) +{ + dump_tlb(0, boot_info.tlb_entries - 1); +} + +void +dump_tlb_nonwired(void) +{ + dump_tlb(read_32bit_cp0_register(CP0_WIRED), boot_info.tlb_entries - 1); +} + +#include <linux/kernel.h> +#include <linux/mm.h> + +#include <asm/mipsconfig.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +void +dump_list_process(struct task_struct *t, void *address) +{ + pgd_t *page_dir, *pgd; + pmd_t *pmd; + pte_t *pte, page; + unsigned int addr; + + addr = (unsigned int) address; + printk("Addr == %08x\n", addr); + + printk("tasks->tss.pg_dir == %08x\n", + (unsigned int) t->tss.pg_dir); + + page_dir = pgd_offset(t, 0); + printk("page_dir == %08x\n", (unsigned int) page_dir); + + pgd = pgd_offset(t, addr); + printk("pgd == %08x, ", (unsigned int) pgd); + + pmd = pmd_offset(pgd, addr); + printk("pmd == %08x, ", (unsigned int) pmd); + + pte = pte_offset(pmd, addr); + printk("pte == %08x, ", (unsigned int) pte); + + page = *pte; + printk("page == %08x\n", (unsigned int) pte_val(page)); + +} + +void +dump_list_current(void *address) +{ + unsigned int addr, *pt; + + dump_list_process(current, address); + + addr = (unsigned int) address; + pt = (unsigned int *) TLB_ROOT + (addr >> 22); + printk("L1: *%08x == ", (unsigned int) pt); + printk("%08x, ", *pt); + + pt = (unsigned int *) (TLBMAP + ((addr >> 10) & ~3)); + printk("L2: *%08x == ", (unsigned int) pt); + printk("%08x\n", *pt); +} + +#include <asm/segment.h> + +unsigned int +vtop(void *address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned int addr, paddr; + + addr = (unsigned int) address; + pgd = pgd_offset(current, addr); + pmd = pmd_offset(pgd, addr); + pte = pte_offset(pmd, addr); + paddr = (KSEG1 | (unsigned int) pte_val(*pte)) & PAGE_MASK; + paddr |= (addr & ~PAGE_MASK); + + return paddr; +} + +void +dump16(unsigned long *p) +{ + int i; + + for(i=0;i<8;i++) + { + printk("*%08lx == %08lx, ", + (unsigned long)p, (unsigned long)*p++); + printk("*%08lx == %08lx\n", + (unsigned long)p, (unsigned long)*p++); + } +} + diff --git a/arch/mips/lib/tinycon.c b/arch/mips/lib/tinycon.c new file mode 100644 index 000000000..4410986d8 --- /dev/null +++ b/arch/mips/lib/tinycon.c @@ -0,0 +1,133 @@ +/* ---------------------------------------------------------------------- + * console.c + * + * Copyright (C) 1994 by Waldorf Electronic, + * written by Ralf Baechle and Andreas Busse + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * ---------------------------------------------------------------------- */ +/* + * FIXME: This file is hacked to be hardwired for the Deskstation + * Only thought as a debugging console output + */ + +#include <linux/tty.h> +#include <asm/bootinfo.h> + +static unsigned int size_x; +static unsigned int size_y; +static unsigned short cursor_x; +static unsigned short cursor_y; +static volatile unsigned short *vram_addr; +static int console_needs_init = 1; + +extern struct bootinfo boot_info; +extern struct screen_info screen_info; + +/* ---------------------------------------------------------------------- + * init_console() + * ---------------------------------------------------------------------- */ + +void init_console(void) +{ + size_x = 80; + size_y = 50; + cursor_x = 0; + cursor_y = 0; + + vram_addr = (unsigned short *)0xe10b8000; + + console_needs_init = 0; +} + +void +set_size_x(unsigned int x) +{ + size_x = x; +} + +void +set_size_y(unsigned int y) +{ + size_y = y; +} + +void +set_vram(unsigned short *vram) +{ + vram_addr = vram; +} + +void +set_cursor(unsigned int x, unsigned int y) +{ + cursor_x = x; + cursor_y = y; +} + +void +print_char(unsigned int x, unsigned int y, unsigned char c) +{ + volatile unsigned short *caddr; + + caddr = vram_addr + (y * size_x) + x; + *caddr = (*caddr & 0xff00) | 0x0f00 | (unsigned short) c; +} + +static void +scroll(void) +{ + volatile unsigned short *caddr; + register int i; + + caddr = vram_addr; + for(i=0; i<size_x * (size_y-1); i++) + *(caddr++) = *(caddr + size_x); + + /* blank last line */ + + caddr = vram_addr + (size_x * (size_y-1)); + for(i=0; i<size_x; i++) + *(caddr++) = (*caddr & 0xff00) | (unsigned short) ' '; +} + +void print_string(const unsigned char *str) +{ + unsigned char c; + + if (console_needs_init) + init_console(); + + while((c = *str++)) + switch(c) + { + case '\n': + cursor_x = 0; + cursor_y++; + if(cursor_y == size_y) + { + scroll(); + cursor_y = size_y - 1; + } + break; + + default: + print_char(cursor_x, cursor_y, c); + cursor_x++; + if(cursor_x == size_x) + { + cursor_x = 0; + cursor_y++; + if(cursor_y == size_y) + { + scroll(); + cursor_y = size_y - 1; + } + } + break; + } +} + +/* end of file */ diff --git a/arch/mips/lib/watch.S b/arch/mips/lib/watch.S new file mode 100644 index 000000000..36b54d5c0 --- /dev/null +++ b/arch/mips/lib/watch.S @@ -0,0 +1,102 @@ +/* + * Kernel debug stuff to use the Watch registers. + * Usefull to find stack overflows etc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1995 Ralf Baechle + */ +#include <asm/asm.h> +#include <asm/mipsregs.h> + + .set noreorder +/* + * Parameter: a0 - logic address to watch + * Currently only KSEG0 addresses are allowed! + * a1 - set bit #1 to trap on load references + * bit #0 to trap on store references + * Results : none + */ + LEAF(watch_set) + li t0,0x80000000 + subu a0,t0 + ori a0,7 + xori a0,7 + or a0,a1 + mtc0 a0,CP0_WATCHLO + jr ra + mtc0 zero,CP0_WATCHHI + END(watch_set) + +/* + * The stuff below are just some kernel debugging gadgets. It will + * go away. + */ + +/* + * Parameter: none + * Results : none + */ + LEAF(watch_clear) + jr ra + mtc0 zero,CP0_WATCHLO + END(watch_clear) + +/* + * Parameter: none + * Results : none + */ + LEAF(get_sp) + jr ra + move v0,sp + END(get_sp) + +/* + * Parameter: none + * Results : none + */ + LEAF(get_ra) + jr ra + move v0,ra + END(get_ra) + +/* + * Parameter: none + * Results : none + */ + LEAF(get_status) + jr ra + mfc0 v0,CP0_STATUS + END(get_status) + +/* + * Parameter: none + * Results : none + */ + NESTED(print_sp, 24, sp) + .mask 0x80000000,16 + subu sp,24 + sw ra,16(sp) + move a1,sp + PRINT("$sp == %08lx\n") + lw ra,16(sp) + jr ra + addiu sp,24 + END(print_sp) + +/* + * Parameter: none + * Results : none + */ + NESTED(print_st, 24, sp) + .mask 0x80000000,16 + subu sp,24 + sw ra,16(sp) + mfc0 a1,CP0_STATUS + PRINT("cp0_status == %08lx\n") + lw ra,16(sp) + jr ra + addiu sp,24 + END(print_st) diff --git a/arch/mips/main.c b/arch/mips/main.c deleted file mode 100644 index 8cb92d5f2..000000000 --- a/arch/mips/main.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * arch/mips/main.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * MIPSified by Ralf Baechle - */ - -#include <stdarg.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/bootinfo.h> - -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/config.h> -#include <linux/sched.h> -#include <linux/tty.h> -#include <linux/head.h> -#include <linux/unistd.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/fs.h> -#include <linux/ctype.h> -#include <linux/delay.h> -#include <linux/utsname.h> -#include <linux/ioport.h> - -extern unsigned long * prof_buffer; -extern unsigned long prof_len; -extern char edata, end; -extern char *linux_banner; - -/* - * we need this inline - forking from kernel space will result - * in NO COPY ON WRITE (!!!), until an execve is executed. This - * is no problem, but for the stack. This is handled by not letting - * main() use the stack at all after fork(). Thus, no function - * calls - which means inline code for fork too, as otherwise we - * would use the stack upon exit from 'fork()'. - * - * Actually only pause and fork are needed inline, so that there - * won't be any messing with the stack from main(), but we define - * some others too. - */ -#define __NR__exit __NR_exit -static inline _syscall0(int,idle) -static inline _syscall0(int,fork) - -extern int console_loglevel; - -extern char empty_zero_page[PAGE_SIZE]; -extern void init(void); -extern void init_IRQ(void); -extern void init_modules(void); -extern long console_init(long, long); -extern long kmalloc_init(long,long); -extern long blk_dev_init(long,long); -extern long chr_dev_init(long,long); -extern void floppy_init(void); -extern void sock_init(void); -extern long rd_init(long mem_start, int length); -unsigned long net_dev_init(unsigned long, unsigned long); -#if 0 -extern long bios32_init(long, long); -#endif - -extern void hd_setup(char *str, int *ints); -extern void bmouse_setup(char *str, int *ints); -extern void eth_setup(char *str, int *ints); -extern void xd_setup(char *str, int *ints); -extern void mcd_setup(char *str, int *ints); -extern void st_setup(char *str, int *ints); -extern void st0x_setup(char *str, int *ints); -extern void tmc8xx_setup(char *str, int *ints); -extern void t128_setup(char *str, int *ints); -extern void pas16_setup(char *str, int *ints); -extern void generic_NCR5380_setup(char *str, int *intr); -extern void aha152x_setup(char *str, int *ints); -extern void aha1542_setup(char *str, int *ints); -extern void aha274x_setup(char *str, int *ints); -extern void scsi_luns_setup(char *str, int *ints); -extern void sound_setup(char *str, int *ints); -#ifdef CONFIG_SBPCD -extern void sbpcd_setup(char *str, int *ints); -#endif CONFIG_SBPCD -#ifdef CONFIG_CDU31A -extern void cdu31a_setup(char *str, int *ints); -#endif CONFIG_CDU31A -void ramdisk_setup(char *str, int *ints); - -#ifdef CONFIG_SYSVIPC -extern void ipc_init(void); -#endif -#ifdef CONFIG_SCSI -extern unsigned long scsi_dev_init(unsigned long, unsigned long); -#endif - -/* - * This is set up by the setup-routine at boot-time - */ -#define PARAM empty_zero_page -#define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80)) -#define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2)) -#define RAMDISK_SIZE (*(unsigned short *) (PARAM+0x1F8)) -#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) -#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) - -/* - * Defaults, may be overwritten by milo - */ -#define SCREEN_INFO {0,0,{0,0},52,3,80,4626,3,9,50} - -/* - * Information passed by milo - */ -struct bootinfo boot_info; -struct screen_info screen_info = SCREEN_INFO; - -/* - * Boot command-line arguments - */ -extern void copy_options(char * to, char * from); -void parse_options(char *line); -#define MAX_INIT_ARGS 8 -#define MAX_INIT_ENVS 8 -#define COMMAND_LINE ((char *) (PARAM+2048)) -#define COMMAND_LINE_SIZE 256 - -extern void time_init(void); - -static unsigned long memory_start = 0; /* After mem_init, stores the */ - /* amount of free user memory */ -/* static */ unsigned long memory_end = 0; -/* static unsigned long low_memory_start = 0; */ - -int rows, cols; - -struct drive_info_struct { char dummy[32]; } drive_info; - -unsigned char aux_device_present; -int ramdisk_size; -int root_mountflags; - -static char command_line[COMMAND_LINE_SIZE] = { 0, }; - -struct { - char *str; - void (*setup_func)(char *, int *); -} bootsetups[] = { - { "ramdisk=", ramdisk_setup }, -#ifdef CONFIG_INET - { "ether=", eth_setup }, -#endif -#ifdef CONFIG_SCSI - { "max_scsi_luns=", scsi_luns_setup }, -#endif -#ifdef CONFIG_BLK_DEV_HD - { "hd=", hd_setup }, -#endif -#ifdef CONFIG_CHR_DEV_ST - { "st=", st_setup }, -#endif -#ifdef CONFIG_BUSMOUSE - { "bmouse=", bmouse_setup }, -#endif -#ifdef CONFIG_SCSI_SEAGATE - { "st0x=", st0x_setup }, - { "tmc8xx=", tmc8xx_setup }, -#endif -#ifdef CONFIG_SCSI_T128 - { "t128=", t128_setup }, -#endif -#ifdef CONFIG_SCSI_PAS16 - { "pas16=", pas16_setup }, -#endif -#ifdef CONFIG_SCSI_GENERIC_NCR5380 - { "ncr5380=", generic_NCR5380_setup }, -#endif -#ifdef CONFIG_SCSI_AHA152X - { "aha152x=", aha152x_setup}, -#endif -#ifdef CONFIG_SCSI_AHA1542 - { "aha1542=", aha1542_setup}, -#endif -#ifdef CONFIG_SCSI_AHA274X - { "aha274x=", aha274x_setup}, -#endif -#ifdef CONFIG_BLK_DEV_XD - { "xd=", xd_setup }, -#endif -#ifdef CONFIG_MCD - { "mcd=", mcd_setup }, -#endif -#ifdef CONFIG_SOUND - { "sound=", sound_setup }, -#endif -#ifdef CONFIG_SBPCD - { "sbpcd=", sbpcd_setup }, -#endif CONFIG_SBPCD -#ifdef CONFIG_CDU31A - { "cdu31a=", cdu31a_setup }, -#endif CONFIG_CDU31A - { 0, 0 } -}; - -void ramdisk_setup(char *str, int *ints) -{ - if (ints[0] > 0 && ints[1] >= 0) - ramdisk_size = ints[1]; -} - -unsigned long loops_per_sec = 1; - -static void calibrate_delay(void) -{ - int ticks; - - printk("Calibrating delay loop.. "); - while (loops_per_sec <<= 1) { - /* wait for "start of" clock tick */ - ticks = jiffies; - while (ticks == jiffies) - /* nothing */; - /* Go .. */ - ticks = jiffies; - __delay(loops_per_sec); - ticks = jiffies - ticks; - if (ticks >= HZ) { - /* - * No assembler - should be ok - */ - loops_per_sec = (loops_per_sec * HZ) / ticks; - printk("ok - %lu.%02lu BogoMips\n", - loops_per_sec/500000, - (loops_per_sec/5000) % 100); - return; - } - } - printk("failed\n"); -} - -int parse_machine_options(char *line) -{ - /* - * No special MIPS options yet - */ - return 0; -} - -asmlinkage void start_kernel(void) -{ - /* - * Interrupts are still disabled. Do necessary setups, then - * enable them - */ - ROOT_DEV = ORIG_ROOT_DEV; - drive_info = DRIVE_INFO; - aux_device_present = AUX_DEVICE_INFO; -#if 0 - memory_end = (1<<20) + (EXT_MEM_K<<10); -#else - memory_end = 0x80800000; -#endif - memory_end &= PAGE_MASK; - ramdisk_size = RAMDISK_SIZE; - copy_options(command_line,COMMAND_LINE); - - if (MOUNT_ROOT_RDONLY) - root_mountflags |= MS_RDONLY; - memory_start = 0x7fffffff & (unsigned long) &end; - - memory_start = paging_init(memory_start,memory_end); - trap_init(); - init_IRQ(); - sched_init(); - parse_options(command_line); - init_modules(); -#ifdef CONFIG_PROFILE - prof_buffer = (unsigned long *) memory_start; - prof_len = (unsigned long) &end; - prof_len >>= 2; - memory_start += prof_len * sizeof(unsigned long); -#endif - memory_start = console_init(memory_start,memory_end); - memory_start = kmalloc_init(memory_start,memory_end); - memory_start = chr_dev_init(memory_start,memory_end); - memory_start = blk_dev_init(memory_start,memory_end); - sti(); - calibrate_delay(); -#ifdef CONFIG_SCSI - memory_start = scsi_dev_init(memory_start,memory_end); -#endif -#ifdef CONFIG_INET - memory_start = net_dev_init(memory_start,memory_end); -#endif -while(1); - memory_start = inode_init(memory_start,memory_end); - memory_start = file_table_init(memory_start,memory_end); - memory_start = name_cache_init(memory_start,memory_end); - mem_init(memory_start,memory_end); - buffer_init(); - time_init(); - floppy_init(); - sock_init(); -#ifdef CONFIG_SYSVIPC - ipc_init(); -#endif - sti(); - - /* - * Get CPU type - * FIXME: Not implemented yet - */ - - printk(linux_banner); - - move_to_user_mode(); - if (!fork()) /* we count on this going ok */ - init(); -/* - * task[0] is meant to be used as an "idle" task: it may not sleep, but - * it might do some general things like count free pages or it could be - * used to implement a reasonable LRU algorithm for the paging routines: - * anything that can be useful, but shouldn't take time from the real - * processes. - * - * Right now task[0] just does a infinite idle loop. - */ - for(;;) - idle(); -} diff --git a/arch/mips/mkdisk b/arch/mips/mkdisk new file mode 100644 index 000000000..468d34727 --- /dev/null +++ b/arch/mips/mkdisk @@ -0,0 +1,5 @@ +#!/bin/sh +cp vmlinux vmlinux.tmp +mipsel-linux-strip -g -x vmlinux.tmp +mwrite -n vmlinux.tmp a:vmlinux +rm -f vmlinux.tmp diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile index 5063d60c2..6ff21fafd 100644 --- a/arch/mips/mm/Makefile +++ b/arch/mips/mm/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the linux memory manager. +# Makefile for the linux mips-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here @@ -14,11 +14,13 @@ .c.s: $(CC) $(CFLAGS) -S $< -OBJS = memory.o swap.o mmap.o mprotect.o kmalloc.o vmalloc.o +OBJS = fault.o init.o mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) +modules: + dep: $(CPP) -M *.c > .depend diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c new file mode 100644 index 000000000..9256025d9 --- /dev/null +++ b/arch/mips/mm/fault.c @@ -0,0 +1,92 @@ +/* + * arch/mips/mm/fault.c + * + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * Ported to MIPS by Ralf Baechle + */ +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void die_if_kernel(char *, struct pt_regs *, long); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + */ +asmlinkage void +do_page_fault(struct pt_regs *regs, unsigned long writeaccess) +{ + struct vm_area_struct * vma; + unsigned long address; + + /* get the address */ + __asm__(".set\tmips3\n\t" + "dmfc0\t%0,$8\n\t" + ".set\tmips0" + : "=r" (address)); + +#if 0 + printk("do_page_fault() #1: %s %08lx (epc == %08lx)\n", + writeaccess ? "writeaccess to" : "readaccess from", + address, regs->cp0_epc); +#endif + vma = find_vma(current, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) + goto bad_area; + vma->vm_offset -= vma->vm_start - (address & PAGE_MASK); + vma->vm_start = (address & PAGE_MASK); +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + if (writeaccess) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else { + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + handle_mm_fault(vma, address, writeaccess); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + if (user_mode(regs)) { + current->tss.cp0_badvaddr = address; + current->tss.error_code = writeaccess; + send_sig(SIGSEGV, current, 1); + return; + } + /* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + printk(KERN_ALERT "Unable to handle kernel paging request at virtual address %08lx", + address); + die_if_kernel("Oops", regs, writeaccess); + do_exit(SIGKILL); +} diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c new file mode 100644 index 000000000..37912e2d0 --- /dev/null +++ b/arch/mips/mm/init.c @@ -0,0 +1,299 @@ +/* + * arch/mips/mm/init.c + * + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * Ported to MIPS by Ralf Baechle + */ +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/head.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> + +#include <asm/cachectl.h> +#include <asm/vector.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> + +extern void deskstation_tyne_dma_init(void); +extern void scsi_mem_init(unsigned long); +extern void sound_mem_init(void); +extern void die_if_kernel(char *,struct pt_regs *,long); +extern void show_net_buffers(void); + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +pte_t * __bad_pagetable(void) +{ + extern char empty_bad_page_table[PAGE_SIZE]; + unsigned long page; + unsigned long dummy1, dummy2; + + page = ((unsigned long)empty_bad_page_table) + (PT_OFFSET - PAGE_OFFSET); +#ifdef __R4000__ + /* + * Use 64bit code even for Linux/MIPS 32bit on R4000 + */ + __asm__ __volatile__( + ".set\tnoreorder\n" + ".set\tnoat\n\t" + ".set\tmips3\n\t" + "dsll32\t$1,%2,0\n\t" + "dsrl32\t%2,$1,0\n\t" + "or\t%2,$1\n" + "1:\tsd\t%2,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,8\n\t" + ".set\tmips0\n\t" + ".set\tat\n" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"r" (pte_val(BAD_PAGE)), + "0" (page), + "1" (PAGE_SIZE/8)); +#else + __asm__ __volatile__( + ".set\tnoreorder\n" + "1:\tsw\t%2,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,4\n\t" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"r" (pte_val(BAD_PAGE)), + "0" (page), + "1" (PAGE_SIZE/4)); +#endif + + return (pte_t *)page; +} + +static inline void +__zeropage(unsigned long page) +{ + unsigned long dummy1, dummy2; + +#ifdef __R4000__ + /* + * Use 64bit code even for Linux/MIPS 32bit on R4000 + */ + __asm__ __volatile__( + ".set\tnoreorder\n" + ".set\tnoat\n\t" + ".set\tmips3\n" + "1:\tsd\t$0,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,8\n\t" + ".set\tmips0\n\t" + ".set\tat\n" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"0" (page), + "1" (PAGE_SIZE/8)); +#else + __asm__ __volatile__( + ".set\tnoreorder\n" + "1:\tsw\t$0,(%0)\n\t" + "subu\t%1,1\n\t" + "bnez\t%1,1b\n\t" + "addiu\t%0,4\n\t" + ".set\treorder" + :"=r" (dummy1), + "=r" (dummy2) + :"0" (page), + "1" (PAGE_SIZE/4)); +#endif +} + +static inline void +zeropage(unsigned long page) +{ + sys_cacheflush((void *)page, PAGE_SIZE, BCACHE); + sync_mem(); + __zeropage(page + (PT_OFFSET - PAGE_OFFSET)); +} + +pte_t __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + unsigned long page = (unsigned long)empty_bad_page; + + zeropage(page); + return pte_mkdirty(mk_pte(page, PAGE_SHARED)); +} + +unsigned long __zero_page(void) +{ + unsigned long page = (unsigned long) empty_zero_page; + + zeropage(page); + return page; +} + +/* + * This is horribly inefficient ... + */ +void __copy_page(unsigned long from, unsigned long to) +{ + /* + * Now copy page from uncached KSEG1 to KSEG0. The copy destination + * is in KSEG0 so that we keep stupid L2 caches happy. + */ + if(from == (unsigned long) empty_zero_page) + { + /* + * The page copied most is the COW empty_zero_page. Since we + * know it's contents we can avoid the writeback reading of + * the page. Speeds up the standard case alot. + */ + __zeropage(to); + } + else + { + /* + * Force writeback of old page to memory. We don't know the + * virtual address, so we have to flush the entire cache ... + */ + sys_cacheflush(0, ~0, DCACHE); + sync_mem(); + memcpy((void *) to, + (void *) (from + (PT_OFFSET - PAGE_OFFSET)), PAGE_SIZE); + } + /* + * Now writeback the page again if colour has changed. + * Actually this does a Hit_Writeback, but due to an artifact in + * the R4xx0 implementation this should be slightly faster. + * Then sweep chipset controlled secondary caches and the ICACHE. + */ + if (page_colour(from) != page_colour(to)) + sys_cacheflush(0, ~0, DCACHE); + sys_cacheflush(0, ~0, ICACHE); +} + +void show_mem(void) +{ + int i,free = 0,total = 0; + int shared = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); + i = (high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + while (i-- > 0) { + total++; + if (!mem_map[i]) + free++; + else + shared += mem_map[i]-1; + } + printk("%d pages of RAM\n", total); + printk("%d free pages\n", free); + printk("%d pages shared\n", shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + pgd_init((unsigned long)swapper_pg_dir - (PT_OFFSET - PAGE_OFFSET)); + return free_area_init(start_mem, end_mem); +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + int codepages = 0; + int datapages = 0; + unsigned long tmp; + extern int _etext; + + end_mem &= PAGE_MASK; + high_memory = end_mem; + + /* mark usable pages in the mem_map[] */ + start_mem = PAGE_ALIGN(start_mem); + + tmp = start_mem; + while (tmp < high_memory) { + mem_map[MAP_NR(tmp)] = 0; + tmp += PAGE_SIZE; + } +#ifdef CONFIG_DESKSTATION_TYNE + deskstation_tyne_dma_init(); +#endif +#ifdef CONFIG_SCSI + scsi_mem_init(high_memory); +#endif +#ifdef CONFIG_SOUND + sound_mem_init(); +#endif + for (tmp = PAGE_OFFSET ; tmp < high_memory ; tmp += PAGE_SIZE) { + if (mem_map[MAP_NR(tmp)]) { + if (tmp < (unsigned long) &_etext) + codepages++; + else if (tmp < start_mem) + datapages++; + continue; + } + mem_map[MAP_NR(tmp)] = 1; + free_page(tmp); + } + tmp = nr_free_pages << PAGE_SHIFT; + printk("Memory: %luk/%luk available (%dk kernel code, %dk data)\n", + tmp >> 10, + (high_memory - PAGE_OFFSET) >> 10, + codepages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10)); + + return; +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = high_memory >> PAGE_SHIFT; + val->totalram = 0; + val->sharedram = 0; + val->freeram = nr_free_pages << PAGE_SHIFT; + val->bufferram = buffermem; + while (i-- > 0) { + if (mem_map[i] & MAP_PAGE_RESERVED) + continue; + val->totalram++; + if (!mem_map[i]) + continue; + val->sharedram += mem_map[i]-1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff --git a/arch/mips/mm/kmalloc.c b/arch/mips/mm/kmalloc.c deleted file mode 100644 index 018f8db8f..000000000 --- a/arch/mips/mm/kmalloc.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * linux/mm/kmalloc.c - * - * Copyright (C) 1991, 1992 Linus Torvalds & Roger Wolff. - * - * Written by R.E. Wolff Sept/Oct '93. - * - */ - -/* - * Modified by Alex Bligh (alex@cconcepts.co.uk) 4 Apr 1994 to use multiple - * pages. So for 'page' throughout, read 'area'. - */ - -#include <linux/mm.h> -#include <asm/system.h> -#include <linux/delay.h> - -#define GFP_LEVEL_MASK 0xf - -/* I want this low enough for a while to catch errors. - I want this number to be increased in the near future: - loadable device drivers should use this function to get memory */ - -#define MAX_KMALLOC_K ((PAGE_SIZE<<(NUM_AREA_ORDERS-1))>>10) - - -/* This defines how many times we should try to allocate a free page before - giving up. Normally this shouldn't happen at all. */ -#define MAX_GET_FREE_PAGE_TRIES 4 - - -/* Private flags. */ - -#define MF_USED 0xffaa0055 -#define MF_FREE 0x0055ffaa - - -/* - * Much care has gone into making these routines in this file reentrant. - * - * The fancy bookkeeping of nbytesmalloced and the like are only used to - * report them to the user (oooohhhhh, aaaaahhhhh....) are not - * protected by cli(). (If that goes wrong. So what?) - * - * These routines restore the interrupt status to allow calling with ints - * off. - */ - -/* - * A block header. This is in front of every malloc-block, whether free or not. - */ -struct block_header { - unsigned long bh_flags; - union { - unsigned long ubh_length; - struct block_header *fbh_next; - } vp; -}; - - -#define bh_length vp.ubh_length -#define bh_next vp.fbh_next -#define BH(p) ((struct block_header *)(p)) - - -/* - * The page descriptor is at the front of every page that malloc has in use. - */ -struct page_descriptor { - struct page_descriptor *next; - struct block_header *firstfree; - int order; - int nfree; -}; - - -#define PAGE_DESC(p) ((struct page_descriptor *)(((unsigned long)(p)) & PAGE_MASK)) - - -/* - * A size descriptor describes a specific class of malloc sizes. - * Each class of sizes has its own freelist. - */ -struct size_descriptor { - struct page_descriptor *firstfree; - int size; - int nblocks; - - int nmallocs; - int nfrees; - int nbytesmalloced; - int npages; - unsigned long gfporder; /* number of pages in the area required */ -}; - -/* - * For now it is unsafe to allocate bucket sizes between n & n=16 where n is - * 4096 * any power of two - */ - -struct size_descriptor sizes[] = { - { NULL, 32,127, 0,0,0,0, 0}, - { NULL, 64, 63, 0,0,0,0, 0 }, - { NULL, 128, 31, 0,0,0,0, 0 }, - { NULL, 252, 16, 0,0,0,0, 0 }, - { NULL, 508, 8, 0,0,0,0, 0 }, - { NULL,1020, 4, 0,0,0,0, 0 }, - { NULL,2040, 2, 0,0,0,0, 0 }, - { NULL,4096-16, 1, 0,0,0,0, 0 }, - { NULL,8192-16, 1, 0,0,0,0, 1 }, - { NULL,16384-16, 1, 0,0,0,0, 2 }, - { NULL,32768-16, 1, 0,0,0,0, 3 }, - { NULL,65536-16, 1, 0,0,0,0, 4 }, - { NULL,131072-16, 1, 0,0,0,0, 5 }, - { NULL, 0, 0, 0,0,0,0, 0 } -}; - - -#define NBLOCKS(order) (sizes[order].nblocks) -#define BLOCKSIZE(order) (sizes[order].size) -#define AREASIZE(order) (PAGE_SIZE<<(sizes[order].gfporder)) - - -long kmalloc_init (long start_mem,long end_mem) -{ - int order; - -/* - * Check the static info array. Things will blow up terribly if it's - * incorrect. This is a late "compile time" check..... - */ -for (order = 0;BLOCKSIZE(order);order++) - { - if ((NBLOCKS (order)*BLOCKSIZE(order) + sizeof (struct page_descriptor)) > - AREASIZE(order)) - { - printk ("Cannot use %d bytes out of %d in order = %d block mallocs\n", - NBLOCKS (order) * BLOCKSIZE(order) + - sizeof (struct page_descriptor), - (int) AREASIZE(order), - BLOCKSIZE (order)); - panic ("This only happens if someone messes with kmalloc"); - } - } -return start_mem; -} - - - -int get_order (int size) -{ - int order; - - /* Add the size of the header */ - size += sizeof (struct block_header); - for (order = 0;BLOCKSIZE(order);order++) - if (size <= BLOCKSIZE (order)) - return order; - return -1; -} - -void * kmalloc (size_t size, int priority) -{ - unsigned long flags; - int order,tries,i,sz; - struct block_header *p; - struct page_descriptor *page; - -/* Sanity check... */ - if (intr_count && priority != GFP_ATOMIC) { - static int count = 0; - if (++count < 5) { - printk("kmalloc called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - priority = GFP_ATOMIC; - } - } - -order = get_order (size); -if (order < 0) - { - printk ("kmalloc of too large a block (%d bytes).\n",size); - return (NULL); - } - -save_flags(flags); - -/* It seems VERY unlikely to me that it would be possible that this - loop will get executed more than once. */ -tries = MAX_GET_FREE_PAGE_TRIES; -while (tries --) - { - /* Try to allocate a "recently" freed memory block */ - cli (); - if ((page = sizes[order].firstfree) && - (p = page->firstfree)) - { - if (p->bh_flags == MF_FREE) - { - page->firstfree = p->bh_next; - page->nfree--; - if (!page->nfree) - { - sizes[order].firstfree = page->next; - page->next = NULL; - } - restore_flags(flags); - - sizes [order].nmallocs++; - sizes [order].nbytesmalloced += size; - p->bh_flags = MF_USED; /* As of now this block is officially in use */ - p->bh_length = size; - return p+1; /* Pointer arithmetic: increments past header */ - } - printk ("Problem: block on freelist at %08lx isn't free.\n",(long)p); - return (NULL); - } - restore_flags(flags); - - - /* Now we're in trouble: We need to get a new free page..... */ - - sz = BLOCKSIZE(order); /* sz is the size of the blocks we're dealing with */ - - /* This can be done with ints on: This is private to this invocation */ - page = (struct page_descriptor *) __get_free_pages (priority & GFP_LEVEL_MASK, sizes[order].gfporder); - if (!page) { - static unsigned long last = 0; - if (last + 10*HZ < jiffies) { - last = jiffies; - printk ("Couldn't get a free page.....\n"); - } - return NULL; - } -#if 0 - printk ("Got page %08x to use for %d byte mallocs....",(long)page,sz); -#endif - sizes[order].npages++; - - /* Loop for all but last block: */ - for (i=NBLOCKS(order),p=BH (page+1);i > 1;i--,p=p->bh_next) - { - p->bh_flags = MF_FREE; - p->bh_next = BH ( ((long)p)+sz); - } - /* Last block: */ - p->bh_flags = MF_FREE; - p->bh_next = NULL; - - page->order = order; - page->nfree = NBLOCKS(order); - page->firstfree = BH(page+1); -#if 0 - printk ("%d blocks per page\n",page->nfree); -#endif - /* Now we're going to muck with the "global" freelist for this size: - this should be uninterruptible */ - cli (); - /* - * sizes[order].firstfree used to be NULL, otherwise we wouldn't be - * here, but you never know.... - */ - page->next = sizes[order].firstfree; - sizes[order].firstfree = page; - restore_flags(flags); - } - -/* Pray that printk won't cause this to happen again :-) */ - -printk ("Hey. This is very funny. I tried %d times to allocate a whole\n" - "new page for an object only %d bytes long, but some other process\n" - "beat me to actually allocating it. Also note that this 'error'\n" - "message is soooo very long to catch your attention. I'd appreciate\n" - "it if you'd be so kind as to report what conditions caused this to\n" - "the author of this kmalloc: wolff@dutecai.et.tudelft.nl.\n" - "(Executive summary: This can't happen)\n", - MAX_GET_FREE_PAGE_TRIES, - size); -return NULL; -} - - -void kfree_s (void *ptr,int size) -{ -unsigned long flags; -int order; -register struct block_header *p=((struct block_header *)ptr) -1; -struct page_descriptor *page,*pg2; - -page = PAGE_DESC (p); -order = page->order; -if ((order < 0) || - (order > sizeof (sizes)/sizeof (sizes[0])) || - (((long)(page->next)) & ~PAGE_MASK) || - (p->bh_flags != MF_USED)) - { - printk ("kfree of non-kmalloced memory: %p, next= %p, order=%d\n", - p, page->next, page->order); - return; - } -if (size && - size != p->bh_length) - { - printk ("Trying to free pointer at %p with wrong size: %d instead of %lu.\n", - p,size,p->bh_length); - return; - } -size = p->bh_length; -p->bh_flags = MF_FREE; /* As of now this block is officially free */ -save_flags(flags); -cli (); -p->bh_next = page->firstfree; -page->firstfree = p; -page->nfree ++; - -if (page->nfree == 1) - { /* Page went from full to one free block: put it on the freelist */ - if (page->next) - { - printk ("Page %p already on freelist dazed and confused....\n", page); - } - else - { - page->next = sizes[order].firstfree; - sizes[order].firstfree = page; - } - } - -/* If page is completely free, free it */ -if (page->nfree == NBLOCKS (page->order)) - { -#if 0 - printk ("Freeing page %08x.\n", (long)page); -#endif - if (sizes[order].firstfree == page) - { - sizes[order].firstfree = page->next; - } - else - { - for (pg2=sizes[order].firstfree; - (pg2 != NULL) && (pg2->next != page); - pg2=pg2->next) - /* Nothing */; - if (pg2 != NULL) - pg2->next = page->next; - else - printk ("Ooops. page %p doesn't show on freelist.\n", page); - } -/* FIXME: I'm sure we should do something with npages here (like npages--) */ - free_pages ((long)page, sizes[order].gfporder); - } -restore_flags(flags); - -/* FIXME: ?? Are these increment & decrement operations guaranteed to be - * atomic? Could an IRQ not occur between the read & the write? - * Maybe yes on a x86 with GCC...?? - */ -sizes[order].nfrees++; /* Noncritical (monitoring) admin stuff */ -sizes[order].nbytesmalloced -= size; -} diff --git a/arch/mips/mm/memory.c b/arch/mips/mm/memory.c deleted file mode 100644 index 5872f8bd5..000000000 --- a/arch/mips/mm/memory.c +++ /dev/null @@ -1,1295 +0,0 @@ -/* - * arch/mips/mm/memory.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * Ported to MIPS by Ralf Baechle - */ - -/* - * 05.04.94 - Multi-page memory management added for v1.1. - * Idea by Alex Bligh (alex@cconcepts.co.uk) - */ - -#include <linux/config.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/ptrace.h> -#include <linux/mman.h> - -#include <asm/system.h> -#include <asm/segment.h> - -unsigned long high_memory = 0; - -extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ -extern unsigned long invalid_pg_table[1024]; - -extern void sound_mem_init(void); -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); - -/* - * The free_area_list arrays point to the queue heads of the free areas - * of different sizes - */ -int nr_swap_pages = 0; -int nr_free_pages = 0; -struct mem_list free_area_list[NR_MEM_LISTS]; -unsigned char * free_area_map[NR_MEM_LISTS]; - -unsigned short * mem_map = NULL; - -/* - * oom() prints a message (so that the user knows why the process died), - * and gives the process an untrappable SIGKILL. - */ -void oom(struct task_struct * task) -{ - printk("\nOut of memory.\n"); - task->sigaction[SIGKILL-1].sa_handler = NULL; - task->blocked &= ~(1<<(SIGKILL-1)); - send_sig(SIGKILL,task,1); -} - -static void free_one_table(unsigned long * page_dir) -{ - int j; - unsigned long pg_table = *page_dir; - unsigned long * page_table; - - if ((long) pg_table & PAGE_MASK != (long) invalid_pg_table & PAGE_MASK ) - return; - *page_dir = PAGE_VALID | (unsigned long) invalid_pg_table; - if (pg_table >= high_memory || !(pg_table & PAGE_VALID)) { - printk("Bad page table: [%p]=%08lx\n",page_dir,pg_table); - return; - } - if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) - return; - page_table = (unsigned long *) (pg_table & PAGE_MASK); - for (j = 0 ; j < PTRS_PER_PAGE ; j++,page_table++) { - unsigned long pg = *page_table; - - if (!pg) - continue; - *page_table = 0; - if (pg & PAGE_VALID) - free_page(PAGE_MASK & pg); - else - swap_free(pg); - } - free_page(PAGE_MASK & pg_table); -} - -/* - * This function clears all user-level page tables of a process - this - * is needed by execve(), so that old pages aren't in the way. Note that - * unlike 'free_page_tables()', this function still leaves a valid - * page-table-tree in memory: it just removes the user pages. The two - * functions are similar, but there is a fundamental difference. - */ -void clear_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long pg_dir; - unsigned long * page_dir; - - if (!tsk) - return; - if (tsk == task[0]) - panic("task[0] (swapper) doesn't support exec()\n"); - pg_dir = tsk->tss.pg_dir; - page_dir = (unsigned long *) pg_dir; - if (!page_dir || page_dir == swapper_pg_dir) { - printk("Trying to clear kernel page-directory: not good\n"); - return; - } - if (mem_map[MAP_NR(pg_dir)] > 1) { - unsigned long * new_pg; - - if (!(new_pg = (unsigned long*) get_free_page(GFP_KERNEL))) { - oom(tsk); - return; - } - for (i = 768 ; i < 1024 ; i++) - new_pg[i] = page_dir[i]; - free_page(pg_dir); - tsk->tss.pg_dir = (unsigned long) new_pg; - return; - } - for (i = 0 ; i < 768 ; i++,page_dir++) - free_one_table(page_dir); - invalidate(); - return; -} - -/* - * This function frees up all page tables of a process when it exits. - */ -void free_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long pg_dir; - unsigned long * page_dir; - - if (!tsk) - return; - if (tsk == task[0]) { - printk("task[0] (swapper) killed: unable to recover\n"); - panic("Trying to free up swapper memory space"); - } - pg_dir = tsk->tss.pg_dir; - if (!pg_dir || pg_dir == (unsigned long) swapper_pg_dir) { - printk("Trying to free kernel page-directory: not good\n"); - return; - } - tsk->tss.pg_dir = (unsigned long) swapper_pg_dir; - if (mem_map[MAP_NR(pg_dir)] > 1) { - free_page(pg_dir); - return; - } - page_dir = (unsigned long *) pg_dir; - for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++) - free_one_table(page_dir); - free_page(pg_dir); - invalidate(); -} - -/* - * clone_page_tables() clones the page table for a process - both - * processes will have the exact same pages in memory. There are - * probably races in the memory management with cloning, but we'll - * see.. - */ -int clone_page_tables(struct task_struct * tsk) -{ - unsigned long pg_dir; - - pg_dir = current->tss.pg_dir; - mem_map[MAP_NR(pg_dir)]++; - tsk->tss.pg_dir = pg_dir; - return 0; -} - -/* - * copy_page_tables() just copies the whole process memory range: - * note the special handling of RESERVED (ie kernel) pages, which - * means that they are always shared by all processes. - */ -int copy_page_tables(struct task_struct * tsk) -{ - int i; - unsigned long old_pg_dir, *old_page_dir; - unsigned long new_pg_dir, *new_page_dir; - - if (!(new_pg_dir = get_free_page(GFP_KERNEL))) - return -ENOMEM; - old_pg_dir = current->tss.pg_dir; - tsk->tss.pg_dir = new_pg_dir; - old_page_dir = (unsigned long *) old_pg_dir; - new_page_dir = (unsigned long *) new_pg_dir; - for (i = 0 ; i < PTRS_PER_PAGE ; i++,old_page_dir++,new_page_dir++) { - int j; - unsigned long old_pg_table, *old_page_table; - unsigned long new_pg_table, *new_page_table; - - old_pg_table = *old_page_dir; - if (old_pg_table == (unsigned long) invalid_pg_table) - continue; - if (old_pg_table >= high_memory || !(old_pg_table & PAGE_VALID)) { - printk("copy_page_tables: bad page table: " - "probable memory corruption\n"); - *old_page_dir = PAGE_TABLE | (unsigned long)invalid_pg_table; - continue; - } - if (mem_map[MAP_NR(old_pg_table)] & MAP_PAGE_RESERVED) { - *new_page_dir = old_pg_table; - continue; - } - if (!(new_pg_table = get_free_page(GFP_KERNEL))) { - free_page_tables(tsk); - return -ENOMEM; - } - old_page_table = (unsigned long *) (PAGE_MASK & old_pg_table); - new_page_table = (unsigned long *) (PAGE_MASK & new_pg_table); - for (j = 0 ; j < PTRS_PER_PAGE ; j++,old_page_table++,new_page_table++) { - unsigned long pg; - pg = *old_page_table; - if (!pg) - continue; - if (!(pg & PAGE_VALID)) { - *new_page_table = swap_duplicate(pg); - continue; - } - if (pg > high_memory || (mem_map[MAP_NR(pg)] & MAP_PAGE_RESERVED)) { - *new_page_table = pg; - continue; - } - if (pg & PAGE_COW) - pg &= ~PAGE_RW; - if (delete_from_swap_cache(pg)) - pg |= PAGE_DIRTY; - *new_page_table = pg; - *old_page_table = pg; - mem_map[MAP_NR(pg)]++; - } - *new_page_dir = new_pg_table | PAGE_TABLE; - } - invalidate(); - return 0; -} - -/* - * a more complete version of free_page_tables which performs with page - * granularity. - */ -int unmap_page_range(unsigned long from, unsigned long size) -{ - unsigned long page, page_dir; - unsigned long *page_table, *dir; - unsigned long poff, pcnt, pc; - - if (from & ~PAGE_MASK) { - printk("unmap_page_range called with wrong alignment\n"); - return -EINVAL; - } - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - dir = PAGE_DIR_OFFSET(current->tss.pg_dir,from); - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - for ( ; size > 0; ++dir, size -= pcnt, - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size)) { - if (!(page_dir = *dir)) { - poff = 0; - continue; - } - if (!(page_dir & PAGE_VALID)) { - printk("unmap_page_range: bad page directory."); - continue; - } - page_table = (unsigned long *)(PAGE_MASK & page_dir); - if (poff) { - page_table += poff; - poff = 0; - } - for (pc = pcnt; pc--; page_table++) { - if ((page = *page_table) != (unsigned long) invalid_pg_table) { - *page_table = (unsigned long) invalid_pg_table; - if (PAGE_VALID & page) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - } - if (pcnt == PTRS_PER_PAGE) { - *dir = 0; - free_page(PAGE_MASK & page_dir); - } - } - invalidate(); - return 0; -} - -int zeromap_page_range(unsigned long from, unsigned long size, int mask) -{ - unsigned long *page_table, *dir; - unsigned long poff, pcnt; - unsigned long page; - - if (mask) { - if ((mask & (PAGE_MASK|PAGE_VALID)) != PAGE_VALID) { - printk("zeromap_page_range: mask = %08x\n",mask); - return -EINVAL; - } - mask |= ZERO_PAGE; - } - if (from & ~PAGE_MASK) { - printk("zeromap_page_range: from = %08lx\n",from); - return -EINVAL; - } - dir = PAGE_DIR_OFFSET(current->tss.pg_dir,from); - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - while (size > 0) { - if (PAGE_MASK & *dir == (unsigned long) invalid_pg_table) { - /* clear page needed here? SRB. */ - if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { - invalidate(); - return -ENOMEM; - } - if (PAGE_MASK & *dir == (unsigned long) invalid_pg_table) { - free_page((unsigned long) page_table); - page_table = (unsigned long *)(PAGE_MASK & *dir++); - } else - *dir++ = ((unsigned long) page_table) | PAGE_TABLE; - } else - page_table = (unsigned long *)(PAGE_MASK & *dir++); - page_table += poff; - poff = 0; - for (size -= pcnt; pcnt-- ;) { - if (PAGE_MASK & (page = *page_table) != (unsigned long) invalid_pg_table) { - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - if (page & PAGE_VALID) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - *page_table++ = mask; - } - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); - } - invalidate(); - return 0; -} - -/* - * Maps a range of physical memory into the requested pages. the old - * mappings are removed. Any references to nonexistent pages results - * in null mappings (currently treated as "copy-on-access") - */ -int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask) -{ - unsigned long *page_table, *dir; - unsigned long poff, pcnt; - unsigned long page; - - if (mask) { - if ((mask & (PAGE_MASK|PAGE_VALID)) != PAGE_VALID) { - printk("remap_page_range: mask = %08x\n",mask); - return -EINVAL; - } - } - if ((from & ~PAGE_MASK) || (to & ~PAGE_MASK)) { - printk("remap_page_range: from = %08lx, to=%08lx\n",from,to); - return -EINVAL; - } - dir = PAGE_DIR_OFFSET(current->tss.pg_dir,from); - size = (size + ~PAGE_MASK) >> PAGE_SHIFT; - poff = (from >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if ((pcnt = PTRS_PER_PAGE - poff) > size) - pcnt = size; - - while (size > 0) { - if (PAGE_MASK & *dir != (unsigned long) invalid_pg_table) { - /* clearing page here, needed? SRB. */ - if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) { - invalidate(); - return -1; - } - *dir++ = ((unsigned long) page_table) | PAGE_TABLE; - } - else - page_table = (unsigned long *)(PAGE_MASK & *dir++); - if (poff) { - page_table += poff; - poff = 0; - } - - for (size -= pcnt; pcnt-- ;) { - if (PAGE_MASK & (page = *page_table) != (unsigned long) invalid_pg_table) { - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - if (PAGE_VALID & page) { - if (!(mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED)) - if (current->mm->rss > 0) - --current->mm->rss; - free_page(PAGE_MASK & page); - } else - swap_free(page); - } - - /* - * the first condition should return an invalid access - * when the page is referenced. current assumptions - * cause it to be treated as demand allocation in some - * cases. - */ - if (!mask) - *page_table++ = 0; /* not present */ - else if (to >= high_memory) - *page_table++ = (to | mask); - else if (!mem_map[MAP_NR(to)]) - *page_table++ = 0; /* not present */ - else { - *page_table++ = (to | mask); - if (!(mem_map[MAP_NR(to)] & MAP_PAGE_RESERVED)) { - ++current->mm->rss; - mem_map[MAP_NR(to)]++; - } - } - to += PAGE_SIZE; - } - pcnt = (size > PTRS_PER_PAGE ? PTRS_PER_PAGE : size); - } - invalidate(); - return 0; -} - -/* - * This function puts a page in memory at the wanted address. - * It returns the physical address of the page gotten, 0 if - * out of memory (either when trying to access page-table or - * page.) - */ -unsigned long put_page(struct task_struct * tsk,unsigned long page, - unsigned long address,int prot) -{ - unsigned long *page_table; - - if ((prot & (PAGE_MASK|PAGE_VALID)) != PAGE_VALID) - printk("put_page: prot = %08x\n",prot); - if (page >= high_memory) { - printk("put_page: trying to put page %08lx at %08lx\n",page,address); - return 0; - } - page_table = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if ((*page_table) & PAGE_VALID) - page_table = (unsigned long *) (PAGE_MASK & *page_table); - else { - printk("put_page: bad page directory entry\n"); - oom(tsk); - *page_table = BAD_PAGETABLE | PAGE_TABLE; - return 0; - } - page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if (*page_table) { - printk("put_page: page already exists\n"); - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - invalidate(); - } - *page_table = page | prot; -/* no need for invalidate */ - return page; -} - -/* - * The previous function doesn't work very well if you also want to mark - * the page dirty: exec.c wants this, as it has earlier changed the page, - * and we want the dirty-status to be correct (for VM). Thus the same - * routine, but this time we mark it dirty too. - */ -unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsigned long address) -{ - unsigned long tmp, *page_table; - - if (page >= high_memory) - printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address); - if (mem_map[MAP_NR(page)] != 1) - printk("mem_map disagrees with %08lx at %08lx\n",page,address); - page_table = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if (PAGE_MASK & *page_table == (unsigned long) invalid_pg_table) - page_table = (unsigned long *) (PAGE_MASK & *page_table); - else { - if (!(tmp = get_free_page(GFP_KERNEL))) - return 0; - if (PAGE_MASK & *page_table == (unsigned long) invalid_pg_table) { - free_page(tmp); - page_table = (unsigned long *) (PAGE_MASK & *page_table); - } else { - *page_table = tmp | PAGE_TABLE; - page_table = (unsigned long *) tmp; - } - } - page_table += (address >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - if (*page_table) { - printk("put_dirty_page: page already exists\n"); - *page_table = PAGE_TABLE | (unsigned long) invalid_pg_table; - invalidate(); - } - *page_table = page | (PAGE_DIRTY | PAGE_PRIVATE); -/* no need for invalidate */ - return page; -} - -/* - * Note that processing of the dirty bit has already been done - * before in the assembler part. That way it's not only faster - - * we also can use the i386 code with very little changes. - * - * This routine handles present pages, when users try to write - * to a shared page. It is done by copying the page to a new address - * and decrementing the shared-page counter for the old page. - * - * Goto-purists beware: the only reason for goto's here is that it results - * in better assembly code.. The "default" path will see no jumps at all. - */ -void do_wp_page(struct vm_area_struct * vma, unsigned long address, - unsigned long error_code) -{ - unsigned long *pde, pte, old_page, prot; - unsigned long new_page; - - new_page = __get_free_page(GFP_KERNEL); - pde = PAGE_DIR_OFFSET(vma->vm_task->tss.pg_dir,address); - pte = *pde; - if (!(pte & PAGE_VALID)) - goto end_wp_page; - if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) - goto bad_wp_pagetable; - pte &= PAGE_MASK; - pte += PAGE_PTR(address); - old_page = *(unsigned long *) pte; - if (!(old_page & PAGE_VALID)) - goto end_wp_page; - if (old_page >= high_memory) - goto bad_wp_page; - if (old_page & PAGE_RW) - goto end_wp_page; - vma->vm_task->mm->min_flt++; - prot = (old_page & ~PAGE_MASK) | PAGE_RW | PAGE_DIRTY; - old_page &= PAGE_MASK; - if (mem_map[MAP_NR(old_page)] != 1) { - if (new_page) { - if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED) - ++vma->vm_task->mm->rss; - copy_page(old_page,new_page); - *(unsigned long *) pte = new_page | prot; - free_page(old_page); - invalidate(); - return; - } - free_page(old_page); - oom(vma->vm_task); - *(unsigned long *) pte = BAD_PAGE | prot; - invalidate(); - return; - } - *(unsigned long *) pte |= PAGE_RW | PAGE_DIRTY; - invalidate(); - if (new_page) - free_page(new_page); - return; -bad_wp_page: - printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page); - *(unsigned long *) pte = BAD_PAGE | PAGE_SHARED; - send_sig(SIGKILL, vma->vm_task, 1); - goto end_wp_page; -bad_wp_pagetable: - printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte); - *pde = BAD_PAGETABLE | PAGE_TABLE; - send_sig(SIGKILL, vma->vm_task, 1); -end_wp_page: - if (new_page) - free_page(new_page); - return; -} - -/* - * Ugly, ugly, but the goto's result in better assembly.. - */ -int verify_area(int type, const void * addr, unsigned long size) -{ - struct vm_area_struct * vma; - unsigned long start = (unsigned long) addr; - - /* If the current user space is mapped to kernel space (for the - * case where we use a fake user buffer with get_fs/set_fs()) we - * don't expect to find the address in the user vm map. - */ - if (get_fs() == get_ds()) - return 0; - - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > start) - break; - } - if (vma->vm_start <= start) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur) - goto bad_area; - -good_area: - if (!wp_works_ok && type == VERIFY_WRITE) - goto check_wp_fault_by_hand; - for (;;) { - struct vm_area_struct * next; - if (!(vma->vm_page_prot & PAGE_USER)) - goto bad_area; - if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) - goto bad_area; - if (vma->vm_end - start >= size) - return 0; - next = vma->vm_next; - if (!next || vma->vm_end != next->vm_start) - goto bad_area; - vma = next; - } - -check_wp_fault_by_hand: - size--; - size += start & ~PAGE_MASK; - size >>= PAGE_SHIFT; - start &= PAGE_MASK; - - for (;;) { - if (!(vma->vm_page_prot & (PAGE_COW | PAGE_RW))) - goto bad_area; - do_wp_page(vma, start, PAGE_VALID); - if (!size) - return 0; - size--; - start += PAGE_SIZE; - if (start < vma->vm_end) - continue; - vma = vma->vm_next; - if (!vma || vma->vm_start != start) - break; - } - -bad_area: - return -EFAULT; -} - -static inline void get_empty_page(struct task_struct * tsk, unsigned long address) -{ - unsigned long tmp; - - if (!(tmp = get_free_page(GFP_KERNEL))) { - oom(tsk); - tmp = BAD_PAGE; - } - if (!put_page(tsk,tmp,address,PAGE_PRIVATE)) - free_page(tmp); -} - -/* - * try_to_share() checks the page at address "address" in the task "p", - * to see if it exists, and if it is clean. If so, share it with the current - * task. - * - * NOTE! This assumes we have checked that p != current, and that they - * share the same inode and can generally otherwise be shared. - */ -static int try_to_share(unsigned long to_address, struct vm_area_struct * to_area, - unsigned long from_address, struct vm_area_struct * from_area, - unsigned long newpage) -{ - unsigned long from; - unsigned long to; - unsigned long from_page; - unsigned long to_page; - - from_page = (unsigned long)PAGE_DIR_OFFSET(from_area->vm_task->tss.pg_dir,from_address); - to_page = (unsigned long)PAGE_DIR_OFFSET(to_area->vm_task->tss.pg_dir,to_address); -/* is there a page-directory at from? */ - from = *(unsigned long *) from_page; - if (from & PAGE_MASK == (unsigned long) invalid_pg_table) - return 0; - from &= PAGE_MASK; - from_page = from + PAGE_PTR(from_address); - from = *(unsigned long *) from_page; -/* is the page present? */ - if (!(from & PAGE_VALID)) - return 0; -/* if it is private, it must be clean to be shared */ - if (from & PAGE_DIRTY) { - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - } -/* is the page reasonable at all? */ - if (from >= high_memory) - return 0; - if (mem_map[MAP_NR(from)] & MAP_PAGE_RESERVED) - return 0; -/* is the destination ok? */ - to = *(unsigned long *) to_page; - if (to & PAGE_MASK == (unsigned long) invalid_pg_table) - return 0; - to &= PAGE_MASK; - to_page = to + PAGE_PTR(to_address); - if (*(unsigned long *) to_page) - return 0; -/* do we copy? */ - if (newpage) { - if (in_swap_cache(from)) { /* implies PAGE_DIRTY */ - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - } - copy_page((from & PAGE_MASK), newpage); - *(unsigned long *) to_page = newpage | to_area->vm_page_prot; - return 1; - } -/* do a final swap-cache test before sharing them.. */ - if (in_swap_cache(from)) { - if (from_area->vm_page_prot & PAGE_COW) - return 0; - if (!(from_area->vm_page_prot & PAGE_RW)) - return 0; - from |= PAGE_DIRTY; - *(unsigned long *) from_page = from; - delete_from_swap_cache(from); - invalidate(); - } - mem_map[MAP_NR(from)]++; -/* fill in the 'to' field, checking for COW-stuff */ - to = (from & (PAGE_MASK | PAGE_DIRTY)) | to_area->vm_page_prot; - if (to & PAGE_COW) - to &= ~PAGE_RW; - *(unsigned long *) to_page = to; -/* Check if we need to do anything at all to the 'from' field */ - if (!(from & PAGE_RW)) - return 1; - if (!(from_area->vm_page_prot & PAGE_COW)) - return 1; -/* ok, need to mark it read-only, so invalidate any possible old TB entry */ - from &= ~PAGE_RW; - *(unsigned long *) from_page = from; - invalidate(); - return 1; -} - -/* - * share_page() tries to find a process that could share a page with - * the current one. - * - * We first check if it is at all feasible by checking inode->i_count. - * It should be >1 if there are other tasks sharing this inode. - */ -static int share_page(struct vm_area_struct * area, unsigned long address, - unsigned long error_code, unsigned long newpage) -{ - struct inode * inode; - struct task_struct ** p; - unsigned long offset; - unsigned long from_address; - unsigned long give_page; - - if (!area || !(inode = area->vm_inode) || inode->i_count < 2) - return 0; - /* do we need to copy or can we just share? */ - give_page = 0; - if ((area->vm_page_prot & PAGE_COW) && (error_code & PAGE_RW)) { - if (!newpage) - return 0; - give_page = newpage; - } - offset = address - area->vm_start + area->vm_offset; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - struct vm_area_struct * mpnt; - if (!*p) - continue; - if (area->vm_task == *p) - continue; - /* Now see if there is something in the VMM that - we can share pages with */ - for (mpnt = (*p)->mm->mmap; mpnt; mpnt = mpnt->vm_next) { - /* must be same inode */ - if (mpnt->vm_inode != inode) - continue; - /* offsets must be mutually page-aligned */ - if ((mpnt->vm_offset ^ area->vm_offset) & ~PAGE_MASK) - continue; - /* the other area must actually cover the wanted page.. */ - from_address = offset + mpnt->vm_start - mpnt->vm_offset; - if (from_address < mpnt->vm_start || from_address >= mpnt->vm_end) - continue; - /* .. NOW we can actually try to use the same physical page */ - if (!try_to_share(address, area, from_address, mpnt, give_page)) - continue; - /* free newpage if we never used it.. */ - if (give_page || !newpage) - return 1; - free_page(newpage); - return 1; - } - } - return 0; -} - -/* - * fill in an empty page-table if none exists. - */ -static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long address) -{ - unsigned long page; - unsigned long *p; - - p = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if (*p & PAGE_MASK == (unsigned long) invalid_pg_table) - return *p; - if (*p) { - printk("get_empty_pgtable: bad page-directory entry \n"); - *p = 0; - } - page = get_free_page(GFP_KERNEL); - p = PAGE_DIR_OFFSET(tsk->tss.pg_dir,address); - if (*p & PAGE_MASK == (unsigned long) invalid_pg_table) { - free_page(page); - return *p; - } - if (*p) { - printk("get_empty_pgtable: bad page-directory entry \n"); - *p = 0; - } - if (page) { - *p = page | PAGE_TABLE; - return *p; - } - oom(current); - *p = BAD_PAGETABLE | PAGE_TABLE; - return 0; -} - -static inline void do_swap_page(struct vm_area_struct * vma, - unsigned long address, unsigned long * pge, unsigned long entry) -{ - unsigned long page; - - if (vma->vm_ops && vma->vm_ops->swapin) - page = vma->vm_ops->swapin(vma, entry); - else - page = swap_in(entry); - if (*pge != entry) { - free_page(page); - return; - } - page = page | vma->vm_page_prot; - if (mem_map[MAP_NR(page)] > 1 && (page & PAGE_COW)) - page &= ~PAGE_RW; - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->maj_flt; - *pge = page; - return; -} - -void do_no_page(struct vm_area_struct * vma, unsigned long address, - unsigned long error_code) -{ - unsigned long page, entry, prot; - - page = get_empty_pgtable(vma->vm_task,address); - if (!page) - return; - page &= PAGE_MASK; - page += PAGE_PTR(address); - entry = *(unsigned long *) page; - if (entry & PAGE_VALID) - return; - if (entry) { - do_swap_page(vma, address, (unsigned long *) page, entry); - return; - } - address &= PAGE_MASK; - - if (!vma->vm_ops || !vma->vm_ops->nopage) { - ++vma->vm_task->mm->rss; - ++vma->vm_task->mm->min_flt; - get_empty_page(vma->vm_task,address); - return; - } - page = get_free_page(GFP_KERNEL); - if (share_page(vma, address, error_code, page)) { - ++vma->vm_task->mm->min_flt; - ++vma->vm_task->mm->rss; - return; - } - if (!page) { - oom(current); - put_page(vma->vm_task, BAD_PAGE, address, PAGE_PRIVATE); - return; - } - ++vma->vm_task->mm->maj_flt; - ++vma->vm_task->mm->rss; - prot = vma->vm_page_prot; - /* - * The fourth argument is "no_share", which tells the low-level code - * to copy, not share the page even if sharing is possible. It's - * essentially an early COW detection ("moo at 5 AM"). - */ - page = vma->vm_ops->nopage(vma, address, page, (error_code & PAGE_RW) && (prot & PAGE_COW)); - if (share_page(vma, address, error_code, 0)) { - free_page(page); - return; - } - /* - * This silly early PAGE_DIRTY setting removes a race - * due to the bad i386 page protection. - */ - if (error_code & PAGE_RW) { - prot |= PAGE_DIRTY; /* can't be COW-shared: see "no_share" above */ - } else if ((prot & PAGE_COW) && mem_map[MAP_NR(page)] > 1) - prot &= ~PAGE_RW; - if (put_page(vma->vm_task, page, address, prot)) - return; - free_page(page); - oom(current); -} - -/* - * This routine handles page faults. It determines the address, - * and the problem, and then passes it off to one of the appropriate - * routines. - */ -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) -{ - struct vm_area_struct * vma; - unsigned long address; - unsigned long page; - - /* get the address */ - __asm__("dmfc0\t%0,$8":"=r" (address)); - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > address) - break; - } - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) - goto bad_area; - vma->vm_offset -= vma->vm_start - (address & PAGE_MASK); - vma->vm_start = (address & PAGE_MASK); -/* - * Ok, we have a good vm_area for this memory access, so - * we can handle it.. - */ -good_area: -#if 0 - if (regs->eflags & VM_MASK) { - unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT; - if (bit < 32) - current->screen_bitmap |= 1 << bit; - } -#endif - if (!(vma->vm_page_prot & PAGE_USER)) - goto bad_area; - if (error_code & PAGE_VALID) { - if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW))) - goto bad_area; - do_wp_page(vma, address, error_code); - return; - } - do_no_page(vma, address, error_code); - return; - -/* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. - */ -bad_area: - if (error_code & PAGE_USER) { - current->tss.cp0_badvaddr = address; - current->tss.error_code = error_code; - current->tss.trap_no = 14; - send_sig(SIGSEGV, current, 1); - return; - } -/* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ - if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & PAGE_VALID)) { - wp_works_ok = 1; - pg0[0] = PAGE_SHARED; - printk("This processor honours the WP bit even when in supervisor mode. Good.\n"); - return; - } - if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) { - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - pg0[0] = PAGE_SHARED; - } else - printk(KERN_ALERT "Unable to handle kernel paging request"); - printk(" at virtual address %08lx\n",address); - printk(KERN_ALERT "current->tss.pg_dir = %08lx\n", current->tss.pg_dir); - page = ((unsigned long *) page)[address >> 22]; - printk(KERN_ALERT "*pde = %08lx\n", page); - if (page & PAGE_VALID) { - page &= PAGE_MASK; - address &= 0x003ff000; - page = ((unsigned long *) page)[address >> PAGE_SHIFT]; - printk(KERN_ALERT "*pte = %08lx\n", page); - } - die_if_kernel("Oops", regs, error_code); - do_exit(SIGKILL); -} - -/* - * BAD_PAGE is the page that is used for page faults when linux - * is out-of-memory. Older versions of linux just did a - * do_exit(), but using this instead means there is less risk - * for a process dying in kernel mode, possibly leaving a inode - * unused etc.. - * - * BAD_PAGETABLE is the accompanying page-table: it is initialized - * to point to BAD_PAGE entries. - * - * ZERO_PAGE is a special page that is used for zero-initialized - * data and COW. - */ -unsigned long __bad_pagetable(void) -{ - extern char empty_bad_page_table[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t%2,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"r" (BAD_PAGE + PAGE_TABLE), - "0" ((long) empty_bad_page_table), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_bad_page_table; -} - -unsigned long __bad_page(void) -{ - extern char empty_bad_page[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t$0,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"0" ((long) empty_bad_page), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_bad_page; -} - -unsigned long __zero_page(void) -{ - extern char empty_zero_page[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t$0,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"0" ((long) empty_zero_page), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_zero_page; -} - -void show_mem(void) -{ - int i,free = 0,total = 0,reserved = 0; - int shared = 0; - - printk("Mem-info:\n"); - show_free_areas(); - printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = high_memory >> PAGE_SHIFT; - while (i-- > 0) { - total++; - if (mem_map[i] & MAP_PAGE_RESERVED) - reserved++; - else if (!mem_map[i]) - free++; - else - shared += mem_map[i]-1; - } - printk("%d pages of RAM\n",total); - printk("%d free pages\n",free); - printk("%d reserved pages\n",reserved); - printk("%d pages shared\n",shared); - show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif -} - -#if 0 -extern unsigned long free_area_init(unsigned long, unsigned long); - -/* - * paging_init() sets up the page tables - note that the first 4MB are - * already mapped by head.S. - * - * This routines also unmaps the page at virtual kernel address 0, so - * that we can trap those pesky NULL-reference errors in the kernel. - */ -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned long * pg_dir; - unsigned long * pg_table; - unsigned long tmp; - unsigned long address; - - start_mem = PAGE_ALIGN(start_mem); - address = 0; - pg_dir = swapper_pg_dir; - while (address < end_mem) { - /* - * at virtual addr 0xC0000000 - */ - tmp = *(pg_dir + 768); - tmp &= PAGE_MASK; - if (!tmp) { - tmp = start_mem | PAGE_TABLE; - *(pg_dir + 768) = tmp; - start_mem += PAGE_SIZE; - } - /* - * also map it in at 0x0000000 for init - */ - *pg_dir = tmp; - pg_dir++; - pg_table = (unsigned long *) (tmp & PAGE_MASK); - for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) { - if (address < end_mem) - *pg_table = address | PAGE_SHARED; - else - *pg_table = 0; - address += PAGE_SIZE; - } - } - invalidate(); - return free_area_init(start_mem, end_mem); -} -#endif - -void mem_init(unsigned long start_mem, unsigned long end_mem) -{ - int codepages = 0; - int reservedpages = 0; - int datapages = 0; - unsigned long tmp; - extern int etext; - - cli(); - end_mem &= PAGE_MASK; - high_memory = end_mem; - - /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); - - while (start_mem < high_memory) { - mem_map[MAP_NR(start_mem)] = 0; - start_mem += PAGE_SIZE; - } -#ifdef CONFIG_SOUND - sound_mem_init(); -#endif - for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) { - if (mem_map[MAP_NR(tmp)]) { - /* - * Don't have any reserved pages - */ - if (0) - reservedpages++; - else if (tmp < (0x7fffffff & (unsigned long) &etext)) - codepages++; - else - datapages++; - continue; - } - mem_map[MAP_NR(tmp)] = 1; - free_page(tmp); - } - tmp = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n", - tmp >> 10, - high_memory >> 10, - codepages << (PAGE_SHIFT-10), - reservedpages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); - -#if 0 - /* - * No need to cope with Intel bugs - */ - wp_works_ok = 1; -#endif - invalidate(); - return; -} - -void si_meminfo(struct sysinfo *val) -{ - int i; - - i = high_memory >> PAGE_SHIFT; - val->totalram = 0; - val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; - val->bufferram = buffermem; - while (i-- > 0) { - if (mem_map[i] & MAP_PAGE_RESERVED) - continue; - val->totalram++; - if (!mem_map[i]) - continue; - val->sharedram += mem_map[i]-1; - } - val->totalram <<= PAGE_SHIFT; - val->sharedram <<= PAGE_SHIFT; - return; -} - - -/* - * This handles a generic mmap of a disk file. - */ -static unsigned long file_mmap_nopage(struct vm_area_struct * area, unsigned long address, - unsigned long page, int no_share) -{ - struct inode * inode = area->vm_inode; - unsigned int block; - int nr[8]; - int i, *p; - - address &= PAGE_MASK; - block = address - area->vm_start + area->vm_offset; - block >>= inode->i_sb->s_blocksize_bits; - i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; - p = nr; - do { - *p = bmap(inode,block); - i--; - block++; - p++; - } while (i > 0); - return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share); -} - -struct vm_operations_struct file_mmap = { - NULL, /* open */ - NULL, /* close */ - file_mmap_nopage, /* nopage */ - NULL, /* wppage */ - NULL, /* share */ - NULL, /* unmap */ -}; diff --git a/arch/mips/mm/mmap.c b/arch/mips/mm/mmap.c deleted file mode 100644 index dbe63b55e..000000000 --- a/arch/mips/mm/mmap.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * linux/mm/mmap.c - * - * Written by obz. - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> - -#include <asm/segment.h> -#include <asm/system.h> - -static int anon_map(struct inode *, struct file *, struct vm_area_struct *); - -/* - * description of effects of mapping type and prot in current implementation. - * this is due to the limited x86 page protection hardware. The expected - * behavior is in parens: - * - * map_type prot - * PROT_NONE PROT_READ PROT_WRITE PROT_EXEC - * MAP_SHARED r: (no) no r: (yes) yes r: (no) yes r: (no) yes - * w: (no) no w: (no) no w: (yes) yes w: (no) no - * x: (no) no x: (no) yes x: (no) yes x: (yes) yes - * - * MAP_PRIVATE r: (no) no r: (yes) yes r: (no) yes r: (no) yes - * w: (no) no w: (no) no w: (copy) copy w: (no) no - * x: (no) no x: (no) yes x: (no) yes x: (yes) yes - * - */ - -int do_mmap(struct file * file, unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, unsigned long off) -{ - int mask, error; - struct vm_area_struct * vma; - - if ((len = PAGE_ALIGN(len)) == 0) - return addr; - - if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len) - return -EINVAL; - - /* offset overflow? */ - if (off + len < off) - return -EINVAL; - - /* - * do simple checking here so the lower-level routines won't have - * to. we assume access permissions have been handled by the open - * of the memory object, so we don't do any here. - */ - - if (file != NULL) { - switch (flags & MAP_TYPE) { - case MAP_SHARED: - if ((prot & PROT_WRITE) && !(file->f_mode & 2)) - return -EACCES; - /* fall through */ - case MAP_PRIVATE: - if (!(file->f_mode & 1)) - return -EACCES; - break; - - default: - return -EINVAL; - } - if ((flags & MAP_DENYWRITE) && (file->f_inode->i_wcount > 0)) - return -ETXTBSY; - } else if ((flags & MAP_TYPE) == MAP_SHARED) - return -EINVAL; - - /* - * obtain the address to map to. we verify (or select) it and ensure - * that it represents a valid section of the address space. - */ - - if (flags & MAP_FIXED) { - if (addr & ~PAGE_MASK) - return -EINVAL; - if (len > TASK_SIZE || addr > TASK_SIZE - len) - return -EINVAL; - } else { - addr = get_unmapped_area(len); - if (!addr) - return -ENOMEM; - } - - /* - * determine the object being mapped and call the appropriate - * specific mapper. the address has already been validated, but - * not unmapped, but the maps are removed from the list. - */ - if (file && (!file->f_op || !file->f_op->mmap)) - return -ENODEV; - mask = PAGE_VALID; - if (prot & (PROT_READ | PROT_EXEC)) - mask |= PAGE_READONLY; - if (prot & PROT_WRITE) - if ((flags & MAP_TYPE) == MAP_PRIVATE) - mask |= PAGE_COPY; - else - mask |= PAGE_SHARED; - - vma = (struct vm_area_struct *)kmalloc(sizeof(struct vm_area_struct), - GFP_KERNEL); - if (!vma) - return -ENOMEM; - - vma->vm_task = current; - vma->vm_start = addr; - vma->vm_end = addr + len; - vma->vm_page_prot = mask; - vma->vm_flags = prot & (VM_READ | VM_WRITE | VM_EXEC); - vma->vm_flags |= flags & (VM_GROWSDOWN | VM_DENYWRITE | VM_EXECUTABLE); - - if (file) { - if (file->f_mode & 1) - vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; - if (flags & MAP_SHARED) { - vma->vm_flags |= VM_SHARED | VM_MAYSHARE; - if (!(file->f_mode & 2)) - vma->vm_flags &= ~VM_MAYWRITE; - } - } else - vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC; - vma->vm_ops = NULL; - vma->vm_offset = off; - vma->vm_inode = NULL; - vma->vm_pte = 0; - - do_munmap(addr, len); /* Clear old maps */ - - if (file) - error = file->f_op->mmap(file->f_inode, file, vma); - else - error = anon_map(NULL, NULL, vma); - - if (error) { - kfree(vma); - return error; - } - insert_vm_struct(current, vma); - merge_segments(current->mm->mmap); - return addr; -} - -/* - * Get an address range which is currently unmapped. - * For mmap() without MAP_FIXED and shmat() with addr=0. - * Return value 0 means ENOMEM. - */ -unsigned long get_unmapped_area(unsigned long len) -{ - struct vm_area_struct * vmm; - unsigned long gap_start = 0, gap_end; - - for (vmm = current->mm->mmap; ; vmm = vmm->vm_next) { - if (gap_start < SHM_RANGE_START) - gap_start = SHM_RANGE_START; - if (!vmm || ((gap_end = vmm->vm_start) > SHM_RANGE_END)) - gap_end = SHM_RANGE_END; - gap_start = PAGE_ALIGN(gap_start); - gap_end &= PAGE_MASK; - if ((gap_start <= gap_end) && (gap_end - gap_start >= len)) - return gap_start; - if (!vmm) - return 0; - gap_start = vmm->vm_end; - } -} - -asmlinkage int sys_mmap(unsigned long *buffer) -{ - int error; - unsigned long flags; - struct file * file = NULL; - - error = verify_area(VERIFY_READ, buffer, 6*sizeof(long)); - if (error) - return error; - flags = get_fs_long(buffer+3); - if (!(flags & MAP_ANONYMOUS)) { - unsigned long fd = get_fs_long(buffer+4); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; - } - return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1), - get_fs_long(buffer+2), flags, get_fs_long(buffer+5)); -} - -/* - * Normal function to fix up a mapping - * This function is the default for when an area has no specific - * function. This may be used as part of a more specific routine. - * This function works out what part of an area is affected and - * adjusts the mapping information. Since the actual page - * manipulation is done in do_mmap(), none need be done here, - * though it would probably be more appropriate. - * - * By the time this function is called, the area struct has been - * removed from the process mapping list, so it needs to be - * reinserted if necessary. - * - * The 4 main cases are: - * Unmapping the whole area - * Unmapping from the start of the segment to a point in it - * Unmapping from an intermediate point to the end - * Unmapping between to intermediate points, making a hole. - * - * Case 4 involves the creation of 2 new areas, for each side of - * the hole. - */ -void unmap_fixup(struct vm_area_struct *area, - unsigned long addr, size_t len) -{ - struct vm_area_struct *mpnt; - unsigned long end = addr + len; - - if (addr < area->vm_start || addr >= area->vm_end || - end <= area->vm_start || end > area->vm_end || - end < addr) - { - printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n", - area->vm_start, area->vm_end, addr, end); - return; - } - - /* Unmapping the whole area */ - if (addr == area->vm_start && end == area->vm_end) { - if (area->vm_ops && area->vm_ops->close) - area->vm_ops->close(area); - if (area->vm_inode) - iput(area->vm_inode); - return; - } - - /* Work out to one of the ends */ - if (addr >= area->vm_start && end == area->vm_end) - area->vm_end = addr; - if (addr == area->vm_start && end <= area->vm_end) { - area->vm_offset += (end - area->vm_start); - area->vm_start = end; - } - - /* Unmapping a hole */ - if (addr > area->vm_start && end < area->vm_end) - { - /* Add end mapping -- leave beginning for below */ - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - - if (!mpnt) - return; - *mpnt = *area; - mpnt->vm_offset += (end - area->vm_start); - mpnt->vm_start = end; - if (mpnt->vm_inode) - mpnt->vm_inode->i_count++; - if (mpnt->vm_ops && mpnt->vm_ops->open) - mpnt->vm_ops->open(mpnt); - area->vm_end = addr; /* Truncate area */ - insert_vm_struct(current, mpnt); - } - - /* construct whatever mapping is needed */ - mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); - if (!mpnt) - return; - *mpnt = *area; - if (mpnt->vm_ops && mpnt->vm_ops->open) - mpnt->vm_ops->open(mpnt); - if (area->vm_ops && area->vm_ops->close) { - area->vm_end = area->vm_start; - area->vm_ops->close(area); - } - insert_vm_struct(current, mpnt); -} - -asmlinkage int sys_munmap(unsigned long addr, size_t len) -{ - return do_munmap(addr, len); -} - -/* - * Munmap is split into 2 main parts -- this part which finds - * what needs doing, and the areas themselves, which do the - * work. This now handles partial unmappings. - * Jeremy Fitzhardine <jeremy@sw.oz.au> - */ -int do_munmap(unsigned long addr, size_t len) -{ - struct vm_area_struct *mpnt, **npp, *free; - - if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr) - return -EINVAL; - - if ((len = PAGE_ALIGN(len)) == 0) - return 0; - - /* - * Check if this memory area is ok - put it on the temporary - * list if so.. The checks here are pretty simple -- - * every area affected in some way (by any overlap) is put - * on the list. If nothing is put on, nothing is affected. - */ - npp = ¤t->mm->mmap; - free = NULL; - for (mpnt = *npp; mpnt != NULL; mpnt = *npp) { - unsigned long end = addr+len; - - if ((addr < mpnt->vm_start && end <= mpnt->vm_start) || - (addr >= mpnt->vm_end && end > mpnt->vm_end)) - { - npp = &mpnt->vm_next; - continue; - } - - *npp = mpnt->vm_next; - mpnt->vm_next = free; - free = mpnt; - } - - if (free == NULL) - return 0; - - /* - * Ok - we have the memory areas we should free on the 'free' list, - * so release them, and unmap the page range.. - * If the one of the segments is only being partially unmapped, - * it will put new vm_area_struct(s) into the address space. - */ - while (free) { - unsigned long st, end; - - mpnt = free; - free = free->vm_next; - - st = addr < mpnt->vm_start ? mpnt->vm_start : addr; - end = addr+len; - end = end > mpnt->vm_end ? mpnt->vm_end : end; - - if (mpnt->vm_ops && mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, st, end-st); - else - unmap_fixup(mpnt, st, end-st); - - kfree(mpnt); - } - - unmap_page_range(addr, len); - return 0; -} - -/* This is used for a general mmap of a disk file */ -int generic_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) -{ - extern struct vm_operations_struct file_mmap; - - if (vma->vm_page_prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */ - return -EINVAL; - if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) - return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; - if (!inode->i_op || !inode->i_op->bmap) - return -ENOEXEC; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - vma->vm_inode = inode; - inode->i_count++; - vma->vm_ops = &file_mmap; - return 0; -} - -/* - * Insert vm structure into process list sorted by address. - */ -void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp) -{ - struct vm_area_struct **p, *mpnt; - - p = &t->mm->mmap; - while ((mpnt = *p) != NULL) { - if (mpnt->vm_start > vmp->vm_start) - break; - if (mpnt->vm_end > vmp->vm_start) - printk("insert_vm_struct: overlapping memory areas\n"); - p = &mpnt->vm_next; - } - vmp->vm_next = mpnt; - *p = vmp; -} - -/* - * Merge a list of memory segments if possible. - * Redundant vm_area_structs are freed. - * This assumes that the list is ordered by address. - */ -void merge_segments(struct vm_area_struct *mpnt) -{ - struct vm_area_struct *prev, *next; - - if (mpnt == NULL) - return; - - for(prev = mpnt, mpnt = mpnt->vm_next; - mpnt != NULL; - prev = mpnt, mpnt = next) - { - next = mpnt->vm_next; - - /* - * To share, we must have the same inode, operations.. - */ - if (mpnt->vm_inode != prev->vm_inode) - continue; - if (mpnt->vm_pte != prev->vm_pte) - continue; - if (mpnt->vm_ops != prev->vm_ops) - continue; - if (mpnt->vm_page_prot != prev->vm_page_prot || - mpnt->vm_flags != prev->vm_flags) - continue; - if (prev->vm_end != mpnt->vm_start) - continue; - /* - * and if we have an inode, the offsets must be contiguous.. - */ - if ((mpnt->vm_inode != NULL) || (mpnt->vm_flags & VM_SHM)) { - if (prev->vm_offset + prev->vm_end - prev->vm_start != mpnt->vm_offset) - continue; - } - - /* - * merge prev with mpnt and set up pointers so the new - * big segment can possibly merge with the next one. - * The old unused mpnt is freed. - */ - prev->vm_end = mpnt->vm_end; - prev->vm_next = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) { - mpnt->vm_offset += mpnt->vm_end - mpnt->vm_start; - mpnt->vm_start = mpnt->vm_end; - mpnt->vm_ops->close(mpnt); - } - if (mpnt->vm_inode) - mpnt->vm_inode->i_count--; - kfree_s(mpnt, sizeof(*mpnt)); - mpnt = prev; - } -} - -/* - * Map memory not associated with any file into a process - * address space. Adjacent memory is merged. - */ -static int anon_map(struct inode *ino, struct file * file, struct vm_area_struct * vma) -{ - if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -ENOMEM; - return 0; -} diff --git a/arch/mips/mm/mprotect.c b/arch/mips/mm/mprotect.c deleted file mode 100644 index 7bb4148f4..000000000 --- a/arch/mips/mm/mprotect.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * linux/mm/mprotect.c - * - * (C) Copyright 1994 Linus Torvalds - */ -#include <linux/stat.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/errno.h> -#include <linux/mman.h> -#include <linux/string.h> -#include <linux/malloc.h> - -#include <asm/segment.h> -#include <asm/system.h> - -#define CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY | CACHE_UNCACHED) - -static void change_protection(unsigned long start, unsigned long end, int prot) -{ - unsigned long *page_table, *dir; - unsigned long page, offset; - int nr; - - dir = PAGE_DIR_OFFSET(current->tss.pg_dir, start); - offset = (start >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - nr = (end - start) >> PAGE_SHIFT; - while (nr > 0) { - page = *dir; - dir++; - if (!(page & PAGE_VALID)) { - nr = nr - PTRS_PER_PAGE + offset; - offset = 0; - continue; - } - page_table = offset + (unsigned long *) (page & PAGE_MASK); - offset = PTRS_PER_PAGE - offset; - if (offset > nr) - offset = nr; - nr = nr - offset; - do { - page = *page_table; - if (page & PAGE_VALID) - *page_table = (page & CHG_MASK) | prot; - ++page_table; - } while (--offset); - } - return; -} - -static inline int mprotect_fixup_all(struct vm_area_struct * vma, - int newflags, int prot) -{ - vma->vm_flags = newflags; - vma->vm_page_prot = prot; - return 0; -} - -static inline int mprotect_fixup_start(struct vm_area_struct * vma, - unsigned long end, - int newflags, int prot) -{ - struct vm_area_struct * n; - - n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!n) - return -ENOMEM; - *n = *vma; - vma->vm_start = end; - n->vm_end = end; - vma->vm_offset += vma->vm_start - n->vm_start; - n->vm_flags = newflags; - n->vm_page_prot = prot; - if (n->vm_inode) - n->vm_inode->i_count++; - if (n->vm_ops && n->vm_ops->open) - n->vm_ops->open(n); - insert_vm_struct(current, n); - return 0; -} - -static inline int mprotect_fixup_end(struct vm_area_struct * vma, - unsigned long start, - int newflags, int prot) -{ - struct vm_area_struct * n; - - n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!n) - return -ENOMEM; - *n = *vma; - vma->vm_end = start; - n->vm_start = start; - n->vm_offset += n->vm_start - vma->vm_start; - n->vm_flags = newflags; - n->vm_page_prot = prot; - if (n->vm_inode) - n->vm_inode->i_count++; - if (n->vm_ops && n->vm_ops->open) - n->vm_ops->open(n); - insert_vm_struct(current, n); - return 0; -} - -static inline int mprotect_fixup_middle(struct vm_area_struct * vma, - unsigned long start, unsigned long end, - int newflags, int prot) -{ - struct vm_area_struct * left, * right; - - left = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!left) - return -ENOMEM; - right = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!right) { - kfree(left); - return -ENOMEM; - } - *left = *vma; - *right = *vma; - left->vm_end = start; - vma->vm_start = start; - vma->vm_end = end; - right->vm_start = end; - vma->vm_offset += vma->vm_start - left->vm_start; - right->vm_offset += right->vm_start - left->vm_start; - vma->vm_flags = newflags; - vma->vm_page_prot = prot; - if (vma->vm_inode) - vma->vm_inode->i_count += 2; - if (vma->vm_ops && vma->vm_ops->open) { - vma->vm_ops->open(left); - vma->vm_ops->open(right); - } - insert_vm_struct(current, left); - insert_vm_struct(current, right); - return 0; -} - -static int mprotect_fixup(struct vm_area_struct * vma, - unsigned long start, unsigned long end, unsigned int newflags) -{ - int prot, error; - - if (newflags == vma->vm_flags) - return 0; - prot = PAGE_VALID; - if (newflags & (VM_READ | VM_EXEC)) - prot |= PAGE_READONLY; - if (newflags & VM_WRITE) - if (newflags & VM_SHARED) - prot |= PAGE_SHARED; - else - prot |= PAGE_COPY; - - if (start == vma->vm_start) - if (end == vma->vm_end) - error = mprotect_fixup_all(vma, newflags, prot); - else - error = mprotect_fixup_start(vma, end, newflags, prot); - else if (end == vma->vm_end) - error = mprotect_fixup_end(vma, start, newflags, prot); - else - error = mprotect_fixup_middle(vma, start, end, newflags, prot); - - if (error) - return error; - - change_protection(start, end, prot); - return 0; -} - -asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot) -{ - unsigned long end, tmp; - struct vm_area_struct * vma, * next; - int error; - - if (start & ~PAGE_MASK) - return -EINVAL; - len = (len + ~PAGE_MASK) & PAGE_MASK; - end = start + len; - if (end < start) - return -EINVAL; - if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC)) - return -EINVAL; - if (end == start) - return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return -EFAULT; - if (vma->vm_end > start) - break; - } - if (vma->vm_start > start) - return -EFAULT; - - for ( ; ; ) { - unsigned int newflags; - - /* Here we know that vma->vm_start <= start < vma->vm_end. */ - - newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC)); - if ((newflags & ~(newflags >> 4)) & 0xf) { - error = -EACCES; - break; - } - - if (vma->vm_end >= end) { - error = mprotect_fixup(vma, start, end, newflags); - break; - } - - tmp = vma->vm_end; - next = vma->vm_next; - error = mprotect_fixup(vma, start, tmp, newflags); - if (error) - break; - start = tmp; - vma = next; - if (!vma || vma->vm_start != start) { - error = -EFAULT; - break; - } - } - merge_segments(current->mm->mmap); - return error; -} diff --git a/arch/mips/mm/swap.c b/arch/mips/mm/swap.c deleted file mode 100644 index 084208c04..000000000 --- a/arch/mips/mm/swap.c +++ /dev/null @@ -1,986 +0,0 @@ -/* - * linux/mm/swap.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - */ - -/* - * This file should contain most things doing the swapping from/to disk. - * Started 18.12.91 - */ - -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/kernel_stat.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/stat.h> -#include <linux/fs.h> - -#include <asm/system.h> /* for cli()/sti() */ -#include <asm/bitops.h> - -#define MAX_SWAPFILES 8 - -#define SWP_USED 1 -#define SWP_WRITEOK 3 - -#define SWP_TYPE(entry) (((entry) & 0xfe) >> 1) -#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT) -#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT)) - -int min_free_pages = 20; - -static int nr_swapfiles = 0; -static struct wait_queue * lock_queue = NULL; - -static struct swap_info_struct { - unsigned long flags; - struct inode * swap_file; - unsigned int swap_device; - unsigned char * swap_map; - unsigned char * swap_lockmap; - int pages; - int lowest_bit; - int highest_bit; - unsigned long max; -} swap_info[MAX_SWAPFILES]; - -extern int shm_swap (int); - -unsigned long *swap_cache; - -#ifdef SWAP_CACHE_INFO -unsigned long swap_cache_add_total = 0; -unsigned long swap_cache_add_success = 0; -unsigned long swap_cache_del_total = 0; -unsigned long swap_cache_del_success = 0; -unsigned long swap_cache_find_total = 0; -unsigned long swap_cache_find_success = 0; - -extern inline void show_swap_cache_info(void) -{ - printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n", - swap_cache_add_total, swap_cache_add_success, - swap_cache_del_total, swap_cache_del_success, - swap_cache_find_total, swap_cache_find_success); -} -#endif - -extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry) -{ - struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)]; - -#ifdef SWAP_CACHE_INFO - swap_cache_add_total++; -#endif - if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { - atomic_exchange(swap_cache[addr >> PAGE_SHIFT],entry); - if (entry) { - printk("swap_cache: replacing non-NULL entry\n"); - } -#ifdef SWAP_CACHE_INFO - swap_cache_add_success++; -#endif - return 1; - } - return 0; -} - -static unsigned long init_swap_cache(unsigned long mem_start, - unsigned long mem_end) -{ - unsigned long swap_cache_size; - - mem_start = (mem_start + 15) & ~15; - swap_cache = (unsigned long *) mem_start; - swap_cache_size = mem_end >> PAGE_SHIFT; - memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long)); - return (unsigned long) (swap_cache + swap_cache_size); -} - -void rw_swap_page(int rw, unsigned long entry, char * buf) -{ - unsigned long type, offset; - struct swap_info_struct * p; - - type = SWP_TYPE(entry); - if (type >= nr_swapfiles) { - printk("Internal error: bad swap-device\n"); - return; - } - p = &swap_info[type]; - offset = SWP_OFFSET(entry); - if (offset >= p->max) { - printk("rw_swap_page: weirdness\n"); - return; - } - if (!(p->flags & SWP_USED)) { - printk("Trying to swap to unused swap-device\n"); - return; - } - while (set_bit(offset,p->swap_lockmap)) - sleep_on(&lock_queue); - if (rw == READ) - kstat.pswpin++; - else - kstat.pswpout++; - if (p->swap_device) { - ll_rw_page(rw,p->swap_device,offset,buf); - } else if (p->swap_file) { - struct inode *swapf = p->swap_file; - unsigned int zones[8]; - int i; - if (swapf->i_op->bmap == NULL - && swapf->i_op->smap != NULL){ - /* - With MsDOS, we use msdos_smap which return - a sector number (not a cluster or block number). - It is a patch to enable the UMSDOS project. - Other people are working on better solution. - - It sounds like ll_rw_swap_file defined - it operation size (sector size) based on - PAGE_SIZE and the number of block to read. - So using bmap or smap should work even if - smap will require more blocks. - */ - int j; - unsigned int block = offset << 3; - - for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){ - if (!(zones[i] = swapf->i_op->smap(swapf,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; - } - } - }else{ - int j; - unsigned int block = offset - << (12 - swapf->i_sb->s_blocksize_bits); - - for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize) - if (!(zones[i] = bmap(swapf,block++))) { - printk("rw_swap_page: bad swap file\n"); - return; - } - } - ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf); - } else - printk("re_swap_page: no swap file or device\n"); - if (offset && !clear_bit(offset,p->swap_lockmap)) - printk("rw_swap_page: lock already cleared\n"); - wake_up(&lock_queue); -} - -unsigned int get_swap_page(void) -{ - struct swap_info_struct * p; - unsigned int offset, type; - - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) { - if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) { - if (p->swap_map[offset]) - continue; - p->swap_map[offset] = 1; - nr_swap_pages--; - if (offset == p->highest_bit) - p->highest_bit--; - p->lowest_bit = offset; - return SWP_ENTRY(type,offset); - } - } - return 0; -} - -unsigned long swap_duplicate(unsigned long entry) -{ - struct swap_info_struct * p; - unsigned long offset, type; - - if (!entry) - return 0; - offset = SWP_OFFSET(entry); - type = SWP_TYPE(entry); - if (type == SHM_SWP_TYPE) - return entry; - if (type >= nr_swapfiles) { - printk("Trying to duplicate nonexistent swap-page\n"); - return 0; - } - p = type + swap_info; - if (offset >= p->max) { - printk("swap_duplicate: weirdness\n"); - return 0; - } - if (!p->swap_map[offset]) { - printk("swap_duplicate: trying to duplicate unused page\n"); - return 0; - } - p->swap_map[offset]++; - return entry; -} - -void swap_free(unsigned long entry) -{ - struct swap_info_struct * p; - unsigned long offset, type; - - if (!entry) - return; - type = SWP_TYPE(entry); - if (type == SHM_SWP_TYPE) - return; - if (type >= nr_swapfiles) { - printk("Trying to free nonexistent swap-page\n"); - return; - } - p = & swap_info[type]; - offset = SWP_OFFSET(entry); - if (offset >= p->max) { - printk("swap_free: weirdness\n"); - return; - } - if (!(p->flags & SWP_USED)) { - printk("Trying to free swap from unused swap-device\n"); - return; - } - while (set_bit(offset,p->swap_lockmap)) - sleep_on(&lock_queue); - if (offset < p->lowest_bit) - p->lowest_bit = offset; - if (offset > p->highest_bit) - p->highest_bit = offset; - if (!p->swap_map[offset]) - printk("swap_free: swap-space map bad (entry %08lx)\n",entry); - else - if (!--p->swap_map[offset]) - nr_swap_pages++; - if (!clear_bit(offset,p->swap_lockmap)) - printk("swap_free: lock already cleared\n"); - wake_up(&lock_queue); -} - -unsigned long swap_in(unsigned long entry) -{ - unsigned long page; - - if (!(page = get_free_page(GFP_KERNEL))) { - oom(current); - return BAD_PAGE; - } - read_swap_page(entry, (char *) page); - if (add_to_swap_cache(page, entry)) - return page | PAGE_VALID; - swap_free(entry); - return page | PAGE_DIRTY | PAGE_VALID; -} - -static inline int try_to_swap_out(unsigned long * table_ptr) -{ - unsigned long page, entry; - - page = *table_ptr; - if (!(PAGE_VALID & page)) - return 0; - if (page >= high_memory) - return 0; - if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) - return 0; - - if ((PAGE_DIRTY & page) && delete_from_swap_cache(page)) { - *table_ptr &= ~PAGE_ACCESSED; - return 0; - } - if (PAGE_ACCESSED & page) { - *table_ptr &= ~PAGE_ACCESSED; - return 0; - } - if (PAGE_DIRTY & page) { - page &= PAGE_MASK; - if (mem_map[MAP_NR(page)] != 1) - return 0; - if (!(entry = get_swap_page())) - return 0; - *table_ptr = entry; - invalidate(); - write_swap_page(entry, (char *) page); - free_page(page); - return 1; - } - if ((entry = find_in_swap_cache(page))) { - if (mem_map[MAP_NR(page)] != 1) { - *table_ptr |= PAGE_DIRTY; - printk("Aiee.. duplicated cached swap-cache entry\n"); - return 0; - } - *table_ptr = entry; - invalidate(); - free_page(page & PAGE_MASK); - return 1; - } - page &= PAGE_MASK; - *table_ptr = 0; - invalidate(); - free_page(page); - return 1 + mem_map[MAP_NR(page)]; -} - -/* - * A new implementation of swap_out(). We do not swap complete processes, - * but only a small number of blocks, before we continue with the next - * process. The number of blocks actually swapped is determined on the - * number of page faults, that this process actually had in the last time, - * so we won't swap heavily used processes all the time ... - * - * Note: the priority argument is a hint on much CPU to waste with the - * swap block search, not a hint, of how much blocks to swap with - * each process. - * - * (C) 1993 Kai Petzke, wpp@marie.physik.tu-berlin.de - */ - -/* - * These are the minimum and maximum number of pages to swap from one process, - * before proceeding to the next: - */ -#define SWAP_MIN 4 -#define SWAP_MAX 32 - -/* - * The actual number of pages to swap is determined as: - * SWAP_RATIO / (number of recent major page faults) - */ -#define SWAP_RATIO 128 - -static int swap_out_process(struct task_struct * p) -{ - unsigned long address; - unsigned long offset; - unsigned long *pgdir; - unsigned long pg_table; - - /* - * Go through process' page directory. - */ - address = p->mm->swap_address; - pgdir = (address >> PGDIR_SHIFT) + (unsigned long *) p->tss.pg_dir; - offset = address & ~PGDIR_MASK; - address &= PGDIR_MASK; - for ( ; address < TASK_SIZE ; - pgdir++, address = address + PGDIR_SIZE, offset = 0) { - pg_table = *pgdir; - if (pg_table >= high_memory) - continue; - if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED) - continue; - if (!(PAGE_VALID & pg_table)) { - printk("swap_out_process (%s): bad page-table at vm %08lx: %08lx\n", - p->comm, address + offset, pg_table); - *pgdir = 0; - continue; - } - pg_table &= 0xfffff000; - - /* - * Go through this page table. - */ - for( ; offset < ~PGDIR_MASK ; offset += PAGE_SIZE) { - switch(try_to_swap_out((unsigned long *) (pg_table + (offset >> 10)))) { - case 0: - break; - - case 1: - p->mm->rss--; - /* continue with the following page the next time */ - p->mm->swap_address = address + offset + PAGE_SIZE; - return 1; - - default: - p->mm->rss--; - break; - } - } - } - /* - * Finish work with this process, if we reached the end of the page - * directory. Mark restart from the beginning the next time. - */ - p->mm->swap_address = 0; - return 0; -} - -static int swap_out(unsigned int priority) -{ - static int swap_task; - int loop; - int counter = NR_TASKS * 2 >> priority; - struct task_struct *p; - - counter = NR_TASKS * 2 >> priority; - for(; counter >= 0; counter--, swap_task++) { - /* - * Check that swap_task is suitable for swapping. If not, look for - * the next suitable process. - */ - loop = 0; - while(1) { - if (swap_task >= NR_TASKS) { - swap_task = 1; - if (loop) - /* all processes are unswappable or already swapped out */ - return 0; - loop = 1; - } - - p = task[swap_task]; - if (p && p->mm->swappable && p->mm->rss) - break; - - swap_task++; - } - - /* - * Determine the number of pages to swap from this process. - */ - if (!p->mm->swap_cnt) { - p->mm->dec_flt = (p->mm->dec_flt * 3) / 4 + p->mm->maj_flt - p->mm->old_maj_flt; - p->mm->old_maj_flt = p->mm->maj_flt; - - if (p->mm->dec_flt >= SWAP_RATIO / SWAP_MIN) { - p->mm->dec_flt = SWAP_RATIO / SWAP_MIN; - p->mm->swap_cnt = SWAP_MIN; - } else if (p->mm->dec_flt <= SWAP_RATIO / SWAP_MAX) - p->mm->swap_cnt = SWAP_MAX; - else - p->mm->swap_cnt = SWAP_RATIO / p->mm->dec_flt; - } - if (swap_out_process(p)) { - if ((--p->mm->swap_cnt) == 0) - swap_task++; - return 1; - } - } - return 0; -} - -static int try_to_free_page(int priority) -{ - int i=6; - - while (i--) { - if (priority != GFP_NOBUFFER && shrink_buffers(i)) - return 1; - if (shm_swap(i)) - return 1; - if (swap_out(i)) - return 1; - } - return 0; -} - -static inline void add_mem_queue(struct mem_list * head, struct mem_list * entry) -{ - entry->prev = head; - entry->next = head->next; - entry->next->prev = entry; - head->next = entry; -} - -static inline void remove_mem_queue(struct mem_list * head, struct mem_list * entry) -{ - entry->next->prev = entry->prev; - entry->prev->next = entry->next; -} - -/* - * Free_page() adds the page to the free lists. This is optimized for - * fast normal cases (no error jumps taken normally). - * - * The way to optimize jumps for gcc-2.2.2 is to: - * - select the "normal" case and put it inside the if () { XXX } - * - no else-statements if you can avoid them - * - * With the above two rules, you get a straight-line execution path - * for the normal case, giving better asm-code. - */ - -/* - * Buddy system. Hairy. You really aren't expected to understand this - */ -static inline void free_pages_ok(unsigned long addr, unsigned long order) -{ - unsigned long index = addr >> (PAGE_SHIFT + 1 + order); - unsigned long mask = PAGE_MASK << order; - - addr &= mask; - nr_free_pages += 1 << order; - while (order < NR_MEM_LISTS-1) { - if (!change_bit(index, free_area_map[order])) - break; - remove_mem_queue(free_area_list+order, (struct mem_list *) (addr ^ (1+~mask))); - order++; - index >>= 1; - mask <<= 1; - addr &= mask; - } - add_mem_queue(free_area_list+order, (struct mem_list *) addr); -} - -static inline void check_free_buffers(unsigned long addr) -{ - struct buffer_head * bh; - - bh = buffer_pages[MAP_NR(addr)]; - if (bh) { - struct buffer_head *tmp = bh; - do { - if (tmp->b_list == BUF_SHARED && tmp->b_dev != 0xffff) - refile_buffer(tmp); - tmp = tmp->b_this_page; - } while (tmp != bh); - } -} - -void free_pages(unsigned long addr, unsigned long order) -{ - if (addr < high_memory) { - unsigned long flag; - unsigned short * map = mem_map + MAP_NR(addr); - if (*map) { - if (!(*map & MAP_PAGE_RESERVED)) { - save_flags(flag); - cli(); - if (!--*map) { - free_pages_ok(addr, order); - delete_from_swap_cache(addr); - } - restore_flags(flag); - if (*map == 1) - check_free_buffers(addr); - } - return; - } - printk("Trying to free free memory (%08lx): memory probably corrupted\n",addr); - printk("PC = %08lx\n",*(((unsigned long *)&addr)-1)); - return; - } -} - -/* - * Some ugly macros to speed up __get_free_pages().. - */ -#define RMQUEUE(order) \ -do { struct mem_list * queue = free_area_list+order; \ - unsigned long new_order = order; \ - do { struct mem_list *next = queue->next; \ - if (queue != next) { \ - (queue->next = next->next)->prev = queue; \ - mark_used((unsigned long) next, new_order); \ - nr_free_pages -= 1 << order; \ - restore_flags(flags); \ - EXPAND(next, order, new_order); \ - return (unsigned long) next; \ - } new_order++; queue++; \ - } while (new_order < NR_MEM_LISTS); \ -} while (0) - -static inline int mark_used(unsigned long addr, unsigned long order) -{ - return change_bit(addr >> (PAGE_SHIFT+1+order), free_area_map[order]); -} - -#define EXPAND(addr,low,high) \ -do { unsigned long size = PAGE_SIZE << high; \ - while (high > low) { \ - high--; size >>= 1; cli(); \ - add_mem_queue(free_area_list+high, addr); \ - mark_used((unsigned long) addr, high); \ - restore_flags(flags); \ - addr = (struct mem_list *) (size + (unsigned long) addr); \ - } mem_map[MAP_NR((unsigned long) addr)] = 1; \ -} while (0) - -unsigned long __get_free_pages(int priority, unsigned long order) -{ - unsigned long flags; - int reserved_pages; - - if (intr_count && priority != GFP_ATOMIC) { - static int count = 0; - if (++count < 5) { - printk("gfp called nonatomically from interrupt %p\n", - __builtin_return_address(0)); - priority = GFP_ATOMIC; - } - } - reserved_pages = 5; - if (priority != GFP_NFS) - reserved_pages = min_free_pages; - save_flags(flags); -repeat: - cli(); - if ((priority==GFP_ATOMIC) || nr_free_pages > reserved_pages) { - RMQUEUE(order); - restore_flags(flags); - return 0; - } - restore_flags(flags); - if (priority != GFP_BUFFER && try_to_free_page(priority)) - goto repeat; - return 0; -} - -/* - * Yes, I know this is ugly. Don't tell me. - */ -unsigned long __get_dma_pages(int priority, unsigned long order) -{ - unsigned long list = 0; - unsigned long result; - unsigned long limit = 16*1024*1024; - - /* if (EISA_bus) limit = ~0UL; */ - if (priority != GFP_ATOMIC) - priority = GFP_BUFFER; - for (;;) { - result = __get_free_pages(priority, order); - if (result < limit) /* covers failure as well */ - break; - *(unsigned long *) result = list; - list = result; - } - while (list) { - unsigned long tmp = list; - list = *(unsigned long *) list; - free_pages(tmp, order); - } - return result; -} - -/* - * Show free area list (used inside shift_scroll-lock stuff) - * We also calculate the percentage fragmentation. We do this by counting the - * memory on each free list with the exception of the first item on the list. - */ -void show_free_areas(void) -{ - unsigned long order, flags; - unsigned long total = 0; - - printk("Free pages: %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10)); - save_flags(flags); - cli(); - for (order=0 ; order < NR_MEM_LISTS; order++) { - struct mem_list * tmp; - unsigned long nr = 0; - for (tmp = free_area_list[order].next ; tmp != free_area_list + order ; tmp = tmp->next) { - nr ++; - } - total += nr * (4 << order); - printk("%lu*%ukB ", nr, 4 << order); - } - restore_flags(flags); - printk("= %lukB)\n", total); -#ifdef SWAP_CACHE_INFO - show_swap_cache_info(); -#endif -} - -/* - * Trying to stop swapping from a file is fraught with races, so - * we repeat quite a bit here when we have to pause. swapoff() - * isn't exactly timing-critical, so who cares? - */ -static int try_to_unuse(unsigned int type) -{ - int nr, pgt, pg; - unsigned long page, *ppage; - unsigned long tmp = 0; - struct task_struct *p; - - nr = 0; - -/* - * When we have to sleep, we restart the whole algorithm from the same - * task we stopped in. That at least rids us of all races. - */ -repeat: - for (; nr < NR_TASKS ; nr++) { - p = task[nr]; - if (!p) - continue; - for (pgt = 0 ; pgt < PTRS_PER_PAGE ; pgt++) { - ppage = pgt + ((unsigned long *) p->tss.pg_dir); - page = *ppage; - if (!page) - continue; - if (!(page & PAGE_VALID) || (page >= high_memory)) - continue; - if (mem_map[MAP_NR(page)] & MAP_PAGE_RESERVED) - continue; - ppage = (unsigned long *) (page & PAGE_MASK); - for (pg = 0 ; pg < PTRS_PER_PAGE ; pg++,ppage++) { - page = *ppage; - if (!page) - continue; - if (page & PAGE_VALID) { - if (!(page = in_swap_cache(page))) - continue; - if (SWP_TYPE(page) != type) - continue; - *ppage |= PAGE_DIRTY; - delete_from_swap_cache(*ppage); - continue; - } - if (SWP_TYPE(page) != type) - continue; - if (!tmp) { - if (!(tmp = __get_free_page(GFP_KERNEL))) - return -ENOMEM; - goto repeat; - } - read_swap_page(page, (char *) tmp); - if (*ppage == page) { - *ppage = tmp | (PAGE_DIRTY | PAGE_PRIVATE); - ++p->mm->rss; - swap_free(page); - tmp = 0; - } - goto repeat; - } - } - } - free_page(tmp); - return 0; -} - -asmlinkage int sys_swapoff(const char * specialfile) -{ - struct swap_info_struct * p; - struct inode * inode; - unsigned int type; - int i; - - if (!suser()) - return -EPERM; - i = namei(specialfile,&inode); - if (i) - return i; - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) { - if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - if (p->swap_file) { - if (p->swap_file == inode) - break; - } else { - if (!S_ISBLK(inode->i_mode)) - continue; - if (p->swap_device == inode->i_rdev) - break; - } - } - iput(inode); - if (type >= nr_swapfiles) - return -EINVAL; - p->flags = SWP_USED; - i = try_to_unuse(type); - if (i) { - p->flags = SWP_WRITEOK; - return i; - } - nr_swap_pages -= p->pages; - iput(p->swap_file); - p->swap_file = NULL; - p->swap_device = 0; - vfree(p->swap_map); - p->swap_map = NULL; - free_page((long) p->swap_lockmap); - p->swap_lockmap = NULL; - p->flags = 0; - return 0; -} - -/* - * Written 01/25/92 by Simmule Turner, heavily changed by Linus. - * - * The swapon system call - */ -asmlinkage int sys_swapon(const char * specialfile) -{ - struct swap_info_struct * p; - struct inode * swap_inode; - unsigned int type; - int i,j; - int error; - - if (!suser()) - return -EPERM; - p = swap_info; - for (type = 0 ; type < nr_swapfiles ; type++,p++) - if (!(p->flags & SWP_USED)) - break; - if (type >= MAX_SWAPFILES) - return -EPERM; - if (type >= nr_swapfiles) - nr_swapfiles = type+1; - p->flags = SWP_USED; - p->swap_file = NULL; - p->swap_device = 0; - p->swap_map = NULL; - p->swap_lockmap = NULL; - p->lowest_bit = 0; - p->highest_bit = 0; - p->max = 1; - error = namei(specialfile,&swap_inode); - if (error) - goto bad_swap; - p->swap_file = swap_inode; - error = -EBUSY; - if (swap_inode->i_count != 1) - goto bad_swap; - error = -EINVAL; - if (S_ISBLK(swap_inode->i_mode)) { - p->swap_device = swap_inode->i_rdev; - p->swap_file = NULL; - iput(swap_inode); - error = -ENODEV; - if (!p->swap_device) - goto bad_swap; - error = -EBUSY; - for (i = 0 ; i < nr_swapfiles ; i++) { - if (i == type) - continue; - if (p->swap_device == swap_info[i].swap_device) - goto bad_swap; - } - } else if (!S_ISREG(swap_inode->i_mode)) - goto bad_swap; - p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER); - if (!p->swap_lockmap) { - printk("Unable to start swapping: out of memory :-)\n"); - error = -ENOMEM; - goto bad_swap; - } - read_swap_page(SWP_ENTRY(type,0), (char *) p->swap_lockmap); - if (memcmp("SWAP-SPACE",p->swap_lockmap+4086,10)) { - printk("Unable to find swap-space signature\n"); - error = -EINVAL; - goto bad_swap; - } - memset(p->swap_lockmap+PAGE_SIZE-10,0,10); - j = 0; - p->lowest_bit = 0; - p->highest_bit = 0; - for (i = 1 ; i < 8*PAGE_SIZE ; i++) { - if (test_bit(i,p->swap_lockmap)) { - if (!p->lowest_bit) - p->lowest_bit = i; - p->highest_bit = i; - p->max = i+1; - j++; - } - } - if (!j) { - printk("Empty swap-file\n"); - error = -EINVAL; - goto bad_swap; - } - p->swap_map = (unsigned char *) vmalloc(p->max); - if (!p->swap_map) { - error = -ENOMEM; - goto bad_swap; - } - for (i = 1 ; i < p->max ; i++) { - if (test_bit(i,p->swap_lockmap)) - p->swap_map[i] = 0; - else - p->swap_map[i] = 0x80; - } - p->swap_map[0] = 0x80; - memset(p->swap_lockmap,0,PAGE_SIZE); - p->flags = SWP_WRITEOK; - p->pages = j; - nr_swap_pages += j; - printk("Adding Swap: %dk swap-space\n",j<<2); - return 0; -bad_swap: - free_page((long) p->swap_lockmap); - vfree(p->swap_map); - iput(p->swap_file); - p->swap_device = 0; - p->swap_file = NULL; - p->swap_map = NULL; - p->swap_lockmap = NULL; - p->flags = 0; - return error; -} - -void si_swapinfo(struct sysinfo *val) -{ - unsigned int i, j; - - val->freeswap = val->totalswap = 0; - for (i = 0; i < nr_swapfiles; i++) { - if ((swap_info[i].flags & SWP_WRITEOK) != SWP_WRITEOK) - continue; - for (j = 0; j < swap_info[i].max; ++j) - switch (swap_info[i].swap_map[j]) { - case 128: - continue; - case 0: - ++val->freeswap; - default: - ++val->totalswap; - } - } - val->freeswap <<= PAGE_SHIFT; - val->totalswap <<= PAGE_SHIFT; - return; -} - -/* - * set up the free-area data structures: - * - mark all pages MAP_PAGE_RESERVED - * - mark all memory queues empty - * - clear the memory bitmaps - */ -unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned short * p; - unsigned long mask = PAGE_MASK; - int i; - - /* - * select nr of pages we try to keep free for important stuff - * with a minimum of 16 pages. This is totally arbitrary - */ - i = end_mem >> (PAGE_SHIFT+6); - if (i < 16) - i = 16; - min_free_pages = i; - start_mem = init_swap_cache(start_mem, end_mem); - mem_map = (unsigned short *) start_mem; - p = mem_map + MAP_NR(end_mem); - start_mem = (unsigned long) p; - while (p > mem_map) - *--p = MAP_PAGE_RESERVED; - - for (i = 0 ; i < NR_MEM_LISTS ; i++, mask <<= 1) { - unsigned long bitmap_size; - free_area_list[i].prev = free_area_list[i].next = &free_area_list[i]; - end_mem = (end_mem + ~mask) & mask; - bitmap_size = end_mem >> (PAGE_SHIFT + i); - bitmap_size = (bitmap_size + 7) >> 3; - free_area_map[i] = (unsigned char *) start_mem; - memset((void *) start_mem, 0, bitmap_size); - start_mem += bitmap_size; - } - return start_mem; -} diff --git a/arch/mips/mm/vmalloc.c b/arch/mips/mm/vmalloc.c deleted file mode 100644 index 9b3ab7f59..000000000 --- a/arch/mips/mm/vmalloc.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * linux/mm/vmalloc.c - * - * Copyright (C) 1993 Linus Torvalds - */ - -#include <asm/system.h> -#include <linux/config.h> - -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/head.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/malloc.h> -#include <asm/segment.h> - -struct vm_struct { - unsigned long flags; - void * addr; - unsigned long size; - struct vm_struct * next; -}; - -static struct vm_struct * vmlist = NULL; - -/* Just any arbitrary offset to the start of the vmalloc VM area: the - * current 8MB value just means that there will be a 8MB "hole" after the - * physical memory until the kernel virtual memory starts. That means that - * any out-of-bounds memory accesses will hopefully be caught. - * The vmalloc() routines leaves a hole of 4kB between each vmalloced - * area for the same reason. ;) - */ -#define VMALLOC_OFFSET (8*1024*1024) - -static inline void set_pgdir(unsigned long dindex, unsigned long value) -{ - struct task_struct * p; - - p = &init_task; - do { - ((unsigned long *) p->tss.pg_dir)[dindex] = value; - p = p->next_task; - } while (p != &init_task); -} - -static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr) -{ - unsigned long page, *pte; - - if (!(PAGE_VALID & (page = swapper_pg_dir[dindex]))) - return 0; - page &= PAGE_MASK; - pte = index + (unsigned long *) page; - do { - unsigned long pg = *pte; - *pte = 0; - if (pg & PAGE_VALID) - free_page(pg); - pte++; - } while (--nr); - pte = (unsigned long *) page; - for (nr = 0 ; nr < 1024 ; nr++, pte++) - if (*pte) - return 0; - set_pgdir(dindex,0); - mem_map[MAP_NR(page)] = 1; - free_page(page); - invalidate(); - return 0; -} - -static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr) -{ - unsigned long page, *pte; - - page = swapper_pg_dir[dindex]; - if (!page) { - page = get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - if (swapper_pg_dir[dindex]) { - free_page(page); - page = swapper_pg_dir[dindex]; - } else { - mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED; - set_pgdir(dindex, page | PAGE_SHARED); - } - } - page &= PAGE_MASK; - pte = index + (unsigned long *) page; - *pte = PAGE_SHARED; /* remove a race with vfree() */ - do { - unsigned long pg = get_free_page(GFP_KERNEL); - - if (!pg) - return -ENOMEM; - *pte = pg | PAGE_SHARED; - pte++; - } while (--nr); - invalidate(); - return 0; -} - -static int do_area(void * addr, unsigned long size, - int (*area_fn)(unsigned long,unsigned long,unsigned long)) -{ - unsigned long nr, dindex, index; - - nr = size >> PAGE_SHIFT; - dindex = (TASK_SIZE + (unsigned long) addr) >> 22; - index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1); - while (nr > 0) { - unsigned long i = PTRS_PER_PAGE - index; - - if (i > nr) - i = nr; - nr -= i; - if (area_fn(dindex, index, i)) - return -1; - index = 0; - dindex++; - } - return 0; -} - -void vfree(void * addr) -{ - struct vm_struct **p, *tmp; - - if (!addr) - return; - if ((PAGE_SIZE-1) & (unsigned long) addr) { - printk("Trying to vfree() bad address (%p)\n", addr); - return; - } - for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { - if (tmp->addr == addr) { - *p = tmp->next; - do_area(tmp->addr, tmp->size, free_area_pages); - kfree(tmp); - return; - } - } - printk("Trying to vfree() nonexistent vm area (%p)\n", addr); -} - -void * vmalloc(unsigned long size) -{ - void * addr; - struct vm_struct **p, *tmp, *area; - - size = PAGE_ALIGN(size); - if (!size || size > high_memory) - return NULL; - area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); - if (!area) - return NULL; - addr = (void *) ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); - area->size = size + PAGE_SIZE; - area->next = NULL; - for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { - if (size + (unsigned long) addr < (unsigned long) tmp->addr) - break; - addr = (void *) (tmp->size + (unsigned long) tmp->addr); - } - area->addr = addr; - area->next = *p; - *p = area; - if (do_area(addr, size, alloc_area_pages)) { - vfree(addr); - return NULL; - } - return addr; -} - -int vread(char *buf, char *addr, int count) -{ - struct vm_struct **p, *tmp; - char *vaddr, *buf_start = buf; - int n; - - for (p = &vmlist; (tmp = *p) ; p = &tmp->next) { - vaddr = (char *) tmp->addr; - while (addr < vaddr) { - if (count == 0) - goto finished; - put_fs_byte('\0', buf++), addr++, count--; - } - n = tmp->size - PAGE_SIZE; - if (addr > vaddr) - n -= addr - vaddr; - while (--n >= 0) { - if (count == 0) - goto finished; - put_fs_byte(*addr++, buf++), count--; - } - } -finished: - return buf - buf_start; -} diff --git a/arch/mips/sched.c b/arch/mips/sched.c deleted file mode 100644 index 5c60764a8..000000000 --- a/arch/mips/sched.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * 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 <asm/system.h> -#include <asm/io.h> -#include <asm/segment.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); - -/* - * 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; - -/* - * Tell us the machine setup.. - */ -int hard_math = 0; /* set by boot/head.S */ -int wp_works_ok = 0; /* set if paging hardware honours WP */ - -/* - * Bus types .. - */ -int EISA_bus = 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); -asmlinkage int system_call(void); - -static unsigned long init_kernel_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, }; - -long user_stack [ PAGE_SIZE>>2 ] = { STACK_MAGIC, }; - -struct kernel_stat kstat = { 0 }; - -#ifndef CONFIG_MATH_EMULATION - -/* - * FIXME: There is no fpa emulator yet - */ -asmlinkage void math_emulate(long arg) -{ - printk("math-emulation not enabled and no coprocessor found.\n"); - printk("killing %s.\n",current->comm); - send_sig(SIGFPE,current,1); - schedule(); -} - -#endif /* CONFIG_MATH_EMULATION */ - -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; - } - cli(); - ticks = itimer_ticks; - itimer_ticks = 0; - itimer_next = ~0; - sti(); - need_resched = 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 && 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 (pc = %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; - /* last time the cmos clock got updated */ - static long last_rtc_update=0; - extern int set_rtc_mmss(unsigned long); - - /* 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 GMT\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 GMT\n"); - } - break; - - case TIME_OOP: - time_status = TIME_OK; - break; - } - if (xtime.tv_sec > last_rtc_update + 660) - 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 one min */ -} - -/* - * 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(struct pt_regs * regs) -{ - unsigned long mask; - struct timer_struct *tp; - - 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(); - } - - jiffies++; - calc_load(); - if (USES_USER_TIME(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++; -#if defined (CONFIG_PROFILE) & !defined (__mips__) - if (prof_buffer && current != task[0]) { - unsigned long eip = regs->eip; - eip >>= 2; - 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(" "); - if (p == current) - printk(" current "); - else - printk(" %08lX ", (unsigned long *)p->tss.cp0_epc); - 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 (sizeof(struct sigaction) != 16) - panic("Struct sigaction MUST be 16 bytes"); - - outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ - outb_p(LATCH & 0xff , 0x40); /* LSB */ - outb(LATCH >> 8 , 0x40); /* MSB */ - if (request_irq(TIMER_IRQ,(void (*)(int)) do_timer, 0, "timer") != 0) - panic("Could not allocate timer IRQ!"); -} diff --git a/arch/mips/splx.c b/arch/mips/splx.c deleted file mode 100644 index 5ff5e8284..000000000 --- a/arch/mips/splx.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * splx.c - SYSV DDI/DKI ipl manipulation functions - * - * Internally, many unices use a range of different interrupt - * privilege levels, ie from "allow all interrupts" (7) to - * "allow no interrupts." (0) under SYSV. - * - * This a simple splx() function behaves as the SYSV DDI/DKI function does, - * although since Linux only implements the equivalent of level 0 (cli) and - * level 7 (sti), this implementation only implements those levels. - * - * Also, unlike the current Linux routines, splx() also returns the - * old privilege level so that it can be restored. - */ - -#include <asm/system.h> -#include <asm/mipsregs.h> - -int splx (int new_level) -{ - register int old_level, tmp; - - save_flags(tmp); - old_level = ((tmp & (ST0_IE|ST0_ERL|ST0_EXL)) == ST0_IE) ? 7 : 0; - if (new_level) - sti(); - else - cli(); - return old_level; -} diff --git a/arch/mips/traps.c b/arch/mips/traps.c deleted file mode 100644 index 9cb1252d5..000000000 --- a/arch/mips/traps.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * linux/kernel/traps.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. Currently mostly a debugging-aid, will be extended - * to mainly kill the offending process (probably by giving it a signal, - * but possibly by killing it outright if necessary). - */ -#include <linux/head.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/ptrace.h> - -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/io.h> -#include <asm/mipsregs.h> - -static inline void console_verbose(void) -{ - extern int console_loglevel; - console_loglevel = 15; -} - -asmlinkage extern void handle_int(void); -asmlinkage extern void handle_mod(void); -asmlinkage extern void handle_tlbl(void); -asmlinkage extern void handle_tlbs(void); -asmlinkage extern void handle_adel(void); -asmlinkage extern void handle_ades(void); -asmlinkage extern void handle_ibe(void); -asmlinkage extern void handle_dbe(void); -asmlinkage extern void handle_sys(void); -asmlinkage extern void handle_bp(void); -asmlinkage extern void handle_ri(void); -asmlinkage extern void handle_cpu(void); -asmlinkage extern void handle_ov(void); -asmlinkage extern void handle_tr(void); -asmlinkage extern void handle_reserved(void); -asmlinkage extern void handle_fpe(void); - -void die_if_kernel(char * str, struct pt_regs * regs, long err) -{ - int i; - unsigned long *sp, *pc; - - if (regs->cp0_status & (ST0_ERL|ST0_EXL) == 0) - return; - - sp = (unsigned long *)regs->reg29; - pc = (unsigned long *)regs->cp0_epc; - - console_verbose(); - printk("%s: %08lx\n", str, err ); - - /* - * Saved main processor registers - */ - printk("at : %08lx\n", regs->reg1); - printk("v0 : %08lx %08lx\n", regs->reg2, regs->reg3); - printk("a0 : %08lx %08lx %08lx %08lx\n", - regs->reg4, regs->reg5, regs->reg6, regs->reg7); - printk("t0 : %08lx %08lx %08lx %08lx %08lx\n", - regs->reg8, regs->reg9, regs->reg10, regs->reg11, regs->reg12); - printk("t5 : %08lx %08lx %08lx %08lx %08lx\n", - regs->reg13, regs->reg14, regs->reg15, regs->reg24, regs->reg25); - printk("s0 : %08lx %08lx %08lx %08lx\n", - regs->reg16, regs->reg17, regs->reg18, regs->reg19); - printk("s4 : %08lx %08lx %08lx %08lx\n", - regs->reg20, regs->reg21, regs->reg22, regs->reg23); - printk("gp : %08lx\n", regs->reg28); - printk("sp : %08lx\n", regs->reg29); - printk("fp/s8: %08lx\n", regs->reg30); - printk("ra : %08lx\n", regs->reg31); - - /* - * Saved cp0 registers - */ - printk("EPC : %08lx\nErrorEPC: %08lx\nStatus: %08lx\n", - regs->cp0_epc, regs->cp0_errorepc, regs->cp0_status); - /* - * Some goodies... - */ - printk("Int : %ld\n", regs->interrupt); - - /* - * Dump the stack - */ - if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page) - printk("Corrupted stack page\n"); - printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ", - current->comm, current->pid, 0xffff & i, - current->kernel_stack_page); - for(i=0;i<5;i++) - printk("%08lx ", *sp++); - - printk("\nCode: "); - for(i=0;i<5;i++) - printk("%08lx ", *pc++); - printk("\n"); - do_exit(SIGSEGV); -} - -void trap_init(void) -{ - int i; - -#if 0 - set_except_vector(0, handle_int); - set_except_vector(1, handle_mod); - set_except_vector(2, handle_tlbl); - set_except_vector(3, handle_tlbs); - set_except_vector(4, handle_adel); - set_except_vector(5, handle_ades); - set_except_vector(6, handle_ibe); - set_except_vector(7, handle_dbe); - set_except_vector(8, handle_sys); - set_except_vector(9, handle_bp); - set_except_vector(10, handle_ri); - set_except_vector(11, handle_cpu); - set_except_vector(12, handle_ov); - set_except_vector(13, handle_tr); - set_except_vector(14, handle_reserved); - set_except_vector(15, handle_fpe); - - for (i=16;i<256;i++) - set_except_vector(i, handle_reserved); -#endif -} |