summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
committer <ralf@linux-mips.org>1995-11-14 08:00:00 +0000
commite7c2a72e2680827d6a733931273a93461c0d8d1b (patch)
treec9abeda78ef7504062bb2e816bcf3e3c9d680112 /arch
parentec6044459060a8c9ce7f64405c465d141898548c (diff)
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'arch')
-rw-r--r--arch/alpha/Makefile74
-rw-r--r--arch/alpha/boot/Makefile53
-rw-r--r--arch/alpha/boot/head.S76
-rw-r--r--arch/alpha/boot/main.c217
-rw-r--r--arch/alpha/boot/tools/build.c173
-rw-r--r--arch/alpha/config.in95
-rw-r--r--arch/alpha/kernel/Makefile47
-rw-r--r--arch/alpha/kernel/bios32.c478
-rw-r--r--arch/alpha/kernel/entry.S554
-rw-r--r--arch/alpha/kernel/head.S97
-rw-r--r--arch/alpha/kernel/irq.c414
-rw-r--r--arch/alpha/kernel/lca.c304
-rw-r--r--arch/alpha/kernel/osf_sys.c494
-rw-r--r--arch/alpha/kernel/process.c186
-rw-r--r--arch/alpha/kernel/setup.c172
-rw-r--r--arch/alpha/kernel/signal.c319
-rw-r--r--arch/alpha/kernel/traps.c159
-rw-r--r--arch/alpha/lib/Makefile38
-rw-r--r--arch/alpha/lib/divide.S160
-rw-r--r--arch/alpha/lib/io.c98
-rw-r--r--arch/alpha/lib/memcpy.c74
-rw-r--r--arch/alpha/lib/memset.c44
-rw-r--r--arch/alpha/mm/Makefile32
-rw-r--r--arch/alpha/mm/fault.c100
-rw-r--r--arch/alpha/mm/init.c204
-rw-r--r--arch/i386/Makefile140
-rw-r--r--arch/i386/boot/Makefile60
-rw-r--r--arch/i386/boot/compressed/Makefile41
-rw-r--r--arch/i386/boot/compressed/crypt.h12
-rw-r--r--arch/i386/boot/compressed/gzip.h284
-rw-r--r--arch/i386/boot/compressed/head.S59
-rw-r--r--arch/i386/boot/compressed/inflate.c810
-rw-r--r--arch/i386/boot/compressed/lzw.h42
-rw-r--r--arch/i386/boot/compressed/misc.c418
-rw-r--r--arch/i386/boot/compressed/piggyback.c81
-rw-r--r--arch/i386/boot/compressed/unzip.c180
-rw-r--r--arch/i386/boot/compressed/xtract.c86
-rw-r--r--arch/i386/boot/install.sh39
-rw-r--r--arch/i386/boot/tools/build.c238
-rw-r--r--arch/i386/bootsect.S460
-rw-r--r--arch/i386/config.in149
-rw-r--r--arch/i386/dummy.c11
-rw-r--r--arch/i386/head.S349
-rw-r--r--arch/i386/ibcs/Makefile37
-rw-r--r--arch/i386/ibcs/binfmt_coff.c784
-rw-r--r--arch/i386/ibcs/binfmt_elf.c655
-rw-r--r--arch/i386/ibcs/emulate.c10
-rw-r--r--arch/i386/ioport.c194
-rw-r--r--arch/i386/kernel/Makefile48
-rw-r--r--arch/i386/kernel/bios32.c (renamed from arch/i386/bios32.c)105
-rw-r--r--arch/i386/kernel/entry.S (renamed from arch/i386/entry.S)8
-rw-r--r--arch/i386/kernel/head.S (renamed from arch/i386/boot/head.S)43
-rw-r--r--arch/i386/kernel/ioport.c89
-rw-r--r--arch/i386/kernel/irq.c (renamed from arch/i386/irq.c)202
-rw-r--r--arch/i386/kernel/ldt.c (renamed from arch/i386/ldt.c)3
-rw-r--r--arch/i386/kernel/process.c288
-rw-r--r--arch/i386/kernel/ptrace.c (renamed from arch/i386/ptrace.c)110
-rw-r--r--arch/i386/kernel/setup.c181
-rw-r--r--arch/i386/kernel/signal.c (renamed from arch/i386/signal.c)163
-rw-r--r--arch/i386/kernel/traps.c (renamed from arch/i386/traps.c)132
-rw-r--r--arch/i386/kernel/vm86.c (renamed from arch/i386/vm86.c)92
-rw-r--r--arch/i386/main.c481
-rw-r--r--arch/i386/math-emu/Makefile52
-rw-r--r--arch/i386/math-emu/README436
-rw-r--r--arch/i386/math-emu/control_w.h45
-rw-r--r--arch/i386/math-emu/div_Xsig.S369
-rw-r--r--arch/i386/math-emu/div_small.S50
-rw-r--r--arch/i386/math-emu/errors.c671
-rw-r--r--arch/i386/math-emu/exception.h53
-rw-r--r--arch/i386/math-emu/fpu_arith.c179
-rw-r--r--arch/i386/math-emu/fpu_asm.h30
-rw-r--r--arch/i386/math-emu/fpu_aux.c184
-rw-r--r--arch/i386/math-emu/fpu_emu.h171
-rw-r--r--arch/i386/math-emu/fpu_entry.c690
-rw-r--r--arch/i386/math-emu/fpu_etc.c129
-rw-r--r--arch/i386/math-emu/fpu_proto.h137
-rw-r--r--arch/i386/math-emu/fpu_system.h83
-rw-r--r--arch/i386/math-emu/fpu_trig.c1718
-rw-r--r--arch/i386/math-emu/get_address.c423
-rw-r--r--arch/i386/math-emu/load_store.c260
-rw-r--r--arch/i386/math-emu/mul_Xsig.S182
-rw-r--r--arch/i386/math-emu/poly.h116
-rw-r--r--arch/i386/math-emu/poly_2xm1.c152
-rw-r--r--arch/i386/math-emu/poly_atan.c197
-rw-r--r--arch/i386/math-emu/poly_l2.c255
-rw-r--r--arch/i386/math-emu/poly_sin.c408
-rw-r--r--arch/i386/math-emu/poly_tan.c213
-rw-r--r--arch/i386/math-emu/polynom_Xsig.S137
-rw-r--r--arch/i386/math-emu/reg_add_sub.c318
-rw-r--r--arch/i386/math-emu/reg_compare.c378
-rw-r--r--arch/i386/math-emu/reg_constant.c116
-rw-r--r--arch/i386/math-emu/reg_constant.h31
-rw-r--r--arch/i386/math-emu/reg_div.S251
-rw-r--r--arch/i386/math-emu/reg_ld_str.c1438
-rw-r--r--arch/i386/math-emu/reg_mul.c105
-rw-r--r--arch/i386/math-emu/reg_norm.S150
-rw-r--r--arch/i386/math-emu/reg_round.S701
-rw-r--r--arch/i386/math-emu/reg_u_add.S189
-rw-r--r--arch/i386/math-emu/reg_u_div.S477
-rw-r--r--arch/i386/math-emu/reg_u_mul.S163
-rw-r--r--arch/i386/math-emu/reg_u_sub.S292
-rw-r--r--arch/i386/math-emu/round_Xsig.S148
-rw-r--r--arch/i386/math-emu/shr_Xsig.S90
-rw-r--r--arch/i386/math-emu/status_w.h65
-rw-r--r--arch/i386/math-emu/version.h12
-rw-r--r--arch/i386/math-emu/wm_shrx.S208
-rw-r--r--arch/i386/math-emu/wm_sqrt.S474
-rw-r--r--arch/i386/mm/Makefile6
-rw-r--r--arch/i386/mm/fault.c135
-rw-r--r--arch/i386/mm/init.c239
-rw-r--r--arch/i386/mm/kmalloc.c362
-rw-r--r--arch/i386/mm/memory.c1320
-rw-r--r--arch/i386/mm/mmap.c470
-rw-r--r--arch/i386/mm/mprotect.c230
-rw-r--r--arch/i386/mm/swap.c1017
-rw-r--r--arch/i386/mm/vmalloc.c202
-rw-r--r--arch/i386/sched.c861
-rw-r--r--arch/mips/.gdbinit7
-rw-r--r--arch/mips/Makefile140
-rw-r--r--arch/mips/TODO10
-rw-r--r--arch/mips/bios32.c8
-rw-r--r--arch/mips/boot/Makefile47
-rw-r--r--arch/mips/boot/a.out.c254
-rw-r--r--arch/mips/boot/compressed/Makefile45
-rw-r--r--arch/mips/boot/compressed/cache.S162
-rw-r--r--arch/mips/boot/compressed/crypt.h12
-rw-r--r--arch/mips/boot/compressed/gzip.h284
-rw-r--r--arch/mips/boot/compressed/head.S52
-rw-r--r--arch/mips/boot/compressed/inflate.c810
-rw-r--r--arch/mips/boot/compressed/lzw.h42
-rw-r--r--arch/mips/boot/compressed/misc.c438
-rw-r--r--arch/mips/boot/compressed/piggyback.c81
-rw-r--r--arch/mips/boot/compressed/unzip.c180
-rw-r--r--arch/mips/boot/compressed/xtract.c86
-rw-r--r--arch/mips/boot/head.S387
-rw-r--r--arch/mips/boot/loader.h24
-rw-r--r--arch/mips/boot/milo.c316
-rw-r--r--arch/mips/config.in188
-rw-r--r--arch/mips/dummy.c17
-rw-r--r--arch/mips/entry.S665
-rw-r--r--arch/mips/ioport.c20
-rw-r--r--arch/mips/irq.S642
-rw-r--r--arch/mips/irq.c292
-rw-r--r--arch/mips/kernel/Makefile94
-rw-r--r--arch/mips/kernel/bios32.c7
-rw-r--r--arch/mips/kernel/entry.S625
-rw-r--r--arch/mips/kernel/gdb-low.S300
-rw-r--r--arch/mips/kernel/gdb-stub.c748
-rw-r--r--arch/mips/kernel/head.S412
-rw-r--r--arch/mips/kernel/ioport.c36
-rw-r--r--arch/mips/kernel/irq.c334
-rw-r--r--arch/mips/kernel/jazzdma.c518
-rw-r--r--arch/mips/kernel/magnum4000.S261
-rw-r--r--arch/mips/kernel/pica.S252
-rw-r--r--arch/mips/kernel/process.c216
-rw-r--r--arch/mips/kernel/ptrace.c (renamed from arch/mips/ptrace.c)86
-rw-r--r--arch/mips/kernel/r4xx0.S732
-rw-r--r--arch/mips/kernel/setup.c207
-rw-r--r--arch/mips/kernel/signal.c (renamed from arch/mips/signal.c)267
-rw-r--r--arch/mips/kernel/traps.c438
-rw-r--r--arch/mips/kernel/tyne.S114
-rw-r--r--arch/mips/kernel/tynedma.c36
-rw-r--r--arch/mips/kernel/vm86.c (renamed from arch/mips/vm86.c)0
-rw-r--r--arch/mips/ldt.c13
-rw-r--r--arch/mips/lib/Makefile33
-rw-r--r--arch/mips/lib/dump_tlb.c161
-rw-r--r--arch/mips/lib/tinycon.c133
-rw-r--r--arch/mips/lib/watch.S102
-rw-r--r--arch/mips/main.c333
-rw-r--r--arch/mips/mkdisk5
-rw-r--r--arch/mips/mm/Makefile6
-rw-r--r--arch/mips/mm/fault.c92
-rw-r--r--arch/mips/mm/init.c299
-rw-r--r--arch/mips/mm/kmalloc.c362
-rw-r--r--arch/mips/mm/memory.c1295
-rw-r--r--arch/mips/mm/mmap.c470
-rw-r--r--arch/mips/mm/mprotect.c230
-rw-r--r--arch/mips/mm/swap.c986
-rw-r--r--arch/mips/mm/vmalloc.c202
-rw-r--r--arch/mips/sched.c804
-rw-r--r--arch/mips/splx.c30
-rw-r--r--arch/mips/traps.c135
-rw-r--r--arch/sparc/Makefile33
-rw-r--r--arch/sparc/config.in246
-rw-r--r--arch/sparc/kernel/Makefile47
-rw-r--r--arch/sparc/kernel/entry.S927
-rw-r--r--arch/sparc/kernel/head.S1045
-rw-r--r--arch/sparc/kernel/idprom.c183
-rw-r--r--arch/sparc/kernel/ioport.c12
-rw-r--r--arch/sparc/kernel/irq.c335
-rw-r--r--arch/sparc/kernel/probe.c432
-rw-r--r--arch/sparc/kernel/process.c112
-rw-r--r--arch/sparc/kernel/promops.c107
-rw-r--r--arch/sparc/kernel/setup.c120
-rw-r--r--arch/sparc/kernel/signal.c71
-rw-r--r--arch/sparc/kernel/traps.c47
-rw-r--r--arch/sparc/lib/COPYING.LIB481
-rw-r--r--arch/sparc/lib/Makefile48
-rw-r--r--arch/sparc/lib/ashrdi3.S28
-rw-r--r--arch/sparc/lib/mul.S127
-rw-r--r--arch/sparc/lib/rem.S359
-rw-r--r--arch/sparc/lib/sdiv.S363
-rw-r--r--arch/sparc/lib/udiv.S346
-rw-r--r--arch/sparc/lib/umul.S158
-rw-r--r--arch/sparc/lib/urem.S344
-rw-r--r--arch/sparc/mm/Makefile32
-rw-r--r--arch/sparc/mm/fault.c173
-rw-r--r--arch/sparc/mm/init.c364
-rw-r--r--arch/sparc/mm/vac-flush.c94
209 files changed, 38899 insertions, 13852 deletions
diff --git a/arch/alpha/Makefile b/arch/alpha/Makefile
index 35800431a..beff9f4ed 100644
--- a/arch/alpha/Makefile
+++ b/arch/alpha/Makefile
@@ -8,70 +8,28 @@
# Copyright (C) 1994 by Linus Torvalds
#
-AS =as
-LD =ld
-HOSTCC =gcc -I$(TOPDIR)/include
-CC =gcc -D__KERNEL__ -I$(TOPDIR)/include
-MAKE =make
-CPP =$(CC) -E
-AR =ar
-STRIP =strip
+NM := nm -B
-zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem
- $(MAKE) -C zBoot
+LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N
+CFLAGS := $(CFLAGS) -mno-fp-regs
-zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build
- tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage
- sync
+HEAD := arch/alpha/kernel/head.o
-zdisk: zImage
- dd bs=8192 if=zImage of=/dev/fd0
+SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib
+ARCHIVES := arch/alpha/kernel/kernel.o arch/alpha/mm/mm.o $(ARCHIVES)
+LIBS := $(TOPDIR)/arch/alpha/lib/lib.a $(LIBS) $(TOPDIR)/arch/alpha/lib/lib.a
-zlilo: $(CONFIGURE) zImage
- if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
- if [ -f $(INSTALL_PATH)/zSystem.map ]; then mv $(INSTALL_PATH)/zSystem.map $(INSTALL_PATH)/zSystem.old; fi
- cat zImage > $(INSTALL_PATH)/vmlinuz
- cp zSystem.map $(INSTALL_PATH)/
- if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
-
-tools/system: boot/head.o init/main.o tools/version.o linuxsubdirs
- $(LD) $(LOWLDFLAGS) boot/head.o init/main.o tools/version.o \
- $(ARCHIVES) \
- $(FILESYSTEMS) \
- $(DRIVERS) \
- $(LIBS) \
- -o tools/system
- nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
- sort > System.map
-
-boot/setup.o: boot/setup.s
- $(AS) -o $@ $<
-
-boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile
- $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
-
-boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile
- $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
-
-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) \
- -o tools/zSystem
- nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
- sort > zSystem.map
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
#
-# Leave these dummy entries for now to tell people that they are going away..
+# my boot writes directly to a specific disk partition, I doubt most
+# people will want to do that without changes..
#
-lilo:
- @echo
- @echo Uncompressed kernel images no longer supported. Use
- @echo \"make zlilo\" instead.
- @echo
- @exit 1
+msb my-special-boot:
+ @$(MAKEBOOT) msb
archclean:
- rm -f boot/bootsect boot/setup
+ @$(MAKEBOOT) clean
+
+archdep:
+ @$(MAKEBOOT) dep
diff --git a/arch/alpha/boot/Makefile b/arch/alpha/boot/Makefile
new file mode 100644
index 000000000..b05047c2a
--- /dev/null
+++ b/arch/alpha/boot/Makefile
@@ -0,0 +1,53 @@
+#
+# arch/alpha/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) 1994 by Linus Torvalds
+#
+
+.c.s:
+ $(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $<
+.S.s:
+ $(CC) -D__ASSEMBLY__ -traditional -E -o $*.o $<
+.S.o:
+ $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $<
+
+OBJECTS = head.o main.o
+
+all: tools/lxboot tools/bootlx vmlinux
+ @echo run mkfloppy on machine with floppy drive
+
+msb: tools/lxboot tools/bootlx vmlinux
+ ( cat tools/lxboot tools/bootlx vmlinux ) > /dev/rz0a
+ disklabel -rw rz0 'linux' tools/lxboot tools/bootlx
+
+vmlinux: tools/build $(TOPDIR)/vmlinux
+ tools/build -v $(TOPDIR)/vmlinux > vmlinux
+
+tools/lxboot: tools/build
+ tools/build > tools/lxboot
+
+tools/bootlx: bootloader tools/build
+ tools/build -vb bootloader > tools/bootlx
+
+tools/build: tools/build.c
+ $(HOSTCC) tools/build.c -o tools/build
+
+bootloader: $(OBJECTS)
+ $(LD) -non_shared -T 0x20000000 -N \
+ $(OBJECTS) \
+ $(LIBS) \
+ -o bootloader || \
+ (rm -f bootloader && exit 1)
+
+clean:
+ rm -f vmlinux bootloader tools/build tools/bootlx tools/lxboot
+
+dep:
diff --git a/arch/alpha/boot/head.S b/arch/alpha/boot/head.S
index 03a2a9010..efca165e6 100644
--- a/arch/alpha/boot/head.S
+++ b/arch/alpha/boot/head.S
@@ -1,44 +1,23 @@
/*
- * alpha/head.S
+ * arch/alpha/boot/head.S
*
- * initial boot stuff..
+ * initial bootloader stuff..
*/
#include <asm/system.h>
#define halt .long PAL_halt
-/*
- * NOTE! The console bootstrap will load us at 0x20000000, but this image
- * is linked to run at START_ADDR, so the first thing we do is to move
- * ourself up to the right address.. We'd better be position-independent
- * at that stage :-)
- */
.set noreorder
.globl __start
.ent __start
__start:
bis $31,$31,$31
- br $1,$200
- .long START_ADDR, START_ADDR >> 32 /* strange bug in the assembler.. duh */
- .long START_SIZE, START_SIZE >> 32
-$200: ldq $30,0($1) /* new stack - below this */
- lda $2,-8($1) /* __start */
- bis $30,$30,$3 /* new address */
- subq $3,$2,$6 /* difference */
- ldq $4,8($1) /* size */
-$201: subq $4,8,$4
- ldq $5,0($2)
- addq $2,8,$2
- stq $5,0($3)
- addq $3,8,$3
- bne $4,$201
- br $1,$202
-$202: addq $1,$6,$1
- addq $1,12,$1 /* $203 in the new address space */
- jmp $31,($1),$203
-$203: br $27,$100
-$100: ldgp $29,0($27)
+ br 1f
+ /* room for the initial PCB, which comes here */
+ .quad 0,0,0,0,0,0,0,0
+1: br $27,2f
+2: ldgp $29,0($27)
lda $27,start_kernel
jsr $26,($27),start_kernel
halt
@@ -109,3 +88,44 @@ switch_to_osf_pal:
__do_swppal:
.long PAL_swppal
.end switch_to_osf_pal
+
+.globl dispatch
+.ent dispatch
+dispatch:
+ subq $30,80,$30
+ stq $26,0($30)
+ stq $29,8($30)
+
+ stq $8,16($30)
+ stq $9,24($30)
+ stq $10,32($30)
+ stq $11,40($30)
+ stq $12,48($30)
+ stq $13,56($30)
+ stq $14,64($30)
+ stq $15,72($30)
+
+ lda $1,0x10000000 /* hwrpb */
+ ldq $2,0xc0($1) /* crb offset */
+ addq $2,$1,$2 /* crb */
+ ldq $27,0($2) /* dispatch procedure value */
+
+ ldq $2,8($27) /* dispatch call address */
+ jsr $26,($2) /* call it (weird VMS call seq) */
+
+ ldq $26,0($30)
+ ldq $29,8($30)
+
+ ldq $8,16($30)
+ ldq $9,24($30)
+ ldq $10,32($30)
+ ldq $11,40($30)
+ ldq $12,48($30)
+ ldq $13,56($30)
+ ldq $14,64($30)
+ ldq $15,72($30)
+
+ addq $30,80,$30
+ ret $31,($26)
+.end dispatch
+
diff --git a/arch/alpha/boot/main.c b/arch/alpha/boot/main.c
new file mode 100644
index 000000000..d122eb6f6
--- /dev/null
+++ b/arch/alpha/boot/main.c
@@ -0,0 +1,217 @@
+/*
+ * arch/alpha/boot/main.c
+ *
+ * Copyright (C) 1994, 1995 Linus Torvalds
+ *
+ * This file is the bootloader for the Linux/AXP kernel
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/version.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/console.h>
+#include <asm/hwrpb.h>
+
+#include <stdarg.h>
+
+extern int vsprintf(char *, const char *, va_list);
+extern unsigned long switch_to_osf_pal(unsigned long nr,
+ struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa,
+ unsigned long vptb, unsigned long *kstk);
+
+int printk(const char * fmt, ...)
+{
+ va_list args;
+ int i;
+ static char buf[1024];
+
+ va_start(args, fmt);
+ i = vsprintf(buf, fmt, args);
+ va_end(args);
+ puts(buf,i);
+ return i;
+}
+
+#define hwrpb (*INIT_HWRPB)
+
+/*
+ * Find a physical address of a virtual object..
+ *
+ * This is easy using the virtual page table address.
+ */
+struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb)
+{
+ unsigned long address = (unsigned long) pcb;
+ unsigned long result;
+
+ result = vptb[address >> 13];
+ result >>= 32;
+ result <<= 13;
+ result |= address & 0x1fff;
+ return (struct pcb_struct *) result;
+}
+
+/*
+ * This function moves into OSF/1 pal-code, and has a temporary
+ * PCB for that. The kernel proper should replace this PCB with
+ * the real one as soon as possible.
+ *
+ * The page table muckery in here depends on the fact that the boot
+ * code has the L1 page table identity-map itself in the second PTE
+ * in the L1 page table. Thus the L1-page is virtually addressable
+ * itself (through three levels) at virtual address 0x200802000.
+ *
+ * As we don't want it there anyway, we also move the L1 self-map
+ * up as high as we can, so that the last entry in the L1 page table
+ * maps the page tables.
+ *
+ * As a result, the OSF/1 pal-code will instead use a virtual page table
+ * map located at 0xffffffe00000000.
+ */
+#define pcb_va ((struct pcb_struct *) 0x20000000)
+#define old_vptb (0x0000000200000000UL)
+#define new_vptb (0xfffffffe00000000UL)
+void pal_init(void)
+{
+ unsigned long i, rev, sum;
+ unsigned long *L1, *l;
+ struct percpu_struct * percpu;
+ struct pcb_struct * pcb_pa;
+
+ /* Find the level 1 page table and duplicate it in high memory */
+ L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */
+ L1[1023] = L1[1];
+
+ percpu = (struct percpu_struct *) (hwrpb.processor_offset + (unsigned long) &hwrpb),
+
+ pcb_va->ksp = 0;
+ pcb_va->usp = 0;
+ pcb_va->ptbr = L1[1] >> 32;
+ pcb_va->asn = 0;
+ pcb_va->pcc = 0;
+ pcb_va->unique = 0;
+ pcb_va->flags = 1;
+ pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va);
+ printk("Switching to OSF PAL-code .. ");
+ /*
+ * a0 = 2 (OSF)
+ * a1 = return address, but we give the asm the virtual addr of the PCB
+ * a2 = physical addr of PCB
+ * a3 = new virtual page table pointer
+ * a4 = KSP (but we give it 0, asm sets it)
+ */
+ i = switch_to_osf_pal(
+ 2,
+ pcb_va,
+ pcb_pa,
+ new_vptb,
+ 0);
+ if (i) {
+ printk("failed, code %ld\n", i);
+ halt();
+ }
+ rev = percpu->pal_revision = percpu->palcode_avail[2];
+
+ hwrpb.vptb = new_vptb;
+
+ /* update checksum: */
+ sum = 0;
+ for (l = (unsigned long *) &hwrpb; l < (unsigned long *) &hwrpb.chksum; ++l)
+ sum += *l;
+ hwrpb.chksum = sum;
+
+ printk("Ok (rev %lx)\n", rev);
+ /* remove the old virtual page-table mapping */
+ L1[1] = 0;
+ invalidate_all();
+}
+
+extern int _end;
+
+static inline long openboot(void)
+{
+ char bootdev[256];
+ long result;
+
+ result = dispatch(CCB_GET_ENV, ENV_BOOTED_DEV, bootdev, 255);
+ if (result < 0)
+ return result;
+ return dispatch(CCB_OPEN, bootdev, result & 255);
+}
+
+static inline long close(long dev)
+{
+ return dispatch(CCB_CLOSE, dev);
+}
+
+static inline long load(long dev, unsigned long addr, unsigned long count)
+{
+ char bootfile[256];
+ long result;
+
+ result = dispatch(CCB_GET_ENV, ENV_BOOTED_FILE, bootfile, 255);
+ if (result < 0)
+ return result;
+ result &= 255;
+ bootfile[result] = '\0';
+ if (result)
+ printk("Boot file specification (%s) not implemented\n", bootfile);
+ return dispatch(CCB_READ, dev, count, addr, BOOT_SIZE/512 + 1);
+}
+
+/*
+ * Start the kernel.
+ */
+static void runkernel(void)
+{
+ __asm__ __volatile__(
+ "bis %1,%1,$30\n\t"
+ "bis %0,%0,$26\n\t"
+ "ret ($26)"
+ : /* no outputs: it doesn't even return */
+ : "r" (START_ADDR),
+ "r" (PAGE_SIZE + INIT_STACK));
+}
+
+void start_kernel(void)
+{
+ long i;
+ long dev;
+ int nbytes;
+ char envval[256];
+
+ printk("Linux/AXP bootloader for Linux " UTS_RELEASE "\n");
+ if (hwrpb.pagesize != 8192) {
+ printk("Expected 8kB pages, got %ldkB\n", hwrpb.pagesize >> 10);
+ return;
+ }
+ pal_init();
+ dev = openboot();
+ if (dev < 0) {
+ printk("Unable to open boot device: %016lx\n", dev);
+ return;
+ }
+ dev &= 0xffffffff;
+ printk("Loading vmlinux ...");
+ i = load(dev, START_ADDR, START_SIZE);
+ close(dev);
+ if (i != START_SIZE) {
+ printk("Failed (%lx)\n", i);
+ return;
+ }
+
+ nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS,
+ envval, sizeof(envval));
+ if (nbytes > 0) {
+ envval[nbytes] = '\0';
+ strcpy((char*)ZERO_PGE, envval);
+ }
+
+ printk(" Ok\nNow booting the kernel\n");
+ runkernel();
+ for (i = 0 ; i < 0x100000000 ; i++)
+ /* nothing */;
+ halt();
+}
diff --git a/arch/alpha/boot/tools/build.c b/arch/alpha/boot/tools/build.c
new file mode 100644
index 000000000..040655a54
--- /dev/null
+++ b/arch/alpha/boot/tools/build.c
@@ -0,0 +1,173 @@
+/*
+ * arch/alpha/boot/tools/build.c
+ *
+ * Build a bootable image from the vmlinux binary
+ */
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <a.out.h>
+
+#include <asm/system.h>
+
+#define MAXSECT 10
+#define MAXBUF 8192
+
+int verbose = 0;
+int pad = 0;
+char * program = "tools/build";
+char buffer[MAXBUF];
+unsigned long bootblock[64];
+struct filehdr fhdr;
+struct aouthdr ahdr;
+struct scnhdr shdr[MAXSECT];
+
+char * usage = "'build [-b] system > secondary' or 'build > primary'";
+
+static void die(char * str)
+{
+ fprintf(stderr,"%s: %s\n", program, str);
+ exit(1);
+}
+
+static int comp(struct scnhdr * a, struct scnhdr * b)
+{
+ return a->s_vaddr - b->s_vaddr;
+}
+
+int main(int argc, char ** argv)
+{
+ int fd, i;
+ unsigned long tmp, start;
+ unsigned long system_start, system_size;
+ char * infile = NULL;
+
+ system_start = START_ADDR;
+ system_size = START_SIZE;
+ if (argc) {
+ program = *(argv++);
+ argc--;
+ }
+ while (argc > 0) {
+ if (**argv == '-') {
+ while (*++*argv) {
+ switch (**argv) {
+ case 'b':
+ system_start = BOOT_ADDR;
+ system_size = BOOT_SIZE;
+ pad = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ die(usage);
+ }
+ }
+ } else if (infile)
+ die(usage);
+ else
+ infile = *argv;
+ argv++;
+ argc--;
+ }
+ if (!infile) {
+ memcpy(bootblock, "Linux Test", 10);
+ bootblock[60] = BOOT_SIZE / 512; /* count */
+ bootblock[61] = 1; /* starting LBM */
+ bootblock[62] = 0; /* flags */
+ tmp = 0;
+ for (i = 0 ; i < 63 ; i++)
+ tmp += bootblock[i];
+ bootblock[63] = tmp;
+ if (write(1, (char *) bootblock, 512) != 512) {
+ perror("bbwrite");
+ exit(1);
+ }
+ return 0;
+ }
+ fd = open(infile, O_RDONLY);
+ if (fd < 0) {
+ perror(infile);
+ exit(1);
+ }
+ if (read(fd, &fhdr, sizeof(struct filehdr)) != sizeof(struct filehdr))
+ die("unable to read file header");
+ if (fhdr.f_nscns > MAXSECT)
+ die("Too many sections");
+ if (fhdr.f_opthdr != AOUTHSZ)
+ die("optional header doesn't look like a.out");
+ if (read(fd, &ahdr, sizeof(struct aouthdr)) != sizeof(struct aouthdr))
+ die("unable to read a.out header");
+ for (i = 0 ; i < fhdr.f_nscns ; i++) {
+ if (read(fd, i+shdr, sizeof(struct scnhdr)) != sizeof(struct scnhdr))
+ die("unable to read section header");
+ if (shdr[i].s_paddr != shdr[i].s_vaddr)
+ die("unable to handle different phys/virt addresses");
+ if (shdr[i].s_relptr)
+ die("Unable to handle relocation info");
+ if (verbose) {
+ fprintf(stderr, "section %d (%.8s):\t%lx - %lx (at %x)\n",
+ i, shdr[i].s_name,
+ shdr[i].s_vaddr,
+ shdr[i].s_vaddr + shdr[i].s_size,
+ shdr[i].s_scnptr);
+ }
+ }
+ qsort(shdr, fhdr.f_nscns, sizeof(shdr[1]), comp);
+ start = system_start;
+ for (i = 0 ; i < fhdr.f_nscns ; i++) {
+ unsigned long size, offset;
+ memset(buffer, 0, MAXBUF);
+ if (!strcmp(shdr[i].s_name, ".comment"))
+ continue;
+ if (shdr[i].s_vaddr != start)
+ die("Unordered or badly placed segments");
+ size = shdr[i].s_size;
+ start += size;
+ offset = shdr[i].s_scnptr;
+ if (lseek(fd, offset, SEEK_SET) != offset)
+ die("Unable to seek in in-file");
+ while (size > 0) {
+ unsigned long num = size;
+ if (num > MAXBUF)
+ num = MAXBUF;
+ if (offset)
+ if (read(fd, buffer, num) != num)
+ die("partial read");
+ if (write(1, buffer, num) != num)
+ die("partial write");
+ size -= num;
+ }
+ if (verbose) {
+ fprintf(stderr, "section %d (%.8s):\t%lx - %lx (at %x)\n",
+ i, shdr[i].s_name,
+ shdr[i].s_vaddr,
+ shdr[i].s_vaddr + shdr[i].s_size,
+ shdr[i].s_scnptr);
+ }
+ }
+ if (start > system_start + system_size) {
+ fprintf(stderr, "Boot image too large\n");
+ exit(1);
+ }
+ if (pad) {
+ unsigned long count = (system_start + system_size) - start;
+ memset(buffer, 0, MAXBUF);
+ while (count > 0) {
+ int i = MAXBUF;
+ if (i > count)
+ i = count;
+ i = write(1, buffer, i);
+ if (i <= 0) {
+ perror("pad write");
+ exit(1);
+ }
+ count -= i;
+ }
+ }
+
+ return 0;
+}
diff --git a/arch/alpha/config.in b/arch/alpha/config.in
index fef727364..8906e8c8c 100644
--- a/arch/alpha/config.in
+++ b/arch/alpha/config.in
@@ -5,17 +5,38 @@
comment 'General setup'
-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 drivers/block/README.ide for help/info on IDE drives'
+ bool ' Use old (reliable) disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n
+ 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 y
+ fi
+ if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+ bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n
+ fi
+fi
+
bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
bool 'Networking support' CONFIG_NET y
-bool 'System V IPC' CONFIG_SYSVIPC y
+bool 'PCI alpha motherboard' CONFIG_PCI n
+bool 'System V IPC' CONFIG_SYSVIPC n
bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+comment 'Loadable module support'
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
+
if [ "$CONFIG_NET" = "y" ]; then
comment 'Networking options'
bool 'TCP/IP networking' CONFIG_INET y
-if [ "$CONFIG_INET" "=" "y" ]; then
+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
comment '(it is safe to leave these untouched)'
bool 'PC/TCP compatibility mode' CONFIG_INET_PCTCP n
bool 'Reverse ARP' CONFIG_INET_RARP n
@@ -23,6 +44,7 @@ bool 'Assume subnets are local' CONFIG_INET_SNARL y
bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
fi
bool 'The IPX protocol' CONFIG_IPX n
+#bool 'Appletalk DDP' CONFIG_ATALK n
#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
fi
@@ -38,10 +60,10 @@ 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 n
-bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR n
-bool 'Scsi generic support' CONFIG_CHR_DEV_SG n
+bool 'SCSI disk support' CONFIG_BLK_DEV_SD y
+bool 'SCSI tape support' CONFIG_CHR_DEV_ST n
+bool 'SCSI CDROM support' CONFIG_BLK_DEV_SR y
+bool 'SCSI generic support' CONFIG_CHR_DEV_SG n
comment 'SCSI low-level drivers'
@@ -50,18 +72,21 @@ bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n
bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 y
bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n
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
-bool 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 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
bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n
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
@@ -79,18 +104,19 @@ else
bool 'Dummy net driver support' CONFIG_DUMMY n
bool 'SLIP (serial line) support' CONFIG_SLIP n
if [ "$CONFIG_SLIP" = "y" ]; then
- bool ' CSLIP compressed headers' SL_COMPRESSED y
+ bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED y
+ bool ' 16 channels instead of 4' SL_SLIP_LOTS n
# bool ' SLIP debugging on' SL_DUMP y
fi
bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP 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 n
if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
bool 'WD80*3 support' CONFIG_WD80x3 n
bool 'SMC Ultra support' CONFIG_ULTRA n
fi
+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
@@ -101,42 +127,53 @@ if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
fi
bool '3c509/3c579 support' CONFIG_EL3 y
fi
-bool 'Other ISA cards' CONFIG_NET_ISA y
+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 'Cabletron E21xx support' CONFIG_E2100 n
bool 'DEPCA support' CONFIG_DEPCA y
bool 'EtherWorks 3 support' CONFIG_EWRK3 n
if [ "$CONFIG_NET_ALPHA" = "y" ]; then
- bool 'EtherExpress support' CONFIG_EEXPRESS n
+ bool 'Arcnet support' CONFIG_ARCNET 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 n
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 'DE425, DE434, DE435 support' CONFIG_DE4X5 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 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
fi
fi
-comment 'CD-ROM drivers'
+comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
-bool 'Mitsumi CDROM driver support' CONFIG_MCD n
+bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
if [ "$CONFIG_SBPCD" = "y" ]; then
bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
@@ -147,6 +184,8 @@ if [ "$CONFIG_SBPCD" = "y" ]; then
fi
fi
fi
+bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
+bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
comment 'Filesystems'
@@ -162,7 +201,7 @@ bool '/proc filesystem support' CONFIG_PROC_FS y
if [ "$CONFIG_INET" = "y" ]; then
bool 'NFS filesystem support' CONFIG_NFS_FS y
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
+if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
else
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
@@ -172,6 +211,7 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
comment 'character devices'
+bool 'Cyclades async mux support' CONFIG_CYCLADES n
bool 'Parallel printer support' CONFIG_PRINTER n
bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE y
@@ -180,7 +220,7 @@ bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
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
@@ -210,6 +250,9 @@ comment 'Kernel hacking'
#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/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
new file mode 100644
index 000000000..2843d4481
--- /dev/null
+++ b/arch/alpha/kernel/Makefile
@@ -0,0 +1,47 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.S.s:
+ $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
+.S.o:
+ $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+
+OBJS = entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \
+ lca.o bios32.o
+
+all: kernel.o head.o
+
+head.o: head.s
+
+head.s: head.S $(TOPDIR)/include/asm-alpha/system.h
+ $(CPP) -traditional -o $*.s $<
+
+kernel.o: $(OBJS)
+ $(LD) -r -o kernel.o $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+
diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c
new file mode 100644
index 000000000..37c566fb1
--- /dev/null
+++ b/arch/alpha/kernel/bios32.c
@@ -0,0 +1,478 @@
+#define DEBUG
+/*
+ * bios32.c - PCI BIOS functions for Alpha systems not using BIOS
+ * emulation code.
+ *
+ * Written by Dave Rusling (david.rusling@reo.mts.dec.com)
+ *
+ * Adapted to 64-bit kernel and then rewritten by David Mosberger
+ * (davidm@cs.arizona.edu)
+ *
+ * For more information, please consult
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ *
+ * Manuals are $25 each or $50 for all three, plus $7 shipping
+ * within the United States, $35 abroad.
+ */
+#include <linux/config.h>
+
+#ifndef CONFIG_PCI
+
+int pcibios_present(void)
+{
+ return 0;
+}
+
+#else /* CONFIG_PCI */
+
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <asm/hwrpb.h>
+#include <asm/io.h>
+
+
+#define KB 1024
+#define MB (1024*KB)
+#define GB (1024*MB)
+
+#define MAJOR_REV 0
+#define MINOR_REV 2
+
+/*
+ * Align VAL to ALIGN, which must be a power of two.
+ */
+#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
+
+
+/*
+ * Temporary internal macro. If this 0, then do not write to any of
+ * the PCI registers, merely read them (i.e., use configuration as
+ * determined by SRM). The SRM seem do be doing a less than perfect
+ * job in configuring PCI devices, so for now we do it ourselves.
+ * Reconfiguring PCI devices breaks console (RPB) callbacks, but
+ * those don't work properly with 64 bit addresses anyways.
+ *
+ * The accepted convention seems to be that the console (POST
+ * software) should fully configure boot devices and configure the
+ * interrupt routing of *all* devices. In particular, the base
+ * addresses of non-boot devices need not be initialized. For
+ * example, on the AXPpci33 board, the base address a #9 GXE PCI
+ * graphics card reads as zero (this may, however, be due to a bug in
+ * the graphics card---there have been some rumor that the #9 BIOS
+ * incorrectly resets that address to 0...).
+ */
+#define PCI_MODIFY 1
+
+
+extern struct hwrpb_struct *hwrpb;
+
+
+#if PCI_MODIFY
+
+static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */
+static unsigned int mem_base = 16*MB; /* <16MB is ISA memory */
+
+
+/*
+ * Layout memory and I/O for a device:
+ */
+static void layout_dev(struct pci_dev *dev)
+{
+ struct pci_bus *bus;
+ unsigned short cmd;
+ unsigned int base, mask, size, reg;
+
+ bus = dev->bus;
+ pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd);
+
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ /*
+ * Figure out how much space and of what type this
+ * device wants.
+ */
+ pcibios_write_config_dword(bus->number, dev->devfn, reg,
+ 0xffffffff);
+ pcibios_read_config_dword(bus->number, dev->devfn, reg, &base);
+ if (!base) {
+ break; /* done with this device */
+ }
+ /*
+ * We've read the base address register back after
+ * writing all ones and so now we must decode it.
+ */
+ if (base & PCI_BASE_ADDRESS_SPACE_IO) {
+ /*
+ * I/O space base address register.
+ */
+ cmd |= PCI_COMMAND_IO;
+
+ base &= PCI_BASE_ADDRESS_IO_MASK;
+ mask = (~base << 1) | 0x1;
+ size = (mask & base) & 0xffffffff;
+ base = ALIGN(io_base, size);
+ io_base = base + size;
+ pcibios_write_config_dword(bus->number, dev->devfn,
+ reg, base | 0x1);
+ } else {
+ unsigned int type;
+ /*
+ * Memory space base address register.
+ */
+ cmd |= PCI_COMMAND_MEMORY;
+
+ type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ mask = (~base << 1) | 0x1;
+ size = (mask & base) & 0xffffffff;
+ switch (type) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ break;
+
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ printk("bios32 WARNING: "
+ "ignoring 64-bit device in "
+ "slot %d, function %d: \n",
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ reg += 4; /* skip extra 4 bytes */
+ continue;
+
+ case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+ /*
+ * Allocating memory below 1MB is *very*
+ * tricky, as there may be all kinds of
+ * ISA devices lurking that we don't know
+ * about. For now, we just cross fingers
+ * and hope nobody tries to do this on an
+ * Alpha (or that the console has set it
+ * up properly).
+ */
+ printk("bios32 WARNING: slot %d, function %d "
+ "requests memory below 1MB---don't "
+ "know how to do that.\n",
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ continue;
+ }
+ /*
+ * The following holds at least for the Low Cost
+ * Alpha implementation of the PCI interface:
+ *
+ * In sparse memory address space, the first
+ * octant (16MB) of every 128MB segment is
+ * aliased to the the very first 16MB of the
+ * address space (i.e., it aliases the ISA
+ * memory address space). Thus, we try to
+ * avoid allocating PCI devices in that range.
+ * Can be allocated in 2nd-7th octant only.
+ * Devices that need more than 112MB of
+ * address space must be accessed through
+ * dense memory space only!
+ */
+ base = ALIGN(mem_base, size);
+ if (size > 7 * 16*MB) {
+ printk("bios32 WARNING: slot %d, function %d "
+ "requests %dB of contiguous address "
+ " space---don't use sparse memory "
+ " accesses on this device!!\n",
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn), size);
+ } else {
+ if (((base / 16*MB) & 0x7) == 0) {
+ base &= ~(128*MB - 1);
+ base += 16*MB;
+ base = ALIGN(base, size);
+ }
+ if (base / 128*MB != (base + size) / 128*MB) {
+ base &= ~(128*MB - 1);
+ base += (128 + 16)*MB;
+ base = ALIGN(base, size);
+ }
+ }
+ mem_base = base + size;
+ pcibios_write_config_dword(bus->number, dev->devfn,
+ reg, base);
+ }
+ }
+ /* enable device: */
+ pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND,
+ cmd | PCI_COMMAND_MASTER);
+}
+
+
+static void layout_bus(struct pci_bus *bus)
+{
+ unsigned int l, tio, bio, tmem, bmem;
+ struct pci_bus *child;
+ struct pci_dev *dev;
+
+ if (!bus->devices && !bus->children)
+ return;
+
+ /*
+ * Align the current bases on appropriate boundaries (4K for
+ * IO and 1MB for memory).
+ */
+ bio = io_base = ALIGN(io_base, 4*KB);
+ bmem = mem_base = ALIGN(mem_base, 1*MB);
+
+ /*
+ * Allocate space to each device:
+ */
+ for (dev = bus->devices; dev; dev = dev->sibling) {
+ if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) {
+ layout_dev(dev);
+ }
+ }
+ /*
+ * Recursively allocate space for all of the sub-buses:
+ */
+ for (child = bus->children; child; child = child->next) {
+ layout_bus(child);
+ }
+ /*
+ * Align the current bases on 4K and 1MB boundaries:
+ */
+ tio = io_base = ALIGN(io_base, 4*KB);
+ tmem = mem_base = ALIGN(mem_base, 1*MB);
+
+ if (bus->self) {
+ struct pci_dev *bridge = bus->self;
+ /*
+ * Set up the top and bottom of the I/O memory segment
+ * for this bus.
+ */
+ pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
+ 0x1c, &l);
+ l = l | (bio >> 8) | ((tio - 1) & 0xf000);
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x1c, l);
+
+ l = ((bmem & 0xfff00000) >> 16) | ((tmem - 1) & 0xfff00000);
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x20, l);
+ /*
+ * Turn off downstream PF memory address range:
+ */
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x24, 0x0000ffff);
+ /*
+ * Tell bridge that there is an ISA bus in the system:
+ */
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x3c, 0x00040000);
+ /*
+ * Clear status bits, enable I/O (for downstream I/O),
+ * turn on master enable (for upstream I/O), turn on
+ * memory enable (for downstream memory), turn on
+ * master enable (for upstream memory and I/O).
+ */
+ pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
+ 0x4, 0xffff0007);
+ }
+}
+
+#endif /* !PCI_MODIFY */
+
+
+/*
+ * Given the vendor and device ids, find the n'th instance of that device
+ * in the system.
+ */
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus,
+ unsigned char *devfn)
+{
+ unsigned int current = 0;
+ struct pci_dev *dev;
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if (dev->vendor == vendor && dev->device == device_id) {
+ if (current == index) {
+ *devfn = dev->devfn;
+ *bus = dev->bus->number;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++current;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+ unsigned char *bus, unsigned char *devfn)
+{
+ unsigned int current = 0;
+ struct pci_dev *dev;
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ if (dev->class == class_code) {
+ if (current == index) {
+ *devfn = dev->devfn;
+ *bus = dev->bus->number;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++current;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+int pcibios_present(void)
+{
+ return 1;
+}
+
+
+unsigned long pcibios_init(unsigned long mem_start,
+ unsigned long mem_end)
+{
+ printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
+
+#if !PCI_MODIFY
+ printk("...NOT modifying existing (SRM) PCI configuration\n");
+#endif
+ return mem_start;
+}
+
+
+/*
+ * Fixup configuration for Noname boards (AXPpci33).
+ */
+static void noname_fixup(void)
+{
+ struct pci_dev *dev;
+
+ /*
+ * The Noname board has 5 PCI slots with each of the 4
+ * interrupt pins routed to different pins on the PCI/ISA
+ * bridge (PIRQ0-PIRQ3). I don't have any information yet as
+ * to how INTB, INTC, and INTD get routed (4/12/95,
+ * davidm@cs.arizona.edu).
+ */
+ static const char pirq_tab[5][4] = {
+ { 3, -1, -1, -1}, /* slot 6 (53c810) */
+ {-1, -1, -1, -1}, /* slot 7 (PCI/ISA bridge) */
+ { 2, -1, -1, -1}, /* slot 8 (slot closest to ISA) */
+ { 1, -1, -1, -1}, /* slot 9 (middle slot) */
+ { 0, -1, -1, -1}, /* slot 10 (slot furthest from ISA) */
+ };
+ /*
+ * route_tab selects irq routing in PCI/ISA bridge so that:
+ * PIRQ0 -> irq 15
+ * PIRQ1 -> irq 9
+ * PIRQ2 -> irq 10
+ * PIRQ3 -> irq 11
+ */
+ const unsigned int route_tab = 0x0b0a090f;
+ unsigned char pin;
+ int pirq;
+
+ pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab);
+
+ /* ensure irq 9, 10, 11, and 15 are level sensitive: */
+ outb((1<<(9-8)) | (1<<(10-8)) | (1<<(11-8)) | (1<<(15-8)), 0x4d1);
+
+ /*
+ * Go through all devices, fixing up irqs as we see fit:
+ */
+ for (dev = pci_devices; dev; dev = dev->next) {
+ dev->irq = 0;
+ if (dev->bus->number != 0 ||
+ PCI_SLOT(dev->devfn) < 6 || PCI_SLOT(dev->devfn) > 10)
+ {
+ printk("noname_set_irq: no dev on bus %d, slot %d!!\n",
+ dev->bus->number, PCI_SLOT(dev->devfn));
+ continue;
+ }
+
+ pcibios_read_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_PIN, &pin);
+ if (!pin) {
+ if (dev->vendor == PCI_VENDOR_ID_S3 &&
+ (dev->device == PCI_DEVICE_ID_S3_864_1 ||
+ dev->device == PCI_DEVICE_ID_S3_864_2))
+ {
+ pin = 1;
+ } else {
+ continue; /* no interrupt line */
+ }
+ }
+ pirq = pirq_tab[PCI_SLOT(dev->devfn) - 6][pin - 1];
+ if (pirq < 0) {
+ continue;
+ }
+ dev->irq = (route_tab >> (8 * pirq)) & 0xff;
+#if PCI_MODIFY
+ /* tell the device: */
+ pcibios_write_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_LINE, dev->irq);
+#endif
+ }
+
+#if PCI_MODIFY
+ {
+ unsigned char hostid;
+ /*
+ * SRM console version X3.9 seems to reset the SCSI
+ * host-id to 0 no matter what console environment
+ * variable pka0_host_id is set to. Thus, if the
+ * host-id reads out as a zero, we set it to 7. The
+ * SCSI controller is on the motherboard on bus 0,
+ * slot 6
+ */
+ if (pcibios_read_config_byte(0, PCI_DEVFN(6, 0), 0x84, &hostid)
+ == PCIBIOS_SUCCESSFUL && (hostid == 0))
+ {
+ pcibios_write_config_byte(0, PCI_DEVFN(6, 0),
+ 0x84, 7);
+ }
+ }
+#endif /* !PCI_MODIFY */
+}
+
+
+unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
+{
+#if PCI_MODIFY
+ /*
+ * Scan the tree, allocating PCI memory and I/O space.
+ */
+ layout_bus(&pci_root);
+#endif
+
+ /*
+ * Now is the time to do all those dirty little deeds...
+ */
+ switch (hwrpb->sys_type) {
+ case ST_DEC_AXPPCI_33: noname_fixup(); break;
+
+ default:
+ printk("pcibios_fixup: don't know how to fixup sys type %ld\n",
+ hwrpb->sys_type);
+ break;
+ }
+ return mem_start;
+}
+
+#endif /* CONFIG_PCI */
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
new file mode 100644
index 000000000..b54ea220b
--- /dev/null
+++ b/arch/alpha/kernel/entry.S
@@ -0,0 +1,554 @@
+/*
+ * alpha/entry.S
+ *
+ * kernel entry-points
+ */
+
+#include <asm/system.h>
+
+#define halt .long PAL_halt
+#define rti .long PAL_rti
+
+#define NR_SYSCALLS 310
+#define osf_vfork sys_fork
+
+/*
+ * These offsets must match with "struct hae" in io.h:
+ */
+#define HAE_CACHE 0
+#define HAE_REG 8
+
+/*
+ * stack offsets
+ */
+#define SP_OFF 160
+
+#define SWITCH_STACK_SIZE 320
+
+/*
+ * task structure offsets
+ */
+#define TASK_STATE 0
+#define TASK_COUNTER 8
+#define TASK_PRIORITY 16
+#define TASK_SIGNAL 24
+#define TASK_BLOCKED 32
+#define TASK_FLAGS 40
+
+/*
+ * This defines the normal kernel pt-regs layout.
+ *
+ * regs 9-15 preserved by C code
+ * regs 16-18 saved by PAL-code
+ * regs 29-30 saved and set up by PAL-code
+ */
+#define SAVE_ALL \
+ subq $30,160,$30; \
+ stq $0,0($30); \
+ stq $1,8($30); \
+ stq $2,16($30); \
+ stq $3,24($30); \
+ stq $4,32($30); \
+ stq $5,40($30); \
+ stq $6,48($30); \
+ stq $7,56($30); \
+ stq $8,64($30); \
+ stq $19,72($30); \
+ stq $20,80($30); \
+ stq $21,88($30); \
+ stq $22,96($30); \
+ stq $23,104($30); \
+ stq $24,112($30); \
+ stq $25,120($30); \
+ stq $26,128($30); \
+ stq $27,136($30); \
+ stq $28,144($30); \
+ lda $2,hae; \
+ ldq $2,HAE_CACHE($2); \
+ stq $2,152($30)
+
+#define RESTORE_ALL \
+ lda $8,hae; \
+ ldq $7,HAE_CACHE($8); \
+ ldq $6,152($30); \
+ subq $7,$6,$5; \
+ beq $5,99f; \
+ ldq $7,HAE_REG($8); \
+ addq $31,7,$16; \
+ call_pal PAL_swpipl; \
+ stq $6,HAE_CACHE($8); \
+ stq $6,0($7); \
+ mb; \
+ bis $0,$0,$16; \
+ call_pal PAL_swpipl; \
+99:; \
+ ldq $0,0($30); \
+ ldq $1,8($30); \
+ ldq $2,16($30); \
+ ldq $3,24($30); \
+ ldq $4,32($30); \
+ ldq $5,40($30); \
+ ldq $6,48($30); \
+ ldq $7,56($30); \
+ ldq $8,64($30); \
+ ldq $19,72($30); \
+ ldq $20,80($30); \
+ ldq $21,88($30); \
+ ldq $22,96($30); \
+ ldq $23,104($30); \
+ ldq $24,112($30); \
+ ldq $25,120($30); \
+ ldq $26,128($30); \
+ ldq $27,136($30); \
+ ldq $28,144($30); \
+ addq $30,160,$30
+
+.text
+.set noat
+
+.align 3
+.globl entInt
+.ent entInt
+entInt:
+ SAVE_ALL
+/* start atomic operation with respect to software interrupts */
+ lda $0,intr_count
+ ldq $1,0($0)
+ addq $1,1,$1
+ stq $1,0($0)
+/* set up the arguments to the C interrupt handler */
+ lda $27,do_entInt
+ jsr $26,($27),do_entInt
+/* ok, check if we need to do software interrupts */
+1: lda $0,intr_count
+ ldq $1,0($0)
+ subq $1,1,$1
+ bne $1,2f /* interrupt within interrupt: return now */
+ lda $2,bh_active
+ ldq $3,0($2)
+ lda $2,bh_mask
+ ldq $2,0($2)
+ and $2,$3,$2
+ bne $2,3f
+ stq $1,0($0)
+ br $31,ret_from_sys_call
+.align 3
+2: stq $1,0($0)
+ br $31,restore_all
+.align 3
+3: lda $27,do_bottom_half
+ jsr $26,($27),do_bottom_half
+ br $31,1b
+.end entInt
+
+.align 3
+.globl entMM
+.ent entMM
+entMM:
+ SAVE_ALL
+ lda $27,do_page_fault
+ lda $26,ret_from_sys_call
+ jsr $31,($27),do_page_fault
+.end entMM
+
+.align 3
+.globl entArith
+.ent entArith
+entArith:
+ SAVE_ALL
+ lda $27,do_entArith
+ lda $26,ret_from_sys_call
+ jsr $31,($27),do_entArith
+.end entArith
+
+.align 3
+.globl entIF
+.ent entIF
+entIF:
+ SAVE_ALL
+ lda $27,do_entIF
+ lda $26,ret_from_sys_call
+ jsr $31,($27),do_entIF
+.end entIF
+
+/*
+ * Fork() is one of the special system calls: it needs to
+ * save the callee-saved regs so that the regs can be found
+ * for the new process.. We save them in the "context switch"
+ * stack format (see arch/alpha/kernel/process.c).
+ *
+ * Also, for the kernel fork, we need to fake the system call
+ * stack buildup, as we can't do system calls from kernel space.
+ */
+.align 3
+.globl kernel_fork
+.ent kernel_fork
+kernel_fork:
+ subq $30,6*8,$30
+ stq $31,0($30)
+ stq $26,8($30)
+ stq $29,16($30)
+ stq $16,24($30)
+ stq $17,32($30)
+ stq $18,40($30)
+ SAVE_ALL
+ lda $27,sys_fork
+ jsr $26,($27),sys_fork
+ br ret_from_sys_call
+.end kernel_fork
+
+.align 3
+.ent do_switch_stack
+do_switch_stack:
+ lda $30,-SWITCH_STACK_SIZE($30)
+ stq $9,0($30)
+ stq $10,8($30)
+ stq $11,16($30)
+ stq $12,24($30)
+ stq $13,32($30)
+ stq $14,40($30)
+ stq $15,48($30)
+ stq $26,56($30)
+ stt $f0,64($30)
+ stt $f1,72($30)
+ stt $f2,80($30)
+ stt $f3,88($30)
+ stt $f4,96($30)
+ stt $f5,104($30)
+ stt $f6,112($30)
+ stt $f7,120($30)
+ stt $f8,128($30)
+ stt $f9,136($30)
+ stt $f10,144($30)
+ stt $f11,152($30)
+ stt $f12,160($30)
+ stt $f13,168($30)
+ stt $f14,176($30)
+ stt $f15,184($30)
+ stt $f16,192($30)
+ stt $f17,200($30)
+ stt $f18,208($30)
+ stt $f19,216($30)
+ stt $f20,224($30)
+ stt $f21,232($30)
+ stt $f22,240($30)
+ stt $f23,248($30)
+ stt $f24,256($30)
+ stt $f25,264($30)
+ stt $f26,272($30)
+ stt $f27,280($30)
+ stt $f28,288($30)
+ stt $f29,296($30)
+ stt $f30,304($30)
+ ret $31,($0),1
+.end do_switch_stack
+
+.align 3
+.ent undo_switch_stack
+undo_switch_stack:
+ ldq $9,0($30)
+ ldq $10,8($30)
+ ldq $11,16($30)
+ ldq $12,24($30)
+ ldq $13,32($30)
+ ldq $14,40($30)
+ ldq $15,48($30)
+ ldq $26,56($30)
+ ldt $f0,64($30)
+ ldt $f1,72($30)
+ ldt $f2,80($30)
+ ldt $f3,88($30)
+ ldt $f4,96($30)
+ ldt $f5,104($30)
+ ldt $f6,112($30)
+ ldt $f7,120($30)
+ ldt $f8,128($30)
+ ldt $f9,136($30)
+ ldt $f10,144($30)
+ ldt $f11,152($30)
+ ldt $f12,160($30)
+ ldt $f13,168($30)
+ ldt $f14,176($30)
+ ldt $f15,184($30)
+ ldt $f16,192($30)
+ ldt $f17,200($30)
+ ldt $f18,208($30)
+ ldt $f19,216($30)
+ ldt $f20,224($30)
+ ldt $f21,232($30)
+ ldt $f22,240($30)
+ ldt $f23,248($30)
+ ldt $f24,256($30)
+ ldt $f25,264($30)
+ ldt $f26,272($30)
+ ldt $f27,280($30)
+ ldt $f28,288($30)
+ ldt $f29,296($30)
+ ldt $f30,304($30)
+ lda $30,SWITCH_STACK_SIZE($30)
+ ret $31,($0),1
+.end undo_switch_stack
+
+.align 3
+.globl entUna
+.ent entUna
+entUna:
+ lda $30,-256($30)
+ stq $0,0($30)
+ stq $1,8($30)
+ stq $2,16($30)
+ stq $3,24($30)
+ stq $4,32($30)
+ stq $5,40($30)
+ stq $6,48($30)
+ stq $7,56($30)
+ stq $8,64($30)
+ stq $9,72($30)
+ stq $10,80($30)
+ stq $11,88($30)
+ stq $12,96($30)
+ stq $13,104($30)
+ stq $14,112($30)
+ stq $15,120($30)
+ /* 16-18 PAL-saved */
+ stq $19,152($30)
+ stq $20,160($30)
+ stq $21,168($30)
+ stq $22,176($30)
+ stq $23,184($30)
+ stq $24,192($30)
+ stq $25,200($30)
+ stq $26,208($30)
+ stq $27,216($30)
+ stq $28,224($30)
+ stq $29,232($30)
+ stq $30,240($30)
+ stq $31,248($30)
+ lda $27,do_entUna
+ jsr $26,($27),do_entUna
+ ldq $0,0($30)
+ ldq $1,8($30)
+ ldq $2,16($30)
+ ldq $3,24($30)
+ ldq $4,32($30)
+ ldq $5,40($30)
+ ldq $6,48($30)
+ ldq $7,56($30)
+ ldq $8,64($30)
+ ldq $9,72($30)
+ ldq $10,80($30)
+ ldq $11,88($30)
+ ldq $12,96($30)
+ ldq $13,104($30)
+ ldq $14,112($30)
+ ldq $15,120($30)
+ /* 16-18 PAL-saved */
+ ldq $19,152($30)
+ ldq $20,160($30)
+ ldq $21,168($30)
+ ldq $22,176($30)
+ ldq $23,184($30)
+ ldq $24,192($30)
+ ldq $25,200($30)
+ ldq $26,208($30)
+ ldq $27,216($30)
+ ldq $28,224($30)
+ ldq $29,232($30)
+ ldq $30,240($30)
+ lda $30,256($30)
+ rti
+.end entUna
+
+.align 3
+.globl sys_fork
+.ent sys_fork
+sys_fork:
+ br $0,do_switch_stack
+ bis $30,$30,$16
+ lda $27,alpha_fork
+ jsr $26,($27),alpha_fork
+ br $0,undo_switch_stack
+ ldq $0,0($30)
+ ret $31,($26),1
+.end sys_fork
+
+.align 3
+.globl alpha_switch_to
+.ent alpha_switch_to
+alpha_switch_to:
+ br $0,do_switch_stack
+ call_pal PAL_swpctx
+ br $0,undo_switch_stack
+ ret $31,($26),1
+.end alpha_switch_to
+
+/*
+ * Oh, well.. Disassembling OSF/1 binaries to find out how the
+ * system calls work isn't much fun.
+ *
+ * entSys is special in that the PAL-code doesn't save a0-a2, so
+ * we start off by doing that by hand.
+ */
+.align 3
+.globl entSys
+.globl ret_from_sys_call
+.ent entSys
+entSys:
+ stq $16,24($30)
+ stq $17,32($30)
+ stq $18,40($30)
+ SAVE_ALL
+ lda $1,NR_SYSCALLS($31)
+ lda $2,sys_call_table
+ lda $27,do_entSys
+ cmpult $0,$1,$1
+ s8addq $0,$2,$2
+ beq $1,1f
+ ldq $27,0($2)
+1: jsr $26,($27),do_entSys
+ bis $31,$31,$1
+ bge $0,2f
+ bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */
+ ldq $19,0($30) /* .. with this syscall nr */
+ ldq $20,72($30) /* .. and this a3 */
+ addq $31,1,$1 /* set a3 for errno return */
+ subq $31,$0,$0 /* with error in v0 */
+2: stq $0,0($30)
+ stq $1,72($30) /* a3 for return */
+.align 3
+ret_from_sys_call:
+ ldq $0,SP_OFF($30)
+ cmovne $26,0,$19
+ and $0,8,$0
+ beq $0,restore_all
+ret_from_reschedule:
+ lda $0,need_resched
+ lda $1,current
+ ldl $2,0($0)
+ lda $4,init_task
+ ldq $3,0($1)
+ bne $2,reschedule
+ subq $4,$3,$4
+ beq $4,restore_all
+ ldq $4,TASK_SIGNAL($3)
+ ldq $16,TASK_BLOCKED($3)
+ bic $4,$16,$4
+ bne $4,signal_return
+restore_all:
+ RESTORE_ALL
+ rti
+.align 3
+signal_return:
+ bis $30,$30,$17
+ br $0,do_switch_stack
+ bis $30,$30,$18
+ lda $27,do_signal
+ jsr $26,($27),do_signal
+ lda $30,SWITCH_STACK_SIZE($30)
+ br $31,restore_all
+.end entSys
+
+.align 3
+.ent reschedule
+reschedule:
+ subq $30,16,$30
+ stq $19,0($30)
+ stq $20,8($30)
+ lda $27,schedule
+ jsr $26,($27),schedule
+ ldq $19,0($30)
+ ldq $20,8($30)
+ addq $30,16,$30
+ br $31,ret_from_reschedule
+.end reschedule
+
+.align 3
+.ent sys_sigreturn
+sys_sigreturn:
+ bis $30,$30,$17
+ lda $30,-SWITCH_STACK_SIZE($30)
+ bis $30,$30,$18
+ lda $27,do_sigreturn
+ jsr $26,($27),do_sigreturn
+ br $0,undo_switch_stack
+ br $31,ret_from_sys_call
+.end sys_sigreturn
+
+.align 3
+.ent sys_sigsuspend
+sys_sigsuspend:
+ bis $30,$30,$17
+ br $0,do_switch_stack
+ bis $30,$30,$18
+ lda $27,do_sigsuspend
+ jsr $26,($27),do_sigsuspend
+ lda $30,SWITCH_STACK_SIZE($30)
+ br $31,ret_from_sys_call
+.end sys_sigreturn
+
+ .align 3
+ .globl sys_call_table
+sys_call_table:
+/*0*/ .quad do_entSys, sys_exit, sys_fork, sys_read, sys_write
+ .quad do_entSys, sys_close, sys_wait4, do_entSys, sys_link
+ .quad sys_unlink, do_entSys, sys_chdir, sys_fchdir, sys_mknod
+ .quad sys_chmod, sys_chown, sys_brk, do_entSys, sys_lseek
+ .quad sys_getxpid, osf_mount, osf_umount, sys_setuid, sys_getxuid
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, sys_access, do_entSys
+ .quad do_entSys, sys_sync, sys_kill, do_entSys, sys_setpgid
+ .quad do_entSys, sys_dup, sys_pipe, do_entSys, do_entSys
+ .quad sys_open, do_entSys, sys_getxgid, osf_sigprocmask, do_entSys
+/*50*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_ioctl
+ .quad do_entSys, do_entSys, sys_symlink, sys_readlink, sys_execve
+ .quad sys_umask, do_entSys, do_entSys, sys_getpgrp, sys_getpagesize
+ .quad do_entSys, osf_vfork, sys_newstat, sys_newlstat, do_entSys
+ .quad do_entSys, osf_mmap, do_entSys, sys_munmap, sys_mprotect
+ .quad sys_madvise, do_entSys, do_entSys, do_entSys, sys_getgroups
+ .quad do_entSys, do_entSys, do_entSys, sys_setitimer, do_entSys
+ .quad do_entSys, sys_getitimer, sys_gethostname, do_entSys, sys_getdtablesize
+ .quad sys_dup2, sys_newfstat, sys_fcntl, sys_select, do_entSys
+ .quad sys_fsync, do_entSys, sys_socket, do_entSys, do_entSys
+/*100*/ .quad do_entSys, do_entSys, do_entSys, sys_sigreturn, sys_bind
+ .quad do_entSys, sys_listen, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, sys_sigsuspend, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, sys_gettimeofday, sys_getrusage, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, sys_settimeofday, sys_fchown, sys_fchmod
+ .quad do_entSys, sys_setreuid, sys_setregid, sys_rename, sys_truncate
+ .quad sys_ftruncate, do_entSys, sys_setgid, do_entSys, do_entSys
+ .quad do_entSys, sys_mkdir, sys_rmdir, sys_utimes, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_getrlimit
+ .quad sys_setrlimit, do_entSys, sys_setsid, do_entSys, do_entSys
+/*150*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries
+ .quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys
+ .quad osf_getdomainname, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, osf_swapon
+/*200*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, osf_utsname, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+/*250*/ .quad do_entSys, osf_usleep_thread, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
+/* linux-specific system calls start at 300 */
+/*300*/ .quad sys_bdflush, sys_sethae, do_entSys, do_entSys, do_entSys
+ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys
diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S
new file mode 100644
index 000000000..2cff1159a
--- /dev/null
+++ b/arch/alpha/kernel/head.S
@@ -0,0 +1,97 @@
+/*
+ * alpha/boot/head.S
+ *
+ * initial boot stuff.. At this point, the bootloader has already
+ * switched into OSF/1 PAL-code, and loaded us at the correct address
+ * (START_ADDR). So there isn't much left for us to do: just set up
+ * the kernel global pointer and jump to the kernel entry-point.
+ */
+
+#define __ASSEMBLY__
+#include <asm/system.h>
+#include <linux/fd.h>
+
+#define halt .long PAL_halt
+
+.globl swapper_pg_dir
+swapper_pg_dir=SWAPPER_PGD
+
+ .set noreorder
+ .globl __start
+ .ent __start
+__start:
+ br $27,1f
+1: ldgp $29,0($27)
+ lda $27,start_kernel
+ jsr $26,($27),start_kernel
+ halt
+ .end __start
+
+ .align 3
+ .globl wrent
+ .ent wrent
+wrent:
+ .long PAL_wrent
+ ret ($26)
+ .end wrent
+
+ .align 3
+ .globl wrkgp
+ .ent wrkgp
+wrkgp:
+ .long PAL_wrkgp
+ ret ($26)
+ .end wrkgp
+
+ .align 3
+ .globl wrusp
+ .ent wrusp
+wrusp:
+ .long PAL_wrusp
+ ret ($26)
+ .end wrusp
+
+ .align 3
+ .globl rdusp
+ .ent rdusp
+rdusp:
+ .long PAL_rdusp
+ ret ($26)
+ .end rdusp
+
+ .align 3
+ .globl tbi
+ .ent tbi
+tbi:
+ .long PAL_tbi
+ ret ($26)
+ .end tbi
+
+ .align 3
+ .globl imb
+ .ent imb
+imb:
+ .long PAL_imb
+ ret ($26)
+ .end imb
+
+ .align 3
+ .globl rdmces
+ .ent rdmces
+rdmces:
+ call_pal PAL_rdmces
+ ret ($26)
+ .end rdmces
+
+ .align 3
+ .globl wrmces
+ .ent wrmces
+wrmces:
+ call_pal PAL_wrmces
+ ret ($26)
+ .end wrmces
+
+.align 9
+.globl floppy_track_buffer
+floppy_track_buffer:
+ .space 512*2*MAX_BUFFER_SECTORS,1
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
new file mode 100644
index 000000000..a59077539
--- /dev/null
+++ b/arch/alpha/kernel/irq.c
@@ -0,0 +1,414 @@
+/*
+ * linux/arch/alpha/kernel/irq.c
+ *
+ * Copyright (C) 1995 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.
+ */
+
+#include <linux/config.h>
+#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>
+#include <asm/bitops.h>
+#include <asm/dma.h>
+
+static unsigned char cache_21 = 0xff;
+static unsigned char cache_A1 = 0xff;
+
+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);
+}
+
+/*
+ * 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;
+}
+
+static inline void ack_irq(int irq)
+{
+ /* ACK the interrupt making it the lowest priority */
+ /* First the slave .. */
+ if (irq > 7) {
+ outb(0xE0 | (irq - 8), 0xa0);
+ irq = 2;
+ }
+ /* .. then the master */
+ outb(0xE0 | irq, 0x20);
+}
+
+static inline void mask_irq(int irq)
+{
+ if (irq < 8) {
+ cache_21 |= 1 << irq;
+ outb(cache_21, 0x21);
+ } else {
+ cache_A1 |= 1 << (irq - 8);
+ outb(cache_A1, 0xA1);
+ }
+}
+
+static inline void unmask_irq(unsigned long irq)
+{
+ if (irq < 8) {
+ cache_21 &= ~(1 << irq);
+ outb(cache_21, 0x21);
+ } else {
+ cache_A1 &= ~(1 << (irq - 8));
+ outb(cache_A1, 0xA1);
+ }
+}
+
+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 (irq < 8) {
+ if (irq) {
+ 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();
+ mask_irq(irq);
+ action->handler = NULL;
+ action->flags = 0;
+ action->mask = 0;
+ action->name = NULL;
+ restore_flags(flags);
+}
+
+static void handle_nmi(struct pt_regs * regs)
+{
+ printk("Whee.. NMI received. Probable hardware error\n");
+ printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461));
+}
+
+static void unexpected_irq(int irq, struct pt_regs * regs)
+{
+ int i;
+
+ printk("IO device interrupt, irq = %d\n", irq);
+ printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
+ printk("Expecting: ");
+ for (i = 0; i < 16; i++)
+ if (irq_action[i].handler)
+ printk("[%s:%d] ", irq_action[i].name, i);
+ printk("\n");
+ printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n",
+ inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa));
+ outb(0x0c, 0x3fc);
+ outb(0x0c, 0x2fc);
+ outb(0,0x61);
+ outb(0,0x461);
+}
+
+static inline void handle_irq(int irq, struct pt_regs * regs)
+{
+ struct irqaction * action = irq + irq_action;
+
+ kstat.interrupts[irq]++;
+ if (!action->handler) {
+ unexpected_irq(irq, regs);
+ return;
+ }
+ action->handler(irq, regs);
+}
+
+static void local_device_interrupt(unsigned long vector, struct pt_regs * regs)
+{
+ switch (vector) {
+ /* com1: map to irq 4 */
+ case 0x900:
+ handle_irq(4, regs);
+ return;
+
+ /* com2: map to irq 3 */
+ case 0x920:
+ handle_irq(3, regs);
+ return;
+
+ /* keyboard: map to irq 1 */
+ case 0x980:
+ handle_irq(1, regs);
+ return;
+
+ /* mouse: map to irq 9 */
+ case 0x990:
+ handle_irq(9, regs);
+ return;
+ default:
+ printk("Unknown local interrupt %lx\n", vector);
+ }
+}
+
+/*
+ * The vector is 0x8X0 for EISA interrupt X, and 0x9X0 for the local
+ * motherboard interrupts.. This is for the Jensen.
+ *
+ * 0x660 - NMI
+ *
+ * 0x800 - IRQ0 interval timer (not used, as we use the RTC timer)
+ * 0x810 - IRQ1 line printer (duh..)
+ * 0x860 - IRQ6 floppy disk
+ * 0x8E0 - IRQ14 SCSI controller
+ *
+ * 0x900 - COM1
+ * 0x920 - COM2
+ * 0x980 - keyboard
+ * 0x990 - mouse
+ *
+ * The PCI version is more sane: it doesn't have the local interrupts at
+ * all, and has only normal PCI interrupts from devices. Happily it's easy
+ * enough to do a sane mapping from the Jensen.. Note that this means
+ * that we may have to do a hardware "ack" to a different interrupt than
+ * we report to the rest of the world..
+ */
+static void device_interrupt(unsigned long vector, struct pt_regs * regs)
+{
+ int irq, ack;
+ struct irqaction * action;
+
+ if (vector == 0x660) {
+ handle_nmi(regs);
+ return;
+ }
+
+ ack = irq = (vector - 0x800) >> 4;
+#ifndef CONFIG_PCI
+ if (vector >= 0x900) {
+ local_device_interrupt(vector, regs);
+ return;
+ }
+ /* irq1 is supposed to be the keyboard, silly Jensen */
+ if (irq == 1)
+ irq = 7;
+#endif
+ kstat.interrupts[irq]++;
+ action = irq_action + irq;
+ /* quick interrupts get executed with no extra overhead */
+ if (action->flags & SA_INTERRUPT) {
+ action->handler(irq, regs);
+ ack_irq(ack);
+ return;
+ }
+ /*
+ * For normal interrupts, we mask it out, and then ACK it.
+ * This way another (more timing-critical) interrupt can
+ * come through while we're doing this one.
+ *
+ * Note! A irq without a handler gets masked and acked, but
+ * never unmasked. The autoirq stuff depends on this (it looks
+ * at the masks before and after doing the probing).
+ */
+ mask_irq(ack);
+ ack_irq(ack);
+ if (!action->handler)
+ return;
+ action->handler(irq, regs);
+ unmask_irq(ack);
+}
+
+/*
+ * Start listening for interrupts..
+ */
+unsigned int probe_irq_on(void)
+{
+ unsigned int i, irqs = 0, irqmask;
+ unsigned long delay;
+
+ for (i = 15; i > 0; i--) {
+ if (!irq_action[i].handler) {
+ enable_irq(i);
+ irqs |= (1 << i);
+ }
+ }
+
+ /* wait for spurious interrupts to mask themselves out again */
+ for (delay = jiffies + HZ/10; delay > jiffies; )
+ /* about 100 ms delay */;
+
+ /* now filter out any obviously spurious interrupts */
+ irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int) cache_21;
+ irqs &= ~irqmask;
+ return irqs;
+}
+
+/*
+ * Get the result of the IRQ probe.. A negative result means that
+ * we have several candidates (but we return the lowest-numbered
+ * one).
+ */
+int probe_irq_off(unsigned int irqs)
+{
+ unsigned int i, irqmask;
+
+ irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21;
+ irqs &= irqmask;
+ if (!irqs)
+ return 0;
+ i = ffz(~irqs);
+ if (irqs != (1 << i))
+ i = -i;
+ return i;
+}
+
+static void machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs)
+{
+ printk("Machine check\n");
+}
+
+asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ switch (type) {
+ case 0:
+ printk("Interprocessor interrupt? You must be kidding\n");
+ break;
+ case 1:
+ /* timer interrupt.. */
+ handle_irq(0, &regs);
+ return;
+ case 2:
+ machine_check(vector, la_ptr, &regs);
+ break;
+ case 3:
+ device_interrupt(vector, &regs);
+ return;
+ case 4:
+ printk("Performance counter interrupt\n");
+ break;;
+ default:
+ printk("Hardware intr %ld %lx? Huh?\n", type, vector);
+ }
+ printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps);
+}
+
+extern asmlinkage void entInt(void);
+
+void init_IRQ(void)
+{
+ wrent(entInt, 0);
+ dma_outb(0, DMA1_RESET_REG);
+ dma_outb(0, DMA2_RESET_REG);
+ dma_outb(0, DMA1_CLR_MASK_REG);
+ dma_outb(0, DMA2_CLR_MASK_REG);
+}
diff --git a/arch/alpha/kernel/lca.c b/arch/alpha/kernel/lca.c
new file mode 100644
index 000000000..c32c308fb
--- /dev/null
+++ b/arch/alpha/kernel/lca.c
@@ -0,0 +1,304 @@
+/*
+ * Code common to all LCA chips.
+ *
+ * Written by David Mosberger (davidm@cs.arizona.edu) with some code
+ * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit
+ * bios code.
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+#ifdef CONFIG_PCI
+
+#define vulp volatile unsigned long *
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address and setup the LCA_IOC_CONF register
+ * accordingly. It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ *
+ * Type 0:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:11 Device select bit.
+ * 10:8 Function number
+ * 7:2 Register number
+ *
+ * Type 1:
+ *
+ * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
+ * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 31:24 reserved
+ * 23:16 bus number (8 bits = 128 possible buses)
+ * 15:11 Device number (5 bits)
+ * 10:8 function number
+ * 7:2 register number
+ *
+ * Notes:
+ * The function number selects which function of a multi-function device
+ * (e.g., scsi and ethernet).
+ *
+ * The register selects a DWORD (32 bit) register offset. Hence it
+ * doesn't get shifted by 2 bits as we want to "drop" the bottom two
+ * bits.
+ */
+static int mk_conf_addr(unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned long *pci_addr)
+{
+ unsigned long addr;
+
+ if (bus == 0) {
+ int device = device_fn >> 3;
+ int func = device_fn & 0x7;
+
+ /* type 0 configuration cycle: */
+
+ if (device > 12) {
+ return -1;
+ } /* if */
+
+ *((volatile unsigned long*) LCA_IOC_CONF) = 0;
+ addr = (1 << (11 + device)) | (func << 8) | where;
+ } else {
+ /* type 1 configuration cycle: */
+ *((volatile unsigned long*) LCA_IOC_CONF) = 1;
+ addr = (bus << 16) | (device_fn << 8) | where;
+ } /* if */
+ *pci_addr = addr;
+
+ return 0;
+}
+
+
+static unsigned int conf_read(unsigned long addr)
+{
+ unsigned long old_ipl, code, stat0;
+ unsigned int value;
+
+ old_ipl = swpipl(7); /* avoid getting hit by machine check */
+
+ /* reset status register to avoid loosing errors: */
+ stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
+ *((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+ mb();
+
+ /* access configuration space: */
+
+ value = *((volatile unsigned int*)addr);
+ draina();
+
+ stat0 = *((unsigned long*)LCA_IOC_STAT0);
+ if (stat0 & LCA_IOC_STAT0_ERR) {
+ code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
+ & LCA_IOC_STAT0_CODE_MASK);
+ if (code != 1) {
+ printk("lca.c:conf_read: got stat0=%lx\n", stat0);
+ }
+
+ /* reset error status: */
+ *((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+ mb();
+ wrmces(0x7); /* reset machine check */
+
+ value = 0xffffffff;
+ }
+ swpipl(old_ipl);
+
+ return value;
+}
+
+
+static void conf_write(unsigned long addr, unsigned int value)
+{
+ unsigned long old_ipl, code, stat0;
+
+ old_ipl = swpipl(7); /* avoid getting hit by machine check */
+
+ /* reset status register to avoid loosing errors: */
+ stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
+ *((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+ mb();
+
+ /* access configuration space: */
+
+ *((volatile unsigned int*)addr) = value;
+ draina();
+
+ stat0 = *((unsigned long*)LCA_IOC_STAT0);
+ if (stat0 & LCA_IOC_STAT0_ERR) {
+ code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
+ & LCA_IOC_STAT0_CODE_MASK);
+ if (code != 1) {
+ printk("lca.c:conf_write: got stat0=%lx\n", stat0);
+ }
+
+ /* reset error status: */
+ *((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+ mb();
+ wrmces(0x7); /* reset machine check */
+ }
+ swpipl(old_ipl);
+}
+
+
+int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char *value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ *value = 0xff;
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x00;
+
+ *value = conf_read(addr) >> ((where & 3) * 8);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short *value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ *value = 0xffff;
+
+ if (where & 0x1) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ } /* if */
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr)) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x08;
+
+ *value = conf_read(addr) >> ((where & 3) * 8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned int *value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ *value = 0xffffffff;
+
+ if (where & 0x3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ } /* if */
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr)) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x18;
+
+ *value = conf_read(addr);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x00;
+
+ conf_write(addr, value << ((where & 3) * 8));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x08;
+
+ conf_write(addr, value << ((where & 3) * 8));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned int value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x18;
+
+ conf_write(addr, value << ((where & 3) * 8));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+unsigned long lca_init(unsigned long mem_start, unsigned long mem_end)
+{
+ /*
+ * Set up the PCI->physical memory translation windows.
+ * For now, window 1 is disabled. In the future, we may
+ * want to use it to do scatter/gather DMA. Window 0
+ * goes at 1 GB and is 1 GB large.
+ */
+ *(vulp)LCA_IOC_W_BASE1 = 0UL<<33;
+ *(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE;
+ *(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1;
+ *(vulp)LCA_IOC_T_BASE0 = 0;
+
+ return mem_start;
+}
+
+#endif /* CONFIG_PCI */
+
+ /*** end of lca.c ***/
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
new file mode 100644
index 000000000..ddf090283
--- /dev/null
+++ b/arch/alpha/kernel/osf_sys.c
@@ -0,0 +1,494 @@
+/*
+ * linux/arch/alpha/kernel/osf_sys.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * This file handles some of the stranger OSF/1 system call interfaces.
+ * Some of the system calls expect a non-C calling standard, others have
+ * special parameter blocks..
+ */
+
+#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 <linux/utsname.h>
+#include <linux/time.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+extern int do_mount(dev_t, const char *, char *, int, void *);
+extern int do_pipe(int *);
+
+extern struct file_operations * get_blkfops(unsigned int);
+extern struct file_operations * get_chrfops(unsigned int);
+
+extern dev_t get_unnamed_dev(void);
+extern void put_unnamed_dev(dev_t);
+
+extern asmlinkage int sys_umount(char *);
+extern asmlinkage int sys_swapon(const char *specialfile);
+
+/*
+ * OSF/1 directory handling functions...
+ *
+ * The "getdents()" interface is much more sane: the "basep" stuff is
+ * braindamage (it can't really handle filesystems where the directory
+ * offset differences aren't the same as "d_reclen").
+ */
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+7) & ~7)
+
+struct osf_dirent {
+ unsigned int d_ino;
+ unsigned short d_reclen;
+ unsigned short d_namlen;
+ char d_name[1];
+};
+
+struct osf_dirent_callback {
+ struct osf_dirent * dirent;
+ long *basep;
+ int count;
+ int error;
+};
+
+static int osf_filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino)
+{
+ struct osf_dirent * dirent;
+ struct osf_dirent_callback * buf = (struct osf_dirent_callback *) __buf;
+ int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
+
+ buf->error = -EINVAL; /* unly used if we fail */
+ if (reclen > buf->count)
+ return -EINVAL;
+ if (buf->basep) {
+ put_user(offset, buf->basep);
+ buf->basep = NULL;
+ }
+ dirent = buf->dirent;
+ put_user(ino, &dirent->d_ino);
+ put_user(namlen, &dirent->d_namlen);
+ put_user(reclen, &dirent->d_reclen);
+ memcpy_tofs(dirent->d_name, name, namlen);
+ put_fs_byte(0, dirent->d_name + namlen);
+ ((char *) dirent) += reclen;
+ buf->dirent = dirent;
+ buf->count -= reclen;
+ return 0;
+}
+
+asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent * dirent,
+ unsigned int count, long *basep)
+{
+ int error;
+ struct file * file;
+ struct osf_dirent_callback buf;
+
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ return -EBADF;
+ if (!file->f_op || !file->f_op->readdir)
+ return -ENOTDIR;
+ error = verify_area(VERIFY_WRITE, dirent, count);
+ if (error)
+ return error;
+ if (basep) {
+ error = verify_area(VERIFY_WRITE, basep, sizeof(long));
+ if (error)
+ return error;
+ }
+ buf.dirent = dirent;
+ buf.basep = basep;
+ buf.count = count;
+ buf.error = 0;
+ error = file->f_op->readdir(file->f_inode, file, dirent, osf_filldir);
+ if (error < 0)
+ return error;
+ if (count == buf.count)
+ return buf.error;
+ return count - buf.count;
+}
+
+/*
+ * Heh. As documented by DEC..
+ */
+asmlinkage unsigned long sys_madvise(void)
+{
+ return 0;
+}
+
+asmlinkage unsigned long sys_getxuid(int a0, int a1, int a2, int a3, int a4, int a5,
+ struct pt_regs regs)
+{
+ (&regs)->r20 = current->euid;
+ return current->uid;
+}
+
+asmlinkage unsigned long sys_getxgid(int a0, int a1, int a2, int a3, int a4, int a5,
+ struct pt_regs regs)
+{
+ (&regs)->r20 = current->egid;
+ return current->gid;
+}
+
+asmlinkage unsigned long sys_getxpid(int a0, int a1, int a2, int a3, int a4, int a5,
+ struct pt_regs regs)
+{
+ (&regs)->r20 = current->p_opptr->pid;
+ return current->pid;
+}
+
+#define OSF_MAP_ANONYMOUS 0x0010
+#define OSF_MAP_FIXED 0x0100
+#define OSF_MAP_HASSEMAPHORE 0x0200
+#define OSF_MAP_INHERIT 0x0400
+#define OSF_MAP_UNALIGNED 0x0800
+
+asmlinkage unsigned long osf_mmap(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long osf_flags, unsigned long fd,
+ unsigned long off)
+{
+ struct file * file = NULL;
+ unsigned long flags = osf_flags & 0x0f;
+
+ if (osf_flags & (OSF_MAP_HASSEMAPHORE | OSF_MAP_INHERIT | OSF_MAP_UNALIGNED))
+ printk("%s: unimplemented OSF mmap flags %04lx\n", current->comm, osf_flags);
+ if (osf_flags & OSF_MAP_FIXED)
+ flags |= MAP_FIXED;
+ if (osf_flags & OSF_MAP_ANONYMOUS)
+ flags |= MAP_ANONYMOUS;
+ else {
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ return -EBADF;
+ }
+ return do_mmap(file, addr, len, prot, flags, off);
+}
+
+asmlinkage int osf_statfs(char * path, struct statfs * buffer, unsigned long bufsiz)
+{
+ struct inode * inode;
+ int retval;
+
+ if (bufsiz > sizeof(struct statfs))
+ bufsiz = sizeof(struct statfs);
+ retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
+ if (retval)
+ return retval;
+ retval = namei(path, &inode);
+ if (retval)
+ return retval;
+ if (!inode->i_sb->s_op->statfs) {
+ iput(inode);
+ return -ENOSYS;
+ }
+ inode->i_sb->s_op->statfs(inode->i_sb, buffer, bufsiz);
+ iput(inode);
+ return 0;
+}
+
+asmlinkage int osf_fstatfs(unsigned long fd, struct statfs * buffer, unsigned long bufsiz)
+{
+ struct file * file;
+ struct inode * inode;
+ int retval;
+
+ retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
+ if (retval)
+ return retval;
+ if (bufsiz > sizeof(struct statfs))
+ bufsiz = sizeof(struct statfs);
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ return -EBADF;
+ if (!(inode = file->f_inode))
+ return -ENOENT;
+ if (!inode->i_sb->s_op->statfs)
+ return -ENOSYS;
+ inode->i_sb->s_op->statfs(inode->i_sb, buffer, bufsiz);
+ return 0;
+}
+
+/*
+ * Uhh.. OSF/1 mount parameters aren't exactly obvious..
+ *
+ * Although to be frank, neither are the native Linux/i386 ones..
+ */
+struct ufs_args {
+ char * devname;
+ int flags;
+ uid_t exroot;
+};
+
+struct cdfs_args {
+ char * devname;
+ int flags;
+ uid_t exroot;
+/*
+ * this has lots more here, which linux handles with the option block
+ * but I'm too lazy to do the translation into ascii..
+ */
+};
+
+struct procfs_args {
+ char * devname;
+ int flags;
+ uid_t exroot;
+};
+
+static int getdev(const char * name, int rdonly, struct inode ** ino)
+{
+ dev_t dev;
+ struct inode * inode;
+ struct file_operations * fops;
+ int retval;
+
+ retval = namei(name, &inode);
+ if (retval)
+ return retval;
+ if (!S_ISBLK(inode->i_mode)) {
+ iput(inode);
+ return -ENOTBLK;
+ }
+ if (IS_NODEV(inode)) {
+ iput(inode);
+ return -EACCES;
+ }
+ dev = inode->i_rdev;
+ if (MAJOR(dev) >= MAX_BLKDEV) {
+ iput(inode);
+ return -ENXIO;
+ }
+ fops = get_blkfops(MAJOR(dev));
+ if (!fops) {
+ iput(inode);
+ return -ENODEV;
+ }
+ if (fops->open) {
+ struct file dummy;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.f_inode = inode;
+ dummy.f_mode = rdonly ? 1 : 3;
+ retval = fops->open(inode, &dummy);
+ if (retval) {
+ iput(inode);
+ return retval;
+ }
+ }
+ *ino = inode;
+ return 0;
+}
+
+static void putdev(struct inode * inode)
+{
+ struct file_operations * fops;
+
+ fops = get_blkfops(MAJOR(inode->i_rdev));
+ if (fops->release)
+ fops->release(inode, NULL);
+}
+
+/*
+ * We can't actually handle ufs yet, so we translate UFS mounts to
+ * ext2fs mounts... I wouldn't mind a USF filesystem, but the UFS
+ * layout is so braindead it's a major headache doing it..
+ */
+static int osf_ufs_mount(char * dirname, struct ufs_args * args, int flags)
+{
+ int retval;
+ struct inode * inode;
+ struct cdfs_args tmp;
+
+ retval = verify_area(VERIFY_READ, args, sizeof(*args));
+ if (retval)
+ return retval;
+ memcpy_fromfs(&tmp, args, sizeof(tmp));
+ retval = getdev(tmp.devname, 0, &inode);
+ if (retval)
+ return retval;
+ retval = do_mount(inode->i_rdev, dirname, "ext2", flags, NULL);
+ if (retval)
+ putdev(inode);
+ iput(inode);
+ return retval;
+}
+
+static int osf_cdfs_mount(char * dirname, struct cdfs_args * args, int flags)
+{
+ int retval;
+ struct inode * inode;
+ struct cdfs_args tmp;
+
+ retval = verify_area(VERIFY_READ, args, sizeof(*args));
+ if (retval)
+ return retval;
+ memcpy_fromfs(&tmp, args, sizeof(tmp));
+ retval = getdev(tmp.devname, 1, &inode);
+ if (retval)
+ return retval;
+ retval = do_mount(inode->i_rdev, dirname, "iso9660", flags, NULL);
+ if (retval)
+ putdev(inode);
+ iput(inode);
+ return retval;
+}
+
+static int osf_procfs_mount(char * dirname, struct procfs_args * args, int flags)
+{
+ dev_t dev;
+ int retval;
+ struct procfs_args tmp;
+
+ retval = verify_area(VERIFY_READ, args, sizeof(*args));
+ if (retval)
+ return retval;
+ memcpy_fromfs(&tmp, args, sizeof(tmp));
+ dev = get_unnamed_dev();
+ if (!dev)
+ return -ENODEV;
+ retval = do_mount(dev, dirname, "proc", flags, NULL);
+ if (retval)
+ put_unnamed_dev(dev);
+ return retval;
+}
+
+asmlinkage int osf_mount(unsigned long typenr, char * path, int flag, void * data)
+{
+ int retval;
+
+ retval = -EINVAL;
+ switch (typenr) {
+ case 1:
+ retval = osf_ufs_mount(path, (struct ufs_args *) data, flag);
+ break;
+ case 6:
+ retval = osf_cdfs_mount(path, (struct cdfs_args *) data, flag);
+ break;
+ case 9:
+ retval = osf_procfs_mount(path, (struct procfs_args *) data, flag);
+ break;
+ default:
+ printk("osf_mount(%ld, %x)\n", typenr, flag);
+ }
+ return retval;
+}
+
+asmlinkage int osf_umount(char * path, int flag)
+{
+ return sys_umount(path);
+}
+
+/*
+ * I don't know what the parameters are: the first one
+ * seems to be a timeval pointer, and I suspect the second
+ * one is the time remaining.. Ho humm.. No documentation.
+ */
+asmlinkage int osf_usleep_thread(struct timeval * sleep, struct timeval * remain)
+{
+ struct timeval tmp;
+ unsigned long ticks;
+ int retval;
+
+ retval = verify_area(VERIFY_READ, sleep, sizeof(*sleep));
+ if (retval)
+ return retval;
+ if (remain && (retval = verify_area(VERIFY_WRITE, remain, sizeof(*remain))))
+ return retval;
+ memcpy_fromfs(&tmp, sleep, sizeof(*sleep));
+ ticks = tmp.tv_usec;
+ ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ);
+ ticks += tmp.tv_sec * HZ;
+ current->timeout = ticks + jiffies;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (!remain)
+ return 0;
+ ticks = jiffies;
+ if (ticks < current->timeout)
+ ticks = current->timeout - ticks;
+ else
+ ticks = 0;
+ current->timeout = 0;
+ tmp.tv_sec = ticks / HZ;
+ tmp.tv_usec = ticks % HZ;
+ memcpy_tofs(remain, &tmp, sizeof(*remain));
+ return 0;
+}
+
+asmlinkage int osf_utsname(char * name)
+{
+ int error = verify_area(VERIFY_WRITE, name, 5*32);
+ if (error)
+ return error;
+ memcpy_tofs(name + 0, system_utsname.sysname, 32);
+ memcpy_tofs(name + 32, system_utsname.nodename, 32);
+ memcpy_tofs(name + 64, system_utsname.release, 32);
+ memcpy_tofs(name + 96, system_utsname.version, 32);
+ memcpy_tofs(name + 128, system_utsname.machine, 32);
+ return 0;
+}
+
+asmlinkage int osf_swapon(const char * path, int flags, int lowat, int hiwat)
+{
+ /* for now, simply ignore flags, lowat and hiwat... */
+ return sys_swapon(path);
+}
+
+asmlinkage unsigned long sys_getpagesize(void)
+{
+ return PAGE_SIZE;
+}
+
+asmlinkage unsigned long sys_getdtablesize(void)
+{
+ return NR_OPEN;
+}
+
+asmlinkage int sys_pipe(int a0, int a1, int a2, int a3, int a4, int a5,
+ struct pt_regs regs)
+{
+ int fd[2];
+ int error;
+
+ error = do_pipe(fd);
+ if (error)
+ return error;
+ (&regs)->r20 = fd[1];
+ return fd[0];
+}
+
+/*
+ * For compatibility with OSF/1 only. Use utsname(2) instead.
+ */
+asmlinkage int osf_getdomainname(char *name, int namelen)
+{
+ unsigned len;
+ int i, error;
+
+ error = verify_area(VERIFY_WRITE, name, namelen);
+ if (error)
+ return error;
+
+ len = namelen;
+ if (namelen > 32)
+ len = 32;
+
+ for (i = 0; i < len; ++i) {
+ put_user(system_utsname.domainname[i], name + i);
+ if (system_utsname.domainname[i] == '\0')
+ break;
+ }
+ return 0;
+}
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
new file mode 100644
index 000000000..257f431a5
--- /dev/null
+++ b/arch/alpha/kernel/process.c
@@ -0,0 +1,186 @@
+/*
+ * linux/arch/alpha/kernel/process.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * 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 <linux/utsname.h>
+#include <linux/time.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ (&regs)->hae = hae;
+ 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 (;;) {
+ schedule();
+ }
+}
+
+void hard_reset_now(void)
+{
+ halt();
+}
+
+void show_regs(struct pt_regs * regs)
+{
+ printk("\nps: %04lx pc: %016lx\n", regs->ps, regs->pc);
+ printk("rp: %016lx sp: %p\n", regs->r26, regs+1);
+ printk(" r0: %016lx r1: %016lx r2: %016lx r3: %016lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ printk(" r4: %016lx r5: %016lx r6: %016lx r7: %016lx\n",
+ regs->r4, regs->r5, regs->r6, regs->r7);
+ printk(" r8: %016lx r16: %016lx r17: %016lx r18: %016lx\n",
+ regs->r8, regs->r16, regs->r17, regs->r18);
+ printk("r19: %016lx r20: %016lx r21: %016lx r22: %016lx\n",
+ regs->r19, regs->r20, regs->r21, regs->r22);
+ printk("r23: %016lx r24: %016lx r25: %016lx r26: %016lx\n",
+ regs->r23, regs->r24, regs->r25, regs->r26);
+ printk("r27: %016lx r28: %016lx r29: %016lx hae: %016lx\n",
+ regs->r27, regs->r28, regs->gp, regs->hae);
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+}
+
+void flush_thread(void)
+{
+}
+
+/*
+ * "alpha_fork()".. By the time we get here, the
+ * non-volatile registers have also been saved on the
+ * stack. We do some ugly pointer stuff here.. (see
+ * also copy_thread)
+ */
+int alpha_fork(struct switch_stack * swstack)
+{
+ return do_fork(COPYVM | SIGCHLD,
+ rdusp(),
+ (struct pt_regs *) (swstack+1));
+}
+
+extern void ret_from_sys_call(void);
+/*
+ * Copy an alpha thread..
+ *
+ * Note the "stack_offset" stuff: when returning to kernel mode, we need
+ * to have some extra stack-space for the kernel stack that still exists
+ * after the "ret_from_sys_call". When returning to user mode, we only
+ * want the space needed by the syscall stack frame (ie "struct pt_regs").
+ * Use the passed "regs" pointer to determine how much space we need
+ * for a kernel fork().
+ */
+void copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
+ struct task_struct * p, struct pt_regs * regs)
+{
+ struct pt_regs * childregs;
+ struct switch_stack * childstack, *stack;
+ unsigned long stack_offset;
+
+ stack_offset = PAGE_SIZE - sizeof(struct pt_regs);
+ if (!(regs->ps & 8))
+ stack_offset = (PAGE_SIZE-1) & (unsigned long) regs;
+ childregs = (struct pt_regs *) (p->kernel_stack_page + stack_offset);
+
+ *childregs = *regs;
+ childregs->r0 = 0;
+ childregs->r19 = 0;
+ childregs->r20 = 1; /* OSF/1 has some strange fork() semantics.. */
+ regs->r0 = p->pid;
+ regs->r20 = 0;
+ stack = ((struct switch_stack *) regs) - 1;
+ childstack = ((struct switch_stack *) childregs) - 1;
+ *childstack = *stack;
+ childstack->r26 = (unsigned long) ret_from_sys_call;
+ p->tss.usp = usp;
+ p->tss.ksp = (unsigned long) childstack;
+ p->tss.flags = 1;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+}
+
+/*
+ * sys_execve() executes a new program.
+ *
+ * This works due to the alpha calling sequence: the first 6 args
+ * are gotten from registers, while the rest is on the stack, so
+ * we get a0-a5 for free, and then magically find "struct pt_regs"
+ * on the stack for us..
+ *
+ * Don't do this at home.
+ */
+asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ int error;
+ char * filename;
+
+ error = getname((char *) a0, &filename);
+ if (error)
+ return error;
+ error = do_execve(filename, (char **) a1, (char **) a2, &regs);
+ putname(filename);
+ return error;
+}
+
+/*
+ * This doesn't actually work correctly like this: we need to do the
+ * same stack setups that fork() does first.
+ */
+asmlinkage int sys_clone(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ unsigned long clone_flags = a0;
+ unsigned long newsp;
+
+ newsp = rdusp();
+ if (newsp == a1 || !a1)
+ clone_flags |= COPYVM;
+ else
+ newsp = a1;
+ return do_fork(clone_flags, newsp, &regs);
+}
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
new file mode 100644
index 000000000..23a2efd65
--- /dev/null
+++ b/arch/alpha/kernel/setup.c
@@ -0,0 +1,172 @@
+/*
+ * linux/arch/alpha/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#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 <linux/tty.h>
+#include <linux/delay.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/hwrpb.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+struct hae hae = {
+ 0,
+ (unsigned long*) HAE_ADDRESS
+};
+
+struct hwrpb_struct *hwrpb;
+
+unsigned char aux_device_present;
+
+/*
+ * This is setup by the secondary bootstrap loader. Because
+ * the zero page is zeroed out as soon as the vm system is
+ * initialized, we need to copy things out into a more permanent
+ * place.
+ */
+#define PARAM ZERO_PGE
+#define COMMAND_LINE ((char*)(PARAM + 0x0000))
+#define COMMAND_LINE_SIZE 256
+
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+
+/*
+ * The format of "screen_info" is strange, and due to early
+ * i386-setup code. This is just enough to make the console
+ * code think we're on a EGA+ colour display.
+ */
+struct screen_info screen_info = {
+ 0, 0, /* orig-x, orig-y */
+ { 0, 0 }, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 80, /* orig-video-cols */
+ 0,0,0, /* ega_ax, ega_bx, ega_cx */
+ 25 /* orig-video-lines */
+};
+
+static unsigned long find_end_memory(void)
+{
+ int i;
+ unsigned long high = 0;
+ struct memclust_struct * cluster;
+ struct memdesc_struct * memdesc;
+
+ memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
+ cluster = memdesc->cluster;
+ for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
+ unsigned long tmp;
+ tmp = (cluster->start_pfn + cluster->numpages) << PAGE_SHIFT;
+ if (tmp > high)
+ high = tmp;
+ }
+ /* round it up to an even number of pages.. */
+ high = (high + PAGE_SIZE) & (PAGE_MASK*2);
+ return PAGE_OFFSET + high;
+}
+
+void setup_arch(char **cmdline_p,
+ unsigned long * memory_start_p, unsigned long * memory_end_p)
+{
+ extern int _end;
+
+ hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr);
+
+ set_hae(hae.cache); /* sync HAE register w/hae_cache */
+
+ ROOT_DEV = 0x0802; /* sda2 */
+#ifndef CONFIG_PCI
+ aux_device_present = 0xaa;
+#else
+ aux_device_present = 0x00;
+#endif
+ command_line[COMMAND_LINE_SIZE - 1] = '\0';
+ strcpy(command_line, COMMAND_LINE);
+
+ *cmdline_p = command_line;
+ *memory_start_p = (unsigned long) &_end;
+ *memory_end_p = find_end_memory();
+
+#ifdef CONFIG_PCI
+ *memory_start_p = lca_init(*memory_start_p, *memory_end_p);
+#endif
+}
+
+asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
+{
+ return -EIO;
+}
+
+
+/*
+ * BUFFER is PAGE_SIZE bytes long.
+ */
+int get_cpuinfo(char *buffer)
+{
+ const char *cpu_name[] = {
+ "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45"
+ };
+ const char *systype_name[] = {
+ "ADU", "Cobra", "Ruby", "Flamingo", "Unknown 1", "Jensen",
+ "Pelican", "Unknown 2", "Sable", "AXPvme", "Noname",
+ "Turbolaser", "Avanti", "Mustang", "Alcor", "Unknown 3",
+ "Mikasa", "Unknown3", "EB66", "EB64+"
+ };
+ struct percpu_struct *cpu;
+ unsigned int cpu_index, system_index;
+# define N(a) (sizeof(a)/sizeof(a[0]))
+
+ cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset);
+ cpu_index = (unsigned) (cpu->type - 1);
+ system_index = (unsigned) (hwrpb->sys_type - 1);
+
+ return sprintf(buffer,
+ "cpu\t\t\t: Alpha\n"
+ "cpu model\t\t: %s\n"
+ "cpu variation\t\t: %ld\n"
+ "cpu revision\t\t: %ld\n"
+ "cpu serial number\t: %s\n"
+ "system type\t\t: %s\n"
+ "system variation\t: %ld\n"
+ "system revision\t\t: %ld\n"
+ "system serial number\t: %s\n"
+ "cycle frequency [Hz]\t: %lu\n"
+ "timer frequency [Hz]\t: %lu.%02lu\n"
+ "page size [bytes]\t: %ld\n"
+ "phys. address bits\t: %ld\n"
+ "max. addr. space #\t: %ld\n"
+ "BogoMIPS\t\t: %lu.%02lu\n",
+
+ (cpu_index < N(cpu_name) ? cpu_name[cpu_index] : "Unknown"),
+ cpu->variation, cpu->revision, (char*)cpu->serial_no,
+ (system_index < N(systype_name) ? systype_name[system_index] : "Unknown"),
+ hwrpb->sys_variation, hwrpb->sys_revision,
+ (char*)hwrpb->ssn,
+ hwrpb->cycle_freq,
+ hwrpb->intr_freq / 4096,
+ (100 * hwrpb->intr_freq / 4096) % 100,
+ hwrpb->pagesize,
+ hwrpb->pa_bits,
+ hwrpb->max_asn,
+ loops_per_sec / 500000, (loops_per_sec / 5000) % 100);
+# undef N
+}
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
new file mode 100644
index 000000000..cc7069728
--- /dev/null
+++ b/arch/alpha/kernel/signal.c
@@ -0,0 +1,319 @@
+/*
+ * linux/arch/alpha/kernel/signal.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/mm.h>
+
+#include <asm/bitops.h>
+#include <asm/segment.h>
+
+#define _S(nr) (1<<((nr)-1))
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
+asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options);
+asmlinkage void ret_from_sys_call(void);
+asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *,
+ unsigned long, unsigned long);
+asmlinkage void imb(void);
+
+/*
+ * The OSF/1 sigprocmask calling sequence is different from the
+ * C sigprocmask() sequence..
+ */
+asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask)
+{
+ unsigned long oldmask = current->blocked;
+
+ newmask &= _BLOCKABLE;
+ switch (how) {
+ case SIG_BLOCK:
+ current->blocked |= newmask;
+ return oldmask;
+ case SIG_UNBLOCK:
+ current->blocked &= ~newmask;
+ return oldmask;
+ case SIG_SETMASK:
+ current->blocked = newmask;
+ return oldmask;
+ }
+ return -EINVAL;
+}
+
+/*
+ * atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int do_sigsuspend(unsigned long mask, struct pt_regs * regs, struct switch_stack * sw)
+{
+ unsigned long oldmask = current->blocked;
+ current->blocked = mask & _BLOCKABLE;
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(oldmask,regs, sw, 0, 0))
+ return -EINTR;
+ }
+}
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+asmlinkage void do_sigreturn(struct sigcontext_struct * sc,
+ struct pt_regs * regs, struct switch_stack * sw)
+{
+ unsigned long mask;
+ int i;
+
+ /* verify that it's a good sigcontext before using it */
+ if (verify_area(VERIFY_READ, sc, sizeof(*sc)))
+ do_exit(SIGSEGV);
+ if (get_fs_quad(&sc->sc_ps) != 8)
+ do_exit(SIGSEGV);
+ mask = get_fs_quad(&sc->sc_mask);
+ if (mask & ~_BLOCKABLE)
+ do_exit(SIGSEGV);
+
+ /* ok, looks fine, start restoring */
+ wrusp(get_fs_quad(sc->sc_regs+30));
+ regs->pc = get_fs_quad(&sc->sc_pc);
+ sw->r26 = (unsigned long) ret_from_sys_call;
+ current->blocked = mask;
+
+ regs->r0 = get_fs_quad(sc->sc_regs+0);
+ regs->r1 = get_fs_quad(sc->sc_regs+1);
+ regs->r2 = get_fs_quad(sc->sc_regs+2);
+ regs->r3 = get_fs_quad(sc->sc_regs+3);
+ regs->r4 = get_fs_quad(sc->sc_regs+4);
+ regs->r5 = get_fs_quad(sc->sc_regs+5);
+ regs->r6 = get_fs_quad(sc->sc_regs+6);
+ regs->r7 = get_fs_quad(sc->sc_regs+7);
+ regs->r8 = get_fs_quad(sc->sc_regs+8);
+ sw->r9 = get_fs_quad(sc->sc_regs+9);
+ sw->r10 = get_fs_quad(sc->sc_regs+10);
+ sw->r11 = get_fs_quad(sc->sc_regs+11);
+ sw->r12 = get_fs_quad(sc->sc_regs+12);
+ sw->r13 = get_fs_quad(sc->sc_regs+13);
+ sw->r14 = get_fs_quad(sc->sc_regs+14);
+ sw->r15 = get_fs_quad(sc->sc_regs+15);
+ regs->r16 = get_fs_quad(sc->sc_regs+16);
+ regs->r17 = get_fs_quad(sc->sc_regs+17);
+ regs->r18 = get_fs_quad(sc->sc_regs+18);
+ regs->r19 = get_fs_quad(sc->sc_regs+19);
+ regs->r20 = get_fs_quad(sc->sc_regs+20);
+ regs->r21 = get_fs_quad(sc->sc_regs+21);
+ regs->r22 = get_fs_quad(sc->sc_regs+22);
+ regs->r23 = get_fs_quad(sc->sc_regs+23);
+ regs->r24 = get_fs_quad(sc->sc_regs+24);
+ regs->r25 = get_fs_quad(sc->sc_regs+25);
+ regs->r26 = get_fs_quad(sc->sc_regs+26);
+ regs->r27 = get_fs_quad(sc->sc_regs+27);
+ regs->r28 = get_fs_quad(sc->sc_regs+28);
+ regs->gp = get_fs_quad(sc->sc_regs+29);
+ for (i = 0; i < 31; i++)
+ sw->fp[i] = get_fs_quad(sc->sc_fpregs+i);
+}
+
+/*
+ * Set up a signal frame...
+ */
+static void setup_frame(struct sigaction * sa, struct sigcontext_struct ** fp, unsigned long pc,
+ struct pt_regs * regs, struct switch_stack * sw, int signr, unsigned long oldmask)
+{
+ int i;
+ struct sigcontext_struct * sc;
+
+ sc = *fp;
+ /* check here if we would need to switch stacks.. */
+ sc--;
+ if (verify_area(VERIFY_WRITE, sc, sizeof(*sc)))
+ do_exit(SIGSEGV);
+
+ put_fs_quad(oldmask, &sc->sc_mask);
+ put_fs_quad(8, &sc->sc_ps);
+ put_fs_quad(pc, &sc->sc_pc);
+ put_fs_quad((unsigned long)*fp, sc->sc_regs+30);
+
+ put_fs_quad(regs->r0 , sc->sc_regs+0);
+ put_fs_quad(regs->r1 , sc->sc_regs+1);
+ put_fs_quad(regs->r2 , sc->sc_regs+2);
+ put_fs_quad(regs->r3 , sc->sc_regs+3);
+ put_fs_quad(regs->r4 , sc->sc_regs+4);
+ put_fs_quad(regs->r5 , sc->sc_regs+5);
+ put_fs_quad(regs->r6 , sc->sc_regs+6);
+ put_fs_quad(regs->r7 , sc->sc_regs+7);
+ put_fs_quad(regs->r8 , sc->sc_regs+8);
+ put_fs_quad(sw->r9 , sc->sc_regs+9);
+ put_fs_quad(sw->r10 , sc->sc_regs+10);
+ put_fs_quad(sw->r11 , sc->sc_regs+11);
+ put_fs_quad(sw->r12 , sc->sc_regs+12);
+ put_fs_quad(sw->r13 , sc->sc_regs+13);
+ put_fs_quad(sw->r14 , sc->sc_regs+14);
+ put_fs_quad(sw->r15 , sc->sc_regs+15);
+ put_fs_quad(regs->r16, sc->sc_regs+16);
+ put_fs_quad(regs->r17, sc->sc_regs+17);
+ put_fs_quad(regs->r18, sc->sc_regs+18);
+ put_fs_quad(regs->r19, sc->sc_regs+19);
+ put_fs_quad(regs->r20, sc->sc_regs+20);
+ put_fs_quad(regs->r21, sc->sc_regs+21);
+ put_fs_quad(regs->r22, sc->sc_regs+22);
+ put_fs_quad(regs->r23, sc->sc_regs+23);
+ put_fs_quad(regs->r24, sc->sc_regs+24);
+ put_fs_quad(regs->r25, sc->sc_regs+25);
+ put_fs_quad(regs->r26, sc->sc_regs+26);
+ put_fs_quad(regs->r27, sc->sc_regs+27);
+ put_fs_quad(regs->r28, sc->sc_regs+28);
+ put_fs_quad(regs->gp , sc->sc_regs+29);
+ for (i = 0; i < 31; i++)
+ put_fs_quad(sw->fp[i], sc->sc_fpregs+i);
+
+ /*
+ * The following is:
+ *
+ * bis $30,$30,$16
+ * addq $31,0x67,$0
+ * call_pal callsys
+ *
+ * ie, "sigreturn(stack-pointer)"
+ */
+ put_fs_quad(0x43ecf40047de0410, sc->sc_retcode+0);
+ put_fs_quad(0x0000000000000083, sc->sc_retcode+1);
+ regs->r26 = (unsigned long) sc->sc_retcode;
+ regs->r16 = signr;
+ *fp = sc;
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ *
+ * "r0" and "r19" are the registers we need to restore for system call
+ * restart. "r0" is also used as an indicator whether we can restart at
+ * all (if we get here from anything but a syscall return, it will be 0)
+ */
+asmlinkage int do_signal(unsigned long oldmask,
+ struct pt_regs * regs,
+ struct switch_stack * sw,
+ unsigned long r0, unsigned long r19)
+{
+ unsigned long mask = ~current->blocked;
+ unsigned long handler_signal = 0;
+ struct sigcontext_struct *frame = NULL;
+ unsigned long pc = 0;
+ unsigned long signr;
+ struct sigaction * sa;
+
+ while ((signr = current->signal & mask) != 0) {
+ signr = ffz(~signr);
+ clear_bit(signr, &current->signal);
+ sa = current->sigaction + signr;
+ signr++;
+ if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
+ current->exit_code = signr;
+ current->state = TASK_STOPPED;
+ notify_parent(current);
+ schedule();
+ if (!(signr = current->exit_code))
+ continue;
+ current->exit_code = 0;
+ if (signr == SIGSTOP)
+ continue;
+ if (_S(signr) & current->blocked) {
+ current->signal |= _S(signr);
+ continue;
+ }
+ sa = current->sigaction + signr - 1;
+ }
+ if (sa->sa_handler == SIG_IGN) {
+ if (signr != SIGCHLD)
+ continue;
+ /* check for SIGCHLD: it's special */
+ while (sys_waitpid(-1,NULL,WNOHANG) > 0)
+ /* nothing */;
+ continue;
+ }
+ if (sa->sa_handler == SIG_DFL) {
+ if (current->pid == 1)
+ continue;
+ switch (signr) {
+ case SIGCONT: case SIGCHLD: case SIGWINCH:
+ continue;
+
+ case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU:
+ if (current->flags & PF_PTRACED)
+ continue;
+ current->state = TASK_STOPPED;
+ current->exit_code = signr;
+ if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
+ SA_NOCLDSTOP))
+ notify_parent(current);
+ schedule();
+ continue;
+
+ case SIGQUIT: case SIGILL: case SIGTRAP:
+ case SIGABRT: case SIGFPE: case SIGSEGV:
+ if (current->binfmt && current->binfmt->core_dump) {
+ if (current->binfmt->core_dump(signr, regs))
+ signr |= 0x80;
+ }
+ /* fall through */
+ default:
+ current->signal |= _S(signr & 0x7f);
+ do_exit(signr);
+ }
+ }
+ /*
+ * OK, we're invoking a handler
+ */
+ if (r0) {
+ if (regs->r0 == ERESTARTNOHAND ||
+ (regs->r0 == ERESTARTSYS && !(sa->sa_flags & SA_RESTART)))
+ regs->r0 = EINTR;
+ }
+ handler_signal |= 1 << (signr-1);
+ mask &= ~sa->sa_mask;
+ }
+ if (r0 &&
+ (regs->r0 == ERESTARTNOHAND ||
+ regs->r0 == ERESTARTSYS ||
+ regs->r0 == ERESTARTNOINTR)) {
+ regs->r0 = r0;
+ regs->r19 = r19;
+ regs->pc -= 4;
+ }
+ if (!handler_signal) /* no handler will be called - return 0 */
+ return 0;
+ pc = regs->pc;
+ frame = (struct sigcontext_struct *) rdusp();
+ signr = 1;
+ sa = current->sigaction;
+ for (mask = 1 ; mask ; sa++,signr++,mask += mask) {
+ if (mask > handler_signal)
+ break;
+ if (!(mask & handler_signal))
+ continue;
+ setup_frame(sa,&frame,pc,regs,sw,signr,oldmask);
+ pc = (unsigned long) sa->sa_handler;
+ regs->r27 = pc;
+ if (sa->sa_flags & SA_ONESHOT)
+ sa->sa_handler = NULL;
+ current->blocked |= sa->sa_mask;
+ oldmask |= sa->sa_mask;
+ }
+ imb();
+ wrusp((unsigned long) frame);
+ regs->pc = pc; /* "return" to the first handler */
+ return 1;
+}
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
new file mode 100644
index 000000000..4e3512ddd
--- /dev/null
+++ b/arch/alpha/kernel/traps.c
@@ -0,0 +1,159 @@
+/*
+ * kernel/traps.c
+ *
+ * (C) Copyright 1994 Linus Torvalds
+ */
+
+/*
+ * This file initializes the trap entry points
+ */
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <asm/unaligned.h>
+
+void die_if_kernel(char * str, struct pt_regs * regs, long err)
+{
+ long i;
+ unsigned long sp;
+ unsigned int * pc;
+
+ printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err);
+ sp = (unsigned long) (regs+1);
+ if (regs->ps & 8)
+ sp = rdusp();
+ printk("pc = %lx ps = %04lx\n", regs->pc, regs->ps);
+ printk("rp = %lx sp = %lx\n", regs->r26, sp);
+ printk("r0=%lx r1=%lx r2=%lx r3=%lx\n",
+ regs->r0, regs->r1, regs->r2, regs->r3);
+ printk("r8=%lx\n", regs->r8);
+ printk("r16=%lx r17=%lx r18=%lx r19=%lx\n",
+ regs->r16, regs->r17, regs->r18, regs->r19);
+ printk("r20=%lx r21=%lx r22=%lx r23=%lx\n",
+ regs->r20, regs->r21, regs->r22, regs->r23);
+ printk("r24=%lx r25=%lx r26=%lx r27=%lx\n",
+ regs->r24, regs->r25, regs->r26, regs->r27);
+ printk("r28=%lx r29=%lx r30=%lx\n",
+ regs->r28, regs->gp, sp);
+ if (regs->ps & 8)
+ return;
+ printk("Code:");
+ pc = (unsigned int *) regs->pc;
+ for (i = -3; i < 6; i++)
+ printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>');
+ printk("\n");
+ for (i = 0 ; i < 5000000000 ; i++)
+ /* pause */;
+ halt();
+}
+
+asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask,
+ unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ printk("Arithmetic trap: %02lx %016lx\n", summary, write_mask);
+ die_if_kernel("Arithmetic fault", &regs, 0);
+}
+
+asmlinkage void do_entIF(unsigned long type, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ die_if_kernel("Instruction fault", &regs, type);
+}
+
+/*
+ * entUna has a different register layout to be reasonably simple. It
+ * needs access to all the integer registers (the kernel doesn't use
+ * fp-regs), and it needs to have them in order for simpler access.
+ *
+ * Due to the non-standard register layout (and because we don't want
+ * to handle floating-point regs), we disallow user-mode unaligned
+ * accesses (we'd need to do "verify_area()" checking, as well as
+ * do a full "ret_from_sys_call" return).
+ *
+ * Oh, btw, we don't handle the "gp" register correctly, but if we fault
+ * on a gp-register unaligned load/store, something is _very_ wrong
+ * in the kernel anyway..
+ */
+struct allregs {
+ unsigned long regs[32];
+ unsigned long ps, pc, gp, a0, a1, a2;
+};
+
+asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct allregs regs)
+{
+ static int cnt = 0;
+
+ if (regs.ps & 8)
+ do_exit(SIGSEGV);
+ if (++cnt < 5)
+ printk("Unaligned trap at %016lx: %p %lx %ld\n",
+ regs.pc, va, opcode, reg);
+ /* $16-$18 are PAL-saved, and are offset by 19 entries */
+ if (reg >= 16 && reg <= 18)
+ reg += 19;
+ switch (opcode) {
+ case 0x28: /* ldl */
+ *(reg+regs.regs) = (int) ldl_u(va);
+ return;
+ case 0x29: /* ldq */
+ *(reg+regs.regs) = ldq_u(va);
+ return;
+ case 0x2c: /* stl */
+ stl_u(*(reg+regs.regs), va);
+ return;
+ case 0x2d: /* stq */
+ stq_u(*(reg+regs.regs), va);
+ return;
+ }
+ printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n",
+ regs.pc, va, opcode, reg);
+ do_exit(SIGSEGV);
+}
+
+/*
+ * DEC means people to use the "retsys" instruction for return from
+ * a system call, but they are clearly misguided about this. We use
+ * "rti" in all cases, and fill in the stack with the return values.
+ * That should make signal handling etc much cleaner.
+ *
+ * Even more horribly, DEC doesn't allow system calls from kernel mode.
+ * "Security" features letting the user do something the kernel can't
+ * are a thinko. DEC palcode is strange. The PAL-code designers probably
+ * got terminally tainted by VMS at some point.
+ */
+asmlinkage long do_entSys(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs)
+{
+ printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2);
+ return -1;
+}
+
+extern asmlinkage void entMM(void);
+extern asmlinkage void entIF(void);
+extern asmlinkage void entArith(void);
+extern asmlinkage void entUna(void);
+extern asmlinkage void entSys(void);
+
+void trap_init(void)
+{
+ unsigned long gptr;
+
+ /*
+ * Tell PAL-code what global pointer we want in the kernel..
+ */
+ __asm__("br %0,___tmp\n"
+ "___tmp:\tldgp %0,0(%0)"
+ : "=r" (gptr));
+ wrkgp(gptr);
+
+ wrent(entArith, 1);
+ wrent(entMM, 2);
+ wrent(entIF, 3);
+ wrent(entUna, 4);
+ wrent(entSys, 5);
+}
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
new file mode 100644
index 000000000..9ac9310b2
--- /dev/null
+++ b/arch/alpha/lib/Makefile
@@ -0,0 +1,38 @@
+#
+# Makefile for alpha-specific library files..
+#
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+OBJS = __divqu.o __remqu.o __divlu.o __remlu.o memset.o memcpy.o io.o
+
+lib.a: $(OBJS)
+ $(AR) rcs lib.a $(OBJS)
+ sync
+
+__divqu.o: divide.S
+ $(CC) -DDIV -c -o __divqu.o divide.S
+
+__remqu.o: divide.S
+ $(CC) -DREM -c -o __remqu.o divide.S
+
+__divlu.o: divide.S
+ $(CC) -DDIV -DINTSIZE -c -o __divlu.o divide.S
+
+__remlu.o: divide.S
+ $(CC) -DREM -DINTSIZE -c -o __remlu.o divide.S
+
+dep:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
diff --git a/arch/alpha/lib/divide.S b/arch/alpha/lib/divide.S
new file mode 100644
index 000000000..9388d3f34
--- /dev/null
+++ b/arch/alpha/lib/divide.S
@@ -0,0 +1,160 @@
+/*
+ * arch/alpha/lib/divide.S
+ *
+ * (C) 1995 Linus Torvalds
+ *
+ * Alpha division..
+ */
+
+/*
+ * The alpha chip doesn't provide hardware division, so we have to do it
+ * by hand. The compiler expects the functions
+ *
+ * __divqu: 64-bit unsigned long divide
+ * __remqu: 64-bit unsigned long remainder
+ * __divqs/__remqs: signed 64-bit
+ * __divlu/__remlu: unsigned 32-bit
+ * __divls/__remls: signed 32-bit
+ *
+ * These are not normal C functions: instead of the normal
+ * calling sequence, these expect their arguments in registers
+ * $24 and $25, and return the result in $27. Register $28 may
+ * be clobbered (assembly temporary), anything else must be saved.
+ *
+ * In short: painful.
+ *
+ * This is a rather simple bit-at-a-time algorithm: it's very good
+ * at dividing random 64-bit numbers, but the more usual case where
+ * the divisor is small is handled better by the DEC algorithm
+ * using lookup tables. This uses much less memory, though, and is
+ * nicer on the cache.. Besides, I don't know the copyright status
+ * of the DEC code.
+ */
+
+/*
+ * My temporaries:
+ * $0 - current bit
+ * $1 - shifted divisor
+ * $2 - modulus/quotient
+ *
+ * $23 - return address
+ * $24 - dividend
+ * $25 - divisor
+ *
+ * $27 - quotient/modulus
+ * $28 - compare status
+ */
+
+#define halt .long 0
+
+/*
+ * Select function type and registers
+ */
+#define mask $0
+#define divisor $1
+#define compare $28
+
+#ifdef DIV
+#define func(x) __div##x
+#define modulus $2
+#define quotient $27
+#define GETSIGN(x) xor $24,$25,x
+#else
+#define func(x) __rem##x
+#define modulus $27
+#define quotient $2
+#define GETSIGN(x) bis $24,$24,x
+#endif
+
+/*
+ * For 32-bit operations, we need to extend to 64-bit
+ */
+#ifdef INTSIZE
+#define ufunction func(lu)
+#define sfunction func(l)
+#define LONGIFY(x) zapnot x,15,x
+#define SLONGIFY(x) addl x,0,x
+#else
+#define ufunction func(qu)
+#define sfunction func(q)
+#define LONGIFY(x)
+#define SLONGIFY(x)
+#endif
+
+.set noat
+.globl ufunction
+.ent ufunction
+ufunction:
+ subq $30,32,$30
+ stq $0, 0($30)
+ stq $1, 8($30)
+ stq $2,16($30)
+
+ bis $25,$25,divisor
+ bis $24,$24,modulus
+ bis $31,$31,quotient
+ LONGIFY(divisor)
+ LONGIFY(modulus)
+ beq divisor, 9f /* div by zero */
+ bis $31,1,mask
+
+ /* shift divisor left */
+1: cmpult divisor,modulus,compare
+ blt divisor, 3f
+ addq divisor,divisor,divisor
+ addq mask,mask,mask
+ bne compare,1b
+
+ /* ok, start to go right again.. */
+2: srl divisor,1,divisor
+ beq mask,9f
+ srl mask,1,mask
+3: cmpule divisor,modulus,compare
+ beq compare,2b
+ addq quotient,mask,quotient
+ beq mask,9f
+ subq modulus,divisor,modulus
+ br 2b
+
+9: ldq $0, 0($30)
+ ldq $1, 8($30)
+ ldq $2, 16($30)
+ addq $30,32,$30
+ ret $31,($23),1
+ .end ufunction
+
+/*
+ * Uhh.. Ugly signed division. I'd rather not have it at all, but
+ * it's needed in some circumstances. There are different ways to
+ * handle this, really. This does:
+ * -a / b = a / -b = -(a / b)
+ * -a % b = -(a % b)
+ * a % -b = a % b
+ * which is probably not the best solution, but at least should
+ * have the property that (x/y)*y + (x%y) = x.
+ */
+.globl sfunction
+.ent sfunction
+sfunction:
+ bis $24,$25,$28
+ SLONGIFY($28)
+ bge $28,ufunction
+ subq $30,32,$30
+ stq $23,0($30)
+ stq $24,8($30)
+ stq $25,16($30)
+ subq $31,$24,$28
+ cmovlt $24,$28,$24 /* abs($24) */
+ subq $31,$25,$28
+ cmovlt $25,$28,$25 /* abs($25) */
+ bsr $23,ufunction
+ ldq $23,0($30)
+ ldq $24,8($30)
+ ldq $25,16($30)
+ addq $30,32,$30
+ GETSIGN($28)
+ SLONGIFY($28)
+ bge $28,1f
+ subq $31,$27,$27
+1: ret $31,($23),1
+ .end sfunction
diff --git a/arch/alpha/lib/io.c b/arch/alpha/lib/io.c
new file mode 100644
index 000000000..b5f7290ab
--- /dev/null
+++ b/arch/alpha/lib/io.c
@@ -0,0 +1,98 @@
+/*
+ * Alpha IO and memory functions.. Just expand the inlines in the header
+ * files..
+ */
+#include <asm/io.h>
+
+/*
+ * Jensen has a separate "local" and "bus" IO space for
+ * byte-wide IO.
+ */
+#ifdef __is_local
+#undef __bus_inb
+unsigned int __bus_inb(unsigned long addr)
+{
+ return ___bus_inb(addr);
+}
+
+#undef __bus_outb
+void __bus_outb(unsigned char b, unsigned long addr)
+{
+ ___bus_outb(b, addr);
+}
+#endif
+
+#undef inb
+unsigned int inb(unsigned long addr)
+{
+ return __inb(addr);
+}
+
+#undef inw
+unsigned int inw(unsigned long addr)
+{
+ return __inw(addr);
+}
+
+#undef inl
+unsigned int inl(unsigned long addr)
+{
+ return __inl(addr);
+}
+
+
+#undef outb
+void outb(unsigned char b, unsigned long addr)
+{
+ __outb(b, addr);
+}
+
+#undef outw
+void outw(unsigned short b, unsigned long addr)
+{
+ __outw(b, addr);
+}
+
+#undef outl
+void outl(unsigned int b, unsigned long addr)
+{
+ __outl(b, addr);
+}
+
+
+#undef readb
+unsigned long readb(unsigned long addr)
+{
+ return __readb(addr);
+}
+
+#undef readw
+unsigned long readw(unsigned long addr)
+{
+ return __readw(addr);
+}
+
+#undef readl
+unsigned long readl(unsigned long addr)
+{
+ return __readl(addr);
+}
+
+
+#undef writeb
+void writeb(unsigned short b, unsigned long addr)
+{
+ __writeb(b, addr);
+}
+
+#undef writew
+void writew(unsigned short b, unsigned long addr)
+{
+ __writew(b, addr);
+}
+
+#undef writel
+void writel(unsigned int b, unsigned long addr)
+{
+ __writel(b, addr);
+}
diff --git a/arch/alpha/lib/memcpy.c b/arch/alpha/lib/memcpy.c
new file mode 100644
index 000000000..4bbf92f69
--- /dev/null
+++ b/arch/alpha/lib/memcpy.c
@@ -0,0 +1,74 @@
+/*
+ * linux/arch/alpha/lib/memcpy.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * This is reasonably optimized for the quad-word-aligned case, which
+ * happens with page/buffer copies. It's horribly bad for the unaligned
+ * case: it could be made much better, but that would require lots of
+ * assembly (unaligned 8-byte load + shift + aligned 4-byte store, for
+ * example).
+ */
+
+#include <linux/types.h>
+
+static inline void __memcpy_b(unsigned long d, unsigned long s, long n)
+{
+ while (--n >= 0)
+ *(char *) (d++) = *(char *) (s++);
+}
+
+static inline void __memcpy_q(unsigned long d, unsigned long s, long n)
+{
+ /* this first part could be done in one go with ldq_u*2/mask/stq_u */
+ while (d & 7) {
+ if (--n < 0)
+ return;
+ *(char *) d = *(char *) s;
+ d++;
+ s++;
+ }
+ while ((n -= 8) >= 0) {
+ *(unsigned long *) d = *(unsigned long *) s;
+ d += 8;
+ s += 8;
+ }
+ /* as could this.. */
+ __memcpy_b(d,s,n+8);
+}
+
+static inline void __memcpy_l(unsigned long d, unsigned long s, long n)
+{
+ while (d & 3) {
+ if (--n < 0)
+ return;
+ *(char *) d = *(char *) s;
+ d++;
+ s++;
+ }
+ while ((n -= 4) >= 0) {
+ *(unsigned int *) d = *(unsigned int *) s;
+ d += 4;
+ s += 4;
+ }
+ __memcpy_b(d,s,n+4);
+}
+
+void * __memcpy(void * dest, const void *src, size_t n)
+{
+ unsigned long differ;
+ differ = ((unsigned long) dest ^ (unsigned long) src) & 7;
+
+ if (!differ) {
+ __memcpy_q((unsigned long) dest, (unsigned long) src, n);
+ return dest;
+ }
+ if (differ == 4) {
+ __memcpy_l((unsigned long) dest, (unsigned long) src, n);
+ return dest;
+ }
+ __memcpy_b((unsigned long) dest, (unsigned long) src, n);
+ return dest;
+}
diff --git a/arch/alpha/lib/memset.c b/arch/alpha/lib/memset.c
new file mode 100644
index 000000000..d22d44424
--- /dev/null
+++ b/arch/alpha/lib/memset.c
@@ -0,0 +1,44 @@
+/*
+ * linux/arch/alpha/lib/memset.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * These are only slightly optimized so far..
+ */
+
+#include <linux/types.h>
+
+void * __constant_c_memset(void * s, unsigned long c, long count)
+{
+ unsigned long xs = (unsigned long) s;
+
+ /*
+ * the first and last parts could be done with just one
+ * unaligned load/store, but I don't want to think about it
+ */
+ while (count > 0 && (xs & 7)) {
+ *(char *) xs = c;
+ count--; xs++;
+ }
+ while (count > 7) {
+ *(unsigned long *) xs = c;
+ count -=8; xs += 8;
+ }
+ while (count > 0) {
+ *(char *) xs = c;
+ count--; xs++;
+ }
+ return s;
+}
+
+void * __memset(void * s,char c,size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
diff --git a/arch/alpha/mm/Makefile b/arch/alpha/mm/Makefile
new file mode 100644
index 000000000..9d825b683
--- /dev/null
+++ b/arch/alpha/mm/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for the linux alpha-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
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+
+OBJS = init.o fault.o
+
+mm.o: $(OBJS)
+ $(LD) -r -o mm.o $(OBJS)
+
+modules:
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
new file mode 100644
index 000000000..10ac8f496
--- /dev/null
+++ b/arch/alpha/mm/fault.c
@@ -0,0 +1,100 @@
+/*
+ * linux/arch/alpha/mm/fault.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+#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);
+extern void tbi(unsigned long type, unsigned long arg);
+#define tbisi(x) tbi(1,(x))
+#define tbisd(x) tbi(2,(x))
+#define tbis(x) tbi(3,(x))
+
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to handle_mm_fault().
+ *
+ * mmcsr:
+ * 0 = translation not valid
+ * 1 = access violation
+ * 2 = fault-on-read
+ * 3 = fault-on-execute
+ * 4 = fault-on-write
+ *
+ * cause:
+ * -1 = instruction fetch
+ * 0 = load
+ * 1 = store
+ */
+asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ struct pt_regs regs)
+{
+ struct vm_area_struct * vma;
+
+ 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 (cause < 0) {
+ if (!(vma->vm_flags & VM_EXEC))
+ goto bad_area;
+ } else if (!cause) {
+ /* Allow reads even for write-only mappings */
+ if (!(vma->vm_flags & (VM_READ | VM_WRITE)))
+ goto bad_area;
+ } else {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ }
+ tbis(address);
+ handle_mm_fault(vma, address, cause > 0);
+ 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)) {
+ printk("memory violation at pc=%08lx (%08lx)\n", regs.pc, address);
+ die_if_kernel("oops", &regs, cause);
+ 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\n",address);
+ die_if_kernel("Oops", &regs, cause);
+ do_exit(SIGKILL);
+}
diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c
new file mode 100644
index 000000000..62203ef84
--- /dev/null
+++ b/arch/alpha/mm/init.c
@@ -0,0 +1,204 @@
+/*
+ * linux/arch/alpha/mm/init.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+#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>
+#include <asm/hwrpb.h>
+
+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);
+
+/*
+ * 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.
+ */
+pmd_t * __bad_pagetable(void)
+{
+ memset((void *) EMPTY_PGT, 0, PAGE_SIZE);
+ return (pmd_t *) EMPTY_PGT;
+}
+
+pte_t __bad_page(void)
+{
+ memset((void *) EMPTY_PGE, 0, PAGE_SIZE);
+ return pte_mkdirty(mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED));
+}
+
+unsigned long __zero_page(void)
+{
+ memset((void *) ZERO_PGE, 0, PAGE_SIZE);
+ return (unsigned long) ZERO_PGE;
+}
+
+void show_mem(void)
+{
+ int i,free = 0,total = 0,reserved = 0;
+ int shared = 0;
+
+ printk("\nMem-info:\n");
+ show_free_areas();
+ printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10));
+ i = MAP_NR(high_memory);
+ 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
+}
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+static void load_PCB(struct thread_struct * pcb)
+{
+ __asm__ __volatile__(
+ "stq $30,0(%0)\n\t"
+ "bis %0,%0,$16\n\t"
+ "call_pal %1"
+ : /* no outputs */
+ : "r" (pcb), "i" (PAL_swpctx)
+ : "$0", "$1", "$16", "$22", "$23", "$24", "$25");
+}
+
+/*
+ * paging_init() sets up the page tables: in the alpha version this actually
+ * unmaps the bootup page table (as we're now in KSEG, so we don't need it).
+ */
+unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+ int i;
+ unsigned long newptbr;
+ struct memclust_struct * cluster;
+ struct memdesc_struct * memdesc;
+
+ /* initialize mem_map[] */
+ start_mem = free_area_init(start_mem, end_mem);
+
+ /* find free clusters, update mem_map[] accordingly */
+ memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB);
+ cluster = memdesc->cluster;
+ for (i = memdesc->numclusters ; i > 0; i--, cluster++) {
+ unsigned long pfn, nr;
+ if (cluster->usage & 1)
+ continue;
+ pfn = cluster->start_pfn;
+ nr = cluster->numpages;
+
+ /* non-volatile memory. We might want to mark this for later */
+ if (cluster->usage & 2)
+ continue;
+
+ while (nr--)
+ mem_map[pfn++] = 0;
+ }
+
+ /* unmap the console stuff: we don't need it, and we don't want it */
+ /* Also set up the real kernel PCB while we're at it.. */
+ memset((void *) ZERO_PGE, 0, PAGE_SIZE);
+ memset(swapper_pg_dir, 0, PAGE_SIZE);
+ newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
+ pgd_val(swapper_pg_dir[1023]) = (newptbr << 32) | pgprot_val(PAGE_KERNEL);
+ init_task.tss.ptbr = newptbr;
+ init_task.tss.flags = 1;
+ init_task.kernel_stack_page = INIT_STACK;
+ load_PCB(&init_task.tss);
+
+ invalidate_all();
+ return start_mem;
+}
+
+void mem_init(unsigned long start_mem, unsigned long end_mem)
+{
+ unsigned long tmp;
+
+ end_mem &= PAGE_MASK;
+ high_memory = end_mem;
+ start_mem = PAGE_ALIGN(start_mem);
+
+ /*
+ * Mark the pages used by the kernel as reserved..
+ */
+ tmp = KERNEL_START;
+ while (tmp < start_mem) {
+ mem_map[MAP_NR(tmp)] = MAP_PAGE_RESERVED;
+ tmp += PAGE_SIZE;
+ }
+
+
+#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)])
+ continue;
+ mem_map[MAP_NR(tmp)] = 1;
+ free_page(tmp);
+ }
+ tmp = nr_free_pages << PAGE_SHIFT;
+ printk("Memory: %luk available\n", tmp >> 10);
+ return;
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+ int i;
+
+ i = MAP_NR(high_memory);
+ 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/i386/Makefile b/arch/i386/Makefile
index b1aebc71a..0b296da44 100644
--- a/arch/i386/Makefile
+++ b/arch/i386/Makefile
@@ -1,6 +1,11 @@
#
# i386/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.
@@ -8,100 +13,69 @@
# Copyright (C) 1994 by Linus Torvalds
#
-AS86 =as86 -0 -a
-LD86 =ld86 -0
-AS =as
-LD =ld
-HOSTCC =gcc -I$(TOPDIR)/include
-CC =gcc -D__KERNEL__ -I$(TOPDIR)/include
-MAKE =make
-CPP =$(CC) -E
-AR =ar
-STRIP =strip
+#
+# Set these to indicate how to link it..
+#
+# -zmagic:
+#
+# ZLINKFLAGS = -Ttext 0x1000
+# LINKFLAGS = -Ttext 0x100000
+#
+# -qmagic (we need to remove the 32 byte header for bootup purposes)
+#
+ZLINKFLAGS =-qmagic -Ttext 0xfe0
+LINKFLAGS =-qmagic -Ttext 0xfffe0
+CFLAGS := $(CFLAGS) -pipe
ifdef CONFIG_M486
CFLAGS := $(CFLAGS) -m486
else
+ifdef CONFIG_M586
+CFLAGS := $(CFLAGS) -mpentium
+else
CFLAGS := $(CFLAGS) -m386
endif
+endif
-zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem
- $(MAKE) -C zBoot
+HEAD := arch/i386/kernel/head.o
-zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build
- tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage
- sync
+SUBDIRS := $(SUBDIRS) arch/i386/kernel arch/i386/mm
+ARCHIVES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(ARCHIVES)
-zdisk: zImage
- dd bs=8192 if=zImage of=/dev/fd0
+ifdef CONFIG_IBCS
+SUBDIRS := $(SUBDIRS) arch/i386/ibcs
+DRIVERS := $(DRIVERS) arch/i386/ibcs/ibcs.o
+endif
-zlilo: $(CONFIGURE) zImage
- if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
- if [ -f $(INSTALL_PATH)/zSystem.map ]; then mv $(INSTALL_PATH)/zSystem.map $(INSTALL_PATH)/zSystem.old; fi
- cat zImage > $(INSTALL_PATH)/vmlinuz
- cp zSystem.map $(INSTALL_PATH)/
- if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
+ifdef CONFIG_MATH_EMULATION
+SUBDIRS := $(SUBDIRS) arch/i386/math-emu
+DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a
+endif
-#
-# Set these to indicate how to link it..
-#
-# -zmagic:
-#
-#LOWLDFLAGS = -Ttext 0x1000
-#HIGHLDFLAGS = -Ttext 0x100000
-#
-# -qmagic (we need to remove the 32 byte header for bootup purposes)
-#
-LOWLDFLAGS =-qmagic -Ttext 0xfe0
-HIGHLDFLAGS =-qmagic -Ttext 0xfffe0
-
-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) \
- -o tools/system
- nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
- sort > System.map
-
-boot/setup: boot/setup.o
- $(LD86) -s -o $@ $<
-
-boot/setup.o: boot/setup.s
- $(AS86) -o $@ $<
-
-boot/setup.s: boot/setup.S $(CONFIGURE) include/linux/config.h Makefile
- $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
-
-boot/bootsect: boot/bootsect.o
- $(LD86) -s -o $@ $<
-
-boot/bootsect.o: boot/bootsect.s
- $(AS86) -o $@ $<
-
-boot/bootsect.s: boot/bootsect.S $(CONFIGURE) include/linux/config.h Makefile
- $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
-
-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) \
- -o tools/zSystem
- nm tools/zSystem | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | \
- sort > zSystem.map
+arch/i386/kernel: dummy
+ $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel
-#
-# Leave these dummy entries for now to tell people that they are going away..
-#
-lilo:
- @echo
- @echo Uncompressed kernel images no longer supported. Use
- @echo \"make zlilo\" instead.
- @echo
- @exit 1
+arch/i386/mm: dummy
+ $(MAKE) linuxsubdirs SUBDIRS=arch/i386/mm
+
+MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+
+zImage: vmlinux
+ @$(MAKEBOOT) zImage
+
+compressed: zImage
+
+zlilo: vmlinux
+ @$(MAKEBOOT) zlilo
+
+zdisk: vmlinux
+ @$(MAKEBOOT) zdisk
+
+install: vmlinux
+ @$(MAKEBOOT) install
archclean:
- rm -f boot/bootsect boot/setup
+ @$(MAKEBOOT) clean
+
+archdep:
+ @$(MAKEBOOT) dep
diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile
new file mode 100644
index 000000000..b6a5f1c15
--- /dev/null
+++ b/arch/i386/boot/Makefile
@@ -0,0 +1,60 @@
+#
+# arch/i386/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) 1994 by Linus Torvalds
+#
+
+AS86 =as86 -0 -a
+LD86 =ld86 -0
+
+zImage: $(CONFIGURE) bootsect setup compressed/vmlinux tools/build
+ tools/build bootsect setup compressed/vmlinux $(ROOT_DEV) > zImage
+ sync
+
+compressed/vmlinux: $(TOPDIR)/vmlinux
+ @$(MAKE) -C compressed vmlinux
+
+zdisk: zImage
+ dd bs=8192 if=zImage of=/dev/fd0
+
+zlilo: $(CONFIGURE) zImage
+ if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
+ if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
+ cat zImage > $(INSTALL_PATH)/vmlinuz
+ cp $(TOPDIR)/System.map $(INSTALL_PATH)/
+ if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
+
+install: $(CONFIGURE) zImage
+ sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+tools/build: tools/build.c
+ $(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
+
+setup: setup.o
+ $(LD86) -s -o $@ $<
+
+setup.o: setup.s
+ $(AS86) -o $@ $<
+
+setup.s: setup.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
+ $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
+
+bootsect: bootsect.o
+ $(LD86) -s -o $@ $<
+
+bootsect.o: bootsect.s
+ $(AS86) -o $@ $<
+
+bootsect.s: bootsect.S $(CONFIGURE) $(TOPDIR)/include/linux/config.h Makefile
+ $(CPP) -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
+
+dep:
+
+clean:
+ rm -f bootsect setup
+ rm -f zImage tools/build
+ @$(MAKE) -C compressed clean
diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile
new file mode 100644
index 000000000..b4e3f8482
--- /dev/null
+++ b/arch/i386/boot/compressed/Makefile
@@ -0,0 +1,41 @@
+#
+# 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) -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+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/i386/boot/compressed/crypt.h b/arch/i386/boot/compressed/crypt.h
new file mode 100644
index 000000000..2a4c203ca
--- /dev/null
+++ b/arch/i386/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/i386/boot/compressed/gzip.h b/arch/i386/boot/compressed/gzip.h
new file mode 100644
index 000000000..2f738b945
--- /dev/null
+++ b/arch/i386/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/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S
new file mode 100644
index 000000000..6035cea85
--- /dev/null
+++ b/arch/i386/boot/compressed/head.S
@@ -0,0 +1,59 @@
+/*
+ * linux/boot/head.S
+ *
+ * Copyright (C) 1991, 1992, 1993 Linus Torvalds
+ */
+
+/*
+ * head.S contains the 32-bit startup code.
+ *
+ * NOTE!!! Startup happens at absolute address 0x00001000, which is also where
+ * the page directory will exist. The startup code will be overwritten by
+ * the page directory.
+ *
+ * Page 0 is deliberately kept safe, since System Management Mode code in
+ * laptops may need to access the BIOS data stored there. This is also
+ * useful for future device drivers that either access the BIOS via VM86
+ * mode.
+ */
+.text
+
+#define __ASSEMBLY__
+#include <asm/segment.h>
+
+startup_32:
+ cld
+ cli
+ movl $(KERNEL_DS),%eax
+ mov %ax,%ds
+ mov %ax,%es
+ mov %ax,%fs
+ mov %ax,%gs
+ lss _stack_start,%esp
+ xorl %eax,%eax
+1: incl %eax # check that A20 really IS enabled
+ movl %eax,0x000000 # loop forever if it isn't
+ cmpl %eax,0x100000
+ je 1b
+/*
+ * Initialize eflags. Some BIOS's leave bits like NT set. This would
+ * confuse the debugger if this code is traced.
+ * XXX - best to initialize before switching to protected mode.
+ */
+ pushl $0
+ popfl
+/*
+ * Clear BSS
+ */
+ xorl %eax,%eax
+ movl $__edata,%edi
+ movl $__end,%ecx
+ subl %edi,%ecx
+ cld
+ rep
+ stosb
+/*
+ * Do the decompression, and jump to the new kernel..
+ */
+ call _decompress_kernel
+ ljmp $(KERNEL_CS), $0x100000
diff --git a/arch/i386/boot/compressed/inflate.c b/arch/i386/boot/compressed/inflate.c
new file mode 100644
index 000000000..848fef6ae
--- /dev/null
+++ b/arch/i386/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/i386/boot/compressed/lzw.h b/arch/i386/boot/compressed/lzw.h
new file mode 100644
index 000000000..4e640f5a2
--- /dev/null
+++ b/arch/i386/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/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c
new file mode 100644
index 000000000..2623925c7
--- /dev/null
+++ b/arch/i386/boot/compressed/misc.c
@@ -0,0 +1,418 @@
+/*
+ * misc.c
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * 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;
+
+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;
+
+ 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 (4096)
+
+long user_stack [STACK_SIZE];
+
+struct {
+ long * a;
+ short b;
+ } stack_start = { & user_stack [STACK_SIZE] , KERNEL_DS };
+
+void decompress_kernel()
+{
+ if (SCREEN_INFO.orig_video_mode == 7)
+ vidmem = (char *) 0xb0000;
+ else
+ vidmem = (char *) 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(in)
+ 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/i386/boot/compressed/piggyback.c b/arch/i386/boot/compressed/piggyback.c
new file mode 100644
index 000000000..40284118b
--- /dev/null
+++ b/arch/i386/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/i386/boot/compressed/unzip.c b/arch/i386/boot/compressed/unzip.c
new file mode 100644
index 000000000..d4a6617cd
--- /dev/null
+++ b/arch/i386/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/i386/boot/compressed/xtract.c b/arch/i386/boot/compressed/xtract.c
new file mode 100644
index 000000000..91de49ca7
--- /dev/null
+++ b/arch/i386/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/i386/boot/install.sh b/arch/i386/boot/install.sh
new file mode 100644
index 000000000..346ea8ff7
--- /dev/null
+++ b/arch/i386/boot/install.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# arch/i386/boot/install.sh
+#
+# 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 Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+#
+# "make install" script for i386 architecture
+#
+# Arguments:
+# $1 - kernel version
+# $2 - kernel image file
+# $3 - kernel map file
+# $4 - default install path (blank if root directory)
+#
+
+# User may have a custom install script
+
+if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi
+
+# Default install - same as make zlilo
+
+if [ -f $4/vmlinuz ]; then
+ mv $4/vmlinuz $4/vmlinuz.old
+fi
+
+if [ -f $4/System.map ]; then
+ mv $4/System.map $4/System.old
+fi
+
+cat $2 > $4/vmlinuz
+cp $3 $4/System.map
+
+if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c
new file mode 100644
index 000000000..31277a019
--- /dev/null
+++ b/arch/i386/boot/tools/build.c
@@ -0,0 +1,238 @@
+/*
+ * linux/tools/build.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * This file builds a disk-image from three different files:
+ *
+ * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest
+ * - setup: 8086 machine code, sets up system parm
+ * - system: 80386 code for actual system
+ *
+ * It does some checking that all files are of the correct type, and
+ * just writes the result to stdout, removing headers and padding to
+ * the right amount. It also writes some system data to stderr.
+ */
+
+/*
+ * Changes by tytso to allow root device specification
+ */
+
+#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 <linux/a.out.h>
+#include <linux/config.h>
+
+#define MINIX_HEADER 32
+
+#define N_MAGIC_OFFSET 1024
+static int GCC_HEADER = sizeof(struct exec);
+
+#define SYS_SIZE DEF_SYSSIZE
+
+#define DEFAULT_MAJOR_ROOT 0
+#define DEFAULT_MINOR_ROOT 0
+
+/* max nr of sectors of setup: don't change unless you also change
+ * bootsect etc */
+#define SETUP_SECTS 4
+
+#define STRINGIFY(x) #x
+
+typedef union {
+ long l;
+ short s[2];
+ char b[4];
+} conv;
+
+long intel_long(long l)
+{
+ conv t;
+
+ t.b[0] = l & 0xff; l >>= 8;
+ t.b[1] = l & 0xff; l >>= 8;
+ t.b[2] = l & 0xff; l >>= 8;
+ t.b[3] = l & 0xff; l >>= 8;
+ return t.l;
+}
+
+short intel_short(short l)
+{
+ conv t;
+
+ t.b[0] = l & 0xff; l >>= 8;
+ t.b[1] = l & 0xff; l >>= 8;
+ return t.s[0];
+}
+
+void die(char * str)
+{
+ fprintf(stderr,"%s\n",str);
+ exit(1);
+}
+
+void usage(void)
+{
+ die("Usage: build bootsect setup system [rootdev] [> image]");
+}
+
+int main(int argc, char ** argv)
+{
+ int i,c,id, sz;
+ unsigned long sys_size;
+ char buf[1024];
+ struct exec *ex = (struct exec *)buf;
+ char major_root, minor_root;
+ struct stat sb;
+ unsigned char setup_sectors;
+
+ if ((argc < 4) || (argc > 5))
+ usage();
+ if (argc > 4) {
+ if (!strcmp(argv[4], "CURRENT")) {
+ if (stat("/", &sb)) {
+ perror("/");
+ die("Couldn't stat /");
+ }
+ major_root = major(sb.st_dev);
+ minor_root = minor(sb.st_dev);
+ } else if (strcmp(argv[4], "FLOPPY")) {
+ if (stat(argv[4], &sb)) {
+ perror(argv[4]);
+ die("Couldn't stat root device.");
+ }
+ major_root = major(sb.st_rdev);
+ minor_root = minor(sb.st_rdev);
+ } else {
+ major_root = 0;
+ minor_root = 0;
+ }
+ } else {
+ major_root = DEFAULT_MAJOR_ROOT;
+ minor_root = DEFAULT_MINOR_ROOT;
+ }
+ fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
+ for (i=0;i<sizeof buf; i++) buf[i]=0;
+ if ((id=open(argv[1],O_RDONLY,0))<0)
+ die("Unable to open 'boot'");
+ if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
+ die("Unable to read header of 'boot'");
+ if (((long *) buf)[0]!=intel_long(0x04100301))
+ die("Non-Minix header of 'boot'");
+ if (((long *) buf)[1]!=intel_long(MINIX_HEADER))
+ die("Non-Minix header of 'boot'");
+ if (((long *) buf)[3] != 0)
+ die("Illegal data segment in 'boot'");
+ if (((long *) buf)[4] != 0)
+ die("Illegal bss in 'boot'");
+ if (((long *) buf)[5] != 0)
+ die("Non-Minix header of 'boot'");
+ if (((long *) buf)[7] != 0)
+ die("Illegal symbol table in 'boot'");
+ i=read(id,buf,sizeof buf);
+ fprintf(stderr,"Boot sector %d bytes.\n",i);
+ if (i != 512)
+ die("Boot block must be exactly 512 bytes");
+ if ((*(unsigned short *)(buf+510)) != (unsigned short)intel_short(0xAA55))
+ die("Boot block hasn't got boot flag (0xAA55)");
+ buf[508] = (char) minor_root;
+ buf[509] = (char) major_root;
+ i=write(1,buf,512);
+ if (i!=512)
+ die("Write call failed");
+ close (id);
+
+ if ((id=open(argv[2],O_RDONLY,0))<0)
+ die("Unable to open 'setup'");
+ if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
+ die("Unable to read header of 'setup'");
+ if (((long *) buf)[0]!=intel_long(0x04100301))
+ die("Non-Minix header of 'setup'");
+ if (((long *) buf)[1]!=intel_long(MINIX_HEADER))
+ die("Non-Minix header of 'setup'");
+ if (((long *) buf)[3] != 0)
+ die("Illegal data segment in 'setup'");
+ if (((long *) buf)[4] != 0)
+ die("Illegal bss in 'setup'");
+ if (((long *) buf)[5] != 0)
+ die("Non-Minix header of 'setup'");
+ if (((long *) buf)[7] != 0)
+ die("Illegal symbol table in 'setup'");
+ for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
+ if (write(1,buf,c)!=c)
+ die("Write call failed");
+ if (c != 0)
+ die("read-error on 'setup'");
+ close (id);
+ setup_sectors = (unsigned char)((i + 511) / 512);
+ /* for compatibility with LILO */
+ if (setup_sectors < SETUP_SECTS)
+ setup_sectors = SETUP_SECTS;
+ fprintf(stderr,"Setup is %d bytes.\n",i);
+ for (c=0 ; c<sizeof(buf) ; c++)
+ buf[c] = '\0';
+ while (i < setup_sectors * 512) {
+ c = setup_sectors * 512 - i;
+ if (c > sizeof(buf))
+ c = sizeof(buf);
+ if (write(1,buf,c) != c)
+ die("Write call failed");
+ i += c;
+ }
+
+ if ((id=open(argv[3],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'");
+ fprintf(stderr,"System is %d kB (%d kB code, %d kB data and %d kB bss)\n",
+ (ex->a_text+ex->a_data+ex->a_bss)/1024,
+ ex->a_text /1024,
+ ex->a_data /1024,
+ ex->a_bss /1024);
+ sz = N_SYMOFF(*ex) - GCC_HEADER + 4;
+ sys_size = (sz + 15) / 16;
+ if (sys_size > SYS_SIZE)
+ die("System is too big");
+ while (sz > 0) {
+ 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'");
+ }
+ if (write(1, buf, l) != l)
+ die("Write failed");
+ sz -= l;
+ }
+ close(id);
+ if (lseek(1, 497, 0) == 497) {
+ if (write(1, &setup_sectors, 1) != 1)
+ die("Write of setup sectors failed");
+ }
+ if (lseek(1,500,0) == 500) {
+ buf[0] = (sys_size & 0xff);
+ buf[1] = ((sys_size >> 8) & 0xff);
+ if (write(1, buf, 2) != 2)
+ die("Write failed");
+ }
+ return(0);
+}
diff --git a/arch/i386/bootsect.S b/arch/i386/bootsect.S
deleted file mode 100644
index f6a0d3158..000000000
--- a/arch/i386/bootsect.S
+++ /dev/null
@@ -1,460 +0,0 @@
-!
-! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
-! 0x7F00 is 0x7F000 bytes = 508kB, more than enough for current
-! versions of linux which compress the kernel
-!
-#include <linux/config.h>
-SYSSIZE = DEF_SYSSIZE
-!
-! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds
-! modified by Drew Eckhardt
-! modified by Bruce Evans (bde)
-!
-! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
-! itself out of the way to address 0x90000, and jumps there.
-!
-! bde - should not jump blindly, there may be systems with only 512K low
-! memory. Use int 0x12 to get the top of memory, etc.
-!
-! It then loads 'setup' directly after itself (0x90200), and the system
-! at 0x10000, using BIOS interrupts.
-!
-! NOTE! currently system is at most (8*65536-4096) bytes long. This should
-! be no problem, even in the future. I want to keep it simple. This 508 kB
-! kernel size should be enough, especially as this doesn't contain the
-! buffer cache as in minix (and especially now that the kernel is
-! compressed :-)
-!
-! The loader has been made as simple as possible, and continuous
-! read errors will result in a unbreakable loop. Reboot by hand. It
-! loads pretty fast by getting whole tracks at a time whenever possible.
-
-.text
-
-SETUPSECS = 4 ! nr of setup-sectors
-BOOTSEG = 0x07C0 ! original address of boot-sector
-INITSEG = DEF_INITSEG ! we move boot here - out of the way
-SETUPSEG = DEF_SETUPSEG ! setup starts here
-SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
-
-! ROOT_DEV & SWAP_DEV are now written by "build".
-ROOT_DEV = 0
-SWAP_DEV = 0
-#ifndef SVGA_MODE
-#define SVGA_MODE ASK_VGA
-#endif
-#ifndef RAMDISK
-#define RAMDISK 0
-#endif
-#ifndef CONFIG_ROOT_RDONLY
-#define CONFIG_ROOT_RDONLY 0
-#endif
-
-! ld86 requires an entry symbol. This may as well be the usual one.
-.globl _main
-_main:
-#if 0 /* hook for debugger, harmless unless BIOS is fussy (old HP) */
- int 3
-#endif
- mov ax,#BOOTSEG
- mov ds,ax
- mov ax,#INITSEG
- mov es,ax
- mov cx,#256
- sub si,si
- sub di,di
- cld
- rep
- movsw
- jmpi go,INITSEG
-
-! ax and es already contain INITSEG
-
-go: mov di,#0x4000-12 ! 0x4000 is arbitrary value >= length of
- ! bootsect + length of setup + room for stack
- ! 12 is disk parm size
-
-! bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
-! wouldn't have to worry about this if we checked the top of memory. Also
-! my BIOS can be configured to put the wini drive tables in high memory
-! instead of in the vector table. The old stack might have clobbered the
-! drive table.
-
- mov ds,ax
- mov ss,ax ! put stack at INITSEG:0x4000-12.
- mov sp,di
-/*
- * Many BIOS's default disk parameter tables will not
- * recognize multi-sector reads beyond the maximum sector number
- * specified in the default diskette parameter tables - this may
- * mean 7 sectors in some cases.
- *
- * Since single sector reads are slow and out of the question,
- * we must take care of this by creating new parameter tables
- * (for the first disk) in RAM. We will set the maximum sector
- * count to 36 - the most we will encounter on an ED 2.88.
- *
- * High doesn't hurt. Low does.
- *
- * Segments are as follows: ds=es=ss=cs - INITSEG,
- * fs = 0, gs is unused.
- */
-
-! cx contains 0 from rep movsw above
-
- mov fs,cx
- mov bx,#0x78 ! fs:bx is parameter table address
- push ds
- seg fs
- lds si,(bx) ! ds:si is source
-
- mov cl,#6 ! copy 12 bytes
- cld
- push di
-
- rep
- movsw
-
- pop di
- pop ds
-
- movb 4(di),*36 ! patch sector count
-
- seg fs
- mov (bx),di
- seg fs
- mov 2(bx),es
-
-! load the setup-sectors directly after the bootblock.
-! Note that 'es' is already set up.
-! Also cx is 0 from rep movsw above.
-
-load_setup:
- xor ah,ah ! reset FDC
- xor dl,dl
- int 0x13
-
- xor dx, dx ! drive 0, head 0
- mov cl,#0x02 ! sector 2, track 0
- mov bx,#0x0200 ! address = 512, in INITSEG
- mov ah,#0x02 ! service 2, nr of sectors
- mov al,setup_sects ! (assume all on head 0, track 0)
- int 0x13 ! read it
- jnc ok_load_setup ! ok - continue
-
- push ax ! dump error code
- call print_nl
- mov bp, sp
- call print_hex
- pop ax
-
- jmp load_setup
-
-ok_load_setup:
-
-! Get disk drive parameters, specifically nr of sectors/track
-
-#if 0
-
-! bde - the Phoenix BIOS manual says function 0x08 only works for fixed
-! disks. It doesn't work for one of my BIOS's (1987 Award). It was
-! fatal not to check the error code.
-
- xor dl,dl
- mov ah,#0x08 ! AH=8 is get drive parameters
- int 0x13
- xor ch,ch
-#else
-
-! It seems that there is no BIOS call to get the number of sectors. Guess
-! 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read,
-! 15 if sector 15 can be read. Otherwise guess 9.
-
- mov si,#disksizes ! table of sizes to try
-
-probe_loop:
- lodsb
- cbw ! extend to word
- mov sectors, ax
- cmp si,#disksizes+4
- jae got_sectors ! if all else fails, try 9
- xchg ax, cx ! cx = track and sector
- xor dx, dx ! drive 0, head 0
- xor bl, bl
- mov bh,setup_sects
- inc bh
- shl bh,#1 ! address after setup (es = cs)
- mov ax,#0x0201 ! service 2, 1 sector
- int 0x13
- jc probe_loop ! try next value
-
-#endif
-
-got_sectors:
-
-! Restore es
-
- mov ax,#INITSEG
- mov es,ax
-
-! Print some inane message
-
- mov ah,#0x03 ! read cursor pos
- xor bh,bh
- int 0x10
-
- mov cx,#9
- mov bx,#0x0007 ! page 0, attribute 7 (normal)
- mov bp,#msg1
- mov ax,#0x1301 ! write string, move cursor
- int 0x10
-
-! ok, we've written the message, now
-! we want to load the system (at 0x10000)
-
- mov ax,#SYSSEG
- mov es,ax ! segment of 0x010000
- call read_it
- call kill_motor
- call print_nl
-
-! After that we check which root-device to use. If the device is
-! defined (!= 0), nothing is done and the given device is used.
-! Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8),
-! depending on the number of sectors we pretend to know we have.
-
- seg cs
- mov ax,root_dev
- or ax,ax
- jne root_defined
- seg cs
- mov bx,sectors
- mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
- cmp bx,#15
- je root_defined
- mov al,#0x1c ! /dev/PS0 - 1.44Mb
- cmp bx,#18
- je root_defined
- mov al,#0x20 ! /dev/fd0H2880 - 2.88Mb
- cmp bx,#36
- je root_defined
- mov al,#0 ! /dev/fd0 - autodetect
-root_defined:
- seg cs
- mov root_dev,ax
-
-! after that (everything loaded), we jump to
-! the setup-routine loaded directly after
-! the bootblock:
-
- jmpi 0,SETUPSEG
-
-! This routine loads the system at address 0x10000, making sure
-! no 64kB boundaries are crossed. We try to load it as fast as
-! possible, loading whole tracks whenever we can.
-!
-! in: es - starting address segment (normally 0x1000)
-!
-sread: .word 0 ! sectors read of current track
-head: .word 0 ! current head
-track: .word 0 ! current track
-
-read_it:
- mov al,setup_sects
- inc al
- mov sread,al
- mov ax,es
- test ax,#0x0fff
-die: jne die ! es must be at 64kB boundary
- xor bx,bx ! bx is starting address within segment
-rp_read:
- mov ax,es
- sub ax,#SYSSEG
- cmp ax,syssize ! have we loaded all yet?
- jbe ok1_read
- ret
-ok1_read:
- mov ax,sectors
- sub ax,sread
- mov cx,ax
- shl cx,#9
- add cx,bx
- jnc ok2_read
- je ok2_read
- xor ax,ax
- sub ax,bx
- shr ax,#9
-ok2_read:
- call read_track
- mov cx,ax
- add ax,sread
- cmp ax,sectors
- jne ok3_read
- mov ax,#1
- sub ax,head
- jne ok4_read
- inc track
-ok4_read:
- mov head,ax
- xor ax,ax
-ok3_read:
- mov sread,ax
- shl cx,#9
- add bx,cx
- jnc rp_read
- mov ax,es
- add ah,#0x10
- mov es,ax
- xor bx,bx
- jmp rp_read
-
-read_track:
- pusha
- pusha
- mov ax, #0xe2e ! loading... message 2e = .
- mov bx, #7
- int 0x10
- popa
-
- mov dx,track
- mov cx,sread
- inc cx
- mov ch,dl
- mov dx,head
- mov dh,dl
- and dx,#0x0100
- mov ah,#2
-
- push dx ! save for error dump
- push cx
- push bx
- push ax
-
- int 0x13
- jc bad_rt
- add sp, #8
- popa
- ret
-
-bad_rt: push ax ! save error code
- call print_all ! ah = error, al = read
-
-
- xor ah,ah
- xor dl,dl
- int 0x13
-
-
- add sp, #10
- popa
- jmp read_track
-
-/*
- * print_all is for debugging purposes.
- * It will print out all of the registers. The assumption is that this is
- * called from a routine, with a stack frame like
- * dx
- * cx
- * bx
- * ax
- * error
- * ret <- sp
- *
-*/
-
-print_all:
- mov cx, #5 ! error code + 4 registers
- mov bp, sp
-
-print_loop:
- push cx ! save count left
- call print_nl ! nl for readability
-
- cmp cl, #5
- jae no_reg ! see if register name is needed
-
- mov ax, #0xe05 + 'A - 1
- sub al, cl
- int 0x10
-
- mov al, #'X
- int 0x10
-
- mov al, #':
- int 0x10
-
-no_reg:
- add bp, #2 ! next register
- call print_hex ! print it
- pop cx
- loop print_loop
- ret
-
-print_nl:
- mov ax, #0xe0d ! CR
- int 0x10
- mov al, #0xa ! LF
- int 0x10
- ret
-
-/*
- * print_hex is for debugging purposes, and prints the word
- * pointed to by ss:bp in hexadecimal.
-*/
-
-print_hex:
- mov cx, #4 ! 4 hex digits
- mov dx, (bp) ! load word into dx
-print_digit:
- rol dx, #4 ! rotate so that lowest 4 bits are used
- mov ax, #0xe0f ! ah = request, al = mask for nybble
- and al, dl
- add al, #0x90 ! convert al to ascii hex (four instructions)
- daa
- adc al, #0x40
- daa
- int 0x10
- loop print_digit
- ret
-
-
-/*
- * This procedure turns off the floppy drive motor, so
- * that we enter the kernel in a known state, and
- * don't have to worry about it later.
- */
-kill_motor:
- push dx
- mov dx,#0x3f2
- xor al, al
- outb
- pop dx
- ret
-
-sectors:
- .word 0
-
-disksizes:
- .byte 36,18,15,9
-
-msg1:
- .byte 13,10
- .ascii "Loading"
-
-.org 497
-setup_sects:
- .byte SETUPSECS
-root_flags:
- .word CONFIG_ROOT_RDONLY
-syssize:
- .word SYSSIZE
-swap_dev:
- .word SWAP_DEV
-ram_size:
- .word RAMDISK
-vid_mode:
- .word SVGA_MODE
-root_dev:
- .word ROOT_DEV
-boot_flag:
- .word 0xAA55
diff --git a/arch/i386/config.in b/arch/i386/config.in
index 2c4bc4ba5..bfbf42acb 100644
--- a/arch/i386/config.in
+++ b/arch/i386/config.in
@@ -7,32 +7,68 @@ comment 'General setup'
bool 'Kernel math emulation' CONFIG_MATH_EMULATION n
bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
-bool 'Normal harddisk support' CONFIG_BLK_DEV_HD y
+bool 'Normal (MFM/RLL) disk and IDE disk/cdrom support' CONFIG_ST506 y
+if [ "$CONFIG_ST506" = "y" ]; then
+ comment 'Please see drivers/block/README.ide for help/info on IDE drives'
+ bool ' Use old disk-only driver for primary i/f' CONFIG_BLK_DEV_HD n
+ 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 y
+ fi
+ if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+ bool ' Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD n
+ fi
+fi
+
bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
bool 'Networking support' CONFIG_NET y
bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
+bool 'PCI bios support' CONFIG_PCI y
+if [ "$CONFIG_PCI" = "y" ]; then
+ bool ' PCI bridge optimisation (experimental)' CONFIG_PCI_OPTIMIZE y
+fi
bool 'System V IPC' CONFIG_SYSVIPC y
bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586 n
+#if [ "$CONFIG_M586" = "n" ]; then
bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
+#fi
+
+comment 'Loadable module support'
+bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS n
if [ "$CONFIG_NET" = "y" ]; then
comment 'Networking options'
bool 'TCP/IP networking' CONFIG_INET y
-if [ "$CONFIG_INET" "=" "y" ]; then
-bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD n
+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'
-bool 'SCSI support?' CONFIG_SCSI n
+bool 'SCSI support?' CONFIG_SCSI y
if [ "$CONFIG_SCSI" = "n" ]; then
@@ -42,22 +78,29 @@ 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 n
-bool 'Scsi CDROM support' CONFIG_BLK_DEV_SR n
-bool 'Scsi generic support' CONFIG_CHR_DEV_SG n
+bool 'SCSI disk support' CONFIG_BLK_DEV_SD y
+bool 'SCSI tape support' CONFIG_CHR_DEV_ST n
+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 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n
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
@@ -65,7 +108,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
@@ -80,21 +123,28 @@ 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 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
+bool 'EQL (serial line load balancing) support' CONFIG_EQUALIZER n
bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
bool 'WD80*3 support' CONFIG_WD80x3 n
bool 'SMC Ultra support' CONFIG_ULTRA n
fi
+bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n
bool '3COM cards' CONFIG_NET_VENDOR_3COM y
if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
bool '3c501 support' CONFIG_EL1 n
@@ -107,40 +157,58 @@ 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 'DE425, DE434, DE435 support' CONFIG_DE4X5 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 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
-comment 'CD-ROM drivers'
+comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
-bool 'Mitsumi CDROM driver support' CONFIG_MCD n
+bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
if [ "$CONFIG_SBPCD" = "y" ]; then
bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
@@ -151,6 +219,8 @@ if [ "$CONFIG_SBPCD" = "y" ]; then
fi
fi
fi
+bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
+bool 'Sony CDU535 CDROM driver support' CONFIG_CDU535 n
comment 'Filesystems'
@@ -160,13 +230,14 @@ bool 'Second extended fs support' CONFIG_EXT2_FS y
bool 'xiafs filesystem support' CONFIG_XIA_FS n
bool 'msdos fs support' CONFIG_MSDOS_FS y
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 y
if [ "$CONFIG_INET" = "y" ]; then
bool 'NFS filesystem support' CONFIG_NFS_FS y
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
+if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_AZTCD" = "y" -o "$CONFIG_CDU535" = "y" ]; then
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
else
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
@@ -176,6 +247,7 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
comment 'character devices'
+bool 'Cyclades async mux support' CONFIG_CYCLADES n
bool 'Parallel printer support' CONFIG_PRINTER n
bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
@@ -184,7 +256,7 @@ bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
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
@@ -213,7 +285,10 @@ bool 'Sound card support' CONFIG_SOUND n
comment 'Kernel hacking'
#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n
-bool 'Kernel profiling support' CONFIG_PROFILE n
+bool 'Kernel profiling support' CONFIG_PROFILE y
+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/i386/dummy.c b/arch/i386/dummy.c
deleted file mode 100644
index dd2410a7a..000000000
--- a/arch/i386/dummy.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * This file handles Systemcalls not available for all CPUs.
- *
- * Written by Ralf Baechle,
- * Copyright (C) 1994 by Waldorf GMBH
- */
-
-/*
- * Nothing yet for i386...
- */
-
diff --git a/arch/i386/head.S b/arch/i386/head.S
deleted file mode 100644
index e720c14d0..000000000
--- a/arch/i386/head.S
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * linux/boot/head.S
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * head.S contains the 32-bit startup code.
- */
-
-.text
-.globl _idt,_gdt,
-.globl _swapper_pg_dir,_pg0
-.globl _empty_bad_page
-.globl _empty_bad_page_table
-.globl _empty_zero_page
-.globl _floppy_track_buffer
-
-#include <linux/tasks.h>
-#include <linux/segment.h>
-#define ASSEMBLER
-#include <linux/fd.h>
-
-#define CL_MAGIC_ADDR 0x90020
-#define CL_MAGIC 0xA33F
-#define CL_BASE_ADDR 0x90000
-#define CL_OFFSET 0x90022
-
-/*
- * swapper_pg_dir is the main page directory, address 0x00001000 (or at
- * address 0x00101000 for a compressed boot).
- */
-startup_32:
- cld
- movl $(KERNEL_DS),%eax
- mov %ax,%ds
- mov %ax,%es
- mov %ax,%fs
- mov %ax,%gs
- lss _stack_start,%esp
-/*
- * Clear BSS first so that there are no surprises...
- */
- xorl %eax,%eax
- movl $__edata,%edi
- movl $__end,%ecx
- subl %edi,%ecx
- cld
- rep
- stosb
-/*
- * start system 32-bit setup. We need to re-do some of the things done
- * in 16-bit mode for the "real" operations.
- */
- call setup_idt
- xorl %eax,%eax
-1: incl %eax # check that A20 really IS enabled
- movl %eax,0x000000 # loop forever if it isn't
- cmpl %eax,0x100000
- je 1b
-/*
- * Initialize eflags. Some BIOS's leave bits like NT set. This would
- * confuse the debugger if this code is traced.
- * XXX - best to initialize before switching to protected mode.
- */
- pushl $0
- popfl
-/*
- * Copy bootup parameters out of the way. First 2kB of
- * _empty_zero_page is for boot parameters, second 2kB
- * is for the command line.
- */
- 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
-1:
-/* check if it is 486 or 386. */
-/*
- * XXX - this does a lot of unnecessary setup. Alignment checks don't
- * apply at our cpl of 0 and the stack ought to be aligned already, and
- * we don't need to preserve eflags.
- */
- movl %esp,%edi # save stack pointer
- andl $0xfffffffc,%esp # align stack to avoid AC fault
- movl $3,_x86
- pushfl # push EFLAGS
- popl %eax # get EFLAGS
- movl %eax,%ecx # save original EFLAGS
- xorl $0x40000,%eax # flip AC bit in EFLAGS
- pushl %eax # copy to EFLAGS
- popfl # set EFLAGS
- pushfl # get new EFLAGS
- popl %eax # put it in eax
- xorl %ecx,%eax # change in flags
- andl $0x40000,%eax # check if AC bit changed
- je is386
- movl $4,_x86
- movl %ecx,%eax
- xorl $0x200000,%eax # check ID flag
- pushl %eax
- popfl # if we are on a straight 486DX, SX, or
- pushfl # 487SX we can't change it
- popl %eax
- xorl %ecx,%eax
- andl $0x200000,%eax
- je is486
-isnew: pushl %ecx # restore original EFLAGS
- popfl
- movl $1, %eax # Use the CPUID instruction to
- .byte 0x0f, 0xa2 # check the processor type
- andl $0xf00, %eax # Set _x86 with the family
- shrl $8, %eax # returned.
- movl %eax, _x86
- movl %edi,%esp # restore esp
- movl %cr0,%eax # 486+
- andl $0x80000011,%eax # Save PG,PE,ET
- orl $0x50022,%eax # set AM, WP, NE and MP
- jmp 2f
-is486: pushl %ecx # restore original EFLAGS
- popfl
- movl %edi,%esp # restore esp
- movl %cr0,%eax # 486
- andl $0x80000011,%eax # Save PG,PE,ET
- orl $0x50022,%eax # set AM, WP, NE and MP
- jmp 2f
-is386: pushl %ecx # restore original EFLAGS
- popfl
- movl %edi,%esp # restore esp
- movl %cr0,%eax # 386
- andl $0x80000011,%eax # Save PG,PE,ET
- orl $2,%eax # set MP
-2: movl %eax,%cr0
- call check_x87
- call setup_paging
- lgdt gdt_descr
- lidt idt_descr
- ljmp $(KERNEL_CS),$1f
-1: movl $(KERNEL_DS),%eax # reload all the segment registers
- mov %ax,%ds # after changing gdt.
- mov %ax,%es
- mov %ax,%fs
- mov %ax,%gs
- lss _stack_start,%esp
- xorl %eax,%eax
- lldt %ax
- pushl %eax # These are the parameters to main :-)
- pushl %eax
- pushl %eax
- cld # gcc2 wants the direction flag cleared at all times
- call _start_kernel
-L6:
- jmp L6 # main should never return here, but
- # just in case, we know what happens.
-
-/*
- * We depend on ET to be correct. This checks for 287/387.
- */
-check_x87:
- movl $0,_hard_math
- clts
- fninit
- fstsw %ax
- cmpb $0,%al
- je 1f
- movl %cr0,%eax /* no coprocessor: have to set bits */
- xorl $4,%eax /* set EM */
- movl %eax,%cr0
- ret
-.align 2
-1: movl $1,_hard_math
- .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
- ret
-
-/*
- * setup_idt
- *
- * sets up a idt with 256 entries pointing to
- * ignore_int, interrupt gates. It doesn't actually load
- * idt - that can be done only after paging has been enabled
- * and the kernel moved to 0xC0000000. Interrupts
- * are enabled elsewhere, when we can be relatively
- * sure everything is ok.
- */
-setup_idt:
- lea ignore_int,%edx
- movl $(KERNEL_CS << 16),%eax
- movw %dx,%ax /* selector = 0x0010 = cs */
- movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
-
- lea _idt,%edi
- mov $256,%ecx
-rp_sidt:
- movl %eax,(%edi)
- movl %edx,4(%edi)
- addl $8,%edi
- dec %ecx
- jne rp_sidt
- ret
-
-
-/*
- * Setup_paging
- *
- * This routine sets up paging by setting the page bit
- * in cr0. The page tables are set up, identity-mapping
- * the first 4MB. The rest are initialized later.
- *
- * (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
- * (ref: update, 25Sept92) -- croutons@crunchy.uucp
- * (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
- */
-.align 2
-setup_paging:
- movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
- xorl %eax,%eax
- movl $_swapper_pg_dir,%edi /* swapper_pg_dir is at 0x1000 */
- cld;rep;stosl
-/* Identity-map the kernel in low 4MB memory for ease of transition */
- movl $_pg0+7,_swapper_pg_dir /* set present bit/user r/w */
-/* But the real place is at 0xC0000000 */
- movl $_pg0+7,_swapper_pg_dir+3072 /* set present bit/user r/w */
- movl $_pg0+4092,%edi
- movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
- std
-1: stosl /* fill the page backwards - more efficient :-) */
- subl $0x1000,%eax
- jge 1b
- cld
- movl $_swapper_pg_dir,%eax
- movl %eax,%cr3 /* cr3 - page directory start */
- movl %cr0,%eax
- orl $0x80000000,%eax
- movl %eax,%cr0 /* set paging (PG) bit */
- ret /* this also flushes the prefetch-queue */
-
-/*
- * 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.....
- */
-.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
-_empty_zero_page:
-
-.org 0x6000
-/*
- * 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*MAX_BUFFER_SECTORS,1,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
-
-/*
- * The interrupt descriptor table has room for 256 idt's
- */
-.align 4
-.word 0
-idt_descr:
- .word 256*8-1 # idt contains 256 entries
- .long 0xc0000000+_idt
-
-.align 4
-_idt:
- .fill 256,8,0 # idt is uninitialized
-
-.align 4
-.word 0
-gdt_descr:
- .word (8+2*NR_TASKS)*8-1
- .long 0xc0000000+_gdt
-
-/*
- * This gdt setup gives the kernel a 1GB address space at virtual
- * address 0xC0000000 - space enough for expansion, I hope.
- */
-.align 4
-_gdt:
- .quad 0x0000000000000000 /* NULL descriptor */
- .quad 0x0000000000000000 /* not used */
- .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */
- .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */
- .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */
- .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */
- .quad 0x0000000000000000 /* not used */
- .quad 0x0000000000000000 /* not used */
- .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */
diff --git a/arch/i386/ibcs/Makefile b/arch/i386/ibcs/Makefile
new file mode 100644
index 000000000..7b81ff2bc
--- /dev/null
+++ b/arch/i386/ibcs/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for the iBCS emulator files
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.s:
+ $(CPP) -traditional $< -o $*.s
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+SUBDIRS =
+
+OBJS = emulate.o
+
+ibcs.o: $(OBJS)
+ $(LD) -r -o ibcs.o $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/arch/i386/ibcs/binfmt_coff.c b/arch/i386/ibcs/binfmt_coff.c
new file mode 100644
index 000000000..1a7c760cd
--- /dev/null
+++ b/arch/i386/ibcs/binfmt_coff.c
@@ -0,0 +1,784 @@
+/*
+ * These are the functions used to load COFF IBSC style executables.
+ * Information on COFF format may be obtained in either the Intel Binary
+ * Compatibility Specification 2 or O'Rilley's book on COFF. The shared
+ * libraries are defined only the in the Intel book.
+ *
+ * This file is based upon code written by Eric Youngdale for the ELF object
+ * file format.
+ *
+ * Author: Al Longyear (longyear@sii.com)
+ *
+ * Latest Revision:
+ * 3 February 1994
+ * Al Longyear (longyear@sii.com)
+ * Cleared first page of bss section using put_fs_byte.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/binfmts.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/coff.h>
+#include <linux/malloc.h>
+
+asmlinkage int sys_exit (int exit_code);
+asmlinkage int sys_close (unsigned fd);
+asmlinkage int sys_open (const char *, int, int);
+asmlinkage int sys_uselib(const char * library);
+
+static int preload_library (struct linux_binprm *exe_bprm,
+ COFF_SCNHDR * sect,
+ struct file *fp);
+
+static int load_object (struct linux_binprm *bprm,
+ struct pt_regs *regs,
+ int lib_ok);
+
+/*
+ * Small procedure to test for the proper file alignment.
+ */
+
+static inline int
+is_properly_aligned (COFF_SCNHDR *sect)
+{
+ long scnptr = COFF_LONG (sect->s_scnptr);
+ long vaddr = COFF_LONG (sect->s_vaddr);
+/*
+ * Print the section information if needed
+ */
+
+#ifdef COFF_DEBUG
+ printk ("%s, scnptr = %d, vaddr = %d\n",
+ sect->s_name,
+ scnptr, vaddr);
+#endif
+
+/*
+ * Return the error code if the section is not properly aligned.
+ */
+
+#ifdef COFF_DEBUG
+ if (((vaddr - scnptr) & ~PAGE_MASK) != 0)
+ printk ("bad alignment in %s\n", sect->s_name);
+#endif
+ return ((((vaddr - scnptr) & ~PAGE_MASK) != 0) ? -ENOEXEC : 0);
+}
+
+/*
+ * Clear the bytes in the last page of data.
+ */
+
+static
+int clear_memory (unsigned long addr, unsigned long size)
+{
+ int status;
+
+ size = (PAGE_SIZE - (addr & ~PAGE_MASK)) & ~PAGE_MASK;
+ if (size == 0)
+ status = 0;
+ else {
+
+#ifdef COFF_DEBUG
+ printk ("un-initialized storage in last page %d\n", size);
+#endif
+
+ status = verify_area (VERIFY_WRITE,
+ (void *) addr, size);
+#ifdef COFF_DEBUG
+ printk ("result from verify_area = %d\n", status);
+#endif
+
+ if (status >= 0)
+ while (size-- != 0)
+ put_fs_byte (0, addr++);
+ }
+ return status;
+}
+
+/*
+ * Helper function to process the load operation.
+ */
+
+static int
+load_object (struct linux_binprm * bprm, struct pt_regs *regs, int lib_ok)
+{
+ COFF_FILHDR *coff_hdr = (COFF_FILHDR *) bprm->buf; /* COFF Header */
+ COFF_SCNHDR *sect_bufr; /* Pointer to section table */
+ COFF_SCNHDR *text_sect; /* Pointer to the text section */
+ COFF_SCNHDR *data_sect; /* Pointer to the data section */
+ COFF_SCNHDR *bss_sect; /* Pointer to the bss section */
+ int text_count; /* Number of text sections */
+ int data_count; /* Number of data sections */
+ int bss_count; /* Number of bss sections */
+ int lib_count; /* Number of lib sections */
+ unsigned int start_addr = 0;/* Starting location for program */
+ int status = 0; /* Result status register */
+ int fd = -1; /* Open file descriptor */
+ struct file *fp = NULL; /* Pointer to the file at "fd" */
+ short int sections = 0; /* Number of sections in the file */
+ short int aout_size = 0; /* Size of the a.out header area */
+ short int flags; /* Flag bits from the COFF header */
+
+#ifdef COFF_DEBUG
+ printk ("binfmt_coff entry: %s\n", bprm->filename);
+#endif
+
+/*
+ * Validate the magic value for the object file.
+ */
+ do {
+ if (COFF_I386BADMAG (*coff_hdr)) {
+#ifdef COFF_DEBUG
+ printk ("bad filehdr magic\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * The object file should have 32 BIT little endian format. Do not allow
+ * it to have the 16 bit object file flag set as Linux is not able to run
+ * on the 80286/80186/8086.
+ */
+ flags = COFF_SHORT (coff_hdr->f_flags);
+ if ((flags & (COFF_F_AR32WR | COFF_F_AR16WR)) != COFF_F_AR32WR) {
+#ifdef COFF_DEBUG
+ printk ("invalid f_flags bits\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * Extract the header information which we need.
+ */
+ sections = COFF_SHORT (coff_hdr->f_nscns); /* Number of sections */
+ aout_size = COFF_SHORT (coff_hdr->f_opthdr); /* Size of opt. headr */
+/*
+ * If the file is not executable then reject the execution. This means
+ * that there must not be external references.
+ */
+ if ((flags & COFF_F_EXEC) == 0) {
+#ifdef COFF_DEBUG
+ printk ("not executable bit\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * There must be at least one section.
+ */
+ if (sections == 0) {
+#ifdef COFF_DEBUG
+ printk ("no sections\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * Do some additional consistency checks.
+ * The system requires mapping for this loader. If you try
+ * to use a file system with no mapping, the format is not valid.
+ */
+ if (!bprm->inode->i_op ||
+ !bprm->inode->i_op->default_file_ops->mmap) {
+#ifdef COFF_DEBUG
+ printk ("no mmap in fs\n");
+#endif
+ status = -ENOEXEC;
+ }
+ }
+ while (0);
+/*
+ * Allocate a buffer to hold the entire coff section list.
+ */
+ if (status >= 0) {
+ int nbytes = sections * COFF_SCNHSZ;
+
+ sect_bufr = (COFF_SCNHDR *) kmalloc (nbytes, GFP_KERNEL);
+ if (0 == sect_bufr) {
+#ifdef COFF_DEBUG
+ printk ("kmalloc failed\n");
+#endif
+ status = -ENOEXEC;
+ }
+/*
+ * Read the section list from the disk file.
+ */
+ else {
+ int old_fs = get_fs ();
+ set_fs (get_ds ()); /* Make it point to the proper location */
+ status = read_exec (bprm->inode, /* INODE for file */
+ aout_size + COFF_FILHSZ, /* Offset in the file */
+ (char *) sect_bufr, /* Buffer for read */
+ nbytes); /* Byte count reqd. */
+ set_fs (old_fs); /* Restore the selector */
+#ifdef COFF_DEBUG
+ if (status < 0)
+ printk ("read aout hdr, status = %d\n", status);
+#endif
+ }
+ }
+ else
+ sect_bufr = NULL; /* Errors do not have a section buffer */
+/*
+ * Count the number of sections for the required types and store the location
+ * of the last section for the three primary types.
+ */
+ text_count = 0;
+ data_count = 0;
+ bss_count = 0;
+ lib_count = 0;
+
+ text_sect = NULL;
+ data_sect = NULL;
+ bss_sect = NULL;
+/*
+ * Loop through the sections and find the various types
+ */
+ if (status >= 0) {
+ int nIndex;
+ COFF_SCNHDR *sect_ptr = sect_bufr;
+
+ for (nIndex = 0; nIndex < sections; ++nIndex) {
+ long int sect_flags = COFF_LONG (sect_ptr->s_flags);
+
+ switch (sect_flags) {
+ case COFF_STYP_TEXT:
+ text_sect = sect_ptr;
+ ++text_count;
+ status = is_properly_aligned (sect_ptr);
+ break;
+
+ case COFF_STYP_DATA:
+ data_sect = sect_ptr;
+ ++data_count;
+ status = is_properly_aligned (sect_ptr);
+ break;
+
+ case COFF_STYP_BSS:
+ bss_sect = sect_ptr;
+ ++bss_count;
+ break;
+
+ case COFF_STYP_LIB:
+#ifdef COFF_DEBUG
+ printk (".lib section found\n");
+#endif
+ ++lib_count;
+ break;
+
+ default:
+ break;
+ }
+ sect_ptr = (COFF_SCNHDR *) & ((char *) sect_ptr)[COFF_SCNHSZ];
+ }
+/*
+ * Ensure that there are the required sections. There must be one text
+ * sections and one each of the data and bss sections for an executable.
+ * A library may or may not have a data / bss section.
+ */
+ if (text_count != 1) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("no text sections\n");
+#endif
+ }
+ else {
+ if (lib_ok) {
+ if (data_count != 1 || bss_count != 1) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("no .data nor .bss sections\n");
+#endif
+ }
+ }
+ }
+ }
+/*
+ * If there is no additional header then assume the file starts at
+ * the first byte of the text section. This may not be the proper place,
+ * so the best solution is to include the optional header. A shared library
+ * __MUST__ have an optional header to indicate that it is a shared library.
+ */
+ if (status >= 0) {
+ if (aout_size == 0) {
+ if (!lib_ok) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("no header in library\n");
+#endif
+ }
+ start_addr = COFF_LONG (text_sect->s_vaddr);
+ }
+/*
+ * There is some header. Ensure that it is sufficient.
+ */
+ else {
+ if (aout_size < COFF_AOUTSZ) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("header too small\n");
+#endif
+ }
+ else {
+ COFF_AOUTHDR *aout_hdr = /* Pointer to a.out header */
+ (COFF_AOUTHDR *) & ((char *) coff_hdr)[COFF_FILHSZ];
+ short int aout_magic = COFF_SHORT (aout_hdr->magic); /* id */
+/*
+ * Validate the magic number in the a.out header. If it is valid then
+ * update the starting symbol location. Do not accept these file formats
+ * when loading a shared library.
+ */
+ switch (aout_magic) {
+ case COFF_OMAGIC:
+ case COFF_ZMAGIC:
+ case COFF_STMAGIC:
+ if (!lib_ok) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("wrong a.out header magic\n");
+#endif
+ }
+ start_addr = (unsigned int) COFF_LONG (aout_hdr->entry);
+ break;
+/*
+ * Magic value for a shared library. This is valid only when loading a
+ * shared library. (There is no need for a start_addr. It won't be used.)
+ */
+ case COFF_SHMAGIC:
+ if (lib_ok) {
+#ifdef COFF_DEBUG
+ printk ("wrong a.out header magic\n");
+#endif
+ status = -ENOEXEC;
+ }
+ break;
+
+ default:
+#ifdef COFF_DEBUG
+ printk ("wrong a.out header magic\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+ }
+ }
+ }
+/*
+ * Fetch a file pointer to the executable.
+ */
+ if (status >= 0) {
+ fd = open_inode (bprm->inode, O_RDONLY);
+ if (fd < 0) {
+#ifdef COFF_DEBUG
+ printk ("can not open inode, result = %d\n", fd);
+#endif
+ status = fd;
+ }
+ else
+ fp = current->files->fd[fd];
+ }
+ else
+ fd = -1; /* Invalidate the open file descriptor */
+/*
+ * Generate the proper values for the text fields
+ *
+ * THIS IS THE POINT OF NO RETURN. THE NEW PROCESS WILL TRAP OUT SHOULD
+ * SOMETHING FAIL IN THE LOAD SEQUENCE FROM THIS POINT ONWARD.
+ */
+ if (status >= 0) {
+ long text_scnptr = COFF_LONG (text_sect->s_scnptr);
+ long text_size = COFF_LONG (text_sect->s_size);
+ long text_vaddr = COFF_LONG (text_sect->s_vaddr);
+
+ long data_scnptr;
+ long data_size;
+ long data_vaddr;
+
+ long bss_size;
+ long bss_vaddr;
+/*
+ * Generate the proper values for the data fields
+ */
+ if (data_sect != NULL) {
+ data_scnptr = COFF_LONG (data_sect->s_scnptr);
+ data_size = COFF_LONG (data_sect->s_size);
+ data_vaddr = COFF_LONG (data_sect->s_vaddr);
+ }
+ else {
+ data_scnptr = 0;
+ data_size = 0;
+ data_vaddr = 0;
+ }
+/*
+ * Generate the proper values for the bss fields
+ */
+ if (bss_sect != NULL) {
+ bss_size = COFF_LONG (bss_sect->s_size);
+ bss_vaddr = COFF_LONG (bss_sect->s_vaddr);
+ }
+ else {
+ bss_size = 0;
+ bss_vaddr = 0;
+ }
+/*
+ * Flush the executable from memory. At this point the executable is
+ * committed to being defined or a segmentation violation will occur.
+ */
+ if (lib_ok) {
+#ifdef COFF_DEBUG
+ printk ("flushing executable\n");
+#endif
+ flush_old_exec (bprm);
+/*
+ * Define the initial locations for the various items in the new process
+ */
+ current->mm->mmap = NULL;
+ current->mm->rss = 0;
+/*
+ * Construct the parameter and environment string table entries.
+ */
+ bprm->p += change_ldt (0, bprm->page);
+ bprm->p -= MAX_ARG_PAGES*PAGE_SIZE;
+ bprm->p = (unsigned long) create_tables ((char *) bprm->p,
+ bprm->argc,
+ bprm->envc,
+ 1);
+/*
+ * Do the end processing once the stack has been constructed
+ */
+ current->mm->start_code = text_vaddr & PAGE_MASK;
+ current->mm->end_code = text_vaddr + text_size;
+ current->mm->end_data = data_vaddr + data_size;
+ current->mm->start_brk =
+ current->mm->brk = bss_vaddr + bss_size;
+ current->suid =
+ current->euid = bprm->e_uid;
+ current->sgid =
+ current->egid = bprm->e_gid;
+ current->executable = bprm->inode; /* Store inode for file */
+ ++bprm->inode->i_count; /* Count the open inode */
+ regs->eip = start_addr; /* Current EIP register */
+ regs->esp =
+ current->mm->start_stack = bprm->p;
+ }
+/*
+ * Map the text pages
+ */
+
+#ifdef COFF_DEBUG
+ printk (".text: vaddr = %d, size = %d, scnptr = %d\n",
+ text_vaddr,
+ text_size,
+ text_scnptr);
+#endif
+ status = do_mmap (fp,
+ text_vaddr & PAGE_MASK,
+ text_size + (text_vaddr & ~PAGE_MASK),
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_SHARED,
+ text_scnptr & PAGE_MASK);
+
+ status = (status == (text_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC;
+/*
+ * Map the data pages
+ */
+ if (status >= 0 && data_size != 0) {
+#ifdef COFF_DEBUG
+ printk (".data: vaddr = %d, size = %d, scnptr = %d\n",
+ data_vaddr,
+ data_size,
+ data_scnptr);
+#endif
+ status = do_mmap (fp,
+ data_vaddr & PAGE_MASK,
+ data_size + (data_vaddr & ~PAGE_MASK),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE,
+ data_scnptr & PAGE_MASK);
+
+ status = (status == (data_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC;
+ }
+/*
+ * Construct the bss data for the process. The bss ranges from the
+ * end of the data (which may not be on a page boundary) to the end
+ * of the bss section. Allocate any necessary pages for the data.
+ */
+ if (status >= 0 && bss_size != 0) {
+#ifdef COFF_DEBUG
+ printk (".bss: vaddr = %d, size = %d\n",
+ bss_vaddr,
+ bss_size);
+#endif
+ zeromap_page_range (PAGE_ALIGN (bss_vaddr),
+ PAGE_ALIGN (bss_size),
+ PAGE_COPY);
+
+ status = clear_memory (bss_vaddr, bss_size);
+ }
+/*
+ * Load any shared library for the executable.
+ */
+ if (status >= 0 && lib_ok && lib_count != 0) {
+ int nIndex;
+ COFF_SCNHDR *sect_ptr = sect_bufr;
+/*
+ * Find the library sections. (There should be at least one. It was counted
+ * earlier.) This will eventually recurse to our code and load the shared
+ * library with our own procedures.
+ */
+ for (nIndex = 0; nIndex < sections; ++nIndex) {
+ long int sect_flags = COFF_LONG (sect_ptr->s_flags);
+ if (sect_flags == COFF_STYP_LIB) {
+ status = preload_library (bprm, sect_ptr, fp);
+ if (status != 0)
+ break;
+ }
+ sect_ptr = (COFF_SCNHDR *) &((char *) sect_ptr) [COFF_SCNHSZ];
+ }
+ }
+/*
+ * Generate any needed trap for this process. If an error occurred then
+ * generate a segmentation violation. If the process is being debugged
+ * then generate the load trap. (Note: If this is a library load then
+ * do not generate the trap here. Pass the error to the caller who
+ * will do it for the process in the outer lay of this procedure call.)
+ */
+ if (lib_ok) {
+ if (status < 0)
+ send_sig (SIGSEGV, current, 0); /* Generate the error trap */
+ else {
+ if (current->flags & PF_PTRACED)
+ send_sig (SIGTRAP, current, 0);
+ }
+ status = 0; /* We are committed. It can't fail */
+ }
+ }
+/*
+ * Do any cleanup processing
+ */
+ if (fd >= 0)
+ sys_close (fd); /* Close unused code file */
+
+ if (sect_bufr != NULL)
+ kfree (sect_bufr); /* Release section list buffer */
+/*
+ * Return the completion status.
+ */
+#ifdef COFF_DEBUG
+ printk ("binfmt_coff: result = %d\n", status);
+#endif
+ return (status);
+}
+
+/*
+ * This procedure will load the library listed in the file name given
+ * as the parameter. The result will be non-zero should something fail
+ * to load.
+ */
+
+static int
+preload_this_library (struct linux_binprm *exe_bprm, char *lib_name)
+{
+ int status;
+ int old_fs = get_fs();
+/*
+ * If debugging then print "we have arrived"
+ */
+#ifdef COFF_DEBUG
+ printk ("%s loading shared library %s\n",
+ exe_bprm->filename,
+ lib_name);
+#endif
+/*
+ * Change the FS register to the proper kernel address space and attempt
+ * to load the library. The library name is allocated from the kernel
+ * pool.
+ */
+ set_fs (get_ds ());
+ status = sys_uselib (lib_name);
+ set_fs (old_fs);
+/*
+ * Return the success/failure to the caller.
+ */
+ return (status);
+}
+
+/*
+ * This procedure is called to load a library section. The various
+ * libraries are loaded from the list given in the section data.
+ */
+
+static int
+preload_library (struct linux_binprm *exe_bprm,
+ COFF_SCNHDR * sect, struct file *fp)
+{
+ int status = 0; /* Completion status */
+ long nbytes; /* Count of bytes in the header area */
+/*
+ * Fetch the size of the section. There must be enough room for at least
+ * one entry.
+ */
+ nbytes = COFF_LONG (sect->s_size);
+ if (nbytes < COFF_SLIBSZ) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("library section too small\n");
+#endif
+ }
+/*
+ * Allocate a buffer to hold the section data
+ */
+ else {
+ COFF_SLIBHD *phdr;
+ char *buffer = (char *) kmalloc (nbytes, GFP_KERNEL);
+
+ if (0 == buffer) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("kmalloc failed\n");
+#endif
+ }
+ else {
+ int old_fs = get_fs ();
+/*
+ * Read the section data from the disk file.
+ */
+ set_fs (get_ds ()); /* Make it point to the proper location */
+ status = read_exec (exe_bprm->inode, /* INODE for file */
+ COFF_LONG (sect->s_scnptr), /* Disk location */
+ buffer, /* Buffer for read */
+ nbytes); /* Byte count reqd. */
+ set_fs (old_fs); /* Restore the selector */
+/*
+ * Check the result. The value returned is the byte count actually read.
+ */
+ if (status >= 0 && status != nbytes) {
+#ifdef COFF_DEBUG
+ printk ("read of lib section was short\n");
+#endif
+ status = -ENOEXEC;
+ }
+ }
+/*
+ * At this point, go through the list of libraries in the data area.
+ */
+ phdr = (COFF_SLIBHD *) buffer;
+ while (status >= 0 && nbytes > COFF_SLIBSZ) {
+ int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long);
+ int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long);
+/*
+ * Validate the sizes of the various items. I don't trust the linker!!
+ */
+ if ((unsigned) header_size >= (unsigned) nbytes ||
+ entry_size <= 0 ||
+ (unsigned) entry_size <= (unsigned) header_size) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("header count is invalid\n");
+#endif
+ }
+/*
+ * Load the library. Stop the load process on the first error.
+ */
+ else {
+ status = preload_this_library (exe_bprm,
+ &((char *) phdr)[header_size]);
+#ifdef COFF_DEBUG
+ printk ("preload_this_library result = %d\n", status);
+#endif
+ }
+/*
+ * Point to the next library in the section data.
+ */
+ nbytes -= entry_size;
+ phdr = (COFF_SLIBHD *) &((char *) phdr)[entry_size];
+ }
+/*
+ * Release the space for the library list.
+ */
+ if (buffer != NULL)
+ kfree (buffer);
+ }
+/*
+ * Return the resulting status to the caller.
+ */
+ return (status);
+}
+
+/*
+ * This procedure is called by the main load sequence. It will load
+ * the executable and prepare it for execution. It provides the additional
+ * parameters used by the recursive coff loader and tells the loader that
+ * this is the main executable. How simple it is . . . .
+ */
+
+int
+load_coff_binary (struct linux_binprm *bprm, struct pt_regs *regs)
+{
+ return (load_object (bprm, regs, 1));
+}
+
+/*
+ * Load the image for any shared library.
+ *
+ * This is called when we need to load a library based upon a file name.
+ */
+
+int
+load_coff_library (int fd)
+{
+ struct linux_binprm *bprm; /* Parameters for the load operation */
+ int status; /* Status of the request */
+/*
+ * Read the first portion of the file.
+ */
+ bprm = (struct linux_binprm *) kmalloc (sizeof (struct linux_binprm),
+ GFP_KERNEL);
+ if (0 == bprm) {
+#ifdef COFF_DEBUG
+ printk ("kmalloc failed\n");
+#endif
+ status = -ENOEXEC;
+ }
+ else {
+ struct file *file; /* Pointer to the file table */
+ struct pt_regs regs; /* Register work area */
+ int old_fs = get_fs (); /* Previous FS register value */
+
+ memset (bprm, '\0', sizeof (struct linux_binprm));
+
+ file = current->files->fd[fd];
+ bprm->inode = file->f_inode; /* The only item _really_ needed */
+ bprm->filename = ""; /* Make it a legal string */
+/*
+ * Read the section list from the disk file.
+ */
+ set_fs (get_ds ()); /* Make it point to the proper location */
+ status = read_exec (bprm->inode, /* INODE for file */
+ 0L, /* Offset in the file */
+ bprm->buf, /* Buffer for read */
+ sizeof (bprm->buf)); /* Size of the buffer */
+ set_fs (old_fs); /* Restore the selector */
+/*
+ * Try to load the library.
+ */
+ status = load_object (bprm, &regs, 0);
+/*
+ * Release the work buffer and return the result.
+ */
+ kfree (bprm); /* Release the buffer area */
+ }
+/*
+ * Return the result of the load operation
+ */
+ return (status);
+}
diff --git a/arch/i386/ibcs/binfmt_elf.c b/arch/i386/ibcs/binfmt_elf.c
new file mode 100644
index 000000000..ab6d36915
--- /dev/null
+++ b/arch/i386/ibcs/binfmt_elf.c
@@ -0,0 +1,655 @@
+/*
+ * linux/fs/binfmt_elf.c
+ *
+ * These are the functions used to load ELF format executables as used
+ * on SVr4 machines. Information on the format may be found in the book
+ * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support
+ * Tools".
+ *
+ * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/binfmts.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/shm.h>
+#include <linux/personality.h>
+
+#include <asm/segment.h>
+
+asmlinkage int sys_exit(int exit_code);
+asmlinkage int sys_close(unsigned fd);
+asmlinkage int sys_open(const char *, int, int);
+asmlinkage int sys_brk(unsigned long);
+
+#define DLINFO_ITEMS 8
+
+#include <linux/elf.h>
+
+/* We need to explicitly zero any fractional pages
+ after the data section (i.e. bss). This would
+ contain the junk from the file that should not
+ be in memory */
+
+static void padzero(int elf_bss){
+ unsigned int fpnt, nbyte;
+
+ if(elf_bss & 0xfff) {
+
+ nbyte = (PAGE_SIZE - (elf_bss & 0xfff)) & 0xfff;
+ if(nbyte){
+ verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte);
+
+ fpnt = elf_bss;
+ while(fpnt & 0xfff) put_fs_byte(0, fpnt++);
+ };
+ };
+}
+
+unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs)
+{
+ unsigned long *argv,*envp, *dlinfo;
+ unsigned long * sp;
+ struct vm_area_struct *mpnt;
+
+ mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
+ if (mpnt) {
+ mpnt->vm_task = current;
+ mpnt->vm_start = PAGE_MASK & (unsigned long) p;
+ mpnt->vm_end = TASK_SIZE;
+ mpnt->vm_page_prot = PAGE_COPY;
+ mpnt->vm_flags = VM_STACK_FLAGS;
+ mpnt->vm_ops = NULL;
+ mpnt->vm_inode = NULL;
+ mpnt->vm_offset = 0;
+ mpnt->vm_pte = 0;
+ insert_vm_struct(current, mpnt);
+ }
+ sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
+ if(exec) sp -= DLINFO_ITEMS*2;
+ dlinfo = sp;
+ sp -= envc+1;
+ envp = sp;
+ sp -= argc+1;
+ argv = sp;
+ if (!ibcs) {
+ put_fs_long((unsigned long)envp,--sp);
+ put_fs_long((unsigned long)argv,--sp);
+ }
+
+ /* The constant numbers (0-9) that we are writing here are
+ described in the header file sys/auxv.h on at least
+ some versions of SVr4 */
+ if(exec) { /* Put this here for an ELF program interpreter */
+ struct elf_phdr * eppnt;
+ eppnt = (struct elf_phdr *) exec->e_phoff;
+ put_fs_long(3,dlinfo++); put_fs_long(load_addr + exec->e_phoff,dlinfo++);
+ put_fs_long(4,dlinfo++); put_fs_long(sizeof(struct elf_phdr),dlinfo++);
+ put_fs_long(5,dlinfo++); put_fs_long(exec->e_phnum,dlinfo++);
+ put_fs_long(9,dlinfo++); put_fs_long((unsigned long) exec->e_entry,dlinfo++);
+ put_fs_long(7,dlinfo++); put_fs_long(SHM_RANGE_START,dlinfo++);
+ put_fs_long(8,dlinfo++); put_fs_long(0,dlinfo++);
+ put_fs_long(6,dlinfo++); put_fs_long(PAGE_SIZE,dlinfo++);
+ put_fs_long(0,dlinfo++); put_fs_long(0,dlinfo++);
+ };
+
+ put_fs_long((unsigned long)argc,--sp);
+ current->mm->arg_start = (unsigned long) p;
+ while (argc-->0) {
+ put_fs_long((unsigned long) p,argv++);
+ while (get_fs_byte(p++)) /* nothing */ ;
+ }
+ put_fs_long(0,argv);
+ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
+ while (envc-->0) {
+ put_fs_long((unsigned long) p,envp++);
+ while (get_fs_byte(p++)) /* nothing */ ;
+ }
+ put_fs_long(0,envp);
+ current->mm->env_end = (unsigned long) p;
+ return sp;
+}
+
+
+/* This is much more generalized than the library routine read function,
+ so we keep this separate. Technically the library read function
+ is only provided so that we can read a.out libraries that have
+ an ELF header */
+
+static unsigned int load_elf_interp(struct elfhdr * interp_elf_ex,
+ struct inode * interpreter_inode)
+{
+ struct file * file;
+ struct elf_phdr *elf_phdata = NULL;
+ struct elf_phdr *eppnt;
+ unsigned int len;
+ unsigned int load_addr;
+ int elf_exec_fileno;
+ int elf_bss;
+ int old_fs, retval;
+ unsigned int last_bss;
+ int error;
+ int i, k;
+
+ elf_bss = 0;
+ last_bss = 0;
+ error = load_addr = 0;
+
+ /* First of all, some simple consistency checks */
+ if((interp_elf_ex->e_type != ET_EXEC &&
+ interp_elf_ex->e_type != ET_DYN) ||
+ (interp_elf_ex->e_machine != EM_386 && interp_elf_ex->e_machine != EM_486) ||
+ (!interpreter_inode->i_op ||
+ !interpreter_inode->i_op->default_file_ops->mmap)){
+ return 0xffffffff;
+ };
+
+ /* Now read in all of the header information */
+
+ if(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE)
+ return 0xffffffff;
+
+ elf_phdata = (struct elf_phdr *)
+ kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL);
+ if(!elf_phdata) return 0xffffffff;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata,
+ sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+ set_fs(old_fs);
+
+ elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY);
+ if (elf_exec_fileno < 0) return 0xffffffff;
+ file = current->files->fd[elf_exec_fileno];
+
+ eppnt = elf_phdata;
+ for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
+ if(eppnt->p_type == PT_LOAD) {
+ error = do_mmap(file,
+ eppnt->p_vaddr & 0xfffff000,
+ eppnt->p_filesz + (eppnt->p_vaddr & 0xfff),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | (interp_elf_ex->e_type == ET_EXEC ? MAP_FIXED : 0),
+ eppnt->p_offset & 0xfffff000);
+
+ if(!load_addr && interp_elf_ex->e_type == ET_DYN)
+ load_addr = error;
+ k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
+ if(k > elf_bss) elf_bss = k;
+ if(error < 0 && error > -1024) break; /* Real error */
+ k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
+ if(k > last_bss) last_bss = k;
+ }
+
+ /* Now use mmap to map the library into memory. */
+
+
+ sys_close(elf_exec_fileno);
+ if(error < 0 && error > -1024) {
+ kfree(elf_phdata);
+ return 0xffffffff;
+ }
+
+ padzero(elf_bss);
+ len = (elf_bss + 0xfff) & 0xfffff000; /* What we have mapped so far */
+
+ /* Map the last of the bss segment */
+ if (last_bss > len)
+ do_mmap(NULL, len, last_bss-len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ kfree(elf_phdata);
+
+ return ((unsigned int) interp_elf_ex->e_entry) + load_addr;
+}
+
+static unsigned int load_aout_interp(struct exec * interp_ex,
+ struct inode * interpreter_inode)
+{
+ int retval;
+ unsigned int elf_entry;
+
+ current->mm->brk = interp_ex->a_bss +
+ (current->mm->end_data = interp_ex->a_data +
+ (current->mm->end_code = interp_ex->a_text));
+ elf_entry = interp_ex->a_entry;
+
+
+ if (N_MAGIC(*interp_ex) == OMAGIC) {
+ do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ retval = read_exec(interpreter_inode, 32, (char *) 0,
+ interp_ex->a_text+interp_ex->a_data);
+ } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) {
+ do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ retval = read_exec(interpreter_inode,
+ N_TXTOFF(*interp_ex) ,
+ (char *) N_TXTADDR(*interp_ex),
+ interp_ex->a_text+interp_ex->a_data);
+ } else
+ retval = -1;
+
+ if(retval >= 0)
+ do_mmap(NULL, (interp_ex->a_text + interp_ex->a_data + 0xfff) &
+ 0xfffff000, interp_ex->a_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ if(retval < 0) return 0xffffffff;
+ return elf_entry;
+}
+
+/*
+ * These are the functions used to load ELF style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+#define INTERPRETER_NONE 0
+#define INTERPRETER_AOUT 1
+#define INTERPRETER_ELF 2
+
+static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ struct elfhdr elf_ex;
+ struct elfhdr interp_elf_ex;
+ struct file * file;
+ struct exec interp_ex;
+ struct inode *interpreter_inode;
+ unsigned int load_addr;
+ unsigned int interpreter_type = INTERPRETER_NONE;
+ int i;
+ int old_fs;
+ int error;
+ struct elf_phdr * elf_ppnt, *elf_phdata;
+ int elf_exec_fileno;
+ unsigned int elf_bss, k, elf_brk;
+ int retval;
+ char * elf_interpreter;
+ unsigned int elf_entry;
+ int status;
+ unsigned int start_code, end_code, end_data;
+ unsigned int elf_stack;
+ char passed_fileno[6];
+
+ status = 0;
+ load_addr = 0;
+ elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
+
+ if (elf_ex.e_ident[0] != 0x7f ||
+ strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
+ return -ENOEXEC;
+
+
+ /* First of all, some simple consistency checks */
+ if(elf_ex.e_type != ET_EXEC ||
+ (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
+ (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops ||
+ !bprm->inode->i_op->default_file_ops->mmap)){
+ return -ENOEXEC;
+ };
+
+ /* Now read in all of the header information */
+
+ elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize *
+ elf_ex.e_phnum, GFP_KERNEL);
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata,
+ elf_ex.e_phentsize * elf_ex.e_phnum);
+ set_fs(old_fs);
+ if (retval < 0) {
+ kfree (elf_phdata);
+ return retval;
+ }
+
+ elf_ppnt = elf_phdata;
+
+ elf_bss = 0;
+ elf_brk = 0;
+
+ elf_exec_fileno = open_inode(bprm->inode, O_RDONLY);
+
+ if (elf_exec_fileno < 0) {
+ kfree (elf_phdata);
+ return elf_exec_fileno;
+ }
+
+ file = current->files->fd[elf_exec_fileno];
+
+ elf_stack = 0xffffffff;
+ elf_interpreter = NULL;
+ start_code = 0;
+ end_code = 0;
+ end_data = 0;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ for(i=0;i < elf_ex.e_phnum; i++){
+ if(elf_ppnt->p_type == PT_INTERP) {
+ /* This is the program interpreter used for shared libraries -
+ for now assume that this is an a.out format binary */
+
+ elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
+ GFP_KERNEL);
+
+ retval = read_exec(bprm->inode,elf_ppnt->p_offset,elf_interpreter,
+ elf_ppnt->p_filesz);
+#if 0
+ printk("Using ELF interpreter %s\n", elf_interpreter);
+#endif
+ if(retval >= 0)
+ retval = namei(elf_interpreter, &interpreter_inode);
+ if(retval >= 0)
+ retval = read_exec(interpreter_inode,0,bprm->buf,128);
+
+ if(retval >= 0){
+ interp_ex = *((struct exec *) bprm->buf); /* exec-header */
+ interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
+
+ };
+ if(retval < 0) {
+ kfree (elf_phdata);
+ kfree(elf_interpreter);
+ return retval;
+ };
+ };
+ elf_ppnt++;
+ };
+
+ set_fs(old_fs);
+
+ /* Some simple consistency checks for the interpreter */
+ if(elf_interpreter){
+ interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
+ if(retval < 0) {
+ kfree(elf_interpreter);
+ kfree(elf_phdata);
+ return -ELIBACC;
+ };
+ /* Now figure out which format our binary is */
+ if((N_MAGIC(interp_ex) != OMAGIC) &&
+ (N_MAGIC(interp_ex) != ZMAGIC) &&
+ (N_MAGIC(interp_ex) != QMAGIC))
+ interpreter_type = INTERPRETER_ELF;
+
+ if (interp_elf_ex.e_ident[0] != 0x7f ||
+ strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0)
+ interpreter_type &= ~INTERPRETER_ELF;
+
+ if(!interpreter_type)
+ {
+ kfree(elf_interpreter);
+ kfree(elf_phdata);
+ return -ELIBBAD;
+ };
+ }
+
+ /* OK, we are done with that, now set up the arg stuff,
+ and then start this sucker up */
+
+ if (!bprm->sh_bang) {
+ char * passed_p;
+
+ if(interpreter_type == INTERPRETER_AOUT) {
+ sprintf(passed_fileno, "%d", elf_exec_fileno);
+ passed_p = passed_fileno;
+
+ if(elf_interpreter) {
+ bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2);
+ bprm->argc++;
+ };
+ };
+ if (!bprm->p) {
+ if(elf_interpreter) {
+ kfree(elf_interpreter);
+ }
+ kfree (elf_phdata);
+ return -E2BIG;
+ }
+ }
+
+ /* OK, This is the point of no return */
+ flush_old_exec(bprm);
+
+ current->mm->end_data = 0;
+ current->mm->end_code = 0;
+ current->mm->start_mmap = ELF_START_MMAP;
+ current->mm->mmap = NULL;
+ elf_entry = (unsigned int) elf_ex.e_entry;
+
+ /* Do this so that we can load the interpreter, if need be. We will
+ change some of these later */
+ current->mm->rss = 0;
+ bprm->p += change_ldt(0, bprm->page);
+ current->mm->start_stack = bprm->p;
+
+ /* Now we do a little grungy work by mmaping the ELF image into
+ the correct location in memory. At this point, we assume that
+ the image should be loaded at fixed address, not at a variable
+ address. */
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ elf_ppnt = elf_phdata;
+ for(i=0;i < elf_ex.e_phnum; i++){
+
+ if(elf_ppnt->p_type == PT_INTERP) {
+ /* Set these up so that we are able to load the interpreter */
+ /* Now load the interpreter into user address space */
+ set_fs(old_fs);
+
+ if(interpreter_type & 1) elf_entry =
+ load_aout_interp(&interp_ex, interpreter_inode);
+
+ if(interpreter_type & 2) elf_entry =
+ load_elf_interp(&interp_elf_ex, interpreter_inode);
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ iput(interpreter_inode);
+ kfree(elf_interpreter);
+
+ if(elf_entry == 0xffffffff) {
+ printk("Unable to load interpreter\n");
+ kfree(elf_phdata);
+ send_sig(SIGSEGV, current, 0);
+ return 0;
+ };
+ };
+
+
+ if(elf_ppnt->p_type == PT_LOAD) {
+ error = do_mmap(file,
+ elf_ppnt->p_vaddr & 0xfffff000,
+ elf_ppnt->p_filesz + (elf_ppnt->p_vaddr & 0xfff),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE,
+ elf_ppnt->p_offset & 0xfffff000);
+
+#ifdef LOW_ELF_STACK
+ if(elf_ppnt->p_vaddr & 0xfffff000 < elf_stack)
+ elf_stack = elf_ppnt->p_vaddr & 0xfffff000;
+#endif
+
+ if(!load_addr)
+ load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
+ k = elf_ppnt->p_vaddr;
+ if(k > start_code) start_code = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
+ if(k > elf_bss) elf_bss = k;
+ if((elf_ppnt->p_flags | PROT_WRITE) && end_code < k)
+ end_code = k;
+ if(end_data < k) end_data = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
+ if(k > elf_brk) elf_brk = k;
+ };
+ elf_ppnt++;
+ };
+ set_fs(old_fs);
+
+ kfree(elf_phdata);
+
+ if(interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno);
+
+ /* The following 3 lines need a little bit of work if we are loading
+ an iBCS2 binary. We should initially load it this way, and if
+ we get a lcall7, then we should look to see if the iBCS2 execution
+ profile is present. If it is, then switch to that, otherwise
+ bomb. */
+ current->personality = PER_LINUX;
+ current->lcall7 = no_lcall7;
+ current->signal_map = current->signal_invmap = ident_map;
+
+ current->executable = bprm->inode;
+ bprm->inode->i_count++;
+#ifdef LOW_ELF_STACK
+ current->start_stack = p = elf_stack - 4;
+#endif
+ bprm->p -= MAX_ARG_PAGES*PAGE_SIZE;
+ bprm->p = (unsigned long)
+ create_elf_tables((char *)bprm->p,
+ bprm->argc,
+ bprm->envc,
+ (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
+ load_addr,
+ (interpreter_type == INTERPRETER_AOUT ? 0 : 1));
+ if(interpreter_type == INTERPRETER_AOUT)
+ current->mm->arg_start += strlen(passed_fileno) + 1;
+ current->mm->start_brk = current->mm->brk = elf_brk;
+ current->mm->end_code = end_code;
+ current->mm->start_code = start_code;
+ current->mm->end_data = end_data;
+ current->mm->start_stack = bprm->p;
+ current->suid = current->euid = bprm->e_uid;
+ current->sgid = current->egid = bprm->e_gid;
+
+ /* Calling sys_brk effectively mmaps the pages that we need for the bss and break
+ sections */
+ current->mm->brk = (elf_bss + 0xfff) & 0xfffff000;
+ sys_brk((elf_brk + 0xfff) & 0xfffff000);
+
+ padzero(elf_bss);
+
+ /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
+ and some applications "depend" upon this behavior.
+ Since we do not have the power to recompile these, we
+ emulate the SVr4 behavior. Sigh. */
+ error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, 0);
+
+ regs->eip = elf_entry; /* eip, magic happens :-) */
+ regs->esp = bprm->p; /* stack pointer */
+ if (current->flags & PF_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ return 0;
+}
+
+/* This is really simpleminded and specialized - we are loading an
+ a.out library that is given an ELF header. */
+
+static int load_elf_library(int fd){
+ struct file * file;
+ struct elfhdr elf_ex;
+ struct elf_phdr *elf_phdata = NULL;
+ struct inode * inode;
+ unsigned int len;
+ int elf_bss;
+ int old_fs, retval;
+ unsigned int bss;
+ int error;
+ int i,j, k;
+
+ len = 0;
+ file = current->files->fd[fd];
+ inode = file->f_inode;
+ elf_bss = 0;
+
+ set_fs(KERNEL_DS);
+ if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) {
+ sys_close(fd);
+ return -EACCES;
+ }
+ set_fs(USER_DS);
+
+ if (elf_ex.e_ident[0] != 0x7f ||
+ strncmp(&elf_ex.e_ident[1], "ELF",3) != 0)
+ return -ENOEXEC;
+
+ /* First of all, some simple consistency checks */
+ if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
+ (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) ||
+ (!inode->i_op ||
+ !inode->i_op->default_file_ops->mmap)){
+ return -ENOEXEC;
+ };
+
+ /* Now read in all of the header information */
+
+ if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE)
+ return -ENOEXEC;
+
+ elf_phdata = (struct elf_phdr *)
+ kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL);
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata,
+ sizeof(struct elf_phdr) * elf_ex.e_phnum);
+ set_fs(old_fs);
+
+ j = 0;
+ for(i=0; i<elf_ex.e_phnum; i++)
+ if((elf_phdata + i)->p_type == PT_LOAD) j++;
+
+ if(j != 1) {
+ kfree(elf_phdata);
+ return -ENOEXEC;
+ };
+
+ while(elf_phdata->p_type != PT_LOAD) elf_phdata++;
+
+ /* Now use mmap to map the library into memory. */
+ error = do_mmap(file,
+ elf_phdata->p_vaddr & 0xfffff000,
+ elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE,
+ elf_phdata->p_offset & 0xfffff000);
+
+ k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
+ if(k > elf_bss) elf_bss = k;
+
+ sys_close(fd);
+ if (error != elf_phdata->p_vaddr & 0xfffff000) {
+ kfree(elf_phdata);
+ return error;
+ }
+
+ padzero(elf_bss);
+
+ len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000;
+ bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
+ if (bss > len)
+ do_mmap(NULL, len, bss-len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ kfree(elf_phdata);
+ return 0;
+}
+
+struct linux_binfmt elf_format = { NULL, load_elf_binary, load_elf_library };
diff --git a/arch/i386/ibcs/emulate.c b/arch/i386/ibcs/emulate.c
new file mode 100644
index 000000000..dc3b3d576
--- /dev/null
+++ b/arch/i386/ibcs/emulate.c
@@ -0,0 +1,10 @@
+/*
+ * linux/abi/emulate.c
+ *
+ * Copyright (C) 1993 Linus Torvalds
+ */
+
+/*
+ * Yes, sir, this file is completely empty, waiting for some real code..
+ * I still copyright it, silly me.
+ */
diff --git a/arch/i386/ioport.c b/arch/i386/ioport.c
deleted file mode 100644
index c61690e3c..000000000
--- a/arch/i386/ioport.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * linux/kernel/ioport.c
- *
- * This contains the io-permission bitmap code - written by obz, with changes
- * by Linus.
- */
-
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/ioport.h>
-
-static unsigned long ioport_registrar[IO_BITMAP_SIZE] = {0, /* ... */};
-
-#define _IODEBUG
-
-#ifdef IODEBUG
-static char * ios(unsigned long l)
-{
- static char str[33] = { '\0' };
- int i;
- unsigned long mask;
-
- for (i = 0, mask = 0x80000000; i < 32; ++i, mask >>= 1)
- str[i] = (l & mask) ? '1' : '0';
- return str;
-}
-
-static void dump_io_bitmap(void)
-{
- int i, j;
- int numl = sizeof(current->tss.io_bitmap) >> 2;
-
- for (i = j = 0; j < numl; ++i)
- {
- printk("%4d [%3x]: ", 64*i, 64*i);
- printk("%s ", ios(current->tss.io_bitmap[j++]));
- if (j < numl)
- printk("%s", ios(current->tss.io_bitmap[j++]));
- printk("\n");
- }
-}
-#endif
-
-/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
-asmlinkage void set_bitmap(unsigned long *bitmap,
- short base, short extent, int new_value)
-{
- int mask;
- unsigned long *bitmap_base = bitmap + (base >> 5);
- unsigned short low_index = base & 0x1f;
- int length = low_index + extent;
-
- if (low_index != 0) {
- mask = (~0 << low_index);
- if (length < 32)
- mask &= ~(~0 << length);
- if (new_value)
- *bitmap_base++ |= mask;
- else
- *bitmap_base++ &= ~mask;
- length -= 32;
- }
-
- mask = (new_value ? ~0 : 0);
- while (length >= 32) {
- *bitmap_base++ = mask;
- length -= 32;
- }
-
- if (length > 0) {
- mask = ~(~0 << length);
- if (new_value)
- *bitmap_base++ |= mask;
- else
- *bitmap_base++ &= ~mask;
- }
-}
-
-/* Check for set bits in BITMAP starting at BASE, going to EXTENT. */
-asmlinkage int check_bitmap(unsigned long *bitmap, short base, short extent)
-{
- int mask;
- unsigned long *bitmap_base = bitmap + (base >> 5);
- unsigned short low_index = base & 0x1f;
- int length = low_index + extent;
-
- if (low_index != 0) {
- mask = (~0 << low_index);
- if (length < 32)
- mask &= ~(~0 << length);
- if (*bitmap_base++ & mask)
- return 1;
- length -= 32;
- }
- while (length >= 32) {
- if (*bitmap_base++ != 0)
- return 1;
- length -= 32;
- }
-
- if (length > 0) {
- mask = ~(~0 << length);
- if (*bitmap_base++ & mask)
- return 1;
- }
- return 0;
-}
-
-/*
- * this changes the io permissions bitmap in the current task.
- */
-asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
-{
- if (from + num <= from)
- return -EINVAL;
- if (from + num > IO_BITMAP_SIZE*32)
- return -EINVAL;
- if (!suser())
- return -EPERM;
-
-#ifdef IODEBUG
- printk("io: from=%d num=%d %s\n", from, num, (turn_on ? "on" : "off"));
-#endif
- set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on);
- return 0;
-}
-
-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)
-{
- unsigned int level = ebx;
-
- if (level > 3)
- return -EINVAL;
- if (!suser())
- return -EPERM;
- *(&eflags) = (eflags & 0xffffcfff) | (level << 12);
- return 0;
-}
-
-
-void snarf_region(unsigned int from, unsigned int num)
-{
- if (from > IO_BITMAP_SIZE*32)
- return;
- if (from + num > IO_BITMAP_SIZE*32)
- num = IO_BITMAP_SIZE*32 - from;
- set_bitmap(ioport_registrar, from, num, 1);
- return;
-}
-
-void release_region(unsigned int from, unsigned int num)
-{
- if (from > IO_BITMAP_SIZE*32)
- return;
- if (from + num > IO_BITMAP_SIZE*32)
- num = IO_BITMAP_SIZE*32 - from;
- set_bitmap(ioport_registrar, from, num, 0);
- return;
-}
-
-int check_region(unsigned int from, unsigned int num)
-{
- if (from > IO_BITMAP_SIZE*32)
- return 0;
- if (from + num > IO_BITMAP_SIZE*32)
- num = IO_BITMAP_SIZE*32 - from;
- return check_bitmap(ioport_registrar, from, num);
-}
-
-/* Called from init/main.c to reserve IO ports. */
-void reserve_setup(char *str, int *ints)
-{
- int i;
-
- for (i = 1; i < ints[0]; i += 2)
- snarf_region(ints[i], ints[i+1]);
-}
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
new file mode 100644
index 000000000..701926d26
--- /dev/null
+++ b/arch/i386/kernel/Makefile
@@ -0,0 +1,48 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.S.s:
+ $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s
+.S.o:
+ $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+
+OBJS = process.o signal.o entry.o traps.o irq.o vm86.o bios32.o ptrace.o ioport.o ldt.o setup.o
+
+all: kernel.o head.o
+
+head.o: head.s
+
+head.s: head.S $(TOPDIR)/include/linux/tasks.h
+ $(CPP) -traditional -o $*.s $<
+
+kernel.o: $(OBJS)
+ $(LD) -r -o kernel.o $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+
+modules:
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+
diff --git a/arch/i386/bios32.c b/arch/i386/kernel/bios32.c
index 311dd111e..8cc3d76fa 100644
--- a/arch/i386/bios32.c
+++ b/arch/i386/kernel/bios32.c
@@ -32,25 +32,31 @@
* CHANGELOG :
* Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION
* Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard.
+ *
+ * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic
+ * Potter, potter@cao-vlsi.ibp.fr
+ *
+ * Jan 10, 1995 : Modified to store the information about configured pci
+ * devices into a list, which can be accessed via /proc/pci by
+ * Curtis Varner, cvarner@cs.ucr.edu
+ *
+ * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
+ * Alpha version. Intel & UMC chipset support only.
+ *
+ * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
+ * moved to drivers/pci/pci.c.
+ *
+ *
*/
#include <linux/config.h>
+#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/bios32.h>
#include <linux/pci.h>
#include <asm/segment.h>
-/*
- * It would seem some PCI bioses are buggy, so we don't actually use these
- * routines unless we need to..
- */
-#ifdef CONFIG_SCSI_NCR53C7xx
- #define CONFIG_PCI
-#else
- #undef CONFIG_PCI
-#endif
-
#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
#define PCIBIOS_FIND_PCI_DEVICE 0xb102
@@ -63,6 +69,7 @@
#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
+
/* BIOS32 signature: "_32_" */
#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
@@ -97,7 +104,7 @@ union bios32 {
/*
* Physical address of the service directory. I don't know if we're
* allowed to have more than one of these or not, so just in case
- * we'll make bios32_init() take a memory start parameter and store
+ * we'll make pcibios_present() take a memory start parameter and store
* the array there.
*/
@@ -147,9 +154,8 @@ static struct {
unsigned short segment;
} pci_indirect = { 0, KERNEL_CS };
-void NCR53c810_test(void);
-static unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
+extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
{
unsigned long signature;
unsigned char present_status;
@@ -191,10 +197,6 @@ static unsigned long pcibios_init(unsigned long memory_start, unsigned long memo
major_revision, minor_revision, pcibios_entry);
}
}
-
-#if 0
- NCR53c810_test();
-#endif
return memory_start;
}
@@ -203,7 +205,7 @@ int pcibios_present(void)
return pcibios_entry ? 1 : 0;
}
-int pcibios_find_class_code (unsigned long class_code, unsigned short index,
+int pcibios_find_class (unsigned int class_code, unsigned short index,
unsigned char *bus, unsigned char *device_fn)
{
unsigned long bx;
@@ -286,7 +288,7 @@ int pcibios_read_config_word (unsigned char bus,
}
int pcibios_read_config_dword (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned long *value)
+ unsigned char device_fn, unsigned char where, unsigned int *value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
@@ -343,7 +345,7 @@ int pcibios_write_config_word (unsigned char bus,
}
int pcibios_write_config_dword (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned long value)
+ unsigned char device_fn, unsigned char where, unsigned int value)
{
unsigned long ret;
unsigned long bx = (bus << 8) | device_fn;
@@ -361,36 +363,6 @@ int pcibios_write_config_dword (unsigned char bus,
return (int) (ret & 0xff00) >> 8;
}
-void NCR53c810_test(void)
-{
- unsigned char bus, device_fn;
- unsigned short index;
- int ret;
- unsigned char row, col;
- unsigned long val;
-
- for (index = 0; index < 4; ++index) {
- ret = pcibios_find_device (
- (unsigned short) PCI_VENDOR_ID_NCR,
- (unsigned short) PCI_DEVICE_ID_NCR_53C810,
- index, &bus, &device_fn);
- if (ret)
- break;
- printk ("ncr53c810 : at PCI bus %d, device %d, function %d.",
- bus, ((device_fn & 0xf8) >> 3), (device_fn & 7));
- for (row = 0; row < 0x3c; row += 0x10) {
- printk ("\n reg 0x%02x ", row);
- for (col = 0; col < 0x10; col += 4) {
- if (!(ret = pcibios_read_config_dword (bus, device_fn, row+col, &val)))
- printk ("0x%08lx ", val);
- else
- printk ("error 0x%02x ", ret);
- }
- }
- printk ("\n");
- }
-}
-
char *pcibios_strerror (int error)
{
static char buf[80];
@@ -417,9 +389,16 @@ char *pcibios_strerror (int error)
}
}
+
+unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
+{
+return mem_start;
+}
+
+
#endif
-unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
+unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
{
union bios32 *check;
unsigned char sum;
@@ -430,8 +409,6 @@ unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
* directory by scanning the permissible address range from
* 0xe0000 through 0xfffff for a valid BIOS32 structure.
*
- * The PCI BIOS doesn't seem to work too well on many machines,
- * so we disable this unless it's really needed (NCR SCSI driver)
*/
for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
@@ -446,31 +423,35 @@ unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
if (sum != 0)
continue;
if (check->fields.revision != 0) {
- printk("bios32_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
+ printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
check->fields.revision, check);
continue;
}
- printk ("bios32_init : BIOS32 Service Directory structure at 0x%p\n", check);
+ printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
if (!bios32_entry) {
- bios32_indirect.address = bios32_entry = check->fields.entry;
- printk ("bios32_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+ if (check->fields.entry >= 0x100000) {
+ printk("pcibios_init: entry in high memory, unable to access\n");
+ } else {
+ bios32_indirect.address = bios32_entry = check->fields.entry;
+ printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+ }
} else {
- printk ("bios32_init : multiple entries, mail drew@colorado.edu\n");
+ printk ("pcibios_init : multiple entries, mail drew@colorado.edu\n");
/*
* Jeremy Fitzhardinge reports at least one PCI BIOS
* with two different service directories, and as both
* worked for him, we'll just mention the fact, and
* not actually disallow it..
*/
-#if 0
- return memory_start;
-#endif
}
}
#ifdef CONFIG_PCI
if (bios32_entry) {
- memory_start = pcibios_init (memory_start, memory_end);
+ memory_start = check_pcibios (memory_start, memory_end);
}
#endif
return memory_start;
}
+
+
+
diff --git a/arch/i386/entry.S b/arch/i386/kernel/entry.S
index d7008a74b..1bdf062c8 100644
--- a/arch/i386/entry.S
+++ b/arch/i386/kernel/entry.S
@@ -40,7 +40,6 @@
* 40(%esp) - %oldss
*/
-#define __ASSEMBLY__
#include <linux/sys.h>
#include <asm/segment.h>
@@ -483,14 +482,14 @@ _sys_call_table:
.long _sys_settimeofday
.long _sys_getgroups /* 80 */
.long _sys_setgroups
- .long _sys_select
+ .long _old_select
.long _sys_symlink
.long _sys_lstat
.long _sys_readlink /* 85 */
.long _sys_uselib
.long _sys_swapon
.long _sys_reboot
- .long _sys_readdir
+ .long _old_readdir
.long _sys_mmap /* 90 */
.long _sys_munmap
.long _sys_truncate
@@ -542,4 +541,7 @@ _sys_call_table:
.long _sys_setfsuid
.long _sys_setfsgid
.long _sys_llseek /* 140 */
+ .long _sys_getdents
+ .long _sys_select
+ .long _sys_flock
.space (NR_syscalls-140)*4
diff --git a/arch/i386/boot/head.S b/arch/i386/kernel/head.S
index 896c62f18..a3e0df213 100644
--- a/arch/i386/boot/head.S
+++ b/arch/i386/kernel/head.S
@@ -1,5 +1,5 @@
/*
- * linux/boot/head.S
+ * linux/arch/i386/head.S
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
@@ -9,7 +9,7 @@
*/
.text
-.globl _idt,_gdt,
+.globl _idt,_gdt,_stext,__stext
.globl _swapper_pg_dir,_pg0
.globl _empty_bad_page
.globl _empty_bad_page_table
@@ -30,6 +30,8 @@
* swapper_pg_dir is the main page directory, address 0x00001000 (or at
* address 0x00101000 for a compressed boot).
*/
+_stext:
+__stext:
startup_32:
cld
movl $(KERNEL_DS),%eax
@@ -37,7 +39,7 @@ startup_32:
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
- lss _stack_start,%esp
+ lss stack_start,%esp
/*
* Clear BSS first so that there are no surprises...
*/
@@ -95,8 +97,6 @@ startup_32:
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
- movl %esp,%edi # save stack pointer
- andl $0xfffffffc,%esp # align stack to avoid AC fault
movl $3,_x86
pushfl # push EFLAGS
popl %eax # get EFLAGS
@@ -121,26 +121,37 @@ startup_32:
je is486
isnew: pushl %ecx # restore original EFLAGS
popfl
+ /* get processor type */
movl $1, %eax # Use the CPUID instruction to
.byte 0x0f, 0xa2 # check the processor type
- andl $0xf00, %eax # Set _x86 with the family
- shrl $8, %eax # returned.
- movl %eax, _x86
- movl %edi,%esp # restore esp
+ movb %al, %cl # save reg for future use
+ andb $0x0f,%ah # mask processor family
+ movb %ah, _x86
+ andb $0xf0, %eax # mask model
+ shrb $4, %al
+ movb %al, _x86_model
+ andb $0x0f, %cl # mask mask revision
+ movb %cl, _x86_mask
+ movl %edx, _x86_capability
+ /* get vendor info */
+ xorl %eax, %eax # call CPUID with 0 -> return vendor ID
+ .byte 0x0f, 0xa2 # CPUID
+ movl %ebx, _x86_vendor_id # lo 4 chars
+ movl %edx, _x86_vendor_id+4 # next 4 chars
+ movl %ecx, _x86_vendor_id+8 # last 4 chars
+
movl %cr0,%eax # 486+
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is486: pushl %ecx # restore original EFLAGS
popfl
- movl %edi,%esp # restore esp
movl %cr0,%eax # 486
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
jmp 2f
is386: pushl %ecx # restore original EFLAGS
popfl
- movl %edi,%esp # restore esp
movl %cr0,%eax # 386
andl $0x80000011,%eax # Save PG,PE,ET
orl $2,%eax # set MP
@@ -155,7 +166,7 @@ is386: pushl %ecx # restore original EFLAGS
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
- lss _stack_start,%esp
+ lss stack_start,%esp
xorl %eax,%eax
lldt %ax
pushl %eax # These are the parameters to main :-)
@@ -171,7 +182,7 @@ L6:
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
- movl $0,_hard_math
+ movb $0,_hard_math
clts
fninit
fstsw %ax
@@ -182,7 +193,7 @@ check_x87:
movl %eax,%cr0
ret
.align 2
-1: movl $1,_hard_math
+1: movb $1,_hard_math
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
@@ -285,6 +296,10 @@ _empty_zero_page:
*/
_floppy_track_buffer:
.fill 512*2*MAX_BUFFER_SECTORS,1,0
+
+stack_start:
+ .long _init_user_stack+4096
+ .long KERNEL_DS
/* This is the default interrupt "handler" :-) */
int_msg:
diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c
new file mode 100644
index 000000000..c949f70cd
--- /dev/null
+++ b/arch/i386/kernel/ioport.c
@@ -0,0 +1,89 @@
+/*
+ * linux/arch/i386/kernel/ioport.c
+ *
+ * This contains the io-permission bitmap code - written by obz, with changes
+ * by Linus.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+
+/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
+static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
+{
+ int mask;
+ unsigned long *bitmap_base = bitmap + (base >> 5);
+ unsigned short low_index = base & 0x1f;
+ int length = low_index + extent;
+
+ if (low_index != 0) {
+ mask = (~0 << low_index);
+ if (length < 32)
+ mask &= ~(~0 << length);
+ if (new_value)
+ *bitmap_base++ |= mask;
+ else
+ *bitmap_base++ &= ~mask;
+ length -= 32;
+ }
+
+ mask = (new_value ? ~0 : 0);
+ while (length >= 32) {
+ *bitmap_base++ = mask;
+ length -= 32;
+ }
+
+ if (length > 0) {
+ mask = ~(~0 << length);
+ if (new_value)
+ *bitmap_base++ |= mask;
+ else
+ *bitmap_base++ &= ~mask;
+ }
+}
+
+/*
+ * this changes the io permissions bitmap in the current task.
+ */
+asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
+{
+ if (from + num <= from)
+ return -EINVAL;
+ if (from + num > IO_BITMAP_SIZE*32)
+ return -EINVAL;
+ if (!suser())
+ return -EPERM;
+
+ set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on);
+ return 0;
+}
+
+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)
+{
+ unsigned int level = ebx;
+
+ if (level > 3)
+ return -EINVAL;
+ if (!suser())
+ return -EPERM;
+ *(&eflags) = (eflags & 0xffffcfff) | (level << 12);
+ return 0;
+}
diff --git a/arch/i386/irq.c b/arch/i386/kernel/irq.c
index 2de16db53..23085dc6c 100644
--- a/arch/i386/irq.c
+++ b/arch/i386/kernel/irq.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/irq.c
+ * linux/arch/i386/kernel/irq.c
*
* Copyright (C) 1992 Linus Torvalds
*
@@ -12,14 +12,7 @@
/*
* 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
+ * Naturally it's not a 1:1 relation, but there are similarities.
*/
#include <linux/ptrace.h>
@@ -27,22 +20,20 @@
#include <linux/kernel_stat.h>
#include <linux/signal.h>
#include <linux/sched.h>
+#include <linux/ioport.h>
#include <linux/interrupt.h>
+#include <linux/timex.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/bitops.h>
#define CR0_NE 32
static unsigned char cache_21 = 0xff;
static unsigned char cache_A1 = 0xff;
-unsigned long intr_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;
@@ -84,34 +75,6 @@ void enable_irq(unsigned int irq_nr)
}
/*
- * 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");
-}
-
-/*
* This builds up the IRQ handler stubs using some ugly macros in irq.h
*
* These macros create the low-level assembly IRQ routines that do all
@@ -179,7 +142,14 @@ static void (*bad_interrupt[16])(void) = {
/*
* Initial irq handlers.
*/
-static struct sigaction irq_sigaction[16] = {
+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 },
@@ -193,15 +163,15 @@ static struct sigaction irq_sigaction[16] = {
int get_irq_list(char *buf)
{
int i, len = 0;
- struct sigaction * sa = irq_sigaction;
+ struct irqaction * action = irq_action;
- for (i = 0 ; i < 16 ; i++, sa++) {
- if (!sa->sa_handler)
+ for (i = 0 ; i < 16 ; i++, action++) {
+ if (!action->handler)
continue;
len += sprintf(buf+len, "%2d: %8d %c %s\n",
i, kstat.interrupts[i],
- (sa->sa_flags & SA_INTERRUPT) ? '+' : ' ',
- (char *) sa->sa_mask);
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
}
return len;
}
@@ -215,10 +185,10 @@ int get_irq_list(char *buf)
*/
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
- struct sigaction * sa = irq + irq_sigaction;
+ struct irqaction * action = irq + irq_action;
kstat.interrupts[irq]++;
- sa->sa_handler((int) regs);
+ action->handler(irq, regs);
}
/*
@@ -228,35 +198,39 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
*/
asmlinkage void do_fast_IRQ(int irq)
{
- struct sigaction * sa = irq + irq_sigaction;
+ struct irqaction * action = irq + irq_action;
kstat.interrupts[irq]++;
- sa->sa_handler(irq);
+ action->handler(irq, NULL);
}
-/*
- * 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)
+#define SA_PROBE SA_ONESHOT
+
+int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+ unsigned long irqflags, const char * devname)
{
- struct sigaction * sa;
+ struct irqaction * action;
unsigned long flags;
if (irq > 15)
return -EINVAL;
- sa = irq + irq_sigaction;
- if (sa->sa_handler)
+ action = irq + irq_action;
+ if (action->handler)
return -EBUSY;
- if (!new_sa->sa_handler)
+ if (!handler)
return -EINVAL;
save_flags(flags);
cli();
- *sa = *new_sa;
- if (sa->sa_flags & SA_INTERRUPT)
- set_intr_gate(0x20+irq,fast_interrupt[irq]);
- else
- set_intr_gate(0x20+irq,interrupt[irq]);
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
+ if (action->flags & SA_INTERRUPT)
+ set_intr_gate(0x20+irq,fast_interrupt[irq]);
+ else
+ set_intr_gate(0x20+irq,interrupt[irq]);
+ }
if (irq < 8) {
cache_21 &= ~(1<<irq);
outb(cache_21,0x21);
@@ -270,28 +244,16 @@ static int irqaction(unsigned int irq, struct sigaction * new_sa)
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;
+ struct irqaction * action = irq + irq_action;
unsigned long flags;
if (irq > 15) {
printk("Trying to free IRQ%d\n",irq);
return;
}
- if (!sa->sa_handler) {
+ if (!action->handler) {
printk("Trying to free free IRQ%d\n",irq);
return;
}
@@ -305,10 +267,10 @@ void free_irq(unsigned int irq)
outb(cache_A1,0xA1);
}
set_intr_gate(0x20+irq,bad_interrupt[irq]);
- sa->sa_handler = NULL;
- sa->sa_flags = 0;
- sa->sa_mask = 0;
- sa->sa_restorer = NULL;
+ action->handler = NULL;
+ action->flags = 0;
+ action->mask = 0;
+ action->name = NULL;
restore_flags(flags);
}
@@ -323,7 +285,7 @@ void free_irq(unsigned int irq)
* leads to races. IBM designers who came up with it should
* be shot.
*/
-static void math_error_irq(int cpl)
+static void math_error_irq(int cpl, struct pt_regs *regs)
{
outb(0,0xF0);
if (ignore_irq13 || !hard_math)
@@ -331,24 +293,74 @@ static void math_error_irq(int cpl)
math_error();
}
-static void no_action(int cpl) { }
+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;
+ /* 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 */
for (i = 0; i < 16 ; i++)
set_intr_gate(0x20+i,bad_interrupt[i]);
if (request_irq(2, no_action, SA_INTERRUPT, "cascade"))
printk("Unable to get IRQ2 for cascade\n");
if (request_irq(13,math_error_irq, 0, "math error"))
printk("Unable to get IRQ13 for math-error handler\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;
-}
+ request_region(0x20,0x20,"pic1");
+ request_region(0xa0,0x20,"pic2");
+}
diff --git a/arch/i386/ldt.c b/arch/i386/kernel/ldt.c
index dd0e477d4..bace95f4e 100644
--- a/arch/i386/ldt.c
+++ b/arch/i386/kernel/ldt.c
@@ -4,10 +4,10 @@
* Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/mm.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/ldt.h>
@@ -66,6 +66,7 @@ static int write_ldt(void * ptr, unsigned long bytecount)
if (task[i] == current) {
if (!(current->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
return -ENOMEM;
+ memset(current->ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, current->ldt, LDT_ENTRIES);
load_ldt(i);
}
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
new file mode 100644
index 000000000..a5e8777bf
--- /dev/null
+++ b/arch/i386/kernel/process.c
@@ -0,0 +1,288 @@
+/*
+ * linux/arch/i386/kernel/process.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * 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/segment.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
+
+static int hlt_counter=0;
+
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
+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;
+}
+
+/*
+ * The idle loop on a i386..
+ */
+asmlinkage int sys_idle(void)
+{
+ int i;
+ pmd_t * pmd;
+
+ if (current->pid != 0)
+ return -EPERM;
+
+ /* Map out the low memory: it's no longer needed */
+ pmd = pmd_offset(swapper_pg_dir, 0);
+ for (i = 0 ; i < 768 ; i++)
+ pmd_clear(pmd++);
+
+ /* endless idle loop with no priority at all */
+ current->counter = -100;
+ for (;;) {
+ if (hlt_works_ok && !hlt_counter && !need_resched)
+ __asm__("hlt");
+ 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.
+ */
+static long no_idt[2] = {0, 0};
+
+static inline void kb_wait(void)
+{
+ int i;
+
+ for (i=0; i<0x10000; i++)
+ if ((inb_p(0x64) & 0x02) == 0)
+ break;
+}
+
+void hard_reset_now(void)
+{
+ int i, j;
+
+ sti();
+/* rebooting needs to touch the page at absolute addr 0 */
+ pg0[0] = 7;
+ *((unsigned short *)0x472) = 0x1234;
+ for (;;) {
+ for (i=0; i<100; i++) {
+ kb_wait();
+ for(j = 0; j < 100000 ; j++)
+ /* nothing */;
+ outb(0xfe,0x64); /* pulse reset low */
+ }
+ __asm__ __volatile__("\tlidt %0": "=m" (no_idt));
+ }
+}
+
+void show_regs(struct pt_regs * regs)
+{
+ printk("\n");
+ printk("EIP: %04x:%08lx",0xffff & regs->cs,regs->eip);
+ if (regs->cs & 3)
+ printk(" ESP: %04x:%08lx",0xffff & regs->ss,regs->esp);
+ printk(" EFLAGS: %08lx\n",regs->eflags);
+ printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
+ regs->eax,regs->ebx,regs->ecx,regs->edx);
+ printk("ESI: %08lx EDI: %08lx EBP: %08lx",
+ regs->esi, regs->edi, regs->ebp);
+ printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
+ 0xffff & regs->ds,0xffff & regs->es,
+ 0xffff & regs->fs,0xffff & regs->gs);
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+ /* forget local segments */
+ __asm__ __volatile__("mov %w0,%%fs ; mov %w0,%%gs ; lldt %w0"
+ : /* no outputs */
+ : "r" (0));
+ current->tss.ldt = 0;
+ if (current->ldt) {
+ void * ldt = current->ldt;
+ current->ldt = NULL;
+ vfree(ldt);
+ }
+}
+
+void flush_thread(void)
+{
+ int i;
+
+ if (current->ldt) {
+ free_page((unsigned long) current->ldt);
+ current->ldt = NULL;
+ for (i=1 ; i<NR_TASKS ; i++) {
+ if (task[i] == current) {
+ set_ldt_desc(gdt+(i<<1)+
+ FIRST_LDT_ENTRY,&default_ldt, 1);
+ load_ldt(i);
+ }
+ }
+ }
+
+ for (i=0 ; i<8 ; i++)
+ current->debugreg[i] = 0;
+}
+
+void copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
+ struct task_struct * p, struct pt_regs * regs)
+{
+ int i;
+ struct pt_regs * childregs;
+
+ p->tss.es = KERNEL_DS;
+ p->tss.cs = KERNEL_CS;
+ p->tss.ss = KERNEL_DS;
+ p->tss.ds = KERNEL_DS;
+ p->tss.fs = USER_DS;
+ p->tss.gs = KERNEL_DS;
+ p->tss.ss0 = KERNEL_DS;
+ p->tss.esp0 = p->kernel_stack_page + PAGE_SIZE;
+ p->tss.tr = _TSS(nr);
+ childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
+ p->tss.esp = (unsigned long) childregs;
+ p->tss.eip = (unsigned long) ret_from_sys_call;
+ *childregs = *regs;
+ childregs->eax = 0;
+ childregs->esp = esp;
+ p->tss.back_link = 0;
+ p->tss.eflags = regs->eflags & 0xffffcfff; /* iopl is always 0 for a new process */
+ p->tss.ldt = _LDT(nr);
+ if (p->ldt) {
+ p->ldt = (struct desc_struct*) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
+ if (p->ldt != NULL)
+ memcpy(p->ldt, current->ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
+ }
+ set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
+ if (p->ldt)
+ set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512);
+ else
+ set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);
+ p->tss.bitmap = offsetof(struct thread_struct,io_bitmap);
+ for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */
+ p->tss.io_bitmap[i] = ~0;
+ if (last_task_used_math == current)
+ __asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387));
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+ int i;
+
+/* changed the size calculations - should hopefully work better. lbt */
+ dump->magic = CMAGIC;
+ dump->start_code = 0;
+ dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
+ dump->u_tsize = ((unsigned long) current->mm->end_code) >> 12;
+ dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> 12;
+ dump->u_dsize -= dump->u_tsize;
+ dump->u_ssize = 0;
+ for (i = 0; i < 8; i++)
+ dump->u_debugreg[i] = current->debugreg[i];
+
+ if (dump->start_stack < TASK_SIZE)
+ dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> 12;
+
+ dump->regs = *regs;
+
+/* Flag indicating the math stuff is valid. We don't support this for the
+ soft-float routines yet */
+ if (hard_math) {
+ if ((dump->u_fpvalid = current->used_math) != 0) {
+ if (last_task_used_math == current)
+ __asm__("clts ; fnsave %0": :"m" (dump->i387));
+ else
+ memcpy(&dump->i387,&current->tss.i387.hard,sizeof(dump->i387));
+ }
+ } else {
+ /* we should dump the emulator state here, but we need to
+ convert it into standard 387 format first.. */
+ dump->u_fpvalid = 0;
+ }
+}
+
+asmlinkage int sys_fork(struct pt_regs regs)
+{
+ return do_fork(COPYVM | SIGCHLD, regs.esp, &regs);
+}
+
+asmlinkage int sys_clone(struct pt_regs regs)
+{
+#ifdef CLONE_ACTUALLY_WORKS_OK
+ unsigned long clone_flags;
+ unsigned long newsp;
+
+ newsp = regs.ebx;
+ clone_flags = regs.ecx;
+ if (!newsp)
+ newsp = regs.esp;
+ if (newsp == regs.esp)
+ clone_flags |= COPYVM;
+ return do_fork(clone_flags, newsp, &regs);
+#else
+ return -ENOSYS;
+#endif
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(struct pt_regs regs)
+{
+ int error;
+ char * filename;
+
+ error = getname((char *) regs.ebx, &filename);
+ if (error)
+ return error;
+ error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
+ putname(filename);
+ return error;
+}
diff --git a/arch/i386/ptrace.c b/arch/i386/kernel/ptrace.c
index cade04750..f32035edc 100644
--- a/arch/i386/ptrace.c
+++ b/arch/i386/kernel/ptrace.c
@@ -9,10 +9,11 @@
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
+#include <linux/debugreg.h>
#include <asm/segment.h>
+#include <asm/pgtable.h>
#include <asm/system.h>
-#include <linux/debugreg.h>
/*
* does not yet catch signals sent when the child dies.
@@ -84,23 +85,41 @@ 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;
+ pmd_t * pgmiddle;
+ 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 = pgd_offset(vma->vm_task, addr);
+ if (pgd_none(*pgdir)) {
+ do_no_page(vma, addr, 0);
+ goto repeat;
}
- if (!(page & PAGE_PRESENT)) {
+ if (pgd_bad(*pgdir)) {
+ printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
+ pgd_clear(pgdir);
+ return 0;
+ }
+ pgmiddle = pmd_offset(pgdir, addr);
+ if (pmd_none(*pgmiddle)) {
do_no_page(vma, addr, 0);
goto repeat;
}
+ if (pmd_bad(*pgmiddle)) {
+ printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
+ pmd_clear(pgmiddle);
+ return 0;
+ }
+ pgtable = pte_offset(pgmiddle, addr);
+ 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 +136,61 @@ 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;
+ pmd_t *pgmiddle;
+ 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 = pgd_offset(vma->vm_task, addr);
+ if (!pgd_present(*pgdir)) {
+ do_no_page(vma, addr, 1);
+ goto repeat;
}
- if (!(page & PAGE_PRESENT)) {
- do_no_page(vma, addr, 0 /* PAGE_RW */);
+ if (pgd_bad(*pgdir)) {
+ printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir));
+ pgd_clear(pgdir);
+ return;
+ }
+ pgmiddle = pmd_offset(pgdir, addr);
+ if (pmd_none(*pgmiddle)) {
+ 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);
+ if (pmd_bad(*pgmiddle)) {
+ printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle));
+ pmd_clear(pgmiddle);
+ return;
+ }
+ pgtable = pte_offset(pgmiddle, addr);
+ if (!pte_present(*pgtable)) {
+ do_no_page(vma, addr, 1);
+ goto repeat;
+ }
+ 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 +209,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 +251,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/i386/kernel/setup.c b/arch/i386/kernel/setup.c
new file mode 100644
index 000000000..5cc4c5d7d
--- /dev/null
+++ b/arch/i386/kernel/setup.c
@@ -0,0 +1,181 @@
+/*
+ * linux/arch/i386/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * 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 <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * Tell us the machine setup..
+ */
+char hard_math = 0; /* set by boot/head.S */
+char x86 = 0; /* set by boot/head.S to 3 or 4 */
+char x86_model = 0; /* set by boot/head.S */
+char x86_mask = 0; /* set by boot/head.S */
+int x86_capability = 0; /* set by boot/head.S */
+int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */
+
+char x86_vendor_id[13] = "Unknown";
+
+char ignore_irq13 = 0; /* set if exception 16 works */
+char wp_works_ok = 0; /* set if paging hardware honours WP */
+char hlt_works_ok = 1; /* set if the "hlt" instruction works */
+
+/*
+ * Bus types ..
+ */
+int EISA_bus = 0;
+
+/*
+ * Setup options
+ */
+struct drive_info_struct { char dummy[32]; } drive_info;
+struct screen_info screen_info;
+
+unsigned char aux_device_present;
+extern int ramdisk_size;
+extern int root_mountflags;
+extern int etext, edata, end;
+
+extern char empty_zero_page[PAGE_SIZE];
+
+/*
+ * 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 SCREEN_INFO (*(struct screen_info *) (PARAM+0))
+#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))
+#define COMMAND_LINE ((char *) (PARAM+2048))
+#define COMMAND_LINE_SIZE 256
+
+static char command_line[COMMAND_LINE_SIZE] = { 0, };
+
+void setup_arch(char **cmdline_p,
+ unsigned long * memory_start_p, unsigned long * memory_end_p)
+{
+ unsigned long memory_start, memory_end;
+ char c = ' ', *to = command_line, *from = COMMAND_LINE;
+ int len = 0;
+
+ ROOT_DEV = ORIG_ROOT_DEV;
+ drive_info = DRIVE_INFO;
+ screen_info = SCREEN_INFO;
+ aux_device_present = AUX_DEVICE_INFO;
+ memory_end = (1<<20) + (EXT_MEM_K<<10);
+ memory_end &= PAGE_MASK;
+ ramdisk_size = RAMDISK_SIZE;
+#ifdef CONFIG_MAX_16M
+ if (memory_end > 16*1024*1024)
+ memory_end = 16*1024*1024;
+#endif
+ if (MOUNT_ROOT_RDONLY)
+ root_mountflags |= MS_RDONLY;
+ memory_start = (unsigned long) &end;
+ init_task.mm->start_code = TASK_SIZE;
+ init_task.mm->end_code = TASK_SIZE + (unsigned long) &etext;
+ init_task.mm->end_data = TASK_SIZE + (unsigned long) &edata;
+ init_task.mm->brk = TASK_SIZE + (unsigned long) &end;
+
+ for (;;) {
+ if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=") {
+ memory_end = simple_strtoul(from+4, &from, 0);
+ if ( *from == 'K' || *from == 'k' ) {
+ memory_end = memory_end << 10;
+ from++;
+ } else if ( *from == 'M' || *from == 'm' ) {
+ memory_end = memory_end << 20;
+ from++;
+ }
+ }
+ c = *(from++);
+ if (!c)
+ break;
+ if (COMMAND_LINE_SIZE <= ++len)
+ break;
+ *(to++) = c;
+ }
+ *to = '\0';
+ *cmdline_p = command_line;
+ *memory_start_p = memory_start;
+ *memory_end_p = memory_end;
+ /* request io space for devices used on all i[345]86 PC'S */
+ request_region(0x00,0x20,"dma1");
+ request_region(0x40,0x20,"timer");
+ request_region(0x70,0x10,"rtc");
+ request_region(0x80,0x20,"dma page reg");
+ request_region(0xc0,0x20,"dma2");
+ request_region(0xf0,0x2,"npu");
+ request_region(0xf8,0x8,"npu");
+}
+
+int get_cpuinfo(char * buffer)
+{
+ char *model[2][9]={{"DX","SX","DX/2","4","SX/2","6",
+ "DX/2-WB","DX/4"},
+ {"Pentium 60/66","Pentium 90/100","3",
+ "4","5","6","7","8"}};
+ char mask[2];
+ mask[0] = x86_mask+'@';
+ mask[1] = '\0';
+ return sprintf(buffer,"cpu\t\t: %c86\n"
+ "model\t\t: %s\n"
+ "mask\t\t: %s\n"
+ "vid\t\t: %s\n"
+ "fdiv_bug\t: %s\n"
+ "math\t\t: %s\n"
+ "hlt\t\t: %s\n"
+ "wp\t\t: %s\n"
+ "Integrated NPU\t: %s\n"
+ "Enhanced VM86\t: %s\n"
+ "IO Breakpoints\t: %s\n"
+ "4MB Pages\t: %s\n"
+ "TS Counters\t: %s\n"
+ "Pentium MSR\t: %s\n"
+ "Mach. Ch. Exep.\t: %s\n"
+ "CMPXCHGB8B\t: %s\n"
+ "BogoMips\t: %lu.%02lu\n",
+ x86+'0',
+ x86_model ? model[x86-4][x86_model-1] : "Unknown",
+ x86_mask ? mask : "Unknown",
+ x86_vendor_id,
+ fdiv_bug ? "yes" : "no",
+ hard_math ? "yes" : "no",
+ hlt_works_ok ? "yes" : "no",
+ wp_works_ok ? "yes" : "no",
+ x86_capability & 1 ? "yes" : "no",
+ x86_capability & 2 ? "yes" : "no",
+ x86_capability & 4 ? "yes" : "no",
+ x86_capability & 8 ? "yes" : "no",
+ x86_capability & 16 ? "yes" : "no",
+ x86_capability & 32 ? "yes" : "no",
+ x86_capability & 128 ? "yes" : "no",
+ x86_capability & 256 ? "yes" : "no",
+ loops_per_sec/500000, (loops_per_sec/5000) % 100
+ );
+}
diff --git a/arch/i386/signal.c b/arch/i386/kernel/signal.c
index df7324294..3db1a6985 100644
--- a/arch/i386/signal.c
+++ b/arch/i386/kernel/signal.c
@@ -1,10 +1,11 @@
/*
- * linux/kernel/signal.c
+ * linux/arch/i386/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>
@@ -18,63 +19,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.
@@ -96,99 +41,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->esp even though we don't actually use sigstacks yet..
*/
asmlinkage int sys_sigreturn(unsigned long __unused)
@@ -229,7 +81,7 @@ badframe:
* Set up a signal frame... Make the stack look the way iBCS2 expects
* it to look.
*/
-static void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip,
+void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip,
struct pt_regs * regs, int signr, unsigned long oldmask)
{
unsigned long * frame;
@@ -299,10 +151,10 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
struct sigaction * sa;
while ((signr = current->signal & mask)) {
- __asm__("bsf %2,%1\n\t"
+ __asm__("bsf %3,%1\n\t"
"btrl %1,%0"
:"=m" (current->signal),"=r" (signr)
- :"1" (signr));
+ :"0" (current->signal), "1" (signr));
sa = current->sigaction + signr;
signr++;
if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
@@ -348,7 +200,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 SIGABRT: case SIGFPE: case SIGSEGV:
if (current->binfmt && current->binfmt->core_dump) {
if (current->binfmt->core_dump(signr, regs))
signr |= 0x80;
@@ -402,6 +254,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
}
regs->esp = (unsigned long) frame;
regs->eip = eip; /* "return" to the first handler */
+ regs->eflags &= ~TF_MASK;
current->tss.trap_no = current->tss.error_code = 0;
return 1;
}
diff --git a/arch/i386/traps.c b/arch/i386/kernel/traps.c
index 150b702b3..6dd7fc65b 100644
--- a/arch/i386/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/traps.c
+ * linux/arch/i386/traps.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
@@ -16,11 +16,18 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
+#include <linux/config.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
+asmlinkage int system_call(void);
+asmlinkage void lcall7(void);
+struct desc_struct default_ldt;
+
static inline void console_verbose(void)
{
extern int console_loglevel;
@@ -76,11 +83,23 @@ asmlinkage void coprocessor_error(void);
asmlinkage void reserved(void);
asmlinkage void alignment_check(void);
+int kstack_depth_to_print = 24;
+
+/*
+ * These constants are for searching for possible module text
+ * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
+ * a guess of how much space is likely to be vmalloced.
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define MODULE_RANGE (8*1024*1024)
+
/*static*/ void die_if_kernel(char * str, struct pt_regs * regs, long err)
{
int i;
unsigned long esp;
unsigned short ss;
+ unsigned long *stack, addr, module_start, module_end;
+ extern char start_kernel, etext;
esp = (unsigned long) &regs->esp;
ss = KERNEL_DS;
@@ -104,8 +123,38 @@ asmlinkage void alignment_check(void);
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 ", get_seg_long(ss,(i+(unsigned long *)esp)));
+ stack = (unsigned long *) esp;
+ for(i=0; i < kstack_depth_to_print; i++) {
+ if (((long) stack & 4095) == 0)
+ break;
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("%08lx ", get_seg_long(ss,stack++));
+ }
+ printk("\nCall Trace: ");
+ stack = (unsigned long *) esp;
+ i = 1;
+ module_start = ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
+ module_end = module_start + MODULE_RANGE;
+ while (((long) stack & 4095) != 0) {
+ addr = get_seg_long(ss, 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 >= (unsigned long) &start_kernel) &&
+ (addr <= (unsigned long) &etext)) ||
+ ((addr >= module_start) && (addr <= module_end))) {
+ if (i && ((i % 8) == 0))
+ printk("\n ");
+ printk("%08lx ", addr);
+ i++;
+ }
+ }
printk("\nCode: ");
for(i=0;i<20;i++)
printk("%02x ",0xff & get_seg_byte(regs->cs,(i+(char *)regs->eip)));
@@ -129,29 +178,23 @@ DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current)
asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
{
- int signr = SIGSEGV;
-
if (regs->eflags & VM_MASK) {
handle_vm86_fault((struct vm86_regs *) regs, error_code);
return;
}
die_if_kernel("general protection",regs,error_code);
- switch (get_seg_byte(regs->cs, (char *)regs->eip)) {
- case 0xCD: /* INT */
- case 0xF4: /* HLT */
- case 0xFA: /* CLI */
- case 0xFB: /* STI */
- signr = SIGILL;
- }
current->tss.error_code = error_code;
current->tss.trap_no = 13;
- send_sig(signr, current, 1);
+ send_sig(SIGSEGV, current, 1);
}
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
+#ifndef CONFIG_IGNORE_NMI
printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
- printk("You probably have a hardware problem with your RAM chips\n");
+ printk("You probably have a hardware problem with your RAM chips or a\n");
+ printk("power saving mode enabled.\n");
+#endif
}
asmlinkage void do_debug(struct pt_regs * regs, long error_code)
@@ -218,10 +261,54 @@ asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
math_error();
}
+/*
+ * 'math_state_restore()' saves the current math information in the
+ * old math state array, and gets the new ones from the current task
+ *
+ * Careful.. There are problems with IBM-designed IRQ13 behaviour.
+ * Don't touch unless you *really* know how it works.
+ */
+asmlinkage void math_state_restore(void)
+{
+ __asm__ __volatile__("clts");
+ if (last_task_used_math == current)
+ return;
+ timer_table[COPRO_TIMER].expires = jiffies+50;
+ timer_active |= 1<<COPRO_TIMER;
+ if (last_task_used_math)
+ __asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
+ else
+ __asm__("fnclex");
+ last_task_used_math = current;
+ if (current->used_math) {
+ __asm__("frstor %0": :"m" (current->tss.i387));
+ } else {
+ __asm__("fninit");
+ current->used_math=1;
+ }
+ timer_active &= ~(1<<COPRO_TIMER);
+}
+
+#ifndef CONFIG_MATH_EMULATION
+
+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 */
+
void trap_init(void)
{
int i;
+ struct desc_struct * p;
+ if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0)
+ EISA_bus = 1;
+ set_call_gate(&default_ldt,lcall7);
set_trap_gate(0,&divide_error);
set_trap_gate(1,&debug);
set_trap_gate(2,&nmi);
@@ -242,4 +329,21 @@ void trap_init(void)
set_trap_gate(17,&alignment_check);
for (i=18;i<48;i++)
set_trap_gate(i,&reserved);
+ set_system_gate(0x80,&system_call);
+/* set up GDT task & ldt entries */
+ p = gdt+FIRST_TSS_ENTRY;
+ set_tss_desc(p, &init_task.tss);
+ p++;
+ set_ldt_desc(p, &default_ldt, 1);
+ p++;
+ for(i=1 ; i<NR_TASKS ; i++) {
+ p->a=p->b=0;
+ p++;
+ p->a=p->b=0;
+ p++;
+ }
+/* Clear NT, so that we won't have troubles with that later on */
+ __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
+ load_TR(0);
+ load_ldt(0);
}
diff --git a/arch/i386/vm86.c b/arch/i386/kernel/vm86.c
index 144d93a02..d55f8248f 100644
--- a/arch/i386/vm86.c
+++ b/arch/i386/kernel/vm86.c
@@ -9,8 +9,10 @@
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/ptrace.h>
+#include <linux/mm.h>
#include <asm/segment.h>
+#include <asm/pgtable.h>
#include <asm/io.h>
/*
@@ -39,8 +41,8 @@
/*
* virtual flags (16 and 32-bit versions)
*/
-#define VFLAGS (*(unsigned short *)&(current->v86flags))
-#define VEFLAGS (current->v86flags)
+#define VFLAGS (*(unsigned short *)&(current->tss.v86flags))
+#define VEFLAGS (current->tss.v86flags)
#define set_flags(X,new,mask) \
((X) = ((X) & ~(mask)) | ((new) & (mask)))
@@ -52,13 +54,13 @@ asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs)
{
unsigned long tmp;
- if (!current->vm86_info) {
+ if (!current->tss.vm86_info) {
printk("no vm86_info: BAD\n");
do_exit(SIGSEGV);
}
- set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->v86mask);
- memcpy_tofs(&current->vm86_info->regs,regs,sizeof(*regs));
- put_fs_long(current->screen_bitmap,&current->vm86_info->screen_bitmap);
+ set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->tss.v86mask);
+ memcpy_tofs(&current->tss.vm86_info->regs,regs,sizeof(*regs));
+ put_fs_long(current->tss.screen_bitmap,&current->tss.vm86_info->screen_bitmap);
tmp = current->tss.esp0;
current->tss.esp0 = current->saved_kernel_stack;
current->saved_kernel_stack = 0;
@@ -67,22 +69,34 @@ asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs)
static void mark_screen_rdonly(struct task_struct * tsk)
{
- unsigned long tmp;
- unsigned long *pg_table;
-
- if ((tmp = tsk->tss.cr3) != 0) {
- tmp = *(unsigned long *) tmp;
- if (tmp & PAGE_PRESENT) {
- tmp &= PAGE_MASK;
- pg_table = (0xA0000 >> PAGE_SHIFT) + (unsigned long *) tmp;
- tmp = 32;
- while (tmp--) {
- if (PAGE_PRESENT & *pg_table)
- *pg_table &= ~PAGE_RW;
- pg_table++;
- }
- }
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+ int i;
+
+ pgd = pgd_offset(tsk, 0xA0000);
+ if (pgd_none(*pgd))
+ return;
+ if (pgd_bad(*pgd)) {
+ printk("vm86: bad pgd entry [%p]:%08lx\n", pgd, pgd_val(*pgd));
+ pgd_clear(pgd);
+ return;
+ }
+ pmd = pmd_offset(pgd, 0xA0000);
+ if (pmd_none(*pmd))
+ return;
+ if (pmd_bad(*pmd)) {
+ printk("vm86: bad pmd entry [%p]:%08lx\n", pmd, pmd_val(*pmd));
+ pmd_clear(pmd);
+ return;
}
+ pte = pte_offset(pmd, 0xA0000);
+ for (i = 0; i < 32; i++) {
+ if (pte_present(*pte))
+ *pte = pte_wrprotect(*pte);
+ pte++;
+ }
+ invalidate();
}
asmlinkage int sys_vm86(struct vm86_struct * v86)
@@ -117,16 +131,16 @@ asmlinkage int sys_vm86(struct vm86_struct * v86)
switch (info.cpu_type) {
case CPU_286:
- current->v86mask = 0;
+ current->tss.v86mask = 0;
break;
case CPU_386:
- current->v86mask = NT_MASK | IOPL_MASK;
+ current->tss.v86mask = NT_MASK | IOPL_MASK;
break;
case CPU_486:
- current->v86mask = AC_MASK | NT_MASK | IOPL_MASK;
+ current->tss.v86mask = AC_MASK | NT_MASK | IOPL_MASK;
break;
default:
- current->v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
+ current->tss.v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
break;
}
@@ -136,9 +150,9 @@ asmlinkage int sys_vm86(struct vm86_struct * v86)
pt_regs->eax = 0;
current->saved_kernel_stack = current->tss.esp0;
current->tss.esp0 = (unsigned long) pt_regs;
- current->vm86_info = v86;
+ current->tss.vm86_info = v86;
- current->screen_bitmap = info.screen_bitmap;
+ current->tss.screen_bitmap = info.screen_bitmap;
if (info.flags & VM86_SCREEN_BITMAP)
mark_screen_rdonly(current);
__asm__ __volatile__("movl %0,%%esp\n\t"
@@ -178,7 +192,7 @@ static inline void clear_TF(struct vm86_regs * regs)
static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs)
{
- set_flags(VEFLAGS, eflags, current->v86mask);
+ set_flags(VEFLAGS, eflags, current->tss.v86mask);
set_flags(regs->eflags, eflags, SAFE_MASK);
if (eflags & IF_MASK)
set_IF(regs);
@@ -186,7 +200,7 @@ static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs
static inline void set_vflags_short(unsigned short flags, struct vm86_regs * regs)
{
- set_flags(VFLAGS, flags, current->v86mask);
+ set_flags(VFLAGS, flags, current->tss.v86mask);
set_flags(regs->eflags, flags, SAFE_MASK);
if (flags & IF_MASK)
set_IF(regs);
@@ -198,7 +212,7 @@ static inline unsigned long get_vflags(struct vm86_regs * regs)
if (VEFLAGS & VIF_MASK)
flags |= IF_MASK;
- return flags | (VEFLAGS & current->v86mask);
+ return flags | (VEFLAGS & current->tss.v86mask);
}
static inline int is_revectored(int nr, struct revectored_struct * bitmap)
@@ -287,9 +301,9 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned
unsigned short seg = get_fs_word((void *) ((i<<2)+2));
if (seg == BIOSSEG || regs->cs == BIOSSEG ||
- is_revectored(i, &current->vm86_info->int_revectored))
+ is_revectored(i, &current->tss.vm86_info->int_revectored))
return_to_32bit(regs, VM86_INTx + (i << 8));
- if (i==0x21 && is_revectored(AH(regs),&current->vm86_info->int21_revectored))
+ if (i==0x21 && is_revectored(AH(regs),&current->tss.vm86_info->int21_revectored))
return_to_32bit(regs, VM86_INTx + (i << 8));
pushw(ssp, sp, get_vflags(regs));
pushw(ssp, sp, regs->cs);
@@ -344,6 +358,14 @@ void handle_vm86_fault(struct vm86_regs * regs, long error_code)
IP(regs) += 2;
set_vflags_long(popl(ssp, sp), regs);
return;
+
+ /* iretd */
+ case 0xcf:
+ SP(regs) += 12;
+ IP(regs) = (unsigned short)popl(ssp, sp);
+ regs->cs = (unsigned short)popl(ssp, sp);
+ set_vflags_long(popl(ssp, sp), regs);
+ return;
}
/* pushf */
@@ -360,12 +382,6 @@ void handle_vm86_fault(struct vm86_regs * regs, long error_code)
set_vflags_short(popw(ssp, sp), regs);
return;
- /* int 3 */
- case 0xcc:
- IP(regs)++;
- do_int(regs, 3, ssp, sp);
- return;
-
/* int xx */
case 0xcd:
IP(regs) += 2;
diff --git a/arch/i386/main.c b/arch/i386/main.c
deleted file mode 100644
index 49606d4de..000000000
--- a/arch/i386/main.c
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * linux/init/main.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-#include <stdarg.h>
-
-#include <asm/system.h>
-#include <asm/io.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;
-asmlinkage void lcall7(void);
-struct desc_struct default_ldt;
-
-/*
- * 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)
-static inline _syscall0(int,pause)
-static inline _syscall0(int,setup)
-static inline _syscall0(int,sync)
-static inline _syscall0(pid_t,setsid)
-static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
-static inline _syscall1(int,dup,int,fd)
-static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
-static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
-static inline _syscall1(int,close,int,fd)
-static inline _syscall1(int,_exit,int,exitcode)
-static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-
-static inline pid_t wait(int * wait_stat)
-{
- return waitpid(-1,wait_stat,0);
-}
-
-static char printbuf[1024];
-
-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 sock_init(void);
-extern long rd_init(long mem_start, int length);
-unsigned long net_dev_init(unsigned long, unsigned long);
-extern long bios32_init(long, long);
-
-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 SCREEN_INFO (*(struct screen_info *) (PARAM+0))
-#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))
-
-/*
- * Boot command-line arguments
- */
-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;
-
-static char term[21];
-int rows, cols;
-
-static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
-static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", term, NULL, };
-
-static char * argv_rc[] = { "/bin/sh", NULL };
-static char * envp_rc[] = { "HOME=/", term, NULL };
-
-static char * argv[] = { "-/bin/sh",NULL };
-static char * envp[] = { "HOME=/usr/root", term, NULL };
-
-struct drive_info_struct { char dummy[32]; } drive_info;
-struct screen_info screen_info;
-
-unsigned char aux_device_present;
-int ramdisk_size;
-int root_mountflags = 0;
-
-static char fpu_error = 0;
-
-static char command_line[COMMAND_LINE_SIZE] = { 0, };
-
-char *get_options(char *str, int *ints)
-{
- char *cur = str;
- int i=1;
-
- while (cur && isdigit(*cur) && i <= 10) {
- ints[i++] = simple_strtoul(cur,NULL,0);
- if ((cur = strchr(cur,',')) != NULL)
- cur++;
- }
- ints[0] = i-1;
- return(cur);
-}
-
-struct {
- char *str;
- void (*setup_func)(char *, int *);
-} bootsetups[] = {
- { "reserve=", reserve_setup },
- { "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];
-}
-
-static int checksetup(char *line)
-{
- int i = 0;
- int ints[11];
-
- while (bootsetups[i].str) {
- int n = strlen(bootsetups[i].str);
- if (!strncmp(line,bootsetups[i].str,n)) {
- bootsetups[i].setup_func(get_options(line+n,ints), ints);
- return 1;
- }
- i++;
- }
- return 0;
-}
-
-unsigned long loops_per_sec = 1;
-
-static void calibrate_delay(void)
-{
- int ticks;
-
- printk("Calibrating delay loop.. ");
- while (loops_per_sec <<= 1) {
- ticks = jiffies;
- __delay(loops_per_sec);
- ticks = jiffies - ticks;
- if (ticks >= HZ) {
- __asm__("mull %1 ; divl %2"
- :"=a" (loops_per_sec)
- :"d" (HZ),
- "r" (ticks),
- "0" (loops_per_sec)
- :"dx");
- printk("ok - %lu.%02lu BogoMips\n",
- loops_per_sec/500000,
- (loops_per_sec/5000) % 100);
- return;
- }
- }
- printk("failed\n");
-}
-
-/*
- * parse machine depended options
- */
-int parse_machine_options(char *line)
-{
- if (!strcmp(line,"no-hlt")) {
- hlt_works_ok = 0;
- return 1;
- }
- if (!strcmp(line,"no387")) {
- hard_math = 0;
- __asm__("movl %%cr0,%%eax\n\t"
- "orl $0xE,%%eax\n\t"
- "movl %%eax,%%cr0\n\t" : : : "ax");
- return 1;
- }
-}
-
-static void copro_timeout(void)
-{
- fpu_error = 1;
- timer_table[COPRO_TIMER].expires = jiffies+100;
- timer_active |= 1<<COPRO_TIMER;
- printk("387 failed: trying to reset\n");
- send_sig(SIGFPE, last_task_used_math, 1);
- outb_p(0,0xf1);
- outb_p(0,0xf0);
-}
-
-static void check_fpu(void)
-{
- static double x = 4195835.0;
- static double y = 3145727.0;
- unsigned short control_word;
- int i;
-
- if (!hard_math) {
-#ifndef CONFIG_MATH_EMULATION
- printk("No coprocessor found and no math emulation present.\n");
- printk("Giving up.\n");
- for (;;) ;
-#endif
- return;
- }
- /*
- * check if exception 16 works correctly.. This is truly evil
- * code: it disables the high 8 interrupts to make sure that
- * the irq13 doesn't happen. But as this will lead to a lockup
- * if no exception16 arrives, it depends on the fact that the
- * high 8 interrupts will be re-enabled by the next timer tick.
- * So the irq13 will happen eventually, but the exception 16
- * should get there first..
- */
- printk("Checking 386/387 coupling... ");
- timer_table[COPRO_TIMER].expires = jiffies+50;
- timer_table[COPRO_TIMER].fn = copro_timeout;
- timer_active |= 1<<COPRO_TIMER;
- __asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word));
- control_word &= 0xffc0;
- __asm__("fldcw %0 ; fwait": :"m" (*&control_word));
- outb_p(inb_p(0x21) | (1 << 2), 0x21);
- __asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait");
- timer_active &= ~(1<<COPRO_TIMER);
- if (fpu_error)
- return;
- if (!ignore_irq13) {
- printk("Ok, fpu using old IRQ13 error reporting\n");
- return;
- }
- __asm__("fninit\n\t"
- "fldl %1\n\t"
- "fdivl %2\n\t"
- "fmull %2\n\t"
- "fldl %1\n\t"
- "fsubp %%st,%%st(1)\n\t"
- "fistpl %0\n\t"
- "fwait\n\t"
- "fninit"
- : "=m" (*&i)
- : "m" (*&x), "m" (*&y));
- if (!i) {
- printk("Ok, fpu using exception 16 error reporting.\n");
- return;
-
- }
- printk("Ok, FDIV bug i%c86 system\n", '0'+x86);
-}
-
-static void check_hlt(void)
-{
- printk("Checking 'hlt' instruction... ");
- if (!hlt_works_ok) {
- printk("disabled\n");
- return;
- }
- __asm__ __volatile__("hlt ; hlt ; hlt ; hlt");
- printk("Ok.\n");
-}
-
-static void check_bugs(void)
-{
- check_fpu();
- check_hlt();
-}
-
-asmlinkage void start_kernel(void)
-{
-/*
- * Interrupts are still disabled. Do necessary setups, then
- * enable them
- */
- set_call_gate(&default_ldt,lcall7);
- ROOT_DEV = ORIG_ROOT_DEV;
- drive_info = DRIVE_INFO;
- screen_info = SCREEN_INFO;
- aux_device_present = AUX_DEVICE_INFO;
- memory_end = (1<<20) + (EXT_MEM_K<<10);
- memory_end &= PAGE_MASK;
- ramdisk_size = RAMDISK_SIZE;
- copy_options(command_line,COMMAND_LINE);
-#ifdef CONFIG_MAX_16M
- if (memory_end > 16*1024*1024)
- memory_end = 16*1024*1024;
-#endif
- if (MOUNT_ROOT_RDONLY)
- root_mountflags |= MS_RDONLY;
- if ((unsigned long)&end >= (1024*1024)) {
- memory_start = (unsigned long) &end;
- low_memory_start = PAGE_SIZE;
- } else {
- memory_start = 1024*1024;
- low_memory_start = (unsigned long) &end;
- }
- low_memory_start = PAGE_ALIGN(low_memory_start);
- memory_start = paging_init(memory_start,memory_end);
- if (strncmp((char*)0x0FFFD9, "EISA", 4) == 0)
- EISA_bus = 1;
- 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 = bios32_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
- 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(low_memory_start,memory_start,memory_end);
- buffer_init();
- time_init();
- sock_init();
-#ifdef CONFIG_SYSVIPC
- ipc_init();
-#endif
- sti();
- check_bugs();
-
- system_utsname.machine[1] = '0' + x86;
- 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/i386/math-emu/Makefile b/arch/i386/math-emu/Makefile
new file mode 100644
index 000000000..2d391a9e6
--- /dev/null
+++ b/arch/i386/math-emu/Makefile
@@ -0,0 +1,52 @@
+#
+# Makefile for wm-FPU-emu
+#
+
+#DEBUG = -DDEBUGGING
+DEBUG =
+PARANOID = -DPARANOID
+CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin
+
+.c.o:
+ $(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
+
+.S.o:
+ $(CC) -D__ASSEMBLER__ $(PARANOID) -c $<
+
+.s.o:
+ $(CC) -c $<
+
+OBJS = fpu_entry.o div_small.o errors.o \
+ fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \
+ load_store.o get_address.o \
+ poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \
+ reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \
+ reg_div.o reg_mul.o reg_norm.o \
+ reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \
+ reg_round.o \
+ wm_shrx.o wm_sqrt.o \
+ div_Xsig.o polynom_Xsig.o round_Xsig.o \
+ shr_Xsig.o mul_Xsig.o
+
+math.a: $(OBJS)
+ rm -f math.a
+ $(AR) rcs math.a $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+ $(CPP) -D__ASSEMBLER__ -M *.S >> .depend
+
+proto:
+ cproto -e -DMAKING_PROTO *.c >fpu_proto.h
+
+modules:
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/arch/i386/math-emu/README b/arch/i386/math-emu/README
new file mode 100644
index 000000000..2c0acb423
--- /dev/null
+++ b/arch/i386/math-emu/README
@@ -0,0 +1,436 @@
+ +---------------------------------------------------------------------------+
+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | This program is free software; you can redistribute it and/or modify |
+ | it under the terms of the GNU General Public License version 2 as |
+ | published by the Free Software Foundation. |
+ | |
+ | This program is distributed in the hope that it will be useful, |
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ | GNU General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU General Public License |
+ | along with this program; if not, write to the Free Software |
+ | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
+ | |
+ +---------------------------------------------------------------------------+
+
+
+
+wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
+which was my 80387 emulator for early versions of djgpp (gcc under
+msdos); wm-emu387 was in turn based upon emu387 which was written by
+DJ Delorie for djgpp. The interface to the Linux kernel is based upon
+the original Linux math emulator by Linus Torvalds.
+
+My target FPU for wm-FPU-emu is that described in the Intel486
+Programmer's Reference Manual (1992 edition). Unfortunately, numerous
+facets of the functioning of the FPU are not well covered in the
+Reference Manual. The information in the manual has been supplemented
+with measurements on real 80486's. Unfortunately, it is simply not
+possible to be sure that all of the peculiarities of the 80486 have
+been discovered, so there is always likely to be obscure differences
+in the detailed behaviour of the emulator and a real 80486.
+
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU,
+but is very close. See "Limitations" later in this file for a list of
+some differences.
+
+Please report bugs, etc to me at:
+ billm@vaxc.cc.monash.edu.au
+ or at:
+ billm@jacobi.maths.monash.edu.au
+
+
+--Bill Metzenthen
+ August 1994
+
+
+----------------------- Internals of wm-FPU-emu -----------------------
+
+Numeric algorithms:
+(1) Add, subtract, and multiply. Nothing remarkable in these.
+(2) Divide has been tuned to get reasonable performance. The algorithm
+ is not the obvious one which most people seem to use, but is designed
+ to take advantage of the characteristics of the 80386. I expect that
+ it has been invented many times before I discovered it, but I have not
+ seen it. It is based upon one of those ideas which one carries around
+ for years without ever bothering to check it out.
+(3) The sqrt function has been tuned to get good performance. It is based
+ upon Newton's classic method. Performance was improved by capitalizing
+ upon the properties of Newton's method, and the code is once again
+ structured taking account of the 80386 characteristics.
+(4) The trig, log, and exp functions are based in each case upon quasi-
+ "optimal" polynomial approximations. My definition of "optimal" was
+ based upon getting good accuracy with reasonable speed.
+(5) The argument reducing code for the trig function effectively uses
+ a value of pi which is accurate to more than 128 bits. As a consequence,
+ the reduced argument is accurate to more than 64 bits for arguments up
+ to a few pi, and accurate to more than 64 bits for most arguments,
+ even for arguments approaching 2^63. This is far superior to an
+ 80486, which uses a value of pi which is accurate to 66 bits.
+
+The code of the emulator is complicated slightly by the need to
+account for a limited form of re-entrancy. Normally, the emulator will
+emulate each FPU instruction to completion without interruption.
+However, it may happen that when the emulator is accessing the user
+memory space, swapping may be needed. In this case the emulator may be
+temporarily suspended while disk i/o takes place. During this time
+another process may use the emulator, thereby perhaps changing static
+variables. The code which accesses user memory is confined to five
+files:
+ fpu_entry.c
+ reg_ld_str.c
+ load_store.c
+ get_address.c
+ errors.c
+As from version 1.12 of the emulator, no static variables are used
+(apart from those in the kernel's per-process tables). The emulator is
+therefore now fully re-entrant, rather than having just the restricted
+form of re-entrancy which is required by the Linux kernel.
+
+----------------------- Limitations of wm-FPU-emu -----------------------
+
+There are a number of differences between the current wm-FPU-emu
+(version 1.20) and the 80486 FPU (apart from bugs). Some of the more
+important differences are listed below:
+
+The Roundup flag does not have much meaning for the transcendental
+functions and its 80486 value with these functions is likely to differ
+from its emulator value.
+
+In a few rare cases the Underflow flag obtained with the emulator will
+be different from that obtained with an 80486. This occurs when the
+following conditions apply simultaneously:
+(a) the operands have a higher precision than the current setting of the
+ precision control (PC) flags.
+(b) the underflow exception is masked.
+(c) the magnitude of the exact result (before rounding) is less than 2^-16382.
+(d) the magnitude of the final result (after rounding) is exactly 2^-16382.
+(e) the magnitude of the exact result would be exactly 2^-16382 if the
+ operands were rounded to the current precision before the arithmetic
+ operation was performed.
+If all of these apply, the emulator will set the Underflow flag but a real
+80486 will not.
+
+NOTE: Certain formats of Extended Real are UNSUPPORTED. They are
+unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities,
+and Unnormals. None of these will be generated by an 80486 or by the
+emulator. Do not use them. The emulator treats them differently in
+detail from the way an 80486 does.
+
+The emulator treats PseudoDenormals differently from an 80486. These
+numbers are in fact properly normalised numbers with the exponent
+offset by 1, and the emulator treats them as such. Unlike the 80486,
+the emulator does not generate a Denormal Operand exception for these
+numbers. The arithmetical results produced when using such a number as
+an operand are the same for the emulator and a real 80486 (apart from
+any slight precision difference for the transcendental functions).
+Neither the emulator nor an 80486 produces one of these numbers as the
+result of any arithmetic operation. An 80486 can keep one of these
+numbers in an FPU register with its identity as a PseudoDenormal, but
+the emulator will not; they are always converted to a valid number.
+
+Self modifying code can cause the emulator to fail. An example of such
+code is:
+ movl %esp,[%ebx]
+ fld1
+The FPU instruction may be (usually will be) loaded into the pre-fetch
+queue of the cpu before the mov instruction is executed. If the
+destination of the 'movl' overlaps the FPU instruction then the bytes
+in the prefetch queue and memory will be inconsistent when the FPU
+instruction is executed. The emulator will be invoked but will not be
+able to find the instruction which caused the device-not-present
+exception. For this case, the emulator cannot emulate the behaviour of
+an 80486DX.
+
+Handling of the address size override prefix byte (0x67) has not been
+extensively tested yet. A major problem exists because using it in
+vm86 mode can cause a general protection fault. Address offsets
+greater than 0xffff appear to be illegal in vm86 mode but are quite
+acceptable (and work) in real mode. A small test program developed to
+check the addressing, and which runs successfully in real mode,
+crashes dosemu under Linux and also brings Windows down with a general
+protection fault message when run under the MS-DOS prompt of Windows
+3.1. (The program simply reads data from a valid address).
+
+The emulator supports 16-bit protected mode, with one difference from
+an 80486DX. A 80486DX will allow some floating point instructions to
+write a few bytes below the lowest address of the stack. The emulator
+will not allow this in 16-bit protected mode: no instructions are
+allowed to write outside the bounds set by the protection.
+
+----------------------- Performance of wm-FPU-emu -----------------------
+
+Speed.
+-----
+
+The speed of floating point computation with the emulator will depend
+upon instruction mix. Relative performance is best for the instructions
+which require most computation. The simple instructions are adversely
+affected by the fpu instruction trap overhead.
+
+
+Timing: Some simple timing tests have been made on the emulator functions.
+The times include load/store instructions. All times are in microseconds
+measured on a 33MHz 386 with 64k cache. The Turbo C tests were under
+ms-dos, the next two columns are for emulators running with the djgpp
+ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97,
+using libm4.0 (hard).
+
+function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu
+
+ + 60.5 154.8 76.5 139.4
+ - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7
+ * 71.0 190.8 79.6 146.6
+ / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1
+
+ sin() 310.8 4692.0 319.0 398.5
+ cos() 284.4 4855.2 308.0 388.7
+ tan() 495.0 8807.1 394.9 504.7
+ atan() 328.9 4866.4 601.1 419.5-491.9
+
+ sqrt() 128.7 crashed 145.2 227.0
+ log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1
+ exp() 479.1 6619.2 469.1 850.8
+
+
+The performance under Linux is improved by the use of look-ahead code.
+The following results show the improvement which is obtained under
+Linux due to the look-ahead code. Also given are the times for the
+original Linux emulator with the 4.1 'soft' lib.
+
+ [ Linus' note: I changed look-ahead to be the default under linux, as
+ there was no reason not to use it after I had edited it to be
+ disabled during tracing ]
+
+ wm-FPU-emu w original w
+ look-ahead 'soft' lib
+ + 106.4 190.2
+ - 108.6-111.6 192.4-216.2
+ * 113.4 193.1
+ / 108.8-124.4 700.1-706.2
+
+ sin() 390.5 2642.0
+ cos() 381.5 2767.4
+ tan() 496.5 3153.3
+ atan() 367.2-435.5 2439.4-3396.8
+
+ sqrt() 195.1 4732.5
+ log() 358.0-387.5 3359.2-3390.3
+ exp() 619.3 4046.4
+
+
+These figures are now somewhat out-of-date. The emulator has become
+progressively slower for most functions as more of the 80486 features
+have been implemented.
+
+
+----------------------- Accuracy of wm-FPU-emu -----------------------
+
+
+The accuracy of the emulator is in almost all cases equal to or better
+than that of an Intel 80486 FPU.
+
+The results of the basic arithmetic functions (+,-,*,/), and fsqrt
+match those of an 80486 FPU. They are the best possible; the error for
+these never exceeds 1/2 an lsb. The fprem and fprem1 instructions
+return exact results; they have no error.
+
+
+The following table compares the emulator accuracy for the sqrt(),
+trig and log functions against the Turbo C "emulator". For this table,
+each function was tested at about 400 points. Ideal worst-case results
+would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for
+arguments greater than pi/4 can be thought of as being related to the
+precision of the argument x; e.g. an argument of pi/2-(1e-10) which is
+accurate to 64 bits can result in a relative accuracy in cos() of
+about 64 + log2(cos(x)) = 31 bits.
+
+
+Function Tested x range Worst result Turbo C
+ (relative bits)
+
+sqrt(x) 1 .. 2 64.1 63.2
+atan(x) 1e-10 .. 200 64.2 62.8
+cos(x) 0 .. pi/2-(1e-10) 64.4 (x <= pi/4) 62.4
+ 64.1 (x = pi/2-(1e-10)) 31.9
+sin(x) 1e-10 .. pi/2 64.0 62.8
+tan(x) 1e-10 .. pi/2-(1e-10) 64.0 (x <= pi/4) 62.1
+ 64.1 (x = pi/2-(1e-10)) 31.9
+exp(x) 0 .. 1 63.1 ** 62.9
+log(x) 1+1e-6 .. 2 63.8 ** 62.1
+
+** The accuracy for exp() and log() is low because the FPU (emulator)
+does not compute them directly; two operations are required.
+
+
+The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or
+later) for 'float' variables (24 bit precision numbers) when precision
+control is set to 24, 53 or 64 bits, and for 'double' variables (53
+bit precision numbers) when precision control is set to 53 bits (a
+properly performing FPU cannot pass the 'paranoia' tests for 'double'
+variables when precision control is set to 64 bits).
+
+The code for reducing the argument for the trig functions (fsin, fcos,
+fptan and fsincos) has been improved and now effectively uses a value
+for pi which is accurate to more than 128 bits precision. As a
+consequence, the accuracy of these functions for large arguments has
+been dramatically improved (and is now very much better than an 80486
+FPU). There is also now no degradation of accuracy for fcos and fptan
+for operands close to pi/2. Measured results are (note that the
+definition of accuracy has changed slightly from that used for the
+above table):
+
+Function Tested x range Worst result
+ (absolute bits)
+
+cos(x) 0 .. 9.22e+18 62.0
+sin(x) 1e-16 .. 9.22e+18 62.1
+tan(x) 1e-16 .. 9.22e+18 61.8
+
+It is possible with some effort to find very large arguments which
+give much degraded precision. For example, the integer number
+ 8227740058411162616.0
+is within about 10e-7 of a multiple of pi. To find the tan (for
+example) of this number to 64 bits precision it would be necessary to
+have a value of pi which had about 150 bits precision. The FPU
+emulator computes the result to about 42.6 bits precision (the correct
+result is about -9.739715e-8). On the other hand, an 80486 FPU returns
+0.01059, which in relative terms is hopelessly inaccurate.
+
+For arguments close to critical angles (which occur at multiples of
+pi/2) the emulator is more accurate than an 80486 FPU. For very large
+arguments, the emulator is far more accurate.
+
+
+Prior to version 1.20 of the emulator, the accuracy of the results for
+the transcendental functions (in their principal range) was not as
+good as the results from an 80486 FPU. From version 1.20, the accuracy
+has been considerably improved and these functions now give measured
+worst-case results which are better than the worst-case results given
+by an 80486 FPU.
+
+The following table gives the measured results for the emulator. The
+number of randomly selected arguments in each case is about half a
+million. The group of three columns gives the frequency of the given
+accuracy in number of times per million, thus the second of these
+columns shows that an accuracy of between 63.80 and 63.89 bits was
+found at a rate of 133 times per one million measurements for fsin.
+The results show that the fsin, fcos and fptan instructions return
+results which are in error (i.e. less accurate than the best possible
+result (which is 64 bits)) for about one per cent of all arguments
+between -pi/2 and +pi/2. The other instructions have a lower
+frequency of results which are in error. The last two columns give
+the worst accuracy which was found (in bits) and the approximate value
+of the argument which produced it.
+
+ frequency (per M)
+ ------------------- ---------------
+instr arg range # tests 63.7 63.8 63.9 worst at arg
+ bits bits bits bits
+----- ------------ ------- ---- ---- ----- ----- --------
+fsin (0,pi/2) 547756 0 133 10673 63.89 0.451317
+fcos (0,pi/2) 547563 0 126 10532 63.85 0.700801
+fptan (0,pi/2) 536274 11 267 10059 63.74 0.784876
+fpatan 4 quadrants 517087 0 8 1855 63.88 0.435121 (4q)
+fyl2x (0,20) 541861 0 0 1323 63.94 1.40923 (x)
+fyl2xp1 (-.293,.414) 520256 0 0 5678 63.93 0.408542 (x)
+f2xm1 (-1,1) 538847 4 481 6488 63.79 0.167709
+
+
+Tests performed on an 80486 FPU showed results of lower accuracy. The
+following table gives the results which were obtained with an AMD
+486DX2/66 (other tests indicate that an Intel 486DX produces
+identical results). The tests were basically the same as those used
+to measure the emulator (the values, being random, were in general not
+the same). The total number of tests for each instruction are given
+at the end of the table, in case each about 100k tests were performed.
+Another line of figures at the end of the table shows that most of the
+instructions return results which are in error for more than 10
+percent of the arguments tested.
+
+The numbers in the body of the table give the approx number of times a
+result of the given accuracy in bits (given in the left-most column)
+was obtained per one million arguments. For three of the instructions,
+two columns of results are given: * The second column for f2xm1 gives
+the number cases where the results of the first column were for a
+positive argument, this shows that this instruction gives better
+results for positive arguments than it does for negative. * In the
+cases of fcos and fptan, the first column gives the results when all
+cases where arguments greater than 1.5 were removed from the results
+given in the second column. Unlike the emulator, an 80486 FPU returns
+results of relatively poor accuracy for these instructions when the
+argument approaches pi/2. The table does not show those cases when the
+accuracy of the results were less than 62 bits, which occurs quite
+often for fsin and fptan when the argument approaches pi/2. This poor
+accuracy is discussed above in relation to the Turbo C "emulator", and
+the accuracy of the value of pi.
+
+
+bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan
+62.0 0 0 0 0 437 0 0 0 0 925
+62.1 0 0 10 0 894 0 0 0 0 1023
+62.2 14 0 0 0 1033 0 0 0 0 945
+62.3 57 0 0 0 1202 0 0 0 0 1023
+62.4 385 0 0 10 1292 0 23 0 0 1178
+62.5 1140 0 0 119 1649 0 39 0 0 1149
+62.6 2037 0 0 189 1620 0 16 0 0 1169
+62.7 5086 14 0 646 2315 10 101 35 39 1402
+62.8 8818 86 0 984 3050 59 287 131 224 2036
+62.9 11340 1355 0 2126 4153 79 605 357 321 1948
+63.0 15557 4750 0 3319 5376 246 1281 862 808 2688
+63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302
+63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724
+63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236
+63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587
+63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262
+63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346
+63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209
+63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329
+63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601
+
+Per cent with error:
+ 30.9 3.2 18.5 9.8 13.1 11.6 17.4
+Total arguments tested:
+ 70194 70099 101784 100641 100641 101799 128853 114893 102675 102675
+
+
+------------------------- Contributors -------------------------------
+
+A number of people have contributed to the development of the
+emulator, often by just reporting bugs, sometimes with suggested
+fixes, and a few kind people have provided me with access in one way
+or another to an 80486 machine. Contributors include (to those people
+who I may have forgotten, please forgive me):
+
+Linus Torvalds
+Tommy.Thorn@daimi.aau.dk
+Andrew.Tridgell@anu.edu.au
+Nick Holloway, alfie@dcs.warwick.ac.uk
+Hermano Moura, moura@dcs.gla.ac.uk
+Jon Jagger, J.Jagger@scp.ac.uk
+Lennart Benschop
+Brian Gallew, geek+@CMU.EDU
+Thomas Staniszewski, ts3v+@andrew.cmu.edu
+Martin Howell, mph@plasma.apana.org.au
+M Saggaf, alsaggaf@athena.mit.edu
+Peter Barker, PETER@socpsy.sci.fau.edu
+tom@vlsivie.tuwien.ac.at
+Dan Russel, russed@rpi.edu
+Daniel Carosone, danielce@ee.mu.oz.au
+cae@jpmorgan.com
+Hamish Coleman, t933093@minyos.xx.rmit.oz.au
+Bruce Evans, bde@kralizec.zeta.org.au
+Timo Korvola, Timo.Korvola@hut.fi
+Rick Lyons, rick@razorback.brisnet.org.au
+Rick, jrs@world.std.com
+
+...and numerous others who responded to my request for help with
+a real 80486.
+
diff --git a/arch/i386/math-emu/control_w.h b/arch/i386/math-emu/control_w.h
new file mode 100644
index 000000000..ef5fced39
--- /dev/null
+++ b/arch/i386/math-emu/control_w.h
@@ -0,0 +1,45 @@
+/*---------------------------------------------------------------------------+
+ | control_w.h |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _CONTROLW_H_
+#define _CONTROLW_H_
+
+#ifdef __ASSEMBLER__
+#define _Const_(x) $##x
+#else
+#define _Const_(x) x
+#endif
+
+#define CW_RC _Const_(0x0C00) /* rounding control */
+#define CW_PC _Const_(0x0300) /* precision control */
+
+#define CW_Precision Const_(0x0020) /* loss of precision mask */
+#define CW_Underflow Const_(0x0010) /* underflow mask */
+#define CW_Overflow Const_(0x0008) /* overflow mask */
+#define CW_ZeroDiv Const_(0x0004) /* divide by zero mask */
+#define CW_Denormal Const_(0x0002) /* denormalized operand mask */
+#define CW_Invalid Const_(0x0001) /* invalid operation mask */
+
+#define CW_Exceptions _Const_(0x003f) /* all masks */
+
+#define RC_RND _Const_(0x0000)
+#define RC_DOWN _Const_(0x0400)
+#define RC_UP _Const_(0x0800)
+#define RC_CHOP _Const_(0x0C00)
+
+/* p 15-5: Precision control bits affect only the following:
+ ADD, SUB(R), MUL, DIV(R), and SQRT */
+#define PR_24_BITS _Const_(0x000)
+#define PR_53_BITS _Const_(0x200)
+#define PR_64_BITS _Const_(0x300)
+#define PR_RESERVED_BITS _Const_(0x100)
+/* FULL_PRECISION simulates all exceptions masked */
+#define FULL_PRECISION (PR_64_BITS | RC_RND | 0x3f)
+
+#endif _CONTROLW_H_
diff --git a/arch/i386/math-emu/div_Xsig.S b/arch/i386/math-emu/div_Xsig.S
new file mode 100644
index 000000000..67d8be964
--- /dev/null
+++ b/arch/i386/math-emu/div_Xsig.S
@@ -0,0 +1,369 @@
+ .file "div_Xsig.S"
+/*---------------------------------------------------------------------------+
+ | div_Xsig.S |
+ | |
+ | Division subroutine for 96 bit quantities |
+ | |
+ | Copyright (C) 1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Divide the 96 bit quantity pointed to by a, by that pointed to by b, and |
+ | put the 96 bit result at the location d. |
+ | |
+ | The result may not be accurate to 96 bits. It is intended for use where |
+ | a result better than 64 bits is required. The result should usually be |
+ | good to at least 94 bits. |
+ | The returned result is actually divided by one half. This is done to |
+ | prevent overflow. |
+ | |
+ | .aaaaaaaaaaaaaa / .bbbbbbbbbbbbb -> .dddddddddddd |
+ | |
+ | void div_Xsig(Xsig *a, Xsig *b, Xsig *dest) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+#define XsigLL(x) (x)
+#define XsigL(x) 4(x)
+#define XsigH(x) 8(x)
+
+
+#ifndef NON_REENTRANT_FPU
+/*
+ Local storage on the stack:
+ Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+ */
+#define FPU_accum_3 -4(%ebp)
+#define FPU_accum_2 -8(%ebp)
+#define FPU_accum_1 -12(%ebp)
+#define FPU_accum_0 -16(%ebp)
+#define FPU_result_3 -20(%ebp)
+#define FPU_result_2 -24(%ebp)
+#define FPU_result_1 -28(%ebp)
+
+#else
+.data
+/*
+ Local storage in a static area:
+ Accumulator: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+ */
+ .align 2,0
+FPU_accum_3:
+ .long 0
+FPU_accum_2:
+ .long 0
+FPU_accum_1:
+ .long 0
+FPU_accum_0:
+ .long 0
+FPU_result_3:
+ .long 0
+FPU_result_2:
+ .long 0
+FPU_result_1:
+ .long 0
+#endif NON_REENTRANT_FPU
+
+
+.text
+ .align 2,144
+
+.globl _div_Xsig
+
+_div_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+#ifndef NON_REENTRANT_FPU
+ subl $28,%esp
+#endif NON_REENTRANT_FPU
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ebx /* pointer to denom */
+
+#ifdef PARANOID
+ testl $0x80000000, XsigH(%ebx) /* Divisor */
+ je L_bugged
+#endif PARANOID
+
+
+/*---------------------------------------------------------------------------+
+ | Divide: Return arg1/arg2 to arg3. |
+ | |
+ | The maximum returned value is (ignoring exponents) |
+ | .ffffffff ffffffff |
+ | ------------------ = 1.ffffffff fffffffe |
+ | .80000000 00000000 |
+ | and the minimum is |
+ | .80000000 00000000 |
+ | ------------------ = .80000000 00000001 (rounded) |
+ | .ffffffff ffffffff |
+ | |
+ +---------------------------------------------------------------------------*/
+
+ /* Save extended dividend in local register */
+
+ /* Divide by 2 to prevent overflow */
+ clc
+ movl XsigH(%esi),%eax
+ rcrl %eax
+ movl %eax,FPU_accum_3
+ movl XsigL(%esi),%eax
+ rcrl %eax
+ movl %eax,FPU_accum_2
+ movl XsigLL(%esi),%eax
+ rcrl %eax
+ movl %eax,FPU_accum_1
+ movl $0,%eax
+ rcrl %eax
+ movl %eax,FPU_accum_0
+
+ movl FPU_accum_2,%eax /* Get the current num */
+ movl FPU_accum_3,%edx
+
+/*----------------------------------------------------------------------*/
+/* Initialization done.
+ Do the first 32 bits. */
+
+ /* We will divide by a number which is too large */
+ movl XsigH(%ebx),%ecx
+ addl $1,%ecx
+ jnc LFirst_div_not_1
+
+ /* here we need to divide by 100000000h,
+ i.e., no division at all.. */
+ mov %edx,%eax
+ jmp LFirst_div_done
+
+LFirst_div_not_1:
+ divl %ecx /* Divide the numerator by the augmented
+ denom ms dw */
+
+LFirst_div_done:
+ movl %eax,FPU_result_3 /* Put the result in the answer */
+
+ mull XsigH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,FPU_accum_2 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_3
+
+ movl FPU_result_3,%eax /* Get the result back */
+ mull XsigL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_2
+ sbbl $0,FPU_accum_3
+ je LDo_2nd_32_bits /* Must check for non-zero result here */
+
+#ifdef PARANOID
+ jb L_bugged_1
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ incl FPU_result_3 /* Correct the answer */
+
+ movl XsigL(%ebx),%eax
+ movl XsigH(%ebx),%edx
+ subl %eax,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_2
+
+#ifdef PARANOID
+ sbbl $0,FPU_accum_3
+ jne L_bugged_1 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* Half of the main problem is done, there is just a reduced numerator
+ to handle now.
+ Work with the second 32 bits, FPU_accum_0 not used from now on */
+LDo_2nd_32_bits:
+ movl FPU_accum_2,%edx /* get the reduced num */
+ movl FPU_accum_1,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl XsigH(%ebx),%edx
+ jb LDo_2nd_div
+ ja LPrevent_2nd_overflow
+
+ cmpl XsigL(%ebx),%eax
+ jb LDo_2nd_div
+
+LPrevent_2nd_overflow:
+/* The numerator is greater or equal, would cause overflow */
+ /* prevent overflow */
+ subl XsigL(%ebx),%eax
+ sbbl XsigH(%ebx),%edx
+ movl %edx,FPU_accum_2
+ movl %eax,FPU_accum_1
+
+ incl FPU_result_3 /* Reflect the subtraction in the answer */
+
+#ifdef PARANOID
+ je L_bugged_2 /* Can't bump the result to 1.0 */
+#endif PARANOID
+
+LDo_2nd_div:
+ cmpl $0,%ecx /* augmented denom msw */
+ jnz LSecond_div_not_1
+
+ /* %ecx == 0, we are dividing by 1.0 */
+ mov %edx,%eax
+ jmp LSecond_div_done
+
+LSecond_div_not_1:
+ divl %ecx /* Divide the numerator by the denom ms dw */
+
+LSecond_div_done:
+ movl %eax,FPU_result_2 /* Put the result in the answer */
+
+ mull XsigH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ movl FPU_result_2,%eax /* Get the result back */
+ mull XsigL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,FPU_accum_0 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl $0,FPU_accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ jz LDo_3rd_32_bits
+
+#ifdef PARANOID
+ cmpl $1,FPU_accum_2
+ jne L_bugged_2
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ movl XsigL(%ebx),%eax
+ movl XsigH(%ebx),%edx
+ subl %eax,FPU_accum_0 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_1
+ sbbl $0,FPU_accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+ jne L_bugged_2
+#endif PARANOID
+
+ addl $1,FPU_result_2 /* Correct the answer */
+ adcl $0,FPU_result_3
+
+#ifdef PARANOID
+ jc L_bugged_2 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* The division is essentially finished here, we just need to perform
+ tidying operations.
+ Deal with the 3rd 32 bits */
+LDo_3rd_32_bits:
+ /* We use an approximation for the third 32 bits.
+ To take account of the 3rd 32 bits of the divisor
+ (call them del), we subtract del * (a/b) */
+
+ movl FPU_result_3,%eax /* a/b */
+ mull XsigLL(%ebx) /* del */
+
+ subl %edx,FPU_accum_1
+
+ /* A borrow indicates that the result is negative */
+ jnb LTest_over
+
+ movl XsigH(%ebx),%edx
+ addl %edx,FPU_accum_1
+
+ subl $1,FPU_result_2 /* Adjust the answer */
+ sbbl $0,FPU_result_3
+
+ /* The above addition might not have been enough, check again. */
+ movl FPU_accum_1,%edx /* get the reduced num */
+ cmpl XsigH(%ebx),%edx /* denom */
+ jb LDo_3rd_div
+
+ movl XsigH(%ebx),%edx
+ addl %edx,FPU_accum_1
+
+ subl $1,FPU_result_2 /* Adjust the answer */
+ sbbl $0,FPU_result_3
+ jmp LDo_3rd_div
+
+LTest_over:
+ movl FPU_accum_1,%edx /* get the reduced num */
+
+ /* need to check for possible subsequent overflow */
+ cmpl XsigH(%ebx),%edx /* denom */
+ jb LDo_3rd_div
+
+ /* prevent overflow */
+ subl XsigH(%ebx),%edx
+ movl %edx,FPU_accum_1
+
+ addl $1,FPU_result_2 /* Reflect the subtraction in the answer */
+ adcl $0,FPU_result_3
+
+LDo_3rd_div:
+ movl FPU_accum_0,%eax
+ movl FPU_accum_1,%edx
+ divl XsigH(%ebx)
+
+ movl %eax,FPU_result_1 /* Rough estimate of third word */
+
+ movl PARAM3,%esi /* pointer to answer */
+
+ movl FPU_result_1,%eax
+ movl %eax,XsigLL(%esi)
+ movl FPU_result_2,%eax
+ movl %eax,XsigL(%esi)
+ movl FPU_result_3,%eax
+ movl %eax,XsigH(%esi)
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+
+ leave
+ ret
+
+
+#ifdef PARANOID
+/* The logic is wrong if we got here */
+L_bugged:
+ pushl EX_INTERNAL|0x240
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_1:
+ pushl EX_INTERNAL|0x241
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x242
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
diff --git a/arch/i386/math-emu/div_small.S b/arch/i386/math-emu/div_small.S
new file mode 100644
index 000000000..0225a96d4
--- /dev/null
+++ b/arch/i386/math-emu/div_small.S
@@ -0,0 +1,50 @@
+ .file "div_small.S"
+/*---------------------------------------------------------------------------+
+ | div_small.S |
+ | |
+ | Divide a 64 bit integer by a 32 bit integer & return remainder. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | unsigned long div_small(unsigned long long *x, unsigned long y) |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+.globl _div_small
+
+_div_small:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ecx /* The denominator */
+
+ movl 4(%esi),%eax /* Get the current num msw */
+ xorl %edx,%edx
+ divl %ecx
+
+ movl %eax,4(%esi)
+
+ movl (%esi),%eax /* Get the num lsw */
+ divl %ecx
+
+ movl %eax,(%esi)
+
+ movl %edx,%eax /* Return the remainder in eax */
+
+ popl %esi
+
+ leave
+ ret
+
diff --git a/arch/i386/math-emu/errors.c b/arch/i386/math-emu/errors.c
new file mode 100644
index 000000000..e34eec942
--- /dev/null
+++ b/arch/i386/math-emu/errors.c
@@ -0,0 +1,671 @@
+/*---------------------------------------------------------------------------+
+ | errors.c |
+ | |
+ | The error handling functions for wm-FPU-emu |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include <linux/signal.h>
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "control_w.h"
+#include "reg_constant.h"
+#include "version.h"
+
+/* */
+#undef PRINT_MESSAGES
+/* */
+
+
+void Un_impl(void)
+{
+ unsigned char byte1, FPU_modrm;
+ unsigned long address = FPU_ORIG_EIP;
+
+ RE_ENTRANT_CHECK_OFF;
+ /* No need to verify_area(), we have previously fetched these bytes. */
+ printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address);
+ if ( FPU_CS == USER_CS )
+ {
+ while ( 1 )
+ {
+ byte1 = get_fs_byte((unsigned char *) address);
+ if ( (byte1 & 0xf8) == 0xd8 ) break;
+ printk("[%02x]", byte1);
+ address++;
+ }
+ printk("%02x ", byte1);
+ FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
+
+ if (FPU_modrm >= 0300)
+ printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
+ else
+ printk("/%d\n", (FPU_modrm >> 3) & 7);
+ }
+ else
+ {
+ printk("cs selector = %04x\n", FPU_CS);
+ }
+
+ RE_ENTRANT_CHECK_ON;
+
+ EXCEPTION(EX_Invalid);
+
+}
+
+
+/*
+ Called for opcodes which are illegal and which are known to result in a
+ SIGILL with a real 80486.
+ */
+void FPU_illegal(void)
+{
+ math_abort(FPU_info,SIGILL);
+}
+
+
+
+void emu_printall()
+{
+ int i;
+ static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
+ "DeNorm", "Inf", "NaN", "Empty" };
+ unsigned char byte1, FPU_modrm;
+ unsigned long address = FPU_ORIG_EIP;
+
+ RE_ENTRANT_CHECK_OFF;
+ /* No need to verify_area(), we have previously fetched these bytes. */
+ printk("At %p:", (void *) address);
+ if ( FPU_CS == USER_CS )
+ {
+#define MAX_PRINTED_BYTES 20
+ for ( i = 0; i < MAX_PRINTED_BYTES; i++ )
+ {
+ byte1 = get_fs_byte((unsigned char *) address);
+ if ( (byte1 & 0xf8) == 0xd8 )
+ {
+ printk(" %02x", byte1);
+ break;
+ }
+ printk(" [%02x]", byte1);
+ address++;
+ }
+ if ( i == MAX_PRINTED_BYTES )
+ printk(" [more..]\n");
+ else
+ {
+ FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
+
+ if (FPU_modrm >= 0300)
+ printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
+ else
+ printk(" /%d, mod=%d rm=%d\n",
+ (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
+ }
+ }
+ else
+ {
+ printk("%04x\n", FPU_CS);
+ }
+
+ partial_status = status_word();
+
+#ifdef DEBUGGING
+if ( partial_status & SW_Backward ) printk("SW: backward compatibility\n");
+if ( partial_status & SW_C3 ) printk("SW: condition bit 3\n");
+if ( partial_status & SW_C2 ) printk("SW: condition bit 2\n");
+if ( partial_status & SW_C1 ) printk("SW: condition bit 1\n");
+if ( partial_status & SW_C0 ) printk("SW: condition bit 0\n");
+if ( partial_status & SW_Summary ) printk("SW: exception summary\n");
+if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n");
+if ( partial_status & SW_Precision ) printk("SW: loss of precision\n");
+if ( partial_status & SW_Underflow ) printk("SW: underflow\n");
+if ( partial_status & SW_Overflow ) printk("SW: overflow\n");
+if ( partial_status & SW_Zero_Div ) printk("SW: divide by zero\n");
+if ( partial_status & SW_Denorm_Op ) printk("SW: denormalized operand\n");
+if ( partial_status & SW_Invalid ) printk("SW: invalid operation\n");
+#endif DEBUGGING
+
+ printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
+ partial_status & 0x8000 ? 1 : 0, /* busy */
+ (partial_status & 0x3800) >> 11, /* stack top pointer */
+ partial_status & 0x80 ? 1 : 0, /* Error summary status */
+ partial_status & 0x40 ? 1 : 0, /* Stack flag */
+ partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */
+ partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */
+ partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0,
+ partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
+ partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0);
+
+printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d ef=%d%d%d%d%d%d\n",
+ control_word & 0x1000 ? 1 : 0,
+ (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
+ (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
+ control_word & 0x80 ? 1 : 0,
+ control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0,
+ control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0,
+ control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0);
+
+ for ( i = 0; i < 8; i++ )
+ {
+ FPU_REG *r = &st(i);
+ switch (r->tag)
+ {
+ case TW_Empty:
+ continue;
+ break;
+ case TW_Zero:
+#if 0
+ printk("st(%d) %c .0000 0000 0000 0000 ",
+ i, r->sign ? '-' : '+');
+ break;
+#endif
+ case TW_Valid:
+ case TW_NaN:
+/* case TW_Denormal: */
+ case TW_Infinity:
+ printk("st(%d) %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
+ r->sign ? '-' : '+',
+ (long)(r->sigh >> 16),
+ (long)(r->sigh & 0xFFFF),
+ (long)(r->sigl >> 16),
+ (long)(r->sigl & 0xFFFF),
+ r->exp - EXP_BIAS + 1);
+ break;
+ default:
+ printk("Whoops! Error in errors.c ");
+ break;
+ }
+ printk("%s\n", tag_desc[(int) (unsigned) r->tag]);
+ }
+
+#ifdef OBSOLETE
+ printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
+ FPU_loaded_data.sign ? '-' : '+',
+ (long)(FPU_loaded_data.sigh >> 16),
+ (long)(FPU_loaded_data.sigh & 0xFFFF),
+ (long)(FPU_loaded_data.sigl >> 16),
+ (long)(FPU_loaded_data.sigl & 0xFFFF),
+ FPU_loaded_data.exp - EXP_BIAS + 1);
+ printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
+#endif OBSOLETE
+ RE_ENTRANT_CHECK_ON;
+
+}
+
+static struct {
+ int type;
+ char *name;
+} exception_names[] = {
+ { EX_StackOver, "stack overflow" },
+ { EX_StackUnder, "stack underflow" },
+ { EX_Precision, "loss of precision" },
+ { EX_Underflow, "underflow" },
+ { EX_Overflow, "overflow" },
+ { EX_ZeroDiv, "divide by zero" },
+ { EX_Denormal, "denormalized operand" },
+ { EX_Invalid, "invalid operation" },
+ { EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION },
+ { 0, NULL }
+};
+
+/*
+ EX_INTERNAL is always given with a code which indicates where the
+ error was detected.
+
+ Internal error types:
+ 0x14 in fpu_etc.c
+ 0x1nn in a *.c file:
+ 0x101 in reg_add_sub.c
+ 0x102 in reg_mul.c
+ 0x104 in poly_atan.c
+ 0x105 in reg_mul.c
+ 0x107 in fpu_trig.c
+ 0x108 in reg_compare.c
+ 0x109 in reg_compare.c
+ 0x110 in reg_add_sub.c
+ 0x111 in fpe_entry.c
+ 0x112 in fpu_trig.c
+ 0x113 in errors.c
+ 0x115 in fpu_trig.c
+ 0x116 in fpu_trig.c
+ 0x117 in fpu_trig.c
+ 0x118 in fpu_trig.c
+ 0x119 in fpu_trig.c
+ 0x120 in poly_atan.c
+ 0x121 in reg_compare.c
+ 0x122 in reg_compare.c
+ 0x123 in reg_compare.c
+ 0x125 in fpu_trig.c
+ 0x126 in fpu_entry.c
+ 0x127 in poly_2xm1.c
+ 0x128 in fpu_entry.c
+ 0x129 in fpu_entry.c
+ 0x130 in get_address.c
+ 0x131 in get_address.c
+ 0x132 in get_address.c
+ 0x133 in get_address.c
+ 0x140 in load_store.c
+ 0x141 in load_store.c
+ 0x150 in poly_sin.c
+ 0x151 in poly_sin.c
+ 0x160 in reg_ld_str.c
+ 0x161 in reg_ld_str.c
+ 0x162 in reg_ld_str.c
+ 0x163 in reg_ld_str.c
+ 0x2nn in an *.S file:
+ 0x201 in reg_u_add.S
+ 0x202 in reg_u_div.S
+ 0x203 in reg_u_div.S
+ 0x204 in reg_u_div.S
+ 0x205 in reg_u_mul.S
+ 0x206 in reg_u_sub.S
+ 0x207 in wm_sqrt.S
+ 0x208 in reg_div.S
+ 0x209 in reg_u_sub.S
+ 0x210 in reg_u_sub.S
+ 0x211 in reg_u_sub.S
+ 0x212 in reg_u_sub.S
+ 0x213 in wm_sqrt.S
+ 0x214 in wm_sqrt.S
+ 0x215 in wm_sqrt.S
+ 0x220 in reg_norm.S
+ 0x221 in reg_norm.S
+ 0x230 in reg_round.S
+ 0x231 in reg_round.S
+ 0x232 in reg_round.S
+ 0x233 in reg_round.S
+ 0x234 in reg_round.S
+ 0x235 in reg_round.S
+ 0x236 in reg_round.S
+ 0x240 in div_Xsig.S
+ 0x241 in div_Xsig.S
+ 0x242 in div_Xsig.S
+ */
+
+void exception(int n)
+{
+ int i, int_type;
+
+ int_type = 0; /* Needed only to stop compiler warnings */
+ if ( n & EX_INTERNAL )
+ {
+ int_type = n - EX_INTERNAL;
+ n = EX_INTERNAL;
+ /* Set lots of exception bits! */
+ partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
+ }
+ else
+ {
+ /* Extract only the bits which we use to set the status word */
+ n &= (SW_Exc_Mask);
+ /* Set the corresponding exception bit */
+ partial_status |= n;
+ /* Set summary bits iff exception isn't masked */
+ if ( partial_status & ~control_word & CW_Exceptions )
+ partial_status |= (SW_Summary | SW_Backward);
+ if ( n & (SW_Stack_Fault | EX_Precision) )
+ {
+ if ( !(n & SW_C1) )
+ /* This bit distinguishes over- from underflow for a stack fault,
+ and roundup from round-down for precision loss. */
+ partial_status &= ~SW_C1;
+ }
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) )
+ {
+#ifdef PRINT_MESSAGES
+ /* My message from the sponsor */
+ printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n");
+#endif PRINT_MESSAGES
+
+ /* Get a name string for error reporting */
+ for (i=0; exception_names[i].type; i++)
+ if ( (exception_names[i].type & n) == exception_names[i].type )
+ break;
+
+ if (exception_names[i].type)
+ {
+#ifdef PRINT_MESSAGES
+ printk("FP Exception: %s!\n", exception_names[i].name);
+#endif PRINT_MESSAGES
+ }
+ else
+ printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
+
+ if ( n == EX_INTERNAL )
+ {
+ printk("FPU emulator: Internal error type 0x%04x\n", int_type);
+ emu_printall();
+ }
+#ifdef PRINT_MESSAGES
+ else
+ emu_printall();
+#endif PRINT_MESSAGES
+
+ /*
+ * The 80486 generates an interrupt on the next non-control FPU
+ * instruction. So we need some means of flagging it.
+ * We use the ES (Error Summary) bit for this, assuming that
+ * this is the way a real FPU does it (until I can check it out),
+ * if not, then some method such as the following kludge might
+ * be needed.
+ */
+/* regs[0].tag |= TW_FPU_Interrupt; */
+ }
+ RE_ENTRANT_CHECK_ON;
+
+#ifdef __DEBUG__
+ math_abort(FPU_info,SIGFPE);
+#endif __DEBUG__
+
+}
+
+
+/* Real operation attempted on two operands, one a NaN. */
+/* Returns nz if the exception is unmasked */
+asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
+{
+ FPU_REG const *x;
+ int signalling;
+
+ /* The default result for the case of two "equal" NaNs (signs may
+ differ) is chosen to reproduce 80486 behaviour */
+ x = a;
+ if (a->tag == TW_NaN)
+ {
+ if (b->tag == TW_NaN)
+ {
+ signalling = !(a->sigh & b->sigh & 0x40000000);
+ /* find the "larger" */
+ if ( significand(a) < significand(b) )
+ x = b;
+ }
+ else
+ {
+ /* return the quiet version of the NaN in a */
+ signalling = !(a->sigh & 0x40000000);
+ }
+ }
+ else
+#ifdef PARANOID
+ if (b->tag == TW_NaN)
+#endif PARANOID
+ {
+ signalling = !(b->sigh & 0x40000000);
+ x = b;
+ }
+#ifdef PARANOID
+ else
+ {
+ signalling = 0;
+ EXCEPTION(EX_INTERNAL|0x113);
+ x = &CONST_QNaN;
+ }
+#endif PARANOID
+
+ if ( !signalling )
+ {
+ if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ return 0;
+ }
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ if ( !(x->sigh & 0x80000000) ) /* pseudo-NaN ? */
+ x = &CONST_QNaN;
+ reg_move(x, dest);
+ /* ensure a Quiet NaN */
+ dest->sigh |= 0x40000000;
+ }
+
+ EXCEPTION(EX_Invalid);
+
+ return !(control_word & CW_Invalid);
+}
+
+
+/* Invalid arith operation on Valid registers */
+/* Returns nz if the exception is unmasked */
+asmlinkage int arith_invalid(FPU_REG *dest)
+{
+
+ EXCEPTION(EX_Invalid);
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, dest);
+ }
+
+ return !(control_word & CW_Invalid);
+
+}
+
+
+/* Divide a finite number by zero */
+asmlinkage int divide_by_zero(int sign, FPU_REG *dest)
+{
+
+ if ( control_word & CW_ZeroDiv )
+ {
+ /* The masked response */
+ reg_move(&CONST_INF, dest);
+ dest->sign = (unsigned char)sign;
+ }
+
+ EXCEPTION(EX_ZeroDiv);
+
+ return !(control_word & CW_ZeroDiv);
+
+}
+
+
+/* This may be called often, so keep it lean */
+int set_precision_flag(int flags)
+{
+ if ( control_word & CW_Precision )
+ {
+ partial_status &= ~(SW_C1 & flags);
+ partial_status |= flags; /* The masked response */
+ return 0;
+ }
+ else
+ {
+ exception(flags);
+ return 1;
+ }
+}
+
+
+/* This may be called often, so keep it lean */
+asmlinkage void set_precision_flag_up(void)
+{
+ if ( control_word & CW_Precision )
+ partial_status |= (SW_Precision | SW_C1); /* The masked response */
+ else
+ exception(EX_Precision | SW_C1);
+
+}
+
+
+/* This may be called often, so keep it lean */
+asmlinkage void set_precision_flag_down(void)
+{
+ if ( control_word & CW_Precision )
+ { /* The masked response */
+ partial_status &= ~SW_C1;
+ partial_status |= SW_Precision;
+ }
+ else
+ exception(EX_Precision);
+}
+
+
+asmlinkage int denormal_operand(void)
+{
+ if ( control_word & CW_Denormal )
+ { /* The masked response */
+ partial_status |= SW_Denorm_Op;
+ return 0;
+ }
+ else
+ {
+ exception(EX_Denormal);
+ return 1;
+ }
+}
+
+
+asmlinkage int arith_overflow(FPU_REG *dest)
+{
+
+ if ( control_word & CW_Overflow )
+ {
+ char sign;
+ /* The masked response */
+/* ###### The response here depends upon the rounding mode */
+ sign = dest->sign;
+ reg_move(&CONST_INF, dest);
+ dest->sign = sign;
+ }
+ else
+ {
+ /* Subtract the magic number from the exponent */
+ dest->exp -= (3 * (1 << 13));
+ }
+
+ EXCEPTION(EX_Overflow);
+ if ( control_word & CW_Overflow )
+ {
+ /* The overflow exception is masked. */
+ /* By definition, precision is lost.
+ The roundup bit (C1) is also set because we have
+ "rounded" upwards to Infinity. */
+ EXCEPTION(EX_Precision | SW_C1);
+ return !(control_word & CW_Precision);
+ }
+
+ return !(control_word & CW_Overflow);
+
+}
+
+
+asmlinkage int arith_underflow(FPU_REG *dest)
+{
+
+ if ( control_word & CW_Underflow )
+ {
+ /* The masked response */
+ if ( dest->exp <= EXP_UNDER - 63 )
+ {
+ reg_move(&CONST_Z, dest);
+ partial_status &= ~SW_C1; /* Round down. */
+ }
+ }
+ else
+ {
+ /* Add the magic number to the exponent. */
+ dest->exp += (3 * (1 << 13));
+ }
+
+ EXCEPTION(EX_Underflow);
+ if ( control_word & CW_Underflow )
+ {
+ /* The underflow exception is masked. */
+ EXCEPTION(EX_Precision);
+ return !(control_word & CW_Precision);
+ }
+
+ return !(control_word & CW_Underflow);
+
+}
+
+
+void stack_overflow(void)
+{
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ top--;
+ reg_move(&CONST_QNaN, &st(0));
+ }
+
+ EXCEPTION(EX_StackOver);
+
+ return;
+
+}
+
+
+void stack_underflow(void)
+{
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &st(0));
+ }
+
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void stack_underflow_i(int i)
+{
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ }
+
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
+
+void stack_underflow_pop(int i)
+{
+
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, &(st(i)));
+ pop();
+ }
+
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
diff --git a/arch/i386/math-emu/exception.h b/arch/i386/math-emu/exception.h
new file mode 100644
index 000000000..2e629a30c
--- /dev/null
+++ b/arch/i386/math-emu/exception.h
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------------+
+ | exception.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _EXCEPTION_H_
+#define _EXCEPTION_H_
+
+
+#ifdef __ASSEMBLER__
+#define Const_(x) $##x
+#else
+#define Const_(x) x
+#endif
+
+#ifndef SW_C1
+#include "fpu_emu.h"
+#endif SW_C1
+
+#define FPU_BUSY Const_(0x8000) /* FPU busy bit (8087 compatibility) */
+#define EX_ErrorSummary Const_(0x0080) /* Error summary status */
+/* Special exceptions: */
+#define EX_INTERNAL Const_(0x8000) /* Internal error in wm-FPU-emu */
+#define EX_StackOver Const_(0x0041|SW_C1) /* stack overflow */
+#define EX_StackUnder Const_(0x0041) /* stack underflow */
+/* Exception flags: */
+#define EX_Precision Const_(0x0020) /* loss of precision */
+#define EX_Underflow Const_(0x0010) /* underflow */
+#define EX_Overflow Const_(0x0008) /* overflow */
+#define EX_ZeroDiv Const_(0x0004) /* divide by zero */
+#define EX_Denormal Const_(0x0002) /* denormalized operand */
+#define EX_Invalid Const_(0x0001) /* invalid operation */
+
+
+#define PRECISION_LOST_UP Const_((EX_Precision | SW_C1))
+#define PRECISION_LOST_DOWN Const_(EX_Precision)
+
+
+#ifndef __ASSEMBLER__
+
+#ifdef DEBUG
+#define EXCEPTION(x) { printk("exception in %s at line %d\n", \
+ __FILE__, __LINE__); exception(x); }
+#else
+#define EXCEPTION(x) exception(x)
+#endif
+
+#endif __ASSEMBLER__
+
+#endif _EXCEPTION_H_
diff --git a/arch/i386/math-emu/fpu_arith.c b/arch/i386/math-emu/fpu_arith.c
new file mode 100644
index 000000000..96e6bd89b
--- /dev/null
+++ b/arch/i386/math-emu/fpu_arith.c
@@ -0,0 +1,179 @@
+/*---------------------------------------------------------------------------+
+ | fpu_arith.c |
+ | |
+ | Code to implement the FPU register/register arithmetic instructions |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+void fadd__()
+{
+ /* fadd st,st(i) */
+ clear_C1();
+ reg_add(&st(0), &st(FPU_rm), &st(0), control_word);
+}
+
+
+void fmul__()
+{
+ /* fmul st,st(i) */
+ clear_C1();
+ reg_mul(&st(0), &st(FPU_rm), &st(0), control_word);
+}
+
+
+
+void fsub__()
+{
+ /* fsub st,st(i) */
+ clear_C1();
+ reg_sub(&st(0), &st(FPU_rm), &st(0), control_word);
+}
+
+
+void fsubr_()
+{
+ /* fsubr st,st(i) */
+ clear_C1();
+ reg_sub(&st(FPU_rm), &st(0), &st(0), control_word);
+}
+
+
+void fdiv__()
+{
+ /* fdiv st,st(i) */
+ clear_C1();
+ reg_div(&st(0), &st(FPU_rm), &st(0), control_word);
+}
+
+
+void fdivr_()
+{
+ /* fdivr st,st(i) */
+ clear_C1();
+ reg_div(&st(FPU_rm), &st(0), &st(0), control_word);
+}
+
+
+
+void fadd_i()
+{
+ /* fadd st(i),st */
+ clear_C1();
+ reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void fmul_i()
+{
+ /* fmul st(i),st */
+ clear_C1();
+ reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void fsubri()
+{
+ /* fsubr st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */
+ clear_C1();
+ reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void fsub_i()
+{
+ /* fsub st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */
+ clear_C1();
+ reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word);
+}
+
+
+void fdivri()
+{
+ /* fdivr st(i),st */
+ clear_C1();
+ reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word);
+}
+
+
+void fdiv_i()
+{
+ /* fdiv st(i),st */
+ clear_C1();
+ reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word);
+}
+
+
+
+void faddp_()
+{
+ /* faddp st(i),st */
+ clear_C1();
+ if ( !reg_add(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
+}
+
+
+void fmulp_()
+{
+ /* fmulp st(i),st */
+ clear_C1();
+ if ( !reg_mul(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
+}
+
+
+
+void fsubrp()
+{
+ /* fsubrp st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word); */
+ clear_C1();
+ if ( !reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
+}
+
+
+void fsubp_()
+{
+ /* fsubp st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(&st(0), &st(FPU_rm), &st(FPU_rm), control_word); */
+ clear_C1();
+ if ( !reg_sub(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) )
+ pop();
+}
+
+
+void fdivrp()
+{
+ /* fdivrp st(i),st */
+ clear_C1();
+ if ( !reg_div(&st(0), &st(FPU_rm), &st(FPU_rm), control_word) )
+ pop();
+}
+
+
+void fdivp_()
+{
+ /* fdivp st(i),st */
+ clear_C1();
+ if ( !reg_div(&st(FPU_rm), &st(0), &st(FPU_rm), control_word) )
+ pop();
+}
+
diff --git a/arch/i386/math-emu/fpu_asm.h b/arch/i386/math-emu/fpu_asm.h
new file mode 100644
index 000000000..8eb60148d
--- /dev/null
+++ b/arch/i386/math-emu/fpu_asm.h
@@ -0,0 +1,30 @@
+/*---------------------------------------------------------------------------+
+ | fpu_asm.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _FPU_ASM_H_
+#define _FPU_ASM_H_
+
+#include "fpu_emu.h"
+
+#define EXCEPTION _exception
+
+
+#define PARAM1 8(%ebp)
+#define PARAM2 12(%ebp)
+#define PARAM3 16(%ebp)
+#define PARAM4 20(%ebp)
+
+#define SIGL_OFFSET 8
+#define SIGN(x) (x)
+#define TAG(x) 1(x)
+#define EXP(x) 4(x)
+#define SIG(x) SIGL_OFFSET##(x)
+#define SIGL(x) SIGL_OFFSET##(x)
+#define SIGH(x) 12(x)
+
+#endif _FPU_ASM_H_
diff --git a/arch/i386/math-emu/fpu_aux.c b/arch/i386/math-emu/fpu_aux.c
new file mode 100644
index 000000000..0d35fe19b
--- /dev/null
+++ b/arch/i386/math-emu/fpu_aux.c
@@ -0,0 +1,184 @@
+/*---------------------------------------------------------------------------+
+ | fpu_aux.c |
+ | |
+ | Code to implement some of the FPU auxiliary instructions. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "control_w.h"
+
+
+static void fnop(void)
+{
+}
+
+void fclex(void)
+{
+ partial_status &= ~(SW_Backward|SW_Summary|SW_Stack_Fault|SW_Precision|
+ SW_Underflow|SW_Overflow|SW_Zero_Div|SW_Denorm_Op|
+ SW_Invalid);
+ no_ip_update = 1;
+}
+
+/* Needs to be externally visible */
+void finit()
+{
+ int r;
+ control_word = 0x037f;
+ partial_status = 0;
+ top = 0; /* We don't keep top in the status word internally. */
+ for (r = 0; r < 8; r++)
+ {
+ regs[r].tag = TW_Empty;
+ }
+ /* The behaviour is different to that detailed in
+ Section 15.1.6 of the Intel manual */
+ operand_address.offset = 0;
+ operand_address.selector = 0;
+ instruction_address.offset = 0;
+ instruction_address.selector = 0;
+ instruction_address.opcode = 0;
+ no_ip_update = 1;
+}
+
+/*
+ * These are nops on the i387..
+ */
+#define feni fnop
+#define fdisi fnop
+#define fsetpm fnop
+
+static FUNC const finit_table[] = {
+ feni, fdisi, fclex, finit,
+ fsetpm, FPU_illegal, FPU_illegal, FPU_illegal
+};
+
+void finit_()
+{
+ (finit_table[FPU_rm])();
+}
+
+
+static void fstsw_ax(void)
+{
+ *(short *) &FPU_EAX = status_word();
+ no_ip_update = 1;
+}
+
+static FUNC const fstsw_table[] = {
+ fstsw_ax, FPU_illegal, FPU_illegal, FPU_illegal,
+ FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
+};
+
+void fstsw_()
+{
+ (fstsw_table[FPU_rm])();
+}
+
+
+static FUNC const fp_nop_table[] = {
+ fnop, FPU_illegal, FPU_illegal, FPU_illegal,
+ FPU_illegal, FPU_illegal, FPU_illegal, FPU_illegal
+};
+
+void fp_nop()
+{
+ (fp_nop_table[FPU_rm])();
+}
+
+
+void fld_i_()
+{
+ FPU_REG *st_new_ptr;
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+
+ /* fld st(i) */
+ if ( NOT_EMPTY(FPU_rm) )
+ { reg_move(&st(FPU_rm), st_new_ptr); push(); }
+ else
+ {
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ stack_underflow();
+ }
+ else
+ EXCEPTION(EX_StackUnder);
+ }
+
+}
+
+
+void fxch_i()
+{
+ /* fxch st(i) */
+ FPU_REG t;
+ register FPU_REG *sti_ptr = &st(FPU_rm), *st0_ptr = &st(0);
+
+ if ( st0_ptr->tag == TW_Empty )
+ {
+ if ( sti_ptr->tag == TW_Empty )
+ {
+ stack_underflow();
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ if ( control_word & CW_Invalid )
+ reg_move(sti_ptr, st0_ptr); /* Masked response */
+ stack_underflow_i(FPU_rm);
+ return;
+ }
+ if ( sti_ptr->tag == TW_Empty )
+ {
+ if ( control_word & CW_Invalid )
+ reg_move(st0_ptr, sti_ptr); /* Masked response */
+ stack_underflow();
+ return;
+ }
+ clear_C1();
+ reg_move(st0_ptr, &t);
+ reg_move(sti_ptr, st0_ptr);
+ reg_move(&t, sti_ptr);
+}
+
+
+void ffree_()
+{
+ /* ffree st(i) */
+ st(FPU_rm).tag = TW_Empty;
+}
+
+
+void ffreep()
+{
+ /* ffree st(i) + pop - unofficial code */
+ st(FPU_rm).tag = TW_Empty;
+ pop();
+}
+
+
+void fst_i_()
+{
+ /* fst st(i) */
+ reg_move(&st(0), &st(FPU_rm));
+}
+
+
+void fstp_i()
+{
+ /* fstp st(i) */
+ reg_move(&st(0), &st(FPU_rm));
+ pop();
+}
+
diff --git a/arch/i386/math-emu/fpu_emu.h b/arch/i386/math-emu/fpu_emu.h
new file mode 100644
index 000000000..9d2c5dd13
--- /dev/null
+++ b/arch/i386/math-emu/fpu_emu.h
@@ -0,0 +1,171 @@
+/*---------------------------------------------------------------------------+
+ | fpu_emu.h |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#ifndef _FPU_EMU_H_
+#define _FPU_EMU_H_
+
+/*
+ * Define DENORM_OPERAND to make the emulator detect denormals
+ * and use the denormal flag of the status word. Note: this only
+ * affects the flag and corresponding interrupt, the emulator
+ * will always generate denormals and operate upon them as required.
+ */
+#define DENORM_OPERAND
+
+/*
+ * Define PECULIAR_486 to get a closer approximation to 80486 behaviour,
+ * rather than behaviour which appears to be cleaner.
+ * This is a matter of opinion: for all I know, the 80486 may simply
+ * be complying with the IEEE spec. Maybe one day I'll get to see the
+ * spec...
+ */
+#define PECULIAR_486
+
+#ifdef __ASSEMBLER__
+#include "fpu_asm.h"
+#define Const(x) $##x
+#else
+#define Const(x) x
+#endif
+
+#define EXP_BIAS Const(0)
+#define EXP_OVER Const(0x4000) /* smallest invalid large exponent */
+#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
+#define EXP_Infinity EXP_OVER
+#define EXP_NaN EXP_OVER
+
+#define SIGN_POS Const(0)
+#define SIGN_NEG Const(1)
+
+/* Keep the order TW_Valid, TW_Zero, TW_Denormal */
+#define TW_Valid Const(0) /* valid */
+#define TW_Zero Const(1) /* zero */
+/* The following fold to 2 (Special) in the Tag Word */
+/* #define TW_Denormal Const(4) */ /* De-normal */
+#define TW_Infinity Const(5) /* + or - infinity */
+#define TW_NaN Const(6) /* Not a Number */
+
+#define TW_Empty Const(7) /* empty */
+
+
+#ifndef __ASSEMBLER__
+
+#include <linux/math_emu.h>
+#include <linux/linkage.h>
+
+/*
+#define RE_ENTRANT_CHECKING
+ */
+
+#ifdef RE_ENTRANT_CHECKING
+extern char emulating;
+# define RE_ENTRANT_CHECK_OFF emulating = 0
+# define RE_ENTRANT_CHECK_ON emulating = 1
+#else
+# define RE_ENTRANT_CHECK_OFF
+# define RE_ENTRANT_CHECK_ON
+#endif RE_ENTRANT_CHECKING
+
+#define FWAIT_OPCODE 0x9b
+#define OP_SIZE_PREFIX 0x66
+#define ADDR_SIZE_PREFIX 0x67
+#define PREFIX_CS 0x2e
+#define PREFIX_DS 0x3e
+#define PREFIX_ES 0x26
+#define PREFIX_SS 0x36
+#define PREFIX_FS 0x64
+#define PREFIX_GS 0x65
+#define PREFIX_REPE 0xf3
+#define PREFIX_REPNE 0xf2
+#define PREFIX_LOCK 0xf0
+#define PREFIX_CS_ 1
+#define PREFIX_DS_ 2
+#define PREFIX_ES_ 3
+#define PREFIX_FS_ 4
+#define PREFIX_GS_ 5
+#define PREFIX_SS_ 6
+#define PREFIX_DEFAULT 7
+
+struct address {
+ unsigned int offset;
+ unsigned int selector:16;
+ unsigned int opcode:11;
+ unsigned int empty:5;
+};
+typedef void (*FUNC)(void);
+typedef struct fpu_reg FPU_REG;
+typedef void (*FUNC_ST0)(FPU_REG *st0_ptr);
+typedef struct { unsigned char address_size, operand_size, segment; }
+ overrides;
+/* This structure is 32 bits: */
+typedef struct { overrides override;
+ unsigned char default_mode; } fpu_addr_modes;
+/* PROTECTED has a restricted meaning in the emulator; it is used
+ to signal that the emulator needs to do special things to ensure
+ that protection is respected in a segmented model. */
+#define PROTECTED 4
+#define SIXTEEN 1 /* We rely upon this being 1 (true) */
+#define VM86 SIXTEEN
+#define PM16 (SIXTEEN | PROTECTED)
+#define SEG32 PROTECTED
+extern unsigned char const data_sizes_16[32];
+
+#define st(x) ( regs[((top+x) &7 )] )
+
+#define STACK_OVERFLOW (st_new_ptr = &st(-1), st_new_ptr->tag != TW_Empty)
+#define NOT_EMPTY(i) (st(i).tag != TW_Empty)
+#define NOT_EMPTY_ST0 (st0_tag ^ TW_Empty)
+
+#define pop() { regs[(top++ & 7 )].tag = TW_Empty; }
+#define poppop() { regs[((top + 1) & 7 )].tag \
+ = regs[(top & 7 )].tag = TW_Empty; \
+ top += 2; }
+
+/* push() does not affect the tags */
+#define push() { top--; }
+
+
+#define reg_move(x, y) { \
+ *(short *)&((y)->sign) = *(short *)&((x)->sign); \
+ *(long *)&((y)->exp) = *(long *)&((x)->exp); \
+ *(long long *)&((y)->sigl) = *(long long *)&((x)->sigl); }
+
+#define significand(x) ( ((unsigned long long *)&((x)->sigl))[0] )
+
+
+/*----- Prototypes for functions written in assembler -----*/
+/* extern void reg_move(FPU_REG *a, FPU_REG *b); */
+
+asmlinkage void normalize(FPU_REG *x);
+asmlinkage void normalize_nuo(FPU_REG *x);
+asmlinkage int reg_div(FPU_REG const *arg1, FPU_REG const *arg2,
+ FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_sub(FPU_REG const *arg1, FPU_REG const *arg2,
+ FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_mul(FPU_REG const *arg1, FPU_REG const *arg2,
+ FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_div(FPU_REG const *arg1, FPU_REG const *arg2,
+ FPU_REG *answ, unsigned int control_w);
+asmlinkage int reg_u_add(FPU_REG const *arg1, FPU_REG const *arg2,
+ FPU_REG *answ, unsigned int control_w);
+asmlinkage int wm_sqrt(FPU_REG *n, unsigned int control_w);
+asmlinkage unsigned shrx(void *l, unsigned x);
+asmlinkage unsigned shrxs(void *v, unsigned x);
+asmlinkage unsigned long div_small(unsigned long long *x, unsigned long y);
+asmlinkage void round_reg(FPU_REG *arg, unsigned int extent,
+ unsigned int control_w);
+
+#ifndef MAKING_PROTO
+#include "fpu_proto.h"
+#endif
+
+#endif __ASSEMBLER__
+
+#endif _FPU_EMU_H_
diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c
new file mode 100644
index 000000000..b2777a722
--- /dev/null
+++ b/arch/i386/math-emu/fpu_entry.c
@@ -0,0 +1,690 @@
+/*---------------------------------------------------------------------------+
+ | fpu_entry.c |
+ | |
+ | The entry function for wm-FPU-emu |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | See the files "README" and "COPYING" for further copyright and warranty |
+ | information. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | math_emulate() is the sole entry point for wm-FPU-emu |
+ +---------------------------------------------------------------------------*/
+
+#include <linux/signal.h>
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+#include "exception.h"
+#include "control_w.h"
+#include "status_w.h"
+
+#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
+
+#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */
+
+/* WARNING: These codes are not documented by Intel in their 80486 manual
+ and may not work on FPU clones or later Intel FPUs. */
+
+/* Changes to support the un-doc codes provided by Linus Torvalds. */
+
+#define _d9_d8_ fstp_i /* unofficial code (19) */
+#define _dc_d0_ fcom_st /* unofficial code (14) */
+#define _dc_d8_ fcompst /* unofficial code (1c) */
+#define _dd_c8_ fxch_i /* unofficial code (0d) */
+#define _de_d0_ fcompst /* unofficial code (16) */
+#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
+#define _df_c8_ fxch_i /* unofficial code (0f) */
+#define _df_d0_ fstp_i /* unofficial code (17) */
+#define _df_d8_ fstp_i /* unofficial code (1f) */
+
+static FUNC const st_instr_table[64] = {
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
+ fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
+ fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
+ fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+};
+
+#else /* Support only documented FPU op-codes */
+
+static FUNC const st_instr_table[64] = {
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
+ fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
+ fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
+ fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+};
+
+#endif NO_UNDOC_CODE
+
+
+#define _NONE_ 0 /* Take no special action */
+#define _REG0_ 1 /* Need to check for not empty st(0) */
+#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
+#define _REGi_ 0 /* Uses st(rm) */
+#define _PUSH_ 3 /* Need to check for space to push onto stack */
+#define _null_ 4 /* Function illegal or not implemented */
+#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
+#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
+#define _REGIc 0 /* Compare st(0) and st(rm) */
+#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
+
+#ifndef NO_UNDOC_CODE
+
+/* Un-documented FPU op-codes supported by default. (see above) */
+
+static unsigned char const type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
+ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+};
+
+#else /* Support only documented FPU op-codes */
+
+static unsigned char const type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
+ _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
+ _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
+ _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
+};
+
+#endif NO_UNDOC_CODE
+
+
+#ifdef RE_ENTRANT_CHECKING
+char emulating=0;
+#endif RE_ENTRANT_CHECKING
+
+static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
+ overrides *override);
+
+asmlinkage void math_emulate(long arg)
+{
+ unsigned char FPU_modrm, byte1;
+ unsigned short code;
+ fpu_addr_modes addr_modes;
+ int unmasked;
+ FPU_REG loaded_data;
+ void *data_address;
+ struct address data_sel_off;
+ struct address entry_sel_off;
+ unsigned long code_base = 0;
+ unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
+ char st0_tag;
+ FPU_REG *st0_ptr;
+ struct desc_struct code_descriptor;
+
+#ifdef RE_ENTRANT_CHECKING
+ if ( emulating )
+ {
+ printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
+ }
+ RE_ENTRANT_CHECK_ON;
+#endif RE_ENTRANT_CHECKING
+
+ if (!current->used_math)
+ {
+ int i;
+ for ( i = 0; i < 8; i++ )
+ {
+ /* Make sure that the registers are compatible
+ with the assumptions of the emulator. */
+ regs[i].exp = 0;
+ regs[i].sigh = 0x80000000;
+ }
+ finit();
+ current->used_math = 1;
+ }
+
+ SETUP_DATA_AREA(arg);
+
+ FPU_ORIG_EIP = FPU_EIP;
+
+ if ( (FPU_EFLAGS & 0x00020000) != 0 )
+ {
+ /* Virtual 8086 mode */
+ addr_modes.default_mode = VM86;
+ FPU_EIP += code_base = FPU_CS << 4;
+ code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */
+ }
+ else if ( FPU_CS == USER_CS && FPU_DS == USER_DS )
+ {
+ addr_modes.default_mode = 0;
+ }
+ else if ( FPU_CS == KERNEL_CS )
+ {
+ printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP);
+ panic("Math emulation needed in kernel");
+ }
+ else
+ {
+
+ if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */
+ {
+ /* Can only handle segmented addressing via the LDT
+ for now, and it must be 16 bit */
+ printk("FPU emulator: Unsupported addressing mode\n");
+ math_abort(FPU_info, SIGILL);
+ }
+
+ if ( SEG_D_SIZE(code_descriptor = LDT_DESCRIPTOR(FPU_CS)) )
+ {
+ /* The above test may be wrong, the book is not clear */
+ /* Segmented 32 bit protected mode */
+ addr_modes.default_mode = SEG32;
+ }
+ else
+ {
+ /* 16 bit protected mode */
+ addr_modes.default_mode = PM16;
+ }
+ FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor);
+ code_limit = code_base
+ + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor)
+ - 1;
+ if ( code_limit < code_base ) code_limit = 0xffffffff;
+ }
+
+ FPU_lookahead = 1;
+ if (current->flags & PF_PTRACED)
+ FPU_lookahead = 0;
+
+ if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
+ &addr_modes.override) )
+ {
+ RE_ENTRANT_CHECK_OFF;
+ printk("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
+ "FPU emulator: self-modifying code! (emulation impossible)\n",
+ byte1);
+ RE_ENTRANT_CHECK_ON;
+ EXCEPTION(EX_INTERNAL|0x126);
+ math_abort(FPU_info,SIGILL);
+ }
+
+do_another_FPU_instruction:
+
+ no_ip_update = 0;
+
+ FPU_EIP++; /* We have fetched the prefix and first code bytes. */
+
+ if ( addr_modes.default_mode )
+ {
+ /* This checks for the minimum instruction bytes.
+ We also need to check any extra (address mode) code access. */
+ if ( FPU_EIP > code_limit )
+ math_abort(FPU_info,SIGSEGV);
+ }
+
+ if ( (byte1 & 0xf8) != 0xd8 )
+ {
+ if ( byte1 == FWAIT_OPCODE )
+ {
+ if (partial_status & SW_Summary)
+ goto do_the_FPU_interrupt;
+ else
+ goto FPU_fwait_done;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x128);
+ math_abort(FPU_info,SIGILL);
+#endif PARANOID
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ FPU_modrm = get_fs_byte((unsigned char *) FPU_EIP);
+ RE_ENTRANT_CHECK_ON;
+ FPU_EIP++;
+
+ if (partial_status & SW_Summary)
+ {
+ /* Ignore the error for now if the current instruction is a no-wait
+ control instruction */
+ /* The 80486 manual contradicts itself on this topic,
+ but a real 80486 uses the following instructions:
+ fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
+ */
+ code = (FPU_modrm << 8) | byte1;
+ if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
+ (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
+ fnstsw */
+ ((code & 0xc000) != 0xc000))) ) )
+ {
+ /*
+ * We need to simulate the action of the kernel to FPU
+ * interrupts here.
+ * Currently, the "real FPU" part of the kernel (0.99.10)
+ * clears the exception flags, sets the registers to empty,
+ * and passes information back to the interrupted process
+ * via the cs selector and operand selector, so we do the same.
+ */
+ do_the_FPU_interrupt:
+ instruction_address.selector = status_word();
+ operand_address.selector = tag_word();
+ partial_status = 0;
+ top = 0;
+ {
+ int r;
+ for (r = 0; r < 8; r++)
+ {
+ regs[r].tag = TW_Empty;
+ }
+ }
+
+ FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
+
+ RE_ENTRANT_CHECK_OFF;
+ current->tss.trap_no = 16;
+ current->tss.error_code = 0;
+ send_sig(SIGFPE, current, 1);
+ return;
+ }
+ }
+
+ entry_sel_off.offset = FPU_ORIG_EIP;
+ entry_sel_off.selector = FPU_CS;
+ entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
+
+ FPU_rm = FPU_modrm & 7;
+
+ if ( FPU_modrm < 0300 )
+ {
+ /* All of these instructions use the mod/rm byte to get a data address */
+
+ if ( (addr_modes.default_mode & SIXTEEN)
+ ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) )
+ data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off,
+ addr_modes);
+ else
+ data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
+ addr_modes);
+
+ if ( addr_modes.default_mode )
+ {
+ if ( FPU_EIP-1 > code_limit )
+ math_abort(FPU_info,SIGSEGV);
+ }
+
+ if ( !(byte1 & 1) )
+ {
+ unsigned short status1 = partial_status;
+
+ st0_ptr = &st(0);
+ st0_tag = st0_ptr->tag;
+
+ /* Stack underflow has priority */
+ if ( NOT_EMPTY_ST0 )
+ {
+ if ( addr_modes.default_mode & PROTECTED )
+ {
+ /* This table works for 16 and 32 bit protected mode */
+ if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] )
+ math_abort(FPU_info,SIGSEGV);
+ }
+
+ unmasked = 0; /* Do this here to stop compiler warnings. */
+ switch ( (byte1 >> 1) & 3 )
+ {
+ case 0:
+ unmasked = reg_load_single((float *)data_address,
+ &loaded_data);
+ break;
+ case 1:
+ reg_load_int32((long *)data_address, &loaded_data);
+ break;
+ case 2:
+ unmasked = reg_load_double((double *)data_address,
+ &loaded_data);
+ break;
+ case 3:
+ reg_load_int16((short *)data_address, &loaded_data);
+ break;
+ }
+
+ /* No more access to user memory, it is safe
+ to use static data now */
+
+ /* NaN operands have the next priority. */
+ /* We have to delay looking at st(0) until after
+ loading the data, because that data might contain an SNaN */
+ if ( (st0_tag == TW_NaN) ||
+ (loaded_data.tag == TW_NaN) )
+ {
+ /* Restore the status word; we might have loaded a
+ denormal. */
+ partial_status = status1;
+ if ( (FPU_modrm & 0x30) == 0x10 )
+ {
+ /* fcom or fcomp */
+ EXCEPTION(EX_Invalid);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
+ pop(); /* fcomp, masked, so we pop. */
+ }
+ else
+ {
+#ifdef PECULIAR_486
+ /* This is not really needed, but gives behaviour
+ identical to an 80486 */
+ if ( (FPU_modrm & 0x28) == 0x20 )
+ /* fdiv or fsub */
+ real_2op_NaN(&loaded_data, st0_ptr,
+ st0_ptr);
+ else
+#endif PECULIAR_486
+ /* fadd, fdivr, fmul, or fsubr */
+ real_2op_NaN(st0_ptr, &loaded_data,
+ st0_ptr);
+ }
+ goto reg_mem_instr_done;
+ }
+
+ if ( unmasked && !((FPU_modrm & 0x30) == 0x10) )
+ {
+ /* Is not a comparison instruction. */
+ if ( (FPU_modrm & 0x38) == 0x38 )
+ {
+ /* fdivr */
+ if ( (st0_tag == TW_Zero) &&
+ (loaded_data.tag == TW_Valid) )
+ {
+ if ( divide_by_zero(loaded_data.sign,
+ st0_ptr) )
+ {
+ /* We use the fact here that the unmasked
+ exception in the loaded data was for a
+ denormal operand */
+ /* Restore the state of the denormal op bit */
+ partial_status &= ~SW_Denorm_Op;
+ partial_status |= status1 & SW_Denorm_Op;
+ }
+ }
+ }
+ goto reg_mem_instr_done;
+ }
+
+ switch ( (FPU_modrm >> 3) & 7 )
+ {
+ case 0: /* fadd */
+ clear_C1();
+ reg_add(st0_ptr, &loaded_data, st0_ptr,
+ control_word);
+ break;
+ case 1: /* fmul */
+ clear_C1();
+ reg_mul(st0_ptr, &loaded_data, st0_ptr,
+ control_word);
+ break;
+ case 2: /* fcom */
+ compare_st_data(&loaded_data);
+ break;
+ case 3: /* fcomp */
+ if ( !compare_st_data(&loaded_data) && !unmasked )
+ pop();
+ break;
+ case 4: /* fsub */
+ clear_C1();
+ reg_sub(st0_ptr, &loaded_data, st0_ptr,
+ control_word);
+ break;
+ case 5: /* fsubr */
+ clear_C1();
+ reg_sub(&loaded_data, st0_ptr, st0_ptr,
+ control_word);
+ break;
+ case 6: /* fdiv */
+ clear_C1();
+ reg_div(st0_ptr, &loaded_data, st0_ptr,
+ control_word);
+ break;
+ case 7: /* fdivr */
+ clear_C1();
+ if ( st0_tag == TW_Zero )
+ partial_status = status1; /* Undo any denorm tag,
+ zero-divide has priority. */
+ reg_div(&loaded_data, st0_ptr, st0_ptr,
+ control_word);
+ break;
+ }
+ }
+ else
+ {
+ if ( (FPU_modrm & 0x30) == 0x10 )
+ {
+ /* The instruction is fcom or fcomp */
+ EXCEPTION(EX_StackUnder);
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
+ pop(); /* fcomp */
+ }
+ else
+ stack_underflow();
+ }
+ reg_mem_instr_done:
+ operand_address = data_sel_off;
+ }
+ else
+ {
+ if ( !(no_ip_update =
+ load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
+ addr_modes, data_address)) )
+ {
+ operand_address = data_sel_off;
+ }
+ }
+
+ }
+ else
+ {
+ /* None of these instructions access user memory */
+ unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
+
+#ifdef PECULIAR_486
+ /* This is supposed to be undefined, but a real 80486 seems
+ to do this: */
+ operand_address.offset = 0;
+ operand_address.selector = FPU_DS;
+#endif PECULIAR_486
+
+ st0_ptr = &st(0);
+ st0_tag = st0_ptr->tag;
+ switch ( type_table[(int) instr_index] )
+ {
+ case _NONE_: /* also _REGIc: _REGIn */
+ break;
+ case _REG0_:
+ if ( !NOT_EMPTY_ST0 )
+ {
+ stack_underflow();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIi:
+ if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
+ {
+ stack_underflow_i(FPU_rm);
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGIp:
+ if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
+ {
+ stack_underflow_pop(FPU_rm);
+ goto FPU_instruction_done;
+ }
+ break;
+ case _REGI_:
+ if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
+ {
+ stack_underflow();
+ goto FPU_instruction_done;
+ }
+ break;
+ case _PUSH_: /* Only used by the fld st(i) instruction */
+ break;
+ case _null_:
+ FPU_illegal();
+ goto FPU_instruction_done;
+ default:
+ EXCEPTION(EX_INTERNAL|0x111);
+ goto FPU_instruction_done;
+ }
+ (*st_instr_table[(int) instr_index])();
+
+FPU_instruction_done:
+ ;
+ }
+
+ if ( ! no_ip_update )
+ instruction_address = entry_sel_off;
+
+FPU_fwait_done:
+
+#ifdef DEBUG
+ RE_ENTRANT_CHECK_OFF;
+ emu_printall();
+ RE_ENTRANT_CHECK_ON;
+#endif DEBUG
+
+ if (FPU_lookahead && !need_resched)
+ {
+ FPU_ORIG_EIP = FPU_EIP - code_base;
+ if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
+ &addr_modes.override) )
+ goto do_another_FPU_instruction;
+ }
+
+ if ( addr_modes.default_mode )
+ FPU_EIP -= code_base;
+
+ RE_ENTRANT_CHECK_OFF;
+}
+
+
+/* Support for prefix bytes is not yet complete. To properly handle
+ all prefix bytes, further changes are needed in the emulator code
+ which accesses user address space. Access to separate segments is
+ important for msdos emulation. */
+static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
+ overrides *override)
+{
+ unsigned char byte;
+ unsigned char *ip = *fpu_eip;
+
+ *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ byte = get_fs_byte(ip);
+ RE_ENTRANT_CHECK_ON;
+
+ while ( 1 )
+ {
+ switch ( byte )
+ {
+ case ADDR_SIZE_PREFIX:
+ override->address_size = ADDR_SIZE_PREFIX;
+ goto do_next_byte;
+
+ case OP_SIZE_PREFIX:
+ override->operand_size = OP_SIZE_PREFIX;
+ goto do_next_byte;
+
+ case PREFIX_CS:
+ override->segment = PREFIX_CS_;
+ goto do_next_byte;
+ case PREFIX_ES:
+ override->segment = PREFIX_ES_;
+ goto do_next_byte;
+ case PREFIX_SS:
+ override->segment = PREFIX_SS_;
+ goto do_next_byte;
+ case PREFIX_FS:
+ override->segment = PREFIX_FS_;
+ goto do_next_byte;
+ case PREFIX_GS:
+ override->segment = PREFIX_GS_;
+ goto do_next_byte;
+ case PREFIX_DS:
+ override->segment = PREFIX_DS_;
+ goto do_next_byte;
+
+/* lock is not a valid prefix for FPU instructions,
+ let the cpu handle it to generate a SIGILL. */
+/* case PREFIX_LOCK: */
+
+ /* rep.. prefixes have no meaning for FPU instructions */
+ case PREFIX_REPE:
+ case PREFIX_REPNE:
+
+ do_next_byte:
+ ip++;
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ byte = get_fs_byte(ip);
+ RE_ENTRANT_CHECK_ON;
+ break;
+ case FWAIT_OPCODE:
+ *Byte = byte;
+ return 1;
+ default:
+ if ( (byte & 0xf8) == 0xd8 )
+ {
+ *Byte = byte;
+ *fpu_eip = ip;
+ return 1;
+ }
+ else
+ {
+ /* Not a valid sequence of prefix bytes followed by
+ an FPU instruction. */
+ *Byte = byte; /* Needed for error message. */
+ return 0;
+ }
+ }
+ }
+}
+
+
+void math_abort(struct info * info, unsigned int signal)
+{
+ FPU_EIP = FPU_ORIG_EIP;
+ current->tss.trap_no = 16;
+ current->tss.error_code = 0;
+ send_sig(signal,current,1);
+ RE_ENTRANT_CHECK_OFF;
+ __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4));
+#ifdef PARANOID
+ printk("ERROR: wm-FPU-emu math_abort failed!\n");
+#endif PARANOID
+}
diff --git a/arch/i386/math-emu/fpu_etc.c b/arch/i386/math-emu/fpu_etc.c
new file mode 100644
index 000000000..20e3294ca
--- /dev/null
+++ b/arch/i386/math-emu/fpu_etc.c
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------------------+
+ | fpu_etc.c |
+ | |
+ | Implement a few FPU instructions. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+static void fchs(FPU_REG *st0_ptr)
+{
+ if ( st0_ptr->tag ^ TW_Empty )
+ {
+ st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ clear_C1();
+ }
+ else
+ stack_underflow();
+}
+
+static void fabs(FPU_REG *st0_ptr)
+{
+ if ( st0_ptr->tag ^ TW_Empty )
+ {
+ st0_ptr->sign = SIGN_POS;
+ clear_C1();
+ }
+ else
+ stack_underflow();
+}
+
+
+static void ftst_(FPU_REG *st0_ptr)
+{
+ switch (st0_ptr->tag)
+ {
+ case TW_Zero:
+ setcc(SW_C3);
+ break;
+ case TW_Valid:
+ if (st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ {
+#ifdef PECULIAR_486
+ /* This is weird! */
+ if (st0_ptr->sign == SIGN_POS)
+ setcc(SW_C3);
+#endif PECULIAR_486
+ return;
+ }
+#endif DENORM_OPERAND
+
+ break;
+ case TW_NaN:
+ setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Infinity:
+ if (st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+ break;
+ case TW_Empty:
+ setcc(SW_C0|SW_C2|SW_C3);
+ EXCEPTION(EX_StackUnder);
+ break;
+ default:
+ setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */
+ EXCEPTION(EX_INTERNAL|0x14);
+ break;
+ }
+}
+
+static void fxam(FPU_REG *st0_ptr)
+{
+ int c=0;
+ switch (st0_ptr->tag)
+ {
+ case TW_Empty:
+ c = SW_C3|SW_C0;
+ break;
+ case TW_Zero:
+ c = SW_C3;
+ break;
+ case TW_Valid:
+ /* This will need to be changed if TW_Denormal is ever used. */
+ if ( st0_ptr->exp <= EXP_UNDER )
+ c = SW_C2|SW_C3; /* Denormal */
+ else
+ c = SW_C2;
+ break;
+ case TW_NaN:
+ c = SW_C0;
+ break;
+ case TW_Infinity:
+ c = SW_C2|SW_C0;
+ break;
+ }
+ if (st0_ptr->sign == SIGN_NEG)
+ c |= SW_C1;
+ setcc(c);
+}
+
+
+static FUNC_ST0 const fp_etc_table[] = {
+ fchs, fabs, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal,
+ ftst_, fxam, (FUNC_ST0)FPU_illegal, (FUNC_ST0)FPU_illegal
+};
+
+void fp_etc()
+{
+ (fp_etc_table[FPU_rm])(&st(0));
+}
diff --git a/arch/i386/math-emu/fpu_proto.h b/arch/i386/math-emu/fpu_proto.h
new file mode 100644
index 000000000..b4392fe57
--- /dev/null
+++ b/arch/i386/math-emu/fpu_proto.h
@@ -0,0 +1,137 @@
+/* errors.c */
+extern void Un_impl(void);
+extern void FPU_illegal(void);
+extern void emu_printall(void);
+extern void stack_overflow(void);
+extern void stack_underflow(void);
+extern void stack_underflow_i(int i);
+extern void stack_underflow_pop(int i);
+extern int set_precision_flag(int flags);
+asmlinkage void exception(int n);
+asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest);
+asmlinkage int arith_invalid(FPU_REG *dest);
+asmlinkage int divide_by_zero(int sign, FPU_REG *dest);
+asmlinkage void set_precision_flag_up(void);
+asmlinkage void set_precision_flag_down(void);
+asmlinkage int denormal_operand(void);
+asmlinkage int arith_overflow(FPU_REG *dest);
+asmlinkage int arith_underflow(FPU_REG *dest);
+
+/* fpu_arith.c */
+extern void fadd__(void);
+extern void fmul__(void);
+extern void fsub__(void);
+extern void fsubr_(void);
+extern void fdiv__(void);
+extern void fdivr_(void);
+extern void fadd_i(void);
+extern void fmul_i(void);
+extern void fsubri(void);
+extern void fsub_i(void);
+extern void fdivri(void);
+extern void fdiv_i(void);
+extern void faddp_(void);
+extern void fmulp_(void);
+extern void fsubrp(void);
+extern void fsubp_(void);
+extern void fdivrp(void);
+extern void fdivp_(void);
+
+/* fpu_aux.c */
+extern void fclex(void);
+extern void finit(void);
+extern void finit_(void);
+extern void fstsw_(void);
+extern void fp_nop(void);
+extern void fld_i_(void);
+extern void fxch_i(void);
+extern void ffree_(void);
+extern void ffreep(void);
+extern void fst_i_(void);
+extern void fstp_i(void);
+
+/* fpu_entry.c */
+asmlinkage void math_emulate(long arg);
+extern void math_abort(struct info *info, unsigned int signal);
+
+/* fpu_etc.c */
+extern void fp_etc(void);
+
+/* fpu_trig.c */
+extern void convert_l2reg(long const *arg, FPU_REG *dest);
+extern void trig_a(void);
+extern void trig_b(void);
+
+/* get_address.c */
+extern void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
+ struct address *addr,
+ fpu_addr_modes);
+extern void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
+ struct address *addr,
+ fpu_addr_modes);
+
+/* load_store.c */
+extern int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
+ void *address);
+
+/* poly_2xm1.c */
+extern int poly_2xm1(FPU_REG const *arg, FPU_REG *result);
+
+/* poly_atan.c */
+extern void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result);
+
+/* poly_l2.c */
+extern void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result);
+extern int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result);
+
+/* poly_sin.c */
+extern void poly_sine(FPU_REG const *arg, FPU_REG *result);
+extern void poly_cos(FPU_REG const *arg, FPU_REG *result);
+
+/* poly_tan.c */
+extern void poly_tan(FPU_REG const *arg, FPU_REG *result);
+
+/* reg_add_sub.c */
+extern int reg_add(FPU_REG const *a, FPU_REG const *b,
+ FPU_REG *dest, int control_w);
+extern int reg_sub(FPU_REG const *a, FPU_REG const *b,
+ FPU_REG *dest, int control_w);
+
+/* reg_compare.c */
+extern int compare(FPU_REG const *b);
+extern int compare_st_data(FPU_REG const *b);
+extern void fcom_st(void);
+extern void fcompst(void);
+extern void fcompp(void);
+extern void fucom_(void);
+extern void fucomp(void);
+extern void fucompp(void);
+
+/* reg_constant.c */
+extern void fconst(void);
+
+/* reg_ld_str.c */
+extern int reg_load_extended(long double *addr, FPU_REG *loaded_data);
+extern int reg_load_double(double *dfloat, FPU_REG *loaded_data);
+extern int reg_load_single(float *single, FPU_REG *loaded_data);
+extern void reg_load_int64(long long *_s, FPU_REG *loaded_data);
+extern void reg_load_int32(long *_s, FPU_REG *loaded_data);
+extern void reg_load_int16(short *_s, FPU_REG *loaded_data);
+extern void reg_load_bcd(char *s, FPU_REG *loaded_data);
+extern int reg_store_extended(long double *d, FPU_REG *st0_ptr);
+extern int reg_store_double(double *dfloat, FPU_REG *st0_ptr);
+extern int reg_store_single(float *single, FPU_REG *st0_ptr);
+extern int reg_store_int64(long long *d, FPU_REG *st0_ptr);
+extern int reg_store_int32(long *d, FPU_REG *st0_ptr);
+extern int reg_store_int16(short *d, FPU_REG *st0_ptr);
+extern int reg_store_bcd(char *d, FPU_REG *st0_ptr);
+extern int round_to_int(FPU_REG *r);
+extern char *fldenv(fpu_addr_modes addr_modes, char *address);
+extern void frstor(fpu_addr_modes addr_modes, char *address);
+extern unsigned short tag_word(void);
+extern char *fstenv(fpu_addr_modes addr_modes, char *address);
+extern void fsave(fpu_addr_modes addr_modes, char *address);
+
+/* reg_mul.c */
+extern int reg_mul(FPU_REG const *a, FPU_REG const *b,
+ FPU_REG *dest, unsigned int control_w);
diff --git a/arch/i386/math-emu/fpu_system.h b/arch/i386/math-emu/fpu_system.h
new file mode 100644
index 000000000..d2c3fa716
--- /dev/null
+++ b/arch/i386/math-emu/fpu_system.h
@@ -0,0 +1,83 @@
+/*---------------------------------------------------------------------------+
+ | fpu_system.h |
+ | |
+ | Copyright (C) 1992,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _FPU_SYSTEM_H
+#define _FPU_SYSTEM_H
+
+/* system dependent definitions */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+/* This sets the pointer FPU_info to point to the argument part
+ of the stack frame of math_emulate() */
+#define SETUP_DATA_AREA(arg) FPU_info = (struct info *) &arg
+
+#define LDT_DESCRIPTOR(s) (current->ldt[(s) >> 3])
+#define SEG_D_SIZE(x) ((x).b & (3 << 21))
+#define SEG_G_BIT(x) ((x).b & (1 << 23))
+#define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1)
+#define SEG_286_MODE(x) ((x).b & ( 0xff000000 | 0xf0000 | (1 << 23)))
+#define SEG_BASE_ADDR(s) (((s).b & 0xff000000) \
+ | (((s).b & 0xff) << 16) | ((s).a >> 16))
+#define SEG_LIMIT(s) (((s).b & 0xff0000) | ((s).a & 0xffff))
+#define SEG_EXECUTE_ONLY(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 11))
+#define SEG_WRITE_PERM(s) (((s).b & ((1 << 11) | (1 << 9))) == (1 << 9))
+#define SEG_EXPAND_DOWN(s) (((s).b & ((1 << 11) | (1 << 10))) \
+ == (1 << 10))
+
+#define I387 (current->tss.i387)
+#define FPU_info (I387.soft.info)
+
+#define FPU_CS (*(unsigned short *) &(FPU_info->___cs))
+#define FPU_SS (*(unsigned short *) &(FPU_info->___ss))
+#define FPU_DS (*(unsigned short *) &(FPU_info->___ds))
+#define FPU_EAX (FPU_info->___eax)
+#define FPU_EFLAGS (FPU_info->___eflags)
+#define FPU_EIP (FPU_info->___eip)
+#define FPU_ORIG_EIP (FPU_info->___orig_eip)
+
+#define FPU_lookahead (I387.soft.lookahead)
+
+/* nz if ip_offset and cs_selector are not to be set for the current
+ instruction. */
+#define no_ip_update (((char *)&(I387.soft.twd))[0])
+#define FPU_rm (((unsigned char *)&(I387.soft.twd))[1])
+
+/* Number of bytes of data which can be legally accessed by the current
+ instruction. This only needs to hold a number <= 108, so a byte will do. */
+#define access_limit (((unsigned char *)&(I387.soft.twd))[2])
+
+#define partial_status (I387.soft.swd)
+#define control_word (I387.soft.cwd)
+#define regs (I387.soft.regs)
+#define top (I387.soft.top)
+
+#define instruction_address (*(struct address *)&I387.soft.fip)
+#define operand_address (*(struct address *)&I387.soft.foo)
+
+#define FPU_verify_area(x,y,z) if ( verify_area(x,y,z) ) \
+ math_abort(FPU_info,SIGSEGV)
+
+#undef FPU_IGNORE_CODE_SEGV
+#ifdef FPU_IGNORE_CODE_SEGV
+/* verify_area() is very expensive, and causes the emulator to run
+ about 20% slower if applied to the code. Anyway, errors due to bad
+ code addresses should be much rarer than errors due to bad data
+ addresses. */
+#define FPU_code_verify_area(z)
+#else
+/* A simpler test than verify_area() can probably be done for
+ FPU_code_verify_area() because the only possible error is to step
+ past the upper boundary of a legal code area. */
+#define FPU_code_verify_area(z) FPU_verify_area(VERIFY_READ,(void *)FPU_EIP,z)
+#endif
+
+#endif
diff --git a/arch/i386/math-emu/fpu_trig.c b/arch/i386/math-emu/fpu_trig.c
new file mode 100644
index 000000000..05241f700
--- /dev/null
+++ b/arch/i386/math-emu/fpu_trig.c
@@ -0,0 +1,1718 @@
+/*---------------------------------------------------------------------------+
+ | fpu_trig.c |
+ | |
+ | Implementation of the FPU "transcendental" functions. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "control_w.h"
+#include "reg_constant.h"
+
+
+static void rem_kernel(unsigned long long st0, unsigned long long *y,
+ unsigned long long st1,
+ unsigned long long q, int n);
+
+#define BETTER_THAN_486
+
+#define FCOS 4
+/* Not needed now with new code
+#define FPTAN 1
+ */
+
+/* Used only by fptan, fsin, fcos, and fsincos. */
+/* This routine produces very accurate results, similar to
+ using a value of pi with more than 128 bits precision. */
+/* Limited measurements show no results worse than 64 bit precision
+ except for the results for arguments close to 2^63, where the
+ precision of the result sometimes degrades to about 63.9 bits */
+static int trig_arg(FPU_REG *X, int even)
+{
+ FPU_REG tmp;
+ unsigned long long q;
+ int old_cw = control_word, saved_status = partial_status;
+
+ if ( X->exp >= EXP_BIAS + 63 )
+ {
+ partial_status |= SW_C2; /* Reduction incomplete. */
+ return -1;
+ }
+
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+
+ reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+ round_to_int(&tmp); /* Fortunately, this can't overflow
+ to 2^64 */
+ q = significand(&tmp);
+ if ( q )
+ {
+ rem_kernel(significand(X),
+ &significand(&tmp),
+ significand(&CONST_PI2),
+ q, X->exp - CONST_PI2.exp);
+ tmp.exp = CONST_PI2.exp;
+ normalize(&tmp);
+ reg_move(&tmp, X);
+ }
+
+#ifdef FPTAN
+ if ( even == FPTAN )
+ {
+ if ( ((X->exp >= EXP_BIAS) ||
+ ((X->exp == EXP_BIAS-1)
+ && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
+ even = FCOS;
+ else
+ even = 0;
+ }
+#endif FPTAN
+
+ if ( (even && !(q & 1)) || (!even && (q & 1)) )
+ {
+ reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
+#ifdef BETTER_THAN_486
+ /* So far, the results are exact but based upon a 64 bit
+ precision approximation to pi/2. The technique used
+ now is equivalent to using an approximation to pi/2 which
+ is accurate to about 128 bits. */
+ if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
+ {
+ /* This code gives the effect of having p/2 to better than
+ 128 bits precision. */
+ significand(&tmp) = q + 1;
+ tmp.exp = EXP_BIAS + 63;
+ tmp.tag = TW_Valid;
+ normalize(&tmp);
+ reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
+ reg_add(X, &tmp, X, FULL_PRECISION);
+ if ( X->sign == SIGN_NEG )
+ {
+ /* CONST_PI2extra is negative, so the result of the addition
+ can be negative. This means that the argument is actually
+ in a different quadrant. The correction is always < pi/2,
+ so it can't overflow into yet another quadrant. */
+ X->sign = SIGN_POS;
+ q++;
+ }
+ }
+#endif BETTER_THAN_486
+ }
+#ifdef BETTER_THAN_486
+ else
+ {
+ /* So far, the results are exact but based upon a 64 bit
+ precision approximation to pi/2. The technique used
+ now is equivalent to using an approximation to pi/2 which
+ is accurate to about 128 bits. */
+ if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
+ {
+ /* This code gives the effect of having p/2 to better than
+ 128 bits precision. */
+ significand(&tmp) = q;
+ tmp.exp = EXP_BIAS + 63;
+ tmp.tag = TW_Valid;
+ normalize(&tmp);
+ reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
+ reg_sub(X, &tmp, X, FULL_PRECISION);
+ if ( (X->exp == CONST_PI2.exp) &&
+ ((X->sigh > CONST_PI2.sigh)
+ || ((X->sigh == CONST_PI2.sigh)
+ && (X->sigl > CONST_PI2.sigl))) )
+ {
+ /* CONST_PI2extra is negative, so the result of the
+ subtraction can be larger than pi/2. This means
+ that the argument is actually in a different quadrant.
+ The correction is always < pi/2, so it can't overflow
+ into yet another quadrant. */
+ reg_sub(&CONST_PI, X, X, FULL_PRECISION);
+ q++;
+ }
+ }
+ }
+#endif BETTER_THAN_486
+
+ control_word = old_cw;
+ partial_status = saved_status & ~SW_C2; /* Reduction complete. */
+
+ return (q & 3) | even;
+}
+
+
+/* Convert a long to register */
+void convert_l2reg(long const *arg, FPU_REG *dest)
+{
+ long num = *arg;
+
+ if (num == 0)
+ { reg_move(&CONST_Z, dest); return; }
+
+ if (num > 0)
+ dest->sign = SIGN_POS;
+ else
+ { num = -num; dest->sign = SIGN_NEG; }
+
+ dest->sigh = num;
+ dest->sigl = 0;
+ dest->exp = EXP_BIAS + 31;
+ dest->tag = TW_Valid;
+ normalize(dest);
+}
+
+
+static void single_arg_error(FPU_REG *st0_ptr)
+{
+ switch ( st0_ptr->tag )
+ {
+ case TW_NaN:
+ if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
+ {
+ EXCEPTION(EX_Invalid);
+ if ( control_word & CW_Invalid )
+ st0_ptr->sigh |= 0x40000000; /* Convert to a QNaN */
+ }
+ break; /* return with a NaN in st(0) */
+ case TW_Empty:
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x0112);
+#endif PARANOID
+ }
+}
+
+
+static void single_arg_2_error(FPU_REG *st0_ptr)
+{
+ FPU_REG *st_new_ptr;
+
+ switch ( st0_ptr->tag )
+ {
+ case TW_NaN:
+ if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
+ {
+ EXCEPTION(EX_Invalid);
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ /* Convert to a QNaN */
+ st0_ptr->sigh |= 0x40000000;
+ st_new_ptr = &st(-1);
+ push();
+ reg_move(&st(1), st_new_ptr);
+ }
+ }
+ else
+ {
+ /* A QNaN */
+ st_new_ptr = &st(-1);
+ push();
+ reg_move(&st(1), st_new_ptr);
+ }
+ break; /* return with a NaN in st(0) */
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x0112);
+#endif PARANOID
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static void f2xm1(FPU_REG *st0_ptr)
+{
+ clear_C1();
+ switch ( st0_ptr->tag )
+ {
+ case TW_Valid:
+ {
+ if ( st0_ptr->exp >= 0 )
+ {
+ /* For an 80486 FPU, the result is undefined. */
+ }
+#ifdef DENORM_OPERAND
+ else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ else
+ {
+ /* poly_2xm1(x) requires 0 < x < 1. */
+ poly_2xm1(st0_ptr, st0_ptr);
+ }
+ if ( st0_ptr->exp <= EXP_UNDER )
+ {
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ arith_underflow(st0_ptr);
+ }
+ set_precision_flag_up(); /* 80486 appears to always do this */
+ return;
+ }
+ case TW_Zero:
+ return;
+ case TW_Infinity:
+ if ( st0_ptr->sign == SIGN_NEG )
+ {
+ /* -infinity gives -1 (p16-10) */
+ reg_move(&CONST_1, st0_ptr);
+ st0_ptr->sign = SIGN_NEG;
+ }
+ return;
+ default:
+ single_arg_error(st0_ptr);
+ }
+}
+
+
+static void fptan(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ FPU_REG *st_new_ptr;
+ int q;
+ char arg_sign = st0_ptr->sign;
+
+ /* Stack underflow has higher priority */
+ if ( st0_tag == TW_Empty )
+ {
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ if ( control_word & CW_Invalid )
+ {
+ st_new_ptr = &st(-1);
+ push();
+ stack_underflow(); /* Puts a QNaN in the new st(0) */
+ }
+ return;
+ }
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+
+ switch ( st0_tag )
+ {
+ case TW_Valid:
+ if ( st0_ptr->exp > EXP_BIAS - 40 )
+ {
+ st0_ptr->sign = SIGN_POS;
+ if ( (q = trig_arg(st0_ptr, 0)) != -1 )
+ {
+ poly_tan(st0_ptr, st0_ptr);
+ st0_ptr->sign = (q & 1) ^ arg_sign;
+ }
+ else
+ {
+ /* Operand is out of range */
+ st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ set_precision_flag_up(); /* We do not really know if up or down */
+ }
+ else
+ {
+ /* For a small arg, the result == the argument */
+ /* Underflow may happen */
+
+ if ( st0_ptr->exp <= EXP_UNDER )
+ {
+#ifdef DENORM_OPERAND
+ if ( denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ if ( arith_underflow(st0_ptr) )
+ return;
+ }
+ set_precision_flag_down(); /* Must be down. */
+ }
+ push();
+ reg_move(&CONST_1, st_new_ptr);
+ return;
+ break;
+ case TW_Infinity:
+ /* The 80486 treats infinity as an invalid operand */
+ arith_invalid(st0_ptr);
+ if ( control_word & CW_Invalid )
+ {
+ st_new_ptr = &st(-1);
+ push();
+ arith_invalid(st_new_ptr);
+ }
+ return;
+ case TW_Zero:
+ push();
+ reg_move(&CONST_1, st_new_ptr);
+ setcc(0);
+ break;
+ default:
+ single_arg_2_error(st0_ptr);
+ break;
+ }
+}
+
+
+static void fxtract(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ FPU_REG *st_new_ptr;
+ register FPU_REG *st1_ptr = st0_ptr; /* anticipate */
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+ clear_C1();
+ if ( !(st0_tag ^ TW_Valid) )
+ {
+ long e;
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ push();
+ reg_move(st1_ptr, st_new_ptr);
+ st_new_ptr->exp = EXP_BIAS;
+ e = st1_ptr->exp - EXP_BIAS;
+ convert_l2reg(&e, st1_ptr);
+ return;
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ char sign = st0_ptr->sign;
+ if ( divide_by_zero(SIGN_NEG, st0_ptr) )
+ return;
+ push();
+ reg_move(&CONST_Z, st_new_ptr);
+ st_new_ptr->sign = sign;
+ return;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ char sign = st0_ptr->sign;
+ st0_ptr->sign = SIGN_POS;
+ push();
+ reg_move(&CONST_INF, st_new_ptr);
+ st_new_ptr->sign = sign;
+ return;
+ }
+ else if ( st0_tag == TW_NaN )
+ {
+ if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) )
+ return;
+ push();
+ reg_move(st1_ptr, st_new_ptr);
+ return;
+ }
+ else if ( st0_tag == TW_Empty )
+ {
+ /* Is this the correct behaviour? */
+ if ( control_word & EX_Invalid )
+ {
+ stack_underflow();
+ push();
+ stack_underflow();
+ }
+ else
+ EXCEPTION(EX_StackUnder);
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x119);
+#endif PARANOID
+}
+
+
+static void fdecstp(FPU_REG *st0_ptr)
+{
+ clear_C1();
+ top--; /* st0_ptr will be fixed in math_emulate() before the next instr */
+}
+
+static void fincstp(FPU_REG *st0_ptr)
+{
+ clear_C1();
+ top++; /* st0_ptr will be fixed in math_emulate() before the next instr */
+}
+
+
+static void fsqrt_(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+
+ clear_C1();
+ if ( !(st0_tag ^ TW_Valid) )
+ {
+ int expon;
+
+ if (st0_ptr->sign == SIGN_NEG)
+ {
+ arith_invalid(st0_ptr); /* sqrt(negative) is invalid */
+ return;
+ }
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ expon = st0_ptr->exp - EXP_BIAS;
+ st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */
+
+ wm_sqrt(st0_ptr, control_word); /* Do the computation */
+
+ st0_ptr->exp += expon >> 1;
+ st0_ptr->sign = SIGN_POS;
+ }
+ else if ( st0_tag == TW_Zero )
+ return;
+ else if ( st0_tag == TW_Infinity )
+ {
+ if ( st0_ptr->sign == SIGN_NEG )
+ arith_invalid(st0_ptr); /* sqrt(-Infinity) is invalid */
+ return;
+ }
+ else
+ { single_arg_error(st0_ptr); return; }
+
+}
+
+
+static void frndint_(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ int flags;
+
+ if ( !(st0_tag ^ TW_Valid) )
+ {
+ if (st0_ptr->exp > EXP_BIAS+63)
+ return;
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* Fortunately, this can't overflow to 2^64 */
+ if ( (flags = round_to_int(st0_ptr)) )
+ set_precision_flag(flags);
+
+ st0_ptr->exp = EXP_BIAS + 63;
+ normalize(st0_ptr);
+ return;
+ }
+ else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) )
+ return;
+ else
+ single_arg_error(st0_ptr);
+}
+
+
+static void fsin(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ char arg_sign = st0_ptr->sign;
+
+ if ( st0_tag == TW_Valid )
+ {
+ FPU_REG rv;
+ int q;
+
+ if ( st0_ptr->exp > EXP_BIAS - 40 )
+ {
+ st0_ptr->sign = SIGN_POS;
+ if ( (q = trig_arg(st0_ptr, 0)) != -1 )
+ {
+
+ poly_sine(st0_ptr, &rv);
+
+ if (q & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ rv.sign ^= arg_sign;
+ reg_move(&rv, st0_ptr);
+
+ /* We do not really know if up or down */
+ set_precision_flag_up();
+ return;
+ }
+ else
+ {
+ /* Operand is out of range */
+ st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ }
+ else
+ {
+ /* For a small arg, the result == the argument */
+ /* Underflow may happen */
+
+ if ( st0_ptr->exp <= EXP_UNDER )
+ {
+#ifdef DENORM_OPERAND
+ if ( denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ arith_underflow(st0_ptr);
+ return;
+ }
+
+ set_precision_flag_up(); /* Must be up. */
+ }
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ setcc(0);
+ return;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ /* The 80486 treats infinity as an invalid operand */
+ arith_invalid(st0_ptr);
+ return;
+ }
+ else
+ single_arg_error(st0_ptr);
+}
+
+
+static int f_cos(FPU_REG *arg)
+{
+ char arg_sign = arg->sign;
+
+ if ( arg->tag == TW_Valid )
+ {
+ FPU_REG rv;
+ int q;
+
+ if ( arg->exp > EXP_BIAS - 40 )
+ {
+ arg->sign = SIGN_POS;
+ if ( (arg->exp < EXP_BIAS)
+ || ((arg->exp == EXP_BIAS)
+ && (significand(arg) <= 0xc90fdaa22168c234LL)) )
+ {
+ poly_cos(arg, &rv);
+ reg_move(&rv, arg);
+
+ /* We do not really know if up or down */
+ set_precision_flag_down();
+
+ return 0;
+ }
+ else if ( (q = trig_arg(arg, FCOS)) != -1 )
+ {
+ poly_sine(arg, &rv);
+
+ if ((q+1) & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ reg_move(&rv, arg);
+
+ /* We do not really know if up or down */
+ set_precision_flag_down();
+
+ return 0;
+ }
+ else
+ {
+ /* Operand is out of range */
+ arg->sign = arg_sign; /* restore st(0) */
+ return 1;
+ }
+ }
+ else
+ {
+#ifdef DENORM_OPERAND
+ if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
+ return 1;
+#endif DENORM_OPERAND
+
+ setcc(0);
+ reg_move(&CONST_1, arg);
+#ifdef PECULIAR_486
+ set_precision_flag_down(); /* 80486 appears to do this. */
+#else
+ set_precision_flag_up(); /* Must be up. */
+#endif PECULIAR_486
+ return 0;
+ }
+ }
+ else if ( arg->tag == TW_Zero )
+ {
+ reg_move(&CONST_1, arg);
+ setcc(0);
+ return 0;
+ }
+ else if ( arg->tag == TW_Infinity )
+ {
+ /* The 80486 treats infinity as an invalid operand */
+ arith_invalid(arg);
+ return 1;
+ }
+ else
+ {
+ single_arg_error(arg); /* requires arg == &st(0) */
+ return 1;
+ }
+}
+
+
+static void fcos(FPU_REG *st0_ptr)
+{
+ f_cos(st0_ptr);
+}
+
+
+static void fsincos(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ FPU_REG *st_new_ptr;
+ FPU_REG arg;
+
+ /* Stack underflow has higher priority */
+ if ( st0_tag == TW_Empty )
+ {
+ stack_underflow(); /* Puts a QNaN in st(0) */
+ if ( control_word & CW_Invalid )
+ {
+ st_new_ptr = &st(-1);
+ push();
+ stack_underflow(); /* Puts a QNaN in the new st(0) */
+ }
+ return;
+ }
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+
+ if ( st0_tag == TW_NaN )
+ {
+ single_arg_2_error(st0_ptr);
+ return;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ /* The 80486 treats infinity as an invalid operand */
+ if ( !arith_invalid(st0_ptr) )
+ {
+ /* unmasked response */
+ push();
+ arith_invalid(st_new_ptr);
+ }
+ return;
+ }
+
+ reg_move(st0_ptr,&arg);
+ if ( !f_cos(&arg) )
+ {
+ fsin(st0_ptr);
+ push();
+ reg_move(&arg,st_new_ptr);
+ }
+
+}
+
+
+/*---------------------------------------------------------------------------*/
+/* The following all require two arguments: st(0) and st(1) */
+
+/* A lean, mean kernel for the fprem instructions. This relies upon
+ the division and rounding to an integer in do_fprem giving an
+ exact result. Because of this, rem_kernel() needs to deal only with
+ the least significant 64 bits, the more significant bits of the
+ result must be zero.
+ */
+static void rem_kernel(unsigned long long st0, unsigned long long *y,
+ unsigned long long st1,
+ unsigned long long q, int n)
+{
+ unsigned long long x;
+
+ x = st0 << n;
+
+ /* Do the required multiplication and subtraction in the one operation */
+ asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
+ movl %3,%%eax; mull %4; subl %%eax,%1;
+ movl %2,%%eax; mull %5; subl %%eax,%1;"
+ :"=m" (x), "=m" (((unsigned *)&x)[1])
+ :"m" (st1),"m" (((unsigned *)&st1)[1]),
+ "m" (q),"m" (((unsigned *)&q)[1])
+ :"%ax","%dx");
+
+ *y = x;
+}
+
+
+/* Remainder of st(0) / st(1) */
+/* This routine produces exact results, i.e. there is never any
+ rounding or truncation, etc of the result. */
+static void do_fprem(FPU_REG *st0_ptr, int round)
+{
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+ char st0_tag = st0_ptr->tag;
+ char sign = st0_ptr->sign;
+
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ FPU_REG tmp;
+ int old_cw = control_word;
+ int expdif = st0_ptr->exp - st1_ptr->exp;
+ long long q;
+ unsigned short saved_status;
+ int cc = 0;
+
+#ifdef DENORM_OPERAND
+ if ( ((st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* We want the status following the denorm tests, but don't want
+ the status changed by the arithmetic operations. */
+ saved_status = partial_status;
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+
+ if (expdif < 64)
+ {
+ /* This should be the most common case */
+
+ if ( expdif > -2 )
+ {
+ reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+
+ if ( tmp.exp >= EXP_BIAS )
+ {
+ round_to_int(&tmp); /* Fortunately, this can't overflow
+ to 2^64 */
+ q = significand(&tmp);
+
+ rem_kernel(significand(st0_ptr),
+ &significand(&tmp),
+ significand(st1_ptr),
+ q, expdif);
+
+ tmp.exp = st1_ptr->exp;
+ }
+ else
+ {
+ reg_move(st0_ptr, &tmp);
+ q = 0;
+ }
+ tmp.sign = sign;
+
+ if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
+ {
+ /* We may need to subtract st(1) once more,
+ to get a result <= 1/2 of st(1). */
+ unsigned long long x;
+ expdif = st1_ptr->exp - tmp.exp;
+ if ( expdif <= 1 )
+ {
+ if ( expdif == 0 )
+ x = significand(st1_ptr) - significand(&tmp);
+ else /* expdif is 1 */
+ x = (significand(st1_ptr) << 1) - significand(&tmp);
+ if ( (x < significand(&tmp)) ||
+ /* or equi-distant (from 0 & st(1)) and q is odd */
+ ((x == significand(&tmp)) && (q & 1) ) )
+ {
+ tmp.sign ^= (SIGN_POS^SIGN_NEG);
+ significand(&tmp) = x;
+ q++;
+ }
+ }
+ }
+
+ if (q & 4) cc |= SW_C0;
+ if (q & 2) cc |= SW_C3;
+ if (q & 1) cc |= SW_C1;
+ }
+ else
+ {
+ control_word = old_cw;
+ setcc(0);
+ return;
+ }
+ }
+ else
+ {
+ /* There is a large exponent difference ( >= 64 ) */
+ /* To make much sense, the code in this section should
+ be done at high precision. */
+ int exp_1;
+
+ /* prevent overflow here */
+ /* N is 'a number between 32 and 63' (p26-113) */
+ reg_move(st0_ptr, &tmp);
+ tmp.exp = EXP_BIAS + 56;
+ exp_1 = st1_ptr->exp; st1_ptr->exp = EXP_BIAS;
+ expdif -= 56;
+
+ reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+ st1_ptr->exp = exp_1;
+
+ round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */
+
+ rem_kernel(significand(st0_ptr),
+ &significand(&tmp),
+ significand(st1_ptr),
+ significand(&tmp),
+ tmp.exp - EXP_BIAS
+ );
+ tmp.exp = exp_1 + expdif;
+ tmp.sign = sign;
+
+ /* It is possible for the operation to be complete here.
+ What does the IEEE standard say? The Intel 80486 manual
+ implies that the operation will never be completed at this
+ point, and the behaviour of a real 80486 confirms this.
+ */
+ if ( !(tmp.sigh | tmp.sigl) )
+ {
+ /* The result is zero */
+ control_word = old_cw;
+ partial_status = saved_status;
+ reg_move(&CONST_Z, st0_ptr);
+ st0_ptr->sign = sign;
+#ifdef PECULIAR_486
+ setcc(SW_C2);
+#else
+ setcc(0);
+#endif PECULIAR_486
+ return;
+ }
+ cc = SW_C2;
+ }
+
+ control_word = old_cw;
+ partial_status = saved_status;
+ normalize_nuo(&tmp);
+ reg_move(&tmp, st0_ptr);
+ setcc(cc);
+
+ /* The only condition to be looked for is underflow,
+ and it can occur here only if underflow is unmasked. */
+ if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero)
+ && !(control_word & CW_Underflow) )
+ arith_underflow(st0_ptr);
+
+ return;
+ }
+ else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+ {
+ stack_underflow();
+ return;
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ if ( st1_tag == TW_Valid )
+ {
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ setcc(0); return;
+ }
+ else if ( st1_tag == TW_Zero )
+ { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */
+ else if ( st1_tag == TW_Infinity )
+ { setcc(0); return; }
+ }
+ else if ( st0_tag == TW_Valid )
+ {
+ if ( st1_tag == TW_Zero )
+ {
+ arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */
+ return;
+ }
+ else if ( st1_tag != TW_NaN )
+ {
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ if ( st1_tag == TW_Infinity )
+ {
+ /* fprem(Valid,Infinity) is o.k. */
+ setcc(0); return;
+ }
+ }
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ if ( st1_tag != TW_NaN )
+ {
+ arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */
+ return;
+ }
+ }
+
+ /* One of the registers must contain a NaN is we got here. */
+
+#ifdef PARANOID
+ if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
+ EXCEPTION(EX_INTERNAL | 0x118);
+#endif PARANOID
+
+ real_2op_NaN(st1_ptr, st0_ptr, st0_ptr);
+
+}
+
+
+/* ST(1) <- ST(1) * log ST; pop ST */
+static void fyl2x(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ FPU_REG *st1_ptr = &st(1), exponent;
+ char st1_tag = st1_ptr->tag;
+ int e;
+
+ clear_C1();
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ if ( st0_ptr->sign == SIGN_POS )
+ {
+#ifdef DENORM_OPERAND
+ if ( ((st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) )
+ {
+ /* Special case. The result can be precise. */
+ e = st0_ptr->exp - EXP_BIAS;
+ if ( e > 0 )
+ {
+ exponent.sigh = e;
+ exponent.sign = SIGN_POS;
+ }
+ else
+ {
+ exponent.sigh = -e;
+ exponent.sign = SIGN_NEG;
+ }
+ exponent.sigl = 0;
+ exponent.exp = EXP_BIAS + 31;
+ exponent.tag = TW_Valid;
+ normalize_nuo(&exponent);
+ reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION);
+ }
+ else
+ {
+ /* The usual case */
+ poly_l2(st0_ptr, st1_ptr, st1_ptr);
+ if ( st1_ptr->exp <= EXP_UNDER )
+ {
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ arith_underflow(st1_ptr);
+ }
+ else
+ set_precision_flag_up(); /* 80486 appears to always do this */
+ }
+ pop();
+ return;
+ }
+ else
+ {
+ /* negative */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
+ }
+ else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
+ {
+ stack_underflow_pop(1);
+ return;
+ }
+ else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+ {
+ if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
+ pop();
+ return;
+ }
+ else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
+ {
+ /* one of the args is zero, the other valid, or both zero */
+ if ( st0_tag == TW_Zero )
+ {
+ if ( st1_tag == TW_Zero )
+ {
+ /* Both args zero is invalid */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ }
+#ifdef PECULIAR_486
+ /* This case is not specifically covered in the manual,
+ but divide-by-zero would seem to be the best response.
+ However, a real 80486 does it this way... */
+ else if ( st0_ptr->tag == TW_Infinity )
+ {
+ reg_move(&CONST_INF, st1_ptr);
+ pop();
+ }
+#endif PECULIAR_486
+ else
+ {
+ if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
+ pop();
+ }
+ return;
+ }
+ else
+ {
+ /* st(1) contains zero, st(0) valid <> 0 */
+ /* Zero is the valid answer */
+ char sign = st1_ptr->sign;
+
+ if ( st0_ptr->sign == SIGN_NEG )
+ {
+ /* log(negative) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
+ pop(); st0_ptr = &st(0);
+ reg_move(&CONST_Z, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ }
+ /* One or both arg must be an infinity */
+ else if ( st0_tag == TW_Infinity )
+ {
+ if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
+ {
+ /* log(-infinity) or 0*log(infinity) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
+ else
+ {
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ pop(); st0_ptr = &st(0);
+ reg_move(&CONST_INF, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ }
+ /* st(1) must be infinity here */
+ else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) )
+ {
+ if ( st0_ptr->exp >= EXP_BIAS )
+ {
+ if ( (st0_ptr->exp == EXP_BIAS) &&
+ (st0_ptr->sigh == 0x80000000) &&
+ (st0_ptr->sigl == 0) )
+ {
+ /* st(0) holds 1.0 */
+ /* infinity*log(1) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
+ /* st(0) is positive and > 1.0 */
+ pop();
+ }
+ else
+ {
+ /* st(0) is positive and < 1.0 */
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ st1_ptr->sign ^= SIGN_NEG;
+ pop();
+ }
+ return;
+ }
+ else
+ {
+ /* st(0) must be zero or negative */
+ if ( st0_ptr->tag == TW_Zero )
+ {
+ /* This should be invalid, but a real 80486 is happy with it. */
+#ifndef PECULIAR_486
+ if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
+#endif PECULIAR_486
+ {
+ st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
+ pop();
+ }
+ }
+ else
+ {
+ /* log(negative) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ }
+ return;
+ }
+}
+
+
+static void fpatan(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ clear_C1();
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+#ifdef DENORM_OPERAND
+ if ( ((st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ poly_atan(st0_ptr, st1_ptr, st1_ptr);
+
+ if ( st1_ptr->exp <= EXP_UNDER )
+ {
+ /* A denormal result has been produced.
+ Precision must have been lost.
+ This is by definition an underflow. */
+ arith_underflow(st1_ptr);
+ pop();
+ return;
+ }
+ }
+ else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
+ {
+ stack_underflow_pop(1);
+ return;
+ }
+ else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+ {
+ if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
+ pop();
+ return;
+ }
+ else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
+ {
+ char sign = st1_ptr->sign;
+ if ( st0_tag == TW_Infinity )
+ {
+ if ( st1_tag == TW_Infinity )
+ {
+ if ( st0_ptr->sign == SIGN_POS )
+ { reg_move(&CONST_PI4, st1_ptr); }
+ else
+ reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
+ }
+ else
+ {
+#ifdef DENORM_OPERAND
+ if ( st1_tag != TW_Zero )
+ {
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+ }
+#endif DENORM_OPERAND
+
+ if ( st0_ptr->sign == SIGN_POS )
+ {
+ reg_move(&CONST_Z, st1_ptr);
+ st1_ptr->sign = sign; /* An 80486 preserves the sign */
+ pop();
+ return;
+ }
+ else
+ reg_move(&CONST_PI, st1_ptr);
+ }
+ }
+ else
+ {
+ /* st(1) is infinity, st(0) not infinity */
+#ifdef DENORM_OPERAND
+ if ( st0_tag != TW_Zero )
+ {
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+ }
+#endif DENORM_OPERAND
+
+ reg_move(&CONST_PI2, st1_ptr);
+ }
+ st1_ptr->sign = sign;
+ }
+ else if ( st1_tag == TW_Zero )
+ {
+ /* st(0) must be valid or zero */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( st0_tag != TW_Zero )
+ {
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+ }
+#endif DENORM_OPERAND
+
+ if ( st0_ptr->sign == SIGN_POS )
+ { /* An 80486 preserves the sign */ pop(); return; }
+ else
+ reg_move(&CONST_PI, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ /* st(1) must be TW_Valid here */
+ char sign = st1_ptr->sign;
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ reg_move(&CONST_PI2, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x125);
+#endif PARANOID
+
+ pop();
+ set_precision_flag_up(); /* We do not really know if up or down */
+}
+
+
+static void fprem(FPU_REG *st0_ptr)
+{
+ do_fprem(st0_ptr, RC_CHOP);
+}
+
+
+static void fprem1(FPU_REG *st0_ptr)
+{
+ do_fprem(st0_ptr, RC_RND);
+}
+
+
+static void fyl2xp1(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag, sign;
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ clear_C1();
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+#ifdef DENORM_OPERAND
+ if ( ((st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
+ return;
+#endif DENORM_OPERAND
+
+ if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) )
+ {
+#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+#else
+ if ( arith_invalid(st1_ptr) ) /* poly_l2p1() returned invalid */
+ return;
+#endif PECULIAR_486
+ }
+ if ( st1_ptr->exp <= EXP_UNDER )
+ {
+ /* A denormal result has been produced.
+ Precision must have been lost, this is always
+ an underflow. */
+ sign = st1_ptr->sign;
+ arith_underflow(st1_ptr);
+ st1_ptr->sign = sign;
+ }
+ else
+ set_precision_flag_up(); /* 80486 appears to always do this */
+ pop();
+ return;
+ }
+ else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+ {
+ stack_underflow_pop(1);
+ return;
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ if ( st1_tag <= TW_Zero )
+ {
+#ifdef DENORM_OPERAND
+ if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
+ (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ st0_ptr->sign ^= st1_ptr->sign;
+ reg_move(st0_ptr, st1_ptr);
+ }
+ else if ( st1_tag == TW_Infinity )
+ {
+ /* Infinity*log(1) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
+ else if ( st1_tag == TW_NaN )
+ {
+ if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL | 0x116);
+ return;
+ }
+#endif PARANOID
+ pop(); return;
+ }
+ else if ( st0_tag == TW_Valid )
+ {
+ if ( st1_tag == TW_Zero )
+ {
+ if ( st0_ptr->sign == SIGN_NEG )
+ {
+ if ( st0_ptr->exp >= EXP_BIAS )
+ {
+ /* st(0) holds <= -1.0 */
+#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+#else
+ if ( arith_invalid(st1_ptr) ) return;
+#endif PECULIAR_486
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ pop(); return;
+ }
+ if ( st1_tag == TW_Infinity )
+ {
+ if ( st0_ptr->sign == SIGN_NEG )
+ {
+ if ( (st0_ptr->exp >= EXP_BIAS) &&
+ !((st0_ptr->sigh == 0x80000000) &&
+ (st0_ptr->sigl == 0)) )
+ {
+ /* st(0) holds < -1.0 */
+#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+#else
+ if ( arith_invalid(st1_ptr) ) return;
+#endif PECULIAR_486
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ pop(); return;
+ }
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+ pop(); return;
+ }
+ if ( st1_tag == TW_NaN )
+ {
+ if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
+ pop();
+ return;
+ }
+ }
+ else if ( st0_tag == TW_NaN )
+ {
+ if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
+ pop();
+ return;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ if ( st1_tag == TW_NaN )
+ {
+ if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
+ pop();
+ return;
+ }
+ else if ( st0_ptr->sign == SIGN_NEG )
+ {
+ int exponent = st1_ptr->exp;
+#ifndef PECULIAR_486
+ /* This should have higher priority than denormals, but... */
+ if ( arith_invalid(st1_ptr) ) /* log(-infinity) */
+ return;
+#endif PECULIAR_486
+#ifdef DENORM_OPERAND
+ if ( st1_tag != TW_Zero )
+ {
+ if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
+ return;
+ }
+#endif DENORM_OPERAND
+#ifdef PECULIAR_486
+ /* Denormal operands actually get higher priority */
+ if ( arith_invalid(st1_ptr) ) /* log(-infinity) */
+ return;
+#endif PECULIAR_486
+ pop();
+ return;
+ }
+ else if ( st1_tag == TW_Zero )
+ {
+ /* log(infinity) */
+ if ( !arith_invalid(st1_ptr) )
+ pop();
+ return;
+ }
+
+ /* st(1) must be valid here. */
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ /* The Manual says that log(Infinity) is invalid, but a real
+ 80486 sensibly says that it is o.k. */
+ { char sign = st1_ptr->sign;
+ reg_move(&CONST_INF, st1_ptr);
+ st1_ptr->sign = sign;
+ }
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL | 0x117);
+ }
+#endif PARANOID
+}
+
+
+static void fscale(FPU_REG *st0_ptr)
+{
+ char st0_tag = st0_ptr->tag;
+ FPU_REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+ int old_cw = control_word;
+ char sign = st0_ptr->sign;
+
+ clear_C1();
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ long scale;
+ FPU_REG tmp;
+
+#ifdef DENORM_OPERAND
+ if ( ((st0_ptr->exp <= EXP_UNDER) ||
+ (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ if ( st1_ptr->exp > EXP_BIAS + 30 )
+ {
+ /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
+ char sign;
+
+ if ( st1_ptr->sign == SIGN_POS )
+ {
+ EXCEPTION(EX_Overflow);
+ sign = st0_ptr->sign;
+ reg_move(&CONST_INF, st0_ptr);
+ st0_ptr->sign = sign;
+ }
+ else
+ {
+ EXCEPTION(EX_Underflow);
+ sign = st0_ptr->sign;
+ reg_move(&CONST_Z, st0_ptr);
+ st0_ptr->sign = sign;
+ }
+ return;
+ }
+
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+ reg_move(st1_ptr, &tmp);
+ round_to_int(&tmp); /* This can never overflow here */
+ control_word = old_cw;
+ scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
+ scale += st0_ptr->exp;
+ st0_ptr->exp = scale;
+
+ /* Use round_reg() to properly detect under/overflow etc */
+ round_reg(st0_ptr, 0, control_word);
+
+ return;
+ }
+ else if ( st0_tag == TW_Valid )
+ {
+ if ( st1_tag == TW_Zero )
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ return;
+ }
+ if ( st1_tag == TW_Infinity )
+ {
+#ifdef DENORM_OPERAND
+ if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ if ( st1_ptr->sign == SIGN_POS )
+ { reg_move(&CONST_INF, st0_ptr); }
+ else
+ reg_move(&CONST_Z, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ if ( st1_tag == TW_NaN )
+ { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ if ( st1_tag == TW_Valid )
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ return;
+ }
+ else if ( st1_tag == TW_Zero ) { return; }
+ else if ( st1_tag == TW_Infinity )
+ {
+ if ( st1_ptr->sign == SIGN_NEG )
+ return;
+ else
+ {
+ arith_invalid(st0_ptr); /* Zero scaled by +Infinity */
+ return;
+ }
+ }
+ else if ( st1_tag == TW_NaN )
+ { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ if ( st1_tag == TW_Valid )
+ {
+
+#ifdef DENORM_OPERAND
+ if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+ return;
+#endif DENORM_OPERAND
+
+ return;
+ }
+ if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
+ || (st1_tag == TW_Zero) )
+ return;
+ else if ( st1_tag == TW_Infinity )
+ {
+ arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */
+ return;
+ }
+ else if ( st1_tag == TW_NaN )
+ { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+ }
+ else if ( st0_tag == TW_NaN )
+ {
+ if ( st1_tag != TW_Empty )
+ { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+ }
+
+#ifdef PARANOID
+ if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) )
+ {
+ EXCEPTION(EX_INTERNAL | 0x115);
+ return;
+ }
+#endif
+
+ /* At least one of st(0), st(1) must be empty */
+ stack_underflow();
+
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static FUNC_ST0 const trig_table_a[] = {
+ f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
+};
+
+void trig_a(void)
+{
+ (trig_table_a[FPU_rm])(&st(0));
+}
+
+
+static FUNC_ST0 const trig_table_b[] =
+ {
+ fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
+ };
+
+void trig_b(void)
+{
+ (trig_table_b[FPU_rm])(&st(0));
+}
diff --git a/arch/i386/math-emu/get_address.c b/arch/i386/math-emu/get_address.c
new file mode 100644
index 000000000..6f3270ae3
--- /dev/null
+++ b/arch/i386/math-emu/get_address.c
@@ -0,0 +1,423 @@
+/*---------------------------------------------------------------------------+
+ | get_address.c |
+ | |
+ | Get the effective address from an FPU instruction. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+
+#include <linux/stddef.h>
+#include <linux/head.h>
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+
+
+#define FPU_WRITE_BIT 0x10
+
+static int reg_offset[] = {
+ offsetof(struct info,___eax),
+ offsetof(struct info,___ecx),
+ offsetof(struct info,___edx),
+ offsetof(struct info,___ebx),
+ offsetof(struct info,___esp),
+ offsetof(struct info,___ebp),
+ offsetof(struct info,___esi),
+ offsetof(struct info,___edi)
+};
+
+#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
+
+static int reg_offset_vm86[] = {
+ offsetof(struct info,___cs),
+ offsetof(struct info,___vm86_ds),
+ offsetof(struct info,___vm86_es),
+ offsetof(struct info,___vm86_fs),
+ offsetof(struct info,___vm86_gs),
+ offsetof(struct info,___ss),
+ offsetof(struct info,___vm86_ds)
+ };
+
+#define VM86_REG_(x) (*(unsigned short *) \
+ (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
+
+static int reg_offset_pm[] = {
+ offsetof(struct info,___cs),
+ offsetof(struct info,___ds),
+ offsetof(struct info,___es),
+ offsetof(struct info,___fs),
+ offsetof(struct info,___gs),
+ offsetof(struct info,___ss),
+ offsetof(struct info,___ds)
+ };
+
+#define PM_REG_(x) (*(unsigned short *) \
+ (reg_offset_pm[((unsigned)x)]+(char *) FPU_info))
+
+
+/* Decode the SIB byte. This function assumes mod != 0 */
+static int sib(int mod, unsigned long *fpu_eip)
+{
+ unsigned char ss,index,base;
+ long offset;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */
+ RE_ENTRANT_CHECK_ON;
+ (*fpu_eip)++;
+ ss = base >> 6;
+ index = (base >> 3) & 7;
+ base &= 7;
+
+ if ((mod == 0) && (base == 5))
+ offset = 0; /* No base register */
+ else
+ offset = REG_(base);
+
+ if (index == 4)
+ {
+ /* No index register */
+ /* A non-zero ss is illegal */
+ if ( ss )
+ EXCEPTION(EX_Invalid);
+ }
+ else
+ {
+ offset += (REG_(index)) << ss;
+ }
+
+ if (mod == 1)
+ {
+ /* 8 bit signed displacement */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ offset += (signed char) get_fs_byte((char *) (*fpu_eip));
+ RE_ENTRANT_CHECK_ON;
+ (*fpu_eip)++;
+ }
+ else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
+ {
+ /* 32 bit displacement */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(4);
+ offset += (signed) get_fs_long((unsigned long *) (*fpu_eip));
+ RE_ENTRANT_CHECK_ON;
+ (*fpu_eip) += 4;
+ }
+
+ return offset;
+}
+
+
+static unsigned long vm86_segment(unsigned char segment,
+ unsigned short *selector)
+{
+ segment--;
+#ifdef PARANOID
+ if ( segment > PREFIX_SS_ )
+ {
+ EXCEPTION(EX_INTERNAL|0x130);
+ math_abort(FPU_info,SIGSEGV);
+ }
+#endif PARANOID
+ *selector = VM86_REG_(segment);
+ return (unsigned long)VM86_REG_(segment) << 4;
+}
+
+
+/* This should work for 16 and 32 bit protected mode. */
+static long pm_address(unsigned char FPU_modrm, unsigned char segment,
+ unsigned short *selector, long offset)
+{
+ struct desc_struct descriptor;
+ unsigned long base_address, limit, address, seg_top;
+
+ segment--;
+#ifdef PARANOID
+ if ( segment > PREFIX_SS_ )
+ {
+ EXCEPTION(EX_INTERNAL|0x132);
+ math_abort(FPU_info,SIGSEGV);
+ }
+#endif PARANOID
+
+ *selector = PM_REG_(segment);
+
+ descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
+ base_address = SEG_BASE_ADDR(descriptor);
+ address = base_address + offset;
+ limit = base_address
+ + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1;
+ if ( limit < base_address ) limit = 0xffffffff;
+
+ if ( SEG_EXPAND_DOWN(descriptor) )
+ {
+ if ( SEG_G_BIT(descriptor) )
+ seg_top = 0xffffffff;
+ else
+ {
+ seg_top = base_address + (1 << 20);
+ if ( seg_top < base_address ) seg_top = 0xffffffff;
+ }
+ access_limit =
+ (address <= limit) || (address >= seg_top) ? 0 :
+ ((seg_top-address) >= 255 ? 255 : seg_top-address);
+ }
+ else
+ {
+ access_limit =
+ (address > limit) || (address < base_address) ? 0 :
+ ((limit-address) >= 254 ? 255 : limit-address+1);
+ }
+ if ( SEG_EXECUTE_ONLY(descriptor) ||
+ (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) )
+ {
+ access_limit = 0;
+ }
+ return address;
+}
+
+
+/*
+ MOD R/M byte: MOD == 3 has a special use for the FPU
+ SIB byte used iff R/M = 100b
+
+ 7 6 5 4 3 2 1 0
+ ..... ......... .........
+ MOD OPCODE(2) R/M
+
+
+ SIB byte
+
+ 7 6 5 4 3 2 1 0
+ ..... ......... .........
+ SS INDEX BASE
+
+*/
+
+void *get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
+ struct address *addr,
+/* unsigned short *selector, unsigned long *offset, */
+ fpu_addr_modes addr_modes)
+{
+ unsigned char mod;
+ unsigned rm = FPU_modrm & 7;
+ long *cpu_reg_ptr;
+ int address = 0; /* Initialized just to stop compiler warnings. */
+
+ /* Memory accessed via the cs selector is write protected
+ in `non-segmented' 32 bit protected mode. */
+ if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
+ && (addr_modes.override.segment == PREFIX_CS_) )
+ {
+ math_abort(FPU_info,SIGSEGV);
+ }
+
+ addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
+
+ mod = (FPU_modrm >> 6) & 3;
+
+ if (rm == 4 && mod != 3)
+ {
+ address = sib(mod, fpu_eip);
+ }
+ else
+ {
+ cpu_reg_ptr = & REG_(rm);
+ switch (mod)
+ {
+ case 0:
+ if (rm == 5)
+ {
+ /* Special case: disp32 */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(4);
+ address = get_fs_long((unsigned long *) (*fpu_eip));
+ (*fpu_eip) += 4;
+ RE_ENTRANT_CHECK_ON;
+ addr->offset = address;
+ return (void *) address;
+ }
+ else
+ {
+ address = *cpu_reg_ptr; /* Just return the contents
+ of the cpu register */
+ addr->offset = address;
+ return (void *) address;
+ }
+ case 1:
+ /* 8 bit signed displacement */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ address = (signed char) get_fs_byte((char *) (*fpu_eip));
+ RE_ENTRANT_CHECK_ON;
+ (*fpu_eip)++;
+ break;
+ case 2:
+ /* 32 bit displacement */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(4);
+ address = (signed) get_fs_long((unsigned long *) (*fpu_eip));
+ (*fpu_eip) += 4;
+ RE_ENTRANT_CHECK_ON;
+ break;
+ case 3:
+ /* Not legal for the FPU */
+ EXCEPTION(EX_Invalid);
+ }
+ address += *cpu_reg_ptr;
+ }
+
+ addr->offset = address;
+
+ switch ( addr_modes.default_mode )
+ {
+ case 0:
+ break;
+ case VM86:
+ address += vm86_segment(addr_modes.override.segment,
+ (unsigned short *)&(addr->selector));
+ break;
+ case PM16:
+ case SEG32:
+ address = pm_address(FPU_modrm, addr_modes.override.segment,
+ (unsigned short *)&(addr->selector), address);
+ break;
+ default:
+ EXCEPTION(EX_INTERNAL|0x133);
+ }
+
+ return (void *)address;
+}
+
+
+void *get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
+ struct address *addr,
+/* unsigned short *selector, unsigned long *offset, */
+ fpu_addr_modes addr_modes)
+{
+ unsigned char mod;
+ unsigned rm = FPU_modrm & 7;
+ int address = 0; /* Default used for mod == 0 */
+
+ /* Memory accessed via the cs selector is write protected
+ in `non-segmented' 32 bit protected mode. */
+ if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
+ && (addr_modes.override.segment == PREFIX_CS_) )
+ {
+ math_abort(FPU_info,SIGSEGV);
+ }
+
+ addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */
+
+ mod = (FPU_modrm >> 6) & 3;
+
+ switch (mod)
+ {
+ case 0:
+ if (rm == 6)
+ {
+ /* Special case: disp16 */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(2);
+ address = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
+ (*fpu_eip) += 2;
+ RE_ENTRANT_CHECK_ON;
+ goto add_segment;
+ }
+ break;
+ case 1:
+ /* 8 bit signed displacement */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(1);
+ address = (signed char) get_fs_byte((signed char *) (*fpu_eip));
+ RE_ENTRANT_CHECK_ON;
+ (*fpu_eip)++;
+ break;
+ case 2:
+ /* 16 bit displacement */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_code_verify_area(2);
+ address = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
+ (*fpu_eip) += 2;
+ RE_ENTRANT_CHECK_ON;
+ break;
+ case 3:
+ /* Not legal for the FPU */
+ EXCEPTION(EX_Invalid);
+ break;
+ }
+ switch ( rm )
+ {
+ case 0:
+ address += FPU_info->___ebx + FPU_info->___esi;
+ break;
+ case 1:
+ address += FPU_info->___ebx + FPU_info->___edi;
+ break;
+ case 2:
+ address += FPU_info->___ebp + FPU_info->___esi;
+ if ( addr_modes.override.segment == PREFIX_DEFAULT )
+ addr_modes.override.segment = PREFIX_SS_;
+ break;
+ case 3:
+ address += FPU_info->___ebp + FPU_info->___edi;
+ if ( addr_modes.override.segment == PREFIX_DEFAULT )
+ addr_modes.override.segment = PREFIX_SS_;
+ break;
+ case 4:
+ address += FPU_info->___esi;
+ break;
+ case 5:
+ address += FPU_info->___edi;
+ break;
+ case 6:
+ address += FPU_info->___ebp;
+ if ( addr_modes.override.segment == PREFIX_DEFAULT )
+ addr_modes.override.segment = PREFIX_SS_;
+ break;
+ case 7:
+ address += FPU_info->___ebx;
+ break;
+ }
+
+ add_segment:
+ address &= 0xffff;
+
+ addr->offset = address;
+
+ switch ( addr_modes.default_mode )
+ {
+ case 0:
+ break;
+ case VM86:
+ address += vm86_segment(addr_modes.override.segment,
+ (unsigned short *)&(addr->selector));
+ break;
+ case PM16:
+ case SEG32:
+ address = pm_address(FPU_modrm, addr_modes.override.segment,
+ (unsigned short *)&(addr->selector), address);
+ break;
+ default:
+ EXCEPTION(EX_INTERNAL|0x131);
+ }
+
+ return (void *)address ;
+}
diff --git a/arch/i386/math-emu/load_store.c b/arch/i386/math-emu/load_store.c
new file mode 100644
index 000000000..6f0e167d6
--- /dev/null
+++ b/arch/i386/math-emu/load_store.c
@@ -0,0 +1,260 @@
+/*---------------------------------------------------------------------------+
+ | load_store.c |
+ | |
+ | This file contains most of the code to interpret the FPU instructions |
+ | which load and store from user memory. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "control_w.h"
+
+
+#define _NONE_ 0 /* st0_ptr etc not needed */
+#define _REG0_ 1 /* Will be storing st(0) */
+#define _PUSH_ 3 /* Need to check for space to push onto stack */
+#define _null_ 4 /* Function illegal or not implemented */
+
+#define pop_0() { st0_ptr->tag = TW_Empty; top++; }
+
+
+static unsigned char const type_table[32] = {
+ _PUSH_, _PUSH_, _PUSH_, _PUSH_,
+ _null_, _null_, _null_, _null_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _NONE_, _null_, _NONE_, _PUSH_,
+ _NONE_, _PUSH_, _null_, _PUSH_,
+ _NONE_, _null_, _NONE_, _REG0_,
+ _NONE_, _REG0_, _NONE_, _REG0_
+ };
+
+unsigned char const data_sizes_16[32] = {
+ 4, 4, 8, 2, 0, 0, 0, 0,
+ 4, 4, 8, 2, 4, 4, 8, 2,
+ 14, 0, 94, 10, 2, 10, 0, 8,
+ 14, 0, 94, 10, 2, 10, 2, 8
+};
+
+unsigned char const data_sizes_32[32] = {
+ 4, 4, 8, 2, 0, 0, 0, 0,
+ 4, 4, 8, 2, 4, 4, 8, 2,
+ 28, 0,108, 10, 2, 10, 0, 8,
+ 28, 0,108, 10, 2, 10, 2, 8
+};
+
+int load_store_instr(unsigned char type, fpu_addr_modes addr_modes,
+ void *data_address)
+{
+ FPU_REG loaded_data;
+ FPU_REG *st0_ptr;
+
+ st0_ptr = NULL; /* Initialized just to stop compiler warnings. */
+
+ if ( addr_modes.default_mode & PROTECTED )
+ {
+ if ( addr_modes.default_mode == SEG32 )
+ {
+ if ( access_limit < data_sizes_32[type] )
+ math_abort(FPU_info,SIGSEGV);
+ }
+ else if ( addr_modes.default_mode == PM16 )
+ {
+ if ( access_limit < data_sizes_16[type] )
+ math_abort(FPU_info,SIGSEGV);
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL|0x140);
+#endif PARANOID
+ }
+
+ switch ( type_table[type] )
+ {
+ case _NONE_:
+ break;
+ case _REG0_:
+ st0_ptr = &st(0); /* Some of these instructions pop after
+ storing */
+ break;
+ case _PUSH_:
+ {
+ st0_ptr = &st(-1);
+ if ( st0_ptr->tag != TW_Empty )
+ { stack_overflow(); return 0; }
+ top--;
+ }
+ break;
+ case _null_:
+ FPU_illegal();
+ return 0;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x141);
+ return 0;
+#endif PARANOID
+ }
+
+ switch ( type )
+ {
+ case 000: /* fld m32real */
+ clear_C1();
+ reg_load_single((float *)data_address, &loaded_data);
+ if ( (loaded_data.tag == TW_NaN) &&
+ real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) )
+ {
+ top++;
+ break;
+ }
+ reg_move(&loaded_data, st0_ptr);
+ break;
+ case 001: /* fild m32int */
+ clear_C1();
+ reg_load_int32((long *)data_address, st0_ptr);
+ break;
+ case 002: /* fld m64real */
+ clear_C1();
+ reg_load_double((double *)data_address, &loaded_data);
+ if ( (loaded_data.tag == TW_NaN) &&
+ real_2op_NaN(&loaded_data, &loaded_data, &loaded_data) )
+ {
+ top++;
+ break;
+ }
+ reg_move(&loaded_data, st0_ptr);
+ break;
+ case 003: /* fild m16int */
+ clear_C1();
+ reg_load_int16((short *)data_address, st0_ptr);
+ break;
+ case 010: /* fst m32real */
+ clear_C1();
+ reg_store_single((float *)data_address, st0_ptr);
+ break;
+ case 011: /* fist m32int */
+ clear_C1();
+ reg_store_int32((long *)data_address, st0_ptr);
+ break;
+ case 012: /* fst m64real */
+ clear_C1();
+ reg_store_double((double *)data_address, st0_ptr);
+ break;
+ case 013: /* fist m16int */
+ clear_C1();
+ reg_store_int16((short *)data_address, st0_ptr);
+ break;
+ case 014: /* fstp m32real */
+ clear_C1();
+ if ( reg_store_single((float *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 015: /* fistp m32int */
+ clear_C1();
+ if ( reg_store_int32((long *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 016: /* fstp m64real */
+ clear_C1();
+ if ( reg_store_double((double *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 017: /* fistp m16int */
+ clear_C1();
+ if ( reg_store_int16((short *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 020: /* fldenv m14/28byte */
+ fldenv(addr_modes, (char *)data_address);
+ /* Ensure that the values just loaded are not changed by
+ fix-up operations. */
+ return 1;
+ case 022: /* frstor m94/108byte */
+ frstor(addr_modes, (char *)data_address);
+ /* Ensure that the values just loaded are not changed by
+ fix-up operations. */
+ return 1;
+ case 023: /* fbld m80dec */
+ clear_C1();
+ reg_load_bcd((char *)data_address, st0_ptr);
+ break;
+ case 024: /* fldcw */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, data_address, 2);
+ control_word = get_fs_word((unsigned short *) data_address);
+ RE_ENTRANT_CHECK_ON;
+ if ( partial_status & ~control_word & CW_Exceptions )
+ partial_status |= (SW_Summary | SW_Backward);
+ else
+ partial_status &= ~(SW_Summary | SW_Backward);
+#ifdef PECULIAR_486
+ control_word |= 0x40; /* An 80486 appears to always set this bit */
+#endif PECULIAR_486
+ return 1;
+ case 025: /* fld m80real */
+ clear_C1();
+ reg_load_extended((long double *)data_address, st0_ptr);
+ break;
+ case 027: /* fild m64int */
+ clear_C1();
+ reg_load_int64((long long *)data_address, st0_ptr);
+ break;
+ case 030: /* fstenv m14/28byte */
+ fstenv(addr_modes, (char *)data_address);
+ return 1;
+ case 032: /* fsave */
+ fsave(addr_modes, (char *)data_address);
+ return 1;
+ case 033: /* fbstp m80dec */
+ clear_C1();
+ if ( reg_store_bcd((char *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 034: /* fstcw m16int */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,data_address,2);
+ put_fs_word(control_word, (short *) data_address);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
+ case 035: /* fstp m80real */
+ clear_C1();
+ if ( reg_store_extended((long double *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 036: /* fstsw m2byte */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,data_address,2);
+ put_fs_word(status_word(),(short *) data_address);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
+ case 037: /* fistp m64int */
+ clear_C1();
+ if ( reg_store_int64((long long *)data_address, st0_ptr) )
+ pop_0(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ }
+ return 0;
+}
diff --git a/arch/i386/math-emu/mul_Xsig.S b/arch/i386/math-emu/mul_Xsig.S
new file mode 100644
index 000000000..1d88d4466
--- /dev/null
+++ b/arch/i386/math-emu/mul_Xsig.S
@@ -0,0 +1,182 @@
+/*---------------------------------------------------------------------------+
+ | mul_Xsig.S |
+ | |
+ | Multiply a 12 byte fixed point number by another fixed point number. |
+ | |
+ | Copyright (C) 1992,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void mul32_Xsig(Xsig *x, unsigned b) |
+ | |
+ | void mul64_Xsig(Xsig *x, unsigned long long *b) |
+ | |
+ | void mul_Xsig_Xsig(Xsig *x, unsigned *b) |
+ | |
+ | The result is neither rounded nor normalized, and the ls bit or so may |
+ | be wrong. |
+ | |
+ +---------------------------------------------------------------------------*/
+ .file "mul_Xsig.S"
+
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+.globl _mul32_Xsig
+_mul32_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $16,%esp
+ pushl %esi
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+ movl (%esi),%eax /* lsl of Xsig */
+ mull %ecx /* msl of b */
+ movl %edx,-12(%ebp)
+
+ movl 4(%esi),%eax /* midl of Xsig */
+ mull %ecx /* msl of b */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 8(%esi),%eax /* msl of Xsig */
+ mull %ecx /* msl of b */
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+ movl -12(%ebp),%eax
+ movl %eax,(%esi)
+ movl -8(%ebp),%eax
+ movl %eax,4(%esi)
+ movl -4(%ebp),%eax
+ movl %eax,8(%esi)
+
+ popl %esi
+ leave
+ ret
+
+
+ .align 2,144
+.globl _mul64_Xsig
+_mul64_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $16,%esp
+ pushl %esi
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+ movl (%esi),%eax /* lsl of Xsig */
+ mull 4(%ecx) /* msl of b */
+ movl %edx,-12(%ebp)
+
+ movl 4(%esi),%eax /* midl of Xsig */
+ mull (%ecx) /* lsl of b */
+ addl %edx,-12(%ebp)
+ adcl $0,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax /* midl of Xsig */
+ mull 4(%ecx) /* msl of b */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 8(%esi),%eax /* msl of Xsig */
+ mull (%ecx) /* lsl of b */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 8(%esi),%eax /* msl of Xsig */
+ mull 4(%ecx) /* msl of b */
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+ movl -12(%ebp),%eax
+ movl %eax,(%esi)
+ movl -8(%ebp),%eax
+ movl %eax,4(%esi)
+ movl -4(%ebp),%eax
+ movl %eax,8(%esi)
+
+ popl %esi
+ leave
+ ret
+
+
+
+ .align 2,144
+.globl _mul_Xsig_Xsig
+_mul_Xsig_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $16,%esp
+ pushl %esi
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+ movl (%esi),%eax /* lsl of Xsig */
+ mull 8(%ecx) /* msl of b */
+ movl %edx,-12(%ebp)
+
+ movl 4(%esi),%eax /* midl of Xsig */
+ mull 4(%ecx) /* midl of b */
+ addl %edx,-12(%ebp)
+ adcl $0,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 8(%esi),%eax /* msl of Xsig */
+ mull (%ecx) /* lsl of b */
+ addl %edx,-12(%ebp)
+ adcl $0,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax /* midl of Xsig */
+ mull 8(%ecx) /* msl of b */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 8(%esi),%eax /* msl of Xsig */
+ mull 4(%ecx) /* midl of b */
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 8(%esi),%eax /* msl of Xsig */
+ mull 8(%ecx) /* msl of b */
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+ movl -12(%ebp),%edx
+ movl %edx,(%esi)
+ movl -8(%ebp),%edx
+ movl %edx,4(%esi)
+ movl -4(%ebp),%edx
+ movl %edx,8(%esi)
+
+ popl %esi
+ leave
+ ret
+
diff --git a/arch/i386/math-emu/poly.h b/arch/i386/math-emu/poly.h
new file mode 100644
index 000000000..397cb9e3e
--- /dev/null
+++ b/arch/i386/math-emu/poly.h
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------+
+ | poly.h |
+ | |
+ | Header file for the FPU-emu poly*.c source files. |
+ | |
+ | Copyright (C) 1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Declarations and definitions for functions operating on Xsig (12-byte |
+ | extended-significand) quantities. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _POLY_H
+#define _POLY_H
+
+/* This 12-byte structure is used to improve the accuracy of computation
+ of transcendental functions.
+ Intended to be used to get results better than 8-byte computation
+ allows. 9-byte would probably be sufficient.
+ */
+typedef struct {
+ unsigned long lsw;
+ unsigned long midw;
+ unsigned long msw;
+} Xsig;
+
+asmlinkage void mul64(unsigned long long const *a, unsigned long long const *b,
+ unsigned long long *result);
+asmlinkage void polynomial_Xsig(Xsig *, const unsigned long long *x,
+ const unsigned long long terms[], const int n);
+
+asmlinkage void mul32_Xsig(Xsig *, const unsigned long mult);
+asmlinkage void mul64_Xsig(Xsig *, const unsigned long long *mult);
+asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult);
+
+asmlinkage void shr_Xsig(Xsig *, const int n);
+asmlinkage int round_Xsig(Xsig *);
+asmlinkage int norm_Xsig(Xsig *);
+asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest);
+
+/* Macro to extract the most significant 32 bits from a long long */
+#define LL_MSW(x) (((unsigned long *)&x)[1])
+
+/* Macro to initialize an Xsig struct */
+#define MK_XSIG(a,b,c) { c, b, a }
+
+/* Macro to access the 8 ms bytes of an Xsig as a long long */
+#define XSIG_LL(x) (*(unsigned long long *)&x.midw)
+
+
+/*
+ Need to run gcc with optimizations on to get these to
+ actually be in-line.
+ */
+
+/* Multiply two fixed-point 32 bit numbers. */
+extern inline void mul_32_32(const unsigned long arg1,
+ const unsigned long arg2,
+ unsigned long *out)
+{
+ asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \
+ :"=g" (*out) \
+ :"g" (arg1), "g" (arg2) \
+ :"ax","dx");
+}
+
+
+/* Add the 12 byte Xsig x2 to Xsig dest, with no checks for overflow. */
+extern inline void add_Xsig_Xsig(Xsig *dest, const Xsig *x2)
+{
+ asm volatile ("movl %1,%%edi; movl %2,%%esi;
+ movl (%%esi),%%eax; addl %%eax,(%%edi);
+ movl 4(%%esi),%%eax; adcl %%eax,4(%%edi);
+ movl 8(%%esi),%%eax; adcl %%eax,8(%%edi);"
+ :"=g" (*dest):"g" (dest), "g" (x2)
+ :"ax","si","di");
+}
+
+
+/* Add the 12 byte Xsig x2 to Xsig dest, adjust exp if overflow occurs. */
+/* Note: the constraints in the asm statement didn't always work properly
+ with gcc 2.5.8. Changing from using edi to using ecx got around the
+ problem, but keep fingers crossed! */
+extern inline int add_two_Xsig(Xsig *dest, const Xsig *x2, long int *exp)
+{
+ asm volatile ("movl %2,%%ecx; movl %3,%%esi;
+ movl (%%esi),%%eax; addl %%eax,(%%ecx);
+ movl 4(%%esi),%%eax; adcl %%eax,4(%%ecx);
+ movl 8(%%esi),%%eax; adcl %%eax,8(%%ecx);
+ jnc 0f;
+ rcrl 8(%%ecx); rcrl 4(%%ecx); rcrl (%%ecx)
+ movl %4,%%ecx; incl (%%ecx)
+ movl $1,%%eax; jmp 1f;
+ 0: xorl %%eax,%%eax;
+ 1:"
+ :"=g" (*exp), "=g" (*dest)
+ :"g" (dest), "g" (x2), "g" (exp)
+ :"cx","si","ax");
+}
+
+
+/* Negate (subtract from 1.0) the 12 byte Xsig */
+/* This is faster in a loop on my 386 than using the "neg" instruction. */
+extern inline void negate_Xsig(Xsig *x)
+{
+ asm volatile("movl %1,%%esi; "
+ "xorl %%ecx,%%ecx; "
+ "movl %%ecx,%%eax; subl (%%esi),%%eax; movl %%eax,(%%esi); "
+ "movl %%ecx,%%eax; sbbl 4(%%esi),%%eax; movl %%eax,4(%%esi); "
+ "movl %%ecx,%%eax; sbbl 8(%%esi),%%eax; movl %%eax,8(%%esi); "
+ :"=g" (*x):"g" (x):"si","ax","cx");
+}
+
+#endif _POLY_H
diff --git a/arch/i386/math-emu/poly_2xm1.c b/arch/i386/math-emu/poly_2xm1.c
new file mode 100644
index 000000000..f7c585d60
--- /dev/null
+++ b/arch/i386/math-emu/poly_2xm1.c
@@ -0,0 +1,152 @@
+/*---------------------------------------------------------------------------+
+ | poly_2xm1.c |
+ | |
+ | Function to compute 2^x-1 by a polynomial approximation. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "poly.h"
+
+
+#define HIPOWER 11
+static const unsigned long long lterms[HIPOWER] =
+{
+ 0x0000000000000000LL, /* This term done separately as 12 bytes */
+ 0xf5fdeffc162c7543LL,
+ 0x1c6b08d704a0bfa6LL,
+ 0x0276556df749cc21LL,
+ 0x002bb0ffcf14f6b8LL,
+ 0x0002861225ef751cLL,
+ 0x00001ffcbfcd5422LL,
+ 0x00000162c005d5f1LL,
+ 0x0000000da96ccb1bLL,
+ 0x0000000078d1b897LL,
+ 0x000000000422b029LL
+};
+
+static const Xsig hiterm = MK_XSIG(0xb17217f7, 0xd1cf79ab, 0xc8a39194);
+
+/* Four slices: 0.0 : 0.25 : 0.50 : 0.75 : 1.0,
+ These numbers are 2^(1/4), 2^(1/2), and 2^(3/4)
+ */
+static const Xsig shiftterm0 = MK_XSIG(0, 0, 0);
+static const Xsig shiftterm1 = MK_XSIG(0x9837f051, 0x8db8a96f, 0x46ad2318);
+static const Xsig shiftterm2 = MK_XSIG(0xb504f333, 0xf9de6484, 0x597d89b3);
+static const Xsig shiftterm3 = MK_XSIG(0xd744fcca, 0xd69d6af4, 0x39a68bb9);
+
+static const Xsig *shiftterm[] = { &shiftterm0, &shiftterm1,
+ &shiftterm2, &shiftterm3 };
+
+
+/*--- poly_2xm1() -----------------------------------------------------------+
+ | Requires an argument which is TW_Valid and < 1. |
+ +---------------------------------------------------------------------------*/
+int poly_2xm1(FPU_REG const *arg, FPU_REG *result)
+{
+ long int exponent, shift;
+ unsigned long long Xll;
+ Xsig accumulator, Denom, argSignif;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+#ifdef PARANOID
+ if ( (exponent >= 0) /* Don't want a |number| >= 1.0 */
+ || (arg->tag != TW_Valid) )
+ {
+ /* Number negative, too large, or not Valid. */
+ EXCEPTION(EX_INTERNAL|0x127);
+ return 1;
+ }
+#endif PARANOID
+
+ argSignif.lsw = 0;
+ XSIG_LL(argSignif) = Xll = significand(arg);
+
+ if ( exponent == -1 )
+ {
+ shift = (argSignif.msw & 0x40000000) ? 3 : 2;
+ /* subtract 0.5 or 0.75 */
+ exponent -= 2;
+ XSIG_LL(argSignif) <<= 2;
+ Xll <<= 2;
+ }
+ else if ( exponent == -2 )
+ {
+ shift = 1;
+ /* subtract 0.25 */
+ exponent--;
+ XSIG_LL(argSignif) <<= 1;
+ Xll <<= 1;
+ }
+ else
+ shift = 0;
+
+ if ( exponent < -2 )
+ {
+ /* Shift the argument right by the required places. */
+ if ( shrx(&Xll, -2-exponent) >= 0x80000000U )
+ Xll++; /* round up */
+ }
+
+ accumulator.lsw = accumulator.midw = accumulator.msw = 0;
+ polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1);
+ mul_Xsig_Xsig(&accumulator, &argSignif);
+ shr_Xsig(&accumulator, 3);
+
+ mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */
+ add_two_Xsig(&accumulator, &argSignif, &exponent);
+
+ if ( shift )
+ {
+ /* The argument is large, use the identity:
+ f(x+a) = f(a) * (f(x) + 1) - 1;
+ */
+ shr_Xsig(&accumulator, - exponent);
+ accumulator.msw |= 0x80000000; /* add 1.0 */
+ mul_Xsig_Xsig(&accumulator, shiftterm[shift]);
+ accumulator.msw &= 0x3fffffff; /* subtract 1.0 */
+ exponent = 1;
+ }
+
+ if ( arg->sign != SIGN_POS )
+ {
+ /* The argument is negative, use the identity:
+ f(-x) = -f(x) / (1 + f(x))
+ */
+ Denom.lsw = accumulator.lsw;
+ XSIG_LL(Denom) = XSIG_LL(accumulator);
+ if ( exponent < 0 )
+ shr_Xsig(&Denom, - exponent);
+ else if ( exponent > 0 )
+ {
+ /* exponent must be 1 here */
+ XSIG_LL(Denom) <<= 1;
+ if ( Denom.lsw & 0x80000000 )
+ XSIG_LL(Denom) |= 1;
+ (Denom.lsw) <<= 1;
+ }
+ Denom.msw |= 0x80000000; /* add 1.0 */
+ div_Xsig(&accumulator, &Denom, &accumulator);
+ }
+
+ /* Convert to 64 bit signed-compatible */
+ exponent += round_Xsig(&accumulator);
+
+ significand(result) = XSIG_LL(accumulator);
+ result->tag = TW_Valid;
+ result->exp = exponent + EXP_BIAS;
+ result->sign = arg->sign;
+
+ return 0;
+
+}
diff --git a/arch/i386/math-emu/poly_atan.c b/arch/i386/math-emu/poly_atan.c
new file mode 100644
index 000000000..6edca625f
--- /dev/null
+++ b/arch/i386/math-emu/poly_atan.c
@@ -0,0 +1,197 @@
+/*---------------------------------------------------------------------------+
+ | poly_atan.c |
+ | |
+ | Compute the arctan of a FPU_REG, using a polynomial approximation. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "control_w.h"
+#include "poly.h"
+
+
+#define HIPOWERon 6 /* odd poly, negative terms */
+static const unsigned long long oddnegterms[HIPOWERon] =
+{
+ 0x0000000000000000LL, /* Dummy (not for - 1.0) */
+ 0x015328437f756467LL,
+ 0x0005dda27b73dec6LL,
+ 0x0000226bf2bfb91aLL,
+ 0x000000ccc439c5f7LL,
+ 0x0000000355438407LL
+} ;
+
+#define HIPOWERop 6 /* odd poly, positive terms */
+static const unsigned long long oddplterms[HIPOWERop] =
+{
+/* 0xaaaaaaaaaaaaaaabLL, transferred to fixedpterm[] */
+ 0x0db55a71875c9ac2LL,
+ 0x0029fce2d67880b0LL,
+ 0x0000dfd3908b4596LL,
+ 0x00000550fd61dab4LL,
+ 0x0000001c9422b3f9LL,
+ 0x000000003e3301e1LL
+};
+
+static const unsigned long long denomterm = 0xebd9b842c5c53a0eLL;
+
+static const Xsig fixedpterm = MK_XSIG(0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa);
+
+static const Xsig pi_signif = MK_XSIG(0xc90fdaa2, 0x2168c234, 0xc4c6628b);
+
+
+/*--- poly_atan() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_atan(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *result)
+{
+ char transformed, inverted,
+ sign1 = arg1->sign, sign2 = arg2->sign;
+ long int exponent, dummy_exp;
+ Xsig accumulator, Numer, Denom, accumulatore, argSignif,
+ argSq, argSqSq;
+
+
+ arg1->sign = arg2->sign = SIGN_POS;
+ if ( (compare(arg2) & ~COMP_Denormal) == COMP_A_lt_B )
+ {
+ inverted = 1;
+ exponent = arg1->exp - arg2->exp;
+ Numer.lsw = Denom.lsw = 0;
+ XSIG_LL(Numer) = significand(arg1);
+ XSIG_LL(Denom) = significand(arg2);
+ }
+ else
+ {
+ inverted = 0;
+ exponent = arg2->exp - arg1->exp;
+ Numer.lsw = Denom.lsw = 0;
+ XSIG_LL(Numer) = significand(arg2);
+ XSIG_LL(Denom) = significand(arg1);
+ }
+ div_Xsig(&Numer, &Denom, &argSignif);
+ exponent += norm_Xsig(&argSignif);
+
+ if ( (exponent >= -1)
+ || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) )
+ {
+ /* The argument is greater than sqrt(2)-1 (=0.414213562...) */
+ /* Convert the argument by an identity for atan */
+ transformed = 1;
+
+ if ( exponent >= 0 )
+ {
+#ifdef PARANOID
+ if ( !( (exponent == 0) &&
+ (argSignif.lsw == 0) && (argSignif.midw == 0) &&
+ (argSignif.msw == 0x80000000) ) )
+ {
+ EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
+ return;
+ }
+#endif PARANOID
+ argSignif.msw = 0; /* Make the transformed arg -> 0.0 */
+ }
+ else
+ {
+ Numer.lsw = Denom.lsw = argSignif.lsw;
+ XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif);
+
+ if ( exponent < -1 )
+ shr_Xsig(&Numer, -1-exponent);
+ negate_Xsig(&Numer);
+
+ shr_Xsig(&Denom, -exponent);
+ Denom.msw |= 0x80000000;
+
+ div_Xsig(&Numer, &Denom, &argSignif);
+
+ exponent = -1 + norm_Xsig(&argSignif);
+ }
+ }
+ else
+ {
+ transformed = 0;
+ }
+
+ argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw;
+ argSq.msw = argSignif.msw;
+ mul_Xsig_Xsig(&argSq, &argSq);
+
+ argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw;
+ mul_Xsig_Xsig(&argSqSq, &argSqSq);
+
+ accumulatore.lsw = argSq.lsw;
+ XSIG_LL(accumulatore) = XSIG_LL(argSq);
+
+ shr_Xsig(&argSq, 2*(-1-exponent-1));
+ shr_Xsig(&argSqSq, 4*(-1-exponent-1));
+
+ /* Now have argSq etc with binary point at the left
+ .1xxxxxxxx */
+
+ /* Do the basic fixed point polynomial evaluation */
+ accumulator.msw = accumulator.midw = accumulator.lsw = 0;
+ polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq),
+ oddplterms, HIPOWERop-1);
+ mul64_Xsig(&accumulator, &XSIG_LL(argSq));
+ negate_Xsig(&accumulator);
+ polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1);
+ negate_Xsig(&accumulator);
+ add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp);
+
+ mul64_Xsig(&accumulatore, &denomterm);
+ shr_Xsig(&accumulatore, 1 + 2*(-1-exponent));
+ accumulatore.msw |= 0x80000000;
+
+ div_Xsig(&accumulator, &accumulatore, &accumulator);
+
+ mul_Xsig_Xsig(&accumulator, &argSignif);
+ mul_Xsig_Xsig(&accumulator, &argSq);
+
+ shr_Xsig(&accumulator, 3);
+ negate_Xsig(&accumulator);
+ add_Xsig_Xsig(&accumulator, &argSignif);
+
+ if ( transformed )
+ {
+ /* compute pi/4 - accumulator */
+ shr_Xsig(&accumulator, -1-exponent);
+ negate_Xsig(&accumulator);
+ add_Xsig_Xsig(&accumulator, &pi_signif);
+ exponent = -1;
+ }
+
+ if ( inverted )
+ {
+ /* compute pi/2 - accumulator */
+ shr_Xsig(&accumulator, -exponent);
+ negate_Xsig(&accumulator);
+ add_Xsig_Xsig(&accumulator, &pi_signif);
+ exponent = 0;
+ }
+
+ if ( sign1 )
+ {
+ /* compute pi - accumulator */
+ shr_Xsig(&accumulator, 1 - exponent);
+ negate_Xsig(&accumulator);
+ add_Xsig_Xsig(&accumulator, &pi_signif);
+ exponent = 1;
+ }
+
+ exponent += round_Xsig(&accumulator);
+ significand(result) = XSIG_LL(accumulator);
+ result->exp = exponent + EXP_BIAS;
+ result->tag = TW_Valid;
+ result->sign = sign2;
+
+}
diff --git a/arch/i386/math-emu/poly_l2.c b/arch/i386/math-emu/poly_l2.c
new file mode 100644
index 000000000..1677f4aff
--- /dev/null
+++ b/arch/i386/math-emu/poly_l2.c
@@ -0,0 +1,255 @@
+/*---------------------------------------------------------------------------+
+ | poly_l2.c |
+ | |
+ | Compute the base 2 log of a FPU_REG, using a polynomial approximation. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "poly.h"
+
+
+
+static void log2_kernel(FPU_REG const *arg,
+ Xsig *accum_result, long int *expon);
+
+
+/*--- poly_l2() -------------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ +---------------------------------------------------------------------------*/
+void poly_l2(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
+{
+ long int exponent, expon, expon_expon;
+ Xsig accumulator, expon_accum, yaccum;
+ char sign;
+ FPU_REG x;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ /* From arg, make a number > sqrt(2)/2 and < sqrt(2) */
+ if ( arg->sigh > (unsigned)0xb504f334 )
+ {
+ /* Treat as sqrt(2)/2 < arg < 1 */
+ significand(&x) = - significand(arg);
+ x.sign = SIGN_NEG;
+ x.tag = TW_Valid;
+ x.exp = EXP_BIAS-1;
+ exponent++;
+ normalize(&x);
+ }
+ else
+ {
+ /* Treat as 1 <= arg < sqrt(2) */
+ x.sigh = arg->sigh - 0x80000000;
+ x.sigl = arg->sigl;
+ x.sign = SIGN_POS;
+ x.tag = TW_Valid;
+ x.exp = EXP_BIAS;
+ normalize(&x);
+ }
+
+ if ( x.tag == TW_Zero )
+ {
+ expon = 0;
+ accumulator.msw = accumulator.midw = accumulator.lsw = 0;
+ }
+ else
+ {
+ log2_kernel(&x, &accumulator, &expon);
+ }
+
+ sign = exponent < 0;
+ if ( sign ) exponent = -exponent;
+ expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0;
+ if ( exponent )
+ {
+ expon_expon = 31 + norm_Xsig(&expon_accum);
+ shr_Xsig(&accumulator, expon_expon - expon);
+
+ if ( sign ^ (x.sign == SIGN_NEG) )
+ negate_Xsig(&accumulator);
+ add_Xsig_Xsig(&accumulator, &expon_accum);
+ }
+ else
+ {
+ expon_expon = expon;
+ sign = x.sign;
+ }
+
+ yaccum.lsw = 0; XSIG_LL(yaccum) = significand(y);
+ mul_Xsig_Xsig(&accumulator, &yaccum);
+
+ expon_expon += round_Xsig(&accumulator);
+
+ if ( accumulator.msw == 0 )
+ {
+ reg_move(&CONST_Z, y);
+ }
+ else
+ {
+ result->exp = expon_expon + y->exp + 1;
+ significand(result) = XSIG_LL(accumulator);
+ result->tag = TW_Valid; /* set the tags to Valid */
+ result->sign = sign ^ y->sign;
+ }
+
+ return;
+}
+
+
+/*--- poly_l2p1() -----------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ | log2(x+1) |
+ +---------------------------------------------------------------------------*/
+int poly_l2p1(FPU_REG const *arg, FPU_REG const *y, FPU_REG *result)
+{
+ char sign;
+ long int exponent;
+ Xsig accumulator, yaccum;
+
+
+ sign = arg->sign;
+
+ if ( arg->exp < EXP_BIAS )
+ {
+ log2_kernel(arg, &accumulator, &exponent);
+
+ yaccum.lsw = 0;
+ XSIG_LL(yaccum) = significand(y);
+ mul_Xsig_Xsig(&accumulator, &yaccum);
+
+ exponent += round_Xsig(&accumulator);
+
+ result->exp = exponent + y->exp + 1;
+ significand(result) = XSIG_LL(accumulator);
+ result->tag = TW_Valid; /* set the tags to Valid */
+ result->sign = sign ^ y->sign;
+
+ return 0;
+ }
+ else
+ {
+ /* The magnitude of arg is far too large. */
+ reg_move(y, result);
+ if ( sign != SIGN_POS )
+ {
+ /* Trying to get the log of a negative number. */
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+}
+
+
+
+
+#undef HIPOWER
+#define HIPOWER 10
+static const unsigned long long logterms[HIPOWER] =
+{
+ 0x2a8eca5705fc2ef0LL,
+ 0xf6384ee1d01febceLL,
+ 0x093bb62877cdf642LL,
+ 0x006985d8a9ec439bLL,
+ 0x0005212c4f55a9c8LL,
+ 0x00004326a16927f0LL,
+ 0x0000038d1d80a0e7LL,
+ 0x0000003141cc80c6LL,
+ 0x00000002b1668c9fLL,
+ 0x000000002c7a46aaLL
+};
+
+static const unsigned long leadterm = 0xb8000000;
+
+
+/*--- log2_kernel() ---------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ | log2(x+1) |
+ +---------------------------------------------------------------------------*/
+static void log2_kernel(FPU_REG const *arg, Xsig *accum_result,
+ long int *expon)
+{
+ char sign;
+ long int exponent, adj;
+ unsigned long long Xsq;
+ Xsig accumulator, Numer, Denom, argSignif, arg_signif;
+
+ sign = arg->sign;
+
+ exponent = arg->exp - EXP_BIAS;
+ Numer.lsw = Denom.lsw = 0;
+ XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
+ if ( sign == SIGN_POS )
+ {
+ shr_Xsig(&Denom, 2 - (1 + exponent));
+ Denom.msw |= 0x80000000;
+ div_Xsig(&Numer, &Denom, &argSignif);
+ }
+ else
+ {
+ shr_Xsig(&Denom, 1 - (1 + exponent));
+ negate_Xsig(&Denom);
+ if ( Denom.msw & 0x80000000 )
+ {
+ div_Xsig(&Numer, &Denom, &argSignif);
+ exponent ++;
+ }
+ else
+ {
+ /* Denom must be 1.0 */
+ argSignif.lsw = Numer.lsw; argSignif.midw = Numer.midw;
+ argSignif.msw = Numer.msw;
+ }
+ }
+
+#ifndef PECULIAR_486
+ /* Should check here that |local_arg| is within the valid range */
+ if ( exponent >= -2 )
+ {
+ if ( (exponent > -2) ||
+ (argSignif.msw > (unsigned)0xafb0ccc0) )
+ {
+ /* The argument is too large */
+ }
+ }
+#endif PECULIAR_486
+
+ arg_signif.lsw = argSignif.lsw; XSIG_LL(arg_signif) = XSIG_LL(argSignif);
+ adj = norm_Xsig(&argSignif);
+ accumulator.lsw = argSignif.lsw; XSIG_LL(accumulator) = XSIG_LL(argSignif);
+ mul_Xsig_Xsig(&accumulator, &accumulator);
+ shr_Xsig(&accumulator, 2*(-1 - (1 + exponent + adj)));
+ Xsq = XSIG_LL(accumulator);
+ if ( accumulator.lsw & 0x80000000 )
+ Xsq++;
+
+ accumulator.msw = accumulator.midw = accumulator.lsw = 0;
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER-1);
+
+ mul_Xsig_Xsig(&accumulator, &argSignif);
+ shr_Xsig(&accumulator, 6 - adj);
+
+ mul32_Xsig(&arg_signif, leadterm);
+ add_two_Xsig(&accumulator, &arg_signif, &exponent);
+
+ *expon = exponent + 1;
+ accum_result->lsw = accumulator.lsw;
+ accum_result->midw = accumulator.midw;
+ accum_result->msw = accumulator.msw;
+
+}
diff --git a/arch/i386/math-emu/poly_sin.c b/arch/i386/math-emu/poly_sin.c
new file mode 100644
index 000000000..03db5b6aa
--- /dev/null
+++ b/arch/i386/math-emu/poly_sin.c
@@ -0,0 +1,408 @@
+/*---------------------------------------------------------------------------+
+ | poly_sin.c |
+ | |
+ | Computation of an approximation of the sin function and the cosine |
+ | function by a polynomial. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "poly.h"
+
+
+#define N_COEFF_P 4
+#define N_COEFF_N 4
+
+static const unsigned long long pos_terms_l[N_COEFF_P] =
+{
+ 0xaaaaaaaaaaaaaaabLL,
+ 0x00d00d00d00cf906LL,
+ 0x000006b99159a8bbLL,
+ 0x000000000d7392e6LL
+};
+
+static const unsigned long long neg_terms_l[N_COEFF_N] =
+{
+ 0x2222222222222167LL,
+ 0x0002e3bc74aab624LL,
+ 0x0000000b09229062LL,
+ 0x00000000000c7973LL
+};
+
+
+
+#define N_COEFF_PH 4
+#define N_COEFF_NH 4
+static const unsigned long long pos_terms_h[N_COEFF_PH] =
+{
+ 0x0000000000000000LL,
+ 0x05b05b05b05b0406LL,
+ 0x000049f93edd91a9LL,
+ 0x00000000c9c9ed62LL
+};
+
+static const unsigned long long neg_terms_h[N_COEFF_NH] =
+{
+ 0xaaaaaaaaaaaaaa98LL,
+ 0x001a01a01a019064LL,
+ 0x0000008f76c68a77LL,
+ 0x0000000000d58f5eLL
+};
+
+
+/*--- poly_sine() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_sine(FPU_REG const *arg, FPU_REG *result)
+{
+ int exponent, echange;
+ Xsig accumulator, argSqrd, argTo4;
+ unsigned long fix_up, adj;
+ unsigned long long fixed_arg;
+
+
+#ifdef PARANOID
+ if ( arg->tag == TW_Zero )
+ {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return;
+ }
+#endif PARANOID
+
+ exponent = arg->exp - EXP_BIAS;
+
+ accumulator.lsw = accumulator.midw = accumulator.msw = 0;
+
+ /* Split into two ranges, for arguments below and above 1.0 */
+ /* The boundary between upper and lower is approx 0.88309101259 */
+ if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) )
+ {
+ /* The argument is <= 0.88309101259 */
+
+ argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0;
+ mul64_Xsig(&argSqrd, &significand(arg));
+ shr_Xsig(&argSqrd, 2*(-1-exponent));
+ argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
+ argTo4.lsw = argSqrd.lsw;
+ mul_Xsig_Xsig(&argTo4, &argTo4);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l,
+ N_COEFF_N-1);
+ mul_Xsig_Xsig(&accumulator, &argSqrd);
+ negate_Xsig(&accumulator);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l,
+ N_COEFF_P-1);
+
+ shr_Xsig(&accumulator, 2); /* Divide by four */
+ accumulator.msw |= 0x80000000; /* Add 1.0 */
+
+ mul64_Xsig(&accumulator, &significand(arg));
+ mul64_Xsig(&accumulator, &significand(arg));
+ mul64_Xsig(&accumulator, &significand(arg));
+
+ /* Divide by four, FPU_REG compatible, etc */
+ exponent = 3*exponent + EXP_BIAS;
+
+ /* The minimum exponent difference is 3 */
+ shr_Xsig(&accumulator, arg->exp - exponent);
+
+ negate_Xsig(&accumulator);
+ XSIG_LL(accumulator) += significand(arg);
+
+ echange = round_Xsig(&accumulator);
+
+ result->exp = arg->exp + echange;
+ }
+ else
+ {
+ /* The argument is > 0.88309101259 */
+ /* We use sin(arg) = cos(pi/2-arg) */
+
+ fixed_arg = significand(arg);
+
+ if ( exponent == 0 )
+ {
+ /* The argument is >= 1.0 */
+
+ /* Put the binary point at the left. */
+ fixed_arg <<= 1;
+ }
+ /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
+ fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+
+ XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
+ mul64_Xsig(&argSqrd, &fixed_arg);
+
+ XSIG_LL(argTo4) = XSIG_LL(argSqrd); argTo4.lsw = argSqrd.lsw;
+ mul_Xsig_Xsig(&argTo4, &argTo4);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h,
+ N_COEFF_NH-1);
+ mul_Xsig_Xsig(&accumulator, &argSqrd);
+ negate_Xsig(&accumulator);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h,
+ N_COEFF_PH-1);
+ negate_Xsig(&accumulator);
+
+ mul64_Xsig(&accumulator, &fixed_arg);
+ mul64_Xsig(&accumulator, &fixed_arg);
+
+ shr_Xsig(&accumulator, 3);
+ negate_Xsig(&accumulator);
+
+ add_Xsig_Xsig(&accumulator, &argSqrd);
+
+ shr_Xsig(&accumulator, 1);
+
+ accumulator.lsw |= 1; /* A zero accumulator here would cause problems */
+ negate_Xsig(&accumulator);
+
+ /* The basic computation is complete. Now fix the answer to
+ compensate for the error due to the approximation used for
+ pi/2
+ */
+
+ /* This has an exponent of -65 */
+ fix_up = 0x898cc517;
+ /* The fix-up needs to be improved for larger args */
+ if ( argSqrd.msw & 0xffc00000 )
+ {
+ /* Get about 32 bit precision in these: */
+ mul_32_32(0x898cc517, argSqrd.msw, &adj);
+ fix_up -= adj/6;
+ }
+ mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up);
+
+ adj = accumulator.lsw; /* temp save */
+ accumulator.lsw -= fix_up;
+ if ( accumulator.lsw > adj )
+ XSIG_LL(accumulator) --;
+
+ echange = round_Xsig(&accumulator);
+
+ result->exp = EXP_BIAS - 1 + echange;
+ }
+
+ significand(result) = XSIG_LL(accumulator);
+ result->tag = TW_Valid;
+ result->sign = arg->sign;
+
+#ifdef PARANOID
+ if ( (result->exp >= EXP_BIAS)
+ && (significand(result) > 0x8000000000000000LL) )
+ {
+ EXCEPTION(EX_INTERNAL|0x150);
+ }
+#endif PARANOID
+
+}
+
+
+
+/*--- poly_cos() ------------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_cos(FPU_REG const *arg, FPU_REG *result)
+{
+ long int exponent, exp2, echange;
+ Xsig accumulator, argSqrd, fix_up, argTo4;
+ unsigned long adj;
+ unsigned long long fixed_arg;
+
+
+#ifdef PARANOID
+ if ( arg->tag == TW_Zero )
+ {
+ /* Return 1.0 */
+ reg_move(&CONST_1, result);
+ return;
+ }
+
+ if ( (arg->exp > EXP_BIAS)
+ || ((arg->exp == EXP_BIAS)
+ && (significand(arg) > 0xc90fdaa22168c234LL)) )
+ {
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+#endif PARANOID
+
+ exponent = arg->exp - EXP_BIAS;
+
+ accumulator.lsw = accumulator.midw = accumulator.msw = 0;
+
+ if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) )
+ {
+ /* arg is < 0.687705 */
+
+ argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0;
+ mul64_Xsig(&argSqrd, &significand(arg));
+
+ if ( exponent < -1 )
+ {
+ /* shift the argument right by the required places */
+ shr_Xsig(&argSqrd, 2*(-1-exponent));
+ }
+
+ argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
+ argTo4.lsw = argSqrd.lsw;
+ mul_Xsig_Xsig(&argTo4, &argTo4);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h,
+ N_COEFF_NH-1);
+ mul_Xsig_Xsig(&accumulator, &argSqrd);
+ negate_Xsig(&accumulator);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h,
+ N_COEFF_PH-1);
+ negate_Xsig(&accumulator);
+
+ mul64_Xsig(&accumulator, &significand(arg));
+ mul64_Xsig(&accumulator, &significand(arg));
+ shr_Xsig(&accumulator, -2*(1+exponent));
+
+ shr_Xsig(&accumulator, 3);
+ negate_Xsig(&accumulator);
+
+ add_Xsig_Xsig(&accumulator, &argSqrd);
+
+ shr_Xsig(&accumulator, 1);
+
+ /* It doesn't matter if accumulator is all zero here, the
+ following code will work ok */
+ negate_Xsig(&accumulator);
+
+ if ( accumulator.lsw & 0x80000000 )
+ XSIG_LL(accumulator) ++;
+ if ( accumulator.msw == 0 )
+ {
+ /* The result is 1.0 */
+ reg_move(&CONST_1, result);
+ }
+ else
+ {
+ significand(result) = XSIG_LL(accumulator);
+
+ /* will be a valid positive nr with expon = -1 */
+ *(short *)&(result->sign) = 0;
+ result->exp = EXP_BIAS - 1;
+ }
+ }
+ else
+ {
+ fixed_arg = significand(arg);
+
+ if ( exponent == 0 )
+ {
+ /* The argument is >= 1.0 */
+
+ /* Put the binary point at the left. */
+ fixed_arg <<= 1;
+ }
+ /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
+ fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+
+ exponent = -1;
+ exp2 = -1;
+
+ /* A shift is needed here only for a narrow range of arguments,
+ i.e. for fixed_arg approx 2^-32, but we pick up more... */
+ if ( !(LL_MSW(fixed_arg) & 0xffff0000) )
+ {
+ fixed_arg <<= 16;
+ exponent -= 16;
+ exp2 -= 16;
+ }
+
+ XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
+ mul64_Xsig(&argSqrd, &fixed_arg);
+
+ if ( exponent < -1 )
+ {
+ /* shift the argument right by the required places */
+ shr_Xsig(&argSqrd, 2*(-1-exponent));
+ }
+
+ argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw;
+ argTo4.lsw = argSqrd.lsw;
+ mul_Xsig_Xsig(&argTo4, &argTo4);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l,
+ N_COEFF_N-1);
+ mul_Xsig_Xsig(&accumulator, &argSqrd);
+ negate_Xsig(&accumulator);
+
+ polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l,
+ N_COEFF_P-1);
+
+ shr_Xsig(&accumulator, 2); /* Divide by four */
+ accumulator.msw |= 0x80000000; /* Add 1.0 */
+
+ mul64_Xsig(&accumulator, &fixed_arg);
+ mul64_Xsig(&accumulator, &fixed_arg);
+ mul64_Xsig(&accumulator, &fixed_arg);
+
+ /* Divide by four, FPU_REG compatible, etc */
+ exponent = 3*exponent;
+
+ /* The minimum exponent difference is 3 */
+ shr_Xsig(&accumulator, exp2 - exponent);
+
+ negate_Xsig(&accumulator);
+ XSIG_LL(accumulator) += fixed_arg;
+
+ /* The basic computation is complete. Now fix the answer to
+ compensate for the error due to the approximation used for
+ pi/2
+ */
+
+ /* This has an exponent of -65 */
+ XSIG_LL(fix_up) = 0x898cc51701b839a2ll;
+ fix_up.lsw = 0;
+
+ /* The fix-up needs to be improved for larger args */
+ if ( argSqrd.msw & 0xffc00000 )
+ {
+ /* Get about 32 bit precision in these: */
+ mul_32_32(0x898cc517, argSqrd.msw, &adj);
+ fix_up.msw -= adj/2;
+ mul_32_32(0x898cc517, argTo4.msw, &adj);
+ fix_up.msw += adj/24;
+ }
+
+ exp2 += norm_Xsig(&accumulator);
+ shr_Xsig(&accumulator, 1); /* Prevent overflow */
+ exp2++;
+ shr_Xsig(&fix_up, 65 + exp2);
+
+ add_Xsig_Xsig(&accumulator, &fix_up);
+
+ echange = round_Xsig(&accumulator);
+
+ result->exp = exp2 + EXP_BIAS + echange;
+ *(short *)&(result->sign) = 0; /* Is a valid positive nr */
+ significand(result) = XSIG_LL(accumulator);
+ }
+
+#ifdef PARANOID
+ if ( (result->exp >= EXP_BIAS)
+ && (significand(result) > 0x8000000000000000LL) )
+ {
+ EXCEPTION(EX_INTERNAL|0x151);
+ }
+#endif PARANOID
+
+}
diff --git a/arch/i386/math-emu/poly_tan.c b/arch/i386/math-emu/poly_tan.c
new file mode 100644
index 000000000..d9b09e438
--- /dev/null
+++ b/arch/i386/math-emu/poly_tan.c
@@ -0,0 +1,213 @@
+/*---------------------------------------------------------------------------+
+ | poly_tan.c |
+ | |
+ | Compute the tan of a FPU_REG, using a polynomial approximation. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "poly.h"
+
+
+#define HiPOWERop 3 /* odd poly, positive terms */
+static const unsigned long long oddplterm[HiPOWERop] =
+{
+ 0x0000000000000000LL,
+ 0x0051a1cf08fca228LL,
+ 0x0000000071284ff7LL
+};
+
+#define HiPOWERon 2 /* odd poly, negative terms */
+static const unsigned long long oddnegterm[HiPOWERon] =
+{
+ 0x1291a9a184244e80LL,
+ 0x0000583245819c21LL
+};
+
+#define HiPOWERep 2 /* even poly, positive terms */
+static const unsigned long long evenplterm[HiPOWERep] =
+{
+ 0x0e848884b539e888LL,
+ 0x00003c7f18b887daLL
+};
+
+#define HiPOWERen 2 /* even poly, negative terms */
+static const unsigned long long evennegterm[HiPOWERen] =
+{
+ 0xf1f0200fd51569ccLL,
+ 0x003afb46105c4432LL
+};
+
+static const unsigned long long twothirds = 0xaaaaaaaaaaaaaaabLL;
+
+
+/*--- poly_tan() ------------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_tan(FPU_REG const *arg, FPU_REG *result)
+{
+ long int exponent;
+ int invert;
+ Xsig argSq, argSqSq, accumulatoro, accumulatore, accum,
+ argSignif, fix_up;
+ unsigned long adj;
+
+ exponent = arg->exp - EXP_BIAS;
+
+#ifdef PARANOID
+ if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
+ { arith_invalid(result); return; } /* Need a positive number */
+#endif PARANOID
+
+ /* Split the problem into two domains, smaller and larger than pi/4 */
+ if ( (exponent == 0) || ((exponent == -1) && (arg->sigh > 0xc90fdaa2)) )
+ {
+ /* The argument is greater than (approx) pi/4 */
+ invert = 1;
+ accum.lsw = 0;
+ XSIG_LL(accum) = significand(arg);
+
+ if ( exponent == 0 )
+ {
+ /* The argument is >= 1.0 */
+ /* Put the binary point at the left. */
+ XSIG_LL(accum) <<= 1;
+ }
+ /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
+ XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum);
+
+ argSignif.lsw = accum.lsw;
+ XSIG_LL(argSignif) = XSIG_LL(accum);
+ exponent = -1 + norm_Xsig(&argSignif);
+ }
+ else
+ {
+ invert = 0;
+ argSignif.lsw = 0;
+ XSIG_LL(accum) = XSIG_LL(argSignif) = significand(arg);
+
+ if ( exponent < -1 )
+ {
+ /* shift the argument right by the required places */
+ if ( shrx(&XSIG_LL(accum), -1-exponent) >= 0x80000000U )
+ XSIG_LL(accum) ++; /* round up */
+ }
+ }
+
+ XSIG_LL(argSq) = XSIG_LL(accum); argSq.lsw = accum.lsw;
+ mul_Xsig_Xsig(&argSq, &argSq);
+ XSIG_LL(argSqSq) = XSIG_LL(argSq); argSqSq.lsw = argSq.lsw;
+ mul_Xsig_Xsig(&argSqSq, &argSqSq);
+
+ /* Compute the negative terms for the numerator polynomial */
+ accumulatoro.msw = accumulatoro.midw = accumulatoro.lsw = 0;
+ polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddnegterm, HiPOWERon-1);
+ mul_Xsig_Xsig(&accumulatoro, &argSq);
+ negate_Xsig(&accumulatoro);
+ /* Add the positive terms */
+ polynomial_Xsig(&accumulatoro, &XSIG_LL(argSqSq), oddplterm, HiPOWERop-1);
+
+
+ /* Compute the positive terms for the denominator polynomial */
+ accumulatore.msw = accumulatore.midw = accumulatore.lsw = 0;
+ polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evenplterm, HiPOWERep-1);
+ mul_Xsig_Xsig(&accumulatore, &argSq);
+ negate_Xsig(&accumulatore);
+ /* Add the negative terms */
+ polynomial_Xsig(&accumulatore, &XSIG_LL(argSqSq), evennegterm, HiPOWERen-1);
+ /* Multiply by arg^2 */
+ mul64_Xsig(&accumulatore, &XSIG_LL(argSignif));
+ mul64_Xsig(&accumulatore, &XSIG_LL(argSignif));
+ /* de-normalize and divide by 2 */
+ shr_Xsig(&accumulatore, -2*(1+exponent) + 1);
+ negate_Xsig(&accumulatore); /* This does 1 - accumulator */
+
+ /* Now find the ratio. */
+ if ( accumulatore.msw == 0 )
+ {
+ /* accumulatoro must contain 1.0 here, (actually, 0) but it
+ really doesn't matter what value we use because it will
+ have negligible effect in later calculations
+ */
+ XSIG_LL(accum) = 0x8000000000000000LL;
+ accum.lsw = 0;
+ }
+ else
+ {
+ div_Xsig(&accumulatoro, &accumulatore, &accum);
+ }
+
+ /* Multiply by 1/3 * arg^3 */
+ mul64_Xsig(&accum, &XSIG_LL(argSignif));
+ mul64_Xsig(&accum, &XSIG_LL(argSignif));
+ mul64_Xsig(&accum, &XSIG_LL(argSignif));
+ mul64_Xsig(&accum, &twothirds);
+ shr_Xsig(&accum, -2*(exponent+1));
+
+ /* tan(arg) = arg + accum */
+ add_two_Xsig(&accum, &argSignif, &exponent);
+
+ if ( invert )
+ {
+ /* We now have the value of tan(pi_2 - arg) where pi_2 is an
+ approximation for pi/2
+ */
+ /* The next step is to fix the answer to compensate for the
+ error due to the approximation used for pi/2
+ */
+
+ /* This is (approx) delta, the error in our approx for pi/2
+ (see above). It has an exponent of -65
+ */
+ XSIG_LL(fix_up) = 0x898cc51701b839a2LL;
+ fix_up.lsw = 0;
+
+ if ( exponent == 0 )
+ adj = 0xffffffff; /* We want approx 1.0 here, but
+ this is close enough. */
+ else if ( exponent > -30 )
+ {
+ adj = accum.msw >> -(exponent+1); /* tan */
+ mul_32_32(adj, adj, &adj); /* tan^2 */
+ }
+ else
+ adj = 0;
+ mul_32_32(0x898cc517, adj, &adj); /* delta * tan^2 */
+
+ fix_up.msw += adj;
+ if ( !(fix_up.msw & 0x80000000) ) /* did fix_up overflow ? */
+ {
+ /* Yes, we need to add an msb */
+ shr_Xsig(&fix_up, 1);
+ fix_up.msw |= 0x80000000;
+ shr_Xsig(&fix_up, 64 + exponent);
+ }
+ else
+ shr_Xsig(&fix_up, 65 + exponent);
+
+ add_two_Xsig(&accum, &fix_up, &exponent);
+
+ /* accum now contains tan(pi/2 - arg).
+ Use tan(arg) = 1.0 / tan(pi/2 - arg)
+ */
+ accumulatoro.lsw = accumulatoro.midw = 0;
+ accumulatoro.msw = 0x80000000;
+ div_Xsig(&accumulatoro, &accum, &accum);
+ exponent = - exponent - 1;
+ }
+
+ /* Transfer the result */
+ round_Xsig(&accum);
+ *(short *)&(result->sign) = 0;
+ significand(result) = XSIG_LL(accum);
+ result->exp = EXP_BIAS + exponent;
+
+}
diff --git a/arch/i386/math-emu/polynom_Xsig.S b/arch/i386/math-emu/polynom_Xsig.S
new file mode 100644
index 000000000..585221f96
--- /dev/null
+++ b/arch/i386/math-emu/polynom_Xsig.S
@@ -0,0 +1,137 @@
+/*---------------------------------------------------------------------------+
+ | polynomial_Xsig.S |
+ | |
+ | Fixed point arithmetic polynomial evaluation. |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void polynomial_Xsig(Xsig *accum, unsigned long long x, |
+ | unsigned long long terms[], int n) |
+ | |
+ | Computes: |
+ | terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x |
+ | and adds the result to the 12 byte Xsig. |
+ | The terms[] are each 8 bytes, but all computation is performed to 12 byte |
+ | precision. |
+ | |
+ | This function must be used carefully: most overflow of intermediate |
+ | results is controlled, but overflow of the result is not. |
+ | |
+ +---------------------------------------------------------------------------*/
+ .file "polynomial_Xsig.S"
+
+#include "fpu_asm.h"
+
+
+#define TERM_SIZE $8
+#define SUM_MS -20(%ebp) /* sum ms long */
+#define SUM_MIDDLE -24(%ebp) /* sum middle long */
+#define SUM_LS -28(%ebp) /* sum ls long */
+#define ACCUM_MS -4(%ebp) /* accum ms long */
+#define ACCUM_MIDDLE -8(%ebp) /* accum middle long */
+#define ACCUM_LS -12(%ebp) /* accum ls long */
+#define OVERFLOWED -16(%ebp) /* addition overflow flag */
+
+.text
+ .align 2,144
+.globl _polynomial_Xsig
+_polynomial_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $32,%esp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM2,%esi /* x */
+ movl PARAM3,%edi /* terms */
+
+ movl TERM_SIZE,%eax
+ mull PARAM4 /* n */
+ addl %eax,%edi
+
+ movl 4(%edi),%edx /* terms[n] */
+ movl %edx,SUM_MS
+ movl (%edi),%edx /* terms[n] */
+ movl %edx,SUM_MIDDLE
+ xor %eax,%eax
+ movl %eax,SUM_LS
+ movb %al,OVERFLOWED
+
+ subl TERM_SIZE,%edi
+ decl PARAM4
+ js L_accum_done
+
+L_accum_loop:
+ xor %eax,%eax
+ movl %eax,ACCUM_MS
+ movl %eax,ACCUM_MIDDLE
+
+ movl SUM_MIDDLE,%eax
+ mull (%esi) /* x ls long */
+ movl %edx,ACCUM_LS
+
+ movl SUM_MIDDLE,%eax
+ mull 4(%esi) /* x ms long */
+ addl %eax,ACCUM_LS
+ adcl %edx,ACCUM_MIDDLE
+ adcl $0,ACCUM_MS
+
+ movl SUM_MS,%eax
+ mull (%esi) /* x ls long */
+ addl %eax,ACCUM_LS
+ adcl %edx,ACCUM_MIDDLE
+ adcl $0,ACCUM_MS
+
+ movl SUM_MS,%eax
+ mull 4(%esi) /* x ms long */
+ addl %eax,ACCUM_MIDDLE
+ adcl %edx,ACCUM_MS
+
+ testb $0xff,OVERFLOWED
+ jz L_no_overflow
+
+ movl (%esi),%eax
+ addl %eax,ACCUM_MIDDLE
+ movl 4(%esi),%eax
+ adcl %eax,ACCUM_MS /* This could overflow too */
+
+L_no_overflow:
+
+/*
+ * Now put the sum of next term and the accumulator
+ * into the sum register
+ */
+ movl ACCUM_LS,%eax
+ addl (%edi),%eax /* term ls long */
+ movl %eax,SUM_LS
+ movl ACCUM_MIDDLE,%eax
+ adcl (%edi),%eax /* term ls long */
+ movl %eax,SUM_MIDDLE
+ movl ACCUM_MS,%eax
+ adcl 4(%edi),%eax /* term ms long */
+ movl %eax,SUM_MS
+ sbbb %al,%al
+ movb %al,OVERFLOWED /* Used in the next iteration */
+
+ subl TERM_SIZE,%edi
+ decl PARAM4
+ jns L_accum_loop
+
+L_accum_done:
+ movl PARAM1,%edi /* accum */
+ movl SUM_LS,%eax
+ addl %eax,(%edi)
+ movl SUM_MIDDLE,%eax
+ adcl %eax,4(%edi)
+ movl SUM_MS,%eax
+ adcl %eax,8(%edi)
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/arch/i386/math-emu/reg_add_sub.c b/arch/i386/math-emu/reg_add_sub.c
new file mode 100644
index 000000000..d70889b40
--- /dev/null
+++ b/arch/i386/math-emu/reg_add_sub.c
@@ -0,0 +1,318 @@
+/*---------------------------------------------------------------------------+
+ | reg_add_sub.c |
+ | |
+ | Functions to add or subtract two registers and put the result in a third. |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | For each function, the destination may be any FPU_REG, including one of |
+ | the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "fpu_system.h"
+
+
+int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
+{
+ char saved_sign = dest->sign;
+ int diff;
+
+ if ( !(a->tag | b->tag) )
+ {
+ /* Both registers are valid */
+ if (!(a->sign ^ b->sign))
+ {
+ /* signs are the same */
+ dest->sign = a->sign;
+ if ( reg_u_add(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ return 0;
+ }
+
+ /* The signs are different, so do a subtraction */
+ diff = a->exp - b->exp;
+ if (!diff)
+ {
+ diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
+ if (!diff)
+ {
+ diff = a->sigl > b->sigl;
+ if (!diff)
+ diff = -(a->sigl < b->sigl);
+ }
+ }
+
+ if (diff > 0)
+ {
+ dest->sign = a->sign;
+ if ( reg_u_sub(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ }
+ else if ( diff == 0 )
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(&CONST_Z, dest);
+ /* sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ else
+ {
+ dest->sign = b->sign;
+ if ( reg_u_sub(b, a, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ }
+ return 0;
+ }
+ else
+ {
+ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
+ { return real_2op_NaN(a, b, dest); }
+ else if (a->tag == TW_Zero)
+ {
+ if (b->tag == TW_Zero)
+ {
+ char different_signs = a->sign ^ b->sign;
+ /* Both are zero, result will be zero. */
+ reg_move(a, dest);
+ if (different_signs)
+ {
+ /* Signs are different. */
+ /* Sign of answer depends upon rounding mode. */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ }
+ else
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(b, dest);
+ }
+ return 0;
+ }
+ else if (b->tag == TW_Zero)
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(a, dest); return 0;
+ }
+ else if (a->tag == TW_Infinity)
+ {
+ if (b->tag != TW_Infinity)
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(a, dest); return 0;
+ }
+ if (a->sign == b->sign)
+ {
+ /* They are both + or - infinity */
+ reg_move(a, dest); return 0;
+ }
+ return arith_invalid(dest); /* Infinity-Infinity is undefined. */
+ }
+ else if (b->tag == TW_Infinity)
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(b, dest); return 0;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x101);
+#endif
+ return 1;
+}
+
+
+/* Subtract b from a. (a-b) -> dest */
+int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
+{
+ char saved_sign = dest->sign;
+ int diff;
+
+ if ( !(a->tag | b->tag) )
+ {
+ /* Both registers are valid */
+ diff = a->exp - b->exp;
+ if (!diff)
+ {
+ diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
+ if (!diff)
+ {
+ diff = a->sigl > b->sigl;
+ if (!diff)
+ diff = -(a->sigl < b->sigl);
+ }
+ }
+
+ switch (a->sign*2 + b->sign)
+ {
+ case 0: /* P - P */
+ case 3: /* N - N */
+ if (diff > 0)
+ {
+ /* |a| > |b| */
+ dest->sign = a->sign;
+ if ( reg_u_sub(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ return 0;
+ }
+ else if ( diff == 0 )
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(&CONST_Z, dest);
+ /* sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ else
+ {
+ dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
+ if ( reg_u_sub(b, a, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ }
+ break;
+ case 1: /* P - N */
+ dest->sign = SIGN_POS;
+ if ( reg_u_add(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ break;
+ case 2: /* N - P */
+ dest->sign = SIGN_NEG;
+ if ( reg_u_add(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+ }
+ else
+ {
+ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
+ { return real_2op_NaN(b, a, dest); }
+ else if (b->tag == TW_Zero)
+ {
+ if (a->tag == TW_Zero)
+ {
+ char same_signs = !(a->sign ^ b->sign);
+ /* Both are zero, result will be zero. */
+ reg_move(a, dest); /* Answer for different signs. */
+ if (same_signs)
+ {
+ /* Sign depends upon rounding mode */
+ dest->sign = ((control_w & CW_RC) != RC_DOWN)
+ ? SIGN_POS : SIGN_NEG;
+ }
+ }
+ else
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(a, dest);
+ }
+ return 0;
+ }
+ else if (a->tag == TW_Zero)
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS^SIGN_NEG;
+ return 0;
+ }
+ else if (a->tag == TW_Infinity)
+ {
+ if (b->tag != TW_Infinity)
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(a, dest); return 0;
+ }
+ /* Both args are Infinity */
+ if (a->sign == b->sign)
+ {
+ /* Infinity-Infinity is undefined. */
+ return arith_invalid(dest);
+ }
+ reg_move(a, dest);
+ return 0;
+ }
+ else if (b->tag == TW_Infinity)
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS^SIGN_NEG;
+ return 0;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x110);
+#endif
+ return 1;
+}
+
diff --git a/arch/i386/math-emu/reg_compare.c b/arch/i386/math-emu/reg_compare.c
new file mode 100644
index 000000000..eb4a1fa99
--- /dev/null
+++ b/arch/i386/math-emu/reg_compare.c
@@ -0,0 +1,378 @@
+/*---------------------------------------------------------------------------+
+ | reg_compare.c |
+ | |
+ | Compare two floating point registers |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | compare() is the core FPU_REG comparison function |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+int compare(FPU_REG const *b)
+{
+ int diff;
+ char st0_tag;
+ FPU_REG *st0_ptr;
+
+ st0_ptr = &st(0);
+ st0_tag = st0_ptr->tag;
+
+ if ( st0_tag | b->tag )
+ {
+ if ( st0_tag == TW_Zero )
+ {
+ if ( b->tag == TW_Zero ) return COMP_A_eq_B;
+ if ( b->tag == TW_Valid )
+ {
+ return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+#ifdef DENORM_OPERAND
+ | ((b->exp <= EXP_UNDER) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+ }
+ else if ( b->tag == TW_Zero )
+ {
+ if ( st0_tag == TW_Valid )
+ {
+ return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
+ : COMP_A_lt_B)
+#ifdef DENORM_OPERAND
+ | ((st0_ptr->exp <= EXP_UNDER )
+ ? COMP_Denormal : 0 )
+#endif DENORM_OPERAND
+ ;
+ }
+ }
+
+ if ( st0_tag == TW_Infinity )
+ {
+ if ( (b->tag == TW_Valid) || (b->tag == TW_Zero) )
+ {
+ return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B
+ : COMP_A_lt_B)
+#ifdef DENORM_OPERAND
+ | (((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0 )
+#endif DENORM_OPERAND
+;
+ }
+ else if ( b->tag == TW_Infinity )
+ {
+ /* The 80486 book says that infinities can be equal! */
+ return (st0_ptr->sign == b->sign) ? COMP_A_eq_B :
+ ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
+ }
+ /* Fall through to the NaN code */
+ }
+ else if ( b->tag == TW_Infinity )
+ {
+ if ( (st0_tag == TW_Valid) || (st0_tag == TW_Zero) )
+ {
+ return ((b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+#ifdef DENORM_OPERAND
+ | (((st0_tag == TW_Valid)
+ && (st0_ptr->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+ /* Fall through to the NaN code */
+ }
+
+ /* The only possibility now should be that one of the arguments
+ is a NaN */
+ if ( (st0_tag == TW_NaN) || (b->tag == TW_NaN) )
+ {
+ if ( ((st0_tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000))
+ || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
+ /* At least one arg is a signaling NaN */
+ return COMP_No_Comp | COMP_SNaN | COMP_NaN;
+ else
+ /* Neither is a signaling NaN */
+ return COMP_No_Comp | COMP_NaN;
+ }
+
+ EXCEPTION(EX_Invalid);
+ }
+
+#ifdef PARANOID
+ if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
+ if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
+#endif PARANOID
+
+
+ if (st0_ptr->sign != b->sign)
+ {
+ return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+#ifdef DENORM_OPERAND
+ |
+ ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+
+ diff = st0_ptr->exp - b->exp;
+ if ( diff == 0 )
+ {
+ diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
+ identical */
+ if ( diff == 0 )
+ {
+ diff = st0_ptr->sigl > b->sigl;
+ if ( diff == 0 )
+ diff = -(st0_ptr->sigl < b->sigl);
+ }
+ }
+
+ if ( diff > 0 )
+ {
+ return ((st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
+#ifdef DENORM_OPERAND
+ |
+ ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+ if ( diff < 0 )
+ {
+ return ((st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
+#ifdef DENORM_OPERAND
+ |
+ ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+ }
+
+ return COMP_A_eq_B
+#ifdef DENORM_OPERAND
+ |
+ ( ((st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) ?
+ COMP_Denormal : 0)
+#endif DENORM_OPERAND
+ ;
+
+}
+
+
+/* This function requires that st(0) is not empty */
+int compare_st_data(FPU_REG const *loaded_data)
+{
+ int f, c;
+
+ c = compare(loaded_data);
+
+ if (c & COMP_NaN)
+ {
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ }
+ else
+ switch (c & 7)
+ {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x121);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif PARANOID
+ }
+ setcc(f);
+ if (c & COMP_Denormal)
+ {
+ return denormal_operand();
+ }
+ return 0;
+}
+
+
+static int compare_st_st(int nr)
+{
+ int f, c;
+
+ if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
+ {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return !(control_word & CW_Invalid);
+ }
+
+ c = compare(&st(nr));
+ if (c & COMP_NaN)
+ {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ EXCEPTION(EX_Invalid);
+ return !(control_word & CW_Invalid);
+ }
+ else
+ switch (c & 7)
+ {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x122);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif PARANOID
+ }
+ setcc(f);
+ if (c & COMP_Denormal)
+ {
+ return denormal_operand();
+ }
+ return 0;
+}
+
+
+static int compare_u_st_st(int nr)
+{
+ int f, c;
+
+ if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
+ {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ /* Stack fault */
+ EXCEPTION(EX_StackUnder);
+ return !(control_word & CW_Invalid);
+ }
+
+ c = compare(&st(nr));
+ if (c & COMP_NaN)
+ {
+ setcc(SW_C3 | SW_C2 | SW_C0);
+ if (c & COMP_SNaN) /* This is the only difference between
+ un-ordered and ordinary comparisons */
+ {
+ EXCEPTION(EX_Invalid);
+ return !(control_word & CW_Invalid);
+ }
+ return 0;
+ }
+ else
+ switch (c & 7)
+ {
+ case COMP_A_lt_B:
+ f = SW_C0;
+ break;
+ case COMP_A_eq_B:
+ f = SW_C3;
+ break;
+ case COMP_A_gt_B:
+ f = 0;
+ break;
+ case COMP_No_Comp:
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x123);
+ f = SW_C3 | SW_C2 | SW_C0;
+ break;
+#endif PARANOID
+ }
+ setcc(f);
+ if (c & COMP_Denormal)
+ {
+ return denormal_operand();
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+void fcom_st()
+{
+ /* fcom st(i) */
+ compare_st_st(FPU_rm);
+}
+
+
+void fcompst()
+{
+ /* fcomp st(i) */
+ if ( !compare_st_st(FPU_rm) )
+ pop();
+}
+
+
+void fcompp()
+{
+ /* fcompp */
+ if (FPU_rm != 1)
+ {
+ FPU_illegal();
+ return;
+ }
+ if ( !compare_st_st(1) )
+ poppop();
+}
+
+
+void fucom_()
+{
+ /* fucom st(i) */
+ compare_u_st_st(FPU_rm);
+
+}
+
+
+void fucomp()
+{
+ /* fucomp st(i) */
+ if ( !compare_u_st_st(FPU_rm) )
+ pop();
+}
+
+
+void fucompp()
+{
+ /* fucompp */
+ if (FPU_rm == 1)
+ {
+ if ( !compare_u_st_st(1) )
+ poppop();
+ }
+ else
+ FPU_illegal();
+}
diff --git a/arch/i386/math-emu/reg_constant.c b/arch/i386/math-emu/reg_constant.c
new file mode 100644
index 000000000..c1981ce24
--- /dev/null
+++ b/arch/i386/math-emu/reg_constant.c
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------+
+ | reg_constant.c |
+ | |
+ | All of the constant FPU_REGs |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+FPU_REG const CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x00000000, 0x80000000 };
+FPU_REG const CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0x00000000, 0x80000000 };
+FPU_REG const CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0x00000000, 0x80000000 };
+FPU_REG const CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0xcd1b8afe, 0xd49a784b };
+FPU_REG const CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x5c17f0bc, 0xb8aa3b29 };
+FPU_REG const CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0x2168c235, 0xc90fdaa2 };
+FPU_REG const CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x2168c235, 0xc90fdaa2 };
+FPU_REG const CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0x2168c235, 0xc90fdaa2 };
+FPU_REG const CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
+ 0xfbcff799, 0x9a209a84 };
+FPU_REG const CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0xd1cf79ac, 0xb17217f7 };
+
+/* Extra bits to take pi/2 to more than 128 bits precision. */
+FPU_REG const CONST_PI2extra = { SIGN_NEG, TW_Valid, EXP_BIAS-66,
+ 0xfc8f8cbb, 0xece675d1 };
+
+/* Only the sign (and tag) is used in internal zeroes */
+FPU_REG const CONST_Z = { SIGN_POS, TW_Zero, EXP_UNDER, 0x0, 0x0 };
+
+/* Only the sign and significand (and tag) are used in internal NaNs */
+/* The 80486 never generates one of these
+FPU_REG const CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+ */
+/* This is the real indefinite QNaN */
+FPU_REG const CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
+
+/* Only the sign (and tag) is used in internal infinities */
+FPU_REG const CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
+
+
+
+static void fld_const(FPU_REG const *c)
+{
+ FPU_REG *st_new_ptr;
+
+ if ( STACK_OVERFLOW )
+ {
+ stack_overflow();
+ return;
+ }
+ push();
+ reg_move(c, st_new_ptr);
+ clear_C1();
+}
+
+
+static void fld1(void)
+{
+ fld_const(&CONST_1);
+}
+
+static void fldl2t(void)
+{
+ fld_const(&CONST_L2T);
+}
+
+static void fldl2e(void)
+{
+ fld_const(&CONST_L2E);
+}
+
+static void fldpi(void)
+{
+ fld_const(&CONST_PI);
+}
+
+static void fldlg2(void)
+{
+ fld_const(&CONST_LG2);
+}
+
+static void fldln2(void)
+{
+ fld_const(&CONST_LN2);
+}
+
+static void fldz(void)
+{
+ fld_const(&CONST_Z);
+}
+
+static FUNC constants_table[] = {
+ fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, FPU_illegal
+};
+
+void fconst(void)
+{
+ (constants_table[FPU_rm])();
+}
diff --git a/arch/i386/math-emu/reg_constant.h b/arch/i386/math-emu/reg_constant.h
new file mode 100644
index 000000000..b7db97e34
--- /dev/null
+++ b/arch/i386/math-emu/reg_constant.h
@@ -0,0 +1,31 @@
+/*---------------------------------------------------------------------------+
+ | reg_constant.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _REG_CONSTANT_H_
+#define _REG_CONSTANT_H_
+
+#include "fpu_emu.h"
+
+extern FPU_REG const CONST_1;
+extern FPU_REG const CONST_2;
+extern FPU_REG const CONST_HALF;
+extern FPU_REG const CONST_L2T;
+extern FPU_REG const CONST_L2E;
+extern FPU_REG const CONST_PI;
+extern FPU_REG const CONST_PI2;
+extern FPU_REG const CONST_PI2extra;
+extern FPU_REG const CONST_PI4;
+extern FPU_REG const CONST_LG2;
+extern FPU_REG const CONST_LN2;
+extern FPU_REG const CONST_Z;
+extern FPU_REG const CONST_PINF;
+extern FPU_REG const CONST_INF;
+extern FPU_REG const CONST_MINF;
+extern FPU_REG const CONST_QNaN;
+
+#endif _REG_CONSTANT_H_
diff --git a/arch/i386/math-emu/reg_div.S b/arch/i386/math-emu/reg_div.S
new file mode 100644
index 000000000..2fbc5f7c4
--- /dev/null
+++ b/arch/i386/math-emu/reg_div.S
@@ -0,0 +1,251 @@
+ .file "reg_div.S"
+/*---------------------------------------------------------------------------+
+ | reg_div.S |
+ | |
+ | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |
+ | unsigned int control_word) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+.text
+ .align 2
+
+.globl _reg_div
+_reg_div:
+ pushl %ebp
+ movl %esp,%ebp
+#ifndef NON_REENTRANT_FPU
+ subl $28,%esp /* Needed by divide_kernel */
+#endif NON_REENTRANT_FPU
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ebx
+ movl PARAM3,%edi
+
+ movb TAG(%esi),%al
+ orb TAG(%ebx),%al
+
+ jne L_div_special /* Not (both numbers TW_Valid) */
+
+#ifdef DENORM_OPERAND
+/* Check for denormals */
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xL_arg1_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xL_arg1_not_denormal:
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg xL_arg2_not_denormal
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xL_arg2_not_denormal:
+#endif DENORM_OPERAND
+
+/* Both arguments are TW_Valid */
+ movb TW_Valid,TAG(%edi)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ setne (%edi) /* Set the sign, requires SIGN_NEG=1, SIGN_POS=0 */
+
+ movl EXP(%esi),%edx
+ movl EXP(%ebx),%eax
+ subl %eax,%edx
+ addl EXP_BIAS,%edx
+ movl %edx,EXP(%edi)
+
+ jmp _divide_kernel
+
+
+/*-----------------------------------------------------------------------*/
+L_div_special:
+ cmpb TW_NaN,TAG(%esi) /* A NaN with anything to give NaN */
+ je L_arg1_NaN
+
+ cmpb TW_NaN,TAG(%ebx) /* A NaN with anything to give NaN */
+ jne L_no_NaN_arg
+
+/* Operations on NaNs */
+L_arg1_NaN:
+L_arg2_NaN:
+ pushl %edi /* Destination */
+ pushl %esi
+ pushl %ebx /* Ordering is important here */
+ call _real_2op_NaN
+ jmp LDiv_exit
+
+/* Invalid operations */
+L_zero_zero:
+L_inf_inf:
+ pushl %edi /* Destination */
+ call _arith_invalid /* 0/0 or Infinity/Infinity */
+ jmp LDiv_exit
+
+L_no_NaN_arg:
+ cmpb TW_Infinity,TAG(%esi)
+ jne L_arg1_not_inf
+
+ cmpb TW_Infinity,TAG(%ebx)
+ je L_inf_inf /* invalid operation */
+
+ cmpb TW_Valid,TAG(%ebx)
+ je L_inf_valid
+
+#ifdef PARANOID
+ /* arg2 must be zero or valid */
+ cmpb TW_Zero,TAG(%ebx)
+ ja L_unknown_tags
+#endif PARANOID
+
+ /* Note that p16-9 says that infinity/0 returns infinity */
+ jmp L_copy_arg1 /* Answer is Inf */
+
+L_inf_valid:
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 /* Answer is Inf */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_copy_arg1 /* Answer is Inf */
+
+L_arg1_not_inf:
+ cmpb TW_Zero,TAG(%ebx) /* Priority to div-by-zero error */
+ jne L_arg2_not_zero
+
+ cmpb TW_Zero,TAG(%esi)
+ je L_zero_zero /* invalid operation */
+
+#ifdef PARANOID
+ /* arg1 must be valid */
+ cmpb TW_Valid,TAG(%esi)
+ ja L_unknown_tags
+#endif PARANOID
+
+/* Division by zero error */
+ pushl %edi /* destination */
+ movb SIGN(%esi),%al
+ xorb SIGN(%ebx),%al
+ pushl %eax /* lower 8 bits have the sign */
+ call _divide_by_zero
+ jmp LDiv_exit
+
+L_arg2_not_zero:
+ cmpb TW_Infinity,TAG(%ebx)
+ jne L_arg2_not_inf
+
+#ifdef DENORM_OPERAND
+ cmpb TW_Valid,TAG(%esi)
+ jne L_return_zero
+
+ cmpl EXP_UNDER,EXP(%esi)
+ jg L_return_zero /* Answer is zero */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+#endif DENORM_OPERAND
+
+ jmp L_return_zero /* Answer is zero */
+
+L_arg2_not_inf:
+
+#ifdef PARANOID
+ cmpb TW_Zero,TAG(%esi)
+ jne L_unknown_tags
+#endif PARANOID
+
+ /* arg1 is zero, arg2 is not Infinity or a NaN */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%ebx)
+ jg L_copy_arg1 /* Answer is zero */
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+#endif DENORM_OPERAND
+
+L_copy_arg1:
+ movb TAG(%esi),%ax
+ movb %ax,TAG(%edi)
+ movl EXP(%esi),%eax
+ movl %eax,EXP(%edi)
+ movl SIGL(%esi),%eax
+ movl %eax,SIGL(%edi)
+ movl SIGH(%esi),%eax
+ movl %eax,SIGH(%edi)
+
+LDiv_set_result_sign:
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ jne LDiv_negative_result
+
+ movb SIGN_POS,SIGN(%edi)
+ xorl %eax,%eax /* Valid result */
+ jmp LDiv_exit
+
+LDiv_negative_result:
+ movb SIGN_NEG,SIGN(%edi)
+ xorl %eax,%eax /* Valid result */
+
+LDiv_exit:
+#ifndef NON_REENTRANT_FPU
+ leal -40(%ebp),%esp
+#else
+ leal -12(%ebp),%esp
+#endif NON_REENTRANT_FPU
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+L_return_zero:
+ xorl %eax,%eax
+ movl %eax,SIGH(%edi)
+ movl %eax,SIGL(%edi)
+ movl EXP_UNDER,EXP(%edi)
+ movb TW_Zero,TAG(%edi)
+ jmp LDiv_set_result_sign
+
+#ifdef PARANOID
+L_unknown_tags:
+ pushl EX_INTERNAL | 0x208
+ call EXCEPTION
+
+ /* Generate a NaN for unknown tags */
+ movl _CONST_QNaN,%eax
+ movl %eax,(%edi)
+ movl _CONST_QNaN+4,%eax
+ movl %eax,SIGL(%edi)
+ movl _CONST_QNaN+8,%eax
+ movl %eax,SIGH(%edi)
+ jmp LDiv_exit /* %eax is nz */
+#endif PARANOID
diff --git a/arch/i386/math-emu/reg_ld_str.c b/arch/i386/math-emu/reg_ld_str.c
new file mode 100644
index 000000000..efec9e010
--- /dev/null
+++ b/arch/i386/math-emu/reg_ld_str.c
@@ -0,0 +1,1438 @@
+/*---------------------------------------------------------------------------+
+ | reg_ld_str.c |
+ | |
+ | All of the functions which transfer data between user memory and FPU_REGs.|
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Note: |
+ | The file contains code which accesses user memory. |
+ | Emulator static data may change when user memory is accessed, due to |
+ | other processes using the emulator while swapping is in progress. |
+ +---------------------------------------------------------------------------*/
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+#include "status_w.h"
+
+
+#define EXTENDED_Ebias 0x3fff
+#define EXTENDED_Emin (-0x3ffe) /* smallest valid exponent */
+
+#define DOUBLE_Emax 1023 /* largest valid exponent */
+#define DOUBLE_Ebias 1023
+#define DOUBLE_Emin (-1022) /* smallest valid exponent */
+
+#define SINGLE_Emax 127 /* largest valid exponent */
+#define SINGLE_Ebias 127
+#define SINGLE_Emin (-126) /* smallest valid exponent */
+
+static void write_to_extended(FPU_REG *rp, char *d);
+
+
+/* Get a long double from user memory */
+int reg_load_extended(long double *s, FPU_REG *loaded_data)
+{
+ unsigned long sigl, sigh, exp;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, s, 10);
+ sigl = get_fs_long((unsigned long *) s);
+ sigh = get_fs_long(1 + (unsigned long *) s);
+ exp = get_fs_word(4 + (unsigned short *) s);
+ RE_ENTRANT_CHECK_ON;
+
+ loaded_data->tag = TW_Valid; /* Default */
+ loaded_data->sigl = sigl;
+ loaded_data->sigh = sigh;
+ if (exp & 0x8000)
+ loaded_data->sign = SIGN_NEG;
+ else
+ loaded_data->sign = SIGN_POS;
+ exp &= 0x7fff;
+ loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS;
+
+ if ( exp == 0 )
+ {
+ if ( !(sigh | sigl) )
+ {
+ loaded_data->tag = TW_Zero;
+ return 0;
+ }
+ /* The number is a de-normal or pseudodenormal. */
+ if (sigh & 0x80000000)
+ {
+ /* Is a pseudodenormal. */
+ /* Convert it for internal use. */
+ /* This is non-80486 behaviour because the number
+ loses its 'denormal' identity. */
+ loaded_data->exp++;
+ return 1;
+ }
+ else
+ {
+ /* Is a denormal. */
+ /* Convert it for internal use. */
+ loaded_data->exp++;
+ normalize_nuo(loaded_data);
+ return 0;
+ }
+ }
+ else if ( exp == 0x7fff )
+ {
+ if ( !((sigh ^ 0x80000000) | sigl) )
+ {
+ /* Matches the bit pattern for Infinity. */
+ loaded_data->exp = EXP_Infinity;
+ loaded_data->tag = TW_Infinity;
+ return 0;
+ }
+
+ loaded_data->exp = EXP_NaN;
+ loaded_data->tag = TW_NaN;
+ if ( !(sigh & 0x80000000) )
+ {
+ /* NaNs have the ms bit set to 1. */
+ /* This is therefore an Unsupported NaN data type. */
+ /* This is non 80486 behaviour */
+ /* This should generate an Invalid Operand exception
+ later, so we convert it to a SNaN */
+ loaded_data->sigh = 0x80000000;
+ loaded_data->sigl = 0x00000001;
+ loaded_data->sign = SIGN_NEG;
+ return 1;
+ }
+ return 0;
+ }
+
+ if ( !(sigh & 0x80000000) )
+ {
+ /* Unsupported data type. */
+ /* Valid numbers have the ms bit set to 1. */
+ /* Unnormal. */
+ /* Convert it for internal use. */
+ /* This is non-80486 behaviour */
+ /* This should generate an Invalid Operand exception
+ later, so we convert it to a SNaN */
+ loaded_data->sigh = 0x80000000;
+ loaded_data->sigl = 0x00000001;
+ loaded_data->sign = SIGN_NEG;
+ loaded_data->exp = EXP_NaN;
+ loaded_data->tag = TW_NaN;
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Get a double from user memory */
+int reg_load_double(double *dfloat, FPU_REG *loaded_data)
+{
+ int exp;
+ unsigned m64, l64;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, dfloat, 8);
+ m64 = get_fs_long(1 + (unsigned long *) dfloat);
+ l64 = get_fs_long((unsigned long *) dfloat);
+ RE_ENTRANT_CHECK_ON;
+
+ if (m64 & 0x80000000)
+ loaded_data->sign = SIGN_NEG;
+ else
+ loaded_data->sign = SIGN_POS;
+ exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
+ m64 &= 0xfffff;
+ if (exp > DOUBLE_Emax)
+ {
+ /* Infinity or NaN */
+ if ((m64 == 0) && (l64 == 0))
+ {
+ /* +- infinity */
+ loaded_data->sigh = 0x80000000;
+ loaded_data->sigl = 0x00000000;
+ loaded_data->exp = EXP_Infinity;
+ loaded_data->tag = TW_Infinity;
+ return 0;
+ }
+ else
+ {
+ /* Must be a signaling or quiet NaN */
+ loaded_data->exp = EXP_NaN;
+ loaded_data->tag = TW_NaN;
+ loaded_data->sigh = (m64 << 11) | 0x80000000;
+ loaded_data->sigh |= l64 >> 21;
+ loaded_data->sigl = l64 << 11;
+ return 0; /* The calling function must look for NaNs */
+ }
+ }
+ else if ( exp < DOUBLE_Emin )
+ {
+ /* Zero or de-normal */
+ if ((m64 == 0) && (l64 == 0))
+ {
+ /* Zero */
+ int c = loaded_data->sign;
+ reg_move(&CONST_Z, loaded_data);
+ loaded_data->sign = c;
+ return 0;
+ }
+ else
+ {
+ /* De-normal */
+ loaded_data->exp = DOUBLE_Emin + EXP_BIAS;
+ loaded_data->tag = TW_Valid;
+ loaded_data->sigh = m64 << 11;
+ loaded_data->sigh |= l64 >> 21;
+ loaded_data->sigl = l64 << 11;
+ normalize_nuo(loaded_data);
+ return denormal_operand();
+ }
+ }
+ else
+ {
+ loaded_data->exp = exp + EXP_BIAS;
+ loaded_data->tag = TW_Valid;
+ loaded_data->sigh = (m64 << 11) | 0x80000000;
+ loaded_data->sigh |= l64 >> 21;
+ loaded_data->sigl = l64 << 11;
+
+ return 0;
+ }
+}
+
+
+/* Get a float from user memory */
+int reg_load_single(float *single, FPU_REG *loaded_data)
+{
+ unsigned m32;
+ int exp;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, single, 4);
+ m32 = get_fs_long((unsigned long *) single);
+ RE_ENTRANT_CHECK_ON;
+
+ if (m32 & 0x80000000)
+ loaded_data->sign = SIGN_NEG;
+ else
+ loaded_data->sign = SIGN_POS;
+ if (!(m32 & 0x7fffffff))
+ {
+ /* Zero */
+ int c = loaded_data->sign;
+ reg_move(&CONST_Z, loaded_data);
+ loaded_data->sign = c;
+ return 0;
+ }
+ exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
+ m32 = (m32 & 0x7fffff) << 8;
+ if ( exp < SINGLE_Emin )
+ {
+ /* De-normals */
+ loaded_data->exp = SINGLE_Emin + EXP_BIAS;
+ loaded_data->tag = TW_Valid;
+ loaded_data->sigh = m32;
+ loaded_data->sigl = 0;
+ normalize_nuo(loaded_data);
+ return denormal_operand();
+ }
+ else if ( exp > SINGLE_Emax )
+ {
+ /* Infinity or NaN */
+ if ( m32 == 0 )
+ {
+ /* +- infinity */
+ loaded_data->sigh = 0x80000000;
+ loaded_data->sigl = 0x00000000;
+ loaded_data->exp = EXP_Infinity;
+ loaded_data->tag = TW_Infinity;
+ return 0;
+ }
+ else
+ {
+ /* Must be a signaling or quiet NaN */
+ loaded_data->exp = EXP_NaN;
+ loaded_data->tag = TW_NaN;
+ loaded_data->sigh = m32 | 0x80000000;
+ loaded_data->sigl = 0;
+ return 0; /* The calling function must look for NaNs */
+ }
+ }
+ else
+ {
+ loaded_data->exp = exp + EXP_BIAS;
+ loaded_data->sigh = m32 | 0x80000000;
+ loaded_data->sigl = 0;
+ loaded_data->tag = TW_Valid;
+ return 0;
+ }
+}
+
+
+/* Get a long long from user memory */
+void reg_load_int64(long long *_s, FPU_REG *loaded_data)
+{
+ int e;
+ long long s;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, _s, 8);
+ ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
+ ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
+ RE_ENTRANT_CHECK_ON;
+
+ if (s == 0)
+ { reg_move(&CONST_Z, loaded_data); return; }
+
+ if (s > 0)
+ loaded_data->sign = SIGN_POS;
+ else
+ {
+ s = -s;
+ loaded_data->sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 63;
+ significand(loaded_data) = s;
+ loaded_data->exp = e;
+ loaded_data->tag = TW_Valid;
+ normalize_nuo(loaded_data);
+}
+
+
+/* Get a long from user memory */
+void reg_load_int32(long *_s, FPU_REG *loaded_data)
+{
+ long s;
+ int e;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, _s, 4);
+ s = (long)get_fs_long((unsigned long *) _s);
+ RE_ENTRANT_CHECK_ON;
+
+ if (s == 0)
+ { reg_move(&CONST_Z, loaded_data); return; }
+
+ if (s > 0)
+ loaded_data->sign = SIGN_POS;
+ else
+ {
+ s = -s;
+ loaded_data->sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 31;
+ loaded_data->sigh = s;
+ loaded_data->sigl = 0;
+ loaded_data->exp = e;
+ loaded_data->tag = TW_Valid;
+ normalize_nuo(loaded_data);
+}
+
+
+/* Get a short from user memory */
+void reg_load_int16(short *_s, FPU_REG *loaded_data)
+{
+ int s, e;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, _s, 2);
+ /* Cast as short to get the sign extended. */
+ s = (short)get_fs_word((unsigned short *) _s);
+ RE_ENTRANT_CHECK_ON;
+
+ if (s == 0)
+ { reg_move(&CONST_Z, loaded_data); return; }
+
+ if (s > 0)
+ loaded_data->sign = SIGN_POS;
+ else
+ {
+ s = -s;
+ loaded_data->sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 15;
+ loaded_data->sigh = s << 16;
+
+ loaded_data->sigl = 0;
+ loaded_data->exp = e;
+ loaded_data->tag = TW_Valid;
+ normalize_nuo(loaded_data);
+}
+
+
+/* Get a packed bcd array from user memory */
+void reg_load_bcd(char *s, FPU_REG *loaded_data)
+{
+ int pos;
+ unsigned char bcd;
+ long long l=0;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, s, 10);
+ RE_ENTRANT_CHECK_ON;
+ for ( pos = 8; pos >= 0; pos--)
+ {
+ l *= 10;
+ RE_ENTRANT_CHECK_OFF;
+ bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
+ RE_ENTRANT_CHECK_ON;
+ l += bcd >> 4;
+ l *= 10;
+ l += bcd & 0x0f;
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ loaded_data->sign =
+ ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
+ SIGN_NEG : SIGN_POS;
+ RE_ENTRANT_CHECK_ON;
+
+ if (l == 0)
+ {
+ char sign = loaded_data->sign;
+ reg_move(&CONST_Z, loaded_data);
+ loaded_data->sign = sign;
+ }
+ else
+ {
+ significand(loaded_data) = l;
+ loaded_data->exp = EXP_BIAS + 63;
+ loaded_data->tag = TW_Valid;
+ normalize_nuo(loaded_data);
+ }
+}
+
+/*===========================================================================*/
+
+/* Put a long double into user memory */
+int reg_store_extended(long double *d, FPU_REG *st0_ptr)
+{
+ /*
+ The only exception raised by an attempt to store to an
+ extended format is the Invalid Stack exception, i.e.
+ attempting to store from an empty register.
+ */
+
+ if ( st0_ptr->tag != TW_Empty )
+ {
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE, d, 10);
+ RE_ENTRANT_CHECK_ON;
+ write_to_extended(st0_ptr, (char *) d);
+ return 1;
+ }
+
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,10);
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0xc0000000, 1 + (unsigned long *) d);
+ put_fs_word(0xffff, 4 + (short *) d);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
+ }
+ else
+ return 0;
+
+}
+
+
+/* Put a double into user memory */
+int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
+{
+ unsigned long l[2];
+ unsigned long increment = 0; /* avoid gcc warnings */
+ char st0_tag = st0_ptr->tag;
+
+ if (st0_tag == TW_Valid)
+ {
+ int exp;
+ FPU_REG tmp;
+
+ reg_move(st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
+
+ if ( exp < DOUBLE_Emin ) /* It may be a denormal */
+ {
+ int precision_loss;
+
+ /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+ /* An 80486 is supposed to be able to generate
+ a denormal exception here, but... */
+ if ( st0_ptr->exp <= EXP_UNDER )
+ {
+ /* Underflow has priority. */
+ if ( control_word & CW_Underflow )
+ denormal_operand();
+ }
+#endif PECULIAR_486
+
+ tmp.exp += -DOUBLE_Emin + 52; /* largest exp to be 51 */
+
+ if ( (precision_loss = round_to_int(&tmp)) )
+ {
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as peculiar, it appears
+ that the 80486 rounds to the dest precision, then
+ converts to decide underflow. */
+ if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
+ (st0_ptr->sigl & 0x000007ff)) )
+#endif PECULIAR_486
+ {
+ EXCEPTION(EX_Underflow);
+ /* This is a special case: see sec 16.2.5.1 of
+ the 80486 book */
+ if ( !(control_word & CW_Underflow) )
+ return 0;
+ }
+ EXCEPTION(precision_loss);
+ if ( !(control_word & CW_Precision) )
+ return 0;
+ }
+ l[0] = tmp.sigl;
+ l[1] = tmp.sigh;
+ }
+ else
+ {
+ if ( tmp.sigl & 0x000007ff )
+ {
+ switch (control_word & CW_RC)
+ {
+ case RC_RND:
+ /* Rounding can get a little messy.. */
+ increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
+ ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate the mantissa */
+ tmp.sigl &= 0xfffff800;
+
+ if ( increment )
+ {
+ set_precision_flag_up();
+
+ if ( tmp.sigl >= 0xfffff800 )
+ {
+ /* the sigl part overflows */
+ if ( tmp.sigh == 0xffffffff )
+ {
+ /* The sigh part overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if (exp >= EXP_OVER)
+ goto overflow;
+ }
+ else
+ {
+ tmp.sigh ++;
+ }
+ tmp.sigl = 0x00000000;
+ }
+ else
+ {
+ /* We only need to increment sigl */
+ tmp.sigl += 0x00000800;
+ }
+ }
+ else
+ set_precision_flag_down();
+ }
+
+ l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
+ l[1] = ((tmp.sigh >> 11) & 0xfffff);
+
+ if ( exp > DOUBLE_Emax )
+ {
+ overflow:
+ EXCEPTION(EX_Overflow);
+ if ( !(control_word & CW_Overflow) )
+ return 0;
+ set_precision_flag_up();
+ if ( !(control_word & CW_Precision) )
+ return 0;
+
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ /* Overflow to infinity */
+ l[0] = 0x00000000; /* Set to */
+ l[1] = 0x7ff00000; /* + INF */
+ }
+ else
+ {
+ /* Add the exponent */
+ l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
+ }
+ }
+ }
+ else if (st0_tag == TW_Zero)
+ {
+ /* Number is zero */
+ l[0] = 0;
+ l[1] = 0;
+ }
+ else if (st0_tag == TW_Infinity)
+ {
+ l[0] = 0;
+ l[1] = 0x7ff00000;
+ }
+ else if (st0_tag == TW_NaN)
+ {
+ /* See if we can get a valid NaN from the FPU_REG */
+ l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
+ l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
+ if ( !(st0_ptr->sigh & 0x40000000) )
+ {
+ /* It is a signalling NaN */
+ EXCEPTION(EX_Invalid);
+ if ( !(control_word & CW_Invalid) )
+ return 0;
+ l[1] |= (0x40000000 >> 11);
+ }
+ l[1] |= 0x7ff00000;
+ }
+ else if ( st0_tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & CW_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
+ put_fs_long(0, (unsigned long *) dfloat);
+ put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
+ }
+ else
+ return 0;
+ }
+ if ( st0_ptr->sign )
+ l[1] |= 0x80000000;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
+ put_fs_long(l[0], (unsigned long *)dfloat);
+ put_fs_long(l[1], 1 + (unsigned long *)dfloat);
+ RE_ENTRANT_CHECK_ON;
+
+ return 1;
+}
+
+
+/* Put a float into user memory */
+int reg_store_single(float *single, FPU_REG *st0_ptr)
+{
+ long templ;
+ unsigned long increment = 0; /* avoid gcc warnings */
+ char st0_tag = st0_ptr->tag;
+
+ if (st0_tag == TW_Valid)
+ {
+ int exp;
+ FPU_REG tmp;
+
+ reg_move(st0_ptr, &tmp);
+ exp = tmp.exp - EXP_BIAS;
+
+ if ( exp < SINGLE_Emin )
+ {
+ int precision_loss;
+
+ /* A denormal will always underflow. */
+#ifndef PECULIAR_486
+ /* An 80486 is supposed to be able to generate
+ a denormal exception here, but... */
+ if ( st0_ptr->exp <= EXP_UNDER )
+ {
+ /* Underflow has priority. */
+ if ( control_word & CW_Underflow )
+ denormal_operand();
+ }
+#endif PECULIAR_486
+
+ tmp.exp += -SINGLE_Emin + 23; /* largest exp to be 22 */
+
+ if ( (precision_loss = round_to_int(&tmp)) )
+ {
+#ifdef PECULIAR_486
+ /* Did it round to a non-denormal ? */
+ /* This behaviour might be regarded as peculiar, it appears
+ that the 80486 rounds to the dest precision, then
+ converts to decide underflow. */
+ if ( !((tmp.sigl == 0x00800000) &&
+ ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) )
+#endif PECULIAR_486
+ {
+ EXCEPTION(EX_Underflow);
+ /* This is a special case: see sec 16.2.5.1 of
+ the 80486 book */
+ if ( !(control_word & EX_Underflow) )
+ return 0;
+ }
+ EXCEPTION(precision_loss);
+ if ( !(control_word & EX_Precision) )
+ return 0;
+ }
+ templ = tmp.sigl;
+ }
+ else
+ {
+ if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
+ {
+ unsigned long sigh = tmp.sigh;
+ unsigned long sigl = tmp.sigl;
+
+ switch (control_word & CW_RC)
+ {
+ case RC_RND:
+ increment = ((sigh & 0xff) > 0x80) /* more than half */
+ || (((sigh & 0xff) == 0x80) && sigl) /* more than half */
+ || ((sigh & 0x180) == 0x180); /* round to even */
+ break;
+ case RC_DOWN: /* towards -infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? 0 : (sigl | (sigh & 0xff));
+ break;
+ case RC_UP: /* towards +infinity */
+ increment = (tmp.sign == SIGN_POS)
+ ? (sigl | (sigh & 0xff)) : 0;
+ break;
+ case RC_CHOP:
+ increment = 0;
+ break;
+ }
+
+ /* Truncate part of the mantissa */
+ tmp.sigl = 0;
+
+ if (increment)
+ {
+ set_precision_flag_up();
+
+ if ( sigh >= 0xffffff00 )
+ {
+ /* The sigh part overflows */
+ tmp.sigh = 0x80000000;
+ exp++;
+ if ( exp >= EXP_OVER )
+ goto overflow;
+ }
+ else
+ {
+ tmp.sigh &= 0xffffff00;
+ tmp.sigh += 0x100;
+ }
+ }
+ else
+ {
+ set_precision_flag_down();
+ tmp.sigh &= 0xffffff00; /* Finish the truncation */
+ }
+ }
+
+ templ = (tmp.sigh >> 8) & 0x007fffff;
+
+ if ( exp > SINGLE_Emax )
+ {
+ overflow:
+ EXCEPTION(EX_Overflow);
+ if ( !(control_word & CW_Overflow) )
+ return 0;
+ set_precision_flag_up();
+ if ( !(control_word & CW_Precision) )
+ return 0;
+
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
+ /* Masked response is overflow to infinity. */
+ templ = 0x7f800000;
+ }
+ else
+ templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
+ }
+ }
+ else if (st0_tag == TW_Zero)
+ {
+ templ = 0;
+ }
+ else if (st0_tag == TW_Infinity)
+ {
+ templ = 0x7f800000;
+ }
+ else if (st0_tag == TW_NaN)
+ {
+ /* See if we can get a valid NaN from the FPU_REG */
+ templ = st0_ptr->sigh >> 8;
+ if ( !(st0_ptr->sigh & 0x40000000) )
+ {
+ /* It is a signalling NaN */
+ EXCEPTION(EX_Invalid);
+ if ( !(control_word & CW_Invalid) )
+ return 0;
+ templ |= (0x40000000 >> 8);
+ }
+ templ |= 0x7f800000;
+ }
+ else if ( st0_tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,(void *)single,4);
+ put_fs_long(0xffc00000, (unsigned long *) single);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
+ }
+ else
+ return 0;
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL|0x163);
+ return 0;
+ }
+#endif
+ if (st0_ptr->sign)
+ templ |= 0x80000000;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,(void *)single,4);
+ put_fs_long(templ,(unsigned long *) single);
+ RE_ENTRANT_CHECK_ON;
+
+ return 1;
+}
+
+
+/* Put a long long into user memory */
+int reg_store_int64(long long *d, FPU_REG *st0_ptr)
+{
+ FPU_REG t;
+ long long tll;
+ int precision_loss;
+ char st0_tag = st0_ptr->tag;
+
+ if ( st0_tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ goto invalid_operand;
+ }
+ else if ( (st0_tag == TW_Infinity) ||
+ (st0_tag == TW_NaN) )
+ {
+ EXCEPTION(EX_Invalid);
+ goto invalid_operand;
+ }
+
+ reg_move(st0_ptr, &t);
+ precision_loss = round_to_int(&t);
+ ((long *)&tll)[0] = t.sigl;
+ ((long *)&tll)[1] = t.sigh;
+ if ( (precision_loss == 1) ||
+ ((t.sigh & 0x80000000) &&
+ !((t.sigh == 0x80000000) && (t.sigl == 0) &&
+ (t.sign == SIGN_NEG))) )
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
+ if ( control_word & EX_Invalid )
+ {
+ /* Produce something like QNaN "indefinite" */
+ tll = 0x8000000000000000LL;
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ if ( precision_loss )
+ set_precision_flag(precision_loss);
+ if ( t.sign )
+ tll = - tll;
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,(void *)d,8);
+ put_fs_long(((long *)&tll)[0],(unsigned long *) d);
+ put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
+ RE_ENTRANT_CHECK_ON;
+
+ return 1;
+}
+
+
+/* Put a long into user memory */
+int reg_store_int32(long *d, FPU_REG *st0_ptr)
+{
+ FPU_REG t;
+ int precision_loss;
+ char st0_tag = st0_ptr->tag;
+
+ if ( st0_tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ goto invalid_operand;
+ }
+ else if ( (st0_tag == TW_Infinity) ||
+ (st0_tag == TW_NaN) )
+ {
+ EXCEPTION(EX_Invalid);
+ goto invalid_operand;
+ }
+
+ reg_move(st0_ptr, &t);
+ precision_loss = round_to_int(&t);
+ if (t.sigh ||
+ ((t.sigl & 0x80000000) &&
+ !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
+ if ( control_word & EX_Invalid )
+ {
+ /* Produce something like QNaN "indefinite" */
+ t.sigl = 0x80000000;
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ if ( precision_loss )
+ set_precision_flag(precision_loss);
+ if ( t.sign )
+ t.sigl = -(long)t.sigl;
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,4);
+ put_fs_long(t.sigl, (unsigned long *) d);
+ RE_ENTRANT_CHECK_ON;
+
+ return 1;
+}
+
+
+/* Put a short into user memory */
+int reg_store_int16(short *d, FPU_REG *st0_ptr)
+{
+ FPU_REG t;
+ int precision_loss;
+ char st0_tag = st0_ptr->tag;
+
+ if ( st0_tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ goto invalid_operand;
+ }
+ else if ( (st0_tag == TW_Infinity) ||
+ (st0_tag == TW_NaN) )
+ {
+ EXCEPTION(EX_Invalid);
+ goto invalid_operand;
+ }
+
+ reg_move(st0_ptr, &t);
+ precision_loss = round_to_int(&t);
+ if (t.sigh ||
+ ((t.sigl & 0xffff8000) &&
+ !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
+ if ( control_word & EX_Invalid )
+ {
+ /* Produce something like QNaN "indefinite" */
+ t.sigl = 0x8000;
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ if ( precision_loss )
+ set_precision_flag(precision_loss);
+ if ( t.sign )
+ t.sigl = -t.sigl;
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,2);
+ put_fs_word((short)t.sigl,(short *) d);
+ RE_ENTRANT_CHECK_ON;
+
+ return 1;
+}
+
+
+/* Put a packed bcd array into user memory */
+int reg_store_bcd(char *d, FPU_REG *st0_ptr)
+{
+ FPU_REG t;
+ unsigned long long ll;
+ unsigned char b;
+ int i, precision_loss;
+ unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
+ char st0_tag = st0_ptr->tag;
+
+ if ( st0_tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ goto invalid_operand;
+ }
+
+ reg_move(st0_ptr, &t);
+ precision_loss = round_to_int(&t);
+ ll = significand(&t);
+
+ /* Check for overflow, by comparing with 999999999999999999 decimal. */
+ if ( (t.sigh > 0x0de0b6b3) ||
+ ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ invalid_operand:
+ if ( control_word & CW_Invalid )
+ {
+ /* Produce the QNaN "indefinite" */
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,10);
+ for ( i = 0; i < 7; i++)
+ put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
+ put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
+ put_fs_byte(0xff, (unsigned char *) d+8);
+ put_fs_byte(0xff, (unsigned char *) d+9);
+ RE_ENTRANT_CHECK_ON;
+ return 1;
+ }
+ else
+ return 0;
+ }
+ else if ( precision_loss )
+ {
+ /* Precision loss doesn't stop the data transfer */
+ set_precision_flag(precision_loss);
+ }
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,10);
+ RE_ENTRANT_CHECK_ON;
+ for ( i = 0; i < 9; i++)
+ {
+ b = div_small(&ll, 10);
+ b |= (div_small(&ll, 10)) << 4;
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_byte(b,(unsigned char *) d+i);
+ RE_ENTRANT_CHECK_ON;
+ }
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_byte(sign,(unsigned char *) d+9);
+ RE_ENTRANT_CHECK_ON;
+
+ return 1;
+}
+
+/*===========================================================================*/
+
+/* r gets mangled such that sig is int, sign:
+ it is NOT normalized */
+/* The return value (in eax) is zero if the result is exact,
+ if bits are changed due to rounding, truncation, etc, then
+ a non-zero value is returned */
+/* Overflow is signalled by a non-zero return value (in eax).
+ In the case of overflow, the returned significand always has the
+ largest possible value */
+int round_to_int(FPU_REG *r)
+{
+ char very_big;
+ unsigned eax;
+
+ if (r->tag == TW_Zero)
+ {
+ /* Make sure that zero is returned */
+ significand(r) = 0;
+ return 0; /* o.k. */
+ }
+
+ if (r->exp > EXP_BIAS + 63)
+ {
+ r->sigl = r->sigh = ~0; /* The largest representable number */
+ return 1; /* overflow */
+ }
+
+ eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
+ very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
+#define half_or_more (eax & 0x80000000)
+#define frac_part (eax)
+#define more_than_half ((eax & 0x80000001) == 0x80000001)
+ switch (control_word & CW_RC)
+ {
+ case RC_RND:
+ if ( more_than_half /* nearest */
+ || (half_or_more && (r->sigl & 1)) ) /* odd -> even */
+ {
+ if ( very_big ) return 1; /* overflow */
+ significand(r) ++;
+ return PRECISION_LOST_UP;
+ }
+ break;
+ case RC_DOWN:
+ if (frac_part && r->sign)
+ {
+ if ( very_big ) return 1; /* overflow */
+ significand(r) ++;
+ return PRECISION_LOST_UP;
+ }
+ break;
+ case RC_UP:
+ if (frac_part && !r->sign)
+ {
+ if ( very_big ) return 1; /* overflow */
+ significand(r) ++;
+ return PRECISION_LOST_UP;
+ }
+ break;
+ case RC_CHOP:
+ break;
+ }
+
+ return eax ? PRECISION_LOST_DOWN : 0;
+
+}
+
+/*===========================================================================*/
+
+char *fldenv(fpu_addr_modes addr_modes, char *s)
+{
+ unsigned short tag_word = 0;
+ unsigned char tag;
+ int i;
+
+ if ( (addr_modes.default_mode == VM86) ||
+ ((addr_modes.default_mode == PM16)
+ ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
+ {
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, s, 0x0e);
+ control_word = get_fs_word((unsigned short *) s);
+ partial_status = get_fs_word((unsigned short *) (s+2));
+ tag_word = get_fs_word((unsigned short *) (s+4));
+ instruction_address.offset = get_fs_word((unsigned short *) (s+6));
+ instruction_address.selector = get_fs_word((unsigned short *) (s+8));
+ operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
+ operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
+ RE_ENTRANT_CHECK_ON;
+ s += 0x0e;
+ if ( addr_modes.default_mode == VM86 )
+ {
+ instruction_address.offset
+ += (instruction_address.selector & 0xf000) << 4;
+ operand_address.offset += (operand_address.selector & 0xf000) << 4;
+ }
+ }
+ else
+ {
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, s, 0x1c);
+ control_word = get_fs_word((unsigned short *) s);
+ partial_status = get_fs_word((unsigned short *) (s+4));
+ tag_word = get_fs_word((unsigned short *) (s+8));
+ instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
+ instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
+ instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
+ operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
+ operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
+ RE_ENTRANT_CHECK_ON;
+ s += 0x1c;
+ }
+
+#ifdef PECULIAR_486
+ control_word &= ~0xe080;
+#endif PECULIAR_486
+
+ top = (partial_status >> SW_Top_Shift) & 7;
+
+ if ( partial_status & ~control_word & CW_Exceptions )
+ partial_status |= (SW_Summary | SW_Backward);
+ else
+ partial_status &= ~(SW_Summary | SW_Backward);
+
+ for ( i = 0; i < 8; i++ )
+ {
+ tag = tag_word & 3;
+ tag_word >>= 2;
+
+ if ( tag == 3 )
+ /* New tag is empty. Accept it */
+ regs[i].tag = TW_Empty;
+ else if ( regs[i].tag == TW_Empty )
+ {
+ /* Old tag is empty and new tag is not empty. New tag is determined
+ by old reg contents */
+ if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
+ {
+ if ( !(regs[i].sigl | regs[i].sigh) )
+ regs[i].tag = TW_Zero;
+ else
+ regs[i].tag = TW_Valid;
+ }
+ else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
+ {
+ if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
+ regs[i].tag = TW_Infinity;
+ else
+ regs[i].tag = TW_NaN;
+ }
+ else
+ regs[i].tag = TW_Valid;
+ }
+ /* Else old tag is not empty and new tag is not empty. Old tag
+ remains correct */
+ }
+
+ return s;
+}
+
+
+void frstor(fpu_addr_modes addr_modes, char *data_address)
+{
+ int i, stnr;
+ unsigned char tag;
+ char *s = fldenv(addr_modes, data_address);
+
+ for ( i = 0; i < 8; i++ )
+ {
+ /* Load each register. */
+ stnr = (i+top) & 7;
+ tag = regs[stnr].tag; /* Derived from the fldenv() loaded tag word. */
+ reg_load_extended((long double *)(s+i*10), &regs[stnr]);
+ if ( tag == TW_Empty ) /* The loaded data over-rides all other cases. */
+ regs[stnr].tag = tag;
+ }
+
+}
+
+
+unsigned short tag_word(void)
+{
+ unsigned short word = 0;
+ unsigned char tag;
+ int i;
+
+ for ( i = 7; i >= 0; i-- )
+ {
+ switch ( tag = regs[i].tag )
+ {
+ case TW_Valid:
+ if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
+ tag = 2;
+ break;
+ case TW_Infinity:
+ case TW_NaN:
+ tag = 2;
+ break;
+ case TW_Empty:
+ tag = 3;
+ break;
+ /* TW_Zero already has the correct value */
+ }
+ word <<= 2;
+ word |= tag;
+ }
+ return word;
+}
+
+
+char *fstenv(fpu_addr_modes addr_modes, char *d)
+{
+ if ( (addr_modes.default_mode == VM86) ||
+ ((addr_modes.default_mode == PM16)
+ ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
+ {
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,14);
+#ifdef PECULIAR_486
+ put_fs_long(control_word & ~0xe080, (unsigned short *) d);
+#else
+ put_fs_word(control_word, (unsigned short *) d);
+#endif PECULIAR_486
+ put_fs_word(status_word(), (unsigned short *) (d+2));
+ put_fs_word(tag_word(), (unsigned short *) (d+4));
+ put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
+ put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
+ if ( addr_modes.default_mode == VM86 )
+ {
+ put_fs_word((instruction_address.offset & 0xf0000) >> 4,
+ (unsigned short *) (d+8));
+ put_fs_word((operand_address.offset & 0xf0000) >> 4,
+ (unsigned short *) (d+0x0c));
+ }
+ else
+ {
+ put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
+ put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
+ }
+ RE_ENTRANT_CHECK_ON;
+ d += 0x0e;
+ }
+ else
+ {
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,28);
+#ifdef PECULIAR_486
+ /* An 80486 sets all the reserved bits to 1. */
+ put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
+ put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
+ put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
+#else
+ put_fs_word(control_word, (unsigned short *) d);
+ put_fs_word(status_word(), (unsigned short *) (d+4));
+ put_fs_word(tag_word(), (unsigned short *) (d+8));
+#endif PECULIAR_486
+ put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
+ put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
+ put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
+ put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
+#ifdef PECULIAR_486
+ /* An 80486 sets all the reserved bits to 1. */
+ put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
+ put_fs_word(0xffff, (unsigned short *) (d+0x1a));
+#else
+ put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
+#endif PECULIAR_486
+ RE_ENTRANT_CHECK_ON;
+ d += 0x1c;
+ }
+
+ control_word |= CW_Exceptions;
+ partial_status &= ~(SW_Summary | SW_Backward);
+
+ return d;
+}
+
+
+void fsave(fpu_addr_modes addr_modes, char *data_address)
+{
+ char *d;
+ int i;
+
+ d = fstenv(addr_modes, data_address);
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE,d,80);
+ RE_ENTRANT_CHECK_ON;
+ for ( i = 0; i < 8; i++ )
+ write_to_extended(&regs[(top + i) & 7], d + 10 * i);
+
+ finit();
+
+}
+
+/*===========================================================================*/
+
+/*
+ A call to this function must be preceded by a call to
+ FPU_verify_area() to verify access to the 10 bytes at d
+ */
+static void write_to_extended(FPU_REG *rp, char *d)
+{
+ long e;
+ FPU_REG tmp;
+
+ e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
+
+#ifdef PARANOID
+ switch ( rp->tag )
+ {
+ case TW_Zero:
+ if ( rp->sigh | rp->sigl | e )
+ EXCEPTION(EX_INTERNAL | 0x160);
+ break;
+ case TW_Infinity:
+ case TW_NaN:
+ if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
+ EXCEPTION(EX_INTERNAL | 0x161);
+ break;
+ default:
+ if (e > 0x7fff || e < -63)
+ EXCEPTION(EX_INTERNAL | 0x162);
+ }
+#endif PARANOID
+
+ /*
+ All numbers except denormals are stored internally in a
+ format which is compatible with the extended real number
+ format.
+ */
+ if ( e > 0 )
+ {
+ /* just copy the reg */
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_long(rp->sigl, (unsigned long *) d);
+ put_fs_long(rp->sigh, (unsigned long *) (d + 4));
+ RE_ENTRANT_CHECK_ON;
+ }
+ else
+ {
+ /*
+ The number is a de-normal stored as a normal using our
+ extra exponent range, or is Zero.
+ Convert it back to a de-normal, or leave it as Zero.
+ */
+ reg_move(rp, &tmp);
+ tmp.exp += -EXTENDED_Emin + 63; /* largest exp to be 63 */
+ round_to_int(&tmp);
+ e = 0;
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_long(tmp.sigl, (unsigned long *) d);
+ put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
+ RE_ENTRANT_CHECK_ON;
+ }
+ e |= rp->sign == SIGN_POS ? 0 : 0x8000;
+ RE_ENTRANT_CHECK_OFF;
+ put_fs_word(e, (unsigned short *) (d + 8));
+ RE_ENTRANT_CHECK_ON;
+}
diff --git a/arch/i386/math-emu/reg_mul.c b/arch/i386/math-emu/reg_mul.c
new file mode 100644
index 000000000..75246187b
--- /dev/null
+++ b/arch/i386/math-emu/reg_mul.c
@@ -0,0 +1,105 @@
+/*---------------------------------------------------------------------------+
+ | reg_mul.c |
+ | |
+ | Multiply one FPU_REG by another, put the result in a destination FPU_REG. |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | The destination may be any FPU_REG, including one of the source FPU_REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "fpu_system.h"
+
+
+/* This routine must be called with non-empty source registers */
+int reg_mul(FPU_REG const *a, FPU_REG const *b,
+ FPU_REG *dest, unsigned int control_w)
+{
+ char saved_sign = dest->sign;
+ char sign = (a->sign ^ b->sign);
+
+ if (!(a->tag | b->tag))
+ {
+ /* Both regs Valid, this should be the most common case. */
+ dest->sign = sign;
+ if ( reg_u_mul(a, b, dest, control_w) )
+ {
+ dest->sign = saved_sign;
+ return 1;
+ }
+ return 0;
+ }
+ else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
+ {
+#ifdef DENORM_OPERAND
+ if ( ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)) ||
+ ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER)) )
+ {
+ if ( denormal_operand() ) return 1;
+ }
+#endif DENORM_OPERAND
+ /* Must have either both arguments == zero, or
+ one valid and the other zero.
+ The result is therefore zero. */
+ reg_move(&CONST_Z, dest);
+ /* The 80486 book says that the answer is +0, but a real
+ 80486 behaves this way.
+ IEEE-754 apparently says it should be this way. */
+ dest->sign = sign;
+ return 0;
+ }
+ else
+ {
+ /* Must have infinities, NaNs, etc */
+ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
+ { return real_2op_NaN(a, b, dest); }
+ else if (a->tag == TW_Infinity)
+ {
+ if (b->tag == TW_Zero)
+ { return arith_invalid(dest); } /* Zero*Infinity is invalid */
+ else
+ {
+#ifdef DENORM_OPERAND
+ if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(a, dest);
+ dest->sign = sign;
+ }
+ return 0;
+ }
+ else if (b->tag == TW_Infinity)
+ {
+ if (a->tag == TW_Zero)
+ { return arith_invalid(dest); } /* Zero*Infinity is invalid */
+ else
+ {
+#ifdef DENORM_OPERAND
+ if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
+ denormal_operand() )
+ return 1;
+#endif DENORM_OPERAND
+ reg_move(b, dest);
+ dest->sign = sign;
+ }
+ return 0;
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL|0x102);
+ return 1;
+ }
+#endif PARANOID
+ }
+}
diff --git a/arch/i386/math-emu/reg_norm.S b/arch/i386/math-emu/reg_norm.S
new file mode 100644
index 000000000..9b7a9d77d
--- /dev/null
+++ b/arch/i386/math-emu/reg_norm.S
@@ -0,0 +1,150 @@
+/*---------------------------------------------------------------------------+
+ | reg_norm.S |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Normalize the value in a FPU_REG. |
+ | |
+ | Call from C as: |
+ | void normalize(FPU_REG *n) |
+ | |
+ | void normalize_nuo(FPU_REG *n) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+
+.text
+
+ .align 2,144
+.globl _normalize
+
+_normalize:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+#ifdef PARANOID
+ cmpb TW_Valid,TAG(%ebx)
+ je L_ok
+
+ pushl $0x220
+ call _exception
+ addl $4,%esp
+
+L_ok:
+#endif PARANOID
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx /* ms bits */
+ js L_done /* Already normalized */
+ jnz L_shift_1 /* Shift left 1 - 31 bits */
+
+ orl %eax,%eax
+ jz L_zero /* The contents are zero */
+
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) /* This can cause an underflow */
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) /* This can cause an underflow */
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+
+L_done:
+ cmpl EXP_OVER,EXP(%ebx)
+ jge L_overflow
+
+ cmpl EXP_UNDER,EXP(%ebx)
+ jle L_underflow
+
+L_exit:
+ popl %ebx
+ leave
+ ret
+
+
+L_zero:
+ movl EXP_UNDER,EXP(%ebx)
+ movb TW_Zero,TAG(%ebx)
+ jmp L_exit
+
+L_underflow:
+ push %ebx
+ call _arith_underflow
+ pop %ebx
+ jmp L_exit
+
+L_overflow:
+ push %ebx
+ call _arith_overflow
+ pop %ebx
+ jmp L_exit
+
+
+
+/* Normalise without reporting underflow or overflow */
+ .align 2,144
+.globl _normalize_nuo
+
+_normalize_nuo:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+#ifdef PARANOID
+ cmpb TW_Valid,TAG(%ebx)
+ je L_ok_nuo
+
+ pushl $0x221
+ call _exception
+ addl $4,%esp
+
+L_ok_nuo:
+#endif PARANOID
+
+ movl SIGH(%ebx),%edx
+ movl SIGL(%ebx),%eax
+
+ orl %edx,%edx /* ms bits */
+ js L_exit /* Already normalized */
+ jnz L_nuo_shift_1 /* Shift left 1 - 31 bits */
+
+ orl %eax,%eax
+ jz L_zero /* The contents are zero */
+
+ movl %eax,%edx
+ xorl %eax,%eax
+ subl $32,EXP(%ebx) /* This can cause an underflow */
+
+/* We need to shift left by 1 - 31 bits */
+L_nuo_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%eax,%edx
+ shl %cl,%eax
+ subl %ecx,EXP(%ebx) /* This can cause an underflow */
+
+ movl %edx,SIGH(%ebx)
+ movl %eax,SIGL(%ebx)
+ jmp L_exit
+
+
diff --git a/arch/i386/math-emu/reg_round.S b/arch/i386/math-emu/reg_round.S
new file mode 100644
index 000000000..bd8a40dc4
--- /dev/null
+++ b/arch/i386/math-emu/reg_round.S
@@ -0,0 +1,701 @@
+ .file "reg_round.S"
+/*---------------------------------------------------------------------------+
+ | reg_round.S |
+ | |
+ | Rounding/truncation/etc for FPU basic arithmetic functions. |
+ | |
+ | Copyright (C) 1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | This code has four possible entry points. |
+ | The following must be entered by a jmp instruction: |
+ | fpu_reg_round, fpu_reg_round_sqrt, and fpu_Arith_exit. |
+ | |
+ | The _round_reg entry point is intended to be used by C code. |
+ | From C, call as: |
+ | void round_reg(FPU_REG *arg, unsigned int extent, unsigned int control_w) |
+ | |
+ | For correct "up" and "down" rounding, the argument must have the correct |
+ | sign. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Four entry points. |
+ | |
+ | Needed by both the fpu_reg_round and fpu_reg_round_sqrt entry points: |
+ | %eax:%ebx 64 bit significand |
+ | %edx 32 bit extension of the significand |
+ | %edi pointer to an FPU_REG for the result to be stored |
+ | stack calling function must have set up a C stack frame and |
+ | pushed %esi, %edi, and %ebx |
+ | |
+ | Needed just for the fpu_reg_round_sqrt entry point: |
+ | %cx A control word in the same format as the FPU control word. |
+ | Otherwise, PARAM4 must give such a value. |
+ | |
+ | |
+ | The significand and its extension are assumed to be exact in the |
+ | following sense: |
+ | If the significand by itself is the exact result then the significand |
+ | extension (%edx) must contain 0, otherwise the significand extension |
+ | must be non-zero. |
+ | If the significand extension is non-zero then the significand is |
+ | smaller than the magnitude of the correct exact result by an amount |
+ | greater than zero and less than one ls bit of the significand. |
+ | The significand extension is only required to have three possible |
+ | non-zero values: |
+ | less than 0x80000000 <=> the significand is less than 1/2 an ls |
+ | bit smaller than the magnitude of the |
+ | true exact result. |
+ | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit |
+ | smaller than the magnitude of the true |
+ | exact result. |
+ | greater than 0x80000000 <=> the significand is more than 1/2 an ls |
+ | bit smaller than the magnitude of the |
+ | true exact result. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | The code in this module has become quite complex, but it should handle |
+ | all of the FPU flags which are set at this stage of the basic arithmetic |
+ | computations. |
+ | There are a few rare cases where the results are not set identically to |
+ | a real FPU. These require a bit more thought because at this stage the |
+ | results of the code here appear to be more consistent... |
+ | This may be changed in a future version. |
+ +---------------------------------------------------------------------------*/
+
+
+#include "fpu_asm.h"
+#include "exception.h"
+#include "control_w.h"
+
+/* Flags for FPU_bits_lost */
+#define LOST_DOWN $1
+#define LOST_UP $2
+
+/* Flags for FPU_denormal */
+#define DENORMAL $1
+#define UNMASKED_UNDERFLOW $2
+
+
+#ifndef NON_REENTRANT_FPU
+/* Make the code re-entrant by putting
+ local storage on the stack: */
+#define FPU_bits_lost (%esp)
+#define FPU_denormal 1(%esp)
+
+#else
+/* Not re-entrant, so we can gain speed by putting
+ local storage in a static area: */
+.data
+ .align 2,0
+FPU_bits_lost:
+ .byte 0
+FPU_denormal:
+ .byte 0
+#endif NON_REENTRANT_FPU
+
+
+.text
+ .align 2,144
+.globl fpu_reg_round
+.globl fpu_reg_round_sqrt
+.globl fpu_Arith_exit
+.globl _round_reg
+
+/* Entry point when called from C */
+_round_reg:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%edi
+ movl SIGH(%edi),%eax
+ movl SIGL(%edi),%ebx
+ movl PARAM2,%edx
+ movl PARAM3,%ecx
+ jmp fpu_reg_round_sqrt
+
+fpu_reg_round: /* Normal entry point */
+ movl PARAM4,%ecx
+
+fpu_reg_round_sqrt: /* Entry point from wm_sqrt.S */
+
+#ifndef NON_REENTRANT_FPU
+ pushl %ebx /* adjust the stack pointer */
+#endif NON_REENTRANT_FPU
+
+#ifdef PARANOID
+/* Cannot use this here yet */
+/* orl %eax,%eax */
+/* jns L_entry_bugged */
+#endif PARANOID
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xMake_denorm /* The number is a de-normal */
+
+ movb $0,FPU_denormal /* 0 -> not a de-normal */
+
+xDenorm_done:
+ movb $0,FPU_bits_lost /* No bits yet lost in rounding */
+
+ movl %ecx,%esi
+ andl CW_PC,%ecx
+ cmpl PR_64_BITS,%ecx
+ je LRound_To_64
+
+ cmpl PR_53_BITS,%ecx
+ je LRound_To_53
+
+ cmpl PR_24_BITS,%ecx
+ je LRound_To_24
+
+#ifdef PECULIAR_486
+/* With the precision control bits set to 01 "(reserved)", a real 80486
+ behaves as if the precision control bits were set to 11 "64 bits" */
+ cmpl PR_RESERVED_BITS,%ecx
+ je LRound_To_64
+#ifdef PARANOID
+ jmp L_bugged_denorm_486
+#endif PARANOID
+#else
+#ifdef PARANOID
+ jmp L_bugged_denorm /* There is no bug, just a bad control word */
+#endif PARANOID
+#endif PECULIAR_486
+
+
+/* Round etc to 24 bit precision */
+LRound_To_24:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_24
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_24
+
+ cmpl RC_UP,%ecx /* Towards +infinity */
+ je LUp_24
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity */
+ je LDown_24
+
+#ifdef PARANOID
+ jmp L_bugged_round24
+#endif PARANOID
+
+LUp_24:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_24 /* If negative then up==truncate */
+
+ jmp LCheck_24_round_up
+
+LDown_24:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_24 /* If positive then down==truncate */
+
+LCheck_24_round_up:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jnz LDo_24_round_up
+ jmp LRe_normalise
+
+LRound_nearest_24:
+ /* Do rounding of the 24th bit if needed (nearest or even) */
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ cmpl $0x00000080,%ecx
+ jc LCheck_truncate_24 /* less than half, no increment needed */
+
+ jne LGreater_Half_24 /* greater than half, increment needed */
+
+ /* Possibly half, we need to check the ls bits */
+ orl %ebx,%ebx
+ jnz LGreater_Half_24 /* greater than half, increment needed */
+
+ orl %edx,%edx
+ jnz LGreater_Half_24 /* greater than half, increment needed */
+
+ /* Exactly half, increment only if 24th bit is 1 (round to even) */
+ testl $0x00000100,%eax
+ jz LDo_truncate_24
+
+LGreater_Half_24: /* Rounding: increment at the 24th bit */
+LDo_24_round_up:
+ andl $0xffffff00,%eax /* Truncate to 24 bits */
+ xorl %ebx,%ebx
+ movb LOST_UP,FPU_bits_lost
+ addl $0x00000100,%eax
+ jmp LCheck_Round_Overflow
+
+LCheck_truncate_24:
+ movl %eax,%ecx
+ andl $0x000000ff,%ecx
+ orl %ebx,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise /* No truncation needed */
+
+LDo_truncate_24:
+ andl $0xffffff00,%eax /* Truncate to 24 bits */
+ xorl %ebx,%ebx
+ movb LOST_DOWN,FPU_bits_lost
+ jmp LRe_normalise
+
+
+/* Round etc to 53 bit precision */
+LRound_To_53:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_53
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_53
+
+ cmpl RC_UP,%ecx /* Towards +infinity */
+ je LUp_53
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity */
+ je LDown_53
+
+#ifdef PARANOID
+ jmp L_bugged_round53
+#endif PARANOID
+
+LUp_53:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_53 /* If negative then up==truncate */
+
+ jmp LCheck_53_round_up
+
+LDown_53:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_53 /* If positive then down==truncate */
+
+LCheck_53_round_up:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jnz LDo_53_round_up
+ jmp LRe_normalise
+
+LRound_nearest_53:
+ /* Do rounding of the 53rd bit if needed (nearest or even) */
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ cmpl $0x00000400,%ecx
+ jc LCheck_truncate_53 /* less than half, no increment needed */
+
+ jnz LGreater_Half_53 /* greater than half, increment needed */
+
+ /* Possibly half, we need to check the ls bits */
+ orl %edx,%edx
+ jnz LGreater_Half_53 /* greater than half, increment needed */
+
+ /* Exactly half, increment only if 53rd bit is 1 (round to even) */
+ testl $0x00000800,%ebx
+ jz LTruncate_53
+
+LGreater_Half_53: /* Rounding: increment at the 53rd bit */
+LDo_53_round_up:
+ movb LOST_UP,FPU_bits_lost
+ andl $0xfffff800,%ebx /* Truncate to 53 bits */
+ addl $0x00000800,%ebx
+ adcl $0,%eax
+ jmp LCheck_Round_Overflow
+
+LCheck_truncate_53:
+ movl %ebx,%ecx
+ andl $0x000007ff,%ecx
+ orl %edx,%ecx
+ jz LRe_normalise
+
+LTruncate_53:
+ movb LOST_DOWN,FPU_bits_lost
+ andl $0xfffff800,%ebx /* Truncate to 53 bits */
+ jmp LRe_normalise
+
+
+/* Round etc to 64 bit precision */
+LRound_To_64:
+ movl %esi,%ecx
+ andl CW_RC,%ecx
+ cmpl RC_RND,%ecx
+ je LRound_nearest_64
+
+ cmpl RC_CHOP,%ecx
+ je LCheck_truncate_64
+
+ cmpl RC_UP,%ecx /* Towards +infinity */
+ je LUp_64
+
+ cmpl RC_DOWN,%ecx /* Towards -infinity */
+ je LDown_64
+
+#ifdef PARANOID
+ jmp L_bugged_round64
+#endif PARANOID
+
+LUp_64:
+ cmpb SIGN_POS,SIGN(%edi)
+ jne LCheck_truncate_64 /* If negative then up==truncate */
+
+ orl %edx,%edx
+ jnz LDo_64_round_up
+ jmp LRe_normalise
+
+LDown_64:
+ cmpb SIGN_POS,SIGN(%edi)
+ je LCheck_truncate_64 /* If positive then down==truncate */
+
+ orl %edx,%edx
+ jnz LDo_64_round_up
+ jmp LRe_normalise
+
+LRound_nearest_64:
+ cmpl $0x80000000,%edx
+ jc LCheck_truncate_64
+
+ jne LDo_64_round_up
+
+ /* Now test for round-to-even */
+ testb $1,%ebx
+ jz LCheck_truncate_64
+
+LDo_64_round_up:
+ movb LOST_UP,FPU_bits_lost
+ addl $1,%ebx
+ adcl $0,%eax
+
+LCheck_Round_Overflow:
+ jnc LRe_normalise
+
+ /* Overflow, adjust the result (significand to 1.0) */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+ incl EXP(%edi)
+ jmp LRe_normalise
+
+LCheck_truncate_64:
+ orl %edx,%edx
+ jz LRe_normalise
+
+LTruncate_64:
+ movb LOST_DOWN,FPU_bits_lost
+
+LRe_normalise:
+ testb $0xff,FPU_denormal
+ jnz xNormalise_result
+
+xL_Normalised:
+ cmpb LOST_UP,FPU_bits_lost
+ je xL_precision_lost_up
+
+ cmpb LOST_DOWN,FPU_bits_lost
+ je xL_precision_lost_down
+
+xL_no_precision_loss:
+ /* store the result */
+ movb TW_Valid,TAG(%edi)
+
+xL_Store_significand:
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+ xorl %eax,%eax /* No errors detected. */
+
+ cmpl EXP_OVER,EXP(%edi)
+ jge L_overflow
+
+fpu_reg_round_exit:
+#ifndef NON_REENTRANT_FPU
+ popl %ebx /* adjust the stack pointer */
+#endif NON_REENTRANT_FPU
+
+fpu_Arith_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+/*
+ * Set the FPU status flags to represent precision loss due to
+ * round-up.
+ */
+xL_precision_lost_up:
+ push %eax
+ call _set_precision_flag_up
+ popl %eax
+ jmp xL_no_precision_loss
+
+/*
+ * Set the FPU status flags to represent precision loss due to
+ * truncation.
+ */
+xL_precision_lost_down:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+ jmp xL_no_precision_loss
+
+
+/*
+ * The number is a denormal (which might get rounded up to a normal)
+ * Shift the number right the required number of bits, which will
+ * have to be undone later...
+ */
+xMake_denorm:
+ /* The action to be taken depends upon whether the underflow
+ exception is masked */
+ testb CW_Underflow,%cl /* Underflow mask. */
+ jz xUnmasked_underflow /* Do not make a denormal. */
+
+ movb DENORMAL,FPU_denormal
+
+ pushl %ecx /* Save */
+ movl EXP_UNDER+1,%ecx
+ subl EXP(%edi),%ecx
+
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc xDenorm_shift_more_than_32
+
+/*
+ * We got here without jumps by assuming that the most common requirement
+ * is for a small de-normalising shift.
+ * Shift by [1..31] bits
+ */
+ addl %ecx,EXP(%edi)
+ orl %edx,%edx /* extension */
+ setne %ch /* Save whether %edx is non-zero */
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orb %ch,%dl
+ popl %ecx
+ jmp xDenorm_done
+
+/* Shift by [32..63] bits */
+xDenorm_shift_more_than_32:
+ addl %ecx,EXP(%edi)
+ subb $32,%cl
+ orl %edx,%edx
+ setne %ch
+ orb %ch,%bl
+ xorl %edx,%edx
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %edx,%edx /* test these 32 bits */
+ setne %cl
+ orb %ch,%bl
+ orb %cl,%bl
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ popl %ecx
+ jmp xDenorm_done
+
+/* Shift by [64..) bits */
+xDenorm_shift_more_than_63:
+ cmpl $64,%ecx
+ jne xDenorm_shift_more_than_64
+
+/* Exactly 64 bit shift */
+ addl %ecx,EXP(%edi)
+ xorl %ecx,%ecx
+ orl %edx,%edx
+ setne %cl
+ orl %ebx,%ebx
+ setne %ch
+ orb %ch,%cl
+ orb %cl,%al
+ movl %eax,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+xDenorm_shift_more_than_64:
+ movl EXP_UNDER+1,EXP(%edi)
+/* This is easy, %eax must be non-zero, so.. */
+ movl $1,%edx
+ xorl %eax,%eax
+ xorl %ebx,%ebx
+ popl %ecx
+ jmp xDenorm_done
+
+
+xUnmasked_underflow:
+ movb UNMASKED_UNDERFLOW,FPU_denormal
+ jmp xDenorm_done
+
+
+/* Undo the de-normalisation. */
+xNormalise_result:
+ cmpb UNMASKED_UNDERFLOW,FPU_denormal
+ je xSignal_underflow
+
+/* The number must be a denormal if we got here. */
+#ifdef PARANOID
+ /* But check it... just in case. */
+ cmpl EXP_UNDER+1,EXP(%edi)
+ jne L_norm_bugged
+#endif PARANOID
+
+#ifdef PECULIAR_486
+ /*
+ * This implements a special feature of 80486 behaviour.
+ * Underflow will be signalled even if the number is
+ * not a denormal after rounding.
+ * This difference occurs only for masked underflow, and not
+ * in the unmasked case.
+ * Actual 80486 behaviour differs from this in some circumstances.
+ */
+ orl %eax,%eax /* ms bits */
+ js LNormalise_shift_done /* Will be masked underflow */
+#endif PECULIAR_486
+
+ orl %eax,%eax /* ms bits */
+ js xL_Normalised /* No longer a denormal */
+
+ jnz LNormalise_shift_up_to_31 /* Shift left 0 - 31 bits */
+
+ orl %ebx,%ebx
+ jz L_underflow_to_zero /* The contents are zero */
+
+/* Shift left 32 - 63 bits */
+ movl %ebx,%eax
+ xorl %ebx,%ebx
+ subl $32,EXP(%edi)
+
+LNormalise_shift_up_to_31:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shl %cl,%ebx
+ subl %ecx,EXP(%edi)
+
+LNormalise_shift_done:
+ testb $0xff,FPU_bits_lost /* bits lost == underflow */
+ jz xL_Normalised
+
+ /* There must be a masked underflow */
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+/*
+ * The operations resulted in a number too small to represent.
+ * Masked response.
+ */
+L_underflow_to_zero:
+ push %eax
+ call _set_precision_flag_down
+ popl %eax
+
+ push %eax
+ pushl EX_Underflow
+ call _exception
+ popl %eax
+ popl %eax
+
+/* Reduce the exponent to EXP_UNDER */
+ movl EXP_UNDER,EXP(%edi)
+ movb TW_Zero,TAG(%edi)
+ jmp xL_Store_significand
+
+
+/* The operations resulted in a number too large to represent. */
+L_overflow:
+ push %edi
+ call _arith_overflow
+ pop %edi
+ jmp fpu_reg_round_exit
+
+
+xSignal_underflow:
+ /* The number may have been changed to a non-denormal */
+ /* by the rounding operations. */
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xDo_unmasked_underflow
+
+ jmp xL_Normalised
+
+xDo_unmasked_underflow:
+ /* Increase the exponent by the magic number */
+ addl $(3*(1<<13)),EXP(%edi)
+ push %eax
+ pushl EX_Underflow
+ call EXCEPTION
+ popl %eax
+ popl %eax
+ jmp xL_Normalised
+
+
+#ifdef PARANOID
+#ifdef PECULIAR_486
+L_bugged_denorm_486:
+ pushl EX_INTERNAL|0x236
+ call EXCEPTION
+ popl %ebx
+ jmp L_exception_exit
+#else
+L_bugged_denorm:
+ pushl EX_INTERNAL|0x230
+ call EXCEPTION
+ popl %ebx
+ jmp L_exception_exit
+#endif PECULIAR_486
+
+L_bugged_round24:
+ pushl EX_INTERNAL|0x231
+ call EXCEPTION
+ popl %ebx
+ jmp L_exception_exit
+
+L_bugged_round53:
+ pushl EX_INTERNAL|0x232
+ call EXCEPTION
+ popl %ebx
+ jmp L_exception_exit
+
+L_bugged_round64:
+ pushl EX_INTERNAL|0x233
+ call EXCEPTION
+ popl %ebx
+ jmp L_exception_exit
+
+L_norm_bugged:
+ pushl EX_INTERNAL|0x234
+ call EXCEPTION
+ popl %ebx
+ jmp L_exception_exit
+
+L_entry_bugged:
+ pushl EX_INTERNAL|0x235
+ call EXCEPTION
+ popl %ebx
+L_exception_exit:
+ mov $1,%eax
+ jmp fpu_reg_round_exit
+#endif PARANOID
diff --git a/arch/i386/math-emu/reg_u_add.S b/arch/i386/math-emu/reg_u_add.S
new file mode 100644
index 000000000..4410f8fd4
--- /dev/null
+++ b/arch/i386/math-emu/reg_u_add.S
@@ -0,0 +1,189 @@
+ .file "reg_u_add.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_add.S |
+ | |
+ | Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the |
+ | result in a destination FPU_REG. |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
+ | int control_w) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*
+ | Kernel addition routine reg_u_add(reg *arg1, reg *arg2, reg *answ).
+ | Takes two valid reg f.p. numbers (TW_Valid), which are
+ | treated as unsigned numbers,
+ | and returns their sum as a TW_Valid or TW_S f.p. number.
+ | The returned number is normalized.
+ | Basic checks are performed if PARANOID is defined.
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2,144
+.globl _reg_u_add
+_reg_u_add:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+ jge L_arg1_larger
+
+ /* num1 is smaller */
+ movl SIGL(%esi),%ebx
+ movl SIGH(%esi),%eax
+
+ movl %edi,%esi
+ negw %cx
+ jmp L_accum_loaded
+
+L_arg1_larger:
+ /* num1 has larger or equal exponent */
+ movl SIGL(%edi),%ebx
+ movl SIGH(%edi),%eax
+
+L_accum_loaded:
+ movl PARAM3,%edi /* destination */
+/* movb SIGN(%esi),%dl
+ movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
+
+
+ movl EXP(%esi),%edx
+ movl %edx,EXP(%edi) /* Copy exponent to destination */
+
+ xorl %edx,%edx /* clear the extension */
+
+#ifdef PARANOID
+ testl $0x80000000,%eax
+ je L_bugged
+
+ testl $0x80000000,SIGH(%esi)
+ je L_bugged
+#endif PARANOID
+
+/* The number to be shifted is in %eax:%ebx:%edx */
+ cmpw $32,%cx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ jmp L_shift_done
+
+L_more_than_31:
+ cmpw $64,%cx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ jz L_exactly_32
+
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ orl %ebx,%ebx
+ jz L_more_31_no_low /* none of the lowest bits is set */
+
+ orl $1,%edx /* record the fact in the extension */
+
+L_more_31_no_low:
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_exactly_32:
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $65,%cx
+ jnc L_more_than_64
+
+ movl %eax,%edx
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_64:
+ movl $1,%edx /* The shifted nr always at least one '1' */
+
+L_more_63_no_low:
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+
+L_shift_done:
+ /* Now do the addition */
+ addl SIGL(%esi),%ebx
+ adcl SIGH(%esi),%eax
+ jnc L_round_the_result
+
+ /* Overflow, adjust the result */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+ rcrl $1,%edx
+ jnc L_no_bit_lost
+
+ orl $1,%edx
+
+L_no_bit_lost:
+ incl EXP(%edi)
+
+L_round_the_result:
+ jmp fpu_reg_round /* Round the result */
+
+
+
+#ifdef PARANOID
+/* If we ever get here then we have problems! */
+L_bugged:
+ pushl EX_INTERNAL|0x201
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
+
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/arch/i386/math-emu/reg_u_div.S b/arch/i386/math-emu/reg_u_div.S
new file mode 100644
index 000000000..328e9116e
--- /dev/null
+++ b/arch/i386/math-emu/reg_u_div.S
@@ -0,0 +1,477 @@
+ .file "reg_u_div.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_div.S |
+ | |
+ | Core division routines |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Kernel for the division routines. |
+ | |
+ | void reg_u_div(FPU_REG *a, FPU_REG *a, |
+ | FPU_REG *dest, unsigned int control_word) |
+ | |
+ | Does not compute the destination exponent, but does adjust it. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+
+/* #define dSIGL(x) (x) */
+/* #define dSIGH(x) 4(x) */
+
+
+#ifndef NON_REENTRANT_FPU
+/*
+ Local storage on the stack:
+ Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+ Overflow flag: ovfl_flag
+ */
+#define FPU_accum_3 -4(%ebp)
+#define FPU_accum_2 -8(%ebp)
+#define FPU_accum_1 -12(%ebp)
+#define FPU_accum_0 -16(%ebp)
+#define FPU_result_1 -20(%ebp)
+#define FPU_result_2 -24(%ebp)
+#define FPU_ovfl_flag -28(%ebp)
+
+#else
+.data
+/*
+ Local storage in a static area:
+ Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
+ Overflow flag: ovfl_flag
+ */
+ .align 2,0
+FPU_accum_3:
+ .long 0
+FPU_accum_2:
+ .long 0
+FPU_accum_1:
+ .long 0
+FPU_accum_0:
+ .long 0
+FPU_result_1:
+ .long 0
+FPU_result_2:
+ .long 0
+FPU_ovfl_flag:
+ .byte 0
+#endif NON_REENTRANT_FPU
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_div
+
+.globl _divide_kernel
+
+_reg_u_div:
+ pushl %ebp
+ movl %esp,%ebp
+#ifndef NON_REENTRANT_FPU
+ subl $28,%esp
+#endif NON_REENTRANT_FPU
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ebx /* pointer to denom */
+ movl PARAM3,%edi /* pointer to answer */
+
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%ebx),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+_divide_kernel:
+#ifdef PARANOID
+/* testl $0x80000000, SIGH(%esi) // Dividend */
+/* je L_bugged */
+ testl $0x80000000, SIGH(%ebx) /* Divisor */
+ je L_bugged
+#endif PARANOID
+
+/* Check if the divisor can be treated as having just 32 bits */
+ cmpl $0,SIGL(%ebx)
+ jnz L_Full_Division /* Can't do a quick divide */
+
+/* We should be able to zip through the division here */
+ movl SIGH(%ebx),%ecx /* The divisor */
+ movl SIGH(%esi),%edx /* Dividend */
+ movl SIGL(%esi),%eax /* Dividend */
+
+ cmpl %ecx,%edx
+ setaeb FPU_ovfl_flag /* Keep a record */
+ jb L_no_adjust
+
+ subl %ecx,%edx /* Prevent the overflow */
+
+L_no_adjust:
+ /* Divide the 64 bit number by the 32 bit denominator */
+ divl %ecx
+ movl %eax,FPU_result_2
+
+ /* Work on the remainder of the first division */
+ xorl %eax,%eax
+ divl %ecx
+ movl %eax,FPU_result_1
+
+ /* Work on the remainder of the 64 bit division */
+ xorl %eax,%eax
+ divl %ecx
+
+ testb $255,FPU_ovfl_flag /* was the num > denom ? */
+ je L_no_overflow
+
+ /* Do the shifting here */
+ /* increase the exponent */
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc /* To set the ms bit */
+ rcrl FPU_result_2
+ rcrl FPU_result_1
+ rcrl %eax
+
+L_no_overflow:
+ jmp LRound_precision /* Do the rounding as required */
+
+
+/*---------------------------------------------------------------------------+
+ | Divide: Return arg1/arg2 to arg3. |
+ | |
+ | This routine does not use the exponents of arg1 and arg2, but does |
+ | adjust the exponent of arg3. |
+ | |
+ | The maximum returned value is (ignoring exponents) |
+ | .ffffffff ffffffff |
+ | ------------------ = 1.ffffffff fffffffe |
+ | .80000000 00000000 |
+ | and the minimum is |
+ | .80000000 00000000 |
+ | ------------------ = .80000000 00000001 (rounded) |
+ | .ffffffff ffffffff |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+L_Full_Division:
+ /* Save extended dividend in local register */
+ movl SIGL(%esi),%eax
+ movl %eax,FPU_accum_2
+ movl SIGH(%esi),%eax
+ movl %eax,FPU_accum_3
+ xorl %eax,%eax
+ movl %eax,FPU_accum_1 /* zero the extension */
+ movl %eax,FPU_accum_0 /* zero the extension */
+
+ movl SIGL(%esi),%eax /* Get the current num */
+ movl SIGH(%esi),%edx
+
+/*----------------------------------------------------------------------*/
+/* Initialization done.
+ Do the first 32 bits. */
+
+ movb $0,FPU_ovfl_flag
+ cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
+ jb LLess_than_1
+ ja LGreater_than_1
+
+ cmpl SIGL(%ebx),%eax
+ jb LLess_than_1
+
+LGreater_than_1:
+/* The dividend is greater or equal, would cause overflow */
+ setaeb FPU_ovfl_flag /* Keep a record */
+
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx /* Prevent the overflow */
+ movl %eax,FPU_accum_2
+ movl %edx,FPU_accum_3
+
+LLess_than_1:
+/* At this point, we have a dividend < divisor, with a record of
+ adjustment in FPU_ovfl_flag */
+
+ /* We will divide by a number which is too large */
+ movl SIGH(%ebx),%ecx
+ addl $1,%ecx
+ jnc LFirst_div_not_1
+
+ /* here we need to divide by 100000000h,
+ i.e., no division at all.. */
+ mov %edx,%eax
+ jmp LFirst_div_done
+
+LFirst_div_not_1:
+ divl %ecx /* Divide the numerator by the augmented
+ denom ms dw */
+
+LFirst_div_done:
+ movl %eax,FPU_result_2 /* Put the result in the answer */
+
+ mull SIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,FPU_accum_2 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_3
+
+ movl FPU_result_2,%eax /* Get the result back */
+ mull SIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_2
+ sbbl $0,FPU_accum_3
+ je LDo_2nd_32_bits /* Must check for non-zero result here */
+
+#ifdef PARANOID
+ jb L_bugged_1
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ incl FPU_result_2 /* Correct the answer */
+
+ movl SIGL(%ebx),%eax
+ movl SIGH(%ebx),%edx
+ subl %eax,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_2
+
+#ifdef PARANOID
+ sbbl $0,FPU_accum_3
+ jne L_bugged_1 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* Half of the main problem is done, there is just a reduced numerator
+ to handle now.
+ Work with the second 32 bits, FPU_accum_0 not used from now on */
+LDo_2nd_32_bits:
+ movl FPU_accum_2,%edx /* get the reduced num */
+ movl FPU_accum_1,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl SIGH(%ebx),%edx
+ jb LDo_2nd_div
+ ja LPrevent_2nd_overflow
+
+ cmpl SIGL(%ebx),%eax
+ jb LDo_2nd_div
+
+LPrevent_2nd_overflow:
+/* The numerator is greater or equal, would cause overflow */
+ /* prevent overflow */
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx
+ movl %edx,FPU_accum_2
+ movl %eax,FPU_accum_1
+
+ incl FPU_result_2 /* Reflect the subtraction in the answer */
+
+#ifdef PARANOID
+ je L_bugged_2 /* Can't bump the result to 1.0 */
+#endif PARANOID
+
+LDo_2nd_div:
+ cmpl $0,%ecx /* augmented denom msw */
+ jnz LSecond_div_not_1
+
+ /* %ecx == 0, we are dividing by 1.0 */
+ mov %edx,%eax
+ jmp LSecond_div_done
+
+LSecond_div_not_1:
+ divl %ecx /* Divide the numerator by the denom ms dw */
+
+LSecond_div_done:
+ movl %eax,FPU_result_1 /* Put the result in the answer */
+
+ mull SIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ movl FPU_result_1,%eax /* Get the result back */
+ mull SIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,FPU_accum_0 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
+ sbbl $0,FPU_accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+#endif PARANOID
+
+ jz LDo_3rd_32_bits
+
+#ifdef PARANOID
+ cmpl $1,FPU_accum_2
+ jne L_bugged_2
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ movl SIGL(%ebx),%eax
+ movl SIGH(%ebx),%edx
+ subl %eax,FPU_accum_0 /* Subtract from the num local reg */
+ sbbl %edx,FPU_accum_1
+ sbbl $0,FPU_accum_2
+
+#ifdef PARANOID
+ jc L_bugged_2
+ jne L_bugged_2
+#endif PARANOID
+
+ addl $1,FPU_result_1 /* Correct the answer */
+ adcl $0,FPU_result_2
+
+#ifdef PARANOID
+ jc L_bugged_2 /* Must check for non-zero result here */
+#endif PARANOID
+
+/*----------------------------------------------------------------------*/
+/* The division is essentially finished here, we just need to perform
+ tidying operations.
+ Deal with the 3rd 32 bits */
+LDo_3rd_32_bits:
+ movl FPU_accum_1,%edx /* get the reduced num */
+ movl FPU_accum_0,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl SIGH(%ebx),%edx /* denom */
+ jb LRound_prep
+ ja LPrevent_3rd_overflow
+
+ cmpl SIGL(%ebx),%eax /* denom */
+ jb LRound_prep
+
+LPrevent_3rd_overflow:
+ /* prevent overflow */
+ subl SIGL(%ebx),%eax
+ sbbl SIGH(%ebx),%edx
+ movl %edx,FPU_accum_1
+ movl %eax,FPU_accum_0
+
+ addl $1,FPU_result_1 /* Reflect the subtraction in the answer */
+ adcl $0,FPU_result_2
+ jne LRound_prep
+ jnc LRound_prep
+
+ /* This is a tricky spot, there is an overflow of the answer */
+ movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */
+
+LRound_prep:
+/*
+ * Prepare for rounding.
+ * To test for rounding, we just need to compare 2*accum with the
+ * denom.
+ */
+ movl FPU_accum_0,%ecx
+ movl FPU_accum_1,%edx
+ movl %ecx,%eax
+ orl %edx,%eax
+ jz LRound_ovfl /* The accumulator contains zero. */
+
+ /* Multiply by 2 */
+ clc
+ rcll $1,%ecx
+ rcll $1,%edx
+ jc LRound_large /* No need to compare, denom smaller */
+
+ subl SIGL(%ebx),%ecx
+ sbbl SIGH(%ebx),%edx
+ jnc LRound_not_small
+
+ movl $0x70000000,%eax /* Denom was larger */
+ jmp LRound_ovfl
+
+LRound_not_small:
+ jnz LRound_large
+
+ movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
+ jmp LRound_ovfl
+
+LRound_large:
+ movl $0xff000000,%eax /* Denom was smaller */
+
+LRound_ovfl:
+/* We are now ready to deal with rounding, but first we must get
+ the bits properly aligned */
+ testb $255,FPU_ovfl_flag /* was the num > denom ? */
+ je LRound_precision
+
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc /* Will set the ms bit */
+ rcrl FPU_result_2
+ rcrl FPU_result_1
+ rcrl %eax
+
+/* Round the result as required */
+LRound_precision:
+ decl EXP(%edi) /* binary point between 1st & 2nd bits */
+
+ movl %eax,%edx
+ movl FPU_result_1,%ebx
+ movl FPU_result_2,%eax
+ jmp fpu_reg_round
+
+
+#ifdef PARANOID
+/* The logic is wrong if we got here */
+L_bugged:
+ pushl EX_INTERNAL|0x202
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_1:
+ pushl EX_INTERNAL|0x203
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x204
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+
+ leave
+ ret
+#endif PARANOID
diff --git a/arch/i386/math-emu/reg_u_mul.S b/arch/i386/math-emu/reg_u_mul.S
new file mode 100644
index 000000000..8250666bd
--- /dev/null
+++ b/arch/i386/math-emu/reg_u_mul.S
@@ -0,0 +1,163 @@
+ .file "reg_u_mul.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_mul.S |
+ | |
+ | Core multiplication routine |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Basic multiplication routine. |
+ | Does not check the resulting exponent for overflow/underflow |
+ | |
+ | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); |
+ | |
+ | Internal working is at approx 128 bits. |
+ | Result is rounded to nearest 53 or 64 bits, using "nearest or even". |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+
+
+#ifndef NON_REENTRANT_FPU
+/* Local storage on the stack: */
+#define FPU_accum_0 -4(%ebp) /* ms word */
+#define FPU_accum_1 -8(%ebp)
+
+#else
+/* Local storage in a static area: */
+.data
+ .align 4,0
+FPU_accum_0:
+ .long 0
+FPU_accum_1:
+ .long 0
+#endif NON_REENTRANT_FPU
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_mul
+_reg_u_mul:
+ pushl %ebp
+ movl %esp,%ebp
+#ifndef NON_REENTRANT_FPU
+ subl $8,%esp
+#endif NON_REENTRANT_FPU
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%edi
+
+#ifdef PARANOID
+ testl $0x80000000,SIGH(%esi)
+ jz L_bugged
+ testl $0x80000000,SIGH(%edi)
+ jz L_bugged
+#endif PARANOID
+
+#ifdef DENORM_OPERAND
+ movl EXP(%esi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp1_not_denorm:
+ movl EXP(%edi),%eax
+ cmpl EXP_UNDER,%eax
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+ xorl %ecx,%ecx
+ xorl %ebx,%ebx
+
+ movl SIGL(%esi),%eax
+ mull SIGL(%edi)
+ movl %eax,FPU_accum_0
+ movl %edx,FPU_accum_1
+
+ movl SIGL(%esi),%eax
+ mull SIGH(%edi)
+ addl %eax,FPU_accum_1
+ adcl %edx,%ebx
+/* adcl $0,%ecx // overflow here is not possible */
+
+ movl SIGH(%esi),%eax
+ mull SIGL(%edi)
+ addl %eax,FPU_accum_1
+ adcl %edx,%ebx
+ adcl $0,%ecx
+
+ movl SIGH(%esi),%eax
+ mull SIGH(%edi)
+ addl %eax,%ebx
+ adcl %edx,%ecx
+
+ movl EXP(%esi),%eax /* Compute the exponent */
+ addl EXP(%edi),%eax
+ subl EXP_BIAS-1,%eax
+
+/* Have now finished with the sources */
+ movl PARAM3,%edi /* Point to the destination */
+ movl %eax,EXP(%edi)
+
+/* Now make sure that the result is normalized */
+ testl $0x80000000,%ecx
+ jnz LResult_Normalised
+
+ /* Normalize by shifting left one bit */
+ shll $1,FPU_accum_0
+ rcll $1,FPU_accum_1
+ rcll $1,%ebx
+ rcll $1,%ecx
+ decl EXP(%edi)
+
+LResult_Normalised:
+ movl FPU_accum_0,%eax
+ movl FPU_accum_1,%edx
+ orl %eax,%eax
+ jz L_extent_zero
+
+ orl $1,%edx
+
+L_extent_zero:
+ movl %ecx,%eax
+ jmp fpu_reg_round
+
+
+#ifdef PARANOID
+L_bugged:
+ pushl EX_INTERNAL|0x205
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+#endif PARANOID
+
diff --git a/arch/i386/math-emu/reg_u_sub.S b/arch/i386/math-emu/reg_u_sub.S
new file mode 100644
index 000000000..fbec17dfb
--- /dev/null
+++ b/arch/i386/math-emu/reg_u_sub.S
@@ -0,0 +1,292 @@
+ .file "reg_u_sub.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_sub.S |
+ | |
+ | Core floating point subtraction routine. |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |
+ | int control_w) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*
+ | Kernel subtraction routine reg_u_sub(reg *arg1, reg *arg2, reg *answ).
+ | Takes two valid reg f.p. numbers (TW_Valid), which are
+ | treated as unsigned numbers,
+ | and returns their difference as a TW_Valid or TW_Zero f.p.
+ | number.
+ | The first number (arg1) must be the larger.
+ | The returned number is normalized.
+ | Basic checks are performed if PARANOID is defined.
+ */
+
+#include "exception.h"
+#include "fpu_asm.h"
+#include "control_w.h"
+
+.text
+ .align 2,144
+.globl _reg_u_sub
+_reg_u_sub:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+#ifdef DENORM_OPERAND
+ cmpl EXP_UNDER,EXP(%esi)
+ jg xOp1_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp1_not_denorm:
+ cmpl EXP_UNDER,EXP(%edi)
+ jg xOp2_not_denorm
+
+ call _denormal_operand
+ orl %eax,%eax
+ jnz fpu_Arith_exit
+
+xOp2_not_denorm:
+#endif DENORM_OPERAND
+
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+
+#ifdef PARANOID
+ /* source 2 is always smaller than source 1 */
+ js L_bugged_1
+
+ testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */
+ je L_bugged_2
+
+ testl $0x80000000,SIGH(%esi)
+ je L_bugged_2
+#endif PARANOID
+
+/*--------------------------------------+
+ | Form a register holding the |
+ | smaller number |
+ +--------------------------------------*/
+ movl SIGH(%edi),%eax /* register ms word */
+ movl SIGL(%edi),%ebx /* register ls word */
+
+ movl PARAM3,%edi /* destination */
+ movl EXP(%esi),%edx
+ movl %edx,EXP(%edi) /* Copy exponent to destination */
+/* movb SIGN(%esi),%dl
+ movb %dl,SIGN(%edi) */ /* Copy the sign from the first arg */
+
+ xorl %edx,%edx /* register extension */
+
+/*--------------------------------------+
+ | Shift the temporary register |
+ | right the required number of |
+ | places. |
+ +--------------------------------------*/
+L_shift_r:
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ jmp L_shift_done
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ jz L_exactly_32
+
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ orl %ebx,%ebx
+ jz L_more_31_no_low /* none of the lowest bits is set */
+
+ orl $1,%edx /* record the fact in the extension */
+
+L_more_31_no_low:
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_exactly_32:
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $65,%cx
+ jnc L_more_than_64
+
+ /* Shift right by 64 bits */
+ movl %eax,%edx
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_64:
+ jne L_more_than_65
+
+ /* Shift right by 65 bits */
+ /* Carry is clear if we get here */
+ movl %eax,%edx
+ rcrl %edx
+ jnc L_shift_65_nc
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_shift_65_nc:
+ orl %ebx,%ebx
+ jz L_more_63_no_low
+
+ orl $1,%edx
+ jmp L_more_63_no_low
+
+L_more_than_65:
+ movl $1,%edx /* The shifted nr always at least one '1' */
+
+L_more_63_no_low:
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+
+L_shift_done:
+L_subtr:
+/*------------------------------+
+ | Do the subtraction |
+ +------------------------------*/
+ xorl %ecx,%ecx
+ subl %edx,%ecx
+ movl %ecx,%edx
+ movl SIGL(%esi),%ecx
+ sbbl %ebx,%ecx
+ movl %ecx,%ebx
+ movl SIGH(%esi),%ecx
+ sbbl %eax,%ecx
+ movl %ecx,%eax
+
+#ifdef PARANOID
+ /* We can never get a borrow */
+ jc L_bugged
+#endif PARANOID
+
+/*--------------------------------------+
+ | Normalize the result |
+ +--------------------------------------*/
+ testl $0x80000000,%eax
+ jnz L_round /* no shifting needed */
+
+ orl %eax,%eax
+ jnz L_shift_1 /* shift left 1 - 31 bits */
+
+ orl %ebx,%ebx
+ jnz L_shift_32 /* shift left 32 - 63 bits */
+
+/*
+ * A rare case, the only one which is non-zero if we got here
+ * is: 1000000 .... 0000
+ * -0111111 .... 1111 1
+ * --------------------
+ * 0000000 .... 0000 1
+ */
+
+ cmpl $0x80000000,%edx
+ jnz L_must_be_zero
+
+ /* Shift left 64 bits */
+ subl $64,EXP(%edi)
+ xchg %edx,%eax
+ jmp fpu_reg_round
+
+L_must_be_zero:
+#ifdef PARANOID
+ orl %edx,%edx
+ jnz L_bugged_3
+#endif PARANOID
+
+ /* The result is zero */
+ movb TW_Zero,TAG(%edi)
+ movl $0,EXP(%edi) /* exponent */
+ movl $0,SIGL(%edi)
+ movl $0,SIGH(%edi)
+ jmp L_exit /* %eax contains zero */
+
+L_shift_32:
+ movl %ebx,%eax
+ movl %edx,%ebx
+ movl $0,%edx
+ subl $32,EXP(%edi) /* Can get underflow here */
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %eax,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ shld %cl,%ebx,%eax
+ shld %cl,%edx,%ebx
+ shl %cl,%edx
+ subl %ecx,EXP(%edi) /* Can get underflow here */
+
+L_round:
+ jmp fpu_reg_round /* Round the result */
+
+
+#ifdef PARANOID
+L_bugged_1:
+ pushl EX_INTERNAL|0x206
+ call EXCEPTION
+ pop %ebx
+ jmp L_error_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x209
+ call EXCEPTION
+ pop %ebx
+ jmp L_error_exit
+
+L_bugged_3:
+ pushl EX_INTERNAL|0x210
+ call EXCEPTION
+ pop %ebx
+ jmp L_error_exit
+
+L_bugged_4:
+ pushl EX_INTERNAL|0x211
+ call EXCEPTION
+ pop %ebx
+ jmp L_error_exit
+
+L_bugged:
+ pushl EX_INTERNAL|0x212
+ call EXCEPTION
+ pop %ebx
+ jmp L_error_exit
+#endif PARANOID
+
+
+L_error_exit:
+ movl $1,%eax
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/arch/i386/math-emu/round_Xsig.S b/arch/i386/math-emu/round_Xsig.S
new file mode 100644
index 000000000..163755878
--- /dev/null
+++ b/arch/i386/math-emu/round_Xsig.S
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------------+
+ | round_Xsig.S |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Normalize and round a 12 byte quantity. |
+ | Call from C as: |
+ | int round_Xsig(Xsig *n) |
+ | |
+ | Normalize a 12 byte quantity. |
+ | Call from C as: |
+ | int norm_Xsig(Xsig *n) |
+ | |
+ | Each function returns the size of the shift (nr of bits). |
+ | |
+ +---------------------------------------------------------------------------*/
+ .file "round_Xsig.S"
+
+#include "fpu_asm.h"
+
+
+.text
+
+ .align 2,144
+.globl _round_Xsig
+
+_round_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx /* Reserve some space */
+ pushl %ebx
+ pushl %esi
+
+ movl PARAM1,%esi
+
+ movl 8(%esi),%edx
+ movl 4(%esi),%ebx
+ movl (%esi),%eax
+
+ movl $0,-4(%ebp)
+
+ orl %edx,%edx /* ms bits */
+ js L_round /* Already normalized */
+ jnz L_shift_1 /* Shift left 1 - 31 bits */
+
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ movl $-32,-4(%ebp)
+
+/* We need to shift left by 1 - 31 bits */
+L_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ subl %ecx,-4(%ebp)
+ shld %cl,%ebx,%edx
+ shld %cl,%eax,%ebx
+ shl %cl,%eax
+
+L_round:
+ testl $0x80000000,%eax
+ jz L_exit
+
+ addl $1,%ebx
+ adcl $0,%edx
+ jnz L_exit
+
+ movl $0x80000000,%edx
+ incl -4(%ebp)
+
+L_exit:
+ movl %edx,8(%esi)
+ movl %ebx,4(%esi)
+ movl %eax,(%esi)
+
+ movl -4(%ebp),%eax
+
+ popl %esi
+ popl %ebx
+ leave
+ ret
+
+
+
+
+ .align 2,144
+.globl _norm_Xsig
+
+_norm_Xsig:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx /* Reserve some space */
+ pushl %ebx
+ pushl %esi
+
+ movl PARAM1,%esi
+
+ movl 8(%esi),%edx
+ movl 4(%esi),%ebx
+ movl (%esi),%eax
+
+ movl $0,-4(%ebp)
+
+ orl %edx,%edx /* ms bits */
+ js L_n_exit /* Already normalized */
+ jnz L_n_shift_1 /* Shift left 1 - 31 bits */
+
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ movl $-32,-4(%ebp)
+
+ orl %edx,%edx /* ms bits */
+ js L_n_exit /* Normalized now */
+ jnz L_n_shift_1 /* Shift left 1 - 31 bits */
+
+ movl %ebx,%edx
+ movl %eax,%ebx
+ xorl %eax,%eax
+ addl $-32,-4(%ebp)
+ jmp L_n_exit /* Might not be normalized,
+ but shift no more. */
+
+/* We need to shift left by 1 - 31 bits */
+L_n_shift_1:
+ bsrl %edx,%ecx /* get the required shift in %ecx */
+ subl $31,%ecx
+ negl %ecx
+ subl %ecx,-4(%ebp)
+ shld %cl,%ebx,%edx
+ shld %cl,%eax,%ebx
+ shl %cl,%eax
+
+L_n_exit:
+ movl %edx,8(%esi)
+ movl %ebx,4(%esi)
+ movl %eax,(%esi)
+
+ movl -4(%ebp),%eax
+
+ popl %esi
+ popl %ebx
+ leave
+ ret
+
diff --git a/arch/i386/math-emu/shr_Xsig.S b/arch/i386/math-emu/shr_Xsig.S
new file mode 100644
index 000000000..d6724a204
--- /dev/null
+++ b/arch/i386/math-emu/shr_Xsig.S
@@ -0,0 +1,90 @@
+ .file "shr_Xsig.S"
+/*---------------------------------------------------------------------------+
+ | shr_Xsig.S |
+ | |
+ | 12 byte right shift function |
+ | |
+ | Copyright (C) 1992,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void shr_Xsig(Xsig *arg, unsigned nr) |
+ | |
+ | Extended shift right function. |
+ | Fastest for small shifts. |
+ | Shifts the 12 byte quantity pointed to by the first arg (arg) |
+ | right by the number of bits specified by the second arg (nr). |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+ .globl _shr_Xsig
+_shr_Xsig:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ pushl %ebx
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%ebx /* midl */
+ movl 8(%esi),%edx /* msl */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ movl %eax,(%esi)
+ movl %ebx,4(%esi)
+ movl %edx,8(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ movl 4(%esi),%eax /* midl */
+ movl 8(%esi),%edx /* msl */
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ movl %eax,(%esi)
+ movl %edx,4(%esi)
+ movl $0,8(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_63:
+ cmpl $96,%ecx
+ jnc L_more_than_95
+
+ subb $64,%cl
+ movl 8(%esi),%eax /* msl */
+ shr %cl,%eax
+ xorl %edx,%edx
+ movl %eax,(%esi)
+ movl %edx,4(%esi)
+ movl %edx,8(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_95:
+ xorl %eax,%eax
+ movl %eax,(%esi)
+ movl %eax,4(%esi)
+ movl %eax,8(%esi)
+ popl %esi
+ leave
+ ret
diff --git a/arch/i386/math-emu/status_w.h b/arch/i386/math-emu/status_w.h
new file mode 100644
index 000000000..96607d0e1
--- /dev/null
+++ b/arch/i386/math-emu/status_w.h
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------------+
+ | status_w.h |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+#include "fpu_emu.h" /* for definition of PECULIAR_486 */
+
+#ifdef __ASSEMBLER__
+#define Const__(x) $##x
+#else
+#define Const__(x) x
+#endif
+
+#define SW_Backward Const__(0x8000) /* backward compatibility */
+#define SW_C3 Const__(0x4000) /* condition bit 3 */
+#define SW_Top Const__(0x3800) /* top of stack */
+#define SW_Top_Shift Const__(11) /* shift for top of stack bits */
+#define SW_C2 Const__(0x0400) /* condition bit 2 */
+#define SW_C1 Const__(0x0200) /* condition bit 1 */
+#define SW_C0 Const__(0x0100) /* condition bit 0 */
+#define SW_Summary Const__(0x0080) /* exception summary */
+#define SW_Stack_Fault Const__(0x0040) /* stack fault */
+#define SW_Precision Const__(0x0020) /* loss of precision */
+#define SW_Underflow Const__(0x0010) /* underflow */
+#define SW_Overflow Const__(0x0008) /* overflow */
+#define SW_Zero_Div Const__(0x0004) /* divide by zero */
+#define SW_Denorm_Op Const__(0x0002) /* denormalized operand */
+#define SW_Invalid Const__(0x0001) /* invalid operation */
+
+#define SW_Exc_Mask Const__(0x27f) /* Status word exception bit mask */
+
+#ifndef __ASSEMBLER__
+
+#define COMP_A_gt_B 1
+#define COMP_A_eq_B 2
+#define COMP_A_lt_B 3
+#define COMP_No_Comp 4
+#define COMP_Denormal 0x20
+#define COMP_NaN 0x40
+#define COMP_SNaN 0x80
+
+#define status_word() \
+ ((partial_status & ~SW_Top & 0xffff) | ((top << SW_Top_Shift) & SW_Top))
+#define setcc(cc) ({ \
+ partial_status &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
+ partial_status |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+
+#ifdef PECULIAR_486
+ /* Default, this conveys no information, but an 80486 does it. */
+ /* Clear the SW_C1 bit, "other bits undefined". */
+# define clear_C1() { partial_status &= ~SW_C1; }
+# else
+# define clear_C1()
+#endif PECULIAR_486
+
+#endif __ASSEMBLER__
+
+#endif _STATUS_H_
diff --git a/arch/i386/math-emu/version.h b/arch/i386/math-emu/version.h
new file mode 100644
index 000000000..4c75a4792
--- /dev/null
+++ b/arch/i386/math-emu/version.h
@@ -0,0 +1,12 @@
+/*---------------------------------------------------------------------------+
+ | version.h |
+ | |
+ | |
+ | Copyright (C) 1992,1993,1994 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#define FPU_VERSION "wm-FPU-emu version 1.20"
diff --git a/arch/i386/math-emu/wm_shrx.S b/arch/i386/math-emu/wm_shrx.S
new file mode 100644
index 000000000..bef0e1963
--- /dev/null
+++ b/arch/i386/math-emu/wm_shrx.S
@@ -0,0 +1,208 @@
+ .file "wm_shrx.S"
+/*---------------------------------------------------------------------------+
+ | wm_shrx.S |
+ | |
+ | 64 bit right shift functions |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | unsigned shrx(void *arg1, unsigned arg2) |
+ | and |
+ | unsigned shrxs(void *arg1, unsigned arg2) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+
+/*---------------------------------------------------------------------------+
+ | unsigned shrx(void *arg1, unsigned arg2) |
+ | |
+ | Extended shift right function. |
+ | Fastest for small shifts. |
+ | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
+ | right by the number of bits specified by the second arg (arg2). |
+ | Forms a 96 bit quantity from the 64 bit arg and eax: |
+ | [ 64 bit arg ][ eax ] |
+ | shift right ---------> |
+ | The eax register is initialized to 0 before the shifting. |
+ | Results returned in the 64 bit arg and eax. |
+ +---------------------------------------------------------------------------*/
+
+ .globl _shrx
+
+_shrx:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jnc L_more_than_31
+
+/* less than 32 bits */
+ pushl %ebx
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %eax,%eax /* extension */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ movl %ebx,(%esi)
+ movl %edx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+L_more_than_31:
+ cmpl $64,%ecx
+ jnc L_more_than_63
+
+ subb $32,%cl
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%edx /* msl */
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ movl %edx,(%esi)
+ movl $0,4(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_63:
+ cmpl $96,%ecx
+ jnc L_more_than_95
+
+ subb $64,%cl
+ movl 4(%esi),%eax /* msl */
+ shr %cl,%eax
+ xorl %edx,%edx
+ movl %edx,(%esi)
+ movl %edx,4(%esi)
+ popl %esi
+ leave
+ ret
+
+L_more_than_95:
+ xorl %eax,%eax
+ movl %eax,(%esi)
+ movl %eax,4(%esi)
+ popl %esi
+ leave
+ ret
+
+
+/*---------------------------------------------------------------------------+
+ | unsigned shrxs(void *arg1, unsigned arg2) |
+ | |
+ | Extended shift right function (optimized for small floating point |
+ | integers). |
+ | Shifts the 64 bit quantity pointed to by the first arg (arg1) |
+ | right by the number of bits specified by the second arg (arg2). |
+ | Forms a 96 bit quantity from the 64 bit arg and eax: |
+ | [ 64 bit arg ][ eax ] |
+ | shift right ---------> |
+ | The eax register is initialized to 0 before the shifting. |
+ | The lower 8 bits of eax are lost and replaced by a flag which is |
+ | set (to 0x01) if any bit, apart from the first one, is set in the |
+ | part which has been shifted out of the arg. |
+ | Results returned in the 64 bit arg and eax. |
+ +---------------------------------------------------------------------------*/
+ .globl _shrxs
+_shrxs:
+ push %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %ebx
+ movl PARAM2,%ecx
+ movl PARAM1,%esi
+ cmpl $64,%ecx /* shrd only works for 0..31 bits */
+ jnc Ls_more_than_63
+
+ cmpl $32,%ecx /* shrd only works for 0..31 bits */
+ jc Ls_less_than_32
+
+/* We got here without jumps by assuming that the most common requirement
+ is for small integers */
+/* Shift by [32..63] bits */
+ subb $32,%cl
+ movl (%esi),%eax /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %ebx,%ebx
+ shrd %cl,%eax,%ebx
+ shrd %cl,%edx,%eax
+ shr %cl,%edx
+ orl %ebx,%ebx /* test these 32 bits */
+ setne %bl
+ test $0x7fffffff,%eax /* and 31 bits here */
+ setne %bh
+ orw %bx,%bx /* Any of the 63 bit set ? */
+ setne %al
+ movl %edx,(%esi)
+ movl $0,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+/* Shift by [0..31] bits */
+Ls_less_than_32:
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%edx /* msl */
+ xorl %eax,%eax /* extension */
+ shrd %cl,%ebx,%eax
+ shrd %cl,%edx,%ebx
+ shr %cl,%edx
+ test $0x7fffffff,%eax /* only need to look at eax here */
+ setne %al
+ movl %ebx,(%esi)
+ movl %edx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+/* Shift by [64..95] bits */
+Ls_more_than_63:
+ cmpl $96,%ecx
+ jnc Ls_more_than_95
+
+ subb $64,%cl
+ movl (%esi),%ebx /* lsl */
+ movl 4(%esi),%eax /* msl */
+ xorl %edx,%edx /* extension */
+ shrd %cl,%ebx,%edx
+ shrd %cl,%eax,%ebx
+ shr %cl,%eax
+ orl %ebx,%edx
+ setne %bl
+ test $0x7fffffff,%eax /* only need to look at eax here */
+ setne %bh
+ orw %bx,%bx
+ setne %al
+ xorl %edx,%edx
+ movl %edx,(%esi) /* set to zero */
+ movl %edx,4(%esi) /* set to zero */
+ popl %ebx
+ popl %esi
+ leave
+ ret
+
+Ls_more_than_95:
+/* Shift by [96..inf) bits */
+ xorl %eax,%eax
+ movl (%esi),%ebx
+ orl 4(%esi),%ebx
+ setne %al
+ xorl %ebx,%ebx
+ movl %ebx,(%esi)
+ movl %ebx,4(%esi)
+ popl %ebx
+ popl %esi
+ leave
+ ret
diff --git a/arch/i386/math-emu/wm_sqrt.S b/arch/i386/math-emu/wm_sqrt.S
new file mode 100644
index 000000000..4e028cb80
--- /dev/null
+++ b/arch/i386/math-emu/wm_sqrt.S
@@ -0,0 +1,474 @@
+ .file "wm_sqrt.S"
+/*---------------------------------------------------------------------------+
+ | wm_sqrt.S |
+ | |
+ | Fixed point arithmetic square root evaluation. |
+ | |
+ | Copyright (C) 1992,1993 |
+ | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail billm@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void wm_sqrt(FPU_REG *n, unsigned int control_word) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | wm_sqrt(FPU_REG *n, unsigned int control_word) |
+ | returns the square root of n in n. |
+ | |
+ | Use Newton's method to compute the square root of a number, which must |
+ | be in the range [1.0 .. 4.0), to 64 bits accuracy. |
+ | Does not check the sign or tag of the argument. |
+ | Sets the exponent, but not the sign or tag of the result. |
+ | |
+ | The guess is kept in %esi:%edi |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+#ifndef NON_REENTRANT_FPU
+/* Local storage on the stack: */
+#define FPU_accum_3 -4(%ebp) /* ms word */
+#define FPU_accum_2 -8(%ebp)
+#define FPU_accum_1 -12(%ebp)
+#define FPU_accum_0 -16(%ebp)
+
+/*
+ * The de-normalised argument:
+ * sq_2 sq_1 sq_0
+ * b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
+ * ^ binary point here
+ */
+#define FPU_fsqrt_arg_2 -20(%ebp) /* ms word */
+#define FPU_fsqrt_arg_1 -24(%ebp)
+#define FPU_fsqrt_arg_0 -28(%ebp) /* ls word, at most the ms bit is set */
+
+#else
+/* Local storage in a static area: */
+.data
+ .align 4,0
+FPU_accum_3:
+ .long 0 /* ms word */
+FPU_accum_2:
+ .long 0
+FPU_accum_1:
+ .long 0
+FPU_accum_0:
+ .long 0
+
+/* The de-normalised argument:
+ sq_2 sq_1 sq_0
+ b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0
+ ^ binary point here
+ */
+FPU_fsqrt_arg_2:
+ .long 0 /* ms word */
+FPU_fsqrt_arg_1:
+ .long 0
+FPU_fsqrt_arg_0:
+ .long 0 /* ls word, at most the ms bit is set */
+#endif NON_REENTRANT_FPU
+
+
+.text
+ .align 2,144
+
+.globl _wm_sqrt
+_wm_sqrt:
+ pushl %ebp
+ movl %esp,%ebp
+#ifndef NON_REENTRANT_FPU
+ subl $28,%esp
+#endif NON_REENTRANT_FPU
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+
+ movl SIGH(%esi),%eax
+ movl SIGL(%esi),%ecx
+ xorl %edx,%edx
+
+/* We use a rough linear estimate for the first guess.. */
+
+ cmpl EXP_BIAS,EXP(%esi)
+ jnz sqrt_arg_ge_2
+
+ shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
+ rcrl $1,%ecx
+ rcrl $1,%edx
+
+sqrt_arg_ge_2:
+/* From here on, n is never accessed directly again until it is
+ replaced by the answer. */
+
+ movl %eax,FPU_fsqrt_arg_2 /* ms word of n */
+ movl %ecx,FPU_fsqrt_arg_1
+ movl %edx,FPU_fsqrt_arg_0
+
+/* Make a linear first estimate */
+ shrl $1,%eax
+ addl $0x40000000,%eax
+ movl $0xaaaaaaaa,%ecx
+ mull %ecx
+ shll %edx /* max result was 7fff... */
+ testl $0x80000000,%edx /* but min was 3fff... */
+ jnz sqrt_prelim_no_adjust
+
+ movl $0x80000000,%edx /* round up */
+
+sqrt_prelim_no_adjust:
+ movl %edx,%esi /* Our first guess */
+
+/* We have now computed (approx) (2 + x) / 3, which forms the basis
+ for a few iterations of Newton's method */
+
+ movl FPU_fsqrt_arg_2,%ecx /* ms word */
+
+/*
+ * From our initial estimate, three iterations are enough to get us
+ * to 30 bits or so. This will then allow two iterations at better
+ * precision to complete the process.
+ */
+
+/* Compute (g + n/g)/2 at each iteration (g is the guess). */
+ shrl %ecx /* Doing this first will prevent a divide */
+ /* overflow later. */
+
+ movl %ecx,%edx /* msw of the arg / 2 */
+ divl %esi /* current estimate */
+ shrl %esi /* divide by 2 */
+ addl %eax,%esi /* the new estimate */
+
+ movl %ecx,%edx
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+ movl %ecx,%edx
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+/*
+ * Now that an estimate accurate to about 30 bits has been obtained (in %esi),
+ * we improve it to 60 bits or so.
+ *
+ * The strategy from now on is to compute new estimates from
+ * guess := guess + (n - guess^2) / (2 * guess)
+ */
+
+/* First, find the square of the guess */
+ movl %esi,%eax
+ mull %esi
+/* guess^2 now in %edx:%eax */
+
+ movl FPU_fsqrt_arg_1,%ecx
+ subl %ecx,%eax
+ movl FPU_fsqrt_arg_2,%ecx /* ms word of normalized n */
+ sbbl %ecx,%edx
+ jnc sqrt_stage_2_positive
+
+/* Subtraction gives a negative result,
+ negate the result before division. */
+ notl %edx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%edx
+
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+ jmp sqrt_stage_2_finish
+
+sqrt_stage_2_positive:
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ notl %ecx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%ecx
+
+sqrt_stage_2_finish:
+ sarl $1,%ecx /* divide by 2 */
+ rcrl $1,%eax
+
+ /* Form the new estimate in %esi:%edi */
+ movl %eax,%edi
+ addl %ecx,%esi
+
+ jnz sqrt_stage_2_done /* result should be [1..2) */
+
+#ifdef PARANOID
+/* It should be possible to get here only if the arg is ffff....ffff */
+ cmp $0xffffffff,FPU_fsqrt_arg_1
+ jnz sqrt_stage_2_error
+#endif PARANOID
+
+/* The best rounded result. */
+ xorl %eax,%eax
+ decl %eax
+ movl %eax,%edi
+ movl %eax,%esi
+ movl $0x7fffffff,%eax
+ jmp sqrt_round_result
+
+#ifdef PARANOID
+sqrt_stage_2_error:
+ pushl EX_INTERNAL|0x213
+ call EXCEPTION
+#endif PARANOID
+
+sqrt_stage_2_done:
+
+/* Now the square root has been computed to better than 60 bits. */
+
+/* Find the square of the guess. */
+ movl %edi,%eax /* ls word of guess */
+ mull %edi
+ movl %edx,FPU_accum_1
+
+ movl %esi,%eax
+ mull %esi
+ movl %edx,FPU_accum_3
+ movl %eax,FPU_accum_2
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,FPU_accum_1
+ adcl %edx,FPU_accum_2
+ adcl $0,FPU_accum_3
+
+/* movl %esi,%eax */
+/* mull %edi */
+ addl %eax,FPU_accum_1
+ adcl %edx,FPU_accum_2
+ adcl $0,FPU_accum_3
+
+/* guess^2 now in FPU_accum_3:FPU_accum_2:FPU_accum_1 */
+
+ movl FPU_fsqrt_arg_0,%eax /* get normalized n */
+ subl %eax,FPU_accum_1
+ movl FPU_fsqrt_arg_1,%eax
+ sbbl %eax,FPU_accum_2
+ movl FPU_fsqrt_arg_2,%eax /* ms word of normalized n */
+ sbbl %eax,FPU_accum_3
+ jnc sqrt_stage_3_positive
+
+/* Subtraction gives a negative result,
+ negate the result before division */
+ notl FPU_accum_1
+ notl FPU_accum_2
+ notl FPU_accum_3
+ addl $1,FPU_accum_1
+ adcl $0,FPU_accum_2
+
+#ifdef PARANOID
+ adcl $0,FPU_accum_3 /* This must be zero */
+ jz sqrt_stage_3_no_error
+
+sqrt_stage_3_error:
+ pushl EX_INTERNAL|0x207
+ call EXCEPTION
+
+sqrt_stage_3_no_error:
+#endif PARANOID
+
+ movl FPU_accum_2,%edx
+ movl FPU_accum_1,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx /* divide by 2 */
+ rcrl $1,%eax
+
+ /* prepare to round the result */
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+ jmp sqrt_stage_3_finished
+
+sqrt_stage_3_positive:
+ movl FPU_accum_2,%edx
+ movl FPU_accum_1,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx /* divide by 2 */
+ rcrl $1,%eax
+
+ /* prepare to round the result */
+
+ notl %eax /* Negate the correction term */
+ notl %ecx
+ addl $1,%eax
+ adcl $0,%ecx /* carry here ==> correction == 0 */
+ adcl $0xffffffff,%esi
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+sqrt_stage_3_finished:
+
+/*
+ * The result in %esi:%edi:%esi should be good to about 90 bits here,
+ * and the rounding information here does not have sufficient accuracy
+ * in a few rare cases.
+ */
+ cmpl $0xffffffe0,%eax
+ ja sqrt_near_exact_x
+
+ cmpl $0x00000020,%eax
+ jb sqrt_near_exact
+
+ cmpl $0x7fffffe0,%eax
+ jb sqrt_round_result
+
+ cmpl $0x80000020,%eax
+ jb sqrt_get_more_precision
+
+sqrt_round_result:
+/* Set up for rounding operations */
+ movl %eax,%edx
+ movl %esi,%eax
+ movl %edi,%ebx
+ movl PARAM1,%edi
+ movl EXP_BIAS,EXP(%edi) /* Result is in [1.0 .. 2.0) */
+ movl PARAM2,%ecx
+ jmp fpu_reg_round_sqrt
+
+
+sqrt_near_exact_x:
+/* First, the estimate must be rounded up. */
+ addl $1,%edi
+ adcl $0,%esi
+
+sqrt_near_exact:
+/*
+ * This is an easy case because x^1/2 is monotonic.
+ * We need just find the square of our estimate, compare it
+ * with the argument, and deduce whether our estimate is
+ * above, below, or exact. We use the fact that the estimate
+ * is known to be accurate to about 90 bits.
+ */
+ movl %edi,%eax /* ls word of guess */
+ mull %edi
+ movl %edx,%ebx /* 2nd ls word of square */
+ movl %eax,%ecx /* ls word of square */
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,%ebx
+ addl %eax,%ebx
+
+#ifdef PARANOID
+ cmp $0xffffffb0,%ebx
+ jb sqrt_near_exact_ok
+
+ cmp $0x00000050,%ebx
+ ja sqrt_near_exact_ok
+
+ pushl EX_INTERNAL|0x214
+ call EXCEPTION
+
+sqrt_near_exact_ok:
+#endif PARANOID
+
+ or %ebx,%ebx
+ js sqrt_near_exact_small
+
+ jnz sqrt_near_exact_large
+
+ or %ebx,%edx
+ jnz sqrt_near_exact_large
+
+/* Our estimate is exactly the right answer */
+ xorl %eax,%eax
+ jmp sqrt_round_result
+
+sqrt_near_exact_small:
+/* Our estimate is too small */
+ movl $0x000000ff,%eax
+ jmp sqrt_round_result
+
+sqrt_near_exact_large:
+/* Our estimate is too large, we need to decrement it */
+ subl $1,%edi
+ sbbl $0,%esi
+ movl $0xffffff00,%eax
+ jmp sqrt_round_result
+
+
+sqrt_get_more_precision:
+/* This case is almost the same as the above, except we start
+ with an extra bit of precision in the estimate. */
+ stc /* The extra bit. */
+ rcll $1,%edi /* Shift the estimate left one bit */
+ rcll $1,%esi
+
+ movl %edi,%eax /* ls word of guess */
+ mull %edi
+ movl %edx,%ebx /* 2nd ls word of square */
+ movl %eax,%ecx /* ls word of square */
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,%ebx
+ addl %eax,%ebx
+
+/* Put our estimate back to its original value */
+ stc /* The ms bit. */
+ rcrl $1,%esi /* Shift the estimate left one bit */
+ rcrl $1,%edi
+
+#ifdef PARANOID
+ cmp $0xffffff60,%ebx
+ jb sqrt_more_prec_ok
+
+ cmp $0x000000a0,%ebx
+ ja sqrt_more_prec_ok
+
+ pushl EX_INTERNAL|0x215
+ call EXCEPTION
+
+sqrt_more_prec_ok:
+#endif PARANOID
+
+ or %ebx,%ebx
+ js sqrt_more_prec_small
+
+ jnz sqrt_more_prec_large
+
+ or %ebx,%ecx
+ jnz sqrt_more_prec_large
+
+/* Our estimate is exactly the right answer */
+ movl $0x80000000,%eax
+ jmp sqrt_round_result
+
+sqrt_more_prec_small:
+/* Our estimate is too small */
+ movl $0x800000ff,%eax
+ jmp sqrt_round_result
+
+sqrt_more_prec_large:
+/* Our estimate is too large */
+ movl $0x7fffff00,%eax
+ jmp sqrt_round_result
diff --git a/arch/i386/mm/Makefile b/arch/i386/mm/Makefile
index 5063d60c2..af75a20a4 100644
--- a/arch/i386/mm/Makefile
+++ b/arch/i386/mm/Makefile
@@ -1,5 +1,5 @@
#
-# Makefile for the linux memory manager.
+# Makefile for the linux i386-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 = init.o fault.o
mm.o: $(OBJS)
$(LD) -r -o mm.o $(OBJS)
+modules:
+
dep:
$(CPP) -M *.c > .depend
diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c
new file mode 100644
index 000000000..01c259f0f
--- /dev/null
+++ b/arch/i386/mm/fault.c
@@ -0,0 +1,135 @@
+/*
+ * linux/arch/i386/mm/fault.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+#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.
+ *
+ * error_code:
+ * bit 0 == 0 means no page found, 1 means protection fault
+ * bit 1 == 0 means read, 1 means write
+ * bit 2 == 0 means kernel, 1 means user-mode
+ */
+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__("movl %%cr2,%0":"=r" (address));
+ 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:
+ /*
+ * was it a write?
+ */
+ if (error_code & 2) {
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ /* read with protection fault? */
+ if (error_code & 1)
+ goto bad_area;
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+ /*
+ * Did it hit the DOS screen memory VA from vm86 mode?
+ */
+ if (regs->eflags & VM_MASK) {
+ unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;
+ if (bit < 32)
+ current->tss.screen_bitmap |= 1 << bit;
+ }
+ if (error_code & 1) {
+#ifdef CONFIG_TEST_VERIFY_AREA
+ if (regs->cs == KERNEL_CS)
+ printk("WP fault at %08x\n", regs->eip);
+#endif
+ do_wp_page(vma, address, error_code & 2);
+ return;
+ }
+ do_no_page(vma, address, error_code & 2);
+ 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 & 4) {
+ current->tss.cr2 = 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.
+ *
+ * First we check if it was the bootup rw-test, though..
+ */
+ if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) {
+ wp_works_ok = 1;
+ pg0[0] = pte_val(mk_pte(0, PAGE_SHARED));
+ invalidate();
+ 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] = pte_val(mk_pte(0, PAGE_SHARED));
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ __asm__("movl %%cr3,%0" : "=r" (page));
+ printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n",
+ current->tss.cr3, page);
+ page = ((unsigned long *) page)[address >> 22];
+ printk(KERN_ALERT "*pde = %08lx\n", page);
+ if (page & 1) {
+ 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);
+}
diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c
new file mode 100644
index 000000000..296595644
--- /dev/null
+++ b/arch/i386/mm/init.c
@@ -0,0 +1,239 @@
+/*
+ * linux/arch/i386/mm/init.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+#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 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);
+
+/*
+ * 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];
+
+ __asm__ __volatile__("cld ; rep ; stosl":
+ :"a" (pte_val(BAD_PAGE)),
+ "D" ((long) empty_bad_page_table),
+ "c" (PAGE_SIZE/4)
+ :"di","cx");
+ return (pte_t *) empty_bad_page_table;
+}
+
+pte_t __bad_page(void)
+{
+ extern char empty_bad_page[PAGE_SIZE];
+
+ __asm__ __volatile__("cld ; rep ; stosl":
+ :"a" (0),
+ "D" ((long) empty_bad_page),
+ "c" (PAGE_SIZE/4)
+ :"di","cx");
+ return pte_mkdirty(mk_pte((unsigned long) empty_bad_page, PAGE_SHARED));
+}
+
+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
+}
+
+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)
+{
+ pgd_t * pg_dir;
+ pte_t * pg_table;
+ unsigned long tmp;
+ unsigned long address;
+
+/*
+ * Physical page 0 is special; it's not touched by Linux since BIOS
+ * and SMM (for laptops with [34]86/SL chips) may need it. It is read
+ * and write protected to detect null pointer references in the
+ * kernel.
+ */
+#if 0
+ memset((void *) 0, 0, PAGE_SIZE);
+#endif
+ start_mem = PAGE_ALIGN(start_mem);
+ address = 0;
+ pg_dir = swapper_pg_dir;
+ while (address < end_mem) {
+ /* map the memory at virtual addr 0xC0000000 */
+ pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768]));
+ if (!pg_table) {
+ pg_table = (pte_t *) start_mem;
+ start_mem += PAGE_SIZE;
+ }
+
+ /* also map it temporarily at 0x0000000 for init */
+ pgd_val(pg_dir[0]) = _PAGE_TABLE | (unsigned long) pg_table;
+ pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table;
+ pg_dir++;
+ for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
+ if (address < end_mem)
+ *pg_table = mk_pte(address, PAGE_SHARED);
+ else
+ pte_clear(pg_table);
+ address += PAGE_SIZE;
+ }
+ }
+ invalidate();
+ return free_area_init(start_mem, end_mem);
+}
+
+void mem_init(unsigned long start_mem, unsigned long end_mem)
+{
+ unsigned long start_low_mem = PAGE_SIZE;
+ int codepages = 0;
+ int reservedpages = 0;
+ int datapages = 0;
+ unsigned long tmp;
+ extern int etext;
+
+ end_mem &= PAGE_MASK;
+ high_memory = end_mem;
+
+ /* clear the zero-page */
+ memset(empty_zero_page, 0, PAGE_SIZE);
+
+ /* mark usable pages in the mem_map[] */
+ start_low_mem = PAGE_ALIGN(start_low_mem);
+ start_mem = PAGE_ALIGN(start_mem);
+
+ /*
+ * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000.
+ * They seem to have done something stupid with the floppy
+ * controller as well..
+ */
+ while (start_low_mem < 0x9f000) {
+ mem_map[MAP_NR(start_low_mem)] = 0;
+ start_low_mem += PAGE_SIZE;
+ }
+
+ while (start_mem < high_memory) {
+ mem_map[MAP_NR(start_mem)] = 0;
+ start_mem += PAGE_SIZE;
+ }
+#ifdef CONFIG_SCSI
+ scsi_mem_init(high_memory);
+#endif
+#ifdef CONFIG_SOUND
+ sound_mem_init();
+#endif
+ for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) {
+ if (mem_map[MAP_NR(tmp)]) {
+ if (tmp >= 0xA0000 && tmp < 0x100000)
+ reservedpages++;
+ else if (tmp < (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));
+/* test if the WP bit is honoured in supervisor mode */
+ wp_works_ok = -1;
+ pg0[0] = pte_val(mk_pte(0, PAGE_READONLY));
+ invalidate();
+ __asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory");
+ pg0[0] = 0;
+ invalidate();
+ if (wp_works_ok < 0)
+ wp_works_ok = 0;
+#ifdef CONFIG_TEST_VERIFY_AREA
+ wp_works_ok = 0;
+#endif
+ 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/i386/mm/kmalloc.c b/arch/i386/mm/kmalloc.c
deleted file mode 100644
index 018f8db8f..000000000
--- a/arch/i386/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/i386/mm/memory.c b/arch/i386/mm/memory.c
deleted file mode 100644
index 3e5a67041..000000000
--- a/arch/i386/mm/memory.c
+++ /dev/null
@@ -1,1320 +0,0 @@
-/*
- * linux/mm/memory.c
- *
- * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
- */
-
-/*
- * demand-loading started 01.12.91 - seems it is high on the list of
- * things wanted, and it should be easy to implement. - Linus
- */
-
-/*
- * Ok, demand-loading was easy, shared pages a little bit tricker. Shared
- * pages started 02.12.91, seems to work. - Linus.
- *
- * Tested sharing by executing about 30 /bin/sh: under the old kernel it
- * would have taken more than the 6M I have free, but it worked well as
- * far as I could see.
- *
- * Also corrected some "invalidate()"s - I wasn't doing enough of them.
- */
-
-/*
- * Real VM (paging to/from disk) started 18.12.91. Much more work and
- * thought has to go into this. Oh, well..
- * 19.12.91 - works, somewhat. Sometimes I get faults, don't know why.
- * Found it. Everything seems to work now.
- * 20.12.91 - Ok, making the swap-device changeable like the root.
- */
-
-/*
- * 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>
-
-/*
- * Define this if things work differently on a i386 and a i486:
- * it will (on a i486) warn about kernel memory accesses that are
- * done without a 'verify_area(VERIFY_WRITE,..)'
- */
-#undef CONFIG_TEST_VERIFY_AREA
-
-unsigned long high_memory = 0;
-
-extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */
-
-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];
-
-#define copy_page(from,to) \
-__asm__("cld ; rep ; movsl": :"S" (from),"D" (to),"c" (1024):"cx","di","si")
-
-unsigned short * mem_map = NULL;
-
-#define CODE_SPACE(addr,p) ((addr) < (p)->end_code)
-
-/*
- * 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 (!pg_table)
- return;
- *page_dir = 0;
- if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
- 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_PRESENT)
- 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.cr3;
- 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.cr3 = (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.cr3;
- if (!pg_dir || pg_dir == (unsigned long) swapper_pg_dir) {
- printk("Trying to free kernel page-directory: not good\n");
- return;
- }
- tsk->tss.cr3 = (unsigned long) swapper_pg_dir;
- if (tsk == current)
- __asm__ __volatile__("movl %0,%%cr3": :"a" (tsk->tss.cr3));
- 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.cr3;
- mem_map[MAP_NR(pg_dir)]++;
- tsk->tss.cr3 = 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.cr3;
- tsk->tss.cr3 = 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)
- continue;
- if (old_pg_table >= high_memory || !(old_pg_table & PAGE_PRESENT)) {
- printk("copy_page_tables: bad page table: "
- "probable memory corruption\n");
- *old_page_dir = 0;
- 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_PRESENT)) {
- *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.cr3,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_PRESENT)) {
- 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) != 0) {
- *page_table = 0;
- if (PAGE_PRESENT & 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_PRESENT)) != PAGE_PRESENT) {
- 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.cr3,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_PRESENT & *dir)) {
- /* clear page needed here? SRB. */
- if (!(page_table = (unsigned long*) get_free_page(GFP_KERNEL))) {
- invalidate();
- return -ENOMEM;
- }
- if (PAGE_PRESENT & *dir) {
- 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 = *page_table) != 0) {
- *page_table = 0;
- if (page & PAGE_PRESENT) {
- 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_PRESENT)) != PAGE_PRESENT) {
- 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.cr3,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_PRESENT & *dir)) {
- /* 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 = *page_table) != 0) {
- *page_table = 0;
- if (PAGE_PRESENT & 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_PRESENT)) != PAGE_PRESENT)
- 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.cr3,address);
- if ((*page_table) & PAGE_PRESENT)
- 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 = 0;
- 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.cr3,address);
- if (PAGE_PRESENT & *page_table)
- page_table = (unsigned long *) (PAGE_MASK & *page_table);
- else {
- if (!(tmp = get_free_page(GFP_KERNEL)))
- return 0;
- if (PAGE_PRESENT & *page_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 = 0;
- invalidate();
- }
- *page_table = page | (PAGE_DIRTY | PAGE_PRIVATE);
-/* no need for invalidate */
- return page;
-}
-
-/*
- * 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.cr3,address);
- pte = *pde;
- if (!(pte & PAGE_PRESENT))
- 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_PRESENT))
- 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_PRESENT);
- 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.cr3,from_address);
- to_page = (unsigned long)PAGE_DIR_OFFSET(to_area->vm_task->tss.cr3,to_address);
-/* is there a page-directory at from? */
- from = *(unsigned long *) from_page;
- if (!(from & PAGE_PRESENT))
- return 0;
- from &= PAGE_MASK;
- from_page = from + PAGE_PTR(from_address);
- from = *(unsigned long *) from_page;
-/* is the page present? */
- if (!(from & PAGE_PRESENT))
- 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_PRESENT))
- 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.cr3,address);
- if (PAGE_PRESENT & *p)
- 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.cr3,address);
- if (PAGE_PRESENT & *p) {
- 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_PRESENT)
- 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__("movl %%cr2,%0":"=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 (regs->eflags & VM_MASK) {
- unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;
- if (bit < 32)
- current->screen_bitmap |= 1 << bit;
- }
- if (!(vma->vm_page_prot & PAGE_USER))
- goto bad_area;
- if (error_code & PAGE_PRESENT) {
- if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW)))
- goto bad_area;
-#ifdef CONFIG_TEST_VERIFY_AREA
- if (regs->cs == KERNEL_CS)
- printk("WP fault at %08x\n", regs->eip);
-#endif
- 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.cr2 = 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_PRESENT)) {
- wp_works_ok = 1;
- pg0[0] = PAGE_SHARED;
- invalidate();
- 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);
- __asm__("movl %%cr3,%0" : "=r" (page));
- printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n",
- current->tss.cr3, page);
- page = ((unsigned long *) page)[address >> 22];
- printk(KERN_ALERT "*pde = %08lx\n", page);
- if (page & PAGE_PRESENT) {
- 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];
-
- __asm__ __volatile__("cld ; rep ; stosl":
- :"a" (BAD_PAGE + PAGE_TABLE),
- "D" ((long) empty_bad_page_table),
- "c" (PTRS_PER_PAGE)
- :"di","cx");
- return (unsigned long) empty_bad_page_table;
-}
-
-unsigned long __bad_page(void)
-{
- extern char empty_bad_page[PAGE_SIZE];
-
- __asm__ __volatile__("cld ; rep ; stosl":
- :"a" (0),
- "D" ((long) empty_bad_page),
- "c" (PTRS_PER_PAGE)
- :"di","cx");
- return (unsigned long) empty_bad_page;
-}
-
-unsigned long __zero_page(void)
-{
- extern char empty_zero_page[PAGE_SIZE];
-
- __asm__ __volatile__("cld ; rep ; stosl":
- :"a" (0),
- "D" ((long) empty_zero_page),
- "c" (PTRS_PER_PAGE)
- :"di","cx");
- 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
-}
-
-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;
-
-/*
- * Physical page 0 is special; it's not touched by Linux since BIOS
- * and SMM (for laptops with [34]86/SL chips) may need it. It is read
- * and write protected to detect null pointer references in the
- * kernel.
- */
-#if 0
- memset((void *) 0, 0, PAGE_SIZE);
-#endif
- start_mem = PAGE_ALIGN(start_mem);
- address = 0;
- pg_dir = swapper_pg_dir;
- while (address < end_mem) {
- tmp = *(pg_dir + 768); /* at virtual addr 0xC0000000 */
- if (!tmp) {
- tmp = start_mem | PAGE_TABLE;
- *(pg_dir + 768) = tmp;
- start_mem += PAGE_SIZE;
- }
- *pg_dir = tmp; /* also map it in at 0x0000000 for init */
- 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);
-}
-
-void mem_init(unsigned long start_low_mem,
- 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_low_mem = PAGE_ALIGN(start_low_mem);
- start_mem = PAGE_ALIGN(start_mem);
-
- /*
- * IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000.
- * They seem to have done something stupid with the floppy
- * controller as well..
- */
- while (start_low_mem < 0x9f000) {
- mem_map[MAP_NR(start_low_mem)] = 0;
- start_low_mem += PAGE_SIZE;
- }
-
- 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)]) {
- if (tmp >= 0xA0000 && tmp < 0x100000)
- reservedpages++;
- else if (tmp < (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));
-/* test if the WP bit is honoured in supervisor mode */
- wp_works_ok = -1;
- pg0[0] = PAGE_READONLY;
- invalidate();
- __asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory");
- pg0[0] = 0;
- invalidate();
- if (wp_works_ok < 0)
- wp_works_ok = 0;
-#ifdef CONFIG_TEST_VERIFY_AREA
- wp_works_ok = 0;
-#endif
- 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/i386/mm/mmap.c b/arch/i386/mm/mmap.c
deleted file mode 100644
index fbbea985c..000000000
--- a/arch/i386/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_PRESENT;
- 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 = &current->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/i386/mm/mprotect.c b/arch/i386/mm/mprotect.c
deleted file mode 100644
index 99252183b..000000000
--- a/arch/i386/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 | PAGE_PWT | PAGE_PCD)
-
-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.cr3, start);
- offset = (start >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
- nr = (end - start) >> PAGE_SHIFT;
- while (nr > 0) {
- page = *dir;
- dir++;
- if (!(page & PAGE_PRESENT)) {
- 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_PRESENT)
- *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_PRESENT;
- 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/i386/mm/swap.c b/arch/i386/mm/swap.c
deleted file mode 100644
index f7a1f54b3..000000000
--- a/arch/i386/mm/swap.c
+++ /dev/null
@@ -1,1017 +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) {
- __asm__ __volatile__ (
- "xchgl %0,%1\n"
- : "=m" (swap_cache[addr >> PAGE_SHIFT]),
- "=r" (entry)
- : "0" (swap_cache[addr >> PAGE_SHIFT]),
- "1" (entry));
- if (entry) {
- printk("swap_cache: replacing non-NULL entry\n");
- }
-#ifdef SWAP_CACHE_INFO
- swap_cache_add_success++;
-#endif
- return 1;
- }
- return 0;
-}
-
-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) {
- __asm__ __volatile__ (
- "xchgl %0,%1\n"
- : "=m" (swap_cache[addr >> PAGE_SHIFT]),
- "=r" (entry)
- : "0" (swap_cache[addr >> PAGE_SHIFT]),
- "1" (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_PRESENT;
- swap_free(entry);
- return page | PAGE_DIRTY | PAGE_PRESENT;
-}
-
-static inline int try_to_swap_out(unsigned long * table_ptr)
-{
- unsigned long page, entry;
-
- page = *table_ptr;
- if (!(PAGE_PRESENT & 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.cr3;
- 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_PRESENT & 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.cr3);
- page = *ppage;
- if (!page)
- continue;
- if (!(page & PAGE_PRESENT) || (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_PRESENT) {
- 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/i386/mm/vmalloc.c b/arch/i386/mm/vmalloc.c
deleted file mode 100644
index 0dbd16d54..000000000
--- a/arch/i386/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.cr3)[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_PRESENT & (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_PRESENT)
- 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/i386/sched.c b/arch/i386/sched.c
deleted file mode 100644
index 6eed6e8f5..000000000
--- a/arch/i386/sched.c
+++ /dev/null
@@ -1,861 +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 x86 = 0; /* set by boot/head.S to 3 or 4 */
-int ignore_irq13 = 0; /* set if exception 16 works */
-int wp_works_ok = 0; /* set if paging hardware honours WP */
-int hlt_works_ok = 1; /* set if the "hlt" instruction works */
-
-/*
- * 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 {
- long * a;
- short b;
- } stack_start = { & user_stack [PAGE_SIZE>>2] , KERNEL_DS };
-
-struct kernel_stat kstat = { 0 };
-
-/*
- * 'math_state_restore()' saves the current math information in the
- * old math state array, and gets the new ones from the current task
- *
- * Careful.. There are problems with IBM-designed IRQ13 behaviour.
- * Don't touch unless you *really* know how it works.
- */
-asmlinkage void math_state_restore(void)
-{
- __asm__ __volatile__("clts");
- if (last_task_used_math == current)
- return;
- timer_table[COPRO_TIMER].expires = jiffies+50;
- timer_active |= 1<<COPRO_TIMER;
- if (last_task_used_math)
- __asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
- else
- __asm__("fnclex");
- last_task_used_math = current;
- if (current->used_math) {
- __asm__("frstor %0": :"m" (current->tss.i387));
- } else {
- __asm__("fninit");
- current->used_math=1;
- }
- timer_active &= ~(1<<COPRO_TIMER);
-}
-
-#ifndef CONFIG_MATH_EMULATION
-
-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);
- /* Now maybe reload the debug registers */
- if(current->debugreg[7]){
- loaddebug(0);
- loaddebug(1);
- loaddebug(2);
- loaddebug(3);
- loaddebug(6);
- };
-}
-
-asmlinkage int sys_pause(void)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- return -ERESTARTNOHAND;
-}
-
-/*
- * wake_up doesn't wake up stopped processes - they have to be awakened
- * with signals or similar.
- *
- * Note that this doesn't need cli-sti pairs: interrupts may not change
- * the wait-queue structures directly, but only call wake_up() to wake
- * a process. The process itself must remove the queue once it has woken.
- */
-void wake_up(struct wait_queue **q)
-{
- struct wait_queue *tmp;
- struct task_struct * p;
-
- if (!q || !(tmp = *q))
- return;
- do {
- if ((p = tmp->task) != NULL) {
- if ((p->state == TASK_UNINTERRUPTIBLE) ||
- (p->state == TASK_INTERRUPTIBLE)) {
- p->state = TASK_RUNNING;
- if (p->counter > current->counter + 3)
- need_resched = 1;
- }
- }
- if (!tmp->next) {
- printk("wait_queue is bad (eip = %p)\n",
- __builtin_return_address(0));
- printk(" q = %p\n",q);
- printk(" *q = %p\n",*q);
- printk(" tmp = %p\n",tmp);
- break;
- }
- tmp = tmp->next;
- } while (tmp != *q);
-}
-
-void wake_up_interruptible(struct wait_queue **q)
-{
- struct wait_queue *tmp;
- struct task_struct * p;
-
- if (!q || !(tmp = *q))
- return;
- do {
- if ((p = tmp->task) != NULL) {
- if (p->state == TASK_INTERRUPTIBLE) {
- p->state = TASK_RUNNING;
- if (p->counter > current->counter + 3)
- need_resched = 1;
- }
- }
- if (!tmp->next) {
- printk("wait_queue is bad (eip = %p)\n",
- __builtin_return_address(0));
- printk(" q = %p\n",q);
- printk(" *q = %p\n",*q);
- printk(" tmp = %p\n",tmp);
- break;
- }
- tmp = tmp->next;
- } while (tmp != *q);
-}
-
-void __down(struct semaphore * sem)
-{
- struct wait_queue wait = { current, NULL };
- add_wait_queue(&sem->wait, &wait);
- current->state = TASK_UNINTERRUPTIBLE;
- while (sem->count <= 0) {
- schedule();
- current->state = TASK_UNINTERRUPTIBLE;
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&sem->wait, &wait);
-}
-
-static inline void __sleep_on(struct wait_queue **p, int state)
-{
- unsigned long flags;
- struct wait_queue wait = { current, NULL };
-
- if (!p)
- return;
- if (current == task[0])
- panic("task[0] trying to sleep");
- current->state = state;
- add_wait_queue(p, &wait);
- save_flags(flags);
- sti();
- schedule();
- remove_wait_queue(p, &wait);
- restore_flags(flags);
-}
-
-void interruptible_sleep_on(struct wait_queue **p)
-{
- __sleep_on(p,TASK_INTERRUPTIBLE);
-}
-
-void sleep_on(struct wait_queue **p)
-{
- __sleep_on(p,TASK_UNINTERRUPTIBLE);
-}
-
-/*
- * The head for the timer-list has a "expires" field of MAX_UINT,
- * and the sorting routine counts on this..
- */
-static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
-#define SLOW_BUT_DEBUGGING_TIMERS 1
-
-void add_timer(struct timer_list * timer)
-{
- unsigned long flags;
- struct timer_list *p;
-
-#if SLOW_BUT_DEBUGGING_TIMERS
- if (timer->next || timer->prev) {
- printk("add_timer() called with non-zero list from %p\n",
- __builtin_return_address(0));
- return;
- }
-#endif
- p = &timer_head;
- timer->expires += jiffies;
- save_flags(flags);
- cli();
- do {
- p = p->next;
- } while (timer->expires > p->expires);
- timer->next = p;
- timer->prev = p->prev;
- p->prev = timer;
- timer->prev->next = timer;
- restore_flags(flags);
-}
-
-int del_timer(struct timer_list * timer)
-{
- unsigned long flags;
-#if SLOW_BUT_DEBUGGING_TIMERS
- struct timer_list * p;
-
- p = &timer_head;
- save_flags(flags);
- cli();
- while ((p = p->next) != &timer_head) {
- if (p == timer) {
- timer->next->prev = timer->prev;
- timer->prev->next = timer->next;
- timer->next = timer->prev = NULL;
- restore_flags(flags);
- timer->expires -= jiffies;
- return 1;
- }
- }
- if (timer->next || timer->prev)
- printk("del_timer() called from %p with timer not initialized\n",
- __builtin_return_address(0));
- restore_flags(flags);
- return 0;
-#else
- save_flags(flags);
- cli();
- if (timer->next) {
- timer->next->prev = timer->prev;
- timer->prev->next = timer->next;
- timer->next = timer->prev = NULL;
- restore_flags(flags);
- timer->expires -= jiffies;
- return 1;
- }
- restore_flags(flags);
- return 0;
-#endif
-}
-
-unsigned long timer_active = 0;
-struct timer_struct timer_table[32];
-
-/*
- * Hmm.. Changed this, as the GNU make sources (load.c) seems to
- * imply that avenrun[] is the standard name for this kind of thing.
- * Nothing else seems to be standardized: the fractional size etc
- * all seem to differ on different machines.
- */
-unsigned long avenrun[3] = { 0,0,0 };
-
-/*
- * Nr of active tasks - counted in fixed-point numbers
- */
-static unsigned long count_active_tasks(void)
-{
- struct task_struct **p;
- unsigned long nr = 0;
-
- for(p = &LAST_TASK; p > &FIRST_TASK; --p)
- if (*p && ((*p)->state == TASK_RUNNING ||
- (*p)->state == TASK_UNINTERRUPTIBLE ||
- (*p)->state == TASK_SWAPPING))
- nr += FIXED_1;
- return nr;
-}
-
-static inline void calc_load(void)
-{
- unsigned long active_tasks; /* fixed-point */
- static int count = LOAD_FREQ;
-
- if (count-- > 0)
- return;
- count = LOAD_FREQ;
- active_tasks = count_active_tasks();
- CALC_LOAD(avenrun[0], EXP_1, active_tasks);
- CALC_LOAD(avenrun[1], EXP_5, active_tasks);
- CALC_LOAD(avenrun[2], EXP_15, active_tasks);
-}
-
-/*
- * this routine handles the overflow of the microsecond field
- *
- * The tricky bits of code to handle the accurate clock support
- * were provided by Dave Mills (Mills@UDEL.EDU) of NTP fame.
- * They were originally developed for SUN and DEC kernels.
- * All the kudos should go to Dave for this stuff.
- *
- * These were ported to Linux by Philip Gladstone.
- */
-static void second_overflow(void)
-{
- long ltemp;
- /* 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 ((VM_MASK & regs->eflags) || (3 & regs->cs)) {
- current->utime++;
- if (current != task[0]) {
- if (current->priority < 15)
- kstat.cpu_nice++;
- else
- kstat.cpu_user++;
- }
- /* Update ITIMER_VIRT for current task if not in a system call */
- if (current->it_virt_value && !(--current->it_virt_value)) {
- current->it_virt_value = current->it_virt_incr;
- send_sig(SIGVTALRM,current,1);
- }
- } else {
- current->stime++;
- if(current != task[0])
- kstat.cpu_system++;
-#ifdef CONFIG_PROFILE
- if (prof_buffer && current != task[0]) {
- 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.esp)[3]);
- 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)
-{
- int i;
- struct desc_struct * p;
-
- 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");
- set_tss_desc(gdt+FIRST_TSS_ENTRY,&init_task.tss);
- set_ldt_desc(gdt+FIRST_LDT_ENTRY,&default_ldt,1);
- set_system_gate(0x80,&system_call);
- p = gdt+2+FIRST_TSS_ENTRY;
- for(i=1 ; i<NR_TASKS ; i++) {
- task[i] = NULL;
- p->a=p->b=0;
- p++;
- p->a=p->b=0;
- p++;
- }
-/* Clear NT, so that we won't have troubles with that later on */
- __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
- load_TR(0);
- load_ldt(0);
- 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/.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 *)&regs->cp0_epc, ptr, 4, 0);
+ *ptr++ = ';';
+
+ /*
+ * Send frame pointer
+ */
+ *ptr++ = hexchars[REG_FP >> 4];
+ *ptr++ = hexchars[REG_FP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex((char *)&regs->reg30, ptr, 4, 0);
+ *ptr++ = ';';
+
+ /*
+ * Send stack pointer
+ */
+ *ptr++ = hexchars[REG_SP >> 4];
+ *ptr++ = hexchars[REG_SP & 0xf];
+ *ptr++ = ':';
+ ptr = mem2hex((char *)&regs->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 *)&regs->reg0, ptr, 32*4, 0); /* r0...r31 */
+ ptr = mem2hex((char *)&regs->cp0_status, ptr, 6*4, 0); /* cp0 */
+ ptr = mem2hex((char *)&regs->fpr0, ptr, 32*4, 0); /* f0...31 */
+ ptr = mem2hex((char *)&regs->cp1_fsr, ptr, 2*4, 0); /* cp1 */
+ ptr = mem2hex((char *)&regs->frame_ptr, ptr, 2*4, 0); /* frp */
+ ptr = mem2hex((char *)&regs->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, &regs);
+}
+
+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, &regs);
+}
+
+/*
+ * 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, &regs);
+ 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, &current->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 = &current->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
-}
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
new file mode 100644
index 000000000..88bcd6578
--- /dev/null
+++ b/arch/sparc/Makefile
@@ -0,0 +1,33 @@
+#
+# sparc/Makefile
+#
+# Makefile for the architecture dependent flags and dependencies on the
+# Sparc.
+#
+# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+#
+
+
+# If the solaris /bin/sh wasn't so broken, I wouldn't need the following
+# line...
+SHELL =/bin/bash
+
+#
+# How to link, we send the linker the address at which the text section
+# is to start. The prom loads us at 0x0-kernel_size. There is also an
+# alias of this address space at 0xf8000000-(0xf8000000+kernel_size) but
+# I ignore it and eliminate those mappings during vm initialization and
+# just leave the low mapping.
+#
+LINKFLAGS = -N -Ttext 0x00004000
+CFLAGS := $(CFLAGS) -pipe
+
+HEAD := arch/sparc/kernel/head.o
+
+SUBDIRS := $(SUBDIRS) arch/sparc/kernel arch/sparc/lib arch/sparc/mm
+ARCHIVES := arch/sparc/kernel/kernel.o arch/sparc/mm/mm.o $(ARCHIVES)
+LIBS := $(TOPDIR)/lib/lib.a $(LIBS) $(TOPDIR)/arch/sparc/lib/lib.a
+
+archclean:
+
+archdep:
diff --git a/arch/sparc/config.in b/arch/sparc/config.in
new file mode 100644
index 000000000..be9336eed
--- /dev/null
+++ b/arch/sparc/config.in
@@ -0,0 +1,246 @@
+#
+# arch/sparc/config.in
+#
+# Bare minimum configuration file for the Sparc.
+#
+# Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+
+comment 'Sparc Kernel setup'
+
+bool 'Sparc V8 kernel' CONFIG_SPARC_V8 y
+bool 'Sparc SMP support' CONFIG_LINUX_SMP n
+bool 'Sparc SUN4M support' CONFIG_SUN4M n
+bool 'Sparc Reference MMU' CONFIG_SRMMU n
+bool 'Networking support' CONFIG_NET n
+bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
+bool 'System V IPC' CONFIG_SYSVIPC y
+bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
+
+if [ "$CONFIG_NET" = "y" ]; then
+comment 'Networking options'
+bool 'TCP/IP networking' CONFIG_INET y
+if [ "$CONFIG_INET" "=" "y" ]; then
+bool 'IP forwarding/gatewaying' CONFIG_IP_FORWARD n
+bool 'IP multicasting (ALPHA)' CONFIG_IP_MULTICAST n
+bool 'IP firewalling' CONFIG_IP_FIREWALL n
+bool 'IP accounting' CONFIG_IP_ACCT n
+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
+fi
+bool 'The IPX protocol' CONFIG_IPX n
+#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
+fi
+
+comment 'SCSI support'
+
+bool 'SCSI support?' CONFIG_SCSI n
+
+if [ "$CONFIG_SCSI" = "n" ]; then
+
+comment 'Skipping SCSI configuration options...'
+
+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 n
+bool 'SCSI CDROM support' CONFIG_BLK_DEV_SR n
+bool 'SCSI generic support' CONFIG_CHR_DEV_SG 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 AHA274X/284X support' CONFIG_SCSI_AHA274X n
+bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC 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
+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
+bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE n
+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 '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
+
+
+if [ "$CONFIG_NET" = "y" ]; then
+
+comment 'Network device support'
+
+bool 'Network device support?' CONFIG_NETDEVICES y
+if [ "$CONFIG_NETDEVICES" = "n" ]; then
+
+comment 'Skipping network driver configuration options...'
+
+else
+bool 'Dummy net driver support' CONFIG_DUMMY n
+bool 'SLIP (serial line) support' CONFIG_SLIP n
+if [ "$CONFIG_SLIP" = "y" ]; then
+ bool ' CSLIP compressed headers' SL_COMPRESSED y
+ bool ' 16 channels instead of 4' SL_SLIP_LOTS n
+# bool ' SLIP debugging on' SL_DUMP y
+fi
+bool 'PPP (point-to-point) support' CONFIG_PPP n
+bool 'PLIP (parallel port) support' CONFIG_PLIP 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 n
+if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
+ bool 'WD80*3 support' CONFIG_WD80x3 n
+ bool 'SMC Ultra support' CONFIG_ULTRA n
+fi
+bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE n
+bool '3COM cards' CONFIG_NET_VENDOR_3COM y
+if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
+ bool '3c501 support' CONFIG_EL1 n
+ bool '3c503 support' CONFIG_EL2 n
+ if [ "$CONFIG_NET_ALPHA" = "y" ]; then
+ bool '3c505 support' CONFIG_ELPLUS n
+ bool '3c507 support' CONFIG_EL16 n
+ fi
+ bool '3c509/3c579 support' CONFIG_EL3 y
+fi
+bool 'Other ISA cards' CONFIG_NET_ISA n
+if [ "$CONFIG_NET_ISA" = "y" ]; then
+ 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 'Arcnet support' CONFIG_ARCNET 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
+ fi
+ 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
+ bool 'SK_G16 support' CONFIG_SK_G16 n
+fi
+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 '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 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 '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
+fi
+fi
+
+comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
+
+bool 'Sony CDU31A/CDU33A CDROM driver support' CONFIG_CDU31A n
+bool 'Mitsumi (not IDE/ATAPI) CDROM driver support' CONFIG_MCD n
+bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
+if [ "$CONFIG_SBPCD" = "y" ]; then
+ bool 'Matsushita/Panasonic second CDROM controller support' CONFIG_SBPCD2 n
+ if [ "$CONFIG_SBPCD2" = "y" ]; then
+ bool 'Matsushita/Panasonic third CDROM controller support' CONFIG_SBPCD3 n
+ if [ "$CONFIG_SBPCD3" = "y" ]; then
+ bool 'Matsushita/Panasonic fourth CDROM controller support' CONFIG_SBPCD4 n
+ fi
+ fi
+fi
+bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n
+
+comment 'Filesystems'
+
+bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
+bool 'Extended fs support' CONFIG_EXT_FS y
+bool 'Second extended fs support' CONFIG_EXT2_FS y
+bool 'xiafs filesystem support' CONFIG_XIA_FS y
+bool 'msdos fs support' CONFIG_MSDOS_FS y
+if [ "$CONFIG_MSDOS_FS" = "y" ]; then
+bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
+fi
+bool '/proc filesystem support' CONFIG_PROC_FS n
+if [ "$CONFIG_INET" = "y" ]; then
+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
+else
+ bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
+fi
+bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
+bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS y
+
+
+comment 'character devices'
+
+bool 'Cyclades async mux support' CONFIG_CYCLADES n
+bool 'Parallel printer support' CONFIG_PRINTER n
+bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
+bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
+if [ "$CONFIG_PSMOUSE" = "y" ]; then
+bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE y
+fi
+bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
+bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE 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 y
+if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+
+comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
+
+else
+
+comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
+comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
+
+fi
+fi
+
+bool 'QIC-117 tape support' CONFIG_FTAPE n
+if [ "$CONFIG_FTAPE" = "y" ]; then
+int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
+fi
+
+comment 'Sound'
+
+bool 'Sound card support' CONFIG_SOUND n
+
+comment 'Kernel hacking'
+
+#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
+fi
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
new file mode 100644
index 000000000..b5151d77e
--- /dev/null
+++ b/arch/sparc/kernel/Makefile
@@ -0,0 +1,47 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.S.s:
+ $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+.S.o:
+ $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+
+OBJS = entry.o traps.o irq.o process.o promops.o signal.o ioport.o setup.o \
+ idprom.o probe.o
+
+all: kernel.o head.o
+
+head.o: head.s
+
+head.s: head.S $(TOPDIR)/include/asm-sparc/head.h
+ $(CPP) -D__ASSEMBLY__ -ansi -o $*.s $<
+
+kernel.o: $(OBJS)
+ $(LD) -r -o kernel.o $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+
+dummy:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
new file mode 100644
index 000000000..21548015e
--- /dev/null
+++ b/arch/sparc/kernel/entry.S
@@ -0,0 +1,927 @@
+/* arch/sparc/kernel/entry.S: Sparc trap low-level entry points.
+ *
+ * Sparc traps are so ugly, this code is going to go through a lot
+ * of changes as I find out more interesting things. See head.S for
+ * the trap table and how it works, this will show you how we get
+ * to these routines.
+ *
+ * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/head.h>
+#include <asm/asi.h>
+#include <asm/psr.h>
+#include <asm/cprefix.h>
+#include <asm/vaddrs.h>
+
+/* Here are macros for routines we do often, this allows me to inline this
+ * without making the code look real ugly. Well, the macro looks ugly too but
+ * makes the trap entry code easier to understand.
+ */
+
+/* I really don't like synthetic instructions. So I avoid them like the
+ * plague.
+ */
+
+/* Note that when I have to write a window out, and it is a user's window, I
+ * have to check that the pages of memory that I am going to throw the window(s)
+ * onto are valid and are writable by the user (this is %sp to %sp + 64) before
+ * I start dumping stuff there. We always assume that kernels stack is ok.
+ *
+ * If we have to save a kernel window, only one branch is taken. This should
+ * make trap handlers quicker in this scenario.
+ *
+ * Once 'current' is loaded into %g6, it stays there until we leave
+ * this macro.
+ *
+ * XXX must do some checking on the assumption that kernel stack is always ok
+ */
+
+/* I will document how this works real soon. TODO */
+
+#define TRAP_WIN_CLEAN \
+ or %g0, %g5, %l5; /* we need the globals to do our work */ \
+ or %g0, %g6, %l6; /* and %l0 to %l4 are loaded with important */ \
+ or %g0, %g7, %l7; /* information like the psr and pc's to return to */ \
+ sethi %hi( C_LABEL(current) ), %g6; \
+ ld [%g6 + %lo( C_LABEL(current) )], %g6; \
+ ld [%g6 + THREAD_UWINDOWS], %g7; /* how many user wins are active? */ \
+ subcc %g7, 0x0, %g0; \
+ bne 2f; /* If there are any, branch. */ \
+ save %g0, %g0, %g0; /* Save into that window either way. */ \
+ std %l0, [%sp]; /* If above shows only kernel windows */ \
+1: std %l2, [%sp + 0x8]; /* then we get here. */ \
+ std %l4, [%sp + 0x10]; \
+ std %l6, [%sp + 0x18]; \
+ std %i0, [%sp + 0x20]; \
+ std %i2, [%sp + 0x28]; \
+ std %i4, [%sp + 0x30]; \
+ std %i6, [%sp + 0x38]; \
+ or %g0, 0x1, %g5; \
+ rd %psr, %g7; \
+ sll %g5, %g7, %g5; \
+ wr %g5, 0x0, %wim; /* update %wim to 'now' invalid */ \
+ and %g7, 0x1f, %g7; \
+ st %g7, [%g6 + THREAD_WIM]; /* save 'this' threads mask */ \
+ restore %g0, %g0, %g0; \
+ or %g0, %l5, %g5; /* restore the globals we used */ \
+ or %g0, %l6, %g6; \
+ b 8f; /* we are done */ \
+ or %g0, %l7, %g7; \
+2: sub %g7, 0x1, %g7; \
+ st %g7, [%g6 + THREAD_UWINDOWS]; /* There are user windows if we */ \
+ andcc %sp, 0x7, %g0; /* get here. Check for stack alignment. */ \
+ bne 5f; /* Stack is unaligned, yuck. */ \
+ sra %sp, 0x1e, %g7; /* This stuff checks to see if top 3-bits */ \
+ subcc %g7, 0x0, %g0; /* of stack pointer address are ok. */ \
+ be,a 3f; \
+ andn %sp, 0xfff, %g7; \
+ subcc %g7, -1, %g0; \
+ bne 5f; /* bad stack pointer, ugh */ \
+ andn %sp, 0xfff, %g7; \
+3: lda [%g7] ASI_PTE, %g7; /* Ok, user stack is a valid address */ \
+ srl %g7, 0x1d, %g7; \
+ subcc %g7, 0x6, %g0; /* Can the user write to it? */ \
+ bne 5f; \
+ and %sp, 0xfff, %g7; \
+ subcc %g7, 0xfc1, %g0; /* Is our save area on one page? */ \
+ bl,a 1b; \
+ std %l0, [%sp]; \
+ add %sp, 0x38, %g5; /* Nope, have to check both pages */ \
+ sra %g5, 0x1e, %g7; \
+ subcc %g7, 0x0, %g0; \
+ be,a 4f; \
+ andn %g5, 0xfff, %g7; \
+ subcc %g7, -1, %g0; \
+ bne 5f; \
+ andn %g5, 0xfff, %g7; \
+4: lda [%g7] ASI_PTE, %g7; /* Stack space in 2nd page is valid */ \
+ srl %g7, 0x1d, %g7; \
+ subcc %g7, 0x6, %g0; /* Can user write here too? */ \
+ be,a 1b; \
+ std %l0, [%sp]; \
+5: ld [%g6 + THREAD_UWINDOWS], %g7; /* This is due to either bad page perms */ \
+ add %g6, THREAD_REG_WINDOW, %g5; /* for the users stack area, or the stack */ \
+6: std %l0, [%g5]; /* pointer is misaligned. See above. */ \
+ std %l2, [%g5 + 0x8]; \
+ std %l4, [%g5 + 0x10]; \
+ std %l6, [%g5 + 0x18]; \
+ std %i0, [%g5 + 0x20]; \
+ std %i2, [%g5 + 0x28]; \
+ std %i4, [%g5 + 0x30]; \
+ std %i6, [%g5 + 0x38]; \
+ subcc %g7, 0x1, %g7; \
+ bge,a 6b; /* while(uwindows>=0) { write_win(); */ \
+ save %g5, 0x40, %g5; /* uwindows--; } */ \
+ st %sp, [%g6 + THREAD_USP]; \
+ or %g0, 0x1, %g5; \
+ rd %psr, %g7; \
+ sll %g5, %g7, %g5; \
+ wr %g5, 0x0, %wim; \
+ and %g7, 0x1f, %g7; \
+ st %g7, [%g6 + THREAD_WIM]; /* Update thread_struct fields */ \
+ ld [%g6 + THREAD_UWINDOWS], %g7; \
+ add %g7, 0x1, %g5; \
+ st %g5, [%g6 + THREAD_W_SAVED]; \
+ st %g0, [%g6 + THREAD_UWINDOWS]; \
+7: subcc %g7, 0x1, %g7; /* Restore back to where we started. */ \
+ bge 7b; \
+ restore %g0, %g0, %g0; \
+ or %g0, %l5, %g5; /* Restore the globals. */ \
+ or %g0, %l6, %g6; \
+ or %g0, %l7, %g7; \
+8: nop; /* We are done when we get here. */ \
+
+/* As if the last macro wasn't enough, we have to go through a very similar routine
+ * upon entry to most traps and interrupts. This is save away the current window
+ * if it is the trap window, clean it, and adjust the stack for the handler c-code
+ * to work.
+ */
+
+#define ENTER_TRAP \
+ rd %wim, %l4; \
+ or %g0, 0x1, %l5; \
+ sll %l5, %l0, %l5; \
+ andcc %l0, 0x40, %g0; \
+ bz 1f; \
+ andcc %l4, %l5, %g0; \
+ bz,a 3f; \
+ sub %fp, 0xb0, %sp; \
+ TRAP_WIN_CLEAN \
+ b 3f; \
+ sub %fp, 0xb0, %sp; \
+1: sethi %hi( C_LABEL(current) ), %l6; \
+ ld [%l6 + %lo( C_LABEL(current) )], %l6; \
+ ld [%l6 + THREAD_WIM], %l5; \
+ and %l0, 0x1f, %l4; \
+ cmp %l5, %l3; \
+ ble,a 4f; \
+ sethi %hi( C_LABEL(nwindowsm1) ), %l4; \
+ sub %l5, %l3, %l3; \
+ b 5f; \
+ sub %l3, 0x1, %l5; \
+4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \
+ sub %l4, %l3, %l4; \
+ add %l5, %l4, %l5; \
+5: st %l5, [%l6 + THREAD_UWINDOWS]; \
+ bz,a 2f; \
+ sethi %hi(TASK_SIZE-176), %l5; \
+ TRAP_WIN_CLEAN; \
+ sethi %hi( C_LABEL(current) ), %l6; \
+ ld [%l6 + %lo( C_LABEL(current) )], %l6; \
+ sethi %hi(TASK_SIZE-176), %l5; \
+2: or %l5, %lo(TASK_SIZE-176), %l5; \
+ add %l6, %l5, %sp; \
+3: \
+
+#define ENTER_IRQ \
+ rd %wim, %l4; \
+ or %g0, 0x1, %l5; \
+ sll %l5, %l0, %l5; \
+ andcc %l0, 0x40, %g0; \
+ bz 1f; \
+ andcc %l4, %l5, %g0; \
+ sethi %hi( C_LABEL(eintstack) ), %l7; \
+ or %l7, %lo( C_LABEL(eintstack) ), %l7; \
+ bz 0f; \
+ nop; \
+ TRAP_WIN_CLEAN \
+ sethi %hi( C_LABEL(eintstack) ), %l7; \
+ or %l7, %lo( C_LABEL(eintstack) ), %l7; \
+0: subcc %fp, %l7, %g0; \
+ bg,a 3f; \
+ sub %l7, 0xb0, %sp; \
+ b 3f; \
+ sub %fp, 0xb0, %sp; \
+1: sethi %hi( C_LABEL(current) ), %l6; \
+ ld [%l6 + %lo( C_LABEL(current) )], %l6; \
+ ld [%l6 + THREAD_WIM], %l5; \
+ and %l0, 0x1f, %l7; \
+ cmp %l5, %l7; \
+ ble,a 4f; \
+ sethi %hi( C_LABEL(nwindowsm1) ), %l4; \
+ sub %l5, %l7, %l7; \
+ b 5f; \
+ sub %l7, 0x1, %l5; \
+4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4; \
+ sub %l4, %l7, %l4; \
+ add %l5, %l4, %l5; \
+5: st %l5, [%l6 + THREAD_UWINDOWS]; \
+ bz,a 2f; \
+ sethi %hi( C_LABEL(eintstack) ), %l7; \
+ TRAP_WIN_CLEAN \
+ sethi %hi( C_LABEL(eintstack) ), %l7; \
+2: \
+ sub %l7, 0xb0, %sp; \
+3:
+
+
+ .text
+ .align 4
+
+/* Default trap handler */
+ .globl my_trap_handler
+my_trap_handler:
+#if 1
+ jmp %l1
+ rett %l2
+ nop
+#else
+ rd %wim, %l4
+ or %g0, 0x1, %l5
+ sll %l5, %l0, %l5
+ cmp %l4, %l5 ! are we in the invalid window?
+
+ TRAP_WIN_CLEAN
+
+ nop
+ or %g0, %l3, %o0
+ call C_LABEL(do_hw_interrupt)
+ or %g0, %g0, %o1
+ wr %l0, 0x20, %psr ! re-enable traps and reset the condition codes
+ nop
+ nop
+ nop ! click our heels three times, "no place like home"
+ jmp %l1
+ rett %l2
+#endif /* bogon */
+
+ .align 4
+ .globl sparc_timer
+sparc_timer:
+ sethi %hi(TIMER_VADDR), %l4
+ or %l4, %lo(TIMER_VADDR), %l4 ! read the limit register
+ ld [%l4 + 0xc], %l4 ! to clear the interrupt
+ rd %wim, %l4
+ or %g0, 0x1, %l5
+ sll %l5, %l0, %l5
+ andcc %l0, 0x40, %g0
+ bz st1
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ andcc %l4, %l5, %g0
+ bz st0
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+ TRAP_WIN_CLEAN
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+st0: subcc %fp, %l7, %g0
+ bg,a st3
+ sub %l7, 0xb0, %sp
+ b st3
+ sub %fp, 0xb0, %sp
+st1: sethi %hi( C_LABEL(current) ), %l6
+ ld [%l6 + %lo( C_LABEL(current) )], %l6
+ ld [%l6 + THREAD_WIM], %l5
+ and %l0, 0x1f, %l7
+ cmp %l5, %l7
+ ble,a st4
+ sethi %hi( C_LABEL(nwindowsm1) ), %l4
+ sub %l5, %l7, %l7
+ b st5
+ sub %l7, 0x1, %l5
+st4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
+ sub %l4, %l7, %l4
+ add %l5, %l4, %l5
+st5: st %l5, [%l6 + THREAD_UWINDOWS]
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ bz,a st2
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+ TRAP_WIN_CLEAN
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+st2: sub %l7, 0xb0, %sp
+
+st3: std %g2, [%sp + 96 + 24]
+ or %g0, %g1, %l7
+ rd %y, %l6
+ std %g4, [%sp + 96 + 32]
+ andn %l0, PSR_PIL, %l4
+ sll %l3, 0x8, %l5
+ std %g6, [%sp + 96 + 40]
+ or %l5, %l4, %l4
+
+ wr %l4, 0x0, %psr
+ wr %l4, PSR_ET, %psr
+
+ std %l0, [%sp + 96 + 0]
+ std %l2, [%sp + 96 + 8]
+ st %fp, [%sp + 96 + 16]
+
+ or %g0, 14, %o0
+ or %g0, %g0, %o1
+ call C_LABEL(do_sparc_timer)
+ nop
+
+ or %g0, %l7, %g1
+ wr %l6, 0x0, %y
+ ldd [%sp + 96 + 24], %g2
+ ldd [%sp + 96 + 32], %g4
+ ldd [%sp + 96 + 40], %g6
+ wr %l0, 0x0, %psr
+ nop
+ nop
+ nop
+
+ and %l0, 31, %l5
+ sethi %hi(lnx_winmask), %l6
+ or %l6, %lo(lnx_winmask), %l6
+ ldub [%l6 + %l5], %l5
+ andcc %l0, PSR_PS, %g0
+ bnz 1f
+ rd %wim, %l4
+
+1: andcc %l5, %l4, %g0
+ bnz 2f
+ wr %l0, 0x0, %psr
+ nop
+ nop
+ nop
+
+ jmp %l1
+ rett %l2
+
+2: wr %g0, 0x0, %wim
+ nop
+ nop
+ nop
+
+ restore
+ restore %g0, 0x1, %l1
+ rd %psr, %l0
+ and %l0, 31, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ sethi %hi( C_LABEL(current) ), %l1
+ ld [%l1 + %lo( C_LABEL(current) ) ], %l1
+ st %l0, [%l1 + THREAD_WIM]
+ save %g0, %g0, %g0
+
+ ldd [%sp], %l0
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+
+ save %g0, %g0, %g0
+
+ jmp %l1
+ rett %l2
+
+
+/* For now all IRQ's not registered get sent here so I can see
+ * what is poking the chip.
+ */
+
+ .align 4
+ .globl stray_irq_entry
+stray_irq_entry:
+ rd %wim, %l4
+ or %g0, 0x1, %l5
+ sll %l5, %l0, %l5
+ andcc %l0, 0x40, %g0
+ bz tt1
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ andcc %l4, %l5, %g0
+ bz tt0
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+ TRAP_WIN_CLEAN
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+tt0: subcc %fp, %l7, %g0
+ bg,a tt3
+ sub %l7, 0xb0, %sp
+ b tt3
+ sub %fp, 0xb0, %sp
+tt1: sethi %hi( C_LABEL(current) ), %l6
+ ld [%l6 + %lo( C_LABEL(current) )], %l6
+ ld [%l6 + THREAD_WIM], %l5
+ and %l0, 0x1f, %l7
+ cmp %l5, %l7
+ ble,a tt4
+ sethi %hi( C_LABEL(nwindowsm1) ), %l4
+ sub %l5, %l7, %l7
+ b tt5
+ sub %l7, 0x1, %l5
+tt4: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
+ sub %l4, %l7, %l4
+ add %l5, %l4, %l5
+tt5: st %l5, [%l6 + THREAD_UWINDOWS]
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ bz,a tt2
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+ TRAP_WIN_CLEAN
+ sethi %hi( C_LABEL(eintstack) ), %l7
+ or %l7, %lo( C_LABEL(eintstack) ), %l7
+tt2: sub %l7, 0xb0, %sp
+
+tt3: std %g2, [%sp + 96 + 24]
+ or %g0, %g1, %l7
+ rd %y, %l6
+ std %g4, [%sp + 96 + 32]
+ andn %l0, PSR_PIL, %l4
+ sll %l3, 0x8, %l5
+ std %g6, [%sp + 96 + 40]
+ or %l5, %l4, %l4
+
+ wr %l4, 0x0, %psr
+ wr %l4, PSR_ET, %psr
+
+ std %l0, [%sp + 96 + 0]
+ std %l2, [%sp + 96 + 8]
+ st %fp, [%sp + 96 + 16]
+
+ or %g0, %l3, %o0
+ or %g0, %g0, %o1
+ call C_LABEL(unexpected_irq)
+ nop
+
+ or %g0, %l7, %g1
+ wr %l6, 0x0, %y
+ ldd [%sp + 96 + 24], %g2
+ ldd [%sp + 96 + 32], %g4
+ ldd [%sp + 96 + 40], %g6
+ wr %l0, 0x0, %psr
+ nop
+ nop
+ nop
+
+ and %l0, 31, %l5
+ sethi %hi(lnx_winmask), %l6
+ or %l6, %lo(lnx_winmask), %l6
+ ldub [%l6 + %l5], %l5
+ andcc %l0, PSR_PS, %g0
+ bnz 1f
+ rd %wim, %l4
+
+1: andcc %l5, %l4, %g0
+ bnz 2f
+ wr %l0, 0x0, %psr
+ nop
+ nop
+ nop
+
+ jmp %l1
+ rett %l2
+
+2: wr %g0, 0x0, %wim
+ nop
+ nop
+ nop
+
+ restore
+ restore %g0, 0x1, %l1
+ rd %psr, %l0
+ and %l0, 31, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ sethi %hi( C_LABEL(current) ), %l1
+ ld [%l1 + %lo( C_LABEL(current) ) ], %l1
+ st %l0, [%l1 + THREAD_WIM]
+ save %g0, %g0, %g0
+
+ ldd [%sp], %l0
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+
+ save %g0, %g0, %g0
+
+ jmp %l1
+ rett %l2
+
+
+
+/* This routine is optimized for kernel window fills. User fills take about two
+ * or three extra jumps on the average. We'll see how this works out.
+ */
+
+/* Don't use local labels, or if you do be REAL CAREFUL. TRAP_WIN_CLEAN is
+ * full of them! If you think this routine is hairy, window spills are worse,
+ * see below.
+ */
+
+ .align 4
+ .globl spill_window_entry
+spill_window_entry:
+ andcc %l0, 0x40, %g0 ! see if this is a user window fill
+ bz,a spill_from_user
+ nop
+
+ TRAP_WIN_CLEAN /* danger, danger... */
+ wr %l0, 0x0, %psr
+ nop
+ jmp %l1
+ rett %l2
+
+spill_from_user:
+ sethi %hi( C_LABEL(current) ), %l6
+ ld [%l6 + %lo( C_LABEL(current) )], %l6
+ ld [%l6 + THREAD_WIM], %l5
+ and %l0, 0x1f, %l3
+
+/* I don't know what's worse, the extra comparison here, or an extra load
+ * from a lookup table, we'll see.
+ */
+ cmp %l5, %l3
+ ble,a 1f
+ sethi %hi( C_LABEL(nwindowsm1) ), %l4
+ sub %l5, %l3, %l3
+ b 2f
+ sub %l3, 0x1, %l5
+1: ld [%l4 + %lo( C_LABEL(nwindowsm1) )], %l4
+ sub %l4, %l3, %l4
+ add %l5, %l4, %l5
+2: st %l5, [%l6 + THREAD_UWINDOWS]
+
+ TRAP_WIN_CLEAN /* danger, danger... */
+ sethi %hi( C_LABEL(current) ), %l6
+ ld [%l6 + %lo( C_LABEL(current) )], %l6
+ ld [%l6 + THREAD_KSP], %sp
+ and %l0, 0x1f, %l3
+ sethi %hi(lnx_winmask), %l6
+ or %l6, %lo(lnx_winmask), %l6
+ ldub [%l6 + %l3], %l5
+ rd %wim, %l4
+ jmp %l1
+ rett %l2
+
+/* A window spill has occurred. This presents a weird situation, a restore
+ * was attempted and a trap occurred. Therefore the restore attempt had no
+ * effect on window movement and the trap saved, which means it went in the
+ * other direction. :-( We are in a trap window which is two restores away
+ * from the window we want to un-invalidate so to speak and three away from
+ * the one which will become invalid after this routine. There are probably
+ * bugs already this routine. Bugs suck.
+ */
+
+/* This is a very complicated and hairy routine, don't expect to understand
+ * it the first time. :>
+ */
+
+ .align 4
+ .globl fill_window_entry
+fill_window_entry:
+ wr %g0, 0, %wim ! Can not enter invalid register without this.
+ andcc %l0, 0x40, %g0 ! From user?
+ restore ! restore to where trap occurred
+ bz fill_from_user
+ restore ! enter invalid register, whee...
+ restore %g0, 0x1, %l1 ! enter one-past invalid register
+ rd %psr, %l0 ! this is the window we need to save
+ and %l0, 0x1f, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ sethi %hi( C_LABEL(current) ), %l1
+ ld [%l1 + %lo( C_LABEL(current) )], %l1
+ st %l0, [%l1 + THREAD_WIM]
+ save %g0, %g0, %g0 ! back to invalid register
+ ldd [%sp], %l0 ! load the window from stack
+ ldd [%sp + 8], %l2
+ ldd [%sp + 16], %l4
+ ldd [%sp + 24], %l6
+ ldd [%sp + 32], %i0
+ ldd [%sp + 40], %i2
+ ldd [%sp + 48], %i4
+ ldd [%sp + 56], %i6
+ save %g0, %g0, %g0 ! to window where trap happened
+ save %g0, %g0, %g0 ! back to trap window, so rett works
+ wr %l0, 0x0, %psr ! load condition codes
+ nop
+ jmp %l1
+ rett %l2 ! are you as confused as I am?
+
+fill_from_user:
+ andcc %sp, 0x7, %g0 ! check for alignment of user stack
+ bne fill_bad_stack
+ sra %sp, 0x1e, %l7
+ cmp %l7, 0x0
+ be,a 1f
+ andn %sp, 0xfff, %l7
+ cmp %l7, -1
+ bne fill_bad_stack
+ andn %sp, 0xfff, %l7
+1: lda [%l7] ASI_PTE, %l7
+ srl %l7, 0x1d, %l7
+ andn %l7, 0x2, %l7
+ cmp %l7, 0x4
+ bne fill_bad_stack
+ and %sp, 0xfff, %l7
+ cmp %l7, 0xfc1
+ bl,a fill_stack_ok
+ restore %g0, 1, %l1
+ add %sp, 0x38, %l5
+ sra %sp, 0x1e, %l7
+ cmp %l7, 0x0
+ be,a 1f
+ andn %sp, 0xfff, %l7
+ cmp %l7, -1
+ bne fill_bad_stack
+ andn %sp, 0xfff, %l7
+1: lda [%l7] ASI_PTE, %l7
+ srl %l7, 0x1d, %l7
+ andn %l7, 0x2, %l7
+ cmp %l7, 0x4
+ be,a fill_stack_ok
+ restore %g0, 0x1, %l1
+
+fill_bad_stack:
+ save %g0, %g0, %g0 ! save to where restore happened
+ save %g0, 0x1, %l4 ! save is an add remember? to trap window
+ sethi %hi( C_LABEL(current) ), %l6
+ ld [%l6 + %lo( C_LABEL(current) )], %l6
+ st %l4, [%l6 + THREAD_UWINDOWS] ! update current->tss values
+ ld [%l6 + THREAD_WIM], %l5
+ sll %l4, %l5, %l4
+ wr %l4, 0x0, %wim
+ ld [%l6 + THREAD_KSP], %sp ! set to kernel stack pointer
+ wr %l0, 0x20, %psr ! turn off traps
+ std %l0, [%sp + C_STACK] ! set up thread_frame on stack
+ rd %y, %l3
+ std %l2, [%sp + C_STACK + 0x8]
+ or %g0, 0x6, %o0 ! so _sparc_trap knows what to do
+ st %g1, [%sp + C_STACK + 0x14] ! no need to save %g0, always zero
+ or %g0, %l0, %o1
+ std %g2, [%sp + C_STACK + 0x18]
+ or %g0, %l1, %o2
+ std %g4, [%sp + C_STACK + 0x20]
+ add %sp, C_STACK, %o3
+ std %g6, [%sp + C_STACK + 0x28]
+ std %i0, [%sp + C_STACK + 0x30]
+ std %i2, [%sp + C_STACK + 0x38]
+ std %i4, [%sp + C_STACK + 0x40]
+ call sparc_trap
+ std %i6, [%sp + C_STACK + 0x48]
+
+ ldd [%sp + C_STACK], %l0
+ ldd [%sp + C_STACK + 0x8], %l2
+ wr %l3, 0, %y
+ ld [%sp + C_STACK + 0x14], %g1
+ ldd [%sp + C_STACK + 0x18], %g2
+ ldd [%sp + C_STACK + 0x20], %g4
+ ldd [%sp + C_STACK + 0x28], %g6
+ ldd [%sp + C_STACK + 0x30], %i0
+ ldd [%sp + C_STACK + 0x38], %i2
+ ldd [%sp + C_STACK + 0x40], %i4
+ wr %l0, 0, %psr ! disable traps again
+ ldd [%sp + C_STACK + 0x48], %i6
+ sethi %hi( C_LABEL(current) ), %l6
+ ld [%l6 + %lo( C_LABEL(current) )], %l6
+ ld [%l6 + THREAD_W_SAVED], %l7
+ cmp %l7, 0x0
+ bl,a 1f
+ wr %g0, 0x0, %wim
+ b,a leave_trap
+
+1: or %g0, %g6, %l3
+ or %g0, %l6, %g6
+ st %g0, [%g6 + THREAD_W_SAVED]
+ restore %g0, %g0, %g0
+ restore %g0, %g0, %g0
+ restore %g0, 0x1, %l1
+ rd %psr, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ and %l0, 0x1f, %l0
+ st %l0, [%g6 + THREAD_WIM]
+ nop
+ save %g0, %g0, %g0
+ ldd [%sp], %l0 ! load number one
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+ save %g0, %g0, %g0
+ ldd [%sp], %l0 ! load number two
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+ save %g0, %g0, %g0 ! re-enter trap window
+ wr %l0, 0x0, %psr ! restore condition codes
+ or %g0, %l3, %g6 ! restore scratch register
+ jmp %l1
+ rett %l2
+
+fill_stack_ok:
+ rd %psr, %l0
+ sll %l1, %l0, %l1
+ wr %l1, 0x0, %wim
+ sethi %hi( C_LABEL(current) ), %l2
+ ld [%l2 + %lo( C_LABEL(current) )], %l2
+ and %l0, 0x1f, %l0
+ st %l0, [%l2 + THREAD_WIM]
+ save %g0, %g0, %g0
+ ldd [%sp], %l0 ! only one load necessary
+ ldd [%sp + 0x8], %l2
+ ldd [%sp + 0x10], %l4
+ ldd [%sp + 0x18], %l6
+ ldd [%sp + 0x20], %i0
+ ldd [%sp + 0x28], %i2
+ ldd [%sp + 0x30], %i4
+ ldd [%sp + 0x38], %i6
+ save %g0, %g0, %g0
+ save %g0, %g0, %g0 ! save into trap window
+ wr %l0, 0x0, %psr ! local number 0 here has cond codes
+ nop
+ jmp %l1
+ rett %l2
+
+ .align 4
+ .globl trap_entry
+trap_entry:
+ TRAP_WIN_CLEAN
+ jmp %l1
+ rett %l2
+
+ .align 4
+ .globl linux_trap_nmi
+linux_trap_nmi:
+ TRAP_WIN_CLEAN
+ jmp %l1
+ rett %l2
+
+ .align 4
+ .globl sparc_trap
+sparc_trap:
+ TRAP_WIN_CLEAN
+ jmp %l1
+ rett %l2
+
+ .align 4
+ .globl leave_trap
+leave_trap:
+ jmp %l1
+ rett %l2
+
+/* The following two things point to window management tables. The first
+ one is used to quickly look up how many user windows there are from
+ trap-land. The second is used in a trap handler to determine if a rett
+ instruction will land us smack inside the invalid window that possibly
+ the trap was called to fix-up.
+*/
+
+/* For now these are static tables geared for a 7 window sparc. */
+
+ .data
+ .align 4
+lnx_winmask: .byte 2, 4, 8, 16, 32, 64, 128, 1 ! lnx_winmask[0..7]
+
+
+ .align 4
+ .globl C_LABEL(sys_call_table)
+C_LABEL(sys_call_table):
+ .long C_LABEL(sys_setup) /* 0 */
+ .long C_LABEL(sys_exit)
+ .long C_LABEL(sys_fork)
+ .long C_LABEL(sys_read)
+ .long C_LABEL(sys_write)
+ .long C_LABEL(sys_open) /* 5 */
+ .long C_LABEL(sys_close)
+ .long C_LABEL(sys_waitpid)
+ .long C_LABEL(sys_creat)
+ .long C_LABEL(sys_link)
+ .long C_LABEL(sys_unlink) /* 10 */
+ .long C_LABEL(sys_execve)
+ .long C_LABEL(sys_chdir)
+ .long C_LABEL(sys_time)
+ .long C_LABEL(sys_mknod)
+ .long C_LABEL(sys_chmod) /* 15 */
+ .long C_LABEL(sys_chown)
+ .long C_LABEL(sys_break)
+ .long C_LABEL(sys_stat)
+ .long C_LABEL(sys_lseek)
+ .long C_LABEL(sys_getpid) /* 20 */
+ .long C_LABEL(sys_mount)
+ .long C_LABEL(sys_umount)
+ .long C_LABEL(sys_setuid)
+ .long C_LABEL(sys_getuid)
+ .long C_LABEL(sys_stime) /* 25 */
+ .long C_LABEL(sys_ni_syscall) /* this will be sys_ptrace() */
+ .long C_LABEL(sys_alarm)
+ .long C_LABEL(sys_fstat)
+ .long C_LABEL(sys_pause)
+ .long C_LABEL(sys_utime) /* 30 */
+ .long C_LABEL(sys_stty)
+ .long C_LABEL(sys_gtty)
+ .long C_LABEL(sys_access)
+ .long C_LABEL(sys_nice)
+ .long C_LABEL(sys_ftime) /* 35 */
+ .long C_LABEL(sys_sync)
+ .long C_LABEL(sys_kill)
+ .long C_LABEL(sys_rename)
+ .long C_LABEL(sys_mkdir)
+ .long C_LABEL(sys_rmdir) /* 40 */
+ .long C_LABEL(sys_dup)
+ .long C_LABEL(sys_pipe)
+ .long C_LABEL(sys_times)
+ .long C_LABEL(sys_prof)
+ .long C_LABEL(sys_brk) /* 45 */
+ .long C_LABEL(sys_setgid)
+ .long C_LABEL(sys_getgid)
+ .long C_LABEL(sys_signal)
+ .long C_LABEL(sys_geteuid)
+ .long C_LABEL(sys_getegid) /* 50 */
+ .long C_LABEL(sys_acct)
+ .long C_LABEL(sys_phys)
+ .long C_LABEL(sys_lock)
+ .long C_LABEL(sys_ioctl)
+ .long C_LABEL(sys_fcntl) /* 55 */
+ .long C_LABEL(sys_mpx)
+ .long C_LABEL(sys_setpgid)
+ .long C_LABEL(sys_ulimit)
+ .long C_LABEL(sys_olduname)
+ .long C_LABEL(sys_umask) /* 60 */
+ .long C_LABEL(sys_chroot)
+ .long C_LABEL(sys_ustat)
+ .long C_LABEL(sys_dup2)
+ .long C_LABEL(sys_getppid)
+ .long C_LABEL(sys_getpgrp) /* 65 */
+ .long C_LABEL(sys_setsid)
+ .long C_LABEL(sys_sigaction)
+ .long C_LABEL(sys_sgetmask)
+ .long C_LABEL(sys_ssetmask)
+ .long C_LABEL(sys_setreuid) /* 70 */
+ .long C_LABEL(sys_setregid)
+ .long C_LABEL(sys_sigsuspend)
+ .long C_LABEL(sys_sigpending)
+ .long C_LABEL(sys_sethostname)
+ .long C_LABEL(sys_setrlimit) /* 75 */
+ .long C_LABEL(sys_getrlimit)
+ .long C_LABEL(sys_getrusage)
+ .long C_LABEL(sys_gettimeofday)
+ .long C_LABEL(sys_settimeofday)
+ .long C_LABEL(sys_getgroups) /* 80 */
+ .long C_LABEL(sys_setgroups)
+ .long C_LABEL(sys_select)
+ .long C_LABEL(sys_symlink)
+ .long C_LABEL(sys_lstat)
+ .long C_LABEL(sys_readlink) /* 85 */
+ .long C_LABEL(sys_uselib)
+ .long C_LABEL(sys_swapon)
+ .long C_LABEL(sys_reboot)
+ .long C_LABEL(sys_readdir)
+ .long C_LABEL(sys_mmap) /* 90 */
+ .long C_LABEL(sys_munmap)
+ .long C_LABEL(sys_truncate)
+ .long C_LABEL(sys_ftruncate)
+ .long C_LABEL(sys_fchmod)
+ .long C_LABEL(sys_fchown) /* 95 */
+ .long C_LABEL(sys_getpriority)
+ .long C_LABEL(sys_setpriority)
+ .long C_LABEL(sys_profil)
+ .long C_LABEL(sys_statfs)
+ .long C_LABEL(sys_fstatfs) /* 100 */
+ .long C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_socketcall)
+ .long C_LABEL(sys_syslog)
+ .long C_LABEL(sys_setitimer)
+ .long C_LABEL(sys_getitimer) /* 105 */
+ .long C_LABEL(sys_newstat)
+ .long C_LABEL(sys_newlstat)
+ .long C_LABEL(sys_newfstat)
+ .long C_LABEL(sys_uname)
+ .long C_LABEL(sys_ni_syscall) /* 110 */
+ .long C_LABEL(sys_vhangup)
+ .long C_LABEL(sys_idle)
+ .long C_LABEL(sys_ni_syscall) /* was vm86, meaningless on Sparc */
+ .long C_LABEL(sys_wait4)
+ .long C_LABEL(sys_swapoff) /* 115 */
+ .long C_LABEL(sys_sysinfo)
+ .long C_LABEL(sys_ipc)
+ .long C_LABEL(sys_fsync)
+ .long C_LABEL(sys_sigreturn)
+ .long C_LABEL(sys_ni_syscall) /* 120 */
+ .long C_LABEL(sys_setdomainname)
+ .long C_LABEL(sys_newuname)
+ .long C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_adjtimex)
+ .long C_LABEL(sys_mprotect) /* 125 */
+ .long C_LABEL(sys_sigprocmask)
+ .long C_LABEL(sys_create_module)
+ .long C_LABEL(sys_init_module)
+ .long C_LABEL(sys_delete_module)
+ .long C_LABEL(sys_get_kernel_syms) /* 130 */
+ .long C_LABEL(sys_ni_syscall)
+ .long C_LABEL(sys_getpgid)
+ .long C_LABEL(sys_fchdir)
+ .long C_LABEL(sys_bdflush)
+ .long C_LABEL(sys_sysfs) /* 135 */
+ .long C_LABEL(sys_personality)
+ .long 0 /* for afs_syscall */
+ .long C_LABEL(sys_setfsuid)
+ .long C_LABEL(sys_setfsgid)
+ .long C_LABEL(sys_llseek) /* 140 */
+ .align 4
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S
new file mode 100644
index 000000000..c3a5453e7
--- /dev/null
+++ b/arch/sparc/kernel/head.S
@@ -0,0 +1,1045 @@
+/* boot.S: The initial boot code for the Sparc port of Linux.
+
+ Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+
+ This file has to serve three purposes.
+
+ 1) determine the prom-version and cpu/architecture
+ 2) print enough useful info before we start to execute
+ c-code that I can possibly begin to debug things
+ 3) Hold the vector of trap entry points
+
+ The Sparc offers many challenges to kernel design. Here I will
+ document those I have come across thus far. Upon bootup the boot
+ prom loads your a.out image into memory. This memory the prom has
+ already mapped for you in two places, however as far as I can tell
+ the virtual address cache is not turned on although the MMU is
+ translating things. You get loaded at 0x4000 exactly and you are
+ aliased to 0xf8004000 with the appropriate mmu entries. So, when
+ you link a boot-loadable object you want to do something like:
+
+ ld -e start -Ttext 4000 -o mykernel myobj1.o myobj2.o ....
+
+ to produce a proper image.
+
+ At boot time you are given (as far as I can tell at this time)
+ one key to figure out what machine you are one and what devices
+ are available. The prom when it loads you leaves a pointer to
+ the 'rom vector' in register %o0 right before it jumps to your
+ starting address. This is a pointer to a struct that is full of
+ pointer to functions (ie. printf, halt, reboot), pointers to
+ linked lists (ie. memory mappings), and pointer to empirical
+ constants (ie. stdin and stdout magic cookies + rom version).
+ Starting with this piece of information you can figure out
+ just about anything you want about the machine you are on.
+
+ Although I don't use it now, if you are on a Multiprocessor and
+ therefore a v3 or above prom, register %o2 at boot contains a
+ function pointer you must call before you proceed to invoke the
+ other cpu's on the machine. I have no idea what kind of magic this
+ is, give me time.
+*/
+
+#include <asm/cprefix.h>
+#include <asm/head.h>
+#include <asm/version.h>
+#include <asm/asi.h>
+#include <asm/contregs.h>
+#include <asm/psr.h>
+#include <asm/page.h>
+
+ .data
+
+/* First thing to go in the data segment is the interrupt stack. */
+
+ .globl C_LABEL(intstack)
+ .globl C_LABEL(eintstack)
+C_LABEL(intstack):
+ .skip 4 * PAGE_SIZE ! 16k = 128 128-byte stack frames
+C_LABEL(eintstack):
+
+
+
+/*
+ The following are used with the prom_vector node-ops to figure out
+ the cpu-type
+*/
+
+ .globl C_LABEL(cputyp)
+
+C_LABEL(cputyp):
+ .word 1
+
+C_LABEL(cputypval):
+ .asciz "sun4c"
+ .ascii " "
+
+ .align 4
+/*
+ * Sun people can't spell worth damn. "compatability" indeed.
+ * At least we *know* we can't spell, and use a spell-checker.
+ */
+
+/* Uh, actually Linus it is I who cannot spell. Too much murky
+ * Sparc assembly will do this to ya.
+ */
+C_LABEL(cputypvar):
+ .asciz "compatability"
+
+C_LABEL(cputypvallen) = C_LABEL(cputypvar) - C_LABEL(cputypval)
+
+/* This hold the prom-interface-version number for either v0 or v2. */
+
+ .align 4
+ .globl C_LABEL(prom_iface_vers)
+
+C_LABEL(prom_iface_vers): .skip 4
+
+/* WARNING: evil messages follow */
+
+ .align 4
+
+sun4_notsup:
+ .asciz "Sparc-Linux: sun4 support not implemented yet\n\n"
+ .align 4
+
+sun4m_notsup:
+ .asciz "Sparc-Linux: sun4m support does not exist\n\n"
+ .align 4
+
+sun4d_notsup:
+ .asciz "Sparc-Linux: sun4d support does not exist\n\n"
+ .align 4
+
+you_lose:
+ .asciz "You lose..... Thanks for playing...\n"
+ .align 4
+
+
+ .globl boot_msg
+
+/* memory descriptor property strings, v2 = yuk yuk yuk */
+/* XXX how to figure out vm mapped by prom? May have to scan magic addresses */
+
+mem_prop_physavail: .asciz "available"
+
+ .align 4
+mem_prop_phystot: .asciz "reg"
+
+/* v2_memory descriptor struct kludged here for assembly, if it ain't broke */
+
+ .align 4
+v2_mem_struct: .skip 0xff
+
+ .align 4
+v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes"
+
+ .align 4
+v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes"
+
+/* A place to store property strings returned from the prom 'node' funcs */
+
+ .align 4
+prop_string_buf: .skip 32
+
+ .align 4
+prop_name: .asciz "name"
+
+ .align 4
+current_node: .skip 4
+
+
+/* nice little boot message */
+
+ .align 4
+boot_msg:
+ .ascii "Booting Sparc-Linux V0.00PRE-ALPHA "
+ .ascii WHO_COMPILED_ME
+ .ascii "\r\n"
+ .align 4
+
+ .globl boot_msg2
+
+boot_msg2:
+ .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\r\n\n"
+
+ .align 4
+
+pstring1:
+ .asciz "Prom Magic Cookie: 0x%x \n"
+ .align 4
+
+pstring2:
+ .asciz "Interface Version: v%d\n"
+ .align 4
+
+pstring3:
+ .asciz "Prom Revision: V%d\n\n"
+ .align 4
+
+pstring4:
+ .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n"
+ .asciz "Available Physical Memory: %d bytes\n"
+ .align 4
+
+
+ .text
+
+ .globl C_LABEL(msgbuf)
+msgbufsize = PAGE_SIZE ! 1 page for msg buffer
+C_LABEL(msgbuf) = PAGE_SIZE
+
+
+IE_reg_addr = C_LABEL(msgbuf) + msgbufsize ! this page not used; points to IEreg
+
+
+/* Ok, things start to get interesting. We get linked such that 'start'
+ is the entry symbol. However, it is real low in kernel address space
+ and as such a nifty place to place the trap table. We achieve this goal
+ by just jumping to 'gokernel' for the first trap's entry as the sparc
+ never receives the zero trap as it is real special (hw reset).
+
+ Each trap entry point is the size of 4 sparc instructions (or 4 bytes
+ * 4 insns = 16 bytes). There are 128 hardware traps (some undefined
+ or unimplemented) and 128 software traps (sys-calls, etc.).
+
+ One of the instructions must be a branch. More often than not this
+ will be to a trap handler entry point because it is completely
+ impossible to handle any trap in 4 insns. I welcome anyone to
+ challenge this theory. :-)
+
+ On entry into this table the hardware has loaded the program counter
+ at which the trap occurred into register %l1 and the next program
+ counter into %l2, this way we can return from the trap with a simple
+
+ jmp %l1; rett %l2 ! poof...
+
+ after properly servicing the trap. It wouldn't be a bad idea to load
+ some more information into the local regs since we have technically
+ 2 or 3 instructions to play with besides the jmp to the 'real' trap
+ handler (one can even go in the delay slot). For now I am going to put
+ the %psr (processor status register) and the trap-type value in %l0
+ and %l3 respectively. Also, for IRQ's I'll put the level in %l4.
+
+*/
+
+ .globl start
+ .globl _start /* warning, solaris hack */
+ .globl C_LABEL(trapbase)
+_start: /* danger danger */
+start:
+C_LABEL(trapbase):
+ b gokernel; nop; nop; nop; ! we never get trap #0 it is special
+
+ TRAP_ENTRY(0x1, my_trap_handler) /* Instruction Access Exception */
+ TRAP_ENTRY(0x2, my_trap_handler) /* Illegal Instruction */
+ TRAP_ENTRY(0x3, my_trap_handler) /* Privileged Instruction */
+ TRAP_ENTRY(0x4, my_trap_handler) /* Floating Point Disabled */
+ TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */
+ TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */
+ TRAP_ENTRY(0x7, my_trap_handler) /* Memory Address Not Aligned */
+ TRAP_ENTRY(0x8, my_trap_handler) /* Floating Point Exception */
+ TRAP_ENTRY(0x9, my_trap_handler) /* Data Miss Exception */
+ TRAP_ENTRY(0xa, my_trap_handler) /* Tagged Instruction Overflow */
+ TRAP_ENTRY(0xb, my_trap_handler) /* Watchpoint Detected */
+ TRAP_ENTRY(0xc, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0xd, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0xe, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0xf, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x10, my_trap_handler) /* Undefined... */
+
+/* Level'd interrupt entry points, see macro defs above */
+
+ TRAP_ENTRY_INTERRUPT_SOFT(1, 0x101) /* IRQ Software/SBUS Level 1 */
+ TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */
+ TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */
+ TRAP_ENTRY_INTERRUPT_SOFT(4, 0x104) /* IRQ Software Level 4 */
+ TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */
+ TRAP_ENTRY_INTERRUPT_SOFT(6, 0x106) /* IRQ Software Level 6 */
+ TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */
+ TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */
+ TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */
+ TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 */
+ TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */
+ TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */
+ TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */
+ TRAP_ENTRY_TIMER /* IRQ Timer #2 (one we use) */
+ TRAP_ENTRY_INTERRUPT_NMI(15, linux_trap_nmi) /* Level 15 (nmi) */
+
+ TRAP_ENTRY(0x20, my_trap_handler) /* General Register Access Error */
+ TRAP_ENTRY(0x21, my_trap_handler) /* Instruction Access Error */
+ TRAP_ENTRY(0x22, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x23, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x24, my_trap_handler) /* Co-Processor Disabled */
+ TRAP_ENTRY(0x25, my_trap_handler) /* Unimplemented FLUSH inst. */
+ TRAP_ENTRY(0x26, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x27, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x28, my_trap_handler) /* Co-Processor Exception */
+ TRAP_ENTRY(0x29, my_trap_handler) /* Data Access Error */
+ TRAP_ENTRY(0x2a, my_trap_handler) /* Division by zero, you lose... */
+ TRAP_ENTRY(0x2b, my_trap_handler) /* Data Store Error */
+ TRAP_ENTRY(0x2c, my_trap_handler) /* Data Access MMU-Miss */
+ TRAP_ENTRY(0x2d, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x2e, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x2f, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x30, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x31, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x32, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x33, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x34, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x35, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x36, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x37, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x38, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x39, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x3a, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x3b, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x3c, my_trap_handler) /* Instruction Access MMU-Miss */
+ TRAP_ENTRY(0x3d, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x3e, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x3f, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x40, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x41, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x42, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x43, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x44, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x45, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x46, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x47, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x48, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x49, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x4a, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x4b, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x4c, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x4d, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x4e, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x4f, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x50, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x51, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x52, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x53, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x54, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x55, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x56, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x57, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x58, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x59, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x5a, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x5b, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x5c, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x5d, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x5e, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x5f, my_trap_handler) /* Undefined... */
+ TRAP_ENTRY(0x60, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x61, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x62, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x63, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x64, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x65, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x66, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x67, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x68, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x69, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x6a, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x6b, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x6c, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x6d, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x6e, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x6f, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x70, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x71, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x72, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x73, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x74, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x75, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x76, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x77, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x78, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x79, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x7a, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x7b, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x7c, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x7d, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x7e, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x7f, my_trap_handler) /* Impl-Dep Exception */
+ TRAP_ENTRY(0x80, my_trap_handler) /* SunOS System Call */
+ TRAP_ENTRY(0x81, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x82, my_trap_handler) /* Divide by zero trap XXX */
+ TRAP_ENTRY(0x83, my_trap_handler) /* Flush Windows Trap XXX */
+ TRAP_ENTRY(0x84, my_trap_handler) /* Clean Windows Trap XXX */
+ TRAP_ENTRY(0x85, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x86, my_trap_handler) /* Fix Unaligned Access Trap XXX */
+ TRAP_ENTRY(0x87, my_trap_handler) /* Integer Overflow Trap XXX */
+ TRAP_ENTRY(0x88, my_trap_handler) /* Slowaris System Call */
+ TRAP_ENTRY(0x89, my_trap_handler) /* NetBSD System Call */
+ TRAP_ENTRY(0x8a, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x8b, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x8c, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x8d, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x8e, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x8f, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x90, my_trap_handler) /* SparcLinux System Call */
+ TRAP_ENTRY(0x91, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x92, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x93, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x94, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x95, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x96, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x97, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x98, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x99, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x9a, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x9b, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x9c, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x9d, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x9e, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0x9f, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa0, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa1, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa2, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa3, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa4, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa5, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa6, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa7, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa8, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xa9, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xaa, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xab, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xac, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xad, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xae, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xaf, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb0, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb1, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb2, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb3, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb4, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb5, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb6, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb7, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb8, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xb9, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xba, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xbb, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xbc, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xbd, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xbe, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xbf, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc0, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc1, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc2, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc3, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc4, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc5, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc6, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc7, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc8, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xc9, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xca, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xcb, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xcc, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xcd, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xce, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xcf, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd0, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd1, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd2, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd3, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd4, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd5, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd6, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd7, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd8, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xd9, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xda, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xdb, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xdc, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xdd, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xde, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xdf, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe0, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe1, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe2, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe3, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe4, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe5, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe6, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe7, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe8, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xe9, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xea, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xeb, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xec, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xed, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xee, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xef, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf0, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf1, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf2, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf3, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf4, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf5, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf6, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf7, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf8, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xf9, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xfa, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xfb, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xfc, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xfd, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xfe, my_trap_handler) /* Software Trap */
+ TRAP_ENTRY(0xff, my_trap_handler) /* Software Trap */
+
+ .skip 4096
+
+C_LABEL(msgbufmapped):
+ .word 1
+
+
+
+/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in
+ %g7 and at _prom_vector_p. And also quickly check whether we are on
+ a v0 or v2 prom.
+*/
+
+gokernel: or %g0, %o0, %g7
+ sethi %hi( C_LABEL(prom_vector_p) ), %g1
+ st %o0, [%g1 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later
+ rd %psr, %l2
+ rd %wim, %l3
+ rd %tbr, %l4
+ or %g0, %o2, %l5 ! could be prom magic value...
+
+#if 0 /* You think I'm nutz? */
+ subcc %l5, 0x0, %g0 ! check for magic SMP pointer
+ bne nosmp
+ nop
+ call %o2 ! call smp prom setup
+ nop
+#endif /* I will be soon... */
+
+/* Acquire boot time privileged register values, this will help debugging.
+ * I figure out and store nwindows later on.
+ */
+
+nosmp: sethi %hi( C_LABEL(boot_psr) ), %l1
+ st %l2, [%l1 + %lo( C_LABEL(boot_psr) )]
+ sethi %hi( C_LABEL(boot_wim) ), %l1
+ st %l3, [%l1 + %lo( C_LABEL(boot_wim) )]
+ sethi %hi( C_LABEL(boot_tbr) ), %l1
+ st %l4, [%l1 + %lo( C_LABEL(boot_tbr) )]
+ sethi %hi( C_LABEL(boot_smp_ptr) ), %l1
+ st %l5, [%l1 + %lo( C_LABEL(boot_smp_ptr) )]
+
+ or %g0, %o0, %g7
+ sethi %hi( C_LABEL(prom_vector_p) ), %g5
+ st %o0, [%g5 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later
+
+ ld [%g7 + 0x4], %o3
+ subcc %o3, 0x2, %g0 ! a v2 prom?
+ be found_v2
+ nop
+
+ /* paul@sfe.com.au */
+ subcc %o3, 0x3, %g0 ! a v3 prom?
+ or %g0, 0x3, %o5
+ sethi %hi(C_LABEL(prom_iface_vers) ), %g1
+ st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )]
+ be not_v2
+ nop
+
+
+/* Old sun4's pass our load address into %o0 instead of the prom
+ pointer. On sun4's you have to hard code the romvec pointer into
+ your code. Sun probably still does that because they don't even
+ trust their own "OpenBoot" specifications.
+*/
+
+ sethi %hi(LOAD_ADDR), %g6
+ subcc %o0, %g6, %g0 ! an old sun4?
+ be no_sun4_here
+ nop
+
+ sethi %hi( C_LABEL(prom_iface_vers) ), %g1
+ st %g0, [%g1 + %lo( C_LABEL(prom_iface_vers) )]
+ b not_v2
+ nop
+
+found_v2:
+ or %g0, 0x2, %o5
+ sethi %hi( C_LABEL(prom_iface_vers) ), %g1
+ st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )]
+
+not_v2:
+
+/* Get the machine type via the mysterious romvec node operations.
+ * Here we can find out whether we are on a sun4 sun4c, sun4m, or
+ * a sun4m. The "nodes" are set up as a bunch of n-ary trees which
+ * you can traverse to get information about devices and such. The
+ * information acquisition happens via the node-ops which are defined
+ * in the linux_openprom.h header file. Of particular interest is the
+ * 'nextnode(int node)' function as it does the smart thing when
+ * presented with a value of '0', it gives you the first node in the
+ * tree. These node integers probably offset into some internal prom
+ * pointer table the openboot has. It's completely undocumented, so
+ * I'm not about to go sifting through the prom address space, but may
+ * do so if I get suspicious enough. :-)
+ */
+
+ or %g0, %g7, %l1
+ add %l1, 0x1c, %l1
+ ld [%l1], %l0
+ ld [%l0], %l0
+ call %l0
+ or %g0, %g0, %o0 ! next_node(0) = first_node
+
+ sethi %hi( C_LABEL(cputypvar) ), %o1 ! first node has cpu-arch
+ or %o1, %lo( C_LABEL(cputypvar) ), %o1
+ sethi %hi( C_LABEL(cputypval) ), %o2 ! information, the string
+ or %o2, %lo( C_LABEL(cputypval) ), %o2
+ ld [%l1], %l0 ! 'compatibility' tells
+ ld [%l0 + 0xc], %l0 ! that we want 'sun4x' where
+ call %l0 ! x is one of '', 'c', 'm',
+ nop ! 'd' or 'e'. %o2 holds pointer
+ ! to a buf where above string
+ ! will get stored by the prom.
+
+ sethi %hi( C_LABEL(cputypval) ), %o2 ! better safe than sorry
+ or %o2, %lo( C_LABEL(cputypval) ), %o2
+ ldub [%o2 + 0x4], %o0
+ subcc %o0, 'c', %g0 ! we already know we are not
+ be is_sun4c ! on a plain sun4 because of
+ nop ! the check for 0x4000 in %o0
+ subcc %o0, 'm', %g0 ! at start:
+ be is_sun4m
+ nop
+ b no_sun4d_here ! god bless the person who
+ nop ! tried to run this on sun4d
+
+is_sun4m:
+is_sun4c: ! OK, this is a sun4c, yippie
+ or %g0, %g7, %g6 ! load up the promvec offsets
+ sethi %hi(prom_magic), %g5 ! magic mushroom :>
+ st %g6, [%g5 + %lo(prom_magic)]
+ add %g7, 0x4, %g6
+ sethi %hi(prom_rom_vers), %g5
+ st %g6, [%g5 + %lo(prom_rom_vers)]
+ add %g7, 0x8, %g6
+ sethi %hi(prom_pluginvers), %g5
+ st %g6, [%g5 + %lo(prom_pluginvers)]
+ add %g7, 0xc, %g6
+ sethi %hi(prom_revision), %g5
+ st %g6, [%g5 + %lo(prom_revision)]
+ add %g7, 0x10, %g6
+ sethi %hi(prom_v0mem_desc), %g5
+ st %g6, [%g5 + %lo(prom_v0mem_desc)]
+ add %g7, 0x1c, %g6
+ sethi %hi(prom_nodefuncs), %g5
+ st %g6, [%g5 + %lo(prom_nodefuncs)]
+ add %g7, 0x68, %g6
+ sethi %hi(prom_printf), %g5
+ st %g6, [%g5 + %lo(prom_printf)]
+ add %g7, 0x6c, %g6
+ sethi %hi(prom_abort), %g5
+ st %g6, [%g5 + %lo(prom_abort)]
+ add %g7, 0x74, %g6
+ sethi %hi(prom_halt), %g5
+ st %g6, [%g5 + %lo(prom_halt)]
+ add %g7, 0x78, %g6
+ sethi %hi(prom_sync), %g5
+ st %g6, [%g5 + %lo(prom_sync)]
+ add %g7, 0x7c, %g6
+ sethi %hi(prom_eval), %g5
+ st %g6, [%g5 + %lo(prom_eval)]
+ add %g7, 0x80, %g6
+ sethi %hi(prom_v0bootline), %g6
+ st %g6, [%g5 + %lo(prom_v0bootline)]
+
+
+/* That was easy, now lets try to print some message on the screen.
+ * We don't have to worry about bad address translations when the prom
+ * addresses our pointers because our pointers are at 0x0-kern_size
+ * as the prom expects.
+ */
+
+/* paul@sfe.com.au */
+/* V3 doesn't have printf.. And I don't really feel like doing the formatting
+ * myself.. So we miss out on some messages (for now).
+ */
+ ld [%g7 + 0x4], %o0
+ subcc %o3, 0x3, %g0
+ be v3_bootmsg
+ nop
+
+ sethi %hi(boot_msg), %o0
+ or %o0, %lo(boot_msg), %o0
+ sethi %hi(prom_printf), %o1
+ ld [%o1 + %lo(prom_printf)], %o1
+ ld [%o1], %o1
+ call %o1 ! print boot message #1
+ nop
+
+ sethi %hi(pstring1), %o0
+ or %o0, %lo(pstring1), %o0
+ sethi %hi(prom_printf), %o2
+ ld [%o2 + %lo(prom_printf)], %o2
+ ld [%o2], %o2
+ sethi %hi(prom_magic), %o1
+ ld [%o1 + %lo(prom_magic)], %o1
+ ld [%o1], %o1
+ call %o2
+ nop
+
+ sethi %hi(pstring2), %o0
+ or %o0, %lo(pstring2), %o0
+ sethi %hi(prom_printf), %o2
+ ld [%o2 + %lo(prom_printf)], %o2
+ ld [%o2], %o2
+ sethi %hi( C_LABEL(prom_iface_vers) ), %o1
+ ld [%o1 + %lo( C_LABEL(prom_iface_vers) )], %o1
+ ld [%o1], %o1
+ call %o2
+ nop
+
+ b rest_of_boot
+ nop
+
+v3_bootmsg:
+ ld [%g7 + 0x94], %o0
+ ld [%o0], %o0
+ sethi %hi(boot_msg), %o1
+ or %o1, %lo(boot_msg), %o1
+ mov BOOT_MSG_LEN, %o2
+ ld [%g7 + 0xb8], %o4
+ call %o4
+ nop
+
+ ld [%g7 + 0x94], %o0
+ ld [%o0], %o0
+ sethi %hi(boot_msg2), %o1
+ or %o1, %lo(boot_msg2), %o1
+ mov BOOT_MSG2_LEN, %o2
+ ld [%g7 + 0xb8], %o4
+ call %o4
+ nop
+ b rest_of_boot
+ nop
+
+
+no_sun4_here:
+ ld [%g7 + 0x68], %o1
+ set sun4_notsup, %o0
+ call %o1
+ nop
+
+rest_of_boot:
+ or %g0, PAGE_SHIFT, %g5
+
+ sethi %hi(AC_CONTEXT), %g1 ! kernel context, safe now
+ ! the only valid context
+ ! until we call paging_init()
+ stba %g0, [%g1] ASI_CONTROL
+
+
+/* I make the kernel image sit in memory relative to 0x0 with the text
+ * starting at 0x4000. Now it looks like the way memory is set in Linux
+ * on an ix86.
+ */
+
+/* Uh, oh, interrupt time. This crap is real confusing. What I want to do is
+ * clear all interrupts, map the interrupt enable register which in effect
+ * enables non-maskable interrupts (or NMI's). Actually we take no interrupts
+ * until we frob with the %tbr (trap base register) which the prom has set
+ * to all its routines which allows some sanity during bootup.
+ */
+
+ sethi %hi(IE_reg_addr), %l0
+ or %l0, %lo(IE_reg_addr), %l0
+
+ set 0xf4000000, %l3
+ sethi %hi(INT_ENABLE_REG_PHYSADR), %l2
+ or %l2, %lo(INT_ENABLE_REG_PHYSADR), %l2
+ srl %l2, %g5, %l2
+ or %l2, %l3, %l1
+
+#ifndef CONFIG_SRMMU
+ sta %l1, [%l0] ASI_PTE
+#endif
+
+ or %g0, 0x1, %l1
+ stb %l1, [%l0]
+
+
+/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
+ * show-time!
+ */
+
+ sethi %hi(1f), %g1
+ or %g1, %lo(1f), %g1
+ jmp %g1
+ nop
+
+ .align 4
+1: sethi %hi( C_LABEL(cputyp) ), %o0
+ st %g4, [%o0 + %lo( C_LABEL(cputyp) )]
+
+ sethi %hi( C_LABEL(pgshift) ), %o0
+ st %g5, [%o0 + %lo( C_LABEL(pgshift) )]
+
+ mov 1, %o0
+ sll %o0, %g5, %g5
+ sethi %hi( C_LABEL(nbpg) ), %o0
+ st %g5, [%o0 + %lo( C_LABEL(nbpg) )]
+
+ sub %g5, 1, %g5
+ sethi %hi( C_LABEL(pgofset) ), %o0
+ st %g5, [%o0 + %lo( C_LABEL(pgofset) )]
+
+
+ rd %psr, %g3
+ andn %g3, PSR_ET, %g3
+ wr %g3, 0x0, %psr ! make sure traps are off
+ ! before we play around
+ WRITE_PAUSE ! no guarantees until 3 insns
+
+
+ wr %g0, 0x0, %wim ! magical invalid window reg
+ WRITE_PAUSE ! see above
+
+/* I keep the timer interrupt on so that BogoMIPS works and the prom
+ * keeps updating its "jiffies" counter. 100HZ clock on sparcstations.
+ */
+
+/* If gas wasn't so dumb, I could use or'd macros in this next
+ * write. ;-( like this (PSR_PS | PSR_S | PSR_PIL)...
+ */
+
+ sethi %hi(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
+ or %g2, %lo(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
+ wr %g2, 0x0, %psr
+ WRITE_PAUSE
+
+ wr %g0, 0x2, %wim ! window 1 invalid
+ WRITE_PAUSE
+
+ or %g0, 0x1, %g1
+ sethi %hi( C_LABEL(current) + THREAD_WIM), %g2
+ st %g1, [%g2 + %lo( C_LABEL(current) + THREAD_WIM)]
+
+/* I want a kernel stack NOW! */
+
+ set ( C_LABEL(init_user_stack) + 4092 - 96 - 80), %fp
+ set ( C_LABEL(init_user_stack) + 4092), %sp
+
+/* now out stack is set up similarly to the way it is on the i386 */
+
+ rd %psr, %l0
+ wr %l0, PSR_ET, %psr
+ WRITE_PAUSE
+
+/*
+ * Maybe the prom zeroes out our BSS section, maybe it doesn't. I certainly
+ * don't know, do you?
+ */
+
+ set C_LABEL(edata) , %o0
+ set C_LABEL(end) , %o1
+ sub %o1, %o0, %g2
+ sethi %hi( C_LABEL(kernel_bss_len) ), %g3
+ st %g2, [%g3 + %lo( C_LABEL(kernel_bss_len) )]
+ sethi %hi( C_LABEL(trapbase) ), %g3
+ or %g3, %lo( C_LABEL(trapbase) ), %g3
+ sethi %hi( C_LABEL(etext) ), %g4
+ or %g4, %lo( C_LABEL(etext) ), %g4
+ sub %g4, %g3, %g2
+ sethi %hi( C_LABEL(kernel_text_len) ), %g3
+ st %g2, [%g3 + %lo( C_LABEL(kernel_text_len) )]
+ sethi %hi( C_LABEL(etext) ), %g4
+ or %g4, %lo( C_LABEL(etext) ), %g4
+ sethi %hi( C_LABEL(edata) ), %g3
+ or %g3, %lo( C_LABEL(edata) ), %g3
+ sub %g3, %g4, %g2
+ sethi %hi( C_LABEL(kernel_data_len) ), %g3
+ st %g2, [%g3 + %lo( C_LABEL(kernel_data_len) )]
+ or %g0, %g0, %g1
+
+1:
+ st %g0, [%o0]
+ add %o0, 0x4, %o0
+ subcc %o0, %o1, %g0
+ bl 1b
+ nop
+
+/* Compute NWINDOWS and stash it away. Now uses %wim trick explained
+ * in the V8 manual. Ok, this method seems to work, sparc is cool...
+ */
+
+ sethi %hi(0xffffffff), %g1
+ rd %wim, %g2 ! save current value
+ or %g1, %lo(0xffffffff), %g1
+ wr %g1, 0x0, %wim
+ rd %wim, %g1 ! get remaining mask
+ wr %g2, 0x0, %wim ! restore old value
+ WRITE_PAUSE
+
+ or %g0, 0x0, %g3
+
+1: srl %g1, 0x1, %g1 ! shift until highest
+ subcc %g1, 0x0, %g0 ! bit set
+ bne 1b
+ add %g3, 0x1, %g3
+ sethi %hi( C_LABEL(nwindows) ), %g4
+ st %g3, [%g4 + %lo( C_LABEL(nwindows) )] ! store final value
+ sub %g3, 0x1, %g3
+ sethi %hi( C_LABEL(nwindowsm1) ), %g4
+ st %g3, [%g4 + %lo( C_LABEL(nwindowsm1) )]
+
+
+/* Here we go */
+
+#ifndef CONFIG_SUN4M
+ /* paul@sfe.com.au */
+ /* Look into traps later :( */
+ set C_LABEL(trapbase), %g3
+ wr %g3, 0x0, %tbr
+ WRITE_PAUSE
+#endif
+
+
+/* First we call init_prom() to set up romvec, then off to start_kernel() */
+/* XXX put this in arch_init() */
+
+ sethi %hi( C_LABEL(prom_vector_p) ), %g5
+ call C_LABEL(init_prom)
+ ld [%g5 + %lo( C_LABEL(prom_vector_p) )], %o0 /* delay slot */
+
+ call C_LABEL(start_kernel)
+ nop
+
+ call halt_me
+ nop
+
+/* There, happy now adrian? */
+
+no_sun4d_here:
+ ld [%g7 + 0x68], %o1
+ set sun4d_notsup, %o0
+ call %o1
+ nop
+ b halt_me
+ nop
+
+halt_me:
+ ld [%g7 + 0x74], %o0
+ call %o0 ! get us out of here...
+ nop ! apparently solaris is better
+
+ .data
+ .align 4
+
+/*
+ * Fill up the prom vector, note in particular the kind first element,
+ * no joke. I don't need all of them in here as the entire prom vector
+ * gets initialized in c-code so all routines can use it.
+ */
+
+ .globl C_LABEL(prom_vector_p)
+
+C_LABEL(prom_vector_p): .skip 4
+prom_magic: .skip 4 ! magic mushroom, beware...
+prom_rom_vers: .skip 4 ! interface version (v0 or v2)
+prom_pluginvers: .skip 4 ! XXX help help help ???
+prom_revision: .skip 4 ! PROM revision (ie. 1.4)
+prom_halt: .skip 4 ! void halt(void) solaris friend
+prom_eval: .skip 4 ! void eval(int len, char* string)
+prom_v0bootline: .skip 4 ! boot command line
+prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr.
+prom_nodefuncs: .skip 4 ! Magical Node functions
+prom_printf: .skip 4 ! minimal printf()
+
+/* The prom_abort pointer MUST be mapped in all contexts, because if you
+ * don't then if a user process is running when you press the abort key
+ * sequence, all sorts of bad things can happen
+ */
+
+prom_abort: .skip 4 ! L1-A magic cookie
+ ! must be mapped in ALL contexts
+
+/* prom_sync is a place where the kernel should place a pointer to a kernel
+ * function that when called will sync all pending information to the drives
+ * and then promptly return. If the kernel gets aborted with 'L1-A' one can
+ * give the 'sync' command to the boot prompt and this magic cookie gets
+ * executed. Nice feature eh?
+ */
+
+prom_sync: .skip 4 ! hook in prom for sync func
+
+ .align 4
+
+/* We calculate the following at boot time, window fills/spills and trap entry
+ * code uses these to keep track of the register windows.
+ */
+
+ .globl C_LABEL(nwindows)
+ .globl C_LABEL(nwindowsm1)
+C_LABEL(nwindows): .skip 4
+C_LABEL(nwindowsm1): .skip 4
+
+ .align 4
+/* Boot time privileged register values, plus magic %o2 value */
+
+ .globl C_LABEL(boot_wim)
+ .globl C_LABEL(boot_psr)
+ .globl C_LABEL(boot_tbr)
+ .globl C_LABEL(boot_smp_ptr)
+C_LABEL(boot_wim): .skip 4
+C_LABEL(boot_psr): .skip 4
+C_LABEL(boot_tbr): .skip 4
+C_LABEL(boot_smp_ptr): .skip 4
+
+
+ .align 4
+/* Miscellaneous pieces of information saved at kernel startup. */
+ .globl C_LABEL(kernel_text_len)
+ .globl C_LABEL(kernel_data_len)
+ .globl C_LABEL(kernel_bss_len)
+C_LABEL(kernel_text_len): .word 0
+C_LABEL(kernel_data_len): .word 0
+C_LABEL(kernel_bss_len): .word 0
+
+/* These are for page alignment/offset information as they change from
+ machine to machine.
+*/
+
+ .globl C_LABEL(pgshift)
+ .globl C_LABEL(nbpg)
+ .globl C_LABEL(pgofset)
+
+ .align 4
+C_LABEL(pgshift):
+ .word 1
+C_LABEL(nbpg):
+ .word 1
+C_LABEL(pgofset):
+ .word 1
+
+/* Just to get the kernel through the compiler for now */
+ .globl C_LABEL(swapper_pg_dir), C_LABEL(pg0)
+ .globl C_LABEL(empty_bad_page)
+ .globl C_LABEL(empty_bad_page_table)
+ .globl C_LABEL(empty_zero_page)
+ .globl C_LABEL(floppy_track_buffer)
+C_LABEL(floppy_track_buffer):
+ .fill 512*2*36,1,0
+
+ .align 4
+C_LABEL(swapper_pg_dir): .skip 0x1000
+C_LABEL(pg0): .skip 0x1000
+C_LABEL(empty_bad_page): .skip 0x1000
+C_LABEL(empty_bad_page_table): .skip 0x1000
+C_LABEL(empty_zero_page): .skip 0x1000
+
+ .align 4
+diagstr: .asciz "DIAG\n"
+ .align 4
diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c
new file mode 100644
index 000000000..c12f7467b
--- /dev/null
+++ b/arch/sparc/kernel/idprom.c
@@ -0,0 +1,183 @@
+/* idprom.c: Routines to load the idprom into kernel addresses and
+ * interpret the data contained within.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/types.h>
+#include <asm/openprom.h>
+#include <asm/idprom.h>
+
+struct idp_struct idprom;
+extern int num_segmaps, num_contexts;
+
+void get_idprom(void)
+{
+ char* idp_addr;
+ char* knl_idp_addr;
+ int i;
+
+ idp_addr = (char *)IDPROM_ADDR;
+ knl_idp_addr = (char *) &idprom;
+
+ for(i = 0; i<IDPROM_SIZE; i++)
+ *knl_idp_addr++ = *idp_addr++;
+
+ return;
+}
+
+/* find_vac_size() returns the number of bytes in the VAC (virtual
+ * address cache) on this machine.
+ */
+
+int
+find_vac_size(void)
+{
+ int vac_prop_len;
+ int vacsize = 0;
+ int node_root;
+
+ node_root = (*(romvec->pv_nodeops->no_nextnode))(0);
+
+ vac_prop_len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-size");
+
+ if(vac_prop_len != -1)
+ {
+ (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-size", (char *) &vacsize);
+ return vacsize;
+ }
+ else
+ {
+
+ /* The prom node functions can't help, do it via idprom struct */
+ switch(idprom.id_machtype)
+ {
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ return 65536;
+ default:
+ return -1;
+ }
+ };
+}
+
+/* find_vac_linesize() returns the size in bytes of the VAC linesize */
+
+int
+find_vac_linesize(void)
+{
+ int vac_prop_len;
+ int vaclinesize = 0;
+ int node_root;
+
+ node_root = (*(romvec->pv_nodeops->no_nextnode))(0);
+
+ vac_prop_len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-linesize");
+
+ if(vac_prop_len != -1)
+ {
+ (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-linesize",
+ (char *) &vaclinesize);
+ return vaclinesize;
+ }
+ else
+ {
+
+ /* The prom node functions can't help, do it via idprom struct */
+ switch(idprom.id_machtype)
+ {
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ return 16;
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ return 32;
+ default:
+ return -1;
+ }
+ };
+}
+
+int
+find_vac_hwflushes(void)
+{
+ register int len, node_root;
+ int tmp1, tmp2;
+
+ node_root = (*(romvec->pv_nodeops->no_nextnode))(0);
+
+ len = (*(romvec->pv_nodeops->no_proplen))(node_root, "vac_hwflush");
+
+#ifdef DEBUG_IDPROM
+ printf("DEBUG: find_vac_hwflushes: proplen vac_hwflush=0x%x\n", len);
+#endif
+
+ /* Sun 4/75 has typo in prom_node, it's a dash instead of an underscore
+ * in the property name. :-(
+ */
+ len |= (*(romvec->pv_nodeops->no_proplen))(node_root, "vac-hwflush");
+
+#ifdef DEBUG_IDPROM
+ printf("DEBUG: find_vac_hwflushes: proplen vac-hwflush=0x%x\n", len);
+#endif
+
+ len = (*(romvec->pv_nodeops->no_getprop))(node_root,"vac_hwflush",
+ (char *) &tmp1);
+ if(len != 4) tmp1=0;
+
+ len = (*(romvec->pv_nodeops->no_getprop))(node_root, "vac-hwflush",
+ (char *) &tmp2);
+ if(len != 4) tmp2=0;
+
+
+ return (tmp1|tmp2);
+}
+
+void
+find_mmu_num_segmaps(void)
+{
+ register int root_node, len;
+
+ root_node = (*(romvec->pv_nodeops->no_nextnode))(0);
+
+ len = (*(romvec->pv_nodeops->no_getprop))(root_node, "mmu-npmg",
+ (char *) &num_segmaps);
+
+#ifdef DEBUG_MMU
+ printf("find_mmu_num_segmaps: property length = %d\n", len);
+#endif
+
+ if(len != 4) num_segmaps = 128;
+
+ return;
+}
+
+void
+find_mmu_num_contexts(void)
+{
+ register int root_node, len;
+
+ root_node = (*(romvec->pv_nodeops->no_nextnode))(0);
+
+ len = (*(romvec->pv_nodeops->no_getprop))(root_node, "mmu-nctx",
+ (char *) &num_contexts);
+
+#ifdef DEBUG_MMU
+ printf("find_mmu_num_contexts: property length = %d\n", len);
+#endif
+
+ if(len != 4) num_contexts = 8;
+
+ return;
+}
+
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
new file mode 100644
index 000000000..effa6c25e
--- /dev/null
+++ b/arch/sparc/kernel/ioport.c
@@ -0,0 +1,12 @@
+/* ioport.c: I/O access on the Sparc. Work in progress.. Most of the things
+ * in this file are for the sole purpose of getting the kernel
+ * through the compiler. :-)
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c
new file mode 100644
index 000000000..5dcaf1971
--- /dev/null
+++ b/arch/sparc/kernel/irq.c
@@ -0,0 +1,335 @@
+/* arch/sparc/kernel/irq.c: Interrupt request handling routines. On the
+ * Sparc the IRQ's are basically 'cast in stone'
+ * and you are supposed to probe the prom's device
+ * node trees to find out who's got which IRQ.
+ *
+ * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ *
+ */
+
+/*
+ * 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/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/vaddrs.h>
+#include <asm/clock.h>
+#include <asm/openprom.h>
+
+#define DEBUG_IRQ
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+ unsigned char *int_reg;
+
+ save_flags(flags);
+ cli();
+
+ /* We have mapped the irq enable register in head.S and all we
+ * have to do here is frob the bits.
+ */
+
+ int_reg = (unsigned char *) IRQ_ENA_ADR;
+
+ switch(irq_nr)
+ {
+ case 1:
+ *int_reg = ((*int_reg) & (~(0x02)));
+ break;
+ case 4:
+ *int_reg = ((*int_reg) & (~(0x04)));
+ break;
+ case 6:
+ *int_reg = ((*int_reg) & (~(0x08)));
+ break;
+ case 8:
+ *int_reg = ((*int_reg) & (~(0x10)));
+ break;
+ case 10:
+ *int_reg = ((*int_reg) & (~(0x20)));
+ break;
+ case 14:
+ *int_reg = ((*int_reg) & (~(0x80)));
+ break;
+ default:
+ printk("AIEEE, Illegal interrupt disable requested irq=%d\n",
+ (int) irq_nr);
+ break;
+ };
+
+ restore_flags(flags);
+ return;
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+ unsigned char *int_reg;
+
+ save_flags(flags);
+ cli();
+
+ /* We have mapped the irq enable register in head.S and all we
+ * have to do here is frob the bits.
+ */
+
+ int_reg = (unsigned char *) IRQ_ENA_ADR;
+
+#ifdef DEBUG_IRQ
+ printk(" --- Enabling IRQ level %d ---\n", irq_nr);
+#endif
+
+ switch(irq_nr)
+ {
+ case 1:
+ *int_reg = ((*int_reg) | 0x02);
+ break;
+ case 4:
+ *int_reg = ((*int_reg) | 0x04);
+ break;
+ case 6:
+ *int_reg = ((*int_reg) | 0x08);
+ break;
+ case 8:
+ *int_reg = ((*int_reg) | 0x10);
+ break;
+ case 10:
+ *int_reg = ((*int_reg) | 0x20);
+ break;
+ case 14:
+ *int_reg = ((*int_reg) | 0x80);
+ break;
+ default:
+ printk("AIEEE, Illegal interrupt enable requested irq=%d\n",
+ (int) irq_nr);
+ break;
+ };
+
+ restore_flags(flags);
+
+ return;
+}
+
+/*
+ * 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;
+}
+
+void free_irq(unsigned int irq)
+{
+ struct irqaction * action = irq + irq_action;
+ unsigned long flags;
+
+ if (irq > 14) { /* 14 irq levels on the sparc */
+ 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();
+ disable_irq(irq);
+ action->handler = NULL;
+ action->flags = 0;
+ action->mask = 0;
+ action->name = NULL;
+ restore_flags(flags);
+}
+
+#if 0
+static void handle_nmi(struct pt_regs * regs)
+{
+ printk("NMI, probably due to bus-parity error.\n");
+ printk("PC=%08lx, SP=%08lx\n", regs->pc, regs->sp);
+}
+#endif
+
+void unexpected_irq(int irq, struct pt_regs * regs)
+{
+ int i;
+
+ printk("IO device interrupt, irq = %d\n", irq);
+ printk("PC = %08lx NPC = %08lx SP=%08lx\n", regs->pc,
+ regs->npc, regs->sp);
+ printk("Expecting: ");
+ for (i = 0; i < 16; i++)
+ if (irq_action[i].handler)
+ printk("[%s:%d] ", irq_action[i].name, i);
+ printk("AIEEE\n");
+}
+
+static inline void handler_irq(int irq, struct pt_regs * regs)
+{
+ struct irqaction * action = irq + irq_action;
+
+ if (!action->handler) {
+ unexpected_irq(irq, regs);
+ return;
+ }
+ action->handler(irq, regs);
+}
+
+/*
+ * 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;
+
+ kstat.interrupts[irq]++;
+ action->handler(irq, regs);
+ return;
+}
+
+/*
+ * Since we need to special things to clear up the clock chip around
+ * the do_timer() call we have a special version of do_IRQ for the
+ * level 14 interrupt which does these things.
+ */
+
+asmlinkage void do_sparc_timer(int irq, struct pt_regs * regs)
+{
+ struct irqaction *action = irq + irq_action;
+ register volatile int clear;
+
+ kstat.interrupts[irq]++;
+
+ /* I do the following already in the entry code, better safe than
+ * sorry for now. Reading the limit register clears the interrupt.
+ */
+ clear = TIMER_STRUCT->timer_limit14;
+
+ action->handler(irq, regs);
+ return;
+}
+
+/*
+ * 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)
+{
+ kstat.interrupts[irq]++;
+ printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq);
+ return;
+}
+
+extern int first_descent;
+extern void probe_clock(int);
+
+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 > 14) /* Only levels 1-14 are valid on the Sparc. */
+ return -EINVAL;
+
+ if(irq == 0) /* sched_init() requesting the timer IRQ */
+ {
+ irq = 14;
+ probe_clock(first_descent);
+ }
+
+ 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;
+
+ enable_irq(irq);
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+unsigned int probe_irq_on (void)
+{
+ unsigned int irqs = 0;
+
+ return irqs;
+}
+
+int probe_irq_off (unsigned int irqs)
+{
+ unsigned int i = 0;
+
+ return i;
+}
+
+void init_IRQ(void)
+{
+ return;
+}
diff --git a/arch/sparc/kernel/probe.c b/arch/sparc/kernel/probe.c
new file mode 100644
index 000000000..462556164
--- /dev/null
+++ b/arch/sparc/kernel/probe.c
@@ -0,0 +1,432 @@
+/* probe.c: Preliminary device tree probing routines...
+
+ Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+*/
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/vac-ops.h>
+#include <asm/io.h>
+#include <asm/vaddrs.h>
+#include <asm/param.h>
+#include <asm/clock.h>
+#include <asm/system.h>
+
+/* #define DEBUG_PROBING */
+
+char promstr_buf[64]; /* overkill */
+unsigned int promint_buf[1];
+
+extern int prom_node_root;
+extern int num_segmaps, num_contexts;
+
+extern int node_get_sibling(int node);
+extern int node_get_child(int node);
+extern char* get_str_from_prom(int node, char* name, char* value);
+extern unsigned int* get_int_from_prom(int node, char* name, unsigned int *value);
+
+int first_descent;
+
+/* Cpu-type information and manufacturer strings */
+
+
+struct cpu_iu_info {
+ int psr_impl;
+ int psr_vers;
+ char* cpu_name; /* should be enough I hope... */
+};
+
+struct cpu_fp_info {
+ int psr_impl;
+ int fp_vers;
+ char* fp_name;
+};
+
+struct cpu_fp_info linux_sparc_fpu[] = {
+ { 0, 0, "Fujitsu MB86910 or Weitek WTL1164/5"},
+ { 0, 1, "Fujitsu MB86911 or Weitek WTL1164/5"},
+ { 0, 2, "LSI Logic L64802 or Texas Instruments ACT8847"},
+ { 0, 3, "Weitek WTL3170/2"},
+ { 0, 4, "Lsi Logic/Meiko L64804"},
+ { 0, 5, "reserved"},
+ { 0, 6, "reserved"},
+ { 0, 7, "No FPU"},
+ { 1, 0, "Lsi Logic L64812 or Texas Instruments ACT8847"},
+ { 1, 1, "Lsi Logic L64814"},
+ { 1, 2, "Texas Instruments TMS390-C602A"},
+ { 1, 3, "Weitek WTL3171"},
+ { 1, 4, "reserved"},
+ { 1, 5, "reserved"},
+ { 1, 6, "reserved"},
+ { 1, 7, "No FPU"},
+ { 2, 0, "BIT B5010 or B5110/20 or B5210"},
+ { 2, 1, "reserved"},
+ { 2, 2, "reserved"},
+ { 2, 3, "reserved"},
+ { 2, 4, "reserved"},
+ { 2, 5, "reserved"},
+ { 2, 6, "reserved"},
+ { 2, 7, "No FPU"},
+ { 5, 0, "Matsushita MN10501"},
+ { 5, 1, "reserved"},
+ { 5, 2, "reserved"},
+ { 5, 3, "reserved"},
+ { 5, 4, "reserved"},
+ { 5, 5, "reserved"},
+ { 5, 6, "reserved"},
+ { 5, 7, "No FPU"},
+};
+
+struct cpu_iu_info linux_sparc_chips[] = {
+ { 0, 0, "Fujitsu Microelectronics, Inc. - MB86900/1A"},
+ { 1, 0, "Cypress CY7C601"},
+ { 1, 1, "LSI Logic Corporation - L64811"},
+ { 1, 3, "Cypress CY7C611"},
+ { 2, 0, "Bipolar Integrated Technology - B5010"},
+ { 3, 0, "LSI Logic Corporation - unknown-type"},
+ { 4, 0, "Texas Instruments, Inc. - unknown"},
+ { 4, 1, "Texas Instruments, Inc. - Sparc Classic"},
+ { 4, 2, "Texas Instruments, Inc. - unknown"},
+ { 4, 3, "Texas Instruments, Inc. - unknown"},
+ { 4, 4, "Texas Instruments, Inc. - unknown"},
+ { 4, 5, "Texas Instruments, Inc. - unknown"},
+ { 5, 0, "Matsushita - MN10501"},
+ { 6, 0, "Philips Corporation - unknown"},
+ { 7, 0, "Harvest VLSI Design Center, Inc. - unknown"},
+ { 8, 0, "Systems and Processes Engineering Corporation (SPEC)"},
+ { 9, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+ { 0xa, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+ { 0xb, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+ { 0xc, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+ { 0xd, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+ { 0xe, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+ { 0xf, 0, "UNKNOWN CPU-VENDOR/TYPE"},
+};
+
+char *sparc_cpu_type = "cpu-oops";
+char *sparc_fpu_type = "fpu-oops";
+
+/* various Virtual Address Cache parameters we find at boot time... */
+
+extern int vac_size, vac_linesize, vac_do_hw_vac_flushes;
+extern int vac_entries_per_context, vac_entries_per_segment;
+extern int vac_entries_per_page;
+
+extern int find_vac_size(void);
+extern int find_vac_linesize(void);
+extern int find_vac_hwflushes(void);
+extern void find_mmu_num_segmaps(void);
+extern void find_mmu_num_contexts(void);
+
+void
+probe_cpu(void)
+{
+ register int psr_impl=0;
+ register int psr_vers = 0;
+ register int fpu_vers = 0;
+ register int i = 0;
+ unsigned int tmp_fsr;
+
+ &tmp_fsr; /* GCC grrr... */
+
+ __asm__("rd %%psr, %0\n\t"
+ "mov %0, %1\n\t"
+ "srl %0, 28, %0\n\t"
+ "srl %1, 24, %1\n\t"
+ "and %0, 0xf, %0\n\t"
+ "and %1, 0xf, %1\n\t" :
+ "=r" (psr_impl),
+ "=r" (psr_vers) :
+ "0" (psr_impl),
+ "1" (psr_vers));
+
+
+ __asm__("st %%fsr, %1\n\t"
+ "ld %1, %0\n\t"
+ "srl %0, 17, %0\n\t"
+ "and %0, 0x7, %0\n\t" :
+ "=r" (fpu_vers),
+ "=m" (tmp_fsr) :
+ "0" (fpu_vers),
+ "1" (tmp_fsr));
+
+ printk("fpu_vers: %d ", fpu_vers);
+ printk("psr_impl: %d ", psr_impl);
+ printk("psr_vers: %d \n\n", psr_vers);
+
+ for(i = 0; i<23; i++)
+ {
+ if(linux_sparc_chips[i].psr_impl == psr_impl)
+ if(linux_sparc_chips[i].psr_vers == psr_vers)
+ {
+ sparc_cpu_type = linux_sparc_chips[i].cpu_name;
+ break;
+ }
+ }
+
+ if(i==23)
+ {
+ printk("No CPU type! You lose\n");
+ printk("DEBUG: psr.impl = 0x%x psr.vers = 0x%x\n", psr_impl,
+ psr_vers);
+ return;
+ }
+
+ for(i = 0; i<32; i++)
+ {
+ if(linux_sparc_fpu[i].psr_impl == psr_impl)
+ if(linux_sparc_fpu[i].fp_vers == fpu_vers)
+ {
+ sparc_fpu_type = linux_sparc_fpu[i].fp_name;
+ break;
+ }
+ }
+
+ if(i == 32)
+ {
+ printk("No FPU type! You don't completely lose though...\n");
+ printk("DEBUG: psr.impl = 0x%x fsr.vers = 0x%x\n", psr_impl, fpu_vers);
+ sparc_fpu_type = linux_sparc_fpu[31].fp_name;
+ }
+
+ printk("CPU: %s \n", sparc_cpu_type);
+ printk("FPU: %s \n", sparc_fpu_type);
+
+ return;
+}
+
+void
+probe_vac(void)
+{
+ register unsigned int x,y;
+
+#ifndef CONFIG_SRMMU
+ vac_size = find_vac_size();
+ vac_linesize = find_vac_linesize();
+ vac_do_hw_vac_flushes = find_vac_hwflushes();
+
+ /* Calculate various constants that make the cache-flushing code
+ * mode speedy.
+ */
+
+ vac_entries_per_segment = vac_entries_per_context = vac_size >> 12;
+
+ for(x=0,y=vac_linesize; ((1<<x)<y); x++);
+ if((1<<x) != vac_linesize) printk("Warning BOGUS VAC linesize 0x%x",
+ vac_size);
+
+ vac_entries_per_page = x;
+
+ printk("Sparc VAC cache: Size=%d bytes Line-Size=%d bytes ... ", vac_size,
+ vac_linesize);
+
+ /* Here we want to 'invalidate' all the software VAC "tags"
+ * just in case there is garbage in there. Then we enable it.
+ */
+
+ for(x=0x80000000, y=(x+vac_size); x<y; x+=vac_linesize)
+ __asm__("sta %0, [%1] %2" : : "r" (0), "r" (x), "n" (0x2));
+
+ x=enable_vac();
+ printk("ENABLED\n");
+#endif
+
+ return;
+}
+
+void
+probe_mmu(void)
+{
+ find_mmu_num_segmaps();
+ find_mmu_num_contexts();
+
+ printk("MMU segmaps: %d MMU contexts: %d\n", num_segmaps,
+ num_contexts);
+
+ return;
+}
+
+void
+probe_clock(int fchild)
+{
+ register int node, type;
+ register char *node_str;
+
+ /* This will basically traverse the node-tree of the prom to see
+ * which timer chip is on this machine.
+ */
+
+ printk("Probing timer chip... ");
+
+ type = 0;
+ for(node = fchild ; ; )
+ {
+ node_str = get_str_from_prom(node, "model", promstr_buf);
+ if(strcmp(node_str, "mk48t02") == 0)
+ {
+ type = 2;
+ break;
+ }
+
+ if(strcmp(node_str, "mk48t08") == 0)
+ {
+ type = 8;
+ break;
+ }
+
+ node = node_get_sibling(node);
+ if(node == fchild)
+ {
+ printk("Aieee, could not find timer chip type\n");
+ return;
+ }
+ }
+
+ printk("Mostek %s\n", node_str);
+ printk("At OBIO address: 0x%x Virtual address: 0x%x\n",
+ (unsigned int) TIMER_PHYSADDR, (unsigned int) TIMER_STRUCT);
+
+ mapioaddr((unsigned long) TIMER_PHYSADDR,
+ (unsigned long) TIMER_STRUCT);
+
+ TIMER_STRUCT->timer_limit14=(((1000000/HZ) << 10) | 0x80000000);
+
+ return;
+}
+
+
+void
+probe_esp(register int esp_node)
+{
+ register int nd;
+ register char* lbuf;
+
+ nd = node_get_child(esp_node);
+
+ printk("\nProbing ESP:\n");
+ lbuf = get_str_from_prom(nd, "name", promstr_buf);
+
+ if(*get_int_from_prom(nd, "name", promint_buf) != 0)
+ printk("Node: 0x%x Name: %s\n", nd, lbuf);
+
+ while((nd = node_get_sibling(nd)) != 0) {
+ lbuf = get_str_from_prom(nd, "name", promstr_buf);
+ printk("Node: 0x%x Name: %s\n", nd, lbuf);
+ }
+
+ printk("\n");
+
+ return;
+}
+
+void
+probe_sbus(register int cpu_child_node)
+{
+ register int nd, savend;
+ register char* lbuf;
+
+ nd = cpu_child_node;
+
+ lbuf = (char *) 0;
+
+ while((nd = node_get_sibling(nd)) != 0) {
+ lbuf = get_str_from_prom(nd, "name", promstr_buf);
+ if(strcmp(lbuf, "sbus") == 0)
+ break;
+ };
+
+ nd = node_get_child(nd);
+
+ printk("Node: 0x%x Name: %s\n", nd,
+ get_str_from_prom(nd, "name", promstr_buf));
+
+ if(strcmp(lbuf, "esp") == 0) {
+ probe_esp(nd);
+ };
+
+ while((nd = node_get_sibling(nd)) != 0) {
+ printk("Node: 0x%x Name: %s\n", nd,
+ lbuf = get_str_from_prom(nd, "name", promstr_buf));
+
+ if(strcmp(lbuf, "esp") == 0) {
+ savend = nd;
+ probe_esp(nd);
+ nd = savend;
+ };
+ };
+
+ printk("\n");
+ return;
+}
+
+extern unsigned long probe_memory(void);
+extern struct sparc_phys_banks sp_banks[14];
+unsigned int phys_bytes_of_ram, end_of_phys_memory;
+
+void
+probe_devices(void)
+{
+ register int nd, i;
+ register char* str;
+
+ nd = prom_node_root;
+
+ printk("PROBING DEVICES:\n");
+
+ str = get_str_from_prom(nd, "device_type", promstr_buf);
+ if(strcmp(str, "cpu") == 0) {
+ printk("Found CPU root prom device tree node.\n");
+ } else {
+ printk("Root node in device tree was not 'cpu' cannot continue.\n");
+ halt();
+ };
+
+#ifdef DEBUG_PROBING
+ printk("String address for d_type: 0x%x\n", (unsigned int) str);
+ printk("str[0] = %c str[1] = %c str[2] = %c \n", str[0], str[1], str[2]);
+#endif
+
+ str = get_str_from_prom(nd, "name", promstr_buf);
+
+#ifdef DEBUG_PROBING
+ printk("String address for name: 0x%x\n", (unsigned int) str);
+ printk("str[0] = %c str[1] = %c str[2] = %c \n", str[0], str[1], str[2]);
+#endif
+
+ printk("Name: %s \n", str);
+
+ first_descent = nd = node_get_child(nd);
+
+
+/* Ok, here will go a call to each specific device probe. We can
+ * call these now that we have the 'root' node and the child of
+ * this node to send to the routines. ORDER IS IMPORTANT!
+ */
+
+ probe_cpu();
+ probe_vac();
+ probe_mmu();
+ phys_bytes_of_ram = probe_memory();
+
+ printk("Physical Memory: %d bytes\n", (int) phys_bytes_of_ram);
+ for(i=0; sp_banks[i].num_bytes != 0; i++) {
+ printk("Bank %d: base 0x%x bytes %d\n", i,
+ (unsigned int) sp_banks[i].base_addr,
+ (int) sp_banks[i].num_bytes);
+ end_of_phys_memory = sp_banks[i].base_addr + sp_banks[i].num_bytes;
+ }
+
+ printk("PROM Root Child Node: 0x%x Name: %s \n", nd,
+ get_str_from_prom(nd, "name", promstr_buf));
+
+ while((nd = node_get_sibling(nd)) != 0) {
+ printk("Node: 0x%x Name: %s", nd,
+ get_str_from_prom(nd, "name", promstr_buf));
+ printk("\n");
+ };
+
+ printk("\nProbing SBUS:\n");
+ probe_sbus(first_descent);
+
+ return;
+}
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
new file mode 100644
index 000000000..679863ba3
--- /dev/null
+++ b/arch/sparc/kernel/process.c
@@ -0,0 +1,112 @@
+/*
+ * linux/arch/i386/kernel/process.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+/*
+ * 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/segment.h>
+#include <asm/system.h>
+
+void ret_from_sys_call(void) { __asm__("nop"); }
+
+/*
+ * The idle loop on a i386..
+ */
+asmlinkage int sys_idle(void)
+{
+ if (current->pid != 0)
+ return -EPERM;
+
+ /* Map out the low memory: it's no longer needed */
+ /* Sparc version RSN */
+
+ /* endless idle loop with no priority at all */
+ current->counter = -100;
+ for (;;) {
+ schedule();
+ }
+}
+
+void hard_reset_now(void)
+{
+ halt();
+}
+
+void show_regs(struct pt_regs * regs)
+{
+ printk("\nSP: %08lx PC: %08lx NPC: %08lx\n", regs->sp, regs->pc,
+ regs->npc);
+}
+
+/*
+ * Do necessary setup to start up a newly executed thread.
+ */
+void start_thread(struct pt_regs * regs, unsigned long sp, unsigned long fp)
+{
+ regs->sp = sp;
+ regs->fp = fp;
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+ halt();
+}
+
+void flush_thread(void)
+{
+ halt();
+}
+
+void copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct task_struct * p, struct pt_regs * regs)
+{
+ struct pt_regs * childregs;
+
+ childregs = ((struct pt_regs *) (p->kernel_stack_page + PAGE_SIZE)) - 1;
+ p->tss.usp = (unsigned long) childregs;
+ *childregs = *regs;
+ childregs->sp = sp;
+ p->tss.psr = regs->psr; /* for condition codes */
+ return;
+}
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs * regs, struct user * dump)
+{
+ return; /* solaris does this enough */
+}
+
+asmlinkage int sys_fork(struct pt_regs regs)
+{
+ return do_fork(COPYVM | SIGCHLD, regs.sp, &regs);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(struct pt_regs regs)
+{
+ halt();
+ return 0;
+}
+
diff --git a/arch/sparc/kernel/promops.c b/arch/sparc/kernel/promops.c
new file mode 100644
index 000000000..b5c897b0d
--- /dev/null
+++ b/arch/sparc/kernel/promops.c
@@ -0,0 +1,107 @@
+/* promops.c: Prom node tree operations and Prom Vector initialization
+ * initialization routines.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/openprom.h>
+
+/* #define DEBUG_PROMOPS */
+#define MAX_PR_LEN 64 /* exotic hardware probably overshoots this */
+
+int prom_node_root; /* initialized in init_prom */
+
+extern struct linux_romvec *romvec;
+
+/* These two functions return and siblings and direct child descendents
+ * in the prom device tree respectively.
+ */
+
+int
+node_get_sibling(int node)
+{
+ return (*(romvec->pv_nodeops->no_nextnode))(node);
+}
+
+int
+node_get_child(int node)
+{
+ return (*(romvec->pv_nodeops->no_child))(node);
+}
+
+/* The following routine is used during device probing to determine
+ * an integer value property about a (perhaps virtual) device. This
+ * could be anything, like the size of the mmu cache lines, etc.
+ * the default return value is -1 is the prom has nothing interesting.
+ */
+
+unsigned int prom_int_null;
+
+unsigned int *
+get_int_from_prom(int node, char *nd_prop, unsigned int *value)
+{
+ unsigned int pr_len;
+
+ *value = &prom_int_null; /* duh, I was returning -1 as an unsigned int, prom_panic() */
+
+ pr_len = romvec->pv_nodeops->no_proplen(node, nd_prop);
+ if(pr_len > MAX_PR_LEN)
+ {
+#ifdef DEBUG_PROMOPS
+ printk("Bad pr_len in promops -- node: %d nd_prop: %s pr_len: %d",
+ node, nd_prop, (int) pr_len);
+#endif
+ return value; /* XXX */
+ }
+
+ romvec->pv_nodeops->no_getprop(node, nd_prop, (char *) value);
+
+ return value;
+}
+
+
+/* This routine returns what is termed a property string as opposed
+ * to a property integer as above. This can be used to extract the
+ * 'type' of device from the prom. An example could be the clock timer
+ * chip type. By default you get returned a null string if garbage
+ * is returned from the prom.
+ */
+
+char *
+get_str_from_prom(int node, char *nd_prop, char *value)
+{
+ unsigned int pr_len;
+
+ *value='\n';
+
+ pr_len = romvec->pv_nodeops->no_proplen(node, nd_prop);
+ if(pr_len > MAX_PR_LEN)
+ {
+#ifdef DEBUG_PROMOPS
+ printk("Bad pr_len in promops -- node: %d nd_prop: %s pr_len: %d",
+ node, nd_prop, pr_len);
+#endif
+ return value; /* XXX */
+ }
+
+ romvec->pv_nodeops->no_getprop(node, nd_prop, value);
+ value[pr_len] = 0;
+
+ return value;
+}
+
+/* This gets called from head.S upon bootup to initialize the
+ * prom vector pointer for the rest of the kernel.
+ */
+
+void
+init_prom(struct linux_romvec *r_ptr)
+{
+ romvec = r_ptr;
+ prom_node_root = romvec->pv_nodeops->no_nextnode(0);
+ prom_int_null = 0;
+
+ return;
+}
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
new file mode 100644
index 000000000..5ec5b56ba
--- /dev/null
+++ b/arch/sparc/kernel/setup.c
@@ -0,0 +1,120 @@
+/*
+ * linux/arch/alpha/kernel/setup.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#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 <linux/tty.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/openprom.h> /* for console registration + cheese */
+
+extern void get_idprom(void);
+extern void probe_devices(void);
+
+/*
+ * Gcc is hard to keep happy ;-)
+ */
+struct screen_info screen_info = {
+ 0, 0, /* orig-x, orig-y */
+ { 0, 0, }, /* unused */
+ 0, /* orig-video-page */
+ 0, /* orig-video-mode */
+ 80, /* orig-video-cols */
+ 0,0,0, /* ega_ax, ega_bx, ega_cx */
+ 25 /* orig-video-lines */
+};
+
+/* At least I hide the sneaky floppy_track_buffer in my dirty assembly
+ * code. ;-)
+ */
+
+unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end)
+{
+ return memory_start;
+}
+
+/* Lame prom console routines, gets registered below. Thanks for the
+ * tip Linus. First comes the V0 prom routine, then the V3 version
+ * written by Paul Hatchman (paul@sfe.com.au).
+ */
+
+void sparc_console_print(const char * p)
+{
+ unsigned char c;
+
+ while ((c = *(p++)) != 0)
+ {
+ if (c == '\n') romvec->pv_putchar('\r');
+ (*(romvec->pv_putchar))(c);
+ }
+
+ return;
+
+}
+
+/* paul@sfe.com.au */
+/* V3 prom console printing routines */
+void sparc_console_print_v3 (const char *p)
+{
+ unsigned char c;
+
+ while ((c = *(p++)) != 0)
+ {
+ if (c == '\n') romvec->pv_v2devops.v2_dev_write
+ ((*romvec->pv_v2bootargs.fd_stdout), "\r", 1);
+ romvec->pv_v2devops.v2_dev_write
+ ((*romvec->pv_v2bootargs.fd_stdout), &c, 1);
+ }
+
+ return;
+}
+
+
+/* This routine will in the future do all the nasty prom stuff
+ * to probe for the mmu type and its parameters, etc. This will
+ * also be where SMP things happen plus the Sparc specific memory
+ * physical memory probe as on the alpha.
+ */
+
+extern void register_console(void (*proc)(const char *));
+extern unsigned int prom_iface_vers, end_of_phys_memory;
+
+void setup_arch(char **cmdline_p,
+ unsigned long * memory_start_p, unsigned long * memory_end_p)
+{
+ if(romvec->pv_romvers == 0) {
+ register_console(sparc_console_print);
+ } else {
+ register_console(sparc_console_print_v3);
+ };
+
+ printk("Sparc PROM-Console registered...\n");
+ get_idprom(); /* probe_devices expects this to be done */
+ probe_devices(); /* cpu/fpu, mmu probes */
+
+ *memory_start_p = (((unsigned long) &end));
+ *memory_end_p = (((unsigned long) end_of_phys_memory));
+}
+
+asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
+{
+ return -EIO;
+}
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c
new file mode 100644
index 000000000..cd949e4ed
--- /dev/null
+++ b/arch/sparc/kernel/signal.c
@@ -0,0 +1,71 @@
+/*
+ * linux/arch/sparc/kernel/signal.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+
+#include <asm/segment.h>
+
+#define _S(nr) (1<<((nr)-1))
+
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
+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.
+ */
+asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
+{
+ unsigned long mask;
+ struct pt_regs * regs = (struct pt_regs *) &restart;
+
+ mask = current->blocked;
+ current->blocked = set & _BLOCKABLE;
+
+ while (1) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ if (do_signal(mask,regs))
+ return -EINTR;
+ }
+}
+
+asmlinkage int sys_sigreturn(unsigned long __unused)
+{
+ halt();
+ return 0;
+}
+
+/*
+ * Set up a signal frame... Make the stack look the way iBCS2 expects
+ * it to look.
+ */
+void setup_frame(struct sigaction * sa, unsigned long ** fp, unsigned long eip,
+ struct pt_regs * regs, int signr, unsigned long oldmask)
+{
+ halt();
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs)
+{
+ halt();
+ return 1;
+}
diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c
new file mode 100644
index 000000000..9302191c7
--- /dev/null
+++ b/arch/sparc/kernel/traps.c
@@ -0,0 +1,47 @@
+/*
+ * arch/sparc/kernel/traps.c
+ *
+ * Copyright 1994 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/*
+ * I hate traps on the sparc, grrr...
+ */
+
+#include <linux/sched.h> /* for jiffies */
+#include <linux/kernel.h>
+
+void do_hw_interrupt(unsigned long type, unsigned long vector)
+{
+ if (vector == 14) {
+ jiffies++;
+ return;
+ }
+
+ /* Just print garbage for everything else for now. */
+
+ printk("Unimplemented Sparc TRAP, vector = %lx type = %lx\n", vector, type);
+
+ return;
+}
+
+extern unsigned long *trapbase;
+
+void trap_init(void)
+{
+
+ /* load up the trap table */
+
+#if 0 /* not yet */
+ __asm__("wr %0, 0x0, %%tbr\n\t"
+ "nop; nop; nop\n\t" : :
+ "r" (trapbase));
+#endif
+
+ return;
+}
+
+void die_if_kernel(char * str, struct pt_regs * regs, long err)
+{
+ return;
+}
diff --git a/arch/sparc/lib/COPYING.LIB b/arch/sparc/lib/COPYING.LIB
new file mode 100644
index 000000000..eb685a5ec
--- /dev/null
+++ b/arch/sparc/lib/COPYING.LIB
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
new file mode 100644
index 000000000..1f2ce0e1c
--- /dev/null
+++ b/arch/sparc/lib/Makefile
@@ -0,0 +1,48 @@
+#
+# Makefile for Sparc library files..
+#
+
+CFLAGS := $(CFLAGS) -ansi
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.s.o:
+ $(AS) -c -o $*.o $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o
+
+lib.a: $(OBJS)
+ $(AR) rcs lib.a $(OBJS)
+ sync
+
+mul.o: mul.S
+ $(CC) -c -o mul.o mul.S
+
+rem.o: rem.S
+ $(CC) -DST_DIV0=0x2 -c -o rem.o rem.S
+
+sdiv.o: sdiv.S
+ $(CC) -DST_DIV0=0x2 -c -o sdiv.o sdiv.S
+
+udiv.o: udiv.S
+ $(CC) -DST_DIV0=0x2 -c -o udiv.o udiv.S
+
+umul.o: umul.S
+ $(CC) -c -o umul.o umul.S
+
+urem.o: urem.S
+ $(CC) -DST_DIV0=0x2 -c -o urem.o urem.S
+
+ashrdi3.o: ashrdi3.S
+ $(CC) -c -o ashrdi3.o ashrdi3.S
+
+dep:
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/arch/sparc/lib/ashrdi3.S b/arch/sparc/lib/ashrdi3.S
new file mode 100644
index 000000000..c672d2c9f
--- /dev/null
+++ b/arch/sparc/lib/ashrdi3.S
@@ -0,0 +1,28 @@
+/* ashrdi3.S: The filesystem code creates all kinds of references to
+ * this little routine on the sparc with gcc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/cprefix.h>
+
+ .globl C_LABEL(__ashrdi3)
+C_LABEL(__ashrdi3):
+ tst %o2
+ be 3f
+ or %g0, 32, %g2
+ sub %g2, %o2, %g2
+ tst %g2
+ bg 1f
+ sra %o0, %o2, %o4
+ sra %o0, 31, %o4
+ sub %g0, %g2, %g2
+ ba 2f
+ sra %o0, %g2, %o5
+1: sll %o0, %g2, %g3
+ srl %o1, %o2, %g2
+ or %g2, %g3, %o5
+2: or %g0, %o4, %o0
+ or %g0, %o5, %o1
+3: jmpl %o7 + 8, %g0
+ nop
diff --git a/arch/sparc/lib/mul.S b/arch/sparc/lib/mul.S
new file mode 100644
index 000000000..e6d78f85f
--- /dev/null
+++ b/arch/sparc/lib/mul.S
@@ -0,0 +1,127 @@
+/* mul.S: This routine was taken from glibc-1.09 and is covered
+ * by the GNU Library General Public License Version 2.
+ */
+
+/*
+ * Signed multiply, from Appendix E of the Sparc Version 8
+ * Architecture Manual.
+ */
+
+/*
+ * Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the upper 32 bits of
+ * the 64-bit product).
+ *
+ * This code optimizes short (less than 13-bit) multiplies.
+ */
+
+ .globl .mul
+.mul:
+ mov %o0, %y ! multiplier -> Y
+ andncc %o0, 0xfff, %g0 ! test bits 12..31
+ be Lmul_shortway ! if zero, can do it the short way
+ andcc %g0, %g0, %o4 ! zero the partial product and clear N and V
+
+ /*
+ * Long multiply. 32 steps, followed by a final shift step.
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %o1, %o4 ! 13
+ mulscc %o4, %o1, %o4 ! 14
+ mulscc %o4, %o1, %o4 ! 15
+ mulscc %o4, %o1, %o4 ! 16
+ mulscc %o4, %o1, %o4 ! 17
+ mulscc %o4, %o1, %o4 ! 18
+ mulscc %o4, %o1, %o4 ! 19
+ mulscc %o4, %o1, %o4 ! 20
+ mulscc %o4, %o1, %o4 ! 21
+ mulscc %o4, %o1, %o4 ! 22
+ mulscc %o4, %o1, %o4 ! 23
+ mulscc %o4, %o1, %o4 ! 24
+ mulscc %o4, %o1, %o4 ! 25
+ mulscc %o4, %o1, %o4 ! 26
+ mulscc %o4, %o1, %o4 ! 27
+ mulscc %o4, %o1, %o4 ! 28
+ mulscc %o4, %o1, %o4 ! 29
+ mulscc %o4, %o1, %o4 ! 30
+ mulscc %o4, %o1, %o4 ! 31
+ mulscc %o4, %o1, %o4 ! 32
+ mulscc %o4, %g0, %o4 ! final shift
+
+ ! If %o0 was negative, the result is
+ ! (%o0 * %o1) + (%o1 << 32))
+ ! We fix that here.
+
+#if 0
+ tst %o0
+ bge 1f
+ rd %y, %o0
+
+ ! %o0 was indeed negative; fix upper 32 bits of result by subtracting
+ ! %o1 (i.e., return %o4 - %o1 in %o1).
+ retl
+ sub %o4, %o1, %o1
+
+1:
+ retl
+ mov %o4, %o1
+#else
+ /* Faster code adapted from tege@sics.se's code for umul.S. */
+ sra %o0, 31, %o2 ! make mask from sign bit
+ and %o1, %o2, %o2 ! %o2 = 0 or %o1, depending on sign of %o0
+ rd %y, %o0 ! get lower half of product
+ retl
+ sub %o4, %o2, %o1 ! subtract compensation
+ ! and put upper half in place
+#endif
+
+Lmul_shortway:
+ /*
+ * Short multiply. 12 steps, followed by a final shift step.
+ * The resulting bits are off by 12 and (32-12) = 20 bit positions,
+ * but there is no problem with %o0 being negative (unlike above).
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %g0, %o4 ! final shift
+
+ /*
+ * %o4 has 20 of the bits that should be in the low part of the
+ * result; %y has the bottom 12 (as %y's top 12). That is:
+ *
+ * %o4 %y
+ * +----------------+----------------+
+ * | -12- | -20- | -12- | -20- |
+ * +------(---------+------)---------+
+ * --hi-- ----low-part----
+ *
+ * The upper 12 bits of %o4 should be sign-extended to form the
+ * high part of the product (i.e., highpart = %o4 >> 20).
+ */
+
+ rd %y, %o5
+ sll %o4, 12, %o0 ! shift middle bits left 12
+ srl %o5, 20, %o5 ! shift low bits right 20, zero fill at left
+ or %o5, %o0, %o0 ! construct low part of result
+ retl
+ sra %o4, 20, %o1 ! ... and extract high part of result
diff --git a/arch/sparc/lib/rem.S b/arch/sparc/lib/rem.S
new file mode 100644
index 000000000..3c0cc579b
--- /dev/null
+++ b/arch/sparc/lib/rem.S
@@ -0,0 +1,359 @@
+/* rem.S: This routine was taken from glibc-1.09 and is covered
+ * by the GNU Library General Public License Version 2.
+ */
+
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ * .rem name of function to generate
+ * rem rem=div => %o0 / %o1; rem=rem => %o0 % %o1
+ * true true=true => signed; true=false => unsigned
+ *
+ * Algorithm parameters:
+ * N how many bits per iteration we try to get (4)
+ * WORDSIZE total number of bits (32)
+ *
+ * Derived constants:
+ * TOPBITS number of bits in the top decade of a number
+ *
+ * Important variables:
+ * Q the partial quotient under development (initially 0)
+ * R the remainder so far, initially the dividend
+ * ITER number of main division loop iterations required;
+ * equal to ceil(log2(quotient) / N). Note that this
+ * is the log base (2^N) of the quotient.
+ * V the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ * Current estimate for non-large dividend is
+ * ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ * A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ * different path, as the upper bits of the quotient must be developed
+ * one bit at a time.
+ */
+
+
+ .globl .rem
+.rem:
+ ! compute sign of result; if neither is negative, no problem
+ orcc %o1, %o0, %g0 ! either negative?
+ bge 2f ! no, go do the divide
+ xor %o1, %o0, %g6 ! compute sign in any case
+ tst %o1
+ bge 1f
+ tst %o0
+ ! %o1 is definitely negative; %o0 might also be negative
+ bge 2f ! if %o0 not negative...
+ sub %g0, %o1, %o1 ! in any case, make %o1 nonneg
+1: ! %o0 is negative, %o1 is nonnegative
+ sub %g0, %o0, %o0 ! make %o0 nonnegative
+2:
+
+ ! Ready to divide. Compute size of quotient; scale comparand.
+ orcc %o1, %g0, %o5
+ bne 1f
+ mov %o0, %o3
+
+ ! Divide by zero trap. If it returns, return 0 (about as
+ ! wrong as possible, but that is what SunOS does...).
+ ta ST_DIV0
+ retl
+ clr %o0
+
+1:
+ cmp %o3, %o5 ! if %o1 exceeds %o0, done
+ blu Lgot_result ! (and algorithm fails otherwise)
+ clr %o2
+ sethi %hi(1 << (32 - 4 - 1)), %g1
+ cmp %o3, %g1
+ blu Lnot_really_big
+ clr %o4
+
+ ! Here the dividend is >= 2**(31-N) or so. We must be careful here,
+ ! as our usual N-at-a-shot divide step will cause overflow and havoc.
+ ! The number of bits in the result here is N*ITER+SC, where SC <= N.
+ ! Compute ITER in an unorthodox manner: know we need to shift V into
+ ! the top decade: so do not even bother to compare to R.
+ 1:
+ cmp %o5, %g1
+ bgeu 3f
+ mov 1, %g7
+ sll %o5, 4, %o5
+ b 1b
+ add %o4, 1, %o4
+
+ ! Now compute %g7.
+ 2: addcc %o5, %o5, %o5
+ bcc Lnot_too_big
+ add %g7, 1, %g7
+
+ ! We get here if the %o1 overflowed while shifting.
+ ! This means that %o3 has the high-order bit set.
+ ! Restore %o5 and subtract from %o3.
+ sll %g1, 4, %g1 ! high order bit
+ srl %o5, 1, %o5 ! rest of %o5
+ add %o5, %g1, %o5
+ b Ldo_single_div
+ sub %g7, 1, %g7
+
+ Lnot_too_big:
+ 3: cmp %o5, %o3
+ blu 2b
+ nop
+ be Ldo_single_div
+ nop
+ /* NB: these are commented out in the V8-Sparc manual as well */
+ /* (I do not understand this) */
+ ! %o5 > %o3: went too far: back up 1 step
+ ! srl %o5, 1, %o5
+ ! dec %g7
+ ! do single-bit divide steps
+ !
+ ! We have to be careful here. We know that %o3 >= %o5, so we can do the
+ ! first divide step without thinking. BUT, the others are conditional,
+ ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high-
+ ! order bit set in the first step, just falling into the regular
+ ! division loop will mess up the first time around.
+ ! So we unroll slightly...
+ Ldo_single_div:
+ subcc %g7, 1, %g7
+ bl Lend_regular_divide
+ nop
+ sub %o3, %o5, %o3
+ mov 1, %o2
+ b Lend_single_divloop
+ nop
+ Lsingle_divloop:
+ sll %o2, 1, %o2
+ bl 1f
+ srl %o5, 1, %o5
+ ! %o3 >= 0
+ sub %o3, %o5, %o3
+ b 2f
+ add %o2, 1, %o2
+ 1: ! %o3 < 0
+ add %o3, %o5, %o3
+ sub %o2, 1, %o2
+ 2:
+ Lend_single_divloop:
+ subcc %g7, 1, %g7
+ bge Lsingle_divloop
+ tst %o3
+ b,a Lend_regular_divide
+
+Lnot_really_big:
+1:
+ sll %o5, 4, %o5
+ cmp %o5, %o3
+ bleu 1b
+ addcc %o4, 1, %o4
+ be Lgot_result
+ sub %o4, 1, %o4
+
+ tst %o3 ! set up for initial iteration
+Ldivloop:
+ sll %o2, 4, %o2
+ ! depth 1, accumulated bits 0
+ bl L.1.16
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 2, accumulated bits 1
+ bl L.2.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 3
+ bl L.3.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 7
+ bl L.4.23
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2+1), %o2
+
+L.4.23:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2-1), %o2
+
+
+L.3.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 5
+ bl L.4.21
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2+1), %o2
+
+L.4.21:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2-1), %o2
+
+
+
+L.2.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 1
+ bl L.3.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 3
+ bl L.4.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2+1), %o2
+
+L.4.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2-1), %o2
+
+
+L.3.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 1
+ bl L.4.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2+1), %o2
+
+L.4.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2-1), %o2
+
+
+
+
+L.1.16:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 2, accumulated bits -1
+ bl L.2.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -1
+ bl L.3.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -1
+ bl L.4.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2+1), %o2
+
+L.4.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2-1), %o2
+
+
+L.3.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -3
+ bl L.4.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2+1), %o2
+
+L.4.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2-1), %o2
+
+
+
+L.2.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -3
+ bl L.3.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -5
+ bl L.4.11
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2+1), %o2
+
+L.4.11:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2-1), %o2
+
+
+L.3.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -7
+ bl L.4.9
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2+1), %o2
+
+L.4.9:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2-1), %o2
+
+
+
+
+ 9:
+Lend_regular_divide:
+ subcc %o4, 1, %o4
+ bge Ldivloop
+ tst %o3
+ bl,a Lgot_result
+ ! non-restoring fixup here (one instruction only!)
+ add %o3, %o1, %o3
+
+
+Lgot_result:
+
+ retl
+ mov %o3, %o0
diff --git a/arch/sparc/lib/sdiv.S b/arch/sparc/lib/sdiv.S
new file mode 100644
index 000000000..2fa7a9794
--- /dev/null
+++ b/arch/sparc/lib/sdiv.S
@@ -0,0 +1,363 @@
+/* sdiv.S: This routine was taken from glibc-1.09 and is covered
+ * by the GNU Library General Public License Version 2.
+ */
+
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ * .div name of function to generate
+ * div div=div => %o0 / %o1; div=rem => %o0 % %o1
+ * true true=true => signed; true=false => unsigned
+ *
+ * Algorithm parameters:
+ * N how many bits per iteration we try to get (4)
+ * WORDSIZE total number of bits (32)
+ *
+ * Derived constants:
+ * TOPBITS number of bits in the top decade of a number
+ *
+ * Important variables:
+ * Q the partial quotient under development (initially 0)
+ * R the remainder so far, initially the dividend
+ * ITER number of main division loop iterations required;
+ * equal to ceil(log2(quotient) / N). Note that this
+ * is the log base (2^N) of the quotient.
+ * V the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ * Current estimate for non-large dividend is
+ * ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ * A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ * different path, as the upper bits of the quotient must be developed
+ * one bit at a time.
+ */
+
+
+ .globl .div
+.div:
+ ! compute sign of result; if neither is negative, no problem
+ orcc %o1, %o0, %g0 ! either negative?
+ bge 2f ! no, go do the divide
+ xor %o1, %o0, %g6 ! compute sign in any case
+ tst %o1
+ bge 1f
+ tst %o0
+ ! %o1 is definitely negative; %o0 might also be negative
+ bge 2f ! if %o0 not negative...
+ sub %g0, %o1, %o1 ! in any case, make %o1 nonneg
+1: ! %o0 is negative, %o1 is nonnegative
+ sub %g0, %o0, %o0 ! make %o0 nonnegative
+2:
+
+ ! Ready to divide. Compute size of quotient; scale comparand.
+ orcc %o1, %g0, %o5
+ bne 1f
+ mov %o0, %o3
+
+ ! Divide by zero trap. If it returns, return 0 (about as
+ ! wrong as possible, but that is what SunOS does...).
+ ta ST_DIV0
+ retl
+ clr %o0
+
+1:
+ cmp %o3, %o5 ! if %o1 exceeds %o0, done
+ blu Lgot_result ! (and algorithm fails otherwise)
+ clr %o2
+ sethi %hi(1 << (32 - 4 - 1)), %g1
+ cmp %o3, %g1
+ blu Lnot_really_big
+ clr %o4
+
+ ! Here the dividend is >= 2**(31-N) or so. We must be careful here,
+ ! as our usual N-at-a-shot divide step will cause overflow and havoc.
+ ! The number of bits in the result here is N*ITER+SC, where SC <= N.
+ ! Compute ITER in an unorthodox manner: know we need to shift V into
+ ! the top decade: so do not even bother to compare to R.
+ 1:
+ cmp %o5, %g1
+ bgeu 3f
+ mov 1, %g7
+ sll %o5, 4, %o5
+ b 1b
+ add %o4, 1, %o4
+
+ ! Now compute %g7.
+ 2: addcc %o5, %o5, %o5
+ bcc Lnot_too_big
+ add %g7, 1, %g7
+
+ ! We get here if the %o1 overflowed while shifting.
+ ! This means that %o3 has the high-order bit set.
+ ! Restore %o5 and subtract from %o3.
+ sll %g1, 4, %g1 ! high order bit
+ srl %o5, 1, %o5 ! rest of %o5
+ add %o5, %g1, %o5
+ b Ldo_single_div
+ sub %g7, 1, %g7
+
+ Lnot_too_big:
+ 3: cmp %o5, %o3
+ blu 2b
+ nop
+ be Ldo_single_div
+ nop
+ /* NB: these are commented out in the V8-Sparc manual as well */
+ /* (I do not understand this) */
+ ! %o5 > %o3: went too far: back up 1 step
+ ! srl %o5, 1, %o5
+ ! dec %g7
+ ! do single-bit divide steps
+ !
+ ! We have to be careful here. We know that %o3 >= %o5, so we can do the
+ ! first divide step without thinking. BUT, the others are conditional,
+ ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high-
+ ! order bit set in the first step, just falling into the regular
+ ! division loop will mess up the first time around.
+ ! So we unroll slightly...
+ Ldo_single_div:
+ subcc %g7, 1, %g7
+ bl Lend_regular_divide
+ nop
+ sub %o3, %o5, %o3
+ mov 1, %o2
+ b Lend_single_divloop
+ nop
+ Lsingle_divloop:
+ sll %o2, 1, %o2
+ bl 1f
+ srl %o5, 1, %o5
+ ! %o3 >= 0
+ sub %o3, %o5, %o3
+ b 2f
+ add %o2, 1, %o2
+ 1: ! %o3 < 0
+ add %o3, %o5, %o3
+ sub %o2, 1, %o2
+ 2:
+ Lend_single_divloop:
+ subcc %g7, 1, %g7
+ bge Lsingle_divloop
+ tst %o3
+ b,a Lend_regular_divide
+
+Lnot_really_big:
+1:
+ sll %o5, 4, %o5
+ cmp %o5, %o3
+ bleu 1b
+ addcc %o4, 1, %o4
+ be Lgot_result
+ sub %o4, 1, %o4
+
+ tst %o3 ! set up for initial iteration
+Ldivloop:
+ sll %o2, 4, %o2
+ ! depth 1, accumulated bits 0
+ bl L.1.16
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 2, accumulated bits 1
+ bl L.2.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 3
+ bl L.3.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 7
+ bl L.4.23
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2+1), %o2
+
+L.4.23:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2-1), %o2
+
+
+L.3.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 5
+ bl L.4.21
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2+1), %o2
+
+L.4.21:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2-1), %o2
+
+
+
+L.2.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 1
+ bl L.3.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 3
+ bl L.4.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2+1), %o2
+
+L.4.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2-1), %o2
+
+
+L.3.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 1
+ bl L.4.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2+1), %o2
+
+L.4.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2-1), %o2
+
+
+
+
+L.1.16:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 2, accumulated bits -1
+ bl L.2.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -1
+ bl L.3.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -1
+ bl L.4.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2+1), %o2
+
+L.4.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2-1), %o2
+
+
+L.3.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -3
+ bl L.4.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2+1), %o2
+
+L.4.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2-1), %o2
+
+
+
+L.2.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -3
+ bl L.3.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -5
+ bl L.4.11
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2+1), %o2
+
+L.4.11:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2-1), %o2
+
+
+L.3.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -7
+ bl L.4.9
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2+1), %o2
+
+L.4.9:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2-1), %o2
+
+
+
+
+ 9:
+Lend_regular_divide:
+ subcc %o4, 1, %o4
+ bge Ldivloop
+ tst %o3
+ bl,a Lgot_result
+ ! non-restoring fixup here (one instruction only!)
+ sub %o2, 1, %o2
+
+
+Lgot_result:
+ ! check to see if answer should be < 0
+ tst %g6
+ bl,a 1f
+ sub %g0, %o2, %o2
+1:
+ retl
+ mov %o2, %o0
diff --git a/arch/sparc/lib/udiv.S b/arch/sparc/lib/udiv.S
new file mode 100644
index 000000000..53cfeac90
--- /dev/null
+++ b/arch/sparc/lib/udiv.S
@@ -0,0 +1,346 @@
+/* udiv.S: This routine was taken from glibc-1.09 and is covered
+ * by the GNU Library General Public License Version 2.
+ */
+
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ * .udiv name of function to generate
+ * div div=div => %o0 / %o1; div=rem => %o0 % %o1
+ * false false=true => signed; false=false => unsigned
+ *
+ * Algorithm parameters:
+ * N how many bits per iteration we try to get (4)
+ * WORDSIZE total number of bits (32)
+ *
+ * Derived constants:
+ * TOPBITS number of bits in the top decade of a number
+ *
+ * Important variables:
+ * Q the partial quotient under development (initially 0)
+ * R the remainder so far, initially the dividend
+ * ITER number of main division loop iterations required;
+ * equal to ceil(log2(quotient) / N). Note that this
+ * is the log base (2^N) of the quotient.
+ * V the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ * Current estimate for non-large dividend is
+ * ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ * A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ * different path, as the upper bits of the quotient must be developed
+ * one bit at a time.
+ */
+
+
+ .globl .udiv
+.udiv:
+
+ ! Ready to divide. Compute size of quotient; scale comparand.
+ orcc %o1, %g0, %o5
+ bne 1f
+ mov %o0, %o3
+
+ ! Divide by zero trap. If it returns, return 0 (about as
+ ! wrong as possible, but that is what SunOS does...).
+ ta ST_DIV0
+ retl
+ clr %o0
+
+1:
+ cmp %o3, %o5 ! if %o1 exceeds %o0, done
+ blu Lgot_result ! (and algorithm fails otherwise)
+ clr %o2
+ sethi %hi(1 << (32 - 4 - 1)), %g1
+ cmp %o3, %g1
+ blu Lnot_really_big
+ clr %o4
+
+ ! Here the dividend is >= 2**(31-N) or so. We must be careful here,
+ ! as our usual N-at-a-shot divide step will cause overflow and havoc.
+ ! The number of bits in the result here is N*ITER+SC, where SC <= N.
+ ! Compute ITER in an unorthodox manner: know we need to shift V into
+ ! the top decade: so do not even bother to compare to R.
+ 1:
+ cmp %o5, %g1
+ bgeu 3f
+ mov 1, %g7
+ sll %o5, 4, %o5
+ b 1b
+ add %o4, 1, %o4
+
+ ! Now compute %g7.
+ 2: addcc %o5, %o5, %o5
+ bcc Lnot_too_big
+ add %g7, 1, %g7
+
+ ! We get here if the %o1 overflowed while shifting.
+ ! This means that %o3 has the high-order bit set.
+ ! Restore %o5 and subtract from %o3.
+ sll %g1, 4, %g1 ! high order bit
+ srl %o5, 1, %o5 ! rest of %o5
+ add %o5, %g1, %o5
+ b Ldo_single_div
+ sub %g7, 1, %g7
+
+ Lnot_too_big:
+ 3: cmp %o5, %o3
+ blu 2b
+ nop
+ be Ldo_single_div
+ nop
+ /* NB: these are commented out in the V8-Sparc manual as well */
+ /* (I do not understand this) */
+ ! %o5 > %o3: went too far: back up 1 step
+ ! srl %o5, 1, %o5
+ ! dec %g7
+ ! do single-bit divide steps
+ !
+ ! We have to be careful here. We know that %o3 >= %o5, so we can do the
+ ! first divide step without thinking. BUT, the others are conditional,
+ ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high-
+ ! order bit set in the first step, just falling into the regular
+ ! division loop will mess up the first time around.
+ ! So we unroll slightly...
+ Ldo_single_div:
+ subcc %g7, 1, %g7
+ bl Lend_regular_divide
+ nop
+ sub %o3, %o5, %o3
+ mov 1, %o2
+ b Lend_single_divloop
+ nop
+ Lsingle_divloop:
+ sll %o2, 1, %o2
+ bl 1f
+ srl %o5, 1, %o5
+ ! %o3 >= 0
+ sub %o3, %o5, %o3
+ b 2f
+ add %o2, 1, %o2
+ 1: ! %o3 < 0
+ add %o3, %o5, %o3
+ sub %o2, 1, %o2
+ 2:
+ Lend_single_divloop:
+ subcc %g7, 1, %g7
+ bge Lsingle_divloop
+ tst %o3
+ b,a Lend_regular_divide
+
+Lnot_really_big:
+1:
+ sll %o5, 4, %o5
+ cmp %o5, %o3
+ bleu 1b
+ addcc %o4, 1, %o4
+ be Lgot_result
+ sub %o4, 1, %o4
+
+ tst %o3 ! set up for initial iteration
+Ldivloop:
+ sll %o2, 4, %o2
+ ! depth 1, accumulated bits 0
+ bl L.1.16
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 2, accumulated bits 1
+ bl L.2.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 3
+ bl L.3.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 7
+ bl L.4.23
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2+1), %o2
+
+L.4.23:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2-1), %o2
+
+
+L.3.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 5
+ bl L.4.21
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2+1), %o2
+
+L.4.21:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2-1), %o2
+
+
+
+L.2.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 1
+ bl L.3.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 3
+ bl L.4.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2+1), %o2
+
+L.4.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2-1), %o2
+
+
+L.3.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 1
+ bl L.4.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2+1), %o2
+
+L.4.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2-1), %o2
+
+
+
+
+L.1.16:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 2, accumulated bits -1
+ bl L.2.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -1
+ bl L.3.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -1
+ bl L.4.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2+1), %o2
+
+L.4.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2-1), %o2
+
+
+L.3.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -3
+ bl L.4.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2+1), %o2
+
+L.4.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2-1), %o2
+
+
+
+L.2.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -3
+ bl L.3.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -5
+ bl L.4.11
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2+1), %o2
+
+L.4.11:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2-1), %o2
+
+
+L.3.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -7
+ bl L.4.9
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2+1), %o2
+
+L.4.9:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2-1), %o2
+
+
+
+
+ 9:
+Lend_regular_divide:
+ subcc %o4, 1, %o4
+ bge Ldivloop
+ tst %o3
+ bl,a Lgot_result
+ ! non-restoring fixup here (one instruction only!)
+ sub %o2, 1, %o2
+
+
+Lgot_result:
+
+ retl
+ mov %o2, %o0
diff --git a/arch/sparc/lib/umul.S b/arch/sparc/lib/umul.S
new file mode 100644
index 000000000..24f7c3cda
--- /dev/null
+++ b/arch/sparc/lib/umul.S
@@ -0,0 +1,158 @@
+/* umul.S: This routine was taken from glibc-1.09 and is covered
+ * by the GNU Library General Public License Version 2.
+ */
+
+
+/*
+ * Unsigned multiply. Returns %o0 * %o1 in %o1%o0 (i.e., %o1 holds the
+ * upper 32 bits of the 64-bit product).
+ *
+ * This code optimizes short (less than 13-bit) multiplies. Short
+ * multiplies require 25 instruction cycles, and long ones require
+ * 45 instruction cycles.
+ *
+ * On return, overflow has occurred (%o1 is not zero) if and only if
+ * the Z condition code is clear, allowing, e.g., the following:
+ *
+ * call .umul
+ * nop
+ * bnz overflow (or tnz)
+ */
+
+ .globl .umul
+.umul:
+ or %o0, %o1, %o4
+ mov %o0, %y ! multiplier -> Y
+ andncc %o4, 0xfff, %g0 ! test bits 12..31 of *both* args
+ be Lmul_shortway ! if zero, can do it the short way
+ andcc %g0, %g0, %o4 ! zero the partial product and clear N and V
+
+ /*
+ * Long multiply. 32 steps, followed by a final shift step.
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %o1, %o4 ! 13
+ mulscc %o4, %o1, %o4 ! 14
+ mulscc %o4, %o1, %o4 ! 15
+ mulscc %o4, %o1, %o4 ! 16
+ mulscc %o4, %o1, %o4 ! 17
+ mulscc %o4, %o1, %o4 ! 18
+ mulscc %o4, %o1, %o4 ! 19
+ mulscc %o4, %o1, %o4 ! 20
+ mulscc %o4, %o1, %o4 ! 21
+ mulscc %o4, %o1, %o4 ! 22
+ mulscc %o4, %o1, %o4 ! 23
+ mulscc %o4, %o1, %o4 ! 24
+ mulscc %o4, %o1, %o4 ! 25
+ mulscc %o4, %o1, %o4 ! 26
+ mulscc %o4, %o1, %o4 ! 27
+ mulscc %o4, %o1, %o4 ! 28
+ mulscc %o4, %o1, %o4 ! 29
+ mulscc %o4, %o1, %o4 ! 30
+ mulscc %o4, %o1, %o4 ! 31
+ mulscc %o4, %o1, %o4 ! 32
+ mulscc %o4, %g0, %o4 ! final shift
+
+
+ /*
+ * Normally, with the shift-and-add approach, if both numbers are
+ * positive you get the correct result. With 32-bit two's-complement
+ * numbers, -x is represented as
+ *
+ * x 32
+ * ( 2 - ------ ) mod 2 * 2
+ * 32
+ * 2
+ *
+ * (the `mod 2' subtracts 1 from 1.bbbb). To avoid lots of 2^32s,
+ * we can treat this as if the radix point were just to the left
+ * of the sign bit (multiply by 2^32), and get
+ *
+ * -x = (2 - x) mod 2
+ *
+ * Then, ignoring the `mod 2's for convenience:
+ *
+ * x * y = xy
+ * -x * y = 2y - xy
+ * x * -y = 2x - xy
+ * -x * -y = 4 - 2x - 2y + xy
+ *
+ * For signed multiplies, we subtract (x << 32) from the partial
+ * product to fix this problem for negative multipliers (see mul.s).
+ * Because of the way the shift into the partial product is calculated
+ * (N xor V), this term is automatically removed for the multiplicand,
+ * so we don't have to adjust.
+ *
+ * But for unsigned multiplies, the high order bit wasn't a sign bit,
+ * and the correction is wrong. So for unsigned multiplies where the
+ * high order bit is one, we end up with xy - (y << 32). To fix it
+ * we add y << 32.
+ */
+#if 0
+ tst %o1
+ bl,a 1f ! if %o1 < 0 (high order bit = 1),
+ add %o4, %o0, %o4 ! %o4 += %o0 (add y to upper half)
+1: rd %y, %o0 ! get lower half of product
+ retl
+ addcc %o4, %g0, %o1 ! put upper half in place and set Z for %o1==0
+#else
+ /* Faster code from tege@sics.se. */
+ sra %o1, 31, %o2 ! make mask from sign bit
+ and %o0, %o2, %o2 ! %o2 = 0 or %o0, depending on sign of %o1
+ rd %y, %o0 ! get lower half of product
+ retl
+ addcc %o4, %o2, %o1 ! add compensation and put upper half in place
+#endif
+
+Lmul_shortway:
+ /*
+ * Short multiply. 12 steps, followed by a final shift step.
+ * The resulting bits are off by 12 and (32-12) = 20 bit positions,
+ * but there is no problem with %o0 being negative (unlike above),
+ * and overflow is impossible (the answer is at most 24 bits long).
+ */
+ mulscc %o4, %o1, %o4 ! 1
+ mulscc %o4, %o1, %o4 ! 2
+ mulscc %o4, %o1, %o4 ! 3
+ mulscc %o4, %o1, %o4 ! 4
+ mulscc %o4, %o1, %o4 ! 5
+ mulscc %o4, %o1, %o4 ! 6
+ mulscc %o4, %o1, %o4 ! 7
+ mulscc %o4, %o1, %o4 ! 8
+ mulscc %o4, %o1, %o4 ! 9
+ mulscc %o4, %o1, %o4 ! 10
+ mulscc %o4, %o1, %o4 ! 11
+ mulscc %o4, %o1, %o4 ! 12
+ mulscc %o4, %g0, %o4 ! final shift
+
+ /*
+ * %o4 has 20 of the bits that should be in the result; %y has
+ * the bottom 12 (as %y's top 12). That is:
+ *
+ * %o4 %y
+ * +----------------+----------------+
+ * | -12- | -20- | -12- | -20- |
+ * +------(---------+------)---------+
+ * -----result-----
+ *
+ * The 12 bits of %o4 left of the `result' area are all zero;
+ * in fact, all top 20 bits of %o4 are zero.
+ */
+
+ rd %y, %o5
+ sll %o4, 12, %o0 ! shift middle bits left 12
+ srl %o5, 20, %o5 ! shift low bits right 20
+ or %o5, %o0, %o0
+ retl
+ addcc %g0, %g0, %o1 ! %o1 = zero, and set Z
diff --git a/arch/sparc/lib/urem.S b/arch/sparc/lib/urem.S
new file mode 100644
index 000000000..c84aa81e5
--- /dev/null
+++ b/arch/sparc/lib/urem.S
@@ -0,0 +1,344 @@
+/* urem.S: This routine was taken from glibc-1.09 and is covered
+ * by the GNU Library General Public License Version 2.
+ */
+
+/* This file is generated from divrem.m4; DO NOT EDIT! */
+/*
+ * Division and remainder, from Appendix E of the Sparc Version 8
+ * Architecture Manual, with fixes from Gordon Irlam.
+ */
+
+/*
+ * Input: dividend and divisor in %o0 and %o1 respectively.
+ *
+ * m4 parameters:
+ * .urem name of function to generate
+ * rem rem=div => %o0 / %o1; rem=rem => %o0 % %o1
+ * false false=true => signed; false=false => unsigned
+ *
+ * Algorithm parameters:
+ * N how many bits per iteration we try to get (4)
+ * WORDSIZE total number of bits (32)
+ *
+ * Derived constants:
+ * TOPBITS number of bits in the top decade of a number
+ *
+ * Important variables:
+ * Q the partial quotient under development (initially 0)
+ * R the remainder so far, initially the dividend
+ * ITER number of main division loop iterations required;
+ * equal to ceil(log2(quotient) / N). Note that this
+ * is the log base (2^N) of the quotient.
+ * V the current comparand, initially divisor*2^(ITER*N-1)
+ *
+ * Cost:
+ * Current estimate for non-large dividend is
+ * ceil(log2(quotient) / N) * (10 + 7N/2) + C
+ * A large dividend is one greater than 2^(31-TOPBITS) and takes a
+ * different path, as the upper bits of the quotient must be developed
+ * one bit at a time.
+ */
+
+ .globl .urem
+.urem:
+
+ ! Ready to divide. Compute size of quotient; scale comparand.
+ orcc %o1, %g0, %o5
+ bne 1f
+ mov %o0, %o3
+
+ ! Divide by zero trap. If it returns, return 0 (about as
+ ! wrong as possible, but that is what SunOS does...).
+ ta ST_DIV0
+ retl
+ clr %o0
+
+1:
+ cmp %o3, %o5 ! if %o1 exceeds %o0, done
+ blu Lgot_result ! (and algorithm fails otherwise)
+ clr %o2
+ sethi %hi(1 << (32 - 4 - 1)), %g1
+ cmp %o3, %g1
+ blu Lnot_really_big
+ clr %o4
+
+ ! Here the dividend is >= 2**(31-N) or so. We must be careful here,
+ ! as our usual N-at-a-shot divide step will cause overflow and havoc.
+ ! The number of bits in the result here is N*ITER+SC, where SC <= N.
+ ! Compute ITER in an unorthodox manner: know we need to shift V into
+ ! the top decade: so do not even bother to compare to R.
+ 1:
+ cmp %o5, %g1
+ bgeu 3f
+ mov 1, %g7
+ sll %o5, 4, %o5
+ b 1b
+ add %o4, 1, %o4
+
+ ! Now compute %g7.
+ 2: addcc %o5, %o5, %o5
+ bcc Lnot_too_big
+ add %g7, 1, %g7
+
+ ! We get here if the %o1 overflowed while shifting.
+ ! This means that %o3 has the high-order bit set.
+ ! Restore %o5 and subtract from %o3.
+ sll %g1, 4, %g1 ! high order bit
+ srl %o5, 1, %o5 ! rest of %o5
+ add %o5, %g1, %o5
+ b Ldo_single_div
+ sub %g7, 1, %g7
+
+ Lnot_too_big:
+ 3: cmp %o5, %o3
+ blu 2b
+ nop
+ be Ldo_single_div
+ nop
+ /* NB: these are commented out in the V8-Sparc manual as well */
+ /* (I do not understand this) */
+ ! %o5 > %o3: went too far: back up 1 step
+ ! srl %o5, 1, %o5
+ ! dec %g7
+ ! do single-bit divide steps
+ !
+ ! We have to be careful here. We know that %o3 >= %o5, so we can do the
+ ! first divide step without thinking. BUT, the others are conditional,
+ ! and are only done if %o3 >= 0. Because both %o3 and %o5 may have the high-
+ ! order bit set in the first step, just falling into the regular
+ ! division loop will mess up the first time around.
+ ! So we unroll slightly...
+ Ldo_single_div:
+ subcc %g7, 1, %g7
+ bl Lend_regular_divide
+ nop
+ sub %o3, %o5, %o3
+ mov 1, %o2
+ b Lend_single_divloop
+ nop
+ Lsingle_divloop:
+ sll %o2, 1, %o2
+ bl 1f
+ srl %o5, 1, %o5
+ ! %o3 >= 0
+ sub %o3, %o5, %o3
+ b 2f
+ add %o2, 1, %o2
+ 1: ! %o3 < 0
+ add %o3, %o5, %o3
+ sub %o2, 1, %o2
+ 2:
+ Lend_single_divloop:
+ subcc %g7, 1, %g7
+ bge Lsingle_divloop
+ tst %o3
+ b,a Lend_regular_divide
+
+Lnot_really_big:
+1:
+ sll %o5, 4, %o5
+ cmp %o5, %o3
+ bleu 1b
+ addcc %o4, 1, %o4
+ be Lgot_result
+ sub %o4, 1, %o4
+
+ tst %o3 ! set up for initial iteration
+Ldivloop:
+ sll %o2, 4, %o2
+ ! depth 1, accumulated bits 0
+ bl L.1.16
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 2, accumulated bits 1
+ bl L.2.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 3
+ bl L.3.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 7
+ bl L.4.23
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2+1), %o2
+
+L.4.23:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (7*2-1), %o2
+
+
+L.3.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 5
+ bl L.4.21
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2+1), %o2
+
+L.4.21:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (5*2-1), %o2
+
+
+
+L.2.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits 1
+ bl L.3.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 3
+ bl L.4.19
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2+1), %o2
+
+L.4.19:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (3*2-1), %o2
+
+
+L.3.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits 1
+ bl L.4.17
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2+1), %o2
+
+L.4.17:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (1*2-1), %o2
+
+
+
+
+L.1.16:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 2, accumulated bits -1
+ bl L.2.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -1
+ bl L.3.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -1
+ bl L.4.15
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2+1), %o2
+
+L.4.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-1*2-1), %o2
+
+
+L.3.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -3
+ bl L.4.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2+1), %o2
+
+L.4.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-3*2-1), %o2
+
+
+
+L.2.15:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 3, accumulated bits -3
+ bl L.3.13
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -5
+ bl L.4.11
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2+1), %o2
+
+L.4.11:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-5*2-1), %o2
+
+
+L.3.13:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ ! depth 4, accumulated bits -7
+ bl L.4.9
+ srl %o5,1,%o5
+ ! remainder is positive
+ subcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2+1), %o2
+
+L.4.9:
+ ! remainder is negative
+ addcc %o3,%o5,%o3
+ b 9f
+ add %o2, (-7*2-1), %o2
+
+
+
+
+ 9:
+Lend_regular_divide:
+ subcc %o4, 1, %o4
+ bge Ldivloop
+ tst %o3
+ bl,a Lgot_result
+ ! non-restoring fixup here (one instruction only!)
+ add %o3, %o1, %o3
+
+
+Lgot_result:
+
+ retl
+ mov %o3, %o0
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
new file mode 100644
index 000000000..a4148d013
--- /dev/null
+++ b/arch/sparc/mm/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for the linux Sparc-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
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+
+OBJS = fault.o vac-flush.o init.o
+
+mm.o: $(OBJS)
+ $(LD) -r -o mm.o $(OBJS)
+
+modules:
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c
new file mode 100644
index 000000000..4c5fd0bc3
--- /dev/null
+++ b/arch/sparc/mm/fault.c
@@ -0,0 +1,173 @@
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/openprom.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */
+extern struct sparc_phys_banks sp_banks[14];
+
+extern void die_if_kernel(char *,struct pt_regs *,long);
+
+struct linux_romvec *romvec;
+
+/* foo */
+
+int tbase_needs_unmapping;
+
+/* At boot time we determine these two values necessary for setting
+ * up the segment maps and page table entries (pte's).
+ */
+
+int num_segmaps, num_contexts;
+int invalid_segment;
+
+/* various Virtual Address Cache parameters we find at boot time... */
+
+int vac_size, vac_linesize, vac_do_hw_vac_flushes;
+int vac_entries_per_context, vac_entries_per_segment;
+int vac_entries_per_page;
+
+/*
+ * Define this if things work differently on a i386 and a i486:
+ * it will (on a i486) warn about kernel memory accesses that are
+ * done without a 'verify_area(VERIFY_WRITE,..)'
+ */
+#undef CONFIG_TEST_VERIFY_AREA
+
+/* Traverse the memory lists in the prom to see how much physical we
+ * have.
+ */
+
+unsigned long
+probe_memory(void)
+{
+ register struct linux_romvec *lprom;
+ register struct linux_mlist_v0 *mlist;
+ register unsigned long bytes, base_paddr, tally;
+ register int i;
+
+ bytes = tally = 0;
+ base_paddr = 0;
+ i=0;
+ lprom = romvec;
+ switch(lprom->pv_romvers)
+ {
+ case 0:
+ mlist=(*(lprom->pv_v0mem.v0_totphys));
+ bytes = tally = mlist->num_bytes;
+ base_paddr = (unsigned long) mlist->start_adr;
+
+ sp_banks[0].base_addr = base_paddr;
+ sp_banks[0].num_bytes = bytes;
+
+ if(mlist->theres_more != (void *)0) {
+ i++;
+ mlist=mlist->theres_more;
+ bytes=mlist->num_bytes;
+ tally += bytes;
+ sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+ sp_banks[i].num_bytes = mlist->num_bytes;
+ }
+ break;
+ case 2:
+ printk("no v2 memory probe support yet.\n");
+ (*(lprom->pv_halt))();
+ break;
+ }
+
+ i++;
+ sp_banks[i].base_addr = 0xdeadbeef;
+ sp_banks[i].num_bytes = 0;
+
+ return tally;
+}
+
+/* Sparc routine to reserve the mapping of the open boot prom */
+
+/* uncomment this for FAME and FORTUNE! */
+/* #define DEBUG_MAP_PROM */
+
+int
+map_the_prom(int curr_num_segs)
+{
+ register unsigned long prom_va_begin;
+ register unsigned long prom_va_end;
+ register int segmap_entry, i;
+
+ prom_va_begin = LINUX_OPPROM_BEGVM;
+ prom_va_end = LINUX_OPPROM_ENDVM;
+
+#ifdef DEBUG_MAP_PROM
+ printk("\ncurr_num_segs = 0x%x\n", curr_num_segs);
+#endif
+
+ while( prom_va_begin < prom_va_end)
+ {
+ segmap_entry=get_segmap(prom_va_begin);
+
+ curr_num_segs = ((segmap_entry<curr_num_segs)
+ ? segmap_entry : curr_num_segs);
+
+ for(i = num_contexts; --i > 0;)
+ (*romvec->pv_setctxt)(i, (char *) prom_va_begin,
+ segmap_entry);
+
+ if(segmap_entry == invalid_segment)
+ {
+
+#ifdef DEBUG_MAP_PROM
+ printk("invalid_segments, virt_addr 0x%x\n", prom_va_begin);
+#endif
+
+ prom_va_begin += 0x40000; /* num bytes per segment entry */
+ continue;
+ }
+
+ /* DUH, prom maps itself so that users can access it. This is
+ * broken.
+ */
+
+#ifdef DEBUG_MAP_PROM
+ printk("making segmap for prom privileged, va = 0x%x\n",
+ prom_va_begin);
+#endif
+
+ for(i = 0x40; --i >= 0; prom_va_begin+=4096)
+ {
+ put_pte(prom_va_begin, get_pte(prom_va_begin) | 0x20000000);
+ }
+
+ }
+
+ printk("Mapped the PROM in all contexts...\n");
+
+#ifdef DEBUG_MAP_PROM
+ printk("curr_num_segs = 0x%x\n", curr_num_segs);
+#endif
+
+ return curr_num_segs;
+
+}
+
+/*
+ * 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)
+{
+ die_if_kernel("Oops", regs, error_code);
+ do_exit(SIGKILL);
+}
+
+
+
+
diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
new file mode 100644
index 000000000..a65e9e094
--- /dev/null
+++ b/arch/sparc/mm/init.c
@@ -0,0 +1,364 @@
+/*
+ * linux/arch/sparc/mm/init.c
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#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/vac-ops.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+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 int map_the_prom(int);
+
+struct sparc_phys_banks sp_banks[14];
+unsigned long *sun4c_mmu_table;
+extern int invalid_segment, num_segmaps, num_contexts;
+
+/*
+ * 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)
+{
+ memset((void *) EMPTY_PGT, 0, PAGE_SIZE);
+ return (pte_t *) EMPTY_PGT;
+}
+
+pte_t __bad_page(void)
+{
+ memset((void *) EMPTY_PGE, 0, PAGE_SIZE);
+ return pte_mkdirty(mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED));
+}
+
+unsigned long __zero_page(void)
+{
+ memset((void *) ZERO_PGE, 0, PAGE_SIZE);
+ return ZERO_PGE;
+}
+
+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
+}
+
+extern unsigned long free_area_init(unsigned long, unsigned long);
+
+/*
+ * paging_init() sets up the page tables: in the alpha version this actually
+ * unmaps the bootup page table (as we're now in KSEG, so we don't need it).
+ *
+ * The bootup sequence put the virtual page table into high memory: that
+ * means that we can change the L1 page table by just using VL1p below.
+ */
+
+unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
+{
+ unsigned long i, a, b, mask=0;
+ unsigned long curseg, curpte, num_inval;
+ unsigned long address;
+ pte_t *pg_table;
+
+ register int num_segs, num_ctx;
+ register char * c;
+
+ num_segs = num_segmaps;
+ num_ctx = num_contexts;
+
+ num_segs -= 1;
+ invalid_segment = num_segs;
+
+ start_mem = free_area_init(start_mem, end_mem);
+
+/* On the sparc we first need to allocate the segmaps for the
+ * PROM's virtual space, and make those segmaps unusable. We
+ * map the PROM in ALL contexts therefore the break key and the
+ * sync command work no matter what state you took the machine
+ * out of
+ */
+
+ printk("mapping the prom...\n");
+ num_segs = map_the_prom(num_segs);
+
+ start_mem = PAGE_ALIGN(start_mem);
+
+ /* Set up static page tables in kernel space, this will be used
+ * so that the low-level page fault handler can fill in missing
+ * TLB entries since all mmu entries cannot be loaded at once
+ * on the sun4c.
+ */
+
+#if 0
+ /* ugly debugging code */
+ for(i=0; i<40960; i+=PAGE_SIZE)
+ printk("address=0x%x vseg=%d pte=0x%x\n", (unsigned int) i,
+ (int) get_segmap(i), (unsigned int) get_pte(i));
+#endif
+
+ printk("Setting up kernel static mmu table... bounce bounce\n");
+
+ address = 0; /* ((unsigned long) &end) + 524288; */
+ sun4c_mmu_table = (unsigned long *) start_mem;
+ pg_table = (pte_t *) start_mem;
+ curseg = curpte = num_inval = 0;
+ while(address < end_mem) {
+ if(curpte == 0)
+ put_segmap((address&PGDIR_MASK), curseg);
+ for(i=0; sp_banks[i].num_bytes != 0; i++)
+ if((address >= sp_banks[i].base_addr) &&
+ (address <= (sp_banks[i].base_addr + sp_banks[i].num_bytes)))
+ goto good_address;
+ /* No physical memory here, so set the virtual segment to
+ * the invalid one, and put an invalid pte in the static
+ * kernel table.
+ */
+ *pg_table = mk_pte((address >> PAGE_SHIFT), PAGE_INVALID);
+ pg_table++; curpte++; num_inval++;
+ if(curpte > 63) {
+ if(curpte == num_inval) {
+ put_segmap((address&PGDIR_MASK), invalid_segment);
+ } else {
+ put_segmap((address&PGDIR_MASK), curseg);
+ curseg++;
+ }
+ curpte = num_inval = 0;
+ }
+ address += PAGE_SIZE;
+ continue;
+
+ good_address:
+ /* create pte entry */
+ if(address < (((unsigned long) &end) + 524288)) {
+ pte_val(*pg_table) = get_pte(address);
+ } else {
+ *pg_table = mk_pte((address >> PAGE_SHIFT), PAGE_KERNEL);
+ put_pte(address, pte_val(*pg_table));
+ }
+
+ pg_table++; curpte++;
+ if(curpte > 63) {
+ put_segmap((address&PGDIR_MASK), curseg);
+ curpte = num_inval = 0;
+ curseg++;
+ }
+ address += PAGE_SIZE;
+ }
+
+ start_mem = (unsigned long) pg_table;
+ /* ok, allocate the kernel pages, map them in all contexts
+ * (with help from the prom), and lock them. Isn't the sparc
+ * fun kiddies? TODO
+ */
+
+#if 0
+ /* ugly debugging code */
+ for(i=0x1a3000; i<(0x1a3000+40960); i+=PAGE_SIZE)
+ printk("address=0x%x vseg=%d pte=0x%x\n", (unsigned int) i,
+ (int) get_segmap(i), (unsigned int) get_pte(i));
+ halt();
+#endif
+
+ b=PGDIR_ALIGN(start_mem)>>18;
+ c= (char *)0x0;
+
+ printk("mapping kernel in all contexts...\n");
+
+ for(a=0; a<b; a++)
+ {
+ for(i=0; i<num_contexts; i++)
+ {
+ /* map the kernel virt_addrs */
+ (*(romvec->pv_setctxt))(i, (char *) c, a);
+ }
+ c += 0x40000;
+ }
+
+ /* Ok, since now mapped in all contexts, we can free up
+ * context zero to be used amongst user processes.
+ */
+
+ /* free context 0 here TODO */
+
+ /* invalidate all user pages and initialize the pte struct
+ * for userland. TODO
+ */
+
+ /* Make the kernel text unwritable and cacheable, the prom
+ * loaded our text as writable, only sneaky sunos kernels need
+ * self-modifying code.
+ */
+
+ a= (unsigned long) &etext;
+ mask=~(PTE_NC|PTE_W); /* make cacheable + not writable */
+
+ /* must do for every segment since kernel uses all contexts
+ * and unlike some sun kernels I know of, we can't hard wire
+ * context 0 just for the kernel, that is unnecessary.
+ */
+
+ for(i=0; i<8; i++)
+ {
+ b=PAGE_ALIGN((unsigned long) &trapbase);
+
+ switch_to_context(i);
+
+ for(;b<a; b+=4096)
+ {
+ put_pte(b, (get_pte(b) & mask));
+ }
+ }
+
+ invalidate(); /* flush the virtual address cache */
+
+ printk("\nCurrently in context - ");
+ for(i=0; i<num_contexts; i++)
+ {
+ switch_to_context(i);
+ printk("%d ", (int) i);
+ }
+ printk("\n");
+
+ switch_to_context(0);
+
+ invalidate();
+ return start_mem;
+}
+
+void mem_init(unsigned long start_mem, unsigned long end_mem)
+{
+ unsigned long start_low_mem = PAGE_SIZE;
+ int codepages = 0;
+ int reservedpages = 0;
+ int datapages = 0;
+ int i = 0;
+ unsigned long tmp, limit, tmp2, addr;
+ extern char etext;
+
+ end_mem &= PAGE_MASK;
+ high_memory = end_mem;
+
+ start_low_mem = PAGE_ALIGN(start_low_mem);
+ start_mem = PAGE_ALIGN(start_mem);
+
+ for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+ tmp = sp_banks[i].base_addr;
+ limit = (sp_banks[i].base_addr + sp_banks[i].num_bytes);
+ if(tmp<start_mem) {
+ if(limit>start_mem)
+ tmp = start_mem;
+ else continue;
+ }
+
+ while(tmp<limit) {
+ mem_map[MAP_NR(tmp)] = 0;
+ tmp += PAGE_SIZE;
+ }
+ if(sp_banks[i+1].num_bytes != 0)
+ while(tmp < sp_banks[i+1].base_addr) {
+ mem_map[MAP_NR(tmp)] = MAP_PAGE_RESERVED;
+ tmp += PAGE_SIZE;
+ }
+ }
+
+#ifdef CONFIG_SCSI
+ scsi_mem_init(high_memory);
+#endif
+
+ for (addr = 0; addr < high_memory; addr += PAGE_SIZE) {
+ if(mem_map[MAP_NR(addr)]) {
+ if (addr < (unsigned long) &etext)
+ codepages++;
+ else if(addr < start_mem)
+ datapages++;
+ else
+ reservedpages++;
+ continue;
+ }
+ mem_map[MAP_NR(addr)] = 1;
+ free_page(addr);
+ }
+
+ tmp2 = nr_free_pages << PAGE_SHIFT;
+
+ printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
+ tmp2 >> 10,
+ high_memory >> 10,
+ codepages << (PAGE_SHIFT-10),
+ reservedpages << (PAGE_SHIFT-10),
+ datapages << (PAGE_SHIFT-10));
+
+ 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;
+}
diff --git a/arch/sparc/mm/vac-flush.c b/arch/sparc/mm/vac-flush.c
new file mode 100644
index 000000000..796366b53
--- /dev/null
+++ b/arch/sparc/mm/vac-flush.c
@@ -0,0 +1,94 @@
+/* vac.c: Routines for flushing various amount of the Sparc VAC
+ (virtual address cache).
+
+ Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+*/
+
+#include <asm/vac-ops.h>
+#include <asm/page.h>
+
+/* Flush all VAC entries for the current context */
+
+extern int vac_do_hw_vac_flushes, vac_size, vac_linesize;
+extern int vac_entries_per_context, vac_entries_per_segment;
+extern int vac_entries_per_page;
+
+void
+flush_vac_context()
+{
+ register int entries_left, offset;
+ register char* address;
+
+ entries_left = vac_entries_per_context;
+ address = (char *) 0;
+
+ if(vac_do_hw_vac_flushes)
+ {
+ while(entries_left-- >=0)
+ {
+ hw_flush_vac_context_entry(address);
+ address += PAGE_SIZE;
+ }
+ }
+ else
+ {
+ offset = vac_linesize;
+ while(entries_left-- >=0)
+ {
+ sw_flush_vac_context_entry(address);
+ address += offset;
+ }
+ }
+}
+
+void
+flush_vac_segment(register unsigned int segment)
+{
+ register int entries_left, offset;
+ register char* address = (char *) 0;
+
+ entries_left = vac_entries_per_segment;
+ __asm__ __volatile__("sll %0, 18, %1\n\t"
+ "sra %1, 0x2, %1\n\t"
+ : "=r" (segment) : "0" (address));
+
+ if(vac_do_hw_vac_flushes)
+ {
+ while(entries_left-- >=0)
+ {
+ hw_flush_vac_segment_entry(address);
+ address += PAGE_SIZE;
+ }
+ }
+ else
+ {
+ offset = vac_linesize;
+ while(entries_left-- >=0)
+ {
+ sw_flush_vac_segment_entry(address);
+ address += offset;
+ }
+ }
+}
+
+void
+flush_vac_page(register unsigned int addr)
+{
+ register int entries_left, offset;
+
+ if(vac_do_hw_vac_flushes)
+ {
+ hw_flush_vac_page_entry((unsigned long *) addr);
+ }
+ else
+ {
+ entries_left = vac_entries_per_page;
+ offset = vac_linesize;
+ while(entries_left-- >=0)
+ {
+ sw_flush_vac_page_entry((unsigned long *) addr);
+ addr += offset;
+ }
+ }
+}
+