aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@klaava.Helsinki.FI>1992-10-18 14:45:46 +0000
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:09 -0400
commit4c212a02429139af48301686ce7cd24ce1a0b47f (patch)
treedb24a9a2636bc5907d08a450303cd8cdeecc3aa9
parentaee695cd6a47219958fa2068f4732cbf7e159e7e (diff)
downloadarchive-4c212a02429139af48301686ce7cd24ce1a0b47f.tar.gz
ANNOUNCE: 0.98.pl2 is outv0.98-pl2
Patch 2 to 0.98 is out - it's available as both full source and as patches against 0.98.1 at nic.funet.fi: pub/OS/Linux/testing/Linus (and testing is still unreadable, so you have to cd to it blindly). patch-2 is >150kB compressed, as it contains several big changes. Most notable are: - the new FPU-emulator by Bill Metzenthen. It's bigger than the old one, but thanks to it, linux fpu emulation is no longer a quick hack, but a real emulator: it does all the 387(486) math instructions, and does them much faster than the old emulator + the soft library. The new math-emulator means that a separate soft-float library is no longer needed. It also makes even a non-coprocessor system pretty useful for limited math-calcs - the complex functions are much faster when they no longer have to be calculated using simple functions, and even the simpler instructions that my old emulator handled are faster using the new one. The size of the new emulator may mean that people who have little RAM, but do have a coprocessor should probably recompile the kernel with the emulator disabled. - various minor mm fixes by me: trapping kernel NULL dereferences, cleaning up the page table initializations and the 16MB patches, and various other bugfixes. get_free_page(GFP_ATOMIC) should preserve the interrupt flag, so malloc() should be safe now - hopefully no more of the tcp/ip memory management problems. The NULL pointer trapping may result in errors like: Unable to handle kernel paging request at address C0000??? Oops: 0000 ..... debugging info ..... There were several NULL pointer dereferences in the serial and tty drivers, which should now be fixed. I've also fixed any other errors I've seen, but if there are problems in the scsi drivers or similar things I cannot test, I'd like to hear about them. - scsi driver changes by Eric Youngdale. Preliminary support for removable media, and some bug-fixes. Due to white-space problems with eric's patches, the scsi patches are a bit bigger than necessary, but they should be ok even though I had to put them in partly by hand (and being unable to test them...) - The new tcp/ip patches that were sent to the NET channel not long ago. Yes, they are alpha, but so is the whole tcp/ip directory, so I put them in even thought they haven't been extensively tested (and they did have a serious problem in the ioctl code, which I fixed). - psaux mouse patches by Dean Troyer, as well as the mouse.wait = NULL patch. Before (or after) patching, you should remove the old math-emulator (ie "rm -rf /usr/src/linux/kernel/math") as it is no longer needed. You should also do a "make dep" to update dependencies: as usual, I edited out the dependancy-changes. Do a "make clean", edit the main (and net) Makefiles to suit your system, and compile. And finally: I will no longer be making the bootdisks available - they'll be made by hlu/jwinstead and will probably be boot+root-disks using lilo, as done on the hlu disks. That may mean that a bootimage won't be available at once, but most people who want to use the absolutely newest images probably compile them themselves anyway, so that shouldn't be a problem. Linus
-rw-r--r--Makefile42
-rw-r--r--README4
-rw-r--r--boot/head.S110
-rw-r--r--boot/setup.S26
-rw-r--r--fs/Makefile3
-rw-r--r--fs/buffer.c24
-rw-r--r--fs/exec.c21
-rw-r--r--fs/ext/blkdev.c1
-rw-r--r--fs/ext/chrdev.c1
-rw-r--r--fs/ext/dir.c8
-rw-r--r--fs/ext/file.c1
-rw-r--r--fs/ext/namei.c3
-rw-r--r--fs/fifo.c1
-rw-r--r--fs/ioctl.c42
-rw-r--r--fs/minix/blkdev.c1
-rw-r--r--fs/minix/chrdev.c1
-rw-r--r--fs/minix/dir.c8
-rw-r--r--fs/minix/file.c11
-rw-r--r--fs/minix/namei.c3
-rw-r--r--fs/minix/truncate.c10
-rw-r--r--fs/msdos/Makefile3
-rw-r--r--fs/msdos/dir.c27
-rw-r--r--fs/msdos/file.c1
-rw-r--r--fs/msdos/inode.c31
-rw-r--r--fs/pipe.c3
-rw-r--r--fs/proc/base.c1
-rw-r--r--fs/proc/fd.c1
-rw-r--r--fs/proc/mem.c1
-rw-r--r--fs/proc/root.c1
-rw-r--r--fs/read_write.c14
-rw-r--r--include/asm/bitops.h96
-rw-r--r--include/asm/io.h2
-rw-r--r--include/asm/system.h6
-rw-r--r--include/linux/cdrom.h15
-rw-r--r--include/linux/config.dist.h2
-rw-r--r--include/linux/fs.h6
-rw-r--r--include/linux/minix_fs.h7
-rw-r--r--include/linux/mm.h45
-rw-r--r--include/linux/mouse.h7
-rw-r--r--include/linux/sched.h39
-rw-r--r--include/linux/serial.h15
-rw-r--r--include/linux/socket.h9
-rw-r--r--include/linux/termios.h6
-rw-r--r--include/linux/tty.h43
-rw-r--r--include/linux/vmm.h44
-rw-r--r--init/main.c7
-rw-r--r--kernel/FPU-emu/Makefile194
-rw-r--r--kernel/FPU-emu/README185
-rw-r--r--kernel/FPU-emu/control_w.h33
-rw-r--r--kernel/FPU-emu/div_small.S50
-rw-r--r--kernel/FPU-emu/errors.c443
-rw-r--r--kernel/FPU-emu/exception.h49
-rw-r--r--kernel/FPU-emu/fpu_arith.c158
-rw-r--r--kernel/FPU-emu/fpu_asm.h30
-rw-r--r--kernel/FPU-emu/fpu_aux.c148
-rw-r--r--kernel/FPU-emu/fpu_emu.h112
-rw-r--r--kernel/FPU-emu/fpu_entry.c275
-rw-r--r--kernel/FPU-emu/fpu_etc.c114
-rw-r--r--kernel/FPU-emu/fpu_proto.h100
-rw-r--r--kernel/FPU-emu/fpu_system.h35
-rw-r--r--kernel/FPU-emu/fpu_trig.c955
-rw-r--r--kernel/FPU-emu/get_address.c152
-rw-r--r--kernel/FPU-emu/load_store.c182
-rw-r--r--kernel/FPU-emu/poly_2xm1.c98
-rw-r--r--kernel/FPU-emu/poly_atan.c206
-rw-r--r--kernel/FPU-emu/poly_div.S97
-rw-r--r--kernel/FPU-emu/poly_l2.c288
-rw-r--r--kernel/FPU-emu/poly_mul64.S72
-rw-r--r--kernel/FPU-emu/poly_sin.c146
-rw-r--r--kernel/FPU-emu/poly_tan.c179
-rw-r--r--kernel/FPU-emu/polynomial.S140
-rw-r--r--kernel/FPU-emu/reg_add_sub.c160
-rw-r--r--kernel/FPU-emu/reg_compare.c269
-rw-r--r--kernel/FPU-emu/reg_constant.c111
-rw-r--r--kernel/FPU-emu/reg_constant.h31
-rw-r--r--kernel/FPU-emu/reg_div.S172
-rw-r--r--kernel/FPU-emu/reg_ld_str.c1194
-rw-r--r--kernel/FPU-emu/reg_mul.c85
-rw-r--r--kernel/FPU-emu/reg_norm.S84
-rw-r--r--kernel/FPU-emu/reg_u_add.S186
-rw-r--r--kernel/FPU-emu/reg_u_div.S475
-rw-r--r--kernel/FPU-emu/reg_u_mul.S137
-rw-r--r--kernel/FPU-emu/reg_u_sub.S274
-rw-r--r--kernel/FPU-emu/status_w.h51
-rw-r--r--kernel/FPU-emu/version.h12
-rw-r--r--kernel/FPU-emu/wm_shrx.S208
-rw-r--r--kernel/FPU-emu/wm_sqrt.S277
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/blk_drv/floppy.c2
-rw-r--r--kernel/blk_drv/genhd.c23
-rw-r--r--kernel/blk_drv/hd.c154
-rw-r--r--kernel/blk_drv/ramdisk.c1
-rw-r--r--kernel/blk_drv/scsi/Makefile27
-rw-r--r--kernel/blk_drv/scsi/aha1542.c6
-rw-r--r--kernel/blk_drv/scsi/scsi.c21
-rw-r--r--kernel/blk_drv/scsi/scsi.h5
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.c100
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.h8
-rw-r--r--kernel/blk_drv/scsi/sd.c424
-rw-r--r--kernel/blk_drv/scsi/sd_ioctl.c13
-rw-r--r--kernel/blk_drv/scsi/seagate.c3
-rw-r--r--kernel/blk_drv/scsi/sr.c57
-rw-r--r--kernel/blk_drv/scsi/sr.h1
-rw-r--r--kernel/blk_drv/scsi/sr_ioctl.c81
-rw-r--r--kernel/chr_drv/Makefile26
-rw-r--r--kernel/chr_drv/atixlmouse.c5
-rw-r--r--kernel/chr_drv/busmouse.c5
-rw-r--r--kernel/chr_drv/keyboard.c8
-rw-r--r--kernel/chr_drv/lp.c1
-rw-r--r--kernel/chr_drv/mem.c179
-rw-r--r--kernel/chr_drv/mouse.c26
-rw-r--r--kernel/chr_drv/msbusmouse.c5
-rw-r--r--kernel/chr_drv/psaux.c109
-rw-r--r--kernel/chr_drv/pty.c43
-rw-r--r--kernel/chr_drv/serial.c652
-rw-r--r--kernel/chr_drv/tty_io.c165
-rw-r--r--kernel/exit.c10
-rw-r--r--kernel/irq.c10
-rw-r--r--kernel/math/Makefile106
-rw-r--r--kernel/math/add.c92
-rw-r--r--kernel/math/compare.c60
-rw-r--r--kernel/math/convert.c254
-rw-r--r--kernel/math/div.c109
-rw-r--r--kernel/math/ea.c92
-rw-r--r--kernel/math/emulate.c552
-rw-r--r--kernel/math/get_put.c239
-rw-r--r--kernel/math/mul.c73
-rw-r--r--kernel/math/sqrt.c95
-rw-r--r--kernel/sched.c11
-rw-r--r--kernel/sys.c18
-rw-r--r--kernel/traps.c2
-rw-r--r--mm/Makefile3
-rw-r--r--mm/memory.c41
-rw-r--r--mm/mmap.c1
-rw-r--r--mm/swap.c161
-rw-r--r--net/socket.c3
-rw-r--r--net/tcp/Makefile52
-rw-r--r--net/tcp/dev.c7
-rw-r--r--net/tcp/icmp.c4
-rw-r--r--net/tcp/ip.h3
-rw-r--r--net/tcp/protocols.c6
-rw-r--r--net/tcp/sock.c149
-rw-r--r--net/tcp/sock.h2
-rw-r--r--net/tcp/tcp.c127
-rw-r--r--net/tcp/tcp.h2
-rw-r--r--net/tcp/udp.c62
-rw-r--r--tools/version.h6
147 files changed, 10562 insertions, 2862 deletions
diff --git a/Makefile b/Makefile
index 357cd04..c271517 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# default of FLOPPY is used by 'build'.
#
-ROOT_DEV =# /dev/hdb1
+ROOT_DEV = /dev/hdb1
#
# uncomment this if you want kernel profiling: the profile_shift is the
@@ -30,9 +30,9 @@ PROFILING =# -DPROFILE_SHIFT=2
# 0x08 - tilde (~)
# 0x10 - dieresis (umlaut)
-# KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
+KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F
-KEYBOARD = -DKBD_US -DKBDFLAGS=0
+# KEYBOARD = -DKBD_US -DKBDFLAGS=0
# KEYBOARD = -DKBD_GR -DKBDFLAGS=0
# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F
# KEYBOARD = -DKBD_FR -DKBDFLAGS=0
@@ -54,23 +54,31 @@ KEYBOARD = -DKBD_US -DKBDFLAGS=0
MATH_EMULATION = -DKERNEL_MATH_EMULATION
#
-# Maximum memory used by the kernel. This is normally 16MB - some of the
-# SCSI drivers may have problems with anything else due to DMA limits. The
-# drivers should check, but they don't. The ONLY valid values for
-# MAX_MEGABYTES are 16 and 32 - anything else needs kernel diffs.
+# Comment out this line if you don't want the 16MB kernel limit - but
+# note that some of the SCSI drivers may have problems with anything
+# else due to DMA limits. The drivers should check, but they don't.
#
# EVEN IF YOU HAVE > 16MB, YOU SHOULD EDIT THIS ONLY IF YOU ARE 100%
# SURE YOU AREN'T USING ANY DEVICE THAT DOES UNCHECKED DMA!! THE
# FLOPPY DRIVER IS OK, BUT OTHERS MIGHT HAVE PROBLEMS.
#
-MAX_MEGABYTES = 16
+LIMIT_MEMORY = -DMAX_16M
+
+#
+# If you want to preset the SVGA mode, uncomment the next line and
+# set SVGA_MODE to whatever number you want.
+# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
+# The number is the same as you would ordinarily press at bootup.
+#
+
+SVGA_MODE= -DSVGA_MODE=1
#
# standard CFLAGS
#
-CFLAGS =-Wall -O6 -fomit-frame-pointer -DMAX_MEGABYTES=$(MAX_MEGABYTES)
+CFLAGS =-Wall -O6 -fomit-frame-pointer $(LIMIT_MEMORY)
#
# if you want the ram-disk device, define this to be the
@@ -82,27 +90,19 @@ CFLAGS =-Wall -O6 -fomit-frame-pointer -DMAX_MEGABYTES=$(MAX_MEGABYTES)
AS86 =as86 -0 -a
LD86 =ld86 -0
-#
-# If you want to preset the SVGA mode, uncomment the next line and
-# set SVGA_MODE to whatever number you want.
-# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
-# The number is the same as you would ordinarily press at bootup.
-#
-SVGA_MODE=# -DSVGA_MODE=1
-
AS =as
LD =ld
HOSTCC =gcc -static
CC =gcc -DKERNEL
MAKE =make
-CPP =$(CC) -E -DMAX_MEGABYTES=$(MAX_MEGABYTES)
+CPP =$(CC) -E $(LIMIT_MEMORY)
AR =ar
ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o net/net.o
FILESYSTEMS =fs/minix/minix.o fs/ext/ext.o fs/msdos/msdos.o fs/proc/proc.o
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \
kernel/blk_drv/scsi/scsi.a
-MATH =kernel/math/math.a
+MATH =kernel/FPU-emu/math.a
LIBS =lib/lib.a
SUBDIRS =kernel mm fs net lib
@@ -120,14 +120,14 @@ all: Version Image
lilo: Image
if [ -f /vmlinux ]; then mv /vmlinux /vmlinux.old; fi
dd if=Image of=/vmlinux
- /etc/lilo/lilo -b /dev/hda /vmlinux
+ /etc/lilo/lilo -c -b /dev/hda /vmlinux
linuxsubdirs: dummy
@for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
Version:
@./makever.sh
- @echo \#define UTS_RELEASE \"0.98.pl1-`cat .version`\" > tools/version.h
+ @echo \#define UTS_RELEASE \"0.98.pl2-`cat .version`\" > tools/version.h
@echo \#define UTS_VERSION \"`date +%D`\" >> tools/version.h
@echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h
@echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h
diff --git a/README b/README
index 3bc7d7f..0e85e71 100644
--- a/README
+++ b/README
@@ -36,6 +36,10 @@ the FPU if it finds one, even with MATH_EMULATION defined. The kernel
will be slightly bigger. It is probably not worth it to recompile the
kernel just to get rid of the emulation.
+[ Linus' note: the new math-emulator in 0.98.2 is much better than my
+ old one, but it also takes up more memory. You may want to remove it
+ if you are short on memory and long on math-coprocessors ]
+
2. Create the symlinks:
ln -fs /usr/src/linux/include/linux /usr/include/linux
diff --git a/boot/head.S b/boot/head.S
index e55b467..47355cb 100644
--- a/boot/head.S
+++ b/boot/head.S
@@ -4,10 +4,6 @@
* Copyright (C) 1991, 1992 Linus Torvalds
*/
-#if (MAX_MEGABYTES != 16) && (MAX_MEGABYTES != 32)
-#error "MAX_MEGABYTES must be 16 or 32"
-#endif
-
/*
* head.s contains the 32-bit startup code.
*
@@ -17,10 +13,12 @@
*/
.text
.globl _idt,_gdt,_swapper_pg_dir,_tmp_floppy_area,_floppy_track_buffer
+.globl _empty_bad_page
+.globl _empty_bad_page_table
+
/*
- * swapper_pg_dir is the main page directory, address 0x00000000
+ * swapper_pg_dir is the main page directory, address 0x00001000
*/
-_swapper_pg_dir:
startup_32:
cld
movl $0x10,%eax
@@ -75,6 +73,7 @@ startup_32:
* We depend on ET to be correct. This checks for 287/387.
*/
check_x87:
+ movl $0,_hard_math
fninit
fstsw %ax
cmpb $0,%al
@@ -84,7 +83,8 @@ check_x87:
movl %eax,%cr0
ret
.align 2
-1: .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
+1: movl $1,_hard_math
+ .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
ret
/*
@@ -115,64 +115,25 @@ rp_sidt:
ret
/*
- * I put the kernel page tables right after the page directory,
- * using 4 of them to span 16 Mb of physical memory. People with
- * more than 16MB will have to expand this.
- * When MAX_MEGABYTES == 32, this is set up for a maximum of 32 MB
- * (ref: 17Apr92) (redone for 0.97 kernel changes, 1Aug92, ref)
+ * page 0 is made non-existent, so that kernel NULL pointer references get
+ * caught. Thus the swapper page directory has been moved to 0x1000
*/
.org 0x1000
-pg0:
-
+_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
-pg1:
+pg0:
.org 0x3000
-pg2:
-
-.org 0x4000
-pg3:
-
-.org 0x5000
-#if MAX_MEGABYTES == 32
-pg4:
-
-.org 0x6000
-pg5:
-
-.org 0x7000
-pg6:
-
-.org 0x8000
-pg7:
-
-.org 0x9000
-#endif
-/*
- * empty_bad_page is a bogus page that will be used when out of memory,
- * so that a process isn't accidentally killed due to a page fault when
- * it is running in kernel mode..
- */
-.globl _empty_bad_page
_empty_bad_page:
-#if MAX_MEGABYTES == 32
-.org 0xa000
-#else
-.org 0x6000
-#endif
-/*
- * empty_bad_page_table is similar to the above, but is used when the
- * system needs a bogus page-table
- */
-.globl _empty_bad_page_table
+.org 0x4000
_empty_bad_page_table:
-#if MAX_MEGABYTES == 32
-.org 0xb000
-#else
-.org 0x7000
-#endif
+.org 0x5000
/*
* 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
@@ -242,8 +203,7 @@ ignore_int:
*
* This routine sets up paging by setting the page bit
* in cr0. The page tables are set up, identity-mapping
- * the first 16MB. The pager assumes that no illegal
- * addresses are produced (ie >4Mb on a 4Mb machine).
+ * the first 4MB. The rest are initialized later.
*
* NOTE! Although all physical memory should be identity
* mapped by this routine, only the kernel page functions
@@ -263,47 +223,31 @@ ignore_int:
*
* (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:
-#if MAXMEGABYTES == 32
- movl $1024*9,%ecx /* 9 pages - swapper_pg_dir+8 page tables */
-#else
- movl $1024*5,%ecx /* 5 pages - swapper_pg_dir+4 page tables */
-#endif
+ movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
xorl %eax,%eax
- xorl %edi,%edi /* swapper_pg_dir is at 0x000 */
+ 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 $pg1+7,_swapper_pg_dir+3076 /* --------- " " --------- */
- movl $pg2+7,_swapper_pg_dir+3080 /* --------- " " --------- */
- movl $pg3+7,_swapper_pg_dir+3084 /* --------- " " --------- */
-#if MAX_MEGABYTES == 32
- movl $pg4+7,_swapper_pg_dir+3088 /* --------- " " --------- */
- movl $pg5+7,_swapper_pg_dir+3092 /* --------- " " --------- */
- movl $pg6+7,_swapper_pg_dir+3096 /* --------- " " --------- */
- movl $pg7+7,_swapper_pg_dir+3100 /* --------- " " --------- */
-
- movl $pg7+4092,%edi
- movl $0x1fff007,%eax /* 32Mb - 4096 + 7 (r/w user,p) */
-#else
- movl $pg3+4092,%edi
- movl $0x0fff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */
-#endif
+ movl $pg0+4092,%edi
+ movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
std
-1: stosl /* fill pages backwards - more efficient :-) */
+1: stosl /* fill the page backwards - more efficient :-) */
subl $0x1000,%eax
jge 1b
cld
- xorl %eax,%eax /* swapper_pg_dir is at 0x0000 */
- movl %eax,%cr3 /* cr3 - page directory start */
+ 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 prefetch-queue */
+ ret /* this also flushes the prefetch-queue */
/*
* The interrupt descriptor table has room for 256 idt's
diff --git a/boot/setup.S b/boot/setup.S
index af776d7..320c0cf 100644
--- a/boot/setup.S
+++ b/boot/setup.S
@@ -11,6 +11,9 @@
! system to read them from there before the area is overwritten
! for buffer-blocks.
!
+! Move PS/2 aux init code to psaux.c
+! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
+!
! NOTE! These had better be the same as in bootsect.s!
#include <linux/config.h>
@@ -134,29 +137,6 @@ is_disk1:
int 0x11 ! int 0x11: equipment determination
test al,#0x04 ! check if pointing device installed
jz no_psmouse
- mov ax,#0xc201 ! reset pointing device
- int 0x15
- jc no_psmouse
- mov bh,#0x03 ! 3 bytes/packet
- mov ax,#0xc205 ! initialize pointing device
- int 0x15
- jc no_psmouse
- mov ax,#0xc203 ! set resolution
- mov bh,#0x03 ! 8 counts/mm
- int 0x15
- jc no_psmouse
- mov ax,#0xc206 ! set scaling
- mov bh,0x02 ! 2:1 scaling
- int 0x15
- jc no_psmouse
- mov ax,#0xc202 ! set sample rate
- mov bh,#0x05 ! 100 reports per second
- int 0x15
- jc no_psmouse
- mov bh,#0x01
- mov ax,#0xc200 ! enable pointing device
- int 0x15
- jc no_psmouse
mov [0x1ff],#0xaa ! device present
no_psmouse:
diff --git a/fs/Makefile b/fs/Makefile
index e768b45..ed1b330 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -110,7 +110,8 @@ ioctl.o : ioctl.c /usr/include/asm/segment.h /usr/include/linux/sched.h /usr/inc
/usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
/usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
/usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/errno.h \
- /usr/include/linux/string.h /usr/include/linux/stat.h /usr/include/linux/termios.h
+ /usr/include/linux/string.h /usr/include/linux/stat.h /usr/include/linux/termios.h \
+ /usr/include/linux/fcntl.h
locks.o : locks.c /usr/include/asm/segment.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
diff --git a/fs/buffer.c b/fs/buffer.c
index 06ac604..7203b65 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -28,9 +28,15 @@
#include <asm/system.h>
#include <asm/io.h>
-#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
+#ifdef CONFIG_SCSI
+#ifdef CONFIG_BLK_DEV_SR
extern int check_cdrom_media_change(int, int);
#endif
+#ifdef CONFIG_BLK_DEV_SD
+extern int check_scsidisk_media_change(int, int);
+extern int revalidate_scsidisk(int, int);
+#endif
+#endif
static struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list = NULL;
@@ -91,7 +97,7 @@ int sync_dev(int dev)
return 0;
}
-void inline invalidate_buffers(int dev)
+void invalidate_buffers(int dev)
{
int i;
struct buffer_head * bh;
@@ -133,6 +139,13 @@ void check_disk_change(int dev)
brelse(bh);
break;
+#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
+ case 8: /* Removable scsi disk */
+ i = check_scsidisk_media_change(dev, 0);
+ if (i) printk("Flushing buffers and inodes for SCSI disk\n");
+ break;
+#endif
+
#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
case 11: /* CDROM */
i = check_cdrom_media_change(dev, 0);
@@ -151,6 +164,13 @@ void check_disk_change(int dev)
put_super(super_block[i].s_dev);
invalidate_inodes(dev);
invalidate_buffers(dev);
+
+#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
+/* This is trickier for a removable hardisk, because we have to invalidate
+ all of the partitions that lie on the disk. */
+ if (MAJOR(dev) == 8)
+ revalidate_scsidisk(dev, 0);
+#endif
}
#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
diff --git a/fs/exec.c b/fs/exec.c
index 452981b..32b8356 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -124,13 +124,20 @@ int core_dump(long signr, struct pt_regs * regs)
dump.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump)));
dump.signal = signr;
dump.regs = *regs;
-/* Flag indicating the math stuff is valid. */
- if (dump.u_fpvalid = current->used_math) {
- if (last_task_used_math == current)
- __asm__("clts ; fnsave %0"::"m" (dump.i387));
- else
- memcpy(&dump.i387,&current->tss.i387,sizeof(dump.i387));
- };
+/* 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) {
+ 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;
+ }
__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
DUMP_WRITE(&dump,sizeof(dump));
DUMP_SEEK(sizeof(dump));
diff --git a/fs/ext/blkdev.c b/fs/ext/blkdev.c
index 5745c7d..031678a 100644
--- a/fs/ext/blkdev.c
+++ b/fs/ext/blkdev.c
@@ -45,6 +45,7 @@ static struct file_operations def_blk_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
+ NULL, /* mmap */
blkdev_open, /* open */
NULL, /* release */
};
diff --git a/fs/ext/chrdev.c b/fs/ext/chrdev.c
index 18f9fb2..5d31e39 100644
--- a/fs/ext/chrdev.c
+++ b/fs/ext/chrdev.c
@@ -45,6 +45,7 @@ static struct file_operations def_chr_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
+ NULL, /* mmap */
chrdev_open, /* open */
NULL, /* release */
};
diff --git a/fs/ext/dir.c b/fs/ext/dir.c
index 1412e29..1c4b7f1 100644
--- a/fs/ext/dir.c
+++ b/fs/ext/dir.c
@@ -20,15 +20,21 @@
#include <linux/ext_fs.h>
#include <linux/stat.h>
+static int ext_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ return -EISDIR;
+}
+
static int ext_readdir(struct inode *, struct file *, struct dirent *, int);
static struct file_operations ext_dir_operations = {
NULL, /* lseek - default */
- NULL, /* read */
+ ext_dir_read, /* read */
NULL, /* write - bad */
ext_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/fs/ext/file.c b/fs/ext/file.c
index d8f3663..39d3348 100644
--- a/fs/ext/file.c
+++ b/fs/ext/file.c
@@ -52,6 +52,7 @@ static struct file_operations ext_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open is needed */
NULL /* release */
};
diff --git a/fs/ext/namei.c b/fs/ext/namei.c
index 8354e4c..6be0c48 100644
--- a/fs/ext/namei.c
+++ b/fs/ext/namei.c
@@ -771,6 +771,7 @@ start_up:
old_inode = iget(old_dir->i_dev, old_de->inode);
if (!old_inode)
goto end_rename;
+ retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->euid != old_inode->i_uid &&
current->euid != old_dir->i_uid && !suser())
@@ -787,7 +788,7 @@ start_up:
retval = 0;
goto end_rename;
}
- if (S_ISDIR(new_inode->i_mode)) {
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
retval = -EEXIST;
goto end_rename;
}
diff --git a/fs/fifo.c b/fs/fifo.c
index 319e39b..7d12559 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -109,6 +109,7 @@ struct file_operations def_fifo_fops = {
NULL,
NULL,
NULL,
+ NULL,
fifo_open, /* will set read or write pipe_fops */
NULL
};
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 980125d..adc3af0 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -11,6 +11,7 @@
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/termios.h>
+#include <linux/fcntl.h> /* for f_flags values */
static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
@@ -48,12 +49,43 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
+ int on;
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
- if (filp->f_inode && S_ISREG(filp->f_inode->i_mode))
- return file_ioctl(filp,cmd,arg);
- if (filp->f_op && filp->f_op->ioctl)
- return filp->f_op->ioctl(filp->f_inode, filp, cmd,arg);
- return -EINVAL;
+ switch (cmd) {
+ case FIOCLEX:
+ current->close_on_exec |= (1 << fd);
+ return 0;
+
+ case FIONCLEX:
+ current->close_on_exec &= ~(1 << fd);
+ return 0;
+
+ case FIONBIO:
+ on = get_fs_long((unsigned long *) arg);
+ if (on)
+ filp->f_flags |= O_NONBLOCK;
+ else
+ filp->f_flags &= ~O_NONBLOCK;
+ return 0;
+
+ case FIOASYNC: /* O_SYNC is not yet implemented,
+ but it's here for completeness. */
+ on = get_fs_long ((unsigned long *) arg);
+ if (on)
+ filp->f_flags |= O_SYNC;
+ else
+ filp->f_flags &= ~O_SYNC;
+ return 0;
+
+ default:
+ if (filp->f_inode && S_ISREG(filp->f_inode->i_mode))
+ return file_ioctl(filp,cmd,arg);
+
+ if (filp->f_op && filp->f_op->ioctl)
+ return filp->f_op->ioctl(filp->f_inode, filp, cmd,arg);
+
+ return -EINVAL;
+ }
}
diff --git a/fs/minix/blkdev.c b/fs/minix/blkdev.c
index f21c270..061fc5c 100644
--- a/fs/minix/blkdev.c
+++ b/fs/minix/blkdev.c
@@ -39,6 +39,7 @@ static struct file_operations def_blk_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
+ NULL, /* mmap */
blkdev_open, /* open */
NULL, /* release */
};
diff --git a/fs/minix/chrdev.c b/fs/minix/chrdev.c
index 8874ea1..6f5e9d6 100644
--- a/fs/minix/chrdev.c
+++ b/fs/minix/chrdev.c
@@ -39,6 +39,7 @@ static struct file_operations def_chr_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
+ NULL, /* mmap */
chrdev_open, /* open */
NULL, /* release */
};
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index 4142b9a..349139f 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -13,15 +13,21 @@
#include <linux/minix_fs.h>
#include <linux/stat.h>
+static int minix_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ return -EISDIR;
+}
+
static int minix_readdir(struct inode *, struct file *, struct dirent *, int);
static struct file_operations minix_dir_operations = {
NULL, /* lseek - default */
- minix_file_read, /* read */
+ minix_dir_read, /* read */
NULL, /* write - bad */
minix_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 7c8f81b..48ca216 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -32,7 +32,7 @@ static inline void wait_on_buffer(struct buffer_head * bh)
sti();
}
-int minix_file_read(struct inode *, struct file *, char *, int);
+static int minix_file_read(struct inode *, struct file *, char *, int);
static int minix_file_write(struct inode *, struct file *, char *, int);
/*
@@ -46,6 +46,7 @@ static struct file_operations minix_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open is needed */
NULL /* release */
};
@@ -67,13 +68,7 @@ struct inode_operations minix_file_inode_operations = {
minix_truncate /* truncate */
};
-/*
- * minix_file_read() is also needed by the directory read-routine,
- * so it's not static. NOTE! reading directories directly is a bad idea,
- * but has to be supported for now for compatability reasons with older
- * versions.
- */
-int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+static int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count)
{
int read,left,chars;
int block, blocks, offset;
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index af8bef3..bcc5f3f 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -633,6 +633,7 @@ start_up:
old_inode = iget(old_dir->i_dev, old_de->inode);
if (!old_inode)
goto end_rename;
+ retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->euid != old_inode->i_uid &&
current->euid != old_dir->i_uid && !suser())
@@ -649,7 +650,7 @@ start_up:
retval = 0;
goto end_rename;
}
- if (S_ISDIR(new_inode->i_mode)) {
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
retval = -EEXIST;
goto end_rename;
}
diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c
index 507257f..276e5df 100644
--- a/fs/minix/truncate.c
+++ b/fs/minix/truncate.c
@@ -183,13 +183,3 @@ void minix_truncate(struct inode * inode)
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
}
-
-/*
- * Called when a inode is released. Note that this is different
- * from minix_open: open gets called at every open, but release
- * gets called only when /all/ the files are closed.
- */
-void minix_release(struct inode * inode, struct file * filp)
-{
- printk("minix_release not implemented\n");
-}
diff --git a/fs/msdos/Makefile b/fs/msdos/Makefile
index e73202a..c3700df 100644
--- a/fs/msdos/Makefile
+++ b/fs/msdos/Makefile
@@ -62,7 +62,8 @@ inode.o : inode.c /usr/include/linux/msdos_fs.h /usr/include/linux/fs.h /usr/inc
/usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/mm.h \
/usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
/usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/errno.h \
- /usr/include/linux/string.h /usr/include/linux/stat.h /usr/include/asm/segment.h
+ /usr/include/linux/string.h /usr/include/linux/ctype.h /usr/include/linux/stat.h \
+ /usr/include/asm/segment.h
misc.o : misc.c /usr/include/linux/msdos_fs.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
/usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
/usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
diff --git a/fs/msdos/dir.c b/fs/msdos/dir.c
index b01b33f..c19cb8e 100644
--- a/fs/msdos/dir.c
+++ b/fs/msdos/dir.c
@@ -14,19 +14,22 @@
#include <linux/errno.h>
#include <linux/stat.h>
-static int msdos_dummy_read(struct inode *inode,struct file *filp,char *buf,
- int count);
+static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
+{
+ return -EISDIR;
+}
+
static int msdos_readdir(struct inode *inode,struct file *filp,
struct dirent *dirent,int count);
-
static struct file_operations msdos_dir_operations = {
NULL, /* lseek - default */
- msdos_dummy_read, /* read */
+ msdos_dir_read, /* read */
NULL, /* write - bad */
msdos_readdir, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
@@ -48,22 +51,6 @@ struct inode_operations msdos_dir_inode_operations = {
NULL /* truncate */
};
-
-/* So grep * doesn't complain in the presence of directories. */
-
-static int msdos_dummy_read(struct inode *inode,struct file *filp,char *buf,
- int count)
-{
- static long last_warning = 0;
-
- if (CURRENT_TIME-last_warning >= 10) {
- printk("COMPATIBILITY WARNING: reading a directory\n");
- last_warning = CURRENT_TIME;
- }
- return 0;
-}
-
-
static int msdos_readdir(struct inode *inode,struct file *filp,
struct dirent *dirent,int count)
{
diff --git a/fs/msdos/file.c b/fs/msdos/file.c
index 330f46c..f362b3b 100644
--- a/fs/msdos/file.c
+++ b/fs/msdos/file.c
@@ -32,6 +32,7 @@ static struct file_operations msdos_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open is needed */
NULL /* release */
};
diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c
index 298d9da..5b0ff42 100644
--- a/fs/msdos/inode.c
+++ b/fs/msdos/inode.c
@@ -55,18 +55,27 @@ static struct super_operations msdos_sops = {
};
-static unsigned long simple_strtoul(const char *cp,char **endp)
+static unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{
- int base = 10;
- unsigned long result = 0;
+ unsigned long result = 0,value;
- if (*cp == '0') {
- base = 8;
+ if (!base) {
+ base = 10;
+ if (*cp == '0') {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1])) {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+ ? toupper(*cp) : *cp)-'A'+10) < base) {
+ result = result*base + value;
cp++;
}
- while (isdigit(*cp) && (*cp - '0') < base)
- result = result*base + *cp++ - '0';
- if (*endp)
+ if (endp)
*endp = (char *)cp;
return result;
}
@@ -103,21 +112,21 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
else if (!strcmp(this,"uid")) {
if (!value || !*value)
return 0;
- *uid = simple_strtoul(value,&value);
+ *uid = simple_strtoul(value,&value,0);
if (*value)
return 0;
}
else if (!strcmp(this,"gid")) {
if (!value || !*value)
return 0;
- *gid = simple_strtoul(value,&value);
+ *gid = simple_strtoul(value,&value,0);
if (*value)
return 0;
}
else if (!strcmp(this,"umask")) {
if (!value || !*value)
return 0;
- *umask = simple_strtoul(value,&value);
+ *umask = simple_strtoul(value,&value,8);
if (*value)
return 0;
}
diff --git a/fs/pipe.c b/fs/pipe.c
index 9002877..ca1e758 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -173,6 +173,7 @@ struct file_operations read_pipe_fops = {
pipe_readdir,
pipe_select,
pipe_ioctl,
+ NULL, /* no mmap on pipes.. surprise */
NULL, /* no special open code */
pipe_read_release
};
@@ -184,6 +185,7 @@ struct file_operations write_pipe_fops = {
pipe_readdir,
pipe_select,
pipe_ioctl,
+ NULL, /* mmap */
NULL, /* no special open code */
pipe_write_release
};
@@ -195,6 +197,7 @@ struct file_operations rdwr_pipe_fops = {
pipe_readdir,
pipe_select,
pipe_ioctl,
+ NULL, /* mmap */
NULL, /* no special open code */
pipe_rdwr_release
};
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ecb6647..83a66b8 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -23,6 +23,7 @@ static struct file_operations proc_base_operations = {
proc_readbase, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 89aaa03..9ddf1d7 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -23,6 +23,7 @@ static struct file_operations proc_fd_operations = {
proc_readfd, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/fs/proc/mem.c b/fs/proc/mem.c
index f71b83f..7871901 100644
--- a/fs/proc/mem.c
+++ b/fs/proc/mem.c
@@ -135,6 +135,7 @@ static struct file_operations proc_mem_operations = {
NULL, /* mem_readdir */
NULL, /* mem_select */
NULL, /* mem_ioctl */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/fs/proc/root.c b/fs/proc/root.c
index b524870..848c97b 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -23,6 +23,7 @@ static struct file_operations proc_root_operations = {
proc_readroot, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/fs/read_write.c b/fs/read_write.c
index e72e6b2..50f5c9b 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -74,12 +74,12 @@ int sys_read(unsigned int fd,char * buf,unsigned int count)
return -EBADF;
if (!(file->f_mode & 1))
return -EBADF;
+ if (!file->f_op || !file->f_op->read)
+ return -EINVAL;
if (!count)
return 0;
verify_area(buf,count);
- if (file->f_op && file->f_op->read)
- return file->f_op->read(inode,file,buf,count);
- return -EINVAL;
+ return file->f_op->read(inode,file,buf,count);
}
int sys_write(unsigned int fd,char * buf,unsigned int count)
@@ -89,11 +89,11 @@ int sys_write(unsigned int fd,char * buf,unsigned int count)
if (fd>=NR_OPEN || !(file=current->filp[fd]) || !(inode=file->f_inode))
return -EBADF;
- if (!(file->f_mode&2))
+ if (!(file->f_mode & 2))
return -EBADF;
+ if (!file->f_op || !file->f_op->write)
+ return -EINVAL;
if (!count)
return 0;
- if (file->f_op && file->f_op->write)
- return file->f_op->write(inode,file,buf,count);
- return -EINVAL;
+ return file->f_op->write(inode,file,buf,count);
}
diff --git a/include/asm/bitops.h b/include/asm/bitops.h
new file mode 100644
index 0000000..7140009
--- /dev/null
+++ b/include/asm/bitops.h
@@ -0,0 +1,96 @@
+#ifndef _ASM_BITOPS_H
+/*
+ * Copyright 1992, Linus Torvalds.
+ */
+
+#ifdef i386
+/*
+ * These have to be done with inline assembly: that way the bit-setting
+ * is guaranteed to be atomic. Both set_bit and clear_bit return 0
+ * if the bit-setting went ok, != 0 if the bit already was set/cleared.
+ *
+ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
+ */
+extern inline int set_bit(int nr,int * addr)
+{
+ char ok;
+
+ __asm__ __volatile__("btsl %1,%2\n\tsetb %0":
+ "=q" (ok):"r" (nr),"m" (*(addr)));
+ return ok;
+}
+
+extern inline int clear_bit(int nr, int * addr)
+{
+ char ok;
+
+ __asm__ __volatile__("btrl %1,%2\n\tsetnb %0":
+ "=q" (ok):"r" (nr),"m" (*(addr)));
+ return ok;
+}
+
+/*
+ * This routine doesn't need to be atomic, but it's faster to code it
+ * this way.
+ */
+extern inline int test_bit(int nr, int * addr)
+{
+ char ok;
+
+ __asm__ __volatile__("btl %1,%2\n\tsetb %0":
+ "=q" (ok):"r" (nr),"m" (*(addr)));
+ return ok;
+}
+
+#else
+/*
+ * For the benefit of those who are trying to port Linux to another
+ * architecture, here are some C-language equivalents. You should
+ * recode these in the native assmebly language, if at all possible.
+ * To guarantee atomicity, these routines call cli() and sti() to
+ * disable interrupts while they operate. (You have to provide inline
+ * routines to cli() and sti().)
+ *
+ * Also note, these routines assume that you have 32 bit integers.
+ * You will have to change this if you are trying to port Linux to the
+ * Alpha architecture or to a Cray. :-)
+ *
+ * C language equivalents written by Theodore Ts'o, 9/26/92
+ */
+
+extern inline int set_bit(int nr,int * addr)
+{
+ int mask, retval;
+
+ addr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ cli();
+ retval = (mask & *addr) != 0;
+ *addr |= mask;
+ sti();
+ return retval;
+}
+
+extern inline int clear_bit(int nr, int * addr)
+{
+ int mask, retval;
+
+ addr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ cli();
+ retval = (mask & *addr) == 0;
+ *addr &= ~mask;
+ sti();
+ return retval;
+}
+
+extern inline int test_bit(int nr, int * addr)
+{
+ int mask;
+
+ addr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ return ((mask & *addr) != 0);
+}
+#endif /* i386 */
+#endif /* _ASM_BITOPS_H */
diff --git a/include/asm/io.h b/include/asm/io.h
index 7714173..8e7810b 100644
--- a/include/asm/io.h
+++ b/include/asm/io.h
@@ -15,7 +15,7 @@
#ifdef SLOW_IO_BY_JUMPING
#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
#else
-#define __SLOW_DOWN_IO __asm__ __volatile__("inb $0x61,%%al":::"ax")
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
#endif
#ifdef REALLY_SLOW_IO
diff --git a/include/asm/system.h b/include/asm/system.h
index 59b0ba2..45cb87d 100644
--- a/include/asm/system.h
+++ b/include/asm/system.h
@@ -17,6 +17,12 @@ __asm__ __volatile__ ("movl %%esp,%%eax\n\t" \
#define cli() __asm__ __volatile__ ("cli"::)
#define nop() __asm__ __volatile__ ("nop"::)
+#define save_flags(x) \
+__asm__ __volatile__("pushfl ; popl %0":"=r" (x))
+
+#define restore_flags(x) \
+__asm__ __volatile__("pushl %0 ; popfl"::"r" (x))
+
#define iret() __asm__ __volatile__ ("iret"::)
#define _set_gate(gate_addr,type,dpl,addr) \
diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h
index a16d547..a4635f8 100644
--- a/include/linux/cdrom.h
+++ b/include/linux/cdrom.h
@@ -17,14 +17,6 @@
*/
/*
- * These are flags for the SCMD_DOORLOCK command. They really should be defined
- * somewhere more standard.
- */
-
-#define SR_REMOVAL_PREVENT 1
-#define SR_REMOVAL_ALLOW 0
-
-/*
* CD-ROM-specific SCSI command opcodes
*/
@@ -345,11 +337,4 @@ struct cdrom_read
#define CDROMREADMODE1 0x530d /* (struct cdrom_read) */
/* read type-1 data */
-/*
- * Linux-specific CD-ROM ioctls for convenience and ISO-9660 support
- */
-
-#define CDROMDOORLOCK 0x5380 /* lock the eject mechanism */
-#define CDROMDOORUNLOCK 0x5381 /* unlock the mechanism */
-
#endif _LINUX_CDROM_H
diff --git a/include/linux/config.dist.h b/include/linux/config.dist.h
index 9595cb5..0ed4d5c 100644
--- a/include/linux/config.dist.h
+++ b/include/linux/config.dist.h
@@ -2,7 +2,7 @@
#define _LINUX_CONFIG_DIST_H
#ifdef CONFIG_DISTRIBUTION
-#undef CONFG_SCSI
+#undef CONFIG_SCSI
#define CONFIG_SCSI
#undef CONFIG_SCSI_AHA1542
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 105a115..0aeed9b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -78,6 +78,7 @@ extern void buffer_init(void);
#define BLKROSET 4701 /* set device read-only (0 = read-write) */
#define BLKROGET 4702 /* get read-only status (0 = read_write) */
+#define BLKRRPART 4703 /* re-read partition table */
#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
#define FIBMAP 1 /* bmap access */
@@ -125,7 +126,8 @@ struct inode {
struct inode_operations * i_op;
struct super_block * i_sb;
struct wait_queue * i_wait;
- struct file_lock *i_flock;
+ struct file_lock * i_flock;
+ struct vm_area_struct * i_mmap;
unsigned short i_count;
unsigned short i_flags;
unsigned char i_lock;
@@ -194,6 +196,7 @@ struct file_operations {
int (*readdir) (struct inode *, struct file *, struct dirent *, int count);
int (*select) (struct inode *, struct file *, int, select_table *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned int);
+ int (*mmap) (void);
int (*open) (struct inode *, struct file *);
void (*release) (struct inode *, struct file *);
};
@@ -246,6 +249,7 @@ extern int nr_buffer_heads;
extern void check_disk_change(int dev);
extern void invalidate_inodes(int dev);
+extern void invalidate_buffers(int dev);
extern int floppy_change(struct buffer_head * first_block);
extern int ticks_to_floppy_on(unsigned int dev);
extern void floppy_on(unsigned int dev);
diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h
index d97df3d..a4ce2e4 100644
--- a/include/linux/minix_fs.h
+++ b/include/linux/minix_fs.h
@@ -44,8 +44,6 @@ struct minix_dir_entry {
char name[MINIX_NAME_LEN];
};
-extern int minix_open(struct inode * inode, struct file * filp);
-extern void minix_release(struct inode * inode, struct file * filp);
extern int minix_lookup(struct inode * dir,const char * name, int len,
struct inode ** result);
extern int minix_create(struct inode * dir,const char * name, int len, int mode,
@@ -79,11 +77,6 @@ extern void minix_write_inode(struct inode *);
extern void minix_put_inode(struct inode *);
extern void minix_statfs(struct super_block *, struct statfs *);
-extern int minix_lseek(struct inode *, struct file *, off_t, int);
-extern int minix_read(struct inode *, struct file *, char *, int);
-extern int minix_write(struct inode *, struct file *, char *, int);
-extern int minix_file_read(struct inode *, struct file *, char *, int);
-
extern struct inode_operations minix_file_inode_operations;
extern struct inode_operations minix_dir_inode_operations;
extern struct inode_operations minix_symlink_inode_operations;
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 08e12c0..4779b25 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -8,6 +8,46 @@
#include <linux/kernel.h>
/*
+ * Linux kernel virtual memory manager primitives.
+ * The idea being to have a "virtual" mm in the same way
+ * we have a virtual fs - giving a cleaner interface to the
+ * mm details, and allowing different kinds of memory mappings
+ * (from shared memory to executable loading to arbitrary
+ * mmap() functions).
+ */
+
+/*
+ * This struct defines a memory VMM memory area. There is one of these
+ * per VM-area/task. A VM area is any part of the process virtual memory
+ * space that has a special rule for the page-fault handlers (ie a shared
+ * library, the executable area etc).
+ */
+struct vm_area_struct {
+ struct task_struct * vm_task; /* VM area parameters */
+ unsigned long vm_start;
+ unsigned long vm_end;
+ struct vm_area_struct * vm_next; /* linked list */
+ struct vm_area_struct * vm_share; /* linked list */
+ struct inode * vm_inode;
+ unsigned long vm_offset;
+ struct vm_operations_struct * vm_ops;
+};
+
+/*
+ * These are the virtual MM functions - opening of an area, closing it (needed to
+ * keep files on disk up-to-date etc), pointer to the functions called when a
+ * no-page or a wp-page exception occurs, and the function which decides on sharing
+ * of pages between different processes.
+ */
+struct vm_operations_struct {
+ void (*open)(struct vm_area_struct * area);
+ void (*close)(struct vm_area_struct * area);
+ void (*nopage)(struct vm_area_struct * area, unsigned long address);
+ void (*wppage)(struct vm_area_struct * area, unsigned long address);
+ int (*share)(struct vm_area_struct * old, struct vm_area_struct * new, unsigned long address);
+};
+
+/*
* 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
@@ -59,6 +99,8 @@ extern void rw_swap_page(int rw, unsigned int nr, char * buf);
#define write_swap_page(nr,buf) \
rw_swap_page(WRITE,(nr),(buf))
+/* mmap.c */
+
/* memory.c */
extern unsigned long get_free_page(int priority);
@@ -78,12 +120,12 @@ extern void do_wp_page(unsigned long error_code, unsigned long address,
extern void do_no_page(unsigned long error_code, unsigned long address,
struct task_struct *tsk, unsigned long user_esp);
+extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
extern void mem_init(unsigned long low_start_mem,
unsigned long start_mem, unsigned long end_mem);
extern void show_mem(void);
extern void do_page_fault(unsigned long *esp, unsigned long error_code);
extern void oom(struct task_struct * task);
-extern void malloc_grab_pages(void);
/* swap.c */
@@ -98,7 +140,6 @@ extern unsigned long high_memory;
#define MAP_NR(addr) ((addr) >> PAGE_SHIFT)
#define MAP_PAGE_RESERVED (1<<15)
-#define USED 100
extern unsigned short * mem_map;
diff --git a/include/linux/mouse.h b/include/linux/mouse.h
index 9c50571..7e5014a 100644
--- a/include/linux/mouse.h
+++ b/include/linux/mouse.h
@@ -1,11 +1,16 @@
#ifndef _LINUX_MOUSE_H
#define _LINUX_MOUSE_H
+/*
+ * These are the minor numbers for the mice - undefine them
+ * to get rid of the driver from the kernel binary..
+ */
+
#define BUSMOUSE_MINOR 0
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2
#define ATIXL_BUSMOUSE_MINOR 3
-long mouse_init(long);
+unsigned long mouse_init(unsigned long);
#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 97aed5c..a0954f1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -80,15 +80,28 @@ extern void panic(const char * str);
typedef int (*fn_ptr)();
-struct i387_struct {
- long cwd;
- long swd;
- long twd;
- long fip;
- long fcs;
- long foo;
- long fos;
- long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
+union i387_union {
+ struct i387_hard_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
+ } hard;
+ struct i387_soft_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long top;
+ long regs_space[32]; /* 8*16 bytes for each FP-reg = 112 bytes */
+ } soft;
};
struct tss_struct {
@@ -116,7 +129,7 @@ struct tss_struct {
unsigned long ldt; /* 16 high bits zero */
unsigned long trace_bitmap; /* bits: trace 0, bitmap 16-31 */
unsigned long io_bitmap[IO_BITMAP_SIZE];
- struct i387_struct i387;
+ union i387_union i387;
};
struct task_struct {
@@ -168,6 +181,7 @@ struct task_struct {
struct inode * pwd;
struct inode * root;
struct inode * executable;
+ struct vm_area_struct * mmap;
struct {
struct inode * library;
unsigned long start;
@@ -213,7 +227,7 @@ struct task_struct {
/* rss */ 2, \
/* comm */ "swapper", \
/* vm86_info */ NULL, 0, \
-/* fs info */ 0,-1,0022,NULL,NULL,NULL, \
+/* fs info */ 0,-1,0022,NULL,NULL,NULL,NULL, \
/* libraries */ { { NULL, 0, 0}, }, 0, \
/* filp */ {NULL,}, 0, \
{ \
@@ -225,7 +239,7 @@ struct task_struct {
0,0,0,0,0,0,0,0, \
0,0,0x17,0x17,0x17,0x17,0x17,0x17, \
_LDT(0),0x80000000,{0xffffffff}, \
- {} \
+ { { 0, } } \
}, \
}
@@ -236,6 +250,7 @@ extern unsigned long volatile jiffies;
extern unsigned long startup_time;
extern int jiffies_offset;
extern int need_resched;
+extern int hard_math;
#define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ)
diff --git a/include/linux/serial.h b/include/linux/serial.h
index 7b47643..fe9aa19 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -45,10 +45,22 @@ struct async_struct {
int timeout;
int xmit_fifo_size;
int custom_divisor;
+ int x_char; /* xon/xoff characater */
+ int event;
int line;
};
/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_READ_PROCESS 0
+#define RS_EVENT_WRITE_WAKEUP 1
+#define RS_EVENT_HUP_PGRP 2
+#define RS_EVENT_BREAK_INT 3
+#define RS_EVENT_DO_SAK 4
+
+/*
* These are the UART port assignments, expressed as offsets from the base
* register. These assignments should hold for any serial port based on
* a 8250, 16450, or 16550(A).
@@ -113,7 +125,7 @@ struct async_struct {
/*
* These are the definitions for the Interrupt Indentification Register
*/
-#define UART_IIR_PEND 0x01 /* Interrupt pending */
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
#define UART_IIR_MSI 0x00 /* Modem status interrupt */
@@ -149,3 +161,4 @@ struct async_struct {
#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
#define UART_MSR_DDSR 0x02 /* Delta DSR */
#define UART_MSR_DCTS 0x01 /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index f11e8d0..2ae6906 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -36,6 +36,15 @@ struct sockaddr {
#define MSG_OOB 1
#define MSG_PEEK 2
+/* ioctl's */
+#define FIOSETOWN 0x8901 /* the 89 is for uniqueness.
+ This should be somewhere else. */
+#define SIOCSPGRP 0x8902
+#define FIOGETOWN 0x8903 /* this too. */
+#define SIOCGPGRP 0x8904
+#define SIOCATMARK 0x8905
+
+
/* for setsockoptions */
#define SO_DEBUG 1
#define SO_REUSEADDR 2
diff --git a/include/linux/termios.h b/include/linux/termios.h
index 372cda1..1ae8037 100644
--- a/include/linux/termios.h
+++ b/include/linux/termios.h
@@ -38,6 +38,9 @@
#define TIOCPKT 0x5420
#define FIONBIO 0x5421
#define TIOCNOTTY 0x5422
+#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
+#define FIOCLEX 0x5451
+#define FIOASYNC 0x5452
/* Used for packet mode */
#define TIOCPKT_FLUSHREAD 1
@@ -174,7 +177,8 @@ struct termios {
#define HUPCL 0002000
#define CLOCAL 0004000
#define CIBAUD 03600000 /* input baud rate (not used) */
-#define CRTSCTS 020000000000 /* flow control */
+#define CNORTSCTS 010000000000 /* no flow control */
+#define CRTSCTS 020000000000 /* flow control */
/* c_lflag bits */
#define ISIG 0000001
diff --git a/include/linux/tty.h b/include/linux/tty.h
index f333ac8..d214c1f 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -93,6 +93,7 @@ struct serial_struct {
*/
#define ASYNC_NOSCRATCH 0x0001 /* 16XXX UART with no scratch register */
#define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */
#define ASYNC_SPD_MASK 0x0030
#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
@@ -148,6 +149,7 @@ extern int get_tty_queue(struct tty_queue * queue);
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
#define I_NOCR(tty) _I_FLAG((tty),IGNCR)
#define I_IXON(tty) _I_FLAG((tty),IXON)
+#define I_IXANY(tty) _I_FLAG((tty),IXANY)
#define I_STRP(tty) _I_FLAG((tty),ISTRIP)
#define O_POST(tty) _O_FLAG((tty),OPOST)
@@ -221,19 +223,18 @@ struct tty_struct {
* Again, the low-level driver is free to ignore any of these, and has
* to implement RQ_THREHOLD_LW for itself if it wants it.
*/
-#define SQ_THRESHOLD_LW 0
+#define SQ_THRESHOLD_LW 16
#define SQ_THRESHOLD_HW 768
-#define RQ_THRESHOLD_LW 64
+#define RQ_THRESHOLD_LW 16
#define RQ_THRESHOLD_HW 768
/*
- * so that interrupts won't be able to mess up the
- * queues, copy_to_cooked must be atomic with repect
- * to itself, as must tty->write. These are the flag
- * bit-numbers. Use the set_bit() and clear_bit()
- * macros to make it all atomic.
- *
* These bits are used in the flags field of the tty structure.
+ *
+ * So that interrupts won't be able to mess up the queues,
+ * copy_to_cooked must be atomic with repect to itself, as must
+ * tty->write. Thus, you must use the inline functions set_bit() and
+ * clear_bit() to make things atomic.
*/
#define TTY_WRITE_BUSY 0
#define TTY_READ_BUSY 1
@@ -241,29 +242,6 @@ struct tty_struct {
#define TTY_SQ_THROTTLED 3
#define TTY_RQ_THROTTLED 4
-/*
- * These have to be done with inline assembly: that way the bit-setting
- * is guaranteed to be atomic. Both set_bit and clear_bit return 0
- * if the bit-setting went ok, != 0 if the bit already was set/cleared.
- */
-extern inline int set_bit(int nr,int * addr)
-{
- char ok;
-
- __asm__ __volatile__("btsl %1,%2\n\tsetb %0":
- "=q" (ok):"r" (nr),"m" (*(addr)));
- return ok;
-}
-
-extern inline int clear_bit(int nr, int * addr)
-{
- char ok;
-
- __asm__ __volatile__("btrl %1,%2\n\tsetnb %0":
- "=q" (ok):"r" (nr),"m" (*(addr)));
- return ok;
-}
-
#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
#define TTY_READ_FLUSH(tty) tty_read_flush((tty))
@@ -303,13 +281,12 @@ extern int is_orphaned_pgrp(int pgrp);
extern int is_ignored(int sig);
extern int tty_signal(int sig, struct tty_struct *tty);
extern int kill_pg(int pgrp, int sig, int priv);
+extern void do_SAK(struct tty_struct *tty);
/* tty write functions */
extern void rs_write(struct tty_struct * tty);
extern void con_write(struct tty_struct * tty);
-extern void mpty_write(struct tty_struct * tty);
-extern void spty_write(struct tty_struct * tty);
/* serial.c */
diff --git a/include/linux/vmm.h b/include/linux/vmm.h
deleted file mode 100644
index 0c3de12..0000000
--- a/include/linux/vmm.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _LINUX_VMM_H
-#define _LINUX_VMM_H
-
-/*
- * Linux kernel virtual memory manager primitives.
- * The idea being to have a "virtual" mm in the same way
- * we have a virtual fs - giving a cleaner interface to the
- * mm details, and allowing different kinds of memory mappings
- * (from shared memory to executable loading to arbitrary
- * mmap() functions).
- */
-
-/*
- * This struct defines a memory VMM memory area. There is one of these
- * per VM-area/task. A VM area is any part of the process virtual memory
- * space that has a special rule for the page-fault handlers (ie a shared
- * library, the executable area etc).
- */
-struct vm_area_struct {
- struct task_struct * vm_task; /* VM area parameters */
- unsigned long vm_start;
- unsigned long vm_end;
- struct vm_area_struct * vm_next; /* ordered linked list */
- struct vm_area_struct * vm_share; /* circular linked list */
- struct inode * vm_inode;
- unsigned long vm_offset;
- struct vm_operations_struct * vm_ops;
-};
-
-/*
- * These are the virtual MM functions - opening of an area, closing it (needed to
- * keep files on disk up-to-date etc), pointer to the functions called when a
- * no-page or a wp-page exception occurs, and the function which decides on sharing
- * of pages between different processes.
- */
-struct vm_operations_struct {
- void (*open)(struct vm_area_struct * area);
- void (*close)(struct vm_area_struct * area);
- void (*nopage)(struct vm_area_struct * area, unsigned long address);
- void (*wppage)(struct vm_area_struct * area, unsigned long address);
- void (*share)(struct vm_area_struct * old, struct vm_area_struct * new, unsigned long address);
-};
-
-#endif
diff --git a/init/main.c b/init/main.c
index 1012114..9736e8d 100644
--- a/init/main.c
+++ b/init/main.c
@@ -162,12 +162,15 @@ void start_kernel(void)
envp_init[1] = term;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
- if (memory_end > MAX_MEGABYTES*1024*1024)
- memory_end = MAX_MEGABYTES*1024*1024;
+#ifdef MAX_16M
+ if (memory_end > 16*1024*1024)
+ memory_end = 16*1024*1024;
+#endif
memory_start = 1024*1024;
low_memory_start = (unsigned long) &end;
low_memory_start += 0xfff;
low_memory_start &= 0xfffff000;
+ memory_start = paging_init(memory_start,memory_end);
trap_init();
init_IRQ();
sched_init();
diff --git a/kernel/FPU-emu/Makefile b/kernel/FPU-emu/Makefile
new file mode 100644
index 0000000..5277b89
--- /dev/null
+++ b/kernel/FPU-emu/Makefile
@@ -0,0 +1,194 @@
+# Makefile for wm-FPU-emu
+#
+
+CC = gcc
+#OPTS = -O2
+OPTS = -O
+#DEBUG = -DDEBUGGING
+DEBUG =
+CFLAGS2 = $(CFLAGS) -DPARANOID $(DEBUG) -Wall -fno-builtin
+
+.c.o:
+ $(CC) $(CFLAGS2) $(MATH_EMULATION) -c $<
+
+.S.o:
+ $(CC) $(CFLAGS2) -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\
+ poly_div.o poly_mul64.o polynomial.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\
+ wm_shrx.o wm_sqrt.o
+
+
+math.a: $(OBJS)
+ rm -f math.a
+ $(AR) rcs math.a $(OBJS)
+ sync
+
+clean:
+ rm -f core *.o *.a tmp_make *~
+ for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+ sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+ for i in *.c *.S;do $(CPP) -M $$i;done >> tmp_make
+ cp tmp_make Makefile
+
+proto:
+ cproto -e -DMAKING_PROTO *.c >fpu_proto.h
+
+tar:
+ echo "List of source files for wm-FPU-emu" > MANIFEST
+ echo "---- -- ------ ----- --- ----------" >> MANIFEST
+ ls -l Makefile *.c *.S *.h >> MANIFEST
+ ( cd ../../..; \
+ pwd; \
+ tar cvf wm-FPU-emu.t \
+ linux/kernel/wm-FPU-emu/*.c \
+ linux/kernel/wm-FPU-emu/*.S \
+ linux/kernel/wm-FPU-emu/*.h \
+ linux/kernel/wm-FPU-emu/Makefile \
+ linux/kernel/wm-FPU-emu/MANIFEST \
+ linux/kernel/wm-FPU-emu/README \
+ linux/kernel/wm-FPU-emu/Checklist \
+ linux/kernel/wm-FPU-emu/Limitations \
+ linux/kernel/wm-FPU-emu/Internals \
+ linux/kernel/wm-FPU-emu/Performance \
+ linux/kernel/wm-FPU-emu/COPYING \
+ linux/include/linux/sched.h \
+ linux/include/linux/user.h \
+ ; \
+ compress wm-FPU-emu.t \
+ )
+
+dummy:
+
+### Dependencies:
+errors.o : errors.c /usr/include/linux/signal.h /usr/include/asm/segment.h fpu_system.h \
+ /usr/include/linux/math_emu.h /usr/include/linux/sched.h /usr/include/linux/head.h \
+ /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
+ /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
+ /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
+ /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
+ /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
+ /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+ /usr/include/linux/vm86.h exception.h fpu_emu.h fpu_proto.h status_w.h control_w.h \
+ reg_constant.h version.h
+fpu_arith.o : fpu_arith.c fpu_system.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ fpu_emu.h fpu_proto.h
+fpu_aux.o : fpu_aux.c fpu_system.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ exception.h fpu_emu.h fpu_proto.h status_w.h
+fpu_entry.o : fpu_entry.c /usr/include/linux/signal.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/time.h /usr/include/linux/param.h \
+ /usr/include/linux/resource.h /usr/include/linux/vm86.h
+fpu_etc.o : fpu_etc.c fpu_system.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ exception.h fpu_emu.h fpu_proto.h status_w.h reg_constant.h
+fpu_trig.o : fpu_trig.c fpu_system.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ exception.h fpu_emu.h fpu_proto.h status_w.h control_w.h reg_constant.h
+get_address.o : get_address.c /usr/include/linux/stddef.h /usr/include/linux/math_emu.h \
+ /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
+ /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
+ /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+ /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+ /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+ /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+ /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+ /usr/include/linux/vm86.h /usr/include/asm/segment.h fpu_system.h exception.h \
+ fpu_emu.h fpu_proto.h
+load_store.o : load_store.c /usr/include/asm/segment.h fpu_system.h /usr/include/linux/math_emu.h \
+ /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
+ /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
+ /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+ /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+ /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+ /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+ /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+ /usr/include/linux/vm86.h exception.h fpu_emu.h fpu_proto.h status_w.h
+poly_2xm1.o : poly_2xm1.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+poly_atan.o : poly_atan.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+poly_l2.o : poly_l2.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+poly_sin.o : poly_sin.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+poly_tan.o : poly_tan.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+reg_add_sub.o : reg_add_sub.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+reg_compare.o : reg_compare.c fpu_system.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ exception.h fpu_emu.h fpu_proto.h status_w.h
+reg_constant.o : reg_constant.c fpu_system.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ fpu_emu.h fpu_proto.h status_w.h reg_constant.h
+reg_ld_str.o : reg_ld_str.c /usr/include/asm/segment.h fpu_system.h /usr/include/linux/math_emu.h \
+ /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
+ /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
+ /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+ /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+ /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+ /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/signal.h \
+ /usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
+ /usr/include/linux/vm86.h exception.h fpu_emu.h fpu_proto.h reg_constant.h control_w.h
+reg_mul.o : reg_mul.c exception.h fpu_emu.h fpu_proto.h reg_constant.h
+div_small.o : div_small.S fpu_asm.h fpu_emu.h
+poly_div.o : poly_div.S fpu_asm.h fpu_emu.h
+poly_mul64.o : poly_mul64.S fpu_asm.h fpu_emu.h
+polynomial.o : polynomial.S fpu_asm.h fpu_emu.h
+reg_div.o : reg_div.S exception.h fpu_emu.h fpu_asm.h
+reg_norm.o : reg_norm.S fpu_asm.h fpu_emu.h
+reg_u_add.o : reg_u_add.S exception.h fpu_emu.h fpu_asm.h
+reg_u_div.o : reg_u_div.S exception.h fpu_emu.h fpu_asm.h
+reg_u_mul.o : reg_u_mul.S exception.h fpu_emu.h fpu_asm.h
+reg_u_sub.o : reg_u_sub.S exception.h fpu_emu.h fpu_asm.h
+wm_shrx.o : wm_shrx.S fpu_asm.h fpu_emu.h
+wm_sqrt.o : wm_sqrt.S exception.h fpu_emu.h fpu_asm.h
diff --git a/kernel/FPU-emu/README b/kernel/FPU-emu/README
new file mode 100644
index 0000000..07a0ccd
--- /dev/null
+++ b/kernel/FPU-emu/README
@@ -0,0 +1,185 @@
+ +---------------------------------------------------------------------------+
+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@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. |
+ | |
+ +---------------------------------------------------------------------------+
+
+
+***NOTE*** THIS SHOULD BE REGARDED AS AN ALPHA TEST VERSION
+ (although the beta version may be identical)
+
+
+wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387
+which is my 80387 emulator for 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.
+
+My target FPU for wm-FPU-emu is that described in the Intel486
+Programmer's Reference Manual (1992 edition). Numerous facets of the
+functioning of the FPU are not well covered in the Reference Manual;
+in the absence of clear details I have made guesses about the most
+reasonable behaviour.
+
+wm-FPU-emu does not implement all of the behaviour of the 80486 FPU.
+See "Limitations" later in this file for a partial list of some
+differences. I believe that the missing features are never used by
+normal C or FORTRAN programs.
+
+Please report bugs, etc to me at:
+ apm233m@vaxc.cc.monash.edu.au
+
+
+--Bill Metzenthen
+ Oct 1992
+
+----------------------- 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.
+
+
+--Bill Metzenthen
+
+----------------------- Limitations of wm-FPU-emu -----------------------
+
+There are a number of differences between the current wm-FPU-emu
+(version ALPHA 0.5) and the 80486 FPU (apart from bugs). Some of the
+more important differences are listed below:
+
+Internal computations do not use de-normal numbers (but External
+de-normals ARE recognised and generated). The design of wm-FPU-emu
+allows a larger exponent range than the 80486 FPU for internal
+computations.
+
+All computations are performed at full 64 bit precision (the PC bits
+of the FPU control word are ignored). Under Linux, the FPU normally
+runs at 64 bits precision.
+
+The precision flag (PE of the FPU status word) is not implemented.
+Does anyone write code which uses this feature?
+
+The Roundup flag (C1) is not implemented.
+
+The functions which load/store the FPU state are partially implemented,
+but the implementation should be sufficient for handling FPU errors etc
+in 32 bit protected mode.
+
+
+--Bill Metzenthen
+ October 1992
+
+----------------------- 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 can be improved if look-ahead code is used.
+WM-emu387 uses such code. The following results show the improvement
+which can be obtained under Linux. 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
+
+
+----------------------- Accuracy of wm-FPU-emu -----------------------
+
+
+Accuracy: The following table gives the accuracy of the sqrt(), trig
+and log functions. Each function was tested at about 400 points. Ideal
+results would be 64 bits. The reduced accuracy of cos() and tan() for
+arguments greater than pi/4 can be thought of as being due 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. Results for the Turbo C emulator are given
+in the last column.
+
+
+Function Tested x range Worst result (bits) Turbo C
+
+sqrt(x) 1 .. 2 64.1 63.2
+atan(x) 1e-10 .. 200 62.6 62.8
+cos(x) 0 .. pi/2-(1e-10) 63.2 (x <= pi/4) 62.4
+ 35.2 (x = pi/2-(1e-10)) 31.9
+sin(x) 1e-10 .. pi/2 63.0 62.8
+tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1
+ 35.2 (x = pi/2-(1e-10)) 31.9
+exp(x) 0 .. 1 63.1 62.9
+log(x) 1+1e-6 .. 2 62.4 62.1
+
diff --git a/kernel/FPU-emu/control_w.h b/kernel/FPU-emu/control_w.h
new file mode 100644
index 0000000..237a70b
--- /dev/null
+++ b/kernel/FPU-emu/control_w.h
@@ -0,0 +1,33 @@
+/*---------------------------------------------------------------------------+
+ | control_w.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@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_PM _Const_(0x0020) /* precision mask */
+#define CW_UM _Const_(0x0010) /* underflow mask */
+#define CW_OM _Const_(0x0008) /* overflow mask */
+#define CW_ZM _Const_(0x0004) /* divide by zero mask */
+#define CW_DM _Const_(0x0002) /* denormalized operand mask */
+#define CW_IM _Const_(0x0001) /* invalid operation mask */
+#define CW_EXM _Const_(0x007f) /* all masks */
+
+#define RC_RND _Const_(0x0000)
+#define RC_DOWN _Const_(0x0400)
+#define RC_UP _Const_(0x0800)
+#define RC_CHOP _Const_(0x0C00)
+
+#endif _CONTROLW_H_
diff --git a/kernel/FPU-emu/div_small.S b/kernel/FPU-emu/div_small.S
new file mode 100644
index 0000000..0e3b71e
--- /dev/null
+++ b/kernel/FPU-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 apm233m@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/kernel/FPU-emu/errors.c b/kernel/FPU-emu/errors.c
new file mode 100644
index 0000000..b66aa7d
--- /dev/null
+++ b/kernel/FPU-emu/errors.c
@@ -0,0 +1,443 @@
+/*---------------------------------------------------------------------------+
+ | errors.c |
+ | |
+ | The error handling functions for wm-FPU-emu |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#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"
+
+
+extern unsigned char FPU_lookahead;
+
+/* */
+#undef PRINT_MESSAGES
+/* */
+
+
+void Un_impl(void)
+{
+ unsigned char byte1 = get_fs_byte((unsigned char *) FPU_ORIG_EIP);
+
+ printk("Unimplemented FPU Opcode at eip=%p : %02x ",
+ FPU_ORIG_EIP, byte1);
+
+ 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);
+
+ EXCEPTION(EX_Invalid);
+
+}
+
+
+
+
+void emu_printall()
+{
+ int i;
+ static char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
+ "DeNorm", "Inf", "NaN", "Empty" };
+ unsigned char byte1 = get_fs_byte((unsigned char *) FPU_ORIG_EIP);
+
+#ifdef DEBUGGING
+if ( status_word & SW_B ) printk("SW: backward compatibility (=ES)\n");
+if ( status_word & SW_C3 ) printk("SW: condition bit 3\n");
+if ( status_word & SW_C2 ) printk("SW: condition bit 2\n");
+if ( status_word & SW_C1 ) printk("SW: condition bit 1\n");
+if ( status_word & SW_C0 ) printk("SW: condition bit 0\n");
+if ( status_word & SW_ES ) printk("SW: exception summary\n");
+if ( status_word & SW_SF ) printk("SW: stack fault\n");
+if ( status_word & SW_PE ) printk("SW: loss of precision\n");
+if ( status_word & SW_UE ) printk("SW: underflow\n");
+if ( status_word & SW_OE ) printk("SW: overflow\n");
+if ( status_word & SW_ZE ) printk("SW: divide by zero\n");
+if ( status_word & SW_DE ) printk("SW: denormalized operand\n");
+if ( status_word & SW_IE ) printk("SW: invalid operation\n");
+#endif DEBUGGING
+
+ status_word = status_word & ~SW_TOP;
+ status_word |= (top&7) << SW_TOPS;
+
+ printk("At %p: %02x ", FPU_ORIG_EIP, byte1);
+ 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);
+
+ printk(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
+ status_word & 0x8000 ? 1 : 0, /* busy */
+ (status_word & 0x3800) >> 11, /* stack top pointer */
+ status_word & 0x80 ? 1 : 0, /* Error summary status */
+ status_word & 0x40 ? 1 : 0, /* Stack flag */
+ status_word & SW_C3?1:0, status_word & SW_C2?1:0, /* cc */
+ status_word & SW_C1?1:0, status_word & SW_C0?1:0, /* cc */
+ status_word & SW_PE?1:0, status_word & SW_UE?1:0, /* exception fl */
+ status_word & SW_OE?1:0, status_word & SW_ZE?1:0, /* exception fl */
+ status_word & SW_DE?1:0, status_word & SW_IE?1:0); /* exception fl */
+
+printk(" CW: ic=%d rc=%d%d pc=%d%d 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_PE?1:0, control_word & SW_UE?1:0, /* exception */
+ control_word & SW_OE?1:0, control_word & SW_ZE?1:0, /* exception */
+ control_word & SW_DE?1:0, control_word & SW_IE?1:0); /* exception */
+
+ for ( i = 0; i < 8; i++ )
+ {
+ struct reg *r = &st(i);
+ switch (r->tag)
+ {
+ case TW_Empty:
+ continue;
+ break;
+ case TW_Zero:
+ printk("st(%d) %c .0000 0000 0000 0000 ",
+ i, r->sign ? '-' : '+');
+ break;
+ case TW_Valid:
+ case TW_NaN:
+ case TW_Denormal:
+ case TW_Infinity:
+ printk("st(%d) %c .%04x %04x %04x %04x e%+-6d ", 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]);
+ }
+
+ printk("[data] %c .%04x %04x %04x %04x e%+-6d ",
+ 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]);
+
+}
+
+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,0
+};
+
+/*
+ EX_INTERNAL is always given with a code which indicates where the
+ error was detected.
+
+ Internal error types:
+ 0x14 in e14.c
+ 0x1nn in a *.c file:
+ 0x101 in reg_add_sub.c
+ 0x102 in reg_mul.c
+ 0x103 in poly_sin.c
+ 0x104 in poly_tan.c
+ 0x105 in reg_mul.c
+ 0x106 in reg_mov.c
+ 0x107 in fpu_trig.c
+ 0x108 in reg_compare.c
+ 0x109 in reg_compare.c
+ 0x110 in reg_add_sub.c
+ 0x111 in interface.c
+ 0x112 in fpu_trig.c
+ 0x113 in reg_add_sub.c
+ 0x114 in reg_ld_str.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
+ 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
+ */
+
+void exception(int n)
+{
+ int i, int_type;
+
+ if ( n & EX_INTERNAL )
+ {
+ int_type = n - EX_INTERNAL;
+ n = EX_INTERNAL;
+ /* Set lots of exception bits! */
+ status_word |= (0x3f | EX_ErrorSummary | FPU_BUSY);
+ }
+ else
+ {
+ /* Set the corresponding exception bit */
+ status_word |= (n | EX_ErrorSummary | FPU_BUSY);
+ if (n == EX_StackUnder) /* Stack underflow */
+ /* This bit distinguishes over- from underflow */
+ status_word &= ~SW_C1;
+ }
+
+ if ( (~control_word & n & CW_EXM) || (n == EX_INTERNAL) )
+ {
+#ifdef PRINT_MESSAGES
+ /* My message from the sponsor */
+ printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\r\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)
+ break;
+
+ if (exception_names[i].type)
+ {
+#ifdef PRINT_MESSAGES
+ printk("FP Exception: %s!\n", exception_names[i].name);
+#endif PRINT_MESSAGES
+ }
+ else
+ printk("FP emulator: Unknown Exception: 0x%04x!\n", n);
+
+ if ( n == EX_INTERNAL )
+ {
+ printk("FP emulator: Internal error type 0x%04x\n", int_type);
+ emu_printall();
+ }
+#ifdef PRINT_MESSAGES
+ else
+ emu_printall();
+#endif PRINT_MESSAGES
+
+ send_sig(SIGFPE, current, 1);
+ }
+
+#ifdef __DEBUG__
+ math_abort(FPU_info,SIGFPE);
+#endif __DEBUG__
+
+ /* Cause the look-ahead mechanism to terminate */
+ FPU_lookahead = 0;
+}
+
+
+/********
+int EmptyError(void)
+{
+ EXCEPTION(EX_StackUnder);
+ return 0;
+}
+ **********/
+
+
+/****
+int FullError(void)
+{
+ EXCEPTION(EX_StackOver);
+ reg_move(&CONST_QNaN, st0_ptr);
+ return 0;
+}
+ ****/
+
+
+
+
+/* Real operation attempted on two operands, one a NaN */
+void real_2op_NaN(REG *a, REG *b, REG *dest)
+{
+ REG *x;
+
+ x = a;
+ if (a->tag == TW_NaN)
+ {
+ if (b->tag == TW_NaN)
+ {
+ /* find the "larger" */
+ if ( *(long long *)&(a->sigl) < *(long long *)&(b->sigl) )
+ x = b;
+ }
+ /* else return the quiet version of the NaN in a */
+ }
+ else if (b->tag == TW_NaN)
+ {
+ x = b;
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL|0x113);
+ x = &CONST_QNaN;
+ }
+#endif PARANOID
+
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ reg_move(x, dest);
+ /* ensure a Quiet NaN */
+ dest->sigh |= 0x40000000;
+ }
+
+ EXCEPTION(EX_Invalid);
+
+ return;
+}
+
+/* Invalid arith operation on valid registers */
+void arith_invalid(REG *dest)
+{
+
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, dest);
+ }
+
+ EXCEPTION(EX_Invalid);
+
+ return;
+
+}
+
+
+/* Divide a finite number by zero */
+void divide_by_zero(int sign, REG *dest)
+{
+
+ if ( control_word & EX_ZeroDiv )
+ {
+ /* The masked response */
+ reg_move(&CONST_INF, dest);
+ dest->sign = (unsigned char)sign;
+ }
+
+ EXCEPTION(EX_ZeroDiv);
+
+ return;
+
+}
+
+
+void arith_overflow(REG *dest)
+{
+
+ if ( control_word & EX_Overflow )
+ {
+ char sign;
+ /* The masked response */
+ 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);
+
+ return;
+
+}
+
+
+void arith_underflow(REG *dest)
+{
+
+ if ( control_word & EX_Underflow )
+ {
+ /* The masked response */
+ if ( dest->exp <= EXP_UNDER - 63 )
+ reg_move(&CONST_Z, dest);
+ }
+ else
+ {
+ /* Add the magic number to the exponent */
+ dest->exp += (3 * (1 << 13));
+ }
+
+ EXCEPTION(EX_Underflow);
+
+ return;
+}
+
+
+void stack_overflow(void)
+{
+
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ top--;
+ reg_move(&CONST_QNaN, st0_ptr = &st(0));
+ }
+
+ EXCEPTION(EX_StackOver);
+
+ return;
+
+}
+
+
+void stack_underflow(void)
+{
+
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ reg_move(&CONST_QNaN, st0_ptr);
+ }
+
+ EXCEPTION(EX_StackUnder);
+
+ return;
+
+}
+
diff --git a/kernel/FPU-emu/exception.h b/kernel/FPU-emu/exception.h
new file mode 100644
index 0000000..3692065
--- /dev/null
+++ b/kernel/FPU-emu/exception.h
@@ -0,0 +1,49 @@
+/*---------------------------------------------------------------------------+
+ | exception.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@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 */
+
+
+#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/kernel/FPU-emu/fpu_arith.c b/kernel/FPU-emu/fpu_arith.c
new file mode 100644
index 0000000..a1c73f7
--- /dev/null
+++ b/kernel/FPU-emu/fpu_arith.c
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------------+
+ | fpu_arith.c |
+ | |
+ | Code to implement the FPU register/register arithmetis instructions |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+
+
+void fadd__()
+{
+ /* fadd st,st(i) */
+ reg_add(st0_ptr, &st(FPU_rm), st0_ptr);
+}
+
+
+void fmul__()
+{
+ /* fmul st,st(i) */
+ reg_mul(st0_ptr, &st(FPU_rm), st0_ptr);
+}
+
+
+
+void fsub__()
+{
+ /* fsub st,st(i) */
+ reg_sub(st0_ptr, &st(FPU_rm), st0_ptr);
+}
+
+
+void fsubr_()
+{
+ /* fsubr st,st(i) */
+ reg_sub(&st(FPU_rm), st0_ptr, st0_ptr);
+}
+
+
+void fdiv__()
+{
+ /* fdiv st,st(i) */
+ reg_div(st0_ptr, &st(FPU_rm), st0_ptr);
+}
+
+
+void fdivr_()
+{
+ /* fdivr st,st(i) */
+ reg_div(&st(FPU_rm), st0_ptr, st0_ptr);
+}
+
+
+
+void fadd_i()
+{
+ /* fadd st(i),st */
+ reg_add(st0_ptr, &st(FPU_rm), &st(FPU_rm));
+}
+
+
+void fmul_i()
+{
+ /* fmul st(i),st */
+ reg_mul(&st(FPU_rm), st0_ptr, &st(FPU_rm));
+}
+
+
+void fsubri()
+{
+ /* fsubr st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(&st(FPU_rm), st0_ptr, &st(FPU_rm)); */
+ reg_sub(st0_ptr, &st(FPU_rm), &st(FPU_rm));
+}
+
+
+void fsub_i()
+{
+ /* fsub st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(st0_ptr, &st(FPU_rm), &st(FPU_rm)); */
+ reg_sub(&st(FPU_rm), st0_ptr, &st(FPU_rm));
+}
+
+
+void fdivri()
+{
+ /* fdivr st(i),st */
+ reg_div(st0_ptr, &st(FPU_rm), &st(FPU_rm));
+}
+
+
+void fdiv_i()
+{
+ /* fdiv st(i),st */
+ reg_div(&st(FPU_rm), st0_ptr, &st(FPU_rm));
+}
+
+
+
+void faddp_()
+{
+ /* faddp st(i),st */
+ reg_add(st0_ptr, &st(FPU_rm), &st(FPU_rm));
+ pop();
+}
+
+
+void fmulp_()
+{
+ /* fmulp st(i),st */
+ reg_mul(&st(FPU_rm), st0_ptr, &st(FPU_rm));
+ pop();
+}
+
+
+
+void fsubrp()
+{
+ /* fsubrp st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(&st(FPU_rm), st0_ptr, &st(FPU_rm)); */
+ reg_sub(st0_ptr, &st(FPU_rm), &st(FPU_rm));
+ pop();
+}
+
+
+void fsubp_()
+{
+ /* fsubp st(i),st */
+ /* This is the sense of the 80486 manual
+ reg_sub(st0_ptr, &st(FPU_rm), &st(FPU_rm)); */
+ reg_sub(&st(FPU_rm), st0_ptr, &st(FPU_rm));
+ pop();
+}
+
+
+void fdivrp()
+{
+ /* fdivrp st(i),st */
+ reg_div(st0_ptr, &st(FPU_rm), &st(FPU_rm));
+ pop();
+}
+
+
+void fdivp_()
+{
+ /* fdivp st(i),st */
+ reg_div(&st(FPU_rm), st0_ptr, &st(FPU_rm));
+ pop();
+}
+
diff --git a/kernel/FPU-emu/fpu_asm.h b/kernel/FPU-emu/fpu_asm.h
new file mode 100644
index 0000000..b20a123
--- /dev/null
+++ b/kernel/FPU-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 apm233m@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/kernel/FPU-emu/fpu_aux.c b/kernel/FPU-emu/fpu_aux.c
new file mode 100644
index 0000000..50d51d0
--- /dev/null
+++ b/kernel/FPU-emu/fpu_aux.c
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------------+
+ | fpu_aux.c |
+ | |
+ | Code to implement some of the FPU auxiliary instructions. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+
+extern struct info *FPU_info;
+
+#define EAX_REG ((long)(FPU_info->___eax))
+
+
+
+static void fclex()
+{
+ status_word &= ~(SW_B|SW_ES|SW_SF|SW_PE|SW_UE|SW_OE|SW_ZE|SW_DE|SW_IE);
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+}
+
+/* Needs to be externally visible */
+void finit()
+{
+ int r;
+ control_word = 0x037e;
+ status_word = 0;
+ top = 0;
+ for (r = 0; r < 8; r++)
+ {
+ regs[r].sign = 0;
+ regs[r].tag = TW_Empty;
+ regs[r].exp = 0;
+ regs[r].sigh = 0;
+ regs[r].sigl = 0;
+ }
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+}
+
+static FUNC finit_table[] = {
+ Un_impl, Un_impl, fclex, finit, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void finit_()
+{
+ (finit_table[FPU_rm])();
+}
+
+
+static void fstsw_ax()
+{
+
+ status_word &= ~SW_TOP;
+ status_word |= (top&7) << SW_TOPS;
+
+ *(short *) &EAX_REG = status_word;
+
+}
+
+static FUNC fstsw_table[] = {
+ fstsw_ax, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void fstsw_()
+{
+ (fstsw_table[FPU_rm])();
+}
+
+
+
+static void fnop()
+{
+}
+
+FUNC fp_nop_table[] = {
+ fnop, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl, Un_impl
+};
+
+void fp_nop()
+{
+ (fp_nop_table[FPU_rm])();
+}
+
+
+void fld_i_()
+{
+ 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 & EX_Invalid )
+ {
+ /* The masked response */
+ push();
+ stack_underflow();
+ }
+ else
+ EXCEPTION(EX_StackUnder);
+ }
+
+}
+
+
+void fxch_i()
+{
+ /* fxch st(i) */
+ REG t;
+ register REG *sti_ptr = &st(FPU_rm);
+ 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 fst_i_()
+{
+ /* fst st(i) */
+ reg_move(st0_ptr, &st(FPU_rm));
+}
+
+
+void fstp_i()
+{
+ /* fstp st(i) */
+ reg_move(st0_ptr, &st(FPU_rm));
+ pop();
+}
+
diff --git a/kernel/FPU-emu/fpu_emu.h b/kernel/FPU-emu/fpu_emu.h
new file mode 100644
index 0000000..c80aaef
--- /dev/null
+++ b/kernel/FPU-emu/fpu_emu.h
@@ -0,0 +1,112 @@
+/*---------------------------------------------------------------------------+
+ | fpu_emu.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#ifndef _FPU_EMU_H_
+#define _FPU_EMU_H_
+
+#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_MAX Const(16384) */
+#define EXP_UNDER Const(-0x3fff) /* largest invalid small exponent */
+/* #define EXP_MIN Const(-16384) */
+
+#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__
+
+typedef void (*FUNC)();
+
+#define REG struct reg
+
+struct reg {
+ char sign;
+ char tag;
+/* short exp; *****/
+ long exp;
+ unsigned sigl;
+ unsigned sigh;
+};
+
+#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_0 (st0_tag ^ TW_Empty)
+
+extern unsigned char FPU_modrm;
+extern unsigned char FPU_rm;
+
+extern char st0_tag;
+extern REG *st0_ptr;
+
+extern void *FPU_data_address;
+extern unsigned long FPU_entry_eip;
+
+extern REG FPU_loaded_data;
+
+#define pop() { st0_ptr->tag = TW_Empty; \
+ top++; st0_ptr = &(regs[top&7]); }
+
+/* push() does not affect the tags */
+#define push() { top--; st0_ptr = st_new_ptr; }
+
+
+#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); }
+
+
+/*----- Prototypes for functions written in assembler -----*/
+/* extern void reg_move(REG *a, REG *b); */
+
+extern void mul64(long long *a, long long *b, long long *result);
+extern void poly_div2(long long *x);
+extern void poly_div4(long long *x);
+extern void poly_div16(long long *x);
+extern void polynomial(unsigned accum[], unsigned x[],
+ unsigned short terms[][4], int n);
+extern void normalize(REG *x);
+extern void reg_div(REG *arg1, REG *arg2, REG *answ);
+extern void reg_u_sub(REG *arg1, REG *arg2, REG *answ);
+extern void reg_u_mul(REG *arg1, REG *arg2, REG *answ);
+extern void reg_u_div(long long *arg1, long long *arg2, REG *answ);
+extern void reg_u_add(REG *arg1, REG *arg2, REG *answ);
+extern void wm_sqrt(REG *n);
+extern unsigned shrx(void *l, unsigned x);
+extern unsigned shrxs(void *v, unsigned x);
+extern unsigned long div_small(unsigned long long *x, unsigned long y);
+
+#ifndef MAKING_PROTO
+#include "fpu_proto.h"
+#endif
+
+#endif __ASSEMBLER__
+
+#endif _FPU_EMU_H_
diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c
new file mode 100644
index 0000000..81362c6
--- /dev/null
+++ b/kernel/FPU-emu/fpu_entry.c
@@ -0,0 +1,275 @@
+/*---------------------------------------------------------------------------+
+ | fpu_entry.c |
+ | |
+ | The entry function for wm-FPU-emu |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | See the files "README" and "COPYING" for further copyright and warranty |
+ | information. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | math_emulate() is the sole entry point for wm-FPU-emu |
+ +---------------------------------------------------------------------------*/
+
+#ifdef KERNEL_MATH_EMULATION
+
+#include <linux/signal.h>
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+#include "exception.h"
+
+#include <asm/segment.h>
+
+
+#define __BAD__ Un_impl /* Not implemented */
+
+static FUNC 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__,
+};
+
+#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 */
+
+static unsigned char type_table[64] = {
+ _REGI_, _NONE_, _null_, _null_, _REGI_, _REGi_, _REGI_, _null_,
+ _REGI_, _REGI_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
+ _REGI_, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
+ _REGI_, _null_, _null_, _null_, _null_, _REG0_, _REGI_, _null_,
+ _REGI_, _NONE_, _null_, _NONE_, _REGI_, _REGI_, _REGI_, _NONE_,
+ _REGI_, _NONE_, _REGI_, _null_, _REGI_, _REGI_, _REGI_, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
+ _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_
+};
+
+
+unsigned char FPU_lookahead;
+unsigned char FPU_modrm;
+unsigned char FPU_rm;
+char st0_tag;
+struct reg *st0_ptr;
+
+struct info *FPU_info;
+
+unsigned long FPU_entry_eip;
+
+
+#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+
+
+void math_emulate(long arg)
+{
+ unsigned short code;
+
+#ifdef PARANOID
+ static int emulating=0;
+
+ if ( emulating )
+ {
+ printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\r\n");
+ }
+emulating = 1;
+#endif PARANOID
+
+ if (!current->used_math)
+ {
+ finit();
+ current->used_math = 1;
+ control_word = 0x037f;
+ status_word = 0x0000;
+ }
+
+ FPU_info = (struct info *) &arg;
+
+ /* We cannot handle emulation in v86-mode */
+ if (FPU_EFLAGS & 0x00020000)
+ math_abort(FPU_info,SIGILL);
+
+ /* 0x000f means user code space */
+ if (FPU_CS != 0x000f)
+ {
+ printk("math_emulate: %04x:%08x\n\r",FPU_CS,FPU_EIP);
+ panic("Math emulation needed in kernel");
+ }
+
+ FPU_lookahead = 1;
+ if (current->flags & PF_PTRACED)
+ FPU_lookahead = 0;
+do_another:
+
+ FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
+
+ code = get_fs_word((unsigned short *) FPU_EIP);
+ if ( (code & 0xff) == 0x66 )
+ {
+ FPU_EIP++;
+ code = get_fs_word((unsigned short *) FPU_EIP);
+ }
+ FPU_EIP += 2;
+
+ FPU_modrm = code >> 8;
+ FPU_rm = FPU_modrm & 7;
+
+ st0_ptr = &st(0);
+ st0_tag = st0_ptr->tag;
+
+ if ( FPU_modrm < 0300 )
+ {
+ /* All of these instructions use the mod/rm byte to get a data address */
+ get_address();
+ if ( !(code & 1) )
+ {
+ if ( NOT_EMPTY_0 )
+ {
+ switch ( (code >> 1) & 3 )
+ {
+ case 0:
+ reg_load_single();
+ break;
+ case 1:
+ reg_load_int32();
+ break;
+ case 2:
+ reg_load_double();
+ break;
+ case 3:
+ reg_load_int16();
+ break;
+ }
+ switch ( (FPU_modrm >> 3) & 7 )
+ {
+ case 0: /* fadd */
+ reg_add(st0_ptr, &FPU_loaded_data, st0_ptr);
+ break;
+ case 1: /* fmul */
+ reg_mul(st0_ptr, &FPU_loaded_data, st0_ptr);
+ break;
+ case 2: /* fcom */
+ compare_st_data();
+ break;
+ case 3: /* fcomp */
+ compare_st_data();
+ pop();
+ break;
+ case 4: /* fsub */
+ reg_sub(st0_ptr, &FPU_loaded_data, st0_ptr);
+ break;
+ case 5: /* fsubr */
+ reg_sub(&FPU_loaded_data, st0_ptr, st0_ptr);
+ break;
+ case 6: /* fdiv */
+ reg_div(st0_ptr, &FPU_loaded_data, st0_ptr);
+ break;
+ case 7: /* fdivr */
+ reg_div(&FPU_loaded_data, st0_ptr, st0_ptr);
+ break;
+ }
+ }
+ else
+ stack_underflow();
+ }
+ else
+ load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
+
+ data_operand_offset = FPU_data_address;
+ }
+ else
+ {
+ unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
+ switch ( type_table[(int) instr_index] )
+ {
+ case _NONE_:
+ break;
+ case _REG0_:
+ if ( !NOT_EMPTY_0 )
+ {
+ stack_underflow();
+ goto instruction_done;
+ }
+ break;
+ case _REGI_:
+ if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
+ {
+ stack_underflow();
+ goto instruction_done;
+ }
+ break;
+ case _PUSH_: /* Only used by the fld st(i) instruction */
+ break;
+ case _null_:
+ Un_impl();
+ goto instruction_done;
+ default:
+ EXCEPTION(EX_INTERNAL|0x111);
+ goto instruction_done;
+ }
+ (*st_instr_table[(int) instr_index])();
+ }
+
+instruction_done:
+
+ ip_offset = FPU_entry_eip;
+ bswapw(code);
+ *(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
+
+ if (FPU_lookahead && !need_resched)
+ {
+ unsigned char next;
+skip_fwait:
+ next = get_fs_byte((unsigned char *) FPU_EIP);
+test_for_fp:
+ if ( (next & 0xf8) == 0xd8 )
+ {
+ goto do_another;
+ }
+ if ( next == 0x9b ) /* fwait */
+ { FPU_EIP++; goto skip_fwait; }
+ if ( next == 0x66 ) /* size prefix */
+ {
+ next = get_fs_byte((unsigned char *) (FPU_EIP+1));
+ if ( (next & 0xf8) == 0xd8 )
+ goto test_for_fp;
+ }
+ }
+
+#ifdef PARANOID
+ emulating = 0;
+#endif PARANOID
+
+}
+
+
+void __math_abort(struct info * info, unsigned int signal)
+{
+ FPU_EIP = FPU_ORIG_EIP;
+ send_sig(signal,current,1);
+ __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
+}
+
+#else /* no math emulation */
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+void math_emulate(long arg)
+{
+ send_sig(SIGFPE,current,1);
+ schedule();
+}
+
+#endif /* KERNEL_MATH_EMULATION */
diff --git a/kernel/FPU-emu/fpu_etc.c b/kernel/FPU-emu/fpu_etc.c
new file mode 100644
index 0000000..10fbf6a
--- /dev/null
+++ b/kernel/FPU-emu/fpu_etc.c
@@ -0,0 +1,114 @@
+/*---------------------------------------------------------------------------+
+ | fpu_etc.c |
+ | |
+ | Implement a few FPU instructions. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@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()
+{
+ if ( NOT_EMPTY_0 )
+ {
+ st0_ptr->sign ^= SIGN_POS^SIGN_NEG;
+ status_word &= ~SW_C1;
+ }
+ else
+ stack_underflow();
+}
+
+static void fabs()
+{
+ if ( st0_tag ^ TW_Empty )
+ {
+ st0_ptr->sign = SIGN_POS;
+ status_word &= ~SW_C1;
+ }
+ else
+ stack_underflow();
+}
+
+
+static void ftst_()
+{
+ switch (st0_tag)
+ {
+ case TW_Zero:
+ setcc(SW_C3);
+ break;
+ case TW_Valid:
+ if (st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C0);
+ break;
+ case TW_NaN:
+ setcc(SW_C2); /* Operand is not comparable */
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Infinity:
+ if (st0_ptr->sign == SIGN_POS)
+ setcc(0);
+ else
+ setcc(SW_C3);
+ /* setcc(SW_C0|SW_C2|SW_C3); */
+ EXCEPTION(EX_Invalid);
+ break;
+ case TW_Empty:
+ setcc(SW_C0|SW_C2|SW_C3);
+ EXCEPTION(EX_StackUnder);
+ break;
+ default:
+ setcc(SW_C2); /* Operand is not comparable */
+ EXCEPTION(EX_INTERNAL|0x14);
+ break;
+ }
+}
+
+static void fxam()
+{
+ int c=0;
+ switch (st0_tag)
+ {
+ case TW_Empty:
+ c = SW_C3|SW_C0;
+ break;
+ case TW_Zero:
+ c = SW_C3;
+ break;
+ case TW_Valid:
+ if (st0_ptr->sigh & 0x80000000)
+ c = SW_C2;
+ else
+ c = SW_C3|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 fp_etc_table[] = {
+ fchs, fabs, Un_impl, Un_impl, ftst_, fxam, Un_impl, Un_impl
+};
+
+void fp_etc()
+{
+ (fp_etc_table[FPU_rm])();
+}
diff --git a/kernel/FPU-emu/fpu_proto.h b/kernel/FPU-emu/fpu_proto.h
new file mode 100644
index 0000000..e2fbbb1
--- /dev/null
+++ b/kernel/FPU-emu/fpu_proto.h
@@ -0,0 +1,100 @@
+/* errors.c */
+extern void Un_impl(void);
+extern void emu_printall(void);
+extern void exception(int n);
+extern void real_2op_NaN(struct reg *a, struct reg *b, struct reg *dest);
+extern void arith_invalid(struct reg *dest);
+extern void divide_by_zero(int sign, struct reg *dest);
+extern void arith_overflow(struct reg *dest);
+extern void arith_underflow(struct reg *dest);
+extern void stack_overflow(void);
+extern void stack_underflow(void);
+/* 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 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 fst_i_(void);
+extern void fstp_i(void);
+/* fpu_entry.c */
+extern void math_emulate(long arg);
+/* fpu_etc.c */
+extern void fp_etc(void);
+/* fpu_trig.c */
+extern void convert_l2reg(long *arg, struct reg *dest);
+extern void trig_a(void);
+extern void trig_b(void);
+/* get_address.c */
+extern void get_address(void);
+/* load_store.c */
+extern void load_store_instr(char type);
+/* poly_2xm1.c */
+extern int poly_2xm1(struct reg *arg, struct reg *result);
+/* poly_atan.c */
+extern void poly_atan(struct reg *arg);
+extern void poly_add_1(struct reg *src);
+/* poly_l2.c */
+extern void poly_l2(struct reg *arg, struct reg *result);
+extern int poly_l2p1(struct reg *arg, struct reg *result);
+/* poly_sin.c */
+extern void poly_sine(struct reg *arg, struct reg *result);
+/* poly_tan.c */
+extern void poly_tan(struct reg *arg, struct reg *y_reg);
+/* reg_add_sub.c */
+extern void reg_add(struct reg *a, struct reg *b, struct reg *dest);
+extern void reg_sub(struct reg *a, struct reg *b, struct reg *dest);
+/* reg_compare.c */
+extern int compare(struct reg *b);
+extern void compare_st_data(void);
+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 void reg_load_extended(void);
+extern void reg_load_double(void);
+extern void reg_load_single(void);
+extern void reg_load_int64(void);
+extern void reg_load_int32(void);
+extern void reg_load_int16(void);
+extern void reg_load_bcd(void);
+extern int reg_store_extended(void);
+extern int reg_store_double(void);
+extern int reg_store_single(void);
+extern int reg_store_int64(void);
+extern int reg_store_int32(void);
+extern int reg_store_int16(void);
+extern int reg_store_bcd(void);
+extern int round_to_int(struct reg *r);
+extern char *fldenv(void);
+extern void frstor(void);
+extern char *fstenv(void);
+extern void fsave(void);
+/* reg_mul.c */
+extern void reg_mul(struct reg *a, struct reg *b, struct reg *dest);
diff --git a/kernel/FPU-emu/fpu_system.h b/kernel/FPU-emu/fpu_system.h
new file mode 100644
index 0000000..c8998b8
--- /dev/null
+++ b/kernel/FPU-emu/fpu_system.h
@@ -0,0 +1,35 @@
+/*---------------------------------------------------------------------------+
+ | fpu_system.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _FPU_SYSTEM_H
+#define _FPU_SYSTEM_H
+
+/* system dependent definitions */
+
+#include <linux/math_emu.h>
+#include <linux/kernel.h>
+
+#define FPU_CS (*(unsigned short *) &(FPU_info->___cs))
+#define FPU_DS (*(unsigned short *) &(FPU_info->___ds))
+#define FPU_EFLAGS (FPU_info->___eflags)
+#define FPU_EIP (FPU_info->___eip)
+#define FPU_ORIG_EIP (FPU_info->___orig_eip)
+
+#define status_word (I387.soft.swd)
+#define control_word (I387.soft.cwd)
+#define regs ((REG *)(&(I387.soft.regs_space)))
+#define top (I387.soft.top)
+
+#define ip_offset (I387.soft.fip)
+#define cs_selector (I387.soft.fcs)
+#define data_operand_offset (I387.soft.foo)
+#define operand_selector (I387.soft.fos)
+
+extern struct info *FPU_info;
+
+#endif
diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c
new file mode 100644
index 0000000..a298f2e
--- /dev/null
+++ b/kernel/FPU-emu/fpu_trig.c
@@ -0,0 +1,955 @@
+/*---------------------------------------------------------------------------+
+ | fpu_trig.c |
+ | |
+ | Implementation of the FPU "transcendental" functions. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@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 int trig_arg(REG *X)
+{
+ REG tmp, quot;
+ int rv;
+ long long q;
+ int old_cw = control_word;
+
+ control_word &= ~CW_RC;
+ control_word |= RC_CHOP;
+
+ reg_move(X, &quot);
+ reg_div(&quot, &CONST_PI2, &quot);
+
+ reg_move(&quot, &tmp);
+ round_to_int(&tmp);
+ if ( tmp.sigh & 0x80000000 )
+ return -1; /* |Arg| is >= 2^63 */
+ tmp.exp = EXP_BIAS + 63;
+ q = *(long long *)&(tmp.sigl);
+ normalize(&tmp);
+
+ reg_sub(&quot, &tmp, X);
+ rv = q & 7;
+
+ control_word = old_cw;
+ return rv;;
+}
+
+
+/* Convert a long to register */
+void convert_l2reg(long *arg, 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(void)
+{
+ switch ( st0_tag )
+ {
+ case TW_NaN:
+ if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
+ {
+ EXCEPTION(EX_Invalid);
+ /* Convert to a QNaN */
+ st0_ptr->sigh |= 0x40000000;
+ }
+ case TW_Empty:
+ stack_underflow();
+#ifdef PARANOID
+ default:
+ EXCEPTION(EX_INTERNAL|0x0112);
+#endif PARANOID
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+static void f2xm1()
+{
+ switch ( st0_tag )
+ {
+ case TW_Valid:
+ {
+ struct reg rv, tmp;
+
+ if ( st0_ptr->sign == SIGN_POS )
+ {
+ /* poly_2xm1(x) requires 0 < x < 1. */
+ if ( poly_2xm1(st0_ptr, &rv) )
+ return;
+ reg_mul(&rv, st0_ptr, st0_ptr);
+ return;
+ }
+ else
+ {
+/* **** Should change poly_2xm1() to at least handle numbers near 0 */
+ /* poly_2xm1(x) doesn't handle negative numbers. */
+ /* So we compute (poly_2xm1(x+1)-1)/2, for -1 < x < 0 */
+ reg_add(st0_ptr, &CONST_1, &tmp);
+ poly_2xm1(&tmp, &rv);
+ reg_mul(&rv, &tmp, &tmp);
+ reg_sub(&tmp, &CONST_1, st0_ptr);
+ st0_ptr->exp--;
+ }
+ if ( st0_ptr->exp <= EXP_UNDER )
+ arith_underflow(st0_ptr);
+ 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();
+ }
+}
+
+static void fptan()
+{
+ REG *st_new_ptr;
+ int q;
+ char arg_sign = st0_ptr->sign;
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+
+ switch ( st0_tag )
+ {
+ case TW_Valid:
+ st0_ptr->sign = SIGN_POS;
+ if ( (q = trig_arg(st0_ptr)) != -1 )
+ {
+ if (q & 1)
+ reg_sub(&CONST_1, st0_ptr, st0_ptr);
+
+ poly_tan(st0_ptr, st0_ptr);
+
+ st0_ptr->sign = (q & 1) ^ arg_sign;
+
+ if ( st0_ptr->exp <= EXP_UNDER )
+ arith_underflow(st0_ptr);
+
+ push();
+ reg_move(&CONST_1, st0_ptr);
+ setcc(0);
+ }
+ else
+ {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ st0_ptr->sign = arg_sign; /* restore st(0) */
+ return;
+ }
+ break;
+ case TW_Infinity:
+ arith_invalid(st0_ptr);
+ setcc(0);
+ return;
+ case TW_Zero:
+ push();
+ reg_move(&CONST_1, st0_ptr);
+ setcc(0);
+ break;
+ default:
+ single_arg_error();
+ break;
+ }
+}
+
+
+static void fxtract()
+{
+ REG *st_new_ptr;
+ register REG *st1_ptr = st0_ptr; /* anticipate */
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+
+ if ( !(st0_tag ^ TW_Valid) )
+ {
+ long e;
+
+ push();
+ reg_move(st1_ptr, st0_ptr);
+ st0_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;
+ divide_by_zero(SIGN_NEG, st0_ptr);
+ push();
+ reg_move(&CONST_Z, st0_ptr);
+ st0_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, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ else if ( st0_tag == TW_NaN )
+ {
+ if ( !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */
+ {
+ EXCEPTION(EX_Invalid);
+ /* Convert to a QNaN */
+ st0_ptr->sigh |= 0x40000000;
+ }
+ push();
+ reg_move(st1_ptr, st0_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()
+{
+ top--; /* st0_ptr will be fixed in math_emulate() before the next instr */
+}
+
+static void fincstp()
+{
+ top++; /* st0_ptr will be fixed in math_emulate() before the next instr */
+}
+
+
+static void fsqrt_()
+{
+ if ( !(st0_tag ^ TW_Valid) )
+ {
+ int expon;
+
+ if (st0_ptr->sign == SIGN_NEG)
+ {
+ arith_invalid(st0_ptr);
+ return;
+ }
+
+ 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); /* Do the computation */
+
+ st0_ptr->exp += expon >> 1;
+ st0_ptr->tag = TW_Valid;
+ 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);
+ return;
+ }
+ else
+ single_arg_error();
+}
+
+
+static void frndint_()
+{
+ if ( !(st0_tag ^ TW_Valid) )
+ {
+ if (st0_ptr->exp > EXP_BIAS+63)
+ return;
+
+ round_to_int(st0_ptr); /* Fortunately, this can't overflow to 2^64 */
+ 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();
+}
+
+
+static void fsin()
+{
+ if ( st0_tag == TW_Valid )
+ {
+ int q;
+ char arg_sign = st0_ptr->sign;
+ st0_ptr->sign = SIGN_POS;
+ if ( (q = trig_arg(st0_ptr)) != -1 )
+ {
+ REG rv;
+
+ if (q & 1)
+ reg_sub(&CONST_1, st0_ptr, st0_ptr);
+
+ poly_sine(st0_ptr, &rv);
+
+ setcc(0);
+ if (q & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ rv.sign ^= arg_sign;
+ reg_move(&rv, st0_ptr);
+
+ if ( st0_ptr->exp <= EXP_UNDER )
+ arith_underflow(st0_ptr);
+
+ return;
+ }
+ else
+ {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ st0_ptr->sign = arg_sign; /* restore st(0) */
+ EXCEPTION(EX_Invalid);
+ return;
+ }
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ setcc(0);
+ return;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ arith_invalid(st0_ptr);
+ setcc(0);
+ return;
+ }
+ else
+ single_arg_error();
+}
+
+
+static int f_cos(REG *arg)
+{
+ if ( arg->tag == TW_Valid )
+ {
+ int q;
+ char arg_sign = arg->sign;
+ arg->sign = SIGN_POS;
+ if ( (q = trig_arg(arg)) != -1 )
+ {
+ REG rv;
+
+ if ( !(q & 1) )
+ reg_sub(&CONST_1, arg, arg);
+
+ poly_sine(arg, &rv);
+
+ setcc(0);
+ if ((q+1) & 2)
+ rv.sign ^= SIGN_POS ^ SIGN_NEG;
+ reg_move(&rv, arg);
+
+ return 0;
+ }
+ else
+ {
+ /* Operand is out of range */
+ setcc(SW_C2);
+ arg->sign = arg_sign; /* restore st(0) */
+ EXCEPTION(EX_Invalid);
+ return 1;
+ }
+ }
+ else if ( arg->tag == TW_Zero )
+ {
+ reg_move(&CONST_1, arg);
+ setcc(0);
+ return 0;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ arith_invalid(st0_ptr);
+ setcc(0);
+ return 1;
+ }
+ else
+ {
+ single_arg_error(); /* requires arg == &st(0) */
+ return 1;
+ }
+}
+
+
+static void fcos()
+{
+ f_cos(st0_ptr);
+}
+
+
+static void fsincos()
+{
+ REG *st_new_ptr;
+ REG arg;
+
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+
+ reg_move(st0_ptr,&arg);
+ if ( !f_cos(&arg) )
+ {
+ fsin();
+ push();
+ reg_move(&arg,st0_ptr);
+ }
+
+}
+
+
+/*---------------------------------------------------------------------------*/
+/* The following all require two arguments: st(0) and st(1) */
+
+/* remainder of st(0) / st(1) */
+/* Assumes that st(0) and st(1) are both TW_Valid */
+static void fprem_kernel(int round)
+{
+ REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ REG tmp;
+ int old_cw = control_word;
+ int expdif = st0_ptr->exp - (st1_ptr)->exp;
+
+ control_word &= ~CW_RC;
+ control_word |= round;
+
+ if (expdif < 64)
+ {
+ /* This should be the most common case */
+ long long q;
+ int c = 0;
+ reg_div(st0_ptr, st1_ptr, &tmp);
+
+ round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */
+ tmp.exp = EXP_BIAS + 63;
+ q = *(long long *)&(tmp.sigl);
+ normalize(&tmp);
+
+ reg_mul(st1_ptr, &tmp, &tmp);
+ reg_sub(st0_ptr, &tmp, st0_ptr);
+
+ if (q&4) c |= SW_C3;
+ if (q&2) c |= SW_C1;
+ if (q&1) c |= SW_C0;
+
+ setcc(c);
+ }
+ else
+ {
+ /* There is a large exponent difference ( >= 64 ) */
+ int N_exp;
+
+ reg_div(st0_ptr, st1_ptr, &tmp);
+ /* N is 'a number between 32 and 63' (p26-113) */
+ N_exp = (tmp.exp & 31) + 32;
+ tmp.exp = EXP_BIAS + N_exp;
+
+ round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */
+ tmp.exp = EXP_BIAS + 63;
+ normalize(&tmp);
+
+ tmp.exp = EXP_BIAS + expdif - N_exp;
+
+ reg_mul(st1_ptr, &tmp, &tmp);
+ reg_sub(st0_ptr, &tmp, st0_ptr);
+
+ setcc(SW_C2);
+ }
+ control_word = old_cw;
+
+ if ( st0_ptr->exp <= EXP_UNDER )
+ 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) || (st1_tag == TW_Infinity) )
+ { setcc(0); return; }
+ if ( st1_tag == TW_Zero )
+ { arith_invalid(st0_ptr); return; }
+ }
+
+ if ( (st0_tag == TW_NaN) | (st1_tag == TW_NaN) )
+ { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+ else if ( st0_tag == TW_Infinity )
+ { arith_invalid(st0_ptr); return; }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x118);
+#endif PARANOID
+
+}
+
+
+/* ST(1) <- ST(1) * log ST; pop ST */
+static void fyl2x()
+{
+ REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ if ( st0_ptr->sign == SIGN_POS )
+ {
+ poly_l2(st0_ptr, st0_ptr);
+
+ reg_mul(st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ if ( st0_ptr->exp <= EXP_UNDER )
+ arith_underflow(st0_ptr);
+ else if ( st0_ptr->exp >= EXP_OVER )
+ arith_overflow(st0_ptr);
+ }
+ else
+ {
+ /* negative */
+ pop();
+ arith_invalid(st0_ptr);
+ }
+ return;
+ }
+
+ if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
+ { stack_underflow(); return; }
+
+ if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+ {
+ real_2op_NaN(st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+
+ 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 )
+ {
+ pop();
+ if ( st0_ptr->tag == TW_Zero )
+ arith_invalid(st0_ptr);
+ else
+ divide_by_zero(st1_ptr->sign ^ SIGN_NEG, st0_ptr);
+ return;
+ }
+ if ( st1_ptr->sign == SIGN_POS )
+ {
+ /* Zero is the valid answer */
+ char sign = st0_ptr->sign;
+ if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG;
+ pop();
+ reg_move(&CONST_Z, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ pop();
+ arith_invalid(st0_ptr);
+ return;
+ }
+
+ /* One or both arg must be an infinity */
+ if ( st0_tag == TW_Infinity )
+ {
+ if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
+ { pop(); arith_invalid(st0_ptr); return; }
+ else
+ {
+ char sign = st1_ptr->sign;
+ pop();
+ reg_move(&CONST_INF, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ }
+
+ /* st(1) must be infinity here */
+ 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) )
+ {
+ pop();
+ arith_invalid(st0_ptr);
+ return;
+ }
+ pop();
+ return;
+ }
+ else
+ {
+ pop();
+ st0_ptr->sign ^= SIGN_NEG;
+ return;
+ }
+ }
+ /* st(0) must be zero or negative */
+ pop();
+ arith_invalid(st0_ptr);
+ return;
+}
+
+
+static void fpatan()
+{
+ REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ struct reg sum;
+ int quadrant = st1_ptr->sign | ((st0_ptr->sign)<<1);
+ st1_ptr->sign = st0_ptr->sign = SIGN_POS;
+ if (compare(st1_ptr) == COMP_A_LT_B)
+ {
+ quadrant |= 4;
+ reg_div(st0_ptr, st1_ptr, &sum);
+ }
+ else
+ reg_div(st1_ptr, st0_ptr, &sum);
+
+ poly_atan(&sum);
+
+ if (quadrant & 4)
+ {
+ reg_sub(&CONST_PI2, &sum, &sum);
+ }
+ if (quadrant & 2)
+ {
+ reg_sub(&CONST_PI, &sum, &sum);
+ }
+ if (quadrant & 1)
+ sum.sign ^= SIGN_POS^SIGN_NEG;
+
+ reg_move(&sum, st1_ptr);
+ pop();
+ if ( st0_ptr->exp <= EXP_UNDER )
+ arith_underflow(st0_ptr);
+ return;
+ }
+
+ if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
+ { stack_underflow(); return; }
+
+ if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+ {
+ real_2op_NaN(st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+
+ 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);
+ }
+ else
+ {
+ if ( st0_ptr->sign == SIGN_POS )
+ { reg_move(&CONST_Z, st1_ptr); }
+ else
+ reg_move(&CONST_PI, st1_ptr);
+ }
+ }
+ else
+ {
+ reg_move(&CONST_PI2, st1_ptr);
+ }
+ st1_ptr->sign = sign;
+ pop();
+ return;
+ }
+
+ if ( st1_tag == TW_Zero )
+ {
+ char sign = st1_ptr->sign;
+ /* st(0) must be valid or zero */
+ if ( st0_ptr->sign == SIGN_POS )
+ { reg_move(&CONST_Z, st1_ptr); }
+ else
+ reg_move(&CONST_PI, st1_ptr);
+ st1_tag = sign;
+ pop();
+ return;
+ }
+ else if ( st0_tag == TW_Zero )
+ {
+ char sign = st1_ptr->sign;
+ /* st(1) must be TW_Valid here */
+ reg_move(&CONST_PI2, st1_ptr);
+ st1_tag = sign;
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL | 0x220);
+#endif PARANOID
+}
+
+
+static void fprem()
+{
+ fprem_kernel(RC_CHOP);
+}
+
+
+static void fprem1()
+{
+ fprem_kernel(RC_RND);
+}
+
+
+static void fyl2xp1()
+{
+ REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ if ( poly_l2p1(st0_ptr, st0_ptr) )
+ {
+ arith_invalid(st1_ptr); pop(); return;
+ }
+
+ reg_mul(st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+ else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+ stack_underflow();
+ else if ( st0_tag == TW_Zero )
+ {
+ if ( st1_tag <= TW_Zero )
+ {
+ st1_ptr->sign ^= st0_ptr->sign;
+ reg_move(st0_ptr, st1_ptr);
+ }
+ else if ( st1_tag == TW_Infinity )
+ {
+ arith_invalid(st1_ptr);
+ }
+ else if ( st1_tag == TW_NaN )
+ {
+ if ( !(st1_ptr->sigh & 0x40000000) )
+ EXCEPTION(EX_Invalid); /* signaling NaN */
+ st1_ptr->sigh |= 0x40000000; /* QNaN */
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL | 0x116);
+ }
+#endif PARANOID
+ pop();
+ return;
+ }
+ else if ( st0_tag == TW_NaN )
+ {
+ real_2op_NaN(st0_ptr, st1_ptr, st1_ptr);
+ pop();
+ return;
+ }
+ else if ( st0_tag == TW_Infinity )
+ {
+ if ( st1_tag == TW_NaN )
+ real_2op_NaN(st0_ptr, st1_ptr, st1_ptr);
+ else
+ arith_invalid(st1_ptr);
+ pop();
+ return;
+ }
+#ifdef PARANOID
+ else
+ EXCEPTION(EX_INTERNAL | 0x117);
+#endif PARANOID
+}
+
+
+static void fscale()
+{
+ REG *st1_ptr = &st(1);
+ char st1_tag = st1_ptr->tag;
+
+ if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+ {
+ long scale;
+ REG tmp;
+
+ /* 2^31 is far too large, 2^-31 is far too small */
+ if ( st1_ptr->exp > EXP_BIAS + 30 )
+ {
+ char sign;
+ EXCEPTION(EX_Overflow);
+ sign = st0_ptr->sign;
+ reg_move(&CONST_INF, st0_ptr);
+ st0_ptr->sign = sign;
+ return;
+ }
+ else if ( st1_ptr->exp < EXP_BIAS - 30 )
+ {
+ EXCEPTION(EX_Underflow);
+ reg_move(&CONST_Z, st0_ptr);
+ return;
+ }
+
+ reg_move(st1_ptr, &tmp);
+ round_to_int(&tmp); /* This can never overflow here */
+ scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
+ scale += st0_ptr->exp;
+ st0_ptr->exp = scale;
+
+ if ( scale <= EXP_UNDER )
+ arith_underflow(st0_ptr);
+ else if ( scale >= EXP_OVER )
+ arith_overflow(st0_ptr);
+
+ return;
+ }
+ else if ( st0_tag == TW_Valid )
+ {
+ if ( st1_tag == TW_Zero )
+ { return; }
+ if ( st1_tag == TW_Infinity )
+ {
+ char sign = st1_ptr->sign;
+ if ( 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_Zero ) { return; }
+ else if ( st1_tag == TW_Infinity )
+ {
+ if ( st1_ptr->sign == SIGN_NEG )
+ return;
+ else
+ { arith_invalid(st0_ptr); 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_Infinity) && (st1_ptr->sign == SIGN_POS))
+ || (st1_tag <= TW_Zero) )
+ return;
+ else if ( st1_tag == TW_Infinity )
+ { arith_invalid(st0_ptr); 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 trig_table_a[] = {
+ f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
+};
+
+void trig_a()
+{
+ (trig_table_a[FPU_rm])();
+}
+
+
+static FUNC trig_table_b[] =
+ {
+ fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
+ };
+
+void trig_b()
+{
+ (trig_table_b[FPU_rm])();
+}
diff --git a/kernel/FPU-emu/get_address.c b/kernel/FPU-emu/get_address.c
new file mode 100644
index 0000000..e4e7046
--- /dev/null
+++ b/kernel/FPU-emu/get_address.c
@@ -0,0 +1,152 @@
+/*---------------------------------------------------------------------------+
+ | get_address.c |
+ | |
+ | Get the effective address from an FPU instruction. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#include <linux/stddef.h>
+#include <linux/math_emu.h>
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+
+
+
+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))
+
+
+void *FPU_data_address;
+
+
+/* Decode the SIB byte. This function assumes mod != 0 */
+static void *sib(int mod)
+{
+ unsigned char ss,index,base;
+ long offset;
+
+ base = get_fs_byte((char *) FPU_EIP); /* The SIB byte */
+ 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 */
+ offset += (signed char) get_fs_byte((char *) FPU_EIP);
+ FPU_EIP++;
+ }
+ else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
+ {
+ /* 32 bit displacment */
+ offset += (signed) get_fs_long((unsigned long *) FPU_EIP);
+ FPU_EIP += 4;
+ }
+
+ return (void *) offset;
+}
+
+
+/*
+ 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(void)
+{
+ unsigned char mod;
+ long *cpu_reg_ptr;
+ int offset;
+
+ mod = (FPU_modrm >> 6) & 3;
+
+ if (FPU_rm == 4 && mod != 3)
+ {
+ FPU_data_address = sib(mod);
+ return;
+ }
+
+ cpu_reg_ptr = & REG_(FPU_rm);
+ switch (mod)
+ {
+ case 0:
+ if (FPU_rm == 5)
+ {
+ /* Special case: disp32 */
+ offset = get_fs_long((unsigned long *) FPU_EIP);
+ FPU_EIP += 4;
+ FPU_data_address = (void *) offset;
+ return;
+ }
+ else
+ {
+ FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents
+ of the cpu register */
+ return;
+ }
+ case 1:
+ /* 8 bit signed displacement */
+ offset = (signed char) get_fs_byte((char *) FPU_EIP);
+ FPU_EIP++;
+ break;
+ case 2:
+ /* 32 bit displacement */
+ offset = (signed) get_fs_long((unsigned long *) FPU_EIP);
+ FPU_EIP += 4;
+ break;
+ case 3:
+ /* Not legal for the FPU */
+ EXCEPTION(EX_Invalid);
+ }
+
+ FPU_data_address = offset + (char *)*cpu_reg_ptr;
+}
diff --git a/kernel/FPU-emu/load_store.c b/kernel/FPU-emu/load_store.c
new file mode 100644
index 0000000..342f463
--- /dev/null
+++ b/kernel/FPU-emu/load_store.c
@@ -0,0 +1,182 @@
+/*---------------------------------------------------------------------------+
+ | 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 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+
+
+#define _NONE_ 0
+#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 */
+
+static unsigned char type_table[32] = {
+ _PUSH_, _PUSH_, _PUSH_, _PUSH_,
+ _null_, _null_, _null_, _null_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _REG0_, _REG0_, _REG0_, _REG0_,
+ _null_, _null_, _NONE_, _PUSH_,
+ _NONE_, _PUSH_, _null_, _PUSH_,
+ _null_, _null_, _NONE_, _REG0_,
+ _NONE_, _REG0_, _NONE_, _REG0_
+ };
+
+void load_store_instr(char type)
+{
+ switch ( type_table[(int) (unsigned) type] )
+ {
+ case _NONE_:
+ break;
+ case _REG0_:
+ break;
+ case _PUSH_:
+ {
+ REG *st_new_ptr;
+ if ( STACK_OVERFLOW )
+ { stack_overflow(); return; }
+ push();
+ }
+ break;
+ case _null_:
+ return Un_impl();
+#ifdef PARANOID
+ default:
+ return EXCEPTION(EX_INTERNAL);
+#endif PARANOID
+ }
+
+switch ( type )
+ {
+ case 000: /* fld m32real */
+ reg_load_single();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 001: /* fild m32int */
+ reg_load_int32();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 002: /* fld m64real */
+ reg_load_double();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 003: /* fild m16int */
+ reg_load_int16();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 010: /* fst m32real */
+ reg_store_single();
+ break;
+ case 011: /* fist m32int */
+ reg_store_int32();
+ break;
+ case 012: /* fst m64real */
+ reg_store_double();
+ break;
+ case 013: /* fist m16int */
+ reg_store_int16();
+ break;
+ case 014: /* fstp m32real */
+ if ( reg_store_single() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 015: /* fistp m32int */
+ if ( reg_store_int32() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 016: /* fstp m64real */
+ if ( reg_store_double() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 017: /* fistp m16int */
+ if ( reg_store_int16() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 020: /* fldenv m14/28byte */
+ fldenv();
+ break;
+ case 022: /* frstor m94/108byte */
+ frstor();
+ break;
+ case 023: /* fbld m80dec */
+ reg_load_bcd();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 024: /* fldcw */
+ control_word = get_fs_word((unsigned short *) FPU_data_address);
+#ifdef NO_UNDERFLOW_TRAP
+ if ( !(control_word & EX_Underflow) )
+ {
+ control_word |= EX_Underflow;
+ }
+#endif
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 025: /* fld m80real */
+ reg_load_extended();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 027: /* fild m64int */
+ reg_load_int64();
+ reg_move(&FPU_loaded_data, st0_ptr);
+ break;
+ case 030: /* fstenv m14/28byte */
+ fstenv();
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 032: /* fsave */
+ fsave();
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 033: /* fbstp m80dec */
+ if ( reg_store_bcd() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 034: /* fstcw m16int */
+ verify_area(FPU_data_address,2);
+ put_fs_word(control_word, (short *) FPU_data_address);
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 035: /* fstp m80real */
+ if ( reg_store_extended() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ case 036: /* fstsw m2byte */
+ status_word &= ~SW_TOP;
+ status_word |= (top&7) << SW_TOPS;
+ verify_area(FPU_data_address,2);
+ put_fs_word(status_word,(short *) FPU_data_address);
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+ break;
+ case 037: /* fistp m64int */
+ if ( reg_store_int64() )
+ pop(); /* pop only if the number was actually stored
+ (see the 80486 manual p16-28) */
+ break;
+ }
+}
diff --git a/kernel/FPU-emu/poly_2xm1.c b/kernel/FPU-emu/poly_2xm1.c
new file mode 100644
index 0000000..266617f
--- /dev/null
+++ b/kernel/FPU-emu/poly_2xm1.c
@@ -0,0 +1,98 @@
+/*---------------------------------------------------------------------------+
+ | poly_2xm1.c |
+ | |
+ | Function to compute 2^x-1 by a polynomial approximation. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+
+#define HIPOWER 13
+static unsigned short lterms[HIPOWER][4] =
+ {
+ { 0x79b5, 0xd1cf, 0x17f7, 0xb172 },
+ { 0x1b56, 0x058b, 0x7bff, 0x3d7f },
+ { 0x8bb0, 0x8250, 0x846b, 0x0e35 },
+ { 0xbc65, 0xf747, 0x556d, 0x0276 },
+ { 0x17cb, 0x9e39, 0x61ff, 0x0057 },
+ { 0xe018, 0x9776, 0x1848, 0x000a },
+ { 0x66f2, 0xff30, 0xffe5, 0x0000 },
+ { 0x682f, 0xffb6, 0x162b, 0x0000 },
+ { 0xb7ca, 0x2956, 0x01b5, 0x0000 },
+ { 0xcd3e, 0x4817, 0x001e, 0x0000 },
+ { 0xb7e2, 0xecbe, 0x0001, 0x0000 },
+ { 0x0ed5, 0x1a27, 0x0000, 0x0000 },
+ { 0x101d, 0x0222, 0x0000, 0x0000 },
+ };
+
+
+/*--- poly_2xm1() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+int poly_2xm1(REG *arg, REG *result)
+{
+ short exponent;
+ long long Xll;
+ REG accum;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if ( arg->tag == TW_Zero )
+ {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return 0;
+ }
+
+ if ( exponent >= 0 ) /* Can't hack a number >= 1.0 */
+ {
+ arith_invalid(result);
+ return 1;
+ }
+
+ if ( arg->sign != SIGN_POS ) /* Can't hack a number < 0.0 */
+ {
+ arith_invalid(result);
+ return 1;
+ }
+
+ if ( exponent < -64 )
+ {
+ reg_move(&CONST_LN2, result);
+ return 0;
+ }
+
+ *(unsigned *)&Xll = arg->sigl;
+ *(((unsigned *)&Xll)+1) = arg->sigh;
+ if ( exponent < -1 )
+ {
+ /* shift the argument right by the required places */
+ if ( shrx(&Xll, -1-exponent) >= 0x80000000U )
+ Xll++; /* round up */
+ }
+
+ *(short *)&(accum.sign) = 0; /* will be a valid positive nr with expon = 0 */
+ accum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((unsigned *)&accum.sigl, (unsigned *)&Xll, lterms, HIPOWER-1);
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp += EXP_BIAS - 1;
+
+ reg_move(&accum, result);
+
+ normalize(result);
+
+ return 0;
+
+}
diff --git a/kernel/FPU-emu/poly_atan.c b/kernel/FPU-emu/poly_atan.c
new file mode 100644
index 0000000..06b45e2
--- /dev/null
+++ b/kernel/FPU-emu/poly_atan.c
@@ -0,0 +1,206 @@
+/*---------------------------------------------------------------------------+
+ | p_atan.c |
+ | |
+ | Compute the tan of a REG, using a polynomial approximation. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+#define HIPOWERon 6 /* odd poly, negative terms */
+static unsigned oddnegterms[HIPOWERon][2] =
+{
+ { 0x00000000, 0x00000000 }, /* for + 1.0 */
+ { 0x763b6f3d, 0x1adc4428 },
+ { 0x20f0630b, 0x0502909d },
+ { 0x4e825578, 0x0198ce38 },
+ { 0x22b7cb87, 0x008da6e3 },
+ { 0x9b30ca03, 0x00239c79 }
+} ;
+
+#define HIPOWERop 6 /* odd poly, positive terms */
+static unsigned oddplterms[HIPOWERop][2] =
+{
+ { 0xa6f67cb8, 0x94d910bd },
+ { 0xa02ffab4, 0x0a43cb45 },
+ { 0x04265e6b, 0x02bf5655 },
+ { 0x0a728914, 0x00f280f7 },
+ { 0x6d640e01, 0x004d6556 },
+ { 0xf1dd2dbf, 0x000a530a }
+};
+
+
+static unsigned denomterm[2] =
+{ 0xfc4bd208, 0xea2e6612 };
+
+
+
+/*--- poly_atan() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_atan(REG *arg)
+{
+ char recursions = 0;
+ short exponent;
+ REG odd_poly, even_poly, pos_poly, neg_poly;
+ REG argSq;
+ long long arg_signif, argSqSq;
+
+
+#ifdef PARANOID
+ if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
+ { arith_invalid(arg); return; }
+#endif PARANOID
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if ( arg->tag == TW_Zero )
+ {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, arg);
+ return;
+ }
+
+ if ( exponent >= -2 )
+ {
+ /* argument is in the range [0.25 .. 1.0] */
+ if ( exponent >= 0 )
+ {
+#ifdef PARANOID
+ if ( (exponent == 0) &&
+ (arg->sigl == 0) && (arg->sigh == 0x80000000) )
+#endif PARANOID
+ {
+ reg_move(&CONST_PI4, arg);
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
+#endif PARANOID
+ }
+
+ /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */
+ /* convert the argument by an identity for atan */
+ if ( (exponent >= -1) || (arg->sigh > 0xd413ccd0) )
+ {
+ REG numerator, denom;
+
+ recursions++;
+
+ arg_signif = *(long long *)&(arg->sigl);
+ if ( exponent < -1 )
+ {
+ if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
+ arg_signif++; /* round up */
+ }
+ *(long long *)&(numerator.sigl) = -arg_signif;
+ numerator.exp = EXP_BIAS - 1;
+ normalize(&numerator); /* 1 - arg */
+
+ arg_signif = *(long long *)&(arg->sigl);
+ if ( shrx(&arg_signif, -exponent) >= 0x80000000U )
+ arg_signif++; /* round up */
+ *(long long *)&(denom.sigl) = arg_signif;
+ denom.sigh |= 0x80000000; /* 1 + arg */
+
+ arg->exp = numerator.exp;
+ reg_u_div((long long *)&(numerator.sigl),
+ (long long *)&(denom.sigl), arg);
+
+ exponent = arg->exp - EXP_BIAS;
+ }
+ }
+
+ *(long long *)&arg_signif = *(long long *)&(arg->sigl);
+
+#ifdef PARANOID
+ /* This must always be true */
+ if ( exponent >= -1 )
+ {
+ EXCEPTION(EX_INTERNAL|0x120); /* There must be a logic error */
+ }
+#endif PARANOID
+
+ /* shift the argument right by the required places */
+ if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
+ arg_signif++; /* round up */
+
+ /* Now have arg_signif with binary point at the left
+ .1xxxxxxxx */
+ mul64(&arg_signif, &arg_signif, (long long *)(&argSq.sigl));
+ mul64((long long *)(&argSq.sigl), (long long *)(&argSq.sigl), &argSqSq);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&pos_poly.sigl, (unsigned *)&argSqSq,
+ (unsigned short (*)[4])oddplterms, HIPOWERop-1);
+ mul64((long long *)(&argSq.sigl), (long long *)(&pos_poly.sigl),
+ (long long *)(&pos_poly.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&neg_poly.sigl, (unsigned *)&argSqSq,
+ (unsigned short (*)[4])oddnegterms, HIPOWERon-1);
+
+ /* Subtract the mantissas */
+ *((long long *)(&pos_poly.sigl)) -= *((long long *)(&neg_poly.sigl));
+
+ reg_move(&pos_poly, &odd_poly);
+ poly_add_1(&odd_poly);
+
+ reg_u_mul(&odd_poly, arg, &odd_poly); /* The complete odd polynomial */
+ odd_poly.exp -= EXP_BIAS - 1;
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(even_poly.sign) = 0;
+
+ mul64((long long *)(&argSq.sigl),
+ (long long *)(&denomterm), (long long *)(&even_poly.sigl));
+
+ poly_add_1(&even_poly);
+
+ reg_div(&odd_poly, &even_poly, arg);
+
+ if ( recursions )
+ reg_sub(&CONST_PI4, arg, arg);
+}
+
+
+/* The argument to this function must be polynomial() compatible,
+ i.e. have an exponent (not checked) of EXP_BIAS-1 but need not
+ be normalized.
+ This function adds 1.0 to the (assumed positive) argument. */
+void poly_add_1(REG *src)
+{
+/* Rounding in a consistent direction produces better results
+ for the use of this function in poly_atan. Simple truncation
+ is used here instead of round-to-nearest. */
+
+#ifdef OBSOLETE
+char round = (src->sigl & 3) == 3;
+#endif OBSOLETE
+
+shrx(&src->sigl, 1);
+
+#ifdef OBSOLETE
+if ( round ) (*(long long *)&src->sigl)++; /* Round to even */
+#endif OBSOLETE
+
+src->sigh |= 0x80000000;
+
+src->exp = EXP_BIAS;
+
+}
diff --git a/kernel/FPU-emu/poly_div.S b/kernel/FPU-emu/poly_div.S
new file mode 100644
index 0000000..987fea9
--- /dev/null
+++ b/kernel/FPU-emu/poly_div.S
@@ -0,0 +1,97 @@
+ .file "poly_div.S"
+/*---------------------------------------------------------------------------+
+ | poly_div.S |
+ | |
+ | A set of functions to divide 64 bit integers by fixed numbers. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void poly_div2(long long *x) |
+ | void poly_div4(long long *x) |
+ | void poly_div16(long long *x) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+.text
+
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div2
+_poly_div2:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ shrl $1,4(%ecx)
+ rcrl $1,(%ecx)
+
+ testw $1,%ax
+ je poly_div2_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div2_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div4
+_poly_div4:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ movl 4(%ecx),%edx
+ shll $30,%edx
+
+ shrl $2,4(%ecx)
+ shrl $2,(%ecx)
+
+ orl %edx,(%ecx)
+
+ testw $2,%ax
+ je poly_div4_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div4_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
+ .align 2,144
+.globl _poly_div16
+_poly_div16:
+ pushl %ebp
+ movl %esp,%ebp
+
+ movl PARAM1,%ecx
+ movw (%ecx),%ax
+
+ movl 4(%ecx),%edx
+ shll $28,%edx
+
+ shrl $4,4(%ecx)
+ shrl $4,(%ecx)
+
+ orl %edx,(%ecx)
+
+ testw $8,%ax
+ je poly_div16_exit
+
+ addl $1,(%ecx)
+ adcl $0,4(%ecx)
+poly_div16_exit:
+
+ leave
+ ret
+/*---------------------------------------------------------------------------*/
diff --git a/kernel/FPU-emu/poly_l2.c b/kernel/FPU-emu/poly_l2.c
new file mode 100644
index 0000000..3ca20c3
--- /dev/null
+++ b/kernel/FPU-emu/poly_l2.c
@@ -0,0 +1,288 @@
+/*---------------------------------------------------------------------------+
+ | poly_l2.c |
+ | |
+ | Compute the base 2 logarithm of a REG, using a polynomial approximation. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+
+#define HIPOWER 9
+static unsigned short lterms[HIPOWER][4] =
+ {
+ /* Ideal computation with these coeffs gives about
+ 64.6 bit rel accuracy. */
+ { 0xe177, 0xb82f, 0x7652, 0x7154 },
+ { 0xee0f, 0xe80f, 0x2770, 0x7b1c },
+ { 0x0fc0, 0xbe87, 0xb143, 0x49dd },
+ { 0x78b9, 0xdadd, 0xec54, 0x34c2 },
+ { 0x003a, 0x5de9, 0x628b, 0x2909 },
+ { 0x5588, 0xed16, 0x4abf, 0x2193 },
+ { 0xb461, 0x85f7, 0x347a, 0x1c6a },
+ { 0x0975, 0x87b3, 0xd5bf, 0x1876 },
+ { 0xe85c, 0xcec9, 0x84e7, 0x187d }
+ };
+
+
+
+
+/*--- poly_l2() -------------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ +---------------------------------------------------------------------------*/
+void poly_l2(REG *arg, REG *result)
+{
+ short exponent;
+ char zero; /* flag for an Xx == 0 */
+ unsigned short bits, shift;
+ long long Xsq;
+ REG accum, denom, num, Xx;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ accum.tag = TW_Valid; /* set the tags to Valid */
+
+ if ( arg->sigh > (unsigned)0xb504f334 )
+ {
+ /* This is good enough for the computation of the polynomial
+ sum, but actually results in a loss of precision for
+ the computation of Xx. This will matter only if exponent
+ becomes zero. */
+ exponent++;
+ accum.sign = 1; /* sign to negative */
+ num.exp = EXP_BIAS; /* needed to prevent errors in div routine */
+ reg_u_div((long long *)&(CONST_1.sigl), (long long *)&(arg->sigl), &num);
+ }
+ else
+ {
+ accum.sign = 0; /* set the sign to positive */
+ num.sigl = arg->sigl; /* copy the mantissa */
+ num.sigh = arg->sigh;
+ }
+
+ /* shift num left, lose the ms bit */
+ num.sigh <<= 1;
+ if ( num.sigl & 0x80000000 ) num.sigh |= 1;
+ num.sigl <<= 1;
+
+ denom.sigl = num.sigl;
+ denom.sigh = num.sigh;
+ poly_div4((long long *)&(denom.sigl));
+ denom.sigh += 0x80000000; /* set the msb */
+ Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */
+ reg_u_div((long long *)&num.sigl, (long long *)&(denom.sigl), &Xx);
+
+ zero = !(Xx.sigh | Xx.sigl);
+
+ mul64((long long *)&Xx.sigl, (long long *)&Xx.sigl, &Xsq);
+ poly_div16(&Xsq);
+
+ accum.exp = -1; /* exponent of accum */
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial((unsigned *)&accum.sigl, (unsigned *)&Xsq, lterms, HIPOWER-1);
+
+ if ( !exponent )
+ {
+ /* If the exponent is zero, then we would lose precision by
+ sticking to fixed point computation here */
+ /* We need to re-compute Xx because of loss of precision. */
+ REG lXx;
+ char sign;
+
+ sign = accum.sign;
+ accum.sign = 0;
+
+ /* make accum compatible and normalize */
+ accum.exp = EXP_BIAS + accum.exp;
+ normalize(&accum);
+
+ if ( zero )
+ {
+ reg_move(&CONST_Z, result);
+ }
+ else
+ {
+ /* we need to re-compute lXx to better accuracy */
+ num.tag = TW_Valid; /* set the tags to Vaild */
+ num.sign = 0; /* set the sign to positive */
+ num.exp = EXP_BIAS - 1;
+ if ( sign )
+ {
+ /* The argument is of the form 1-x */
+ /* Use 1-1/(1-x) = x/(1-x) */
+ *((long long *)&num.sigl) = - *((long long *)&(arg->sigl));
+ normalize(&num);
+ reg_div(&num, arg, &num);
+ }
+ else
+ {
+ normalize(&num);
+ }
+
+ denom.tag = TW_Valid; /* set the tags to Valid */
+ denom.sign = SIGN_POS; /* set the sign to positive */
+ denom.exp = EXP_BIAS;
+
+ reg_div(&num, &denom, &lXx);
+
+ reg_u_mul(&lXx, &accum, &accum);
+ accum.exp += - EXP_BIAS + 1;
+/* normalize(&accum); ********/
+
+ reg_u_add(&lXx, &accum, result);
+
+ normalize(result);
+ }
+ result->sign = sign;
+ return;
+ }
+
+ mul64((long long *)&accum.sigl,
+ (long long *)&Xx.sigl, (long long *)&accum.sigl);
+
+ *((long long *)(&accum.sigl)) += *((long long *)(&Xx.sigl));
+
+ if ( Xx.sigh > accum.sigh )
+ {
+ /* There was an overflow */
+
+ poly_div2((long long *)&accum.sigl);
+ accum.sigh |= 0x80000000;
+ accum.exp++;
+ }
+
+ /* When we add the exponent to the accum result later, we will
+ require that their signs are the same. Here we ensure that
+ this is so. */
+ if ( exponent && ((exponent < 0) ^ (accum.sign)) )
+ {
+ /* signs are different */
+
+ accum.sign = !accum.sign;
+
+ /* An exceptional case is when accum is zero */
+ if ( accum.sigl | accum.sigh )
+ {
+ /* find 1-accum */
+ /* Shift to get exponent == 0 */
+ if ( accum.exp < 0 )
+ {
+ poly_div2((long long *)&accum.sigl);
+ accum.exp++;
+ }
+ /* Just negate, but throw away the sign */
+ *((long long *)&(accum.sigl)) = - *((long long *)&(accum.sigl));
+ if ( exponent < 0 )
+ exponent++;
+ else
+ exponent--;
+ }
+ }
+
+ shift = exponent >= 0 ? exponent : -exponent ;
+ bits = 0;
+ if ( shift )
+ {
+ if ( accum.exp )
+ {
+ accum.exp++;
+ poly_div2((long long *)&accum.sigl);
+ }
+ while ( shift )
+ {
+ poly_div2((long long *)&accum.sigl);
+ if ( shift & 1)
+ accum.sigh |= 0x80000000;
+ shift >>= 1;
+ bits++;
+ }
+ }
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp += bits + EXP_BIAS - 1;
+
+ reg_move(&accum, result);
+ normalize(result);
+
+ return;
+}
+
+
+/*--- poly_l2p1() -----------------------------------------------------------+
+ | Base 2 logarithm by a polynomial approximation. |
+ | log2(x+1) |
+ +---------------------------------------------------------------------------*/
+int poly_l2p1(REG *arg, REG *result)
+{
+ char sign = 0;
+ long long Xsq;
+ REG arg_pl1, denom, accum, local_arg, poly_arg;
+
+
+ sign = arg->sign;
+
+ reg_add(arg, &CONST_1, &arg_pl1);
+
+ if ( (arg_pl1.sign) | (arg_pl1.tag) )
+ { /* We need a valid positive number! */
+ return 1;
+ }
+
+ reg_add(&CONST_1, &arg_pl1, &denom);
+ reg_div(arg, &denom, &local_arg);
+ local_arg.sign = 0; /* Make the sign positive */
+
+ /* Now we need to check that |local_arg| is less than
+ 3-2*sqrt(2) = 0.17157.. = .0xafb0ccc0 * 2^-2 */
+
+ if ( local_arg.exp >= EXP_BIAS - 3 )
+ {
+ if ( (local_arg.exp > EXP_BIAS - 3) ||
+ (local_arg.sigh > (unsigned)0xafb0ccc0) )
+ {
+ /* The argument is large */
+ poly_l2(&arg_pl1, result); return 0;
+ }
+ }
+
+ /* Make a copy of local_arg */
+ reg_move(&local_arg, &poly_arg);
+
+ /* Get poly_arg bits aligned as required */
+ shrx((unsigned *)&(poly_arg.sigl), -(poly_arg.exp - EXP_BIAS + 3));
+
+ mul64((long long *)&(poly_arg.sigl), (long long *)&(poly_arg.sigl), &Xsq);
+ poly_div16(&Xsq);
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&(accum.sigl), (unsigned *)&Xsq, lterms, HIPOWER-1);
+
+ accum.tag = TW_Valid; /* set the tags to Valid */
+ accum.sign = SIGN_POS; /* and make accum positive */
+
+ /* make accum compatible and normalize */
+ accum.exp = EXP_BIAS - 1;
+ normalize(&accum);
+
+ reg_u_mul(&local_arg, &accum, &accum);
+ accum.exp -= EXP_BIAS - 1;
+
+ reg_u_add(&local_arg, &accum, result);
+
+ /* Multiply the result by 2 */
+ result->exp++;
+
+ result->sign = sign;
+
+ return 0;
+}
diff --git a/kernel/FPU-emu/poly_mul64.S b/kernel/FPU-emu/poly_mul64.S
new file mode 100644
index 0000000..7434094
--- /dev/null
+++ b/kernel/FPU-emu/poly_mul64.S
@@ -0,0 +1,72 @@
+/*---------------------------------------------------------------------------+
+ | poly_mul64.S |
+ | |
+ | Multiply two 64 bit integers. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void mul64(long long *a, long long *b, long long *result) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#include "fpu_asm.h"
+
+.text
+ .align 2,144
+.globl _mul64
+_mul64:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $16,%esp
+ pushl %esi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+ movl PARAM3,%ebx
+
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+ movl (%esi),%eax
+ mull (%ecx)
+ movl %eax,-16(%ebp) // Not used
+ movl %edx,-12(%ebp)
+
+ movl (%esi),%eax
+ mull 4(%ecx)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax
+ mull (%ecx)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl 4(%esi),%eax
+ mull 4(%ecx)
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+ testb $128,-9(%ebp)
+ je L_no_round
+
+ addl $1,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+L_no_round:
+ movl -8(%ebp),%esi
+ movl %esi,(%ebx)
+ movl -4(%ebp),%esi
+ movl %esi,4(%ebx)
+
+ popl %ebx
+ popl %esi
+ leave
+ ret
diff --git a/kernel/FPU-emu/poly_sin.c b/kernel/FPU-emu/poly_sin.c
new file mode 100644
index 0000000..8ec1175
--- /dev/null
+++ b/kernel/FPU-emu/poly_sin.c
@@ -0,0 +1,146 @@
+/*---------------------------------------------------------------------------+
+ | poly_sin.c |
+ | |
+ | Computation of an approximation of the sin function by a polynomial |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+#define HIPOWER 5
+static unsigned short lterms[HIPOWER][4] =
+ {
+ { 0x846a, 0x42d1, 0xb544, 0x921f},
+ { 0xe110, 0x75aa, 0xbc67, 0x1466},
+ { 0x503d, 0xa43f, 0x83c1, 0x000a},
+ { 0x8f9d, 0x7a19, 0x00f4, 0x0000},
+ { 0xda03, 0x06aa, 0x0000, 0x0000},
+ };
+
+static unsigned short negterms[HIPOWER][4] =
+ {
+ { 0x95ed, 0x2df2, 0xe731, 0xa55d},
+ { 0xd159, 0xe62b, 0xd2cc, 0x0132},
+ { 0x6342, 0xe9fb, 0x3c60, 0x0000},
+ { 0x6256, 0xdf5a, 0x0002, 0x0000},
+ { 0xf279, 0x000b, 0x0000, 0x0000},
+ };
+
+
+/*--- poly_sine() -----------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_sine(REG *arg, REG *result)
+{
+ short exponent;
+ REG Xx, Xx2, Xx4, accum, negaccum;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if ( arg->tag == TW_Zero )
+ {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, result);
+ return;
+ }
+
+#ifdef PARANOID
+ if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
+ {
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+
+ if ( exponent >= 0 ) /* Can't hack a number > 1.0 */
+ {
+ if ( (exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000) )
+ {
+ reg_move(&CONST_1, result);
+ return;
+ }
+ EXCEPTION(EX_Invalid);
+ reg_move(&CONST_QNaN, result);
+ return;
+ }
+#endif PARANOID
+
+ Xx.sigl = arg->sigl;
+ Xx.sigh = arg->sigh;
+ if ( exponent < -1 )
+ {
+ /* shift the argument right by the required places */
+ if ( shrx(&(Xx.sigl), -1-exponent) >= 0x80000000U )
+ (*((long long *)(&(Xx.sigl))))++; /* round up */
+ }
+
+ mul64((long long *)&(Xx.sigl), (long long *)&(Xx.sigl),
+ (long long *)&(Xx2.sigl));
+ mul64((long long *)&(Xx2.sigl), (long long *)&(Xx2.sigl),
+ (long long *)&(Xx4.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(accum.sign) = 0;
+ accum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&(accum.sigl), &(Xx4.sigl), lterms, HIPOWER-1);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(negaccum.sign) = 0;
+ negaccum.exp = 0;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&(negaccum.sigl), &(Xx4.sigl), negterms, HIPOWER-1);
+ mul64((long long *)&(Xx2.sigl), (long long *)&(negaccum.sigl),
+ (long long *)&(negaccum.sigl));
+
+ /* Subtract the mantissas */
+ *((long long *)(&(accum.sigl))) -= *((long long *)(&(negaccum.sigl)));
+
+ /* Convert to 64 bit signed-compatible */
+ accum.exp = EXP_BIAS - 1 + accum.exp;
+
+ *(short *)&(result->sign) = *(short *)&(accum.sign);
+ result->exp = accum.exp;
+ result->sigl = accum.sigl;
+ result->sigh = accum.sigh;
+
+ normalize(result);
+
+ reg_mul(result, arg, result);
+ reg_u_add(result, arg, result);
+
+ /* A small overflow may be possible... but an illegal result. */
+ if ( result->exp >= EXP_BIAS )
+ {
+ if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */
+ || (result->sigl > 1) /* Larger than 1.0+msb */
+ || (result->sigh != 0x80000000) /* Much > 1.0 */
+ )
+ {
+#ifdef DEBUGGING
+ printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp,
+ result->sigh, result->sigl);
+#endif DEBUGGING
+ EXCEPTION(EX_INTERNAL|0x103);
+ }
+
+#ifdef DEBUGGING
+ printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n");
+ printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp,
+ result->sigh, result->sigl);
+#endif DEBUGGING
+
+ result->sigl = 0; /* Truncate the result to 1.00 */
+ }
+}
diff --git a/kernel/FPU-emu/poly_tan.c b/kernel/FPU-emu/poly_tan.c
new file mode 100644
index 0000000..2f8fb41
--- /dev/null
+++ b/kernel/FPU-emu/poly_tan.c
@@ -0,0 +1,179 @@
+/*---------------------------------------------------------------------------+
+ | poly_tan.c |
+ | |
+ | Compute the tan of a REG, using a polynomial approximation. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+#define HIPOWERop 3 /* odd poly, positive terms */
+static unsigned short oddplterms[HIPOWERop][4] =
+ {
+ { 0x846a, 0x42d1, 0xb544, 0x921f},
+ { 0x6fb2, 0x0215, 0x95c0, 0x099c},
+ { 0xfce6, 0x0cc8, 0x1c9a, 0x0000}
+ };
+
+#define HIPOWERon 2 /* odd poly, negative terms */
+static unsigned short oddnegterms[HIPOWERon][4] =
+ {
+ { 0x6906, 0xe205, 0x25c8, 0x8838},
+ { 0x1dd7, 0x3fe3, 0x944e, 0x002c}
+ };
+
+#define HIPOWERep 2 /* even poly, positive terms */
+static unsigned short evenplterms[HIPOWERep][4] =
+ {
+ { 0xdb8f, 0x3761, 0x1432, 0x2acf},
+ { 0x16eb, 0x13c1, 0x3099, 0x0003}
+ };
+
+#define HIPOWERen 2 /* even poly, negative terms */
+static unsigned short evennegterms[HIPOWERen][4] =
+ {
+ { 0x3a7c, 0xe4c5, 0x7f87, 0x2945},
+ { 0x572b, 0x664c, 0xc543, 0x018c}
+ };
+
+
+/*--- poly_tan() ------------------------------------------------------------+
+ | |
+ +---------------------------------------------------------------------------*/
+void poly_tan(REG *arg, REG *y_reg)
+{
+ char invert = 0;
+ short exponent;
+ REG odd_poly, even_poly, pos_poly, neg_poly;
+ REG argSq;
+ long long arg_signif, argSqSq;
+
+
+ exponent = arg->exp - EXP_BIAS;
+
+ if ( arg->tag == TW_Zero )
+ {
+ /* Return 0.0 */
+ reg_move(&CONST_Z, y_reg);
+ return;
+ }
+
+ if ( exponent >= -1 )
+ {
+ /* argument is in the range [0.5 .. 1.0] */
+ if ( exponent >= 0 )
+ {
+#ifdef PARANOID
+ if ( (exponent == 0) &&
+ (arg->sigl == 0) && (arg->sigh == 0x80000000) )
+#endif PARANOID
+ {
+ arith_overflow(y_reg);
+ return;
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */
+#endif PARANOID
+ }
+ /* The argument is in the range [0.5 .. 1.0) */
+ /* Convert the argument to a number in the range (0.0 .. 0.5] */
+ *((long long *)(&arg->sigl)) = - *((long long *)(&arg->sigl));
+ normalize(arg); /* Needed later */
+ exponent = arg->exp - EXP_BIAS;
+ invert = 1;
+ }
+
+#ifdef PARANOID
+ if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */
+ { arith_invalid(y_reg); return; }
+#endif PARANOID
+
+ *(long long *)&arg_signif = *(long long *)&(arg->sigl);
+ if ( exponent < -1 )
+ {
+ /* shift the argument right by the required places */
+ if ( shrx(&arg_signif, -1-exponent) >= 0x80000000U )
+ arg_signif++; /* round up */
+ }
+
+ mul64(&arg_signif, &arg_signif, (long long *)(&argSq.sigl));
+ mul64((long long *)(&argSq.sigl), (long long *)(&argSq.sigl), &argSqSq);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, oddplterms, HIPOWERop-1);
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, oddnegterms, HIPOWERon-1);
+ mul64((long long *)(&argSq.sigl), (long long *)(&neg_poly.sigl),
+ (long long *)(&neg_poly.sigl));
+
+ /* Subtract the mantissas */
+ *((long long *)(&pos_poly.sigl)) -= *((long long *)(&neg_poly.sigl));
+
+ /* Convert to 64 bit signed-compatible */
+ pos_poly.exp -= 1;
+
+ reg_move(&pos_poly, &odd_poly);
+ normalize(&odd_poly);
+
+ reg_mul(&odd_poly, arg, &odd_poly);
+ reg_u_add(&odd_poly, arg, &odd_poly); /* This is just the odd polynomial */
+
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(pos_poly.sign) = 0;
+ pos_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&pos_poly.sigl, (unsigned *)&argSqSq, evenplterms, HIPOWERep-1);
+ mul64((long long *)(&argSq.sigl),
+ (long long *)(&pos_poly.sigl), (long long *)(&pos_poly.sigl));
+
+ /* will be a valid positive nr with expon = 0 */
+ *(short *)&(neg_poly.sign) = 0;
+ neg_poly.exp = EXP_BIAS;
+
+ /* Do the basic fixed point polynomial evaluation */
+ polynomial(&neg_poly.sigl, (unsigned *)&argSqSq, evennegterms, HIPOWERen-1);
+
+ /* Subtract the mantissas */
+ *((long long *)(&neg_poly.sigl)) -= *((long long *)(&pos_poly.sigl));
+ /* and multiply by argSq */
+
+ /* Convert argSq to a valid reg number */
+ *(short *)&(argSq.sign) = 0;
+ argSq.exp = EXP_BIAS - 1;
+ normalize(&argSq);
+
+ /* Convert to 64 bit signed-compatible */
+ neg_poly.exp -= 1;
+
+ reg_move(&neg_poly, &even_poly);
+ normalize(&even_poly);
+
+ reg_mul(&even_poly, &argSq, &even_poly);
+ reg_add(&even_poly, &argSq, &even_poly);
+ reg_sub(&CONST_1, &even_poly, &even_poly); /* This is just the even polynomial */
+
+ /* Now ready to copy the results */
+ if ( invert )
+ { reg_div(&even_poly, &odd_poly, y_reg); }
+ else
+ { reg_div(&odd_poly, &even_poly, y_reg); }
+
+}
diff --git a/kernel/FPU-emu/polynomial.S b/kernel/FPU-emu/polynomial.S
new file mode 100644
index 0000000..16118d7
--- /dev/null
+++ b/kernel/FPU-emu/polynomial.S
@@ -0,0 +1,140 @@
+/*---------------------------------------------------------------------------+
+ | polynomial.S |
+ | |
+ | Fixed point arithmetic polynomial evaluation. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void polynomial(unsigned accum[], unsigned x[], unsigned terms[][2], |
+ | int n) |
+ | |
+ | Computes: |
+ | terms[0] + (terms[1] + (terms[2] + ... + (terms[n-1]*x)*x)*x)*x) ... )*x |
+ | The result is returned in accum. |
+ | |
+ +---------------------------------------------------------------------------*/
+
+ .file "fpolynom.s"
+
+#include "fpu_asm.h"
+
+
+// #define EXTRA_PRECISE
+
+#define TERM_SIZE $8
+
+
+.text
+ .align 2,144
+.globl _polynomial
+_polynomial:
+ pushl %ebp
+ movl %esp,%ebp
+ subl $32,%esp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi // accum
+ movl PARAM2,%edi // x
+ movl PARAM3,%ebx // terms
+ movl PARAM4,%ecx // n
+
+ movl TERM_SIZE,%eax
+ mull %ecx
+ movl %eax,%ecx
+
+ movl 4(%ebx,%ecx,1),%edx // terms[n]
+ movl %edx,-20(%ebp)
+ movl (%ebx,%ecx,1),%edx // terms[n]
+ movl %edx,-24(%ebp)
+ xor %eax,%eax
+ movl %eax,-28(%ebp)
+
+ subl TERM_SIZE,%ecx
+ js xL_accum_done
+
+xL_accum_loop:
+ xor %eax,%eax
+ movl %eax,-4(%ebp)
+ movl %eax,-8(%ebp)
+
+#ifdef EXTRA_PRECISE
+ movl -28(%ebp),%eax
+ mull 4(%edi) // x ms long
+ movl %edx,-12(%ebp)
+#endif EXTRA_PRECISE
+
+ movl -24(%ebp),%eax
+ mull (%edi) // x ls long
+// movl %eax,-16(%ebp) // Not needed
+ addl %edx,-12(%ebp)
+ adcl $0,-8(%ebp)
+
+ movl -24(%ebp),%eax
+ mull 4(%edi) // x ms long
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl -20(%ebp),%eax
+ mull (%edi)
+ addl %eax,-12(%ebp)
+ adcl %edx,-8(%ebp)
+ adcl $0,-4(%ebp)
+
+ movl -20(%ebp),%eax
+ mull 4(%edi)
+ addl %eax,-8(%ebp)
+ adcl %edx,-4(%ebp)
+
+/* Now add the next term */
+ movl (%ebx,%ecx,1),%eax
+ addl %eax,-8(%ebp)
+ movl 4(%ebx,%ecx,1),%eax
+ adcl %eax,-4(%ebp)
+
+/* And put into the second register */
+ movl -4(%ebp),%eax
+ movl %eax,-20(%ebp)
+ movl -8(%ebp),%eax
+ movl %eax,-24(%ebp)
+
+#ifdef EXTRA_PRECISE
+ movl -12(%ebp),%eax
+ movl %eax,-28(%ebp)
+#else
+ testb $128,-25(%ebp)
+ je xL_no_poly_round
+
+ addl $1,-24(%ebp)
+ adcl $0,-20(%ebp)
+xL_no_poly_round:
+#endif EXTRA_PRECISE
+
+ subl TERM_SIZE,%ecx
+ jns xL_accum_loop
+
+xL_accum_done:
+#ifdef EXTRA_PRECISE
+/* And round the result */
+ testb $128,-25(%ebp)
+ je xL_poly_done
+
+ addl $1,-24(%ebp)
+ adcl $0,-20(%ebp)
+#endif EXTRA_PRECISE
+
+xL_poly_done:
+ movl -24(%ebp),%eax
+ movl %eax,(%esi)
+ movl -20(%ebp),%eax
+ movl %eax,4(%esi)
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/kernel/FPU-emu/reg_add_sub.c b/kernel/FPU-emu/reg_add_sub.c
new file mode 100644
index 0000000..bd801bb
--- /dev/null
+++ b/kernel/FPU-emu/reg_add_sub.c
@@ -0,0 +1,160 @@
+/*---------------------------------------------------------------------------+
+ | reg_add_sub.c |
+ | |
+ | Functions to add or subtract two registers and put the result in a third. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | For each function, the destination may be any REG, including one of the |
+ | source REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+
+void reg_add(REG *a, REG *b, REG *dest)
+{
+ int diff;
+
+ if ( !(a->tag | b->tag) )
+ {
+ /* Both registers are valid */
+ if (!(a->sign ^ b->sign))
+ {
+ /* signs are the same */
+ reg_u_add(a, b, dest);
+ dest->sign = a->sign;
+ return;
+ }
+
+ /* 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 > 0)
+ {
+ reg_u_sub(a, b, dest);
+ dest->sign = a->sign;
+ return;
+ }
+ else
+ {
+ reg_u_sub(b, a, dest);
+ dest->sign = b->sign;
+ return;
+ }
+ }
+ else
+ {
+ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
+ { real_2op_NaN(a, b, dest); return; }
+ else if (a->tag == TW_Zero)
+ { reg_move(b, dest); return; }
+ else if (b->tag == TW_Zero)
+ { reg_move(a, dest); return; }
+ else if (a->tag == TW_Infinity)
+ {
+ if (b->tag != TW_Infinity)
+ { reg_move(a, dest); return; }
+ /* They are both + or - infinity */
+ if (a->sign == b->sign)
+ { reg_move(a, dest); return; }
+ reg_move(&CONST_QNaN, dest); /* inf - inf is undefined. */
+ return;
+ }
+ else if (b->tag == TW_Infinity)
+ { reg_move(b, dest); return; }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x101);
+#endif
+}
+
+
+/* Subtract b from a. (a-b) -> dest */
+void reg_sub(REG *a, REG *b, REG *dest)
+{
+ 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;
+ }
+
+ switch (a->sign*2 + b->sign)
+ {
+ case 0: /* P - P */
+ case 3: /* N - N */
+ if (diff > 0)
+ {
+ reg_u_sub(a, b, dest);
+ dest->sign = a->sign;
+ }
+ else
+ {
+ reg_u_sub(b, a, dest);
+ dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
+ }
+ return;
+ case 1: /* P - N */
+ reg_u_add(a, b, dest);
+ dest->sign = SIGN_POS;
+ return;
+ case 2: /* N - P */
+ reg_u_add(a, b, dest);
+ dest->sign = SIGN_NEG;
+ return;
+ }
+ }
+ else
+ {
+ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
+ { real_2op_NaN(a, b, dest); return; }
+ else if (b->tag == TW_Zero)
+ { reg_move(a, dest); return; }
+ else if (a->tag == TW_Zero)
+ {
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS^SIGN_NEG;
+ return;
+ }
+ else if (a->tag == TW_Infinity)
+ {
+ if (b->tag != TW_Infinity)
+ { reg_move(a, dest); return; }
+ if (a->sign == b->sign)
+ { reg_move(&CONST_QNaN, dest); return; }
+ reg_move(a, dest);
+ return;
+ }
+ else if (b->tag == TW_Infinity)
+ {
+ reg_move(b, dest);
+ dest->sign ^= SIGN_POS^SIGN_NEG;
+ return;
+ }
+ }
+#ifdef PARANOID
+ EXCEPTION(EX_INTERNAL|0x110);
+#endif
+}
+
diff --git a/kernel/FPU-emu/reg_compare.c b/kernel/FPU-emu/reg_compare.c
new file mode 100644
index 0000000..0cb557a
--- /dev/null
+++ b/kernel/FPU-emu/reg_compare.c
@@ -0,0 +1,269 @@
+/*---------------------------------------------------------------------------+
+ | reg_compare.c |
+ | |
+ | Compare two floating point registers |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | compare() is the core REG comparison function |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+
+
+int compare(REG *b)
+{
+ int diff;
+
+ if ( st0_ptr->tag | b->tag )
+ {
+ if ( st0_ptr->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 ;
+ }
+ }
+ else if ( b->tag == TW_Zero )
+ {
+ if ( st0_ptr->tag == TW_Valid )
+ {
+ return (st0_ptr->sign == SIGN_POS) ? COMP_A_GT_B : COMP_A_LT_B ;
+ }
+ }
+
+ if ( st0_ptr->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;
+ }
+ 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_ptr->tag == TW_Valid) || (st0_ptr->tag == TW_Zero) )
+ {
+ return (b->sign == SIGN_POS) ? COMP_A_LT_B : COMP_A_GT_B;
+ }
+ /* Fall through to the NaN code */
+ }
+
+ /* The only possibility now should be that one of the arguments
+ is a NaN */
+ if ( (st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN) )
+ {
+ if ( ((st0_ptr->tag == TW_NaN) && !(st0_ptr->sigh & 0x40000000))
+ || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)) )
+ /* At least one arg is a signaling NaN */
+ return COMP_NOCOMP | COMP_SNAN | COMP_NAN;
+ else
+ /* Neither is a signaling NaN */
+ return COMP_NOCOMP | 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;
+
+ diff = st0_ptr->exp - b->exp;
+ if ( diff == 0 )
+ {
+ diff = st0_ptr->sigh - b->sigh;
+ 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 ;
+ if ( diff < 0 )
+ return (st0_ptr->sign == SIGN_POS) ? COMP_A_LT_B : COMP_A_GT_B ;
+ return COMP_A_EQ_B;
+
+}
+
+
+void compare_st_data(void)
+{
+ int f;
+ int c = compare(&FPU_loaded_data);
+
+ if (c & COMP_NAN)
+ {
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ }
+ else
+ switch (c)
+ {
+ 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_NOCOMP:
+ 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);
+}
+
+
+static void compare_st_st(int nr)
+{
+ int c = compare(&st(nr));
+ int f;
+ if (c & COMP_NAN)
+ {
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ }
+ else
+ switch (c)
+ {
+ 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_NOCOMP:
+ 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);
+}
+
+
+static void compare_u_st_st(int nr)
+{
+ int f;
+ int c = compare(&st(nr));
+ if (c & COMP_NAN)
+ {
+ if (c & COMP_SNAN) /* This is the only difference between
+ un-ordered and ordinary comparisons */
+ EXCEPTION(EX_Invalid);
+ f = SW_C3 | SW_C2 | SW_C0;
+ }
+ else
+ switch (c)
+ {
+ 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_NOCOMP:
+ 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);
+}
+
+/*---------------------------------------------------------------------------*/
+
+void fcom_st()
+{
+ /* fcom st(i) */
+ compare_st_st(FPU_rm);
+}
+
+
+void fcompst()
+{
+ /* fcomp st(i) */
+ compare_st_st(FPU_rm);
+ pop();
+}
+
+
+void fcompp()
+{
+ /* fcompp */
+ if (FPU_rm != 1)
+ return Un_impl();
+ compare_st_st(1);
+ pop();
+ pop();
+}
+
+
+void fucom_()
+{
+ /* fucom st(i) */
+ compare_u_st_st(FPU_rm);
+}
+
+
+void fucomp()
+{
+ /* fucomp st(i) */
+ compare_u_st_st(FPU_rm);
+ pop();
+}
+
+
+void fucompp()
+{
+ /* fucompp */
+ if (FPU_rm == 1)
+ {
+ compare_u_st_st(1);
+ pop();
+ pop();
+ }
+ else
+ Un_impl();
+}
diff --git a/kernel/FPU-emu/reg_constant.c b/kernel/FPU-emu/reg_constant.c
new file mode 100644
index 0000000..e8b0350
--- /dev/null
+++ b/kernel/FPU-emu/reg_constant.c
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------------+
+ | reg_constant.c |
+ | |
+ | All of the constant REGs |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+struct reg CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x00000000, 0x80000000 };
+struct reg CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0x00000000, 0x80000000 };
+struct reg CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0x00000000, 0x80000000 };
+struct reg CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0xcd1b8afe, 0xd49a784b };
+struct reg CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x5c17f0bc, 0xb8aa3b29 };
+struct reg CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0x2168c235, 0xc90fdaa2 };
+struct reg CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x2168c235, 0xc90fdaa2 };
+struct reg CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0x2168c235, 0xc90fdaa2 };
+struct reg CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
+ 0xfbcff799, 0x9a209a84 };
+struct reg CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0xd1cf79ac, 0xb17217f7 };
+
+/* Only the sign (and tag) is used in internal zeroes */
+struct reg CONST_Z = { SIGN_POS, TW_Zero, 0, 0x0, 0x0 };
+
+/* Only the sign and significand (and tag) are used in internal NaNs */
+/* The 80486 never generates one of these
+struct reg CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+ */
+/* This is the real indefinite QNaN */
+struct reg CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
+
+/* Only the sign (and tag) is used in internal infinities */
+struct reg CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
+
+
+
+static void fld_const(REG *c)
+{
+ REG *st_new_ptr;
+
+ if ( STACK_OVERFLOW )
+ {
+ stack_overflow();
+ return;
+ }
+ push();
+ reg_move(c, st0_ptr);
+ status_word &= ~SW_C1;
+}
+
+
+static void fld1()
+{
+ fld_const(&CONST_1);
+}
+
+static void fldl2t()
+{
+ fld_const(&CONST_L2T);
+}
+
+static void fldl2e()
+{
+ fld_const(&CONST_L2E);
+}
+
+static void fldpi()
+{
+ fld_const(&CONST_PI);
+}
+
+static void fldlg2()
+{
+ fld_const(&CONST_LG2);
+}
+
+static void fldln2()
+{
+ fld_const(&CONST_LN2);
+}
+
+static void fldz()
+{
+ fld_const(&CONST_Z);
+}
+
+static FUNC constants_table[] = {
+ fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl
+};
+
+void fconst()
+{
+ (constants_table[FPU_rm])();
+}
diff --git a/kernel/FPU-emu/reg_constant.h b/kernel/FPU-emu/reg_constant.h
new file mode 100644
index 0000000..4297588
--- /dev/null
+++ b/kernel/FPU-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 apm233m@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _REG_CONSTANT_H_
+#define _REG_CONSTANT_H_
+
+#include "fpu_emu.h"
+
+extern REG CONST_1;
+extern REG CONST_2;
+extern REG CONST_HALF;
+extern REG CONST_L2T;
+extern REG CONST_L2E;
+extern REG CONST_PI;
+extern REG CONST_PI2;
+extern REG CONST_PI4;
+extern REG CONST_LG2;
+extern REG CONST_LN2;
+extern REG CONST_Z;
+extern REG CONST_PINF;
+extern REG CONST_INF;
+extern REG CONST_MINF;
+extern REG CONST_QNaN;
+
+#endif _REG_CONSTANT_H_
+
diff --git a/kernel/FPU-emu/reg_div.S b/kernel/FPU-emu/reg_div.S
new file mode 100644
index 0000000..3d5a4aa
--- /dev/null
+++ b/kernel/FPU-emu/reg_div.S
@@ -0,0 +1,172 @@
+ .file "reg_div.S"
+/*---------------------------------------------------------------------------+
+ | reg_div.S |
+ | |
+ | Divide one REG by another and put the result in a destination REG. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void reg_div(REG *a, REG *b, REG *dest) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+.text
+ .align 2
+
+.globl _reg_div
+_reg_div:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ebx
+ movl PARAM3,%edi
+
+ movb TAG(%esi),%al
+ orb TAG(%ebx),%al
+
+ jne xL_div_special // Not (both numbers TW_Valid)
+
+
+// Both arguments are TW_Valid
+ movl EXP(%esi),%edx
+ movl EXP(%ebx),%eax
+ subl %eax,%edx
+ addl EXP_BIAS,%edx
+ movl %edx,EXP(%edi)
+
+ movb TW_Valid,TAG(%edi)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ setneb (%edi) // Set the sign, requires neg=1, pos=0
+
+ add $SIGL_OFFSET,%ebx
+ add $SIGL_OFFSET,%esi
+
+ jmp _divide_kernel
+
+
+/*-----------------------------------------------------------------------*/
+xL_div_special:
+ cmpb TW_NaN,TAG(%esi) // A NaN with anything to give NaN
+ je xL_arg1_NaN
+
+ cmpb TW_NaN,TAG(%ebx) // A NaN with anything to give NaN
+ jne xL_no_NaN_arg
+
+// Operations on NaNs
+xL_arg1_NaN:
+xL_arg2_NaN:
+ pushl %edi
+ pushl %ebx
+ pushl %esi
+ call _real_2op_NaN
+ jmp xL78
+
+// Invalid operations
+xL_zero_zero:
+xL_inf_inf:
+ pushl %esi
+ call _arith_invalid
+ jmp xL78
+
+xL_no_NaN_arg:
+ cmpb TW_Infinity,TAG(%esi)
+ jne xL_arg1_not_inf
+
+ cmpb TW_Infinity,TAG(%ebx)
+ je xL_inf_inf // invalid operation
+
+ // Note that p16-9 says that infinity/0 returns infinity
+ jmp xL_copy_arg1 // Answer is Inf
+
+xL_arg1_not_inf:
+ cmpb TW_Zero,TAG(%ebx) // Priority to div-by-zero error
+ jne xL_arg2_not_zero
+
+ cmpb TW_Zero,TAG(%esi)
+ je xL_zero_zero // invalid operation
+
+// Division by zero error
+ pushl %esi
+ movb SIGN(%esi),%al
+ xorb SIGN(%ebx),%al
+ pushl %eax // lower 8 bits have the sign
+ call _divide_by_zero
+ jmp xL78
+
+xL_arg2_not_zero:
+ cmpb TW_Infinity,TAG(%ebx)
+ jne xL_arg2_not_inf
+
+ jmp xL_return_zero // Answer is zero
+
+xL_arg2_not_inf:
+ cmpb TW_Zero,TAG(%esi)
+ jne xL_unknown_tags
+
+xL_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)
+
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%ebx)
+ jne xL76
+
+ movb SIGN_POS,SIGN(%edi)
+ jmp xL78
+
+xL71:
+ movb SIGN(%esi),%cl
+ cmpb %cl,SIGN(%edi)
+ jne xL76
+
+ movb SIGN_POS,SIGN(%ebx)
+ jmp xL78
+
+ .align 2,0x90
+xL76:
+ movb SIGN_NEG,SIGN(%edi)
+
+xL78:
+ leal -12(%ebp),%esp
+
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+xL_return_zero:
+ movb TW_Zero,TAG(%edi)
+ jmp xL71
+
+xL_unknown_tags:
+ push 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 xL78
diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c
new file mode 100644
index 0000000..79826cd
--- /dev/null
+++ b/kernel/FPU-emu/reg_ld_str.c
@@ -0,0 +1,1194 @@
+/*---------------------------------------------------------------------------+
+ | reg_ld_str.c |
+ | |
+ | All of the functions which transfer data between user memory and REGs. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include <asm/segment.h>
+
+#include "fpu_system.h"
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+#include "control_w.h"
+
+
+#define EXTENDED_Emax 0x3fff /* largest valid exponent */
+#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 */
+
+
+REG FPU_loaded_data;
+
+
+/* Get a long double from user memory */
+void reg_load_extended(void)
+{
+ long double *s = (long double *)FPU_data_address;
+ FPU_loaded_data.sigl = get_fs_long((unsigned long *) s);
+ FPU_loaded_data.sigh = get_fs_long(1 + (unsigned long *) s);
+ FPU_loaded_data.exp = get_fs_word(4 + (unsigned short *) s);
+ if (FPU_loaded_data.exp & 0x8000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ if ( (FPU_loaded_data.exp &= 0x7fff) == 0 )
+ {
+ if ( !(FPU_loaded_data.sigl | FPU_loaded_data.sigh) )
+ {
+ FPU_loaded_data.tag = TW_Zero;
+ return;
+ }
+ /* The number is de-normal */
+ /* The default behaviour will take care of this */
+ }
+ else if ( FPU_loaded_data.exp == 0x7fff )
+ {
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ if ( (FPU_loaded_data.sigh == 0x80000000)
+ && (FPU_loaded_data.sigl == 0) )
+ {
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ }
+ if ( !(FPU_loaded_data.sigh & 0x80000000) )
+ {
+ /* Unsupported data type */
+ EXCEPTION(EX_Invalid);
+ FPU_loaded_data.tag = TW_NaN;
+ return;
+ }
+ FPU_loaded_data.tag = TW_NaN;
+ return;
+ }
+ FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
+ + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+
+ normalize(&FPU_loaded_data);
+}
+
+
+/* Get a double from user memory */
+void reg_load_double(void)
+{
+ double *dfloat = (double *)FPU_data_address;
+ unsigned m64 = get_fs_long(1 + (unsigned long *) dfloat);
+ unsigned l64 = get_fs_long((unsigned long *) dfloat);
+ int exp;
+
+ if (m64 & 0x80000000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_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 */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ }
+ else
+ {
+ /* Must be a signaling or quiet NaN */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_NaN;
+ FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+ return;
+ }
+ }
+ else if ( exp < DOUBLE_Emin )
+ {
+ /* Zero or de-normal */
+ if ((m64 == 0) && (l64 == 0))
+ {
+ /* Zero */
+ int c = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = c;
+ return;
+ }
+ else
+ {
+ /* De-normal */
+ FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = m64 << 11;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+ normalize(&FPU_loaded_data);
+ return;
+ }
+ }
+ else
+ {
+ FPU_loaded_data.exp = exp + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
+ FPU_loaded_data.sigh |= l64 >> 21;
+ FPU_loaded_data.sigl = l64 << 11;
+
+ return;
+ }
+}
+
+
+/* Get a float from user memory */
+void reg_load_single(void)
+{
+ float *single = (float *)FPU_data_address;
+ unsigned m32 = get_fs_long((unsigned long *) single);
+ int exp;
+
+ if (m32 & 0x80000000)
+ FPU_loaded_data.sign = SIGN_NEG;
+ else
+ FPU_loaded_data.sign = SIGN_POS;
+ if (!(m32 & 0x7fffffff))
+ {
+ /* Zero */
+ int c = FPU_loaded_data.sign;
+ reg_move(&CONST_Z, &FPU_loaded_data);
+ FPU_loaded_data.sign = c;
+ return;
+ }
+ exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
+ m32 = (m32 & 0x7fffff) << 8;
+ if ( exp < SINGLE_Emin )
+ {
+ /* De-normals */
+ FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
+ FPU_loaded_data.tag = TW_Valid;
+ FPU_loaded_data.sigh = m32;
+ FPU_loaded_data.sigl = 0;
+ normalize(&FPU_loaded_data);
+ return;
+ }
+ else if ( exp > SINGLE_Emax )
+ {
+ /* Infinity or NaN */
+ if ( m32 == 0 )
+ {
+ /* +- infinity */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_Infinity;
+ return;
+ }
+ else
+ {
+ /* Must be a signaling or quiet NaN */
+ FPU_loaded_data.exp = EXTENDED_Emax;
+ FPU_loaded_data.tag = TW_NaN;
+ FPU_loaded_data.sigh = m32 | 0x80000000;
+ FPU_loaded_data.sigl = 0;
+ return;
+ }
+ }
+ else
+ {
+ FPU_loaded_data.exp = exp + EXP_BIAS;
+ FPU_loaded_data.sigh = m32 | 0x80000000;
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.tag = TW_Valid;
+ }
+}
+
+
+/* Get a long long from user memory */
+void reg_load_int64(void)
+{
+ long long *_s = (long long *)FPU_data_address;
+ int e;
+ long long s;
+ ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
+ ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
+
+ if (s == 0)
+ { reg_move(&CONST_Z, &FPU_loaded_data); return; }
+
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else
+ {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 63;
+ *((long long *)&FPU_loaded_data.sigl) = s;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize(&FPU_loaded_data);
+}
+
+
+/* Get a long from user memory */
+void reg_load_int32(void)
+{
+ long *_s = (long *)FPU_data_address;
+ long s = (long)get_fs_long((unsigned long *) _s);
+
+ int e;
+
+ if (s == 0)
+ { reg_move(&CONST_Z, &FPU_loaded_data); return; }
+
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else
+ {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 31;
+ FPU_loaded_data.sigh = s;
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize(&FPU_loaded_data);
+}
+
+
+/* Get a short from user memory */
+void reg_load_int16(void)
+{
+ short *_s = (short *)FPU_data_address;
+ int s = (int)get_fs_word((unsigned short *) _s);
+
+ int e;
+
+ if (s == 0)
+ { reg_move(&CONST_Z, &FPU_loaded_data); return; }
+
+ if (s > 0)
+ FPU_loaded_data.sign = SIGN_POS;
+ else
+ {
+ s = -s;
+ FPU_loaded_data.sign = SIGN_NEG;
+ }
+
+ e = EXP_BIAS + 15;
+ FPU_loaded_data.sigh = s << 16;
+
+ FPU_loaded_data.sigl = 0;
+ FPU_loaded_data.exp = e;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize(&FPU_loaded_data);
+}
+
+
+/* Get a packed bcd array from user memory */
+void reg_load_bcd(void)
+{
+ char *s = (char *)FPU_data_address;
+ int pos;
+ unsigned char bcd;
+ long long l=0;
+
+ for ( pos = 8; pos >= 0; pos--)
+ {
+ l *= 10;
+ bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
+ l += bcd >> 4;
+ l *= 10;
+ l += bcd & 0x0f;
+ }
+
+ if (l == 0)
+ { reg_move(&CONST_Z, &FPU_loaded_data); }
+ else
+ {
+ *((long long *)&FPU_loaded_data.sigl) = l;
+ FPU_loaded_data.exp = EXP_BIAS + 63;
+ FPU_loaded_data.tag = TW_Valid;
+ normalize(&FPU_loaded_data);
+ }
+
+ FPU_loaded_data.sign =
+ ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
+ SIGN_NEG : SIGN_POS;
+}
+
+/*===========================================================================*/
+
+/* Put a long double into user memory */
+int reg_store_extended(void)
+{
+ long double *d = (long double *)FPU_data_address;
+ short e;
+
+ verify_area(d,10);
+ e = st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
+
+ if ( st0_ptr->tag == TW_Valid )
+ {
+ if ( e >= 0x7fff )
+ {
+ EXCEPTION(EX_Overflow); /* Overflow */
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Overflow )
+ {
+ /* Overflow to infinity */
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0x80000000, 1 + (unsigned long *) d);
+ e = 0x7fff;
+ }
+ else
+ return 0;
+ }
+ else if ( e <= 0 )
+ {
+ if ( e == 0 )
+ {
+ EXCEPTION(EX_Denormal); /* Pseudo de-normal */
+ put_fs_long(st0_ptr->sigl, (unsigned long *) d);
+ put_fs_long(st0_ptr->sigh, 1 + (unsigned long *) d);
+ }
+ else if ( e > -64 )
+ {
+ /* Make a de-normal */
+ REG tmp;
+ EXCEPTION(EX_Denormal); /* De-normal */
+ reg_move(st0_ptr, &tmp);
+ tmp.exp += EXTENDED_Emin + 64; /* largest exp to be 62 */
+ round_to_int(&tmp);
+ e = 0;
+ put_fs_long(tmp.sigl, (unsigned long *) d);
+ put_fs_long(tmp.sigh, 1 + (unsigned long *) d);
+ }
+ else
+ {
+ EXCEPTION(EX_Underflow); /* Underflow */
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Underflow )
+ {
+ /* Underflow to zero */
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0, 1 + (unsigned long *) d);
+ e = 0;
+ }
+ else
+ return 0;
+ }
+ }
+ else
+ {
+ put_fs_long(st0_ptr->sigl, (unsigned long *) d);
+ put_fs_long(st0_ptr->sigh, 1 + (unsigned long *) d);
+ }
+ }
+ else if ( st0_ptr->tag == TW_Zero )
+ {
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0, 1 + (unsigned long *) d);
+ e = 0;
+ }
+ else if ( st0_ptr->tag == TW_Infinity )
+ {
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0x80000000, 1 + (unsigned long *) d);
+ e = 0x7fff;
+ }
+ else if ( st0_ptr->tag == TW_NaN )
+ {
+ put_fs_long(st0_ptr->sigl, (unsigned long *) d);
+ put_fs_long(st0_ptr->sigh, 1 + (unsigned long *) d);
+ e = 0x7fff;
+ }
+ else if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ put_fs_long(0, (unsigned long *) d);
+ put_fs_long(0xc0000000, 1 + (unsigned long *) d);
+ put_fs_word(0xffff, 4 + (short *) d);
+ return 1;
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ /* We don't use TW_Denormal yet ... perhaps never! */
+ EXCEPTION(EX_Invalid);
+ /* Store a NaN */
+ e = 0x7fff;
+ put_fs_long(1, (unsigned long *) d);
+ put_fs_long(0x80000000, 1 + (unsigned long *) d);
+ }
+ put_fs_word(e + st0_ptr->sign*0x8000, 4 + (short *) d);
+
+ return 1;
+
+}
+
+
+/* Put a double into user memory */
+int reg_store_double(void)
+{
+ double *dfloat = (double *)FPU_data_address;
+ unsigned long l[2];
+
+ verify_area((void *)dfloat,8);
+
+ if (st0_ptr->tag == TW_Valid)
+ {
+ /* Rounding can get a little messy.. */
+ int exp = st0_ptr->exp - EXP_BIAS;
+ int increment = ((st0_ptr->sigl & 0x7ff) > 0x400) | /* nearest */
+ ((st0_ptr->sigl & 0xc00) == 0xc00); /* odd -> even */
+ if ( increment )
+ {
+ if ( st0_ptr->sigl >= 0xfffff800 )
+ {
+ /* the sigl part overflows */
+ if ( st0_ptr->sigh == 0xffffffff )
+ {
+ /* The sigh part overflows */
+ l[0] = l[1] = 0;
+ exp++; /* no need to check here for overflow */
+ }
+ else
+ {
+ /* No overflow of sigh will happen, can safely increment */
+ l[0] = (st0_ptr->sigh+1) << 21;
+ l[1] = (((st0_ptr->sigh+1) >> 11) & 0xfffff);
+ }
+ }
+ else
+ {
+ /* We only need to increment sigl */
+ l[0] = ((st0_ptr->sigl+0x800) >> 11) | (st0_ptr->sigh << 21);
+ l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
+ }
+ }
+ else
+ {
+ /* No increment required */
+ l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
+ l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
+ }
+
+ if ( exp > DOUBLE_Emax )
+ {
+ EXCEPTION(EX_Overflow);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Overflow )
+ {
+ /* Overflow to infinity */
+ l[0] = 0x00000000; /* Set to */
+ l[1] = 0x7ff00000; /* + INF */
+ }
+ else
+ return 0;
+ }
+ else if ( exp < DOUBLE_Emin )
+ {
+ if ( exp > DOUBLE_Emin-53 )
+ {
+ /* Make a de-normal */
+ REG tmp;
+ EXCEPTION(EX_Denormal);
+ reg_move(st0_ptr, &tmp);
+ tmp.exp += DOUBLE_Emin + 52; /* largest exp to be 51 */
+ round_to_int(&tmp);
+ l[0] = tmp.sigl;
+ l[1] = tmp.sigh;
+ }
+ else
+ {
+ EXCEPTION(EX_Underflow);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Underflow )
+ {
+ /* Underflow to zero */
+ l[0] = l[1] = 0;
+ }
+ else
+ return 0;
+ }
+ }
+ else
+ {
+ /* Add the exponent */
+ l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
+ }
+ }
+ else if (st0_ptr->tag == TW_Zero)
+ {
+ /* Number is zero */
+ l[0] = l[1] = 0;
+ }
+ else if (st0_ptr->tag == TW_Infinity)
+ {
+ l[0] = 0;
+ l[1] = 0x7ff00000;
+ }
+ else if (st0_ptr->tag == TW_NaN)
+ {
+ /* See if we can get a valid NaN from the REG */
+ l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
+ l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
+ if ( !(l[0] | l[1]) )
+ {
+ /* This case does not seem to be handled by the 80486 specs */
+ EXCEPTION(EX_Invalid);
+ /* Make the quiet NaN "real indefinite" */
+ goto put_indefinite;
+ }
+ l[1] |= 0x7ff00000;
+ }
+ else if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+put_indefinite:
+ put_fs_long(0, (unsigned long *) dfloat);
+ put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
+ return 1;
+ }
+ else
+ return 0;
+ }
+ else if (st0_ptr->tag == TW_Denormal)
+ {
+ /* Extended real -> double real will always underflow */
+ l[0] = l[1] = 0;
+ EXCEPTION(EX_Underflow);
+ }
+ if (st0_ptr->sign)
+ l[1] |= 0x80000000;
+
+ put_fs_long(l[0], (unsigned long *)dfloat);
+ put_fs_long(l[1], 1 + (unsigned long *)dfloat);
+
+ return 1;
+
+}
+
+
+/* Put a float into user memory */
+int reg_store_single(void)
+{
+ float *single = (float *)FPU_data_address;
+ long templ;
+ int exp = st0_ptr->exp - EXP_BIAS;
+ unsigned long sigh = st0_ptr->sigh;
+
+ verify_area((void *)single,4);
+
+ if (st0_ptr->tag == TW_Valid)
+ {
+ if ( ((sigh & 0xff) > 0x80) /* more than half */
+ || ((sigh & 0x180) == 0x180) ) /* round to even */
+ {
+ /* Round up */
+ if ( sigh >= 0xffffff00 )
+ {
+ /* sigh would overflow */
+ exp++;
+ sigh = 0x80000000;
+ }
+ else
+ {
+ sigh += 0x100;
+ }
+ }
+ templ = (sigh >> 8) & 0x007fffff;
+ if ( exp > SINGLE_Emax )
+ {
+ EXCEPTION(EX_Overflow);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Overflow )
+ {
+ /* Overflow to infinity */
+ templ = 0x7f800000;
+ }
+ else
+ return 0;
+ }
+ else if ( exp < SINGLE_Emin )
+ {
+ if ( exp > SINGLE_Emin-24 )
+ {
+ /* Make a de-normal */
+ REG tmp;
+ EXCEPTION(EX_Denormal);
+ reg_move(st0_ptr, &tmp);
+ tmp.exp += SINGLE_Emin + 53; /* largest exp to be 52 */
+ round_to_int(&tmp);
+ templ = tmp.sigl;
+ }
+ else
+ {
+ EXCEPTION(EX_Underflow);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Underflow )
+ {
+ /* Underflow to zero */
+ templ = 0;
+ }
+ else
+ return 0;
+ }
+ }
+ else
+ templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
+ }
+ else if (st0_ptr->tag == TW_Zero)
+ {
+ templ = 0;
+ }
+ else if (st0_ptr->tag == TW_Infinity)
+ {
+ templ = 0x7f800000;
+ }
+ else if (st0_ptr->tag == TW_NaN)
+ {
+ /* See if we can get a valid NaN from the REG */
+ templ = st0_ptr->sigh >> 8;
+ if ( !(templ & 0x3fffff) )
+ {
+ /* This case does not seem to be handled by the 80486 specs */
+ EXCEPTION(EX_Invalid);
+ /* Make the quiet NaN "real indefinite" */
+ goto put_indefinite;
+ }
+ templ |= 0x7f800000;
+ }
+ else if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+put_indefinite:
+ put_fs_long(0xffc00000, (unsigned long *) single);
+ return 1;
+ }
+ else
+ return 0;
+ }
+ else if (st0_ptr->tag == TW_Denormal)
+ {
+ /* Extended real -> real will always underflow */
+ templ = 0;
+ EXCEPTION(EX_Underflow);
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL|0x106);
+ return 0;
+ }
+#endif
+ if (st0_ptr->sign)
+ templ |= 0x80000000;
+
+ put_fs_long(templ,(unsigned long *) single);
+
+ return 1;
+}
+
+
+/* Put a long long into user memory */
+int reg_store_int64(void)
+{
+ long long *d = (long long *)FPU_data_address;
+ REG t;
+ long long tll;
+
+ verify_area((void *)d,8);
+ if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ goto put_indefinite;
+ }
+ else
+ return 0;
+ }
+
+ reg_move(st0_ptr, &t);
+ round_to_int(&t);
+ ((long *)&tll)[0] = t.sigl;
+ ((long *)&tll)[1] = t.sigh;
+ if ( t.sigh & 0x80000000 )
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Invalid )
+ {
+ /* Produce "indefinite" */
+put_indefinite:
+ ((long *)&tll)[1] = 0x80000000;
+ ((long *)&tll)[0] = 0;
+ }
+ else
+ return 0;
+ }
+ else if (t.sign)
+ tll = - tll;
+
+ put_fs_long(((long *)&tll)[0],(unsigned long *) d);
+ put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
+
+ return 1;
+}
+
+
+/* Put a long into user memory */
+int reg_store_int32(void)
+{
+ long *d = (long *)FPU_data_address;
+ REG t;
+ long tl;
+
+ verify_area(d,4);
+ if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ put_fs_long(0x80000000, (unsigned long *) d);
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ reg_move(st0_ptr, &t);
+ round_to_int(&t);
+ if (t.sigh || (t.sigl & 0x80000000))
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Invalid )
+ {
+ /* Produce "indefinite" */
+ tl = 0x80000000;
+ }
+ else
+ return 0;
+ }
+ else
+ tl = st0_ptr->sign ? -t.sigl : t.sigl;
+
+ put_fs_long(tl, (unsigned long *) d);
+
+ return 1;
+}
+
+
+/* Put a short into user memory */
+int reg_store_int16(void)
+{
+ short *d = (short *)FPU_data_address;
+ REG t;
+ short ts;
+
+ verify_area(d,2);
+ if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ put_fs_word(0x8000, (unsigned short *) d);
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ reg_move(st0_ptr, &t);
+ round_to_int(&t);
+ if (t.sigh || (t.sigl & 0xFFFF8000))
+ {
+ EXCEPTION(EX_Invalid);
+ /* This is a special case: see sec 16.2.5.1 of the 80486 book */
+ if ( control_word & EX_Invalid )
+ {
+ /* Produce "indefinite" */
+ ts = 0x8000;
+ }
+ else
+ return 0;
+ }
+ else
+ ts = st0_ptr->sign ? -t.sigl : t.sigl;
+
+ put_fs_word(ts,(short *) d);
+
+ return 1;
+}
+
+
+/* Put a packed bcd array into user memory */
+int reg_store_bcd(void)
+{
+ char *d = (char *)FPU_data_address;
+ REG t;
+ long long ll;
+ unsigned char b;
+ int i;
+
+ verify_area(d,10);
+ if ( st0_ptr->tag == TW_Empty )
+ {
+ /* Empty register (stack underflow) */
+ EXCEPTION(EX_StackUnder);
+ if ( control_word & EX_Invalid )
+ {
+ /* The masked response */
+ /* Put out the QNaN indefinite */
+ goto put_indefinite;
+ }
+ else
+ return 0;
+ }
+
+ reg_move(st0_ptr, &t);
+ round_to_int(&t);
+ ll = *(long long *)(&t.sigl);
+
+ /* 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 */
+ if ( control_word & EX_Invalid )
+ {
+put_indefinite:
+ /* Produce "indefinite" */
+ put_fs_byte(0xff,(unsigned char *) d+7);
+ put_fs_byte(0xff,(unsigned char *) d+8);
+ put_fs_byte(0xff,(unsigned char *) d+9);
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ for ( i = 0; i < 9; i++)
+ {
+ b = div_small(&ll, 10);
+ b |= (div_small(&ll, 10)) << 4;
+ put_fs_byte(b,(unsigned char *) d+i);
+ }
+ if (st0_ptr->sign == SIGN_NEG)
+ put_fs_byte(0x80,(unsigned char *) d+9);
+ else
+ put_fs_byte(0,(unsigned char *) d+9);
+
+ return 1;
+}
+
+/*===========================================================================*/
+
+/* r gets mangled such that sig is int, sign:
+ it is NOT normalized*/
+/* Overflow is signalled by a non-zero return value (in eax).
+ In the case of overflow, the returned significand always has the
+ the largest possible value */
+/* The value returned in eax is never actually needed :-) */
+int round_to_int(REG *r)
+{
+ char very_big;
+ unsigned eax;
+
+ if (r->tag == TW_Zero)
+ {
+ /* Make sure that zero is returned */
+ *(long long *)&r->sigl = 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 */
+ (*(long long *)(&r->sigl)) ++;
+ }
+ break;
+ case RC_DOWN:
+ if (frac_part && r->sign)
+ {
+ if ( very_big ) return 1; /* overflow */
+ (*(long long *)(&r->sigl)) ++;
+ }
+ break;
+ case RC_UP:
+ if (frac_part && !r->sign)
+ {
+ if ( very_big ) return 1; /* overflow */
+ (*(long long *)(&r->sigl)) ++;
+ }
+ break;
+ case RC_CHOP:
+ break;
+ }
+
+ return 0; /* o.k. */
+}
+
+/*===========================================================================*/
+
+char *fldenv(void)
+{
+ char *s = (char *)FPU_data_address;
+ unsigned short tag_word = 0;
+ unsigned char tag;
+ int i;
+
+ control_word = get_fs_word((unsigned short *) s);
+ status_word = get_fs_word((unsigned short *) (s+4));
+ tag_word = get_fs_word((unsigned short *) (s+8));
+ ip_offset = get_fs_long((unsigned long *) (s+0x0c));
+ cs_selector = get_fs_long((unsigned long *) (s+0x10));
+ data_operand_offset = get_fs_long((unsigned long *) (s+0x14));
+ operand_selector = get_fs_long((unsigned long *) (s+0x18));
+
+
+ for ( i = 7; i >= 0; i-- )
+ {
+ tag = tag_word & 3;
+ tag_word <<= 2;
+
+ switch ( tag )
+ {
+ case 0:
+ regs[i].tag = TW_Valid;
+ break;
+ case 1:
+ regs[i].tag = TW_Zero;
+ break;
+ case 2:
+ regs[i].tag = TW_NaN;
+ break;
+ case 3:
+ regs[i].tag = TW_Empty;
+ break;
+ }
+ }
+
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+ FPU_entry_eip = ip_offset; /* We want no net effect */
+
+ return s + 0x1c;
+}
+
+
+void frstor(void)
+{
+ int i;
+ unsigned char tag;
+ REG *s = (REG *)fldenv();
+
+ for ( i = 0; i < 8; i++ )
+ {
+ /* load each register */
+ FPU_data_address = (void *)&(s[i]);
+ reg_load_extended();
+ tag = regs[i].tag;
+ reg_move(&FPU_loaded_data, &regs[i]);
+ if ( tag == TW_NaN )
+ {
+ unsigned char t = regs[i].tag;
+ if ( (t == TW_Valid) || (t == TW_Zero) )
+ regs[i].tag = TW_NaN;
+ }
+ else
+ regs[i].tag = tag;
+ }
+
+ FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
+}
+
+
+char *fstenv(void)
+{
+ char *d = (char *)FPU_data_address;
+ unsigned short tag_word = 0;
+ unsigned char tag;
+ int i;
+
+ verify_area(d,28);
+
+ for ( i = 7; i >= 0; i-- )
+ {
+ switch ( tag = regs[i].tag )
+ {
+ case TW_Denormal:
+ case TW_Infinity:
+ case TW_NaN:
+ tag = 2;
+ break;
+ case TW_Empty:
+ tag = 3;
+ break;
+ /* TW_Valid and TW_Zero already have the correct value */
+ }
+ tag_word <<= 2;
+ tag_word |= tag;
+ }
+
+ /* This is not what should be done ... but saves overheads. */
+ *(unsigned short *)&cs_selector = FPU_CS;
+ *(unsigned short *)&operand_selector = FPU_DS;
+
+ 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));
+ put_fs_long(ip_offset, (unsigned long *) (d+0x0c));
+ put_fs_long(cs_selector, (unsigned long *) (d+0x10));
+ put_fs_long(data_operand_offset, (unsigned long *) (d+0x14));
+ put_fs_long(operand_selector, (unsigned long *) (d+0x18));
+
+ return d + 0x1c;
+}
+
+
+void fsave(void)
+{
+ char *d;
+ REG tmp, *rp;
+ int i;
+ short e;
+
+ d = fstenv();
+ verify_area(d,80);
+ for ( i = 0; i < 8; i++ )
+ {
+ /* store each register */
+ rp = &regs[i];
+
+ e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
+
+ if ( st0_ptr->tag == TW_Valid )
+ {
+ if ( e >= 0x7fff )
+ {
+ /* Overflow to infinity */
+ put_fs_long(0, (unsigned long *) (d+i*10+2));
+ put_fs_long(0x80000000, (unsigned long *) (d+i*10+6));
+ e = 0x7fff;
+ }
+ else if ( e <= 0 )
+ {
+ if ( e == 0 )
+ {
+ /* Pseudo de-normal */
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ }
+ else if ( e > -64 )
+ {
+ /* Make a de-normal */
+ reg_move(rp, &tmp);
+ tmp.exp += EXTENDED_Emin + 64; /* largest exp to be 62 */
+ round_to_int(&tmp);
+ e = 0;
+ put_fs_long(tmp.sigl, (unsigned long *) (d+i*10+2));
+ put_fs_long(tmp.sigh, (unsigned long *) (d+i*10+6));
+ }
+ else
+ {
+ /* Underflow to zero */
+ put_fs_long(0, (unsigned long *) (d+i*10+2));
+ put_fs_long(0, (unsigned long *) (d+i*10+6));
+ e = 0;
+ }
+ }
+ else
+ {
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ }
+ }
+ else if ( st0_ptr->tag == TW_Zero )
+ {
+ put_fs_long(0, (unsigned long *) (d+i*10+2));
+ put_fs_long(0, (unsigned long *) (d+i*10+6));
+ e = 0;
+ }
+ else if ( st0_ptr->tag == TW_Infinity )
+ {
+ put_fs_long(0, (unsigned long *) (d+i*10+2));
+ put_fs_long(0x80000000, (unsigned long *) (d+i*10+6));
+ e = 0x7fff;
+ }
+ else if ( st0_ptr->tag == TW_NaN )
+ {
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ e = 0x7fff;
+ }
+ else if ( st0_ptr->tag == TW_Empty )
+ {
+ /* just copy the reg */
+ put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
+ put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
+ }
+ put_fs_word(e, (unsigned short *) (d+i*10));
+ }
+
+}
+
+/*===========================================================================*/
diff --git a/kernel/FPU-emu/reg_mul.c b/kernel/FPU-emu/reg_mul.c
new file mode 100644
index 0000000..321d9ea
--- /dev/null
+++ b/kernel/FPU-emu/reg_mul.c
@@ -0,0 +1,85 @@
+/*---------------------------------------------------------------------------+
+ | reg_mul.c |
+ | |
+ | Multiply one REG by another and put the result in a destination REG. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | The destination may be any REG, including one of the source REGs. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "reg_constant.h"
+#include "fpu_emu.h"
+
+
+/* This routine must be called with non-empty registers */
+void reg_mul(REG *a, REG *b, REG *dest)
+{
+ if (!(a->tag | b->tag))
+ {
+ /* This should be the most common case */
+ reg_u_mul(a, b, dest);
+ dest->exp += - EXP_BIAS + 1;
+ dest->sign = (a->sign ^ b->sign);
+ if ( dest->exp <= EXP_UNDER )
+ { arith_underflow(st0_ptr); }
+ else if ( dest->exp >= EXP_OVER )
+ { arith_overflow(st0_ptr); }
+ else
+ dest->tag = TW_Valid;
+ return;
+ }
+ else if ((a->tag <= TW_Zero) && (b->tag <= TW_Zero))
+ {
+ /* Must have either both arguments == zero, or
+ one valid and the other zero.
+ The result is therefore zero. */
+ reg_move(&CONST_Z, dest);
+ }
+ else if ((a->tag <= TW_Denormal) && (b->tag <= TW_Denormal))
+ {
+ /* One or both arguments are de-normalized */
+ /* Internal de-normalized numbers are not supported yet */
+ EXCEPTION(EX_INTERNAL|0x105);
+ reg_move(&CONST_Z, dest);
+ }
+ else
+ {
+ /* Must have infinities, NaNs, etc */
+ if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
+ { real_2op_NaN(a, b, dest); return; }
+ else if (a->tag == TW_Infinity)
+ {
+ if (b->tag == TW_Zero)
+ { arith_invalid(dest); return; }
+ else
+ {
+ reg_move(a, dest);
+ dest->sign = a->sign == b->sign ? SIGN_POS : SIGN_NEG;
+ }
+ }
+ else if (b->tag == TW_Infinity)
+ {
+ if (a->tag == TW_Zero)
+ { arith_invalid(dest); return; }
+ else
+ {
+ reg_move(b, dest);
+ dest->sign = a->sign == b->sign ? SIGN_POS : SIGN_NEG;
+ }
+ }
+#ifdef PARANOID
+ else
+ {
+ EXCEPTION(EX_INTERNAL|0x102);
+ }
+#endif PARANOID
+ dest->sign = (a->sign ^ b->sign);
+ }
+}
diff --git a/kernel/FPU-emu/reg_norm.S b/kernel/FPU-emu/reg_norm.S
new file mode 100644
index 0000000..c7377c5
--- /dev/null
+++ b/kernel/FPU-emu/reg_norm.S
@@ -0,0 +1,84 @@
+/*---------------------------------------------------------------------------+
+ | reg_norm.S |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Normalize the value in a REG. |
+ | |
+ | Call from C as: |
+ | void normalize(REG *n) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_asm.h"
+
+
+.text
+
+ .align 2,144
+.globl _normalize
+
+_normalize:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %ebx
+
+ movl PARAM1,%ebx
+
+ 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
+
+L_shift_32:
+ 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
diff --git a/kernel/FPU-emu/reg_u_add.S b/kernel/FPU-emu/reg_u_add.S
new file mode 100644
index 0000000..c8d58fa
--- /dev/null
+++ b/kernel/FPU-emu/reg_u_add.S
@@ -0,0 +1,186 @@
+ .file "reg_u_add.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_add.S |
+ | |
+ | Add two valid (TW_Valid) REG numbers, of the same sign, and put result in |
+ | a destination REG. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void reg_u_add(reg *arg1, reg *arg2, reg *answ) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*
+ | 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"
+
+.text
+ .align 2,144
+.globl _reg_u_add
+_reg_u_add:
+ pushl %ebp
+ movl %esp,%ebp
+// subl $16,%esp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* source 1 */
+ movl PARAM2,%edi /* source 2 */
+
+ xorl %ecx,%ecx
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+// jnc L_arg1_larger
+ 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 16(%ebp),%edi /* destination */
+
+ 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
+
+ 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
+ shrd %cl,%eax,%edx
+ shr %cl,%eax
+ movl %eax,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpw $66,%cx
+ jnc L_more_than_65
+
+ subb $64,%cl
+ movl %eax,%edx
+ shr %cl,%edx
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_65:
+ /* just copy the larger reg to dest */
+ movw SIGN(%esi),%ax
+ movw %ax,SIGN(%edi)
+ movl SIGL(%esi),%eax
+ movl %eax,SIGL(%edi)
+ movl SIGH(%esi),%eax
+ movl %eax,SIGH(%edi)
+ jmp L_exit // Does not overflow
+
+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
+
+ incl EXP(%edi)
+
+L_round_the_result:
+ /* Round the result */
+ cmpl $0x80000000,%edx
+ jc L_no_round_up
+
+/* Check the rounding algorithm *********/
+ jne L_do_round_up
+
+ testb $1,%ebx
+ jz L_no_round_up
+
+L_do_round_up:
+ addl $1,%ebx
+ adcl $0,%eax
+ jnc L_no_round_up
+
+ /* Overflow, adjust the result */
+ rcrl $1,%eax
+ rcrl $1,%ebx
+
+L_no_round_up:
+ /* store the result */
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+ movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */
+ movb SIGN(%esi),%al
+ movb %al,SIGN(%edi) /* Copy the sign from the first arg */
+
+ // The number may have overflowed
+ cmpl EXP_OVER,EXP(%edi)
+ jge L_overflow
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+/* The addition resulted in a number too large to represent */
+L_overflow:
+ push %edi
+ call _arith_overflow
+ pop %ebx
+ jmp L_exit
+
+
+#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
diff --git a/kernel/FPU-emu/reg_u_div.S b/kernel/FPU-emu/reg_u_div.S
new file mode 100644
index 0000000..6599f7d
--- /dev/null
+++ b/kernel/FPU-emu/reg_u_div.S
@@ -0,0 +1,475 @@
+ .file "reg_u_div.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_div.S |
+ | |
+ | Core division routines |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Kernel for the division routines. |
+ | |
+ | void reg_u_div(unsigned long long *a, unsigned long long *a, REG *dest) |
+ | |
+ | Does not compute the destination exponent, but does adjust it. |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+#define dSIGL(x) (x)
+#define dSIGH(x) 4(x)
+
+
+.data
+/*
+ Local storage:
+ Result: accum_3:accum_2:accum_1:accum_0
+ Overflow flag: ovfl_flag
+ */
+ .align 2,0
+accum_3:
+ .long 0
+accum_2:
+ .long 0
+accum_1:
+ .long 0
+accum_0:
+ .long 0
+result_1:
+ .long 0
+result_2:
+ .long 0
+ovfl_flag:
+ .byte 0
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_div
+
+.globl _divide_kernel
+
+_reg_u_div:
+ pushl %ebp
+ movl %esp,%ebp
+
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi /* pointer to num */
+ movl PARAM2,%ebx /* pointer to denom */
+ movl PARAM3,%edi /* pointer to answer */
+
+_divide_kernel:
+#ifdef PARANOID
+// testl $0x80000000, dSIGH(%esi)
+// je xL_bugged
+ testl $0x80000000, dSIGH(%ebx)
+ je xL_bugged
+#endif PARANOID
+
+/* Check if the denominator can be treated as having just 32 bits */
+ cmpl $0,dSIGL(%ebx)
+ jnz L_Full_Division /* Can't do a quick divide */
+
+/* We should be able to zip through the division here */
+ movl dSIGH(%ebx),%ecx /* The denominator */
+ movl dSIGH(%esi),%edx /* Get the current num */
+ movl dSIGL(%esi),%eax /* Get the current num */
+
+ cmpl %ecx,%edx
+ setaeb ovfl_flag /* Keep a record */
+ jb xL_no_adjust
+
+ subl %ecx,%edx /* Prevent the overflow */
+
+xL_no_adjust:
+ /* Divide the 64 bit number by the 32 bit denominator */
+ divl %ecx
+ movl %eax,SIGH(%edi) /* Put the result in the answer */
+
+ /* Work on the remainder of the first division */
+ xorl %eax,%eax
+ divl %ecx
+ movl %eax,SIGL(%edi) /* Put the result in the answer */
+
+ /* Work on the remainder of the 64 bit division */
+ xorl %eax,%eax
+ divl %ecx
+
+ testb $255,ovfl_flag /* was the num > denom ? */
+ je xL_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 SIGH(%edi)
+ rcrl SIGL(%edi)
+ rcrl %eax
+
+xL_no_overflow:
+ cmpl $0x80000000,%eax
+ jc xL_no_round
+ jnz xL_round_up
+
+ /* "round to even" used */
+ testb $1,SIGL(%edi)
+ jz xL_no_round
+
+xL_round_up:
+ addl $1,SIGL(%edi)
+ adcl $0,SIGH(%edi)
+
+#ifdef PARANOID
+ jc xL_bugged2
+#endif PARANOID
+
+xL_no_round:
+ jmp xL_done
+
+
+#ifdef PARANOID
+xL_bugged:
+ pushl EX_INTERNAL|0x203
+ call EXCEPTION
+ pop %ebx
+ jmp xL_exit
+
+xL_bugged2:
+ pushl EX_INTERNAL|0x204
+ call EXCEPTION
+ pop %ebx
+ jmp xL_exit
+#endif PARANOID
+
+
+/*---------------------------------------------------------------------------+
+ | 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:
+ movl dSIGL(%esi),%eax /* Save extended num in local register */
+ movl %eax,accum_2
+ movl dSIGH(%esi),%eax
+ movl %eax,accum_3
+ xorl %eax,%eax
+ movl %eax,accum_1 /* zero the extension */
+ movl %eax,accum_0 /* zero the extension */
+
+ movl dSIGL(%esi),%eax /* Get the current num */
+ movl dSIGH(%esi),%edx
+
+/*----------------------------------------------------------------------*/
+/* Initialization done */
+/* Do the first 32 bits */
+
+ movb $0,ovfl_flag
+ cmpl dSIGH(%ebx),%edx /* Test for imminent overflow */
+ jb L02
+ ja L01
+
+ cmpl dSIGL(%ebx),%eax
+ jb L02
+
+L01:
+/* The numerator is greater or equal, would cause overflow */
+ setaeb ovfl_flag /* Keep a record */
+
+ subl dSIGL(%ebx),%eax
+ sbbl dSIGH(%ebx),%edx /* Prevent the overflow */
+ movl %eax,accum_2
+ movl %edx,accum_3
+
+L02:
+/* At this point, we have a num < denom, with a record of
+ adjustment in ovfl_flag */
+
+ /* We will divide by a number which is too large */
+ movl dSIGH(%ebx),%ecx
+ addl $1,%ecx
+ jnc L04
+
+ /* here we need to divide by 100000000h,
+ i.e., no division at all.. */
+
+ mov %edx,%eax
+ jmp L05
+
+L04:
+ divl %ecx /* Divide the numerator by the augmented
+ denom ms dw */
+
+L05:
+ movl %eax,result_2 /* Put the result in the answer */
+
+ mull dSIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,accum_2 /* Subtract from the num local reg */
+ sbbl %edx,accum_3
+
+ movl result_2,%eax /* Get the result back */
+ mull dSIGL(%ebx) /* now mul the ls dw of the denom */
+
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+ sbbl $0,accum_3
+ je L10 /* Must check for non-zero result here */
+
+#ifdef PARANOID
+ jb L_bugged
+#endif PARANOID
+
+ /* need to subtract another once of the denom */
+ incl result_2 /* Correct the answer */
+
+ movl dSIGL(%ebx),%eax
+ movl dSIGH(%ebx),%edx
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+
+#ifdef PARANOID
+ sbbl $0,accum_3
+ jne L_bugged /* 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, accum_0 not used from now on */
+L10:
+ movl accum_2,%edx /* get the reduced num */
+ movl accum_1,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl dSIGH(%ebx),%edx
+ jb L22
+ ja L21
+
+ cmpl dSIGL(%ebx),%eax
+ jb L22
+
+L21:
+/* The numerator is greater or equal, would cause overflow */
+ /* prevent overflow */
+ subl dSIGL(%ebx),%eax
+ sbbl dSIGH(%ebx),%edx
+ movl %edx,accum_2
+ movl %eax,accum_1
+
+ incl result_2 /* Reflect the subtraction in the answer */
+
+#ifdef PARANOID
+ je L_bugged
+#endif PARANOID
+
+L22:
+ cmpl $0,%ecx
+ jnz L24
+
+ /* %ecx == 0, we are dividing by 1.0 */
+ mov %edx,%eax
+ jmp L25
+
+L24:
+ divl %ecx /* Divide the numerator by the denom ms dw */
+
+L25:
+ movl %eax,result_1 /* Put the result in the answer */
+
+ mull dSIGH(%ebx) /* mul by the ms dw of the denom */
+
+ subl %eax,accum_1 /* Subtract from the num local reg */
+ sbbl %edx,accum_2
+
+#ifdef PARANOID
+ jc L_bugged
+#endif PARANOID
+
+ movl result_1,%eax /* Get the result back */
+ mull dSIGL(%ebx) /* now mul the ls dw of the denom */
+
+ /* Here we are throwing away some ls bits */
+ subl %eax,accum_0 /* Subtract from the num local reg */
+ sbbl %edx,accum_1 /* Subtract from the num local reg */
+ sbbl $0,accum_2
+
+#ifdef PARANOID
+ jc L_bugged
+#endif PARANOID
+
+ jz L35 /* Just deal with rounding now */
+
+#ifdef PARANOID
+ cmpl $1,accum_2
+ jne L_bugged
+#endif PARANOID
+
+L32:
+ /* need to subtract another once of the denom */
+ movl dSIGL(%ebx),%eax
+ movl dSIGH(%ebx),%edx
+ subl %eax,accum_0 /* Subtract from the num local reg */
+ sbbl %edx,accum_1
+ sbbl $0,accum_2
+
+#ifdef PARANOID
+ jc L_bugged
+ jne L_bugged
+#endif PARANOID
+
+ addl $1,result_1 /* Correct the answer */
+ adcl $0,result_2
+
+#ifdef PARANOID
+ /* Do we ever really need this check? ***** */
+ jc L_bugged /* 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 */
+L35:
+ movl accum_1,%edx /* get the reduced num */
+ movl accum_0,%eax
+
+ /* need to check for possible subsequent overflow */
+ cmpl dSIGH(%ebx),%edx
+ jb L42
+ ja L41
+
+ cmpl dSIGL(%ebx),%eax
+ jb L42
+
+L41:
+ /* prevent overflow */
+ subl dSIGL(%ebx),%eax
+ sbbl dSIGH(%ebx),%edx
+ movl %edx,accum_1
+ movl %eax,accum_0 /* ***** not needed unless extended rounding */
+
+ addl $1,result_1 /* Reflect the subtraction in the answer */
+ adcl $0,result_2
+ jne L42
+ jnc L42
+
+ /* This is a tricky spot, there is an overflow of the answer */
+ movb $255,ovfl_flag /* Overflow -> 1.000 */
+
+L42:
+ /* Now test for rounding */
+ movl accum_1,%edx /* ms byte of accum extension */
+
+L44:
+ cmpl $0,%ecx
+ jnz L241
+
+ /* %ecx == 0, we are dividing by 1.0 */
+ mov %edx,%eax
+ jmp L251
+
+L241:
+ divl %ecx /* Divide the numerator by the denom ms dw */
+
+L251:
+/* We are now ready to deal with rounding, but first we must get
+ the bits properly aligned */
+ testb $255,ovfl_flag /* was the num > denom ? */
+ je L45
+
+ incl EXP(%edi)
+
+ /* shift the mantissa right one bit */
+ stc // Will set the ms bit
+ rcrl result_2
+ rcrl result_1
+ rcrl %eax
+
+L45:
+ cmpl $0x80000000,%eax
+ jc xL_no_round_2 // No round up
+ jnz xL_round_up_2
+
+ /* "round to even" used here for now... */
+ testb $1,result_1
+ jz xL_no_round_2 // No round up
+
+xL_round_up_2:
+ addl $1,result_1
+ adcl $0,result_2
+
+#ifdef PARANOID
+ jc L_bugged
+#endif PARANOID
+
+xL_no_round_2:
+ movl result_1,%eax
+ movl %eax,SIGL(%edi)
+ movl result_2,%eax
+ movl %eax,SIGH(%edi)
+
+xL_done:
+ decl EXP(%edi) /* binary point between 1st & 2nd bits */
+
+xL_check_exponent:
+ cmpl EXP_OVER,EXP(%edi)
+ jge xL_overflow
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle xL_underflow
+
+xL_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+
+ leave
+ ret
+
+
+xL_overflow:
+ pushl %edi
+ call _arith_overflow
+ popl %ebx
+ jmp xL_exit
+
+xL_underflow:
+ pushl %edi
+ call _arith_underflow
+ popl %ebx
+ jmp xL_exit
+
+
+#ifdef PARANOID
+/* The logic is wrong if we got here */
+L_bugged:
+ pushl EX_INTERNAL|0x202
+ call EXCEPTION
+ pop %ebx
+ jmp xL_exit
+#endif PARANOID
diff --git a/kernel/FPU-emu/reg_u_mul.S b/kernel/FPU-emu/reg_u_mul.S
new file mode 100644
index 0000000..71f0289
--- /dev/null
+++ b/kernel/FPU-emu/reg_u_mul.S
@@ -0,0 +1,137 @@
+ .file "reg_u_mul.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_mul.S |
+ | |
+ | Core multiplication routine |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | Basic multiplication routine. |
+ | Does not check the resulting exponent for overflow/underflow |
+ | |
+ | Internal working is at approx 96 bits. |
+ | Result is rounded to nearest 64 bits, using "nearest or even". |
+ +---------------------------------------------------------------------------*/
+
+#include "exception.h"
+#include "fpu_asm.h"
+
+
+
+.data
+ .align 2,0
+accum_1:
+ .long 0
+
+
+.text
+ .align 2,144
+
+.globl _reg_u_mul
+_reg_u_mul:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %esi
+ pushl %edi
+ pushl %ebx
+
+ movl PARAM1,%esi
+ movl PARAM2,%ecx
+
+#ifdef PARANOID
+ testl $0x80000000,SIGH(%esi)
+ jz xL_bugged
+ testl $0x80000000,SIGH(%ecx)
+ jz xL_bugged
+#endif PARANOID
+
+ xorl %edi,%edi
+ xorl %ebx,%ebx
+
+ movl SIGL(%esi),%eax
+ mull SIGL(%ecx)
+// movl %eax,accum_0
+ movl %edx,accum_1
+
+ movl SIGL(%esi),%eax
+ mull SIGH(%ecx)
+ addl %eax,accum_1
+ adcl %edx,%ebx
+// adcl $0,%edi // overflow here is not possible
+
+ movl SIGH(%esi),%eax
+ mull SIGL(%ecx)
+ addl %eax,accum_1
+ adcl %edx,%ebx
+ adcl $0,%edi
+
+ movl SIGH(%esi),%eax
+ mull SIGH(%ecx)
+ addl %eax,%ebx
+ adcl %edx,%edi
+
+ movl EXP(%esi),%eax /* Compute the exponent */
+ addl EXP(%ecx),%eax
+// Have now finished with the sources
+ movl PARAM3,%esi // Point to the destination
+ movl %eax,EXP(%esi)
+
+// Now make sure that the result is normalized
+ testl $0x80000000,%edi
+ jnz L20
+
+ /* Normalize by shifting left one bit */
+// shll $1,accum_0 // If using this, change next to rcll
+ shll $1,accum_1
+ rcll $1,%ebx
+ rcll $1,%edi
+ decl EXP(%esi)
+
+L20:
+ /* Do the rounding */
+ cmpl $0x80000000,accum_1
+ jc L40
+
+ jne L30
+
+ /* 0x80000000, round up only if previous bit is 1 */
+ testl $1,%ebx
+ jz L40
+
+L30:
+ addl $1,%ebx
+ adcl $0,%edi
+
+ /* An overflow can occur here (rare!) */
+ jc xL_overflow_adjust
+
+L40:
+ /* Copy the result to the destination register */
+ movl %ebx,SIGL(%esi)
+ movl %edi,SIGH(%esi)
+
+xL_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+xL_overflow_adjust:
+ rcrl %edi
+ incl EXP(%esi)
+ jmp L40
+
+#ifdef PARANOID
+xL_bugged:
+ pushl EX_INTERNAL|0x205
+ call EXCEPTION
+ pop %ebx
+ jmp xL_exit
+#endif PARANOID
diff --git a/kernel/FPU-emu/reg_u_sub.S b/kernel/FPU-emu/reg_u_sub.S
new file mode 100644
index 0000000..24af560
--- /dev/null
+++ b/kernel/FPU-emu/reg_u_sub.S
@@ -0,0 +1,274 @@
+ .file "reg_u_sub.S"
+/*---------------------------------------------------------------------------+
+ | reg_u_sub.S |
+ | |
+ | Core floating point subtraction routine. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void reg_u_sub(reg *arg1, reg *arg2, reg *answ) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*
+ | 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"
+
+
+.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 */
+
+// xorl %ecx,%ecx
+ movl EXP(%esi),%ecx
+ subl EXP(%edi),%ecx /* exp1 - exp2 */
+
+#ifdef PARANOID
+ /* source 2 is always smaller than source 1 */
+// jc L_bugged
+ 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 */
+
+ 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
+ shrd %cl,%eax,%edx
+ movl %eax,%ebx
+ shr %cl,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_63:
+ cmpl $66,%ecx
+ jnc L_more_than_65
+
+ subb $64,%cl
+ movl %eax,%edx
+ shr %cl,%edx
+ xorl %ebx,%ebx
+ xorl %eax,%eax
+ jmp L_shift_done
+
+L_more_than_65:
+ /* just copy the larger reg to dest */
+ movw SIGN(%esi),%ax
+ movw %ax,SIGN(%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)
+ jmp L_exit // Does not underflow
+
+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)
+ movl %edx,%eax
+ jmp L_store
+
+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 // Does not underflow
+
+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:
+/*------------------------------+
+ | Round the result |
+ +------------------------------*/
+ cmpl $0x80000000,%edx
+ jc L_store
+
+ jne L_round_up
+
+ testb $1,%dl
+ jz L_store
+
+L_round_up:
+ addl $1,%ebx
+ adcl $0,%eax
+
+#ifdef PARANOID
+ /* We can show that an overflow here is not possible */
+ jc L_bugged_4
+#endif PARANOID
+
+L_store:
+/*------------------------------+
+ | Store the result |
+ +------------------------------*/
+ movl %eax,SIGH(%edi)
+ movl %ebx,SIGL(%edi)
+
+ movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */
+
+ cmpl EXP_UNDER,EXP(%edi)
+ jle L_underflow
+
+L_exit:
+ popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+
+L_underflow:
+ push %edi
+ call _arith_underflow
+ pop %ebx
+ jmp L_exit
+
+
+#ifdef PARANOID
+L_bugged_1:
+ pushl EX_INTERNAL|0x206
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_2:
+ pushl EX_INTERNAL|0x209
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_3:
+ pushl EX_INTERNAL|0x210
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged_4:
+ pushl EX_INTERNAL|0x211
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+
+L_bugged:
+ pushl EX_INTERNAL|0x212
+ call EXCEPTION
+ pop %ebx
+ jmp L_exit
+#endif PARANOID
diff --git a/kernel/FPU-emu/status_w.h b/kernel/FPU-emu/status_w.h
new file mode 100644
index 0000000..d7f23c0
--- /dev/null
+++ b/kernel/FPU-emu/status_w.h
@@ -0,0 +1,51 @@
+/*---------------------------------------------------------------------------+
+ | status_w.h |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+
+#ifdef __ASSEMBLER__
+#define Const__(x) $##x
+#else
+#define Const__(x) x
+#endif
+
+#define SW_B Const__(0x8000) /* backward compatibility (=ES) */
+#define SW_C3 Const__(0x4000) /* condition bit 3 */
+#define SW_TOP Const__(0x3800) /* top of stack */
+#define SW_TOPS 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_ES Const__(0x0080) /* exception summary */
+#define SW_SF Const__(0x0040) /* stack fault */
+#define SW_PE Const__(0x0020) /* loss of precision */
+#define SW_UE Const__(0x0010) /* underflow */
+#define SW_OE Const__(0x0008) /* overflow */
+#define SW_ZE Const__(0x0004) /* divide by zero */
+#define SW_DE Const__(0x0002) /* denormalized operand */
+#define SW_IE Const__(0x0001) /* invalid operation */
+
+
+#ifndef __ASSEMBLER__
+
+#define COMP_A_GT_B 1
+#define COMP_A_EQ_B 2
+#define COMP_A_LT_B 3
+#define COMP_NOCOMP 4
+#define COMP_NAN 0x40
+#define COMP_SNAN 0x80
+
+#define setcc(cc) ({ \
+ status_word &= ~(SW_C0|SW_C1|SW_C2|SW_C3); \
+ status_word |= (cc) & (SW_C0|SW_C1|SW_C2|SW_C3); })
+
+#endif __ASSEMBLER__
+
+#endif _STATUS_H_
diff --git a/kernel/FPU-emu/version.h b/kernel/FPU-emu/version.h
new file mode 100644
index 0000000..7b8db07
--- /dev/null
+++ b/kernel/FPU-emu/version.h
@@ -0,0 +1,12 @@
+/*---------------------------------------------------------------------------+
+ | version.h |
+ | |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#define FPU_VERSION "wm-FPU-emu version ALPHA 0.5"
+
diff --git a/kernel/FPU-emu/wm_shrx.S b/kernel/FPU-emu/wm_shrx.S
new file mode 100644
index 0000000..f0cd40f
--- /dev/null
+++ b/kernel/FPU-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 apm233m@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/kernel/FPU-emu/wm_sqrt.S b/kernel/FPU-emu/wm_sqrt.S
new file mode 100644
index 0000000..2ce3af6
--- /dev/null
+++ b/kernel/FPU-emu/wm_sqrt.S
@@ -0,0 +1,277 @@
+ .file "wm_sqrt.S"
+/*---------------------------------------------------------------------------+
+ | wm_sqrt.S |
+ | |
+ | Fixed point arithmetic square root evaluation. |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | Call from C as: |
+ | void wm_sqrt(REG *n) |
+ | |
+ +---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------+
+ | wm_sqrt(REG *n) |
+ | 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"
+
+
+.data
+/*
+ Local storage:
+ */
+ .align 4,0
+accum_2:
+ .long 0 // ms word
+accum_1:
+ .long 0
+accum_0:
+ .long 0
+
+sq_2:
+ .long 0 // ms word
+sq_1:
+ .long 0
+sq_0:
+ .long 0
+
+.text
+ .align 2,144
+
+.globl _wm_sqrt
+
+_wm_sqrt:
+ pushl %ebp
+ movl %esp,%ebp
+ 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 L10
+
+ shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */
+ rcrl $1,%ecx
+ rcrl $1,%edx
+
+L10:
+// From here on, n is never accessed directly again until it is
+// replaced by the answer.
+
+ movl %eax,sq_2 // ms word of n
+ movl %ecx,sq_1
+ movl %edx,sq_0
+
+ 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 no_adjust
+
+ movl $0x80000000,%edx /* round up */
+
+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 sq_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
+ divl %esi
+ shrl %esi
+ addl %eax,%esi
+
+ 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,
+// 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 sq_1,%ecx
+ subl %ecx,%eax
+ movl sq_2,%ecx // ms word of normalized n
+ sbbl %ecx,%edx
+ jnc l40
+
+// subtraction gives a negative result
+// negate the reult before division
+ notl %edx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%edx
+
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+ jmp l45
+
+l40:
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ notl %ecx
+ notl %eax
+ addl $1,%eax
+ adcl $0,%ecx
+
+l45:
+ sarl $1,%ecx // divide by 2
+ rcrl $1,%eax
+
+ movl %eax,%edi
+ addl %ecx,%esi
+
+// 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,accum_0
+
+ movl %esi,%eax
+ mull %esi
+ movl %edx,accum_2
+ movl %eax,accum_1
+
+ movl %edi,%eax
+ mull %esi
+ addl %eax,accum_0
+ adcl %edx,accum_1
+ adcl $0,accum_2
+
+ movl %esi,%eax
+ mull %edi
+ addl %eax,accum_0
+ adcl %edx,accum_1
+ adcl $0,accum_2
+
+// guess^2 now in accum_2:accum_1:accum_0
+
+ movl sq_1,%eax // get normalized n
+ subl %eax,accum_1
+ movl sq_2,%eax // ms word of normalized n
+ sbbl %eax,accum_2
+ jnc l60
+
+// subtraction gives a negative result
+// negate the reult before division
+ notl accum_0
+ notl accum_1
+ notl accum_2
+ addl $1,accum_0
+ adcl $0,accum_1
+
+#ifdef PARANOID
+ adcl $0,accum_2 // This must be zero
+ jz l51
+
+ pushl EX_INTERNAL|0x207
+ call EXCEPTION
+
+l51:
+#endif PARANOID
+
+ movl accum_1,%edx
+ movl accum_0,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx // divide by 2
+ rcrl $1,%eax
+
+ // round the result
+ addl $0x80000000,%eax
+ adcl $0,%ecx
+
+ addl %ecx,%edi
+ adcl $0,%esi
+
+ jmp l65
+
+l60:
+ movl accum_1,%edx
+ movl accum_0,%eax
+ divl %esi
+ movl %eax,%ecx
+
+ movl %edx,%eax
+ divl %esi
+
+ sarl $1,%ecx // divide by 2
+ rcrl $1,%eax
+
+ // round the result
+ addl $0x80000000,%eax
+ adcl $0,%ecx
+
+ subl %ecx,%edi
+ sbbl $0,%esi
+
+l65:
+ movl PARAM1,%ecx
+
+ movl %edi,SIGL(%ecx)
+ movl %esi,SIGH(%ecx)
+
+ movl EXP_BIAS,EXP(%ecx) /* Result is in [1.0 .. 2.0) */
+
+// popl %ebx
+ popl %edi
+ popl %esi
+ leave
+ ret
diff --git a/kernel/Makefile b/kernel/Makefile
index f189aaf..9333a4b 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -16,7 +16,7 @@
.c.o:
$(CC) $(CFLAGS) -c $<
-SUBDIRS = chr_drv blk_drv math
+SUBDIRS = chr_drv blk_drv FPU-emu
OBJS = sched.o sys_call.o traps.o irq.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index 70baaaf..c82c896 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -1174,6 +1174,7 @@ static int floppy_open(struct inode * inode, struct file * filp)
return -EBUSY;
fd_ref[drive]++;
fd_device[drive] = inode->i_rdev;
+ buffer_drive = buffer_track = -1;
if (old_dev && old_dev != inode->i_rdev)
invalidate_buffers(old_dev);
if (filp && filp->f_mode)
@@ -1197,6 +1198,7 @@ static struct file_operations floppy_fops = {
NULL, /* readdir - bad */
NULL, /* select */
fd_ioctl, /* ioctl */
+ NULL, /* mmap */
floppy_open, /* open */
floppy_release /* release */
};
diff --git a/kernel/blk_drv/genhd.c b/kernel/blk_drv/genhd.c
index 6addf89..0e539da 100644
--- a/kernel/blk_drv/genhd.c
+++ b/kernel/blk_drv/genhd.c
@@ -147,6 +147,29 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
brelse(bh);
}
+/* This function is used to re-read partition tables for removable disks.
+ Much of the cleanup from the old partition tables should have already been
+ done */
+
+/* This function will re-read the partition tables for a given device,
+and set things back up again. There are some important caveats,
+however. You must ensure that no one is using the device, and no one
+can start using the device while this function is being executed. */
+
+void resetup_one_dev(struct gendisk *dev, int drive)
+{
+ int i;
+ int start = drive<<dev->minor_shift;
+ int j = start + dev->max_p;
+ int major = dev->major << 8;
+
+ current_minor = 1+(drive<<dev->minor_shift);
+ check_partition(dev, major+(drive<<dev->minor_shift));
+
+ for (i=start ; i < j ; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+}
+
static void setup_dev(struct gendisk *dev)
{
int i;
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index 27957fd..9b68aad 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -38,6 +38,9 @@
#define MAJOR_NR 3
#include "blk.h"
+extern void resetup_one_dev(struct gendisk *, unsigned int);
+static int revalidate_hddisk(int, int);
+
static inline unsigned char CMOS_READ(unsigned char addr)
{
outb_p(0x80|addr,0x70);
@@ -54,6 +57,9 @@ static void recal_intr(void);
static void bad_rw_intr(void);
static char recalibrate[ MAX_HD ] = { 0, };
+static int access_count[MAX_HD] = {0, };
+static char busy[MAX_HD] = {0, };
+static struct wait_queue * busy_wait = NULL;
static int reset = 0;
@@ -456,13 +462,14 @@ static int hd_ioctl(struct inode * inode, struct file * file,
struct hd_geometry *loc = (void *) arg;
int dev;
- if (!loc || !inode)
+ if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev) >> 6;
if (dev >= NR_HD)
return -EINVAL;
switch (cmd) {
case HDIO_REQ:
+ if (!loc) return -EINVAL;
verify_area(loc, sizeof(*loc));
put_fs_byte(hd_info[dev].head,
(char *) &loc->heads);
@@ -473,19 +480,37 @@ static int hd_ioctl(struct inode * inode, struct file * file,
put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_hddisk(inode->i_rdev, 1);
RO_IOCTLS(inode->i_rdev,arg);
default:
return -EINVAL;
}
}
+static int hd_open(struct inode * inode, struct file * filp)
+{
+ int target;
+ target = DEVICE_NR(MINOR(inode->i_rdev));
+
+ while (busy[target])
+ sleep_on(&busy_wait);
+ access_count[target]++;
+ return 0;
+}
+
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
static void hd_release(struct inode * inode, struct file * file)
{
+ int target;
sync_dev(inode->i_rdev);
+
+ target = DEVICE_NR(MINOR(inode->i_rdev));
+ access_count[target]--;
+
}
static void hd_geninit();
@@ -504,6 +529,34 @@ static struct gendisk hd_gendisk = {
NULL /* next */
};
+static void hd_interrupt(int unused)
+{
+ void (*handler)(void) = DEVICE_INTR;
+
+ DEVICE_INTR = NULL;
+ timer_active &= ~(1<<HD_TIMER);
+ if (!handler)
+ handler = unexpected_hd_interrupt;
+ handler();
+ sti();
+}
+
+/*
+ * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
+ * means we run the IRQ-handler with interrupts disabled: this is bad for
+ * interrupt latency, but anything else has led to problems on some
+ * machines...
+ *
+ * We enable interrupts in some of the routines after making sure it's
+ * safe.
+ */
+static struct sigaction hd_sigaction = {
+ hd_interrupt,
+ 0,
+ SA_INTERRUPT,
+ NULL
+};
+
static void hd_geninit(void)
{
int drive, i;
@@ -551,8 +604,13 @@ static void hd_geninit(void)
NR_HD = 1;
else
NR_HD = 0;
+ if (NR_HD) {
+ if (irqaction(HD_IRQ,&hd_sigaction)) {
+ printk("Unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
+ NR_HD = 0;
+ }
+ }
#endif
-
for (i = 0 ; i < NR_HD ; i++)
hd[i<<6].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
@@ -567,48 +625,78 @@ static struct file_operations hd_fops = {
NULL, /* readdir - bad */
NULL, /* select */
hd_ioctl, /* ioctl */
- NULL, /* no special open code */
+ NULL, /* mmap */
+ hd_open, /* open */
hd_release /* release */
};
-static void hd_interrupt(int unused)
-{
- void (*handler)(void) = DEVICE_INTR;
-
- DEVICE_INTR = NULL;
- timer_active &= ~(1<<HD_TIMER);
- if (!handler)
- handler = unexpected_hd_interrupt;
- handler();
- sti();
-}
-
-/*
- * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
- * means we run the IRQ-handler with interrupts disabled: this is bad for
- * interrupt latency, but anything else has led to problems on some
- * machines...
- *
- * We enable interrupts in some of the routines after making sure it's
- * safe.
- */
-static struct sigaction hd_sigaction = {
- hd_interrupt,
- 0,
- SA_INTERRUPT,
- NULL
-};
-
unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
{
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
blkdev_fops[MAJOR_NR] = &hd_fops;
hd_gendisk.next = gendisk_head;
gendisk_head = &hd_gendisk;
- if (irqaction(HD_IRQ,&hd_sigaction))
- printk("Unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
timer_table[HD_TIMER].fn = hd_times_out;
return mem_start;
}
+#define DEVICE_BUSY busy[target]
+#define USAGE access_count[target]
+#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
+/* We assume that the the bios parameters do not change, so the disk capacity
+ will not change */
+#undef MAYBE_REINIT
+#define GENDISK_STRUCT hd_gendisk
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed scsi disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_hddisk(int dev, int maxusage)
+{
+ int target, major;
+ struct gendisk * gdev;
+ int max_p;
+ int start;
+ int i;
+
+ target = DEVICE_NR(MINOR(dev));
+ gdev = &GENDISK_STRUCT;
+
+ cli();
+ if (DEVICE_BUSY || USAGE > maxusage) {
+ sti();
+ return -EBUSY;
+ };
+ DEVICE_BUSY = 1;
+ sti();
+
+ max_p = gdev->max_p;
+ start = target << gdev->minor_shift;
+ major = MAJOR_NR << 8;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ sync_dev(major | start | i);
+ invalidate_inodes(major | start | i);
+ invalidate_buffers(major | start | i);
+ gdev->part[i].start_sect = 0;
+ gdev->part[i].nr_sects = 0;
+ };
+
+#ifdef MAYBE_REINIT
+ MAYBE_REINIT;
+#endif
+
+ gdev->part[start].nr_sects = CAPACITY;
+ resetup_one_dev(gdev, target);
+
+ DEVICE_BUSY = 0;
+ wake_up(&busy_wait);
+ return 0;
+}
+
#endif
diff --git a/kernel/blk_drv/ramdisk.c b/kernel/blk_drv/ramdisk.c
index 8194b44..8690a91 100644
--- a/kernel/blk_drv/ramdisk.c
+++ b/kernel/blk_drv/ramdisk.c
@@ -56,6 +56,7 @@ static struct file_operations rd_fops = {
NULL, /* readdir - bad */
NULL, /* select */
NULL, /* ioctl */
+ NULL, /* mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/kernel/blk_drv/scsi/Makefile b/kernel/blk_drv/scsi/Makefile
index 98f96a1..226a8c0 100644
--- a/kernel/blk_drv/scsi/Makefile
+++ b/kernel/blk_drv/scsi/Makefile
@@ -19,7 +19,7 @@
LOWLEVELCSRC = aha1542.c fdomain.c seagate.c ultrastor.c 7000fasst.c
-LOWLEVELHSRC = aha1542.h fdomain.h seagate.h ultrastor.h 7000fasst.o
+LOWLEVELHSRC = aha1542.h fdomain.h seagate.h ultrastor.h 7000fasst.h
CSRC = hosts.c sd.c sd_ioctl.c st.c st_ioctl.c sr.c sr_ioctl.c scsi.c scsi_ioctl.c $(LOWLEVELCSRC)
HSRC = hosts.h sd.h st.h sr.h sr_ioctl.h scsi.h scsi_ioctl.h $(LOWLEVELHSRC)
@@ -109,7 +109,8 @@ sd.o : sd.c /usr/include/linux/config.h /usr/include/linux/config.dist.h /usr/in
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/mm.h /usr/include/linux/signal.h /usr/include/linux/time.h \
/usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
- /usr/include/linux/string.h scsi.h sd.h /usr/include/linux/genhd.h ../blk.h
+ /usr/include/linux/string.h /usr/include/linux/errno.h /usr/include/asm/system.h \
+ scsi.h sd.h /usr/include/linux/genhd.h scsi_ioctl.h ../blk.h
sd_ioctl.o : sd_ioctl.c /usr/include/linux/config.h /usr/include/linux/config.dist.h \
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -120,15 +121,15 @@ sd_ioctl.o : sd_ioctl.c /usr/include/linux/config.h /usr/include/linux/config.di
/usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
/usr/include/linux/vm86.h scsi.h sd.h /usr/include/linux/genhd.h
seagate.o : seagate.c /usr/include/linux/config.h /usr/include/linux/config.dist.h \
- /usr/include/asm/io.h /usr/include/asm/system.h /usr/include/linux/sched.h /usr/include/linux/head.h \
- /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
- /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
- /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
- /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
- /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
- /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
- /usr/include/linux/resource.h /usr/include/linux/vm86.h seagate.h scsi.h hosts.h \
- max_hosts.h
+ /usr/include/asm/io.h /usr/include/asm/system.h /usr/include/linux/signal.h \
+ /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
+ /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
+ /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
+ /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
+ /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
+ /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/time.h \
+ /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
+ seagate.h scsi.h hosts.h max_hosts.h
sr.o : sr.c /usr/include/linux/config.h /usr/include/linux/config.dist.h /usr/include/linux/fs.h \
/usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
/usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
@@ -137,7 +138,7 @@ sr.o : sr.c /usr/include/linux/config.h /usr/include/linux/config.dist.h /usr/in
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/mm.h /usr/include/linux/signal.h /usr/include/linux/time.h \
/usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
- /usr/include/linux/string.h scsi.h sr.h ../blk.h
+ /usr/include/linux/string.h scsi.h sr.h scsi_ioctl.h ../blk.h
sr_ioctl.o : sr_ioctl.c /usr/include/linux/config.h /usr/include/linux/config.dist.h \
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -147,7 +148,7 @@ sr_ioctl.o : sr_ioctl.c /usr/include/linux/config.h /usr/include/linux/config.di
/usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
/usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
/usr/include/linux/vm86.h /usr/include/asm/segment.h /usr/include/linux/errno.h \
- ../blk.h scsi.h sr.h /usr/include/linux/cdrom.h
+ ../blk.h scsi.h sr.h scsi_ioctl.h /usr/include/linux/cdrom.h
st.o : st.c /usr/include/linux/config.h /usr/include/linux/config.dist.h scsi.h \
st.h /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c
index 48d8d72..06d0bcb 100644
--- a/kernel/blk_drv/scsi/aha1542.c
+++ b/kernel/blk_drv/scsi/aha1542.c
@@ -251,7 +251,9 @@ void aha1542_intr_handle(void)
mb[1].status = 0;
if (ccb.tarstat == 2) {
+#ifdef DEBUG
int i;
+#endif
DEB(printk("aha1542_intr_handle: sense:"));
#ifdef DEBUG
for (i = 0; i < 12; i++)
@@ -431,8 +433,8 @@ int aha1542_detect(int hostnum)
return 0;
}
-#if MAX_MEGABYTES > 16
- printk("Adaptec 1542 disabled for kernels for which MAX_MEGABYTES > 16.\n");
+#ifndef MAX_16M
+ printk("Adaptec 1542 disabled for kernels without memory limiting to 16MB.\n");
return 0;
#endif
diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c
index 28749ed..164813d 100644
--- a/kernel/blk_drv/scsi/scsi.c
+++ b/kernel/blk_drv/scsi/scsi.c
@@ -172,17 +172,23 @@ static void scan_scsis (void)
while (the_result < 0);
- if (!the_result)
+ if (!the_result)
{
- scsi_devices[NR_SCSI_DEVICES].
+ scsi_devices[NR_SCSI_DEVICES].
host_no = host_nr;
- scsi_devices[NR_SCSI_DEVICES].
+ scsi_devices[NR_SCSI_DEVICES].
id = dev;
- scsi_devices[NR_SCSI_DEVICES].
+ scsi_devices[NR_SCSI_DEVICES].
lun = lun;
- scsi_devices[NR_SCSI_DEVICES].
+ scsi_devices[NR_SCSI_DEVICES].
removable = (0x80 &
scsi_result[1]) >> 7;
+ scsi_devices[NR_SCSI_DEVICES].
+ changed = 0;
+ scsi_devices[NR_SCSI_DEVICES].
+ access_count = 0;
+ scsi_devices[NR_SCSI_DEVICES].
+ busy = 0;
/*
* Currently, all sequential devices are assumed to be tapes,
* all random devices disk, with the appropriate read only
@@ -606,9 +612,10 @@ static int check_sense (int host)
case ABORTED_COMMAND:
case NOT_READY:
- case UNIT_ATTENTION:
return SUGGEST_RETRY;
-
+ case UNIT_ATTENTION:
+ return SUGGEST_ABORT;
+
/* these three are not supported */
case COPY_ABORTED:
case VOLUME_OVERFLOW:
diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h
index a64f728..7bf6027 100644
--- a/kernel/blk_drv/scsi/scsi.h
+++ b/kernel/blk_drv/scsi/scsi.h
@@ -213,10 +213,13 @@
typedef struct scsi_device {
unsigned char host_no, id, lun;
+ int access_count; /* Count of open channels/mounts */
unsigned writeable:1;
unsigned removable:1;
unsigned random:1;
- } Scsi_Device;
+ unsigned changed:1; /* Data invalid due to media change */
+ unsigned busy:1; /* Used to prevent races */
+} Scsi_Device;
/*
Use these to separate status msg and our bytes
*/
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c
index 8a700b1..411fa64 100644
--- a/kernel/blk_drv/scsi/scsi_ioctl.c
+++ b/kernel/blk_drv/scsi/scsi_ioctl.c
@@ -63,13 +63,78 @@ static int ioctl_probe(int dev, void *buffer)
* The output area is then filled in starting from the command byte.
*/
-static int the_result[MAX_SCSI_HOSTS];
+static int the_result[MAX_SCSI_HOSTS] = {0,};
static void scsi_ioctl_done (int host, int result)
{
the_result[host] = result;
}
-
+
+static int ioctl_internal_command(Scsi_Device *dev, char ** command)
+{
+ char * buf;
+ char * cmd;
+ int temp, host;
+
+ host = dev->host_no;
+ cmd = command[0];
+ buf = command[1];
+
+ do {
+ cli();
+ if (the_result[host]) {
+ sti();
+ while(the_result[host])
+ /* nothing */;
+ } else {
+ the_result[host]=-1;
+ sti();
+ break;
+ }
+ } while (1);
+
+ scsi_do_cmd(host, dev->id, cmd, buf, 255,
+ scsi_ioctl_done, MAX_TIMEOUT,
+ buf, MAX_RETRIES);
+
+ while (the_result[host] == -1)
+ /* nothing */;
+ temp = the_result[host];
+
+ if(driver_byte(the_result[host]) != 0)
+ switch(buf[2] & 0xf) {
+ case ILLEGAL_REQUEST:
+ printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ if(dev->removable){
+ printk("Device not ready. Make sure there is a disc in the drive.\n");
+ break;
+ };
+ case UNIT_ATTENTION:
+ if (dev->removable){
+ dev->changed = 1;
+ temp = 0; /* This is no longer considered an error */
+ printk("Disc change detected.\n");
+ break;
+ };
+ default: /* Fall through for non-removable media */
+ printk("SCSI CD error: host %d id %d lun %d return code = %x\n",
+ dev->host_no,
+ dev->id,
+ dev->lun,
+ the_result);
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(buf[0]),
+ sense_error(buf[0]),
+ buf[2] & 0xf);
+
+ };
+
+ the_result[host] = 0;
+ return temp;
+}
+
static int ioctl_command(Scsi_Device *dev, void *buffer)
{
char buf[MAX_BUF];
@@ -141,6 +206,9 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
*/
int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
{
+ char scsi_cmd[10];
+ char * command[2];
+
if ((cmd != 0 && dev->id > NR_SCSI_DEVICES))
return -ENODEV;
if ((cmd == 0 && dev->host_no > MAX_SCSI_HOSTS))
@@ -151,6 +219,34 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
return ioctl_probe(dev->host_no, arg);
case SCSI_IOCTL_SEND_COMMAND:
return ioctl_command((Scsi_Device *) dev, arg);
+ case SCSI_IOCTL_DOORLOCK:
+ if (!dev->removable) return 0;
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
+ command[0] = scsi_cmd;
+ command[1] = (char *) arg;
+ return ioctl_internal_command((Scsi_Device *) dev, command);
+ break;
+ case SCSI_IOCTL_DOORUNLOCK:
+ if (!dev->removable) return 0;
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
+ command[0] = scsi_cmd;
+ command[1] = (char *) arg;
+ return ioctl_internal_command((Scsi_Device *) dev, command);
+ case SCSI_IOCTL_TEST_UNIT_READY:
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 0;
+ command[0] = scsi_cmd;
+ command[1] = (char *) arg;
+ return ioctl_internal_command((Scsi_Device *) dev, command);
+ break;
default :
return -EINVAL;
}
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.h b/kernel/blk_drv/scsi/scsi_ioctl.h
index 2acdb55..50769c0 100644
--- a/kernel/blk_drv/scsi/scsi_ioctl.h
+++ b/kernel/blk_drv/scsi/scsi_ioctl.h
@@ -7,6 +7,14 @@
#define SCSI_IOCTL_PROBE_HOST 0
#define SCSI_IOCTL_SEND_COMMAND 1
+#define SCSI_IOCTL_TEST_UNIT_READY 2
+/* The door lock/unlock constants are compatible with Sun constants for
+ the cdrom */
+#define SCSI_IOCTL_DOORLOCK 0x5380 /* lock the eject mechanism */
+#define SCSI_IOCTL_DOORUNLOCK 0x5381 /* unlock the mechanism */
+
+#define SCSI_REMOVAL_PREVENT 1
+#define SCSI_REMOVAL_ALLOW 0
#ifdef CONFIG_BLK_DEV_SD
/* Should start at 128 */
diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c
index 0a47d9c..53fa133 100644
--- a/kernel/blk_drv/scsi/sd.c
+++ b/kernel/blk_drv/scsi/sd.c
@@ -1,7 +1,7 @@
/*
- * sd.c Copyright (C) 1992 Drew Eckhardt
+ * sd.c Copyright (C) 1992 Drew Eckhardt
* Linux scsi disk driver by
- * Drew Eckhardt
+ * Drew Eckhardt
*
* <drew@colorado.edu>
*/
@@ -13,9 +13,12 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
#include "scsi.h"
#include "sd.h"
+#include "scsi_ioctl.h"
#define MAJOR_NR 8
@@ -35,22 +38,58 @@ static const char RCSid[] = "$Header:";
#define SD_TIMEOUT 200
struct hd_struct sd[MAX_SD << 4];
-
+
int NR_SD=0;
Scsi_Disk rscsi_disks[MAX_SD];
static int sd_sizes[MAX_SD << 4] = {0, };
static int this_count, total_count = 0;
static int the_result;
+static int boot_init_done = 0;
static char sense_buffer[255];
int slow_scsi_io = -1; /* This is set by aha1542.c, and others, if needed */
+/* used to re-read partitions. */
+extern void resetup_one_dev(struct gendisk *, unsigned int);
+
+extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned int);
+
+static sd_init_onedisk(int);
+
+static int sd_open(struct inode * inode, struct file * filp)
+{
+ int target;
+ target = DEVICE_NR(MINOR(inode->i_rdev));
+
+/* Make sure that only one process can do a check_change_disk at one time.
+ This is also used to lock out further access when the partition table is being re-read. */
-extern int sd_ioctl(struct inode *, struct file *, unsigned long, unsigned long);
+ while (rscsi_disks[target].device->busy);
+
+ if(rscsi_disks[target].device->removable) {
+ if (filp->f_mode)
+ check_disk_change(inode->i_rdev);
+
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ };
+ rscsi_disks[target].device->access_count++;
+ return 0;
+}
static void sd_release(struct inode * inode, struct file * file)
{
+ int target;
sync_dev(inode->i_rdev);
+
+ target = DEVICE_NR(MINOR(inode->i_rdev));
+
+ rscsi_disks[target].device->access_count--;
+
+ if(rscsi_disks[target].device->removable) {
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ };
}
static struct gendisk sd_gendisk;
@@ -62,6 +101,7 @@ static void sd_geninit (void) {
sd_gendisk.nr_real = NR_SD;
}
+
static struct file_operations sd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
@@ -69,12 +109,13 @@ static struct file_operations sd_fops = {
NULL, /* readdir - bad */
NULL, /* select */
sd_ioctl, /* ioctl */
- NULL, /* no special open code */
+ NULL, /* mmap */
+ sd_open, /* open code */
sd_release /* release */
};
static struct gendisk sd_gendisk = {
- MAJOR_NR, /* Major number */
+ MAJOR_NR, /* Major number */
"sd", /* Major name */
4, /* Bits to shift to get real from partition */
1 << 4, /* Number of partitions per real */
@@ -89,7 +130,7 @@ static struct gendisk sd_gendisk = {
/*
rw_intr is the interrupt routine for the device driver. It will
- be notified on the end of a SCSI read / write, and
+ be notified on the end of a SCSI read / write, and
will take on of several actions based on success or failure.
*/
@@ -104,7 +145,7 @@ static void rw_intr (int host, int result)
/*
First case : we assume that the command succeeded. One of two things will
- happen here. Either we will be finished, or there will be more
+ happen here. Either we will be finished, or there will be more
sectors that we were unable to read last time.
*/
@@ -137,7 +178,7 @@ static void rw_intr (int host, int result)
if (!CURRENT->bh)
{
#ifdef DEBUG
- printk("sd%d : handling page request, no buffer\n",
+ printk("sd%d : handling page request, no buffer\n",
MINOR(CURRENT->dev));
#endif
@@ -157,18 +198,18 @@ static void rw_intr (int host, int result)
}
else
end_request(1);
- do_sd_request();
+ do_sd_request();
}
/*
- * Of course, the error handling code is a little Fubar down in scsi.c.
- * Version 2 of the drivers will fix that, and we will *really* recover
+ * Of course, the error handling code is a little Fubar down in scsi.c.
+ * Version 2 of the drivers will fix that, and we will *really* recover
* from errors.
*/
/*
- Now, if we were good little boys and girls, Santa left us a request
- sense buffer. We can extract information from this, so we
+ Now, if we were good little boys and girls, Santa left us a request
+ sense buffer. We can extract information from this, so we
can choose a block to remap, etc.
*/
@@ -179,23 +220,34 @@ static void rw_intr (int host, int result)
Not yet implemented. A read will fail after being remapped,
a write will call the strategy routine again.
*/
+ else if ((sense_buffer[0] & 0x7f) == 0x70) {
+ if ((sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ /* detected disc change. set a bit and quietly refuse */
+ /* further access. */
+
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].device->changed = 1;
+ end_request(0);
+ do_sd_request();
+ return;
+ }
+ }
if rscsi_disks[DEVICE_NR(CURRENT->dev)].remap
{
result = 0;
}
else
-
+
#endif
}
/*
If we had an ILLEGAL REQUEST returned, then we may have performed
an unsupported command. The only thing this should be would be a ten
- byte read where only a six byte read was supportted. Also, on a
+ byte read where only a six byte read was supportted. Also, on a
system where READ CAPACITY failed, we mave have read past the end of the
disk.
*/
-
+
else if (sense_buffer[7] == ILLEGAL_REQUEST) {
if (rscsi_disks[DEVICE_NR(CURRENT->dev)].ten) {
rscsi_disks[DEVICE_NR(CURRENT->dev)].ten = 0;
@@ -206,27 +258,28 @@ static void rw_intr (int host, int result)
}
}
if (result) {
- printk("SCSI disk error : host %d id %d lun %d return code = %03x\n",
- rscsi_disks[DEVICE_NR(CURRENT->dev)].device->host_no,
+ printk("SCSI disk error : host %d id %d lun %d return code = %x\n",
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].device->host_no,
rscsi_disks[DEVICE_NR(CURRENT->dev)].device->id,
- rscsi_disks[DEVICE_NR(CURRENT->dev)].device->lun);
+ rscsi_disks[DEVICE_NR(CURRENT->dev)].device->lun, result);
- if (driver_byte(result) & DRIVER_SENSE)
+ if (driver_byte(result) & DRIVER_SENSE)
printk("\tSense class %x, sense error %x, extended sense %x\n",
- sense_class(sense_buffer[0]),
+ sense_class(sense_buffer[0]),
sense_error(sense_buffer[0]),
sense_buffer[2] & 0xf);
end_request(0);
+ do_sd_request();
}
}
/*
- do_sd_request() is the request handler function for the sd driver.
- Its function in life is to take block device requests, and translate
+ do_sd_request() is the request handler function for the sd driver.
+ Its function in life is to take block device requests, and translate
them to SCSI commands.
*/
-
+
static void do_sd_request (void)
{
int dev, block;
@@ -241,21 +294,31 @@ repeat:
printk("Doing sd request, dev = %d, block = %d\n", dev, block);
#endif
- if (dev >= (NR_SD << 4) || block + CURRENT->nr_sectors > sd[dev].nr_sects)
+ if (dev >= (NR_SD << 4) || block + CURRENT->nr_sectors > sd[dev].nr_sects)
{
- end_request(0);
+ end_request(0);
goto repeat;
}
-
+
block += sd[dev].start_sect;
dev = DEVICE_NR(dev);
+ if (rscsi_disks[dev].device->changed)
+ {
+/*
+ * quietly refuse to do anything to a changed disc until the changed bit has been reset
+ */
+ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
+ end_request(0);
+ goto repeat;
+ }
+
#ifdef DEBUG
printk("sd%d : real dev = /dev/sd%d, block = %d\n", MINOR(CURRENT->dev), dev, block);
#endif
- if (!CURRENT->bh)
+ if (!CURRENT->bh)
this_count = CURRENT->nr_sectors;
else
this_count = (BLOCK_SIZE / 512);
@@ -269,32 +332,32 @@ repeat:
};
#ifdef DEBUG
- printk("sd%d : %s %d/%d 512 byte blocks.\n", MINOR(CURRENT->dev),
+ printk("sd%d : %s %d/%d 512 byte blocks.\n", MINOR(CURRENT->dev),
(CURRENT->cmd == WRITE) ? "writing" : "reading",
this_count, CURRENT->nr_sectors);
#endif
-
+
switch (CURRENT->cmd)
{
- case WRITE :
+ case WRITE :
if (!rscsi_disks[dev].device->writeable)
{
end_request(0);
goto repeat;
- }
+ }
cmd[0] = WRITE_6;
break;
- case READ :
+ case READ :
cmd[0] = READ_6;
break;
- default :
+ default :
printk ("Unknown sd command %d\r\n", CURRENT->cmd);
panic("");
}
cmd[1] = (LUN << 5) & 0xe0;
- if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten)
+ if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten)
{
if (this_count > 0xffff)
this_count = 0xffff;
@@ -312,127 +375,226 @@ repeat:
{
if (this_count > 0xff)
this_count = 0xff;
-
+
cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
cmd[2] = (unsigned char) ((block >> 8) & 0xff);
cmd[3] = (unsigned char) block & 0xff;
cmd[4] = (unsigned char) this_count;
cmd[5] = 0;
}
-
- scsi_do_cmd (HOST, ID, (void *) cmd, CURRENT->buffer, this_count << 9,
+
+ scsi_do_cmd (HOST, ID, (void *) cmd, CURRENT->buffer, this_count << 9,
rw_intr, SD_TIMEOUT, sense_buffer, MAX_RETRIES);
}
+int check_scsidisk_media_change(int full_dev, int flag){
+ int retval;
+ int target;
+ struct inode inode;
+
+ target = DEVICE_NR(MINOR(full_dev));
+
+ if (target >= NR_SD) {
+ printk("SCSI disk request error: invalid device.\n");
+ return 0;
+ };
+
+ if(!rscsi_disks[target].device->removable) return 0;
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+ retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ means there is no disc in the drive. Mark as changed,
+ and we will figure it out later once the drive is
+ available again. */
+
+ rscsi_disks[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ check_disk_change */
+ };
+
+ retval = rscsi_disks[target].device->changed;
+ if(!flag) rscsi_disks[target].device->changed = 0;
+ return retval;
+}
+
static void sd_init_done (int host, int result)
{
the_result = result;
}
+
+static int sd_init_onedisk(int i)
+{
+ int j = 0;
+ unsigned char cmd[10];
+ unsigned char buffer[513];
+ int try_again;
+
+ try_again=2;
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+
+ /*
+ * Super Kludge - since the midlevel error handling code doesn't work
+ * Version 2 will - it's under development 8^)
+ *
+ * We manually retry
+ */
+
+ do {
+ the_result = -1;
+#ifdef DEBUG
+ printk("sd%d : READ CAPACITY\n ", i);
+#endif
+ scsi_do_cmd (rscsi_disks[i].device->host_no ,
+ rscsi_disks[i].device->id,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT, sense_buffer,
+ MAX_RETRIES);
+
+ while(the_result < 0);
+ } while (try_again && the_result);
+ /*
+ * The SCSI standard says "READ CAPACITY is necessary for self confuring software"
+ * While not mandatory, support of READ CAPACITY is strongly encouraged.
+ * We used to die if we couldn't successfully do a READ CAPACITY.
+ * But, now we go on about our way. The side effects of this are
+ *
+ * 1. We can't know block size with certainty. I have said "512 bytes is it"
+ * as this is most common.
+ *
+ * 2. Recovery from when some one attempts to read past the end of the raw device will
+ * be slower.
+ */
+
+ if (the_result)
+ {
+ printk ("sd%d : READ CAPACITY failed.\n"
+ "sd%d : status = %x, message = %02x, host = %02x, driver = %02x \n",
+ i,i,
+ rscsi_disks[i].device->host_no, rscsi_disks[i].device->id,
+ rscsi_disks[i].device->lun,
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result)
+ );
+ if (driver_byte(the_result) & DRIVER_SENSE)
+ printk("sd%d : extended sense code = %1x \n", i, sense_buffer[2] & 0xf);
+ else
+ printk("sd%d : sense not available. \n", i);
+
+ printk("sd%d : block size assumed to be 512 bytes, disk size 1GB. \n", i);
+ rscsi_disks[i].capacity = 0x1fffff;
+ rscsi_disks[i].sector_size = 512;
+ }
+ else
+ {
+ rscsi_disks[i].capacity = (buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3];
+
+ if ((rscsi_disks[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) |
+ (buffer[6] << 8) |
+ buffer[7]) != 512)
+ {
+ printk ("sd%d : unsupported sector size %d.\n",
+ i, rscsi_disks[i].sector_size);
+ if(rscsi_disks[j].device->removable){
+ rscsi_disks[j].capacity = 0;
+ } else {
+ printk ("scsi : deleting disk entry.\n");
+ for (j=i; j < NR_SD;)
+ rscsi_disks[j] = rscsi_disks[++j];
+ --i;
+ return i;
+ };
+ }
+ }
+
+ rscsi_disks[i].ten = 1;
+ rscsi_disks[i].remap = 1;
+ return i;
+}
+
/*
- The sd_init() function looks at all SCSI drives present, determines
+ The sd_init() function looks at all SCSI drives present, determines
their size, and reads partition table entries for them.
*/
void sd_init(void)
{
- int i,j;
- unsigned char cmd[10];
- unsigned char buffer[513];
- int try_again;
+ int i;
-
for (i = 0; i < NR_SD; ++i)
- {
- try_again=2;
- cmd[0] = READ_CAPACITY;
- cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
- memset ((void *) &cmd[2], 0, 8);
-
-/*
- * Super Kludge - since the midlevel error handling code doesn't work
- * Version 2 will - it's under development 8^)
- *
- * We manually retry
- */
-
-
- do {
- the_result = -1;
-#ifdef DEBUG
- printk("sd%d : READ CAPACITY\n ", i);
-#endif
- scsi_do_cmd (rscsi_disks[i].device->host_no ,
- rscsi_disks[i].device->id,
- (void *) cmd, (void *) buffer,
- 512, sd_init_done, SD_TIMEOUT, sense_buffer,
- MAX_RETRIES);
-
- while(the_result < 0);
- } while (try_again && the_result);
-/*
- * The SCSI standard says "READ CAPACITY is necessary for self confuring software"
- * While not mandatory, support of READ CAPACITY is strongly encouraged.
- * We used to die if we couldn't successfully do a READ CAPACITY.
- * But, now we go on about our way. The side effects of this are
- *
- * 1. We can't know block size with certainty. I have said "512 bytes is it"
- * as this is most common.
- *
- * 2. Recovery from when some one attempts to read past the end of the raw device will
- * be slower.
- */
-
- if (the_result)
- {
- printk ("sd%d : READ CAPACITY failed.\n"
- "sd%d : status = %x, message = %02x, host = %02x, driver = %02x \n",
- i,i,
- rscsi_disks[i].device->host_no, rscsi_disks[i].device->id,
- rscsi_disks[i].device->lun,
- status_byte(the_result),
- msg_byte(the_result),
- host_byte(the_result),
- driver_byte(the_result)
- );
- if (driver_byte(the_result) & DRIVER_SENSE)
- printk("sd%d : extended sense code = %1x \n", i, sense_buffer[2] & 0xf);
- else
- printk("sd%d : sense not available. \n", i);
-
- printk("sd%d : block size assumed to be 512 bytes, disk size 1GB. \n", i);
- rscsi_disks[i].capacity = 0x1fffff;
- rscsi_disks[i].sector_size = 512;
- }
- else
- {
- rscsi_disks[i].capacity = (buffer[0] << 24) |
- (buffer[1] << 16) |
- (buffer[2] << 8) |
- buffer[3];
-
- if ((rscsi_disks[i].sector_size = (buffer[4] << 24) |
- (buffer[5] << 16) |
- (buffer[6] << 8) |
- buffer[7]) != 512)
- {
- printk ("sd%d : unsupported sector size %d.\n",
- i, rscsi_disks[i].sector_size);
- printk ("scsi : deleting disk entry.\n");
- for (j=i; j < NR_SD;)
- rscsi_disks[j] = rscsi_disks[++j];
- --i;
- continue;
- }
- }
-
- rscsi_disks[i].ten = 1;
- rscsi_disks[i].remap = 1;
- }
+ i = sd_init_onedisk(i);
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
blkdev_fops[MAJOR_NR] = &sd_fops;
sd_gendisk.next = gendisk_head;
gendisk_head = &sd_gendisk;
+ boot_init_done++;
+}
+
+#define DEVICE_BUSY rscsi_disks[target].device->busy
+#define USAGE rscsi_disks[target].device->access_count
+#define CAPACITY rscsi_disks[target].capacity
+#define MAYBE_REINIT sd_init_onedisk(target)
+#define GENDISK_STRUCT sd_gendisk
+
+/* This routine is called to flush all partitions and partition tables
+ for a changed scsi disk, and then re-read the new partition table.
+ If we are revalidating a disk because of a media change, then we
+ enter with usage == 0. If we are using an ioctl, we automatically have
+ usage == 1 (we need an open channel to use an ioctl :-), so this
+ is our limit.
+ */
+int revalidate_scsidisk(int dev, int maxusage){
+ int target, major;
+ struct gendisk * gdev;
+ int max_p;
+ int start;
+ int i;
+
+ target = DEVICE_NR(MINOR(dev));
+ gdev = &GENDISK_STRUCT;
+
+ sti();
+ if (DEVICE_BUSY || USAGE > maxusage) {
+ cli();
+ return -EBUSY;
+ };
+ DEVICE_BUSY = 1;
+ cli();
+
+ max_p = gdev->max_p;
+ start = target << gdev->minor_shift;
+ major = MAJOR_NR << 8;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ sync_dev(major | start | i);
+ invalidate_inodes(major | start | i);
+ invalidate_buffers(major | start | i);
+ gdev->part[i].start_sect = 0;
+ gdev->part[i].nr_sects = 0;
+ };
+
+#ifdef MAYBE_REINIT
+ MAYBE_REINIT;
+#endif
+
+ gdev->part[start].nr_sects = CAPACITY;
+ resetup_one_dev(gdev, target);
+
+ DEVICE_BUSY = 0;
+ return 0;
}
#endif
+
+
+
diff --git a/kernel/blk_drv/scsi/sd_ioctl.c b/kernel/blk_drv/scsi/sd_ioctl.c
index d2f18d2..7a447c1 100644
--- a/kernel/blk_drv/scsi/sd_ioctl.c
+++ b/kernel/blk_drv/scsi/sd_ioctl.c
@@ -7,14 +7,23 @@
#include "sd.h"
extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+extern int revalidate_scsidisk(int, int);
int sd_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg)
{
int dev = inode->i_rdev;
-
+
switch (cmd) {
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_scsidisk(dev, 1);
default:
- return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device, cmd, (void *) arg);
+ return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
}
}
#endif
+
+
+
+
+
+
diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c
index aeb0421..201b65c 100644
--- a/kernel/blk_drv/scsi/seagate.c
+++ b/kernel/blk_drv/scsi/seagate.c
@@ -364,10 +364,7 @@ static int internal_command(unsigned char target, const void *cmnd,
if (target > 6)
- {
- if (reselect == RECONNECT_NOW)
return DID_BAD_TARGET;
- }
/*
* We work it differently depending on if this is is "the first time,"
diff --git a/kernel/blk_drv/scsi/sr.c b/kernel/blk_drv/scsi/sr.c
index c7e9bce..c84cf21 100644
--- a/kernel/blk_drv/scsi/sr.c
+++ b/kernel/blk_drv/scsi/sr.c
@@ -20,6 +20,7 @@
#include "scsi.h"
#include "sr.h"
+#include "scsi_ioctl.h" /* For the door lock/unlock commands */
#define MAJOR_NR 11
@@ -43,8 +44,6 @@ struct block_buffer
static struct block_buffer bb[MAX_SR];
-static int sr_result;
-
static int sr_open(struct inode *, struct file *);
extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned int);
@@ -52,6 +51,8 @@ extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned int);
static void sr_release(struct inode * inode, struct file * file)
{
sync_dev(inode->i_rdev);
+ if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
}
static struct file_operations sr_fops =
@@ -62,14 +63,53 @@ static struct file_operations sr_fops =
NULL, /* readdir - bad */
NULL, /* select */
sr_ioctl, /* ioctl */
+ NULL, /* mmap */
sr_open, /* no special open code */
sr_release /* release */
};
/*
- * The sense_buffer is where we put data for all mode sense commands performed.
+ * This function checks to see if the media has been changed in the
+ * CDROM drive. It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it. We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit. If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
*/
+int check_cdrom_media_change(int full_dev, int flag){
+ int retval, target;
+ struct inode inode;
+
+ target = MINOR(full_dev);
+
+ if (target >= NR_SR) {
+ printk("CD-ROM request error: invalid device.\n");
+ return 0;
+ };
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+ retval = sr_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ means there is no disc in the drive. Mark as changed,
+ and we will figure it out later once the drive is
+ available again. */
+
+ scsi_CDs[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ check_disk_change */
+ };
+
+ retval = scsi_CDs[target].device->changed;
+ if(!flag) scsi_CDs[target].device->changed = 0;
+ return retval;
+}
+
+/*
+ * The sense_buffer is where we put data for all mode sense commands performed.
+ */
static unsigned char sense_buffer[255];
/*
@@ -129,8 +169,9 @@ static void rw_intr (int host, int result)
/* detected disc change. set a bit and quietly refuse */
/* further access. */
- scsi_CDs[DEVICE_NR(CURRENT->dev)].changed = 1;
+ scsi_CDs[DEVICE_NR(CURRENT->dev)].device->changed = 1;
end_request(0);
+ do_sr_request();
return;
}
}
@@ -143,7 +184,7 @@ static void rw_intr (int host, int result)
result = 0;
return;
} else {
- end_request(0);
+ printk("CD-ROM error: Drive reports %d.\n", sense_buffer[2]); end_request(0);
do_sr_request(); /* Do next request */
return;
}
@@ -181,6 +222,9 @@ static int sr_open(struct inode * inode, struct file * filp)
{
if (filp->f_mode)
check_disk_change(inode->i_rdev);
+
+ if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
return 0;
}
@@ -220,7 +264,7 @@ void do_sr_request (void)
goto repeat;
}
- if (scsi_CDs[dev].changed)
+ if (scsi_CDs[dev].device->changed)
{
/*
* quietly refuse to do anything to a changed disc until the changed bit has been reset
@@ -337,7 +381,6 @@ void sr_init(void)
scsi_CDs[i].use = 1;
scsi_CDs[i].ten = 1;
scsi_CDs[i].remap = 1;
- scsi_CDs[i].changed = 0;
sr_sizes[i] = scsi_CDs[i].capacity;
bb[i].block = -1;
diff --git a/kernel/blk_drv/scsi/sr.h b/kernel/blk_drv/scsi/sr.h
index 5361efb..6cf0ece 100644
--- a/kernel/blk_drv/scsi/sr.h
+++ b/kernel/blk_drv/scsi/sr.h
@@ -28,7 +28,6 @@ typedef struct
unsigned ten:1; /* support ten byte commands */
unsigned remap:1; /* support remapping */
unsigned use:1; /* is this device still supportable */
- unsigned changed:1; /* disk changed flag */
} Scsi_CD;
extern Scsi_CD scsi_CDs[MAX_SR];
diff --git a/kernel/blk_drv/scsi/sr_ioctl.c b/kernel/blk_drv/scsi/sr_ioctl.c
index ded0094..cdb71bc 100644
--- a/kernel/blk_drv/scsi/sr_ioctl.c
+++ b/kernel/blk_drv/scsi/sr_ioctl.c
@@ -9,6 +9,7 @@
#include "../blk.h"
#include "scsi.h"
#include "sr.h"
+#include "scsi_ioctl.h"
#include <linux/cdrom.h>
@@ -26,7 +27,7 @@ static u_char sr_lock = 0; /* To make sure that only one person is doing
an ioctl at one time */
static int target;
-extern int scsi_ioctl (int dev, int cmd, void *arg);
+extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
static void lock_sr_ioctl( void )
{
@@ -69,7 +70,7 @@ retry:
if(driver_byte(the_result) != 0 &&
(sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
- scsi_CDs[target].changed = 1;
+ scsi_CDs[target].device->changed = 1;
printk("Disc change detected.\n");
};
@@ -83,7 +84,7 @@ retry:
if(driver_byte(the_result) != 0)
switch(sense_buffer[2] & 0xf) {
case UNIT_ATTENTION:
- scsi_CDs[target].changed = 1;
+ scsi_CDs[target].device->changed = 1;
printk("Disc change detected.\n");
break;
case NOT_READY: /* This happens if there is no disc in drive */
@@ -106,53 +107,6 @@ retry:
};
return the_result;
}
-
-/*
- * This function checks to see if the media has been changed in the
- * CDROM drive. It is possible that we have already sensed a change,
- * or the drive may have sensed one and not yet reported it. We must
- * be ready for either case. This function always reports the current
- * value of the changed bit. If flag is 0, then the changed bit is reset.
- * This function could be done as an ioctl, but we would need to have
- * an inode for that to work, and we do not always have one.
- */
-
-int check_cdrom_media_change(int full_dev, int flag){
- int retval;
-
- lock_sr_ioctl();
-
- target = MINOR(full_dev);
-
- if (target >= NR_SR) {
- printk("CD-ROM request error: invalid device.\n");
- unlock_sr_ioctl();
- return 0;
- };
-
- sr_cmd[0] = TEST_UNIT_READY;
- sr_cmd[1] = (scsi_CDs[target].device->lun << 5) & 0xe0;
- sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
-
- retval = do_ioctl();
-
- if(retval){ /* Unable to test, unit probably not ready. This usually
- means there is no disc in the drive. Mark as changed,
- and we will figure it out later once the drive is
- available again. */
-
- scsi_CDs[target].changed = 1;
- unlock_sr_ioctl();
- return 1; /* This will force a flush, if called from
- check_disk_change */
- };
-
- retval = scsi_CDs[target].changed;
- if(!flag) scsi_CDs[target].changed = 0;
- unlock_sr_ioctl();
-
- return retval;
-}
int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg)
{
@@ -163,33 +117,6 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign
switch (cmd)
{
- /* linux-specific */
- case CDROMDOORUNLOCK:
- lock_sr_ioctl();
-
- sr_cmd[0] = ALLOW_MEDIUM_REMOVAL;
- sr_cmd[1] = scsi_CDs[target].device->lun << 5;
- sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
- sr_cmd[4] = SR_REMOVAL_ALLOW;
-
- result = do_ioctl();
-
- unlock_sr_ioctl();
- return result;
-
- case CDROMDOORLOCK:
- lock_sr_ioctl();
-
- sr_cmd[0] = ALLOW_MEDIUM_REMOVAL;
- sr_cmd[1] = scsi_CDs[target].device->lun << 5;
- sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
- sr_cmd[4] = SR_REMOVAL_PREVENT;
-
- result = do_ioctl();
-
- unlock_sr_ioctl();
- return result;
-
/* Sun-compatible */
case CDROMPAUSE:
lock_sr_ioctl();
diff --git a/kernel/chr_drv/Makefile b/kernel/chr_drv/Makefile
index 9be272f..39d3f51 100644
--- a/kernel/chr_drv/Makefile
+++ b/kernel/chr_drv/Makefile
@@ -40,6 +40,16 @@ dep:
cp tmp_make Makefile
### Dependencies:
+atixlmouse.o : atixlmouse.c /usr/include/linux/kernel.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
+ /usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/tty.h \
+ /usr/include/linux/termios.h /usr/include/asm/system.h /usr/include/linux/errno.h \
+ /usr/include/asm/io.h /usr/include/asm/segment.h /usr/include/asm/irq.h
busmouse.o : busmouse.c /usr/include/linux/kernel.h /usr/include/linux/sched.h \
/usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
/usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
@@ -96,6 +106,17 @@ mouse.o : mouse.c /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/inclu
/usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
/usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
/usr/include/linux/msdos_fs_sb.h /usr/include/linux/errno.h /usr/include/linux/mouse.h
+msbusmouse.o : msbusmouse.c /usr/include/linux/kernel.h /usr/include/linux/sched.h \
+ /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
+ /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
+ /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
+ /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
+ /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
+ /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
+ /usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/busmouse.h \
+ /usr/include/linux/tty.h /usr/include/linux/termios.h /usr/include/asm/system.h \
+ /usr/include/linux/errno.h /usr/include/asm/io.h /usr/include/asm/segment.h \
+ /usr/include/asm/irq.h
psaux.o : psaux.c /usr/include/linux/timer.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
@@ -125,7 +146,8 @@ serial.o : serial.c /usr/include/linux/errno.h /usr/include/linux/signal.h /usr/
/usr/include/linux/kernel.h /usr/include/linux/time.h /usr/include/linux/param.h \
/usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/timer.h \
/usr/include/linux/tty.h /usr/include/linux/termios.h /usr/include/asm/system.h \
- /usr/include/linux/serial.h /usr/include/asm/io.h /usr/include/asm/segment.h
+ /usr/include/linux/serial.h /usr/include/asm/io.h /usr/include/asm/segment.h \
+ /usr/include/asm/bitops.h
tty_io.o : tty_io.c /usr/include/linux/types.h /usr/include/linux/errno.h /usr/include/linux/signal.h \
/usr/include/linux/fcntl.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -136,7 +158,7 @@ tty_io.o : tty_io.c /usr/include/linux/types.h /usr/include/linux/errno.h /usr/i
/usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
/usr/include/linux/tty.h /usr/include/linux/termios.h /usr/include/asm/system.h \
/usr/include/linux/ctype.h /usr/include/linux/kd.h /usr/include/linux/string.h \
- /usr/include/asm/io.h /usr/include/asm/segment.h vt_kern.h
+ /usr/include/asm/segment.h /usr/include/asm/bitops.h vt_kern.h
tty_ioctl.o : tty_ioctl.c /usr/include/linux/types.h /usr/include/linux/termios.h \
/usr/include/linux/errno.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
diff --git a/kernel/chr_drv/atixlmouse.c b/kernel/chr_drv/atixlmouse.c
index 0851646..f42aaf6 100644
--- a/kernel/chr_drv/atixlmouse.c
+++ b/kernel/chr_drv/atixlmouse.c
@@ -146,11 +146,12 @@ struct file_operations atixl_busmouse_fops = {
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
+ NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
-long atixl_busmouse_init(long kmem_start)
+unsigned long atixl_busmouse_init(unsigned long kmem_start)
{
unsigned char a,b,c;
@@ -160,7 +161,6 @@ long atixl_busmouse_init(long kmem_start)
if (( a != b ) && ( a == c ))
printk("\nATI Inport ");
else{
- printk("No ATI bus mouse detected\n");
mouse.present = 0;
return kmem_start;
}
@@ -172,6 +172,7 @@ long atixl_busmouse_init(long kmem_start)
mouse.ready = 0;
mouse.buttons = mouse.latch_buttons = 0;
mouse.dx = mouse.dy = 0;
+ mouse.wait = NULL;
printk("Bus mouse detected and installed.\n");
return kmem_start;
}
diff --git a/kernel/chr_drv/busmouse.c b/kernel/chr_drv/busmouse.c
index 95126f6..c7f7750 100644
--- a/kernel/chr_drv/busmouse.c
+++ b/kernel/chr_drv/busmouse.c
@@ -137,11 +137,12 @@ struct file_operations bus_mouse_fops = {
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
+ NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
-long bus_mouse_init(long kmem_start)
+unsigned long bus_mouse_init(unsigned long kmem_start)
{
int i;
@@ -150,7 +151,6 @@ long bus_mouse_init(long kmem_start)
for (i = 0; i < 100000; i++)
/* busy loop */;
if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) {
- printk("No Logitech bus mouse detected.\n");
mouse.present = 0;
return kmem_start;
}
@@ -162,6 +162,7 @@ long bus_mouse_init(long kmem_start)
mouse.buttons = mouse.latch_buttons = 0x80;
mouse.dx = 0;
mouse.dy = 0;
+ mouse.wait = NULL;
printk("Logitech Bus mouse detected and installed.\n");
return kmem_start;
}
diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c
index 2a4d86e..6d0217a 100644
--- a/kernel/chr_drv/keyboard.c
+++ b/kernel/chr_drv/keyboard.c
@@ -1147,7 +1147,7 @@ unsigned int handle_diacr(unsigned int ch)
}
}
-#if defined KBD_FR || defined KBD_US || defined KBD_UK
+#if defined KBD_FR || defined KBD_US || defined KBD_UK || defined KBD_FR_LATIN1
static unsigned char num_table[] = "789-456+1230.";
#else
static unsigned char num_table[] = "789-456+1230,";
@@ -1351,12 +1351,16 @@ long no_idt[2] = {0, 0};
void hard_reset_now(void)
{
int i;
+ unsigned long * pg_dir;
sti();
+/* rebooting needs to touch the page at absolute addr 0 */
+ pg_dir = (unsigned long *) current->tss.cr3;
+ pg_dir[768] = 7; /* 0xC0000000 */
for (;;) {
for (i=0; i<100; i++) {
kb_wait();
- *((unsigned short *)0x472)=0x1234;
+ *((unsigned short *)0x472) = 0x1234;
outb(0xfe,0x64); /* pulse reset low */
}
__asm__("\tlidt _no_idt"::);
diff --git a/kernel/chr_drv/lp.c b/kernel/chr_drv/lp.c
index 4270b2d..e0e2f25 100644
--- a/kernel/chr_drv/lp.c
+++ b/kernel/chr_drv/lp.c
@@ -117,6 +117,7 @@ static struct file_operations lp_fops = {
NULL, /* lp_readdir */
NULL, /* lp_select */
NULL, /* lp_ioctl */
+ NULL, /* lp_mmap */
lp_open,
lp_release
};
diff --git a/kernel/chr_drv/mem.c b/kernel/chr_drv/mem.c
index 43ea86d..301d067 100644
--- a/kernel/chr_drv/mem.c
+++ b/kernel/chr_drv/mem.c
@@ -27,6 +27,7 @@ static int write_ram(struct inode * inode, struct file * file,char * buf, int co
static int read_mem(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos;
+ int read;
if (count < 0)
return -EINVAL;
@@ -34,14 +35,24 @@ static int read_mem(struct inode * inode, struct file * file,char * buf, int cou
return 0;
if (count > high_memory - p)
count = high_memory - p;
+ read = 0;
+ while (p < 4096 && count > 0) {
+ put_fs_byte(0,buf);
+ buf++;
+ p++;
+ count--;
+ read++;
+ }
memcpy_tofs(buf,(void *) p,count);
- file->f_pos += count;
- return count;
+ read += count;
+ file->f_pos += read;
+ return read;
}
static int write_mem(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned long p = file->f_pos;
+ int written;
if (count < 0)
return -EINVAL;
@@ -49,8 +60,17 @@ static int write_mem(struct inode * inode, struct file * file,char * buf, int co
return 0;
if (count > high_memory - p)
count = high_memory - p;
+ written = 0;
+ while (p < 4096 && count > 0) {
+ /* Hmm. Do something? */
+ buf++;
+ p++;
+ count--;
+ written++;
+ }
memcpy_fromfs((void *) p,buf,count);
- file->f_pos += count;
+ written += count;
+ file->f_pos += written;
return count;
}
@@ -82,7 +102,17 @@ static int write_port(struct inode * inode,struct file * file,char * buf, int co
return tmp-buf;
}
-static int read_zero(struct inode *node,struct file *file,char *buf,int count)
+static int read_null(struct inode * node,struct file * file,char * buf,int count)
+{
+ return 0;
+}
+
+static int write_null(struct inode * inode,struct file * file,char * buf, int count)
+{
+ return count;
+}
+
+static int read_zero(struct inode * node,struct file * file,char * buf,int count)
{
int left;
@@ -93,6 +123,11 @@ static int read_zero(struct inode *node,struct file *file,char *buf,int count)
return count;
}
+static int write_zero(struct inode * inode,struct file * file,char * buf, int count)
+{
+ return count;
+}
+
/*
* The memory devices use the full 32 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -101,7 +136,7 @@ static int read_zero(struct inode *node,struct file *file,char *buf,int count)
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
-static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
+static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
switch (orig) {
case 0:
@@ -121,60 +156,122 @@ static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int
#define read_kmem read_mem
#define write_kmem write_mem
-static int mem_read(struct inode * inode, struct file * file, char * buf, int count)
-{
- switch (MINOR(inode->i_rdev)) {
- case 0:
- return read_ram(inode,file,buf,count);
- case 1:
- return read_mem(inode,file,buf,count);
- case 2:
- return read_kmem(inode,file,buf,count);
- case 3:
- return 0; /* /dev/null */
- case 4:
- return read_port(inode,file,buf,count);
- case 5:
- return read_zero(inode,file,buf,count);
- default:
- return -ENODEV;
- }
-}
+static struct file_operations ram_fops = {
+ memory_lseek,
+ read_ram,
+ write_ram,
+ NULL, /* ram_readdir */
+ NULL, /* ram_select */
+ NULL, /* ram_ioctl */
+ NULL, /* ram_mmap */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
+
+static struct file_operations mem_fops = {
+ memory_lseek,
+ read_mem,
+ write_mem,
+ NULL, /* mem_readdir */
+ NULL, /* mem_select */
+ NULL, /* mem_ioctl */
+ NULL, /* mem_mmap */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
+
+static struct file_operations kmem_fops = {
+ memory_lseek,
+ read_kmem,
+ write_kmem,
+ NULL, /* kmem_readdir */
+ NULL, /* kmem_select */
+ NULL, /* kmem_ioctl */
+ NULL, /* kmem_mmap */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
-static int mem_write(struct inode * inode, struct file * file, char * buf, int count)
+static struct file_operations null_fops = {
+ memory_lseek,
+ read_null,
+ write_null,
+ NULL, /* null_readdir */
+ NULL, /* null_select */
+ NULL, /* null_ioctl */
+ NULL, /* null_mmap */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
+
+static struct file_operations port_fops = {
+ memory_lseek,
+ read_port,
+ write_port,
+ NULL, /* port_readdir */
+ NULL, /* port_select */
+ NULL, /* port_ioctl */
+ NULL, /* port_mmap */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
+
+static struct file_operations zero_fops = {
+ memory_lseek,
+ read_zero,
+ write_zero,
+ NULL, /* zero_readdir */
+ NULL, /* zero_select */
+ NULL, /* zero_ioctl */
+ NULL, /* zero_mmap */
+ NULL, /* no special open code */
+ NULL /* no special release code */
+};
+
+static int memory_open(struct inode * inode, struct file * filp)
{
switch (MINOR(inode->i_rdev)) {
case 0:
- return write_ram(inode,file,buf,count);
+ filp->f_op = &ram_fops;
+ break;
case 1:
- return write_mem(inode,file,buf,count);
+ filp->f_op = &mem_fops;
+ break;
case 2:
- return write_kmem(inode,file,buf,count);
+ filp->f_op = &kmem_fops;
+ break;
case 3:
- return count; /* /dev/null */
+ filp->f_op = &null_fops;
+ break;
case 4:
- return write_port(inode,file,buf,count);
+ filp->f_op = &port_fops;
+ break;
case 5:
- return count; /* /dev/zero */
+ filp->f_op = &zero_fops;
+ break;
default:
return -ENODEV;
}
+ if (filp->f_op && filp->f_op->open)
+ return filp->f_op->open(inode,filp);
+ return 0;
}
-static struct file_operations mem_fops = {
- mem_lseek,
- mem_read,
- mem_write,
- NULL, /* mem_readdir */
- NULL, /* mem_select */
- NULL, /* mem_ioctl */
- NULL, /* no special open code */
- NULL /* no special release code */
+static struct file_operations memory_fops = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ memory_open, /* just a selector for the real open */
+ NULL /* release */
};
long chr_dev_init(long mem_start, long mem_end)
{
- chrdev_fops[1] = &mem_fops;
+ chrdev_fops[1] = &memory_fops;
mem_start = tty_init(mem_start);
mem_start = lp_init(mem_start);
mem_start = mouse_init(mem_start);
diff --git a/kernel/chr_drv/mouse.c b/kernel/chr_drv/mouse.c
index ee9ca29..cecde8c 100644
--- a/kernel/chr_drv/mouse.c
+++ b/kernel/chr_drv/mouse.c
@@ -9,21 +9,28 @@
* changes incorporated into 0.97pl4
* by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
* See busmouse.c for particulars.
+ *
+ * Made things a lot mode modular - easy to compile in just one or two
+ * of the mouse drivers, as they are now completely independent. Linus.
*/
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mouse.h>
+/*
+ * note that you can remove any or all of the drivers by undefining
+ * the minor values in <linux/mouse.h>
+ */
extern struct file_operations bus_mouse_fops;
extern struct file_operations psaux_fops;
extern struct file_operations ms_bus_mouse_fops;
extern struct file_operations atixl_busmouse_fops;
-extern long bus_mouse_init(long);
-extern long psaux_init(long);
-extern long ms_bus_mouse_init(long);
-extern long atixl_busmouse_init(long);
+extern unsigned long bus_mouse_init(unsigned long);
+extern unsigned long psaux_init(unsigned long);
+extern unsigned long ms_bus_mouse_init(unsigned long);
+extern unsigned long atixl_busmouse_init(unsigned long);
static int mouse_open(struct inode * inode, struct file * file)
{
@@ -63,16 +70,25 @@ static struct file_operations mouse_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
+ NULL, /* mmap */
mouse_open,
NULL /* release */
};
-long mouse_init(long kmem_start)
+unsigned long mouse_init(unsigned long kmem_start)
{
+#ifdef BUSMOUSE_MINOR
kmem_start = bus_mouse_init(kmem_start);
+#endif
+#ifdef PSMOUSE_MINOR
kmem_start = psaux_init(kmem_start);
+#endif
+#ifdef MS_BUSMOUSE_MINOR
kmem_start = ms_bus_mouse_init(kmem_start);
+#endif
+#ifdef ATIXL_BUSMOUSE_MINOR
kmem_start = atixl_busmouse_init(kmem_start);
+#endif
chrdev_fops[10] = &mouse_fops;
return kmem_start;
}
diff --git a/kernel/chr_drv/msbusmouse.c b/kernel/chr_drv/msbusmouse.c
index f5a4b13..009a229 100644
--- a/kernel/chr_drv/msbusmouse.c
+++ b/kernel/chr_drv/msbusmouse.c
@@ -142,13 +142,14 @@ struct file_operations ms_bus_mouse_fops = {
NULL, /* mouse_readdir */
mouse_select, /* mouse_select */
NULL, /* mouse_ioctl */
+ NULL, /* mouse_mmap */
open_mouse,
release_mouse,
};
#define MS_DELAY 100000
-long ms_bus_mouse_init(long kmem_start)
+unsigned long ms_bus_mouse_init(unsigned long kmem_start)
{
register int mse_byte;
int i, delay_val, msfound = 1;
@@ -157,6 +158,7 @@ long ms_bus_mouse_init(long kmem_start)
mouse.active = mouse.ready = 0;
mouse.buttons = mouse.latch_buttons = 0x80;
mouse.dx = mouse.dy = 0;
+ mouse.wait = NULL;
if (inb(MS_MSE_SIGNATURE_PORT) == 0xde) {
for (delay_val=0; delay_val<MS_DELAY;)
delay_val++;
@@ -180,7 +182,6 @@ long ms_bus_mouse_init(long kmem_start)
}
}
if (msfound == 1) {
- printk("No Microsoft bus mouse detected.\n");
return kmem_start;
}
MS_MSE_INT_OFF();
diff --git a/kernel/chr_drv/psaux.c b/kernel/chr_drv/psaux.c
index 4256ec2..3287d8d 100644
--- a/kernel/chr_drv/psaux.c
+++ b/kernel/chr_drv/psaux.c
@@ -6,6 +6,14 @@
* Supports pointing devices attached to a PS/2 type
* Keyboard and Auxiliary Device Controller.
*
+ * Modified by Dean Troyer (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
+ * to perform (some of) the hardware initialization formerly done in
+ * setup.S by the BIOS
+ *
+ * Modified by Dean Troyer (troyer@saifr00.cfsat.Honeywell.COM) 09Oct92
+ * to perform the hardware initialization formerly done in setup.S by
+ * the BIOS. Mouse characteristic setup is now included.
+ *
*/
#include <linux/timer.h>
@@ -18,11 +26,35 @@
#include <asm/segment.h>
#include <asm/system.h>
+/* aux controller ports */
#define AUX_INPUT_PORT 0x60 /* Aux device output buffer */
#define AUX_OUTPUT_PORT 0x60 /* Aux device input buffer */
#define AUX_COMMAND 0x64 /* Aux device command buffer */
#define AUX_STATUS 0x64 /* Aux device status reg */
+/* aux controller status bits */
+#define AUX_OBUF_FULL 0x01 /* output buffer (from device) full */
+#define AUX_IBUF_FULL 0x02 /* input buffer (to device) full */
+
+/* aux controller commands */
+#define AUX_CMD_WRITE 0x60 /* value to write to controller */
+#define AUX_MAGIC_WRITE 0xd4 /* value to send aux device data */
+
+#define AUX_INTS_ON 0x47 /* enable controller interrupts */
+#define AUX_INTS_OFF 0x65 /* disable controller interrupts */
+
+#define AUX_DISABLE 0xa7 /* disable aux */
+#define AUX_ENABLE 0xa8 /* enable aux */
+
+/* aux device commands */
+#define AUX_SET_RES 0xe8 /* set resolution */
+#define AUX_SET_SCALE 0xe9 /* set scaling factor */
+#define AUX_SET_STREAM 0xea /* set stream mode */
+#define AUX_SET_SAMPLE 0xf3 /* set sample rate */
+#define AUX_ENABLE_DEV 0xf4 /* enable aux device */
+#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
+#define AUX_RESET 0xff /* reset aux device */
+
#define MAX_RETRIES 3
#define AUX_IRQ 12
#define AUX_BUF_SIZE 2048
@@ -44,6 +76,49 @@ static int aux_present = 0;
static int poll_status(void);
+/*
+ * Write to aux device
+ */
+
+static void aux_write_dev(int val)
+{
+ poll_status();
+ outb_p(AUX_MAGIC_WRITE,AUX_COMMAND); /* write magic cookie */
+ poll_status();
+ outb_p(val,AUX_OUTPUT_PORT); /* write data */
+
+}
+
+
+/*
+ * Write to device & handle returned ack
+ */
+
+static int aux_write_ack(int val)
+{
+ aux_write_dev(val); /* write the value to the device */
+ while ((inb(AUX_STATUS) & AUX_OBUF_FULL) == 0); /* wait for ack */
+ if ((inb(AUX_STATUS) & 0x20) == 0x20)
+ {
+ return (inb(AUX_INPUT_PORT));
+ }
+ return 0;
+}
+
+
+/*
+ * Write aux device command
+ */
+
+static void aux_write_cmd(int val)
+{
+ poll_status();
+ outb_p(AUX_CMD_WRITE,AUX_COMMAND);
+ poll_status();
+ outb_p(val,AUX_OUTPUT_PORT);
+}
+
+
static unsigned int get_from_queue()
{
unsigned int result;
@@ -87,11 +162,9 @@ static void aux_interrupt(int cpl)
static void release_aux(struct inode * inode, struct file * file)
{
poll_status();
- outb_p(0xa7,AUX_COMMAND); /* Disable Aux device */
- poll_status();
- outb_p(0x60,AUX_COMMAND);
- poll_status();
- outb_p(0x65,AUX_OUTPUT_PORT);
+ outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
+ aux_write_dev(AUX_DISABLE_DEV); /* disable aux device */
+ aux_write_cmd(AUX_INTS_OFF); /* disable controller ints */
free_irq(AUX_IRQ);
aux_busy = 0;
}
@@ -104,21 +177,20 @@ static void release_aux(struct inode * inode, struct file * file)
static int open_aux(struct inode * inode, struct file * file)
{
- if (aux_busy)
- return -EBUSY;
if (!aux_present)
return -EINVAL;
+ if (aux_busy)
+ return -EBUSY;
if (!poll_status())
return -EBUSY;
aux_busy = 1;
queue->head = queue->tail = 0; /* Flush input queue */
if (request_irq(AUX_IRQ, aux_interrupt))
return -EBUSY;
- outb_p(0x60,AUX_COMMAND); /* Write command */
- poll_status();
- outb_p(0x47,AUX_OUTPUT_PORT); /* Enable AUX and keyb interrupts */
+ aux_write_dev(AUX_ENABLE_DEV); /* enable aux device */
+ aux_write_cmd(AUX_INTS_ON); /* enable controller ints */
poll_status();
- outb_p(0xa8,AUX_COMMAND); /* Enable AUX */
+ outb_p(AUX_ENABLE,AUX_COMMAND); /* Enable Aux */
return 0;
}
@@ -134,7 +206,7 @@ static int write_aux(struct inode * inode, struct file * file, char * buffer, in
while (i--) {
if (!poll_status())
return -EIO;
- outb_p(0xd4,AUX_COMMAND);
+ outb_p(AUX_MAGIC_WRITE,AUX_COMMAND);
if (!poll_status())
return -EIO;
outb_p(get_fs_byte(buffer++),AUX_OUTPUT_PORT);
@@ -194,22 +266,29 @@ struct file_operations psaux_fops = {
NULL, /* readdir */
aux_select,
NULL, /* ioctl */
+ NULL, /* mmap */
open_aux,
release_aux,
};
-long psaux_init(long kmem_start)
+unsigned long psaux_init(unsigned long kmem_start)
{
if (aux_device_present != 0xaa) {
- printk("No PS/2 type pointing device detected.\n");
return kmem_start;
}
+ aux_write_ack(AUX_SET_RES);
+ aux_write_ack(0x03); /* set resultion to 8 counts/mm */
+ aux_write_ack(AUX_SET_SCALE);
+ aux_write_ack(0x02); /* set scaling to 2:1 */
+ aux_write_ack(AUX_SET_SAMPLE);
+ aux_write_ack(0x64); /* set sampling rate to 100/sec */
+ aux_write_ack(AUX_SET_STREAM); /* set stream mode */
printk("PS/2 type pointing device detected and installed.\n");
queue = (struct aux_queue *) kmem_start;
kmem_start += sizeof (struct aux_queue);
queue->head = queue->tail = 0;
- queue->proc_list = 0;
+ queue->proc_list = NULL;
aux_present = 1;
return kmem_start;
}
diff --git a/kernel/chr_drv/pty.c b/kernel/chr_drv/pty.c
index 516672f..31acd3a 100644
--- a/kernel/chr_drv/pty.c
+++ b/kernel/chr_drv/pty.c
@@ -20,30 +20,13 @@
#include <asm/system.h>
#include <asm/io.h>
-static void pty_close(struct tty_struct * tty, struct file * filp);
-
-int pty_open(struct tty_struct *tty, struct file * filp)
-{
- if (!tty || !tty->link)
- return -ENODEV;
- if (IS_A_PTY_MASTER(tty->line))
- tty->write = mpty_write;
- else
- tty->write = spty_write;
- tty->close = pty_close;
- wake_up(&tty->read_q.proc_list);
- if (filp->f_flags & O_NDELAY)
- return 0;
- while (!tty->link->count && !(current->signal & ~current->blocked))
- interruptible_sleep_on(&tty->link->read_q.proc_list);
- if (!tty->link->count)
- return -ERESTARTSYS;
- return 0;
-}
-
static void pty_close(struct tty_struct * tty, struct file * filp)
{
+ if (!tty)
+ return;
wake_up(&tty->read_q.proc_list);
+ if (!tty->link)
+ return;
wake_up(&tty->link->write_q.proc_list);
if (IS_A_PTY_MASTER(tty->line)) {
if (tty->link->pgrp > 0)
@@ -76,14 +59,24 @@ static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
* the write_queue. It copies the input to the output-queue of it's
* slave.
*/
-void mpty_write(struct tty_struct * tty)
+static void pty_write(struct tty_struct * tty)
{
if (tty->link)
pty_copy(tty,tty->link);
}
-void spty_write(struct tty_struct * tty)
+int pty_open(struct tty_struct *tty, struct file * filp)
{
- if (tty->link)
- pty_copy(tty,tty->link);
+ if (!tty || !tty->link)
+ return -ENODEV;
+ tty->write = tty->link->write = pty_write;
+ tty->close = tty->link->close = pty_close;
+ wake_up(&tty->read_q.proc_list);
+ if (filp->f_flags & O_NDELAY)
+ return 0;
+ while (!tty->link->count && !(current->signal & ~current->blocked))
+ interruptible_sleep_on(&tty->link->read_q.proc_list);
+ if (!tty->link->count)
+ return -ERESTARTSYS;
+ return 0;
}
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index 26eda93..e867647 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -27,18 +27,25 @@
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
+#include <asm/bitops.h>
#define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
+#define AUTO_IRQ
/*
- * rs_read_process - Bitfield of serial lines that have I/O processing
- * to be done at the next clock tick.
- * rs_write_timeout - Bitfield of serial lines that have a possible
- * write timeout pending. (In case the THRE
- * interrupt gets lost.)
+ * rs_event - Bitfield of serial lines that events pending
+ * to be processed at the next clock tick.
+ * rs_write_active - Bitfield of serial lines that are actively
+ * transmitting (and therefore have a
+ * write timeout pending, in case the
+ * THRE interrupt gets lost.)
+ * IRQ_ISR[] - Array to store the head of the ISR linked list
+ * for each IRQ.
*/
-static unsigned long rs_read_process = 0;
-static unsigned long rs_write_timeout = 0;
+static unsigned long rs_event = 0;
+static unsigned long rs_write_active = 0;
+
+static async_ISR IRQ_ISR[16];
static void UART_ISR_proc(async_ISR ISR, int line);
static void FourPort_ISR_proc(async_ISR ISR, int line);
@@ -85,9 +92,6 @@ struct async_struct rs_table[] = {
#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
-static async_ISR IRQ_ISR[16]; /* Used to store the head of the */
- /* ISR linked list chain for each IRQ */
-
/*
* This is used to figure out the divsor speeds and the timeouts
*/
@@ -95,6 +99,10 @@ static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 56000, 115200, 0 };
+static void startup(struct async_struct * info);
+static void shutdown(struct async_struct * info);
+static void rs_throttle(struct tty_struct * tty, int status);
+
static void send_break( struct async_struct * info)
{
unsigned short port;
@@ -106,145 +114,154 @@ static void send_break( struct async_struct * info)
current->timeout = jiffies + 25;
outb_p(inb_p(port) | UART_LCR_SBC, port);
schedule();
- outb_p(inb_p(port) & ~UART_LCR_SBC, port);
+ outb(inb_p(port) & ~UART_LCR_SBC, port);
}
-/*
- * There are several races here: we avoid most of them by disabling
- * timer_active for the crucial part of the process.. That's a good
- * idea anyway.
- *
- * The problem is that we have to output characters /both/ from interrupts
- * and from the normal write: the latter to be sure the interrupts start up
- * again. With serial lines, the interrupts can happen so often that the
- * races actually are noticeable.
- */
-static void send_intr(struct async_struct * info)
+static inline void rs_sched_event(int line,
+ struct async_struct *info,
+ int event)
{
- unsigned short port = info->port;
- int line = info->line;
- struct tty_queue * queue = &info->tty->write_q;
- int c, count = 0;
-
- if (info->tty->stopped)
- return;
-
- if (info->type == PORT_16550A)
- count = 16;
- else
- count = 1;
-
- rs_write_timeout &= ~(1 << line);
-
- if (inb_p(UART_LSR + info->port) & UART_LSR_THRE) {
- while (count-- && !info->tty->stopped) {
- if (queue->tail == queue->head)
- goto end_send;
- c = queue->buf[queue->tail];
- queue->tail++;
- queue->tail &= TTY_BUF_SIZE-1;
- outb(c, UART_TX + port);
- }
- }
- info->timer = jiffies + info->timeout;
- if (info->timer < timer_table[RS_TIMER].expires)
- timer_table[RS_TIMER].expires = info->timer;
- rs_write_timeout |= 1 << line;
- timer_active |= 1 << RS_TIMER;
-end_send:
- if (LEFT(queue) > WAKEUP_CHARS)
- wake_up(&queue->proc_list);
-}
-
-static void receive_intr(struct async_struct * info)
-{
- unsigned short port = info->port;
- struct tty_queue * queue = &info->tty->read_q;
- int head = queue->head;
- int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1);
- int count = 0;
-
- rs_read_process &= ~(1 << info->line);
- do {
- count++;
- queue->buf[head] = inb(UART_TX + port);
- if (head != maxhead) {
- head++;
- head &= TTY_BUF_SIZE-1;
- }
- } while (inb(UART_LSR + port) & UART_LSR_DR);
- queue->head = head;
- rs_read_process |= 1 << info->line;
+ info->event |= 1 << event;
+ rs_event |= 1 << line;
timer_table[RS_TIMER].expires = 0;
- timer_active |= 1<<RS_TIMER;
-}
-
-static void line_status_intr(struct async_struct * info)
-{
- unsigned char status = inb(UART_LSR + info->port);
-
-/* printk("line status: %02x\n",status); */
-}
-
-static void modem_status_intr(struct async_struct * info)
-{
- unsigned char status = inb(UART_MSR + info->port);
-
- if (!(info->tty->termios->c_cflag & CLOCAL)) {
- if (((status & (UART_MSR_DCD|UART_MSR_DDCD)) == UART_MSR_DDCD)
- && info->tty->pgrp > 0)
- kill_pg(info->tty->pgrp,SIGHUP,1);
-
- if (info->tty->termios->c_cflag & CRTSCTS)
- info->tty->stopped = !(status & UART_MSR_CTS);
-
- if (!info->tty->stopped)
- send_intr(info);
- }
+ timer_active |= 1 << RS_TIMER;
}
-static void (*jmp_table[4])(struct async_struct *) = {
- modem_status_intr,
- send_intr,
- receive_intr,
- line_status_intr
-};
-
/*
* This ISR handles the COM1-4 8250, 16450, and 16550A UART's. It is
* also called by the FourPort ISR, since the FourPort also uses the
* same National Semiconduct UART's, with some interrupt multiplexing
* thrown in.
+ *
+ * This routine assumes nobody else will be mucking with the tty
+ * queues its working on. It should be called with the interrupts
+ * disabled, since it is not reentrant, and it assumes it doesn't need
+ * to worry about other routines mucking about its data structures
+ * while it keeps copies of critical pointers in registers.
*/
static void UART_ISR_proc(async_ISR ISR, int line)
{
- unsigned char ident;
+ unsigned char status;
struct async_struct * info = rs_table + line;
+ struct tty_queue * queue;
+ int head, tail, count, ch;
+ int cflag, iflag;
+
+ /*
+ * Just like the LEFT(x) macro, except it uses the loal tail
+ * and head variables.
+ */
+#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
if (!info || !info->tty || !info->port)
- return;
- while (1) {
- ident = inb(UART_IIR + info->port) & 7;
- if (ident & 1)
- return;
- ident = ident >> 1;
- jmp_table[ident](info);
- }
-}
-
-/*
- * Again, we disable interrupts to be sure there aren't any races:
- * see send_intr for details.
- */
-static inline void do_rs_write(struct async_struct * info)
-{
- if (!info->tty || !info->port)
- return;
- if (EMPTY(&info->tty->write_q))
return;
- cli();
- send_intr(info);
- sti();
+ cflag = info->tty->termios->c_cflag;
+ iflag = info->tty->termios->c_iflag;
+
+ do {
+ restart:
+ status = inb(UART_LSR + info->port);
+ if (status & UART_LSR_DR) {
+ queue = &info->tty->read_q;
+ head = queue->head;
+ tail = queue->tail;
+ do {
+ ch = inb(UART_RX + info->port);
+ /*
+ * There must be at least 3 characters
+ * free in the queue; otherwise we punt.
+ */
+ if (VLEFT < 3)
+ continue;
+ if (status & (UART_LSR_BI |
+ UART_LSR_FE |
+ UART_LSR_PE)) {
+ if (status & (UART_LSR_BI)) {
+ if (info->flags & ASYNC_SAK)
+ rs_sched_event(line, info, RS_EVENT_DO_SAK);
+ else if (iflag & IGNBRK)
+ continue;
+ else if (iflag & BRKINT)
+ rs_sched_event(line, info, RS_EVENT_BREAK_INT);
+ else
+ ch = 0;
+ } else if (iflag & IGNPAR)
+ continue;
+ if (iflag & PARMRK) {
+ queue->buf[head++] = 0xff;
+ head &= TTY_BUF_SIZE-1;
+ queue->buf[head++] = 0;
+ head &= TTY_BUF_SIZE-1;
+ } else
+ ch = 0;
+ } else if ((iflag & PARMRK) && (ch == 0xff)) {
+ queue->buf[head++] = 0xff;
+ head &= TTY_BUF_SIZE-1;
+ }
+ queue->buf[head++] = ch;
+ head &= TTY_BUF_SIZE-1;
+ } while ((status = inb(UART_LSR + info->port)) &
+ UART_LSR_DR);
+ queue->head = head;
+ if ((VLEFT < RQ_THRESHOLD_LW)
+ && !set_bit(TTY_RQ_THROTTLED, &info->tty->flags))
+ rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL);
+ rs_sched_event(line, info, RS_EVENT_READ_PROCESS);
+ }
+ if ((status & UART_LSR_THRE) &&
+ !info->tty->stopped) {
+ queue = &info->tty->write_q;
+ head = queue->head;
+ tail = queue->tail;
+ if (head==tail && !info->x_char)
+ goto no_xmit;
+ if (info->x_char) {
+ outb_p(info->x_char, UART_TX + info->port);
+ info->x_char = 0;
+ } else {
+ count = info->xmit_fifo_size;
+ while (count--) {
+ if (tail == head)
+ break;
+ outb_p(queue->buf[tail++],
+ UART_TX + info->port);
+ tail &= TTY_BUF_SIZE-1;
+ }
+ }
+ queue->tail = tail;
+ if (VLEFT > WAKEUP_CHARS)
+ rs_sched_event(line, info,
+ RS_EVENT_WRITE_WAKEUP);
+ info->timer = jiffies + info->timeout;
+ if (info->timer < timer_table[RS_TIMER].expires)
+ timer_table[RS_TIMER].expires = info->timer;
+ rs_write_active |= 1 << line;
+ timer_active |= 1 << RS_TIMER;
+ }
+ no_xmit:
+ status = inb(UART_MSR + info->port);
+
+ if (!(cflag & CLOCAL) && (status & UART_MSR_DDCD)) {
+ if (!(status & UART_MSR_DCD))
+ rs_sched_event(line, info, RS_EVENT_HUP_PGRP);
+ }
+ /*
+ * Because of the goto statement, this block must be
+ * last. We have to skip the do/while test condition
+ * because the THRE interrupt has probably been lost.
+ */
+ if ((cflag & CRTSCTS) ||
+ ((status & UART_MSR_DSR) &&
+ !(cflag & CNORTSCTS))) {
+ if (info->tty->stopped) {
+ if (status & UART_MSR_CTS) {
+ info->tty->stopped = 0;
+ goto restart;
+ }
+ } else
+ info->tty->stopped = !(status & UART_MSR_CTS);
+ }
+ } while (!(inb(UART_IIR + info->port) & UART_IIR_NO_INT));
}
/*
@@ -256,11 +273,14 @@ static void FourPort_ISR_proc(async_ISR ISR, int line)
unsigned char ivec;
ivec = ~inb(ISR->port) & 0x0F;
- for (i = line; ivec; i++) {
- if (ivec & 1)
- UART_ISR_proc(ISR, i);
- ivec = ivec >> 1;
- }
+ do {
+ for (i = line; ivec; i++) {
+ if (ivec & 1)
+ UART_ISR_proc(ISR, i);
+ ivec = ivec >> 1;
+ }
+ ivec = ~inb(ISR->port) & 0x0F;
+ } while (ivec);
}
/*
@@ -276,6 +296,20 @@ static void rs_interrupt(int irq)
}
}
+#ifdef AUTO_IRQ
+/*
+ * This is the serial driver's interrupt routine while we are probing
+ * for submarines.
+ */
+static volatile int rs_irq_triggered;
+
+static void rs_probe(int irq)
+{
+ rs_irq_triggered = irq;
+ return;
+}
+#endif
+
/*
* This subroutine handles all of the timer functionality required for
* the serial ports.
@@ -291,95 +325,119 @@ static void rs_timer(void)
info = rs_table;
next_timeout = END_OF_TIME;
for (mask = 1 ; mask ; info++, mask <<= 1) {
- if ((mask > rs_read_process) &&
- (mask > rs_write_timeout))
+ if ((mask > rs_event) &&
+ (mask > rs_write_active))
break;
- if (mask & rs_read_process) {
- TTY_READ_FLUSH(info->tty);
- rs_read_process &= ~mask;
+ if (mask & rs_event) {
+ if (!clear_bit(RS_EVENT_READ_PROCESS, &info->event)) {
+ TTY_READ_FLUSH(info->tty);
+ }
+ if (!clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+ wake_up(&info->tty->write_q.proc_list);
+ }
+ if (!clear_bit(RS_EVENT_HUP_PGRP, &info->event)) {
+ if (info->tty->pgrp > 0)
+ kill_pg(info->tty->pgrp,SIGHUP,1);
+ }
+ if (!clear_bit(RS_EVENT_BREAK_INT, &info->event)) {
+ flush_input(info->tty);
+ flush_output(info->tty);
+ if (info->tty->pgrp > 0)
+ kill_pg(info->tty->pgrp,SIGINT,1);
+ }
+ if (!clear_bit(RS_EVENT_DO_SAK, &info->event)) {
+ do_SAK(info->tty);
+ }
+ cli();
+ if (info->event)
+ next_timeout = 0;
+ else
+ rs_event &= ~mask;
+ sti();
}
- if (mask & rs_write_timeout) {
- if (info->timer > jiffies) {
- rs_write_timeout &= ~mask;
- do_rs_write(info);
+ if (mask & rs_write_active) {
+ if (info->timer <= jiffies) {
+ rs_write_active &= ~mask;
+ rs_write(info->tty);
}
- if ((mask & rs_write_timeout) &&
+ if ((mask & rs_write_active) &&
(info->timer < next_timeout))
next_timeout = info->timer;
}
}
if (next_timeout != END_OF_TIME) {
timer_table[RS_TIMER].expires = next_timeout;
+#ifdef i386
+ /*
+ * This must compile to a single, atomic instruction.
+ * It does using 386 with GCC; if you're not sure, use
+ * the set_bit function, which is supposed to be atomic.
+ */
timer_active |= 1 << RS_TIMER;
+#else
+ set_bit(RS_TIMER, &timer_active);
+#endif
}
}
-static void init(struct async_struct * info)
+/*
+ * This routine gets called when tty_write has put something into
+ * the write_queue. It calls UART_ISR_proc to simulate an interrupt,
+ * which gets things going.
+ */
+void rs_write(struct tty_struct * tty)
{
- unsigned char status1, status2, scratch, scratch2;
- unsigned short port = info->port;
+ struct async_struct *info;
- /*
- * Check to see if a UART is really there.
- */
- scratch = inb_p(UART_MCR + port);
- outb_p(UART_MCR_LOOP | scratch, UART_MCR + port);
- scratch2 = inb_p(UART_MSR + port);
- outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
- status1 = inb_p(UART_MSR + port) & 0xF0;
- outb_p(scratch, UART_MCR + port);
- outb_p(scratch2, UART_MSR + port);
- if (status1 != 0x90) {
- info->type = PORT_UNKNOWN;
+ if (!tty || tty->stopped || EMPTY(&tty->write_q))
return;
+ info = rs_table + DEV_TO_SL(tty->line);
+ if (!test_bit(info->line, &rs_write_active)) {
+ cli();
+ UART_ISR_proc(info->ISR, info->line);
+ sti();
}
- if (!(info->flags & ASYNC_NOSCRATCH)) {
- scratch = inb(UART_SCR + port);
- outb_p(0xa5, UART_SCR + port);
- status1 = inb(UART_SCR + port);
- outb_p(0x5a, UART_SCR + port);
- status2 = inb(UART_SCR + port);
- outb_p(scratch, UART_SCR + port);
- } else {
- status1 = 0xa5;
- status2 = 0x5a;
- }
- if (status1 == 0xa5 && status2 == 0x5a) {
- outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port);
- scratch = inb(UART_IIR + port) >> 6;
- info->xmit_fifo_size = 1;
- switch (scratch) {
- case 0:
- info->type = PORT_16450;
- break;
- case 1:
- info->type = PORT_UNKNOWN;
- break;
- case 2:
- info->type = PORT_16550;
- break;
- case 3:
- info->type = PORT_16550A;
- info->xmit_fifo_size = 16;
- break;
- }
- } else
- info->type = PORT_8250;
- change_speed(info->line);
- outb_p(0x00, UART_IER + port); /* disable all intrs */
- outb_p(0x00, UART_MCR + port); /* reset DTR,RTS,OUT_2 */
- (void)inb(UART_RX + port); /* read data port to reset things (?) */
}
-/*
- * This routine gets called when tty_write has put something into
- * the write_queue. It must check wheter the queue is empty, and
- * set the interrupt register accordingly
- */
-void rs_write(struct tty_struct * tty)
+static void rs_throttle(struct tty_struct * tty, int status)
{
- do_rs_write(rs_table+DEV_TO_SL(tty->line));
+ struct async_struct *info;
+ unsigned char mcr;
+
+ printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
+ status, LEFT(&tty->read_q), LEFT(&tty->secondary));
+ switch (status) {
+ case TTY_THROTTLE_RQ_FULL:
+ info = rs_table + DEV_TO_SL(tty->line);
+ if (tty->termios->c_iflag & IXOFF) {
+ info->x_char = STOP_CHAR(tty);
+ } else if ((tty->termios->c_cflag & CRTSCTS) ||
+ ((inb(UART_MSR + info->port) & UART_MSR_DSR) &&
+ !(tty->termios->c_cflag & CNORTSCTS))) {
+ mcr = inb(UART_MCR + info->port);
+ mcr &= ~UART_MCR_RTS;
+ outb_p(mcr, UART_MCR + info->port);
+ }
+ break;
+ case TTY_THROTTLE_RQ_AVAIL:
+ info = rs_table + DEV_TO_SL(tty->line);
+ if (tty->termios->c_iflag & IXOFF) {
+ cli();
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ sti();
+ } else if ((tty->termios->c_cflag & CRTSCTS) ||
+ ((inb(UART_MSR + info->port) & UART_MSR_DSR) &&
+ !(tty->termios->c_cflag & CNORTSCTS))) {
+ mcr = inb(UART_MCR + info->port);
+ mcr |= UART_MCR_RTS;
+ outb_p(mcr, UART_MCR + info->port);
+ }
+ break;
+ }
}
/*
@@ -401,10 +459,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
info = rs_table + line;
if (!info->port)
return;
+ shutdown(info);
info->tty = 0;
- outb_p(0x00, UART_IER + info->port); /* disable all intrs */
- outb_p(0x00, UART_MCR + info->port); /* reset DTR, RTS, */
- outb(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */
ISR = info->ISR;
irq = ISR->irq;
if (irq == 2)
@@ -471,6 +527,28 @@ static void startup(struct async_struct * info)
outb_p(0x80, ICP);
(void) inb(ICP);
}
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ (void)inb_p(UART_LSR + port);
+ (void)inb_p(UART_RX + port);
+ (void)inb_p(UART_IIR + port);
+ (void)inb_p(UART_MSR + port);
+}
+
+static void shutdown(struct async_struct * info)
+{
+ unsigned short port = info->port;
+
+ outb_p(0x00, UART_IER + port); /* disable all intrs */
+ if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
+ outb_p(UART_MCR_DTR, UART_MCR + port);
+ else
+ /* reset DTR,RTS,OUT_2 */
+ outb_p(0x00, UART_MCR + port);
+ outb_p(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */
+ (void)inb(UART_RX + port); /* read data port to reset things */
}
void change_speed(unsigned int line)
@@ -484,6 +562,8 @@ void change_speed(unsigned int line)
if (line >= NR_PORTS)
return;
info = rs_table + line;
+ if (!info->tty || !info->tty->termios)
+ return;
cflag = info->tty->termios->c_cflag;
if (!(port = info->port))
return;
@@ -509,11 +589,11 @@ void change_speed(unsigned int line)
quot = 0;
info->timeout = 0;
}
- if (!quot)
- outb(0x00,UART_MCR + port);
- else if (!inb(UART_MCR + port))
- startup(info);
-/* byte size and parity */
+ if (!quot) {
+ shutdown(info);
+ return;
+ }
+ /* byte size and parity */
cval = cflag & (CSIZE | CSTOPB);
cval >>= 4;
if (cflag & PARENB)
@@ -569,10 +649,6 @@ static int set_serial_info(struct async_struct * info,
irq = ISR->irq;
if (irq == 2)
irq = 9;
- if (!new_irq)
- new_irq = irq;
- if (!new_port)
- new_port = info->port;
if (irq != new_irq) {
/*
* We need to change the IRQ for this board. OK, if
@@ -614,11 +690,10 @@ static int set_serial_info(struct async_struct * info,
}
cli();
if (new_port != info->port) {
- outb_p(0x00, UART_IER + info->port); /* disable all intrs */
- outb(0x00, UART_MCR + info->port); /* reset DTR, RTS, */
+ shutdown(info);
info->port = new_port;
- init(info);
startup(info);
+ change_speed(info->line);
}
sti();
return 0;
@@ -737,12 +812,13 @@ int rs_open(struct tty_struct *tty, struct file * filp)
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
info = rs_table + line;
- if (!info->port)
+ if (!info->port || !info->ISR->irq)
return -ENODEV;
info->tty = tty;
tty->write = rs_write;
tty->close = rs_close;
tty->ioctl = rs_ioctl;
+ tty->throttle = rs_throttle;
ISR = info->ISR;
irq = ISR->irq;
if (irq == 2)
@@ -771,15 +847,140 @@ int rs_open(struct tty_struct *tty, struct file * filp)
return 0;
}
+static void init(struct async_struct * info)
+{
+#ifdef AUTO_IRQ
+ unsigned char status1, status2, scratch, save_ICP=0;
+ unsigned short ICP=0, port = info->port;
+ unsigned long timeout;
+
+ /*
+ * Enable interrupts and see who answers
+ */
+ rs_irq_triggered = 0;
+ scratch = inb_p(UART_IER + port);
+ status1 = inb_p(UART_MCR + port);
+ if (info->flags & ASYNC_FOURPORT) {
+ outb_p(UART_MCR_DTR | UART_MCR_RTS, UART_MCR + port);
+ outb_p(0x0f,UART_IER + port); /* enable all intrs */
+ ICP = (port & 0xFE0) | 0x01F;
+ save_ICP = inb_p(ICP);
+ outb_p(0x80, ICP);
+ (void) inb(ICP);
+ } else {
+ outb_p(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2,
+ UART_MCR + port);
+ outb_p(0x0f,UART_IER + port); /* enable all intrs */
+ }
+ /*
+ * Next, clear the interrupt registers.
+ */
+ (void)inb_p(UART_LSR + port);
+ (void)inb_p(UART_RX + port);
+ (void)inb_p(UART_IIR + port);
+ (void)inb_p(UART_MSR + port);
+ timeout = jiffies+2;
+ while (timeout >= jiffies) {
+ if (rs_irq_triggered)
+ break;
+ }
+ /*
+ * Now check to see if we got any business, and clean up.
+ */
+ if (rs_irq_triggered) {
+ outb_p(0, UART_IER + port);
+ info->ISR->irq = rs_irq_triggered;
+ } else {
+ outb_p(scratch, UART_IER + port);
+ outb_p(status1, UART_MCR + port);
+ if (info->flags & ASYNC_FOURPORT)
+ outb_p(save_ICP, ICP);
+ info->type = PORT_UNKNOWN;
+ return;
+ }
+#else /* AUTO_IRQ */
+ unsigned char status1, status2, scratch, scratch2;
+ unsigned short port = info->port;
+
+ /*
+ * Check to see if a UART is really there.
+ */
+ scratch = inb_p(UART_MCR + port);
+ outb_p(UART_MCR_LOOP | scratch, UART_MCR + port);
+ scratch2 = inb_p(UART_MSR + port);
+ outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
+ status1 = inb_p(UART_MSR + port) & 0xF0;
+ outb_p(scratch, UART_MCR + port);
+ outb_p(scratch2, UART_MSR + port);
+ if (status1 != 0x90) {
+ info->type = PORT_UNKNOWN;
+ return;
+ }
+#endif /* AUTO_IRQ */
+
+ if (!(info->flags & ASYNC_NOSCRATCH)) {
+ scratch = inb(UART_SCR + port);
+ outb_p(0xa5, UART_SCR + port);
+ status1 = inb(UART_SCR + port);
+ outb_p(0x5a, UART_SCR + port);
+ status2 = inb(UART_SCR + port);
+ outb_p(scratch, UART_SCR + port);
+ } else {
+ status1 = 0xa5;
+ status2 = 0x5a;
+ }
+ if (status1 == 0xa5 && status2 == 0x5a) {
+ outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port);
+ scratch = inb(UART_IIR + port) >> 6;
+ info->xmit_fifo_size = 1;
+ switch (scratch) {
+ case 0:
+ info->type = PORT_16450;
+ break;
+ case 1:
+ info->type = PORT_UNKNOWN;
+ break;
+ case 2:
+ info->type = PORT_16550;
+ break;
+ case 3:
+ info->type = PORT_16550A;
+ info->xmit_fifo_size = 16;
+ break;
+ }
+ } else
+ info->type = PORT_8250;
+ startup(info);
+ change_speed(info->line);
+ shutdown(info);
+}
+
long rs_init(long kmem_start)
{
int i;
struct async_struct * info;
-
+#ifdef AUTO_IRQ
+ int irq_lines = 0;
+ struct sigaction sa;
+ /*
+ * We will be auto probing for irq's, so turn on interrupts now!
+ */
+ sti();
+
+ sa.sa_handler = rs_probe;
+ sa.sa_flags = (SA_INTERRUPT);
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+#endif
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
+
for (i = 0; i < 16; i++) {
IRQ_ISR[i] = 0;
+#ifdef AUTO_IRQ
+ if (!irqaction(i, &sa))
+ irq_lines |= 1 << i;
+#endif
}
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
info->line = i;
@@ -787,6 +988,8 @@ long rs_init(long kmem_start)
info->type = PORT_UNKNOWN;
info->timer = 0;
info->custom_divisor = 0;
+ info->x_char = 0;
+ info->event = 0;
if (!info->ISR->line) {
info->ISR->line = i;
info->ISR->refcnt = 0;
@@ -817,6 +1020,13 @@ long rs_init(long kmem_start)
break;
}
}
+#ifdef AUTO_IRQ
+ cli();
+ for (i = 0; i < 16; i++) {
+ if (irq_lines & (1 << i))
+ free_irq(i);
+ }
+#endif
return kmem_start;
}
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index 3e13096..4117fe7 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -33,9 +33,9 @@
#include <linux/mm.h>
#include <linux/string.h>
-#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
+#include <asm/bitops.h>
#include "vt_kern.h"
@@ -54,7 +54,7 @@ struct wait_queue * keypress_wait;
int initialize_tty_struct(struct tty_struct *tty, int line);
-void inline put_tty_queue(char c, struct tty_queue * queue)
+void put_tty_queue(char c, struct tty_queue * queue)
{
int head;
unsigned long flags;
@@ -68,7 +68,7 @@ void inline put_tty_queue(char c, struct tty_queue * queue)
__asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
}
-int inline get_tty_queue(struct tty_queue * queue)
+int get_tty_queue(struct tty_queue * queue)
{
int result = -1;
unsigned long flags;
@@ -82,7 +82,7 @@ int inline get_tty_queue(struct tty_queue * queue)
return result;
}
-void inline tty_write_flush(struct tty_struct * tty)
+void tty_write_flush(struct tty_struct * tty)
{
if (!tty->write || EMPTY(&tty->write_q))
return;
@@ -95,7 +95,7 @@ void inline tty_write_flush(struct tty_struct * tty)
void tty_read_flush(struct tty_struct * tty)
{
- if (EMPTY(&tty->read_q))
+ if (!tty || EMPTY(&tty->read_q))
return;
if (set_bit(TTY_READ_BUSY, &tty->flags))
return;
@@ -207,8 +207,9 @@ void copy_to_cooked(struct tty_struct * tty)
tty->stopped=1;
continue;
}
- if ((START_CHAR(tty) != __DISABLED_CHAR) &&
- (c==START_CHAR(tty))) {
+ if (((I_IXANY(tty)) && tty->stopped) ||
+ ((START_CHAR(tty) != __DISABLED_CHAR) &&
+ (c==START_CHAR(tty)))) {
tty->status_changed = 1;
tty->ctrl_status |= TIOCPKT_START;
tty->stopped=0;
@@ -258,7 +259,9 @@ void copy_to_cooked(struct tty_struct * tty)
if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW)
&& !clear_bit(TTY_RQ_THROTTLED, &tty->flags))
tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL);
-
+ if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
+ && !clear_bit(TTY_SQ_THROTTLED, &tty->flags))
+ tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
}
int is_ignored(int sig)
@@ -337,22 +340,21 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
minimum = nr;
/* deal with packet mode: First test for status change */
- if (tty->packet && tty->link && tty->link->status_changed)
- {
- put_fs_byte (tty->link->ctrl_status, b);
- tty->link->status_changed = 0;
- return (1);
- }
+ if (tty->packet && tty->link && tty->link->status_changed) {
+ put_fs_byte (tty->link->ctrl_status, b);
+ tty->link->status_changed = 0;
+ return 1;
+ }
/* now bump the buffer up one. */
- if (tty->packet)
- {
- put_fs_byte (0,b++);
- nr --;
- /* this really shouldn't happen, but we need to
+ if (tty->packet) {
+ put_fs_byte (0,b++);
+ nr--;
+ /* this really shouldn't happen, but we need to
put it here. */
- if (nr == 0) return (1);
- }
+ if (nr == 0)
+ return 1;
+ }
while (nr>0) {
TTY_READ_FLUSH(tty);
@@ -373,6 +375,13 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
break;
};
wake_up(&tty->read_q.proc_list);
+ /*
+ * If there is enough space in the secondary queue
+ * now, let the low-level driver know.
+ */
+ if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
+ && !clear_bit(TTY_SQ_THROTTLED, &tty->flags))
+ tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
if (b-buf >= minimum || !current->timeout)
break;
if (current->signal & ~current->blocked)
@@ -382,13 +391,6 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
TTY_READ_FLUSH(tty);
if (tty->link)
TTY_WRITE_FLUSH(tty->link);
- /*
- * If there is enough space in the secondary queue
- * now, let the low-level driver know.
- */
- if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
- && !clear_bit(TTY_SQ_THROTTLED, &tty->flags))
- tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
cli();
if (EMPTY(&tty->secondary))
interruptible_sleep_on(&tty->secondary.proc_list);
@@ -401,16 +403,13 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n
/* packet mode sticks in an extra 0. If that's all we've got,
we should count it a zero bytes. */
- if (tty->packet)
- {
- if ((b-buf) > 1)
- return b-buf;
- }
- else
- {
- if (b-buf)
- return b-buf;
- }
+ if (tty->packet) {
+ if ((b-buf) > 1)
+ return b-buf;
+ } else {
+ if (b-buf)
+ return b-buf;
+ }
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
@@ -558,8 +557,15 @@ static int tty_open(struct inode * inode, struct file * filp)
return retval;
}
if (IS_A_PTY(dev) && !tty_table[PTY_OTHER(dev)]) {
- o_tty = tty_table[PTY_OTHER(dev)] =
- (struct tty_struct *) get_free_page(GFP_USER);
+ o_tty = (struct tty_struct *) get_free_page(GFP_USER);
+ /*
+ * Check for race condition, since get_free_page may sleep.
+ */
+ if (tty_table[PTY_OTHER(dev)]) {
+ free_page((unsigned long) o_tty);
+ goto other_done;
+ }
+ tty_table[PTY_OTHER(dev)] = o_tty;
if (!o_tty) {
free_page((unsigned long)tty);
return -ENOMEM;
@@ -573,6 +579,7 @@ static int tty_open(struct inode * inode, struct file * filp)
tty->link = o_tty;
o_tty->link = tty;
}
+ other_done:
}
if (IS_A_PTY_MASTER(dev)) {
if (tty->count)
@@ -650,19 +657,18 @@ static void tty_release(struct inode * inode, struct file * filp)
return;
if (tty->close)
tty->close(tty, filp);
- if (!tty->count && (tty == redirect))
+ if (tty == redirect)
redirect = NULL;
- if (tty = tty->link)
- if (!tty->count && (tty == redirect))
- redirect = NULL;
- if (!tty->count && !(tty->link && tty->link->count)) {
- if (tty->link) {
- free_page((unsigned long) TTY_TABLE(PTY_OTHER(dev)));
- TTY_TABLE(PTY_OTHER(dev)) = 0;
- }
- free_page((unsigned long) TTY_TABLE(dev));
- TTY_TABLE(dev) = 0;
+ if (tty->link && !tty->link->count && (tty->link == redirect))
+ redirect = NULL;
+ if (tty->link) {
+ if (tty->link->count)
+ return;
+ free_page((unsigned long) TTY_TABLE(PTY_OTHER(dev)));
+ TTY_TABLE(PTY_OTHER(dev)) = 0;
}
+ free_page((unsigned long) TTY_TABLE(dev));
+ TTY_TABLE(dev) = 0;
}
static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
@@ -715,11 +721,53 @@ static struct file_operations tty_fops = {
NULL, /* tty_readdir */
tty_select,
tty_ioctl,
+ NULL, /* tty_mmap */
tty_open,
tty_release
};
/*
+ * This implements the "Secure Attention Key" --- the idea is to
+ * prevent trojan horses by killing all processes associated with this
+ * tty when the user hits the "Secure Attention Key". Required for
+ * super-paranoid applications --- see the Orange Book for more details.
+ *
+ * This code could be nicer; ideally it should send a HUP, wait a few
+ * seconds, then send a INT, and then a KILL signal. But you then
+ * have to coordinate with the init process, since all processes associated
+ * with the current tty must be dead before the new getty is allowed
+ * to spawn.
+ */
+void do_SAK( struct tty_struct *tty)
+{
+ struct task_struct **p;
+ int line = tty->line;
+ int session = tty->session;
+ int i;
+ struct file *filp;
+
+ flush_input(tty);
+ flush_output(tty);
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ if (!(*p))
+ continue;
+ if (((*p)->tty == line) ||
+ ((session > 0) && ((*p)->session == session)))
+ send_sig(SIGKILL, *p, 1);
+ else {
+ for (i=0; i < NR_FILE; i++) {
+ filp = (*p)->filp[i];
+ if (filp && (filp->f_op == &tty_fops) &&
+ (MINOR(filp->f_rdev) == line)) {
+ send_sig(SIGKILL, *p, 1);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
* This subroutine initializes a tty structure. We have to set up
* things correctly for each different type of tty.
*/
@@ -739,18 +787,17 @@ int initialize_tty_struct(struct tty_struct *tty, int line)
memset(tp, 0, sizeof(struct termios));
memcpy(tp->c_cc, INIT_C_CC, NCCS);
if (IS_A_CONSOLE(line)) {
- tp->c_iflag = ICRNL;
+ tp->c_iflag = ICRNL | IXON;
tp->c_oflag = OPOST | ONLCR;
- tp->c_cflag = B38400 | CS8;
- tp->c_lflag = IXON | ISIG | ICANON | ECHO |
- ECHOCTL | ECHOKE;
+ tp->c_cflag = B38400 | CS8 | CREAD;
+ tp->c_lflag = ISIG | ICANON | ECHO | ECHOCTL | ECHOKE;
} else if IS_A_SERIAL(line) {
- tp->c_cflag = B2400 | CS8;
+ tp->c_cflag = B2400 | CS8 | CREAD | HUPCL;
} else if IS_A_PTY_MASTER(line) {
- tp->c_cflag = B9600 | CS8;
+ tp->c_cflag = B9600 | CS8 | CREAD;
} else if IS_A_PTY_SLAVE(line) {
- tp->c_cflag = B9600 | CS8;
- tp->c_lflag = IXON | ISIG | ICANON;
+ tp->c_cflag = B9600 | CS8 | CREAD;
+ tp->c_lflag = ISIG | ICANON;
}
}
tty->termios = tty_termios[line];
diff --git a/kernel/exit.c b/kernel/exit.c
index 97ca319..6b7e65e 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -401,10 +401,12 @@ fake_volatile:
if (current->tty >= 0) {
tty = TTY_TABLE(current->tty);
- if (tty->pgrp > 0)
- kill_pg(tty->pgrp, SIGHUP, 1);
- tty->pgrp = -1;
- tty->session = 0;
+ if (tty) {
+ if (tty->pgrp > 0)
+ kill_pg(tty->pgrp, SIGHUP, 1);
+ tty->pgrp = -1;
+ tty->session = 0;
+ }
}
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p && (*p)->session == current->session)
diff --git a/kernel/irq.c b/kernel/irq.c
index e86ea79..b3105a3 100644
--- a/kernel/irq.c
+++ b/kernel/irq.c
@@ -152,7 +152,8 @@ int irqaction(unsigned int irq, struct sigaction * new)
return -EBUSY;
if (!new->sa_handler)
return -EINVAL;
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ save_flags(flags);
+ cli();
*sa = *new;
sa->sa_mask = 1;
if (sa->sa_flags & SA_INTERRUPT)
@@ -165,7 +166,7 @@ int irqaction(unsigned int irq, struct sigaction * new)
outb(inb_p(0x21) & ~(1<<2),0x21);
outb(inb_p(0xA1) & ~(1<<(irq-8)),0xA1);
}
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ restore_flags(flags);
return 0;
}
@@ -193,7 +194,8 @@ void free_irq(unsigned int irq)
printk("Trying to free free IRQ%d\n",irq);
return;
}
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ save_flags(flags);
+ cli();
if (irq < 8)
outb(inb_p(0x21) | (1<<irq),0x21);
else
@@ -203,7 +205,7 @@ void free_irq(unsigned int irq)
sa->sa_flags = 0;
sa->sa_mask = 0;
sa->sa_restorer = NULL;
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ restore_flags(flags);
}
extern void do_coprocessor_error(long,long);
diff --git a/kernel/math/Makefile b/kernel/math/Makefile
deleted file mode 100644
index b0d1eb3..0000000
--- a/kernel/math/Makefile
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# Makefile for the kernel math emulation routines
-#
-# 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) $(MATH_EMULATION) -S $<
-.s.o:
- $(AS) -c -o $*.o $<
-.c.o:
- $(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
-
-OBJS = emulate.o convert.o ea.o get_put.o \
- add.o mul.o div.o compare.o sqrt.o
-
-math.a: $(OBJS)
- $(AR) rcs math.a $(OBJS)
- sync
-
-clean:
- rm -f core *.o *.a tmp_make
- for i in *.c;do rm -f `basename $$i .c`.s;done
-
-dep:
- sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
- $(CPP) -M *.c >> tmp_make
- cp tmp_make Makefile
-
-### Dependencies:
-add.o : add.c /usr/include/linux/math_emu.h /usr/include/linux/sched.h /usr/include/linux/head.h \
- /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
- /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
- /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
- /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
- /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
- /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
- /usr/include/linux/resource.h /usr/include/linux/vm86.h
-compare.o : compare.c /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
- /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
- /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
- /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
- /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
- /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
- /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
- /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h
-convert.o : convert.c /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
- /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
- /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
- /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
- /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
- /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
- /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
- /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h
-div.o : div.c /usr/include/linux/math_emu.h /usr/include/linux/sched.h /usr/include/linux/head.h \
- /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
- /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
- /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
- /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
- /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
- /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
- /usr/include/linux/resource.h /usr/include/linux/vm86.h
-ea.o : ea.c /usr/include/linux/stddef.h /usr/include/linux/math_emu.h /usr/include/linux/sched.h \
- /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
- /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
- /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
- /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
- /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
- /usr/include/linux/kernel.h /usr/include/linux/signal.h /usr/include/linux/time.h \
- /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
- /usr/include/asm/segment.h
-emulate.o : emulate.c /usr/include/linux/signal.h /usr/include/linux/sched.h \
- /usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
- /usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
- /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
- /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h \
- /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h \
- /usr/include/linux/kernel.h /usr/include/linux/time.h /usr/include/linux/param.h \
- /usr/include/linux/resource.h /usr/include/linux/vm86.h
-get_put.o : get_put.c /usr/include/linux/signal.h /usr/include/linux/math_emu.h \
- /usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
- /usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
- /usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
- /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h /usr/include/linux/msdos_fs_i.h \
- /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h /usr/include/linux/msdos_fs_sb.h \
- /usr/include/linux/mm.h /usr/include/linux/kernel.h /usr/include/linux/time.h \
- /usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
- /usr/include/asm/segment.h
-mul.o : mul.c /usr/include/linux/math_emu.h /usr/include/linux/sched.h /usr/include/linux/head.h \
- /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
- /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
- /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
- /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
- /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
- /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
- /usr/include/linux/resource.h /usr/include/linux/vm86.h
-sqrt.o : sqrt.c /usr/include/linux/math_emu.h /usr/include/linux/sched.h /usr/include/linux/head.h \
- /usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
- /usr/include/linux/types.h /usr/include/linux/dirent.h /usr/include/linux/vfs.h \
- /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h /usr/include/linux/ext_fs_i.h \
- /usr/include/linux/msdos_fs_i.h /usr/include/linux/minix_fs_sb.h /usr/include/linux/ext_fs_sb.h \
- /usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
- /usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
- /usr/include/linux/resource.h /usr/include/linux/vm86.h
diff --git a/kernel/math/add.c b/kernel/math/add.c
deleted file mode 100644
index 563ec69..0000000
--- a/kernel/math/add.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * linux/kernel/math/add.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * temporary real addition routine.
- *
- * NOTE! These aren't exact: they are only 62 bits wide, and don't do
- * correct rounding. Fast hack. The reason is that we shift right the
- * values by two, in order not to have overflow (1 bit), and to be able
- * to move the sign into the mantissa (1 bit). Much simpler algorithms,
- * and 62 bits (61 really - no rounding) accuracy is usually enough. The
- * only time you should notice anything weird is when adding 64-bit
- * integers together. When using doubles (52 bits accuracy), the
- * 61-bit accuracy never shows at all.
- */
-
-#include <linux/math_emu.h>
-
-#define NEGINT(a) \
-__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \
- :"=r" (a->a),"=r" (a->b) \
- :"0" (a->a),"1" (a->b))
-
-static void signify(temp_real * a)
-{
- a->exponent += 2;
- __asm__("shrdl $2,%1,%0 ; shrl $2,%1"
- :"=r" (a->a),"=r" (a->b)
- :"0" (a->a),"1" (a->b));
- if (a->exponent < 0)
- NEGINT(a);
- a->exponent &= 0x7fff;
-}
-
-static void unsignify(temp_real * a)
-{
- if (!(a->a || a->b)) {
- a->exponent = 0;
- return;
- }
- a->exponent &= 0x7fff;
- if (a->b < 0) {
- NEGINT(a);
- a->exponent |= 0x8000;
- }
- while (a->b >= 0) {
- a->exponent--;
- __asm__("addl %0,%0 ; adcl %1,%1"
- :"=r" (a->a),"=r" (a->b)
- :"0" (a->a),"1" (a->b));
- }
-}
-
-void fadd(const temp_real * src1, const temp_real * src2, temp_real * result)
-{
- temp_real a,b;
- int x1,x2,shift;
-
- x1 = src1->exponent & 0x7fff;
- x2 = src2->exponent & 0x7fff;
- if (x1 > x2) {
- a = *src1;
- b = *src2;
- shift = x1-x2;
- } else {
- a = *src2;
- b = *src1;
- shift = x2-x1;
- }
- if (shift >= 64) {
- *result = a;
- return;
- }
- if (shift >= 32) {
- b.a = b.b;
- b.b = 0;
- shift -= 32;
- }
- __asm__("shrdl %4,%1,%0 ; shrl %4,%1"
- :"=r" (b.a),"=r" (b.b)
- :"0" (b.a),"1" (b.b),"c" ((char) shift));
- signify(&a);
- signify(&b);
- __asm__("addl %4,%0 ; adcl %5,%1"
- :"=r" (a.a),"=r" (a.b)
- :"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));
- unsignify(&a);
- *result = a;
-}
diff --git a/kernel/math/compare.c b/kernel/math/compare.c
deleted file mode 100644
index e3d676c..0000000
--- a/kernel/math/compare.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * linux/kernel/math/compare.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * temporary real comparison routines
- */
-
-#include <linux/math_emu.h>
-
-#define clear_Cx() (I387.swd &= ~0x4500)
-
-static void normalize(temp_real * a)
-{
- int i = a->exponent & 0x7fff;
- int sign = a->exponent & 0x8000;
-
- if (!(a->a || a->b)) {
- a->exponent = 0;
- return;
- }
- while (i && a->b >= 0) {
- i--;
- __asm__("addl %0,%0 ; adcl %1,%1"
- :"=r" (a->a),"=r" (a->b)
- :"0" (a->a),"1" (a->b));
- }
- a->exponent = i | sign;
-}
-
-void ftst(const temp_real * a)
-{
- temp_real b;
-
- clear_Cx();
- b = *a;
- normalize(&b);
- if (b.a || b.b || b.exponent) {
- if (b.exponent < 0)
- set_C0();
- } else
- set_C3();
-}
-
-void fcom(const temp_real * src1, const temp_real * src2)
-{
- temp_real a;
-
- a = *src1;
- a.exponent ^= 0x8000;
- fadd(&a,src2,&a);
- ftst(&a);
-}
-
-void fucom(const temp_real * src1, const temp_real * src2)
-{
- fcom(src1,src2);
-}
diff --git a/kernel/math/convert.c b/kernel/math/convert.c
deleted file mode 100644
index 3f5bbdf..0000000
--- a/kernel/math/convert.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * linux/kernel/math/convert.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-#include <linux/math_emu.h>
-
-/*
- * NOTE!!! There is some "non-obvious" optimisations in the temp_to_long
- * and temp_to_short conversion routines: don't touch them if you don't
- * know what's going on. They are the adding of one in the rounding: the
- * overflow bit is also used for adding one into the exponent. Thus it
- * looks like the overflow would be incorrectly handled, but due to the
- * way the IEEE numbers work, things are correct.
- *
- * There is no checking for total overflow in the conversions, though (ie
- * if the temp-real number simply won't fit in a short- or long-real.)
- */
-
-void short_to_temp(const short_real * a, temp_real * b)
-{
- if (!(*a & 0x7fffffff)) {
- b->a = b->b = 0;
- if (*a)
- b->exponent = 0x8000;
- else
- b->exponent = 0;
- return;
- }
- b->exponent = ((*a>>23) & 0xff)-127+16383;
- if (*a<0)
- b->exponent |= 0x8000;
- b->b = (*a<<8) | 0x80000000;
- b->a = 0;
-}
-
-void long_to_temp(const long_real * a, temp_real * b)
-{
- if (!a->a && !(a->b & 0x7fffffff)) {
- b->a = b->b = 0;
- if (a->b)
- b->exponent = 0x8000;
- else
- b->exponent = 0;
- return;
- }
- b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383;
- if (a->b<0)
- b->exponent |= 0x8000;
- b->b = 0x80000000 | (a->b<<11) | (((unsigned long)a->a)>>21);
- b->a = a->a<<11;
-}
-
-void temp_to_short(const temp_real * a, short_real * b)
-{
- if (!(a->exponent & 0x7fff)) {
- *b = (a->exponent)?0x80000000:0;
- return;
- }
- *b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000;
- if (a->exponent < 0)
- *b |= 0x80000000;
- *b |= (a->b >> 8) & 0x007fffff;
- switch (ROUNDING) {
- case ROUND_NEAREST:
- if ((a->b & 0xff) > 0x80)
- ++*b;
- break;
- case ROUND_DOWN:
- if ((a->exponent & 0x8000) && (a->b & 0xff))
- ++*b;
- break;
- case ROUND_UP:
- if (!(a->exponent & 0x8000) && (a->b & 0xff))
- ++*b;
- break;
- }
-}
-
-void temp_to_long(const temp_real * a, long_real * b)
-{
- if (!(a->exponent & 0x7fff)) {
- b->a = 0;
- b->b = (a->exponent)?0x80000000:0;
- return;
- }
- b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000;
- if (a->exponent < 0)
- b->b |= 0x80000000;
- b->b |= (a->b >> 11) & 0x000fffff;
- b->a = a->b << 21;
- b->a |= (a->a >> 11) & 0x001fffff;
- switch (ROUNDING) {
- case ROUND_NEAREST:
- if ((a->a & 0x7ff) > 0x400)
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- case ROUND_DOWN:
- if ((a->exponent & 0x8000) && (a->b & 0xff))
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- case ROUND_UP:
- if (!(a->exponent & 0x8000) && (a->b & 0xff))
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- }
-}
-
-void frndint(const temp_real * a, temp_real * b)
-{
- int shift = 16383 + 63 - (a->exponent & 0x7fff);
- unsigned long underflow;
-
- if ((shift < 0) || (shift == 16383+63)) {
- *b = *a;
- return;
- }
- b->a = b->b = underflow = 0;
- b->exponent = a->exponent;
- if (shift < 32) {
- b->b = a->b; b->a = a->a;
- } else if (shift < 64) {
- b->a = a->b; underflow = a->a;
- shift -= 32;
- b->exponent += 32;
- } else if (shift < 96) {
- underflow = a->b;
- shift -= 64;
- b->exponent += 64;
- } else {
- underflow = 1;
- shift = 0;
- }
- b->exponent += shift;
- __asm__("shrdl %2,%1,%0"
- :"=r" (underflow),"=r" (b->a)
- :"c" ((char) shift),"0" (underflow),"1" (b->a));
- __asm__("shrdl %2,%1,%0"
- :"=r" (b->a),"=r" (b->b)
- :"c" ((char) shift),"0" (b->a),"1" (b->b));
- __asm__("shrl %1,%0"
- :"=r" (b->b)
- :"c" ((char) shift),"0" (b->b));
- switch (ROUNDING) {
- case ROUND_NEAREST:
- __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b)
- ,"r" (0x7fffffff + (b->a & 1))
- ,"m" (*&underflow));
- break;
- case ROUND_UP:
- if ((b->exponent >= 0) && underflow)
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- case ROUND_DOWN:
- if ((b->exponent < 0) && underflow)
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- }
- if (b->a || b->b)
- while (b->b >= 0) {
- b->exponent--;
- __asm__("addl %0,%0 ; adcl %1,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- }
- else
- b->exponent = 0;
-}
-
-void real_to_int(const temp_real * a, temp_int * b)
-{
- int shift = 16383 + 63 - (a->exponent & 0x7fff);
- unsigned long underflow;
-
- b->a = b->b = underflow = 0;
- b->sign = (a->exponent < 0);
- if (shift < 0) {
- set_OE();
- return;
- }
- if (shift < 32) {
- b->b = a->b; b->a = a->a;
- } else if (shift < 64) {
- b->a = a->b; underflow = a->a;
- shift -= 32;
- } else if (shift < 96) {
- underflow = a->b;
- shift -= 64;
- } else {
- underflow = 1;
- shift = 0;
- }
- __asm__("shrdl %2,%1,%0"
- :"=r" (underflow),"=r" (b->a)
- :"c" ((char) shift),"0" (underflow),"1" (b->a));
- __asm__("shrdl %2,%1,%0"
- :"=r" (b->a),"=r" (b->b)
- :"c" ((char) shift),"0" (b->a),"1" (b->b));
- __asm__("shrl %1,%0"
- :"=r" (b->b)
- :"c" ((char) shift),"0" (b->b));
- switch (ROUNDING) {
- case ROUND_NEAREST:
- __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b)
- ,"r" (0x7fffffff + (b->a & 1))
- ,"m" (*&underflow));
- break;
- case ROUND_UP:
- if (!b->sign && underflow)
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- case ROUND_DOWN:
- if (b->sign && underflow)
- __asm__("addl $1,%0 ; adcl $0,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- break;
- }
-}
-
-void int_to_real(const temp_int * a, temp_real * b)
-{
- b->a = a->a;
- b->b = a->b;
- if (b->a || b->b)
- b->exponent = 16383 + 63 + (a->sign? 0x8000:0);
- else {
- b->exponent = 0;
- return;
- }
- while (b->b >= 0) {
- b->exponent--;
- __asm__("addl %0,%0 ; adcl %1,%1"
- :"=r" (b->a),"=r" (b->b)
- :"0" (b->a),"1" (b->b));
- }
-}
diff --git a/kernel/math/div.c b/kernel/math/div.c
deleted file mode 100644
index 55ee711..0000000
--- a/kernel/math/div.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * linux/kernel/math/div.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * temporary real division routine.
- */
-
-#include <linux/math_emu.h>
-
-static void shift_left(int * c)
-{
- __asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
- "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
- "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
- "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
- ::"r" ((long) c):"ax");
-}
-
-static void shift_right(int * c)
-{
- __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
- ::"r" ((long) c));
-}
-
-static int try_sub(int * a, int * b)
-{
- char ok;
-
- __asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t"
- "movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t"
- "movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t"
- "movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t"
- "setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b));
- return ok;
-}
-
-static void div64(int * a, int * b, int * c)
-{
- int tmp[4];
- int i;
- unsigned int mask = 0;
-
- c += 4;
- for (i = 0 ; i<64 ; i++) {
- if (!(mask >>= 1)) {
- c--;
- mask = 0x80000000;
- }
- tmp[0] = a[0]; tmp[1] = a[1];
- tmp[2] = a[2]; tmp[3] = a[3];
- if (try_sub(b,tmp)) {
- *c |= mask;
- a[0] = tmp[0]; a[1] = tmp[1];
- a[2] = tmp[2]; a[3] = tmp[3];
- }
- shift_right(b);
- }
-}
-
-void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
-{
- int i,sign;
- int a[4],b[4],tmp[4] = {0,0,0,0};
-
- sign = (src1->exponent ^ src2->exponent) & 0x8000;
- if (!(src2->a || src2->b)) {
- set_ZE();
- return;
- }
- i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383;
- if (i<0) {
- set_UE();
- result->exponent = sign;
- result->a = result->b = 0;
- return;
- }
- a[0] = a[1] = 0;
- a[2] = src1->a;
- a[3] = src1->b;
- b[0] = b[1] = 0;
- b[2] = src2->a;
- b[3] = src2->b;
- while (b[3] >= 0) {
- i++;
- shift_left(b);
- }
- div64(a,b,tmp);
- if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
- while (i && tmp[3] >= 0) {
- i--;
- shift_left(tmp);
- }
- if (tmp[3] >= 0)
- set_DE();
- } else
- i = 0;
- if (i>0x7fff) {
- set_OE();
- return;
- }
- if (tmp[0] || tmp[1])
- set_PE();
- result->exponent = i | sign;
- result->a = tmp[2];
- result->b = tmp[3];
-}
diff --git a/kernel/math/ea.c b/kernel/math/ea.c
deleted file mode 100644
index dba41ff..0000000
--- a/kernel/math/ea.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * linux/kernel/math/ea.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * Calculate the effective address.
- */
-
-#include <linux/stddef.h>
-#include <linux/math_emu.h>
-
-#include <asm/segment.h>
-
-static int __regoffset[] = {
- 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 *)(__regoffset[(x)]+(char *) info))
-
-static char * sib(struct info * info, int mod)
-{
- unsigned char ss,index,base;
- long offset = 0;
-
- base = get_fs_byte((char *) EIP);
- EIP++;
- ss = base >> 6;
- index = (base >> 3) & 7;
- base &= 7;
- if (index == 4)
- offset = 0;
- else
- offset = REG(index);
- offset <<= ss;
- if (mod || base != 5)
- offset += REG(base);
- if (mod == 1) {
- offset += (signed char) get_fs_byte((char *) EIP);
- EIP++;
- } else if (mod == 2 || base == 5) {
- offset += (signed) get_fs_long((unsigned long *) EIP);
- EIP += 4;
- }
- I387.foo = offset;
- I387.fos = 0x17;
- return (char *) offset;
-}
-
-char * ea(struct info * info, unsigned short code)
-{
- unsigned char mod,rm;
- long * tmp;
- int offset = 0;
-
- mod = (code >> 6) & 3;
- rm = code & 7;
- if (rm == 4 && mod != 3)
- return sib(info,mod);
- if (rm == 5 && !mod) {
- offset = get_fs_long((unsigned long *) EIP);
- EIP += 4;
- I387.foo = offset;
- I387.fos = 0x17;
- return (char *) offset;
- }
- tmp = & REG(rm);
- switch (mod) {
- case 0: offset = 0; break;
- case 1:
- offset = (signed char) get_fs_byte((char *) EIP);
- EIP++;
- break;
- case 2:
- offset = (signed) get_fs_long((unsigned long *) EIP);
- EIP += 4;
- break;
- case 3:
- math_abort(info,SIGILL);
- }
- I387.foo = offset;
- I387.fos = 0x17;
- return offset + (char *) *tmp;
-}
diff --git a/kernel/math/emulate.c b/kernel/math/emulate.c
deleted file mode 100644
index 9c86a6b..0000000
--- a/kernel/math/emulate.c
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * linux/kernel/math/emulate.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
- * even for soft-float, unless you use bruce evans' patches. The patches
- * are great, but they have to be re-applied for every version, and the
- * library is different for soft-float and 80387. So emulation is more
- * practical, even though it's slower.
- *
- * 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
- * about add/sub/mul/div. Urgel. I should find some good source, but I'll
- * just fake up something.
- *
- * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
- * test every possible combination.
- */
-
-/*
- * This file is full of ugly macros etc: one problem was that gcc simply
- * didn't want to make the structures as they should be: it has to try to
- * align them. Sickening code, but at least I've hidden the ugly things
- * in this one file: the other files don't need to know about these things.
- *
- * The other files also don't care about ST(x) etc - they just get addresses
- * to 80-bit temporary reals, and do with them as they please. I wanted to
- * hide most of the 387-specific things here.
- */
-
-#ifdef KERNEL_MATH_EMULATION
-
-#include <linux/signal.h>
-
-#define __ALIGNED_TEMP_REAL 1
-#include <linux/math_emu.h>
-#include <linux/kernel.h>
-#include <asm/segment.h>
-
-#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
-#define ST(x) (*__st((x)))
-#define PST(x) ((const temp_real *) __st((x)))
-
-/*
- * We don't want these inlined - it gets too messy in the machine-code.
- */
-static void fpop(void);
-static void fpush(void);
-static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
-static temp_real_unaligned * __st(int i);
-
-static void do_emu(struct info * info)
-{
- unsigned short code;
- temp_real tmp;
- char * address;
-
- if (I387.cwd & I387.swd & 0x3f)
- I387.swd |= 0x8000;
- else
- I387.swd &= 0x7fff;
- ORIG_EIP = EIP;
-/* We cannot handle emulation in v86-mode */
- if (EFLAGS & 0x00020000)
- math_abort(info,SIGILL);
-/* 0x0007 means user code space */
- if (CS != 0x000F) {
- printk("math_emulate: %04x:%08x\n\r",CS,EIP);
- panic("Math emulation needed in kernel");
- }
- code = get_fs_word((unsigned short *) EIP);
- bswapw(code);
- code &= 0x7ff;
- I387.fip = EIP;
- *(unsigned short *) &I387.fcs = CS;
- *(1+(unsigned short *) &I387.fcs) = code;
- EIP += 2;
- switch (code) {
- case 0x1d0: /* fnop */
- return;
- case 0x1d1: case 0x1d2: case 0x1d3:
- case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
- math_abort(info,SIGILL);
- case 0x1e0:
- ST(0).exponent ^= 0x8000;
- return;
- case 0x1e1:
- ST(0).exponent &= 0x7fff;
- return;
- case 0x1e2: case 0x1e3:
- math_abort(info,SIGILL);
- case 0x1e4:
- ftst(PST(0));
- return;
- case 0x1e5:
- printk("fxam not implemented\n\r");
- math_abort(info,SIGILL);
- case 0x1e6: case 0x1e7:
- math_abort(info,SIGILL);
- case 0x1e8:
- fpush();
- ST(0) = CONST1;
- return;
- case 0x1e9:
- fpush();
- ST(0) = CONSTL2T;
- return;
- case 0x1ea:
- fpush();
- ST(0) = CONSTL2E;
- return;
- case 0x1eb:
- fpush();
- ST(0) = CONSTPI;
- return;
- case 0x1ec:
- fpush();
- ST(0) = CONSTLG2;
- return;
- case 0x1ed:
- fpush();
- ST(0) = CONSTLN2;
- return;
- case 0x1ee:
- fpush();
- ST(0) = CONSTZ;
- return;
- case 0x1ef:
- math_abort(info,SIGILL);
- case 0x1fa:
- fsqrt(PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
- case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
- case 0x1f8: case 0x1f9: case 0x1fb: case 0x1fd:
- case 0x1fe: case 0x1ff:
- printk("%04x fxxx not implemented\n\r",code + 0xd800);
- math_abort(info,SIGILL);
- case 0x1fc:
- frndint(PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x2e9:
- fucom(PST(1),PST(0));
- fpop(); fpop();
- return;
- case 0x3d0: case 0x3d1:
- return;
- case 0x3e2:
- I387.swd &= 0x7f00;
- return;
- case 0x3e3:
- I387.cwd = 0x037f;
- I387.swd = 0x0000;
- I387.twd = 0x0000;
- return;
- case 0x3e4:
- return;
- case 0x6d9:
- fcom(PST(1),PST(0));
- fpop(); fpop();
- return;
- case 0x7e0:
- *(short *) &EAX = I387.swd;
- return;
- }
- switch (code >> 3) {
- case 0x18:
- fadd(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x19:
- fmul(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x1a:
- fcom(PST(code & 7),PST(0));
- return;
- case 0x1b:
- fcom(PST(code & 7),PST(0));
- fpop();
- return;
- case 0x1c:
- real_to_real(&ST(code & 7),&tmp);
- tmp.exponent ^= 0x8000;
- fadd(PST(0),&tmp,&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x1d:
- ST(0).exponent ^= 0x8000;
- fadd(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x1e:
- fdiv(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x1f:
- fdiv(PST(code & 7),PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x38:
- fpush();
- ST(0) = ST((code+1) & 7);
- return;
- case 0x39:
- fxchg(&ST(0),&ST(code & 7));
- return;
- case 0x3b:
- ST(code & 7) = ST(0);
- fpop();
- return;
- case 0x98:
- fadd(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- return;
- case 0x99:
- fmul(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- return;
- case 0x9a:
- fcom(PST(code & 7),PST(0));
- return;
- case 0x9b:
- fcom(PST(code & 7),PST(0));
- fpop();
- return;
- case 0x9c:
- ST(code & 7).exponent ^= 0x8000;
- fadd(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- return;
- case 0x9d:
- real_to_real(&ST(0),&tmp);
- tmp.exponent ^= 0x8000;
- fadd(PST(code & 7),&tmp,&tmp);
- real_to_real(&tmp,&ST(code & 7));
- return;
- case 0x9e:
- fdiv(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- return;
- case 0x9f:
- fdiv(PST(code & 7),PST(0),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- return;
- case 0xb8:
- printk("ffree not implemented\n\r");
- math_abort(info,SIGILL);
- case 0xb9:
- fxchg(&ST(0),&ST(code & 7));
- return;
- case 0xba:
- ST(code & 7) = ST(0);
- return;
- case 0xbb:
- ST(code & 7) = ST(0);
- fpop();
- return;
- case 0xbc:
- fucom(PST(code & 7),PST(0));
- return;
- case 0xbd:
- fucom(PST(code & 7),PST(0));
- fpop();
- return;
- case 0xd8:
- fadd(PST(code & 7),PST(0),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- fpop();
- return;
- case 0xd9:
- fmul(PST(code & 7),PST(0),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- fpop();
- return;
- case 0xda:
- fcom(PST(code & 7),PST(0));
- fpop();
- return;
- case 0xdc:
- ST(code & 7).exponent ^= 0x8000;
- fadd(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- fpop();
- return;
- case 0xdd:
- real_to_real(&ST(0),&tmp);
- tmp.exponent ^= 0x8000;
- fadd(PST(code & 7),&tmp,&tmp);
- real_to_real(&tmp,&ST(code & 7));
- fpop();
- return;
- case 0xde:
- fdiv(PST(0),PST(code & 7),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- fpop();
- return;
- case 0xdf:
- fdiv(PST(code & 7),PST(0),&tmp);
- real_to_real(&tmp,&ST(code & 7));
- fpop();
- return;
- case 0xf8:
- printk("ffree not implemented\n\r");
- math_abort(info,SIGILL);
- fpop();
- return;
- case 0xf9:
- fxchg(&ST(0),&ST(code & 7));
- return;
- case 0xfa:
- case 0xfb:
- ST(code & 7) = ST(0);
- fpop();
- return;
- }
- switch ((code>>3) & 0xe7) {
- case 0x22:
- put_short_real(PST(0),info,code);
- return;
- case 0x23:
- put_short_real(PST(0),info,code);
- fpop();
- return;
- case 0x24:
- address = ea(info,code);
- for (code = 0 ; code < 7 ; code++) {
- ((long *) & I387)[code] =
- get_fs_long((unsigned long *) address);
- address += 4;
- }
- return;
- case 0x25:
- address = ea(info,code);
- *(unsigned short *) &I387.cwd =
- get_fs_word((unsigned short *) address);
- return;
- case 0x26:
- address = ea(info,code);
- verify_area(address,28);
- for (code = 0 ; code < 7 ; code++) {
- put_fs_long( ((long *) & I387)[code],
- (unsigned long *) address);
- address += 4;
- }
- return;
- case 0x27:
- address = ea(info,code);
- verify_area(address,2);
- put_fs_word(I387.cwd,(short *) address);
- return;
- case 0x62:
- put_long_int(PST(0),info,code);
- return;
- case 0x63:
- put_long_int(PST(0),info,code);
- fpop();
- return;
- case 0x65:
- fpush();
- get_temp_real(&tmp,info,code);
- real_to_real(&tmp,&ST(0));
- return;
- case 0x67:
- put_temp_real(PST(0),info,code);
- fpop();
- return;
- case 0xa2:
- put_long_real(PST(0),info,code);
- return;
- case 0xa3:
- put_long_real(PST(0),info,code);
- fpop();
- return;
- case 0xa4:
- address = ea(info,code);
- for (code = 0 ; code < 27 ; code++) {
- ((long *) & I387)[code] =
- get_fs_long((unsigned long *) address);
- address += 4;
- }
- return;
- case 0xa6:
- address = ea(info,code);
- verify_area(address,108);
- for (code = 0 ; code < 27 ; code++) {
- put_fs_long( ((long *) & I387)[code],
- (unsigned long *) address);
- address += 4;
- }
- I387.cwd = 0x037f;
- I387.swd = 0x0000;
- I387.twd = 0x0000;
- return;
- case 0xa7:
- address = ea(info,code);
- verify_area(address,2);
- put_fs_word(I387.swd,(short *) address);
- return;
- case 0xe2:
- put_short_int(PST(0),info,code);
- return;
- case 0xe3:
- put_short_int(PST(0),info,code);
- fpop();
- return;
- case 0xe4:
- fpush();
- get_BCD(&tmp,info,code);
- real_to_real(&tmp,&ST(0));
- return;
- case 0xe5:
- fpush();
- get_longlong_int(&tmp,info,code);
- real_to_real(&tmp,&ST(0));
- return;
- case 0xe6:
- put_BCD(PST(0),info,code);
- fpop();
- return;
- case 0xe7:
- put_longlong_int(PST(0),info,code);
- fpop();
- return;
- }
- switch (code >> 9) {
- case 0:
- get_short_real(&tmp,info,code);
- break;
- case 1:
- get_long_int(&tmp,info,code);
- break;
- case 2:
- get_long_real(&tmp,info,code);
- break;
- case 4:
- get_short_int(&tmp,info,code);
- }
- switch ((code>>3) & 0x27) {
- case 0:
- fadd(&tmp,PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 1:
- fmul(&tmp,PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 2:
- fcom(&tmp,PST(0));
- return;
- case 3:
- fcom(&tmp,PST(0));
- fpop();
- return;
- case 4:
- tmp.exponent ^= 0x8000;
- fadd(&tmp,PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 5:
- ST(0).exponent ^= 0x8000;
- fadd(&tmp,PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 6:
- fdiv(PST(0),&tmp,&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- case 7:
- fdiv(&tmp,PST(0),&tmp);
- real_to_real(&tmp,&ST(0));
- return;
- }
- if ((code & 0x138) == 0x100) {
- fpush();
- real_to_real(&tmp,&ST(0));
- return;
- }
- printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
- math_abort(info,SIGFPE);
-}
-
-void math_emulate(long ___false)
-{
- if (!current->used_math) {
- current->used_math = 1;
- I387.cwd = 0x037f;
- I387.swd = 0x0000;
- I387.twd = 0x0000;
- }
- do_emu((struct info *) &___false);
-}
-
-void __math_abort(struct info * info, unsigned int signal)
-{
- EIP = ORIG_EIP;
- send_sig(signal,current,1);
- __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
-}
-
-static void fpop(void)
-{
- unsigned long tmp;
-
- tmp = I387.swd & 0xffffc7ff;
- I387.swd += 0x00000800;
- I387.swd &= 0x00003800;
- I387.swd |= tmp;
-}
-
-static void fpush(void)
-{
- unsigned long tmp;
-
- tmp = I387.swd & 0xffffc7ff;
- I387.swd += 0x00003800;
- I387.swd &= 0x00003800;
- I387.swd |= tmp;
-}
-
-static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
-{
- temp_real_unaligned c;
-
- c = *a;
- *a = *b;
- *b = c;
-}
-
-static temp_real_unaligned * __st(int i)
-{
- i += I387.swd >> 11;
- i &= 7;
- return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
-}
-
-#else /* no math emulation */
-
-#include <linux/signal.h>
-#include <linux/sched.h>
-
-void math_emulate(long ___false)
-{
- send_sig(SIGFPE,current,1);
- schedule();
-}
-
-#endif /* KERNEL_MATH_EMULATION */
diff --git a/kernel/math/get_put.c b/kernel/math/get_put.c
deleted file mode 100644
index bb603cb..0000000
--- a/kernel/math/get_put.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * linux/kernel/math/get_put.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * This file handles all accesses to user memory: getting and putting
- * ints/reals/BCD etc. This is the only part that concerns itself with
- * other than temporary real format. All other cals are strictly temp_real.
- */
-#include <linux/signal.h>
-#include <linux/math_emu.h>
-#include <linux/kernel.h>
-#include <asm/segment.h>
-
-void get_short_real(temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- short_real sr;
-
- addr = ea(info,code);
- sr = get_fs_long((unsigned long *) addr);
- short_to_temp(&sr,tmp);
-}
-
-void get_long_real(temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- long_real lr;
-
- addr = ea(info,code);
- lr.a = get_fs_long((unsigned long *) addr);
- lr.b = get_fs_long(1 + (unsigned long *) addr);
- long_to_temp(&lr,tmp);
-}
-
-void get_temp_real(temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
-
- addr = ea(info,code);
- tmp->a = get_fs_long((unsigned long *) addr);
- tmp->b = get_fs_long(1 + (unsigned long *) addr);
- tmp->exponent = get_fs_word(4 + (unsigned short *) addr);
-}
-
-void get_short_int(temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- temp_int ti;
-
- addr = ea(info,code);
- ti.a = (signed short) get_fs_word((unsigned short *) addr);
- ti.b = 0;
- if (ti.sign = (ti.a < 0))
- ti.a = - ti.a;
- int_to_real(&ti,tmp);
-}
-
-void get_long_int(temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- temp_int ti;
-
- addr = ea(info,code);
- ti.a = get_fs_long((unsigned long *) addr);
- ti.b = 0;
- if (ti.sign = (ti.a < 0))
- ti.a = - ti.a;
- int_to_real(&ti,tmp);
-}
-
-void get_longlong_int(temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- temp_int ti;
-
- addr = ea(info,code);
- ti.a = get_fs_long((unsigned long *) addr);
- ti.b = get_fs_long(1 + (unsigned long *) addr);
- if (ti.sign = (ti.b < 0))
- __asm__("notl %0 ; notl %1\n\t"
- "addl $1,%0 ; adcl $0,%1"
- :"=r" (ti.a),"=r" (ti.b)
- :"0" (ti.a),"1" (ti.b));
- int_to_real(&ti,tmp);
-}
-
-#define MUL10(low,high) \
-__asm__("addl %0,%0 ; adcl %1,%1\n\t" \
-"movl %0,%%ecx ; movl %1,%%ebx\n\t" \
-"addl %0,%0 ; adcl %1,%1\n\t" \
-"addl %0,%0 ; adcl %1,%1\n\t" \
-"addl %%ecx,%0 ; adcl %%ebx,%1" \
-:"=a" (low),"=d" (high) \
-:"0" (low),"1" (high):"cx","bx")
-
-#define ADD64(val,low,high) \
-__asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \
-:"0" (low),"1" (high),"r" ((unsigned long) (val)))
-
-void get_BCD(temp_real * tmp, struct info * info, unsigned short code)
-{
- int k;
- char * addr;
- temp_int i;
- unsigned char c;
-
- addr = ea(info,code);
- addr += 9;
- i.sign = 0x80 & get_fs_byte(addr--);
- i.a = i.b = 0;
- for (k = 0; k < 9; k++) {
- c = get_fs_byte(addr--);
- MUL10(i.a, i.b);
- ADD64((c>>4), i.a, i.b);
- MUL10(i.a, i.b);
- ADD64((c&0xf), i.a, i.b);
- }
- int_to_real(&i,tmp);
-}
-
-void put_short_real(const temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- short_real sr;
-
- addr = ea(info,code);
- verify_area(addr,4);
- temp_to_short(tmp,&sr);
- put_fs_long(sr,(unsigned long *) addr);
-}
-
-void put_long_real(const temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- long_real lr;
-
- addr = ea(info,code);
- verify_area(addr,8);
- temp_to_long(tmp,&lr);
- put_fs_long(lr.a, (unsigned long *) addr);
- put_fs_long(lr.b, 1 + (unsigned long *) addr);
-}
-
-void put_temp_real(const temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
-
- addr = ea(info,code);
- verify_area(addr,10);
- put_fs_long(tmp->a, (unsigned long *) addr);
- put_fs_long(tmp->b, 1 + (unsigned long *) addr);
- put_fs_word(tmp->exponent, 4 + (short *) addr);
-}
-
-void put_short_int(const temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- temp_int ti;
-
- addr = ea(info,code);
- real_to_int(tmp,&ti);
- verify_area(addr,2);
- if (ti.sign)
- ti.a = -ti.a;
- put_fs_word(ti.a,(short *) addr);
-}
-
-void put_long_int(const temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- temp_int ti;
-
- addr = ea(info,code);
- real_to_int(tmp,&ti);
- verify_area(addr,4);
- if (ti.sign)
- ti.a = -ti.a;
- put_fs_long(ti.a,(unsigned long *) addr);
-}
-
-void put_longlong_int(const temp_real * tmp,
- struct info * info, unsigned short code)
-{
- char * addr;
- temp_int ti;
-
- addr = ea(info,code);
- real_to_int(tmp,&ti);
- verify_area(addr,8);
- if (ti.sign)
- __asm__("notl %0 ; notl %1\n\t"
- "addl $1,%0 ; adcl $0,%1"
- :"=r" (ti.a),"=r" (ti.b)
- :"0" (ti.a),"1" (ti.b));
- put_fs_long(ti.a,(unsigned long *) addr);
- put_fs_long(ti.b,1 + (unsigned long *) addr);
-}
-
-#define DIV10(low,high,rem) \
-__asm__("divl %6 ; xchgl %1,%2 ; divl %6" \
- :"=d" (rem),"=a" (low),"=r" (high) \
- :"0" (0),"1" (high),"2" (low),"c" (10))
-
-void put_BCD(const temp_real * tmp,struct info * info, unsigned short code)
-{
- int k,rem;
- char * addr;
- temp_int i;
- unsigned char c;
-
- addr = ea(info,code);
- verify_area(addr,10);
- real_to_int(tmp,&i);
- if (i.sign)
- put_fs_byte(0x80, addr+9);
- else
- put_fs_byte(0, addr+9);
- for (k = 0; k < 9; k++) {
- DIV10(i.a,i.b,rem);
- c = rem;
- DIV10(i.a,i.b,rem);
- c += rem<<4;
- put_fs_byte(c,addr++);
- }
-}
diff --git a/kernel/math/mul.c b/kernel/math/mul.c
deleted file mode 100644
index 506f418..0000000
--- a/kernel/math/mul.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * linux/kernel/math/mul.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * temporary real multiplication routine.
- */
-
-#include <linux/math_emu.h>
-
-static void shift(int * c)
-{
- __asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
- "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
- "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
- "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
- ::"r" ((long) c):"ax");
-}
-
-static void mul64(const temp_real * a, const temp_real * b, int * c)
-{
- __asm__("movl (%0),%%eax\n\t"
- "mull (%1)\n\t"
- "movl %%eax,(%2)\n\t"
- "movl %%edx,4(%2)\n\t"
- "movl 4(%0),%%eax\n\t"
- "mull 4(%1)\n\t"
- "movl %%eax,8(%2)\n\t"
- "movl %%edx,12(%2)\n\t"
- "movl (%0),%%eax\n\t"
- "mull 4(%1)\n\t"
- "addl %%eax,4(%2)\n\t"
- "adcl %%edx,8(%2)\n\t"
- "adcl $0,12(%2)\n\t"
- "movl 4(%0),%%eax\n\t"
- "mull (%1)\n\t"
- "addl %%eax,4(%2)\n\t"
- "adcl %%edx,8(%2)\n\t"
- "adcl $0,12(%2)"
- ::"S" ((long) a),"c" ((long) b),"D" ((long) c)
- :"ax","dx");
-}
-
-void fmul(const temp_real * src1, const temp_real * src2, temp_real * result)
-{
- int i,sign;
- int tmp[4] = {0,0,0,0};
-
- sign = (src1->exponent ^ src2->exponent) & 0x8000;
- i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1;
- if (i<0) {
- result->exponent = sign;
- result->a = result->b = 0;
- return;
- }
- if (i>0x7fff) {
- set_OE();
- return;
- }
- mul64(src1,src2,tmp);
- if (tmp[0] || tmp[1] || tmp[2] || tmp[3])
- while (i && tmp[3] >= 0) {
- i--;
- shift(tmp);
- }
- else
- i = 0;
- result->exponent = i | sign;
- result->a = tmp[2];
- result->b = tmp[3];
-}
diff --git a/kernel/math/sqrt.c b/kernel/math/sqrt.c
deleted file mode 100644
index 8522224..0000000
--- a/kernel/math/sqrt.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * linux/kernel/math/sqrt.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
- * simple and stupid temporary real fsqrt() routine
- *
- * There are probably better ways to do this, but this should work ok.
- */
-
-#include <linux/math_emu.h>
-#include <linux/sched.h>
-
-static void shift_right(int * c)
-{
- __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
- ::"r" ((long) c));
-}
-
-static int sqr64(unsigned long * a, unsigned long * b)
-{
- unsigned long tmp[4];
-
- __asm__("movl (%0),%%eax ; mull %%eax\n\t"
- "movl %%eax,(%1) ; movl %%edx,4(%1)\n\t"
- "movl 4(%0),%%eax ; mull %%eax\n\t"
- "movl %%eax,8(%1) ; movl %%edx,12(%1)\n\t"
- "movl (%0),%%eax ; mull 4(%0)\n\t"
- "addl %%eax,%%eax ; adcl %%edx,%%edx\n\t"
- "adcl $0,12(%1) ; addl %%eax,4(%1)\n\t"
- "adcl %%edx,8(%1) ; adcl $0,12(%1)"
- ::"b" ((long) a),"c" ((long) tmp)
- :"ax","bx","cx","dx");
- if (tmp[3] > b[3] ||
- (tmp[3] == b[3] && (tmp[2] > b[2] ||
- (tmp[2] == b[2] && (tmp[1] > b[1] ||
- (tmp[1] == b[1] && tmp[0] > b[0]))))))
- return 0;
- return 1;
-}
-
-void fsqrt(const temp_real * s, temp_real * d)
-{
- unsigned long src[4];
- unsigned long res[2];
- int exponent;
- unsigned long mask, *c;
- int i;
-
- exponent = s->exponent;
- src[0] = src[1] = 0;
- src[2] = s->a;
- src[3] = s->b;
- d->exponent = 0;
- d->a = d->b = 0;
- if (!exponent) /* fsqrt(0.0) = 0.0 */
- return;
- if (!src[2] && !src[3])
- return;
- if (exponent & 0x8000) {
- send_sig(SIGFPE,current,0);
- return;
- }
- if (exponent & 1) {
- shift_right(src);
- exponent++;
- }
- exponent >>= 1;
- exponent += 0x1fff;
- c = res + 2;
- mask = 0;
- for (i = 64 ; i > 0 ; i--) {
- if (!(mask >>= 1)) {
- c--;
- mask = 0x80000000;
- }
- res[0] = d->a; res[1] = d->b;
- *c |= mask;
- if (sqr64(res,src)) {
- d->a = res[0];
- d->b = res[1];
- }
- }
- if (!d->a && !d->b)
- return;
- while (!(d->b & 0x80000000)) {
- __asm__("addl %%eax,%%eax ; adcl %%edx,%%edx"
- :"=a" (d->a),"=d" (d->b)
- :"0" (d->a),"1" (d->b));
- exponent--;
- }
- d->exponent = exponent;
-}
diff --git a/kernel/sched.c b/kernel/sched.c
index d9b065d..5d68ca6 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -28,6 +28,7 @@
#include <asm/segment.h>
int need_resched = 0;
+int hard_math = 0; /* set by boot/head.S */
unsigned long * prof_buffer = NULL;
unsigned long prof_len = 0;
@@ -206,7 +207,8 @@ void wake_up(struct wait_queue **q)
if (!q || !(next = *q))
return;
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ save_flags(flags);
+ cli();
do {
tmp = next;
next = tmp->next;
@@ -221,7 +223,7 @@ void wake_up(struct wait_queue **q)
}
tmp->next = NULL;
} while (next && next != *q);
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ restore_flags(flags);
}
static inline void __sleep_on(struct wait_queue **p, int state)
@@ -234,13 +236,14 @@ static inline void __sleep_on(struct wait_queue **p, int state)
panic("task[0] trying to sleep");
if (current->wait.next)
printk("__sleep_on: wait->next exists\n");
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ save_flags(flags);
+ cli();
current->state = state;
add_wait_queue(p,&current->wait);
sti();
schedule();
remove_wait_queue(p,&current->wait);
- __asm__("pushl %0 ; popfl"::"r" (flags));
+ restore_flags(flags);
}
void interruptible_sleep_on(struct wait_queue **p)
diff --git a/kernel/sys.c b/kernel/sys.c
index 90a558d..f5cd736 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -253,16 +253,16 @@ void ctrl_alt_del(void)
* 100% compatible with BSD. A program which uses just setgid() will be
* 100% compatible with POSIX w/ Saved ID's.
*/
-int sys_setregid(int rgid, int egid)
+int sys_setregid(gid_t rgid, gid_t egid)
{
- if (rgid >= 0) {
+ if (rgid != (gid_t) -1) {
if ((current->gid == rgid) ||
suser())
current->gid = rgid;
else
return(-EPERM);
}
- if (egid >= 0) {
+ if (egid != (gid_t) -1) {
if ((current->gid == egid) ||
(current->egid == egid) ||
suser()) {
@@ -277,7 +277,7 @@ int sys_setregid(int rgid, int egid)
/*
* setgid() is implemeneted like SysV w/ SAVED_IDS
*/
-int sys_setgid(int gid)
+int sys_setgid(gid_t gid)
{
if (suser())
current->gid = current->egid = current->sgid = gid;
@@ -338,11 +338,11 @@ int sys_time(long * tloc)
* 100% compatible with BSD. A program which uses just setuid() will be
* 100% compatible with POSIX w/ Saved ID's.
*/
-int sys_setreuid(int ruid, int euid)
+int sys_setreuid(uid_t ruid, uid_t euid)
{
int old_ruid = current->uid;
- if (ruid >= 0) {
+ if (ruid != (uid_t) -1) {
if ((current->euid==ruid) ||
(old_ruid == ruid) ||
suser())
@@ -350,7 +350,7 @@ int sys_setreuid(int ruid, int euid)
else
return(-EPERM);
}
- if (euid >= 0) {
+ if (euid != (uid_t) -1) {
if ((old_ruid == euid) ||
(current->euid == euid) ||
suser()) {
@@ -375,7 +375,7 @@ int sys_setreuid(int ruid, int euid)
* will allow a root program to temporarily drop privileges and be able to
* regain them by swapping the real and effective uid.
*/
-int sys_setuid(int uid)
+int sys_setuid(uid_t uid)
{
if (suser())
current->uid = current->euid = current->suid = uid;
@@ -424,7 +424,7 @@ int sys_brk(unsigned long end_data_seg)
* only important on a multi-user system anyway, to make sure one user
* can't send a signal to a process owned by another. -TYT, 12/12/91
*/
-int sys_setpgid(int pid, int pgid)
+int sys_setpgid(pid_t pid, pid_t pgid)
{
int i;
diff --git a/kernel/traps.c b/kernel/traps.c
index 9a5f086..169851d 100644
--- a/kernel/traps.c
+++ b/kernel/traps.c
@@ -58,7 +58,7 @@ void coprocessor_error(void);
void reserved(void);
void alignment_check(void);
-static void die_if_kernel(char * str,long esp_ptr,long nr)
+/*static*/ void die_if_kernel(char * str,long esp_ptr,long nr)
{
long * esp = (long *) esp_ptr;
int i;
diff --git a/mm/Makefile b/mm/Makefile
index 6a8f5ba..6230013 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -46,7 +46,8 @@ mmap.o : mmap.c /usr/include/linux/stat.h /usr/include/linux/sched.h /usr/includ
/usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/kernel.h \
/usr/include/linux/signal.h /usr/include/linux/time.h /usr/include/linux/param.h \
/usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/errno.h \
- /usr/include/linux/mman.h /usr/include/asm/segment.h /usr/include/asm/system.h
+ /usr/include/linux/mman.h /usr/include/linux/string.h /usr/include/asm/segment.h \
+ /usr/include/asm/system.h
swap.o : swap.c /usr/include/linux/mm.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
/usr/include/linux/wait.h /usr/include/linux/types.h /usr/include/linux/dirent.h \
/usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h /usr/include/linux/minix_fs_i.h \
diff --git a/mm/memory.c b/mm/memory.c
index d556e4f..3c56e16 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -789,11 +789,13 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
{
unsigned long address;
unsigned long user_esp = 0;
+ extern void die_if_kernel();
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
if (address >= TASK_SIZE) {
printk("Unable to handle kernel paging request at address %08x\n",address);
+ die_if_kernel("Oops",esp,error_code);
do_exit(SIGSEGV);
}
if (esp[2] & VM_MASK) {
@@ -811,6 +813,45 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
do_wp_page(error_code, address, current, user_esp);
}
+/*
+ * 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 += 4095;
+ start_mem &= 0xfffff000;
+ address = 0;
+ pg_dir = swapper_pg_dir + 768; /* at virtual addr 0xC0000000 */
+ while (address < end_mem) {
+ tmp = *pg_dir;
+ if (!tmp) {
+ tmp = start_mem;
+ *pg_dir = tmp | 7;
+ start_mem += 4096;
+ }
+ pg_dir++;
+ pg_table = (unsigned long *) (tmp & 0xfffff000);
+ for (tmp = 0 ; tmp < 1024 ; tmp++,pg_table++) {
+ if (address && address < end_mem)
+ *pg_table = 7 + address;
+ else
+ *pg_table = 0;
+ address += 4096;
+ }
+ }
+ invalidate();
+ return start_mem;
+}
+
void mem_init(unsigned long start_low_mem,
unsigned long start_mem, unsigned long end_mem)
{
diff --git a/mm/mmap.c b/mm/mmap.c
index 550d747..9776cb5 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -9,6 +9,7 @@
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/mman.h>
+#include <linux/string.h>
#include <asm/segment.h>
#include <asm/system.h>
diff --git a/mm/swap.c b/mm/swap.c
index e0f1809..0a47c0d 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -40,6 +40,7 @@ extern unsigned long free_page_list;
/*
* The following are used to make sure we don't thrash too much...
+ * NOTE!! NR_LAST_FREE_PAGES must be a power of 2...
*/
#define NR_LAST_FREE_PAGES 32
static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {0,};
@@ -350,110 +351,114 @@ static inline void add_mem_queue(unsigned long addr, unsigned long * queue)
*queue = addr;
}
+/*
+ * 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.
+ */
void free_page(unsigned long addr)
{
- unsigned long i;
- unsigned long flag;
-
- if (addr >= high_memory) {
- printk("Trying to free nonexistent page %08x\n",addr);
+ if (addr < high_memory) {
+ unsigned short * map = mem_map + MAP_NR(addr);
+
+ if (*map) {
+ if (!(*map & MAP_PAGE_RESERVED)) {
+ unsigned long flag;
+
+ save_flags(flag);
+ cli();
+ if (!--*map) {
+ if (nr_secondary_pages < MAX_SECONDARY_PAGES) {
+ add_mem_queue(addr,&secondary_page_list);
+ nr_secondary_pages++;
+ restore_flags(flag);
+ return;
+ }
+ add_mem_queue(addr,&free_page_list);
+ nr_free_pages++;
+ }
+ restore_flags(flag);
+ }
+ return;
+ }
+ printk("Trying to free free memory (%08x): memory probabably corrupted\n",addr);
return;
}
- i = MAP_NR(addr);
- if (mem_map[i] & MAP_PAGE_RESERVED)
- return;
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flag));
- if (!mem_map[i])
- goto bad_free_page;
- if (!--mem_map[i])
- if (nr_secondary_pages < MAX_SECONDARY_PAGES) {
- add_mem_queue(addr,&secondary_page_list);
- nr_secondary_pages++;
- } else {
- add_mem_queue(addr,&free_page_list);
- nr_free_pages++;
- }
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flag));
+ printk("Trying to free nonexistent page %08x\n",addr);
return;
-bad_free_page:
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flag));
- printk("Trying to free free memory (%08x): memory probabably corrupted\n");
}
-static unsigned long remove_from_mem_queue(unsigned long * queue)
-{
- unsigned long result;
- static unsigned long index = 0;
-
- cli();
- result = *queue;
- if (!result) {
- sti();
- return 0;
- }
- if ((result & 0xfff) || result >= high_memory) {
- *queue = 0;
- printk("Result = %08x - memory map destroyed\n");
- sti();
- panic("mm error");
- }
- *queue = *(unsigned long *) result;
- sti();
- if (mem_map[MAP_NR(result)]) {
- printk("Free page %08x has mem_map = %d\n",
- result,mem_map[MAP_NR(result)]);
- return 0;
- }
- mem_map[MAP_NR(result)] = 1;
- cli();
- if (index >= NR_LAST_FREE_PAGES)
- index = 0;
- last_free_pages[index] = result;
- index++;
- sti();
- __asm__ __volatile__("cld ; rep ; stosl"
- ::"a" (0),"c" (1024),"D" (result)
- :"di","cx");
- return result;
-}
+/*
+ * This is one ugly macro, but it simplifies checking, and makes
+ * this speed-critical place reasonably fast, especially as we have
+ * to do things with the interrupt flag etc.
+ *
+ * Note that this #define is heavily optimized to give fast code
+ * for the normal case - the if-statements are ordered so that gcc-2.2.2
+ * will make *no* jumps for the normal code. Don't touch unless you
+ * know what you are doing.
+ */
+#define REMOVE_FROM_MEM_QUEUE(queue,nr) \
+ cli(); \
+ if (result = queue) { \
+ if (!(result & 0xfff) && result < high_memory) { \
+ queue = *(unsigned long *) result; \
+ if (!mem_map[MAP_NR(result)]) { \
+ mem_map[MAP_NR(result)] = 1; \
+ nr--; \
+last_free_pages[index = (index + 1) & (NR_LAST_FREE_PAGES - 1)] = result; \
+ restore_flags(flag); \
+ __asm__ __volatile__("cld ; rep ; stosl" \
+ ::"a" (0),"c" (1024),"D" (result) \
+ :"di","cx"); \
+ return result; \
+ } \
+ printk("Free page %08x has mem_map = %d\n", \
+ result,mem_map[MAP_NR(result)]); \
+ } else \
+ printk("Result = 0x%08x - memory map destroyed\n", result); \
+ queue = 0; \
+ nr = 0; \
+ } else if (nr) { \
+ printk(#nr " is %d, but " #queue " is empty\n",nr); \
+ nr = 0; \
+ } \
+ restore_flags(flag)
/*
* Get physical address of first (actually last :-) free page, and mark it
* used. If no free pages left, return 0.
+ *
+ * Note that this is one of the most heavily called functions in the kernel,
+ * so it's a bit timing-critical (especially as we have to disable interrupts
+ * in it). See the above macro which does most of the work, and which is
+ * optimized for a fast normal path of execution.
*/
unsigned long get_free_page(int priority)
{
- unsigned long result;
+ unsigned long result, flag;
+ static unsigned long index = 0;
/* this routine can be called at interrupt time via
malloc. We want to make sure that the critical
sections of code have interrupts disabled. -RAB
Is this code reentrant? */
+ save_flags(flag);
repeat:
- result = remove_from_mem_queue(&free_page_list);
- if (result) {
- nr_free_pages--;
- return result;
- }
- if (nr_free_pages) {
- printk("nr_free_pages is %d, but no free memory found\n",nr_free_pages);
- nr_free_pages = 0;
- }
+ REMOVE_FROM_MEM_QUEUE(free_page_list,nr_free_pages);
if (priority == GFP_BUFFER)
return 0;
if (priority != GFP_ATOMIC)
if (try_to_free_page())
goto repeat;
- result = remove_from_mem_queue(&secondary_page_list);
- if (result) {
- nr_secondary_pages--;
- return result;
- }
- if (nr_secondary_pages) {
- printk("nr_secondary_pages is %d, but no free memory found\n",nr_secondary_pages);
- nr_secondary_pages = 0;
- }
+ REMOVE_FROM_MEM_QUEUE(secondary_page_list,nr_secondary_pages);
return 0;
}
diff --git a/net/socket.c b/net/socket.c
index 46d5cd1..3928a69 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -62,8 +62,9 @@ static struct file_operations socket_file_ops = {
sock_read,
sock_write,
sock_readdir,
- sock_select, /* not in vfs yet */
+ sock_select,
sock_ioctl,
+ NULL, /* mmap */
NULL, /* no special open code... */
sock_close
};
diff --git a/net/tcp/Makefile b/net/tcp/Makefile
index 74c7d0f..fe99baf 100644
--- a/net/tcp/Makefile
+++ b/net/tcp/Makefile
@@ -50,7 +50,7 @@ arp.o : arp.c /usr/include/linux/types.h /usr/include/linux/string.h /usr/includ
/usr/include/linux/resource.h /usr/include/linux/vm86.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h /usr/include/asm/system.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h tcp.h sock.h arp.h
+ eth.h tcp.h sock.h arp.h
dev.o : dev.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/types.h \
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -62,7 +62,7 @@ dev.o : dev.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/
/usr/include/linux/string.h /usr/include/linux/socket.h /usr/include/netinet/in.h \
/usr/include/features.h /usr/include/sys/socket.h /usr/include/traditional.h \
/usr/include/asm/memory.h dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h tcp.h sock.h /usr/include/linux/errno.h arp.h
+ tcp.h sock.h /usr/include/linux/errno.h arp.h
eth.o : eth.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/types.h \
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -74,7 +74,7 @@ eth.o : eth.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/
/usr/include/linux/string.h /usr/include/linux/socket.h /usr/include/netinet/in.h \
/usr/include/features.h /usr/include/sys/socket.h /usr/include/traditional.h \
/usr/include/asm/memory.h dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h tcp.h sock.h /usr/include/linux/errno.h arp.h
+ tcp.h sock.h /usr/include/linux/errno.h arp.h
icmp.o : icmp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
@@ -85,9 +85,8 @@ icmp.o : icmp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/inclu
/usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
- /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
- ../kern_sock.h icmp.h
+ eth.h tcp.h sock.h /usr/include/linux/errno.h /usr/include/linux/timer.h /usr/include/asm/system.h \
+ /usr/include/asm/segment.h ../kern_sock.h icmp.h
ip.o : ip.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/types.h \
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -98,8 +97,8 @@ ip.o : ip.c /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/li
/usr/include/linux/param.h /usr/include/linux/resource.h /usr/include/linux/vm86.h \
/usr/include/linux/string.h /usr/include/linux/socket.h /usr/include/netinet/in.h \
/usr/include/features.h /usr/include/sys/socket.h /usr/include/traditional.h \
- timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h \
- eth.h tcp.h sock.h /usr/include/linux/errno.h arp.h icmp.h
+ timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
+ arp.h icmp.h
loopback.o : loopback.c /usr/include/linux/config.h /usr/include/linux/config.dist.h \
/usr/include/linux/kernel.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -113,8 +112,8 @@ loopback.o : loopback.c /usr/include/linux/config.h /usr/include/linux/config.di
/usr/include/asm/io.h /usr/include/asm/memory.h /usr/include/errno.h /usr/include/traditional.h \
/usr/include/linux/errno.h /usr/include/linux/fcntl.h /usr/include/netinet/in.h \
/usr/include/features.h /usr/include/sys/socket.h /usr/include/linux/socket.h \
- dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h \
- tcp.h sock.h arp.h ../kern_sock.h
+ dev.h eth.h timer.h ip.h /usr/include/linux/sock_ioctl.h tcp.h sock.h arp.h \
+ ../kern_sock.h
pack_type.o : pack_type.c /usr/include/linux/stddef.h dev.h eth.h
packet.o : packet.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
@@ -126,9 +125,8 @@ packet.o : packet.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/i
/usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
- /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
- ../kern_sock.h
+ eth.h tcp.h sock.h /usr/include/linux/errno.h /usr/include/linux/timer.h /usr/include/asm/system.h \
+ /usr/include/asm/segment.h ../kern_sock.h
protocols.o : protocols.c /usr/include/asm/segment.h /usr/include/asm/system.h \
/usr/include/linux/types.h /usr/include/linux/kernel.h /usr/include/linux/sched.h \
/usr/include/linux/head.h /usr/include/linux/fs.h /usr/include/linux/limits.h \
@@ -140,7 +138,7 @@ protocols.o : protocols.c /usr/include/asm/segment.h /usr/include/asm/system.h \
/usr/include/linux/vm86.h /usr/include/linux/string.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h tcp.h sock.h icmp.h
+ eth.h tcp.h sock.h icmp.h
raw.o : raw.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
@@ -151,9 +149,8 @@ raw.o : raw.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include
/usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
- /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
- ../kern_sock.h
+ eth.h tcp.h sock.h /usr/include/linux/errno.h /usr/include/linux/timer.h /usr/include/asm/system.h \
+ /usr/include/asm/segment.h ../kern_sock.h
sock.o : sock.c /usr/include/linux/errno.h /usr/include/linux/types.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h /usr/include/linux/kernel.h /usr/include/linux/sched.h \
@@ -164,9 +161,8 @@ sock.o : sock.c /usr/include/linux/errno.h /usr/include/linux/types.h /usr/inclu
/usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
/usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
/usr/include/linux/vm86.h /usr/include/linux/timer.h /usr/include/linux/sock_ioctl.h \
- /usr/include/asm/memory.h ../kern_sock.h timer.h ip.h dev.h /usr/include/netinet/protocols.h \
- eth.h tcp.h udp.h sock.h /usr/include/asm/segment.h /usr/include/asm/system.h \
- /usr/include/linux/fcntl.h
+ /usr/include/asm/memory.h ../kern_sock.h timer.h ip.h dev.h eth.h tcp.h udp.h \
+ sock.h /usr/include/asm/segment.h /usr/include/asm/system.h /usr/include/linux/fcntl.h
tcp.o : tcp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
@@ -177,8 +173,8 @@ tcp.o : tcp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include
/usr/include/linux/vm86.h /usr/include/asm/memory.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h /usr/include/linux/fcntl.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h icmp.h tcp.h sock.h /usr/include/linux/errno.h \
- /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
+ eth.h icmp.h tcp.h sock.h /usr/include/linux/errno.h /usr/include/linux/timer.h \
+ /usr/include/asm/system.h /usr/include/asm/segment.h /usr/include/linux/termios.h \
../kern_sock.h
timer.o : timer.c /usr/include/linux/types.h /usr/include/linux/errno.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
@@ -190,8 +186,8 @@ timer.o : timer.c /usr/include/linux/types.h /usr/include/linux/errno.h /usr/inc
/usr/include/linux/msdos_fs_sb.h /usr/include/linux/mm.h /usr/include/linux/signal.h \
/usr/include/linux/time.h /usr/include/linux/param.h /usr/include/linux/resource.h \
/usr/include/linux/vm86.h /usr/include/linux/timer.h /usr/include/asm/system.h \
- timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h \
- eth.h tcp.h sock.h arp.h ../kern_sock.h
+ timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h eth.h tcp.h sock.h arp.h \
+ ../kern_sock.h
udp.o : udp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include/linux/head.h \
/usr/include/linux/fs.h /usr/include/linux/limits.h /usr/include/linux/wait.h \
/usr/include/linux/dirent.h /usr/include/linux/vfs.h /usr/include/linux/pipe_fs_i.h \
@@ -202,9 +198,8 @@ udp.o : udp.c /usr/include/linux/types.h /usr/include/linux/sched.h /usr/include
/usr/include/linux/vm86.h /usr/include/linux/fcntl.h /usr/include/linux/socket.h \
/usr/include/netinet/in.h /usr/include/features.h /usr/include/sys/socket.h \
/usr/include/traditional.h timer.h ip.h dev.h /usr/include/linux/sock_ioctl.h \
- /usr/include/netinet/protocols.h eth.h tcp.h sock.h /usr/include/linux/errno.h \
- /usr/include/linux/timer.h /usr/include/asm/system.h /usr/include/asm/segment.h \
- ../kern_sock.h udp.h icmp.h
+ eth.h tcp.h sock.h /usr/include/linux/errno.h /usr/include/linux/timer.h /usr/include/linux/termios.h \
+ /usr/include/asm/system.h /usr/include/asm/segment.h ../kern_sock.h udp.h icmp.h
we.o : we.c /usr/include/linux/config.h /usr/include/linux/config.dist.h /usr/include/linux/kernel.h \
/usr/include/linux/sched.h /usr/include/linux/head.h /usr/include/linux/fs.h \
/usr/include/linux/limits.h /usr/include/linux/wait.h /usr/include/linux/types.h \
@@ -218,5 +213,4 @@ we.o : we.c /usr/include/linux/config.h /usr/include/linux/config.dist.h /usr/in
/usr/include/asm/memory.h /usr/include/errno.h /usr/include/traditional.h /usr/include/linux/errno.h \
/usr/include/linux/fcntl.h /usr/include/netinet/in.h /usr/include/features.h \
/usr/include/sys/socket.h /usr/include/linux/socket.h dev.h eth.h timer.h ip.h \
- /usr/include/linux/sock_ioctl.h /usr/include/netinet/protocols.h tcp.h sock.h \
- arp.h wereg.h
+ /usr/include/linux/sock_ioctl.h tcp.h sock.h arp.h wereg.h
diff --git a/net/tcp/dev.c b/net/tcp/dev.c
index fd6e8b6..3fd6305 100644
--- a/net/tcp/dev.c
+++ b/net/tcp/dev.c
@@ -188,8 +188,11 @@ dev_rint(unsigned char *buff, unsigned long len, int flags,
if (len > 0 && buff != NULL)
{
skb = malloc (sizeof (*skb) + len);
- skb->mem_len = sizeof (*skb) + len;
- skb->mem_addr = skb;
+ if (skb != NULL)
+ {
+ skb->mem_len = sizeof (*skb) + len;
+ skb->mem_addr = skb;
+ }
}
/* firs we copy the packet into a buffer, and save it for later. */
diff --git a/net/tcp/icmp.c b/net/tcp/icmp.c
index 9c75ea1..c48e967 100644
--- a/net/tcp/icmp.c
+++ b/net/tcp/icmp.c
@@ -102,7 +102,7 @@ icmp_reply (struct sk_buff *skb_in, int type, int code, struct device *dev)
/* Build Layer 2-3 headers for message back to source */
offset = ip_build_header( skb, iph->daddr, iph->saddr,
- &dev, IP_ICMP, NULL, len );
+ &dev, IPPROTO_ICMP, NULL, len );
if (offset < 0)
{
@@ -235,7 +235,7 @@ icmp_rcv(struct sk_buff *skb1, struct device *dev, struct options *opt,
skb->mem_len = size;
/* Build Layer 2-3 headers for message back to source */
- offset = ip_build_header( skb, daddr, saddr, &dev, IP_ICMP, opt, len );
+ offset = ip_build_header( skb, daddr, saddr, &dev, IPPROTO_ICMP, opt, len );
if (offset < 0)
{
/* Problems building header */
diff --git a/net/tcp/ip.h b/net/tcp/ip.h
index f3faad5..260f24f 100644
--- a/net/tcp/ip.h
+++ b/net/tcp/ip.h
@@ -24,7 +24,8 @@
#include "dev.h"
#include <linux/sock_ioctl.h>
-#include <netinet/protocols.h>
+/* #include <netinet/protocols.h>*/
+#include <netinet/in.h>
struct rtable
{
diff --git a/net/tcp/protocols.c b/net/tcp/protocols.c
index d0942a7..a59b6dc 100644
--- a/net/tcp/protocols.c
+++ b/net/tcp/protocols.c
@@ -41,7 +41,7 @@ static struct ip_protocol tcp_protocol =
tcp_rcv,
tcp_err,
NULL,
- IP_TCP,
+ IPPROTO_TCP,
0, /* copy */
NULL
};
@@ -51,7 +51,7 @@ static struct ip_protocol udp_protocol =
udp_rcv,
udp_err,
&tcp_protocol,
- IP_UDP,
+ IPPROTO_UDP,
0, /* copy */
NULL
};
@@ -61,7 +61,7 @@ static struct ip_protocol icmp_protocol =
icmp_rcv,
NULL,
&udp_protocol,
- IP_ICMP,
+ IPPROTO_ICMP,
0, /* copy */
NULL
};
diff --git a/net/tcp/sock.c b/net/tcp/sock.c
index 09ee78f..0f4b8ca 100644
--- a/net/tcp/sock.c
+++ b/net/tcp/sock.c
@@ -651,6 +651,16 @@ ip_proto_listen(struct socket *sock, int backlog)
printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
return (0);
}
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
sk->state = TCP_LISTEN;
return (0);
}
@@ -706,7 +716,7 @@ ip_proto_create (struct socket *sock, int protocol)
{
case SOCK_STREAM:
case SOCK_SEQPACKET:
- if (protocol && protocol != IP_TCP)
+ if (protocol && protocol != IPPROTO_TCP)
{
free_s ((void *)sk, sizeof (*sk));
return (-EPROTONOSUPPORT);
@@ -716,7 +726,7 @@ ip_proto_create (struct socket *sock, int protocol)
break;
case SOCK_DGRAM:
- if (protocol && protocol != IP_UDP)
+ if (protocol && protocol != IPPROTO_UDP)
{
free_s ((void *)sk, sizeof (*sk));
return (-EPROTONOSUPPORT);
@@ -845,22 +855,17 @@ ip_proto_create (struct socket *sock, int protocol)
sk->dummy_th.ack = 0;
sk->dummy_th.urg = 0;
sk->dummy_th.dest = 0;
+
if (sk->num)
{
- put_sock (sk->num, sk);
+ /* it assumes that any protocol which allows
+ the user to assign a number at socket
+ creation time automatically
+ shares. */
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
}
- else
- {
- sk->num = get_new_socknum(sk->prot, 0);
- }
- /* make sure there was a free socket. */
- if (sk->num == 0)
- {
- destroy_sock(sk);
- return (-EAGAIN);
- }
- put_sock(sk->num, sk);
- sk->dummy_th.source = net16(sk->num);
+
if (sk->prot->init)
{
err = sk->prot->init(sk);
@@ -922,6 +927,10 @@ ip_proto_release(struct socket *sock, struct socket *peer)
}
+/* this needs to be changed to dissallow
+ the rebinding of sockets. What error
+ should it return? */
+
static int
ip_proto_bind (struct socket *sock, struct sockaddr *uaddr,
int addr_len)
@@ -929,6 +938,7 @@ ip_proto_bind (struct socket *sock, struct sockaddr *uaddr,
struct sockaddr_in addr;
volatile struct sock *sk, *sk2;
unsigned short snum;
+
sk = sock->data;
if (sk == NULL)
{
@@ -937,10 +947,13 @@ ip_proto_bind (struct socket *sock, struct sockaddr *uaddr,
}
/* check this error. */
if (sk->state != TCP_CLOSE) return (-EIO);
+ if (sk->num != 0) return (-EINVAL);
+
verify_area (uaddr, addr_len);
memcpy_fromfs (&addr, uaddr, min (sizeof (addr), addr_len));
if (addr.sin_family && addr.sin_family != AF_INET)
- return (-EIO); /* this needs to be changed. */
+ return (-EINVAL); /* this needs to be changed. */
+
snum = net16(addr.sin_port);
PRINTK ("bind sk =%X to port = %d\n", sk, snum);
print_sk (sk);
@@ -957,10 +970,11 @@ ip_proto_bind (struct socket *sock, struct sockaddr *uaddr,
}
if (snum <= PROT_SOCK && !suser())
- return (-EPERM);
+ return (-EACCES);
if (my_ip_addr(addr.sin_addr.s_addr) || addr.sin_addr.s_addr == 0)
sk->saddr = addr.sin_addr.s_addr;
+
PRINTK ("sock_array[%d] = %X:\n", snum & (SOCK_ARRAY_SIZE -1),
sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]);
print_sk (sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)]);
@@ -996,6 +1010,16 @@ ip_proto_connect (struct socket *sock, struct sockaddr * uaddr,
printk ("Warning: sock->data = NULL: %d\n" ,__LINE__);
return (0);
}
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
if (sk->prot->connect == NULL)
return (-EOPNOTSUPP);
@@ -1059,7 +1083,11 @@ ip_proto_accept (struct socket *sock, struct socket *newsock, int flags)
{
sk2 = sk1->prot->accept (sk1,flags);
if (sk2 == NULL)
- return (-sk1->err);
+ {
+ if (sk1->err <= 0)
+ printk ("Warning sock.c:sk1->err <= 0. Returning non-error.\n");
+ return (-sk1->err);
+ }
}
newsock->data = (void *)sk2;
sk2->sleep = (void *)newsock->wait;
@@ -1082,7 +1110,7 @@ ip_proto_accept (struct socket *sock, struct socket *newsock, int flags)
}
sti();
- if (sk2->state != TCP_ESTABLISHED && sk2->err)
+ if (sk2->state != TCP_ESTABLISHED && sk2->err > 0)
{
int err;
err = -sk2->err;
@@ -1143,6 +1171,16 @@ ip_proto_read (struct socket *sock, char *ubuf, int size, int noblock)
}
if (sk->shutdown & RCV_SHUTDOWN)
return (-EIO);
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
return (sk->prot->read (sk, ubuf, size, noblock,0));
}
@@ -1159,6 +1197,16 @@ ip_proto_recv (struct socket *sock, void *ubuf, int size, int noblock,
}
if (sk->shutdown & RCV_SHUTDOWN)
return (-EIO);
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
return (sk->prot->read (sk, ubuf, size, noblock, flags));
}
@@ -1174,6 +1222,16 @@ ip_proto_write (struct socket *sock, char *ubuf, int size, int noblock)
}
if (sk->shutdown & SEND_SHUTDOWN)
return (-EIO);
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
return (sk->prot->write (sk, ubuf, size, noblock, 0));
}
@@ -1191,6 +1249,16 @@ ip_proto_send (struct socket *sock, void *ubuf, int size, int noblock,
}
if (sk->shutdown & SEND_SHUTDOWN)
return (-EIO);
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
return (sk->prot->write (sk, ubuf, size, noblock, flags));
}
@@ -1209,6 +1277,16 @@ ip_proto_sendto (struct socket *sock, void *ubuf, int size, int noblock,
if (sk->shutdown & SEND_SHUTDOWN)
return (-EIO);
if (sk->prot->sendto == NULL) return (-EOPNOTSUPP);
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
return (sk->prot->sendto (sk, ubuf, size, noblock, flags,
(struct sockaddr_in *)sin, addr_len));
}
@@ -1227,6 +1305,16 @@ ip_proto_recvfrom (struct socket *sock, void *ubuf, int size, int noblock,
if (sk->shutdown & RCV_SHUTDOWN)
return (-EIO);
if (sk->prot->recvfrom == NULL) return (-EOPNOTSUPP);
+
+ /* we may need to bind the socket. */
+ if (sk->num == 0)
+ {
+ sk->num = get_new_socknum (sk->prot, 0);
+ if (sk->num == 0) return (-EAGAIN);
+ put_sock (sk->num, sk);
+ sk->dummy_th.source = net16(sk->num);
+ }
+
return (sk->prot->recvfrom (sk, ubuf, size, noblock, flags,
(struct sockaddr_in*)sin, addr_len));
}
@@ -1292,11 +1380,24 @@ ip_proto_ioctl (struct socket *sock, unsigned int cmd,
if (!suser())
return (-EPERM);
return (ip_set_dev((struct ip_config *)arg));
-#if 0
- case IP_ADD_ROUTE:
- ip_add_route ((struct rtable *) arg);
- return (0);
-#endif
+
+ case FIOSETOWN:
+ case SIOCSPGRP:
+ {
+ long user;
+ user = get_fs_long ((void *) arg);
+ sk->proc = user;
+ return (0);
+ }
+
+ case FIOGETOWN:
+ case SIOCGPGRP:
+ {
+ verify_area ((void *)arg, sizeof (long));
+ put_fs_long (sk->proc, (void *)arg);
+ return (0);
+ }
+
default:
if (!sk->prot->ioctl)
return (-EINVAL);
diff --git a/net/tcp/sock.h b/net/tcp/sock.h
index 1642509..b2655e7 100644
--- a/net/tcp/sock.h
+++ b/net/tcp/sock.h
@@ -64,10 +64,10 @@ struct sock
unsigned short urg;
unsigned short shutdown;
short rtt;
+ short err;
unsigned char protocol;
unsigned char state;
unsigned char ack_backlog;
- unsigned char err;
unsigned char max_ack_backlog;
unsigned char priority;
struct tcp_header dummy_th; /* I may be able to get rid of this. */
diff --git a/net/tcp/tcp.c b/net/tcp/tcp.c
index db36788..65c2331 100644
--- a/net/tcp/tcp.c
+++ b/net/tcp/tcp.c
@@ -37,6 +37,7 @@
#include <asm/system.h>
#include <asm/segment.h>
/* #include <signal.h>*/
+#include <linux/termios.h> /* for ioctl's */
#include "../kern_sock.h" /* for PRINTK */
#define tmax(a,b) (before ((a),(b)) ? (b) : (a))
@@ -223,27 +224,60 @@ tcp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
{
default:
return (-EINVAL);
-#if 0
+
+ case TIOCINQ:
+/* case FIONREAD:*/
+ {
+ unsigned long amount;
+ struct sk_buff *skb;
+
+ if (sk->state == TCP_LISTEN)
+ return (-EINVAL);
+
+ amount = 0;
+ if (sk->rqueue != NULL)
+ {
+ skb = sk->rqueue->next;
+ /* go until a push or until we are out of data. */
+ do {
+ amount += skb -> len;
+ if (skb->h.th->psh) break;
+ skb = skb->next;
+ } while (skb != sk->rqueue->next);
+ }
+
+ verify_area ((void *)arg, sizeof (unsigned long));
+ put_fs_long (amount, (unsigned long *)arg);
+ return (0);
+ }
+
case SIOCATMARK:
- /* try to figure out if we need to read some urgent data. */
- if (sk->rqueue && sk->rqueue->next->th.urg)
- {
- int offset;
- struct sk_buff *skb;
- skb = sk->rqueue->next;
- offset = sk->copied_seq +1 - skb->th.seq - skb->th.syn;
- /* now we know we are at the urgent data. */
- if (offset >= skb->len)
- {
- verify_area ((void *)arg, sizeof (int));
- put_fs_long(1, (unsigned long *)arg);
- return (0);
- }
- }
- verify_area ((void *)arg, sizeof (int));
- put_fs_long(0, (unsigned long *)arg);
- return (0);
-#endif
+ {
+ struct sk_buff *skb;
+ int answ=0;
+ /* try to figure out if we need to read some urgent data. */
+ if (sk->rqueue != NULL)
+ {
+ skb = sk->rqueue->next;
+ if (sk->copied_seq+1 == skb->h.th->seq && skb->h.th->urg)
+ answ = 1;
+ }
+ verify_area ((void *) arg, sizeof (unsigned long));
+ put_fs_long (answ, (void *) arg);
+ return (0);
+ }
+
+ case TIOCOUTQ:
+ {
+ unsigned long amount;
+ if (sk->state == TCP_LISTEN)
+ return (-EINVAL);
+ amount = sk->prot->wspace(sk)/2;
+ verify_area ((void *)arg, sizeof (unsigned long));
+ put_fs_long (amount, (unsigned long *)arg);
+ return (0);
+ }
+
}
}
@@ -261,7 +295,7 @@ tcp_check (struct tcp_header *th, int len, unsigned long saddr,
"\t adcl %%edx,%%ebx\n"
"\t adcl $0, %%ebx\n"
: "=b" (sum)
- : "0" (daddr), "c" (saddr), "d" ((net16(len) << 16) + IP_TCP*256)
+ : "0" (daddr), "c" (saddr), "d" ((net16(len) << 16) + IPPROTO_TCP*256)
: "cx","bx","dx" );
if (len > 3)
@@ -363,7 +397,7 @@ tcp_send_check (struct tcp_header *th, unsigned long saddr,
t1 = (struct tcp_header *)(buff + 1);
/* put in the ip_header and routing stuff. */
tmp = sk->prot->build_header (buff, sk->saddr, daddr, &dev,
- IP_TCP, sk->opt, MAX_ACK_SIZE);
+ IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
if (tmp < 0)
{
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
@@ -499,8 +533,11 @@ tcp_write(volatile struct sock *sk, unsigned char *from,
/* we also need to worry about the window. The smallest we
will send is about 200 bytes. */
+
copy = min (sk->mtu, diff(sk->window_seq, sk->send_seq));
- if (copy < 200) copy = sk->mtu;
+
+ /* redundent check here. */
+ if (copy < 200 || copy > sk->mtu) copy = sk->mtu;
copy = min (copy, len);
skb=prot->wmalloc (sk, copy + prot->max_header+sizeof (*skb),0);
@@ -540,7 +577,7 @@ tcp_write(volatile struct sock *sk, unsigned char *from,
would be good. */
tmp = prot->build_header (skb, sk->saddr, sk->daddr, &dev,
- IP_TCP, sk->opt, skb->mem_len);
+ IPPROTO_TCP, sk->opt, skb->mem_len);
if (tmp < 0 )
{
prot->wfree (sk, skb->mem_addr, skb->mem_len);
@@ -639,7 +676,7 @@ tcp_read_wakeup(volatile struct sock *sk)
/* put in the ip_header and routing stuff. */
tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
- IP_TCP, sk->opt, MAX_ACK_SIZE);
+ IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
if (tmp < 0)
{
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
@@ -800,6 +837,15 @@ tcp_read(volatile struct sock *sk, unsigned char *to,
/* this error should be checked. */
if (sk->state == TCP_LISTEN) return (-ENOTCONN);
+ /* will catch some errors. */
+ if (sk->err)
+ {
+ int err;
+ err = -sk->err;
+ sk->err = 0;
+ return (err);
+ }
+
/* urgent data needs to be handled specially. */
if ((flags & MSG_OOB))
return (tcp_read_urg (sk, to, len, flags));
@@ -826,13 +872,6 @@ tcp_read(volatile struct sock *sk, unsigned char *to,
cleanup_rbuf(sk);
- if (nonblock || ((flags & MSG_PEEK) && copied))
- {
- release_sock (sk);
- if (copied) return (copied);
- return (-EAGAIN);
- }
-
release_sock (sk); /* now we may have some data waiting. */
@@ -852,6 +891,15 @@ tcp_read(volatile struct sock *sk, unsigned char *to,
return (-ENOTCONN);
}
+
+ if (nonblock || ((flags & MSG_PEEK) && copied))
+ {
+ sti();
+ release_sock (sk);
+ if (copied) return (copied);
+ return (-EAGAIN);
+ }
+
if ( sk->rqueue == NULL ||
before (sk->copied_seq+1, sk->rqueue->next->h.th->seq))
{
@@ -966,7 +1014,7 @@ tcp_reset(unsigned long saddr, unsigned long daddr, struct tcp_header *th,
t1=(struct tcp_header *)(buff + 1);
/* put in the ip_header and routing stuff. */
- tmp = prot->build_header (buff, saddr, daddr, &dev, IP_TCP, opt,
+ tmp = prot->build_header (buff, saddr, daddr, &dev, IPPROTO_TCP, opt,
sizeof(struct tcp_header));
if (tmp < 0)
{
@@ -1100,7 +1148,7 @@ tcp_conn_request(volatile struct sock *sk, struct sk_buff *skb,
if (skb->h.th->doff == 5)
{
- newsk->mtu=dev->mtu-HEADER_SIZE;
+ newsk->mtu=576-HEADER_SIZE;
}
else
{
@@ -1136,7 +1184,7 @@ tcp_conn_request(volatile struct sock *sk, struct sk_buff *skb,
/* put in the ip_header and routing stuff. */
tmp = sk->prot->build_header (buff, newsk->saddr, newsk->daddr, &dev,
- IP_TCP, NULL, MAX_SYN_SIZE);
+ IPPROTO_TCP, NULL, MAX_SYN_SIZE);
/* something went wrong. */
if (tmp < 0)
@@ -1145,6 +1193,7 @@ tcp_conn_request(volatile struct sock *sk, struct sk_buff *skb,
sk->prot->wfree(newsk, buff->mem_addr, buff->mem_len);
newsk->dead = 1;
release_sock (newsk);
+ skb->sk = sk;
free_skb (skb, FREE_READ);
return;
}
@@ -1294,7 +1343,7 @@ tcp_close (volatile struct sock *sk, int timeout)
t1=(struct tcp_header *)(buff + 1);
/* put in the ip_header and routing stuff. */
tmp = prot->build_header (buff,sk->saddr, sk->daddr, &dev,
- IP_TCP, sk->opt,
+ IPPROTO_TCP, sk->opt,
sizeof(struct tcp_header));
if (tmp < 0)
{
@@ -1791,7 +1840,7 @@ tcp_fin (volatile struct sock *sk, struct tcp_header *th,
t1 = (struct tcp_header *)(buff + 1);
/* put in the ip_header and routing stuff. */
tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
- IP_TCP, sk->opt, MAX_ACK_SIZE);
+ IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
if (tmp < 0)
{
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
@@ -1939,7 +1988,7 @@ tcp_connect (volatile struct sock *sk, struct sockaddr_in *usin, int addr_len)
/* We need to build the routing stuff fromt the things saved
in skb. */
tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
- IP_TCP, NULL, MAX_SYN_SIZE);
+ IPPROTO_TCP, NULL, MAX_SYN_SIZE);
if (tmp < 0)
{
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
@@ -2457,7 +2506,7 @@ tcp_write_wakeup(volatile struct sock *sk)
/* put in the ip_header and routing stuff. */
tmp = sk->prot->build_header (buff, sk->saddr, sk->daddr, &dev,
- IP_TCP, sk->opt, MAX_ACK_SIZE);
+ IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
if (tmp < 0)
{
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index 878ba03..f85c4bb 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -83,7 +83,7 @@ enum {
checksum . */
void print_th (struct tcp_header *);
-#define HEADER_SIZE 64
+#define HEADER_SIZE 100
/* this next routines deal with comparing 32 bit unsigned ints and
diff --git a/net/tcp/udp.c b/net/tcp/udp.c
index 23529e0..0dcecba 100644
--- a/net/tcp/udp.c
+++ b/net/tcp/udp.c
@@ -31,6 +31,7 @@
#include "sock.h"
#include <linux/errno.h>
#include <linux/timer.h>
+#include <linux/termios.h> /* for ioctl's */
#include <asm/system.h>
#include <asm/segment.h>
#include "../kern_sock.h" /* for PRINTK */
@@ -123,7 +124,7 @@ udp_check (struct udp_header *uh, int len,
"\t adcl %%edx,%%ebx\n"
"\t adcl $0, %%ebx\n"
: "=b" (sum)
- : "0" (daddr), "c" (saddr), "d" ((net16(len) << 16) + IP_UDP*256)
+ : "0" (daddr), "c" (saddr), "d" ((net16(len) << 16) + IPPROTO_UDP*256)
: "cx","bx","dx" );
if (len > 3)
@@ -337,7 +338,7 @@ udp_sendto (volatile struct sock *sk, unsigned char *from, int len,
buff = (unsigned char *)(skb+1);
tmp = sk->prot->build_header (skb, saddr,
sin.sin_addr.s_addr, &dev,
- IP_UDP, sk->opt, skb->mem_len);
+ IPPROTO_UDP, sk->opt, skb->mem_len);
if (tmp < 0 )
{
sk->prot->wfree (sk, skb->mem_addr, skb->mem_len);
@@ -386,6 +387,50 @@ udp_write (volatile struct sock *sk, unsigned char *buff, int len, int noblock,
return (udp_sendto (sk, buff, len, noblock, flags, NULL, 0));
}
+
+static int
+udp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
+{
+ switch (cmd)
+ {
+ default:
+ return (-EINVAL);
+
+ case TIOCOUTQ:
+ {
+ unsigned long amount;
+ if (sk->state == TCP_LISTEN)
+ return (-EINVAL);
+ amount = sk->prot->wspace(sk)/2;
+ verify_area ((void *)arg, sizeof (unsigned long));
+ put_fs_long (amount, (unsigned long *)arg);
+ return (0);
+ }
+
+
+ case TIOCINQ:
+/* case FIONREAD:*/
+ {
+ struct sk_buff *skb;
+ unsigned long amount;
+ if (sk->state == TCP_LISTEN)
+ return (-EINVAL);
+ amount = 0;
+ skb = sk->rqueue;
+ if (skb != NULL)
+ {
+ /* we will only return the amount of this packet since that is all
+ that will be read. */
+ amount = skb->len;
+ }
+
+ verify_area ((void *)arg, sizeof (unsigned long));
+ put_fs_long (amount, (unsigned long *)arg);
+ return (0);
+ }
+ }
+}
+
int
udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
int noblock,
@@ -397,6 +442,17 @@ udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
struct sk_buff *skb;
if (len == 0) return (0);
if (len < 0) return (-EINVAL);
+
+ /* this will pick up errors that occured
+ while the program was doing something
+ else. */
+ if (sk->err)
+ {
+ int err;
+ err = -sk->err;
+ sk->err = 0;
+ return (err);
+ }
if (addr_len)
{
verify_area (addr_len, sizeof(*addr_len));
@@ -626,7 +682,7 @@ struct proto udp_prot =
NULL,
udp_rcv,
udp_select,
- NULL,
+ udp_ioctl,
NULL,
128,
0,
diff --git a/tools/version.h b/tools/version.h
index 80bdb09..fc6e01f 100644
--- a/tools/version.h
+++ b/tools/version.h
@@ -1,5 +1,5 @@
-#define UTS_RELEASE "0.98-22"
-#define UTS_VERSION "09/29/92"
-#define LINUX_COMPILE_TIME "21:12:04"
+#define UTS_RELEASE "0.98.pl2-10"
+#define UTS_VERSION "10/18/92"
+#define LINUX_COMPILE_TIME "11:37:18"
#define LINUX_COMPILE_BY "root"
#define LINUX_COMPILE_HOST "home"