Commit c96bf123 authored by Linus Torvalds's avatar Linus Torvalds

[PATCH] Linux-0.98.2 (October 18, 1992)

Start virtualizing real mmap() functionlity in the kernel.  The first
signs of me thinking about this already showed up as some unused header
files earlier, this fleshes things out some more.  No actual filesystem
code yet..

This also removes my old simple math emulator, and introduces the new
and much improved one from Bill Metzenthen.

Bill originally wrote it for the djgpp suite (DJ Delories gcc port to
DOS extenders).  It was much more accurate and well designed than my
hackish one, and I was happy to throw my old code away.  The new math
emulator also did things that I had never bothered with, notably the
more complex i387 functions (exponentials and trig).

I also fixed the static maximum memory limit: we now generate the kernel
page tables dynamically rather than having a 16M or 32M static limit.

SCSI updates: removable media support (which also implies re-reading the
partition table etc)

[Original announcement below]

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
parent 8ac5a423
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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:
......
......@@ -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 \
......
......@@ -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)
......
......@@ -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));
......
......@@ -45,6 +45,7 @@ static struct file_operations def_blk_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
blkdev_open, /* open */
NULL, /* release */
};
......
......@@ -45,6 +45,7 @@ static struct file_operations def_chr_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
chrdev_open, /* open */
NULL, /* release */
};
......
......@@ -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 */
};
......
......@@ -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 */
};
......
......@@ -771,6 +771,7 @@ static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_
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 @@ static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_
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;
}
......
......@@ -109,6 +109,7 @@ struct file_operations def_fifo_fops = {
NULL,
NULL,
NULL,
NULL,
fifo_open, /* will set read or write pipe_fops */
NULL
};
......@@ -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;
}
}
......@@ -39,6 +39,7 @@ static struct file_operations def_blk_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
blkdev_open, /* open */
NULL, /* release */
};
......
......@@ -39,6 +39,7 @@ static struct file_operations def_chr_fops = {
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
chrdev_open, /* open */
NULL, /* release */
};
......
......@@ -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 */
};
......
......@@ -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;
......
......@@ -633,6 +633,7 @@ static int do_minix_rename(struct inode * old_dir, const char * old_name, int ol
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 @@ static int do_minix_rename(struct inode * old_dir, const char * old_name, int ol
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;
}
......
......@@ -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");
}
......@@ -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 \
......
......@@ -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)
{
......
......@@ -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 */
};
......
......@@ -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;
}
......
......@@ -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
};
......
......@@ -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 */
};
......
......@@ -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 */
};
......
......@@ -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 */
};
......
......@@ -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 */
};
......
......@@ -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);
}
#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 */
......@@ -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
......
......@@ -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) \
......
......@@ -16,14 +16,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
......@@ -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
......
......@@ -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);
......
......@@ -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;
......
......@@ -7,6 +7,46 @@
#include <linux/fs.h>
#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
......@@ -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;
......
#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
......@@ -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)
......
......@@ -45,9 +45,21 @@ 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
......@@ -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! */
......@@ -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
......
......@@ -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
......
......@@ -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 */
......
#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
......@@ -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();
......
+---------------------------------------------------------------------------+
| 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
/*---------------------------------------------------------------------------+
| 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_
.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
This diff is collapsed.
/*---------------------------------------------------------------------------+
| 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_
/*---------------------------------------------------------------------------+
| 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();
}
/*---------------------------------------------------------------------------+
| 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_
/*---------------------------------------------------------------------------+
| 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();
}
/*---------------------------------------------------------------------------+
| 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_
/*---------------------------------------------------------------------------+
| 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 */
/*---------------------------------------------------------------------------+
| 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])();
}
/* 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);
/*---------------------------------------------------------------------------+
| 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
This diff is collapsed.
/*---------------------------------------------------------------------------+
| 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;
}
/*---------------------------------------------------------------------------+
| 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;
}
}
/*---------------------------------------------------------------------------+
| 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;
}
/*---------------------------------------------------------------------------+
| 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;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*---------------------------------------------------------------------------+
| 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"
This diff is collapsed.
This diff is collapsed.
......@@ -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 \
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment