From ddc733f452e0be7a8711d8640c5105f8a929ae15 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:09:03 -0500 Subject: [PATCH] [PATCH] Linux-0.97 (August 1, 1992) Move to Variable-sized buffer blocks and dynamic buffer cache allocation. The VM knows how to shrink it automatically! Add support for "fast" interrupt handlers for serial lines. Update copyrights to say 1992 too. Remove broken VESA video card handling. Separate out partition handling code ("genhd"). Make init unkillable. Norwegian keyboard map. Future Domain SCSI controller driver by Rik Faith. Changes in 0.97: - The VESA-support was removed. I'd be happy to put it back once it works on all hardware. Instead of the VESA-code, I finally put in the automatic SVGA setup patches. See the top-level Makefile. - The IRQ code has solidified, and should work on all machines. Not all of the SCSI drivers use it yet, so I expect patches for that.. - Serial interrupts are handled slightly differently, and performance should be up. I've sent out a few alpha-releases, and testing seems to indicate that's actually true this time. Reactions have ranged from "nice" to "wonderful" :-) - The buffer-cache and memory management code has been edited quite a bit. ps/free etc programs that reads kernel memory directly no longer work, and even a recompilation won't be enough. They actually need editing before they work. The buffer-cache now grows and shrinks dynamically depending on how much free memory there is. Shift+PrintScreen will give some memory statistics. (Ctrl+PrSc gives task-info, ALT+PrSc gives current register values). The mm code changes removed some race-conditions in the VM code, and I also tried to make the Out-of-swapspace error less severe (better thrashing-detection etc). - The super-block code has been cleaned up. Especially the extended fs needs to be edited a bit to take advantage of the new setup, and I expect Remy Card will have a patch out eventually. - include-files have been moved around some more: there are still some names that clash with the standard headers, but not many. - Unswappable processes implemented: by default only 'init' is unswappable. This is a bit safer in low-memory conditions, as at least init won't die due to low memory. I also made killing init impossible: if init doesn't recognize a signal, it simply won't get it. Some other changes ("while (1) fork();" won't kill the machine for non-root users etc) - The new SCSI drivers are in. These make the kernel noticeably bigger, but you can leave them out if you don't want them. - The floppy- and hd-drivers print out more debugging-info in case of errors: this might be irritating if you have hardware that works, but often gives soft-errors. On the other hand, some old debugging-info was removed - notably for user-level protection errors etc. - Various minor fixes. I haven't made cdiffs (and I haven't gotten any requests for them, so I probably never will), but they would be pretty big. Things that I didn't have time for: - I wanted to rewrite the tty drivers to be more "streams-like" (ie not an actual streams-implementation, but some of the ideas from streams). I never got around to it: there was simply too much else to do. - I got a lot of patches, and some went in, others didn't. If you think your patch was important, please re-send it relative to the new version. I'd like comments on the new system: performance / clarity of code etc. 0.97 should correct all known bugs (at least the ones I know about), but I guess that's just wishful thinking. Note that the dynamic buffer-code also handles differently-sized buffers, but that the rest of the system (block device drivers, filesystem code etc) cannot yet take advantage of this - there is still some coding needed. Linus --- .version | 2 +- Makefile | 181 ++-- boot/bootsect.S | 5 +- boot/head.s | 22 +- boot/setup.S | 16 +- fs/Makefile | 231 +++-- fs/block_dev.c | 7 +- fs/buffer.c | 345 ++++++-- fs/exec.c | 207 ++++- fs/ext/Makefile | 128 +++ fs/ext/bitmap.c | 242 ++++++ fs/ext/blkdev.c | 67 ++ fs/ext/chrdev.c | 68 ++ fs/ext/dir.c | 98 +++ fs/ext/fifo.c | 27 + fs/ext/file.c | 220 +++++ fs/ext/freelists.c | 344 ++++++++ fs/ext/inode.c | 416 +++++++++ fs/ext/namei.c | 901 ++++++++++++++++++++ fs/ext/symlink.c | 106 +++ fs/ext/truncate.c | 193 +++++ fs/fcntl.c | 16 +- fs/fifo.c | 114 +++ fs/file_table.c | 17 +- fs/inode.c | 60 +- fs/ioctl.c | 17 +- fs/minix/Makefile | 132 ++- fs/minix/bitmap.c | 75 +- fs/minix/blkdev.c | 61 ++ fs/minix/chrdev.c | 62 ++ fs/minix/dir.c | 91 ++ fs/minix/fifo.c | 27 + fs/minix/file.c | 220 +++++ fs/minix/file_dev.c | 169 ---- fs/minix/inode.c | 111 ++- fs/minix/minix_op.c | 62 -- fs/minix/namei.c | 136 ++- fs/minix/symlink.c | 100 +++ fs/minix/truncate.c | 60 +- fs/msdos/Makefile | 81 ++ fs/msdos/dir.c | 127 +++ fs/msdos/fat.c | 275 ++++++ fs/msdos/file.c | 208 +++++ fs/msdos/inode.c | 276 ++++++ fs/msdos/misc.c | 366 ++++++++ fs/msdos/namei.c | 515 ++++++++++++ fs/namei.c | 99 ++- fs/open.c | 198 +++-- fs/pipe.c | 179 ++-- fs/read_write.c | 16 +- fs/select.c | 191 +---- fs/stat.c | 113 ++- fs/super.c | 217 +++-- include/asm/io.h | 38 +- include/asm/irq.h | 147 ++++ include/asm/memory.h | 2 +- include/asm/segment.h | 2 +- include/asm/system.h | 14 +- include/errno.h | 64 -- include/limits.h | 62 -- include/{ => linux}/a.out.h | 2 + include/linux/config.dist.h | 31 + include/linux/config.h | 56 +- include/linux/config.site.h | 9 + include/linux/config_rel.h | 2 +- include/linux/config_ver.h | 2 +- include/{sys => linux}/dirent.h | 6 +- include/linux/errno.h | 130 +++ include/linux/ext_fs.h | 128 +++ include/linux/ext_fs_sb.h | 19 + include/{ => linux}/fcntl.h | 29 +- include/linux/fd.h | 40 + include/linux/fdreg.h | 24 +- include/linux/fs.h | 173 ++-- include/linux/genhd.h | 52 ++ include/linux/hdreg.h | 20 +- include/linux/head.h | 4 +- include/linux/kernel.h | 7 +- include/linux/limits.h | 27 + include/linux/lp.h | 22 +- include/linux/math_emu.h | 9 +- include/linux/minix_fs.h | 26 +- include/linux/minix_fs_sb.h | 19 + include/linux/mm.h | 88 +- include/linux/mouse.h | 61 ++ include/linux/msdos_fs.h | 172 ++++ include/linux/msdos_fs_sb.h | 18 + include/{sys => linux}/param.h | 0 include/{sys => linux}/ptrace.h | 21 +- include/{sys => linux}/resource.h | 15 +- include/linux/sched.h | 113 ++- include/{ => linux}/signal.h | 31 +- include/linux/socket.h | 30 + include/{sys => linux}/stat.h | 62 +- include/{ => linux}/stddef.h | 13 +- include/linux/string.h | 22 +- include/linux/sys.h | 14 +- include/{ => linux}/termios.h | 32 +- include/linux/time.h | 33 + include/linux/timer.h | 7 +- include/{sys => linux}/times.h | 8 +- include/linux/tty.h | 216 +++-- include/{sys => linux}/types.h | 27 +- include/linux/un.h | 9 + include/linux/unistd.h | 209 +++++ include/linux/user.h | 75 ++ include/linux/utime.h | 9 + include/linux/utsname.h | 24 + include/{sys => linux}/vfs.h | 4 +- include/linux/wait.h | 22 + include/sys/time.h | 64 -- include/sys/utsname.h | 17 - include/sys/wait.h | 24 - include/time.h | 11 + include/unistd.h | 348 -------- include/utime.h | 13 - init/main.c | 118 +-- kernel/Makefile | 195 +++-- kernel/asm.s | 84 -- kernel/blk_drv/Makefile | 94 ++- kernel/blk_drv/blk.h | 98 ++- kernel/blk_drv/floppy.c | 902 +++++++++++++++++--- kernel/blk_drv/genhd.c | 195 +++++ kernel/blk_drv/hd.c | 538 ++++++------ kernel/blk_drv/ll_rw_blk.c | 112 ++- kernel/blk_drv/ramdisk.c | 24 +- kernel/blk_drv/scsi/7000fasst.c | 465 ++++++++++ kernel/blk_drv/scsi/7000fasst.h | 137 +++ kernel/blk_drv/scsi/Makefile | 167 ++++ kernel/blk_drv/scsi/aha1542.c | 483 +++++++++++ kernel/blk_drv/scsi/aha1542.h | 139 +++ kernel/blk_drv/scsi/config.in | 29 + kernel/blk_drv/scsi/config.out | 10 + kernel/blk_drv/scsi/fdomain.c | 1234 +++++++++++++++++++++++++++ kernel/blk_drv/scsi/fdomain.h | 45 + kernel/blk_drv/scsi/hosts.c | 157 ++++ kernel/blk_drv/scsi/hosts.h | 179 ++++ kernel/blk_drv/scsi/scsi.c | 1060 +++++++++++++++++++++++ kernel/blk_drv/scsi/scsi.h | 266 ++++++ kernel/blk_drv/scsi/scsi_ioctl.c | 158 ++++ kernel/blk_drv/scsi/scsi_ioctl.h | 21 + kernel/blk_drv/scsi/sd.c | 422 ++++++++++ kernel/blk_drv/scsi/sd.h | 50 ++ kernel/blk_drv/scsi/sd_ioctl.c | 20 + kernel/blk_drv/scsi/seagate.c | 987 ++++++++++++++++++++++ kernel/blk_drv/scsi/seagate.h | 131 +++ kernel/blk_drv/scsi/seagate2.s | 36 + kernel/blk_drv/scsi/st.c | 32 + kernel/blk_drv/scsi/st.h | 26 + kernel/blk_drv/scsi/st_ioctl.c | 19 + kernel/blk_drv/scsi/ultrastor.c | 503 +++++++++++ kernel/blk_drv/scsi/ultrastor.h | 94 +++ kernel/chr_drv/Makefile | 173 ++-- kernel/chr_drv/console.c | 1038 +++++++++++++++-------- kernel/chr_drv/keyboard.S | 805 ------------------ kernel/chr_drv/keyboard.c | 1307 +++++++++++++++++++++++++++++ kernel/chr_drv/lp.c | 38 +- kernel/chr_drv/mem.c | 87 +- kernel/chr_drv/mouse.c | 182 ++++ kernel/chr_drv/pty.c | 56 +- kernel/chr_drv/rs_io.s | 164 ---- kernel/chr_drv/serial.c | 469 +++++++++-- kernel/chr_drv/tty_io.c | 605 ++++++++----- kernel/chr_drv/tty_ioctl.c | 214 +++-- kernel/chr_drv/vt.c | 98 ++- kernel/chr_drv/vt_kern.h | 13 +- kernel/exit.c | 119 ++- kernel/fork.c | 71 +- kernel/ioport.c | 32 +- kernel/irq.c | 236 ++++++ kernel/itimer.c | 114 +++ kernel/math/Makefile | 128 +-- kernel/math/add.c | 2 +- kernel/math/compare.c | 2 +- kernel/math/convert.c | 2 +- kernel/math/div.c | 2 +- kernel/math/ea.c | 10 +- kernel/math/emulate.c | 36 +- kernel/math/error.c | 16 - kernel/math/get_put.c | 5 +- kernel/math/mul.c | 2 +- kernel/math/sqrt.c | 95 +++ kernel/mktime.c | 2 +- kernel/panic.c | 2 +- kernel/printk.c | 88 +- kernel/ptrace.c | 113 ++- kernel/sched.c | 202 +++-- kernel/signal.c | 85 +- kernel/sys.c | 129 ++- kernel/sys_call.S | 149 +--- kernel/traps.c | 82 +- kernel/vsprintf.c | 3 +- lib/Makefile | 63 +- lib/_exit.c | 6 +- lib/close.c | 5 +- lib/ctype.c | 2 +- lib/dup.c | 5 +- lib/errno.c | 2 +- lib/execve.c | 5 +- lib/malloc.c | 4 +- lib/open.c | 4 +- lib/setsid.c | 6 +- lib/string.c | 4 +- lib/wait.c | 7 +- lib/write.c | 6 +- mm/Makefile | 53 +- mm/memory.c | 393 +++++---- mm/mmap.c | 16 +- mm/swap.c | 209 +++-- net/Makefile | 52 ++ net/kern_sock.h | 67 ++ net/socket.c | 766 +++++++++++++++++ net/socketcall.h | 13 + net/unix.c | 600 +++++++++++++ tools/build.c | 12 +- 215 files changed, 24229 insertions(+), 5472 deletions(-) create mode 100644 fs/ext/Makefile create mode 100644 fs/ext/bitmap.c create mode 100644 fs/ext/blkdev.c create mode 100644 fs/ext/chrdev.c create mode 100644 fs/ext/dir.c create mode 100644 fs/ext/fifo.c create mode 100644 fs/ext/file.c create mode 100644 fs/ext/freelists.c create mode 100644 fs/ext/inode.c create mode 100644 fs/ext/namei.c create mode 100644 fs/ext/symlink.c create mode 100644 fs/ext/truncate.c create mode 100644 fs/fifo.c create mode 100644 fs/minix/blkdev.c create mode 100644 fs/minix/chrdev.c create mode 100644 fs/minix/dir.c create mode 100644 fs/minix/fifo.c create mode 100644 fs/minix/file.c delete mode 100644 fs/minix/file_dev.c delete mode 100644 fs/minix/minix_op.c create mode 100644 fs/minix/symlink.c create mode 100644 fs/msdos/Makefile create mode 100644 fs/msdos/dir.c create mode 100644 fs/msdos/fat.c create mode 100644 fs/msdos/file.c create mode 100644 fs/msdos/inode.c create mode 100644 fs/msdos/misc.c create mode 100644 fs/msdos/namei.c create mode 100644 include/asm/irq.h delete mode 100644 include/errno.h delete mode 100644 include/limits.h rename include/{ => linux}/a.out.h (99%) create mode 100644 include/linux/config.dist.h create mode 100644 include/linux/config.site.h rename include/{sys => linux}/dirent.h (62%) create mode 100644 include/linux/errno.h create mode 100644 include/linux/ext_fs.h create mode 100644 include/linux/ext_fs_sb.h rename include/{ => linux}/fcntl.h (61%) create mode 100644 include/linux/fd.h create mode 100644 include/linux/genhd.h create mode 100644 include/linux/limits.h create mode 100644 include/linux/minix_fs_sb.h create mode 100644 include/linux/mouse.h create mode 100644 include/linux/msdos_fs.h create mode 100644 include/linux/msdos_fs_sb.h rename include/{sys => linux}/param.h (100%) rename include/{sys => linux}/ptrace.h (66%) rename include/{sys => linux}/resource.h (91%) rename include/{ => linux}/signal.h (63%) create mode 100644 include/linux/socket.h rename include/{sys => linux}/stat.h (50%) rename include/{ => linux}/stddef.h (52%) rename include/{ => linux}/termios.h (88%) create mode 100644 include/linux/time.h rename include/{sys => linux}/times.h (51%) rename include/{sys => linux}/types.h (72%) create mode 100644 include/linux/un.h create mode 100644 include/linux/unistd.h create mode 100644 include/linux/user.h create mode 100644 include/linux/utime.h create mode 100644 include/linux/utsname.h rename include/{sys => linux}/vfs.h (83%) create mode 100644 include/linux/wait.h delete mode 100644 include/sys/time.h delete mode 100644 include/sys/utsname.h delete mode 100644 include/sys/wait.h delete mode 100644 include/unistd.h delete mode 100644 include/utime.h delete mode 100644 kernel/asm.s create mode 100644 kernel/blk_drv/genhd.c create mode 100644 kernel/blk_drv/scsi/7000fasst.c create mode 100644 kernel/blk_drv/scsi/7000fasst.h create mode 100644 kernel/blk_drv/scsi/Makefile create mode 100644 kernel/blk_drv/scsi/aha1542.c create mode 100644 kernel/blk_drv/scsi/aha1542.h create mode 100644 kernel/blk_drv/scsi/config.in create mode 100644 kernel/blk_drv/scsi/config.out create mode 100644 kernel/blk_drv/scsi/fdomain.c create mode 100644 kernel/blk_drv/scsi/fdomain.h create mode 100644 kernel/blk_drv/scsi/hosts.c create mode 100644 kernel/blk_drv/scsi/hosts.h create mode 100644 kernel/blk_drv/scsi/scsi.c create mode 100644 kernel/blk_drv/scsi/scsi.h create mode 100644 kernel/blk_drv/scsi/scsi_ioctl.c create mode 100644 kernel/blk_drv/scsi/scsi_ioctl.h create mode 100644 kernel/blk_drv/scsi/sd.c create mode 100644 kernel/blk_drv/scsi/sd.h create mode 100644 kernel/blk_drv/scsi/sd_ioctl.c create mode 100644 kernel/blk_drv/scsi/seagate.c create mode 100644 kernel/blk_drv/scsi/seagate.h create mode 100644 kernel/blk_drv/scsi/seagate2.s create mode 100644 kernel/blk_drv/scsi/st.c create mode 100644 kernel/blk_drv/scsi/st.h create mode 100644 kernel/blk_drv/scsi/st_ioctl.c create mode 100644 kernel/blk_drv/scsi/ultrastor.c create mode 100644 kernel/blk_drv/scsi/ultrastor.h delete mode 100644 kernel/chr_drv/keyboard.S create mode 100644 kernel/chr_drv/keyboard.c create mode 100644 kernel/chr_drv/mouse.c delete mode 100644 kernel/chr_drv/rs_io.s create mode 100644 kernel/irq.c create mode 100644 kernel/itimer.c delete mode 100644 kernel/math/error.c create mode 100644 kernel/math/sqrt.c create mode 100644 net/Makefile create mode 100644 net/kern_sock.h create mode 100644 net/socket.c create mode 100644 net/socketcall.h create mode 100644 net/unix.c diff --git a/.version b/.version index fb1e7bc86996..b4de39476753 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -54 +11 diff --git a/Makefile b/Makefile index dc83ace062bf..39e87db0cbb3 100644 --- a/Makefile +++ b/Makefile @@ -1,38 +1,54 @@ # -# comment this line if you don't want the emulation-code +# ROOT_DEV specifies the default root-device when making the image. +# This can be either FLOPPY, /dev/xxxx or empty, in which case the +# default of FLOPPY is used by 'build'. # -MATH_EMULATION = -DKERNEL_MATH_EMULATION +ROOT_DEV = /dev/hdb1 # # uncomment the correct keyboard: # - -KEYBOARD = -DKBD_FINNISH -# KEYBOARD = -DKBD_US -# KEYBOARD = -DKBD_GR -# KEYBOARD = -DKBD_FR -# KEYBOARD = -DKBD_UK -# KEYBOARD = -DKBD_DK - -# -# uncomment this line if you are using gcc-1.40 -# -#GCC_OPT = -fcombine-regs -fstrength-reduce +# The value of KBDFLAGS should be or'ed together from the following +# bits, depending on which features you want enabled. +# 0x80 - Off: the Alt key will set bit 7 if pressed together with +# another key. +# On: the Alt key will NOT set the high bit; an escape +# character is prepended instead. +# The least significant bits control if the following keys are "dead". +# The key is dead by default if the bit is on. +# 0x01 - backquote (`) +# 0x02 - accent acute +# 0x04 - circumflex (^) +# 0x08 - tilde (~) +# 0x10 - dieresis (umlaut) + +KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0 +# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_US -DKBDFLAGS=0 +# KEYBOARD = -DKBD_GR -DKBDFLAGS=0 +# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_FR -DKBDFLAGS=0 +# KEYBOARD = -DKBD_FR_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_UK -DKBDFLAGS=0 +# KEYBOARD = -DKBD_DK -DKBDFLAGS=0 +# KEYBOARD = -DKBD_DK_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0 +# KEYBOARD = -DKBD_SG -DKBDFLAGS=0 +# KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKDB_NO # -# standard CFLAGS +# comment this line if you don't want the emulation-code # -CFLAGS =-Wall -O6 -fomit-frame-pointer $(GCC_OPT) +MATH_EMULATION = -DKERNEL_MATH_EMULATION # -# ROOT_DEV specifies the default root-device when making the image. -# This can be either FLOPPY, /dev/xxxx or empty, in which case the -# default of FLOPPY is used by 'build'. +# standard CFLAGS # -ROOT_DEV = /dev/hdb1 +CFLAGS =-Wall -O6 -fomit-frame-pointer # # if you want the ram-disk device, define this to be the @@ -44,34 +60,47 @@ ROOT_DEV = /dev/hdb1 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 -#LDFLAGS =-s -x -M -LDFLAGS = -M -CC =gcc $(RAMDISK) -MAKE =make CFLAGS="$(CFLAGS)" -CPP =cpp -nostdinc -Iinclude - -ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o -FILESYSTEMS =fs/minix/minix.o -DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a +HOSTCC =gcc -static +CC =gcc -nostdinc -I$(KERNELHDRS) +MAKE =make +CPP =$(CC) -E +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 +DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a \ + kernel/blk_drv/scsi/scsi.a MATH =kernel/math/math.a LIBS =lib/lib.a +SUBDIRS =kernel mm fs net lib + +KERNELHDRS =/usr/src/linux/include .c.s: - $(CC) $(CFLAGS) \ - -nostdinc -Iinclude -S -o $*.s $< + $(CC) $(CFLAGS) -S $< .s.o: $(AS) -c -o $*.o $< .c.o: - $(CC) $(CFLAGS) \ - -nostdinc -Iinclude -c -o $*.o $< + $(CC) $(CFLAGS) -c -o $*.o $< all: Version Image +linuxsubdirs: dummy + @for i in $(SUBDIRS); do (cd $$i; echo $$i; $(MAKE)) || exit; done + Version: @./makever.sh - @echo \#define UTS_RELEASE \"0.95c-`cat .version`\" > include/linux/config_rel.h + @echo \#define UTS_RELEASE \"0.97-`cat .version`\" > include/linux/config_rel.h @echo \#define UTS_VERSION \"`date +%D`\" > include/linux/config_ver.h touch include/linux/config.h @@ -83,54 +112,29 @@ Image: boot/bootsect boot/setup tools/system tools/build sync disk: Image - dd bs=8192 if=Image of=/dev/PS0 + dd bs=8192 if=Image of=/dev/fd0 tools/build: tools/build.c - $(CC) -static $(CFLAGS) \ + $(HOSTCC) $(CFLAGS) \ -o tools/build tools/build.c boot/head.o: boot/head.s -tools/system: boot/head.o init/main.o \ - $(ARCHIVES) $(FILESYSTEMS) $(DRIVERS) $(MATH) $(LIBS) - $(LD) $(LDFLAGS) boot/head.o init/main.o \ - $(ARCHIVES) \ - $(FILESYSTEMS) \ - $(DRIVERS) \ - $(MATH) \ - $(LIBS) \ - -o tools/system > System.map - -kernel/math/math.a: dummy - (cd kernel/math; $(MAKE) MATH_EMULATION="$(MATH_EMULATION)") - -kernel/blk_drv/blk_drv.a: dummy - (cd kernel/blk_drv; $(MAKE)) - -kernel/chr_drv/chr_drv.a: dummy - (cd kernel/chr_drv; $(MAKE) KEYBOARD="$(KEYBOARD)") - -kernel/kernel.o: dummy - (cd kernel; $(MAKE)) - -mm/mm.o: dummy - (cd mm; $(MAKE)) - -fs/fs.o: dummy - (cd fs; $(MAKE)) - -fs/minix/minix.o: dummy - (cd fs/minix; $(MAKE)) - -lib/lib.a: dummy - (cd lib; $(MAKE)) +tools/system: boot/head.o init/main.o linuxsubdirs + $(LD) $(LDFLAGS) -M boot/head.o init/main.o \ + $(ARCHIVES) \ + $(FILESYSTEMS) \ + $(DRIVERS) \ + $(MATH) \ + $(LIBS) \ + -o tools/system > System.map boot/setup: boot/setup.s $(AS86) -o boot/setup.o boot/setup.s $(LD86) -s -o boot/setup boot/setup.o boot/setup.s: boot/setup.S include/linux/config.h - $(CPP) -traditional boot/setup.S -o boot/setup.s + $(CPP) -traditional $(SVGA_MODE) boot/setup.S -o boot/setup.s boot/bootsect.s: boot/bootsect.S include/linux/config.h $(CPP) -traditional boot/bootsect.S -o boot/bootsect.s @@ -139,35 +143,36 @@ boot/bootsect: boot/bootsect.s $(AS86) -o boot/bootsect.o boot/bootsect.s $(LD86) -s -o boot/bootsect boot/bootsect.o +fs: dummy + $(MAKE) linuxsubdirs SUBDIRS=fs + clean: rm -f Image System.map tmp_make core boot/bootsect boot/setup \ boot/bootsect.s boot/setup.s init/main.s rm -f init/*.o tools/system tools/build boot/*.o - (cd mm;make clean) - (cd fs;make clean) - (cd kernel;make clean) - (cd lib;make clean) + for i in $(SUBDIRS); do (cd $$i; $(MAKE) clean); done backup: clean - (cd .. ; tar cf - linux | compress - > backup.Z) + cd .. ; tar cf - linux | compress - > backup.Z sync -dep: +depend dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make + for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile - (cd fs; make dep) - (cd kernel; make dep) - (cd mm; make dep) - (cd lib; make dep) + for i in $(SUBDIRS); do (cd $$i; $(MAKE) dep) || exit; done dummy: ### Dependencies: -init/main.o : init/main.c include/unistd.h include/sys/stat.h include/sys/types.h \ - include/sys/time.h include/time.h include/sys/times.h include/sys/utsname.h \ - include/sys/param.h include/sys/resource.h include/utime.h include/linux/sched.h \ - include/linux/head.h include/linux/fs.h include/sys/dirent.h include/limits.h \ - include/linux/mm.h include/linux/kernel.h include/signal.h include/linux/tty.h \ - include/termios.h include/linux/string.h include/asm/system.h include/asm/io.h \ - include/stddef.h include/stdarg.h include/fcntl.h +init/main.o : init/main.c /usr/src/linux/include/stdarg.h /usr/src/linux/include/time.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h /usr/src/linux/include/linux/unistd.h diff --git a/boot/bootsect.S b/boot/bootsect.S index 837be3219e07..b69e95ca8682 100644 --- a/boot/bootsect.S +++ b/boot/bootsect.S @@ -6,7 +6,7 @@ #include SYSSIZE = DEF_SYSSIZE ! -! bootsect.s (C) 1991 Linus Torvalds +! bootsect.s Copyright (C) 1991, 1992 Linus Torvalds ! modified by Drew Eckhardt ! modified by Bruce Evans (bde) ! @@ -224,8 +224,7 @@ got_sectors: mov ax,#0x021c ! /dev/PS0 - 1.44Mb cmp bx,#18 je root_defined -undef_root: - jmp undef_root + mov ax,#0x0200 ! /dev/fd0 - autodetect root_defined: seg cs mov root_dev,ax diff --git a/boot/head.s b/boot/head.s index 037713941a85..30ef2d861c5e 100644 --- a/boot/head.s +++ b/boot/head.s @@ -1,7 +1,7 @@ /* * linux/boot/head.s * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -13,6 +13,9 @@ */ .text .globl _idt,_gdt,_pg_dir,_tmp_floppy_area,_floppy_track_buffer +/* + * pg_dir is the main page directory, address 0x00000000 + */ _pg_dir: startup_32: cld @@ -147,6 +150,23 @@ pg2: pg3: .org 0x5000 +/* + * 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: + +.org 0x6000 +/* + * 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 +_empty_bad_page_table: + +.org 0x7000 /* * tmp_floppy_area is used by the floppy-driver when DMA cannot * reach to a buffer-block. It needs to be aligned, so that it isn't diff --git a/boot/setup.S b/boot/setup.S index bd23161825c2..cf4ae0ee5cf3 100644 --- a/boot/setup.S +++ b/boot/setup.S @@ -1,5 +1,5 @@ ! -! setup.s (C) 1991 Linus Torvalds +! setup.s Copyright (C) 1991, 1992 Linus Torvalds ! ! setup.s is responsible for getting the system data from the BIOS, ! and putting them into the appropriate places in system memory. @@ -14,6 +14,7 @@ ! NOTE! These had better be the same as in bootsect.s! #include +#define NORMAL_VGA 0xffff INITSEG = DEF_INITSEG ! we move boot here - out of the way SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536). @@ -189,9 +190,10 @@ end_move: out #0xA1,al .word 0x00eb,0x00eb mov al,#0xFF ! mask off all interrupts for now - out #0x21,al - .word 0x00eb,0x00eb out #0xA1,al + .word 0x00eb,0x00eb + mov al,#0xFB ! mask all irq's but irq2 which + out #0x21,al ! is cascaded ! well, that certainly wasn't fun :-(. Hopefully it works, and we don't ! need no steenking BIOS anyway (except for the initial loading :-). @@ -245,6 +247,7 @@ chsvga: cld mov es,ax lea si,msg1 call prtstr +#ifndef SVGA_MODE flush: in al,#0x60 ! Flush the keyboard buffer cmp al,#0x82 jb nokey @@ -256,9 +259,12 @@ nokey: call getkey ja nokey cmp al,#0x9c je svga +#endif +#if !defined(SVGA_MODE) || SVGA_MODE == NORMAL_VGA mov ax,#0x5019 pop ds ret +#endif svga: cld lea si,idati ! Check ATI 'clues' mov di,#0x31 @@ -497,6 +503,9 @@ tbl: pop bx call prtstr pop si add cl,#0x80 +#if defined(SVGA_MODE) && SVGA_MODE != NORMAL_VGA + mov al,#SVGA_MODE ! Preset SVGA mode +#else nonum: call getkey cmp al,#0x82 jb nonum @@ -508,6 +517,7 @@ nonum: call getkey zero: sub al,#0x0a nozero: sub al,#0x80 dec al +#endif xor ah,ah add di,ax inc di diff --git a/fs/Makefile b/fs/Makefile index 602e09f33472..bfe604b37e05 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -7,108 +7,167 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -AR =ar -AS =as -LD =ld -CC =gcc -nostdinc -I../include -CPP =cpp -nostdinc -I../include +SUBDIRS =minix ext msdos .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) -S $< .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) -c $< .s.o: $(AS) -o $*.o $< OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \ block_dev.o stat.o exec.o pipe.o namei.o fcntl.o ioctl.o \ - select.o + select.o fifo.o + +all: fs.o fssubdirs fs.o: $(OBJS) $(LD) -r -o fs.o $(OBJS) +fssubdirs: dummy + @for i in $(SUBDIRS); do (cd $$i; echo $$i; $(MAKE)) || exit; done + clean: rm -f core *.o *.a tmp_make - for i in *.c;do rm -f `basename $$i .c`.s;done - cd minix; make clean + for i in *.c; do rm -f `basename $$i .c`.s;done + for i in $(SUBDIRS); do (cd $$i; $(MAKE) clean); done -dep: +depend dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile - cd minix; make dep + for i in $(SUBDIRS); do (cd $$i; $(MAKE) dep) || exit; done + +dummy: ### Dependencies: -block_dev.o : block_dev.c ../include/errno.h ../include/linux/sched.h ../include/linux/head.h \ - ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ - ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/segment.h \ - ../include/asm/system.h -buffer.o : buffer.c ../include/stdarg.h ../include/linux/config.h ../include/linux/config_rel.h \ - ../include/linux/config_ver.h ../include/linux/sched.h ../include/linux/head.h \ - ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ - ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/system.h \ - ../include/asm/io.h -exec.o : exec.c ../include/signal.h ../include/sys/types.h ../include/errno.h \ - ../include/linux/string.h ../include/sys/stat.h ../include/a.out.h ../include/linux/fs.h \ - ../include/sys/dirent.h ../include/limits.h ../include/linux/sched.h ../include/linux/head.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h ../include/asm/segment.h -fcntl.o : fcntl.c ../include/linux/string.h ../include/errno.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \ - ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/asm/segment.h ../include/fcntl.h ../include/sys/stat.h -file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \ - ../include/limits.h -inode.o : inode.c ../include/linux/string.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h \ - ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/asm/system.h -ioctl.o : ioctl.c ../include/linux/string.h ../include/errno.h ../include/sys/stat.h \ - ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ - ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h \ - ../include/signal.h ../include/sys/param.h ../include/sys/time.h ../include/time.h \ - ../include/sys/resource.h -namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ - ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h \ - ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h ../include/asm/segment.h ../include/linux/string.h \ - ../include/fcntl.h ../include/errno.h ../include/const.h ../include/sys/stat.h -open.o : open.c ../include/errno.h ../include/fcntl.h ../include/sys/types.h \ - ../include/utime.h ../include/sys/stat.h ../include/sys/vfs.h ../include/linux/string.h \ - ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h \ - ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/asm/segment.h -pipe.o : pipe.c ../include/signal.h ../include/sys/types.h ../include/errno.h \ - ../include/termios.h ../include/fcntl.h ../include/asm/segment.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h -read_write.o : read_write.c ../include/errno.h ../include/sys/types.h ../include/sys/stat.h \ - ../include/sys/dirent.h ../include/limits.h ../include/linux/kernel.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/linux/minix_fs.h ../include/asm/segment.h -select.o : select.c ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \ - ../include/limits.h ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \ - ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/linux/string.h ../include/asm/segment.h ../include/asm/system.h ../include/sys/stat.h \ - ../include/const.h ../include/errno.h -stat.o : stat.c ../include/errno.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/linux/fs.h ../include/sys/dirent.h ../include/limits.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/asm/segment.h -super.o : super.c ../include/linux/config.h ../include/linux/config_rel.h ../include/linux/config_ver.h \ - ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ - ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h \ - ../include/signal.h ../include/sys/param.h ../include/sys/time.h ../include/time.h \ - ../include/sys/resource.h ../include/linux/minix_fs.h ../include/asm/system.h \ - ../include/asm/segment.h ../include/errno.h ../include/sys/stat.h +block_dev.o : block_dev.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h +buffer.o : buffer.c /usr/src/linux/include/stdarg.h /usr/src/linux/include/linux/config.h \ + /usr/src/linux/include/linux/config_rel.h /usr/src/linux/include/linux/config_ver.h \ + /usr/src/linux/include/linux/config.dist.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h +exec.o : exec.c /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/a.out.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/ptrace.h \ + /usr/src/linux/include/linux/user.h /usr/src/linux/include/asm/segment.h +fcntl.o : fcntl.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/string.h +fifo.o : fifo.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/fcntl.h +file_table.o : file_table.c /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/string.h +inode.o : inode.c /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/system.h +ioctl.o : ioctl.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/stat.h +namei.o : namei.c /usr/src/linux/include/const.h /usr/src/linux/include/asm/segment.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/stat.h +open.o : open.c /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/utime.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/asm/segment.h +pipe.o : pipe.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/termios.h +read_write.o : read_write.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h /usr/src/linux/include/linux/minix_fs.h \ + /usr/src/linux/include/asm/segment.h +select.o : select.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/segment.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/const.h +stat.o : stat.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/stat.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h /usr/src/linux/include/asm/segment.h +super.o : super.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/ext_fs.h \ + /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/stat.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/segment.h diff --git a/fs/block_dev.c b/fs/block_dev.c index c102c943a653..8bb0addc7928 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1,11 +1,10 @@ /* * linux/fs/block_dev.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include - +#include #include #include #include @@ -36,7 +35,7 @@ int block_write(struct inode * inode, struct file * filp, char * buf, int count) if (chars > count) chars=count; if (chars == BLOCK_SIZE) - bh = getblk(dev,block); + bh = getblk(dev, block, BLOCK_SIZE); else bh = breada(dev,block,block+1,block+2,-1); block++; diff --git a/fs/buffer.c b/fs/buffer.c index 4b80848ede48..f8efd1f1740c 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1,7 +1,7 @@ /* * linux/fs/buffer.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -23,15 +23,18 @@ #include #include #include +#include + #include #include -extern int end; -static struct buffer_head * start_buffer = (struct buffer_head *) &end; static struct buffer_head * hash_table[NR_HASH]; -static struct buffer_head * free_list; -static struct task_struct * buffer_wait = NULL; -int NR_BUFFERS = 0; +static struct buffer_head * free_list = NULL; +static struct buffer_head * unused_list = NULL; +static struct wait_queue * buffer_wait = NULL; + +int nr_buffers = 0; +int nr_buffer_heads = 0; static inline void wait_on_buffer(struct buffer_head * bh) { @@ -47,23 +50,25 @@ static void sync_buffers(int dev) struct buffer_head * bh; bh = free_list; - for (i=0 ; ib_next_free) { -#if 0 - if (dev && (bh->b_dev != dev)) + for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) { + if (bh->b_lock) continue; -#endif - wait_on_buffer(bh); -#if 0 - if (dev && (bh->b_dev != dev)) + if (!bh->b_dirt) continue; -#endif - if (bh->b_dirt) - ll_rw_block(WRITE,bh); + ll_rw_block(WRITE,bh); } } int sys_sync(void) { + int i; + + for (i=0 ; iwrite_super + && super_block[i].s_dirt) + super_block[i].s_op->write_super(&super_block[i]); sync_inodes(); /* write out inodes into buffers */ sync_buffers(0); return 0; @@ -71,6 +76,11 @@ int sys_sync(void) int sync_dev(int dev) { + struct super_block * sb; + + if (sb = get_super (dev)) + if (sb->s_op && sb->s_op->write_super && sb->s_dirt) + sb->s_op->write_super (sb); sync_buffers(dev); sync_inodes(); sync_buffers(dev); @@ -82,8 +92,8 @@ void inline invalidate_buffers(int dev) int i; struct buffer_head * bh; - bh = start_buffer; - for (i=0 ; i 0 ; bh = bh->b_next_free) { if (bh->b_dev != dev) continue; wait_on_buffer(bh); @@ -113,7 +123,7 @@ void check_disk_change(int dev) if (MAJOR(dev) != 2) return; - if (!(bh = getblk(dev,0))) + if (!(bh = getblk(dev,0,1024))) return; i = floppy_change(bh); brelse(bh); @@ -204,13 +214,18 @@ static inline void insert_into_queues(struct buffer_head * bh) bh->b_next->b_prev = bh; } -static struct buffer_head * find_buffer(int dev, int block) +static struct buffer_head * find_buffer(int dev, int block, int size) { struct buffer_head * tmp; for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) if (tmp->b_dev==dev && tmp->b_blocknr==block) - return tmp; + if (tmp->b_size == size) + return tmp; + else { + printk("wrong block-size on device %04x\n",dev); + return NULL; + } return NULL; } @@ -221,16 +236,16 @@ static struct buffer_head * find_buffer(int dev, int block) * will force it bad). This shouldn't really happen currently, but * the code is ready. */ -struct buffer_head * get_hash_table(int dev, int block) +struct buffer_head * get_hash_table(int dev, int block, int size) { struct buffer_head * bh; for (;;) { - if (!(bh=find_buffer(dev,block))) + if (!(bh=find_buffer(dev,block,size))) return NULL; bh->b_count++; wait_on_buffer(bh); - if (bh->b_dev == dev && bh->b_blocknr == block) { + if (bh->b_dev == dev && bh->b_blocknr == block && bh->b_size == size) { put_last_free(bh); return bh; } @@ -249,45 +264,55 @@ struct buffer_head * get_hash_table(int dev, int block) * when the filesystem starts to get full of dirty blocks (I hope). */ #define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock) -struct buffer_head * getblk(int dev,int block) +struct buffer_head * getblk(int dev, int block, int size) { struct buffer_head * bh, * tmp; int buffers; repeat: - if (bh = get_hash_table(dev,block)) + if (bh = get_hash_table(dev, block, size)) return bh; - buffers = NR_BUFFERS; - tmp = free_list; - do { - tmp = tmp->b_next_free; - if (tmp->b_count) + + if (nr_free_pages > 30) + grow_buffers(size); + + buffers = nr_buffers; + bh = NULL; + + for (tmp = free_list; buffers-- > 0 ; tmp = tmp->b_next_free) { + if (tmp->b_count || tmp->b_size != size) continue; if (!bh || BADNESS(tmp)b_dirt) ll_rw_block(WRITEA,tmp); +#endif + } + + if (!bh && nr_free_pages > 5) { + grow_buffers(size); + goto repeat; + } + /* and repeat until we find something good */ - } while (buffers--); if (!bh) { sleep_on(&buffer_wait); goto repeat; } wait_on_buffer(bh); - if (bh->b_count) + if (bh->b_count || bh->b_size != size) + goto repeat; + if (bh->b_dirt) { + sync_buffers(bh->b_dev); goto repeat; - while (bh->b_dirt) { - sync_dev(bh->b_dev); - wait_on_buffer(bh); - if (bh->b_count) - goto repeat; } /* NOTE!! While we slept waiting for this block, somebody else might */ /* already have added "this" block to the cache. check it */ - if (find_buffer(dev,block)) + if (find_buffer(dev,block,size)) goto repeat; /* OK, FINALLY we know that this buffer is the only one of it's kind, */ /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */ @@ -315,12 +340,14 @@ void brelse(struct buffer_head * buf) * bread() reads a specified block and returns the buffer that contains * it. It returns NULL if the block was unreadable. */ -struct buffer_head * bread(int dev,int block) +struct buffer_head * bread(int dev, int block, int size) { struct buffer_head * bh; - if (!(bh=getblk(dev,block))) - panic("bread: getblk returned NULL\n"); + if (!(bh = getblk(dev, block, size))) { + printk("bread: getblk returned NULL\n"); + return NULL; + } if (bh->b_uptodate) return bh; ll_rw_block(READ,bh); @@ -351,7 +378,7 @@ void bread_page(unsigned long address,int dev,int b[4]) for (i=0 ; i<4 ; i++) if (b[i]) { - if (bh[i] = getblk(dev,b[i])) + if (bh[i] = getblk(dev, b[i], 1024)) if (!bh[i]->b_uptodate) ll_rw_block(READ,bh[i]); } else @@ -376,15 +403,17 @@ struct buffer_head * breada(int dev,int first, ...) struct buffer_head * bh, *tmp; va_start(args,first); - if (!(bh=getblk(dev,first))) - panic("bread: getblk returned NULL\n"); + if (!(bh = getblk(dev, first, 1024))) { + printk("breada: getblk returned NULL\n"); + return NULL; + } if (!bh->b_uptodate) ll_rw_block(READ,bh); while ((first=va_arg(args,int))>=0) { - tmp=getblk(dev,first); + tmp = getblk(dev, first, 1024); if (tmp) { if (!tmp->b_uptodate) - ll_rw_block(READA,bh); + ll_rw_block(READA,tmp); tmp->b_count--; } } @@ -396,41 +425,201 @@ struct buffer_head * breada(int dev,int first, ...) return (NULL); } -void buffer_init(long buffer_end) +static void put_unused_buffer_head(struct buffer_head * bh) +{ + memset((void *) bh,0,sizeof(*bh)); + bh->b_next_free = unused_list; + unused_list = bh; +} + +static void get_more_buffer_heads(void) +{ + unsigned long page; + struct buffer_head * bh; + + if (unused_list) + return; + page = get_free_page(GFP_KERNEL); + if (!page) + return; + bh = (struct buffer_head *) page; + while ((unsigned long) (bh+1) <= page+4096) { + put_unused_buffer_head(bh); + bh++; + nr_buffer_heads++; + } +} + +static struct buffer_head * get_unused_buffer_head(void) +{ + struct buffer_head * bh; + + get_more_buffer_heads(); + if (!unused_list) + return NULL; + bh = unused_list; + unused_list = bh->b_next_free; + bh->b_next_free = NULL; + bh->b_data = NULL; + bh->b_size = 0; + return bh; +} + +/* + * Try to increase the number of buffers available: the size argument + * is used to determine what kind of buffers we want. Currently only + * 1024-byte buffers are supported by the rest of the system, but I + * think this will change eventually. + */ +void grow_buffers(int size) { - struct buffer_head * h = start_buffer; - void * b; + unsigned long page; int i; + struct buffer_head *bh, *tmp; - if (buffer_end == 1<<20) - b = (void *) (640*1024); - else - b = (void *) buffer_end; - while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { - if (((unsigned long) (h+1)) > 0xA0000) { - printk("buffer-list doesn't fit in low meg - contact Linus\n"); + if ((size & 511) || (size > 4096)) { + printk("grow_buffers: size = %d\n",size); + return; + } + page = get_free_page(GFP_BUFFER); + if (!page) + return; + tmp = NULL; + i = 0; + for (i = 0 ; i+size <= 4096 ; i += size) { + bh = get_unused_buffer_head(); + if (!bh) + goto no_grow; + bh->b_this_page = tmp; + tmp = bh; + bh->b_data = (char * ) (page+i); + bh->b_size = size; + i += size; + } + tmp = bh; + while (1) { + tmp->b_next_free = free_list; + tmp->b_prev_free = free_list->b_prev_free; + free_list->b_prev_free->b_next_free = tmp; + free_list->b_prev_free = tmp; + free_list = tmp; + ++nr_buffers; + if (tmp->b_this_page) + tmp = tmp->b_this_page; + else break; + } + tmp->b_this_page = bh; + return; +/* + * In case anything failed, we just free everything we got. + */ +no_grow: + bh = tmp; + while (bh) { + tmp = bh; + bh = bh->b_this_page; + put_unused_buffer_head(tmp); + } + free_page(page); +} + +/* + * try_to_free() checks if all the buffers on this particular page + * are unused, and free's the page if so. + */ +static int try_to_free(struct buffer_head * bh) +{ + unsigned long page; + struct buffer_head * tmp, * p; + + tmp = bh; + do { + if (!tmp) + return 0; + if (tmp->b_count || tmp->b_dirt || tmp->b_lock) + return 0; + tmp = tmp->b_this_page; + } while (tmp != bh); + page = (unsigned long) bh->b_data; + page &= 0xfffff000; + tmp = bh; + do { + p = tmp; + tmp = tmp->b_this_page; + nr_buffers--; + remove_from_queues(p); + put_unused_buffer_head(p); + } while (tmp != bh); + free_page(page); + return 1; +} + +/* + * Try to free up some pages by shrinking the buffer-cache + */ +int shrink_buffers(void) +{ + struct buffer_head *bh; + int i; + + bh = free_list; + for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) { + wait_on_buffer(bh); + if (bh->b_count || !bh->b_this_page) + continue; + if (bh->b_dirt) { + ll_rw_block(WRITEA,bh); + continue; } - h->b_dev = 0; - h->b_dirt = 0; - h->b_count = 0; - h->b_lock = 0; - h->b_uptodate = 0; - h->b_wait = NULL; - h->b_next = NULL; - h->b_prev = NULL; - h->b_data = (char *) b; - h->b_prev_free = h-1; - h->b_next_free = h+1; - h++; - NR_BUFFERS++; - if (b == (void *) 0x100000) - b = (void *) 0xA0000; + if (try_to_free(bh)) + return 1; } - h--; - free_list = start_buffer; - free_list->b_prev_free = h; - h->b_next_free = free_list; - for (i=0;ib_prev_free = free_list; + free_list->b_next_free = free_list; + free_list->b_data = (char *) mem; + free_list->b_size = BLOCK_SIZE; + mem += BLOCK_SIZE; + while (mem + 1024 < 0xA0000) { + bh = get_unused_buffer_head(); + if (!bh) + break; + bh->b_data = (char *) mem; + bh->b_size = BLOCK_SIZE; + mem += BLOCK_SIZE; + bh->b_next_free = free_list; + bh->b_prev_free = free_list->b_prev_free; + free_list->b_prev_free->b_next_free = bh; + free_list->b_prev_free = bh; + free_list = bh; + ++nr_buffers; + } + return; +} diff --git a/fs/exec.c b/fs/exec.c index cb7b2ba0ab8e..2be1aa3580c8 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1,7 +1,7 @@ /* * linux/fs/exec.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -17,16 +17,19 @@ * was less than 2 hours work to get demand-loading completely implemented. */ -#include -#include -#include -#include -#include - #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include extern int sys_exit(int exit_code); @@ -39,6 +42,121 @@ extern int sys_close(int fd); */ #define MAX_ARG_PAGES 32 +/* + * These are the only things you should do on a core-file: use only these + * macros to write out all the necessary info. + */ +#define DUMP_WRITE(addr,nr) \ +while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump + +#define DUMP_SEEK(offset) \ +if (file.f_op->lseek) { \ + if (file.f_op->lseek(inode,&file,(offset),0) != (offset)) \ + goto close_coredump; \ +} else file.f_pos = (offset) + +/* + * Routine writes a core dump image in the current directory. + * Currently only a stub-function. + * + * Note that setuid/setgid files won't make a core-dump if the uid/gid + * changed due to the set[u|g]id. It's enforced by the "current->dumpable" + * field, which also makes sure the core-dumps won't be recursive if the + * dumping of the process results in another error.. + */ +int core_dump(long signr, struct pt_regs * regs) +{ + struct inode * inode = NULL; + struct file file; + unsigned short fs; + int has_dumped = 0; + register int dump_start, dump_size; + struct user dump; + + if (!current->dumpable) + return 0; + current->dumpable = 0; +/* See if we have enough room to write the upage. */ + if(current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE/1024) return 0; + __asm__("mov %%fs,%0":"=r" (fs)); + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); + if (open_namei("core",O_CREAT | O_WRONLY | O_TRUNC,0600,&inode)) + goto end_coredump; + if (!S_ISREG(inode->i_mode)) + goto end_coredump; + if (!inode->i_op || !inode->i_op->default_file_ops) + goto end_coredump; + file.f_mode = 3; + file.f_flags = 0; + file.f_count = 1; + file.f_inode = inode; + file.f_pos = 0; + file.f_reada = 0; + file.f_op = inode->i_op->default_file_ops; + if (file.f_op->open) + if (file.f_op->open(inode,&file)) + goto end_coredump; + if (!file.f_op->write) + goto close_coredump; + has_dumped = 1; +/* write and seek example: from kernel space */ + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); + dump.magic = CMAGIC; + dump.u_tsize = current->end_code / PAGE_SIZE; + dump.u_dsize = (current->brk - current->end_code) / PAGE_SIZE; + dump.u_ssize =((current->start_stack +(PAGE_SIZE-1)) / PAGE_SIZE) - + (regs->esp/ PAGE_SIZE); +/* If the size of the dump file exceeds the rlimit, then see what would happen + if we wrote the stack, but not the data area. */ + if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE/1024 > + current->rlim[RLIMIT_CORE].rlim_cur) + dump.u_dsize = 0; +/* Make sure we have enough room to write the stack and data areas. */ + if ((dump.u_ssize+1) * PAGE_SIZE / 1024 > + current->rlim[RLIMIT_CORE].rlim_cur) + dump.u_ssize = 0; + dump.u_comm = 0; + dump.u_ar0 = (struct pt_regs *)(((int)(&dump.regs)) -((int)(&dump))); + dump.signal = signr; + dump.regs = *regs; + dump.start_code = 0; + dump.start_stack = regs->esp & ~(PAGE_SIZE - 1); +/* 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,¤t->tss.i387,sizeof(dump.i387)); + }; + DUMP_WRITE(&dump,sizeof(dump)); + DUMP_SEEK(sizeof(dump)); + /* Dump the task struct. Not be used by gdb, but could be useful */ + DUMP_WRITE(current,sizeof(*current)); +/* Now dump all of the user data. Include malloced stuff as well */ + DUMP_SEEK(PAGE_SIZE); +/* now we start writing out the user space info */ + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x17)); +/* Dump the data area */ + if (dump.u_dsize != 0) { + dump_start = current->end_code; + dump_size = current->brk - current->end_code; + DUMP_WRITE(dump_start,dump_size); + }; +/* Now prepare to dump the stack area */ + if (dump.u_ssize != 0) { + dump_start = regs->esp & ~(PAGE_SIZE - 1); + dump_size = dump.u_ssize * PAGE_SIZE; + DUMP_WRITE(dump_start,dump_size); + }; +close_coredump: + if (file.f_op->release) + file.f_op->release(inode,&file); +end_coredump: + __asm__("mov %0,%%fs"::"r" (fs)); + iput(inode); + return has_dumped; +} + /* * Note that a shared library must be both readable and executable due to * security reasons. @@ -62,14 +180,18 @@ int sys_uselib(const char * library) inode = NULL; if (!inode) return -ENOENT; - if (!S_ISREG(inode->i_mode) || !permission(inode,MAY_READ|MAY_EXEC)) { + if (!inode->i_sb || !S_ISREG(inode->i_mode) || !permission(inode,MAY_READ)) { iput(inode); return -EACCES; } - if (!(bh = bread(inode->i_dev,inode->i_data[0]))) { + if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize))) { iput(inode); return -EACCES; } + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } ex = *(struct exec *) bh->b_data; brelse(bh); if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || @@ -158,7 +280,7 @@ static int count(char ** argv) static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, unsigned long p, int from_kmem) { - char *tmp, *pag; + char *tmp, *pag = NULL; int len, offset = 0; unsigned long old_fs, new_fs; @@ -191,7 +313,7 @@ static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, set_fs(old_fs); if (!(pag = (char *) page[p/PAGE_SIZE]) && !(pag = (char *) page[p/PAGE_SIZE] = - (unsigned long *) get_free_page())) + (unsigned long *) get_free_page(GFP_USER))) return 0; if (from_kmem==2) set_fs(new_fs); @@ -229,6 +351,36 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page) return data_limit; } +static void read_omagic(struct inode *inode, int bytes) +{ + struct buffer_head *bh; + int n, blkno, blk = 0; + char *dest = (char *) 0; + unsigned int block_size; + + block_size = 1024; + if (inode->i_sb) + block_size = inode->i_sb->s_blocksize; + while (bytes > 0) { + if (!(blkno = bmap(inode, blk))) + sys_exit(-1); + if (!(bh = bread(inode->i_dev, blkno, block_size))) + sys_exit(-1); + n = (blk ? block_size : block_size - sizeof(struct exec)); + if (bytes < n) + n = bytes; + + memcpy_tofs(dest, (blk ? bh->b_data : + bh->b_data + sizeof(struct exec)), n); + brelse(bh); + ++blk; + dest += n; + bytes -= n; + } + iput(inode); + current->executable = NULL; +} + /* * 'do_execve()' executes a new program. * @@ -263,7 +415,21 @@ restart_interp: retval = -EACCES; goto exec_error2; } + if (IS_NOEXEC(inode)) { /* FS mustn't be mounted noexec */ + retval = -EPERM; + goto exec_error2; + } + if (!inode->i_sb) { + retval = -EACCES; + goto exec_error2; + } i = inode->i_mode; + if (IS_NOSUID(inode) && (((i & S_ISUID) && inode->i_uid != current-> + euid) || ((i & S_ISGID) && inode->i_gid != current->egid)) && + !suser()) { + retval = -EPERM; + goto exec_error2; + } /* make sure we don't let suid, sgid files be ptraced. */ if (current->flags & PF_PTRACED) { e_uid = current->euid; @@ -281,10 +447,14 @@ restart_interp: retval = -EACCES; goto exec_error2; } - if (!(bh = bread(inode->i_dev,inode->i_data[0]))) { + if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize))) { retval = -EACCES; goto exec_error2; } + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } ex = *((struct exec *) bh->b_data); /* read exec-header */ if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { /* @@ -359,13 +529,14 @@ restart_interp: goto restart_interp; } brelse(bh); - if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || + if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) || + ex.a_trsize || ex.a_drsize || ex.a_text+ex.a_data+ex.a_bss>0x3000000 || inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { retval = -ENOEXEC; goto exec_error2; } - if (N_TXTOFF(ex) != BLOCK_SIZE) { + if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) { printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); retval = -ENOEXEC; goto exec_error2; @@ -379,6 +550,7 @@ restart_interp: } } /* OK, This is the point of no return */ + current->dumpable = 1; for (i=0; (ch = get_fs_byte(filename++)) != '\0';) if (ch == '/') i = 0; @@ -394,6 +566,9 @@ restart_interp: iput(current->libraries[i].library); current->libraries[i].library = NULL; } + if (e_uid != current->euid || e_gid != current->egid || + !permission(inode,MAY_READ)) + current->dumpable = 0; current->numlibraries = 0; current->executable = inode; current->signal = 0; @@ -422,10 +597,12 @@ restart_interp: current->rss = (LIBRARY_OFFSET - p + PAGE_SIZE-1) / PAGE_SIZE; current->suid = current->euid = e_uid; current->sgid = current->egid = e_gid; + if (N_MAGIC(ex) == OMAGIC) + read_omagic(inode, ex.a_text+ex.a_data); eip[0] = ex.a_entry; /* eip, magic happens :-) */ eip[3] = p; /* stack pointer */ if (current->flags & PF_PTRACED) - send_sig(SIGTRAP, current, 0); + send_sig(SIGTRAP, current, 0); return 0; exec_error2: iput(inode); diff --git a/fs/ext/Makefile b/fs/ext/Makefile new file mode 100644 index 000000000000..43fc4e12bd46 --- /dev/null +++ b/fs/ext/Makefile @@ -0,0 +1,128 @@ +# +# Makefile for the linux ext-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) -S $< +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< + +OBJS= bitmap.o freelists.o truncate.o namei.o inode.o \ + file.o dir.o symlink.o blkdev.o chrdev.o fifo.o + +ext.o: $(OBJS) + $(LD) -r -o ext.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make + cp tmp_make Makefile + +### Dependencies: +bitmap.o : bitmap.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/string.h +blkdev.o : blkdev.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/errno.h +chrdev.o : chrdev.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/errno.h +dir.o : dir.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/ext_fs.h \ + /usr/src/linux/include/linux/stat.h +fifo.o : fifo.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h +file.o : file.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/stat.h +freelists.o : freelists.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/string.h +inode.o : inode.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/segment.h +namei.o : namei.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/const.h +symlink.o : symlink.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/stat.h +truncate.o : truncate.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ext_fs.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/errno.h diff --git a/fs/ext/bitmap.c b/fs/ext/bitmap.c new file mode 100644 index 000000000000..ee68e68cb4d2 --- /dev/null +++ b/fs/ext/bitmap.c @@ -0,0 +1,242 @@ +/* + * linux/fs/ext/bitmap.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/bitmap.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* bitmap.c contains the code that handles the inode and block bitmaps */ + + +#include +#include +#include +#include + +#ifdef EXTFS_BITMAP + +#define clear_block(addr) \ +__asm__("cld\n\t" \ + "rep\n\t" \ + "stosl" \ + ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di") + +#define set_bit(nr,addr) ({\ +char res; \ +__asm__ __volatile__("btsl %1,%2\n\tsetb %0": \ +"=q" (res):"r" (nr),"m" (*(addr))); \ +res;}) + +#define clear_bit(nr,addr) ({\ +char res; \ +__asm__ __volatile__("btrl %1,%2\n\tsetnb %0": \ +"=q" (res):"r" (nr),"m" (*(addr))); \ +res;}) + +#define find_first_zero(addr) ({ \ +int __res; \ +__asm__("cld\n" \ + "1:\tlodsl\n\t" \ + "notl %%eax\n\t" \ + "bsfl %%eax,%%edx\n\t" \ + "jne 2f\n\t" \ + "addl $32,%%ecx\n\t" \ + "cmpl $8192,%%ecx\n\t" \ + "jl 1b\n\t" \ + "xorl %%edx,%%edx\n" \ + "2:\taddl %%edx,%%ecx" \ + :"=c" (__res):"0" (0),"S" (addr):"ax","dx","si"); \ +__res;}) + +static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; + +static unsigned long count_used(struct buffer_head *map[], unsigned numblocks, + unsigned numbits) +{ + unsigned i, j, end, sum = 0; + struct buffer_head *bh; + + for (i=0; (i= (8*BLOCK_SIZE)) { + end = BLOCK_SIZE; + numbits -= 8*BLOCK_SIZE; + } else { + int tmp; + end = numbits >> 3; + numbits &= 0x7; + tmp = bh->b_data[end] & ((1<>4)&0xf]; + numbits = 0; + } + for (j=0; jb_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4)&0xf]; + } + return(sum); +} + +int ext_free_block(int dev, int block) +{ + struct super_block * sb; + struct buffer_head * bh; + unsigned int bit,zone; + + if (!(sb = get_super(dev))) + panic("trying to free block on nonexistent device"); + if (block < sb->s_firstdatazone || block >= sb->s_nzones) + panic("trying to free block not in datazone"); + bh = get_hash_table(dev,block); + if (bh) { + if (bh->b_count > 1) { + brelse(bh); + return 0; + } + bh->b_dirt=0; + bh->b_uptodate=0; + if (bh->b_count) + brelse(bh); + } + zone = block - sb->s_firstdatazone + 1; + bit = zone & 8191; + zone >>= 13; + bh = sb->s_zmap[zone]; + if (clear_bit(bit,bh->b_data)) + printk("free_block (%04x:%d): bit already cleared\n",dev,block); + bh->b_dirt = 1; + return 1; +} + +int ext_new_block(int dev) +{ + struct buffer_head * bh; + struct super_block * sb; + int i,j; + + if (!(sb = get_super(dev))) + panic("trying to get new block from nonexistant device"); + j = 8192; + for (i=0 ; i<8 ; i++) + if (bh=sb->s_zmap[i]) + if ((j=find_first_zero(bh->b_data))<8192) + break; + if (i>=8 || !bh || j>=8192) + return 0; + if (set_bit(j,bh->b_data)) + panic("new_block: bit already set"); + bh->b_dirt = 1; + j += i*8192 + sb->s_firstdatazone-1; + if (j >= sb->s_nzones) + return 0; + if (!(bh=getblk(dev,j))) + panic("new_block: cannot get block"); + if (bh->b_count != 1) + panic("new block: count is != 1"); + clear_block(bh->b_data); + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); +#ifdef EXTFS_DEBUG +printk("ext_new_block: allocating block %d\n", j); +#endif + return j; +} + +unsigned long ext_count_free_blocks(struct super_block *sb) +{ + return (sb->s_nzones - count_used(sb->s_zmap,sb->s_zmap_blocks,sb->s_nzones)) + << sb->s_log_zone_size; +} + +void ext_free_inode(struct inode * inode) +{ + struct buffer_head * bh; + + if (!inode) + return; + if (!inode->i_dev) { + memset(inode,0,sizeof(*inode)); + return; + } + if (inode->i_count>1) { + printk("free_inode: inode has count=%d\n",inode->i_count); + return; + } + if (inode->i_nlink) { + printk("free_inode: inode has nlink=%d\n",inode->i_nlink); + return; + } + if (!inode->i_sb) { + printk("free_inode: inode on nonexistent device\n"); + return; + } + if (inode->i_ino < 1 || inode->i_ino > inode->i_sb->s_ninodes) { + printk("free_inode: inode 0 or nonexistent inode\n"); + return; + } + if (!(bh=inode->i_sb->s_imap[inode->i_ino>>13])) { + printk("free_inode: nonexistent imap in superblock\n"); + return; + } + if (clear_bit(inode->i_ino&8191,bh->b_data)) + printk("free_inode: bit already cleared.\n\r"); + bh->b_dirt = 1; + memset(inode,0,sizeof(*inode)); +} + +struct inode * ext_new_inode(int dev) +{ + struct inode * inode; + struct buffer_head * bh; + int i,j; + + if (!(inode=get_empty_inode())) + return NULL; + if (!(inode->i_sb = get_super(dev))) { + printk("new_inode: unknown device\n"); + iput(inode); + return NULL; + } + inode->i_flags = inode->i_sb->s_flags; + j = 8192; + for (i=0 ; i<8 ; i++) + if (bh=inode->i_sb->s_imap[i]) + if ((j=find_first_zero(bh->b_data))<8192) + break; + if (!bh || j >= 8192 || j+i*8192 > inode->i_sb->s_ninodes) { + iput(inode); + return NULL; + } + if (set_bit(j,bh->b_data)) { /* shouldn't happen */ + printk("new_inode: bit already set"); + iput(inode); + return NULL; + } + bh->b_dirt = 1; + inode->i_count = 1; + inode->i_nlink = 1; + inode->i_dev = dev; + inode->i_uid = current->euid; + inode->i_gid = current->egid; + inode->i_dirt = 1; + inode->i_ino = j + i*8192; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_op = NULL; +#ifdef EXTFS_DEBUG + printk("ext_new_inode : allocating inode %d\n", inode->i_ino); +#endif + return inode; +} + +unsigned long ext_count_free_inodes(struct super_block *sb) +{ + return sb->s_ninodes - count_used(sb->s_imap,sb->s_imap_blocks,sb->s_ninodes); +} + +#endif diff --git a/fs/ext/blkdev.c b/fs/ext/blkdev.c new file mode 100644 index 000000000000..74dd9fe703b4 --- /dev/null +++ b/fs/ext/blkdev.c @@ -0,0 +1,67 @@ +/* + * linux/fs/ext/blkdev.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/blkdev.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +/* + * Called every time an ext block special file is opened + */ +static int blkdev_open(struct inode * inode, struct file * filp) +{ + int i; + + i = MAJOR(inode->i_rdev); + if (i < MAX_BLKDEV) { + filp->f_op = blkdev_fops[i]; + if (filp->f_op && filp->f_op->open) + return filp->f_op->open(inode,filp); + } + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +static struct file_operations def_blk_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + blkdev_open, /* open */ + NULL, /* release */ +}; + +struct inode_operations ext_blkdev_inode_operations = { + &def_blk_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext_bmap, /* bmap */ + ext_truncate /* truncate */ +}; diff --git a/fs/ext/chrdev.c b/fs/ext/chrdev.c new file mode 100644 index 000000000000..b1b55ea2d9db --- /dev/null +++ b/fs/ext/chrdev.c @@ -0,0 +1,68 @@ +/* + * linux/fs/ext/chrdev.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/chrdev.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +/* + * Called every time an ext character special file is opened + */ +static int chrdev_open(struct inode * inode, struct file * filp) +{ + int i; + + i = MAJOR(inode->i_rdev); + if (i < MAX_CHRDEV) { + filp->f_op = chrdev_fops[i]; + if (filp->f_op && filp->f_op->open) + return filp->f_op->open(inode,filp); + } + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +static struct file_operations def_chr_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + chrdev_open, /* open */ + NULL, /* release */ +}; + +struct inode_operations ext_chrdev_inode_operations = { + &def_chr_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext_bmap, /* bmap */ + ext_truncate /* truncate */ +}; + diff --git a/fs/ext/dir.c b/fs/ext/dir.c new file mode 100644 index 000000000000..54bc7ad0e2b2 --- /dev/null +++ b/fs/ext/dir.c @@ -0,0 +1,98 @@ +/* + * linux/fs/ext/dir.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/dir.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext directory handling functions + */ + +#include + +#include +#include +#include +#include + +static int ext_readdir(struct inode *, struct file *, struct dirent *, int); + +static struct file_operations ext_dir_operations = { + NULL, /* lseek - default */ + NULL, /* read */ + NULL, /* write - bad */ + ext_readdir, /* readdir */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* no special open code */ + NULL /* no special release code */ +}; + +/* + * directories can handle most operations... + */ +struct inode_operations ext_dir_inode_operations = { + &ext_dir_operations, /* default directory file-ops */ + ext_create, /* create */ + ext_lookup, /* lookup */ + ext_link, /* link */ + ext_unlink, /* unlink */ + ext_symlink, /* symlink */ + ext_mkdir, /* mkdir */ + ext_rmdir, /* rmdir */ + ext_mknod, /* mknod */ + ext_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext_bmap, /* bmap */ + ext_truncate /* truncate */ +}; + +static int ext_readdir(struct inode * inode, struct file * filp, + struct dirent * dirent, int count) +{ + unsigned int block,offset,i; + char c; + struct buffer_head * bh; + struct ext_dir_entry * de; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; +/* if (filp->f_pos & (sizeof (struct ext_dir_entry) - 1)) + return -EBADF; */ + while (filp->f_pos < inode->i_size) { + offset = filp->f_pos & 1023; + block = ext_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS); + if (!block || !(bh = bread(inode->i_dev, block, BLOCK_SIZE))) { + filp->f_pos += 1024-offset; + continue; + } + de = (struct ext_dir_entry *) (offset + bh->b_data); + while (offset < 1024 && filp->f_pos < inode->i_size) { + offset += de->rec_len; + filp->f_pos += de->rec_len; + if (de->inode) { + for (i = 0; i < de->name_len; i++) + if (c = de->name[i]) + put_fs_byte(c,i+dirent->d_name); + else + break; + if (i) { + put_fs_long(de->inode,&dirent->d_ino); + put_fs_byte(0,i+dirent->d_name); + put_fs_word(i,&dirent->d_reclen); + brelse(bh); + return i; + } + } +/* de++; */ + de = (struct ext_dir_entry *) ((char *) de + de->rec_len); + } + brelse(bh); + } + return 0; +} diff --git a/fs/ext/fifo.c b/fs/ext/fifo.c new file mode 100644 index 000000000000..c094393b453e --- /dev/null +++ b/fs/ext/fifo.c @@ -0,0 +1,27 @@ +/* + * linux/fs/fifo.c + * + * written by Paul H. Hargrove. + */ + +#include +#include + +extern struct file_operations def_fifo_fops; + +struct inode_operations ext_fifo_inode_operations = { + &def_fifo_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL /* truncate */ +}; diff --git a/fs/ext/file.c b/fs/ext/file.c new file mode 100644 index 000000000000..ec366aa21fc0 --- /dev/null +++ b/fs/ext/file.c @@ -0,0 +1,220 @@ +/* + * linux/fs/ext/file.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/file.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext regular file handling primitives + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NBUF 16 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +#include +#include + +static int ext_file_read(struct inode *, struct file *, char *, int); +static int ext_file_write(struct inode *, struct file *, char *, int); + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ext filesystem. + */ +static struct file_operations ext_file_operations = { + NULL, /* lseek - default */ + ext_file_read, /* read */ + ext_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* no special open is needed */ + NULL /* release */ +}; + +struct inode_operations ext_file_inode_operations = { + &ext_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext_bmap, /* bmap */ + ext_truncate /* truncate */ +}; + +static inline void wait_on_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + sti(); +} + +static int ext_file_read(struct inode * inode, struct file * filp, char * buf, int count) +{ + int read,left,chars,nr; + int block, blocks, offset; + struct buffer_head ** bhb, ** bhe; + struct buffer_head * buflist[NBUF]; + + if (!inode) { + printk("ext_file_read: inode = NULL\n"); + return -EINVAL; + } + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { + printk("ext_file_read: mode = %07o\n",inode->i_mode); + return -EINVAL; + } + if (filp->f_pos > inode->i_size) + left = 0; + else + left = inode->i_size - filp->f_pos; + if (left > count) + left = count; + if (left <= 0) + return 0; + read = 0; + block = filp->f_pos >> BLOCK_SIZE_BITS; + offset = filp->f_pos & (BLOCK_SIZE-1); + blocks = (left + offset + BLOCK_SIZE - 1) / BLOCK_SIZE; + bhb = bhe = buflist; + do { + if (blocks) { + --blocks; + if (nr = ext_bmap(inode,block++)) { + *bhb = getblk(inode->i_dev, nr, BLOCK_SIZE); + if (!(*bhb)->b_uptodate) + ll_rw_block(READ,*bhb); + } else + *bhb = NULL; + + if (++bhb == &buflist[NBUF]) + bhb = buflist; + + if (bhb != bhe) + continue; + } + if (*bhe) { + wait_on_buffer(*bhe); + if (!(*bhe)->b_uptodate) { + do { + brelse(*bhe); + if (++bhe == &buflist[NBUF]) + bhe = buflist; + } while (bhe != bhb); + break; + } + } + + if (left < BLOCK_SIZE - offset) + chars = left; + else + chars = BLOCK_SIZE - offset; + filp->f_pos += chars; + left -= chars; + read += chars; + if (*bhe) { + memcpy_tofs(buf,offset+(*bhe)->b_data,chars); + brelse(*bhe); + buf += chars; + } else { + while (chars-->0) + put_fs_byte(0,buf++); + } + offset = 0; + if (++bhe == &buflist[NBUF]) + bhe = buflist; + } while (left > 0); + if (!read) + return -EIO; + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + return read; +} + +static int ext_file_write(struct inode * inode, struct file * filp, char * buf, int count) +{ + off_t pos; + int written,block,c; + struct buffer_head * bh; + char * p; + + if (!inode) { + printk("ext_file_write: inode = NULL\n"); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("ext_file_write: mode = %07o\n",inode->i_mode); + return -EINVAL; + } +/* + * ok, append may not work when many processes are writing at the same time + * but so what. That way leads to madness anyway. + */ + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else + pos = filp->f_pos; + written = 0; + while (written count-written) + c = count-written; + if (c == BLOCK_SIZE) + bh = getblk(inode->i_dev, block, BLOCK_SIZE); + else + bh = bread(inode->i_dev, block, BLOCK_SIZE); + if (!bh) { + if (!written) + written = -EIO; + break; + } + p = (pos % BLOCK_SIZE) + bh->b_data; + pos += c; + if (pos > inode->i_size) { + inode->i_size = pos; + inode->i_dirt = 1; + } + written += c; + memcpy_fromfs(p,buf,c); + buf += c; + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); + } + inode->i_mtime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME; + filp->f_pos = pos; + inode->i_dirt = 1; + return written; +} diff --git a/fs/ext/freelists.c b/fs/ext/freelists.c new file mode 100644 index 000000000000..e39f442711c4 --- /dev/null +++ b/fs/ext/freelists.c @@ -0,0 +1,344 @@ +/* + * linux/fs/ext/freelists.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + */ + +/* freelists.c contains the code that handles the inode and block free lists */ + + +/* + + The free blocks are managed by a linked list. The super block contains the + number of the first free block. This block contains 254 numbers of other + free blocks and the number of the next block in the list. + + When an ext fs is mounted, the number of the first free block is stored + in s->u.ext_sb.s_zmap[0] and the block header is stored in s->u.ext_sb.s_zmap[1]. u.ext_sb.s_zmap[2] + contains the count of free blocks. + + Currently, it is a hack to allow this kind of management with the super_block + structure. + Perhaps, in the future, we may have to change the super_block structure to + include dedicated fields. + + The free inodes are also managed by a linked list in a similar way. The + super block contains the number of the first free inode. This inode contains + 14 numbers of other free inodes and the number of the next inode in the list. + + The number of the first free inode is stored in s->u.ext_sb.s_imap[0] and the header + of the block containing the inode is stored in s->u.ext_sb.s_imap[1]. u.ext_sb.s_imap[2] contains + the count of free inodes. + +*/ + +#include +#include +#include +#include + +#ifdef EXTFS_FREELIST + +#define clear_block(addr) \ +__asm__("cld\n\t" \ + "rep\n\t" \ + "stosl" \ + ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di") + +int ext_free_block(int dev, int block) +{ + struct super_block * sb; + struct buffer_head * bh; + struct ext_free_block * efb; + + if (!(sb = get_super(dev))) + panic("trying to free block on nonexistent device"); + lock_super (sb); + if (block < sb->u.ext_sb.s_firstdatazone || block >= sb->u.ext_sb.s_nzones) + panic("trying to free block not in datazone"); + bh = get_hash_table(dev, block, sb->s_blocksize); + if (bh) { + if (bh->b_count > 1) { + brelse(bh); + free_super (sb); + return 0; + } + bh->b_dirt=0; + bh->b_uptodate=0; + if (bh->b_count) + brelse(bh); + } + if (sb->u.ext_sb.s_zmap[1]) + efb = (struct ext_free_block *) sb->u.ext_sb.s_zmap[1]->b_data; + if (!sb->u.ext_sb.s_zmap[1] || efb->count == 254) { +#ifdef EXTFS_DEBUG +printk("ext_free_block: block full, skipping to %d\n", block); +#endif + if (sb->u.ext_sb.s_zmap[1]) + brelse (sb->u.ext_sb.s_zmap[1]); + if (!(sb->u.ext_sb.s_zmap[1] = bread (dev, block, sb->s_blocksize))) + panic ("ext_free_block: unable to read block to free\n"); + efb = (struct ext_free_block *) sb->u.ext_sb.s_zmap[1]->b_data; + efb->next = (unsigned long) sb->u.ext_sb.s_zmap[0]; + efb->count = 0; + sb->u.ext_sb.s_zmap[0] = (struct buffer_head *) block; + } else { + efb->free[efb->count++] = block; + } + sb->u.ext_sb.s_zmap[2] = (struct buffer_head *) (((unsigned long) sb->u.ext_sb.s_zmap[2]) + 1); + sb->s_dirt = 1; + sb->u.ext_sb.s_zmap[1]->b_dirt = 1; + free_super (sb); + return 1; +} + +int ext_new_block(int dev) +{ + struct buffer_head * bh; + struct super_block * sb; + struct ext_free_block * efb; + int /* i, */ j; + + if (!(sb = get_super(dev))) + panic("trying to get new block from nonexistant device"); + if (!sb->u.ext_sb.s_zmap[1]) + return 0; + lock_super (sb); + efb = (struct ext_free_block *) sb->u.ext_sb.s_zmap[1]->b_data; + if (efb->count) { + j = efb->free[--efb->count]; + sb->u.ext_sb.s_zmap[1]->b_dirt = 1; + } else { +#ifdef EXTFS_DEBUG +printk("ext_new_block: block empty, skipping to %d\n", efb->next); +#endif + j = (unsigned long) sb->u.ext_sb.s_zmap[0]; + sb->u.ext_sb.s_zmap[0] = (struct buffer_head *) efb->next; + brelse (sb->u.ext_sb.s_zmap[1]); + if (!sb->u.ext_sb.s_zmap[0]) { + sb->u.ext_sb.s_zmap[1] = NULL; + } else { + if (!(sb->u.ext_sb.s_zmap[1] = bread (dev, (unsigned long) sb->u.ext_sb.s_zmap[0], sb->s_blocksize))) + panic ("ext_new_block: unable to read next free block\n"); + } + } + if (j < sb->u.ext_sb.s_firstdatazone || j > sb->u.ext_sb.s_nzones) { + printk ("ext_new_block: blk = %d\n", j); + panic ("allocating block not in data zone\n"); + } + sb->u.ext_sb.s_zmap[2] = (struct buffer_head *) (((unsigned long) sb->u.ext_sb.s_zmap[2]) - 1); + sb->s_dirt = 1; + + if (!(bh=getblk(dev, j, sb->s_blocksize))) + panic("new_block: cannot get block"); + if (bh->b_count != 1) + panic("new block: count is != 1"); + clear_block(bh->b_data); + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); +#ifdef EXTFS_DEBUG +printk("ext_new_block: allocating block %d\n", j); +#endif + free_super (sb); + return j; +} + +unsigned long ext_count_free_blocks(struct super_block *sb) +{ +#ifdef EXTFS_DEBUG + struct buffer_head * bh; + struct ext_free_block * efb; + unsigned long count, block; + + lock_super (sb); + if (!sb->u.ext_sb.s_zmap[1]) + count = 0; + else { + efb = (struct ext_free_block *) sb->u.ext_sb.s_zmap[1]->b_data; + count = efb->count + 1; + block = efb->next; + while (block) { + if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) { + printk ("ext_count_free: error while reading free blocks list\n"); + block = 0; + } else { + efb = (struct ext_free_block *) bh->b_data; + count += efb->count + 1; + block = efb->next; + brelse (bh); + } + } + } +printk("ext_count_free_blocks: stored = %d, computed = %d\n", + (unsigned long) sb->u.ext_sb.s_zmap[2], count); + free_super (sb); + return count; +#else + return (unsigned long) sb->u.ext_sb.s_zmap[2]; +#endif +} + +void ext_free_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct ext_free_inode * efi; + unsigned long block; + + if (!inode) + return; + if (!inode->i_dev) { + memset(inode,0,sizeof(*inode)); + return; + } + if (inode->i_count>1) { + printk("free_inode: inode has count=%d\n",inode->i_count); + return; + } + if (inode->i_nlink) { + printk("free_inode: inode has nlink=%d\n",inode->i_nlink); + return; + } + if (!inode->i_sb) { + printk("free_inode: inode on nonexistent device\n"); + return; + } + lock_super (inode->i_sb); + if (inode->i_ino < 1 || inode->i_ino > inode->i_sb->u.ext_sb.s_ninodes) { + printk("free_inode: inode 0 or nonexistent inode\n"); + free_super (inode->i_sb); + return; + } + if (inode->i_sb->u.ext_sb.s_imap[1]) + efi = ((struct ext_free_inode *) inode->i_sb->u.ext_sb.s_imap[1]->b_data) + + (((unsigned long) inode->i_sb->u.ext_sb.s_imap[0])-1)%EXT_INODES_PER_BLOCK; + if (!inode->i_sb->u.ext_sb.s_imap[1] || efi->count == 14) { +#ifdef EXTFS_DEBUG +printk("ext_free_inode: inode full, skipping to %d\n", inode->i_ino); +#endif + if (inode->i_sb->u.ext_sb.s_imap[1]) + brelse (inode->i_sb->u.ext_sb.s_imap[1]); + block = 2 + (inode->i_ino - 1) / EXT_INODES_PER_BLOCK; + if (!(bh = bread(inode->i_dev, block, inode->i_sb->s_blocksize))) + panic("ext_free_inode: unable to read inode block\n"); + efi = ((struct ext_free_inode *) bh->b_data) + + (inode->i_ino - 1) % EXT_INODES_PER_BLOCK; + efi->next = (unsigned long) inode->i_sb->u.ext_sb.s_imap[0]; + efi->count = 0; + inode->i_sb->u.ext_sb.s_imap[0] = (struct buffer_head *) inode->i_ino; + inode->i_sb->u.ext_sb.s_imap[1] = bh; + } else { + efi->free[efi->count++] = inode->i_ino; + } + inode->i_sb->u.ext_sb.s_imap[2] = (struct buffer_head *) (((unsigned long) inode->i_sb->u.ext_sb.s_imap[2]) + 1); + inode->i_sb->s_dirt = 1; + inode->i_sb->u.ext_sb.s_imap[1]->b_dirt = 1; + free_super (inode->i_sb); + memset(inode,0,sizeof(*inode)); +} + +struct inode * ext_new_inode(int dev) +{ + struct inode * inode; + struct ext_free_inode * efi; + unsigned long block; + int /* i, */ j; + + if (!(inode=get_empty_inode())) + return NULL; + if (!(inode->i_sb = get_super(dev))) { + printk("new_inode: unknown device\n"); + iput(inode); + return NULL; + } + inode->i_flags = inode->i_sb->s_flags; + if (!inode->i_sb->u.ext_sb.s_imap[1]) + return 0; + lock_super (inode->i_sb); + efi = ((struct ext_free_inode *) inode->i_sb->u.ext_sb.s_imap[1]->b_data) + + (((unsigned long) inode->i_sb->u.ext_sb.s_imap[0])-1)%EXT_INODES_PER_BLOCK; + if (efi->count) { + j = efi->free[--efi->count]; + inode->i_sb->u.ext_sb.s_imap[1]->b_dirt = 1; + } else { +#ifdef EXTFS_DEBUG +printk("ext_free_inode: inode empty, skipping to %d\n", efi->next); +#endif + j = (unsigned long) inode->i_sb->u.ext_sb.s_imap[0]; + if (efi->next > inode->i_sb->u.ext_sb.s_ninodes) { + printk ("efi->next = %d\n", efi->next); + panic ("ext_new_inode: bad inode number in free list\n"); + } + inode->i_sb->u.ext_sb.s_imap[0] = (struct buffer_head *) efi->next; + block = 2 + (((unsigned long) efi->next) - 1) / EXT_INODES_PER_BLOCK; + brelse (inode->i_sb->u.ext_sb.s_imap[1]); + if (!inode->i_sb->u.ext_sb.s_imap[0]) { + inode->i_sb->u.ext_sb.s_imap[1] = NULL; + } else { + if (!(inode->i_sb->u.ext_sb.s_imap[1] = bread (dev, block, inode->i_sb->s_blocksize))) + panic ("ext_new_inode: unable to read next free inode block\n"); + } + } + inode->i_sb->u.ext_sb.s_imap[2] = (struct buffer_head *) (((unsigned long) inode->i_sb->u.ext_sb.s_imap[2]) - 1); + inode->i_sb->s_dirt = 1; + inode->i_count = 1; + inode->i_nlink = 1; + inode->i_dev = dev; + inode->i_uid = current->euid; + inode->i_gid = current->egid; + inode->i_dirt = 1; + inode->i_ino = j; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_op = NULL; +#ifdef EXTFS_DEBUG +printk("ext_new_inode : allocating inode %d\n", inode->i_ino); +#endif + free_super (inode->i_sb); + return inode; +} + +unsigned long ext_count_free_inodes(struct super_block *sb) +{ +#ifdef EXTFS_DEBUG + struct buffer_head * bh; + struct ext_free_inode * efi; + unsigned long count, block, ino; + + lock_super (sb); + if (!sb->u.ext_sb.s_imap[1]) + count = 0; + else { + efi = ((struct ext_free_inode *) sb->u.ext_sb.s_imap[1]->b_data) + + ((((unsigned long) sb->u.ext_sb.s_imap[0])-1)%EXT_INODES_PER_BLOCK); + count = efi->count + 1; + ino = efi->next; + while (ino) { + if (ino < 1 || ino > sb->u.ext_sb.s_ninodes) { + printk ("u.ext_sb.s_imap[0] = %d, ino = %d\n", + (int) sb->u.ext_sb.s_imap[0],ino); + panic ("ext_count_fre_inodes: bad inode number in free list\n"); + } + block = 2 + ((ino - 1) / EXT_INODES_PER_BLOCK); + if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) { + printk ("ext_count_free_inodes: error while reading free inodes list\n"); + block = 0; + } else { + efi = ((struct ext_free_inode *) bh->b_data) + + ((ino - 1) % EXT_INODES_PER_BLOCK); + count += efi->count + 1; + ino = efi->next; + brelse (bh); + } + } + } +printk("ext_count_free_inodes: stored = %d, computed = %d\n", + (unsigned long) sb->u.ext_sb.s_imap[2], count); + free_super (sb); + return count; +#else + return (unsigned long) sb->u.ext_sb.s_imap[2]; +#endif +} + +#endif diff --git a/fs/ext/inode.c b/fs/ext/inode.c new file mode 100644 index 000000000000..dae86aeeee28 --- /dev/null +++ b/fs/ext/inode.c @@ -0,0 +1,416 @@ +/* + * linux/fs/ext/inode.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/inode.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +int sync_dev(int dev); + +void ext_put_inode(struct inode *inode) +{ + inode->i_size = 0; + ext_truncate(inode); + ext_free_inode(inode); +} + +void ext_put_super(struct super_block *sb) +{ +#ifdef EXTFS_BITMAP + int i; +#endif + + lock_super(sb); + sb->s_dev = 0; +#ifdef EXTFS_BITMAP + for(i = 0 ; i < EXT_I_MAP_SLOTS ; i++) + brelse(sb->u.ext_sb.s_imap[i]); + for(i = 0 ; i < EXT_Z_MAP_SLOTS ; i++) + brelse(sb->u.ext_sb.s_zmap[i]); +#endif +#ifdef EXTFS_FREELIST + if (sb->u.ext_sb.s_imap[1]) + brelse (sb->u.ext_sb.s_imap[1]); + if (sb->u.ext_sb.s_zmap[1]) + brelse (sb->u.ext_sb.s_zmap[1]); +#endif + free_super(sb); + return; +} + +static struct super_operations ext_sops = { + ext_read_inode, + ext_write_inode, + ext_put_inode, + ext_put_super, + ext_write_super, + ext_statfs +}; + +struct super_block *ext_read_super(struct super_block *s,void *data) +{ + struct buffer_head *bh; + struct ext_super_block *es; + int dev = s->s_dev,block; +#ifdef EXTFS_BITMAP + int i; +#endif + + lock_super(s); + if (!(bh = bread(dev, 1, BLOCK_SIZE))) { + s->s_dev=0; + free_super(s); + printk("bread failed\n"); + return NULL; + } +/* *((struct ext_super_block *) s) = + *((struct ext_super_block *) bh->b_data); */ + es = (struct ext_super_block *) bh->b_data; + s->s_blocksize = 1024; + s->u.ext_sb.s_ninodes = es->s_ninodes; + s->u.ext_sb.s_nzones = es->s_nzones; +#ifdef EXTFS_BITMAP + s->u.ext_sb.s_imap_blocks = es->s_imap_blocks; + s->u.ext_sb.s_zmap_blocks = es->s_zmap_blocks; +#endif + s->u.ext_sb.s_firstdatazone = es->s_firstdatazone; + s->u.ext_sb.s_log_zone_size = es->s_log_zone_size; + s->u.ext_sb.s_max_size = es->s_max_size; + s->s_magic = es->s_magic; +#ifdef EXTFS_FREELIST + s->u.ext_sb.s_zmap[0] = (struct buffer_head *) es->s_firstfreeblock; + s->u.ext_sb.s_zmap[2] = (struct buffer_head *) es->s_freeblockscount; + s->u.ext_sb.s_imap[0] = (struct buffer_head *) es->s_firstfreeinode; + s->u.ext_sb.s_imap[2] = (struct buffer_head *) es->s_freeinodescount; +#endif + brelse(bh); + if (s->s_magic != EXT_SUPER_MAGIC) { + s->s_dev = 0; + free_super(s); + printk("magic match failed\n"); + return NULL; + } +#ifdef EXTFS_BITMAP + for (i=0;i < EXT_I_MAP_SLOTS;i++) + s->u.ext_sb.s_imap[i] = NULL; + for (i=0;i < EXT_Z_MAP_SLOTS;i++) + s->u.ext_sb.s_zmap[i] = NULL; + block=2; + for (i=0 ; i < s->u.ext_sb.s_imap_blocks ; i++) + if (s->u.ext_sb.s_imap[i]=bread(dev, block, BLOCK_SIZE)) + block++; + else + break; + for (i=0 ; i < s->u.ext_sb.s_zmap_blocks ; i++) + if (s->u.ext_sb.s_zmap[i]=bread(dev, block, BLOCK_SIZE)) + block++; + else + break; + if (block != 2+s->u.ext_sb.s_imap_blocks+s->u.ext_sb.s_zmap_blocks) { + for(i=0;iu.ext_sb.s_imap[i]); + for(i=0;iu.ext_sb.s_zmap[i]); + s->s_dev=0; + free_super(s); + printk("block failed\n"); + return NULL; + } + s->u.ext_sb.s_imap[0]->b_data[0] |= 1; + s->u.ext_sb.s_zmap[0]->b_data[0] |= 1; +#endif +#ifdef EXTFS_FREELIST + if (!s->u.ext_sb.s_zmap[0]) + s->u.ext_sb.s_zmap[1] = NULL; + else + if (!(s->u.ext_sb.s_zmap[1] = bread(dev, (unsigned long) s->u.ext_sb.s_zmap[0], BLOCK_SIZE))) { + printk ("ext_read_super: unable to read first free block\n"); + s->s_dev = 0; + free_super(s); + return NULL; + } + if (!s->u.ext_sb.s_imap[0]) + s->u.ext_sb.s_imap[1] = NULL; + else { + block = 2 + (((unsigned long) s->u.ext_sb.s_imap[0]) - 1) / EXT_INODES_PER_BLOCK; + if (!(s->u.ext_sb.s_imap[1] = bread(dev, block, BLOCK_SIZE))) { + printk ("ext_read_super: unable to read first free inode block\n"); + brelse(s->u.ext_sb.s_zmap[1]); + s->s_dev = 0; + free_super (s); + return NULL; + } + } +#endif + + free_super(s); + /* set up enough so that it can read an inode */ + s->s_dev = dev; + s->s_op = &ext_sops; + if (!(s->s_mounted = iget(dev,EXT_ROOT_INO))) { + s->s_dev=0; + printk("get root inode failed\n"); + return NULL; + } + return s; +} + +void ext_write_super (struct super_block *sb) +{ +#ifdef EXTFS_FREELIST + struct buffer_head * bh; + struct ext_super_block * es; + +#ifdef EXTFS_DEBUG + printk ("ext_write_super called\n"); +#endif + if (!(bh = bread(sb->s_dev, 1, BLOCK_SIZE))) { + printk ("ext_write_super: bread failed\n"); + return; + } + es = (struct ext_super_block *) bh->b_data; + es->s_firstfreeblock = (unsigned long) sb->u.ext_sb.s_zmap[0]; + es->s_freeblockscount = (unsigned long) sb->u.ext_sb.s_zmap[2]; + es->s_firstfreeinode = (unsigned long) sb->u.ext_sb.s_imap[0]; + es->s_freeinodescount = (unsigned long) sb->u.ext_sb.s_imap[2]; + bh->b_dirt = 1; + brelse (bh); + sb->s_dirt = 0; +#endif +} + +void ext_statfs (struct super_block *sb, struct statfs *buf) +{ + long tmp; + + put_fs_long(EXT_SUPER_MAGIC, &buf->f_type); + put_fs_long(1024, &buf->f_bsize); + put_fs_long(sb->u.ext_sb.s_nzones << sb->u.ext_sb.s_log_zone_size, &buf->f_blocks); + tmp = ext_count_free_blocks(sb); + put_fs_long(tmp, &buf->f_bfree); + put_fs_long(tmp, &buf->f_bavail); + put_fs_long(sb->u.ext_sb.s_ninodes, &buf->f_files); + put_fs_long(ext_count_free_inodes(sb), &buf->f_ffree); + /* Don't know what value to put in buf->f_fsid */ +} + +static int _ext_bmap(struct inode * inode,int block,int create) +{ + struct buffer_head * bh; + int i; + + if (block<0) { + printk("_ext_bmap: block<0"); + return 0; + } + if (block >= 9+256+256*256+256*256*256) { + printk("_ext_bmap: block>big"); + return 0; + } + if (block<9) { + if (create && !inode->i_data[block]) + if (inode->i_data[block]=ext_new_block(inode->i_dev)) { + inode->i_ctime=CURRENT_TIME; + inode->i_dirt=1; + } + return inode->i_data[block]; + } + block -= 9; + if (block<256) { + if (create && !inode->i_data[9]) + if (inode->i_data[9]=ext_new_block(inode->i_dev)) { + inode->i_dirt=1; + inode->i_ctime=CURRENT_TIME; + } + if (!inode->i_data[9]) + return 0; + if (!(bh = bread(inode->i_dev, inode->i_data[9], BLOCK_SIZE))) + return 0; + i = ((unsigned long *) (bh->b_data))[block]; + if (create && !i) + if (i=ext_new_block(inode->i_dev)) { + ((unsigned long *) (bh->b_data))[block]=i; + bh->b_dirt=1; + } + brelse(bh); + return i; + } + block -= 256; + if (block<256*256) { + if (create && !inode->i_data[10]) + if (inode->i_data[10]=ext_new_block(inode->i_dev)) { + inode->i_dirt=1; + inode->i_ctime=CURRENT_TIME; + } + if (!inode->i_data[10]) + return 0; + if (!(bh=bread(inode->i_dev, inode->i_data[10], BLOCK_SIZE))) + return 0; + i = ((unsigned long *)bh->b_data)[block>>8]; + if (create && !i) + if (i=ext_new_block(inode->i_dev)) { + ((unsigned long *) (bh->b_data))[block>>8]=i; + bh->b_dirt=1; + } + brelse(bh); + if (!i) + return 0; + if (!(bh=bread(inode->i_dev, i, BLOCK_SIZE))) + return 0; + i = ((unsigned long *)bh->b_data)[block&255]; + if (create && !i) + if (i=ext_new_block(inode->i_dev)) { + ((unsigned long *) (bh->b_data))[block&255]=i; + bh->b_dirt=1; + } + brelse(bh); + return i; + } + if (create && !inode->i_data[11]) + if (inode->i_data[11] = ext_new_block(inode->i_dev)) { + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + } + if (!inode->i_data[11]) + return 0; + if (!(bh = bread(inode->i_dev, inode->i_data[11], BLOCK_SIZE))) + return 0; + i = ((unsigned long *) bh->b_data)[block >> 16]; + if (create && !i) + if (i = ext_new_block(inode->i_dev)) { + ((unsigned long *) bh->b_data)[block >> 16] = i; + bh->b_dirt = 1; + } + brelse (bh); + if (!i) + return 0; + if (!(bh = bread(inode->i_dev, i, BLOCK_SIZE))) + return 0; + i = ((unsigned long *) bh->b_data)[(block >> 8) & 255]; + if (create && !i) + if (i = ext_new_block(inode->i_dev)) { + ((unsigned long *) bh->b_data)[(block >> 8) & 255] = i; + bh->b_dirt = 1; + } + brelse (bh); + if (!i) + return 0; + if (!(bh = bread(inode->i_dev, i, BLOCK_SIZE))) + return 0; + i = ((unsigned long *) bh->b_data)[block & 255]; + if (create && !i) + if (i = ext_new_block(inode->i_dev)) { + ((unsigned long *) bh->b_data)[block & 255] = i; + bh->b_dirt = 1; + } + brelse (bh); + return i; + + printk("ext_bmap: triple indirection not yet implemented\n"); + return 0; +} + +int ext_bmap(struct inode * inode,int block) +{ + return _ext_bmap(inode,block,0); +} + +int ext_create_block(struct inode * inode, int block) +{ + return _ext_bmap(inode,block,1); +} + +void ext_read_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct ext_inode * raw_inode; + int block; + +#ifdef EXTFS_BITMAP + block = 2 + inode->i_sb->u.ext_sb.s_imap_blocks + inode->i_sb->u.ext_sb.s_zmap_blocks + + (inode->i_ino-1)/EXT_INODES_PER_BLOCK; +#endif +#ifdef EXTFS_FREELIST + block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK; +#endif + if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) + panic("unable to read i-node block"); + raw_inode = ((struct ext_inode *) bh->b_data) + + (inode->i_ino-1)%EXT_INODES_PER_BLOCK; + inode->i_mode = raw_inode->i_mode; + inode->i_uid = raw_inode->i_uid; + inode->i_gid = raw_inode->i_gid; + inode->i_nlink = raw_inode->i_nlinks; + inode->i_size = raw_inode->i_size; + inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + inode->i_rdev = raw_inode->i_zone[0]; + else for (block = 0; block < 12; block++) + inode->i_data[block] = raw_inode->i_zone[block]; + brelse(bh); + inode->i_op = NULL; + if (S_ISREG(inode->i_mode)) + inode->i_op = &ext_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &ext_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &ext_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) + inode->i_op = &ext_chrdev_inode_operations; + else if (S_ISBLK(inode->i_mode)) + inode->i_op = &ext_blkdev_inode_operations; + else if (S_ISFIFO(inode->i_mode)) { + inode->i_op = &ext_fifo_inode_operations; + inode->i_size = 0; + inode->i_pipe = 1; + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + } +} + +void ext_write_inode(struct inode * inode) +{ + struct buffer_head * bh; + struct ext_inode * raw_inode; + int block; + +#ifdef EXTFS_BITMAP + block = 2 + inode->i_sb->u.ext_sb.s_imap_blocks + inode->i_sb->u.ext_sb.s_zmap_blocks + + (inode->i_ino-1)/EXT_INODES_PER_BLOCK; +#endif +#ifdef EXTFS_FREELIST + block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK; +#endif + if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) + panic("unable to read i-node block"); + raw_inode = ((struct ext_inode *)bh->b_data) + + (inode->i_ino-1)%EXT_INODES_PER_BLOCK; + raw_inode->i_mode = inode->i_mode; + raw_inode->i_uid = inode->i_uid; + raw_inode->i_gid = inode->i_gid; + raw_inode->i_nlinks = inode->i_nlink; + raw_inode->i_size = inode->i_size; + raw_inode->i_time = inode->i_mtime; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_zone[0] = inode->i_rdev; + else for (block = 0; block < 12; block++) + raw_inode->i_zone[block] = inode->i_data[block]; + bh->b_dirt=1; + inode->i_dirt=0; + brelse(bh); +} diff --git a/fs/ext/namei.c b/fs/ext/namei.c new file mode 100644 index 000000000000..bea8dc0f0b01 --- /dev/null +++ b/fs/ext/namei.c @@ -0,0 +1,901 @@ +/* + * linux/fs/ext/namei.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/namei.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* + * comment out this line if you want names > EXT_NAME_LEN chars to be + * truncated. Else they will be disallowed. + */ +/* #define NO_TRUNCATE */ + +/* + * EXT_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a power of 2 and must be greater or equal than 8 + * because a directory entry needs 8 bytes for its fixed part + * (4 bytes for the inode, 2 bytes for the entry length and 2 bytes + * for the name length) + */ +#define EXT_DIR_PAD 8 + +/* + * + * EXT_DIR_MIN_SIZE is the minimal size of a directory entry + * + * During allocations, a directory entry is split into 2 ones + * *ONLY* if the size of the unused part is greater than or + * equal to EXT_DIR_MIN_SIZE + */ +#define EXT_DIR_MIN_SIZE 12 + +/* + * ok, we cannot use strncmp, as the name is not in our data space. + * Thus we'll have to use ext_match. No big problem. Match also makes + * some sanity tests. + * + * NOTE! unlike strncmp, ext_match returns 1 for success, 0 for failure. + */ +static int ext_match(int len,const char * name,struct ext_dir_entry * de) +{ + register int same __asm__("ax"); + + if (!de || !de->inode || len > EXT_NAME_LEN) + return 0; + /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ + if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) + return 1; +/* if (len < EXT_NAME_LEN && de->name[len]) + return 0; */ + if (len < EXT_NAME_LEN && len != de->name_len) + return 0; + __asm__("cld\n\t" + "fs ; repe ; cmpsb\n\t" + "setz %%al" + :"=a" (same) + :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) + :"cx","di","si"); + return same; +} + +/* + * ext_find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the cache buffer in which the entry was found, and the entry + * itself (as a parameter - res_dir). It does NOT read the inode of the + * entry - you'll have to do that yourself if you want to. + * + * addition for the ext file system : this function returns the previous + * and next directory entries in the parameters prev_dir and next_dir + */ +static struct buffer_head * ext_find_entry(struct inode * dir, + const char * name, int namelen, struct ext_dir_entry ** res_dir, + struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir) +{ +/* int entries; */ + int block /* ,i */; + long offset; + struct buffer_head * bh; + struct ext_dir_entry * de; + + *res_dir = NULL; + if (!dir) + return NULL; +#ifdef NO_TRUNCATE + if (namelen > EXT_NAME_LEN) + return NULL; +#else + if (namelen > EXT_NAME_LEN) + namelen = EXT_NAME_LEN; +#endif +/* entries = dir->i_size / (sizeof (struct ext_dir_entry)); */ + if (!(block = dir->i_data[0])) + return NULL; + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) + return NULL; + if (prev_dir) + *prev_dir = NULL; + if (next_dir) + *next_dir = NULL; +/* i = 0; */ + offset = 0; + de = (struct ext_dir_entry *) bh->b_data; + while (offset < dir->i_size) { + if ((char *)de >= BLOCK_SIZE+bh->b_data) { + brelse(bh); + bh = NULL; + if (!(block = ext_bmap(dir,offset>>BLOCK_SIZE_BITS)) || + !(bh = bread(dir->i_dev, block, BLOCK_SIZE))) { +/* i += EXT_DIR_ENTRIES_PER_BLOCK; */ +/* offset += BLOCK_SIZE; */ + continue; + } + de = (struct ext_dir_entry *) bh->b_data; + if (prev_dir) + *prev_dir = NULL; + } + if (ext_match(namelen,name,de)) { + *res_dir = de; + if (next_dir) + if (offset + de->rec_len < dir->i_size) + *next_dir = (struct ext_dir_entry *) + ((char *) de + de->rec_len); + else + *next_dir = NULL; + return bh; + } + offset += de->rec_len; + if (prev_dir) + *prev_dir = de; + de = (struct ext_dir_entry *) ((char *) de + de->rec_len); +/* i++; */ + } + brelse(bh); + return NULL; +} + +int ext_lookup(struct inode * dir,const char * name, int len, + struct inode ** result) +{ + int ino; + struct ext_dir_entry * de; + struct buffer_head * bh; + + *result = NULL; + if (!dir) + return -ENOENT; + if (!S_ISDIR(dir->i_mode)) { + iput(dir); + return -ENOENT; + } + if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) { + iput(dir); + return -ENOENT; + } + ino = de->inode; + brelse(bh); + if (!(*result = iget(dir->i_dev,ino))) { + iput(dir); + return -EACCES; + } + iput(dir); + return 0; +} + +/* + * ext_add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as ext_find_entry(). It returns NULL if it failed. + * + * NOTE!! The inode part of 'de' is left at 0 - which means you + * may not sleep between calling this and putting something into + * the entry, as someone else might have used it while you slept. + */ +static struct buffer_head * ext_add_entry(struct inode * dir, + const char * name, int namelen, struct ext_dir_entry ** res_dir) +{ + int block,i; + long offset; + unsigned short rec_len; + struct buffer_head * bh; + struct ext_dir_entry * de, * de1; + + *res_dir = NULL; + if (!dir) + return NULL; +#ifdef NO_TRUNCATE + if (namelen > EXT_NAME_LEN) + return NULL; +#else + if (namelen > EXT_NAME_LEN) + namelen = EXT_NAME_LEN; +#endif + if (!namelen) + return NULL; + if (!(block = dir->i_data[0])) + return NULL; + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) + return NULL; + rec_len = ((8 + namelen + EXT_DIR_PAD - 1) / EXT_DIR_PAD) * EXT_DIR_PAD; +/* i = 0; */ + offset = 0; + de = (struct ext_dir_entry *) bh->b_data; + while (1) { + if ((char *)de >= BLOCK_SIZE+bh->b_data && offset < dir->i_size) { +#ifdef EXTFS_DEBUG +printk ("ext_add_entry: skipping to next block\n"); +#endif + brelse(bh); + bh = NULL; + block = ext_create_block(dir,offset>>BLOCK_SIZE_BITS); + if (!block) + return NULL; + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) { +/* i += EXT_DIR_ENTRIES_PER_BLOCK; */ + offset += BLOCK_SIZE; + continue; + } + de = (struct ext_dir_entry *) bh->b_data; + } + if (offset >= dir->i_size) { + /* Check that the directory entry fits in the block */ + if (offset % BLOCK_SIZE == 0 + || (BLOCK_SIZE - (offset % BLOCK_SIZE)) < rec_len) { + if ((offset % BLOCK_SIZE) != 0) { + /* If the entry does not fit in the + block, the remainder of the block + becomes an unused entry */ + de->inode = 0; + de->rec_len = BLOCK_SIZE + - (offset & (BLOCK_SIZE - 1)); + de->name_len = 0; + offset += de->rec_len; + dir->i_size += de->rec_len; + dir->i_dirt = 1; + dir->i_ctime = CURRENT_TIME; + bh->b_dirt = 1; + } + brelse (bh); + bh = NULL; + block = ext_create_block (dir,offset>>BLOCK_SIZE_BITS); +#ifdef EXTFS_DEBUG +printk ("ext_add_entry : creating next block\n"); +#endif + if (!block) + return NULL; + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) + return NULL; /* Other thing to do ??? */ + de = (struct ext_dir_entry *) bh->b_data; + } + /* Allocate the entry */ + de->inode=0; + de->rec_len = rec_len; +/* dir->i_size = (i+1)*sizeof(struct ext_dir_entry); */ + dir->i_size += de->rec_len; + dir->i_dirt = 1; + dir->i_ctime = CURRENT_TIME; + } + if (!de->inode && de->rec_len >= rec_len) { + if (de->rec_len > rec_len + && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) { + /* The found entry is too big : it is split + into 2 ones : + - the 1st one will be used to hold the name, + - the 2nd one is unused */ + de1 = (struct ext_dir_entry *) ((char *) de + rec_len); + de1->inode = 0; + de1->rec_len = de->rec_len - rec_len; + de1->name_len = 0; + de->rec_len = rec_len; + } + dir->i_mtime = CURRENT_TIME; + de->name_len = namelen; + for (i=0; i < namelen ; i++) + de->name[i]=/*(ib_dirt = 1; + *res_dir = de; + return bh; + } + offset += de->rec_len; + de = (struct ext_dir_entry *) ((char *) de + de->rec_len); + } + brelse(bh); + return NULL; +} + +int ext_create(struct inode * dir,const char * name, int len, int mode, + struct inode ** result) +{ + struct inode * inode; + struct buffer_head * bh; + struct ext_dir_entry * de; + + *result = NULL; + if (!dir) + return -ENOENT; + inode = ext_new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_op = &ext_file_inode_operations; + inode->i_mode = mode; + inode->i_dirt = 1; + bh = ext_add_entry(dir,name,len,&de); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_ino; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + *result = inode; + return 0; +} + +int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev) +{ + struct inode * inode; + struct buffer_head * bh; + struct ext_dir_entry * de; + + if (!dir) + return -ENOENT; + bh = ext_find_entry(dir,name,len,&de,NULL,NULL); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = ext_new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_uid = current->euid; + inode->i_mode = mode; + inode->i_op = NULL; + if (S_ISREG(inode->i_mode)) + inode->i_op = &ext_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &ext_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &ext_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) + inode->i_op = &ext_chrdev_inode_operations; + else if (S_ISBLK(inode->i_mode)) + inode->i_op = &ext_blkdev_inode_operations; + else if (S_ISFIFO(inode->i_mode)) { + inode->i_op = &ext_fifo_inode_operations; + inode->i_size = 0; + inode->i_pipe = 1; + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + } + if (S_ISBLK(mode) || S_ISCHR(mode)) + inode->i_rdev = rdev; + inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + bh = ext_add_entry(dir,name,len,&de); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_ino; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + iput(inode); + return 0; +} + +int ext_mkdir(struct inode * dir, const char * name, int len, int mode) +{ + struct inode * inode; + struct buffer_head * bh, *dir_block; + struct ext_dir_entry * de; + + bh = ext_find_entry(dir,name,len,&de,NULL,NULL); + if (bh) { + brelse(bh); + iput(dir); + return -EEXIST; + } + inode = ext_new_inode(dir->i_dev); + if (!inode) { + iput(dir); + return -ENOSPC; + } + inode->i_op = &ext_dir_inode_operations; + inode->i_size = 2 * 16; /* Each entry is coded on 16 bytes for "." and ".." + - 4 bytes for the inode number, + - 2 bytes for the record length + - 2 bytes for the name length + - 8 bytes for the name */ + inode->i_mtime = inode->i_atime = CURRENT_TIME; + if (!(inode->i_data[0] = ext_new_block(inode->i_dev))) { + iput(dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + return -ENOSPC; + } + inode->i_dirt = 1; + if (!(dir_block = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { + iput(dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + return -EIO; + } + de = (struct ext_dir_entry *) dir_block->b_data; + de->inode=inode->i_ino; + de->rec_len=16; + de->name_len=1; + strcpy(de->name,"."); +/* de++; */ + de = (struct ext_dir_entry *) ((char *) de + de->rec_len); + de->inode = dir->i_ino; + de->rec_len=16; + de->name_len=2; + strcpy(de->name,".."); + inode->i_nlink = 2; + dir_block->b_dirt = 1; + brelse(dir_block); + inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask); + inode->i_dirt = 1; + bh = ext_add_entry(dir,name,len,&de); + if (!bh) { + iput(dir); + inode->i_nlink=0; + iput(inode); + return -ENOSPC; + } + de->inode = inode->i_ino; + bh->b_dirt = 1; + dir->i_nlink++; + dir->i_dirt = 1; + iput(dir); + iput(inode); + brelse(bh); + return 0; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +static int empty_dir(struct inode * inode) +{ + int /* nr, */ block; +/* int len; */ + unsigned long offset; + struct buffer_head * bh; + struct ext_dir_entry * de, * de1; + +/* len = inode->i_size / sizeof (struct ext_dir_entry); */ + if (inode->i_size < 2 * 12 || !inode->i_data[0] || + !(bh=bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } + de = (struct ext_dir_entry *) bh->b_data; + de1 = (struct ext_dir_entry *) ((char *) de + de->rec_len); + if (de->inode != inode->i_ino || !de1->inode || + strcmp(".",de->name) || strcmp("..",de1->name)) { + printk("warning - bad directory on dev %04x\n",inode->i_dev); + return 0; + } +/* nr = 2; */ + offset = de->rec_len + de1->rec_len; + de = (struct ext_dir_entry *) ((char *) de1 + de1->rec_len); + while (offset < inode->i_size ) { + if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) { + brelse(bh); + block = ext_bmap(inode, offset >> BLOCK_SIZE_BITS); + if (!block) { + offset += BLOCK_SIZE; + continue; + } + if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) + return 0; + de = (struct ext_dir_entry *) bh->b_data; + } + if (de->inode) { + brelse(bh); + return 0; + } + offset += de->rec_len; + de = (struct ext_dir_entry *) ((char *) de + de->rec_len); + } + brelse(bh); + return 1; +} + +static inline void ext_merge_entries (struct ext_dir_entry * de, + struct ext_dir_entry * pde, struct ext_dir_entry * nde) +{ + if (! nde->inode) + de->rec_len += nde->rec_len; + if (! pde->inode) + pde->rec_len += de->rec_len; +} + +int ext_rmdir(struct inode * dir, const char * name, int len) +{ + int retval; + struct inode * inode; + struct buffer_head * bh; + struct ext_dir_entry * de, * pde, * nde; + + inode = NULL; + bh = ext_find_entry(dir,name,len,&de,&pde,&nde); + retval = -ENOENT; + if (!bh) + goto end_rmdir; + retval = -EPERM; + if (!(inode = iget(dir->i_dev, de->inode))) + goto end_rmdir; + if ((dir->i_mode & S_ISVTX) && current->euid && + inode->i_uid != current->euid) + goto end_rmdir; + if (inode->i_dev != dir->i_dev) + goto end_rmdir; + if (inode == dir) /* we may not delete ".", but "../dir" is ok */ + goto end_rmdir; + if (!S_ISDIR(inode->i_mode)) { + retval = -ENOTDIR; + goto end_rmdir; + } + if (!empty_dir(inode)) { + retval = -ENOTEMPTY; + goto end_rmdir; + } + if (inode->i_count > 1) { + retval = -EBUSY; + goto end_rmdir; + } + if (inode->i_nlink != 2) + printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink); + de->inode = 0; + de->name_len = 0; + ext_merge_entries (de, pde, nde); + bh->b_dirt = 1; + inode->i_nlink=0; + inode->i_dirt=1; + dir->i_nlink--; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + dir->i_dirt=1; + retval = 0; +end_rmdir: + iput(dir); + iput(inode); + brelse(bh); + return retval; +} + +int ext_unlink(struct inode * dir, const char * name, int len) +{ + int retval; + struct inode * inode; + struct buffer_head * bh; + struct ext_dir_entry * de, * pde, * nde; + + retval = -ENOENT; + inode = NULL; + bh = ext_find_entry(dir,name,len,&de,&pde,&nde); + if (!bh) + goto end_unlink; + if (!(inode = iget(dir->i_dev, de->inode))) + goto end_unlink; + retval = -EPERM; + if ((dir->i_mode & S_ISVTX) && !suser() && + current->euid != inode->i_uid && + current->euid != dir->i_uid) + goto end_unlink; + if (S_ISDIR(inode->i_mode)) + goto end_unlink; + if (!inode->i_nlink) { + printk("Deleting nonexistent file (%04x:%d), %d\n", + inode->i_dev,inode->i_ino,inode->i_nlink); + inode->i_nlink=1; + } + de->inode = 0; + de->name_len = 0; + ext_merge_entries (de, pde, nde); + bh->b_dirt = 1; + inode->i_nlink--; + inode->i_dirt = 1; + inode->i_ctime = CURRENT_TIME; + retval = 0; +end_unlink: + brelse(bh); + iput(inode); + iput(dir); + return retval; +} + +int ext_symlink(struct inode * dir, const char * name, int len, const char * symname) +{ + struct ext_dir_entry * de; + struct inode * inode = NULL; + struct buffer_head * bh = NULL, * name_block = NULL; + int i; + char c; + + if (!(inode = ext_new_inode(dir->i_dev))) { + iput(dir); + return -ENOSPC; + } + inode->i_mode = S_IFLNK | 0777; + inode->i_op = &ext_symlink_inode_operations; + if (!(inode->i_data[0] = ext_new_block(inode->i_dev))) { + iput(dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + return -ENOSPC; + } + inode->i_dirt = 1; + if (!(name_block = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { + iput(dir); + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + return -EIO; + } + i = 0; + while (i < 1023 && (c=get_fs_byte(symname++))) + name_block->b_data[i++] = c; + name_block->b_data[i] = 0; + name_block->b_dirt = 1; + brelse(name_block); + inode->i_size = i; + inode->i_dirt = 1; + bh = ext_find_entry(dir,name,len,&de,NULL,NULL); + if (bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + brelse(bh); + iput(dir); + return -EEXIST; + } + bh = ext_add_entry(dir,name,len,&de); + if (!bh) { + inode->i_nlink--; + inode->i_dirt = 1; + iput(inode); + iput(dir); + return -ENOSPC; + } + de->inode = inode->i_ino; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + iput(inode); + return 0; +} + +int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int len) +{ + struct ext_dir_entry * de; + struct buffer_head * bh; + + if (S_ISDIR(oldinode->i_mode)) { + iput(oldinode); + iput(dir); + return -EPERM; + } + bh = ext_find_entry(dir,name,len,&de,NULL,NULL); + if (bh) { + brelse(bh); + iput(dir); + iput(oldinode); + return -EEXIST; + } + bh = ext_add_entry(dir,name,len,&de); + if (!bh) { + iput(dir); + iput(oldinode); + return -ENOSPC; + } + de->inode = oldinode->i_ino; + bh->b_dirt = 1; + brelse(bh); + iput(dir); + oldinode->i_nlink++; + oldinode->i_ctime = CURRENT_TIME; + oldinode->i_dirt = 1; + iput(oldinode); + return 0; +} + +static int subdir(struct inode * new, struct inode * old) +{ + unsigned short fs; + int ino; + int result; + + __asm__("mov %%fs,%0":"=r" (fs)); + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); + new->i_count++; + result = 0; + for (;;) { + if (new == old) { + result = 1; + break; + } + if (new->i_dev != old->i_dev) + break; + ino = new->i_ino; + if (ext_lookup(new,"..",2,&new)) + break; + if (new->i_ino == ino) + break; + } + iput(new); + __asm__("mov %0,%%fs"::"r" (fs)); + return result; +} + +#define PARENT_INO(buffer) \ +((struct ext_dir_entry *) ((char *) buffer + \ +((struct ext_dir_entry *) buffer)->rec_len))->inode +/* (((struct ext_dir_entry *) (buffer))[1].inode) */ + +#define PARENT_NAME(buffer) \ +((struct ext_dir_entry *) ((char *) buffer + \ +((struct ext_dir_entry *) buffer)->rec_len))->name +/* (((struct ext_dir_entry *) (buffer))[1].name) */ + +/* + * rename uses retrying to avoid race-conditions: at least they should be minimal. + * it tries to allocate all the blocks, then sanity-checks, and if the sanity- + * checks fail, it tries to restart itself again. Very practical - no changes + * are done until we know everything works ok.. and then all the changes can be + * done in one fell swoop when we have claimed all the buffers needed. + * + * Anybody can rename anything with this: the permission checks are left to the + * higher-level routines. + */ +static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_len, + struct inode * new_dir, const char * new_name, int new_len) +{ + struct inode * old_inode, * new_inode; + struct buffer_head * old_bh, * new_bh, * dir_bh; + struct ext_dir_entry * old_de, * new_de, * pde, * nde; + int retval; + + goto start_up; +try_again: + brelse(old_bh); + brelse(new_bh); + brelse(dir_bh); + iput(old_inode); + iput(new_inode); + current->counter = 0; + schedule(); +start_up: + old_inode = new_inode = NULL; + old_bh = new_bh = dir_bh = NULL; + old_bh = ext_find_entry(old_dir,old_name,old_len,&old_de,&pde,&nde); + retval = -ENOENT; + if (!old_bh) + goto end_rename; + old_inode = iget(old_dir->i_dev, old_de->inode); + if (!old_inode) + goto end_rename; + if ((old_dir->i_mode & S_ISVTX) && + current->euid != old_inode->i_uid && + current->euid != old_dir->i_uid && !suser()) + goto end_rename; + new_bh = ext_find_entry(new_dir,new_name,new_len,&new_de,NULL,NULL); + if (new_bh) { + new_inode = iget(new_dir->i_dev, new_de->inode); + if (!new_inode) { + brelse(new_bh); + new_bh = NULL; + } + } + if (new_inode == old_inode) { + retval = 0; + goto end_rename; + } + if (S_ISDIR(new_inode->i_mode)) { + retval = -EEXIST; + goto end_rename; + } + if (S_ISDIR(old_inode->i_mode)) { + retval = -EEXIST; + if (new_bh) + goto end_rename; + retval = -EACCES; + if (!permission(old_inode, MAY_WRITE)) + goto end_rename; + retval = -EINVAL; + if (subdir(new_dir, old_inode)) + goto end_rename; + retval = -EIO; + if (!old_inode->i_data[0]) + goto end_rename; + if (!(dir_bh = bread(old_inode->i_dev, old_inode->i_data[0], BLOCK_SIZE))) + goto end_rename; + if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) + goto end_rename; + } + if (!new_bh) + new_bh = ext_add_entry(new_dir,new_name,new_len,&new_de); + retval = -ENOSPC; + if (!new_bh) + goto end_rename; +/* sanity checking before doing the rename - avoid races */ + if (new_inode && (new_de->inode != new_inode->i_ino)) + goto try_again; + if (new_de->inode && !new_inode) + goto try_again; + if (old_de->inode != old_inode->i_ino) + goto try_again; +/* ok, that's it */ + old_de->inode = 0; + old_de->name_len = 0; + new_de->inode = old_inode->i_ino; + ext_merge_entries (old_de, pde, nde); + if (new_inode) { + new_inode->i_nlink--; + new_inode->i_dirt = 1; + } + old_bh->b_dirt = 1; + new_bh->b_dirt = 1; + if (dir_bh) { + PARENT_INO(dir_bh->b_data) = new_dir->i_ino; + dir_bh->b_dirt = 1; + old_dir->i_nlink--; + new_dir->i_nlink++; + old_dir->i_dirt = 1; + new_dir->i_dirt = 1; + } + retval = 0; +end_rename: + brelse(dir_bh); + brelse(old_bh); + brelse(new_bh); + iput(old_inode); + iput(new_inode); + iput(old_dir); + iput(new_dir); + return retval; +} + +/* + * Ok, rename also locks out other renames, as they can change the parent of + * a directory, and we don't want any races. Other races are checked for by + * "do_rename()", which restarts if there are inconsistencies. + * + * Note that there is no race between different filesystems: it's only within + * the same device that races occur: many renames can happen at once, as long + * as they are on different partitions. + */ +int ext_rename(struct inode * old_dir, const char * old_name, int old_len, + struct inode * new_dir, const char * new_name, int new_len) +{ + static struct wait_queue * wait = NULL; + static int lock = 0; + int result; + + while (lock) + sleep_on(&wait); + lock = 1; + result = do_ext_rename(old_dir, old_name, old_len, + new_dir, new_name, new_len); + lock = 0; + wake_up(&wait); + return result; +} diff --git a/fs/ext/symlink.c b/fs/ext/symlink.c new file mode 100644 index 000000000000..1daf1cb0a23e --- /dev/null +++ b/fs/ext/symlink.c @@ -0,0 +1,106 @@ +/* + * linux/fs/ext/symlink.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/symlink.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext symlink handling code + */ + +#include + +#include +#include +#include +#include +#include + +static int ext_readlink(struct inode *, char *, int); +static struct inode * ext_follow_link(struct inode *, struct inode *); + +/* + * symlinks can't do much... + */ +struct inode_operations ext_symlink_inode_operations = { + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + ext_readlink, /* readlink */ + ext_follow_link, /* follow_link */ + NULL, /* bmap */ + NULL /* truncate */ +}; + +static struct inode * ext_follow_link(struct inode * dir, struct inode * inode) +{ + unsigned short fs; + struct buffer_head * bh; + + if (!dir) { + dir = current->root; + dir->i_count++; + } + if (!inode) { + iput(dir); + return NULL; + } + if (!S_ISLNK(inode->i_mode)) { + iput(dir); + return inode; + } + __asm__("mov %%fs,%0":"=r" (fs)); + if ((current->link_count > 5) || !inode->i_data[0] || + !(bh = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { + iput(dir); + iput(inode); + return NULL; + } + iput(inode); + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); + current->link_count++; + inode = _namei(bh->b_data,dir,1); + current->link_count--; + __asm__("mov %0,%%fs"::"r" (fs)); + brelse(bh); + return inode; +} + +static int ext_readlink(struct inode * inode, char * buffer, int buflen) +{ + struct buffer_head * bh; + int i; + char c; + + if (!S_ISLNK(inode->i_mode)) { + iput(inode); + return -EINVAL; + } + if (buflen > 1023) + buflen = 1023; + if (inode->i_data[0]) + bh = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE); + else + bh = NULL; + iput(inode); + if (!bh) + return 0; + i = 0; + while (ib_data[i])) { + i++; + put_fs_byte(c,buffer++); + } + brelse(bh); + return i; +} diff --git a/fs/ext/truncate.c b/fs/ext/truncate.c new file mode 100644 index 000000000000..3e647e8adfbf --- /dev/null +++ b/fs/ext/truncate.c @@ -0,0 +1,193 @@ +/* + * linux/fs/ext/truncate.c + * + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * from + * + * linux/fs/minix/truncate.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +/* + * Truncate has the most races in the whole filesystem: coding it is + * a pain in the a**. Especially as I don't do any locking... + * + * The code may look a bit weird, but that's just because I've tried to + * handle things like file-size changes in a somewhat graceful manner. + * Anyway, truncating a file at the same time somebody else writes to it + * is likely to result in pretty weird behaviour... + * + * The new code handles normal truncates (size = 0) as well as the more + * general case (size = XXX). I hope. + */ + +static int trunc_direct(struct inode * inode) +{ + int i; + int result = 0; +#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10) + +repeat: + for (i = DIRECT_BLOCK ; i < 9 ; i++) { + if (i < DIRECT_BLOCK) + goto repeat; + if (!inode->i_data[i]) + continue; + result = 1; + if (ext_free_block(inode->i_dev,inode->i_data[i])) + inode->i_data[i] = 0; + } + return result; +} + +static int trunc_indirect(struct inode * inode, int offset, unsigned long * p) +{ + int i; + struct buffer_head * bh = NULL; + unsigned long * ind; + int result = 0; +#define INDIRECT_BLOCK (DIRECT_BLOCK-offset) + + if (*p) + bh = bread(inode->i_dev, *p, BLOCK_SIZE); + if (!bh) + return 0; +repeat: + for (i = INDIRECT_BLOCK ; i < 256 ; i++) { + if (i < 0) + i = 0; + if (i < INDIRECT_BLOCK) + goto repeat; + ind = i+(unsigned long *) bh->b_data; + if (!*ind) + continue; + result = 1; + if (ext_free_block(inode->i_dev,*ind)) + *ind = 0; + } + ind = (unsigned long *) bh->b_data; + for (i = 0; i < 256; i++) + if (*(ind++)) + break; + brelse(bh); + if (i >= 256) { + result = 1; + if (ext_free_block(inode->i_dev,*p)) + *p = 0; + } + return result; +} + +static int trunc_dindirect(struct inode * inode, int offset, unsigned long * p) +{ + int i; + struct buffer_head * bh = NULL; + unsigned long * dind; + int result = 0; +#define DINDIRECT_BLOCK ((DIRECT_BLOCK-offset)>>8) + + if (*p) + bh = bread(inode->i_dev, *p, BLOCK_SIZE); + if (!bh) + return 0; +repeat: + for (i = DINDIRECT_BLOCK ; i < 256 ; i ++) { + if (i < 0) + i = 0; + if (i < DINDIRECT_BLOCK) + goto repeat; + dind = i+(unsigned long *) bh->b_data; + if (!*dind) + continue; + result |= trunc_indirect(inode,offset+(i<<8),dind); + } + dind = (unsigned long *) bh->b_data; + for (i = 0; i < 256; i++) + if (*(dind++)) + break; + brelse(bh); + if (i >= 256) { + result = 1; + if (ext_free_block(inode->i_dev,*p)) + *p = 0; + } + return result; +} + +static int trunc_tindirect(struct inode * inode) +{ + int i; + struct buffer_head * bh = NULL; + unsigned long * tind; + int result = 0; +#define TINDIRECT_BLOCK ((DIRECT_BLOCK-(256*256+256+9))>>16) + + if (inode->i_data[11]) + bh = bread(inode->i_dev, inode->i_data[11], BLOCK_SIZE); + if (!bh) + return 0; +repeat: + for (i = TINDIRECT_BLOCK ; i < 256 ; i ++) { + if (i < 0) + i = 0; + if (i < TINDIRECT_BLOCK) + goto repeat; + tind = i+(unsigned long *) bh->b_data; + if (!*tind) + continue; + result |= trunc_dindirect(inode,9+256+256*256+(i<<16),tind); + } + tind = (unsigned long *) bh->b_data; + for (i = 0; i < 256; i++) + if (*(tind++)) + break; + brelse(bh); + if (i >= 256) { + result = 1; + if (ext_free_block(inode->i_dev,inode->i_data[11])) + inode->i_data[11] = 0; + } + return result; +} + +void ext_truncate(struct inode * inode) +{ + int flag; + + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; +/* if (inode->i_data[7] & 0xffff0000) + printk("BAD! ext inode has 16 high bits set\n"); */ + while (1) { + flag = trunc_direct(inode); + flag |= trunc_indirect(inode,9,(unsigned long *)&inode->i_data[9]); + flag |= trunc_dindirect(inode,9+256,(unsigned long *)&inode->i_data[10]); + flag |= trunc_tindirect(inode); + if (!flag) + break; + current->counter = 0; + schedule(); + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; +} + +/* + * Called when a inode is released. Note that this is different + * from ext_open: open gets called at every open, but release + * gets called only when /all/ the files are closed. + */ +void ext_release(struct inode * inode, struct file * filp) +{ + printk("ext_release not implemented\n"); +} diff --git a/fs/fcntl.c b/fs/fcntl.c index 96584831501d..7e61dd94b2bc 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -1,17 +1,17 @@ /* * linux/fs/fcntl.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include -#include -#include #include -#include -#include +#include +#include +#include +#include +#include +#include extern int sys_close(int fd); @@ -35,6 +35,8 @@ static int dupfd(unsigned int fd, unsigned int arg) int sys_dup2(unsigned int oldfd, unsigned int newfd) { + if (oldfd >= NR_OPEN || !current->filp[oldfd]) + return -EBADF; if (newfd == oldfd) return newfd; sys_close(newfd); diff --git a/fs/fifo.c b/fs/fifo.c new file mode 100644 index 000000000000..84da040a6efe --- /dev/null +++ b/fs/fifo.c @@ -0,0 +1,114 @@ +/* + * linux/fs/fifo.c + * + * written by Paul H. Hargrove + */ + +#include +#include +#include +#include + +extern struct file_operations read_pipe_fops; +extern struct file_operations write_pipe_fops; +extern struct file_operations rdwr_pipe_fops; + +static int fifo_open(struct inode * inode,struct file * filp) +{ + int retval = 0; + unsigned long page; + + switch( filp->f_mode ) { + + case 1: + /* + * O_RDONLY + * POSIX.1 says that O_NONBLOCK means return with the FIFO + * opened, even when there is no process writing the FIFO. + */ + filp->f_op = &read_pipe_fops; + PIPE_READERS(*inode)++; + if (!(filp->f_flags & O_NONBLOCK)) + while (!PIPE_WRITERS(*inode)) { + if (PIPE_HEAD(*inode) != PIPE_TAIL(*inode)) + break; + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } + interruptible_sleep_on(&PIPE_READ_WAIT(*inode)); + } + if (retval) + PIPE_READERS(*inode)--; + break; + + case 2: + /* + * O_WRONLY + * POSIX.1 says that O_NONBLOCK means return -1 with + * errno=ENXIO when there is no process reading the FIFO. + */ + if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode)) { + retval = -ENXIO; + break; + } + filp->f_op = &write_pipe_fops; + PIPE_WRITERS(*inode)++; + while (!PIPE_READERS(*inode)) { + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } + interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode)); + } + if (retval) + PIPE_WRITERS(*inode)--; + break; + + case 3: + /* + * O_RDWR + * POSIX.1 leaves this case "undefined" when O_NONBLOCK is set. + * This implementation will NEVER block on a O_RDWR open, since + * the process can at least talk to itself. + */ + filp->f_op = &rdwr_pipe_fops; + PIPE_WRITERS(*inode) += 1; + PIPE_READERS(*inode) += 1; + break; + + default: + retval = -EINVAL; + } + if (PIPE_WRITERS(*inode)) + wake_up(&PIPE_READ_WAIT(*inode)); + if (PIPE_READERS(*inode)) + wake_up(&PIPE_WRITE_WAIT(*inode)); + if (retval || inode->i_size) + return retval; + page = get_free_page(GFP_KERNEL); + if (inode->i_size) { + free_page(page); + return 0; + } + if (!page) + return -ENOMEM; + inode->i_size = page; + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the access mode of the file... + */ +struct file_operations def_fifo_fops = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + fifo_open, /* will set read or write pipe_fops */ + NULL +}; diff --git a/fs/file_table.c b/fs/file_table.c index e0589acf6455..fe0f44a685a9 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -1,9 +1,24 @@ /* * linux/fs/file_table.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include +#include struct file file_table[NR_FILE]; + +struct file * get_empty_filp(void) +{ + int i; + struct file * f = file_table+0; + + for (i = 0; i++ < NR_FILE; f++) + if (!f->f_count) { + memset(f,0,sizeof(*f)); + f->f_count = 1; + return f; + } + return NULL; +} diff --git a/fs/inode.c b/fs/inode.c index 385f2f991121..eaf3093289f4 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1,15 +1,15 @@ /* * linux/fs/inode.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include - +#include #include #include #include +#include + #include struct inode inode_table[NR_INODE]={{0,},}; @@ -39,13 +39,13 @@ static inline void unlock_inode(struct inode * inode) static void write_inode(struct inode * inode) { - lock_inode(inode); - if (!inode->i_dirt || !inode->i_dev) { - unlock_inode(inode); + if (!inode->i_dirt) return; - } - if (inode->i_op && inode->i_op->write_inode) - inode->i_op->write_inode(inode); + inode->i_dirt = 0; + lock_inode(inode); + if (inode->i_dev && inode->i_sb && + inode->i_sb->s_op && inode->i_sb->s_op->write_inode) + inode->i_sb->s_op->write_inode(inode); unlock_inode(inode); } @@ -100,7 +100,7 @@ void sync_inodes(void) inode = 0+inode_table; for(i=0 ; ii_dirt && !inode->i_pipe) + if (inode->i_dirt) write_inode(inode); } } @@ -110,36 +110,34 @@ void iput(struct inode * inode) if (!inode) return; wait_on_inode(inode); - if (!inode->i_count) - panic("iput: trying to free free inode"); + if (!inode->i_count) { + printk("iput: trying to free free inode\n"); + printk("device %04x, inode %d, mode=%07o\n",inode->i_rdev, + inode->i_ino,inode->i_mode); + return; + } if (inode->i_pipe) { wake_up(&inode->i_wait); wake_up(&inode->i_wait2); - if (--inode->i_count) - return; - free_page(inode->i_size); - inode->i_count=0; - inode->i_dirt=0; - inode->i_pipe=0; - return; } - if (!inode->i_dev) { +repeat: + if (inode->i_count>1) { inode->i_count--; return; } - if (S_ISBLK(inode->i_mode)) { - sync_dev(inode->i_rdev); - wait_on_inode(inode); + if (inode->i_pipe) { + free_page(inode->i_size); + inode->i_size = 0; } -repeat: - if (inode->i_count>1) { + if (!inode->i_dev) { inode->i_count--; return; } if (!inode->i_nlink) { - if (inode->i_op && inode->i_op->put_inode) - inode->i_op->put_inode(inode); - return; + if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) { + inode->i_sb->s_op->put_inode(inode); + return; + } } if (inode->i_dirt) { write_inode(inode); /* we can sleep - so do again */ @@ -190,12 +188,13 @@ struct inode * get_pipe_inode(void) if (!(inode = get_empty_inode())) return NULL; - if (!(inode->i_size=get_free_page())) { + if (!(inode->i_size = get_free_page(GFP_USER))) { inode->i_count = 0; return NULL; } inode->i_count = 2; /* sum of readers/writers */ PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; inode->i_pipe = 1; return inode; } @@ -253,6 +252,7 @@ struct inode * iget(int dev,int nr) } inode->i_dev = dev; inode->i_ino = nr; + inode->i_flags = inode->i_sb->s_flags; read_inode(inode); return inode; } diff --git a/fs/ioctl.c b/fs/ioctl.c index 7b2811674620..243b604d01c9 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -1,21 +1,30 @@ /* * linux/fs/ioctl.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include -#include +#include #include +#include +#include +#include int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; + int block; if (fd >= NR_OPEN || !(filp = current->filp[fd])) return -EBADF; + if (S_ISREG(filp->f_inode->i_mode) && cmd == BMAP_IOCTL && + filp->f_inode->i_op->bmap) { + block = get_fs_long((long *) arg); + block = filp->f_inode->i_op->bmap(filp->f_inode,block); + put_fs_long(block,(long *) arg); + return 0; + } if (filp->f_op && filp->f_op->ioctl) return filp->f_op->ioctl(filp->f_inode, filp, cmd,arg); return -EINVAL; diff --git a/fs/minix/Makefile b/fs/minix/Makefile index 81aebc7af46b..f83453987818 100644 --- a/fs/minix/Makefile +++ b/fs/minix/Makefile @@ -7,22 +7,15 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -AR =ar -AS =as -LD =ld -CC =gcc -nostdinc -I../../include -CPP =cpp -nostdinc -I../../include - .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) -S $< .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) -c $< .s.o: $(AS) -o $*.o $< -OBJS= minix_op.o bitmap.o truncate.o namei.o inode.o file_dev.o +OBJS= bitmap.o truncate.o namei.o inode.o \ + file.o dir.o symlink.o blkdev.o chrdev.o fifo.o minix.o: $(OBJS) $(LD) -r -o minix.o $(OBJS) @@ -33,37 +26,92 @@ clean: dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile ### Dependencies: -bitmap.o : bitmap.c ../../include/linux/string.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/minix_fs.h -file_dev.o : file_dev.c ../../include/errno.h ../../include/fcntl.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/sys/stat.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/linux/mm.h \ - ../../include/linux/kernel.h ../../include/signal.h ../../include/sys/param.h \ - ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h ../../include/linux/minix_fs.h \ - ../../include/asm/segment.h -inode.o : inode.c ../../include/linux/string.h ../../include/sys/stat.h ../../include/sys/types.h \ - ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/minix_fs.h ../../include/asm/system.h -minix_op.o : minix_op.c ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/minix_fs.h -namei.o : namei.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \ - ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \ - ../../include/linux/minix_fs.h ../../include/asm/segment.h ../../include/linux/string.h \ - ../../include/fcntl.h ../../include/errno.h ../../include/const.h ../../include/sys/stat.h -truncate.o : truncate.c ../../include/linux/sched.h ../../include/linux/head.h \ - ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/minix_fs.h ../../include/linux/tty.h \ - ../../include/termios.h ../../include/errno.h ../../include/fcntl.h ../../include/sys/stat.h +bitmap.o : bitmap.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/string.h +blkdev.o : blkdev.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h +chrdev.o : chrdev.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h +dir.o : dir.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/minix_fs.h \ + /usr/src/linux/include/linux/stat.h +fifo.o : fifo.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h +file.o : file.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/stat.h +inode.o : inode.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/segment.h +namei.o : namei.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/const.h +symlink.o : symlink.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/stat.h +truncate.o : truncate.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/minix_fs.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/fcntl.h diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 6a3ead378f17..57bed4ba9438 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -1,15 +1,15 @@ /* - * linux/fs/bitmap.c + * linux/fs/minix/bitmap.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* bitmap.c contains the code that handles the inode and block bitmaps */ -#include #include #include #include +#include #define clear_block(addr) \ __asm__("cld\n\t" \ @@ -44,6 +44,35 @@ __asm__("cld\n" \ :"=c" (__res):"0" (0),"S" (addr):"ax","dx","si"); \ __res;}) +static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; + +static unsigned long count_used(struct buffer_head *map[], unsigned numblocks, + unsigned numbits) +{ + unsigned i, j, end, sum = 0; + struct buffer_head *bh; + + for (i=0; (i= (8*BLOCK_SIZE)) { + end = BLOCK_SIZE; + numbits -= 8*BLOCK_SIZE; + } else { + int tmp; + end = numbits >> 3; + numbits &= 0x7; + tmp = bh->b_data[end] & ((1<>4)&0xf]; + numbits = 0; + } + for (j=0; jb_data[j] & 0xf] + + nibblemap[(bh->b_data[j]>>4)&0xf]; + } + return(sum); +} + int minix_free_block(int dev, int block) { struct super_block * sb; @@ -52,9 +81,9 @@ int minix_free_block(int dev, int block) if (!(sb = get_super(dev))) panic("trying to free block on nonexistent device"); - if (block < sb->s_firstdatazone || block >= sb->s_nzones) + if (block < sb->u.minix_sb.s_firstdatazone || block >= sb->u.minix_sb.s_nzones) panic("trying to free block not in datazone"); - bh = get_hash_table(dev,block); + bh = get_hash_table(dev,block,BLOCK_SIZE); if (bh) { if (bh->b_count > 1) { brelse(bh); @@ -65,10 +94,10 @@ int minix_free_block(int dev, int block) if (bh->b_count) brelse(bh); } - zone = block - sb->s_firstdatazone + 1; + zone = block - sb->u.minix_sb.s_firstdatazone + 1; bit = zone & 8191; zone >>= 13; - bh = sb->s_zmap[zone]; + bh = sb->u.minix_sb.s_zmap[zone]; if (clear_bit(bit,bh->b_data)) printk("free_block (%04x:%d): bit already cleared\n",dev,block); bh->b_dirt = 1; @@ -85,18 +114,18 @@ int minix_new_block(int dev) panic("trying to get new block from nonexistant device"); j = 8192; for (i=0 ; i<8 ; i++) - if (bh=sb->s_zmap[i]) + if (bh=sb->u.minix_sb.s_zmap[i]) if ((j=find_first_zero(bh->b_data))<8192) break; if (i>=8 || !bh || j>=8192) return 0; - if (set_bit(j,bh->b_data)) + if (set_bit(j,bh->b_data)) panic("new_block: bit already set"); bh->b_dirt = 1; - j += i*8192 + sb->s_firstdatazone-1; - if (j >= sb->s_nzones) + j += i*8192 + sb->u.minix_sb.s_firstdatazone-1; + if (j >= sb->u.minix_sb.s_nzones) return 0; - if (!(bh=getblk(dev,j))) + if (!(bh=getblk(dev,j,BLOCK_SIZE))) panic("new_block: cannot get block"); if (bh->b_count != 1) panic("new block: count is != 1"); @@ -107,6 +136,12 @@ int minix_new_block(int dev) return j; } +unsigned long minix_count_free_blocks(struct super_block *sb) +{ + return (sb->u.minix_sb.s_nzones - count_used(sb->u.minix_sb.s_zmap,sb->u.minix_sb.s_zmap_blocks,sb->u.minix_sb.s_nzones)) + << sb->u.minix_sb.s_log_zone_size; +} + void minix_free_inode(struct inode * inode) { struct buffer_head * bh; @@ -129,11 +164,11 @@ void minix_free_inode(struct inode * inode) printk("free_inode: inode on nonexistent device\n"); return; } - if (inode->i_ino < 1 || inode->i_ino > inode->i_sb->s_ninodes) { + if (inode->i_ino < 1 || inode->i_ino > inode->i_sb->u.minix_sb.s_ninodes) { printk("free_inode: inode 0 or nonexistent inode\n"); return; } - if (!(bh=inode->i_sb->s_imap[inode->i_ino>>13])) { + if (!(bh=inode->i_sb->u.minix_sb.s_imap[inode->i_ino>>13])) { printk("free_inode: nonexistent imap in superblock\n"); return; } @@ -156,12 +191,13 @@ struct inode * minix_new_inode(int dev) iput(inode); return NULL; } + inode->i_flags = inode->i_sb->s_flags; j = 8192; for (i=0 ; i<8 ; i++) - if (bh=inode->i_sb->s_imap[i]) + if (bh=inode->i_sb->u.minix_sb.s_imap[i]) if ((j=find_first_zero(bh->b_data))<8192) break; - if (!bh || j >= 8192 || j+i*8192 > inode->i_sb->s_ninodes) { + if (!bh || j >= 8192 || j+i*8192 > inode->i_sb->u.minix_sb.s_ninodes) { iput(inode); return NULL; } @@ -179,6 +215,11 @@ struct inode * minix_new_inode(int dev) inode->i_dirt = 1; inode->i_ino = j + i*8192; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_op = &minix_inode_operations; + inode->i_op = NULL; return inode; } + +unsigned long minix_count_free_inodes(struct super_block *sb) +{ + return sb->u.minix_sb.s_ninodes - count_used(sb->u.minix_sb.s_imap,sb->u.minix_sb.s_imap_blocks,sb->u.minix_sb.s_ninodes); +} diff --git a/fs/minix/blkdev.c b/fs/minix/blkdev.c new file mode 100644 index 000000000000..c8fae84dc39f --- /dev/null +++ b/fs/minix/blkdev.c @@ -0,0 +1,61 @@ +/* + * linux/fs/minix/blkdev.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +/* + * Called every time a minix block special file is opened + */ +static int blkdev_open(struct inode * inode, struct file * filp) +{ + int i; + + i = MAJOR(inode->i_rdev); + if (i < MAX_BLKDEV) { + filp->f_op = blkdev_fops[i]; + if (filp->f_op && filp->f_op->open) + return filp->f_op->open(inode,filp); + } + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +static struct file_operations def_blk_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + blkdev_open, /* open */ + NULL, /* release */ +}; + +struct inode_operations minix_blkdev_inode_operations = { + &def_blk_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + minix_bmap, /* bmap */ + minix_truncate /* truncate */ +}; diff --git a/fs/minix/chrdev.c b/fs/minix/chrdev.c new file mode 100644 index 000000000000..f17979656ffb --- /dev/null +++ b/fs/minix/chrdev.c @@ -0,0 +1,62 @@ +/* + * linux/fs/minix/chrdev.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include + +/* + * Called every time a minix character special file is opened + */ +static int chrdev_open(struct inode * inode, struct file * filp) +{ + int i; + + i = MAJOR(inode->i_rdev); + if (i < MAX_CHRDEV) { + filp->f_op = chrdev_fops[i]; + if (filp->f_op && filp->f_op->open) + return filp->f_op->open(inode,filp); + } + return 0; +} + +/* + * Dummy default file-operations: the only thing this does + * is contain the open that then fills in the correct operations + * depending on the special file... + */ +static struct file_operations def_chr_fops = { + NULL, /* lseek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + NULL, /* ioctl */ + chrdev_open, /* open */ + NULL, /* release */ +}; + +struct inode_operations minix_chrdev_inode_operations = { + &def_chr_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + minix_bmap, /* bmap */ + minix_truncate /* truncate */ +}; + diff --git a/fs/minix/dir.c b/fs/minix/dir.c new file mode 100644 index 000000000000..1a894f8f0142 --- /dev/null +++ b/fs/minix/dir.c @@ -0,0 +1,91 @@ +/* + * linux/fs/minix/dir.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * minix directory handling functions + */ + +#include + +#include +#include +#include +#include + +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 */ + NULL, /* write - bad */ + minix_readdir, /* readdir */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* no special open code */ + NULL /* no special release code */ +}; + +/* + * directories can handle most operations... + */ +struct inode_operations minix_dir_inode_operations = { + &minix_dir_operations, /* default directory file-ops */ + minix_create, /* create */ + minix_lookup, /* lookup */ + minix_link, /* link */ + minix_unlink, /* unlink */ + minix_symlink, /* symlink */ + minix_mkdir, /* mkdir */ + minix_rmdir, /* rmdir */ + minix_mknod, /* mknod */ + minix_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + minix_bmap, /* bmap */ + minix_truncate /* truncate */ +}; + +static int minix_readdir(struct inode * inode, struct file * filp, + struct dirent * dirent, int count) +{ + unsigned int block,offset,i; + char c; + struct buffer_head * bh; + struct minix_dir_entry * de; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -EBADF; + if (filp->f_pos & (sizeof (struct minix_dir_entry) - 1)) + return -EBADF; + while (filp->f_pos < inode->i_size) { + offset = filp->f_pos & 1023; + block = minix_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS); + if (!block || !(bh = bread(inode->i_dev,block,BLOCK_SIZE))) { + filp->f_pos += 1024-offset; + continue; + } + de = (struct minix_dir_entry *) (offset + bh->b_data); + while (offset < 1024 && filp->f_pos < inode->i_size) { + offset += sizeof (struct minix_dir_entry); + filp->f_pos += sizeof (struct minix_dir_entry); + if (de->inode) { + for (i = 0; i < MINIX_NAME_LEN; i++) + if (c = de->name[i]) + put_fs_byte(c,i+dirent->d_name); + else + break; + if (i) { + put_fs_long(de->inode,&dirent->d_ino); + put_fs_byte(0,i+dirent->d_name); + put_fs_word(i,&dirent->d_reclen); + brelse(bh); + return i; + } + } + de++; + } + brelse(bh); + } + return 0; +} diff --git a/fs/minix/fifo.c b/fs/minix/fifo.c new file mode 100644 index 000000000000..671604bad5b6 --- /dev/null +++ b/fs/minix/fifo.c @@ -0,0 +1,27 @@ +/* + * linux/fs/fifo.c + * + * written by Paul H. Hargrove + */ + +#include +#include + +extern struct file_operations def_fifo_fops; + +struct inode_operations minix_fifo_inode_operations = { + &def_fifo_fops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL /* truncate */ +}; diff --git a/fs/minix/file.c b/fs/minix/file.c new file mode 100644 index 000000000000..eb8c1b06952d --- /dev/null +++ b/fs/minix/file.c @@ -0,0 +1,220 @@ +/* + * linux/fs/minix/file.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * minix regular file handling primitives + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NBUF 16 + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +#include +#include + +int minix_file_read(struct inode *, struct file *, char *, int); +static int minix_file_write(struct inode *, struct file *, char *, int); + +/* + * We have mostly NULL's here: the current defaults are ok for + * the minix filesystem. + */ +static struct file_operations minix_file_operations = { + NULL, /* lseek - default */ + minix_file_read, /* read */ + minix_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* no special open is needed */ + NULL /* release */ +}; + +struct inode_operations minix_file_inode_operations = { + &minix_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + minix_bmap, /* bmap */ + minix_truncate /* truncate */ +}; + +static inline void wait_on_buffer(struct buffer_head * bh) +{ + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + sti(); +} + +/* + * 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) +{ + int read,left,chars,nr; + int block, blocks, offset; + struct buffer_head ** bhb, ** bhe; + struct buffer_head * buflist[NBUF]; + + if (!inode) { + printk("minix_file_read: inode = NULL\n"); + return -EINVAL; + } + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { + printk("minix_file_read: mode = %07o\n",inode->i_mode); + return -EINVAL; + } + if (filp->f_pos > inode->i_size) + left = 0; + else + left = inode->i_size - filp->f_pos; + if (left > count) + left = count; + if (left <= 0) + return 0; + read = 0; + block = filp->f_pos >> BLOCK_SIZE_BITS; + offset = filp->f_pos & (BLOCK_SIZE-1); + blocks = (left + offset + BLOCK_SIZE - 1) / BLOCK_SIZE; + bhb = bhe = buflist; + do { + if (blocks) { + --blocks; + if (nr = minix_bmap(inode,block++)) { + *bhb = getblk(inode->i_dev,nr,BLOCK_SIZE); + if (!(*bhb)->b_uptodate) + ll_rw_block(READ,*bhb); + } else + *bhb = NULL; + + if (++bhb == &buflist[NBUF]) + bhb = buflist; + + if (bhb != bhe) + continue; + } + if (*bhe) { + wait_on_buffer(*bhe); + if (!(*bhe)->b_uptodate) { + do { + brelse(*bhe); + if (++bhe == &buflist[NBUF]) + bhe = buflist; + } while (bhe != bhb); + break; + } + } + + if (left < BLOCK_SIZE - offset) + chars = left; + else + chars = BLOCK_SIZE - offset; + filp->f_pos += chars; + left -= chars; + read += chars; + if (*bhe) { + memcpy_tofs(buf,offset+(*bhe)->b_data,chars); + brelse(*bhe); + buf += chars; + } else { + while (chars-->0) + put_fs_byte(0,buf++); + } + offset = 0; + if (++bhe == &buflist[NBUF]) + bhe = buflist; + } while (left > 0); + if (!read) + return -EIO; + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + return read; +} + +static int minix_file_write(struct inode * inode, struct file * filp, char * buf, int count) +{ + off_t pos; + int written,block,c; + struct buffer_head * bh; + char * p; + + if (!inode) { + printk("minix_file_write: inode = NULL\n"); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("minix_file_write: mode = %07o\n",inode->i_mode); + return -EINVAL; + } +/* + * ok, append may not work when many processes are writing at the same time + * but so what. That way leads to madness anyway. + */ + if (filp->f_flags & O_APPEND) + pos = inode->i_size; + else + pos = filp->f_pos; + written = 0; + while (written count-written) + c = count-written; + if (c == BLOCK_SIZE) + bh = getblk(inode->i_dev, block, BLOCK_SIZE); + else + bh = bread(inode->i_dev,block, BLOCK_SIZE); + if (!bh) { + if (!written) + written = -EIO; + break; + } + p = (pos % BLOCK_SIZE) + bh->b_data; + pos += c; + if (pos > inode->i_size) { + inode->i_size = pos; + inode->i_dirt = 1; + } + written += c; + memcpy_fromfs(p,buf,c); + buf += c; + bh->b_uptodate = 1; + bh->b_dirt = 1; + brelse(bh); + } + inode->i_mtime = CURRENT_TIME; + inode->i_ctime = CURRENT_TIME; + filp->f_pos = pos; + inode->i_dirt = 1; + return written; +} diff --git a/fs/minix/file_dev.c b/fs/minix/file_dev.c deleted file mode 100644 index 2ae070554ac8..000000000000 --- a/fs/minix/file_dev.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * linux/fs/file_dev.c - * - * (C) 1991 Linus Torvalds - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - -int minix_readdir(struct inode * inode, struct file * filp, struct dirent * dirent, int count) -{ - unsigned int block,offset,i; - char c; - struct buffer_head * bh; - struct minix_dir_entry * de; - - if (!inode || !S_ISDIR(inode->i_mode)) - return -EBADF; - if (filp->f_pos & (sizeof (struct minix_dir_entry) - 1)) - return -EBADF; - while (filp->f_pos < inode->i_size) { - offset = filp->f_pos & 1023; - block = minix_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS); - if (!block || !(bh = bread(inode->i_dev,block))) { - filp->f_pos += 1024-offset; - continue; - } - de = (struct minix_dir_entry *) (offset + bh->b_data); - while (offset < 1024 && filp->f_pos < inode->i_size) { - offset += sizeof (struct minix_dir_entry); - filp->f_pos += sizeof (struct minix_dir_entry); - if (de->inode) { - for (i = 0; i < MINIX_NAME_LEN; i++) - if (c = de->name[i]) - put_fs_byte(c,i+dirent->d_name); - else - break; - if (i) { - put_fs_long(de->inode,&dirent->d_ino); - put_fs_byte(0,i+dirent->d_name); - put_fs_word(i,&dirent->d_reclen); - brelse(bh); - return i; - } - } - de++; - } - brelse(bh); - } - return 0; -} - -int minix_file_read(struct inode * inode, struct file * filp, char * buf, int count) -{ - int read,left,chars,nr; - struct buffer_head * bh; - - if (!inode) { - printk("minix_file_read: inode = NULL\n"); - return -EINVAL; - } - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { - printk("minix_file_read: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - if (filp->f_pos > inode->i_size) - left = 0; - else - left = inode->i_size - filp->f_pos; - if (left > count) - left = count; - read = 0; - while (left > 0) { - if (nr = minix_bmap(inode,(filp->f_pos)>>BLOCK_SIZE_BITS)) { - if (!(bh=bread(inode->i_dev,nr))) - return read?read:-EIO; - } else - bh = NULL; - nr = filp->f_pos & (BLOCK_SIZE-1); - chars = MIN( BLOCK_SIZE-nr , left ); - filp->f_pos += chars; - left -= chars; - read += chars; - if (bh) { - memcpy_tofs(buf,nr+bh->b_data,chars); - buf += chars; - brelse(bh); - } else { - while (chars-->0) - put_fs_byte(0,buf++); - } - } - inode->i_atime = CURRENT_TIME; - return read; -} - -int minix_file_write(struct inode * inode, struct file * filp, char * buf, int count) -{ - off_t pos; - int written,block,c; - struct buffer_head * bh; - char * p; - - if (!inode) { - printk("minix_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("minix_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } -/* - * ok, append may not work when many processes are writing at the same time - * but so what. That way leads to madness anyway. - */ - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = filp->f_pos; - written = 0; - while (written count-written) - c = count-written; - if (c == BLOCK_SIZE) - bh = getblk(inode->i_dev, block); - else - bh = bread(inode->i_dev,block); - if (!bh) { - if (!written) - written = -EIO; - break; - } - p = (pos % BLOCK_SIZE) + bh->b_data; - pos += c; - if (pos > inode->i_size) { - inode->i_size = pos; - inode->i_dirt = 1; - } - written += c; - memcpy_fromfs(p,buf,c); - buf += c; - bh->b_uptodate = 1; - bh->b_dirt = 1; - brelse(bh); - } - inode->i_mtime = CURRENT_TIME; - if (!(filp->f_flags & O_APPEND)) { - filp->f_pos = pos; - inode->i_ctime = CURRENT_TIME; - } - inode->i_dirt = 1; - return written; -} diff --git a/fs/minix/inode.c b/fs/minix/inode.c index c635e6e85b97..b90a4e8d7d88 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -1,20 +1,28 @@ /* * linux/fs/minix/inode.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include - #include #include #include #include +#include +#include + #include +#include int sync_dev(int dev); +void minix_put_inode(struct inode *inode) +{ + inode->i_size = 0; + minix_truncate(inode); + minix_free_inode(inode); +} + void minix_put_super(struct super_block *sb) { int i; @@ -22,32 +30,45 @@ void minix_put_super(struct super_block *sb) lock_super(sb); sb->s_dev = 0; for(i = 0 ; i < MINIX_I_MAP_SLOTS ; i++) - brelse(sb->s_imap[i]); + brelse(sb->u.minix_sb.s_imap[i]); for(i = 0 ; i < MINIX_Z_MAP_SLOTS ; i++) - brelse(sb->s_zmap[i]); + brelse(sb->u.minix_sb.s_zmap[i]); free_super(sb); return; } static struct super_operations minix_sops = { minix_read_inode, - minix_put_super + minix_write_inode, + minix_put_inode, + minix_put_super, + NULL, + minix_statfs }; struct super_block *minix_read_super(struct super_block *s,void *data) { struct buffer_head *bh; + struct minix_super_block *ms; int i,dev=s->s_dev,block; lock_super(s); - if (!(bh = bread(dev,1))) { + if (!(bh = bread(dev,1,BLOCK_SIZE))) { s->s_dev=0; free_super(s); printk("bread failed\n"); return NULL; } - *((struct minix_super_block *) s) = - *((struct minix_super_block *) bh->b_data); + ms = (struct minix_super_block *) bh->b_data; + s->s_blocksize = 1024; + s->u.minix_sb.s_ninodes = ms->s_ninodes; + s->u.minix_sb.s_nzones = ms->s_nzones; + s->u.minix_sb.s_imap_blocks = ms->s_imap_blocks; + s->u.minix_sb.s_zmap_blocks = ms->s_zmap_blocks; + s->u.minix_sb.s_firstdatazone = ms->s_firstdatazone; + s->u.minix_sb.s_log_zone_size = ms->s_log_zone_size; + s->u.minix_sb.s_max_size = ms->s_max_size; + s->s_magic = ms->s_magic; brelse(bh); if (s->s_magic != MINIX_SUPER_MAGIC) { s->s_dev = 0; @@ -56,32 +77,32 @@ struct super_block *minix_read_super(struct super_block *s,void *data) return NULL; } for (i=0;i < MINIX_I_MAP_SLOTS;i++) - s->s_imap[i] = NULL; + s->u.minix_sb.s_imap[i] = NULL; for (i=0;i < MINIX_Z_MAP_SLOTS;i++) - s->s_zmap[i] = NULL; + s->u.minix_sb.s_zmap[i] = NULL; block=2; - for (i=0 ; i < s->s_imap_blocks ; i++) - if (s->s_imap[i]=bread(dev,block)) + for (i=0 ; i < s->u.minix_sb.s_imap_blocks ; i++) + if (s->u.minix_sb.s_imap[i]=bread(dev,block,BLOCK_SIZE)) block++; else break; - for (i=0 ; i < s->s_zmap_blocks ; i++) - if (s->s_zmap[i]=bread(dev,block)) + for (i=0 ; i < s->u.minix_sb.s_zmap_blocks ; i++) + if (s->u.minix_sb.s_zmap[i]=bread(dev,block,BLOCK_SIZE)) block++; else break; - if (block != 2+s->s_imap_blocks+s->s_zmap_blocks) { + if (block != 2+s->u.minix_sb.s_imap_blocks+s->u.minix_sb.s_zmap_blocks) { for(i=0;is_imap[i]); + brelse(s->u.minix_sb.s_imap[i]); for(i=0;is_zmap[i]); + brelse(s->u.minix_sb.s_zmap[i]); s->s_dev=0; free_super(s); printk("block failed\n"); return NULL; } - s->s_imap[0]->b_data[0] |= 1; - s->s_zmap[0]->b_data[0] |= 1; + s->u.minix_sb.s_imap[0]->b_data[0] |= 1; + s->u.minix_sb.s_zmap[0]->b_data[0] |= 1; free_super(s); /* set up enough so that it can read an inode */ s->s_dev = dev; @@ -94,6 +115,21 @@ struct super_block *minix_read_super(struct super_block *s,void *data) return s; } +void minix_statfs (struct super_block *sb, struct statfs *buf) +{ + long tmp; + + put_fs_long(MINIX_SUPER_MAGIC, &buf->f_type); + put_fs_long(1024, &buf->f_bsize); + put_fs_long(sb->u.minix_sb.s_nzones << sb->u.minix_sb.s_log_zone_size, &buf->f_blocks); + tmp = minix_count_free_blocks(sb); + put_fs_long(tmp, &buf->f_bfree); + put_fs_long(tmp, &buf->f_bavail); + put_fs_long(sb->u.minix_sb.s_ninodes, &buf->f_files); + put_fs_long(minix_count_free_inodes(sb), &buf->f_ffree); + /* Don't know what value to put in buf->f_fsid */ +} + static int _minix_bmap(struct inode * inode,int block,int create) { struct buffer_head * bh; @@ -124,7 +160,7 @@ static int _minix_bmap(struct inode * inode,int block,int create) } if (!inode->i_data[7]) return 0; - if (!(bh = bread(inode->i_dev,inode->i_data[7]))) + if (!(bh = bread(inode->i_dev,inode->i_data[7],BLOCK_SIZE))) return 0; i = ((unsigned short *) (bh->b_data))[block]; if (create && !i) @@ -143,7 +179,7 @@ static int _minix_bmap(struct inode * inode,int block,int create) } if (!inode->i_data[8]) return 0; - if (!(bh=bread(inode->i_dev,inode->i_data[8]))) + if (!(bh=bread(inode->i_dev,inode->i_data[8], BLOCK_SIZE))) return 0; i = ((unsigned short *)bh->b_data)[block>>9]; if (create && !i) @@ -154,7 +190,7 @@ static int _minix_bmap(struct inode * inode,int block,int create) brelse(bh); if (!i) return 0; - if (!(bh=bread(inode->i_dev,i))) + if (!(bh=bread(inode->i_dev,i,BLOCK_SIZE))) return 0; i = ((unsigned short *)bh->b_data)[block&511]; if (create && !i) @@ -182,9 +218,9 @@ void minix_read_inode(struct inode * inode) struct minix_inode * raw_inode; int block; - block = 2 + inode->i_sb->s_imap_blocks + inode->i_sb->s_zmap_blocks + + block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + (inode->i_ino-1)/MINIX_INODES_PER_BLOCK; - if (!(bh=bread(inode->i_dev,block))) + if (!(bh=bread(inode->i_dev,block, BLOCK_SIZE))) panic("unable to read i-node block"); raw_inode = ((struct minix_inode *) bh->b_data) + (inode->i_ino-1)%MINIX_INODES_PER_BLOCK; @@ -199,7 +235,24 @@ void minix_read_inode(struct inode * inode) else for (block = 0; block < 9; block++) inode->i_data[block] = raw_inode->i_zone[block]; brelse(bh); - inode->i_op = &minix_inode_operations; + inode->i_op = NULL; + if (S_ISREG(inode->i_mode)) + inode->i_op = &minix_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &minix_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &minix_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) + inode->i_op = &minix_chrdev_inode_operations; + else if (S_ISBLK(inode->i_mode)) + inode->i_op = &minix_blkdev_inode_operations; + else if (S_ISFIFO(inode->i_mode)) { + inode->i_op = &minix_fifo_inode_operations; + inode->i_size = 0; + inode->i_pipe = 1; + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + } } void minix_write_inode(struct inode * inode) @@ -208,9 +261,9 @@ void minix_write_inode(struct inode * inode) struct minix_inode * raw_inode; int block; - block = 2 + inode->i_sb->s_imap_blocks + inode->i_sb->s_zmap_blocks + + block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + (inode->i_ino-1)/MINIX_INODES_PER_BLOCK; - if (!(bh=bread(inode->i_dev,block))) + if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) panic("unable to read i-node block"); raw_inode = ((struct minix_inode *)bh->b_data) + (inode->i_ino-1)%MINIX_INODES_PER_BLOCK; diff --git a/fs/minix/minix_op.c b/fs/minix/minix_op.c deleted file mode 100644 index a588f095ba8f..000000000000 --- a/fs/minix/minix_op.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * linux/fs/minix/minix_op.c - * - * structures for the minix super_block/inode/file-operations - */ - -#include -#include - -void minix_put_inode(struct inode *inode) -{ - inode->i_size = 0; - minix_truncate(inode); - minix_free_inode(inode); -} - -/* - * These are the low-level inode operations for minix filesystem inodes. - */ -struct inode_operations minix_inode_operations = { - minix_create, - minix_lookup, - minix_link, - minix_unlink, - minix_symlink, - minix_mkdir, - minix_rmdir, - minix_mknod, - minix_rename, - minix_readlink, - minix_open, - minix_release, - minix_follow_link, - minix_bmap, - minix_truncate, - minix_write_inode, - minix_put_inode -}; - -/* - * We have mostly NULL's here: the current defaults are ok for - * the minix filesystem. - */ -struct file_operations minix_file_operations = { - NULL, /* lseek - default */ - minix_file_read, /* read */ - minix_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* close - default */ - NULL, /* select - default */ - NULL /* ioctl - default */ -}; - -struct file_operations minix_dir_operations = { - NULL, /* lseek - default */ - minix_file_read, /* read */ - NULL, /* write - bad */ - minix_readdir, /* readdir */ - NULL, /* close - default */ - NULL, /* select - default */ - NULL /* ioctl - default */ -}; diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 912b88319497..1a51c87f1030 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -1,19 +1,20 @@ /* * linux/fs/minix/namei.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include #include #include +#include +#include +#include +#include + #include -#include -#include -#include #include -#include /* * comment out this line if you want names > MINIX_NAME_LEN chars to be @@ -77,7 +78,7 @@ static struct buffer_head * minix_find_entry(struct inode * dir, entries = dir->i_size / (sizeof (struct minix_dir_entry)); if (!(block = dir->i_data[0])) return NULL; - if (!(bh = bread(dir->i_dev,block))) + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) return NULL; i = 0; de = (struct minix_dir_entry *) bh->b_data; @@ -86,7 +87,7 @@ static struct buffer_head * minix_find_entry(struct inode * dir, brelse(bh); bh = NULL; if (!(block = minix_bmap(dir,i/MINIX_DIR_ENTRIES_PER_BLOCK)) || - !(bh = bread(dir->i_dev,block))) { + !(bh = bread(dir->i_dev, block, BLOCK_SIZE))) { i += MINIX_DIR_ENTRIES_PER_BLOCK; continue; } @@ -103,40 +104,6 @@ static struct buffer_head * minix_find_entry(struct inode * dir, return NULL; } -struct inode * minix_follow_link(struct inode * dir, struct inode * inode) -{ - unsigned short fs; - struct buffer_head * bh; - - if (!dir) { - dir = current->root; - dir->i_count++; - } - if (!inode) { - iput(dir); - return NULL; - } - if (!S_ISLNK(inode->i_mode)) { - iput(dir); - return inode; - } - __asm__("mov %%fs,%0":"=r" (fs)); - if ((current->link_count > 5) || !inode->i_data[0] || - !(bh = bread(inode->i_dev, inode->i_data[0]))) { - iput(dir); - iput(inode); - return NULL; - } - iput(inode); - __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); - current->link_count++; - inode = _namei(bh->b_data,dir,1); - current->link_count--; - __asm__("mov %0,%%fs"::"r" (fs)); - brelse(bh); - return inode; -} - int minix_lookup(struct inode * dir,const char * name, int len, struct inode ** result) { @@ -196,7 +163,7 @@ static struct buffer_head * minix_add_entry(struct inode * dir, return NULL; if (!(block = dir->i_data[0])) return NULL; - if (!(bh = bread(dir->i_dev,block))) + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) return NULL; i = 0; de = (struct minix_dir_entry *) bh->b_data; @@ -207,7 +174,7 @@ static struct buffer_head * minix_add_entry(struct inode * dir, block = minix_create_block(dir,i/MINIX_DIR_ENTRIES_PER_BLOCK); if (!block) return NULL; - if (!(bh = bread(dir->i_dev,block))) { + if (!(bh = bread(dir->i_dev, block, BLOCK_SIZE))) { i += MINIX_DIR_ENTRIES_PER_BLOCK; continue; } @@ -249,11 +216,13 @@ int minix_create(struct inode * dir,const char * name, int len, int mode, iput(dir); return -ENOSPC; } + inode->i_op = &minix_file_inode_operations; inode->i_mode = mode; inode->i_dirt = 1; bh = minix_add_entry(dir,name,len,&de); if (!bh) { inode->i_nlink--; + inode->i_dirt = 1; iput(inode); iput(dir); return -ENOSPC; @@ -287,6 +256,24 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd } inode->i_uid = current->euid; inode->i_mode = mode; + inode->i_op = NULL; + if (S_ISREG(inode->i_mode)) + inode->i_op = &minix_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &minix_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &minix_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) + inode->i_op = &minix_chrdev_inode_operations; + else if (S_ISBLK(inode->i_mode)) + inode->i_op = &minix_blkdev_inode_operations; + else if (S_ISFIFO(inode->i_mode)) { + inode->i_op = &minix_fifo_inode_operations; + inode->i_size = 0; + inode->i_pipe = 1; + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + } if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev; inode->i_mtime = inode->i_atime = CURRENT_TIME; @@ -294,6 +281,7 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd bh = minix_add_entry(dir,name,len,&de); if (!bh) { inode->i_nlink--; + inode->i_dirt = 1; iput(inode); iput(dir); return -ENOSPC; @@ -323,19 +311,21 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) iput(dir); return -ENOSPC; } + inode->i_op = &minix_dir_inode_operations; inode->i_size = 2 * sizeof (struct minix_dir_entry); - inode->i_dirt = 1; inode->i_mtime = inode->i_atime = CURRENT_TIME; if (!(inode->i_data[0] = minix_new_block(inode->i_dev))) { iput(dir); inode->i_nlink--; + inode->i_dirt = 1; iput(inode); return -ENOSPC; } inode->i_dirt = 1; - if (!(dir_block = bread(inode->i_dev,inode->i_data[0]))) { + if (!(dir_block = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { iput(dir); inode->i_nlink--; + inode->i_dirt = 1; iput(inode); return -EIO; } @@ -379,7 +369,7 @@ static int empty_dir(struct inode * inode) len = inode->i_size / sizeof (struct minix_dir_entry); if (len<2 || !inode->i_data[0] || - !(bh=bread(inode->i_dev,inode->i_data[0]))) { + !(bh=bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { printk("warning - bad directory on dev %04x\n",inode->i_dev); return 0; } @@ -399,7 +389,7 @@ static int empty_dir(struct inode * inode) nr += MINIX_DIR_ENTRIES_PER_BLOCK; continue; } - if (!(bh=bread(inode->i_dev,block))) + if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) return 0; de = (struct minix_dir_entry *) bh->b_data; } @@ -517,17 +507,19 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s return -ENOSPC; } inode->i_mode = S_IFLNK | 0777; - inode->i_dirt = 1; + inode->i_op = &minix_symlink_inode_operations; if (!(inode->i_data[0] = minix_new_block(inode->i_dev))) { iput(dir); inode->i_nlink--; + inode->i_dirt = 1; iput(inode); return -ENOSPC; } inode->i_dirt = 1; - if (!(name_block = bread(inode->i_dev,inode->i_data[0]))) { + if (!(name_block = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { iput(dir); inode->i_nlink--; + inode->i_dirt = 1; iput(inode); return -EIO; } @@ -542,6 +534,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s bh = minix_find_entry(dir,name,len,&de); if (bh) { inode->i_nlink--; + inode->i_dirt = 1; iput(inode); brelse(bh); iput(dir); @@ -550,6 +543,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s bh = minix_add_entry(dir,name,len,&de); if (!bh) { inode->i_nlink--; + inode->i_dirt = 1; iput(inode); iput(dir); return -ENOSPC; @@ -667,6 +661,10 @@ start_up: old_inode = iget(old_dir->i_dev, old_de->inode); if (!old_inode) goto end_rename; + if ((old_dir->i_mode & S_ISVTX) && + current->euid != old_inode->i_uid && + current->euid != old_dir->i_uid && !suser()) + goto end_rename; new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de); if (new_bh) { new_inode = iget(new_dir->i_dev, new_de->inode); @@ -679,6 +677,10 @@ start_up: retval = 0; goto end_rename; } + if (S_ISDIR(new_inode->i_mode)) { + retval = -EEXIST; + goto end_rename; + } if (S_ISDIR(old_inode->i_mode)) { retval = -EEXIST; if (new_bh) @@ -692,7 +694,7 @@ start_up: retval = -EIO; if (!old_inode->i_data[0]) goto end_rename; - if (!(dir_bh = bread(old_inode->i_dev, old_inode->i_data[0]))) + if (!(dir_bh = bread(old_inode->i_dev, old_inode->i_data[0], BLOCK_SIZE))) goto end_rename; if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; @@ -712,8 +714,10 @@ start_up: /* ok, that's it */ old_de->inode = 0; new_de->inode = old_inode->i_ino; - if (new_inode) + if (new_inode) { new_inode->i_nlink--; + new_inode->i_dirt = 1; + } old_bh->b_dirt = 1; new_bh->b_dirt = 1; if (dir_bh) { @@ -748,7 +752,7 @@ end_rename: int minix_rename(struct inode * old_dir, const char * old_name, int old_len, struct inode * new_dir, const char * new_name, int new_len) { - static struct task_struct * wait = NULL; + static struct wait_queue * wait = NULL; static int lock = 0; int result; @@ -761,31 +765,3 @@ int minix_rename(struct inode * old_dir, const char * old_name, int old_len, wake_up(&wait); return result; } - -int minix_readlink(struct inode * inode, char * buffer, int buflen) -{ - struct buffer_head * bh; - int i; - char c; - - if (!S_ISLNK(inode->i_mode)) { - iput(inode); - return -EINVAL; - } - if (buflen > 1023) - buflen = 1023; - if (inode->i_data[0]) - bh = bread(inode->i_dev, inode->i_data[0]); - else - bh = NULL; - iput(inode); - if (!bh) - return 0; - i = 0; - while (ib_data[i])) { - i++; - put_fs_byte(c,buffer++); - } - brelse(bh); - return i; -} diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c new file mode 100644 index 000000000000..65263a0e8cc8 --- /dev/null +++ b/fs/minix/symlink.c @@ -0,0 +1,100 @@ +/* + * linux/fs/minix/symlink.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * minix symlink handling code + */ + +#include + +#include +#include +#include +#include +#include + +static int minix_readlink(struct inode *, char *, int); +static struct inode * minix_follow_link(struct inode *, struct inode *); + +/* + * symlinks can't do much... + */ +struct inode_operations minix_symlink_inode_operations = { + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + minix_readlink, /* readlink */ + minix_follow_link, /* follow_link */ + NULL, /* bmap */ + NULL /* truncate */ +}; + +static struct inode * minix_follow_link(struct inode * dir, struct inode * inode) +{ + unsigned short fs; + struct buffer_head * bh; + + if (!dir) { + dir = current->root; + dir->i_count++; + } + if (!inode) { + iput(dir); + return NULL; + } + if (!S_ISLNK(inode->i_mode)) { + iput(dir); + return inode; + } + __asm__("mov %%fs,%0":"=r" (fs)); + if ((current->link_count > 5) || !inode->i_data[0] || + !(bh = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE))) { + iput(dir); + iput(inode); + return NULL; + } + iput(inode); + __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10)); + current->link_count++; + inode = _namei(bh->b_data,dir,1); + current->link_count--; + __asm__("mov %0,%%fs"::"r" (fs)); + brelse(bh); + return inode; +} + +static int minix_readlink(struct inode * inode, char * buffer, int buflen) +{ + struct buffer_head * bh; + int i; + char c; + + if (!S_ISLNK(inode->i_mode)) { + iput(inode); + return -EINVAL; + } + if (buflen > 1023) + buflen = 1023; + if (inode->i_data[0]) + bh = bread(inode->i_dev, inode->i_data[0], BLOCK_SIZE); + else + bh = NULL; + iput(inode); + if (!bh) + return 0; + i = 0; + while (ib_data[i])) { + i++; + put_fs_byte(c,buffer++); + } + brelse(bh); + return i; +} diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index 7df5268aa824..767dd3d08a01 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -1,16 +1,15 @@ /* * linux/fs/truncate.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ +#include #include #include #include - -#include -#include -#include +#include +#include /* * Truncate has the most races in the whole filesystem: coding it is @@ -53,7 +52,7 @@ static int trunc_indirect(struct inode * inode, int offset, unsigned short * p) #define INDIRECT_BLOCK (DIRECT_BLOCK-offset) if (*p) - bh = bread(inode->i_dev,*p); + bh = bread(inode->i_dev, *p, BLOCK_SIZE); if (!bh) return 0; repeat: @@ -91,7 +90,7 @@ static int trunc_dindirect(struct inode * inode) #define DINDIRECT_BLOCK ((DIRECT_BLOCK-(512+7))>>9) if (inode->i_data[8]) - bh = bread(inode->i_dev,inode->i_data[8]); + bh = bread(inode->i_dev, inode->i_data[8], BLOCK_SIZE); if (!bh) return 0; repeat: @@ -149,50 +148,3 @@ void minix_release(struct inode * inode, struct file * filp) { printk("minix_release not implemented\n"); } - -static int check_char_dev(struct inode * inode, struct file * filp) -{ - struct tty_struct *tty; - int min, dev; - - dev = inode->i_rdev; - if (MAJOR(dev) == 4 || MAJOR(dev) == 5) { - if (MAJOR(dev) == 5) - min = current->tty; - else - min = MINOR(dev); - if (min < 0) - return -1; - if ((IS_A_PTY_MASTER(min)) && (inode->i_count>1)) - return -1; - tty = TTY_TABLE(min); - if (!(filp->f_flags & O_NOCTTY) && - current->leader && - current->tty<0 && - tty->session==0) { - current->tty = min; - tty->session= current->session; - tty->pgrp = current->pgrp; - } - if (IS_A_SERIAL(min)) - serial_open(min-64); - } - return 0; -} - -/* - * Called every time a minix-file is opened - */ -int minix_open(struct inode * inode, struct file * filp) -{ - if (S_ISCHR(inode->i_mode)) { - if (check_char_dev(inode,filp)) - return -EAGAIN; - } else if (S_ISBLK(inode->i_mode)) - check_disk_change(inode->i_rdev); - else if (S_ISREG(inode->i_mode)) - filp->f_op = &minix_file_operations; - else if (S_ISDIR(inode->i_mode)) - filp->f_op = &minix_dir_operations; - return 0; -} diff --git a/fs/msdos/Makefile b/fs/msdos/Makefile new file mode 100644 index 000000000000..485a62b063c7 --- /dev/null +++ b/fs/msdos/Makefile @@ -0,0 +1,81 @@ +# +# Makefile for the linux MS-DOS-filesystem routines. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) \ + -S -o $*.s $< +.c.o: + $(CC) $(CFLAGS) -c -o $*.o $< +.s.o: + $(AS) -o $*.o $< + +OBJS= namei.o inode.o file.o dir.o misc.o fat.o + +msdos.o: $(OBJS) + $(LD) -r -o msdos.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make + cp tmp_make Makefile + +### Dependencies: +dir.o : dir.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/stat.h +fat.o : fat.c /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/stat.h +file.o : file.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/stat.h +inode.o : inode.c /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/stat.h \ + /usr/src/linux/include/asm/segment.h +misc.o : misc.c /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/stat.h +namei.o : namei.c /usr/src/linux/include/asm/segment.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/msdos_fs.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/stat.h diff --git a/fs/msdos/dir.c b/fs/msdos/dir.c new file mode 100644 index 000000000000..e3caef26b927 --- /dev/null +++ b/fs/msdos/dir.c @@ -0,0 +1,127 @@ +/* + * linux/fs/msdos/dir.c + * + * Written 1992 by Werner Almesberger + * + * MS-DOS directory handling functions + */ + +#include + +#include +#include +#include +#include +#include + +static int msdos_dummy_read(struct inode *inode,struct file *filp,char *buf, + int count); +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 */ + NULL, /* write - bad */ + msdos_readdir, /* readdir */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* no special open code */ + NULL /* no special release code */ +}; + +struct inode_operations msdos_dir_inode_operations = { + &msdos_dir_operations, /* default directory file-ops */ + msdos_create, /* create */ + msdos_lookup, /* lookup */ + NULL, /* link */ + msdos_unlink, /* unlink */ + NULL, /* symlink */ + msdos_mkdir, /* mkdir */ + msdos_rmdir, /* rmdir */ + NULL, /* mknod */ + msdos_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + msdos_bmap, /* bmap */ + 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\r\n"); + last_warning = CURRENT_TIME; + } + return 0; +} + + +static int msdos_readdir(struct inode *inode,struct file *filp, + struct dirent *dirent,int count) +{ + int ino,i,i2,last; + char c,*walk; + struct buffer_head *bh; + struct msdos_dir_entry *de; + + if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; + if (inode->i_ino == MSDOS_ROOT_INO) { +/* Fake . and .. for the root directory. */ + if (filp->f_pos == 2) filp->f_pos = 0; + else if (filp->f_pos < 2) { + walk = filp->f_pos++ ? ".." : "."; + for (i = 0; *walk; walk++) + put_fs_byte(*walk,dirent->d_name+i++); + put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino); + put_fs_byte(0,dirent->d_name+i); + put_fs_word(i,&dirent->d_reclen); + return i; + } + } + if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT; + bh = NULL; + while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) { + if (de->name[0] && ((unsigned char *) (de->name))[0] != + DELETED_FLAG && !(de->attr & ATTR_VOLUME)) { + for (i = last = 0; i < 8; i++) { + if (!(c = de->name[i])) break; + if (c >= 'A' && c <= 'Z') c += 32; + if (c != ' ') last = i+1; + put_fs_byte(c,i+dirent->d_name); + } + i = last; + if (de->ext[0] && de->ext[0] != ' ') { + put_fs_byte('.',i+dirent->d_name); + i++; + for (i2 = 0; i2 < 3; i2++) { + if (!(c = de->ext[i2])) break; + if (c >= 'A' && c <= 'Z') c += 32; + put_fs_byte(c,i+dirent->d_name); + i++; + if (c != ' ') last = i; + } + } + if (i = last) { + if (!strcmp(de->name,MSDOS_DOT)) + ino = inode->i_ino; + else if (!strcmp(de->name,MSDOS_DOTDOT)) + ino = msdos_parent_ino(inode,0); + put_fs_long(ino,&dirent->d_ino); + put_fs_byte(0,i+dirent->d_name); + put_fs_word(i,&dirent->d_reclen); + brelse(bh); + return i; + } + } + } + if (bh) brelse(bh); + return 0; +} diff --git a/fs/msdos/fat.c b/fs/msdos/fat.c new file mode 100644 index 000000000000..b1a120e7b1dc --- /dev/null +++ b/fs/msdos/fat.c @@ -0,0 +1,275 @@ +/* + * linux/fs/msdos/fat.c + * + * Written 1992 by Werner Almesberger + */ + +#include +#include +#include +#include + +static struct fat_cache *fat_cache,cache[FAT_CACHE]; + +/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If + new_value is != -1, that FAT entry is replaced by it. */ + +int fat_access(struct super_block *sb,int this,int new_value) +{ + struct buffer_head *bh,*bh2,*c_bh,*c_bh2; + unsigned char *p_first,*p_last; + void *data,*data2,*c_data,*c_data2; + int first,last,next,copy; + + if (MSDOS_SB(sb)->fat_bits == 16) first = last = this*2; + else { + first = this*3/2; + last = first+1; + } + if (!(bh = msdos_sread(sb->s_dev,MSDOS_SB(sb)->fat_start+(first >> + SECTOR_BITS),&data))) { + printk("bread in fat_access failed\r\n"); + return 0; + } + if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS)) { + bh2 = bh; + data2 = data; + } + else { + if (!(bh2 = msdos_sread(sb->s_dev,MSDOS_SB(sb)->fat_start+(last + >> SECTOR_BITS),&data2))) { + brelse(bh); + printk("bread in fat_access failed\r\n"); + return 0; + } + } + if (MSDOS_SB(sb)->fat_bits == 16) { + next = ((unsigned short *) data)[(first & (SECTOR_SIZE-1)) + >> 1]; + if (next >= 0xfff8) next = -1; + } + else { + p_first = &((unsigned char *) data)[first & (SECTOR_SIZE-1)]; + p_last = &((unsigned char *) data2)[(first+1) & + (SECTOR_SIZE-1)]; + if (this & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff; + else next = (*p_first+(*p_last << 8)) & 0xfff; + if (next >= 0xff8) next = -1; + } + if (new_value != -1) { + if (MSDOS_SB(sb)->fat_bits == 16) + ((unsigned short *) data)[(first & (SECTOR_SIZE-1)) >> + 1] = new_value; + else { + if (this & 1) { + *p_first = (*p_first & 0xf) | (new_value << 4); + *p_last = new_value >> 4; + } + else { + *p_first = new_value & 0xff; + *p_last = (*p_last & 0xf0) | (new_value >> 8); + } + bh2->b_dirt = 1; + } + bh->b_dirt = 1; + for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) { + if (!(c_bh = msdos_sread(sb->s_dev,MSDOS_SB(sb)-> + fat_start+(first >> SECTOR_BITS)+MSDOS_SB(sb)-> + fat_length*copy,&c_data))) break; + memcpy(c_data,data,SECTOR_SIZE); + c_bh->b_dirt = 1; + if (data != data2 || bh != bh2) { + if (!(c_bh2 = msdos_sread(sb->s_dev, + MSDOS_SB(sb)->fat_start+(first >> + SECTOR_BITS)+MSDOS_SB(sb)->fat_length*copy + +1,&c_data2))) { + brelse(c_bh); + break; + } + memcpy(c_data2,data2,SECTOR_SIZE); + brelse(c_bh2); + } + brelse(c_bh); + } + } + brelse(bh); + if (data != data2) brelse(bh2); + return next; +} + + +void cache_init(void) +{ + static int initialized = 0; + int count; + + if (initialized) return; + fat_cache = &cache[0]; + for (count = 0; count < FAT_CACHE; count++) { + cache[count].device = 0; + cache[count].next = count == FAT_CACHE-1 ? NULL : + &cache[count+1]; + } + initialized = 1; +} + + +void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu) +{ + struct fat_cache *walk; + +#ifdef DEBUG +printk("cache lookup: %d\r\n",*f_clu); +#endif + for (walk = fat_cache; walk; walk = walk->next) + if (inode->i_dev == walk->device && walk->ino == inode->i_ino && + walk->file_cluster <= cluster && walk->file_cluster > + *f_clu) { + *d_clu = walk->disk_cluster; +#ifdef DEBUG +printk("cache hit: %d (%d)\r\n",walk->file_cluster,*d_clu); +#endif + if ((*f_clu = walk->file_cluster) == cluster) return; + } +} + + +#ifdef DEBUG +static void list_cache(void) +{ + struct fat_cache *walk; + + for (walk = fat_cache; walk; walk = walk->next) { + if (walk->device) printk("(%d,%d) ",walk->file_cluster, + walk->disk_cluster); + else printk("-- "); + } + printk("\r\n"); +} +#endif + + +void cache_add(struct inode *inode,int f_clu,int d_clu) +{ + struct fat_cache *walk,*last; + +#ifdef DEBUG +printk("cache add: %d (%d)\r\n",f_clu,d_clu); +#endif + last = NULL; + for (walk = fat_cache; walk->next; walk = (last = walk)->next) + if (inode->i_dev == walk->device && walk->ino == inode->i_ino && + walk->file_cluster == f_clu) { + if (walk->disk_cluster != d_clu) + panic("FAT cache corruption"); + /* update LRU */ + if (last == NULL) return; + last->next = walk->next; + walk->next = fat_cache; + fat_cache = walk; +#ifdef DEBUG +list_cache(); +#endif + return; + } + walk->device = inode->i_dev; + walk->ino = inode->i_ino; + walk->file_cluster = f_clu; + walk->disk_cluster = d_clu; + last->next = NULL; + walk->next = fat_cache; + fat_cache = walk; +#ifdef DEBUG +list_cache(); +#endif +} + + +/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It + fixes itself after a while. */ + +void cache_inval_inode(struct inode *inode) +{ + struct fat_cache *walk; + + for (walk = fat_cache; walk; walk = walk->next) + if (walk->device == inode->i_dev && walk->ino == inode->i_ino) + walk->device = 0; +} + + +void cache_inval_dev(int device) +{ + struct fat_cache *walk; + + for (walk = fat_cache; walk; walk = walk->next) + if (walk->device == device) walk->device = 0; +} + + +int get_cluster(struct inode *inode,int cluster) +{ + int this,count; + + if (!(this = inode->i_data[D_START])) return 0; + if (!cluster) return this; + count = 0; + for (cache_lookup(inode,cluster,&count,&this); count < cluster; + count++) { + if ((this = fat_access(inode->i_sb,this,-1)) == -1) return 0; + if (!this) return 0; + } + cache_add(inode,cluster,this); + return this; +} + + +int msdos_smap(struct inode *inode,int sector) +{ + struct msdos_sb_info *sb; + int cluster,offset; + + sb = MSDOS_SB(inode->i_sb); + if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) && + !inode->i_data[D_START])) { + if (sector >= sb->dir_entries >> MSDOS_DPS_BITS) return 0; + return sector+sb->dir_start; + } + cluster = sector/sb->cluster_size; + offset = sector % sb->cluster_size; + if (!(cluster = get_cluster(inode,cluster))) return 0; + return (cluster-2)*sb->cluster_size+sb->data_start+offset; +} + + +/* Free all clusters after the skip'th cluster. Doesn't use the cache, + because this way we get an additional sanity check. */ + +int fat_free(struct inode *inode,int skip) +{ + int this,last; + + if (!(this = inode->i_data[D_START])) return 0; + last = 0; + while (skip--) { + last = this; + if ((this = fat_access(inode->i_sb,this,-1)) == -1) + return 0; + if (!this) { + printk("fat_free: skipped EOF\r\n"); + return -EIO; + } + } + if (last) + fat_access(inode->i_sb,last,MSDOS_SB(inode->i_sb)->fat_bits == + 12 ? 0xff8 : 0xfff8); + else { + inode->i_data[D_START] = 0; + inode->i_dirt = 1; + } + while (this != -1) + if (!(this = fat_access(inode->i_sb,this,0))) + panic("fat_free: deleting beyond EOF"); + cache_inval_inode(inode); + return 0; +} diff --git a/fs/msdos/file.c b/fs/msdos/file.c new file mode 100644 index 000000000000..eb48bbd1c809 --- /dev/null +++ b/fs/msdos/file.c @@ -0,0 +1,208 @@ +/* + * linux/fs/msdos/file.c + * + * Written 1992 by Werner Almesberger + * + * MS-DOS regular file handling primitives + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +static int msdos_file_read(struct inode *inode,struct file *filp,char *buf, + int count); +static int msdos_file_write(struct inode *inode,struct file *filp,char *buf, + int count); + + +static struct file_operations msdos_file_operations = { + NULL, /* lseek - default */ + msdos_file_read, /* read */ + msdos_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* select - default */ + NULL, /* ioctl - default */ + NULL, /* no special open is needed */ + NULL /* release */ +}; + +struct inode_operations msdos_file_inode_operations = { + &msdos_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + msdos_bmap, /* bmap */ + msdos_truncate /* truncate */ +}; + +/* No bmap for MS-DOS FS' that don't align data at kByte boundaries. */ + +struct inode_operations msdos_file_inode_operations_no_bmap = { + &msdos_file_operations, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + msdos_truncate /* truncate */ +}; + + +static int msdos_file_read(struct inode *inode,struct file *filp,char *buf, + int count) +{ + char *start; + int left,offset,size,sector,cnt; + char ch; + struct buffer_head *bh; + void *data; + +/* printk("msdos_file_read\r\n"); */ + if (!inode) { + printk("msdos_file_read: inode = NULL\r\n"); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("msdos_file_read: mode = %07o\n",inode->i_mode); + return -EINVAL; + } + if (filp->f_pos >= inode->i_size || count <= 0) return 0; + start = buf; + while (left = MIN(inode->i_size-filp->f_pos,count-(buf-start))) { + if (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS))) + break; + offset = filp->f_pos & (SECTOR_SIZE-1); + if (!(bh = msdos_sread(inode->i_dev,sector,&data))) break; + filp->f_pos += (size = MIN(SECTOR_SIZE-offset,left)); + if (inode->i_data[D_BINARY]) { + memcpy_tofs(buf,data+offset,size); + buf += size; + } + else for (cnt = size; cnt; cnt--) { + if ((ch = *((char *) data+offset++)) == '\r') + size--; + else { + if (ch != 26) put_fs_byte(ch,buf++); + else { + filp->f_pos = inode->i_size; + brelse(bh); + return buf-start; + } + } + } + brelse(bh); + } + if (start == buf) return -EIO; + return buf-start; +} + + +static int msdos_file_write(struct inode *inode,struct file *filp,char *buf, + int count) +{ + int sector,offset,size,left,written; + int error,carry; + char *start,*to,ch; + struct buffer_head *bh; + void *data; + + if (!inode) { + printk("msdos_file_write: inode = NULL\n"); + return -EINVAL; + } + if (!S_ISREG(inode->i_mode)) { + printk("msdos_file_write: mode = %07o\n",inode->i_mode); + return -EINVAL; + } +/* + * ok, append may not work when many processes are writing at the same time + * but so what. That way leads to madness anyway. + */ + if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size; + if (count <= 0) return 0; + error = carry = 0; + for (start = buf; count || carry; count -= size) { + while (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS))) + if ((error = msdos_add_cluster(inode)) < 0) break; + if (error) break; + offset = filp->f_pos & (SECTOR_SIZE-1); + size = MIN(SECTOR_SIZE-offset,MAX(carry,count)); + if (!(bh = msdos_sread(inode->i_dev,sector,&data))) { + error = -EIO; + break; + } + if (inode->i_data[D_BINARY]) { + memcpy_fromfs(data+(filp->f_pos & (SECTOR_SIZE-1)), + buf,written = size); + buf += size; + } + else { + written = left = SECTOR_SIZE-offset; + to = data+(filp->f_pos & (SECTOR_SIZE-1)); + if (carry) { + *to++ = '\n'; + left--; + carry = 0; + } + for (size = 0; size < count && left; size++) { + if ((ch = get_fs_byte(buf++)) == '\n') { + *to++ = '\r'; + left--; + } + if (!left) carry = 1; + else { + *to++ = ch; + left--; + } + } + written -= left; + } + filp->f_pos += written; + if (filp->f_pos > inode->i_size) { + inode->i_size = filp->f_pos; + inode->i_dirt = 1; + } + bh->b_dirt = 1; + brelse(bh); + } + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_data[D_ATTRS] |= ATTR_ARCH; + inode->i_dirt = 1; + return start == buf ? error : buf-start; +} + + +void msdos_truncate(struct inode *inode) +{ + int cluster; + + cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size; + (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster); + inode->i_data[D_ATTRS] |= ATTR_ARCH; + inode->i_dirt = 1; +} diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c new file mode 100644 index 000000000000..451cb374e0d4 --- /dev/null +++ b/fs/msdos/inode.c @@ -0,0 +1,276 @@ +/* + * linux/fs/msdos/inode.c + * + * Written 1992 by Werner Almesberger + */ + +#include +#include +#include +#include +#include +#include + +#include + +void msdos_put_inode(struct inode *inode) +{ + struct inode *depend; + + inode->i_size = 0; + msdos_truncate(inode); + depend = (struct inode *) inode->i_data[D_DEPEND]; + memset(inode,0,sizeof(struct inode)); + if (depend) { + if ((struct inode *) depend->i_data[D_OLD] != inode) { + printk("Invalid link (0x%X): expected 0x%X, got " + "0x%X\r\n",(int) depend,(int) inode, + depend->i_data[D_OLD]); + panic("That's fatal"); + } + depend->i_data[D_OLD] = 0; + iput(depend); + } +} + + +void msdos_put_super(struct super_block *sb) +{ + cache_inval_dev(sb->s_dev); + lock_super(sb); + sb->s_dev = 0; + free_super(sb); + return; +} + + +static struct super_operations msdos_sops = { + msdos_read_inode, + msdos_write_inode, + msdos_put_inode, + msdos_put_super, + NULL, /* added in 0.96c */ + msdos_statfs +}; + + +static int parse_options(char *options,char *check,char *conversion) +{ + char *this,*value; + + *check = 'n'; + *conversion = 'b'; + if (!options) return 1; + for (this = strtok(options,","); this; this = strtok(NULL,",")) { + if (value = strchr(this,'=')) *value++ = 0; + if (!strcmp(this,"check") && value) { + if (value[0] && !value[1] && strchr("rns",*value)) + *check = *value; + else if (!strcmp(value,"relaxed")) *check = 'r'; + else if (!strcmp(value,"normal")) *check = 'n'; + else if (!strcmp(value,"strict")) *check = 's'; + else return 0; + } + else if (!strcmp(this,"conv") && value) { + if (value[0] && !value[1] && strchr("bta",*value)) + *conversion = *value; + else if (!strcmp(value,"binary")) *conversion = 'b'; + else if (!strcmp(value,"text")) *conversion = 't'; + else if (!strcmp(value,"auto")) *conversion = 'a'; + else return 0; + } + else return 0; + } + return 1; +} + + +/* Read the super block of an MS-DOS FS. */ + +struct super_block *msdos_read_super(struct super_block *s,void *data) +{ + struct buffer_head *bh; + struct msdos_boot_sector *b; + int data_sectors; + char check,conversion; + + if (!parse_options((char *) data,&check,&conversion)) { + s->s_dev = 0; + return NULL; + } + cache_init(); + lock_super(s); + bh = bread(s->s_dev, 0, BLOCK_SIZE); + free_super(s); + if (bh == NULL) { + s->s_dev = 0; + printk("MSDOS bread failed\r\n"); + return NULL; + } + b = (struct msdos_boot_sector *) bh->b_data; + s->s_blocksize = 1024; /* we cannot handle anything else yet */ + MSDOS_SB(s)->cluster_size = b->cluster_size; + MSDOS_SB(s)->fats = b->fats; + MSDOS_SB(s)->fat_start = b->reserved; + MSDOS_SB(s)->fat_length = b->fat_length; + MSDOS_SB(s)->dir_start = b->reserved+b->fats*b->fat_length; + MSDOS_SB(s)->dir_entries = *((unsigned short *) &b->dir_entries); + MSDOS_SB(s)->data_start = MSDOS_SB(s)->dir_start+((MSDOS_SB(s)-> + dir_entries << 5) >> 9); + data_sectors = (*((unsigned short *) &b->sectors) ? *((unsigned short *) + &b->sectors) : b->total_sect)-MSDOS_SB(s)->data_start; + MSDOS_SB(s)->clusters = b->cluster_size ? data_sectors/b->cluster_size : + 0; + MSDOS_SB(s)->fat_bits = MSDOS_SB(s)->clusters > MSDOS_FAT12 ? 16 : 12; + brelse(bh); +printk("[MS-DOS FS Rel. alpha.6, FAT %d, check=%c, conv=%c]\r\n", + MSDOS_SB(s)->fat_bits,check,conversion); +printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,se=%d,ts=%d]\r\n", + b->media,MSDOS_SB(s)->cluster_size,MSDOS_SB(s)->fats,MSDOS_SB(s)->fat_start, + MSDOS_SB(s)->fat_length,MSDOS_SB(s)->dir_start,MSDOS_SB(s)->dir_entries, + MSDOS_SB(s)->data_start,*(unsigned short *) &b->sectors,b->total_sect); + if (!MSDOS_SB(s)->fats || (MSDOS_SB(s)->dir_entries & (MSDOS_DPS-1)) + || !b->cluster_size || MSDOS_SB(s)->clusters+2 > MSDOS_SB(s)-> + fat_length*SECTOR_SIZE*8/MSDOS_SB(s)->fat_bits) { + s->s_dev = 0; + printk("Unsupported FS parameters\r\n"); + return NULL; + } + if (!MSDOS_CAN_BMAP(MSDOS_SB(s))) printk("No bmap support\r\n"); + s->s_magic = MSDOS_SUPER_MAGIC; + MSDOS_SB(s)->name_check = check; + MSDOS_SB(s)->conversion = conversion; + /* set up enough so that it can read an inode */ + s->s_op = &msdos_sops; + MSDOS_SB(s)->fs_uid = current->uid; + MSDOS_SB(s)->fs_gid = current->gid; + MSDOS_SB(s)->fs_umask = current->umask; + if (!(s->s_mounted = iget(s->s_dev,MSDOS_ROOT_INO))) { + s->s_dev = 0; + printk("get root inode failed\n"); + return NULL; + } + return s; +} + + +void msdos_statfs(struct super_block *sb,struct statfs *buf) +{ + int cluster_size,free,this; + + cluster_size = MSDOS_SB(sb)->cluster_size; + put_fs_long(sb->s_magic,&buf->f_type); + put_fs_long(SECTOR_SIZE,&buf->f_bsize); + put_fs_long(MSDOS_SB(sb)->clusters*cluster_size,&buf->f_blocks); + free = 0; + for (this = 2; this < MSDOS_SB(sb)->clusters+2; this++) + if (!fat_access(sb,this,-1)) free++; + free *= cluster_size; + put_fs_long(free,&buf->f_bfree); + put_fs_long(free,&buf->f_bavail); + put_fs_long(0,&buf->f_files); + put_fs_long(0,&buf->f_ffree); +} + + +int msdos_bmap(struct inode *inode,int block) +{ + struct msdos_sb_info *sb; + int cluster,offset; + + sb = MSDOS_SB(inode->i_sb); + if ((sb->cluster_size & 1) || (sb->data_start & 1)) return 0; + if (inode->i_ino == MSDOS_ROOT_INO) { + if (sb->dir_start & 1) return 0; + return (sb->dir_start >> 1)+block; + } + cluster = (block*2)/sb->cluster_size; + offset = (block*2) % sb->cluster_size; + if (!(cluster = get_cluster(inode,cluster))) return 0; + return ((cluster-2)*sb->cluster_size+sb->data_start+offset) >> 1; +} + + +void msdos_read_inode(struct inode *inode) +{ + struct buffer_head *bh; + struct msdos_dir_entry *raw_entry; + int this; + +/* printk("read inode %d\r\n",inode->i_ino); */ + inode->i_data[D_BUSY] = inode->i_data[D_DEPEND] = + inode->i_data[D_OLD] = 0; + inode->i_data[D_BINARY] = 1; + inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid; + inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid; + if (inode->i_ino == MSDOS_ROOT_INO) { + inode->i_mode = (0777 & ~MSDOS_SB(inode->i_sb)->fs_umask) | + S_IFDIR; + inode->i_op = &msdos_dir_inode_operations; + inode->i_nlink = 1; + inode->i_size = MSDOS_SB(inode->i_sb)->dir_entries* + sizeof(struct msdos_dir_entry); + inode->i_data[D_START] = 0; + inode->i_data[D_ATTRS] = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = 0; + return; + } + if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS, BLOCK_SIZE))) + panic("unable to read i-node block"); + raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) + [inode->i_ino & (MSDOS_DPB-1)]; + if (raw_entry->attr & ATTR_DIR) { + inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0777 & + ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR; + inode->i_op = &msdos_dir_inode_operations; + inode->i_nlink = 3; + inode->i_size = 0; + for (this = raw_entry->start; this && this != -1; this = + fat_access(inode->i_sb,this,-1)) + inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)-> + cluster_size; + } + else { + inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0666 & + ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFREG; + inode->i_op = MSDOS_CAN_BMAP(MSDOS_SB(inode->i_sb)) ? + &msdos_file_inode_operations : + &msdos_file_inode_operations_no_bmap; + inode->i_nlink = 1; + inode->i_size = raw_entry->size; + } + inode->i_data[D_BINARY] = is_binary(MSDOS_SB(inode->i_sb)->conversion, + raw_entry->ext); + inode->i_data[D_START] = raw_entry->start; + inode->i_data[D_ATTRS] = raw_entry->attr & ATTR_UNUSED; + inode->i_mtime = inode->i_atime = inode->i_ctime = + date_dos2unix(raw_entry->time,raw_entry->date); + brelse(bh); +} + + +void msdos_write_inode(struct inode *inode) +{ + struct buffer_head *bh; + struct msdos_dir_entry *raw_entry; + + inode->i_dirt = 0; + if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return; + if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS, BLOCK_SIZE))) + panic("unable to read i-node block"); + raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) + [inode->i_ino & (MSDOS_DPB-1)]; + if (S_ISDIR(inode->i_mode)) { + raw_entry->attr = ATTR_DIR; + raw_entry->size = 0; + } + else { + raw_entry->attr = ATTR_NONE; + raw_entry->size = inode->i_size; + } + raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) | inode->i_data[D_ATTRS]; + raw_entry->start = inode->i_data[D_START]; + date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date); + bh->b_dirt = 1; + brelse(bh); +} diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c new file mode 100644 index 000000000000..65d3ee93a352 --- /dev/null +++ b/fs/msdos/misc.c @@ -0,0 +1,366 @@ +/* + * linux/fs/msdos/misc.c + * + * Written 1992 by Werner Almesberger + */ + +#include +#include +#include +#include +#include +#include + +static char bin_extensions[] = + "EXECOMAPPSYSOVLOBJLIB" /* program code */ + "ARCZIPLHALZHZOOTARZ ARJTZ " /* common archivers */ + "GIFBMPTIFGL JPGPCX" /* graphics */ + "TFMVF GF PK PXLDVI"; /* TeX */ + + +/* Select binary/text conversion */ + +int is_binary(char conversion,char *extension) +{ + char *walk; + + switch (conversion) { + case 'b': + return 1; + case 't': + return 0; + case 'a': + for (walk = bin_extensions; *walk; walk += 3) + if (!strncmp(extension,walk,3)) return 1; + return 0; + default: + panic("Invalid conversion mode"); + } +} + + +static struct wait_queue *creation_wait = NULL; +static creation_lock = 0; + + +void lock_creation(void) +{ + while (creation_lock) sleep_on(&creation_wait); + creation_lock = 1; +} + + +void unlock_creation(void) +{ + creation_lock = 0; + wake_up(&creation_wait); +} + + +int msdos_add_cluster(struct inode *inode) +{ + static struct wait_queue *wait = NULL; + static int lock = 0; + static int previous = 0; /* works best if one FS is being used */ + int count,this,limit,last,current,sector; + void *data; + struct buffer_head *bh; + + if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC; + while (lock) sleep_on(&wait); + lock = 1; + limit = MSDOS_SB(inode->i_sb)->clusters; + this = limit; /* to keep GCC happy */ + for (count = 0; count < limit; count++) { + this = ((count+previous) % limit)+2; + if (fat_access(inode->i_sb,this,-1) == 0) break; + } +#ifdef DEBUG +printk("free cluster: %d\r\n",this); +#endif + previous = (count+previous+1) % limit; + if (count >= limit) { + lock = 0; + wake_up(&wait); + return -ENOSPC; + } + fat_access(inode->i_sb,this,MSDOS_SB(inode->i_sb)->fat_bits == 12 ? + 0xff8 : 0xfff8); + lock = 0; + wake_up(&wait); +#ifdef DEBUG +printk("set to %x\r\n",fat_access(inode->i_sb,this,-1)); +#endif + if (!S_ISDIR(inode->i_mode)) { + last = inode->i_size ? get_cluster(inode,(inode->i_size-1)/ + SECTOR_SIZE/MSDOS_SB(inode->i_sb)->cluster_size) : 0; + } + else { + last = 0; + if (current = inode->i_data[D_START]) { + cache_lookup(inode,0x7fffffff,&last,¤t); + while (current && current != -1) + if (!(current = fat_access(inode->i_sb, + last = current,-1))) + panic("File without EOF"); + } + } +#ifdef DEBUG +printk("last = %d\r\n",last); +#endif + if (last) fat_access(inode->i_sb,last,this); + else { + inode->i_data[D_START] = this; + inode->i_dirt = 1; + } +#ifdef DEBUG +if (last) printk("next set to %d\r\n",fat_access(inode->i_sb,last,-1)); +#endif + for (current = 0; current < MSDOS_SB(inode->i_sb)->cluster_size; + current++) { + sector = MSDOS_SB(inode->i_sb)->data_start+(this-2)* + MSDOS_SB(inode->i_sb)->cluster_size+current; +#ifdef DEBUG +printk("zeroing sector %d\r\n",sector); +#endif + if (current < MSDOS_SB(inode->i_sb)->cluster_size-1 && + !(sector & 1)) { + if (!(bh = getblk(inode->i_dev,sector >> 1, BLOCK_SIZE))) + printk("getblk failed\r\n"); + else { + memset(bh->b_data,0,BLOCK_SIZE); + bh->b_uptodate = 1; + } + current++; + } + else { + if (!(bh = msdos_sread(inode->i_dev,sector,&data))) + printk("msdos_sread failed\r\n"); + else memset(data,0,SECTOR_SIZE); + } + if (bh) { + bh->b_dirt = 1; + brelse(bh); + } + } + if (S_ISDIR(inode->i_mode)) { + if (inode->i_size & (SECTOR_SIZE-1)) + panic("Odd directory size"); + inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)-> + cluster_size; +#ifdef DEBUG +printk("size is %d now (%x)\r\n",inode->i_size,inode); +#endif + inode->i_dirt = 1; + } + return 0; +} + + +/* Linear day numbers of the respective 1sts in non-leap years. */ + +static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 }; + /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ + + +/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ + +int date_dos2unix(unsigned short time,unsigned short date) +{ + int month,year; + + month = ((date >> 5) & 15)-1; + year = date >> 9; + return (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* + ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && + month < 2 ? 1 : 0)+3653); + /* days since 1.1.70 plus 80's leap day */ +} + + +/* Convert linear UNIX date to a MS-DOS time/date pair. */ + +void date_unix2dos(int unix_date,unsigned short *time, + unsigned short *date) +{ + int day,year,nl_day,month; + + *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ + (((unix_date/3600) % 24) << 11); + day = unix_date/86400-3652; + year = day/365; + if ((year+3)/4+365*year > day) year--; + day -= (year+3)/4+365*year; + if (day == 59 && !(year & 3)) { + nl_day = day; + month = 2; + } + else { + nl_day = (year & 3) || day <= 59 ? day : day-1; + for (month = 0; month < 12; month++) + if (day_n[month] > nl_day) break; + } + *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9); +} + + +/* Returns the inode number of the directory entry at offset pos. If bh is + non-NULL, it is brelse'd before. Pos is incremented. The buffer header is + returned in bh. */ + +int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh, + struct msdos_dir_entry **de) +{ + int sector,offset; + void *data; + + while (1) { + offset = *pos; + if ((sector = msdos_smap(dir,*pos >> SECTOR_BITS)) == -1) + return -1; + if (!sector) + return -1; /* FAT error ... */ + *pos += sizeof(struct msdos_dir_entry); + if (*bh) + brelse(*bh); + if (!(*bh = msdos_sread(dir->i_dev,sector,&data))) + continue; + *de = (struct msdos_dir_entry *) (data+(offset & + (SECTOR_SIZE-1))); + return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >> + MSDOS_DIR_BITS); + } +} + + +/* Scans a directory for a given file (name points to its formatted name) or + for an empty directory slot (name is NULL). Returns the inode number. */ + +int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh, + struct msdos_dir_entry **res_de,int *ino) +{ + int pos; + struct msdos_dir_entry *de; + struct inode *inode; + + pos = 0; + *res_bh = NULL; + while ((*ino = msdos_get_entry(dir,&pos,res_bh,&de)) > -1) { + if (name) { + if (de->name[0] && ((unsigned char *) (de->name))[0] + != DELETED_FLAG && !(de->attr & ATTR_VOLUME) && + !strncmp(de->name,name,MSDOS_NAME)) break; + } + else if (!de->name[0] || ((unsigned char *) (de->name))[0] == + DELETED_FLAG) { + if (!(inode = iget(dir->i_dev,*ino))) break; + if (!inode->i_data[D_BUSY]) { + iput(inode); + break; + } + /* skip deleted files that haven't been closed yet */ + iput(inode); + } + } + if (*ino == -1) { + if (*res_bh) brelse(*res_bh); + *res_bh = NULL; + return name ? -ENOENT : -ENOSPC; + } + *res_de = de; + return 0; +} + + +/* Now an ugly part: this set of directory scan routines works on clusters + rather than on inodes and sectors. They are necessary to locate the '..' + directory "inode". */ + + +static int raw_found(struct super_block *sb,int sector,char *name,int number, + int *ino) +{ + struct buffer_head *bh; + struct msdos_dir_entry *data; + int entry,start; + + if (!(bh = msdos_sread(sb->s_dev,sector,(void **) &data))) return -EIO; + for (entry = 0; entry < MSDOS_DPS; entry++) + if (name ? !strncmp(data[entry].name,name,MSDOS_NAME) : + *(unsigned char *) data[entry].name != DELETED_FLAG && + data[entry].start == number) { + if (ino) *ino = sector*MSDOS_DPS+entry; + start = data[entry].start; + brelse(bh); + return start; + } + brelse(bh); + return -1; +} + + +static int raw_scan_root(struct super_block *sb,char *name,int number,int *ino) +{ + int count,cluster; + + for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) { + if ((cluster = raw_found(sb,MSDOS_SB(sb)->dir_start+count,name, + number,ino)) >= 0) return cluster; + } + return -ENOENT; +} + + +static int raw_scan_nonroot(struct super_block *sb,int start,char *name, + int number,int *ino) +{ + int count,cluster; + + do { + for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) { + if ((cluster = raw_found(sb,(start-2)*MSDOS_SB(sb)-> + cluster_size+MSDOS_SB(sb)->data_start+count,name, + number,ino)) >= 0) return cluster; + } + if (!(start = fat_access(sb,start,-1))) panic("FAT error"); + } + while (start != -1); + return -ENOENT; +} + + +static int raw_scan(struct super_block *sb,int start,char *name,int number, + int *ino) +{ + if (start) return raw_scan_nonroot(sb,start,name,number,ino); + else return raw_scan_root(sb,name,number,ino); +} + + +int msdos_parent_ino(struct inode *dir,int locked) +{ + int error,current,prev,this; + + if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i"); + if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino; + if (!locked) lock_creation(); /* prevent renames */ + if ((current = raw_scan(dir->i_sb,dir->i_data[D_START],MSDOS_DOTDOT,0, + NULL)) < 0) { + if (!locked) unlock_creation(); + return current; + } + if (!current) this = MSDOS_ROOT_INO; + else { + if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,0,NULL)) < + 0) { + if (!locked) unlock_creation(); + return prev; + } + if ((error = raw_scan(dir->i_sb,prev,NULL,current,&this)) < 0) { + if (!locked) unlock_creation(); + return error; + } + } + if (!locked) unlock_creation(); + return this; +} diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c new file mode 100644 index 000000000000..0c4cc4fb8f81 --- /dev/null +++ b/fs/msdos/namei.c @@ -0,0 +1,515 @@ +/* + * linux/fs/msdos/namei.c + * + * Written 1992 by Werner Almesberger + */ + +#include + +#include +#include +#include +#include +#include +#include + +/* MS-DOS "device special files" */ + +static char *reserved_names[] = { + "CON ","PRN ","NUL ","AUX ", + "LPT1 ","LPT2 ","LPT3 ","LPT4 ", + "COM1 ","COM2 ","COM3 ","COM4 ", + NULL }; + + +/* Formats an MS-DOS file name. Rejects invalid names. */ + +static int msdos_format_name(char conv,const char *name,int len,char *res) +{ + char *walk,**reserved; + char c; + int space; + + if (get_fs_byte(name) == DELETED_FLAG) return -EINVAL; + if (get_fs_byte(name) == '.' && (len == 1 || (len == 2 && + get_fs_byte(name+1) == '.'))) { + memset(res+1,' ',10); + while (len--) *res++ = '.'; + return 0; + } + space = 0; /* to make GCC happy */ + c = 0; + for (walk = res; len && walk-res < 8; walk++) { + c = get_fs_byte(name++); + len--; + if (c == ' ' && conv != 'r') return -EINVAL; + if (c >= 'A' && c <= 'Z') { + if (conv != 'r') return -EINVAL; + c += 32; + } + if (c < ' ' || c == ':' || c == '\\') return -EINVAL; + if (c == '.') break; + space = c == ' '; + *walk = c >= 'a' && c <= 'z' ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len && c != '.') { + c = get_fs_byte(name++); + len--; + if (c != '.') return -EINVAL; + } + while (c != '.' && len--) c = get_fs_byte(name++); + if (walk == res) return -EINVAL; + if (c == '.') { + while (walk-res < 8) *walk++ = ' '; + while (len > 0 && walk-res < MSDOS_NAME) { + c = get_fs_byte(name++); + len--; + if (c == ' ' && conv != 'r') return -EINVAL; + if (c < ' ' || c == ':' || c == '\\' || c == '.') + return -EINVAL; + if (c >= 'A' && c <= 'Z') { + if (conv != 'r') return -EINVAL; + c += 32; + } + space = c == ' '; + *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; + } + if (space) return -EINVAL; + if (conv == 's' && len) return -EINVAL; + } + while (walk-res < MSDOS_NAME) *walk++ = ' '; + for (reserved = reserved_names; *reserved; reserved++) + if (!strncmp(res,*reserved,8)) return -EINVAL; + return 0; +} + + +/* Locates a directory entry. */ + +static int msdos_find(struct inode *dir,const char *name,int len, + struct buffer_head **bh,struct msdos_dir_entry **de,int *ino) +{ + char msdos_name[MSDOS_NAME]; + int res; + + if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, + msdos_name)) < 0) return res; + return msdos_scan(dir,msdos_name,bh,de,ino); +} + + +int msdos_lookup(struct inode *dir,const char *name,int len, + struct inode **result) +{ + int ino,res; + struct msdos_dir_entry *de; + struct buffer_head *bh; + struct inode *next; + + *result = NULL; + if (!dir) return -ENOENT; + if (!S_ISDIR(dir->i_mode)) { + iput(dir); + return -ENOENT; + } + if (len == 1 && get_fs_byte(name) == '.') { + *result = dir; + return 0; + } + if (len == 2 && get_fs_byte(name) == '.' && get_fs_byte(name+1) == '.') + { + ino = msdos_parent_ino(dir,0); + iput(dir); + if (ino < 0) return ino; + if (!(*result = iget(dir->i_dev,ino))) return -EACCES; + return 0; + } + if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) { + iput(dir); + return res; + } + if (bh) brelse(bh); +/* printk("lookup: ino=%d\r\n",ino); */ + if (!(*result = iget(dir->i_dev,ino))) { + iput(dir); + return -EACCES; + } + if ((*result)->i_data[D_BUSY]) { /* mkdir in progress */ + iput(*result); + iput(dir); + return -ENOENT; + } + while ((*result)->i_data[D_OLD]) { + next = (struct inode *) ((*result)->i_data[D_OLD]); + iput(*result); + if (!(*result = iget(next->i_dev,next->i_ino))) + panic("msdos_lookup: Can't happen"); + } + iput(dir); + return 0; +} + + +/* Creates a directory entry (name is already formatted). */ + +static int msdos_create_entry(struct inode *dir,char *name,int is_dir, + struct inode **result) +{ + struct buffer_head *bh; + struct msdos_dir_entry *de; + int res,ino; + + if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) { + if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC; + if ((res = msdos_add_cluster(dir)) < 0) return res; + if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) return res; + } + memcpy(de->name,name,MSDOS_NAME); + de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; + de->start = 0; + date_unix2dos(CURRENT_TIME,&de->time,&de->date); + de->size = 0; + bh->b_dirt = 1; + if (*result = iget(dir->i_dev,ino)) msdos_read_inode(*result); + brelse(bh); + if (!*result) return -EIO; + (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = + CURRENT_TIME; + (*result)->i_dirt = 1; + return 0; +} + + +int msdos_create(struct inode *dir,const char *name,int len,int mode, + struct inode **result) +{ + struct buffer_head *bh; + struct msdos_dir_entry *de; + char msdos_name[MSDOS_NAME]; + int ino,res; + + if (!dir) return -ENOENT; + if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, + msdos_name)) < 0) { + iput(dir); + return res; + } + lock_creation(); + if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) { + unlock_creation(); + brelse(bh); + iput(dir); + return -EEXIST; + } + res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),result); + unlock_creation(); + iput(dir); + return res; +} + + +int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) +{ + struct buffer_head *bh; + struct msdos_dir_entry *de; + struct inode *inode,*dot; + char msdos_name[MSDOS_NAME]; + int ino,res; + + if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, + msdos_name)) < 0) { + iput(dir); + return res; + } + lock_creation(); + if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) { + unlock_creation(); + brelse(bh); + iput(dir); + return -EEXIST; + } + if ((res = msdos_create_entry(dir,msdos_name,1,&inode)) < 0) { + unlock_creation(); + iput(dir); + return res; + } + inode->i_data[D_BUSY] = 1; /* prevent lookups */ + if ((res = msdos_add_cluster(inode)) < 0) goto mkdir_error; + if ((res = msdos_create_entry(inode,MSDOS_DOT,1,&dot)) < 0) + goto mkdir_error; + dot->i_size = inode->i_size; + dot->i_data[D_START] = inode->i_data[D_START]; + dot->i_dirt = 1; + iput(dot); + if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,&dot)) < 0) + goto mkdir_error; + unlock_creation(); + dot->i_size = dir->i_size; + dot->i_data[D_START] = dir->i_data[D_START]; + dot->i_dirt = 1; + inode->i_data[D_BUSY] = 0; + iput(dot); + iput(inode); + iput(dir); + return 0; +mkdir_error: + iput(inode); + if (msdos_rmdir(dir,name,len) < 0) panic("rmdir in mkdir failed"); + unlock_creation(); + return res; +} + + +int msdos_rmdir(struct inode *dir,const char *name,int len) +{ + int res,ino,pos; + struct buffer_head *bh,*dbh; + struct msdos_dir_entry *de,*dde; + struct inode *inode; + + bh = NULL; + inode = NULL; + res = -EINVAL; + if (len == 1 && get_fs_byte(name) == '.') goto rmdir_done; + if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done; + res = -ENOENT; + if (!(inode = iget(dir->i_dev,ino))) goto rmdir_done; + res = -ENOTDIR; + if (!S_ISDIR(inode->i_mode)) goto rmdir_done; + res = -EBUSY; + if (dir->i_dev != inode->i_dev || dir == inode) goto rmdir_done; + if (inode->i_count > 1) goto rmdir_done; + if (inode->i_data[D_START]) { /* may be zero in mkdir */ + res = -ENOTEMPTY; + pos = 0; + dbh = NULL; + while (msdos_get_entry(inode,&pos,&dbh,&dde) > -1) + if (dde->name[0] && ((unsigned char *) dde->name)[0] != + DELETED_FLAG && strncmp(dde->name,MSDOS_DOT, + MSDOS_NAME) && strncmp(dde->name,MSDOS_DOTDOT, + MSDOS_NAME)) goto rmdir_done; + if (dbh) brelse(dbh); + } + inode->i_nlink = 0; + dir->i_mtime = CURRENT_TIME; + inode->i_dirt = dir->i_dirt = 1; + de->name[0] = DELETED_FLAG; + bh->b_dirt = 1; + res = 0; +rmdir_done: + brelse(bh); + iput(dir); + iput(inode); + return res; +} + + +int msdos_unlink(struct inode *dir,const char *name,int len) +{ + int res,ino; + struct buffer_head *bh; + struct msdos_dir_entry *de; + struct inode *inode; + + bh = NULL; + inode = NULL; + if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) + goto unlink_done; + if (!(inode = iget(dir->i_dev,ino))) { + res = -ENOENT; + goto unlink_done; + } + if (!S_ISREG(inode->i_mode)) { + res = -EPERM; + goto unlink_done; + } + inode->i_nlink = 0; + inode->i_data[D_BUSY] = 1; + inode->i_dirt = 1; + de->name[0] = DELETED_FLAG; + bh->b_dirt = 1; +unlink_done: + brelse(bh); + iput(inode); + iput(dir); + return res; +} + + +static int rename_same_dir(struct inode *old_dir,char *old_name, + struct inode *new_dir,char *new_name,struct buffer_head *old_bh, + struct msdos_dir_entry *old_de,int old_ino) +{ + struct buffer_head *new_bh; + struct msdos_dir_entry *new_de; + struct inode *new_inode,*old_inode; + int new_ino; + int exists; + + if (!strncmp(old_name,new_name,MSDOS_NAME)) return 0; + exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0; + if (*(unsigned char *) old_de->name == DELETED_FLAG) { + if (exists) brelse(new_bh); + return -ENOENT; + } + if (exists) { + if (!(new_inode = iget(new_dir->i_dev,new_ino))) { + brelse(new_bh); + return -EIO; + } + if (S_ISDIR(new_inode->i_mode)) { + iput(new_inode); + brelse(new_bh); + return -EPERM; + } + new_inode->i_nlink = 0; + new_inode->i_data[D_BUSY] = 1; + new_inode->i_dirt = 1; + new_de->name[0] = DELETED_FLAG; + new_bh->b_dirt = 1; + iput(new_inode); + brelse(new_bh); + } + memcpy(old_de->name,new_name,MSDOS_NAME); + old_bh->b_dirt = 1; + if (MSDOS_SB(old_dir->i_sb)->conversion == 'a') /* update binary info */ + if (old_inode = iget(old_dir->i_dev,old_ino)) { + msdos_read_inode(old_inode); + iput(old_inode); + } + return 0; +} + + +static int rename_diff_dir(struct inode *old_dir,char *old_name, + struct inode *new_dir,char *new_name,struct buffer_head *old_bh, + struct msdos_dir_entry *old_de,int old_ino) +{ + struct buffer_head *new_bh,*free_bh,*dotdot_bh; + struct msdos_dir_entry *new_de,*free_de,*dotdot_de; + struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk; + int new_ino,free_ino,dotdot_ino; + int error,exists,ino; + + if (old_dir->i_dev != new_dir->i_dev) return -EINVAL; + if (old_ino == new_dir->i_ino) return -EINVAL; + if (!(walk = iget(new_dir->i_dev,new_dir->i_ino))) return -EIO; + while (walk->i_ino != MSDOS_ROOT_INO) { + ino = msdos_parent_ino(walk,1); + iput(walk); + if (ino < 0) return ino; + if (ino == old_ino) return -EINVAL; + if (!(walk = iget(new_dir->i_dev,ino))) return -EIO; + } + iput(walk); + if ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino)) < 0) + return error; + exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) + >= 0; + if (!(old_inode = iget(old_dir->i_dev,old_ino))) { + brelse(free_bh); + if (exists) brelse(new_bh); + return -EIO; + } + if (*(unsigned char *) old_de->name == DELETED_FLAG) { + iput(old_inode); + brelse(free_bh); + if (exists) brelse(new_bh); + return -ENOENT; + } + new_inode = NULL; /* to make GCC happy */ + if (exists) { + if (!(new_inode = iget(new_dir->i_dev,new_ino))) { + iput(old_inode); + brelse(new_bh); + return -EIO; + } + if (S_ISDIR(new_inode->i_mode)) { + iput(new_inode); + iput(old_inode); + brelse(new_bh); + return -EPERM; + } + new_inode->i_nlink = 0; + new_inode->i_data[D_BUSY] = 1; + new_inode->i_dirt = 1; + new_de->name[0] = DELETED_FLAG; + new_bh->b_dirt = 1; + } + memcpy(free_de,old_de,sizeof(struct msdos_dir_entry)); + memcpy(free_de->name,new_name,MSDOS_NAME); + if (!(free_inode = iget(new_dir->i_dev,free_ino))) { + free_de->name[0] = DELETED_FLAG; +/* Don't mark free_bh as dirty. Both states are supposed to be equivalent. */ + brelse(free_bh); + if (exists) { + iput(new_inode); + brelse(new_bh); + } + return -EIO; + } + msdos_read_inode(free_inode); + old_inode->i_data[D_BUSY] = 1; + old_inode->i_dirt = 1; + old_de->name[0] = DELETED_FLAG; + old_bh->b_dirt = 1; + free_bh->b_dirt = 1; + if (!exists) iput(free_inode); + else { + new_inode->i_data[D_DEPEND] = (int) free_inode; + free_inode->i_data[D_OLD] = (int) new_inode; + /* free_inode is put when putting new_inode */ + iput(new_inode); + brelse(new_bh); + } + if (S_ISDIR(old_inode->i_mode)) { + if ((error = msdos_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, + &dotdot_de,&dotdot_ino)) < 0) goto rename_done; + if (!(dotdot_inode = iget(old_inode->i_dev,dotdot_ino))) { + brelse(dotdot_bh); + error = -EIO; + goto rename_done; + } + dotdot_de->start = dotdot_inode->i_data[D_START] = + new_dir->i_data[D_START]; + dotdot_inode->i_dirt = 1; + dotdot_bh->b_dirt = 1; + iput(dotdot_inode); + brelse(dotdot_bh); + } + error = 0; +rename_done: + brelse(free_bh); + iput(old_inode); + return error; +} + + +int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, + struct inode *new_dir,const char *new_name,int new_len) +{ + char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME]; + struct buffer_head *old_bh; + struct msdos_dir_entry *old_de; + int old_ino,error; + + if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->name_check, + old_name,old_len,old_msdos_name)) < 0) goto rename_done; + if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->name_check, + new_name,new_len,new_msdos_name)) < 0) goto rename_done; + if ((error = msdos_scan(old_dir,old_msdos_name,&old_bh,&old_de, + &old_ino)) < 0) goto rename_done; + lock_creation(); + if (old_dir == new_dir) + error = rename_same_dir(old_dir,old_msdos_name,new_dir, + new_msdos_name,old_bh,old_de,old_ino); + else error = rename_diff_dir(old_dir,old_msdos_name,new_dir, + new_msdos_name,old_bh,old_de,old_ino); + unlock_creation(); + brelse(old_bh); +rename_done: + iput(old_dir); + iput(new_dir); + return error; +} diff --git a/fs/namei.c b/fs/namei.c index 73c16216246c..42eb868bd488 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1,22 +1,23 @@ /* * linux/fs/namei.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* * Some corrections by tytso. */ -#include -#include +#include + #include +#include +#include +#include #include -#include -#include -#include -#include +#include +#include struct inode * _namei(const char * filename, struct inode * base, int follow_links); @@ -167,10 +168,6 @@ struct inode * _namei(const char * pathname, struct inode * base, inode = follow_link(base,inode); else iput(base); - if (inode) { - inode->i_atime=CURRENT_TIME; - inode->i_dirt=1; - } return inode; } @@ -200,12 +197,13 @@ int open_namei(const char * pathname, int flag, int mode, struct inode ** res_inode) { const char * basename; - int namelen,error; + int namelen,error,i; struct inode * dir, *inode; + struct task_struct ** p; if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) flag |= O_WRONLY; - mode &= 0777 & ~current->umask; + mode &= 07777 & ~current->umask; mode |= I_REGULAR; if (!(dir = dir_namei(pathname,&namelen,&basename,NULL))) return -ENOENT; @@ -232,6 +230,10 @@ int open_namei(const char * pathname, int flag, int mode, iput(dir); return -EACCES; } + if (IS_RDONLY(dir)) { + iput(dir); + return -EROFS; + } return dir->i_op->create(dir,basename,namelen,mode,res_inode); } if (flag & O_EXCL) { @@ -241,35 +243,65 @@ int open_namei(const char * pathname, int flag, int mode, } if (!(inode = follow_link(dir,inode))) return -ELOOP; + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { + if (IS_NODEV(inode)) { + iput(inode); + return -EACCES; + } + } else { + if (IS_RDONLY(inode) && (flag & (O_TRUNC | O_ACCMODE))) { + iput(inode); + return -EROFS; + } + } if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || !permission(inode,ACC_MODE(flag))) { iput(inode); return -EPERM; } - inode->i_atime = CURRENT_TIME; + if ((inode->i_count > 1) && (flag & O_ACCMODE)) + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!*p) + continue; + if (inode == (*p)->executable) { + iput(inode); + return -ETXTBSY; + } + for (i=0; i < (*p)->numlibraries; i++) + if (inode == (*p)->libraries[i].library) { + iput(inode); + return -ETXTBSY; + } + } if (flag & O_TRUNC) if (inode->i_op && inode->i_op->truncate) { inode->i_size = 0; inode->i_op->truncate(inode); } + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } *res_inode = inode; return 0; } -int sys_mknod(const char * filename, int mode, int dev) +int do_mknod(const char * filename, int mode, int dev) { const char * basename; int namelen; struct inode * dir; - if (!suser()) - return -EPERM; if (!(dir = dir_namei(filename,&namelen,&basename, NULL))) return -ENOENT; if (!namelen) { iput(dir); return -ENOENT; } + if (IS_RDONLY(dir)) { + iput(dir); + return -EROFS; + } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; @@ -281,6 +313,13 @@ int sys_mknod(const char * filename, int mode, int dev) return dir->i_op->mknod(dir,basename,namelen,mode,dev); } +int sys_mknod(const char * filename, int mode, int dev) +{ + if (S_ISFIFO(mode) || suser()) + return do_mknod(filename,mode,dev); + return -EPERM; +} + int sys_mkdir(const char * pathname, int mode) { const char * basename; @@ -293,6 +332,10 @@ int sys_mkdir(const char * pathname, int mode) iput(dir); return -ENOENT; } + if (IS_RDONLY(dir)) { + iput(dir); + return -EROFS; + } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; @@ -316,6 +359,10 @@ int sys_rmdir(const char * name) iput(dir); return -ENOENT; } + if (IS_RDONLY(dir)) { + iput(dir); + return -EROFS; + } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; @@ -339,6 +386,10 @@ int sys_unlink(const char * name) iput(dir); return -EPERM; } + if (IS_RDONLY(dir)) { + iput(dir); + return -EROFS; + } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; @@ -363,6 +414,10 @@ int sys_symlink(const char * oldname, const char * newname) iput(dir); return -ENOENT; } + if (IS_RDONLY(dir)) { + iput(dir); + return -EROFS; + } if (!permission(dir,MAY_WRITE)) { iput(dir); return -EACCES; @@ -393,6 +448,11 @@ int sys_link(const char * oldname, const char * newname) iput(dir); return -EPERM; } + if (IS_RDONLY(dir)) { + iput(oldinode); + iput(dir); + return -EROFS; + } if (dir->i_dev != oldinode->i_dev) { iput(dir); iput(oldinode); @@ -452,6 +512,11 @@ int sys_rename(const char * oldname, const char * newname) iput(new_dir); return -EXDEV; } + if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir)) { + iput(old_dir); + iput(new_dir); + return -EROFS; + } if (!old_dir->i_op || !old_dir->i_op->rename) { iput(old_dir); iput(new_dir); diff --git a/fs/open.c b/fs/open.c index e2cdce7ebd00..538f7ae11bfb 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1,20 +1,19 @@ /* * linux/fs/open.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include -#include -#include - -#include -#include - +#include +#include +#include +#include +#include +#include #include #include #include +#include #include @@ -33,14 +32,34 @@ int sys_ustat(int dev, struct ustat * ubuf) int sys_statfs(const char * path, struct statfs * buf) { - printk("statfs not implemented\n"); - return -ENOSYS; + struct inode * inode; + + verify_area(buf, sizeof(struct statfs)); + if (!(inode = namei(path))) + return -ENOENT; + if (!inode->i_sb->s_op->statfs) { + iput(inode); + return -ENOSYS; + } + inode->i_sb->s_op->statfs(inode->i_sb, buf); + iput(inode); + return 0; } int sys_fstatfs(unsigned int fd, struct statfs * buf) { - printk("fstatfs not implemented\n"); - return -ENOSYS; + struct inode * inode; + struct file * file; + + verify_area(buf, sizeof(struct statfs)); + if (fd >= NR_OPEN || !(file = current->filp[fd])) + return -EBADF; + if (!(inode = file->f_inode)) + return -ENOENT; + if (!inode->i_sb->s_op->statfs) + return -ENOSYS; + inode->i_sb->s_op->statfs(inode->i_sb, buf); + return 0; } int sys_truncate(const char * path, unsigned int length) @@ -49,9 +68,13 @@ int sys_truncate(const char * path, unsigned int length) if (!(inode = namei(path))) return -ENOENT; - if (!permission(inode,MAY_WRITE)) { + if (S_ISDIR(inode->i_mode) || !permission(inode,MAY_WRITE)) { iput(inode); - return -EPERM; + return -EACCES; + } + if (IS_RDONLY(inode)) { + iput(inode); + return -EROFS; } inode->i_size = length; if (inode->i_op && inode->i_op->truncate) @@ -71,8 +94,8 @@ int sys_ftruncate(unsigned int fd, unsigned int length) return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; - if (!(file->f_flags & 2)) - return -EPERM; + if (S_ISDIR(inode->i_mode) || !(file->f_mode & 2)) + return -EACCES; inode->i_size = length; if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); @@ -81,6 +104,10 @@ int sys_ftruncate(unsigned int fd, unsigned int length) return 0; } +/* If times==NULL, set access and modification to current time, + * must be owner or have write permission. + * Else, update from *times, must be owner or super user. + */ int sys_utime(char * filename, struct utimbuf * times) { struct inode * inode; @@ -88,16 +115,25 @@ int sys_utime(char * filename, struct utimbuf * times) if (!(inode=namei(filename))) return -ENOENT; + if (IS_RDONLY(inode)) { + iput(inode); + return -EROFS; + } if (times) { - if (current->euid != inode->i_uid && - !permission(inode,MAY_WRITE)) { + if ((current->euid != inode->i_uid) && !suser()) { iput(inode); return -EPERM; } actime = get_fs_long((unsigned long *) ×->actime); modtime = get_fs_long((unsigned long *) ×->modtime); - } else + } else { + if ((current->euid != inode->i_uid) && + !permission(inode,MAY_WRITE)) { + iput(inode); + return -EACCES; + } actime = modtime = CURRENT_TIME; + } inode->i_atime = actime; inode->i_mtime = modtime; inode->i_dirt = 1; @@ -121,8 +157,8 @@ int sys_access(const char * filename,int mode) iput(inode); if (current->uid == inode->i_uid) res >>= 6; - else if (current->gid == inode->i_gid) - res >>= 6; + else if (in_group_p(inode->i_gid)) + res >>= 3; if ((res & 0007 & mode) == mode) return 0; /* @@ -147,6 +183,10 @@ int sys_chdir(const char * filename) iput(inode); return -ENOTDIR; } + if (!permission(inode,MAY_EXEC)) { + iput(inode); + return -EACCES; + } iput(current->pwd); current->pwd = inode; return (0); @@ -162,6 +202,10 @@ int sys_chroot(const char * filename) iput(inode); return -ENOTDIR; } + if (!suser()) { + iput(inode); + return -EPERM; + } iput(current->root); current->root = inode; return (0); @@ -177,7 +221,9 @@ int sys_fchmod(unsigned int fd, mode_t mode) if (!(inode = file->f_inode)) return -ENOENT; if ((current->euid != inode->i_uid) && !suser()) - return -EACCES; + return -EPERM; + if (IS_RDONLY(inode)) + return -EROFS; inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); inode->i_dirt = 1; return 0; @@ -191,7 +237,11 @@ int sys_chmod(const char * filename, mode_t mode) return -ENOENT; if ((current->euid != inode->i_uid) && !suser()) { iput(inode); - return -EACCES; + return -EPERM; + } + if (IS_RDONLY(inode)) { + iput(inode); + return -EROFS; } inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); inode->i_dirt = 1; @@ -208,29 +258,40 @@ int sys_fchown(unsigned int fd, uid_t user, gid_t group) return -EBADF; if (!(inode = file->f_inode)) return -ENOENT; - if (!suser()) - return -EACCES; - inode->i_uid = user; - inode->i_gid = group; - inode->i_dirt=1; - return 0; + if (IS_RDONLY(inode)) + return -EROFS; + if ((current->euid == inode->i_uid && user == inode->i_uid && + (in_group_p(group) || group == inode->i_gid)) || + suser()) { + inode->i_uid = user; + inode->i_gid = group; + inode->i_dirt=1; + return 0; + } + return -EPERM; } int sys_chown(const char * filename, uid_t user, gid_t group) { struct inode * inode; - if (!(inode = namei(filename))) + if (!(inode = lnamei(filename))) return -ENOENT; - if (!suser()) { + if (IS_RDONLY(inode)) { iput(inode); - return -EACCES; + return -EROFS; + } + if ((current->euid == inode->i_uid && user == inode->i_uid && + (in_group_p(group) || group == inode->i_gid)) || + suser()) { + inode->i_uid = user; + inode->i_gid = group; + inode->i_dirt=1; + iput(inode); + return 0; } - inode->i_uid = user; - inode->i_gid = group; - inode->i_dirt = 1; iput(inode); - return 0; + return -EPERM; } int sys_open(const char * filename,int flag,int mode) @@ -243,39 +304,29 @@ int sys_open(const char * filename,int flag,int mode) if (!current->filp[fd]) break; if (fd>=NR_OPEN) - return -EINVAL; + return -EMFILE; current->close_on_exec &= ~(1<f_count) break; - if (i>=NR_FILE) - return -EINVAL; - (current->filp[fd] = f)->f_count++; + f = get_empty_filp(); + if (!f) + return -ENFILE; + current->filp[fd] = f; if ((i = open_namei(filename,flag,mode,&inode))<0) { current->filp[fd]=NULL; - f->f_count=0; + f->f_count--; return i; } - f->f_op = NULL; - if (inode) - if (S_ISCHR(inode->i_mode)) { - i = MAJOR(inode->i_rdev); - if (i < MAX_CHRDEV) - f->f_op = chrdev_fops[i]; - } else if (S_ISBLK(inode->i_mode)) { - i = MAJOR(inode->i_rdev); - if (i < MAX_CHRDEV) - f->f_op = blkdev_fops[i]; - } f->f_mode = "\001\002\003\000"[flag & O_ACCMODE]; f->f_flags = flag; - f->f_count = 1; f->f_inode = inode; f->f_pos = 0; - if (inode->i_op && inode->i_op->open) - if (i = inode->i_op->open(inode,f)) { + f->f_reada = 0; + f->f_op = NULL; + if (inode->i_op) + f->f_op = inode->i_op->default_file_ops; + if (f->f_op && f->f_op->open) + if (i = f->f_op->open(inode,f)) { iput(inode); - f->f_count=0; + f->f_count--; current->filp[fd]=NULL; return i; } @@ -290,6 +341,7 @@ int sys_creat(const char * pathname, int mode) int sys_close(unsigned int fd) { struct file * filp; + struct inode * inode; if (fd >= NR_OPEN) return -EINVAL; @@ -297,12 +349,24 @@ int sys_close(unsigned int fd) if (!(filp = current->filp[fd])) return -EINVAL; current->filp[fd] = NULL; - if (filp->f_count == 0) - panic("Close: file count is 0"); - if (--filp->f_count) - return (0); - if (filp->f_op && filp->f_op->close) - return filp->f_op->close(filp->f_inode,filp); - iput(filp->f_inode); + if (filp->f_count == 0) { + printk("Close: file count is 0\n"); + return 0; + } + if (filp->f_count > 1) { + filp->f_count--; + return 0; + } + inode = filp->f_inode; + if (filp->f_op && filp->f_op->release) + filp->f_op->release(inode,filp); + filp->f_count--; + filp->f_inode = NULL; + iput(inode); return 0; } + +int sys_vhangup(void) +{ + return -ENOSYS; +} diff --git a/fs/pipe.c b/fs/pipe.c index 0aef899e08e8..014f69054080 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1,18 +1,17 @@ /* * linux/fs/pipe.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include -#include -#include - #include #include #include +#include +#include +#include +#include static int pipe_read(struct inode * inode, struct file * filp, char * buf, int count) { @@ -21,7 +20,7 @@ static int pipe_read(struct inode * inode, struct file * filp, char * buf, int c if (!(filp->f_flags & O_NONBLOCK)) while (!PIPE_SIZE(*inode)) { wake_up(& PIPE_WRITE_WAIT(*inode)); - if (inode->i_count != 2) /* are there any writers? */ + if (!PIPE_WRITERS(*inode)) /* are there any writers? */ return 0; if (current->signal & ~current->blocked) return -ERESTARTSYS; @@ -33,13 +32,12 @@ static int pipe_read(struct inode * inode, struct file * filp, char * buf, int c chars = count; if (chars > size) chars = size; - count -= chars; + memcpy_tofs(buf, (char *)inode->i_size+PIPE_TAIL(*inode), chars ); read += chars; - size = PIPE_TAIL(*inode); PIPE_TAIL(*inode) += chars; PIPE_TAIL(*inode) &= (PAGE_SIZE-1); - while (chars-->0) - put_fs_byte(((char *)inode->i_size)[size++],buf++); + count -= chars; + buf += chars; } wake_up(& PIPE_WRITE_WAIT(*inode)); return read?read:-EAGAIN; @@ -49,31 +47,44 @@ static int pipe_write(struct inode * inode, struct file * filp, char * buf, int { int chars, size, written = 0; + if (!PIPE_READERS(*inode)) { /* no readers */ + send_sig(SIGPIPE,current,0); + return -EPIPE; + } +/* if count < PAGE_SIZE, we have to make it atomic */ + if (count < PAGE_SIZE) + size = PAGE_SIZE-count; + else + size = PAGE_SIZE-1; while (count>0) { - while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) { - wake_up(& PIPE_READ_WAIT(*inode)); - if (inode->i_count != 2) { /* no readers */ - current->signal |= (1<<(SIGPIPE-1)); - return written?written:-EINTR; + while (PIPE_SIZE(*inode) >= size) { + if (!PIPE_READERS(*inode)) { /* no readers */ + send_sig(SIGPIPE,current,0); + return written?written:-EPIPE; } if (current->signal & ~current->blocked) - return written?written:-EINTR; - interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode)); + return written?written:-ERESTARTSYS; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + else + interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode)); } - chars = PAGE_SIZE-PIPE_HEAD(*inode); - if (chars > count) - chars = count; - if (chars > size) - chars = size; - count -= chars; - written += chars; - size = PIPE_HEAD(*inode); - PIPE_HEAD(*inode) += chars; - PIPE_HEAD(*inode) &= (PAGE_SIZE-1); - while (chars-->0) - ((char *)inode->i_size)[size++]=get_fs_byte(buf++); + while (count>0 && (size = (PAGE_SIZE-1)-PIPE_SIZE(*inode))) { + chars = PAGE_SIZE-PIPE_HEAD(*inode); + if (chars > count) + chars = count; + if (chars > size) + chars = size; + memcpy_fromfs((char *)inode->i_size+PIPE_HEAD(*inode), buf, chars ); + written += chars; + PIPE_HEAD(*inode) += chars; + PIPE_HEAD(*inode) &= (PAGE_SIZE-1); + count -= chars; + buf += chars; + } + wake_up(& PIPE_READ_WAIT(*inode)); + size = PAGE_SIZE-1; } - wake_up(& PIPE_READ_WAIT(*inode)); return written; } @@ -105,24 +116,87 @@ static int pipe_ioctl(struct inode *pino, struct file * filp, } } -static struct file_operations read_pipe_fops = { +static int pipe_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait) +{ + switch (sel_type) { + case SEL_IN: + if (!PIPE_EMPTY(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&PIPE_READ_WAIT(*inode), wait); + return 0; + case SEL_OUT: + if (!PIPE_FULL(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&PIPE_WRITE_WAIT(*inode), wait); + return 0; + case SEL_EX: + if (!PIPE_READERS(*inode) || !PIPE_WRITERS(*inode)) + return 1; + select_wait(&inode->i_wait,wait); + return 0; + } + return 0; +} + +/* + * Ok, these three routines NOW keep track of readers/writers, + * Linus previously did it with inode->i_count checking. + */ +static void pipe_read_release(struct inode * inode, struct file * filp) +{ + PIPE_READERS(*inode)--; + wake_up(&PIPE_WRITE_WAIT(*inode)); +} + +static void pipe_write_release(struct inode * inode, struct file * filp) +{ + PIPE_WRITERS(*inode)--; + wake_up(&PIPE_READ_WAIT(*inode)); +} + +static void pipe_rdwr_release(struct inode * inode, struct file * filp) +{ + PIPE_READERS(*inode)--; + PIPE_WRITERS(*inode)--; + wake_up(&PIPE_READ_WAIT(*inode)); + wake_up(&PIPE_WRITE_WAIT(*inode)); +} + +/* + * The three file_operations structs are not static because they + * are also used in linux/fs/fifo.c to do operations on fifo's. + */ +struct file_operations read_pipe_fops = { pipe_lseek, pipe_read, bad_pipe_rw, pipe_readdir, - NULL, /* pipe_close */ - NULL, /* pipe_select */ - pipe_ioctl + pipe_select, + pipe_ioctl, + NULL, /* no special open code */ + pipe_read_release }; -static struct file_operations write_pipe_fops = { +struct file_operations write_pipe_fops = { pipe_lseek, bad_pipe_rw, pipe_write, pipe_readdir, - NULL, /* pipe_close */ - NULL, /* pipe_select */ - pipe_ioctl + pipe_select, + pipe_ioctl, + NULL, /* no special open code */ + pipe_write_release +}; + +struct file_operations rdwr_pipe_fops = { + pipe_lseek, + pipe_read, + pipe_write, + pipe_readdir, + pipe_select, + pipe_ioctl, + NULL, /* no special open code */ + pipe_rdwr_release }; int sys_pipe(unsigned long * fildes) @@ -132,14 +206,14 @@ int sys_pipe(unsigned long * fildes) int fd[2]; int i,j; - j=0; - for(i=0;j<2 && if_count++; + verify_area(fildes,8); + for(j=0 ; j<2 ; j++) + if (!(f[j] = get_empty_filp())) + break; if (j==1) - f[0]->f_count=0; + f[0]->f_count--; if (j<2) - return -1; + return -ENFILE; j=0; for(i=0;j<2 && ifilp[i]) { @@ -149,17 +223,20 @@ int sys_pipe(unsigned long * fildes) if (j==1) current->filp[fd[0]]=NULL; if (j<2) { - f[0]->f_count=f[1]->f_count=0; - return -1; + f[0]->f_count--; + f[1]->f_count--; + return -EMFILE; } if (!(inode=get_pipe_inode())) { - current->filp[fd[0]] = - current->filp[fd[1]] = NULL; - f[0]->f_count = f[1]->f_count = 0; - return -1; + current->filp[fd[0]] = NULL; + current->filp[fd[1]] = NULL; + f[0]->f_count--; + f[1]->f_count--; + return -ENFILE; } f[0]->f_inode = f[1]->f_inode = inode; f[0]->f_pos = f[1]->f_pos = 0; + f[0]->f_flags = f[1]->f_flags = 0; f[0]->f_op = &read_pipe_fops; f[0]->f_mode = 1; /* read */ f[1]->f_op = &write_pipe_fops; diff --git a/fs/read_write.c b/fs/read_write.c index 19cbcdaad961..e72e6b23f813 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1,17 +1,16 @@ /* * linux/fs/read_write.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include -#include -#include - +#include +#include +#include #include #include #include + #include /* @@ -36,7 +35,7 @@ int sys_readdir(unsigned int fd, struct dirent * dirent, unsigned int count) int sys_lseek(unsigned int fd, off_t offset, unsigned int origin) { struct file * file; - int tmp; + int tmp = -1; if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode)) return -EBADF; @@ -62,6 +61,7 @@ int sys_lseek(unsigned int fd, off_t offset, unsigned int origin) if (tmp < 0) return -EINVAL; file->f_pos = tmp; + file->f_reada = 0; return file->f_pos; } @@ -79,7 +79,6 @@ int sys_read(unsigned int fd,char * buf,unsigned int count) verify_area(buf,count); if (file->f_op && file->f_op->read) return file->f_op->read(inode,file,buf,count); - printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode); return -EINVAL; } @@ -96,6 +95,5 @@ int sys_write(unsigned int fd,char * buf,unsigned int count) return 0; if (file->f_op && file->f_op->write) return file->f_op->write(inode,file,buf,count); - printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode); return -EINVAL; } diff --git a/fs/select.c b/fs/select.c index 98e74c476af1..5cd8e0f01e92 100644 --- a/fs/select.c +++ b/fs/select.c @@ -5,22 +5,20 @@ * patches by Peter MacDonald. Heavily edited by Linus. */ +#include +#include #include #include -#include #include #include +#include +#include +#include #include #include -#include -#include -#include - #include -#include -#include /* * Ok, Peter made a complicated, but straightforward multiple_wait() function. @@ -29,138 +27,44 @@ * understand what I'm doing here, then you understand how the linux sleep/wakeup * mechanism works. * - * Two very simple procedures, add_wait() and free_wait() make all the work. We - * have to have interrupts disabled throughout the select, but that's not really - * such a loss: sleeping automatically frees interrupts when we aren't in this - * task. + * Two very simple procedures, select_wait() and free_wait() make all the work. + * select_wait() is a inline-function defined in , as all select + * functions have to call it to add an entry to the select table. */ -static select_table * sel_tables = NULL; - -static void add_wait(struct task_struct ** wait_address, select_table * p) -{ - int i; - - if (!wait_address) - return; - for (i = 0 ; i < p->nr ; i++) - if (p->entry[i].wait_address == wait_address) - return; - current->next_wait = NULL; - p->entry[p->nr].wait_address = wait_address; - p->entry[p->nr].old_task = *wait_address; - *wait_address = current; - p->nr++; -} - -/* - * free_wait removes the current task from any wait-queues and then - * wakes up the queues. - */ -static void free_one_table(select_table * p) -{ - int i; - struct task_struct ** tpp; - - for(tpp = &LAST_TASK ; tpp > &FIRST_TASK ; --tpp) - if (*tpp && ((*tpp)->next_wait == p->current)) - (*tpp)->next_wait = NULL; - if (!p->nr) - return; - for (i = 0; i < p->nr ; i++) { - wake_up(p->entry[i].wait_address); - wake_up(&p->entry[i].old_task); - } - p->nr = 0; -} - static void free_wait(select_table * p) { - select_table * tmp; + struct select_table_entry * entry = p->entry + p->nr; - if (p->woken) - return; - p = sel_tables; - sel_tables = NULL; - while (p) { - wake_up(&p->current); - p->woken = 1; - tmp = p->next_table; - p->next_table = NULL; - free_one_table(p); - p = tmp; + while (p->nr > 0) { + p->nr--; + entry--; + remove_wait_queue(entry->wait_address,&entry->wait); } } -static struct tty_struct * get_tty(struct inode * inode) -{ - int major, minor; - - if (!S_ISCHR(inode->i_mode)) - return NULL; - if ((major = MAJOR(inode->i_rdev)) != 5 && major != 4) - return NULL; - if (major == 5) - minor = current->tty; - else - minor = MINOR(inode->i_rdev); - if (minor < 0) - return NULL; - return TTY_TABLE(minor); -} - /* * The check_XX functions check out a file. We know it's either - * a pipe, a character device or a fifo (fifo's not implemented) + * a pipe, a character device or a fifo */ -static int check_in(select_table * wait, struct inode * inode) +static int check_in(select_table * wait, struct inode * inode, struct file * file) { - struct tty_struct * tty; - - if (tty = get_tty(inode)) - if (!EMPTY(tty->secondary)) - return 1; - else - add_wait(&tty->secondary->proc_list, wait); - else if (inode->i_pipe) - if (!PIPE_EMPTY(*inode) || inode->i_count < 2) - return 1; - else - add_wait(&inode->i_wait, wait); + if (file->f_op && file->f_op->select) + return file->f_op->select(inode,file,SEL_IN,wait); return 0; } -static int check_out(select_table * wait, struct inode * inode) +static int check_out(select_table * wait, struct inode * inode, struct file * file) { - struct tty_struct * tty; - - if (tty = get_tty(inode)) - if (!FULL(tty->write_q)) - return 1; - else - add_wait(&tty->write_q->proc_list, wait); - else if (inode->i_pipe) - if (!PIPE_FULL(*inode)) - return 1; - else - add_wait(&inode->i_wait, wait); + if (file->f_op && file->f_op->select) + return file->f_op->select(inode,file,SEL_OUT,wait); return 0; } -static int check_ex(select_table * wait, struct inode * inode) +static int check_ex(select_table * wait, struct inode * inode, struct file * file) { - struct tty_struct * tty; - - if (tty = get_tty(inode)) - if (!FULL(tty->write_q)) - return 0; - else - return 0; - else if (inode->i_pipe) - if (inode->i_count < 2) - return 1; - else - add_wait(&inode->i_wait,wait); + if (file->f_op && file->f_op->select) + return file->f_op->select(inode,file,SEL_EX,wait); return 0; } @@ -169,6 +73,7 @@ int do_select(fd_set in, fd_set out, fd_set ex, { int count; select_table wait_table; + struct file * file; int i; fd_set mask; @@ -186,51 +91,49 @@ int do_select(fd_set in, fd_set out, fd_set ex, continue; if (S_ISFIFO(current->filp[i]->f_inode->i_mode)) continue; + if (S_ISSOCK(current->filp[i]->f_inode->i_mode)) + continue; return -EBADF; } repeat: wait_table.nr = 0; - wait_table.woken = 0; - wait_table.current = current; - wait_table.next_table = sel_tables; - sel_tables = &wait_table; *inp = *outp = *exp = 0; count = 0; + current->state = TASK_INTERRUPTIBLE; mask = 1; for (i = 0 ; i < NR_OPEN ; i++, mask += mask) { + file = current->filp[i]; if (mask & in) - if (check_in(&wait_table,current->filp[i]->f_inode)) { + if (check_in(&wait_table,file->f_inode,file)) { *inp |= mask; count++; } if (mask & out) - if (check_out(&wait_table,current->filp[i]->f_inode)) { + if (check_out(&wait_table,file->f_inode,file)) { *outp |= mask; count++; } if (mask & ex) - if (check_ex(&wait_table,current->filp[i]->f_inode)) { + if (check_ex(&wait_table,file->f_inode,file)) { *exp |= mask; count++; } } if (!(current->signal & ~current->blocked) && current->timeout && !count) { - current->state = TASK_INTERRUPTIBLE; - sti(); schedule(); - cli(); free_wait(&wait_table); goto repeat; } free_wait(&wait_table); + current->state = TASK_RUNNING; return count; } /* - * Note that we cannot return -ERESTARTSYS, as we change our input - * parameters. Sad, but there you are. We could do some tweaking in - * the library function ... + * We can actually return ERESTARTSYS insetad of EINTR, but I'd + * like to be certain this leads to no problems. So I return + * EINTR just for safety. */ int sys_select( unsigned long *buffer ) { @@ -266,16 +169,23 @@ int sys_select( unsigned long *buffer ) timeout += jiffies; } current->timeout = timeout; - cli(); i = do_select(in, out, ex, &res_in, &res_out, &res_ex); if (current->timeout > jiffies) timeout = current->timeout - jiffies; else timeout = 0; - sti(); current->timeout = 0; + if (tvp) { + verify_area(tvp, sizeof(*tvp)); + put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec); + timeout %= HZ; + timeout *= (1000000/HZ); + put_fs_long(timeout, (unsigned long *) &tvp->tv_usec); + } if (i < 0) return i; + if (!i && (current->signal & ~current->blocked)) + return -EINTR; if (inp) { verify_area(inp, 4); put_fs_long(res_in,inp); @@ -288,16 +198,5 @@ int sys_select( unsigned long *buffer ) verify_area(exp,4); put_fs_long(res_ex,exp); } - if (tvp) { - verify_area(tvp, sizeof(*tvp)); - put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec); - timeout %= HZ; - timeout *= (1000000/HZ); - put_fs_long(timeout, (unsigned long *) &tvp->tv_usec); - } - if (i) - return i; - if (current->signal & ~current->blocked) - return -EINTR; - return 0; + return i; } diff --git a/fs/stat.c b/fs/stat.c index 029ec2ff7bf9..225f9d3bbecf 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -1,23 +1,23 @@ /* * linux/fs/stat.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include - +#include +#include #include #include #include #include -static void cp_stat(struct inode * inode, struct stat * statbuf) +static void cp_old_stat(struct inode * inode, struct old_stat * statbuf) { - struct stat tmp; - int i; + struct old_stat tmp; - verify_area(statbuf,sizeof (struct stat)); + if (inode->i_ino & 0xffff0000) + printk("Warning: using old stat() call on bigfs\n"); + verify_area(statbuf,sizeof (*statbuf)); tmp.st_dev = inode->i_dev; tmp.st_ino = inode->i_ino; tmp.st_mode = inode->i_mode; @@ -25,44 +25,123 @@ static void cp_stat(struct inode * inode, struct stat * statbuf) tmp.st_uid = inode->i_uid; tmp.st_gid = inode->i_gid; tmp.st_rdev = inode->i_rdev; - tmp.st_size = inode->i_size; + if( S_ISFIFO(inode->i_mode) ) + tmp.st_size = 0; + else + tmp.st_size = inode->i_size; tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; - for (i=0 ; ii_dev; + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + tmp.st_uid = inode->i_uid; + tmp.st_gid = inode->i_gid; + tmp.st_rdev = inode->i_rdev; + if( S_ISFIFO(inode->i_mode) ) + tmp.st_size = 0; + else + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; +/* + * Right now we fake the st_blocks numbers: we'll eventually have to + * add st_blocks to the inode, and let the vfs routines keep track of + * it all. This algorithm doesn't guarantee correct block numbers, but + * at least it tries to come up with a plausible answer... + * + * In fact, the minix fs doesn't use these numbers (it uses 7 and 512 + * instead of 10 and 256), but who cares... It's not that exact anyway. + */ + blocks = (tmp.st_size + 1023) / 1024; + if (blocks > 10) { + indirect = (blocks - 11)/256+1; + if (blocks > 10+256) { + indirect += (blocks - 267)/(256*256)+1; + if (blocks > 10+256+256*256) + indirect++; + } + blocks += indirect; + } + tmp.st_blksize = 1024; + tmp.st_blocks = blocks; + memcpy_tofs(statbuf,&tmp,sizeof(tmp)); +} + +int sys_stat(char * filename, struct old_stat * statbuf) +{ + struct inode * inode; + + if (!(inode=namei(filename))) + return -ENOENT; + cp_old_stat(inode,statbuf); + iput(inode); + return 0; } -int sys_stat(char * filename, struct stat * statbuf) +int sys_newstat(char * filename, struct new_stat * statbuf) { struct inode * inode; if (!(inode=namei(filename))) return -ENOENT; - cp_stat(inode,statbuf); + cp_new_stat(inode,statbuf); iput(inode); return 0; } -int sys_lstat(char * filename, struct stat * statbuf) +int sys_lstat(char * filename, struct old_stat * statbuf) { struct inode * inode; if (!(inode = lnamei(filename))) return -ENOENT; - cp_stat(inode,statbuf); + cp_old_stat(inode,statbuf); iput(inode); return 0; } -int sys_fstat(unsigned int fd, struct stat * statbuf) +int sys_newlstat(char * filename, struct new_stat * statbuf) +{ + struct inode * inode; + + if (!(inode = lnamei(filename))) + return -ENOENT; + cp_new_stat(inode,statbuf); + iput(inode); + return 0; +} + +int sys_fstat(unsigned int fd, struct old_stat * statbuf) +{ + struct file * f; + struct inode * inode; + + if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode)) + return -EBADF; + cp_old_stat(inode,statbuf); + return 0; +} + +int sys_newfstat(unsigned int fd, struct new_stat * statbuf) { struct file * f; struct inode * inode; if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode)) return -EBADF; - cp_stat(inode,statbuf); + cp_new_stat(inode,statbuf); return 0; } diff --git a/fs/super.c b/fs/super.c index 78f8b6c0a4ce..4d0e0303faf5 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1,7 +1,7 @@ /* * linux/fs/super.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -10,13 +10,15 @@ #include #include #include +#include +#include #include +#include +#include + #include #include -#include -#include - int sync_dev(int dev); void wait_for_keypress(void); @@ -34,6 +36,8 @@ int ROOT_DEV = 0; static struct file_system_type file_systems[] = { {minix_read_super,"minix"}, + {ext_read_super,"ext"}, + {msdos_read_super,"msdos"}, {NULL,NULL} }; @@ -60,10 +64,8 @@ void lock_super(struct super_block * sb) void free_super(struct super_block * sb) { - cli(); sb->s_lock = 0; wake_up(&(sb->s_wait)); - sti(); } void wait_on_super(struct super_block * sb) @@ -110,7 +112,7 @@ void put_super(int dev) sb->s_op->put_super(sb); } -static struct super_block * read_super(int dev,char *name,void *data) +static struct super_block * read_super(int dev,char *name,int flags,void *data) { struct super_block * s; struct file_system_type *type; @@ -131,32 +133,21 @@ static struct super_block * read_super(int dev,char *name,void *data) break; } s->s_dev = dev; + s->s_flags = flags; if (!type->read_super(s,data)) return(NULL); s->s_dev = dev; s->s_covered = NULL; - s->s_time = 0; s->s_rd_only = 0; s->s_dirt = 0; return(s); } -int sys_umount(char * dev_name) +static int do_umount(int dev) { - struct inode * inode; struct super_block * sb; - int dev; + struct inode * inode; - if (!suser()) - return -EPERM; - if (!(inode = namei(dev_name))) - return -ENOENT; - dev = inode->i_rdev; - if (!S_ISBLK(inode->i_mode)) { - iput(inode); - return -ENOTBLK; - } - iput(inode); if (dev==ROOT_DEV) return -EBUSY; if (!(sb=get_super(dev)) || !(sb->s_covered)) @@ -174,32 +165,53 @@ int sys_umount(char * dev_name) sb->s_covered = NULL; iput(sb->s_mounted); sb->s_mounted = NULL; + if (sb->s_op && sb->s_op->write_super && sb->s_dirt) + sb->s_op->write_super (sb); put_super(dev); - sync_dev(dev); return 0; } -int sys_mount(char * dev_name, char * dir_name, char * type, int rw_flag) +int sys_umount(char * dev_name) { - struct inode * dev_i, * dir_i; - struct super_block * sb; - int dev; - char tmp[100],*t; - int i; + struct inode * inode; + int dev,retval; if (!suser()) return -EPERM; - if (!(dev_i = namei(dev_name))) + if (!(inode = namei(dev_name))) return -ENOENT; - dev = dev_i->i_rdev; - if (!S_ISBLK(dev_i->i_mode)) { - iput(dev_i); - return -EPERM; + dev = inode->i_rdev; + if (!S_ISBLK(inode->i_mode)) { + iput(inode); + return -ENOTBLK; } - iput(dev_i); - if (!(dir_i=namei(dir_name))) + retval = do_umount(dev); + if (!retval && MAJOR(dev) < MAX_BLKDEV && + blkdev_fops[MAJOR(dev)]->release) + blkdev_fops[MAJOR(dev)]->release(inode,NULL); + iput(inode); + if (retval) return retval; + sync_dev(dev); + return 0; +} + +/* + * do_mount() does the actual mounting after sys_mount has done the ugly + * parameter parsing. When enough time has gone by, and everything uses the + * new mount() parameters, sys_mount() can then be cleaned up. + * + * We cannot mount a filesystem if it has active, used, or dirty inodes. + * We also have to flush all inode-data for this device, as the new mount + * might need new info. + */ +static int do_mount(int dev, const char * dir, char * type, int flags, void * data) +{ + struct inode * inode, * dir_i; + struct super_block * sb; + + if (!(dir_i = namei(dir))) return -ENOENT; - if (dir_i->i_count != 1 || dir_i->i_ino == MINIX_ROOT_INO) { + if (dir_i->i_count != 1 || dir_i->i_mount) { iput(dir_i); return -EBUSY; } @@ -207,34 +219,94 @@ int sys_mount(char * dev_name, char * dir_name, char * type, int rw_flag) iput(dir_i); return -EPERM; } - if (dir_i->i_mount) { + for (inode = inode_table+0 ; inode < inode_table+NR_INODE ; inode++) { + if (inode->i_dev != dev) + continue; + if (inode->i_count || inode->i_dirt || inode->i_lock) { + iput(dir_i); + return -EBUSY; + } + inode->i_dev = 0; + } + sb = read_super(dev,type,flags,data); + if (!sb || sb->s_covered) { iput(dir_i); + return -EBUSY; + } + sb->s_flags = flags; + sb->s_covered = dir_i; + dir_i->i_mount = 1; + return 0; /* we don't iput(dir_i) - see umount */ +} + +/* + * Flags is a 16-bit value that allows up to 16 non-fs dependent flags to + * be given to the mount() call (ie: read-only, no-dev, no-suid etc). + * + * data is a (void *) that can point to any structure up to 4095 bytes, which + * can contain arbitrary fs-dependent information (or be NULL). + * + * NOTE! As old versions of mount() didn't use this setup, the flags has to have + * a special 16-bit magic number in the hight word: 0xC0ED. If this magic word + * isn't present, the flags and data info isn't used, as the syscall assumes we + * are talking to an older version that didn't understand them. + */ +int sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void *data) +{ + struct inode * inode; + int dev; + int retval = 0; + char tmp[100],*t; + int i; + unsigned long flags = 0; + unsigned long page = 0; + + if (!suser()) return -EPERM; + if (!(inode = namei(dev_name))) + return -ENOENT; + dev = inode->i_rdev; + if (!S_ISBLK(inode->i_mode)) + retval = -EPERM; + else if (IS_NODEV(inode)) + retval = -EACCES; + if (!retval && blkdev_fops[MAJOR(dev)]->open) + retval = blkdev_fops[MAJOR(dev)]->open(inode,NULL); + if (retval) { + iput(inode); + return retval; + } + if ((new_flags & 0xffff0000) == 0xC0ED0000) { + flags = new_flags & 0xffff; + if (data && (unsigned long) data < TASK_SIZE) + page = get_free_page(GFP_KERNEL); + } + if (page) { + i = TASK_SIZE - (unsigned long) data; + if (i < 0 || i > 4095) + i = 4095; + memcpy_fromfs((void *) page,data,i); } if (type) { - i = 0; - while (i < 100 && (tmp[i] = get_fs_byte(type++))) - i++; + for (i = 0 ; i < 100 ; i++) + if (!(tmp[i] = get_fs_byte(type++))) + break; t = tmp; } else t = "minix"; - if (!(sb = read_super(dev,t,NULL))) { - iput(dir_i); - return -EBUSY; - } - if (sb->s_covered) { - iput(dir_i); - return -EBUSY; - } - sb->s_covered = dir_i; - dir_i->i_mount = 1; - dir_i->i_dirt = 1; /* NOTE! we don't iput(dir_i) */ - return 0; /* we do that in umount */ + retval = do_mount(dev,dir_name,t,flags,(void *) page); + free_page(page); + if (retval && blkdev_fops[MAJOR(dev)]->release) + blkdev_fops[MAJOR(dev)]->release(inode,NULL); + iput(inode); + return retval; } void mount_root(void) { - int i,free; + int i; + struct file_system_type * fs_type = file_systems; struct super_block * p; struct inode * mi; @@ -248,30 +320,23 @@ void mount_root(void) } for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) { p->s_dev = 0; + p->s_blocksize = 0; p->s_lock = 0; p->s_wait = NULL; + p->s_mounted = p->s_covered = NULL; + } + while (fs_type->read_super && fs_type->name) { + p = read_super(ROOT_DEV,fs_type->name,0,NULL); + if (p) { + mi = p->s_mounted; + mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ + p->s_covered = mi; + p->s_flags = 0; + current->pwd = mi; + current->root = mi; + return; + } + fs_type++; } - if (!(p=read_super(ROOT_DEV,"minix",NULL))) - panic("Unable to mount root"); - /*wait_for_keypress(); - if (!(mi=iget(ROOT_DEV,MINIX_ROOT_INO))) - panic("Unable to read root i-node"); - wait_for_keypress();*/ - mi=p->s_mounted; - mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ - p->s_mounted = p->s_covered = mi; - current->pwd = mi; - current->root = mi; - free=0; - i=p->s_nzones; - while (-- i >= 0) - if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data)) - free++; - printk("%d/%d free blocks\n\r",free,p->s_nzones); - free=0; - i=p->s_ninodes+1; - while (-- i >= 0) - if (!set_bit(i&8191,p->s_imap[i>>13]->b_data)) - free++; - printk("%d/%d free inodes\n\r",free,p->s_ninodes); + panic("Unable to mount root"); } diff --git a/include/asm/io.h b/include/asm/io.h index 226f3439bf60..7c54b0f5d4da 100644 --- a/include/asm/io.h +++ b/include/asm/io.h @@ -1,27 +1,36 @@ #ifndef _ASM_IO_H #define _ASM_IO_H +/* + * Thanks to James van Artsdalen for a better timing-fix than + * the two short jumps: using outb's to a nonexistent port seems + * to guarantee better timings even on fast machines. + * + * Linus + */ + extern void inline outb(char value, unsigned short port) { -__asm__ volatile ("outb %0,%1" +__asm__ __volatile__ ("outb %0,%1" ::"a" ((char) value),"d" ((unsigned short) port)); } extern void inline outb_p(char value, unsigned short port) { -__asm__ volatile ("outb %0,%1\n" - "\tjmp 1f\n" - "1:\tjmp 1f\n" - "1:\tjmp 1f\n" - "1:\tjmp 1f\n" - "1:" +__asm__ __volatile__ ("outb %0,%1\n\t" +#ifdef REALLY_SLOW_IO + "outb %0,$0x80\n\t" + "outb %0,$0x80\n\t" + "outb %0,$0x80\n\t" +#endif + "outb %0,$0x80" ::"a" ((char) value),"d" ((unsigned short) port)); } extern unsigned char inline inb(unsigned short port) { unsigned char _v; -__asm__ volatile ("inb %1,%0" +__asm__ __volatile__ ("inb %1,%0" :"=a" (_v):"d" ((unsigned short) port)); return _v; } @@ -29,12 +38,13 @@ __asm__ volatile ("inb %1,%0" extern unsigned char inline inb_p(unsigned short port) { unsigned char _v; -__asm__ volatile ("inb %1,%0\n" - "\tjmp 1f\n" - "1:\tjmp 1f\n" - "1:\tjmp 1f\n" - "1:\tjmp 1f\n" - "1:" +__asm__ __volatile__ ("inb %1,%0\n\t" +#ifdef REALLY_SLOW_IO + "outb %0,$0x80\n\t" + "outb %0,$0x80\n\t" + "outb %0,$0x80\n\t" +#endif + "outb %0,$0x80" :"=a" (_v):"d" ((unsigned short) port)); return _v; } diff --git a/include/asm/irq.h b/include/asm/irq.h new file mode 100644 index 000000000000..7e2b52452811 --- /dev/null +++ b/include/asm/irq.h @@ -0,0 +1,147 @@ +#ifndef _ASM_IRQ_H +#define _ASM_IRQ_H + +/* + * linux/include/asm/irq.h + * + * (C) 1992 Linus Torvalds + */ + +#define SAVE_ALL \ + "cld\n\t" \ + "push %gs\n\t" \ + "push %fs\n\t" \ + "push %es\n\t" \ + "push %ds\n\t" \ + "pushl %eax\n\t" \ + "pushl %ebp\n\t" \ + "pushl %edi\n\t" \ + "pushl %esi\n\t" \ + "pushl %edx\n\t" \ + "pushl %ecx\n\t" \ + "pushl %ebx\n\t" \ + "movl $0x10,%edx\n\t" \ + "mov %dx,%ds\n\t" \ + "mov %dx,%es\n\t" \ + "movl $0x17,%edx\n\t" \ + "mov %dx,%fs\n\t" + +/* + * SAVE_MOST/RESTORE_MOST is used for the faster version of IRQ handlers, + * installed by using the SA_INTERRUPT flag. These kinds of IRQ's don't + * call the routines that do signal handling etc on return, and can have + * more relaxed register-saving etc. They are also atomic, and are thus + * suited for small, fast interrupts like the serial lines or the harddisk + * drivers, which don't actually need signal handling etc. + * + * Also note that we actually save only those registers that are used in + * C subroutines (%eax, %edx and %ecx), so if you do something weird, + * you're on your own. The only segments that are saved (not counting the + * automatic stack and code segment handling) are %ds and %es, and they + * point to kernel space. No messing around with %fs here. + */ +#define SAVE_MOST \ + "cld\n\t" \ + "push %es\n\t" \ + "push %ds\n\t" \ + "pushl %eax\n\t" \ + "pushl %edx\n\t" \ + "pushl %ecx\n\t" \ + "movl $0x10,%edx\n\t" \ + "mov %dx,%ds\n\t" \ + "mov %dx,%es\n\t" + +#define RESTORE_MOST \ + "popl %ecx\n\t" \ + "popl %edx\n\t" \ + "popl %eax\n\t" \ + "pop %ds\n\t" \ + "pop %es\n\t" \ + "iret" + +#define ACK_FIRST(mask) \ + "inb $0x21,%al\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\torb $" #mask ",%al\n\t" \ + "outb %al,$0x21\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\tmovb $0x20,%al\n\t" \ + "outb %al,$0x20\n\t" + +#define ACK_SECOND(mask) \ + "inb $0xA1,%al\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\torb $" #mask ",%al\n\t" \ + "outb %al,$0xA1\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\tmovb $0x20,%al\n\t" \ + "outb %al,$0xA0\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\toutb %al,$0x20\n\t" + +#define UNBLK_FIRST(mask) \ + "inb $0x21,%al\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\tandb $~(" #mask "),%al\n\t" \ + "outb %al,$0x21\n\t" + +#define UNBLK_SECOND(mask) \ + "inb $0xA1,%al\n\t" \ + "jmp 1f\n" \ + "1:\tjmp 1f\n" \ + "1:\tandb $~(" #mask "),%al\n\t" \ + "outb %al,$0xA1\n\t" + +#define IRQ_NAME2(nr) nr##_interrupt() +#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) +#define FAST_IRQ_NAME(nr) IRQ_NAME2(fast_IRQ##nr) +#define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr) + +#define BUILD_IRQ(chip,nr,mask) \ +void IRQ_NAME(nr); \ +void FAST_IRQ_NAME(nr); \ +void BAD_IRQ_NAME(nr); \ +__asm__( \ +"\n.align 2\n" \ +"_IRQ" #nr "_interrupt:\n\t" \ + "pushl $-1\n\t" \ + SAVE_ALL \ + ACK_##chip(mask) \ + "sti\n\t" \ + "movl %esp,%ebx\n\t" \ + "pushl %ebx\n\t" \ + "pushl $" #nr "\n\t" \ + "call _do_IRQ\n\t" \ + "addl $8,%esp\n\t" \ + "testl %eax,%eax\n\t" \ + "jne ret_from_sys_call\n\t" \ + "cli\n\t" \ + UNBLK_##chip(mask) \ + "jmp ret_from_sys_call\n" \ +"\n.align 2\n" \ +"_fast_IRQ" #nr "_interrupt:\n\t" \ + SAVE_MOST \ + ACK_##chip(mask) \ + "pushl $" #nr "\n\t" \ + "call _do_fast_IRQ\n\t" \ + "addl $4,%esp\n\t" \ + "testl %eax,%eax\n\t" \ + "jne 2f\n\t" \ + "cli\n\t" \ + UNBLK_##chip(mask) \ + "\n2:\t" \ + RESTORE_MOST \ +"\n\n.align 2\n" \ +"_bad_IRQ" #nr "_interrupt:\n\t" \ + "pushl %eax\n\t" \ + ACK_##chip(mask) \ + "popl %eax\n\t" \ + "iret"); + +#endif diff --git a/include/asm/memory.h b/include/asm/memory.h index 4b0a98e7be4c..0d2b7a29fd2e 100644 --- a/include/asm/memory.h +++ b/include/asm/memory.h @@ -7,7 +7,7 @@ */ #define memcpy(dest,src,n) ({ \ void * _res = dest; \ -__asm__ ("cld;rep;movsb" \ +__asm__ __volatile__ ("cld;rep;movsb" \ ::"D" ((long)(_res)),"S" ((long)(src)),"c" ((long) (n)) \ :"di","si","cx"); \ _res; \ diff --git a/include/asm/segment.h b/include/asm/segment.h index cf2890c27e04..b354a8a5fc6b 100644 --- a/include/asm/segment.h +++ b/include/asm/segment.h @@ -94,6 +94,6 @@ extern inline unsigned long get_ds() extern inline void set_fs(unsigned long val) { - __asm__("mov %0,%%fs"::"r" ((unsigned short) val)); + __asm__ __volatile__("mov %0,%%fs"::"r" ((unsigned short) val)); } diff --git a/include/asm/system.h b/include/asm/system.h index 6fbcf1bba082..877ae34777e1 100644 --- a/include/asm/system.h +++ b/include/asm/system.h @@ -1,5 +1,5 @@ #define move_to_user_mode() \ -__asm__ ("movl %%esp,%%eax\n\t" \ +__asm__ __volatile__ ("movl %%esp,%%eax\n\t" \ "pushl $0x17\n\t" \ "pushl %%eax\n\t" \ "pushfl\n\t" \ @@ -13,14 +13,14 @@ __asm__ ("movl %%esp,%%eax\n\t" \ "mov %%ax,%%gs" \ :::"ax") -#define sti() __asm__ ("sti"::) -#define cli() __asm__ ("cli"::) -#define nop() __asm__ ("nop"::) +#define sti() __asm__ __volatile__ ("sti"::) +#define cli() __asm__ __volatile__ ("cli"::) +#define nop() __asm__ __volatile__ ("nop"::) -#define iret() __asm__ ("iret"::) +#define iret() __asm__ __volatile__ ("iret"::) #define _set_gate(gate_addr,type,dpl,addr) \ -__asm__ ("movw %%dx,%%ax\n\t" \ +__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ "movw %0,%%dx\n\t" \ "movl %%eax,%1\n\t" \ "movl %%edx,%2" \ @@ -50,7 +50,7 @@ __asm__ ("movw %%dx,%%ax\n\t" \ ((limit) & 0x0ffff); } #define _set_tssldt_desc(n,addr,type) \ -__asm__ ("movw $232,%1\n\t" \ +__asm__ __volatile__ ("movw $232,%1\n\t" \ "movw %%ax,%2\n\t" \ "rorl $16,%%eax\n\t" \ "movb %%al,%3\n\t" \ diff --git a/include/errno.h b/include/errno.h deleted file mode 100644 index fdb1b14b58c6..000000000000 --- a/include/errno.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _ERRNO_H -#define _ERRNO_H - -/* - * ok, as I hadn't got any other source of information about - * possible error numbers, I was forced to use the same numbers - * as minix. - * Hopefully these are posix or something. I wouldn't know (and posix - * isn't telling me - they want $$$ for their f***ing standard). - * - * We don't use the _SIGN cludge of minix, so kernel returns must - * see to the sign by themselves. - * - * NOTE! Remember to change strerror() if you change this file! - */ - -extern int errno; - -#define EPERM 1 -#define ENOENT 2 -#define ESRCH 3 -#define EINTR 4 -#define EIO 5 -#define ENXIO 6 -#define E2BIG 7 -#define ENOEXEC 8 -#define EBADF 9 -#define ECHILD 10 -#define EAGAIN 11 -#define ENOMEM 12 -#define EACCES 13 -#define EFAULT 14 -#define ENOTBLK 15 -#define EBUSY 16 -#define EEXIST 17 -#define EXDEV 18 -#define ENODEV 19 -#define ENOTDIR 20 -#define EISDIR 21 -#define EINVAL 22 -#define ENFILE 23 -#define EMFILE 24 -#define ENOTTY 25 -#define ETXTBSY 26 -#define EFBIG 27 -#define ENOSPC 28 -#define ESPIPE 29 -#define EROFS 30 -#define EMLINK 31 -#define EPIPE 32 -#define EDOM 33 -#define ERANGE 34 -#define EDEADLK 35 -#define ENAMETOOLONG 36 -#define ENOLCK 37 -#define ENOSYS 38 -#define ENOTEMPTY 39 -#define ELOOP 40 - -/* Should never be seen by user programs */ -#define ERESTARTSYS 512 -#define ERESTARTNOINTR 513 - -#endif diff --git a/include/limits.h b/include/limits.h deleted file mode 100644 index 1303adfd2cc3..000000000000 --- a/include/limits.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _LIMITS_H -#define _LIMITS_H - -#define RAND_MAX 0x7ffffffd /* don't ask - see rand.c */ - -#define CHAR_BIT 8 -#define MB_LEN_MAX 1 - -#define SCHAR_MIN (-128) -#define SCHAR_MAX 127 - -#define UCHAR_MAX 255U - -#ifdef __CHAR_UNSIGNED__ -#define CHAR_MIN 0 -#define CHAR_MAX UCHAR_MAX -#else -#define CHAR_MIN SCHAR_MIN -#define CHAR_MAX SCHAR_MAX -#endif - -#define SHRT_MIN (-32768) -#define SHRT_MAX 32767 - -#define USHRT_MAX 65535U - -#define INT_MIN (-2147483648) -#define INT_MAX 2147483647 - -#define UINT_MAX 4294967295U - -#define LONG_MIN (-2147483648) -#define LONG_MAX 2147483647 - -#define ULONG_MAX 4294967295U - -/* - * Why are these different from the section below? -- TYT - */ -#define _POSIX_ARG_MAX 40960 /* exec() may have 40K worth of args */ -#define _POSIX_CHILD_MAX 6 /* a process may have 6 children */ -#define _POSIX_LINK_MAX 8 /* a file may have 8 links */ -#define _POSIX_MAX_CANON 255 /* size of the canonical input queue */ -#define _POSIX_MAX_INPUT 255 /* you can type 255 chars ahead */ -#define _POSIX_NAME_MAX 14 /* a file name may have 14 chars */ -#define _POSIX_NGROUPS_MAX 32 /* supplementary group IDs are optional */ -#define _POSIX_OPEN_MAX 16 /* a process may have 16 files open */ -#define _POSIX_PATH_MAX 255 /* a pathname may contain 255 chars */ -#define _POSIX_PIPE_BUF 512 /* pipes writes of 512 bytes must be atomic */ - -#define NGROUPS_MAX 32 /* supplemental group IDs are available */ -#define ARG_MAX 40960 /* # bytes of args + environ for exec() */ -#define CHILD_MAX 999 /* no limit :-) */ -#define OPEN_MAX 20 /* # open files a process may have */ -#define LINK_MAX 127 /* # links a file may have */ -#define MAX_CANON 255 /* size of the canonical input queue */ -#define MAX_INPUT 255 /* size of the type-ahead buffer */ -#define NAME_MAX 255 /* # chars in a file name */ -#define PATH_MAX 1024 /* # chars in a path name */ -#define PIPE_BUF 4095 /* # bytes in atomic write to a pipe */ - -#endif diff --git a/include/a.out.h b/include/linux/a.out.h similarity index 99% rename from include/a.out.h rename to include/linux/a.out.h index 5468aa41ec3e..69bf01f3749e 100644 --- a/include/a.out.h +++ b/include/linux/a.out.h @@ -72,6 +72,8 @@ enum machine_type { /* Code indicating demand-paged executable. */ #define ZMAGIC 0413 +/* Code indicating core file. */ +#define CMAGIC 0421 #if !defined (N_BADMAG) #define N_BADMAG(x) \ (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ diff --git a/include/linux/config.dist.h b/include/linux/config.dist.h new file mode 100644 index 000000000000..15b6e4be4167 --- /dev/null +++ b/include/linux/config.dist.h @@ -0,0 +1,31 @@ +#ifndef _CONFIG_DIST_H +#define _CONFIG_DIST_H +#ifdef CONFIG_DISTRIBUTION + +#undef CONFG_SCSI +#define CONFIG_SCSI + +#undef CONFIG_SCSI_AHA1542 +#define CONFIG_SCSI_AHA1542 +#undef CONFIG_SCSI_CSC +#define CONFIG_SCSI_CSC +#undef CONFIG_SCSI_DTC +#define CONFIG_SCSI_DTC +#undef CONFIG_SCSI_FUTURE_DOMAIN +#define CONFIG_SCSI_FUTURE_DOMAIN +#undef CONFIG_SCSI_SEAGATE +#define CONFIG_SCSI_SEAGATE +#undef CONFIG_SCSI_ULTRASTOR +#define CONFIG_SCSI_ULTRASTOR +#undef CONFIG_SCSI_7000FASST +#define CONFIG_SCSI_7000FASST + +#undef CONFIG_BLK_DEV_HD +#define CONFIG_BLK_DEV_HD +#undef CONFIG_BLK_DEV_SD +#define CONFIG_BLK_DEV_SD +#undef CONFIG_BLK_DEV_ST +#define CONFIG_BLK_DEV_ST + +#endif +#endif diff --git a/include/linux/config.h b/include/linux/config.h index 8c79f935c375..ef79c388ae93 100644 --- a/include/linux/config.h +++ b/include/linux/config.h @@ -1,6 +1,8 @@ #ifndef _CONFIG_H #define _CONFIG_H +#define CONFIG_DISTRIBUTION + /* * Defines for what uname() should return */ @@ -24,7 +26,7 @@ #define DEF_INITSEG 0x9000 #define DEF_SYSSEG 0x1000 #define DEF_SETUPSEG 0x9020 -#define DEF_SYSSIZE 0x4000 +#define DEF_SYSSIZE 0x5000 /* * The root-device is no longer hard-coded. You can change the default @@ -62,4 +64,56 @@ leave HD_TYPE undefined. This is the normal thing to do. */ +#undef HD_TYPE + +#define CONFIG_BLK_DEV_HD +#undef CONFIG_BLK_DEV_SD +#undef CONFIG_BLK_DEV_ST + + +/* + Choose supported SCSI adapters here. +*/ + +#undef CONFIG_SCSI_AHA1542 +#undef CONFIG_SCSI_ALWAYS +#undef CONFIG_SCSI_CSC +#undef CONFIG_SCSI_DTC +#undef CONFIG_SCSI_FUTURE_DOMAIN +#undef CONFIG_SCSI_SEAGATE +#undef CONFIG_SCSI_ULTRASTOR +#undef CONFIG_SCSI_7000FASST + +#if defined(CONFIG_BLK_DEV_SD) || defined(CONFIG_BLK_DEV_CD) || \ +defined(CONFIG_CHR_DEV_ST) +#ifndef CONFIG_SCSI + #define CONFIG_SCSI +#endif + +#if !defined(CONFIG_SCSI_AHA1542) && !defined(CONFIG_SCSI_CSC) && !defined(CONFIG_SCSI_DTC) && \ + !defined(CONFIG_SCSI_FUTURE_DOMAIN) && !defined(CONFIG_SCSI_SEAGATE) && !defined(CONFIG_SCSI_ULTRASTOR) && \ + !defined(CONFIG_SCSI_7000FASST) +#error Error : SCSI devices enabled, but no low level drivers have been enabled. +#endif +#endif + +#ifdef CONFIG_DISTRIBUTION +#include +#else +#include +#endif + +/* + File type specific stuff goes into this. +*/ + +#ifdef ASM_SRC +#endif + +#ifdef C_SRC +#endif + +#ifdef MAKE +#endif + #endif diff --git a/include/linux/config.site.h b/include/linux/config.site.h new file mode 100644 index 000000000000..49b731bb0499 --- /dev/null +++ b/include/linux/config.site.h @@ -0,0 +1,9 @@ +#ifndef _CONFIG_SITE_H +#define _CONFIG_SITE_H + +/* + This configuration file contains site specific things, things + that you have added and config.dist will not know about. +*/ + +#endif diff --git a/include/linux/config_rel.h b/include/linux/config_rel.h index a602c1f56132..bd66deea0b7c 100644 --- a/include/linux/config_rel.h +++ b/include/linux/config_rel.h @@ -1 +1 @@ -#define UTS_RELEASE "0.95c-54" +#define UTS_RELEASE "0.97-11" diff --git a/include/linux/config_ver.h b/include/linux/config_ver.h index 34e98a9644b4..e09eeab5d95d 100644 --- a/include/linux/config_ver.h +++ b/include/linux/config_ver.h @@ -1 +1 @@ -#define UTS_VERSION "04/22/92" +#define UTS_VERSION "08/01/92" diff --git a/include/sys/dirent.h b/include/linux/dirent.h similarity index 62% rename from include/sys/dirent.h rename to include/linux/dirent.h index d0873a9ba823..01d16c8aa105 100644 --- a/include/sys/dirent.h +++ b/include/linux/dirent.h @@ -1,7 +1,5 @@ -#ifndef _SYS_DIRENT_H -#define _SYS_DIRENT_H - -#include +#ifndef _LINUX_DIRENT_H +#define _LINUX_DIRENT_H struct dirent { long d_ino; diff --git a/include/linux/errno.h b/include/linux/errno.h new file mode 100644 index 000000000000..420d5ff0a8bf --- /dev/null +++ b/include/linux/errno.h @@ -0,0 +1,130 @@ +#ifndef _LINUX_ERRNO_H +#define _LINUX_ERRNO_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK 41 /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ +#define EDEADLOCK 58 /* File locking deadlock error */ +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ + +/* Should never be seen by user programs */ +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 + +#endif diff --git a/include/linux/ext_fs.h b/include/linux/ext_fs.h new file mode 100644 index 000000000000..c4b3cf435d36 --- /dev/null +++ b/include/linux/ext_fs.h @@ -0,0 +1,128 @@ +#ifndef _EXT_FS_H +#define _EXT_FS_H + +/* + * The ext filesystem constants/structures + */ + +/* + * Free blocks/inodes management style + * + * One of these two constants must be defined + * + */ +/* #define EXTFS_BITMAP */ /* use a bitmap */ +#define EXTFS_FREELIST /* use a linked list */ + +#define EXT_NAME_LEN 255 +#define EXT_ROOT_INO 1 + +#define EXT_I_MAP_SLOTS 8 +#define EXT_Z_MAP_SLOTS 8 +#define EXT_SUPER_MAGIC 0x137D + +#define EXT_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct ext_inode))) +/* #define EXT_DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct ext_dir_entry))) */ + +struct ext_inode { + unsigned short i_mode; + unsigned short i_uid; + unsigned long i_size; + unsigned long i_time; + unsigned short i_gid; + unsigned short i_nlinks; + unsigned long i_zone[12]; +}; + +struct ext_free_inode { + unsigned long count; + unsigned long free[14]; + unsigned long next; +}; + +struct ext_free_block { + unsigned long count; + unsigned long free[254]; + unsigned long next; +}; + +struct ext_super_block { + unsigned long s_ninodes; + unsigned long s_nzones; +#ifdef EXTFS_BITMAP + unsigned long s_imap_blocks; + unsigned long s_zmap_blocks; +#endif +#ifdef EXTFS_FREELIST + unsigned long s_firstfreeblock; + unsigned long s_freeblockscount; + unsigned long s_firstfreeinode; + unsigned long s_freeinodescount; +#endif + unsigned long s_firstdatazone; + unsigned long s_log_zone_size; + unsigned long s_max_size; + unsigned long s_reserved1; + unsigned long s_reserved2; + unsigned long s_reserved3; + unsigned long s_reserved4; + unsigned long s_reserved5; + unsigned short s_magic; +}; + +struct ext_dir_entry { + unsigned long inode; + unsigned short rec_len; + unsigned short name_len; + char name[EXT_NAME_LEN]; +}; + +extern int ext_open(struct inode * inode, struct file * filp); +extern void ext_release(struct inode * inode, struct file * filp); +extern int ext_lookup(struct inode * dir,const char * name, int len, + struct inode ** result); +extern int ext_create(struct inode * dir,const char * name, int len, int mode, + struct inode ** result); +extern int ext_mkdir(struct inode * dir, const char * name, int len, int mode); +extern int ext_rmdir(struct inode * dir, const char * name, int len); +extern int ext_unlink(struct inode * dir, const char * name, int len); +extern int ext_symlink(struct inode * inode, const char * name, int len, + const char * symname); +extern int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int len); +extern int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev); +extern int ext_rename(struct inode * old_dir, const char * old_name, int old_len, + struct inode * new_dir, const char * new_name, int new_len); +extern struct inode * ext_new_inode(int dev); +extern void ext_free_inode(struct inode * inode); +extern unsigned long ext_count_free_inodes(struct super_block *sb); +extern int ext_new_block(int dev); +extern int ext_free_block(int dev, int block); +extern unsigned long ext_count_free_blocks(struct super_block *sb); + +extern int ext_create_block(struct inode *, int); +extern int ext_bmap(struct inode *,int); + +extern void ext_truncate(struct inode *); +extern void ext_put_super(struct super_block *); +extern void ext_write_super(struct super_block *); +extern struct super_block *ext_read_super(struct super_block *,void *); +extern void ext_read_inode(struct inode *); +extern void ext_write_inode(struct inode *); +extern void ext_put_inode(struct inode *); +extern void ext_statfs(struct super_block *, struct statfs *); + +extern int ext_lseek(struct inode *, struct file *, off_t, int); +extern int ext_read(struct inode *, struct file *, char *, int); +extern int ext_write(struct inode *, struct file *, char *, int); + +extern struct inode_operations ext_file_inode_operations; +extern struct inode_operations ext_dir_inode_operations; +extern struct inode_operations ext_symlink_inode_operations; +extern struct inode_operations ext_chrdev_inode_operations; +extern struct inode_operations ext_blkdev_inode_operations; +extern struct inode_operations ext_fifo_inode_operations; + +extern struct file_operations ext_file_operations; +extern struct file_operations ext_dir_operations; + +#endif diff --git a/include/linux/ext_fs_sb.h b/include/linux/ext_fs_sb.h new file mode 100644 index 000000000000..8aff1ff8e364 --- /dev/null +++ b/include/linux/ext_fs_sb.h @@ -0,0 +1,19 @@ +#ifndef _EXT_FS_SB +#define _EXT_FS_SB + +/* + * extended-fs super-block data in memory (same as minix: has to change) + */ +struct ext_sb_info { + unsigned long s_ninodes; + unsigned long s_nzones; + unsigned long s_imap_blocks; + unsigned long s_zmap_blocks; + unsigned long s_firstdatazone; + unsigned long s_log_zone_size; + unsigned long s_max_size; + struct buffer_head * s_imap[8]; + struct buffer_head * s_zmap[8]; +}; + +#endif diff --git a/include/fcntl.h b/include/linux/fcntl.h similarity index 61% rename from include/fcntl.h rename to include/linux/fcntl.h index 4bc0c10cf2b0..e4773700047a 100644 --- a/include/fcntl.h +++ b/include/linux/fcntl.h @@ -1,20 +1,19 @@ #ifndef _FCNTL_H #define _FCNTL_H -#include - -/* open/fcntl - NOCTTY, NDELAY isn't implemented yet */ -#define O_ACCMODE 00003 -#define O_RDONLY 00 -#define O_WRONLY 01 -#define O_RDWR 02 -#define O_CREAT 00100 /* not fcntl */ -#define O_EXCL 00200 /* not fcntl */ -#define O_NOCTTY 00400 /* not fcntl */ -#define O_TRUNC 01000 /* not fcntl */ -#define O_APPEND 02000 -#define O_NONBLOCK 04000 +/* open/fcntl - O_SYNC isn't implemented yet */ +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 #define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 /* Defines for fcntl-commands. Note that currently * locking isn't supported, and other things aren't really @@ -48,8 +47,4 @@ struct flock { pid_t l_pid; }; -extern int creat(const char * filename,mode_t mode); -extern int fcntl(int fildes,int cmd, ...); -extern int open(const char * filename, int flags, ...); - #endif diff --git a/include/linux/fd.h b/include/linux/fd.h new file mode 100644 index 000000000000..4828db0ddfef --- /dev/null +++ b/include/linux/fd.h @@ -0,0 +1,40 @@ +#ifndef _LINUX_FD_H +#define _LINUX_FD_H + +#define FDCLRPRM 0 /* clear user-defined parameters */ +#define FDSETPRM 1 /* set user-defined parameters for current media */ +#define FDDEFPRM 2 /* set user-defined parameters until explicitly cleared */ +#define FDGETPRM 3 /* get disk parameters */ +#define FDMSGON 4 /* issue kernel messages on media type change */ +#define FDMSGOFF 5 /* don't issue kernel messages on media type change */ +#define FDFMTBEG 6 /* begin formatting a disk */ +#define FDFMTTRK 7 /* format the specified track */ +#define FDFMTEND 8 /* end formatting a disk */ +#define FDSETEMSGTRESH 10 /* set fdc error reporting treshold */ + +#define FD_FILL_BYTE 0xF6 /* format fill byte */ + +#define FORMAT_NONE 0 /* no format request */ +#define FORMAT_WAIT 1 /* format request is waiting */ +#define FORMAT_BUSY 2 /* formatting in progress */ +#define FORMAT_OKAY 3 /* successful completion */ +#define FORMAT_ERROR 4 /* formatting error */ + +struct floppy_struct { + unsigned int size, /* nr of 512-byte sectors total */ + sect, /* sectors per track */ + head, /* nr of heads */ + track, /* nr of tracks */ + stretch; /* !=0 means double track steps */ + unsigned char gap, /* gap1 size */ + rate, /* data rate. |= 0x40 for perpendicular */ + spec1, /* stepping rate, head unload time */ + fmt_gap; /* gap2 size */ + char * name; /* used only for predefined formats */ +}; + +struct format_descr { + unsigned int device,head,track; +}; + +#endif diff --git a/include/linux/fdreg.h b/include/linux/fdreg.h index 01355af4a7ed..6d172b6676b7 100644 --- a/include/linux/fdreg.h +++ b/include/linux/fdreg.h @@ -1,10 +1,10 @@ +#ifndef _LINUX_FDREG_H +#define _LINUX_FDREG_H /* * This file contains some defines for the floppy disk controller. * Various sources. Mostly "IBM Microcomputers: A Programmers * Handbook", Sanches and Canton. */ -#ifndef _FDREG_H -#define _FDREG_H extern int ticks_to_floppy_on(unsigned int nr); extern void floppy_on(unsigned int nr); @@ -57,15 +57,23 @@ extern void floppy_deselect(unsigned int nr); #define ST3_WP 0x40 /* Write Protect */ /* Values for FD_COMMAND */ -#define FD_RECALIBRATE 0x07 /* move to track 0 */ -#define FD_SEEK 0x0F /* seek track */ -#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */ -#define FD_WRITE 0xC5 /* write with MT, MFM */ -#define FD_SENSEI 0x08 /* Sense Interrupt Status */ -#define FD_SPECIFY 0x03 /* specify HUT etc */ +#define FD_RECALIBRATE 0x07 /* move to track 0 */ +#define FD_SEEK 0x0F /* seek track */ +#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */ +#define FD_WRITE 0xC5 /* write with MT, MFM */ +#define FD_SENSEI 0x08 /* Sense Interrupt Status */ +#define FD_SPECIFY 0x03 /* specify HUT etc */ +#define FD_FORMAT 0x4D /* format one track */ +#define FD_VERSION 0x10 /* get version code */ +#define FD_CONFIGURE 0x13 /* configure FIFO operation */ +#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */ /* DMA commands */ #define DMA_READ 0x46 #define DMA_WRITE 0x4A +/* FDC version return types */ +#define FDC_TYPE_STD 0x80 /* normal 8272A clone FDC */ +#define FDC_TYPE_82077 0x90 /* FIFO + perpendicular support */ + #endif diff --git a/include/linux/fs.h b/include/linux/fs.h index 7e067bde4f94..86cb92ab6d71 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1,13 +1,16 @@ +#ifndef _LINUX_FS_H +#define _LINUX_FS_H + /* * This file has definitions for some important file table * structures etc. */ -#ifndef _FS_H -#define _FS_H - -#include -#include +#include +#include +#include +#include +#include /* devices are as follows: (same as minix, so we can use the minix * file system. These are major numbers.) @@ -20,9 +23,11 @@ * 5 - /dev/tty * 6 - /dev/lp * 7 - unnamed pipes + * 8 - /dev/sd + * 9 - /dev/st */ -#define IS_SEEKABLE(x) ((x)>=1 && (x)<=3) +#define IS_SEEKABLE(x) ((x)>=1 && (x)<=3 || (x)==8) #define MAY_EXEC 1 #define MAY_WRITE 2 @@ -33,22 +38,11 @@ #define READA 2 /* read-ahead - don't pause */ #define WRITEA 3 /* "write-ahead" - silly, but somewhat useful */ -void buffer_init(long buffer_end); +extern void buffer_init(void); #define MAJOR(a) (((unsigned)(a))>>8) #define MINOR(a) ((a)&0xff) -#define NR_OPEN 20 -#define NR_INODE 128 -#define NR_FILE 64 -#define NR_SUPER 8 -#define NR_HASH 307 -#define NR_BUFFERS nr_buffers -#define BLOCK_SIZE 1024 -#define BLOCK_SIZE_BITS 10 -#define MAX_CHRDEV 16 -#define MAX_BLKDEV 16 - #ifndef NULL #define NULL ((void *) 0) #endif @@ -57,6 +51,8 @@ void buffer_init(long buffer_end); #define PIPE_WRITE_WAIT(inode) ((inode).i_wait2) #define PIPE_HEAD(inode) ((inode).i_data[0]) #define PIPE_TAIL(inode) ((inode).i_data[1]) +#define PIPE_READERS(inode) ((inode).i_data[2]) +#define PIPE_WRITERS(inode) ((inode).i_data[3]) #define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1)) #define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode)) #define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1)) @@ -66,41 +62,74 @@ void buffer_init(long buffer_end); #define SEL_OUT 2 #define SEL_EX 4 +/* + * These are the fs-independent mount-flags: up to 16 flags are supported + */ +#define MS_RDONLY 1 /* mount read-only */ +#define MS_NOSUID 2 /* ignore suid and sgid bits */ +#define MS_NODEV 4 /* disallow access to device special files */ +#define MS_NOEXEC 8 /* disallow program execution */ +#define MS_SYNC 16 /* writes are synced at once */ + +/* + * Note that read-only etc flags are inode-specific: setting some file-system + * flags just means all the inodes inherit those flags by default. It might be + * possible to overrride it sevelctively if you really wanted to with some + * ioctl() that is not currently implemented. + */ +#define IS_RDONLY(inode) ((inode)->i_flags & MS_RDONLY) +#define IS_NOSUID(inode) ((inode)->i_flags & MS_NOSUID) +#define IS_NODEV(inode) ((inode)->i_flags & MS_NODEV) +#define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC) +#define IS_SYNC(inode) ((inode)->i_flags & MS_SYNC) + +/* the read-only stuff doesn't really belong here, but any other place is + probably as bad and I don't want to create yet another include file. */ + +#define BLKROSET 4701 /* set device read-only (0 = read-write) */ +#define BLKROGET 4702 /* get read-only status (0 = read_write) */ + +#define BMAP_IOCTL 1 + typedef char buffer_block[BLOCK_SIZE]; struct buffer_head { char * b_data; /* pointer to data block (1024 bytes) */ + unsigned long b_size; /* block size */ unsigned long b_blocknr; /* block number */ unsigned short b_dev; /* device (0 = free) */ + unsigned short b_count; /* users using this block */ unsigned char b_uptodate; unsigned char b_dirt; /* 0-clean,1-dirty */ - unsigned char b_count; /* users using this block */ unsigned char b_lock; /* 0 - ok, 1 -locked */ - struct task_struct * b_wait; - struct buffer_head * b_prev; + struct wait_queue * b_wait; + struct buffer_head * b_prev; /* doubly linked list of hash-queue */ struct buffer_head * b_next; - struct buffer_head * b_prev_free; + struct buffer_head * b_prev_free; /* doubly linked list of buffers */ struct buffer_head * b_next_free; + struct buffer_head * b_this_page; /* circular list of buffers in one page */ + struct buffer_head * b_reqnext; /* request queue */ }; struct inode { - dev_t i_dev; - ino_t i_ino; - umode_t i_mode; - nlink_t i_nlink; - uid_t i_uid; - gid_t i_gid; - dev_t i_rdev; - off_t i_size; - time_t i_atime; - time_t i_mtime; - time_t i_ctime; + dev_t i_dev; + unsigned long i_ino; + umode_t i_mode; + nlink_t i_nlink; + uid_t i_uid; + gid_t i_gid; + dev_t i_rdev; + off_t i_size; + time_t i_atime; + time_t i_mtime; + time_t i_ctime; unsigned long i_data[16]; struct inode_operations * i_op; struct super_block * i_sb; - struct task_struct * i_wait; - struct task_struct * i_wait2; /* for pipes */ + struct wait_queue * i_wait; + struct wait_queue * i_wait2; /* for pipes */ unsigned short i_count; + unsigned short i_flags; unsigned char i_lock; unsigned char i_dirt; unsigned char i_pipe; @@ -113,45 +142,35 @@ struct file { unsigned short f_mode; unsigned short f_flags; unsigned short f_count; + unsigned short f_reada; + unsigned short f_rdev; /* needed for /dev/tty */ struct inode * f_inode; struct file_operations * f_op; off_t f_pos; }; -typedef struct { - struct task_struct * old_task; - struct task_struct ** wait_address; -} wait_entry; - -typedef struct select_table_struct { - int nr, woken; - struct task_struct * current; - struct select_table_struct * next_table; - wait_entry entry[NR_OPEN*3]; -} select_table; +#include +#include +#include struct super_block { - unsigned short s_ninodes; - unsigned short s_nzones; - unsigned short s_imap_blocks; - unsigned short s_zmap_blocks; - unsigned short s_firstdatazone; - unsigned short s_log_zone_size; - unsigned long s_max_size; - unsigned short s_magic; -/* These are only in memory */ - struct buffer_head * s_imap[8]; - struct buffer_head * s_zmap[8]; unsigned short s_dev; - struct inode * s_covered; - struct inode * s_mounted; - unsigned long s_time; - struct task_struct * s_wait; + unsigned long s_blocksize; unsigned char s_lock; unsigned char s_rd_only; unsigned char s_dirt; - /* TUBE */ struct super_operations *s_op; + unsigned long s_flags; + unsigned long s_magic; + unsigned long s_time; + struct inode * s_covered; + struct inode * s_mounted; + struct wait_queue * s_wait; + union { + struct minix_sb_info minix_sb; + struct ext_sb_info ext_sb; + struct msdos_sb_info msdos_sb; + } u; }; struct file_operations { @@ -159,12 +178,14 @@ struct file_operations { int (*read) (struct inode *, struct file *, char *, int); int (*write) (struct inode *, struct file *, char *, int); int (*readdir) (struct inode *, struct file *, struct dirent *, int count); - int (*close) (struct inode *, struct file *); int (*select) (struct inode *, struct file *, int, select_table *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned int); + int (*open) (struct inode *, struct file *); + void (*release) (struct inode *, struct file *); }; struct inode_operations { + struct file_operations * default_file_ops; int (*create) (struct inode *,const char *,int,int,struct inode **); int (*lookup) (struct inode *,const char *,int,struct inode **); int (*link) (struct inode *,struct inode *,const char *,int); @@ -175,19 +196,18 @@ struct inode_operations { int (*mknod) (struct inode *,const char *,int,int,int); int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int); int (*readlink) (struct inode *,char *,int); - int (*open) (struct inode *, struct file *); - void (*release) (struct inode *, struct file *); struct inode * (*follow_link) (struct inode *, struct inode *); int (*bmap) (struct inode *,int); void (*truncate) (struct inode *); - /* added by entropy */ - void (*write_inode)(struct inode *inode); - void (*put_inode)(struct inode *inode); }; struct super_operations { void (*read_inode)(struct inode *inode); + void (*write_inode) (struct inode *inode); + void (*put_inode) (struct inode *inode); void (*put_super)(struct super_block *sb); + void (*write_super) (struct super_block *sb); + void (*statfs) (struct super_block *sb, struct statfs *buf); }; struct file_system_type { @@ -203,15 +223,19 @@ extern struct file_system_type *get_fs_type(char *name); extern struct inode inode_table[NR_INODE]; extern struct file file_table[NR_FILE]; extern struct super_block super_block[NR_SUPER]; -extern struct buffer_head * start_buffer; + +extern void grow_buffers(int size); +extern int shrink_buffers(void); + extern int nr_buffers; +extern int nr_buffer_heads; extern void check_disk_change(int dev); +extern void invalidate_inodes(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); extern void floppy_off(unsigned int dev); -extern void truncate(struct inode * inode); extern void sync_inodes(void); extern void wait_on(struct inode * inode); extern int bmap(struct inode * inode,int block); @@ -222,21 +246,24 @@ extern struct inode * _namei(const char * filename, struct inode * base, int follow_links); extern int open_namei(const char * pathname, int flag, int mode, struct inode ** res_inode); +extern int do_mknod(const char * filename, int mode, int dev); extern void iput(struct inode * inode); extern struct inode * iget(int dev,int nr); extern struct inode * get_empty_inode(void); extern struct inode * get_pipe_inode(void); -extern struct buffer_head * get_hash_table(int dev, int block); -extern struct buffer_head * getblk(int dev, int block); +extern struct file * get_empty_filp(void); +extern struct buffer_head * get_hash_table(int dev, int block, int size); +extern struct buffer_head * getblk(int dev, int block, int size); extern void ll_rw_block(int rw, struct buffer_head * bh); extern void ll_rw_page(int rw, int dev, int nr, char * buffer); extern void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buffer); extern void brelse(struct buffer_head * buf); -extern struct buffer_head * bread(int dev,int block); +extern struct buffer_head * bread(int dev, int block, int size); extern void bread_page(unsigned long addr,int dev,int b[4]); extern struct buffer_head * breada(int dev,int block,...); extern int sync_dev(int dev); extern struct super_block * get_super(int dev); +extern void put_super(int dev); extern int ROOT_DEV; extern void mount_root(void); diff --git a/include/linux/genhd.h b/include/linux/genhd.h new file mode 100644 index 000000000000..c1d1b4ed3f91 --- /dev/null +++ b/include/linux/genhd.h @@ -0,0 +1,52 @@ +#ifndef _GENHD_H +#define _GENHD_H + +/* + * genhd.h Copyright (C) 1992 Drew Eckhardt + * Generic hard disk header file by + * Drew Eckhardt + * + * + */ + +#define EXTENDED_PARTITION 5 + +struct partition { + unsigned char boot_ind; /* 0x80 - active */ + unsigned char head; /* starting head */ + unsigned char sector; /* starting sector */ + unsigned char cyl; /* starting cylinder */ + unsigned char sys_ind; /* What partition type */ + unsigned char end_head; /* end head */ + unsigned char end_sector; /* end sector */ + unsigned char end_cyl; /* end cylinder */ + unsigned int start_sect; /* starting sector counting from 0 */ + unsigned int nr_sects; /* nr of sectors in partition */ +}; + +struct hd_struct { + long start_sect; + long nr_sects; +}; + +struct gendisk { + int major; /* major number of driver */ + char *major_name; /* name of major driver */ + int minor_shift; /* number of times minor is shifted to + get real minor */ + int max_p; /* maximum partitions per device */ + int max_nr; /* maximum number of real devices */ + + void (*init)(void); /* Initialization called before we do our thing */ + struct hd_struct *part; /* partition table */ + int *sizes; /* block sizes */ + int nr_real; /* number of real devices */ + + void *real_devices; /* internal use */ + struct gendisk *next; +}; + +extern int NR_GENDISKS; /* total */ +extern struct gendisk *gendisk_head; /* linked list of disks */ + +#endif diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index e7e134762d4a..28e30541895d 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -1,10 +1,11 @@ +#ifndef _LINUX_HDREG_H +#define _LINUX_HDREG_H + /* * This file contains some defines for the AT-hd-controller. * Various sources. Check out some definitions (see comments with * a ques). */ -#ifndef _HDREG_H -#define _HDREG_H /* Hd controller regs. Ref: IBM AT Bios-listing */ #define HD_DATA 0x1f0 /* _CTL when writing */ @@ -49,25 +50,12 @@ #define ECC_ERR 0x40 /* ? */ #define BBD_ERR 0x80 /* ? */ -#define EXTENDED_PARTITION 5 - -struct partition { - unsigned char boot_ind; /* 0x80 - active */ - unsigned char head; /* starting head */ - unsigned char sector; /* starting sector */ - unsigned char cyl; /* starting cylinder */ - unsigned char sys_ind; /* What partition type */ - unsigned char end_head; /* end head */ - unsigned char end_sector; /* end sector */ - unsigned char end_cyl; /* end cylinder */ - unsigned int start_sect; /* starting sector counting from 0 */ - unsigned int nr_sects; /* nr of sectors in partition */ -}; #define HDIO_REQ 0x301 struct hd_geometry { unsigned char heads; unsigned char sectors; unsigned short cylinders; + unsigned long start; }; #endif diff --git a/include/linux/head.h b/include/linux/head.h index db3dda2659a2..b871742accb7 100644 --- a/include/linux/head.h +++ b/include/linux/head.h @@ -1,5 +1,5 @@ -#ifndef _HEAD_H -#define _HEAD_H +#ifndef _LINUX_HEAD_H +#define _LINUX_HEAD_H typedef struct desc_struct { unsigned long a,b; diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 2451af9c86bb..cf77d547f5bb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -1,12 +1,14 @@ +#ifndef _LINUX_KERNEL_H +#define _LINUX_KERNEL_H + /* * 'kernel.h' contains some often-used function prototypes etc */ + void verify_area(void * addr,int count); volatile void panic(const char * str); volatile void do_exit(long error_code); -int printf(const char * fmt, ...); int printk(const char * fmt, ...); -void console_print(const char * str); void * malloc(unsigned int size); void free_s(void * obj, int size); @@ -21,3 +23,4 @@ void free_s(void * obj, int size); */ #define suser() (current->euid == 0) +#endif diff --git a/include/linux/limits.h b/include/linux/limits.h new file mode 100644 index 000000000000..9b04df71454d --- /dev/null +++ b/include/linux/limits.h @@ -0,0 +1,27 @@ +#ifndef _LINUX_LIMITS_H +#define _LINUX_LIMITS_H + +#define NAME_MAX 255 + +#define NR_OPEN 32 +#define NR_INODE 128 +#define NR_FILE 128 +#define NR_SUPER 8 +#define NR_HASH 307 +#define BLOCK_SIZE 1024 +#define BLOCK_SIZE_BITS 10 +#define MAX_CHRDEV 16 +#define MAX_BLKDEV 16 + +#define NGROUPS_MAX 32 /* supplemental group IDs are available */ +#define ARG_MAX 40960 /* # bytes of args + environ for exec() */ +#define CHILD_MAX 999 /* no limit :-) */ +#define OPEN_MAX 32 /* # open files a process may have */ +#define LINK_MAX 127 /* # links a file may have */ +#define MAX_CANON 255 /* size of the canonical input queue */ +#define MAX_INPUT 255 /* size of the type-ahead buffer */ +#define NAME_MAX 255 /* # chars in a file name */ +#define PATH_MAX 1024 /* # chars in a path name */ +#define PIPE_BUF 4095 /* # bytes in atomic write to a pipe */ + +#endif diff --git a/include/linux/lp.h b/include/linux/lp.h index 6e565dad6f6a..2fef6882e235 100644 --- a/include/linux/lp.h +++ b/include/linux/lp.h @@ -1,10 +1,14 @@ +#ifndef _LINUX_LP_H +#define _LINUX_LP_H + /* $Header: /usr/src/linux/include/linux/lp.h,v 1.2 1992/01/21 23:59:24 james_r_wiegand Exp james_r_wiegand $ */ -#include +#include #include #include + #include #include @@ -31,24 +35,15 @@ $Header: /usr/src/linux/include/linux/lp.h,v 1.2 1992/01/21 23:59:24 james_r_wie #define LP_B(minor) lp_table[(minor)].base #define LP_F(minor) lp_table[(minor)].flags -#define LP_T(minor) lp_table[(minor)].lp_task #define LP_S(minor) inb(LP_B((minor)) + 1) -#define LP_R(minor) lp_table[(minor)].remainder /* since we are dealing with a horribly slow device I don't see the need for a queue */ -#ifndef __LP_C__ - extern -#endif struct lp_struct { int base; int flags; - /* number of characters yet to be printed in current block */ - int remainder; - /* needed for busy determination */ - int lp_task; }; /* @@ -58,9 +53,6 @@ struct lp_struct { * please let me know if you have different equipment * if you have more than 3 printers, remember to increase LP_NO */ -#ifndef __LP_C__ - extern -#endif struct lp_struct lp_table[] = { { 0x3bc, 0, }, { 0x378, 0, }, @@ -106,4 +98,6 @@ struct lp_struct lp_table[] = { * function prototypes */ -extern void lp_init(void); +extern long lp_init(long); + +#endif diff --git a/include/linux/math_emu.h b/include/linux/math_emu.h index c5501b6eae0d..827e9c9953a2 100644 --- a/include/linux/math_emu.h +++ b/include/linux/math_emu.h @@ -1,8 +1,3 @@ -/* - * linux/include/linux/math_emu.h - * - * (C) 1991 Linus Torvalds - */ #ifndef _LINUX_MATH_EMU_H #define _LINUX_MATH_EMU_H @@ -177,6 +172,10 @@ void fmul(const temp_real *, const temp_real *, temp_real *); void fdiv(const temp_real *, const temp_real *, temp_real *); +/* sqrt.c */ + +void fsqrt(const temp_real *, temp_real *); + /* compare.c */ void fcom(const temp_real *, const temp_real *); diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h index c427cb131b0e..1205d6e2a61e 100644 --- a/include/linux/minix_fs.h +++ b/include/linux/minix_fs.h @@ -1,11 +1,9 @@ -/* - * The minix filesystem constants/structures - */ - #ifndef _MINIX_FS_H #define _MINIX_FS_H -#include +/* + * The minix filesystem constants/structures + */ #define MINIX_NAME_LEN 14 #define MINIX_ROOT_INO 1 @@ -27,6 +25,9 @@ struct minix_inode { unsigned short i_zone[9]; }; +/* + * minix super-block data on disk + */ struct minix_super_block { unsigned short s_ninodes; unsigned short s_nzones; @@ -45,7 +46,6 @@ struct minix_dir_entry { extern int minix_open(struct inode * inode, struct file * filp); extern void minix_release(struct inode * inode, struct file * filp); -extern struct inode * minix_follow_link(struct inode * dir, struct inode * inode); 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, @@ -59,11 +59,12 @@ extern int minix_link(struct inode * oldinode, struct inode * dir, const char * extern int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev); extern int minix_rename(struct inode * old_dir, const char * old_name, int old_len, struct inode * new_dir, const char * new_name, int new_len); -extern int minix_readlink(struct inode * inode, char * buffer, int buflen); extern struct inode * minix_new_inode(int dev); extern void minix_free_inode(struct inode * inode); +extern unsigned long minix_count_free_inodes(struct super_block *sb); extern int minix_new_block(int dev); extern int minix_free_block(int dev, int block); +extern unsigned long minix_count_free_blocks(struct super_block *sb); extern int minix_create_block(struct inode *, int); extern int minix_bmap(struct inode *,int); @@ -73,15 +74,20 @@ extern void minix_put_super(struct super_block *); extern struct super_block *minix_read_super(struct super_block *,void *); extern void minix_read_inode(struct inode *); 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_readdir(struct inode *, struct file *, struct dirent *, int); extern int minix_file_read(struct inode *, struct file *, char *, int); -extern int minix_file_write(struct inode *, struct file *, char *, int); -extern struct inode_operations minix_inode_operations; +extern struct inode_operations minix_file_inode_operations; +extern struct inode_operations minix_dir_inode_operations; +extern struct inode_operations minix_symlink_inode_operations; +extern struct inode_operations minix_chrdev_inode_operations; +extern struct inode_operations minix_blkdev_inode_operations; +extern struct inode_operations minix_fifo_inode_operations; extern struct file_operations minix_file_operations; extern struct file_operations minix_dir_operations; diff --git a/include/linux/minix_fs_sb.h b/include/linux/minix_fs_sb.h new file mode 100644 index 000000000000..18de6ef2d228 --- /dev/null +++ b/include/linux/minix_fs_sb.h @@ -0,0 +1,19 @@ +#ifndef _MINIX_FS_SB +#define _MINIX_FS_SB + +/* + * minix super-block data in memory + */ +struct minix_sb_info { + unsigned long s_ninodes; + unsigned long s_nzones; + unsigned long s_imap_blocks; + unsigned long s_zmap_blocks; + unsigned long s_firstdatazone; + unsigned long s_log_zone_size; + unsigned long s_max_size; + struct buffer_head * s_imap[8]; + struct buffer_head * s_zmap[8]; +}; + +#endif diff --git a/include/linux/mm.h b/include/linux/mm.h index e1eacea98b03..256f032f674b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -5,11 +5,49 @@ #include #include -#include +#include + +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + */ +extern unsigned long inline __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + + __asm__ __volatile__("cld ; rep ; stosl" + ::"a" (0), + "D" ((long) empty_bad_page), + "c" (1024) + :"di","cx"); + return (unsigned long) empty_bad_page; +} +#define BAD_PAGE __bad_page() + +extern unsigned long inline __bad_pagetable(void) +{ + extern char empty_bad_page_table[PAGE_SIZE]; + + __asm__ __volatile__("cld ; rep ; stosl" + ::"a" (7+BAD_PAGE), + "D" ((long) empty_bad_page_table), + "c" (1024) + :"di","cx"); + return (unsigned long) empty_bad_page_table; +} +#define BAD_PAGETABLE __bad_pagetable() extern unsigned int swap_device; extern struct inode * swap_file; +extern int nr_free_pages; + extern void rw_swap_page(int rw, unsigned int nr, char * buf); #define read_swap_page(nr,buf) \ @@ -17,30 +55,44 @@ extern void rw_swap_page(int rw, unsigned int nr, char * buf); #define write_swap_page(nr,buf) \ rw_swap_page(WRITE,(nr),(buf)) -extern unsigned long get_free_page(void); +/* memory.c */ + +extern unsigned long get_free_page(int priority); extern unsigned long put_dirty_page(unsigned long page,unsigned long address); extern void free_page(unsigned long addr); -void swap_free(int page_nr); -void swap_in(unsigned long *table_ptr); +extern int free_page_tables(unsigned long from,unsigned long size); +extern int copy_page_tables(unsigned long from,unsigned long to,long size); +extern int unmap_page_range(unsigned long from, unsigned long size); +extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, + int permiss); +extern void write_verify(unsigned long address); -extern inline volatile void oom(void) -{ - printk("out of memory\n\r"); - do_exit(SIGSEGV); -} +extern void do_wp_page(unsigned long error_code, unsigned long address, + struct task_struct *tsk, unsigned long user_esp); +extern void do_no_page(unsigned long error_code, unsigned long address, + struct task_struct *tsk, unsigned long user_esp); + +extern unsigned long mem_init(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); + +/* swap.c */ + +extern void swap_free(unsigned int page_nr); +extern void swap_in(unsigned long *table_ptr); #define invalidate() \ __asm__("movl %%eax,%%cr3"::"a" (0)) -/* these are not to be changed without changing head.s etc */ -#define LOW_MEM 0x100000 -extern unsigned long HIGH_MEMORY; -#define PAGING_MEMORY (15*1024*1024) -#define PAGING_PAGES (PAGING_MEMORY>>12) -#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) +extern unsigned long low_memory; +extern unsigned long high_memory; +extern unsigned long paging_pages; + +#define MAP_NR(addr) (((addr)-low_memory)>>12) #define USED 100 -extern unsigned char mem_map [ PAGING_PAGES ]; +extern unsigned char * mem_map; #define PAGE_DIRTY 0x40 #define PAGE_ACCESSED 0x20 @@ -48,4 +100,8 @@ extern unsigned char mem_map [ PAGING_PAGES ]; #define PAGE_RW 0x02 #define PAGE_PRESENT 0x01 +#define GFP_BUFFER 0x00 +#define GFP_USER 0x01 +#define GFP_KERNEL 0x02 + #endif diff --git a/include/linux/mouse.h b/include/linux/mouse.h new file mode 100644 index 000000000000..4723a5470c64 --- /dev/null +++ b/include/linux/mouse.h @@ -0,0 +1,61 @@ +#ifndef _LINUX_MOUSE_H +#define _LINUX_MOUSE_H + +/* + * linux/include/linux/mouse.h: header file for Logitech Bus Mouse driver + * by James Banks + * + * based on information gleamed from various mouse drivers on the net + * + * Heavily modified by David giller (rafetmad@oxy.edu) + * + * Minor modifications for Linux 0.96c-pl1 by Nathan Laredo + * gt7080a@prism.gatech.edu (13JUL92) + * + */ + +#define MOUSE_IRQ 5 + +#define MSE_DATA_PORT 0x23c +#define MSE_SIGNATURE_PORT 0x23d +#define MSE_CONTROL_PORT 0x23e +#define MSE_INTERRUPT_PORT 0x23e +#define MSE_CONFIG_PORT 0x23f + +#define MSE_ENABLE_INTERRUPTS 0x00 +#define MSE_DISABLE_INTERRUPTS 0x10 + +#define MSE_READ_X_LOW 0x80 +#define MSE_READ_X_HIGH 0xa0 +#define MSE_READ_Y_LOW 0xc0 +#define MSE_READ_Y_HIGH 0xe0 + +/* Magic number used to check if the mouse exists */ +#define MSE_CONFIG_BYTE 0x91 +#define MSE_DEFAULT_MODE 0x90 +#define MSE_SIGNATURE_BYTE 0xa5 + +/* useful macros */ + +#define MSE_INT_OFF() outb(MSE_DISABLE_INTERRUPTS, MSE_CONTROL_PORT) +#define MSE_INT_ON() outb(MSE_ENABLE_INTERRUPTS, MSE_CONTROL_PORT) + +struct mouse_status + { + char buttons; + char latch_buttons; + int dx; + int dy; + + int present; + int ready; + int active; + + struct inode *inode; + }; + +/* Function Prototypes */ +extern long mouse_init(long); + +#endif + diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h new file mode 100644 index 000000000000..0eebbf4d0bc9 --- /dev/null +++ b/include/linux/msdos_fs.h @@ -0,0 +1,172 @@ +#ifndef _MSDOS_FS_H +#define _MSDOS_FS_H + +/* + * The MS-DOS filesystem constants/structures + */ + +#include + +#define MSDOS_ROOT_INO 1 /* == MINIX_ROOT_INO */ +#define SECTOR_SIZE 512 /* sector size (bytes) */ +#define SECTOR_BITS 9 /* log2(SECTOR_SIZE) */ +#define MSDOS_DPB (MSDOS_DPS*2) /* dir entries per block */ +#define MSDOS_DPB_BITS 5 /* log2(MSDOS_DPB) */ +#define MSDOS_DPS (SECTOR_SIZE/sizeof(struct msdos_dir_entry)) +#define MSDOS_DPS_BITS 4 /* log2(MSDOS_DPS) */ +#define MSDOS_DIR_BITS 5 /* log2(sizeof(struct msdos_dir_entry)) */ + +#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */ + +#define FAT_CACHE 8 /* FAT cache size */ + +#define ATTR_RO 1 /* read-only */ +#define ATTR_HIDDEN 2 /* hidden */ +#define ATTR_SYS 4 /* system */ +#define ATTR_VOLUME 8 /* volume label */ +#define ATTR_DIR 16 /* directory */ +#define ATTR_ARCH 32 /* archived */ + +#define ATTR_NONE 0 /* no attribute bits */ +#define ATTR_UNUSED (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN) + /* attribute bits that are copied "as is" */ + +#define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */ + +#define D_START 0 /* i_data[0]: first cluster or 0 */ +#define D_ATTRS 1 /* i_data[1]: unused attribute bits */ +#define D_BUSY 2 /* i_data[2]: file is either deleted but still open, or + inconsistent (mkdir) */ +#define D_DEPEND 3 /* i_data[3]: pointer to inode that depends on the current + inode */ +#define D_OLD 4 /* i_data[4]: pointer to the old inode this inode depends + on */ +#define D_BINARY 5 /* i_data[5]: file contains non-text data */ + +#define MSDOS_SB(s) (&((s)->u.msdos_sb)) + +#define MSDOS_NAME 11 /* maximum name length */ +#define MSDOS_DOT ". " /* ".", padded to MSDOS_NAME chars */ +#define MSDOS_DOTDOT ".. " /* "..", padded to MSDOS_NAME chars */ + +#define MSDOS_FAT12 4086 /* maximum number of clusters in a 12 bit FAT */ + +struct msdos_boot_sector { + char ignored[13]; + unsigned char cluster_size; /* sectors/cluster */ + unsigned short reserved; /* reserved sectors */ + unsigned char fats; /* number of FATs */ + unsigned char dir_entries[2];/* root directory entries */ + unsigned char sectors[2]; /* number of sectors */ + unsigned char media; /* media code (unused) */ + unsigned short fat_length; /* sectors/FAT */ + unsigned short secs_track; /* sectors per track (unused) */ + unsigned short heads; /* number of heads (unused) */ + unsigned long hidden; /* hidden sectors (unused) */ + unsigned long total_sect; /* number of sectors (if sectors == 0) */ +}; + +struct msdos_dir_entry { + char name[8],ext[3]; /* name and extension */ + unsigned char attr; /* attribute bits */ + char unused[10]; + unsigned short time,date,start; /* time, date and first cluster */ + unsigned long size; /* file size (in bytes) */ +}; + +struct fat_cache { + int device; /* device number. 0 means unused. */ + int ino; /* inode number. */ + int file_cluster; /* cluster number in the file. */ + int disk_cluster; /* cluster number on disk. */ + struct fat_cache *next; /* next cache entry */ +}; + +/* Determine whether this FS has kB-aligned data. */ + +#define MSDOS_CAN_BMAP(mib) (!(((mib)->cluster_size & 1) || \ + ((mib)->data_start & 1))) + +/* Convert attribute bits and a mask to the UNIX mode. */ + +#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? 0444 : 0777)) + +/* Convert the UNIX mode to MS-DOS attribute bits. */ + +#define MSDOS_MKATTR(m) (!(m & 0200) ? ATTR_RO : ATTR_NONE) + + +static inline struct buffer_head *msdos_sread(int dev,int sector,void **start) +{ + struct buffer_head *bh; + + if (!(bh = bread(dev,sector >> 1, 1024))) + return NULL; + *start = bh->b_data+((sector & 1) << SECTOR_BITS); + return bh; +} + + +/* misc.c */ + +extern int is_binary(char conversion,char *extension); +extern void lock_creation(void); +extern void unlock_creation(void); +extern int msdos_add_cluster(struct inode *inode); +extern int date_dos2unix(unsigned short time,unsigned short date); +extern void date_unix2dos(int unix_date,unsigned short *time, + unsigned short *date); +extern int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh, + struct msdos_dir_entry **de); +extern int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh, + struct msdos_dir_entry **res_de,int *ino); +extern int msdos_parent_ino(struct inode *dir,int locked); + +/* fat.c */ + +extern int fat_access(struct super_block *sb,int this,int new_value); +extern int msdos_smap(struct inode *inode,int sector); +extern int fat_free(struct inode *inode,int skip); +extern void cache_init(void); +void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu); +void cache_add(struct inode *inode,int f_clu,int d_clu); +void cache_inval_inode(struct inode *inode); +void cache_inval_dev(int device); +int get_cluster(struct inode *inode,int cluster); + +/* namei.c */ + +extern int msdos_lookup(struct inode *dir,const char *name,int len, + struct inode **result); +extern int msdos_create(struct inode *dir,const char *name,int len,int mode, + struct inode **result); +extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode); +extern int msdos_rmdir(struct inode *dir,const char *name,int len); +extern int msdos_unlink(struct inode *dir,const char *name,int len); +extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, + struct inode *new_dir,const char *new_name,int new_len); + +/* inode.c */ + +extern void msdos_put_inode(struct inode *inode); +extern void msdos_put_super(struct super_block *sb); +extern struct super_block *msdos_read_super(struct super_block *s,void *data); +extern void msdos_statfs(struct super_block *sb,struct statfs *buf); +extern int msdos_bmap(struct inode *inode,int block); +extern void msdos_read_inode(struct inode *inode); +extern void msdos_write_inode(struct inode *inode); + +/* dir.c */ + +extern struct file_operations msdos_dir_operations; +extern struct inode_operations msdos_dir_inode_operations; + +/* file.c */ + +extern struct file_operations msdos_file_operations; +extern struct inode_operations msdos_file_inode_operations; +extern struct inode_operations msdos_file_inode_operations_no_bmap; + +extern void msdos_truncate(struct inode *inode); + +#endif diff --git a/include/linux/msdos_fs_sb.h b/include/linux/msdos_fs_sb.h new file mode 100644 index 000000000000..c02db865debe --- /dev/null +++ b/include/linux/msdos_fs_sb.h @@ -0,0 +1,18 @@ +#ifndef _MSDOS_FS_SB +#define _MSDOS_FS_SB + +struct msdos_sb_info { /* space in struct super_block is 28 bytes */ + unsigned short cluster_size; /* sectors/cluster */ + unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */ + unsigned short fat_start,fat_length; /* FAT start & length (sec.) */ + unsigned short dir_start,dir_entries; /* root dir start & entries */ + unsigned short data_start; /* first data sector */ + unsigned long clusters; /* number of clusters */ + uid_t fs_uid; + gid_t fs_gid; + unsigned short fs_umask; + unsigned char name_check; /* r = relaxed, n = normal, s = strict */ + unsigned char conversion; /* b = binary, t = text, a = auto */ +}; + +#endif diff --git a/include/sys/param.h b/include/linux/param.h similarity index 100% rename from include/sys/param.h rename to include/linux/param.h diff --git a/include/sys/ptrace.h b/include/linux/ptrace.h similarity index 66% rename from include/sys/ptrace.h rename to include/linux/ptrace.h index 14bf3c737a90..ad26e03428f0 100644 --- a/include/sys/ptrace.h +++ b/include/linux/ptrace.h @@ -1,9 +1,24 @@ +#ifndef _LINUX_PTRACE_H +#define _LINUX_PTRACE_H /* ptrace.h */ /* structs and defines to help the user use the ptrace system call. */ -#ifndef _SYS_PTRACE_H -#define _SYS_PTRACE_H /* has the defines to get at the registers. */ + +#define PTRACE_TRACEME 0 +#define PTRACE_PEEKTEXT 1 +#define PTRACE_PEEKDATA 2 +#define PTRACE_PEEKUSR 3 +#define PTRACE_POKETEXT 4 +#define PTRACE_POKEDATA 5 +#define PTRACE_POKEUSR 6 +#define PTRACE_CONT 7 +#define PTRACE_KILL 8 +#define PTRACE_SINGLESTEP 9 + +#define PTRACE_ATTACH 0x10 +#define PTRACE_DETACH 0x11 + /* use ptrace (3 or 6, pid, PT_EXCL, data); to read or write the processes registers. */ @@ -49,4 +64,4 @@ struct pt_regs { long ss; }; -#endif /* _SYS_PTRACE_H */ +#endif diff --git a/include/sys/resource.h b/include/linux/resource.h similarity index 91% rename from include/sys/resource.h rename to include/linux/resource.h index e23ec63987a6..4fa0f410ce59 100644 --- a/include/sys/resource.h +++ b/include/linux/resource.h @@ -1,10 +1,10 @@ +#ifndef _LINUX_RESOURCE_H +#define _LINUX_RESOURCE_H + /* * Resource control/accounting header file for linux */ -#ifndef _SYS_RESOURCE_H -#define _SYS_RESOURCE_H - /* * Definition of struct rusage taken from BSD 4.3 Reno * @@ -60,4 +60,11 @@ struct rlimit { int rlim_max; }; -#endif /* _SYS_RESOURCE_H */ +#define PRIO_MIN -99 +#define PRIO_MAX 14 + +#define PRIO_PROCESS 0 +#define PRIO_PGRP 1 +#define PRIO_USER 2 + +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 9d744cbba3f1..5de4d654bb94 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1,5 +1,5 @@ -#ifndef _SCHED_H -#define _SCHED_H +#ifndef _LINUX_SCHED_H +#define _LINUX_SCHED_H #define HZ 100 @@ -39,10 +39,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include #if (NR_OPEN > 32) #error "Currently the close-on-exec-flags and select masks are in one long, max 32 files/proc" @@ -60,10 +60,8 @@ #define MAX_SHARED_LIBS 6 -extern int copy_page_tables(unsigned long from, unsigned long to, long size); -extern int free_page_tables(unsigned long from, unsigned long size); - extern void sched_init(void); +extern void show_state(void); extern void schedule(void); extern void trap_init(void); extern void panic(const char * str); @@ -119,22 +117,27 @@ struct task_struct { long blocked; /* bitmap of masked signals */ /* various fields */ int exit_code; + int dumpable:1; + int swappable:1; unsigned long start_code,end_code,end_data,brk,start_stack; long pid,pgrp,session,leader; int groups[NGROUPS]; /* - * pointers to parent process, youngest child, younger sibling, + * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->p_pptr->pid) */ - struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr; + struct task_struct *p_opptr,*p_pptr, *p_cptr, *p_ysptr, *p_osptr; /* - * sleep makes a singly linked list with this. + * For ease of programming... Normal sleeps don't need to + * keep track of a wait-queue: every task has an entry of it's own */ - struct task_struct *next_wait; + struct wait_queue wait; unsigned short uid,euid,suid; unsigned short gid,egid,sgid; - unsigned long timeout,alarm; + unsigned long timeout; + unsigned long it_real_value, it_prof_value, it_virt_value; + unsigned long it_real_incr, it_prof_incr, it_virt_incr; long utime,stime,cutime,cstime,start_time; unsigned long min_flt, maj_flt; unsigned long cmin_flt, cmaj_flt; @@ -181,12 +184,13 @@ struct task_struct { #define INIT_TASK \ /* state etc */ { 0,15,15, \ /* signals */ 0,{{},},0, \ -/* ec,brk... */ 0,0,0,0,0,0, \ +/* ec,brk... */ 0,0,0,0,0,0,0,0, \ /* pid etc.. */ 0,0,0,0, \ /* suppl grps*/ {NOGROUP,}, \ -/* proc links*/ &init_task.task,NULL,NULL,NULL,NULL, \ +/* proc links*/ &init_task.task,&init_task.task,NULL,NULL,NULL, \ +/* wait queue*/ {&init_task.task,NULL}, \ /* uid etc */ 0,0,0,0,0,0, \ -/* timeout */ 0,0,0,0,0,0,0, \ +/* timeout */ 0,0,0,0,0,0,0,0,0,0,0,0, \ /* min_flt */ 0,0,0,0, \ /* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \ {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \ @@ -221,11 +225,19 @@ extern int jiffies_offset; #define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ) extern void add_timer(long jiffies, void (*fn)(void)); -extern void sleep_on(struct task_struct ** p); -extern void interruptible_sleep_on(struct task_struct ** p); -extern void wake_up(struct task_struct ** p); + +extern void sleep_on(struct wait_queue ** p); +extern void interruptible_sleep_on(struct wait_queue ** p); +extern void wake_up(struct wait_queue ** p); +extern void wake_one_task(struct task_struct * p); + +extern int send_sig(long sig,struct task_struct * p,int priv); extern int in_group_p(gid_t grp); +extern int request_irq(unsigned int irq,void (*handler)(int)); +extern void free_irq(unsigned int irq); +extern int irqaction(unsigned int irq,struct sigaction * new); + /* * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall * 4-TSS0, 5-LDT0, 6-TSS1 etc ... @@ -253,8 +265,10 @@ struct {long a,b;} __tmp; \ __asm__("cmpl %%ecx,_current\n\t" \ "je 1f\n\t" \ "movw %%dx,%1\n\t" \ + "cli\n\t" \ "xchgl %%ecx,_current\n\t" \ "ljmp %0\n\t" \ + "sti\n\t" \ "cmpl %%ecx,_last_task_used_math\n\t" \ "jne 1f\n\t" \ "clts\n" \ @@ -292,6 +306,51 @@ __asm__("movw %%dx,%0\n\t" \ #define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base ) #define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 ) +extern inline void add_wait_queue(struct wait_queue ** p, struct wait_queue * wait) +{ + unsigned long flags; + struct wait_queue * tmp; + + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + wait->next = *p; + tmp = wait; + while (tmp->next) + if ((tmp = tmp->next)->next == *p) + break; + *p = tmp->next = wait; + __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags)); +} + +extern inline void remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait) +{ + unsigned long flags; + struct wait_queue * tmp; + + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + if (*p == wait) + if ((*p = wait->next) == wait) + *p = NULL; + tmp = wait; + while (tmp && tmp->next != wait) + tmp = tmp->next; + if (tmp) + tmp->next = wait->next; + wait->next = NULL; + __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags)); +} + +extern inline void select_wait(struct wait_queue ** wait_address, select_table * p) +{ + struct select_table_entry * entry = p->entry + p->nr; + + if (!wait_address) + return; + entry->wait_address = wait_address; + entry->wait.task = current; + add_wait_queue(wait_address,&entry->wait); + p->nr++; +} + static unsigned long inline _get_base(char * addr) { unsigned long __base; @@ -316,4 +375,18 @@ static unsigned long inline get_limit(unsigned long segment) return __limit+1; } +#define REMOVE_LINKS(p) \ + if ((p)->p_osptr) \ + (p)->p_osptr->p_ysptr = (p)->p_ysptr; \ + if ((p)->p_ysptr) \ + (p)->p_ysptr->p_osptr = (p)->p_osptr; \ + else \ + (p)->p_pptr->p_cptr = (p)->p_osptr + +#define SET_LINKS(p) \ + (p)->p_ysptr = NULL; \ + if ((p)->p_osptr = (p)->p_pptr->p_cptr) \ + (p)->p_osptr->p_ysptr = p; \ + (p)->p_pptr->p_cptr = p + #endif diff --git a/include/signal.h b/include/linux/signal.h similarity index 63% rename from include/signal.h rename to include/linux/signal.h index e4126d339125..24f098f02895 100644 --- a/include/signal.h +++ b/include/linux/signal.h @@ -1,9 +1,6 @@ -#ifndef _SIGNAL_H -#define _SIGNAL_H +#ifndef _LINUX_SIGNAL_H +#define _LINUX_SIGNAL_H -#include - -typedef int sig_atomic_t; typedef unsigned int sigset_t; /* 32 bits */ #define _NSIG 32 @@ -43,9 +40,10 @@ typedef unsigned int sigset_t; /* 32 bits */ #define SIGPOLL SIGIO #define SIGXCPU 24 #define SIGXFSZ 25 +*/ + #define SIGVTALRM 26 #define SIGPROF 27 -*/ #define SIGWINCH 28 @@ -53,7 +51,6 @@ typedef unsigned int sigset_t; /* 32 bits */ #define SIGLOST 29 */ -/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */ #define SA_NOCLDSTOP 1 #define SA_INTERRUPT 0x20000000 #define SA_NOMASK 0x40000000 @@ -67,11 +64,6 @@ typedef unsigned int sigset_t; /* 32 bits */ #define SIG_IGN ((void (*)(int))1) /* ignore signal */ #define SIG_ERR ((void (*)(int))-1) /* error return from signal */ -#ifdef notdef -#define sigemptyset(mask) ((*(mask) = 0), 1) -#define sigfillset(mask) ((*(mask) = ~0), 1) -#endif - struct sigaction { void (*sa_handler)(int); sigset_t sa_mask; @@ -79,17 +71,4 @@ struct sigaction { void (*sa_restorer)(void); }; -void (*signal(int _sig, void (*_func)(int)))(int); -int raise(int sig); -int kill(pid_t pid, int sig); -int sigaddset(sigset_t *mask, int signo); -int sigdelset(sigset_t *mask, int signo); -int sigemptyset(sigset_t *mask); -int sigfillset(sigset_t *mask); -int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */ -int sigpending(sigset_t *set); -int sigprocmask(int how, sigset_t *set, sigset_t *oldset); -int sigsuspend(sigset_t *sigmask); -int sigaction(int sig, struct sigaction *act, struct sigaction *oldact); - -#endif /* _SIGNAL_H */ +#endif diff --git a/include/linux/socket.h b/include/linux/socket.h new file mode 100644 index 000000000000..e07703ef6f5e --- /dev/null +++ b/include/linux/socket.h @@ -0,0 +1,30 @@ +#ifndef _LINUX_SOCKET_H +#define _LINUX_SOCKET_H + +struct sockaddr { + u_short sa_family; /* address family, AF_xxx */ + char sa_data[14]; /* 14 bytes of protocol address */ +}; + +/* + * socket types + */ +#define SOCK_STREAM 1 /* stream (connection) socket */ +#define SOCK_DGRAM 2 /* datagram (connectionless) socket */ +#define SOCK_SEQPACKET 3 /* sequential packet socket */ +#define SOCK_RAW 4 /* raw socket */ + +/* + * supported address families + */ +#define AF_UNSPEC 0 +#define AF_UNIX 1 +#define AF_INET 2 + +/* + * protocol families, same as address families + */ +#define PF_UNIX AF_UNIX +#define PF_INET AF_INET + +#endif diff --git a/include/sys/stat.h b/include/linux/stat.h similarity index 50% rename from include/sys/stat.h rename to include/linux/stat.h index b709165c4517..2e11b1ce8494 100644 --- a/include/sys/stat.h +++ b/include/linux/stat.h @@ -1,20 +1,41 @@ -#ifndef _SYS_STAT_H -#define _SYS_STAT_H - -#include - -struct stat { - dev_t st_dev; - ino_t st_ino; - umode_t st_mode; - nlink_t st_nlink; - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - off_t st_size; - time_t st_atime; - time_t st_mtime; - time_t st_ctime; +#ifndef _LINUX_STAT_H +#define _LINUX_STAT_H + +struct old_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long st_mtime; + unsigned long st_ctime; +}; + +struct new_stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long __unused1; + unsigned long st_mtime; + unsigned long __unused2; + unsigned long st_ctime; + unsigned long __unused3; + unsigned long __unused4; + unsigned long __unused5; }; #define S_IFMT 00170000 @@ -52,11 +73,4 @@ struct stat { #define S_IWOTH 00002 #define S_IXOTH 00001 -extern int chmod(const char *_path, mode_t mode); -extern int fstat(int fildes, struct stat *stat_buf); -extern int mkdir(const char *_path, mode_t mode); -extern int mkfifo(const char *_path, mode_t mode); -extern int stat(const char *filename, struct stat *stat_buf); -extern mode_t umask(mode_t mask); - #endif diff --git a/include/stddef.h b/include/linux/stddef.h similarity index 52% rename from include/stddef.h rename to include/linux/stddef.h index 697c4f4ad684..c6221e7126af 100644 --- a/include/stddef.h +++ b/include/linux/stddef.h @@ -1,18 +1,15 @@ -#ifndef _STDDEF_H -#define _STDDEF_H - -#ifndef _PTRDIFF_T -#define _PTRDIFF_T -typedef long ptrdiff_t; -#endif +#ifndef _LINUX_STDDEF_H +#define _LINUX_STDDEF_H #ifndef _SIZE_T #define _SIZE_T -typedef unsigned long size_t; +typedef unsigned int size_t; #endif #undef NULL #define NULL ((void *)0) +#undef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + #endif diff --git a/include/linux/string.h b/include/linux/string.h index 7da1793d138e..cdff971dcc5c 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -1,17 +1,10 @@ -#ifndef _STRING_H_ -#define _STRING_H_ +#ifndef _LINUX_STRING_H_ +#define _LINUX_STRING_H_ #ifndef NULL #define NULL ((void *) 0) #endif -#ifndef _SIZE_T -#define _SIZE_T -typedef unsigned int size_t; -#endif - -extern char * strerror(int errno); - /* * This string-include defines all string functions as inline * functions. Use gcc. It also assumes ds=es=data space, this should be @@ -21,7 +14,7 @@ extern char * strerror(int errno); * set, making the functions fast and clean. String instructions have been * used through-out, making for "slightly" unclear code :-) * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ extern inline char * strcpy(char * dest,const char *src) @@ -276,7 +269,7 @@ extern char * ___strtok; extern inline char * strtok(char * s,const char * ct) { -register char * __res __asm__("si"); +register char * __res; __asm__("testl %1,%1\n\t" "jne 1f\n\t" "testl %0,%0\n\t" @@ -327,12 +320,7 @@ __asm__("testl %1,%1\n\t" "jne 8f\n\t" "movl %0,%1\n" "8:" -#if __GNUC__ == 2 - :"=r" (__res) -#else - :"=b" (__res) -#endif - ,"=S" (___strtok) + :"=b" (__res),"=S" (___strtok) :"0" (___strtok),"1" (s),"g" (ct) :"ax","cx","dx","di"); return __res; diff --git a/include/linux/sys.h b/include/linux/sys.h index df8264c3806c..deae36f422fb 100644 --- a/include/linux/sys.h +++ b/include/linux/sys.h @@ -104,6 +104,16 @@ extern int sys_profil(); extern int sys_statfs(); extern int sys_fstatfs(); extern int sys_ioperm(); +extern int sys_socketcall(); +extern int sys_syslog(); +extern int sys_getitimer(); +extern int sys_setitimer(); +extern int sys_newstat(); +extern int sys_newlstat(); +extern int sys_newfstat(); +extern int sys_newuname(); +extern int sys_iopl(); +extern int sys_vhangup(); fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, @@ -123,7 +133,9 @@ sys_gettimeofday, sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink, sys_lstat, sys_readlink, sys_uselib, sys_swapon, sys_reboot, sys_readdir, sys_mmap, sys_munmap, sys_truncate, sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority, -sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm }; +sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, +sys_socketcall, sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, +sys_newlstat, sys_newfstat, sys_newuname, sys_iopl, sys_vhangup }; /* So we don't have to do any more manual updating.... */ int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); diff --git a/include/termios.h b/include/linux/termios.h similarity index 88% rename from include/termios.h rename to include/linux/termios.h index b04a0e9928ec..372cda194a80 100644 --- a/include/termios.h +++ b/include/linux/termios.h @@ -1,7 +1,5 @@ -#ifndef _TERMIOS_H -#define _TERMIOS_H - -#include +#ifndef _LINUX_TERMIOS_H +#define _LINUX_TERMIOS_H /* 0x54 is just a magic number to make these relatively uniqe ('T') */ @@ -34,6 +32,20 @@ #define FIONREAD 0x541B #define TIOCINQ FIONREAD #define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 +#define FIONBIO 0x5421 +#define TIOCNOTTY 0x5422 + +/* Used for packet mode */ +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_DOSTOP 16 +#define TIOCPKT_NOSTOP 32 struct winsize { unsigned short ws_row; @@ -210,16 +222,4 @@ struct termios { #define TCSADRAIN 1 #define TCSAFLUSH 2 -extern speed_t cfgetispeed(struct termios *termios_p); -extern speed_t cfgetospeed(struct termios *termios_p); -extern int cfsetispeed(struct termios *termios_p, speed_t speed); -extern int cfsetospeed(struct termios *termios_p, speed_t speed); -extern int tcdrain(int fildes); -extern int tcflow(int fildes, int action); -extern int tcflush(int fildes, int queue_selector); -extern int tcgetattr(int fildes, struct termios *termios_p); -extern int tcsendbreak(int fildes, int duration); -extern int tcsetattr(int fildes, int optional_actions, - struct termios *termios_p); - #endif diff --git a/include/linux/time.h b/include/linux/time.h new file mode 100644 index 000000000000..f154e5d90053 --- /dev/null +++ b/include/linux/time.h @@ -0,0 +1,33 @@ +#ifndef _LINUX_TIME_H +#define _LINUX_TIME_H + +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; + +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +#define FD_SETSIZE (8*sizeof(fd_set)) +#define FD_SET(fd,fdsetp) (*(fdsetp) |= (1 << (fd))) +#define FD_CLR(fd,fdsetp) (*(fdsetp) &= ~(1 << (fd))) +#define FD_ISSET(fd,fdsetp) ((*(fdsetp) >> fd) & 1) +#define FD_ZERO(fdsetp) (*(fdsetp) = 0) + +/* + * Names of the interval timers, and structure + * defining a timer setting. + */ +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +struct itimerval { + struct timeval it_interval; /* timer interval */ + struct timeval it_value; /* current value */ +}; + +#endif diff --git a/include/linux/timer.h b/include/linux/timer.h index ab3c74ed3be1..84d6aa8d58f0 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -1,5 +1,5 @@ -#ifndef _TIMER_H -#define _TIMER_H +#ifndef _LINUX_TIMER_H +#define _LINUX_TIMER_H /* * DON'T CHANGE THESE!! Most of them are hardcoded into some assembly language @@ -20,6 +20,8 @@ * HD_TIMER harddisk timer * * FLOPPY_TIMER floppy disk timer (not used right now) + * + * SCSI_TIMER scsi.c timeout timer */ #define BLANK_TIMER 0 @@ -37,6 +39,7 @@ #define HD_TIMER 16 #define FLOPPY_TIMER 17 +#define SCSI_TIMER 18 struct timer_struct { unsigned long expires; diff --git a/include/sys/times.h b/include/linux/times.h similarity index 51% rename from include/sys/times.h rename to include/linux/times.h index 68d5bfb27107..e7ae2fa87c20 100644 --- a/include/sys/times.h +++ b/include/linux/times.h @@ -1,7 +1,5 @@ -#ifndef _TIMES_H -#define _TIMES_H - -#include +#ifndef _LINUX_TIMES_H +#define _LINUX_TIMES_H struct tms { time_t tms_utime; @@ -10,6 +8,4 @@ struct tms { time_t tms_cstime; }; -extern time_t times(struct tms * tp); - #endif diff --git a/include/linux/tty.h b/include/linux/tty.h index 22f0f0e98836..d6c1f6e72d40 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -1,3 +1,6 @@ +#ifndef _LINUX_TTY_H +#define _LINUX_TTY_H + /* * 'tty.h' defines some structures used by tty_io.c and some defines. * @@ -6,16 +9,54 @@ * offsets into 'tty_queue' */ -#ifndef _TTY_H -#define _TTY_H +#include + +#include -#define MAX_CONSOLES 8 +#define NR_CONSOLES 8 #define NR_SERIALS 4 #define NR_PTYS 4 -extern int NR_CONSOLES; +/* + * These are set up by the setup-routine at boot-time: + */ + +struct screen_info { + unsigned char orig_x; + unsigned char orig_y; + unsigned char unused1[2]; + unsigned short orig_video_page; + unsigned char orig_video_mode; + unsigned char orig_video_cols; + unsigned short orig_video_ega_ax; + unsigned short orig_video_ega_bx; + unsigned short orig_video_ega_cx; + unsigned char orig_video_lines; +}; + +extern struct screen_info screen_info; + +#define ORIG_X (screen_info.orig_x) +#define ORIG_Y (screen_info.orig_y) +#define ORIG_VIDEO_PAGE (screen_info.orig_video_page) +#define ORIG_VIDEO_MODE (screen_info.orig_video_mode) +#define ORIG_VIDEO_COLS (screen_info.orig_video_cols) +#define ORIG_VIDEO_EGA_AX (screen_info.orig_video_ega_ax) +#define ORIG_VIDEO_EGA_BX (screen_info.orig_video_ega_bx) +#define ORIG_VIDEO_EGA_CX (screen_info.orig_video_ega_cx) +#define ORIG_VIDEO_LINES (screen_info.orig_video_lines) + +#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ +#define VIDEO_TYPE_CGA 0x11 /* CGA Display */ +#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ +#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ -#include +/* + * This character is the same as _POSIX_VDISABLE: it cannot be used as + * a c_cc[] character, but indicates that a particular special character + * isn't in use (eg VINTR ahs no character etc) + */ +#define __DISABLED_CHAR '\0' #define TTY_BUF_SIZE 2048 @@ -23,10 +64,27 @@ struct tty_queue { unsigned long data; unsigned long head; unsigned long tail; - struct task_struct * proc_list; - char buf[TTY_BUF_SIZE]; + struct wait_queue * proc_list; + unsigned char buf[TTY_BUF_SIZE]; }; +struct serial_struct { + unsigned short type; + unsigned short line; + unsigned short port; + unsigned short irq; + struct tty_struct * tty; +}; + +/* + * These are the supported serial types. + */ +#define PORT_UNKNOWN 0 +#define PORT_8250 1 +#define PORT_16450 2 +#define PORT_16550 3 +#define PORT_16550A 4 + #define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00) #define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40) #define IS_A_PTY(min) ((min) & 0x80) @@ -41,10 +99,9 @@ struct tty_queue { #define LAST(a) ((a)->buf[(TTY_BUF_SIZE-1)&((a)->head-1)]) #define FULL(a) (!LEFT(a)) #define CHARS(a) (((a)->head-(a)->tail)&(TTY_BUF_SIZE-1)) -#define GETCH(queue,c) \ -(void)({c=(queue)->buf[(queue)->tail];INC((queue)->tail);}) -#define PUTCH(c,queue) \ -(void)({(queue)->buf[(queue)->head]=(c);INC((queue)->head);}) + +extern void put_tty_queue(char c, struct tty_queue * queue); +extern int get_tty_queue(struct tty_queue * queue); #define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) #define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT]) @@ -55,14 +112,46 @@ struct tty_queue { #define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) #define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP]) +#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) +#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) +#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) + +#define L_CANON(tty) _L_FLAG((tty),ICANON) +#define L_ISIG(tty) _L_FLAG((tty),ISIG) +#define L_ECHO(tty) _L_FLAG((tty),ECHO) +#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) +#define L_ECHONL(tty) _L_FLAG((tty),ECHONL) +#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) +#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) +#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) + +#define I_UCLC(tty) _I_FLAG((tty),IUCLC) +#define I_NLCR(tty) _I_FLAG((tty),INLCR) +#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_STRP(tty) _I_FLAG((tty),ISTRIP) + +#define O_POST(tty) _O_FLAG((tty),OPOST) +#define O_NLCR(tty) _O_FLAG((tty),ONLCR) +#define O_CRNL(tty) _O_FLAG((tty),OCRNL) +#define O_NLRET(tty) _O_FLAG((tty),ONLRET) +#define O_LCUC(tty) _O_FLAG((tty),OLCUC) + +#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD) +#define C_HUP(tty) (C_SPEED((tty)) == B0) + struct tty_struct { struct termios termios; int pgrp; int session; int stopped; - int busy; + int flags; + int count; struct winsize winsize; void (*write)(struct tty_struct * tty); + struct tty_struct *link; struct tty_queue *read_q; struct tty_queue *write_q; struct tty_queue *secondary; @@ -71,38 +160,46 @@ struct tty_struct { /* * 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. + * 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. */ -#define TTY_WRITE_BUSY 1 -#define TTY_READ_BUSY 2 - -#define TTY_WRITE_FLUSH(tty) \ -do { \ - cli(); \ - if (!EMPTY((tty)->write_q) && !(TTY_WRITE_BUSY & (tty)->busy)) { \ - (tty)->busy |= TTY_WRITE_BUSY; \ - sti(); \ - (tty)->write((tty)); \ - cli(); \ - (tty)->busy &= ~TTY_WRITE_BUSY; \ - } \ - sti(); \ -} while (0) - -#define TTY_READ_FLUSH(tty) \ -do { \ - cli(); \ - if (!EMPTY((tty)->read_q) && !(TTY_READ_BUSY & (tty)->busy)) { \ - (tty)->busy |= TTY_READ_BUSY; \ - sti(); \ - copy_to_cooked((tty)); \ - cli(); \ - (tty)->busy &= ~TTY_READ_BUSY; \ - } \ - sti(); \ -} while (0) +#define TTY_WRITE_BUSY 0 +#define TTY_READ_BUSY 1 +#define TTY_CR_PENDING 2 + +/* + * 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)) + +extern void tty_write_flush(struct tty_struct *); +extern void tty_read_flush(struct tty_struct *); extern struct tty_struct tty_table[]; +extern struct serial_struct serial_table[]; +extern struct tty_struct * redirect; extern int fg_console; extern unsigned long video_num_columns; extern unsigned long video_num_lines; @@ -119,22 +216,47 @@ extern unsigned long video_num_lines; */ #define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" -extern void rs_init(void); -extern void lp_init(void); -extern void con_init(void); -extern void tty_init(void); +extern long rs_init(long); +extern long lp_init(long); +extern long con_init(long); +extern long tty_init(long); + +extern void flush_input(struct tty_struct * tty); +extern void flush_output(struct tty_struct * tty); +extern void wait_until_sent(struct tty_struct * tty); +extern void copy_to_cooked(struct tty_struct * tty); extern int tty_ioctl(struct inode *, struct file *, unsigned int, unsigned int); +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); + +/* 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); -extern void serial_open(unsigned int line); +/* serial.c */ + +extern int serial_open(unsigned int line, struct file * filp); +extern void serial_close(unsigned int line, struct file * filp); +extern void change_speed(unsigned int line); +extern void send_break(unsigned int line); +extern int get_serial_info(unsigned int, struct serial_struct *); +extern int set_serial_info(unsigned int, struct serial_struct *); + +/* pty.c */ + +extern int pty_open(unsigned int dev, struct file * filp); +extern void pty_close(unsigned int dev, struct file * filp); -void copy_to_cooked(struct tty_struct * tty); +/* console.c */ void update_screen(int new_console); +void blank_screen(void); +void unblank_screen(void); #endif diff --git a/include/sys/types.h b/include/linux/types.h similarity index 72% rename from include/sys/types.h rename to include/linux/types.h index 8529532ad4c0..53088a28cff2 100644 --- a/include/sys/types.h +++ b/include/linux/types.h @@ -1,16 +1,26 @@ -#ifndef _SYS_TYPES_H -#define _SYS_TYPES_H +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H #ifndef _SIZE_T #define _SIZE_T typedef unsigned int size_t; #endif +#ifndef _SSIZE_T +#define _SSIZE_T +typedef int ssize_t; +#endif + #ifndef _TIME_T #define _TIME_T typedef long time_t; #endif +#ifndef _CLOCK_T +#define _CLOCK_T +typedef long clock_t; +#endif + #ifndef _PTRDIFF_T #define _PTRDIFF_T typedef long ptrdiff_t; @@ -24,16 +34,29 @@ typedef int pid_t; typedef unsigned short uid_t; typedef unsigned short gid_t; typedef unsigned short dev_t; +#ifdef OLD_LINUX typedef unsigned short ino_t; +#else +typedef unsigned long ino_t; +#endif typedef unsigned short mode_t; typedef unsigned short umode_t; typedef unsigned short nlink_t; typedef int daddr_t; typedef long off_t; + +/* bsd */ typedef unsigned char u_char; typedef unsigned short u_short; +typedef unsigned int u_int; typedef unsigned long u_long; + +/* sysv */ +typedef unsigned char unchar; typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + typedef char *caddr_t; typedef unsigned char cc_t; diff --git a/include/linux/un.h b/include/linux/un.h new file mode 100644 index 000000000000..5c4150346fac --- /dev/null +++ b/include/linux/un.h @@ -0,0 +1,9 @@ +#ifndef _LINUX_UN_H +#define _LINUX_UN_H + +struct sockaddr_un { + u_short sun_family; /* AF_UNIX */ + char sun_path[108]; /* pathname */ +}; + +#endif /* _UN_H */ diff --git a/include/linux/unistd.h b/include/linux/unistd.h new file mode 100644 index 000000000000..4675fbce0cff --- /dev/null +++ b/include/linux/unistd.h @@ -0,0 +1,209 @@ +#ifndef _LINUX_UNISTD_H +#define _LINUX_UNISTD_H + +/* + * This file contains the system call numbers and the syscallX + * macros + */ + +#define __NR_setup 0 /* used only by init, to get system going */ +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_chown 16 +#define __NR_break 17 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_phys 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_olduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_profil 98 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_uname 109 +#define __NR_iopl 110 + +extern int errno; + +/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ +#define _syscall0(type,name) \ +type name(void) \ +{ \ +long __res; \ +__asm__ volatile ("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name)); \ +if (__res >= 0) \ + return (type) __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall1(type,name,atype,a) \ +type name(atype a) \ +{ \ +long __res; \ +__asm__ volatile ("movl %2,%%ebx\n\t" \ + "int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"g" ((long)(a)):"bx"); \ +if (__res >= 0) \ + return (type) __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall2(type,name,atype,a,btype,b) \ +type name(atype a,btype b) \ +{ \ +long __res; \ +__asm__ volatile ("movl %2,%%ebx\n\t" \ + "int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)):"bx"); \ +if (__res >= 0) \ + return (type) __res; \ +errno = -__res; \ +return -1; \ +} + +#define _syscall3(type,name,atype,a,btype,b,ctype,c) \ +type name(atype a,btype b,ctype c) \ +{ \ +long __res; \ +__asm__ volatile ("movl %2,%%ebx\n\t" \ + "int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)),"d" ((long)(c)):"bx"); \ +if (__res>=0) \ + return (type) __res; \ +errno=-__res; \ +return -1; \ +} + +#define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \ +type name (atype a, btype b, ctype c, dtype d) \ +{ \ +long __res; \ +__asm__ volatile ("movl %2,%%ebx\n\t" \ + "int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \ + "d" ((long)(c)),"S" ((long)(d))); \ +if (__res>=0) \ + return (type) __res; \ +errno=-__res; \ +return -1; \ +} + +#define _syscall5(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e) \ +type name (atype a,btype b,ctype c,dtype d,etype e) \ +{ \ +long __res; \ +__asm__ volatile ("movl %2,%%ebx\n\t" \ + "int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \ + "d" ((long)(c)),"S" ((long)(d)),"D" ((long)(e))); \ +if (__res>=0) \ + return (type) __res; \ +errno=-__res; \ +return -1; \ +} + +#endif /* _LINUX_UNISTD_H */ diff --git a/include/linux/user.h b/include/linux/user.h new file mode 100644 index 000000000000..106e710f114b --- /dev/null +++ b/include/linux/user.h @@ -0,0 +1,75 @@ +#ifndef _LINUX_USER_H +#define _LINUX_USER_H + +#include +/* Core file format: The core file is written in such a way that gdb + can understand it and provide useful information to the user (under + linux we use the 'trad-core' bfd). There are quite a number of + obstacles to being able to view the contents of the floating point + registers, and until these are solved you will not be able to view the + contents of them. Actually, you can read in the core file and look at + the contents of the user struct to find out what the floating point + registers contain. + The actual file contents are as follows: + UPAGE: 1 page consisting of a user struct that tells gdb what is present + in the file. Directly after this is a copy of the task_struct, which + is currently not used by gdb, but it may come in useful at some point. + All of the registers are stored as part of the upage. The upage should + always be only one page. + DATA: The data area is stored. We use current->end_text to + current->brk to pick up all of the user variables, plus any memory + that may have been malloced. No attempt is made to determine if a page + is demand-zero or if a page is totally unused, we just cover the entire + range. All of the addresses are rounded in such a way that an integral + number of pages is written. + STACK: We need the stack information in order to get a meaningful + backtrace. We need to write the data from (esp) to + current->start_stack, so we round each of these off in order to be able + to write an integer number of pages. + The minimum core file size is 3 pages, or 12288 bytes. +*/ + +struct user_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 */ +}; + +/* When the kernel dumps core, it starts by dumping the user struct - + this will be used by gdb to figure out where the data and stack segments + are within the file, and what virtual addresses to use. */ +struct user{ +/* We start with the registers, to mimic the way that "memory" is returned + from the ptrace(3,...) function. */ + struct pt_regs regs; /* Where the registers are actually stored */ +/* ptrace does not yet supply these. Someday.... */ + int u_fpvalid; /* True if math co-processor being used. */ + /* for this mess. Not yet used. */ + struct user_i387_struct i387; /* Math Co-processor registers. */ +/* The rest of this junk is to help gdb figure out what goes where */ + unsigned long int u_tsize; /* Text segment size (pages). */ + unsigned long int u_dsize; /* Data segment size (pages). */ + unsigned long int u_ssize; /* Stack segment size (pages). */ + unsigned long start_code; /* Starting virtual address of text. */ + unsigned long start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + long int signal; /* Signal that caused the core dump. */ + char * u_comm; /* User command that was responsible */ + struct pt_regs * u_ar0; /* Used by gdb to help find the values for */ + /* the registers. */ + struct user_i387_struct* u_fpstate; /* Math Co-processor pointer. */ + unsigned long magic; /* To uniquely identify a core file */ +}; +#define NBPG 4096 +#define UPAGES 1 +#define HOST_TEXT_START_ADDR (u.start_code) +#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) + +#endif diff --git a/include/linux/utime.h b/include/linux/utime.h new file mode 100644 index 000000000000..c6bf27b7897e --- /dev/null +++ b/include/linux/utime.h @@ -0,0 +1,9 @@ +#ifndef _LINUX_UTIME_H +#define _LINUX_UTIME_H + +struct utimbuf { + time_t actime; + time_t modtime; +}; + +#endif diff --git a/include/linux/utsname.h b/include/linux/utsname.h new file mode 100644 index 000000000000..4efd512949bc --- /dev/null +++ b/include/linux/utsname.h @@ -0,0 +1,24 @@ +#ifndef _LINUX_UTSNAME_H +#define _LINUX_UTSNAME_H + +#define __OLD_UTS_LEN 8 + +struct old_utsname { + char sysname[9]; + char nodename[9]; + char release[9]; + char version[9]; + char machine[9]; +}; + +#define __NEW_UTS_LEN 64 + +struct new_utsname { + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; +}; + +#endif diff --git a/include/sys/vfs.h b/include/linux/vfs.h similarity index 83% rename from include/sys/vfs.h rename to include/linux/vfs.h index c7e113e9e0b6..6d1f62576e0d 100644 --- a/include/sys/vfs.h +++ b/include/linux/vfs.h @@ -1,5 +1,5 @@ -#ifndef _SYS_VFS_H_ -#define _SYS_VFS_H_ +#ifndef _LINUX_VFS_H +#define _LINUX_VFS_H typedef struct { long val[2]; diff --git a/include/linux/wait.h b/include/linux/wait.h new file mode 100644 index 000000000000..0c13811aa0f4 --- /dev/null +++ b/include/linux/wait.h @@ -0,0 +1,22 @@ +#ifndef _LINUX_WAIT_H +#define _LINUX_WAIT_H + +#include + +#define WNOHANG 1 +#define WUNTRACED 2 + +struct wait_queue { + struct task_struct * task; + struct wait_queue * next; +}; + +typedef struct select_table_struct { + int nr; + struct select_table_entry { + struct wait_queue wait; + struct wait_queue ** wait_address; + } entry[NR_OPEN*3]; +} select_table; + +#endif diff --git a/include/sys/time.h b/include/sys/time.h deleted file mode 100644 index 1165c2632cc7..000000000000 --- a/include/sys/time.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _SYS_TIME_H -#define _SYS_TIME_H - -/* gettimofday returns this */ -struct timeval { - long tv_sec; /* seconds */ - long tv_usec; /* microseconds */ -}; - -struct timezone { - int tz_minuteswest; /* minutes west of Greenwich */ - int tz_dsttime; /* type of dst correction */ -}; - -#define DST_NONE 0 /* not on dst */ -#define DST_USA 1 /* USA style dst */ -#define DST_AUST 2 /* Australian style dst */ -#define DST_WET 3 /* Western European dst */ -#define DST_MET 4 /* Middle European dst */ -#define DST_EET 5 /* Eastern European dst */ -#define DST_CAN 6 /* Canada */ -#define DST_GB 7 /* Great Britain and Eire */ -#define DST_RUM 8 /* Rumania */ -#define DST_TUR 9 /* Turkey */ -#define DST_AUSTALT 10 /* Australian style with shift in 1986 */ - -#define FD_SETSIZE (8*sizeof(fd_set)) -#define FD_SET(fd,fdsetp) (*(fdsetp) |= (1 << (fd))) -#define FD_CLR(fd,fdsetp) (*(fdsetp) &= ~(1 << (fd))) -#define FD_ISSET(fd,fdsetp) ((*(fdsetp) >> fd) & 1) -#define FD_ZERO(fdsetp) (*(fdsetp) = 0) - -/* - * Operations on timevals. - * - * NB: timercmp does not work for >= or <=. - */ -#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) -#define timercmp(tvp, uvp, cmp) \ - ((tvp)->tv_sec cmp (uvp)->tv_sec || \ - (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec) -#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) - -/* - * Names of the interval timers, and structure - * defining a timer setting. - */ -#define ITIMER_REAL 0 -#define ITIMER_VIRTUAL 1 -#define ITIMER_PROF 2 - -struct itimerval { - struct timeval it_interval; /* timer interval */ - struct timeval it_value; /* current value */ -}; - -#include -#include - -int gettimeofday(struct timeval * tp, struct timezone * tz); -int select(int width, fd_set * readfds, fd_set * writefds, - fd_set * exceptfds, struct timeval * timeout); - -#endif /*_SYS_TIME_H*/ diff --git a/include/sys/utsname.h b/include/sys/utsname.h deleted file mode 100644 index 0e6aef8b4d52..000000000000 --- a/include/sys/utsname.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _SYS_UTSNAME_H -#define _SYS_UTSNAME_H - -#include -#include - -struct utsname { - char sysname[9]; - char nodename[MAXHOSTNAMELEN+1]; - char release[9]; - char version[9]; - char machine[9]; -}; - -extern int uname(struct utsname * utsbuf); - -#endif diff --git a/include/sys/wait.h b/include/sys/wait.h deleted file mode 100644 index d6c336051bd0..000000000000 --- a/include/sys/wait.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _SYS_WAIT_H -#define _SYS_WAIT_H - -#include - -#define _LOW(v) ( (v) & 0377) -#define _HIGH(v) ( ((v) >> 8) & 0377) - -/* options for waitpid, WUNTRACED not supported */ -#define WNOHANG 1 -#define WUNTRACED 2 - -#define WIFEXITED(s) (!((s)&0xFF)) -#define WIFSTOPPED(s) (((s)&0xFF)==0x7F) -#define WEXITSTATUS(s) (((s)>>8)&0xFF) -#define WTERMSIG(s) ((s)&0x7F) -#define WCOREDUMP(s) ((s)&0x80) -#define WSTOPSIG(s) (((s)>>8)&0xFF) -#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) - -pid_t wait(int *stat_loc); -pid_t waitpid(pid_t pid, int *stat_loc, int options); - -#endif diff --git a/include/time.h b/include/time.h index f7e7ba1982ee..66aa63489eac 100644 --- a/include/time.h +++ b/include/time.h @@ -17,7 +17,10 @@ typedef unsigned int size_t; #define CLOCKS_PER_SEC 100 +#ifndef _CLOCK_T +#define _CLOCK_T typedef long clock_t; +#endif struct tm { int tm_sec; @@ -34,6 +37,10 @@ struct tm { #define __isleap(year) \ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +#ifdef __cplusplus +extern "C" { +#endif + clock_t clock(void); time_t time(time_t * tp); double difftime(time_t time2, time_t time1); @@ -46,4 +53,8 @@ struct tm *localtime(const time_t * tp); size_t strftime(char * s, size_t smax, const char * fmt, const struct tm * tp); void tzset(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/include/unistd.h b/include/unistd.h deleted file mode 100644 index 6631444ad58b..000000000000 --- a/include/unistd.h +++ /dev/null @@ -1,348 +0,0 @@ -#ifndef _UNISTD_H -#define _UNISTD_H - -/* ok, this may be a joke, but I'm working on it */ -#define _POSIX_VERSION 198808L - -#define _POSIX_CHOWN_RESTRICTED 1 /* only root can do a chown (I think..) */ -#define _POSIX_NO_TRUNC 1 /* no pathname truncation (but see kernel) */ -#define _POSIX_VDISABLE '\0' /* character to disable things like ^C */ -#define _POSIX_JOB_CONTROL 1 -#define _POSIX_SAVED_IDS 1 /* Implemented, for whatever good it is */ - -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 - -#ifndef NULL -#define NULL ((void *)0) -#endif - -/* access */ -#define F_OK 0 -#define X_OK 1 -#define W_OK 2 -#define R_OK 4 - -/* lseek */ -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 - -/* _SC stands for System Configuration. We don't use them much */ -#define _SC_ARG_MAX 1 -#define _SC_CHILD_MAX 2 -#define _SC_CLOCKS_PER_SEC 3 -#define _SC_NGROUPS_MAX 4 -#define _SC_OPEN_MAX 5 -#define _SC_JOB_CONTROL 6 -#define _SC_SAVED_IDS 7 -#define _SC_VERSION 8 - -/* more (possibly) configurable things - now pathnames */ -#define _PC_LINK_MAX 1 -#define _PC_MAX_CANON 2 -#define _PC_MAX_INPUT 3 -#define _PC_NAME_MAX 4 -#define _PC_PATH_MAX 5 -#define _PC_PIPE_BUF 6 -#define _PC_NO_TRUNC 7 -#define _PC_VDISABLE 8 -#define _PC_CHOWN_RESTRICTED 9 - -#if 0 -/* XXX - illegally already. - * The rest of these includes are also illegal (too much pollution). - */ -#include -#endif -#include -#include -#include -#include -#include -#include - -#ifdef __LIBRARY__ - -#define __NR_setup 0 /* used only by init, to get system going */ -#define __NR_exit 1 -#define __NR_fork 2 -#define __NR_read 3 -#define __NR_write 4 -#define __NR_open 5 -#define __NR_close 6 -#define __NR_waitpid 7 -#define __NR_creat 8 -#define __NR_link 9 -#define __NR_unlink 10 -#define __NR_execve 11 -#define __NR_chdir 12 -#define __NR_time 13 -#define __NR_mknod 14 -#define __NR_chmod 15 -#define __NR_chown 16 -#define __NR_break 17 -#define __NR_stat 18 -#define __NR_lseek 19 -#define __NR_getpid 20 -#define __NR_mount 21 -#define __NR_umount 22 -#define __NR_setuid 23 -#define __NR_getuid 24 -#define __NR_stime 25 -#define __NR_ptrace 26 -#define __NR_alarm 27 -#define __NR_fstat 28 -#define __NR_pause 29 -#define __NR_utime 30 -#define __NR_stty 31 -#define __NR_gtty 32 -#define __NR_access 33 -#define __NR_nice 34 -#define __NR_ftime 35 -#define __NR_sync 36 -#define __NR_kill 37 -#define __NR_rename 38 -#define __NR_mkdir 39 -#define __NR_rmdir 40 -#define __NR_dup 41 -#define __NR_pipe 42 -#define __NR_times 43 -#define __NR_prof 44 -#define __NR_brk 45 -#define __NR_setgid 46 -#define __NR_getgid 47 -#define __NR_signal 48 -#define __NR_geteuid 49 -#define __NR_getegid 50 -#define __NR_acct 51 -#define __NR_phys 52 -#define __NR_lock 53 -#define __NR_ioctl 54 -#define __NR_fcntl 55 -#define __NR_mpx 56 -#define __NR_setpgid 57 -#define __NR_ulimit 58 -#define __NR_uname 59 -#define __NR_umask 60 -#define __NR_chroot 61 -#define __NR_ustat 62 -#define __NR_dup2 63 -#define __NR_getppid 64 -#define __NR_getpgrp 65 -#define __NR_setsid 66 -#define __NR_sigaction 67 -#define __NR_sgetmask 68 -#define __NR_ssetmask 69 -#define __NR_setreuid 70 -#define __NR_setregid 71 -#define __NR_sigsuspend 72 -#define __NR_sigpending 73 -#define __NR_sethostname 74 -#define __NR_setrlimit 75 -#define __NR_getrlimit 76 -#define __NR_getrusage 77 -#define __NR_gettimeofday 78 -#define __NR_settimeofday 79 -#define __NR_getgroups 80 -#define __NR_setgroups 81 -#define __NR_select 82 -#define __NR_symlink 83 -#define __NR_lstat 84 -#define __NR_readlink 85 -#define __NR_uselib 86 -#define __NR_swapon 87 -#define __NR_reboot 88 -#define __NR_readdir 89 -#define __NR_mmap 90 -#define __NR_munmap 91 -/* - * Not all of these are implemented yet, but these are the - * numbers they will use. - */ -#define __NR_truncate 92 -#define __NR_ftruncate 93 -#define __NR_fchmod 94 -#define __NR_fchown 95 -#define __NR_getpriority 96 -#define __NR_setpriority 97 -#define __NR_profil 98 -#define __NR_statfs 99 -#define __NR_fstatfs 100 -#define __NR_ioperm 101 - -/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ -#define _syscall0(type,name) \ -type name(void) \ -{ \ -long __res; \ -__asm__ volatile ("int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name)); \ -if (__res >= 0) \ - return (type) __res; \ -errno = -__res; \ -return -1; \ -} - -#define _syscall1(type,name,atype,a) \ -type name(atype a) \ -{ \ -long __res; \ -__asm__ volatile ("movl %2,%%ebx\n\t" \ - "int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name),"g" ((long)(a)):"bx"); \ -if (__res >= 0) \ - return (type) __res; \ -errno = -__res; \ -return -1; \ -} - -#define _syscall2(type,name,atype,a,btype,b) \ -type name(atype a,btype b) \ -{ \ -long __res; \ -__asm__ volatile ("movl %2,%%ebx\n\t" \ - "int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)):"bx"); \ -if (__res >= 0) \ - return (type) __res; \ -errno = -__res; \ -return -1; \ -} - -#define _syscall3(type,name,atype,a,btype,b,ctype,c) \ -type name(atype a,btype b,ctype c) \ -{ \ -long __res; \ -__asm__ volatile ("movl %2,%%ebx\n\t" \ - "int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name),"g" ((long)(a)),"c" ((long)(b)),"d" ((long)(c)):"bx"); \ -if (__res>=0) \ - return (type) __res; \ -errno=-__res; \ -return -1; \ -} - -#define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \ -type name (atype a, btype b, ctype c, dtype d) \ -{ \ -long __res; \ -__asm__ volatile ("movl %2,%%ebx\n\t" \ - "int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \ - "d" ((long)(c)),"S" ((long)(d))); \ -if (__res>=0) \ - return (type) __res; \ -errno=-__res; \ -return -1; \ -} - -#define _syscall5(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e) \ -type name (atype a,btype b,ctype c,dtype d,etype e) \ -{ \ -long __res; \ -__asm__ volatile ("movl %2,%%ebx\n\t" \ - "int $0x80" \ - : "=a" (__res) \ - : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)), \ - "d" ((long)(c)),"S" ((long)(d)),"D" ((long)(e))); \ -if (__res>=0) \ - return (type) __res; \ -errno=-__res; \ -return -1; \ -} - -#endif /* __LIBRARY__ */ - -/* XXX - illegal. */ -extern int errno; - -/* XXX - several non-POSIX functions here, and POSIX functions that are - * supposed to be declared elsewhere. Non-promotion of short types in - * prototypes may cause trouble. Arg names should be prefixed by - * underscores. - */ -int access(const char * filename, mode_t mode); /* XXX - short type */ -int acct(const char * filename); -int brk(void * end_data_segment); -/* XXX - POSIX says unsigned alarm(unsigned sec) */ -int alarm(int sec); -void * sbrk(ptrdiff_t increment); -int chdir(const char * filename); -int chmod(const char * filename, mode_t mode); /* XXX - short type */ -int chown(const char * filename, uid_t owner, gid_t group); /* XXX - shorts */ -int chroot(const char * filename); -int close(int fildes); -int creat(const char * filename, mode_t mode); /* XXX - short type */ -int dup(int fildes); -int execve(const char * filename, char ** argv, char ** envp); -int execv(const char * pathname, char ** argv); -int execvp(const char * file, char ** argv); -int execl(const char * pathname, char * arg0, ...); -int execlp(const char * file, char * arg0, ...); -int execle(const char * pathname, char * arg0, ...); -volatile void exit(int status); -volatile void _exit(int status); -int fcntl(int fildes, int cmd, ...); -pid_t fork(void); -pid_t getpid(void); -uid_t getuid(void); -uid_t geteuid(void); -gid_t getgid(void); -gid_t getegid(void); -int ioctl(int fildes, int cmd, ...); -int kill(pid_t pid, int signal); -int link(const char * filename1, const char * filename2); -off_t lseek(int fildes, off_t offset, int origin); -int mknod(const char * filename, mode_t mode, dev_t dev); /* XXX - shorts */ -int mount(const char * specialfile, const char * dir, const char * type, int rwflag); -int nice(int val); -int open(const char * filename, int flag, ...); -int pause(void); -int pipe(int * fildes); -/* XXX**2 - POSIX says unsigned count */ -int read(int fildes, char * buf, off_t count); -int setpgrp(void); -int setpgid(pid_t pid,pid_t pgid); /* XXX - short types */ -int setuid(uid_t uid); /* XXX - short type */ -int setgid(gid_t gid); /* XXX - short type */ -void (*signal(int sig, void (*fn)(int)))(int); -int stat(const char * filename, struct stat * stat_buf); -int fstat(int fildes, struct stat * stat_buf); -int stime(time_t * tptr); -int sync(void); -time_t time(time_t * tloc); -time_t times(struct tms * tbuf); -int ulimit(int cmd, long limit); -mode_t umask(mode_t mask); -int umount(const char * specialfile); -int uname(struct utsname * name); -int unlink(const char * filename); -int ustat(dev_t dev, struct ustat * ubuf); -int utime(const char * filename, struct utimbuf * times); -pid_t waitpid(pid_t pid,int * wait_stat,int options); -pid_t wait(int * wait_stat); -/* XXX**2 - POSIX says unsigned count */ -int write(int fildes, const char * buf, off_t count); -int dup2(int oldfd, int newfd); -int getppid(void); -pid_t getpgrp(void); -pid_t setsid(void); -int sethostname(char *name, int len); -int setrlimit(int resource, struct rlimit *rlp); -int getrlimit(int resource, struct rlimit *rlp); -int getrusage(int who, struct rusage *rusage); -int gettimeofday(struct timeval *tv, struct timezone *tz); -int settimeofday(struct timeval *tv, struct timezone *tz); -int getgroups(int gidsetlen, gid_t *gidset); -int setgroups(int gidsetlen, gid_t *gidset); -int select(int width, fd_set * readfds, fd_set * writefds, - fd_set * exceptfds, struct timeval * timeout); -int swapon(const char * specialfile); -#endif diff --git a/include/utime.h b/include/utime.h deleted file mode 100644 index 83f07c7bbfe7..000000000000 --- a/include/utime.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _UTIME_H -#define _UTIME_H - -#include /* I know - shouldn't do this, but .. */ - -struct utimbuf { - time_t actime; - time_t modtime; -}; - -extern int utime(const char *filename, struct utimbuf *times); - -#endif diff --git a/init/main.c b/init/main.c index 378ca99adb28..aec7bb7ff680 100644 --- a/init/main.c +++ b/init/main.c @@ -1,13 +1,23 @@ /* * linux/init/main.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#define __LIBRARY__ -#include +#include #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + /* * we need this inline - forking from kernel space will result * in NO COPY ON WRITE (!!!), until an execve is executed. This @@ -24,34 +34,36 @@ static inline _syscall0(int,fork) static inline _syscall0(int,pause) static inline _syscall1(int,setup,void *,BIOS) static inline _syscall0(int,sync) - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include +static inline _syscall0(pid_t,setsid) +static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) +static inline _syscall1(int,dup,int,fd) +static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) +static inline _syscall3(int,open,const char *,file,int,flag,int,mode) +static inline _syscall1(int,close,int,fd) +static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) + +static inline pid_t wait(int * wait_stat) +{ + return waitpid(-1,wait_stat,0); +} static char printbuf[1024]; -extern char *strcpy(); extern int vsprintf(); extern void init(void); -extern void blk_dev_init(void); -extern void chr_dev_init(void); +extern void init_IRQ(void); +extern long blk_dev_init(long,long); +extern long chr_dev_init(long,long); extern void hd_init(void); extern void floppy_init(void); -extern void mem_init(long start, long end); +extern void sock_init(void); extern long rd_init(long mem_start, int length); extern long kernel_mktime(struct tm * tm); +#ifdef CONFIG_SCSI +extern void scsi_dev_init(void); +#endif + static int sprintf(char * str, const char *fmt, ...) { va_list args; @@ -67,9 +79,8 @@ static int sprintf(char * str, const char *fmt, ...) * This is set up by the setup-routine at boot-time */ #define EXT_MEM_K (*(unsigned short *)0x90002) -#define CON_ROWS ((*(unsigned short *)0x9000e) & 0xff) -#define CON_COLS (((*(unsigned short *)0x9000e) & 0xff00) >> 8) #define DRIVE_INFO (*(struct drive_info *)0x90080) +#define SCREEN_INFO (*(struct screen_info *)0x90000) #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) /* @@ -108,9 +119,9 @@ static void time_init(void) startup_time = kernel_mktime(&time); } -static long memory_end = 0; -static long buffer_memory_end = 0; -static long main_memory_start = 0; +static unsigned long memory_start = 0; +static unsigned long memory_end = 0; + static char term[32]; static char * argv_init[] = { "/bin/init", NULL }; @@ -123,6 +134,7 @@ static char * argv[] = { "-/bin/sh",NULL }; static char * envp[] = { "HOME=/usr/root", NULL, NULL }; struct drive_info { char dummy[32]; } drive_info; +struct screen_info screen_info; void start_kernel(void) { @@ -131,50 +143,48 @@ void start_kernel(void) * enable them */ ROOT_DEV = ORIG_ROOT_DEV; - sprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS); + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; + sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES); envp[1] = term; envp_rc[1] = term; envp_init[1] = term; - drive_info = DRIVE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); memory_end &= 0xfffff000; if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; - if (memory_end >= 12*1024*1024) - buffer_memory_end = 4*1024*1024; - else if (memory_end >= 6*1024*1024) - buffer_memory_end = 2*1024*1024; - else if (memory_end >= 4*1024*1024) - buffer_memory_end = 3*512*1024; - else - buffer_memory_end = 1*1024*1024; - main_memory_start = buffer_memory_end; -#ifdef RAMDISK - main_memory_start += rd_init(main_memory_start, RAMDISK*1024); -#endif - mem_init(main_memory_start,memory_end); + memory_start = 1024*1024; trap_init(); - chr_dev_init(); - blk_dev_init(); - time_init(); + init_IRQ(); sched_init(); - buffer_init(buffer_memory_end); + memory_start = chr_dev_init(memory_start,memory_end); + memory_start = blk_dev_init(memory_start,memory_end); + memory_start = mem_init(memory_start,memory_end); + buffer_init(); + time_init(); + printk("Linux version " UTS_RELEASE " " __DATE__ " " __TIME__ "\n"); hd_init(); floppy_init(); + sock_init(); sti(); +#ifdef CONFIG_SCSI + scsi_dev_init(); +#endif move_to_user_mode(); if (!fork()) { /* we count on this going ok */ init(); } /* - * NOTE!! For any other task 'pause()' would mean we have to get a - * signal to awaken, but task0 is the sole exception (see 'schedule()') - * as task 0 gets activated at every idle moment (when no other tasks - * can run). For task0 'pause()' just means we go check if some other - * task can run, and if not we return here. + * task[0] is meant to be used as an "idle" task: it may not sleep, but + * it might do some general things like count free pages or it could be + * used to implement a reasonable LRU algorithm for the paging routines: + * anything that can be useful, but shouldn't take time from the real + * processes. + * + * Right now task[0] just does a infinite loop in user mode. */ for(;;) - __asm__("int $0x80"::"a" (__NR_pause):"ax"); + /* nothing */ ; } static int printf(const char *fmt, ...) @@ -196,9 +206,9 @@ void init(void) (void) open("/dev/tty1",O_RDWR,0); (void) dup(0); (void) dup(0); - printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, - NR_BUFFERS*BLOCK_SIZE); - printf("Free mem: %d bytes\n\r",memory_end-main_memory_start); + printf("%d buffers = %d bytes buffer space\n\r",nr_buffers, + nr_buffers*BLOCK_SIZE); + printf("Free mem: %d bytes\n\r",memory_end-memory_start); execve("/etc/init",argv_init,envp_init); execve("/bin/init",argv_init,envp_init); diff --git a/kernel/Makefile b/kernel/Makefile index 0ebd895d7ea2..695e77c5a352 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -7,33 +7,30 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -AR =ar -AS =as -LD =ld -LDFLAGS =-s -x -CC =gcc -nostdinc -I../include -CPP =cpp -nostdinc -I../include - - .S.s: $(CPP) -traditional $< -o $*.s .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) -S $< .s.o: $(AS) -c -o $*.o $< .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) -c $< + +SUBDIRS = chr_drv blk_drv math -OBJS = sched.o sys_call.o traps.o asm.o fork.o \ +OBJS = sched.o sys_call.o traps.o irq.o fork.o \ panic.o printk.o vsprintf.o sys.o exit.o \ - signal.o mktime.o ptrace.o ioport.o + signal.o mktime.o ptrace.o ioport.o itimer.o + +all: kernel.o kernelsubdirs kernel.o: $(OBJS) $(LD) -r -o kernel.o $(OBJS) sync +kernelsubdirs: dummy + @for i in $(SUBDIRS); do (cd $$i; echo $$i; $(MAKE)) || exit; done + sys_call.s: sys_call.S sys_call.o: sys_call.s @@ -44,66 +41,122 @@ sched.o: sched.c clean: rm -f core *.o *.a tmp_make sys_call.s for i in *.c;do rm -f `basename $$i .c`.s;done - (cd chr_drv; make clean) - (cd blk_drv; make clean) - (cd math; make clean) + for i in $(SUBDIRS); do (cd $$i; $(MAKE) clean); done dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ - $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile - (cd chr_drv; make dep) - (cd blk_drv; make dep) - (cd math; make dep) + for i in $(SUBDIRS); do (cd $$i; $(MAKE) dep) || exit; done + +dummy: ### Dependencies: -exit.s exit.o : exit.c ../include/errno.h ../include/signal.h ../include/sys/types.h \ - ../include/sys/wait.h ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ - ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/linux/tty.h ../include/termios.h ../include/asm/segment.h -fork.s fork.o : fork.c ../include/errno.h ../include/stddef.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h \ - ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \ - ../include/sys/param.h ../include/sys/time.h ../include/time.h ../include/sys/resource.h \ - ../include/asm/segment.h ../include/asm/system.h -ioport.s ioport.o : ioport.c ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ - ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h \ - ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h ../include/errno.h -mktime.s mktime.o : mktime.c ../include/time.h -panic.s panic.o : panic.c ../include/linux/kernel.h ../include/linux/sched.h ../include/linux/head.h \ - ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h -printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h ../include/linux/kernel.h -ptrace.s ptrace.o : ptrace.c ../include/linux/head.h ../include/linux/kernel.h ../include/linux/sched.h \ - ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h ../include/errno.h ../include/asm/segment.h \ - ../include/asm/system.h ../include/sys/ptrace.h -sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ - ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h \ - ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h ../include/linux/timer.h ../include/linux/sys.h \ - ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h \ - ../include/errno.h -signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ - ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h ../include/linux/mm.h \ - ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h ../include/sys/time.h \ - ../include/time.h ../include/sys/resource.h ../include/asm/segment.h ../include/sys/wait.h \ - ../include/errno.h -sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h ../include/linux/head.h \ - ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ - ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/linux/tty.h \ - ../include/termios.h ../include/linux/config.h ../include/linux/config_rel.h \ - ../include/linux/config_ver.h ../include/asm/segment.h ../include/sys/times.h \ - ../include/sys/utsname.h ../include/linux/string.h -traps.s traps.o : traps.c ../include/linux/string.h ../include/linux/head.h ../include/linux/sched.h \ - ../include/linux/fs.h ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ - ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/system.h \ - ../include/asm/segment.h ../include/asm/io.h ../include/errno.h -vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/linux/string.h +exit.o : exit.c /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/asm/segment.h +fork.o : fork.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/stddef.h /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h +ioport.o : ioport.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h +irq.o : irq.c /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/irq.h +itimer.o : itimer.c /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/segment.h +mktime.o : mktime.c /usr/src/linux/include/time.h +panic.o : panic.c /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +printk.o : printk.c /usr/src/linux/include/stdarg.h /usr/src/linux/include/asm/segment.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +ptrace.o : ptrace.c /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/kernel.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/segment.h \ + /usr/src/linux/include/asm/system.h +sched.o : sched.c /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/linux/sys.h /usr/src/linux/include/linux/fdreg.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/segment.h +signal.o : signal.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/segment.h +sys.o : sys.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/times.h /usr/src/linux/include/linux/utsname.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/segment.h +traps.o : traps.c /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/io.h +vsprintf.o : vsprintf.c /usr/src/linux/include/stdarg.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/string.h diff --git a/kernel/asm.s b/kernel/asm.s deleted file mode 100644 index f565060bbe63..000000000000 --- a/kernel/asm.s +++ /dev/null @@ -1,84 +0,0 @@ -/* - * linux/kernel/asm.s - * - * (C) 1991 Linus Torvalds - */ - -/* - * asm.s contains the low-level code for interrupts that cannot - * result in an task-switch. These are things like the hd- and - * floppy-interrupt etc. With these interrupts, we don't have to - * care about the stack layout etc. - */ - -.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt - -_hd_interrupt: - cld - pushl %eax - pushl %ecx - pushl %edx - push %ds - push %es - push %fs - movl $0x10,%eax - mov %ax,%ds - mov %ax,%es - movl $0x17,%eax - mov %ax,%fs - movb $0x20,%al - outb %al,$0xA0 # EOI to interrupt controller #1 - jmp 1f # give port chance to breathe -1: jmp 1f -1: outb %al,$0x20 - andl $0xfffeffff,_timer_active - xorl %edx,%edx - xchgl _do_hd,%edx - testl %edx,%edx - jne 1f - movl $_unexpected_hd_interrupt,%edx -1: call *%edx # "interesting" way of handling intr. - pop %fs - pop %es - pop %ds - popl %edx - popl %ecx - popl %eax - iret - -_floppy_interrupt: - cld - pushl %eax - pushl %ecx - pushl %edx - push %ds - push %es - push %fs - movl $0x10,%eax - mov %ax,%ds - mov %ax,%es - movl $0x17,%eax - mov %ax,%fs - movb $0x20,%al - outb %al,$0x20 # EOI to interrupt controller #1 - xorl %eax,%eax - xchgl _do_floppy,%eax - testl %eax,%eax - jne 1f - movl $_unexpected_floppy_interrupt,%eax -1: call *%eax # "interesting" way of handling intr. - pop %fs - pop %es - pop %ds - popl %edx - popl %ecx - popl %eax - iret - -_parallel_interrupt: - cld - pushl %eax - movb $0x20,%al - outb %al,$0x20 - popl %eax - iret diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile index 3aaf96197c76..232a0c8d8924 100644 --- a/kernel/blk_drv/Makefile +++ b/kernel/blk_drv/Makefile @@ -9,62 +9,78 @@ # parent makefile. # -AR =ar -AS =as -LD =ld -LDFLAGS =-s -x -CC =gcc -nostdinc -I../../include -CPP =cpp -nostdinc -I../../include - .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) $(RAMDISK) -S $< .s.o: $(AS) -c -o $*.o $< .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) $(RAMDISK) -c $< + +SUBDIRS = scsi + +OBJS = hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o -OBJS = ll_rw_blk.o floppy.o hd.o ramdisk.o +all: blk_drv.a scsisubdirs blk_drv.a: $(OBJS) + rm -f blk_drv.a $(AR) rcs blk_drv.a $(OBJS) sync +scsisubdirs: dummy + @for i in $(SUBDIRS); do (cd $$i; echo $$i; $(MAKE)) || exit; done + clean: rm -f core *.o *.a tmp_make for i in *.c;do rm -f `basename $$i .c`.s;done + for i in $(SUBDIRS); do (cd $$i; $(MAKE) clean); done dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ - $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile + for i in $(SUBDIRS); do (cd $$i; $(MAKE) dep); done + +dummy: ### Dependencies: -floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \ - ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \ - ../../include/linux/fdreg.h ../../include/asm/system.h ../../include/asm/io.h \ - ../../include/asm/segment.h blk.h -hd.s hd.o : hd.c ../../include/errno.h ../../include/linux/config.h ../../include/linux/config_rel.h \ - ../../include/linux/config_ver.h ../../include/linux/sched.h ../../include/linux/head.h \ - ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/timer.h ../../include/linux/hdreg.h \ - ../../include/asm/system.h ../../include/asm/io.h ../../include/asm/segment.h \ +floppy.o : floppy.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/linux/fdreg.h /usr/src/linux/include/linux/fd.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h \ + /usr/src/linux/include/asm/segment.h blk.h +genhd.o : genhd.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/genhd.h \ + /usr/src/linux/include/linux/kernel.h +hd.o : hd.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/linux/hdreg.h /usr/src/linux/include/linux/genhd.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/segment.h \ + blk.h +ll_rw_blk.o : ll_rw_blk.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/system.h \ blk.h -ll_rw_blk.s ll_rw_blk.o : ll_rw_blk.c ../../include/errno.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/asm/system.h blk.h -ramdisk.s ramdisk.o : ramdisk.c ../../include/linux/string.h ../../include/linux/config.h \ - ../../include/linux/config_rel.h ../../include/linux/config_ver.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/minix_fs.h ../../include/asm/system.h \ - ../../include/asm/segment.h ../../include/asm/memory.h blk.h +ramdisk.o : ramdisk.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h index 746f5f024f05..70dab1bfdfc7 100644 --- a/kernel/blk_drv/blk.h +++ b/kernel/blk_drv/blk.h @@ -1,7 +1,7 @@ #ifndef _BLK_H #define _BLK_H -#define NR_BLK_DEV 7 +#define NR_BLK_DEV 10 /* * NR_REQUEST is the number of entries in the request-queue. * NOTE that writes may use only the low 2/3 of these: reads @@ -26,9 +26,11 @@ struct request { int errors; unsigned long sector; unsigned long nr_sectors; + unsigned long current_nr_sectors; char * buffer; - struct task_struct * waiting; + struct wait_queue * waiting; struct buffer_head * bh; + struct buffer_head * bhtail; struct request * next; }; @@ -38,7 +40,7 @@ struct request { * are much more time-critical than writes. */ #define IN_ORDER(s1,s2) \ -((s1)->cmd<(s2)->cmd || ((s1)->cmd==(s2)->cmd && \ +((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \ ((s1)->dev < (s2)->dev || (((s1)->dev == (s2)->dev && \ (s1)->sector < (s2)->sector))))) @@ -47,12 +49,35 @@ struct blk_dev_struct { struct request * current_request; }; + +struct sec_size { + unsigned block_size; + unsigned block_size_bits; +}; + +/* + * These will have to be changed to be aware of different buffer + * sizes etc.. + */ +#define SECTOR_MASK ((1 << (BLOCK_SIZE_BITS - 9)) -1) +#define SUBSECTOR(block) ((block) & SECTOR_MASK) + +extern struct sec_size * blk_sec[NR_BLK_DEV]; extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; extern struct request request[NR_REQUEST]; -extern struct task_struct * wait_for_request; +extern struct wait_queue * wait_for_request; extern int * blk_size[NR_BLK_DEV]; +extern int is_read_only(int dev); +extern void set_device_ro(int dev,int flag); + +#define RO_IOCTLS(dev,where) \ + case BLKROSET: if (!suser()) return -EPERM; \ + set_device_ro((dev),get_fs_long((long *) (where))); return 0; \ + case BLKROGET: verify_area((void *) (where), sizeof(long)); \ + put_fs_long(is_read_only(dev),(long *) (where)); return 0; + #ifdef MAJOR_NR /* @@ -88,13 +113,35 @@ extern int * blk_size[NR_BLK_DEV]; #define DEVICE_ON(device) #define DEVICE_OFF(device) +#elif (MAJOR_NR == 8) +/* scsi disk */ +#define DEVICE_NAME "scsidisk" +#define DEVICE_INTR do_sd +#define TIMEOUT_VALUE 200 +#define DEVICE_REQUEST do_sd_request +#define DEVICE_NR(device) (MINOR(device) >> 4) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == 9) +/* scsi tape */ +#define DEVICE_NAME "scsitape" +#define DEVICE_INTR do_st +#define DEVICE_REQUEST do_st_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + #elif /* unknown blk device */ #error "unknown blk device" #endif +#ifndef CURRENT #define CURRENT (blk_dev[MAJOR_NR].current_request) +#endif + #define CURRENT_DEV DEVICE_NR(CURRENT->dev) #ifdef DEVICE_INTR @@ -130,22 +177,42 @@ extern inline void unlock_buffer(struct buffer_head * bh) wake_up(&bh->b_wait); } -extern inline void end_request(int uptodate) +static void end_request(int uptodate) { - DEVICE_OFF(CURRENT->dev); - if (CURRENT->bh) { - CURRENT->bh->b_uptodate = uptodate; - unlock_buffer(CURRENT->bh); - } + struct request * req; + struct buffer_head * bh; + + req = CURRENT; + req->errors = 0; if (!uptodate) { printk(DEVICE_NAME " I/O error\n\r"); - printk("dev %04x, block %d\n\r",CURRENT->dev, - CURRENT->bh->b_blocknr); + printk("dev %04x, sector %d\n\r",req->dev,req->sector); + req->nr_sectors--; + req->nr_sectors &= ~SECTOR_MASK; + req->sector += (BLOCK_SIZE / 512); + req->sector &= ~SECTOR_MASK; + } + + if (bh = req->bh) { + req->bh = bh->b_reqnext; + bh->b_reqnext = NULL; + bh->b_uptodate = uptodate; + unlock_buffer(bh); + if (bh = req->bh) { + req->current_nr_sectors = bh->b_size >> 9; + if (req->nr_sectors < req->current_nr_sectors) { + req->nr_sectors = req->current_nr_sectors; + printk("end_request: buffer-list destroyed\n"); + } + req->buffer = bh->b_data; + return; + } } - wake_up(&CURRENT->waiting); + DEVICE_OFF(req->dev); + CURRENT = req->next; + wake_up(&req->waiting); + req->dev = -1; wake_up(&wait_for_request); - CURRENT->dev = -1; - CURRENT = CURRENT->next; } #ifdef DEVICE_INTR @@ -155,7 +222,6 @@ extern inline void end_request(int uptodate) #endif #define INIT_REQUEST \ -repeat: \ if (!CURRENT) {\ CLEAR_INTR; \ return; \ diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c index acda4c925e12..8bfff575243c 100644 --- a/kernel/blk_drv/floppy.c +++ b/kernel/blk_drv/floppy.c @@ -1,7 +1,7 @@ /* * linux/kernel/floppy.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -32,10 +32,33 @@ * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus. */ +/* + * Automatic floppy-detection and formatting written by Werner Almesberger + * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with + * the floppy-change signal detection. + */ + +/* + * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed + * FDC data overrun bug, added some preliminary stuff for vertical + * recording support. + * TODO: Errors are still not counted properly. + */ + +#define REALLY_SLOW_IO +#define FLOPPY_IRQ 6 + #include #include #include +#include #include +#include +#include +#ifdef HHB_SYSMACROS +#include +#endif + #include #include #include @@ -43,10 +66,11 @@ #define MAJOR_NR 2 #include "blk.h" -unsigned int changed_floppies = 0; +static unsigned int changed_floppies = 0, fake_change = 0; static int recalibrate = 0; static int reset = 0; +static int recover = 0; /* recalibrate immediately after resetting */ static int seek = 0; extern unsigned char current_DOR; @@ -56,6 +80,7 @@ __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) #define TYPE(x) ((x)>>2) #define DRIVE(x) ((x)&0x03) + /* * Note that MAX_ERRORS=X doesn't imply that we retry every bad read * max X times - some types of errors increase the errorcount by 2 or @@ -63,6 +88,24 @@ __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port)) */ #define MAX_ERRORS 12 +/* + * Maximum disk size (in kilobytes). This default is used whenever the + * current disk size is unknown. + */ +#define MAX_DISK_SIZE 1440 + +/* + * Maximum number of sectors in a track buffer. Track buffering is disabled + * if tracks are bigger. + */ +#define MAX_BUFFER_SECTORS 18 + +/* + * The DMA channel used by the floppy controller cannot access data at + * addresses >= 1MB + */ +#define LAST_DMA_ADDR (0x100000 - BLOCK_SIZE) + /* * globals used by 'result()' */ @@ -74,33 +117,117 @@ static unsigned char reply_buffer[MAX_REPLIES]; #define ST3 (reply_buffer[3]) /* - * This struct defines the different floppy types. Unlike minix - * linux doesn't have a "search for right type"-type, as the code - * for that is convoluted and weird. I've got enough problems with - * this driver as it is. + * This struct defines the different floppy types. * - * The 'stretch' tells if the tracks need to be boubled for some + * The 'stretch' tells if the tracks need to be doubled for some * types (ie 360kB diskette in 1.2MB drive etc). Others should * be self-explanatory. */ -struct floppy_struct { - unsigned int size, sect, head, track, stretch; - unsigned char gap,rate,spec1; +static struct floppy_struct floppy_type[] = { + { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* no testing */ + { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,NULL }, /* 360kB PC diskettes */ + { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,NULL }, /* 1.2 MB AT-diskettes */ + { 720, 9,2,40,1,0x2A,0x02,0xDF,0x50,NULL }, /* 360kB in 720kB drive */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,NULL }, /* 3.5" 720kB diskette */ + { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,NULL }, /* 360kB in 1.2MB drive */ + { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,NULL }, /* 720kB in 1.2MB drive */ + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }, /* 1.44MB diskette */ }; -static struct floppy_struct floppy_type[] = { - { 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */ - { 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */ - { 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */ - { 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */ - { 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */ - { 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */ - { 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */ - { 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */ +/* + * Auto-detection. Each drive type has a pair of formats which are + * used in succession to try to read the disk. If the FDC cannot lock onto + * the disk, the next format is tried. This uses the variable 'probing'. + */ +static struct floppy_struct floppy_types[] = { + { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */ + { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */ + { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"1.2M" }, /* 1.2 MB AT-diskettes */ + { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"360k/AT" }, /* 360kB in 1.2MB drive */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" }, /* 3.5" 720kB diskette */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" }, /* 3.5" 720kB diskette */ + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" }, /* 1.44MB diskette */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k/AT" }, /* 3.5" 720kB diskette */ }; +/* Auto-detection: Disk type used until the next media change occurs. */ +struct floppy_struct *current_type[4] = { NULL, NULL, NULL, NULL }; + +/* This type is tried first. */ +struct floppy_struct *base_type[4]; + /* - * Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps + * User-provided type information. current_type points to + * the respective entry of this array. + */ +struct floppy_struct user_params[4]; + +static int floppy_sizes[] ={ + MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, + 360, 360 ,360, 360, + 1200,1200,1200,1200, + 360, 360, 360, 360, + 720, 720, 720, 720, + 360, 360, 360, 360, + 720, 720, 720, 720, + 1440,1440,1440,1440 +}; + +/* + * The driver is trying to determine the correct media format + * while probing is set. rw_interrupt() clears it after a + * successful access. + */ +static int probing = 0; + +/* + * (User-provided) media information is _not_ discarded after a media change + * if the corresponding keep_data flag is non-zero. Positive values are + * decremented after each probe. + */ +static int keep_data[4] = { 0,0,0,0 }; + +/* + * Announce successful media type detection and media information loss after + * disk changes. + */ +static ftd_msg[4] = { 1,1,1,1 }; + +/* Prevent "aliased" accesses. */ + +static fd_ref[4] = { 0,0,0,0 }; +static fd_device[4] = { 0,0,0,0 }; + +/* Synchronization of FDC access. */ +static volatile int format_status = FORMAT_NONE, fdc_busy = 0; +static struct wait_queue *fdc_wait = NULL, *format_done = NULL; + +/* Errors during formatting are counted here. */ +static int format_errors; + +/* Format request descriptor. */ +static struct format_descr format_req; + +/* + * Current device number. Taken either from the block header or from the + * format request descriptor. + */ +#define CURRENT_DEVICE (format_status == FORMAT_BUSY ? format_req.device : \ + (CURRENT->dev)) + +/* Current error count. */ +#define CURRENT_ERRORS (format_status == FORMAT_BUSY ? format_errors : \ + (CURRENT->errors)) + +/* + * Treshold for reporting FDC errors to the console. + * Setting this to zero may flood your screen when using + * ultra cheap floppies ;-) + */ +static unsigned short min_report_error_cnt[4] = {2, 2, 2, 2}; + +/* + * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), * H is head unload time (1=16ms, 2=32ms, etc) * @@ -108,9 +235,17 @@ static struct floppy_struct floppy_type[] = { * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). */ -extern void floppy_interrupt(void); -extern char tmp_floppy_area[1024]; -extern char floppy_track_buffer[512*2*18]; +/* + * Track buffer and block buffer (in case track buffering doesn't work). + * Because these are written to by the DMA controller, they must + * not contain a 64k byte boundary crossing, or data will be + * corrupted/lost. Alignment of these is enforced in boot/head.s. + * Note that you must not change the sizes below without updating head.s. + */ +extern char tmp_floppy_area[BLOCK_SIZE]; +extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS]; + +static void redo_fd_request(void); /* * These are global variables, as that's the easiest way to give @@ -119,7 +254,7 @@ extern char floppy_track_buffer[512*2*18]; */ #define NO_TRACK 255 -static int read_track = 0; /* flag to indicate if we want to read all track */ +static int read_track = 0; /* flag to indicate if we want to read entire track */ static int buffer_track = -1; static int buffer_drive = -1; static int cur_spec1 = -1; @@ -132,8 +267,9 @@ static unsigned char track = 0; static unsigned char seek_track = 0; static unsigned char current_track = NO_TRACK; static unsigned char command = 0; +static unsigned char fdc_version = FDC_TYPE_STD; /* FDC version code */ unsigned char selected = 0; -struct task_struct * wait_on_floppy_select = NULL; +struct wait_queue * wait_on_floppy_select = NULL; void floppy_deselect(unsigned int nr) { @@ -143,6 +279,16 @@ void floppy_deselect(unsigned int nr) wake_up(&wait_on_floppy_select); } +void request_done(int uptodate) +{ + timer_active &= ~(1 << FLOPPY_TIMER); + if (format_status != FORMAT_BUSY) end_request(uptodate); + else { + format_status = uptodate ? FORMAT_OKAY : FORMAT_ERROR; + wake_up(&format_done); + } +} + /* * floppy-change is never called from an interrupt, so we can relax a bit * here, sleep etc. Note that floppy-on tries to set current_DOR to point @@ -154,9 +300,15 @@ int floppy_change(struct buffer_head * bh) unsigned int mask = 1 << (bh->b_dev & 0x03); if (MAJOR(bh->b_dev) != 2) { - printk("floppy_changed: not a floppy\n"); + printk("floppy_changed: not a floppy\r\n"); return 0; } + if (fake_change & mask) { + fake_change &= ~mask; +/* omitting the next line breaks formatting in a horrible way ... */ + changed_floppies &= ~mask; + return 1; + } if (changed_floppies & mask) { changed_floppies &= ~mask; recalibrate = 1; @@ -179,7 +331,7 @@ int floppy_change(struct buffer_head * bh) changed_floppies &= ~mask; recalibrate = 1; return 1; - } + } return 0; } @@ -190,21 +342,29 @@ __asm__("cld ; rep ; movsl" \ static void setup_DMA(void) { - unsigned long addr = (long) CURRENT->buffer; - unsigned long count = 1024; + unsigned long addr,count; - cli(); + if (command == FD_FORMAT) { + addr = (long) tmp_floppy_area; + count = floppy->sect*4; + } + else { + addr = (long) CURRENT->buffer; + count = 1024; + } if (read_track) { /* mark buffer-track bad, in case all this fails.. */ buffer_drive = buffer_track = -1; count = floppy->sect*2*512; addr = (long) floppy_track_buffer; - } else if (addr >= 0x100000) { + } else if (addr >= LAST_DMA_ADDR) { addr = (long) tmp_floppy_area; if (command == FD_WRITE) copy_buffer(CURRENT->buffer,tmp_floppy_area); } /* mask DMA 2 */ + cli(); +#ifndef HHB_SYSMACROS immoutb_p(4|2,10); /* output command byte. I don't know why, but everyone (minix, */ /* sanches & canton) output this twice, first to 12 then to 11 */ @@ -227,6 +387,14 @@ static void setup_DMA(void) immoutb_p(count,5); /* activate DMA 2 */ immoutb_p(0|2,10); +#else /* just to show off my macros -- hhb */ + DISABLE_DMA(DMA2); + CLEAR_DMA_FF(DMA2); + SET_DMA_MODE(DMA2, (command == FD_READ)? DMA_MODE_READ : DMA_MODE_WRITE); + SET_DMA_ADDR(DMA2, addr); + SET_DMA_COUNT(DMA2, count); + ENABLE_DMA(DMA2); +#endif sti(); } @@ -257,11 +425,14 @@ static int result(void) return -1; for (counter = 0 ; counter < 10000 ; counter++) { status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); - if (status == STATUS_READY) + if (status == STATUS_READY) { return i; + } if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { - if (i >= MAX_REPLIES) + if (i >= MAX_REPLIES) { + printk("floppy_stat reply overrun\n"); break; + } reply_buffer[i++] = inb_p(FD_DATA); } } @@ -274,34 +445,124 @@ static int result(void) static void bad_flp_intr(void) { current_track = NO_TRACK; - CURRENT->errors++; - if (CURRENT->errors > MAX_ERRORS) { + CURRENT_ERRORS++; + if (CURRENT_ERRORS > MAX_ERRORS) { floppy_deselect(current_drive); - end_request(0); + request_done(0); } - if (CURRENT->errors > MAX_ERRORS/2) + if (CURRENT_ERRORS > MAX_ERRORS/2) reset = 1; else recalibrate = 1; } /* - * Ok, this interrupt is called after a DMA read/write has succeeded, - * so we check the results, and copy any buffers. + * This has only been tested for the case fdc_version == FDC_TYPE_STD. + * In case you have a 82077 and want to test it, you'll have to compile + * with `FDC_FIFO_UNTESTED' defined. You may also want to add support for + * recognizing drives with vertical recording support. + */ +static void configure_fdc_mode(void) +{ + if (fdc_version == FDC_TYPE_82077) { + /* Enhanced version with FIFO & vertical recording. */ + output_byte(FD_CONFIGURE); + output_byte(0); + output_byte(0x1A); /* FIFO on, polling off, 10 byte treshold */ + output_byte(0); /* precompensation from track 0 upwards */ + printk(DEVICE_NAME ": FIFO enabled\n"); + } +} /* configure_fdc_mode */ + + +static void tell_sector(int nr) +{ + if (nr!=7) { + printk(" -- FDC reply errror"); + reset = 1; + } else + printk(": track %d, head %d, sector %d", reply_buffer[3], + reply_buffer[4], reply_buffer[5]); +} /* tell_sector */ + + +/* + * Ok, this interrupt is called after a DMA read/write has succeeded + * or failed, so we check the results, and copy any buffers. + * hhb: Added better error reporting. */ static void rw_interrupt(void) { char * buffer_area; + int nr; + char bad; - if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73)) { - if (ST1 & 0x02) { - printk("Drive %d is write protected\n\r",current_drive); - floppy_deselect(current_drive); - end_request(0); - } else + nr = result(); + /* check IC to find cause of interrupt */ + switch ((ST0 & ST0_INTR)>>6) { + case 1: /* error occured during command execution */ + bad = 1; + if (ST1 & ST1_WP) { + printk(DEVICE_NAME ": Drive %d is write protected\n", current_drive); + floppy_deselect(current_drive); + request_done(0); + bad = 0; + } else if (ST1 & ST1_OR) { + printk(DEVICE_NAME ": Over/Underrun - retrying\n"); + /* could continue from where we stopped, but ... */ + bad = 0; + } else if (CURRENT_ERRORS > min_report_error_cnt[ST0 & ST0_DS]) { + printk(DEVICE_NAME " %d: ", ST0 & ST0_DS); + if (ST0 & ST0_ECE) { + printk("Recalibrate failed!"); + } else if (ST2 & ST2_CRC) { + printk("data CRC error"); + tell_sector(nr); + } else if (ST1 & ST1_CRC) { + printk("CRC error"); + tell_sector(nr); + } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) { + if (!probing) { + printk("sector not found"); + tell_sector(nr); + } else + printk("probe failed..."); + } else if (ST2 & ST2_WC) { /* seek error */ + printk("wrong cylinder"); + } else if (ST2 & ST2_BC) { /* cylinder marked as bad */ + printk("bad cylinder"); + } else { + printk("unknown error. ST[0..3] are: 0x%x 0x%x 0x%x 0x%x\n", ST0, ST1, ST2, ST3); + } + printk("\n"); + + } + if (bad) + bad_flp_intr(); + redo_fd_request(); + return; + case 2: /* invalid command given */ + printk(DEVICE_NAME ": Invalid FDC command given!\n"); + request_done(0); + return; + case 3: + printk(DEVICE_NAME ": Abnormal termination caused by polling\n"); bad_flp_intr(); - do_fd_request(); - return; + redo_fd_request(); + return; + default: /* (0) Normal command termination */ + break; + } + + if (probing) { + int drive = MINOR(CURRENT->dev); + + if (ftd_msg[drive]) + printk("Auto-detected floppy type %s in fd%d\r\n", + floppy->name,drive); + current_type[drive] = floppy; + floppy_sizes[drive] = floppy->size >> 1; + probing = 0; } if (read_track) { buffer_track = seek_track; @@ -310,11 +571,11 @@ static void rw_interrupt(void) ((sector-1 + head*floppy->sect)<<9); copy_buffer(buffer_area,CURRENT->buffer); } else if (command == FD_READ && - (unsigned long)(CURRENT->buffer) >= 0x100000) + (unsigned long)(CURRENT->buffer) >= LAST_DMA_ADDR) copy_buffer(tmp_floppy_area,CURRENT->buffer); floppy_deselect(current_drive); - end_request(1); - do_fd_request(); + request_done(1); + redo_fd_request(); } /* @@ -329,23 +590,31 @@ inline void setup_rw_floppy(void) setup_DMA(); do_floppy = rw_interrupt; output_byte(command); - if (read_track) { - output_byte(current_drive); - output_byte(track); - output_byte(0); - output_byte(1); + if (command != FD_FORMAT) { + if (read_track) { + output_byte(current_drive); + output_byte(track); + output_byte(0); + output_byte(1); + } else { + output_byte(head<<2 | current_drive); + output_byte(track); + output_byte(head); + output_byte(sector); + } + output_byte(2); /* sector size = 512 */ + output_byte(floppy->sect); + output_byte(floppy->gap); + output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ } else { output_byte(head<<2 | current_drive); - output_byte(track); - output_byte(head); - output_byte(sector); - } - output_byte(2); /* sector size = 512 */ - output_byte(floppy->sect); - output_byte(floppy->gap); - output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ + output_byte(2); + output_byte(floppy->sect); + output_byte(floppy->fmt_gap); + output_byte(FD_FILL_BYTE); + } if (reset) - do_fd_request(); + redo_fd_request(); } /* @@ -358,15 +627,44 @@ static void seek_interrupt(void) /* sense drive status */ output_byte(FD_SENSEI); if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { + printk(DEVICE_NAME ": seek failed\n"); recalibrate = 1; bad_flp_intr(); - do_fd_request(); + redo_fd_request(); return; } current_track = ST1; setup_rw_floppy(); } +/* Set perpendicular mode as required, based on data rate, if supported. + * 80277: 1Mbps data rate only possible with 82077-1. + * Untested!! TODO: increase MAX_BUFFER_SECTORS, add floppy_type entries. + */ +static void inline perpendicular_mode(unsigned char rate) +{ + if (fdc_version == FDC_TYPE_82077) { + output_byte(FD_PERPENDICULAR); + if (rate & 0x40) { + unsigned char r = rate & 0x03; + if (r == 0) + output_byte(2); /* perpendicular, 500 kbps */ + else if (r == 3) + output_byte(3); /* perpendicular, 1Mbps */ + else { + printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n"); + reset = 1; + } + } else + output_byte(0); /* conventional mode */ + } else { + if (rate & 0x40) { + printk(DEVICE_NAME ": perpendicular mode not supported by FDC.\n"); + reset = 1; + } + } +} /* perpendicular_mode */ + /* * This routine is called when everything should be correctly set up * for the transfer (ie floppy motor is on and the correct floppy is @@ -374,17 +672,21 @@ static void seek_interrupt(void) */ static void transfer(void) { - read_track = (command == FD_READ) && (CURRENT->errors < 4); + read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) && + (floppy->sect <= MAX_BUFFER_SECTORS); if (cur_spec1 != floppy->spec1) { cur_spec1 = floppy->spec1; output_byte(FD_SPECIFY); output_byte(cur_spec1); /* hut etc */ output_byte(6); /* Head load time =6ms, DMA */ } - if (cur_rate != floppy->rate) - outb_p(cur_rate = floppy->rate,FD_DCR); + if (cur_rate != floppy->rate) { + /* use bit 6 of floppy->rate to indicate perpendicular mode */ + perpendicular_mode(floppy->rate); + outb_p(cur_rate = ((floppy->rate)) & ~0x40, FD_DCR); + } if (reset) { - do_fd_request(); + redo_fd_request(); return; } if (!seek) { @@ -399,25 +701,31 @@ static void transfer(void) output_byte((head<<2) | current_drive); output_byte(seek_track); if (reset) - do_fd_request(); + redo_fd_request(); } /* * Special case - used after a unexpected interrupt (or reset) */ + +static void recalibrate_floppy(void); + static void recal_interrupt(void) { output_byte(FD_SENSEI); current_track = NO_TRACK; if (result()!=2 || (ST0 & 0xE0) == 0x60) reset = 1; - do_fd_request(); +/* Recalibrate until track 0 is reached. Might help on some errors. */ + if ((ST0 & 0x10) == 0x10) recalibrate_floppy(); + else redo_fd_request(); } -void unexpected_floppy_interrupt(void) +static void unexpected_floppy_interrupt(void) { current_track = NO_TRACK; output_byte(FD_SENSEI); + printk(DEVICE_NAME ": unexpected interrupt\n"); if (result()!=2 || (ST0 & 0xE0) == 0x60) reset = 1; else @@ -432,17 +740,29 @@ static void recalibrate_floppy(void) output_byte(FD_RECALIBRATE); output_byte(head<<2 | current_drive); if (reset) - do_fd_request(); + redo_fd_request(); } +/* + * Must do 4 FD_SENSEIs after reset because of ``drive polling''. + */ static void reset_interrupt(void) { - output_byte(FD_SENSEI); - (void) result(); + short i; + + for (i=0; i<4; i++) { + output_byte(FD_SENSEI); + (void) result(); + } output_byte(FD_SPECIFY); output_byte(cur_spec1); /* hut etc */ output_byte(6); /* Head load time =6ms, DMA */ - do_fd_request(); + configure_fdc_mode(); /* reprogram if smart fdc */ + if (!recover) redo_fd_request(); + else { + recalibrate_floppy(); + recover = 0; + } } /* @@ -467,11 +787,73 @@ static void reset_floppy(void) sti(); } +static void floppy_shutdown(void) +{ + cli(); + request_done(0); + recover = 1; + reset_floppy(); + sti(); +} + +static void shake_done(void) +{ + current_track = NO_TRACK; + if (inb(FD_DIR) & 0x80) request_done(0); + redo_fd_request(); +} + +static int retry_recal(void (*proc)(void)) +{ + output_byte(FD_SENSEI); + if (result() == 2 && (ST0 & 0x10) != 0x10) return 0; + do_floppy = proc; + output_byte(FD_RECALIBRATE); + output_byte(head<<2 | current_drive); + return 1; +} + +static void shake_zero(void) +{ + if (!retry_recal(shake_zero)) shake_done(); +} + +static void shake_one(void) +{ + if (retry_recal(shake_one)) return; + do_floppy = shake_done; + output_byte(FD_SEEK); + output_byte(head << 2 | current_drive); + output_byte(1); +} + static void floppy_on_interrupt(void) { if (inb(FD_DIR) & 0x80) { changed_floppies |= 1< 0) + keep_data[current_drive]--; + } + else { + if (ftd_msg[current_drive] && current_type[ + current_drive] != NULL) + printk("Disk type is undefined after disk " + "change in fd%d\r\n",current_drive); + current_type[current_drive] = NULL; + floppy_sizes[current_drive] = MAX_DISK_SIZE; + } +/* Forcing the drive to seek makes the "media changed" condition go away. + * There should be a cleaner solution for that ... + */ + if (!reset && !recalibrate) { + do_floppy = (current_track && current_track != NO_TRACK) + ? shake_zero : shake_one; + output_byte(FD_RECALIBRATE); + output_byte(head<<2 | current_drive); + return; + } } if (reset) { reset_floppy(); @@ -494,45 +876,112 @@ static void floppy_on_interrupt(void) transfer(); } -void do_fd_request(void) +static void setup_format_params(void) +{ + unsigned char *here = (unsigned char *) tmp_floppy_area; + int count; + + /* XXX: should do a check to see this fits in tmp_floppy_area!! */ + for (count = 1; count <= floppy->sect; count++) { + *here++ = track; + *here++ = head; + *here++ = count; + *here++ = 2; /* 512 bytes */ + } +} + +static void redo_fd_request(void) { unsigned int block; char * buffer_area; + int device; - INIT_REQUEST; +repeat: + if (format_status == FORMAT_WAIT) + format_status = FORMAT_BUSY; + if (format_status != FORMAT_BUSY) { + if (!CURRENT) { + if (!fdc_busy) + printk("FDC access conflict"); + fdc_busy = 0; + wake_up(&fdc_wait); + CLEAR_INTR; + return; + } + if (MAJOR(CURRENT->dev) != MAJOR_NR) + panic(DEVICE_NAME ": request list destroyed"); \ + if (CURRENT->bh) { + if (!CURRENT->bh->b_lock) + panic(DEVICE_NAME ": block not locked"); + } + } seek = 0; - floppy = (MINOR(CURRENT->dev)>>2) + floppy_type; - if (current_drive != CURRENT_DEV) - current_track = NO_TRACK; - current_drive = CURRENT_DEV; - block = CURRENT->sector; - if (block+2 > floppy->size) { - end_request(0); - goto repeat; - } - sector = block % floppy->sect; - block /= floppy->sect; - head = block % floppy->head; - track = block / floppy->head; - seek_track = track << floppy->stretch; - if (CURRENT->cmd == READ) - command = FD_READ; - else if (CURRENT->cmd == WRITE) - command = FD_WRITE; - else { - printk("do_fd_request: unknown command\n"); - end_request(0); - goto repeat; + probing = 0; + device = MINOR(CURRENT_DEVICE); + if (device > 3) + floppy = (device >> 2) + floppy_type; + else { /* Auto-detection */ + if ((floppy = current_type[device & 3]) == NULL) { + probing = 1; + if ((floppy = base_type[device & 3]) == + NULL) { + request_done(0); + goto repeat; + } + floppy += CURRENT_ERRORS & 1; + } } + if (format_status != FORMAT_BUSY) { + if (current_drive != CURRENT_DEV) { + current_track = NO_TRACK; + current_drive = CURRENT_DEV; + } + block = CURRENT->sector; + if (block+2 > floppy->size) { + request_done(0); + goto repeat; + } + sector = block % floppy->sect; + block /= floppy->sect; + head = block % floppy->head; + track = block / floppy->head; + seek_track = track << floppy->stretch; + if (CURRENT->cmd == READ) + command = FD_READ; + else if (CURRENT->cmd == WRITE) + command = FD_WRITE; + else { + printk("do_fd_request: unknown command\n"); + request_done(0); + goto repeat; + } + } else { + if (current_drive != (format_req.device & 3)) + current_track = NO_TRACK; + current_drive = format_req.device & 3; + if (((unsigned) format_req.track) >= floppy->track || + (format_req.head & 0xfffe) || probing) { + request_done(0); + goto repeat; + } + head = format_req.head; + track = format_req.track; + seek_track = track << floppy->stretch; + if (seek_track == buffer_track) buffer_track = -1; + command = FD_FORMAT; + setup_format_params(); + } + timer_table[FLOPPY_TIMER].expires = jiffies+10*HZ; + timer_active |= 1 << FLOPPY_TIMER; if ((seek_track == buffer_track) && (current_drive == buffer_drive)) { buffer_area = floppy_track_buffer + ((sector + head*floppy->sect)<<9); if (command == FD_READ) { copy_buffer(buffer_area,CURRENT->buffer); - end_request(1); + request_done(1); goto repeat; - } else + } else if (command == FD_WRITE) copy_buffer(CURRENT->buffer,buffer_area); } if (seek_track != current_track) @@ -541,25 +990,228 @@ void do_fd_request(void) add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt); } -static int floppy_sizes[] ={ - 0, 0, 0, 0, - 360, 360 ,360, 360, - 1200,1200,1200,1200, - 360, 360, 360, 360, - 720, 720, 720, 720, - 360, 360, 360, 360, - 720, 720, 720, 720, - 1440,1440,1440,1440 -}; +void do_fd_request(void) +{ + cli(); + while (fdc_busy) sleep_on(&fdc_wait); + fdc_busy = 1; + sti(); + redo_fd_request(); +} + +static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned int param) +{ + int drive,cnt,okay; + struct floppy_struct *this; + + switch (cmd) { + RO_IOCTLS(inode->i_rdev,param); + } + if (!suser()) return -EPERM; + drive = MINOR(inode->i_rdev); + switch (cmd) { + case FDFMTBEG: + return 0; + case FDFMTEND: + cli(); + fake_change |= 1 << (drive & 3); + sti(); + drive &= 3; + cmd = FDCLRPRM; + break; + case FDGETPRM: + if (drive > 3) this = &floppy_type[drive >> 2]; + else if ((this = current_type[drive & 3]) == NULL) + return -ENODEV; + verify_area((void *) param,sizeof(struct floppy_struct)); + for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++) + put_fs_byte(((char *) this)[cnt], + (char *) param+cnt); + return 0; + case FDFMTTRK: + cli(); + while (format_status != FORMAT_NONE) + sleep_on(&format_done); + for (cnt = 0; cnt < sizeof(struct format_descr); cnt++) + ((char *) &format_req)[cnt] = get_fs_byte( + (char *) param+cnt); + format_req.device = drive; + format_status = FORMAT_WAIT; + format_errors = 0; + while (format_status != FORMAT_OKAY && format_status != + FORMAT_ERROR) { + if (fdc_busy) sleep_on(&fdc_wait); + else { + fdc_busy = 1; + redo_fd_request(); + } + } + while (format_status != FORMAT_OKAY && format_status != + FORMAT_ERROR) + sleep_on(&format_done); + sti(); + okay = format_status == FORMAT_OKAY; + format_status = FORMAT_NONE; + wake_up(&format_done); + return okay ? 0 : -EIO; + } + if (drive < 0 || drive > 3) return -EINVAL; + switch (cmd) { + case FDCLRPRM: + current_type[drive] = NULL; + floppy_sizes[drive] = MAX_DISK_SIZE; + keep_data[drive] = 0; + break; + case FDSETPRM: + case FDDEFPRM: + for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++) + ((char *) &user_params[drive])[cnt] = + get_fs_byte((char *) param+cnt); + current_type[drive] = &user_params[drive]; + floppy_sizes[drive] = user_params[drive].size >> 1; + if (cmd == FDDEFPRM) keep_data[drive] = -1; + else { + cli(); + while (fdc_busy) sleep_on(&fdc_wait); + fdc_busy = 1; + sti(); + outb_p((current_DOR & 0xfc) | drive | + (0x10 << drive),FD_DOR); + for (cnt = 0; cnt < 1000; cnt++) __asm__("nop"); + keep_data[drive] = (inb(FD_DIR) & 0x80) ? 1 : 0; + outb_p(current_DOR,FD_DOR); + fdc_busy = 0; + wake_up(&fdc_wait); + } + break; + case FDMSGON: + ftd_msg[drive] = 1; + break; + case FDMSGOFF: + ftd_msg[drive] = 0; + break; + case FDSETEMSGTRESH: + min_report_error_cnt[drive] = (unsigned short) (param & 0x0f); + break; + default: + return -EINVAL; + } + return 0; +} + +#define CMOS_READ(addr) ({ \ +outb_p(0x80|addr,0x70); \ +inb_p(0x71); \ +}) + +static struct floppy_struct *find_base(int drive,int code) +{ + struct floppy_struct *base; + + if (code > 0 && code < 5) { + base = &floppy_types[(code-1)*2]; + printk("fd%d is %s",drive,base->name); + return base; + } + printk("fd%d is unknown type %d",drive,code); + return NULL; +} + +static void config_types(void) +{ + printk("Floppy drive(s): "); + base_type[0] = find_base(0,(CMOS_READ(0x10) >> 4) & 15); + if (((CMOS_READ(0x14) >> 6) & 1) == 0) + base_type[1] = NULL; + else { + printk(", "); + base_type[1] = find_base(1,CMOS_READ(0x10) & 15); + } + base_type[2] = base_type[3] = NULL; + printk("\r\n"); +} + +/* + * floppy_open check for aliasing (/dev/fd0 can be the same as + * /dev/PS0 etc), and disallows simultaneous access to the same + * drive with different device numbers. + */ +static int floppy_open(struct inode * inode, struct file * filp) +{ + int drive; + int old_dev; + + drive = inode->i_rdev & 3; + old_dev = fd_device[drive]; + if (fd_ref[drive]) + if (old_dev != inode->i_rdev) + return -EBUSY; + fd_ref[drive]++; + fd_device[drive] = inode->i_rdev; + if (old_dev && old_dev != inode->i_rdev) + invalidate_buffers(old_dev); + if (filp && filp->f_mode) + check_disk_change(inode->i_rdev); + return 0; +} + +static void floppy_release(struct inode * inode, struct file * filp) +{ + sync_dev(inode->i_rdev); + if (!fd_ref[inode->i_rdev & 3]--) { + printk("floppy_release with fd_ref == 0"); + fd_ref[inode->i_rdev & 3] = 0; + } +} static struct file_operations floppy_fops = { NULL, /* lseek - default */ block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ NULL, /* readdir - bad */ - NULL, /* close - default */ NULL, /* select */ - NULL /* ioctl */ + fd_ioctl, /* ioctl */ + floppy_open, /* open */ + floppy_release /* release */ +}; + + +/* + * The version command is not supposed to generate an interrupt, but + * my FDC does, except when booting in SVGA screen mode. + * When it does generate an interrupt, it doesn't return any status bytes. + * It appears to have something to do with the version command... + */ +static void ignore_interrupt(void) +{ + if (result() != 0) { + printk(DEVICE_NAME ": weird interrupt ignored\n"); + reset = 1; + } + CLEAR_INTR; /* ignore only once */ +} + + +static void floppy_interrupt(int unused) +{ + void (*handler)(void) = DEVICE_INTR; + + DEVICE_INTR = NULL; + if (!handler) + handler = unexpected_floppy_interrupt; + handler(); +} + +/* + * This is the floppy IRQ description. The SA_INTERRUPT in sa_flags + * means we run the IRQ-handler with interrupts disabled. + */ +static struct sigaction floppy_sigaction = { + floppy_interrupt, + 0, + SA_INTERRUPT, + NULL }; void floppy_init(void) @@ -568,6 +1220,24 @@ void floppy_init(void) blk_size[MAJOR_NR] = floppy_sizes; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; blkdev_fops[MAJOR_NR] = &floppy_fops; - set_intr_gate(0x26,&floppy_interrupt); - outb(inb_p(0x21)&~0x40,0x21); + timer_table[FLOPPY_TIMER].fn = floppy_shutdown; + timer_active &= ~(1 << FLOPPY_TIMER); + config_types(); + if (irqaction(FLOPPY_IRQ,&floppy_sigaction)) + printk("Unable to grab IRQ%d for the floppy driver\n",FLOPPY_IRQ); + + /* Try to determine the floppy controller type */ + DEVICE_INTR = ignore_interrupt; /* don't ask ... */ + output_byte(FD_VERSION); /* get FDC version code */ + if (result() != 1) { + printk(DEVICE_NAME ": FDC failed to return version byte\n"); + fdc_version = FDC_TYPE_STD; + } else + fdc_version = reply_buffer[0]; + if (fdc_version != FDC_TYPE_STD) + printk(DEVICE_NAME ": FDC version 0x%x\n", fdc_version); +#ifndef FDC_FIFO_UNTESTED + fdc_version = FDC_TYPE_STD; /* force std fdc type; can't test other. */ +#endif + configure_fdc_mode(); } diff --git a/kernel/blk_drv/genhd.c b/kernel/blk_drv/genhd.c new file mode 100644 index 000000000000..6618ce17ab04 --- /dev/null +++ b/kernel/blk_drv/genhd.c @@ -0,0 +1,195 @@ +/* + * Code extracted from + * linux/kernel/hd.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug + * in the early extended-partition checks and added DM partitions + */ + +#include +#include +#include +#include +struct gendisk *gendisk_head = NULL; + +static int current_minor = 0; +extern int *blk_size[]; +/* + * Create devices for each logical partition in an extended partition. + * The logical partitions form a linked list, with each entry being + * a partition table with two entries. The first entry + * is the real data partition (with a start relative to the partition + * table start). The second is a pointer to the next logical partition + * (with a start relative to the entire extended partition). + * We do not create a Linux partition for the partition tables, but + * only for the actual data partitions. + */ + +static void extended_partition(struct gendisk *hd, int dev) +{ + struct buffer_head *bh; + struct partition *p; + unsigned long first_sector, this_sector; + int mask = (1 << hd->minor_shift) - 1; + + first_sector = hd->part[MINOR(dev)].start_sect; + this_sector = first_sector; + + while (1) { + if ((current_minor & mask) >= (4 + hd->max_p)) + return; + if (!(bh = bread(dev,0,1024))) { + printk("Unable to read partition table of device %04x\n",dev); + return; + } + /* + * This block is from a device that we're about to stomp on. + * So make sure nobody thinks this block is usable. + */ + bh->b_dirt=0; + bh->b_uptodate=0; + if (*(unsigned short *) (bh->b_data+510) == 0xAA55) { + p = 0x1BE + (void *)bh->b_data; + /* + * Process the first entry, which should be the real + * data partition. + */ + if (p->sys_ind == EXTENDED_PARTITION || + !(hd->part[current_minor].nr_sects = p->nr_sects)) + goto done; /* shouldn't happen */ + hd->part[current_minor].start_sect = this_sector + p->start_sect; + printk(" Logical part %d start %d size %d end %d\n\r", + current_minor, hd->part[current_minor].start_sect, + hd->part[current_minor].nr_sects, + hd->part[current_minor].start_sect + + hd->part[current_minor].nr_sects - 1); + current_minor++; + p++; + /* + * Process the second entry, which should be a link + * to the next logical partition. Create a minor + * for this just long enough to get the next partition + * table. The minor will be reused for the real + * data partition. + */ + if (p->sys_ind != EXTENDED_PARTITION || + !(hd->part[current_minor].nr_sects = p->nr_sects)) + goto done; /* no more logicals in this partition */ + hd->part[current_minor].start_sect = first_sector + p->start_sect; + this_sector = first_sector + p->start_sect; + dev = ((hd->major) << 8) | current_minor; + brelse(bh); + } else + goto done; + } +done: + brelse(bh); +} + +static void check_partition(struct gendisk *hd, unsigned int dev) +{ + int i, minor = current_minor; + struct buffer_head *bh; + struct partition *p; + unsigned long first_sector; + + first_sector = hd->part[MINOR(dev)].start_sect; + + if (!(bh = bread(dev,0,1024))) { + printk("Unable to read partition table of device %04x\n",dev); + return; + } + printk("%s%d :\n\r", hd->major_name, minor >> hd->minor_shift); + current_minor += 4; /* first "extra" minor */ + if (*(unsigned short *) (bh->b_data+510) == 0xAA55) { + p = 0x1BE + (void *)bh->b_data; + for (i=1 ; i<=4 ; minor++,i++,p++) { + if (!(hd->part[minor].nr_sects = p->nr_sects)) + continue; + hd->part[minor].start_sect = first_sector + p->start_sect; + printk(" part %d start %d size %d end %d \n\r", i, + hd->part[minor].start_sect, hd->part[minor].nr_sects, + hd->part[minor].start_sect + hd->part[minor].nr_sects - 1); + if ((current_minor & 0x3f) >= 60) + continue; + if (p->sys_ind == EXTENDED_PARTITION) { + extended_partition(hd, (hd->major << 8) | minor); + } + } + /* + * check for Disk Manager partition table + */ + if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) { + p = 0x1BE + (void *)bh->b_data; + for (i = 4 ; i < 16 ; i++, current_minor++) { + p--; + if ((current_minor & 0x3f) >= 60) + break; + if (!(p->start_sect && p->nr_sects)) + continue; + hd->part[current_minor].start_sect = p->start_sect; + hd->part[current_minor].nr_sects = p->nr_sects; + printk(" DM part %d start %d size %d end %d\n\r", + current_minor, + hd->part[current_minor].start_sect, + hd->part[current_minor].nr_sects, + hd->part[current_minor].start_sect + + hd->part[current_minor].nr_sects - 1); + } + } + } else + printk("Bad partition table on dev %04x\n",dev); + brelse(bh); +} + +static void setup_dev(struct gendisk *dev) +{ + int i; + int j = dev->max_nr * dev->max_p; + int major = dev->major << 8; + int drive; + + + for (i = 0 ; i < j; i++) { + dev->part[i].start_sect = 0; + dev->part[i].nr_sects = 0; + } + dev->init(); + for (drive=0 ; drivenr_real ; drive++) { + current_minor = 1+(drive<minor_shift); + check_partition(dev, major+(drive<minor_shift)); + } + for (i=0 ; i < j ; i++) + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9); + blk_size[dev->major] = dev->sizes; +} + +/* This may be used only once, enforced by 'static int callable' */ +int sys_setup(void * BIOS) +{ + static int callable = 1; + struct gendisk *p; + int nr=0; + + if (!callable) + return -1; + callable = 0; + + for (p = gendisk_head ; p ; p=p->next) { + setup_dev(p); + nr += p->nr_real; + } + + if (nr) + printk("Partition table%s ok.\n\r",(nr>1)?"s":""); + +#ifdef RAMDISK + rd_load(); +#endif + mount_root(); + return (0); +} diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c index 756ebde19b48..b1620d2c1feb 100644 --- a/kernel/blk_drv/hd.c +++ b/kernel/blk_drv/hd.c @@ -1,7 +1,7 @@ /* * linux/kernel/hd.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -16,14 +16,21 @@ * in the early extended-partition checks and added DM partitions */ -#include - #include +#ifdef CONFIG_BLK_DEV_HD + +#define HD_IRQ 14 + +#include +#include #include #include #include #include #include +#include + +#define REALLY_SLOW_IO #include #include #include @@ -37,6 +44,8 @@ static inline unsigned char CMOS_READ(unsigned char addr) return inb_p(0x71); } +#define HD_DELAY 0 + /* Max read/write errors/sector */ #define MAX_ERRORS 7 #define MAX_HD 2 @@ -47,6 +56,10 @@ static void bad_rw_intr(void); static int recalibrate = 0; static int reset = 0; +#if (HD_DELAY > 0) +unsigned long last_req, read_timer(); +#endif + /* * This struct defines the HD's and their types. */ @@ -61,11 +74,7 @@ struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} }; static int NR_HD = 0; #endif -static struct hd_struct { - long start_sect; - long nr_sects; -} hd[MAX_HD<<6]={{0,0},}; - +static struct hd_struct hd[MAX_HD<<6]={{0,0},}; static int hd_sizes[MAX_HD<<6] = {0, }; #define port_read(port,buf,nr) \ @@ -74,207 +83,21 @@ __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di") #define port_write(port,buf,nr) \ __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si") -extern void hd_interrupt(void); -extern void rd_load(void); - -static unsigned int current_minor; - -/* - * Create devices for each logical partition in an extended partition. - * The logical partitions form a linked list, with each entry being - * a partition table with two entries. The first entry - * is the real data partition (with a start relative to the partition - * table start). The second is a pointer to the next logical partition - * (with a start relative to the entire extended partition). - * We do not create a Linux partition for the partition tables, but - * only for the actual data partitions. - */ -static void extended_partition(unsigned int dev) +#if (HD_DELAY > 0) +unsigned long read_timer(void) { - struct buffer_head *bh; - struct partition *p; - unsigned long first_sector, this_sector; - - first_sector = hd[MINOR(dev)].start_sect; - this_sector = first_sector; - - while (1) { - if ((current_minor & 0x3f) >= 60) - return; - if (!(bh = bread(dev,0))) { - printk("Unable to read partition table of device %04x\n",dev); - return; - } - /* - * This block is from a device that we're about to stomp on. - * So make sure nobody thinks this block is usable. - */ - bh->b_dirt=0; - bh->b_uptodate=0; - if (*(unsigned short *) (bh->b_data+510) == 0xAA55) { - p = 0x1BE + (void *)bh->b_data; - /* - * Process the first entry, which should be the real - * data partition. - */ - if (p->sys_ind == EXTENDED_PARTITION || - !(hd[current_minor].nr_sects = p->nr_sects)) - goto done; /* shouldn't happen */ - hd[current_minor].start_sect = this_sector + p->start_sect; - printk(" Logical part %d start %d size %d end %d\n\r", - current_minor, hd[current_minor].start_sect, - hd[current_minor].nr_sects, - hd[current_minor].start_sect + - hd[current_minor].nr_sects - 1); - current_minor++; - p++; - /* - * Process the second entry, which should be a link - * to the next logical partition. Create a minor - * for this just long enough to get the next partition - * table. The minor will be reused for the real - * data partition. - */ - if (p->sys_ind != EXTENDED_PARTITION || - !(hd[current_minor].nr_sects = p->nr_sects)) - goto done; /* no more logicals in this partition */ - hd[current_minor].start_sect = first_sector + p->start_sect; - this_sector = first_sector + p->start_sect; - dev = 0x0300 | current_minor; - brelse(bh); - } else - goto done; - } -done: - brelse(bh); -} + unsigned long t; + int i; -static void check_partition(unsigned int dev) -{ - int i, minor = current_minor; - struct buffer_head *bh; - struct partition *p; - unsigned long first_sector; - - first_sector = hd[MINOR(dev)].start_sect; - if (!(bh = bread(dev,0))) { - printk("Unable to read partition table of device %04x\n",dev); - return; - } - printk("Drive %d:\n\r",minor >> 6); - current_minor += 4; /* first "extra" minor */ - if (*(unsigned short *) (bh->b_data+510) == 0xAA55) { - p = 0x1BE + (void *)bh->b_data; - for (i=1 ; i<=4 ; minor++,i++,p++) { - if (!(hd[minor].nr_sects = p->nr_sects)) - continue; - hd[minor].start_sect = first_sector + p->start_sect; - printk(" part %d start %d size %d end %d \n\r", i, - hd[minor].start_sect, hd[minor].nr_sects, - hd[minor].start_sect + hd[minor].nr_sects - 1); - if ((current_minor & 0x3f) >= 60) - continue; - if (p->sys_ind == EXTENDED_PARTITION) { - extended_partition(0x0300 | minor); - } - } - /* - * check for Disk Manager partition table - */ - if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) { - p = 0x1BE + (void *)bh->b_data; - for (i = 4 ; i < 16 ; i++, current_minor++) { - p--; - if ((current_minor & 0x3f) >= 60) - break; - if (!(p->start_sect && p->nr_sects)) - continue; - hd[current_minor].start_sect = p->start_sect; - hd[current_minor].nr_sects = p->nr_sects; - printk(" DM part %d start %d size %d end %d\n\r", - current_minor, - hd[current_minor].start_sect, - hd[current_minor].nr_sects, - hd[current_minor].start_sect + - hd[current_minor].nr_sects - 1); - } - } - } else - printk("Bad partition table on dev %04x\n",dev); - brelse(bh); + cli(); + outb_p(0xc2, 0x43); + t = jiffies * 11931 + (inb_p(0x40) & 0x80 ? 5966 : 11932); + i = inb_p(0x40); + i |= inb(0x40) << 8; + sti(); + return(t - i / 2); } - -/* This may be used only once, enforced by 'static int callable' */ -int sys_setup(void * BIOS) -{ - static int callable = 1; - int i,drive; - unsigned char cmos_disks; - - if (!callable) - return -1; - callable = 0; -#ifndef HD_TYPE - for (drive=0 ; drive<2 ; drive++) { - hd_info[drive].cyl = *(unsigned short *) BIOS; - hd_info[drive].head = *(unsigned char *) (2+BIOS); - hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); - hd_info[drive].ctl = *(unsigned char *) (8+BIOS); - hd_info[drive].lzone = *(unsigned short *) (12+BIOS); - hd_info[drive].sect = *(unsigned char *) (14+BIOS); - BIOS += 16; - } - - /* - We querry CMOS about hard disks : it could be that - we have a SCSI/ESDI/etc controller that is BIOS - compatable with ST-506, and thus showing up in our - BIOS table, but not register compatable, and therefore - not present in CMOS. - - Furthurmore, we will assume that our ST-506 drives - are the primary drives in the system, and - the ones reflected as drive 1 or 2. - - The first drive is stored in the high nibble of CMOS - byte 0x12, the second in the low nibble. This will be - either a 4 bit drive type or 0xf indicating use byte 0x19 - for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. - - Needless to say, a non-zero value means we have - an AT controller hard disk for that drive. - - - */ - - if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) - if (cmos_disks & 0x0f) - NR_HD = 2; - else - NR_HD = 1; - else - NR_HD = 0; #endif - for (i = 0 ; i < (MAX_HD<<6) ; i++) { - hd[i].start_sect = 0; - hd[i].nr_sects = 0; - } - for (i = 0 ; i < NR_HD ; i++) - hd[i<<6].nr_sects = hd_info[i].head* - hd_info[i].sect*hd_info[i].cyl; - for (drive=0 ; drive>1 ; - blk_size[MAJOR_NR] = hd_sizes; - if (NR_HD) - printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); - rd_load(); - mount_root(); - return (0); -} static int controller_ready(void) { @@ -294,10 +117,13 @@ static int win_result(void) if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) == (READY_STAT | SEEK_STAT)) - return(0); /* ok */ - if (i&1) + return 0; /* ok */ + printk("HD: win_result: status = 0x%02x\n",i); + if (i&1) { i=inb(HD_ERROR); - return (1); + printk("HD: win_result: error = 0x%02x\n",i); + } + return 1; } static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, @@ -308,6 +134,10 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, if (drive>1 || head>15) panic("Trying to write bad sector"); +#if (HD_DELAY > 0) + while (read_timer() - last_req < HD_DELAY) + /* nothing */; +#endif if (reset || !controller_ready()) { reset = 1; return; @@ -321,7 +151,7 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, outb_p(cyl,++port); outb_p(cyl>>8,++port); outb_p(0xA0|(drive<<4)|head,++port); - outb(cmd,++port); + outb_p(cmd,++port); } static int drive_busy(void) @@ -335,7 +165,7 @@ static int drive_busy(void) if (c == (READY_STAT | SEEK_STAT)) return 0; } - printk("HD controller times out, c=%02x\n\r",c); + printk("HD controller times out, status = 0x%02x\n\r",c); return(1); } @@ -343,6 +173,7 @@ static void reset_controller(void) { int i; + printk("HD-controller reset\r\n"); outb(4,HD_CMD); for(i = 0; i < 1000; i++) nop(); outb(hd_info[0].ctl & 0x0f ,HD_CMD); @@ -383,12 +214,9 @@ repeat: */ void unexpected_hd_interrupt(void) { + sti(); printk("Unexpected HD interrupt\n\r"); SET_TIMER; -#if 0 - reset = 1; - do_hd_request(); -#endif } static void bad_rw_intr(void) @@ -397,48 +225,117 @@ static void bad_rw_intr(void) return; if (++CURRENT->errors >= MAX_ERRORS) end_request(0); - if (CURRENT->errors > MAX_ERRORS/2) + else if (CURRENT->errors > MAX_ERRORS/2) reset = 1; else recalibrate = 1; } +static inline int wait_DRQ(void) +{ + int retries = 100000; + + while (--retries > 0) + if (inb_p(HD_STATUS) & DRQ_STAT) + return 0; + return -1; +} + +#define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT) +#define STAT_OK (READY_STAT | SEEK_STAT) + static void read_intr(void) { - SET_INTR(&read_intr); - if (win_result()) { - SET_INTR(NULL); - bad_rw_intr(); - do_hd_request(); - return; + int i; + + i = (unsigned) inb_p(HD_STATUS); + if ((i & STAT_MASK) != STAT_OK) { + printk("HD: read_intr: status = 0x%02x\n",i); + goto bad_read; + } + if (wait_DRQ()) { + printk("HD: read_intr: no DRQ\n"); + goto bad_read; } port_read(HD_DATA,CURRENT->buffer,256); + i = (unsigned) inb_p(HD_STATUS); + if (!(i & BUSY_STAT)) + if ((i & STAT_MASK) != STAT_OK) { + printk("HD: read_intr: second status = 0x%02x\n",i); + goto bad_read; + } CURRENT->errors = 0; CURRENT->buffer += 512; CURRENT->sector++; - if (--CURRENT->nr_sectors) + i = --CURRENT->nr_sectors; + --CURRENT->current_nr_sectors; +#ifdef DEBUG + printk("hd%d : sector = %d, %d remaining to buffer = %08x\n", + MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT-> + buffer); +#endif + if (!i || (CURRENT->bh && !SUBSECTOR(i))) + end_request(1); + if (i > 0) { + SET_INTR(&read_intr); + sti(); return; - SET_INTR(NULL); - end_request(1); + } +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif + do_hd_request(); + return; +bad_read: + if (i & ERR_STAT) { + i = (unsigned) inb(HD_ERROR); + printk("HD: read_intr: error = 0x%02x\n",i); + } + bad_rw_intr(); do_hd_request(); + return; } static void write_intr(void) { - if (win_result()) { - bad_rw_intr(); - do_hd_request(); - return; + int i; + + i = (unsigned) inb_p(HD_STATUS); + if ((i & STAT_MASK) != STAT_OK) { + printk("HD: write_intr: status = 0x%02x\n",i); + goto bad_write; + } + if (CURRENT->nr_sectors > 1 && wait_DRQ()) { + printk("HD: write_intr: no DRQ\n"); + goto bad_write; } - if (--CURRENT->nr_sectors) { - CURRENT->sector++; - CURRENT->buffer += 512; + CURRENT->sector++; + i = --CURRENT->nr_sectors; + --CURRENT->current_nr_sectors; + CURRENT->buffer += 512; + if (!i || (CURRENT->bh && !SUBSECTOR(i))) + end_request(1); + if (i > 0) { SET_INTR(&write_intr); port_write(HD_DATA,CURRENT->buffer,256); - return; + sti(); + } else { +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif + do_hd_request(); + } + return; +bad_write: + sti(); + if (i & ERR_STAT) { + i = (unsigned) inb(HD_ERROR); + printk("HD: write_intr: error = 0x%02x\n",i); } - end_request(1); + bad_rw_intr(); + cli(); do_hd_request(); + return; } static void recal_intr(void) @@ -453,30 +350,50 @@ static void recal_intr(void) * best idea seems to just set reset, and start all over again. */ static void hd_times_out(void) -{ - do_hd = NULL; +{ + sti(); + DEVICE_INTR = NULL; reset = 1; if (!CURRENT) return; printk("HD timeout\n\r"); cli(); - if (++CURRENT->errors >= MAX_ERRORS) + if (++CURRENT->errors >= MAX_ERRORS) { +#ifdef DEBUG + printk("hd : too many errors.\n"); +#endif end_request(0); + } + do_hd_request(); } +/* + * The driver has been modified to enable interrupts a bit more: in order to + * do this we first (a) disable the timeout-interrupt and (b) clear the + * device-interrupt. This way the interrupts won't mess with out code (the + * worst that can happen is that an unexpected HD-interrupt comes in and + * sets the "reset" variable and starts the timer) + */ static void do_hd_request(void) { - int i,r; unsigned int block,dev; unsigned int sec,head,cyl; unsigned int nsect; +repeat: + DEVICE_INTR = NULL; + timer_active &= ~(1<dev); block = CURRENT->sector; nsect = CURRENT->nr_sectors; - if (dev >= (NR_HD<<6) || block+nsect > hd[dev].nr_sects) { + if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) { +#ifdef DEBUG + printk("hd%d : attempted read for sector %d past end of device at sector %d.\n", + block, hd[dev].nr_sects); +#endif end_request(0); goto repeat; } @@ -487,34 +404,41 @@ static void do_hd_request(void) head = block % hd_info[dev].head; cyl = block / hd_info[dev].head; sec++; +#ifdef DEBUG + printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n", + dev, cyl, head, sec, CURRENT->buffer); +#endif + cli(); if (reset) { recalibrate = 1; reset_hd(); + sti(); return; } if (recalibrate) { recalibrate = 0; - hd_out(dev,hd_info[dev].sect,0,0,0, - WIN_RESTORE,&recal_intr); + hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr); if (reset) goto repeat; + sti(); return; } if (CURRENT->cmd == WRITE) { hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); if (reset) goto repeat; - for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++) - /* nothing */ ; - if (!r) { + if (wait_DRQ()) { + printk("HD: do_hd_request: no DRQ\n"); bad_rw_intr(); goto repeat; } port_write(HD_DATA,CURRENT->buffer,256); + sti(); } else if (CURRENT->cmd == READ) { hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); if (reset) goto repeat; + sti(); } else panic("unknown hd-command"); } @@ -532,34 +456,152 @@ static int hd_ioctl(struct inode * inode, struct file * file, return -EINVAL; switch (cmd) { case HDIO_REQ: + verify_area(loc, sizeof(*loc)); put_fs_byte(hd_info[dev].head, (char *) &loc->heads); put_fs_byte(hd_info[dev].sect, (char *) &loc->sectors); put_fs_word(hd_info[dev].cyl, (short *) &loc->cylinders); + put_fs_long(hd[MINOR(inode->i_rdev)].start_sect, + (long *) &loc->start); return 0; + RO_IOCTLS(inode->i_rdev,arg); default: return -EINVAL; } } +/* + * Releasing a block device means we sync() it, so that it can safely + * be forgotten about... + */ +static void hd_release(struct inode * inode, struct file * file) +{ + sync_dev(inode->i_rdev); +} + + +static void hd_geninit(); + +static struct gendisk hd_gendisk = { + MAJOR_NR, /* Major number */ + "hd", /* Major name */ + 6, /* Bits to shift to get real from partition */ + 1 << 6, /* Number of partitions per real */ + MAX_HD, /* maximum number of real */ + hd_geninit, /* init function */ + hd, /* hd struct */ + hd_sizes, /* block sizes */ + 0, /* number */ + (void *) hd_info, /* internal */ + NULL /* next */ +}; + +static void hd_geninit(void) +{ + int drive; +#ifndef HD_TYPE + extern struct drive_info drive_info; + void *BIOS = (void *) &drive_info; + int cmos_disks, i; + + for (drive=0 ; drive<2 ; drive++) { + hd_info[drive].cyl = *(unsigned short *) BIOS; + hd_info[drive].head = *(unsigned char *) (2+BIOS); + hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); + hd_info[drive].ctl = *(unsigned char *) (8+BIOS); + hd_info[drive].lzone = *(unsigned short *) (12+BIOS); + hd_info[drive].sect = *(unsigned char *) (14+BIOS); + BIOS += 16; + } + + /* + We querry CMOS about hard disks : it could be that + we have a SCSI/ESDI/etc controller that is BIOS + compatable with ST-506, and thus showing up in our + BIOS table, but not register compatable, and therefore + not present in CMOS. + + Furthurmore, we will assume that our ST-506 drives + are the primary drives in the system, and + the ones reflected as drive 1 or 2. + + The first drive is stored in the high nibble of CMOS + byte 0x12, the second in the low nibble. This will be + either a 4 bit drive type or 0xf indicating use byte 0x19 + for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. + + Needless to say, a non-zero value means we have + an AT controller hard disk for that drive. + + + */ + + if ((cmos_disks = CMOS_READ(0x12)) & 0xf0) + if (cmos_disks & 0x0f) + NR_HD = 2; + else + NR_HD = 1; + else + NR_HD = 0; +#endif + + for (i = 0 ; i < NR_HD ; i++) + hd[i<<6].nr_sects = hd_info[i].head* + hd_info[i].sect*hd_info[i].cyl; + + hd_gendisk.nr_real = NR_HD; +} + static struct file_operations hd_fops = { NULL, /* lseek - default */ block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ NULL, /* readdir - bad */ - NULL, /* close - default */ NULL, /* select */ - hd_ioctl /* ioctl */ + hd_ioctl, /* ioctl */ + NULL, /* no special open code */ + hd_release /* release */ +}; + +static void hd_interrupt(int unused) +{ + void (*handler)(void) = DEVICE_INTR; + + DEVICE_INTR = NULL; + timer_active &= ~(1< #include #include +#include +#include + #include #include "blk.h" +extern long rd_init(long mem_start, int length); + /* * The request-struct contains all necessary data * to load a nr of sectors into memory @@ -23,7 +27,7 @@ struct request request[NR_REQUEST]; /* * used to wait on when there are no free requests */ -struct task_struct * wait_for_request = NULL; +struct wait_queue * wait_for_request = NULL; /* blk_dev_struct is: * do_request-address @@ -36,7 +40,10 @@ struct blk_dev_struct blk_dev[NR_BLK_DEV] = { { NULL, NULL }, /* dev hd */ { NULL, NULL }, /* dev ttyx */ { NULL, NULL }, /* dev tty */ - { NULL, NULL } /* dev lp */ + { NULL, NULL }, /* dev lp */ + { NULL, NULL }, /* dev pipes */ + { NULL, NULL }, /* dev sd */ + { NULL, NULL } /* dev st */ }; /* @@ -65,6 +72,31 @@ static inline void unlock_buffer(struct buffer_head * bh) wake_up(&bh->b_wait); } +/* RO fail safe mechanism */ + +static long ro_bits[NR_BLK_DEV][8]; + +int is_read_only(int dev) +{ + int minor,major; + + major = MAJOR(dev); + minor = MINOR(dev); + if (major < 0 || major >= NR_BLK_DEV) return 0; + return ro_bits[major][minor >> 5] & (1 << (minor & 31)); +} + +void set_device_ro(int dev,int flag) +{ + int minor,major; + + major = MAJOR(dev); + minor = MINOR(dev); + if (major < 0 || major >= NR_BLK_DEV) return; + if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31); + else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31)); +} + /* * add-request adds a request to the linked list. * It disables interrupts so that it can muck with the @@ -105,6 +137,7 @@ static void add_request(struct blk_dev_struct * dev, struct request * req) static void make_request(int major,int rw, struct buffer_head * bh) { + unsigned int sector, count; struct request * req; int rw_ahead; @@ -122,12 +155,36 @@ static void make_request(int major,int rw, struct buffer_head * bh) printk("Bad block dev command, must be R/W/RA/WA\n"); return; } + count = bh->b_size >> 9; + sector = bh->b_blocknr * count; + if (blk_size[major]) + if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) { + bh->b_dirt = bh->b_uptodate = 0; + return; + } lock_buffer(bh); if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { unlock_buffer(bh); return; } repeat: + cli(); + if ((major == 3 || major == 8 )&& (req = blk_dev[major].current_request)) { + while (req = req->next) { + if (req->dev == bh->b_dev && + !req->waiting && + req->cmd == rw && + req->sector + req->nr_sectors == sector && + req->nr_sectors < 254) { + req->bhtail->b_reqnext = bh; + req->bhtail = bh; + req->nr_sectors += count; + bh->b_dirt = 0; + sti(); + return; + } + } + } /* we don't allow the write-requests to fill up the queue completely: * we want some room for reads: they take precedence. The last third * of the requests are only for reads. @@ -137,7 +194,6 @@ repeat: else req = request+(NR_REQUEST/2); /* find an empty request */ - cli(); while (--req >= request) if (req->dev < 0) goto found; @@ -155,12 +211,14 @@ found: sti(); /* fill up the request-info, and add it to the queue */ req->dev = bh->b_dev; req->cmd = rw; - req->errors=0; - req->sector = bh->b_blocknr<<1; - req->nr_sectors = 2; + req->errors = 0; + req->sector = sector; + req->nr_sectors = count; + req->current_nr_sectors = count; req->buffer = bh->b_data; req->waiting = NULL; req->bh = bh; + req->bhtail = bh; req->next = NULL; add_request(major+blk_dev,req); } @@ -171,11 +229,15 @@ void ll_rw_page(int rw, int dev, int page, char * buffer) unsigned int major = MAJOR(dev); if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { - printk("Trying to read nonexistent block-device\n\r"); + printk("Trying to read nonexistent block-device %04x (%d)\n",dev,page*8); return; } if (rw!=READ && rw!=WRITE) panic("Bad block dev command, must be R/W"); + if (rw == WRITE && is_read_only(dev)) { + printk("Can't page to read-only device 0x%X\n\r",dev); + return; + } cli(); repeat: req = request+NR_REQUEST; @@ -193,8 +255,9 @@ repeat: req->errors = 0; req->sector = page<<3; req->nr_sectors = 8; + req->current_nr_sectors = 8; req->buffer = buffer; - req->waiting = current; + req->waiting = ¤t->wait; req->bh = NULL; req->next = NULL; current->state = TASK_UNINTERRUPTIBLE; @@ -206,15 +269,28 @@ void ll_rw_block(int rw, struct buffer_head * bh) { unsigned int major; + if (!bh) + return; + if (bh->b_size != 1024) { + printk("ll_rw_block: only 1024-char blocks implemented (%d)\n",bh->b_size); + bh->b_dirt = bh->b_uptodate = 0; + return; + } if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { - printk("ll_rw_block: Trying to read nonexistent block-device\n\r"); + printk("ll_rw_block: Trying to read nonexistent block-device %04x (%d)\n",bh->b_dev,bh->b_blocknr); + bh->b_dirt = bh->b_uptodate = 0; + return; + } + if ((rw == WRITE || rw == WRITEA) && is_read_only(bh->b_dev)) { + printk("Can't write to read-only device 0x%X\n\r",bh->b_dev); + bh->b_dirt = bh->b_uptodate = 0; return; } make_request(major,rw,bh); } -void blk_dev_init(void) +long blk_dev_init(long mem_start, long mem_end) { int i; @@ -222,6 +298,11 @@ void blk_dev_init(void) request[i].dev = -1; request[i].next = NULL; } + memset(ro_bits,0,sizeof(ro_bits)); +#ifdef RAMDISK + mem_start += rd_init(mem_start, RAMDISK*1024); +#endif + return mem_start; } void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf) @@ -239,6 +320,10 @@ void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf) printk("ll_rw_swap: bad block dev command, must be R/W"); return; } + if (rw == WRITE && is_read_only(dev)) { + printk("Can't swap to read-only device 0x%X\n\r",dev); + return; + } for (i=0; ierrors = 0; req->sector = b[i] << 1; req->nr_sectors = 2; + req->current_nr_sectors = 2; req->buffer = buf; - req->waiting = current; + req->waiting = ¤t->wait; req->bh = NULL; req->next = NULL; current->state = TASK_UNINTERRUPTIBLE; diff --git a/kernel/blk_drv/ramdisk.c b/kernel/blk_drv/ramdisk.c index c507631aeb58..195146d5f00d 100644 --- a/kernel/blk_drv/ramdisk.c +++ b/kernel/blk_drv/ramdisk.c @@ -4,9 +4,10 @@ * Written by Theodore Ts'o, 12/2/91 */ -#include #include +#ifdef RAMDISK +#include #include #include #include @@ -21,11 +22,12 @@ char *rd_start; int rd_length = 0; -void do_rd_request(void) +static void do_rd_request(void) { int len; char *addr; +repeat: INIT_REQUEST; addr = rd_start + (CURRENT->sector << 9); len = CURRENT->nr_sectors << 9; @@ -52,9 +54,10 @@ static struct file_operations rd_fops = { block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ NULL, /* readdir - bad */ - NULL, /* close - default */ NULL, /* select */ - NULL /* ioctl */ + NULL, /* ioctl */ + NULL, /* no special open code */ + NULL /* no special release code */ }; /* @@ -83,7 +86,7 @@ long rd_init(long mem_start, int length) void rd_load(void) { struct buffer_head *bh; - struct super_block s; + struct minix_super_block s; int block = 256; /* Start at block 256 */ int i = 1; int nblocks; @@ -111,14 +114,14 @@ void rd_load(void) nblocks, rd_length >> BLOCK_SIZE_BITS); return; } - printk("Loading %d bytes into ram disk... 0000k", + printk("Loading %d bytes into ram disk\n", nblocks << BLOCK_SIZE_BITS); cp = rd_start; while (nblocks) { if (nblocks > 2) bh = breada(ROOT_DEV, block, block+1, block+2, -1); else - bh = bread(ROOT_DEV, block); + bh = bread(ROOT_DEV, block, BLOCK_SIZE); if (!bh) { printk("I/O error on block %d, aborting load\n", block); @@ -126,12 +129,13 @@ void rd_load(void) } (void) memcpy(cp, bh->b_data, BLOCK_SIZE); brelse(bh); - printk("\010\010\010\010\010%4dk",i); + if (!(nblocks-- & 15)) + printk("."); cp += BLOCK_SIZE; block++; - nblocks--; i++; } - printk("\010\010\010\010\010done \n"); + printk("\ndone\n"); ROOT_DEV=0x0101; } +#endif diff --git a/kernel/blk_drv/scsi/7000fasst.c b/kernel/blk_drv/scsi/7000fasst.c new file mode 100644 index 000000000000..8a72be4779fe --- /dev/null +++ b/kernel/blk_drv/scsi/7000fasst.c @@ -0,0 +1,465 @@ +/* $Id: 7000fasst.c,v 1.1 1992/07/24 06:27:38 root Exp root $ + * linux/kernel/7000fasst.c + * + * Copyright (C) 1992 Thomas Wuensche + * closely related to the aha1542 driver from Tommy Thorn + * ( as close as different hardware allows on a lowlevel-driver :-) ) + */ + +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +struct mailbox{ + unchar status; + unchar scbptr[3]; +}; + +/* #define DEBUG */ + +#include "7000fasst.h" +#ifdef DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +/*static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/7000fasst.c,v 1.1 1992/07/24 06:27:38 root Exp root $";*/ + +static struct scb scbs[OGMB_CNT]; + +long wd7000fasst_WAITnexttimeout = 3000000; + +void (*wd7000fasst_do_done)() = NULL; +extern void wd7000fasst_interrupt(); +void wd7000fasst_call_buh(); + +static unchar controlstat = 0; +static unchar wd7000fasst_hostno; + +#define wd7000fasst_intr_reset() outb(0,INTR_ACK) +#define PC_IMR 0x21 +#define AT_IMR 0xa1 + +#define wd7000fasst_enable_intr(){\ + controlstat |= INT_EN;\ + outb(controlstat,CONTROL);\ + outb((inb((intr_chan<=7)?PC_IMR:AT_IMR))& ~0xff,(intr_chan<=7)?PC_IMR:AT_IMR);} + +#define wd7000fasst_disable_intr() outb(controlstat |= INT_EN, CONTROL) +#define wd7000fasst_enable_dma() {\ + controlstat |= DMA_EN;\ + outb(controlstat,CONTROL);\ + outb((DMA_CH|CASCADE),DMA_MODE_REG);\ + outb(DMA_CH,DMA_MASK_REG);} + +#define wd7000fasst_disable_dma() {\ + outb(DMA_CH|S_DMA_MASK,DMA_MASK_REG);\ + controlstat &= ~DMA_EN;\ + outb(controlstat,CONTROL);} + +#define WAIT(port, mask, allof, noneof) \ + { register WAITbits; \ + register WAITtimeout = wd7000fasst_WAITnexttimeout; \ + while (1) { \ + WAITbits = inb(port) & (mask); \ + if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \ + break; \ + if (--WAITtimeout == 0) goto fail; \ + } \ + } + +static void wd7000fasst_stat(void) +{ +/* int s = inb(ASC_STAT), i = inb(INTR_STAT);*/ +/* printk("status = %x, intrflags = %x served %d last %x\n", s, i, intr_flag, intr_last); + printk("status=%x intrflags=%x\n", s, i); +*/} + +static int wd7000fasst_out(unchar *cmdp, int len) +{ + while (len--) + { + WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); + outb(*cmdp++, COMMAND); + } + return 0; + fail: + printk("wd7000fasst_out failed(%d): ", len+1); wd7000fasst_stat(); + return 1; +} + +int wd7000fasst_make_error_code(unsigned hosterr, unsigned scsierr) +{ +#ifdef DEBUG + int in_error=hosterr; +#endif + switch ((hosterr&0xff00)>>8){ + case 0: /* It is reserved, should never happen */ + hosterr=DID_ERROR; + break; + case 1: hosterr=DID_OK; + break; + case 2: /* Command complete with logged error */ + /* My actual copies of the manual pages are unreadable + * For now we simply tell there is an error */ + DEB(printk("Hosterror: VUE = %x\n",hosterr&0xff);) + switch (hosterr&0xff) { + default: DEB(printk("wd7000fasst_make_error_code: unknown hoststatus %x\n", hosterr);) + hosterr=DID_ERROR; + break; + } + break; + case 4: hosterr=DID_BAD_TARGET; /* Command failed to complete without SCSI status */ + break; + case 5: hosterr=DID_RESET; /* Cmd terminated; Bus reset by external device */ + break; + case 6: hosterr=DID_ERROR;/* Hardware Failure, requires host reset */ + break; + case 7: hosterr=DID_RESET; + break; + case 8: hosterr=DID_OK; + printk("wd7000fasst: Linked command not implemented\n"); + break; + } +#ifdef DEBUG + if (scsierr||hosterr) printk("SCSI-Command error: SCSI %x HOST %x RETURN %x\n",scsierr,in_error,hosterr); +#endif + return scsierr|(hosterr << 16); +} + +/* The following is space for the Mailboxes */ +struct{ struct mailbox ombox[OGMB_CNT]; + struct mailbox imbox[ICMB_CNT]; } mbstruct; + +int wd7000fasst_init(void) +{ int i; + volatile int debug = 0; + /* Page 47 */ + unchar init_block[]={ 1, 7, 0x18, 0x18, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT }; + /* Reset the adapter. I ought to make a hard reset, but it's not really nessesary */ + + DEB(printk("wd7000fasst_init called \n")); + + outb(SCSI_RES|ASC_RES, CONTROL); + /* Wait at least 25 us */ + for (i=0; i< 1000; i++) inb(ASC_STAT); + /* Now reset the reset */ + outb(0,CONTROL); + debug = 1; + /* Expect Command Port Ready */ + WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); + DEB(printk("wd7000fasst_init: Power on Diagnostics finished\n")); + if ((i=inb(INTR_STAT))!=1) + printk("Power on Diagnostics error %x\n",i); + + debug = 2; + /* Clear mbstruct */ + memset(&mbstruct,0,sizeof (mbstruct)); + /* Set up init block */ + any2scsi(init_block+5,&mbstruct); + /* Execute init command */ + wd7000fasst_out(init_block,sizeof(init_block)); + DEB(printk("Init-Block :"); + for (i=0;i0){ + printk("%x ",*((unchar *)scbptr)); + ((unchar *)scbptr)++; + if (++k==16){ printk("\n"); k=0; } + }); + } + else { printk("Error in interrupt\n"); return; } + /* more error checking left out here */ + + scbptr=(struct scb *)scsi2int(mbstruct.imbox[flag&0x3f].scbptr); + host_error=scbptr->vue | mbstruct.imbox[flag&0x3f].status<<8; + scsi_error=scbptr->sretstat; + errstatus=wd7000fasst_make_error_code(host_error,scsi_error); + DEB(if (errstatus) printk("Target was %x\n",scbptr->idlun>>5);) + DEB(if (errstatus) printk("wd7000fasst_intr_handle: returning %6x\n", errstatus)); + DEB(printk("wd7000fasst_intr_handle: Status of the finished command: %x\n",mbstruct.imbox[flag&0x3f].status)); + /* I make a SCSI reset */ + /* Left out */ + my_done(wd7000fasst_hostno,errstatus); + wd7000fasst_intr_reset(); + return; +} + +volatile static int internal_done_flag = 0; +volatile static int internal_done_errcode = 0; + +/* The following code queues a SCSI command */ +int wd7000fasst_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, + void (*done)(int, int)) +{ + int i; +#ifdef DEBUG + int j; +#endif + unchar *cmd = (unchar *) cmnd; +/* We first look for a free outgoing mailbox */ + for (i=0;i0){ + printk("%x ",*((unchar *)scbptr)); + ((unchar *)scbptr)++; + if (++k==16){ printk("\n"); k=0; } + };) + } + /* Set up the "done" response function */ + if (done) { + DEB(printk("wd7000fasst_queuecommand: now waiting for interrupt "); + wd7000fasst_stat()); + if (wd7000fasst_do_done) + printk("wd7000fasst_queuecommand: Two concurrent queuecommand?\n"); + else + wd7000fasst_do_done = done; + DEB(wd7000fasst_stat()); + wd7000fasst_enable_intr(); + } + else{ + printk("wd7000fasst_queuecommand: done can't be NULL\n"); + return 0; + } + /* Now we initialize execution */ +retry: WAIT(ASC_STAT,STATMASK,CMD_RDY,0); + outb(0x80+i,COMMAND); + WAIT(ASC_STAT,STATMASK,CMD_RDY,0); + if (inb(ASC_STAT)&CMD_REJ) goto retry; + return 1; + /* Wait until done */ + +fail: + return 0; +} + +/* We use this function for queueing a command from wd7000fasst_command */ +static void internal_done(int host, int errcode) +{ + internal_done_errcode = errcode; + ++internal_done_flag; +} + +int wd7000fasst_command(unchar target, const void *cmnd, void *buff, int bufflen) +{ +#ifdef DEBUG + int k; +#endif + wd7000fasst_queuecommand(target, cmnd, buff, bufflen, internal_done); + + while (!internal_done_flag); + internal_done_flag = 0; + DEB(printk("wd7000fasst_command finished: ..leaving with errcode %x\n", + internal_done_errcode)); + DEB(for (k=0;k<5000000;k++) inb(INTR_STAT)); + return internal_done_errcode; +} + +/* a hack to avoid a strange compilation error */ + +void wd7000fasst_call_buh() +{ + set_intr_gate((intr_chan<=7)?intr_chan+8:intr_chan+0x20,&wd7000fasst_interrupt); +} + +/* return non-zero on detection */ +static const char *wd_bases[] = {(char *)0xce000}; +typedef struct {char * signature; + unsigned offset; + unsigned length; + }Signature; + +static const Signature signatures[] = + {{"SSTBIOS",0xd,0x7}}; + +#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) + +int wd7000fasst_detect(int hostnum) /* hostnum ignored for now */ +{ + int i,j; + char const * base_address = 0; + /* Store our host number */ + wd7000fasst_hostno=hostnum; + DEB(printk("wd7000fasst_detect: \n")); + + for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){ + for(j=0;j + +#undef STATMASK +#undef CONTROL + +#define io_base 0x350 +#define intr_chan 15 +#define dma_chan 6 +#define OGMB_CNT 8 +#define ICMB_CNT 8 + +/* I/O Port interface 4.2 */ +/* READ */ +#define ASC_STAT io_base +#define INT_IM 0x80 /* Interrupt Image Flag */ +#define CMD_RDY 0x40 /* Command Port Ready */ +#define CMD_REJ 0x20 /* Command Port Byte Rejected */ +#define ASC_INI 0x10 /* ASC Initialized Flag */ +#define STATMASK 0xf0 /* The lower 4 Bytes are reserved */ + +/* This register saves two purposes + * Diagnostics error + * Interrupt Status + */ +#define INTR_STAT ASC_STAT+1 +#define ANYINTR 0x80 /* Mailbox Service possible/required */ +#define IMB 0x40 /* 1 Incoming / 0 Outgoing */ +#define MBMASK 0x3f +/* if MSB is zero, the content of the lower ones keeps Diagnostic State * + * 00 Power-on, no diagnostics executed + * 01 No diagnostic Error Occured + * 02 RAM Failed + * 03 FIFO R/W Failed + * ... +*/ + +/* WRITE */ +#define COMMAND ASC_STAT + +#define INTR_ACK ASC_STAT+1 + + +#define CONTROL ASC_STAT+2 +#define INT_EN 0x08 /* Interrupt Enable */ +#define DMA_EN 0x04 /* DMA Enable */ +#define SCSI_RES 0x02 /* SCSI Reset */ +#define ASC_RES 0x01 /* ASC Reset */ + +/* The DMA-Controller */ +#define DMA_MODE_REG 0xd6 +#define DMA_MASK_REG 0xd4 +#define S_DMA_MSK 0x04 +#define DMA_CH 0x02 +#define CASCADE 0xc0 + +/* Mailbox Definition 5.3 */ + +/* These belong in scsi.h also */ +#undef any2scsi +#define any2scsi(up, p) \ +(up)[0] = (((long)(p)) >> 16); \ +(up)[1] = ((long)(p)) >> 8; \ +(up)[2] = ((long)(p)); + +#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) ) + +#define xany2scsi(up, p) \ +(up)[0] = ((long)(p)) >> 24; \ +(up)[1] = ((long)(p)) >> 16; \ +(up)[2] = ((long)(p)) >> 8; \ +(up)[3] = ((long)(p)); + +#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \ + + (((long)(up)[2]) << 8) + ((long)(up)[3]) ) + +#define MAX_CDB 12 +#define MAX_SENSE 14 + +struct scb { /* Command Control Block 5.4.1 */ + unchar op; /* Command Control Block Operation Code */ + unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ + /* Outbound data transfer, length is checked*/ + /* Inbound data transfer, length is checked */ + /* Logical Unit Number */ + unchar scbdata[12]; /* SCSI Command Block */ + unchar sretstat; /* SCSI Return Status */ + unchar vue; /* Vendor Unique Error Code */ + unchar maxdata[3]; /* Maximum Data Transfer Length */ + unchar dataptr[3]; /* SCSI Data Block Pointer */ + unchar linkptr[3]; /* Next Command Link Pointer */ + unchar direc; /* Transfer Direction */ + unchar reserved2[6]; /* SCSI Command Descriptor Block */ + /* REQUEST SENSE */ +}; + +int wd7000fasst_detect(int); +int wd7000fasst_command(unsigned char target, const void *cmnd, void *buff, int bufflen); +int wd7000fasst_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, void (*done)(int,int)); +int wd7000fasst_abort(int); +char *wd7000fasst_info(void); +int wd7000fasst_reset(void); + +#ifndef NULL + #define NULL 0 +#endif + +#define WD7000FASST {"Western Digital 7000FASST", wd7000fasst_detect, \ + wd7000fasst_info, wd7000fasst_command, \ + wd7000fasst_queuecommand, \ + wd7000fasst_abort, \ + wd7000fasst_reset, \ + 1, 7, 0} +#endif diff --git a/kernel/blk_drv/scsi/Makefile b/kernel/blk_drv/scsi/Makefile new file mode 100644 index 000000000000..f9d8e57e4ff9 --- /dev/null +++ b/kernel/blk_drv/scsi/Makefile @@ -0,0 +1,167 @@ +# +# Makefile for the linux kernel block device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + +#DEBUG = -DDEBUG=0xffffffff -DDEBUG_NO_CMD + +.c.s: + $(CC) $(CFLAGS) $(DEBUG) -S $< + +.s.o: + $(AS) -c -o $*.o $< + +.c.o: + $(CC) $(CFLAGS) $(DEBUG) -c $< + + +LOWLEVELSSRC = seagate2.s +LOWLEVELCSRC = aha1542.c fdomain.c seagate.c ultrastor.c 7000fasst.c +LOWLEVELHSRC = aha1542.h fdomain.h seagate.h ultrastor.h 7000fasst.o + +CSRC = hosts.c sd.c sd_ioctl.c st.c st_ioctl.c scsi.c scsi_ioctl.c $(LOWLEVELCSRC) +HSRC = hosts.h sd.h st.h scsi.h scsi_ioctl.h $(LOWLEVELHSRC) + +OBJS = scsi.o hosts.o scsi_ioctl.o sd.o sd_ioctl.o st.o st_ioctl.o \ + aha1542.o fdomain.o seagate.o seagate2.o ultrastor.o 7000fasst.o + +all: scsi.a + +figure : hosts.h $(KERNELHDRS)/linux/config.h hosts.c + $(HOSTCC) -N -DFIGURE_MAX_SCSI_HOSTS hosts.c -o figure + +max_hosts.h : figure + (echo "#ifndef _MAX_HOSTS_H"; \ + echo "#define _MAX_HOSTS_H"; \ + echo "#define MAX_SCSI_HOSTS `./figure`";\ + echo "#endif") > tmp_max + cp tmp_max max_hosts.h + +scsi.a: $(OBJS) + $(AR) rcs scsi.a $(OBJS) + sync + +clean: + rm -f core *.o *.a tmp_make tmp_max figure max_hosts.h + +seagate2.o : seagate2.s + +seagate.o: seagate.c + $(CC) -Wall -c seagate.c + +dep: + touch max_hosts.h + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make + rm max_hosts.h + cp tmp_make Makefile + +### Dependencies: +7000fasst.o : 7000fasst.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h \ + scsi.h hosts.h max_hosts.h 7000fasst.h +aha1542.o : aha1542.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h \ + scsi.h hosts.h max_hosts.h aha1542.h +fdomain.o : fdomain.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/asm/io.h fdomain.h scsi.h hosts.h max_hosts.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/errno.h +hosts.o : hosts.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/kernel.h scsi.h hosts.h max_hosts.h aha1542.h /usr/src/linux/include/linux/types.h \ + fdomain.h seagate.h ultrastor.h 7000fasst.h +scsi.o : scsi.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/linux/string.h scsi.h \ + hosts.h max_hosts.h sd.h /usr/src/linux/include/linux/genhd.h st.h +scsi_ioctl.o : scsi_ioctl.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h scsi.h hosts.h max_hosts.h scsi_ioctl.h +sd.o : sd.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/kernel.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h /usr/src/linux/include/linux/string.h \ + scsi.h sd.h /usr/src/linux/include/linux/genhd.h ../blk.h +sd_ioctl.o : sd_ioctl.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h scsi.h sd.h /usr/src/linux/include/linux/genhd.h +seagate.o : seagate.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + seagate.h scsi.h hosts.h max_hosts.h +st.o : st.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + scsi.h st.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + ../blk.h +st_ioctl.o : st_ioctl.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h st.h scsi.h +ultrastor.o : ultrastor.c /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/stddef.h /usr/src/linux/include/linux/string.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/system.h ultrastor.h \ + scsi.h hosts.h max_hosts.h diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c new file mode 100644 index 000000000000..a616675a446f --- /dev/null +++ b/kernel/blk_drv/scsi/aha1542.c @@ -0,0 +1,483 @@ +/* $Id: aha1542.c,v 1.1 1992/07/24 06:27:38 root Exp root $ + * linux/kernel/aha1542.c + * + * Copyright (C) 1992 Tommy Thorn + */ + +#include +#include +#include +#include +#include + +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "aha1542.h" +#ifdef DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +/* +static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1542.c,v 1.1 1992/07/24 06:27:38 root Exp root $"; +*/ + +#define base 0x330 +#define intr_chan 11 + +static struct mailbox mb[2]; +static struct ccb ccb; + +long WAITtimeout, WAITnexttimeout = 3000000; + +void (*do_done)(int, int) = NULL; +int aha1542_host = 0; +extern void aha1542_interrupt(); + +#define aha1542_intr_reset() outb(IRST, CONTROL) +#define aha1542_enable_intr() outb(inb_p(0xA1) & ~8, 0xA1) +#define aha1542_disable_intr() outb(inb_p(0xA1) | 8, 0xA1) + +#define WAIT(port, mask, allof, noneof) \ + { register WAITbits; \ + register WAITtimeout = WAITnexttimeout; \ + while (1) { \ + WAITbits = inb(port) & (mask); \ + if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \ + break; \ + if (--WAITtimeout == 0) goto fail; \ + } \ + } + +static void aha1542_stat(void) +{ +/* int s = inb(STATUS), i = inb(INTRFLAGS); + printk("status=%x intrflags=%x\n", s, i, WAITnexttimeout-WAITtimeout); */ +} + +static int aha1542_out(unchar *cmdp, int len) +{ + while (len--) + { + WAIT(STATUS, CDF, 0, CDF); + outb(*cmdp++, DATA); + } + return 0; + fail: + printk("aha1542_out failed(%d): ", len+1); aha1542_stat(); + return 1; +} + +int makecode(unsigned hosterr, unsigned scsierr) +{ + switch (hosterr) { + case 0x0: + case 0xa: /* Linked command complete without error and linked normally */ + case 0xb: /* Linked command complete without error, interrupt generated */ + hosterr = 0; + break; + + case 0x11: /* Selection time out-The initiator selection or target + reselection was not complete within the SCSI Time out period */ + hosterr = DID_TIME_OUT; + break; + + case 0x12: /* Data overrun/underrun-The target attempted to transfer more data + thean was allocated by the Data Length field or the sum of the + Scatter / Gather Data Length fields. */ + + case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */ + + case 0x15: /* MBO command was not 00, 01 or 02-The first byte of the CB was + invalid. This usually indicates a software failure. */ + + case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid. + This usually indicates a software failure. */ + + case 0x17: /* Linked CCB does not have the same LUN-A subsequent CCB of a set + of linked CCB's does not specify the same logical unit number as + the first. */ + case 0x18: /* Invalid Target Direction received from Host-The direction of a + Target Mode CCB was invalid. */ + + case 0x19: /* Duplicate CCB Received in Target Mode-More than once CCB was + received to service data transfer between the same target LUN + and initiator SCSI ID in the same direction. */ + + case 0x1a: /* Invalid CCB or Segment List Parameter-A segment list with a zero + length segment or invalid segment list boundaries was received. + A CCB parameter was invalid. */ + hosterr = DID_ERROR; /* Couldn't find any better */ + break; + + case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus + phase sequence was requested by the target. The host adapter + will generate a SCSI Reset Condition, notifying the host with + a SCRD interrupt */ + hosterr = DID_RESET; + break; + default: + printk("makecode: unknown hoststatus %x\n", hosterr); + break; + } + return scsierr|(hosterr << 16); +} + +int aha1542_test_port(void) +{ + volatile int debug = 0; + + /* Reset the adapter. I ought to make a hard reset, but it's not really nessesary */ + + /* DEB(printk("aha1542_test_port called \n")); */ + + outb(SRST|IRST/*|SCRST*/, CONTROL); + + debug = 1; + /* Expect INIT and IDLE, any of the others are bad */ + WAIT(STATUS, STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF); + + debug = 2; + /* Shouldn't have generated any interrupts during reset */ + if (inb(INTRFLAGS)&INTRMASK) goto fail; + + debug = 3; + /* Test the basic ECHO command */ + outb(CMD_ECHO, DATA); + + debug = 4; + /* Wait for CDF=0. If any of the others are set, it's bad */ + WAIT(STATUS, STATMASK, 0, STST|DIAGF|INVDCMD|DF|CDF); + + debug = 5; + /* The meaning of life */ + outb(42, DATA); + + debug = 6; + /* Expect only DF, that is, data ready */ + WAIT(STATUS, STATMASK, DF, STST|DIAGF|CDF|INVDCMD); + + debug = 7; + /* Is the answer correct? */ + if (inb(DATA) != 42) goto fail; + + debug = 8; + /* Reading port should reset DF */ + if (inb(STATUS) & DF) goto fail; + + debug = 9; + /* When HACC, command is completed, and we're though testing */ + WAIT(INTRFLAGS, HACC, HACC, 0); + /* now initialize adapter */ + + debug = 10; + /* Clear interrupts */ + outb(IRST, CONTROL); + + debug = 11; + + return debug; /* 1 = ok */ + fail: + return 0; /* 0 = not ok */ +} + +/* What's this little function for? */ +char *aha1542_info(void) +{ + static char buffer[] = "Adaptec 1542"; + return buffer; +} + +/* A "high" level interrupt handler */ +void aha1542_intr_handle(void) +{ + void (*my_done)(int, int) = do_done; + int errstatus; + + do_done = NULL; +#ifdef DEBUG + printk("aha1542_intr_handle: "); + if (!(flag&ANYINTR)) printk("no interrupt?"); + if (flag&MBIF) printk("MBIF "); + if (flag&MBOA) printk("MBOF "); + if (flag&HACC) printk("HACC "); + if (flag&SCRD) printk("SCRD "); + printk("status %02x\n", inb(STATUS)); + if (ccb.tarstat|ccb.hastat) + printk("aha1542_command: returning %x (status %d)\n", ccb.tarstat + ((int) ccb.hastat << 16), mb[1].status); +#endif + aha1542_intr_reset(); + if (!my_done) { + printk("aha1542_intr_handle: Unexpected interrupt\n"); + return; + } + + /* is there mail :-) */ + + if (!mb[1].status) { + DEB(printk("aha1542_intr_handle: strange: mbif but no mail!\n")); + my_done(aha1542_host, DID_TIME_OUT << 16); + return; + } + + /* more error checking left out here */ + if (mb[1].status != 1) + /* This is surely wrong, but I don't know what's right */ + errstatus = makecode(ccb.hastat, ccb.tarstat); + else + errstatus = 0; + + mb[1].status = 0; + + if (ccb.tarstat == 2) { + int i; + DEB(printk("aha1542_intr_handle: sense:")); + for (i = 0; i < 12; i++) + printk("%02x ", ccb.cdb[ccb.cdblen+i]); + printk("\n"); +/* + DEB(printk("aha1542_intr_handle: buf:")); + for (i = 0; i < bufflen; i++) + printk("%02x ", ((unchar *)buff)[i]); + printk("\n"); +*/ + } + DEB(if (errstatus) printk("aha1542_intr_handle: returning %6x\n", errstatus)); + my_done(aha1542_host, errstatus); + return; +} + +int aha1542_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, void (*done)(int, int)) +{ + unchar ahacmd = CMD_START_SCSI; + unchar *cmd = (unchar *) cmnd; + DEB(int i); + + DEB(if (target > 1) {done(aha1542_host, DID_TIME_OUT << 16); return 0;}); + +#ifdef DEBUG + if (*cmd == READ_10 || *cmd == WRITE_10) + i = xscsi2int(cmd+2); + else if (*cmd == READ_6 || *cmd == WRITE_6) + i = scsi2int(cmd+2); + else + i = -1; + if (done) + printk("aha1542_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen); + else + printk("aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen); + aha1542_stat(); + printk("aha1542_queuecommand: dumping scsi cmd:"); + for (i = 0; i < (*cmd<=0x1f?6:10); i++) printk("%02x ", cmd[i]); + printk("\n"); + if (*cmd == WRITE_10 || *cmd == WRITE_6) + return 0; /* we are still testing, so *don't* write */ +#endif + memset(&ccb, 0, sizeof ccb); + + ccb.cdblen = (*cmd<=0x1f)?6:10; /* SCSI Command Descriptor Block Length */ + + memcpy(ccb.cdb, cmd, ccb.cdblen); + ccb.op = 0; /* SCSI Initiator Command */ + ccb.idlun = (target&7)<<5; /* SCSI Target Id */ + ccb.rsalen = 12; + any2scsi(ccb.datalen, bufflen); + any2scsi(ccb.dataptr, buff); + ccb.linkptr[0] = ccb.linkptr[1] = ccb.linkptr[2] = 0; + ccb.commlinkid = 0; + + mb[0].status = 1; + mb[1].status = 0; + +#ifdef DEBUGd + printk("aha1542_command: sending.. "); + for (i = 0; i < sizeof(ccb)-10; i++) + printk("%02x ", ((unchar *)&ccb)[i]); +#endif + + if (done) { + DEB(printk("aha1542_queuecommand: now waiting for interrupt "); aha1542_stat()); + if (do_done) + printk("aha1542_queuecommand: Two concurrent queuecommand?\n"); + else + do_done = done; + aha1542_out(&ahacmd, 1); /* start scsi command */ + DEB(aha1542_stat()); + aha1542_enable_intr(); + } + else + printk("aha1542_queuecommand: done can't be NULL\n"); + + return 0; +} + +volatile static int internal_done_flag = 0; +volatile static int internal_done_errcode = 0; +static void internal_done(int host, int errcode) +{ + internal_done_errcode = errcode; + ++internal_done_flag; +} + +int aha1542_command(unchar target, const void *cmnd, void *buff, int bufflen) +{ + DEB(printk("aha1542_command: ..calling aha1542_queuecommand\n")); + aha1542_queuecommand(target, cmnd, buff, bufflen, internal_done); + + while (!internal_done_flag); + internal_done_flag = 0; + return internal_done_errcode; +} + +/* Initialize mailboxes */ +static void setup_mailboxes() +{ + static unchar cmd[5] = {CMD_MBINIT, 1}; + + mb[0].status = mb[1].status = 0; + aha1542_intr_reset(); /* reset interrupts, so they don't block */ + any2scsi((cmd+2), mb); + any2scsi(mb[0].ccbptr, &ccb); + aha1542_out(cmd, 5); + WAIT(INTRFLAGS, INTRMASK, HACC, 0); + while (0) { + fail: + printk("aha1542_detect: failed setting up mailboxes\n"); + } + aha1542_intr_reset(); +} + +/* a hack to avoid a strange compilation error */ + +void call_buh() +{ + set_intr_gate(0x2b,&aha1542_interrupt); +} + +/* return non-zero on detection */ +int aha1542_detect(int hostnum) +{ + int i; + + DEB(printk("aha1542_detect: \n")); + + if (!(i = aha1542_test_port())) { + return 0; + } + + /* Set the Bus on/off-times as not to ruin floppy performens */ + { + static unchar oncmd[] = {CMD_BUSON_TIME, 5}; + static unchar offcmd[] = {CMD_BUSOFF_TIME, 9}; + + aha1542_intr_reset(); + aha1542_out(oncmd, 2); + WAIT(INTRFLAGS, INTRMASK, HACC, 0); + aha1542_intr_reset(); + aha1542_out(offcmd, 2); + WAIT(INTRFLAGS, INTRMASK, HACC, 0); + while (0) { + fail: + printk("aha1542_detect: setting bus on/off-time failed\n"); + } + aha1542_intr_reset(); + } + + aha1542_stat(); + setup_mailboxes(); + + aha1542_stat(); + + DEB(printk("aha1542_detect: enable interrupt channel %d\n", intr_chan)); + call_buh(); + + if (intr_chan >= 8) + outb(inb_p(0x21)&0xfb,0x21); /* open for slave ?? */ + + DEB(printk("aha1542_detect: enabling interrupts\n")); + aha1542_enable_intr(); + +#ifdef DEBUG + DEB(printk(" *** READ CAPACITY ***\n")); + + { + unchar buf[8]; + static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int i; + + for (i = 0; i < sizeof(buf); ++i) buf[i] = 0x87; + for (i = 0; i < 2; ++i) + if (!aha1542_command(i, cmd, buf, sizeof(buf))) { + printk("aha_detect: LU %d sector_size %d device_size %d\n", + i, xscsi2int(buf+4), xscsi2int(buf)); + } + } + + DEB(printk(" *** NOW RUNNING MY OWN TEST *** \n")); + + for (i = 0; i < 4; ++i) + { + unsigned char cmd[10]; + static buffer[512]; + + cmd[0] = READ_10; + cmd[1] = 0; + xany2scsi(cmd+2, i); + cmd[6] = 0; + cmd[7] = 0; + cmd[8] = 1; + cmd[9] = 0; + aha1542_command(0, cmd, buffer, 512); + } +#endif + aha1542_host = hostnum; + return 1; +} + +int aha1542_abort(int i) +{ + DEB(printk("aha1542_abort\n")); + return 0; +} + +int aha1542_reset(void) +{ + DEB(printk("aha1542_reset called\n")); + return 0; +} + +__asm__(" +_aha1542_interrupt: + cld + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + movb $0x20,%al + outb %al,$0xA0 # EOI to interrupt controller #1 + jmp 1f # give port chance to breathe +1: jmp 1f +1: outb %al,$0x20 +# Please, someone, change this to use the timer +# andl $0xfffeffff,_timer_active + movl $_aha1542_intr_handle,%edx + call *%edx # ``interesting'' way of handling intr. + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret +"); diff --git a/kernel/blk_drv/scsi/aha1542.h b/kernel/blk_drv/scsi/aha1542.h new file mode 100644 index 000000000000..5d3640fb5e86 --- /dev/null +++ b/kernel/blk_drv/scsi/aha1542.h @@ -0,0 +1,139 @@ +#ifndef _AHA1542_H + +/* $Id: aha1542.h,v 1.1 1992/07/24 06:27:38 root Exp root $ + * + * Header file for the adaptec 1542 driver for Linux + * + * $Log: aha1542.h,v $ + * Revision 1.1 1992/07/24 06:27:38 root + * Initial revision + * + * Revision 1.2 1992/07/04 18:41:49 root + * Replaced distribution with current drivers + * + * Revision 1.3 1992/06/23 23:58:20 root + * Fixes. + * + * Revision 1.2 1992/05/26 22:13:23 root + * Changed bug that prevented DMA above first 2 mbytes. + * + * Revision 1.1 1992/05/22 21:00:29 root + * Initial revision + * + * Revision 1.1 1992/04/24 18:01:50 root + * Initial revision + * + * Revision 1.1 1992/04/02 03:23:13 drew + * Initial revision + * + * Revision 1.3 1992/01/27 14:46:29 tthorn + * *** empty log message *** + * + */ + +#include + +/* I/O Port interface 4.2 */ +/* READ */ +#define STATUS base +#define STST 0x80 /* Self Test in Progress */ +#define DIAGF 0x40 /* Internal Diagonostic Failure */ +#define INIT 0x20 /* Mailbox Initialization Required */ +#define IDLE 0x10 /* SCSI Host Adapter Idle */ +#define CDF 0x08 /* Command/Data Out Port Full */ +#define DF 0x04 /* Data In Port Full */ +#define INVDCMD 0x01 /* Invalid H A Command */ +#define STATMASK 0xfd /* 0x02 is reserved */ + +#define INTRFLAGS STATUS+2 +#define ANYINTR 0x80 /* Any Interrupt */ +#define SCRD 0x08 /* SCSI Reset Detected */ +#define HACC 0x04 /* HA Command Complete */ +#define MBOA 0x02 /* MBO Empty */ +#define MBIF 0x01 /* MBI Full */ +#define INTRMASK 0x8f + +/* WRITE */ +#define CONTROL STATUS +#define HRST 0x80 /* Hard Reset */ +#define SRST 0x40 /* Soft Reset */ +#define IRST 0x20 /* Interrupt Reset */ +#define SCRST 0x10 /* SCSI Bus Reset */ + +/* READ/WRITE */ +#define DATA STATUS+1 +#define CMD_NOP 0x00 /* No Operation */ +#define CMD_MBINIT 0x01 /* Mailbox Initialization */ +#define CMD_START_SCSI 0x02 /* Start SCSI Command */ +#define CMD_INQUIRY 0x04 /* Adapter Inquiry */ +#define CMD_EMBOI 0x05 /* Enable MailBox Out Interrupt */ +#define CMD_BUSON_TIME 0x07 /* Set Bus-On Time */ +#define CMD_BUSOFF_TIME 0x08 /* Set Bus-Off Time */ +#define CMD_RETDEVS 0x0a /* Return Installed Devices */ +#define CMD_RETCONF 0x0b /* Return Configuration Data */ +#define CMD_RETSETUP 0x0d /* Return Setup Data */ +#define CMD_ECHO 0x1f /* ECHO Command Data */ + +/* Mailbox Definition 5.2.1 and 5.2.2 */ +struct mailbox { + unchar status; /* Command/Status */ + unchar ccbptr[3]; /* msb, .., lsb */ +}; + +/* These belong in scsi.h also */ +#define any2scsi(up, p) \ +(up)[0] = (((unsigned long)(p)) >> 16) ; \ +(up)[1] = (((unsigned long)(p)) >> 8); \ +(up)[2] = ((unsigned long)(p)); + +#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) ) + +#define xany2scsi(up, p) \ +(up)[0] = ((long)(p)) >> 24; \ +(up)[1] = ((long)(p)) >> 16; \ +(up)[2] = ((long)(p)) >> 8; \ +(up)[3] = ((long)(p)); + +#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \ + + (((long)(up)[2]) << 8) + ((long)(up)[3]) ) + +#define MAX_CDB 12 +#define MAX_SENSE 14 + +struct ccb { /* Command Control Block 5.3 */ + unchar op; /* Command Control Block Operation Code */ + unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ + /* Outbound data transfer, length is checked*/ + /* Inbound data transfer, length is checked */ + /* Logical Unit Number */ + unchar cdblen; /* SCSI Command Length */ + unchar rsalen; /* Request Sense Allocation Length/Disable */ + unchar datalen[3]; /* Data Length (msb, .., lsb) */ + unchar dataptr[3]; /* Data Pointer */ + unchar linkptr[3]; /* Link Pointer */ + unchar commlinkid; /* Command Linking Identifier */ + unchar hastat; /* Host Adapter Status (HASTAT) */ + unchar tarstat; /* Target Device Status */ + unchar reserved[2]; + unchar cdb[MAX_CDB+MAX_SENSE];/* SCSI Command Descriptor Block */ + /* REQUEST SENSE */ +}; + +int aha1542_detect(int); +int aha1542_command(unsigned char target, const void *cmnd, void *buff, int bufflen); +int aha1542_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, void (*done)(int, int)); +int aha1542_abort(int); +char *aha1542_info(void); +int aha1542_reset(void); + +#ifndef NULL + #define NULL 0 +#endif + +#define AHA1542 {"Adaptec 1542", aha1542_detect, \ + aha1542_info, aha1542_command, \ + aha1542_queuecommand, \ + aha1542_abort, \ + aha1542_reset, \ + 1, 7, 0} +#endif diff --git a/kernel/blk_drv/scsi/config.in b/kernel/blk_drv/scsi/config.in new file mode 100644 index 000000000000..75c1fde7e681 --- /dev/null +++ b/kernel/blk_drv/scsi/config.in @@ -0,0 +1,29 @@ +#include + +#ifdef CONFIG_SCSI +scsi.o +hosts.o +scsi_ioctl.o +#endif + +#ifdef CONFIG_BLK_DEV_SD +sd.o +sd_ioctl.o +#endif + +#ifdef CONFIG_BLK_DEV_ST +st.o +st_ioctl.o +#endif + +#ifdef CONFIG_SCSI_AHA1542 +aha1542.o +#endif + +#ifdef CONFIG_SCSI_SEAGATE +seagate.o +#endif + +#ifdef CONFIG_SCSI_ULTRASTOR +ultrastor.o +#endif diff --git a/kernel/blk_drv/scsi/config.out b/kernel/blk_drv/scsi/config.out new file mode 100644 index 000000000000..83ce57d113ee --- /dev/null +++ b/kernel/blk_drv/scsi/config.out @@ -0,0 +1,10 @@ +scsi.o +hosts.o +scsi_ioctl.o +sd.o +sd_ioctl.o +st.o +st_ioctl.o +aha1542.o +seagate.o +ultrastor.o diff --git a/kernel/blk_drv/scsi/fdomain.c b/kernel/blk_drv/scsi/fdomain.c new file mode 100644 index 000000000000..ae05651e0c2e --- /dev/null +++ b/kernel/blk_drv/scsi/fdomain.c @@ -0,0 +1,1234 @@ +/* fdomain.c -- Future Domain TMC-1660/TMC-1680 driver + * Created: Sun May 3 18:53:19 1992 + * Revised: Tue Jul 28 19:45:25 1992 by root + * Author: Rickard E. Faith, faith@cs.unc.edu + * Copyright 1992 Rickard E. Faith + * + * $Log$ + + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + + * 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. + + * WARNING: THIS IS A BETA VERSION! + * USE AT YOUR OWN RISK! + * BACKUP YOUR SYSTEM BEFORE USING! + + * I would like to thank Maxtor, whose *free* 206 page manual on the LXT + * drives was very helpful: "LXT SCSI Products: Specifications and OEM + * Technical Manual (Revision B/September 1991)" + + * I wish that I could thank Future Domain for the necessary documentation, + * but I can't. I used the $25 "TMC-1800 SCSI Chip Specification" document + * (FDC-1800T), which documents the *chip* and not the board. Without it, + * I would have been totally lost, but it would have been nice to have some + * example source. (The DOS BIOS source cost $250 and the UN*X driver + * source was $750 [both required a non-disclosure agreement]. Ever wonder + * why there are no freely available Future Domain drivers?) + + * Thanks to Todd Carrico (todd@wutc.wustl.edu), Dan Poirier + * (poirier@cs.unc.edu ), Ken Corey (kenc@sol.acs.unt.edu), and C. de Bruin + * (bruin@dutiba.tudelft.nl) for alpha testing. Also thanks to Drew + * Eckhardt (drew@cs.colorado.edu) for answering questions. */ + +#include + +#ifdef CONFIG_SCSI_FUTURE_DOMAIN + +#include +#include +#include "fdomain.h" +#include "scsi.h" +#include "hosts.h" +#if QUEUE +#include +#include +#endif + +#define VERSION "1.9" /* Change with each revision */ +#define DEBUG 1 /* Enable debugging output */ +#define SEND_IDENTIFY 0 /* Send IDENTIFY message -- DOESN'T WORK! */ +#define USE_FIFO 1 /* Use the FIFO buffer for I/O */ +#define FAST_SYNCH 1 /* Enable Fast Synchronous */ +#define ALLOW_ALL_IRQ 0 /* Allow all IRQ's -- NOT RECOMMENDED */ +#define NEW_IRQ 1 /* Enable new IRQ handling */ +#define DECREASE_IL 1 /* Try to decrease interrupt latency */ + +#if DEBUG +#define EVERY_ACCESS 0 /* Write a line on every scsi access */ +#define ERRORS_ONLY 1 /* Only write a line if there is an error */ +#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */ +#else +#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */ +#define ERRORS_ONLY 0 +#define DEBUG_DETECT 0 +#endif + +/* Errors are reported on the line, so we don't need to report them again */ +#if EVERY_ACCESS +#undef ERRORS_ONLY +#define ERRORS_ONLY 0 +#endif + +static int port_base = 0; +static void *bios_base = NULL; +static int interrupt_level = 0; +static volatile int aborted = 0; + +static int Data_Mode_Cntl_port; +static int FIFO_Data_Count_port; +static int Interrupt_Cntl_port; +static int Read_FIFO_port; +static int Read_SCSI_Data_port; +static int SCSI_Cntl_port; +static int SCSI_Status_port; +static int TMC_Cntl_port; +static int TMC_Status_port; +static int Write_FIFO_port; +static int Write_SCSI_Data_port; + +#if QUEUE +static unsigned char current_target = 0; +static unsigned char current_cmnd[10] = { 0, }; +static void *current_buff = NULL; +static int current_bufflen = 0; +static void (*current_done)(int,int) = NULL; + +volatile static int in_command = 0; +volatile static int current_phase; +static int this_host = 0; + +enum { in_arbitration, in_selection, in_other }; + +#if NEW_IRQ +extern void fdomain_16x0_intr( int unused ); +#else +extern void fdomain_16x0_interrupt(); +#endif + +static const char *cmd_pt; +static const char *the_command; +static unsigned char *out_buf_pt; +static unsigned char *in_buf_pt; +volatile static int Status; +volatile static int Message; +volatile static unsigned data_sent; +volatile static int have_data_in; + +volatile static int in_interrupt_code = 0; + +#endif + + +enum in_port_type { Read_SCSI_Data = 0, SCSI_Status = 1, TMC_Status = 2, + LSB_ID_Code = 5, MSB_ID_Code = 6, Read_Loopback = 7, + SCSI_Data_NoACK = 8, Option_Select = 10, + Read_FIFO = 12, FIFO_Data_Count = 14 }; + +enum out_port_type { Write_SCSI_Data = 0, SCSI_Cntl = 1, Interrupt_Cntl = 2, + Data_Mode_Cntl = 3, TMC_Cntl = 4, Write_Loopback = 7, + Write_FIFO = 12 }; + +static void *addresses[] = { + (void *)0xc8000, + (void *)0xca000, + (void *)0xce000, + (void *)0xde000 }; +#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned )) + +static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 }; +#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short )) + +static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 }; + +/* + + READ THIS BEFORE YOU ADD A SIGNATURE! + + READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME! + + READ EVERY WORD, ESPECIALLY THE WORD *NOT* + + This driver works *ONLY* for Future Domain cards using the + TMC-1600 chip. This includes models TMC-1660 and TMC-1680 + *ONLY*. + + The following is a BIOS signature for a TMC-950 board, which + looks like it is a 16 bit board (based on card edge), but + which only uses the extra lines for IRQ's (not for data): + + FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90 + + THIS WILL *NOT* WORK WITH THIS DRIVER! + + Here is another BIOS signature for yet another Future + Domain board WHICH WILL *NOT* WORK WITH THIS DRIVER: + + FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90 + + Here is another BIOS signature for the TMC-88x series: + + FUTURE DOMAIN COPR. (C) 1986-1989 V6.0A7/28/90 + + THIS WILL *NOT* WORK WITH THIS DRIVER, but it *WILL* + work with the *SEAGATE* ST-01/ST-02 driver. + + */ + +struct signature { + char *signature; + int sig_offset; + int sig_length; +} signatures[] = { + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0 7/28/89", 5, 50 }, + { "FUTURE DOMAIN CORP. (C) 1986-1990 1800", 5, 37 }, + /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGANTURE */ +}; + +#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature )) + + +/* These functions are based on include/asm/io.h */ + +#if 1 +static unsigned short inline inw( unsigned short port ) +{ + unsigned short _v; + + __asm__ volatile ("inw %1,%0" + :"=a" (_v):"d" ((unsigned short) port)); + return _v; +} + +static void inline outw( unsigned short value, unsigned short port ) +{ + __asm__ volatile ("outw %0,%1" + ::"a" ((unsigned short) value), + "d" ((unsigned short) port)); +} +#else + +#define inw( port ) \ + ({ unsigned short _v; \ + __asm__ volatile ("inw %1,%0" \ + : "=a" (_v) : "d" ((unsigned short) port)); \ + _v; }) + +#define outw( value ) \ + __asm__ volatile \ + ("outw %0,%1" : : "a" ((unsigned short) value), \ + "d" ((unsigned short) port)) +#endif + + +/* These defines are copied from kernel/blk_drv/hd.c */ + +#define insw( buf, count, port ) \ + __asm__ volatile \ + ( "cld;rep;insw"::"d" (port),"D" (buf),"c" (count):"cx","di" ) + +#define outsw( buf, count, port) \ + __asm__ volatile \ + ("cld;rep;outsw"::"d" (port),"S" (buf),"c" (count):"cx","si") + + +static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */ +{ + unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */ + + while (jiffies < the_time); +} + +static void inline fdomain_make_bus_idle( void ) +{ + outb( 0, SCSI_Cntl_port ); + outb( 0, Data_Mode_Cntl_port ); + outb( 1, TMC_Cntl_port ); +} + +static int fdomain_is_valid_port( int port ) +{ + int options; + +#if DEBUG_DETECT + printk( " (%x%x),", + inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) ); +#endif + + /* The MCA ID is a unique id for each MCA compatible board. We + are using ISA boards, but Future Domain provides the MCA ID + anyway. We can use this ID to ensure that this is a Future + Domain TMC-1660/TMC-1680. + */ + + if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */ + if (inb( port + LSB_ID_Code ) != 0x27) return 0; + if (inb( port + MSB_ID_Code ) != 0x61) return 0; + } else { /* test for 0xe960 id */ + if (inb( port + MSB_ID_Code ) != 0x60) return 0; + } + + /* We have a valid MCA ID for a TMC-1660/TMC-1680 Future Domain board. + Now, check to be sure the bios_base matches these ports. + If someone was unlucky enough to have purchased more than one + Future Domain board, then they will have to modify this code, as + we only detect one board here. [The one with the lowest bios_base]. + */ + + options = inb( port + Option_Select ); + +#if DEBUG_DETECT + printk( " Options = %x,", options ); +#endif + + if (addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0; + interrupt_level = ints[ (options & 0x0e) >> 1 ]; + + return 1; +} + +static int fdomain_test_loopback( void ) +{ + int i; + int result; + + for (i = 0; i < 255; i++) { + outb( i, port_base + Write_Loopback ); + result = inb( port_base + Read_Loopback ); + if (i != result) return 1; + } + return 0; +} + +#if !NEW_IRQ +static void fdomain_enable_interrupt( void ) +{ + if (!interrupt_level) return; + +#if ALLOW_ALL_IRQ + if (interrupt_level < 8) { + outb( inb_p( 0x21 ) & ~(1 << interrupt_level), 0x21 ); + } else +#endif + { + outb( inb_p( 0xa1 ) & ~(1 << (interrupt_level - 8)), 0xa1 ); + } +} + +static void fdomain_disable_interrupt( void ) +{ + if (!interrupt_level) return; + +#if ALLOW_ALL_IRQ + if (interrupt_level < 8) { + outb( inb_p( 0x21 ) | (1 << interrupt_level), 0x21 ); + } else +#endif + { + outb( inb_p( 0xa1 ) | (1 << (interrupt_level - 8)), 0xa1 ); + } +} +#endif + +int fdomain_16x0_detect( int hostnum ) +{ + int i, j; + int flag; + unsigned char do_inquiry[] = { 0x12, 0, 0, 0, 255, 0 }; + unsigned char do_request_sense[] = { 0x03, 0, 0, 0, 255, 0 }; + unsigned char do_read_capacity[] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned char buf[256]; + unsigned retcode; + +#if DEBUG_DETECT + printk( "SCSI: fdomain_16x0_detect()," ); +#endif + + for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) { +#if DEBUG_DETECT + printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base ); +#endif + for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) { + if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset), + signatures[j].signature, signatures[j].sig_length )) { + bios_base = addresses[i]; + } + } + } + + if (!bios_base) { +#if DEBUG_DETECT + printk( " FAILED: NO BIOS\n" ); +#endif + return 0; + } + + /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM. + Assuming the ROM is enabled (otherwise we wouldn't have been + able to read the ROM signature :-), then the ROM set up the + RAM area with some magic numbers, such as a list of port + base addresses and a list of the disk "geometry" reported to + DOS (this geometry has nothing to do with physical geometry). + */ + + port_base = *((char *)bios_base + 0x1fcc) + + (*((char *)bios_base + 0x1fcd) << 8); + +#if DEBUG_DETECT + printk( " %x,", port_base ); +#endif + + for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) { + if (port_base == ports[i]) ++flag; + } + + if (flag) flag = fdomain_is_valid_port( port_base ); + + if (!flag) { /* Cannot get port base from BIOS RAM */ + + /* This is a bad sign. It usually means that someone patched the + BIOS signature list (the signatures variable) to contain a BIOS + signature for a board *OTHER THAN* the TMC-1660/TMC-1680. + */ + +#if DEBUG_DETECT + printk( " RAM FAILED, " ); +#endif + /* Anyway, the alternative to finding the address in the RAM is + to just search through every possible port address for one + that is attached to the Future Domain card. Don't panic, + though, about reading all these random port addresses--there + are rumors that the Future Domain BIOS does something very + similar. + */ + + for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) { + port_base = ports[i]; +#if DEBUG_DETECT + printk( " %x,", port_base ); +#endif + flag = fdomain_is_valid_port( port_base ); + } + } + + if (!flag) { +#if DEBUG_DETECT + printk( " FAILED: NO PORT\n" ); +#endif + return 0; /* Cannot find valid set of ports */ + } + +#if DEBUG_DETECT + printk( "\n" ); + printk( "SCSI: bios_base = %x, port_base = %x, interrupt_level = %d\n", + (unsigned)bios_base, port_base, interrupt_level ); +#endif + + if (interrupt_level) { + printk( "Future Domain BIOS at %x; port base at %x; using IRQ %d\n", + (unsigned)bios_base, port_base, interrupt_level ); + } else { + printk( "Future Domain BIOS at %x; port base at %x; *NO* IRQ\n", + (unsigned)bios_base, port_base ); + } + + Data_Mode_Cntl_port = port_base + Data_Mode_Cntl; + FIFO_Data_Count_port = port_base + FIFO_Data_Count; + Interrupt_Cntl_port = port_base + Interrupt_Cntl; + Read_FIFO_port = port_base + Read_FIFO; + Read_SCSI_Data_port = port_base + Read_SCSI_Data; + SCSI_Cntl_port = port_base + SCSI_Cntl; + SCSI_Status_port = port_base + SCSI_Status; + TMC_Cntl_port = port_base + TMC_Cntl; + TMC_Status_port = port_base + TMC_Status; + Write_FIFO_port = port_base + Write_FIFO; + Write_SCSI_Data_port = port_base + Write_SCSI_Data; + + fdomain_16x0_reset(); + + if (fdomain_test_loopback()) { +#if DEBUG_DETECT + printk( "SCSI: LOOPBACK TEST FAILED, FAILING DETECT!\n" ); +#endif + return 0; + } + + /* These routines are here because of the way the SCSI bus behaves + after a reset. This appropriate behavior was not handled correctly + by the higher level SCSI routines when I first wrote this driver. + */ + + printk( "Future Domain detection routine scanning for devices:\n" ); + for (i = 0; i < 8; i++) { + if (i == 6) continue; /* The host adapter is at SCSI ID 6 */ + retcode = fdomain_16x0_command( i, do_request_sense, buf, 255 ); + if (!retcode) { + retcode = fdomain_16x0_command( i, do_inquiry, buf, 255 ); + if (!retcode) { + printk( " SCSI ID %d: ", i ); + for (j = 8; j < 32; j++) printk( "%c", buf[j] ); + retcode = fdomain_16x0_command( i, do_read_capacity, buf, 255 ); + if (!retcode) { + unsigned long blocks, size, capacity; + + blocks = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; + size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; + capacity = +(blocks * size * 10) / +(1024L * 1024L); + + printk( "%lu MB (%lu byte blocks)", + ((capacity + 5L) / 10L), size ); + } + printk ("\n" ); + } + } + } + +#if QUEUE +#if !ALLOW_ALL_IRQ + if (interrupt_level < 8) { + printk( "Future Domain: WILL NOT USE IRQ LESS THAN 8 FOR QUEUEING!\n" ); + scsi_hosts[hostnum].can_queue = 0; + } else +#endif +#if NEW_IRQ + { + int retcode; + struct sigaction sa; + + this_host = hostnum; + + sa.sa_handler = fdomain_16x0_intr; + sa.sa_flags = SA_INTERRUPT; + sa.sa_mask = 0; + sa.sa_restorer = NULL; + + retcode = irqaction( interrupt_level, &sa ); + + if (retcode < 0) { + if (retcode == -EINVAL) { + printk( "Future Domain: IRQ %d is bad!\n", interrupt_level ); + printk( " This shouldn't happen: REPORT TO RIK!\n" ); + } else if (retcode == -EBUSY) { + printk( "Future Domain: IRQ %d is already in use!\n", + interrupt_level ); + printk( " Please use another IRQ for the FD card!\n" ); + } else { + printk( "Future Domain: Error getting IRQ %d\n", + interrupt_level ); + printk( " This shouldn't happen: REPORT TO RIK!\n" ); + } + printk( " IRQs WILL NOT BE USED!\n" ); + + scsi_hosts[this_host].can_queue = 0; + } else { + printk( "Future Domain: IRQ %d selected with retcode = %d\n", + interrupt_level, retcode ); + } + } +#else + { + this_host = hostnum; + set_intr_gate( 0x20 + interrupt_level, &fdomain_16x0_interrupt ); + fdomain_enable_interrupt(); + } +#endif +#endif + + return 1; +} + +char *fdomain_16x0_info(void) +{ + static char buffer[] = + "Future Domain TMC-1660/TMC-1680 SCSI driver version " + VERSION + "\n"; + return buffer; +} + +static int fdomain_arbitrate( void ) +{ + int status = 0; + unsigned long timeout; + +#if VERBOSE + printk( "SCSI: fdomain_arbitrate()\n" ); +#endif + + outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */ + outb( 0x40, port_base + SCSI_Data_NoACK ); /* Set our id bit */ + outb( 0x04, TMC_Cntl_port ); /* Start arbitration */ + + timeout = jiffies + 50; /* 500 mS */ + while (jiffies < timeout) { + status = inb( TMC_Status_port ); /* Read adapter status */ + if (status & 0x02) return 0; /* Arbitration complete */ + } + + /* Make bus idle */ + fdomain_make_bus_idle(); + +#if EVERY_ACCESS + printk( "Arbitration failed, status = %x\n", status ); +#endif +#if ERRORS_ONLY + printk( "SCSI: Arbitration failed, status = %x", status ); +#endif + return 1; +} + +static int fdomain_select( int target ) +{ + int status; + unsigned long timeout; + + outb( 0x80, SCSI_Cntl_port ); /* Bus Enable */ + outb( 0x8a, SCSI_Cntl_port ); /* Bus Enable + Attention + Select */ + + /* Send our address OR'd with target address */ +#if SEND_IDENTIFY + outb( 0x40 | (1 << target), port_base + SCSI_Data_NoACK ); +#else + outb( (1 << target), port_base + SCSI_Data_NoACK ); +#endif + + /* Stop arbitration (also set FIFO for output and enable parity) */ + outb( 0xc8, TMC_Cntl_port ); + + timeout = jiffies + 25; /* 250mS */ + while (jiffies < timeout) { + status = inb( SCSI_Status_port ); /* Read adapter status */ + if (status & 1) { /* Busy asserted */ + /* Enable SCSI Bus (on error, should make bus idle with 0) */ +#if SEND_IDENTIFY + /* Also, set ATN so that the drive will make a MESSAGE OUT phase */ + outb( 0x88, SCSI_Cntl_port ); +#else + outb( 0x80, SCSI_Cntl_port ); +#endif + return 0; + } + } + /* Make bus idle */ + fdomain_make_bus_idle(); +#if EVERY_ACCESS + if (!target) printk( "Select failed\n" ); +#endif +#if ERRORS_ONLY + if (!target) printk( "SCSI: Select failed" ); +#endif + return 1; +} + +#if QUEUE + +#if !USE_FIFO +#pragma error QUEUE requires USE_FIFO +#endif + +void my_done( int error ) +{ + if (in_command) { + in_command = 0; + in_interrupt_code = 0; + outb( 0x00, Interrupt_Cntl_port ); + fdomain_make_bus_idle(); + if (current_done) current_done( this_host, error ); + else panic( "SCSI (Future Domain): current_done() == NULL" ); + } else { + panic( "SCSI (Future Domain): my_done() called outside of command\n" ); + } +} + +#if NEW_IRQ +void fdomain_16x0_intr( int unused ) +#else +void fdomain_16x0_intr( void ) +#endif +{ + int status; + int done = 0; + unsigned data_count; + +#if NEW_IRQ + sti(); +#endif + + if (in_interrupt_code) + panic( "SCSI (Future Domain): fdomain_16x0_intr() NOT REENTRANT!\n" ); + else + ++in_interrupt_code; + + outb( 0x00, Interrupt_Cntl_port ); + +#if EVERY_ACCESS + printk( "aborted = %d, ", aborted ); +#endif + + if (aborted) { + /* Force retry for timeouts after selection complete */ + if (current_phase == in_other) + my_done( DID_BUS_BUSY << 16 ); + else + my_done( aborted << 16 ); +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; + } + + /* We usually have one spurious interrupt after each command. Ignore it. */ + if (!in_command) { /* Spurious interrupt */ + in_interrupt_code = 0; +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; + } + + if (current_phase == in_arbitration) { + status = inb( TMC_Status_port ); /* Read adapter status */ + if (!(status & 0x02)) { +#if EVERY_ACCESS + printk( " AFAIL " ); +#endif + my_done( DID_TIME_OUT << 16 ); +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; + } + current_phase = in_selection; + + outb( 0x80, SCSI_Cntl_port ); /* Bus Enable */ + outb( 0x8a, SCSI_Cntl_port ); /* Bus Enable + Attention + Select */ + + outb( (1 << current_target), port_base + SCSI_Data_NoACK ); + + outb( 0x40, Interrupt_Cntl_port ); + /* Stop arbitration (also set FIFO for output and enable parity) */ + in_interrupt_code = 0; + outb( 0xd8, TMC_Cntl_port ); +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; + } else if (current_phase == in_selection) { + status = inb( SCSI_Status_port ); + if (!(status & 0x01)) { +#if EVERY_ACCESS + printk( " SFAIL " ); +#endif + my_done( DID_NO_CONNECT << 16 ); +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; + } + current_phase = in_other; +#if FAST_SYNCH + outb( 0xc0, Data_Mode_Cntl_port ); +#endif + in_interrupt_code = 0; + outb( 0x90, Interrupt_Cntl_port ); + outb( 0x80, SCSI_Cntl_port ); +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; + } + + /* current_phase == in_other: this is the body of the routine */ + + switch ((unsigned char)*the_command) { + case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a: + case 0x2e: case 0x3b: case 0xea: case 0x3f: + data_count = 0x2000 - inw( FIFO_Data_Count_port ); + if (current_bufflen - data_sent < data_count) + data_count = current_bufflen - data_sent; + if (data_count > 0) { +/* if (data_count > 512) data_count = 512; */ +#if EVERY_ACCESS + printk( "%d OUT, ", data_count ); +#endif + if (data_count == 1) { + outb( *out_buf_pt++, Write_FIFO_port ); + ++data_sent; + } else { + data_count >>= 1; + outsw( out_buf_pt, data_count, Write_FIFO_port ); + out_buf_pt += 2 * data_count; + data_sent += 2 * data_count; + } + } + break; + default: + if (!have_data_in) { + outb( 0x98, TMC_Cntl_port ); + ++have_data_in; + } else { + data_count = inw( FIFO_Data_Count_port ); +/* if (data_count > 512) data_count = 512; */ + if (data_count) { +#if EVERY_ACCESS + printk( "%d IN, ", data_count ); +#endif + if (data_count == 1) { + *in_buf_pt++ = inb( Read_FIFO_port ); + } else { + data_count >>= 1; /* Number of words */ + insw( in_buf_pt, data_count, Read_FIFO_port ); + in_buf_pt += 2 * data_count; + } + } + } + break; + } + + status = inb( SCSI_Status_port ); + + if (status & 0x10) { /* REQ */ + + switch (status & 0x0e) { + case 0x08: /* COMMAND OUT */ + outb( *cmd_pt++, Write_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "CMD = %x,", (unsigned char)cmd_pt[-1] ); +#endif + break; + case 0x0c: /* STATUS IN */ + Status = inb( Read_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "Status = %x, ", Status ); +#endif +#if ERRORS_ONLY + if (Status) { + printk( "SCSI: target = %d, command = %x, Status = %x\n", + current_target, (unsigned char)*the_command, Status ); + } +#endif + break; + case 0x0a: /* MESSAGE OUT */ +#if SEND_IDENTIFY + /* On the first request, send an Identify message */ + if (!sent_identify) { + outb( 0x80, SCSI_Cntl_port ); /* Lower ATN */ + outb( 0x80, Write_SCSI_Data_port ); /* Identify */ + ++sent_identify; + } else +#else + outb( 0x07, Write_SCSI_Data_port ); /* Reject */ +#endif + break; + case 0x0e: /* MESSAGE IN */ + Message = inb( Read_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "Message = %x, ", Message ); +#endif + if (!Message) ++done; + break; + } + } + + if (done) { +#if EVERY_ACCESS + printk( " ** IN DONE ** " ); +#endif + + if (have_data_in) { + while (data_count = inw( FIFO_Data_Count_port )) { + if (data_count == 1) { + *in_buf_pt++ = inb( Read_FIFO_port ); + } else { + data_count >>= 1; /* Number of words */ + insw( in_buf_pt, data_count, Read_FIFO_port ); + in_buf_pt += 2 * data_count; + } + } + } +#if EVERY_ACCESS + printk( "AFTER DATA GET\n" ); +#endif + +#if ERRORS_ONLY + if (*the_command == REQUEST_SENSE && !Status) { + if ((unsigned char)(*((char *)current_buff + 2)) & 0x0f) { + printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n", + (unsigned char)(*((char *)current_buff + 2)) & 0x0f, + (unsigned char)(*((char *)current_buff + 12)) ); + } + } +#endif +#if EVERY_ACCESS + printk( "BEFORE MY_DONE\n" ); +#endif + my_done( (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) ); + } else { + in_interrupt_code = 0; + outb( 0x90, Interrupt_Cntl_port ); + } + +#if NEW_IRQ && !DECREASE_IL + cli(); +#endif + return; +} + +int fdomain_16x0_queue( unsigned char target, const void *cmnd, + void *buff, int bufflen, void (*done)(int,int) ) +{ + if (in_command) { + panic( "SCSI (Future Domain): fdomain_16x0_queue() NOT REENTRANT!\n" ); + } +#if EVERY_ACCESS + printk( "queue %d %x\n", target, *(unsigned char *)cmnd ); +#endif + + fdomain_make_bus_idle(); + + aborted = 0; + current_target = target; + memcpy( current_cmnd, cmnd, ((*(unsigned char *)cmnd) <= 0x1f ? 6 : 10 ) ); + current_buff = buff; + current_bufflen = bufflen; + current_done = done; + + /* Initialize static data */ + cmd_pt = current_cmnd; + the_command = current_cmnd; + out_buf_pt = current_buff; + in_buf_pt = current_buff; + + Status = 0; + Message = 0; + data_sent = 0; + have_data_in = 0; + + /* Start arbitration */ + current_phase = in_arbitration; + outb( 0x00, Interrupt_Cntl_port ); + outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */ + outb( 0x40, port_base + SCSI_Data_NoACK ); /* Set our id bit */ + ++in_command; + outb( 0x20, Interrupt_Cntl_port ); + outb( 0x1c, TMC_Cntl_port ); /* Start arbitration */ + + return 0; +} +#endif + +int fdomain_16x0_command( unsigned char target, const void *cmnd, + void *buff, int bufflen ) +{ + const char *cmd_pt = cmnd; + const char *the_command = cmnd; + unsigned char *out_buf_pt = buff; + unsigned char *in_buf_pt = buff; + int Status = 0; + int Message = 0; + int status; + int done = 0; + unsigned long timeout; + unsigned data_sent = 0; + unsigned data_count; +#if USE_FIFO + int have_data_in = 0; +#endif +#if SEND_IDENTITY + int sent_identify = 0; +#endif + +#if EVERY_ACCESS + printk( "fdomain_command(%d, %x): ", target, (unsigned char)*the_command ); +#endif + + if (fdomain_arbitrate()) { +#if ERRORS_ONLY + printk( ", target = %d, command = %x\n", + target, (unsigned char)*the_command ); +#endif + return DID_TIME_OUT << 16; + } + + if (fdomain_select( target )) { +#if ERRORS_ONLY + if (!target) printk( ", target = %d, command = %x\n", + target, (unsigned char)*the_command ); +#endif + return DID_NO_CONNECT << 16; + } + + timeout = jiffies + 500; /* 5000 mS -- For Maxtor after a RST */ + aborted = 0; /* How is this supposed to get reset??? */ + +#if FAST_SYNCH + outb( 0xc0, Data_Mode_Cntl_port ); +#endif + +#if USE_FIFO + switch ((unsigned char)*the_command) { + case 0x04: case 0x07: case 0x0a: case 0x15: case 0x2a: + case 0x2e: case 0x3b: case 0xea: case 0x3f: + data_count = 0x2000 - inw( FIFO_Data_Count_port ); + if (bufflen - data_sent < data_count) + data_count = bufflen - data_sent; + if (data_count == 1) { + outb( *out_buf_pt++, Write_FIFO_port ); + ++data_sent; + } else { + data_count >>= 1; + outsw( out_buf_pt, data_count, Write_FIFO_port ); + out_buf_pt += 2 * data_count; + data_sent += 2 * data_count; + } + break; + default: + outb( 0x88, TMC_Cntl_port ); + ++have_data_in; + break; + } +#endif + + while (((status = inb( SCSI_Status_port )) & 1) + && !done && !aborted && jiffies < timeout) { + + if (status & 0x10) { /* REQ */ + + switch (status & 0x0e) { + case 0x00: /* DATA OUT */ +#if USE_FIFO + data_count = 0x2000 - inw( FIFO_Data_Count_port ); + if (bufflen - data_sent < data_count) + data_count = bufflen - data_sent; + if (data_count == 1) { + outb( *out_buf_pt++, Write_FIFO_port ); + ++data_sent; + } else { + data_count >>= 1; + outsw( out_buf_pt, data_count, Write_FIFO_port ); + out_buf_pt += 2 * data_count; + data_sent += 2 * data_count; + } +#else + outb( *out_buf_pt++, Write_SCSI_Data_port ); +#endif + break; + case 0x04: /* DATA IN */ +#if USE_FIFO + if (!have_data_in) { + outb( 0x88, TMC_Cntl_port ); + ++have_data_in; + } + data_count = inw( FIFO_Data_Count_port ); + if (data_count == 1) { + *in_buf_pt++ = inb( Read_FIFO_port ); + } else { + data_count >>= 1; /* Number of words */ + insw( in_buf_pt, data_count, Read_FIFO_port ); + in_buf_pt += 2 * data_count; + } +#else + *in_buf_pt++ = inb( Read_SCSI_Data_port ); +#endif + break; + case 0x08: /* COMMAND OUT */ + outb( *cmd_pt++, Write_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "%x,", (unsigned char)cmd_pt[-1] ); +#endif + break; + case 0x0c: /* STATUS IN */ + Status = inb( Read_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "Status = %x, ", Status ); +#endif +#if ERRORS_ONLY + if (Status) { + printk( "SCSI: target = %d, command = %x, Status = %x\n", + target, (unsigned char)*the_command, Status ); + } +#endif + break; + case 0x0a: /* MESSAGE OUT */ +#if SEND_IDENTIFY + /* On the first request, send an Identify message */ + if (!sent_identify) { + outb( 0x80, SCSI_Cntl_port ); /* Lower ATN */ + outb( 0x80, Write_SCSI_Data_port ); /* Identify */ + ++sent_identify; + } else +#else + outb( 0x07, Write_SCSI_Data_port ); /* Reject */ +#endif + break; + case 0x0e: /* MESSAGE IN */ + Message = inb( Read_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "Message = %x, ", Message ); +#endif + if (!Message) ++done; + break; + } + } + } + + if (jiffies >= timeout) { +#if EVERY_ACCESS + printk( "Time out, status = %x\n", status ); +#endif +#if ERRORS_ONLY + printk( "SCSI: Time out, status = %x (target = %d, command = %x)\n", + status, target, (unsigned char)*the_command ); +#endif + fdomain_make_bus_idle(); + return DID_BUS_BUSY << 16; + } + + if (aborted) { +#if EVERY_ACCESS + printk( "Aborted\n" ); +#endif +#if ONLY_ERRORS + printk( "SCSI: Aborted (command = %x)\n", (unsigned char)*the_command ); +#endif + fdomain_16x0_reset(); + return DID_ABORT << 16; + } + +#if USE_FIFO + if (have_data_in) { + while (data_count = inw( FIFO_Data_Count_port )) { + if (data_count == 1) { + *in_buf_pt++ = inb( Read_FIFO_port ); + } else { + data_count >>= 1; /* Number of words */ + insw( in_buf_pt, data_count, Read_FIFO_port ); + in_buf_pt += 2 * data_count; + } + } + } +#endif + + fdomain_make_bus_idle(); + +#if EVERY_ACCESS + printk( "Retcode = %x\n", + (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16) ); +#endif +#if ERRORS_ONLY + if (*the_command == REQUEST_SENSE && !Status) { + if ((unsigned char)(*((char *)buff + 2)) & 0x0f) { + printk( "SCSI REQUEST SENSE: Sense Key = %x, Sense Code = %x\n", + (unsigned char)(*((char *)buff + 2)) & 0x0f, + (unsigned char)(*((char *)buff + 12)) ); + } + } +#endif + + return (Status & 0xff) | ((Message & 0xff) << 8) | (DID_OK << 16); +} + +int fdomain_16x0_abort( int code ) +{ + +#if EVERY_ACCESS + printk( " ABORT " ); +#endif + +#if QUEUE + cli(); + if (!in_command) { + sti(); + return 0; + } + + aborted = code ? code : DID_ABORT; + + sti(); + fdomain_make_bus_idle(); +#else + aborted = code ? code : DID_ABORT; +#endif + + return 0; +} + +int fdomain_16x0_reset( void ) +{ + outb( 1, SCSI_Cntl_port ); + do_pause( 2 ); + outb( 0, SCSI_Cntl_port ); + do_pause( 115 ); + outb( 0, Data_Mode_Cntl_port ); + outb( 0, TMC_Cntl_port ); + + aborted = DID_RESET; + + return 0; +} + +#if QUEUE && !NEW_IRQ + +/* This is copied from kernel/sys_calls.s + and from kernel/blk_drv/scsi/aha1542.c */ + +__asm__(" +_fdomain_16x0_interrupt: + cld + push %gs + push %fs + push %es + push %ds + pushl %eax + pushl %ebp + pushl %edi + pushl %esi + pushl %edx + pushl %ecx + pushl %ebx + movl $0x10,%edx + mov %dx,%ds + mov %dx,%es + movl $0x17,%edx + mov %dx,%fs + + movl $_fdomain_disable_interrupt,%edx + call *%edx + + movb $0x20,%al + outb %al,$0xA0 # EOI to interrupt controller #1 + jmp 1f # give port chance to breathe +1: jmp 1f +1: outb %al,$0x20 + + sti + movl $_fdomain_16x0_intr,%edx + call *%edx # ``interesting'' way of handling intr. + cli + + movl $_fdomain_enable_interrupt,%edx + call *%edx + + popl %ebx + popl %ecx + popl %edx + popl %esi + popl %edi + popl %ebp + popl %eax + pop %ds + pop %es + pop %fs + pop %gs + iret +"); +#endif + +#endif diff --git a/kernel/blk_drv/scsi/fdomain.h b/kernel/blk_drv/scsi/fdomain.h new file mode 100644 index 000000000000..78f2b0944f40 --- /dev/null +++ b/kernel/blk_drv/scsi/fdomain.h @@ -0,0 +1,45 @@ +/* fdomain.h -- Header for Future Domain TMC-1660/TMC-1680 driver + * Created: Sun May 3 18:47:33 1992 + * Revised: Sat May 23 22:42:55 1992 by root + * Author: Rickard E. Faith, faith@cs.unc.edu + * Copyright 1992 Rickard E. Faith + * This program comes with ABSOLUTELY NO WARRANTY. + * + * $Log$ + */ + +#ifndef _FDOMAIN_H +#define _FDOMAIN_H + +#define QUEUE 1 /* Enable command queueing */ + +int fdomain_16x0_detect( int ); +int fdomain_16x0_command( unsigned char target, const void *cmnd, + void *buff, int bufflen); +int fdomain_16x0_abort( int ); +char *fdomain_16x0_info( void ); +int fdomain_16x0_reset( void ); + +#if QUEUE +int fdomain_16x0_queue( unsigned char target, const void *cmnd, + void *buff, int bufflen, void (*done)(int,int) ); + +#define FDOMAIN_16X0 { "Future Domain TMC-1660/TMC-1680", \ + fdomain_16x0_detect, \ + fdomain_16x0_info, \ + fdomain_16x0_command, \ + fdomain_16x0_queue, \ + fdomain_16x0_abort, \ + fdomain_16x0_reset, \ + 1, 6, 0 } +#else +#define FDOMAIN_16X0 { "Future Domain TMC-1660/TMC-1680", \ + fdomain_16x0_detect, \ + fdomain_16x0_info, \ + fdomain_16x0_command, \ + NULL, \ + fdomain_16x0_abort, \ + fdomain_16x0_reset, \ + 0, 6, 0 } +#endif +#endif diff --git a/kernel/blk_drv/scsi/hosts.c b/kernel/blk_drv/scsi/hosts.c new file mode 100644 index 000000000000..b4f227c09e63 --- /dev/null +++ b/kernel/blk_drv/scsi/hosts.c @@ -0,0 +1,157 @@ +/* + * hosts.c Copyright (C) 1992 Drew Eckhardt + * mid to lowlevel SCSI driver interface by + * Drew Eckhardt + * + * + */ + + +/* + * This file contains the medium level SCSI + * host interface initialization, as well as the scsi_hosts array of SCSI + * hosts currently present in the system. + */ + +#include + +#ifdef CONFIG_SCSI +#include +#include "scsi.h" + +#ifndef NULL + #define NULL 0L +#endif + +#ifdef FIGURE_MAX_SCSI_HOSTS + #define MAX_SCSI_HOSTS +#endif + +#include "hosts.h" + +#ifdef CONFIG_SCSI_AHA1542 +#include "aha1542.h" +#endif + +#ifdef CONFIG_SCSI_FUTURE_DOMAIN +#include "fdomain.h" +#endif + +#ifdef CONFIG_SCSI_SEAGATE +#include "seagate.h" +#endif + +#ifdef CONFIG_SCSI_ULTRASTOR +#include "ultrastor.h" +#endif + +#ifdef CONFIG_SCSI_7000FASST +#include "7000fasst.h" +#endif + +/* +static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.c,v 1.1 1992/07/24 06:27:38 root Exp root $"; +*/ + +/* + * The scsi host entries should be in the order you wish the + * cards to be detected. A driver may appear more than once IFF + * it can deal with being detected (and therefore initialized) + * with more than one simulatenous host number, can handle being + * rentrant, etc. + * + * They may appear in any order, as each SCSI host is told which host number it is + * during detection. + */ + +/* + * When figure is run, we don't want to link to any object code. Since + * the macro for each host will contain function pointers, we cannot + * use it and instead must use a "blank" that does no such + * idiocy. + */ + +#ifdef FIGURE_MAX_SCSI_HOSTS + #define BLANKIFY(what) BLANK_HOST +#else + #define BLANKIFY(what) what +#endif + +Scsi_Host scsi_hosts[] = + { +#ifdef CONFIG_SCSI_AHA1542 + BLANKIFY(AHA1542), +#endif + +#ifdef CONFIG_SCSI_FUTURE_DOMAIN + BLANKIFY(FDOMAIN_16X0), +#endif + +#ifdef CONFIG_SCSI_SEAGATE + BLANKIFY(SEAGATE_ST0X), +#endif +#ifdef CONFIG_SCSI_ULTRASTOR + BLANKIFY(ULTRASTOR_14F), +#endif +#ifdef CONFIG_SCSI_7000FASST + BLANKIFY(WD7000FASST), +#endif + }; + +#ifdef FIGURE_MAX_SCSI_HOSTS + #undef MAX_SCSI_HOSTS + #define MAX_SCSI_HOSTS (sizeof(scsi_hosts) / sizeof(Scsi_Host)) +#endif + +#ifdef FIGURE_MAX_SCSI_HOSTS +#include +void main (void) +{ + printf("%d", MAX_SCSI_HOSTS); +} +#else +/* + * Our semaphores and timeout counters, where size depends on MAX_SCSI_HOSTS here. + */ + +volatile unsigned char host_busy[MAX_SCSI_HOSTS]; +volatile int host_timeout[MAX_SCSI_HOSTS]; +volatile Scsi_Cmnd *host_queue[MAX_SCSI_HOSTS]; + +void scsi_init(void) + { + static int called = 0; + int i, count; + if (!called) + { + called = 1; + for (count = i = 0; i < MAX_SCSI_HOSTS; ++i) + { +/* + * Initialize our semaphores. -1 is interpreted to mean + * "inactive" - where as 0 will indicate a time out condition. + */ + + host_busy[i] = 0; + host_timeout[i] = 0; + host_queue[i] = NULL; + + if ((scsi_hosts[i].detect) && (scsi_hosts[i].present = scsi_hosts[i].detect(i))) + { + printk ("scsi%d : %s.\n\r", + count, scsi_hosts[i].name); + printk ("%s", scsi_hosts[i].info()); + ++count; + } + } + printk ("scsi : %d hosts. \n\r", count); + } + + } + +#endif +#else +void main(void) { + printf("0\n"); + } +#endif diff --git a/kernel/blk_drv/scsi/hosts.h b/kernel/blk_drv/scsi/hosts.h new file mode 100644 index 000000000000..f5504d605ac3 --- /dev/null +++ b/kernel/blk_drv/scsi/hosts.h @@ -0,0 +1,179 @@ +/* + * hosts.h Copyright (C) 1992 Drew Eckhardt + * mid to low-level SCSI driver interface header by + * Drew Eckhardt + * + * + */ + +#ifndef _HOSTS_H + #define _HOSTS_H + +#ifndef MAX_SCSI_HOSTS + #include "max_hosts.h" +#endif + +/* + $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hosts.h,v 1.1 1992/07/24 06:27:38 root Exp root $ +*/ + +/* + The Scsi_Cmnd structure is used by scsi.c internally, and for communication with + low level drivers that support multiple outstanding commands. +*/ + +typedef struct scsi_cmnd { + int host; + unsigned char target, lun; + unsigned char cmnd[10]; + unsigned bufflen; + void *buffer; + + unsigned char sense_cmnd[6]; + unsigned char *sense_buffer; + + unsigned flags; + + int retries; + int allowed; + int timeout_per_command, timeout_total, timeout; + + void (*done)(int,int); + struct scsi_cmnd *next, *prev; + } Scsi_Cmnd; + +/* + The Scsi_Host type has all that is needed to interface with a SCSI + host in a device independant matter. +*/ + +typedef struct + { + /* + The name pointer is a pointer to the name of the SCSI + device detected. + */ + + char *name; + + /* + The detect function shall return non zero on detection, + and initialize all data necessary for this particular + SCSI driver. It is passed the host number, so this host + knows where it is in the hosts array + */ + + int (* detect)(int); + + /* + The info function will return whatever useful + information the developer sees fit. + */ + + char *(* info)(void); + + /* + The command function takes a target, a command (this is a SCSI + command formatted as per the SCSI spec, nothing strange), a + data buffer pointer, and data buffer length pointer. The return + is a status int, bit fielded as follows : + Byte What + 0 SCSI status code + 1 SCSI 1 byte message + 2 host error return. + 3 mid level error return + */ + + int (* command)(unsigned char target, const void *cmnd, + void *buff, int bufflen); + + /* + The QueueCommand function works in a similar manner + to the command function. It takes an additional parameter, + void (* done)(int host, int code) which is passed the host + # and exit result when the command is complete. + Host number is the POSITION IN THE hosts array of THIS + host adapter. + */ + + int (* queuecommand)(unsigned char target, const void *cmnd, + void *buff, int bufflen, void (*done)(int,int)); + + + /* + Since the mid level driver handles time outs, etc, we want to + be able to abort the current command. Abort returns 0 if the + abortion was successful. If non-zero, the code passed to it + will be used as the return code, otherwise + DID_ABORT should be returned. + + Note that the scsi driver should "clean up" after itself, + resetting the bus, etc. if necessary. + */ + + int (* abort)(int); + + /* + The reset function will reset the SCSI bus. Any executing + commands should fail with a DID_RESET in the host byte. + */ + + int (* reset)(void); + + /* + This determines if we will use a non-interrupt driven + or an interrupt driven scheme, It is set to the maximum number + of simulataneous commands a given host adapter will accept. + */ + + int can_queue; + + /* + In many instances, especially where disconnect / reconnect are + supported, our host also has an ID on the SCSI bus. If this is + the case, then it must be reserved. Please set this_id to -1 if your settup is in single initiator mode, and the host lacks an + ID. + */ + + int this_id; + + /* + present contains a flag as to weather we are present - + so we don't have to call detect multiple times. + */ + + unsigned present:1; + } Scsi_Host; + +/* + The scsi_hosts array is the array containing the data for all + possible scsi hosts. +*/ + +extern Scsi_Host scsi_hosts[]; + +/* + This is our semaphore array, used by scsi.c, sd.c, st.c. + Other routines SHOULD NOT mess with it. Your driver should NOT mess with it. + This is used to protect against contention by disk and tape drivers. +*/ + +extern volatile unsigned char host_busy[]; +extern volatile int host_timeout[]; + +/* + This is the queue of currently pending commands for a given + SCSI host. +*/ + +extern volatile Scsi_Cmnd *host_queue[]; + +/* + scsi_init initializes the scsi hosts. +*/ + + +void scsi_init(void); + +#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0} +#endif diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c new file mode 100644 index 000000000000..1469ce291b09 --- /dev/null +++ b/kernel/blk_drv/scsi/scsi.c @@ -0,0 +1,1060 @@ +/* + * scsi.c Copyright (C) 1992 Drew Eckhardt + * generic mid-level SCSI driver by + * Drew Eckhardt + * + * + * + * Bug correction thanks go to : + * Rik Faith + * Tommy Thorn + * Thomas Wuensche + */ + +#include + +#ifdef CONFIG_SCSI +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" + +#ifdef CONFIG_BLK_DEV_SD +#include "sd.h" +#endif + +#ifdef CONFIG_BLK_DEV_ST +#include "st.h" +#endif + +/* +static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.c,v 1.1 1992/07/24 06:27:38 root Exp root $"; +*/ + +#define INTERNAL_ERROR (printk ("Internal error in file %s, line %s.\n", __FILE__, __LINE__), panic("")) + +static void scsi_done (int host, int result); +static void update_timeout (void); + +static int time_start; +static int time_elapsed; + +/* + global variables : + NR_SCSI_DEVICES is the number of SCSI devices we have detected, + scsi_devices an array of these specifing the address for each + (host, id, LUN) +*/ + +int NR_SCSI_DEVICES=0; +Scsi_Device scsi_devices[MAX_SCSI_DEVICE]; + +#define SENSE_LENGTH 255 +/* + * As the scsi do command functions are inteligent, and may need to + * redo a command, we need to keep track of the last command + * executed on each one. + */ + +#define WAS_RESET 0x01 +#define WAS_TIMEDOUT 0x02 +#define WAS_SENSE 0x04 +#define IS_RESETTING 0x08 + +static Scsi_Cmnd last_cmnd[MAX_SCSI_HOSTS]; +static int last_reset[MAX_SCSI_HOSTS]; + +/* + * This is the number of clock ticks we should wait before we time out + * and abort the command. This is for where the scsi.c module generates + * the command, not where it originates from a higher level, in which + * case the timeout is specified there. + * + * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT + * respectively. + */ + +#ifdef DEBUG + #define SCSI_TIMEOUT 500 +#else + #define SCSI_TIMEOUT 100 +#endif + +#ifdef DEBUG + #define SENSE_TIMEOUT SCSI_TIMEOUT + #define ABORT_TIMEOUT SCSI_TIMEOUT + #define RESET_TIMEOUT SCSI_TIMEOUT +#else + #define SENSE_TIMEOUT 50 + #define RESET_TIMEOUT 50 + #define ABORT_TIMEOUT 50 + #define MIN_RESET_DELAY 25 +#endif + +/* + * As the actual SCSI command runs in the background, we must set up a + * flag that tells scan_scsis() when the result it has is valid. + * scan_scsis can set the_result to -1, and watch for it to become the + * actual return code for that call. the scan_scsis_done function() is + * our user specified completion function that is passed on to the + * scsi_do_cmd() function. + */ + +volatile static int in_scan = 0; +static int the_result; +static unsigned char sense_buffer[SENSE_LENGTH]; +static void scan_scsis_done (int host, int result) + { + +#ifdef DEBUG + printk ("scan_scsis_done(%d, %06x)\n\r", host, result); +#endif + the_result = result; + } +/* + * Detecting SCSI devices : + * We scan all present host adapter's busses, from ID 0 to ID 6. + * We use the INQUIRY command, determine device type, and pass the ID / + * lun address of all sequential devices to the tape driver, all random + * devices to the disk driver. + */ + +static void scan_scsis (void) + { + int host_nr , dev, lun, type, maxed, slave; + static unsigned char scsi_cmd [12]; + static unsigned char scsi_result [256]; + + ++in_scan; + + for (slave = host_nr = 0; host_nr < MAX_SCSI_HOSTS; ++host_nr, + slave = 0) + if (scsi_hosts[host_nr].present) + { + for (dev = 0; dev < 7; ++dev) + if (scsi_hosts[host_nr].this_id != dev) + #ifdef MULTI_LUN + for (lun = 0; lun < 8; ++lun) + { + #else + { + lun = 0; + #endif +/* + * Build an INQUIRY command block. + */ + + scsi_cmd[0] = INQUIRY; + scsi_cmd[1] = (lun << 5) & 0xe0; + scsi_cmd[2] = 0; + scsi_cmd[3] = 0; + scsi_cmd[4] = 255; + scsi_cmd[5] = 0; + the_result = -1; +#ifdef DEBUG + memset ((void *) scsi_result , 0, 255); +#endif + scsi_do_cmd (host_nr, dev, (void *) scsi_cmd, (void *) + scsi_result, 256, scan_scsis_done, + SCSI_TIMEOUT, sense_buffer, 3); + +/* + * Wait for valid result + */ + + while (the_result < 0); + + if (!the_result) + { + scsi_devices[NR_SCSI_DEVICES]. + host_no = host_nr; + scsi_devices[NR_SCSI_DEVICES]. + id = dev; + scsi_devices[NR_SCSI_DEVICES]. + lun = lun; + scsi_devices[NR_SCSI_DEVICES]. + removable = (0x80 & + scsi_result[1]) >> 7; +/* + * Currently, all sequential devices are assumed to be tapes, + * all random devices disk, with the appropriate read only + * flags set for ROM / WORM treated as RO. + */ + + switch (type = scsi_result[0]) + { + case TYPE_TAPE : + case TYPE_DISK : + scsi_devices[NR_SCSI_DEVICES].writeable = 1; + break; + case TYPE_WORM : + case TYPE_ROM : + scsi_devices[NR_SCSI_DEVICES].writeable = 0; + break; + default : + type = -1; + } + + scsi_devices[NR_SCSI_DEVICES].random = (type == TYPE_TAPE) ? 0 : 1; + + maxed = 0; + switch (type) + { + case -1 : + break; + case TYPE_TAPE : +#ifdef DEBUG + printk("Detected scsi tape at host %d, ID %d, lun %d \n", host_nr, dev, lun); +#endif +#ifdef CONFIG_BLK_DEV_ST + if (!(maxed = (NR_ST == MAX_ST))) + scsi_tapes[NR_ST].device = &scsi_devices[NR_SCSI_DEVICES]; +#endif + break; + default : +#ifdef DEBUG + printk("Detected scsi disk at host %d, ID %d, lun %d \n", host_nr, dev, lun); +#endif +#ifdef CONFIG_BLK_DEV_SD + if (!(maxed = (NR_SD >= MAX_SD))) + rscsi_disks[NR_SD].device = &scsi_devices[NR_SCSI_DEVICES]; +#endif + } + + if (maxed) + { + printk ("scsi : already have detected maximum number of SCSI %ss Unable to \n" + "add drive at SCSI host %s, ID %d, LUN %d\n\r", (type == TYPE_TAPE) ? + "tape" : "disk", scsi_hosts[host_nr].name, + dev, lun); + type = -1; + break; + } + + else if (type != -1) + { + char *p; + char str[25]; +memcpy((void *) str, (void *) &scsi_result[8], 8); +for (p = str; (p < (str + 8)) && (*p != ' '); ++p); +*p++ = ' '; +memcpy((void *) p, (void *) &scsi_result[16], 16); +for (; *p != ' '; ++p); +*p = 0; + +printk("s%c%d at scsi%d, id %d, lun %d : %s\n", + (type == TYPE_TAPE) ? 't' : 'd', + (type == TYPE_TAPE) ? +#ifdef CONFIG_BLK_DEV_ST + NR_ST +#else + -1 +#endif + : +#ifdef CONFIG_BLK_DEV_SD + NR_SD +#else + -1 +#endif + ,host_nr , dev, lun, p); + if (type == TYPE_TAPE) +#ifdef CONFIG_BLK_DEV_ST + ++NR_ST; +#else +; +#endif + + else +#ifdef CONFIG_BLK_DEV_SD + ++NR_SD; +#else +; +#endif + } + ++slave; + ++NR_SCSI_DEVICES; + } /* if result == DID_OK ends */ + } /* for lun ends */ + } /* if present */ + + printk("scsi : detected " +#ifdef CONFIG_BLK_DEV_SD + "%d SCSI disk%s " +#endif + +#ifdef CONFIG_BLK_DEV_ST + "%d tape%s " +#endif + + "total.\n", + +#ifdef CONFIG_BLK_DEV_SD + NR_SD, (NR_SD != 1) ? "s" : "" +#ifdef CONFIG_BLK_DEV_ST + , +#endif +#endif + +#ifdef CONFIG_BLK_DEV_ST + NR_ST, (NR_ST != 1) ? "s" : "" +#endif + ); + in_scan = 0; + } /* scan_scsis ends */ + +/* + * We handle the timeout differently if it happens when a reset, + * abort, etc are in process. + */ + +static unsigned char internal_timeout[MAX_SCSI_HOSTS]; + +/* + * Flag bits for the internal_timeout array + */ + +#define NORMAL_TIMEOUT 0 +#define IN_ABORT 1 +#define IN_RESET 2 +/* + This is our time out function, called when the timer expires for a + given host adapter. It will attempt to abort the currently executing + command, that failing perform a kernel panic. +*/ + +static void scsi_times_out (int host) + { + + switch (internal_timeout[host] & (IN_ABORT | IN_RESET)) + { + case NORMAL_TIMEOUT: + if (!in_scan) + printk("SCSI host %d timed out - aborting command \r\n", + host); + + if (!scsi_abort (host, DID_TIME_OUT)) + return; + case IN_ABORT: + printk("SCSI host %d abort() timed out - reseting \r\n", + host); + if (!scsi_reset (host)) + return; + case IN_RESET: + case (IN_ABORT | IN_RESET): + printk("Unable to reset scsi host %d\r\n",host); + panic(""); + default: + INTERNAL_ERROR; + } + + } + +/* + This is inline because we have stack problemes if we recurse to deeply. +*/ + +static void internal_cmnd (int host, unsigned char target, const void *cmnd , + void *buffer, unsigned bufflen, void (*done)(int,int)) + { + int temp; + +#ifdef DEBUG_DELAY + int clock; +#endif + + if ((host < 0) || (host > MAX_SCSI_HOSTS)) + panic ("Host number in internal_cmnd() is out of range.\n"); + + +/* + We will wait MIN_RESET_DELAY clock ticks after the last reset so + we can avoid the drive not being ready. +*/ +temp = last_reset[host]; +while (jiffies < temp); + +host_timeout[host] = last_cmnd[host].timeout_per_command; +update_timeout(); + +/* + We will use a queued command if possible, otherwise we will emulate the + queing and calling of completion function ourselves. +*/ +#ifdef DEBUG + printk("internal_cmnd (host = %d, target = %d, command = %08x, buffer = %08x, \n" + "bufflen = %d, done = %08x)\n", host, target, cmnd, buffer, bufflen, done); +#endif + + + if (scsi_hosts[host].can_queue) + { +#ifdef DEBUG + printk("queuecommand : routine at %08x\n", + scsi_hosts[host].queuecommand); +#endif + scsi_hosts[host].queuecommand (target, cmnd, buffer, bufflen, + done); + } + else + { + +#ifdef DEBUG + printk("command() : routine at %08x\n", scsi_hosts[host].command); +#endif + temp=scsi_hosts[host].command (target, cmnd, buffer, bufflen); + +#ifdef DEBUG_DELAY + clock = jiffies + 400; + while (jiffies < clock); + printk("done(host = %d, result = %04x) : routine at %08x\n", host, temp, done); +#endif + done(host, temp); + } +#ifdef DEBUG + printk("leaving internal_cmnd()\n"); +#endif + } + +static void scsi_request_sense (int host, unsigned char target, + unsigned char lun) + { + cli(); + host_timeout[host] = SENSE_TIMEOUT; + update_timeout(); + last_cmnd[host].flags |= WAS_SENSE; + sti(); + + last_cmnd[host].sense_cmnd[1] = lun << 5; + + internal_cmnd (host, target, (void *) last_cmnd[host].sense_cmnd, + (void *) last_cmnd[host].sense_buffer, SENSE_LENGTH, + scsi_done); + } + + + + + +/* + scsi_do_cmd sends all the commands out to the low-level driver. It + handles the specifics required for each low level driver - ie queued + or non queud. It also prevents conflicts when different high level + drivers go for the same host at the same time. +*/ + +void scsi_do_cmd (int host, unsigned char target, const void *cmnd , + void *buffer, unsigned bufflen, void (*done)(int,int), + int timeout, unsigned char *sense_buffer, int retries + ) + { + int ok = 0; + +#ifdef DEBUG + int i; + printk ("scsi_do_cmd (host = %d, target = %d, buffer =%08x, " + "bufflen = %d, done = %08x, timeout = %d, retries = %d)\n" + "command : " , host, target, buffer, bufflen, done, timeout, retries); + for (i = 0; i < 10; ++i) + printk ("%02x ", ((unsigned char *) cmnd)[i]); + printk("\n"); +#endif + + if ((host >= MAX_SCSI_HOSTS) || !scsi_hosts[host].present) + { + printk ("Invalid or not present host number. %d\n", host); + panic(""); + } + + +/* + We must prevent reentrancy to the lowlevel host driver. This prevents + it - we enter a loop until the host we want to talk to is not busy. + Race conditions are prevented, as interrupts are disabled inbetween the + time we check for the host being not busy, and the time we mark it busy + ourselves. +*/ + + do { + cli(); + if (host_busy[host]) + { + sti(); +#ifdef DEBUG + printk("Host %d is busy.\n", host); +#endif + while (host_busy[host]); +#ifdef DEBUG + printk("Host %d is no longer busy.\n", host); +#endif + } + else + { + host_busy[host] = 1; + ok = 1; + sti(); + } + } while (!ok); + + +/* + Our own function scsi_done (which marks the host as not busy, disables + the timeout counter, etc) will be called by us or by the + scsi_hosts[host].queuecommand() function needs to also call + the completion function for the high level driver. + +*/ + + memcpy ((void *) last_cmnd[host].cmnd , (void *) cmnd, 10); + last_cmnd[host].host = host; + last_cmnd[host].target = target; + last_cmnd[host].lun = (last_cmnd[host].cmnd[1] >> 5); + last_cmnd[host].bufflen = bufflen; + last_cmnd[host].buffer = buffer; + last_cmnd[host].sense_buffer = sense_buffer; + last_cmnd[host].flags=0; + last_cmnd[host].retries=0; + last_cmnd[host].allowed=retries; + last_cmnd[host].done = done; + last_cmnd[host].timeout_per_command = timeout; + + /* Start the timer ticking. */ + + internal_timeout[host] = 0; + internal_cmnd (host, target, cmnd , buffer, bufflen, scsi_done); + +#ifdef DEBUG + printk ("Leaving scsi_do_cmd()\n"); +#endif + } + + +/* + The scsi_done() function disables the timeout timer for the scsi host, + marks the host as not busy, and calls the user specified completion + function for that host's current command. +*/ + +static void reset (int host) + { + #ifdef DEBUG + printk("reset(%d)\n", host); + #endif + + last_cmnd[host].flags |= (WAS_RESET | IS_RESETTING); + scsi_reset(host); + + #ifdef DEBUG + printk("performing request sense\n"); + #endif + + scsi_request_sense (host, last_cmnd[host].target, last_cmnd[host].lun); + } + + + +static int check_sense (int host) + { + if (((sense_buffer[0] & 0x70) >> 4) == 7) + switch (sense_buffer[2] & 0xf) + { + case NO_SENSE: + case RECOVERED_ERROR: + return 0; + + case ABORTED_COMMAND: + case NOT_READY: + case UNIT_ATTENTION: + return SUGGEST_RETRY; + + /* these three are not supported */ + case COPY_ABORTED: + case VOLUME_OVERFLOW: + case MISCOMPARE: + + case MEDIUM_ERROR: + return SUGGEST_REMAP; + case BLANK_CHECK: + case DATA_PROTECT: + case HARDWARE_ERROR: + case ILLEGAL_REQUEST: + default: + return SUGGEST_ABORT; + } + else + return SUGGEST_RETRY; + } + +static void scsi_done (int host, int result) + { + int status=0; + int exit=0; + int checked; + int oldto; + oldto = host_timeout[host]; + host_timeout[host] = 0; + update_timeout(); + +#define FINISHED 0 +#define MAYREDO 1 +#define REDO 3 + +#ifdef DEBUG + printk("In scsi_done(host = %d, result = %06x)\n", host, result); +#endif + if (host > MAX_SCSI_HOSTS || host < 0) + { + host_timeout[host] = 0; + update_timeout(); + panic("scsi_done() called with invalid host number.\n"); + } + + switch (host_byte(result)) + { + case DID_OK: + if (last_cmnd[host].flags & IS_RESETTING) + { + last_cmnd[host].flags &= ~IS_RESETTING; + status = REDO; + break; + } + + if (status_byte(result) && (last_cmnd[host].flags & + WAS_SENSE)) + { + last_cmnd[host].flags &= ~WAS_SENSE; + cli(); + internal_timeout[host] &= ~SENSE_TIMEOUT; + sti(); + + if (!(last_cmnd[host].flags & WAS_RESET)) + reset(host); + else + { + exit = (DRIVER_HARD | SUGGEST_ABORT); + status = FINISHED; + } + } + else switch(msg_byte(result)) + { + case COMMAND_COMPLETE: + switch (status_byte(result)) + { + case GOOD: + if (last_cmnd[host].flags & WAS_SENSE) + { +#ifdef DEBUG + printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n"); +#endif + + last_cmnd[host].flags &= ~WAS_SENSE; + cli(); + internal_timeout[host] &= ~SENSE_TIMEOUT; + sti(); + + switch (checked = check_sense(host)) + { + case 0: +#ifdef DEBUG + printk("NO SENSE. status = REDO\n"); +#endif + + host_timeout[host] = oldto; + update_timeout(); + status = REDO; + break; + case SUGGEST_REMAP: + case SUGGEST_RETRY: +#ifdef DEBUG + printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n"); +#endif + + status = MAYREDO; + exit = SUGGEST_RETRY; + break; + case SUGGEST_ABORT: +#ifdef DEBUG + printk("SENSE SUGGEST ABORT - status = FINISHED"); +#endif + + status = FINISHED; + exit = DRIVER_SENSE; + break; + default: + printk ("Internal error %s %s \n", __FILE__, + __LINE__); + } + } + else + { +#ifdef DEBUG + printk("COMMAND COMPLETE message returned, status = FINISHED. \n"); +#endif + + exit = DRIVER_OK; + status = FINISHED; + } + break; + + case CHECK_CONDITION: + +#ifdef DEBUG + printk("CHECK CONDITION message returned, performing request sense.\n"); +#endif + + scsi_request_sense (host, last_cmnd[host].target, last_cmnd[host].lun); + break; + + case CONDITION_GOOD: + case INTERMEDIATE_GOOD: + case INTERMEDIATE_C_GOOD: +#ifdef DEBUG + printk("CONDITION GOOD, INTERMEDIATE GOOD, or INTERMEDIATE CONDITION GOOD recieved and ignored. \n"); +#endif + break; + + case BUSY: +#ifdef DEBUG + printk("BUSY message returned, performing REDO"); +#endif + host_timeout[host] = oldto; + update_timeout(); + status = REDO; + break; + + case RESERVATION_CONFLICT: + reset(host); + exit = DRIVER_SOFT | SUGGEST_ABORT; + status = MAYREDO; + break; + default: + printk ("Internal error %s %s \n" + "status byte = %d \n", __FILE__, + __LINE__, status_byte(result)); + + } + break; + default: + panic ("unsupported message byte recieved."); + } + break; + case DID_TIME_OUT: +#ifdef DEBUG + printk("Host returned DID_TIME_OUT - "); +#endif + + if (last_cmnd[host].flags & WAS_TIMEDOUT) + { +#ifdef DEBUG + printk("Aborting\n"); +#endif + exit = (DRIVER_TIMEOUT | SUGGEST_ABORT); + } + else + { +#ifdef DEBUG + printk ("Retrying.\n"); +#endif + last_cmnd[host].flags |= WAS_TIMEDOUT; + status = REDO; + } + break; + case DID_BUS_BUSY: + case DID_PARITY: + status = REDO; + break; + case DID_NO_CONNECT: +#ifdef DEBUG + printk("Couldn't connect.\n"); +#endif + exit = (DRIVER_HARD | SUGGEST_ABORT); + break; + case DID_ERROR: + status = MAYREDO; + exit = (DRIVER_HARD | SUGGEST_ABORT); + break; + case DID_BAD_TARGET: + case DID_ABORT: + exit = (DRIVER_INVALID | SUGGEST_ABORT); + break; + default : + exit = (DRIVER_ERROR | SUGGEST_DIE); + } + + switch (status) + { + case FINISHED: + break; + case MAYREDO: + +#ifdef DEBUG + printk("In MAYREDO, allowing %d retries, have %d\n\r", + last_cmnd[host].allowed, last_cmnd[host].retries); +#endif + + if ((++last_cmnd[host].retries) < last_cmnd[host].allowed) + { + if ((last_cmnd[host].retries >= (last_cmnd[host].allowed >> 1)) + && !(last_cmnd[host].flags & WAS_RESET)) + reset(host); + break; + + } + else + { + status = FINISHED; + break; + } + /* fall through to REDO */ + + case REDO: + if (last_cmnd[host].flags & WAS_SENSE) + scsi_request_sense (host, last_cmnd[host].target, + last_cmnd[host].lun); + else + internal_cmnd (host, last_cmnd[host].target, + last_cmnd[host].cmnd, + last_cmnd[host].buffer, + last_cmnd[host].bufflen, scsi_done); + break; + default: + INTERNAL_ERROR; + } + + if (status == FINISHED) + { + #ifdef DEBUG + printk("Calling done function - at address %08x\n", last_cmnd[host].done); + #endif + host_busy[host] = 0; + last_cmnd[host].done (host, (result | ((exit & 0xff) << 24))); + } + + +#undef FINISHED +#undef REDO +#undef MAYREDO + + } + +/* + The scsi_abort function interfaces with the abort() function of the host + we are aborting, and causes the current command to not complete. The + caller should deal with any error messages or status returned on the + next call. + + This will not be called rentrantly for a given host. +*/ + +/* + Since we're nice guys and specified that abort() and reset() + can be non-reentrant. The internal_timeout flags are used for + this. +*/ + + +int scsi_abort (int host, int why) + { + int temp, oldto; + + while(1) + { + cli(); + if (internal_timeout[host] & IN_ABORT) + { + sti(); + while (internal_timeout[host] & IN_ABORT); + } + else + { + oldto = host_timeout[host]; + internal_timeout[host] |= IN_ABORT; + host_timeout[host] = ABORT_TIMEOUT; + update_timeout(); + + + sti(); + if (!host_busy[host] || !scsi_hosts[host].abort(why)) + temp = 0; + else + temp = 1; + + cli(); + internal_timeout[host] &= ~IN_ABORT; + host_timeout[host]=oldto; + update_timeout(); + sti(); + return temp; + } + } + } + +int scsi_reset (int host) + { + int temp, oldto; + + while (1) { + cli(); + if (internal_timeout[host] & IN_RESET) + { + sti(); + while (internal_timeout[host] & IN_RESET); + } + else + { + oldto = host_timeout[host]; + host_timeout[host] = RESET_TIMEOUT; + update_timeout(); + internal_timeout[host] |= IN_RESET; + + if (host_busy[host]) + { + sti(); + if (!(last_cmnd[host].flags & IS_RESETTING) && !(internal_timeout[host] & IN_ABORT)) + scsi_abort(host, DID_RESET); + + temp = scsi_hosts[host].reset(); + } + else + { + host_busy[host]=1; + + sti(); + temp = scsi_hosts[host].reset(); + last_reset[host] = jiffies; + host_busy[host]=0; + } + + cli(); + host_timeout[host] = oldto; + update_timeout(); + internal_timeout[host] &= ~IN_RESET; + sti(); + return temp; + } + } + } + + +static void scsi_main_timeout(void) + { + /* + We must not enter update_timeout with a timeout condition still pending. + */ + + int i, timed_out; + + do { + cli(); + + /* + Find all timers such that they have 0 or negative (shouldn't happen) + time remaining on them. + */ + + for (i = timed_out = 0; i < MAX_SCSI_HOSTS; ++i) + if (host_timeout[i] != 0 && host_timeout[i] <= time_elapsed) + { + sti(); + host_timeout[i] = 0; + scsi_times_out(i); + ++timed_out; + } + + update_timeout(); + } while (timed_out); + sti(); + } + +/* + These are used to keep track of things. +*/ + +static int time_start, time_elapsed; + +/* + The strategy is to cause the timer code to call scsi_times_out() + when the soonest timeout is pending. +*/ + +static void update_timeout(void) + { + unsigned int i, least, used; + + cli(); + +/* + Figure out how much time has passed since the last time the timeouts + were updated +*/ + used = (time_start) ? (jiffies - time_start) : 0; + +/* + Find out what is due to timeout soonest, and adjust all timeouts for + the amount of time that has passed since the last time we called + update_timeout. +*/ + + for (i = 0, least = 0xffffffff; i < MAX_SCSI_HOSTS; ++i) + if (host_timeout[i] > 0 && (host_timeout[i] -= used) < least) + least = host_timeout[i]; + +/* + If something is due to timeout again, then we will set the next timeout + interrupt to occur. Otherwise, timeouts are disabled. +*/ + + if (least != 0xffffffff) + { + time_start = jiffies; + timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies; + timer_active |= 1 << SCSI_TIMER; + } + else + { + timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0; + timer_active &= ~(1 << SCSI_TIMER); + } + sti(); + } +/* + scsi_dev_init() is our initialization routine, which inturn calls host + initialization, bus scanning, and sd/st initialization routines. It + should be called from main(). +*/ + +static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0}; +void scsi_dev_init (void) + { + int i; +#ifdef FOO_ON_YOU + return; +#endif + timer_table[SCSI_TIMER].fn = scsi_main_timeout; + timer_table[SCSI_TIMER].expires = 0; + + scsi_init(); /* initialize all hosts */ +/* + * Set up sense command in each host structure. + */ + + for (i = 0; i < MAX_SCSI_HOSTS; ++i) + { + memcpy ((void *) last_cmnd[i].sense_cmnd, (void *) generic_sense, + 6); + last_reset[i] = 0; + } + + scan_scsis(); /* scan for scsi devices */ + +#ifdef CONFIG_BLK_DEV_SD + sd_init(); /* init scsi disks */ +#endif + +#ifdef CONFIG_BLK_DEV_ST + st_init(); /* init scsi tapes */ +#endif + } +#endif diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h new file mode 100644 index 000000000000..60ca1761751a --- /dev/null +++ b/kernel/blk_drv/scsi/scsi.h @@ -0,0 +1,266 @@ +/* + * scsi.h Copyright (C) 1992 Drew Eckhardt + * generic SCSI package header file by + * Drew Eckhardt + * + * + */ + +#ifndef _SCSI_H + #define _SCSI_H +/* + $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/scsi.h,v 1.1 1992/07/24 06:27:38 root Exp root $ + + For documentation on the OPCODES, MESSAGES, and SENSE values, + please consult the SCSI standard. + +*/ + +/* + SCSI opcodes +*/ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define INQUIRY 0x12 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECIEVE_DAIGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a + +#define COMMAND_SIZE(opcode) ((opcode) ? ((opcode) > 0x20 ? 10 : 6) : 0) + +/* + MESSAGE CODES +*/ + +#define COMMAND_COMPLETE 0x00 +#define EXTENDED_MESSAGE 0x01 +#define SAVE_POINTERS 0x02 +#define RESTORE_POINTERS 0x03 +#define DISCONNECT 0x04 +#define INITIATOR_ERROR 0x05 +#define ABORT 0x06 +#define MESSAGE_REJECT 0x07 +#define NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define LINKED_CMD_COMPLETE 0x0a +#define LINKED_FLG_CMD_COMPLETE 0x0b +#define BUS_DEVICE_RESET 0x0c +#define IDENTIFY_BASE 0x80 +#define IDENTIFY(can_disconnect, lun) (IDENTIFY_BASE |\ + ((can_disconnect) ? 0x40 : 0) |\ + ((lun) & 0x07)) + + +/* + Status codes +*/ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c + +#define STATUS_MASK 0x1e + +/* + the return of the status word will be in the following format : + The low byte is the status returned by the SCSI command, + with vendor specific bits masked. + + The next byte is the message which followed the SCSI status. + This allows a stos to be used, since the Intel is a little + endian machine. + + The final byte is a host return code, which is one of the following. + + IE + lsb msb + status msg host code + + Our errors returned by OUR driver, NOT SCSI message. Orr'd with + SCSI message passed back to driver . +*/ + +/* NO error */ +#define DID_OK 0x00 +/* Couldn't connect before timeout period */ +#define DID_NO_CONNECT 0x01 +/* BUS stayed busy through time out period */ +#define DID_BUS_BUSY 0x02 +/* TIMED OUT for other reason */ +#define DID_TIME_OUT 0x03 +/* BAD target. */ +#define DID_BAD_TARGET 0x04 +/* Told to abort for some other reason */ +#define DID_ABORT 0x05 +/* + Parity error +*/ +#define DID_PARITY 0x06 +/* + Internal error +*/ +#define DID_ERROR 0x07 +/* + Reset by somebody. +*/ +#define DID_RESET 0x08 +/* + Got an interrupt we weren't expecting. +*/ +#define DID_BAD_INTR 0x09 + +/* + Driver status +*/ +#define DRIVER_OK 0x00 + +/* + These indicate the error that occured, and what is available. +*/ + +#define DRIVER_BUSY 0x01 +#define DRIVER_SOFT 0x02 +#define DRIVER_MEDIA 0x03 +#define DRIVER_ERROR 0x04 + +#define DRIVER_INVALID 0x05 +#define DRIVER_TIMEOUT 0x06 +#define DRIVER_HARD 0x07 + +#define SUGGEST_RETRY 0x08 +#define SUGGEST_ABORT 0x09 +#define SUGGEST_REMAP 0x0a +#define SUGGEST_DIE 0x0b + +#define DRIVER_SENSE 0x10 + +#define DRIVER_MASK 0x0f +#define SUGGEST_MASK 0xf0 + +/* + + SENSE KEYS +*/ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + DEVICE TYPES + +*/ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_NO_LUN 0x7f +/* + Every SCSI command starts with a one byte OP-code. + The next byte's high three bits are the LUN of the + device. Any multi-byte quantities are stored high byte + first, and may have a 5 bit MSB in the same byte + as the LUN. +*/ + + +/* + The scsi_device struct contains what we know about each given scsi + device. +*/ + +typedef struct scsi_device { + unsigned char host_no, id, lun; + unsigned writeable:1; + unsigned removable:1; + unsigned random:1; + } Scsi_Device; +/* + Use these to separate status msg and our bytes +*/ + +#define status_byte(result) (((result) >> 1) & 0xf) +#define msg_byte(result) (((result) >> 8) & 0xff) +#define host_byte(result) (((result) >> 16) & 0xff) +#define driver_byte(result) (((result) >> 24) & 0xff) +#define sugestion(result) (driver_byte(result) & SUGGEST_MASK) + +#define sense_class(sense) (((sense) >> 4) & 0x7) +#define sense_error(sense) ((sense) & 0xf) +#define sense_valid(sense) ((sense) & 0x80); + +/* + These are the SCSI devices available on the system. +*/ + +#define MAX_SCSI_DEVICE 4 +extern int NR_SCSI_DEVICES; +extern Scsi_Device scsi_devices[MAX_SCSI_DEVICE]; +/* + scsi_abort aborts the current command that is executing on host host. + The error code, if non zero is returned in the host byte, otherwise + DID_ABORT is returned in the hostbyte. +*/ + +extern int scsi_abort (int host, int code); + +/* + Initializes all SCSI devices. This scans all scsi busses. +*/ + +extern void scsi_dev_init (void); + +/* + You guesed it. This sends a command to the selected SCSI host +*/ + + + +extern void scsi_do_cmd (int host, unsigned char target, const void *cmnd , + void *buffer, unsigned bufflen, void (*done)(int,int), + int timeout, unsigned char *sense_buffer, int retries); + +extern int scsi_reset (int host); +#endif diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c new file mode 100644 index 000000000000..8a700b132396 --- /dev/null +++ b/kernel/blk_drv/scsi/scsi_ioctl.c @@ -0,0 +1,158 @@ +#include +#ifdef CONFIG_SCSI + +#include +#include +#include + +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "scsi_ioctl.h" + +#define MAX_RETRIES 5 +#define MAX_TIMEOUT 200 +#define MAX_BUF 8192 + +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +/* + * If we are told to probe a host, we will return 0 if the host is not + * present, 1 if the host is present, and will return an identifying + * string at *arg, if arg is non null, filling to the length stored at + * (int *) arg + */ + +static int ioctl_probe(int dev, void *buffer) +{ + int temp; + int len; + + if ((temp = scsi_hosts[dev].present) && buffer) { + len = get_fs_long ((int *) buffer); + memcpy_tofs (buffer, scsi_hosts[dev].info(), len); + } + return temp; +} + +/* + * + * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host. + * The MAX_TIMEOUT and MAX_RETRIES variables are used. + * + * dev is the SCSI device struct ptr, *(int *) arg is the length of the + * input data, if any, not including the command string & counts, + * *((int *)arg + 1) is the output buffer size in bytes. + * + * *(char *) ((int *) arg)[2] the actual command byte. + * + * Note that no more than MAX_BUF data bytes will be transfered. Since + * SCSI block device size is 512 bytes, I figured 1K was good. + * but (WDE) changed it to 8192 to handle large bad track buffers. + * + * This size *does not* include the initial lengths that were passed. + * + * The SCSI command is read from the memory location immediately after the + * length words, and the input data is right after the command. The SCSI + * routines know the command size based on the opcode decode. + * + * The output area is then filled in starting from the command byte. + */ + +static int the_result[MAX_SCSI_HOSTS]; + +static void scsi_ioctl_done (int host, int result) +{ + the_result[host] = result; +} + +static int ioctl_command(Scsi_Device *dev, void *buffer) +{ + char buf[MAX_BUF]; + char cmd[10]; + char * cmd_in; + unsigned char opcode; + int inlen, outlen, cmdlen, temp, host; + + if (!buffer) + return -EINVAL; + + inlen = get_fs_long((int *) buffer); + outlen = get_fs_long( ((int *) buffer) + 1); + + cmd_in = (char *) ( ((int *)buffer) + 2); + opcode = get_fs_byte(cmd_in); + + memcpy_fromfs ((void *) cmd, cmd_in, cmdlen = COMMAND_SIZE (opcode)); + memcpy_fromfs ((void *) buf, (void *) (cmd_in + cmdlen), inlen); + host = dev->host_no; + +#ifndef DEBUG_NO_CMD + do { + cli(); + if (the_result[host]) { + sti(); + while(the_result[host]) + /* nothing */; + } else { + the_result[host]=-1; + sti(); + break; + } + } while (1); + + scsi_do_cmd(host, dev->id, cmd, buf, ((outlen > MAX_BUF) ? + MAX_BUF : outlen), scsi_ioctl_done, MAX_TIMEOUT, + buf, MAX_RETRIES); + + while (the_result[host] == -1) + /* nothing */; + temp = the_result[host]; + the_result[host] = 0; + memcpy_tofs ((void *) cmd_in, buf, (outlen > MAX_BUF) ? MAX_BUF : outlen); + return temp; +#else + { + int i; + printk("scsi_ioctl : device %d. command = ", dev->id); + for (i = 0; i < 10; ++i) + printk("%02x ", cmd[i]); + printk("\r\nbuffer ="); + for (i = 0; i < 20; ++i) + printk("%02x ", buf[i]); + printk("\r\n"); + printk("inlen = %d, outlen = %d, cmdlen = %d\n", + inlen, outlen, cmdlen); + printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in); + } + return 0; +#endif +} + + +/* + the scsi_ioctl() function differs from most ioctls in that it does + not take a major/minor number as the dev filed. Rather, it takes + a pointer to a scsi_devices[] element, a structure. +*/ +int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) +{ + if ((cmd != 0 && dev->id > NR_SCSI_DEVICES)) + return -ENODEV; + if ((cmd == 0 && dev->host_no > MAX_SCSI_HOSTS)) + return -ENODEV; + + switch (cmd) { + case SCSI_IOCTL_PROBE_HOST: + return ioctl_probe(dev->host_no, arg); + case SCSI_IOCTL_SEND_COMMAND: + return ioctl_command((Scsi_Device *) dev, arg); + default : + return -EINVAL; + } +} +#endif diff --git a/kernel/blk_drv/scsi/scsi_ioctl.h b/kernel/blk_drv/scsi/scsi_ioctl.h new file mode 100644 index 000000000000..2acdb55fe4d7 --- /dev/null +++ b/kernel/blk_drv/scsi/scsi_ioctl.h @@ -0,0 +1,21 @@ +#ifndef _SCSI_IOCTL_H +#define _SCSI_IOCTL_H + +#ifndef _CONFIG_H +#include +#endif + +#define SCSI_IOCTL_PROBE_HOST 0 +#define SCSI_IOCTL_SEND_COMMAND 1 + +#ifdef CONFIG_BLK_DEV_SD +/* Should start at 128 */ +#endif + +#ifdef CONFIG_BLK_DEV_SD +/* Should start at 256 */ +#endif + +#endif + + diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c new file mode 100644 index 000000000000..5e034ccf4ecc --- /dev/null +++ b/kernel/blk_drv/scsi/sd.c @@ -0,0 +1,422 @@ +/* + * sd.c Copyright (C) 1992 Drew Eckhardt + * Linux scsi disk driver by + * Drew Eckhardt + * + * + */ + +#include + +#ifdef CONFIG_BLK_DEV_SD +#include +#include +#include +#include + +#include "scsi.h" +#include "sd.h" + +#define MAJOR_NR 8 + +#include "../blk.h" +#include + +/* +static const char RCSid[] = "$Header:"; +*/ + +#define MAX_RETRIES 5 + +/* + * Time out in seconds + */ + +#define SD_TIMEOUT 200 + +struct hd_struct sd[MAX_SD << 4]; + +int NR_SD=0; +Scsi_Disk rscsi_disks[MAX_SD]; +static int sd_sizes[MAX_SD << 4]; +static int this_count; +static int the_result; + +static char sense_buffer[255]; + +extern int sd_ioctl(struct inode *, struct file *, unsigned long, unsigned long); + +static void sd_release(struct inode * inode, struct file * file) +{ + sync_dev(inode->i_rdev); +} + +static struct gendisk sd_gendisk; + +static void sd_geninit (void) { + int i; + for (i = 0; i < NR_SD; ++i) + sd_sizes[i << 4] = + (sd[i << 4].nr_sects = rscsi_disks[i].capacity) >> + (BLOCK_SIZE_BITS - 9); + sd_gendisk.nr_real = NR_SD; +} + +static struct file_operations sd_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + sd_ioctl, /* ioctl */ + NULL, /* no special open code */ + sd_release /* release */ +}; + +static struct gendisk sd_gendisk = { + MAJOR_NR, /* Major number */ + "sd", /* Major name */ + 4, /* Bits to shift to get real from partition */ + 1 << 4, /* Number of partitions per real */ + MAX_SD, /* maximum number of real */ + sd_geninit, /* init function */ + sd, /* hd struct */ + sd_sizes, /* block sizes */ + 0, /* number */ + (void *) rscsi_disks, /* internal */ + NULL /* next */ +}; + +/* + rw_intr is the interrupt routine for the device driver. It will + be notified on the end of a SCSI read / write, and + will take on of several actions based on success or failure. +*/ + +static void rw_intr (int host, int result) +{ + if (HOST != host) + panic ("sd.o : rw_intr() recieving interrupt for different host."); + +#ifdef DEBUG + printk("sd%d : rw_intr(%d, %x)\n", MINOR(CURRENT->dev), host, result); +#endif + +/* + First case : we assume that the command succeeded. One of two things will + happen here. Either we will be finished, or there will be more + sectors that we were unable to read last time. +*/ + + if (!result) { + CURRENT->nr_sectors -= this_count; + +#ifdef DEBUG + printk("sd%d : %d sectors remain.\n", MINOR(CURRENT->dev), CURRENT->nr_sectors); +#endif + +/* + * If multiple sectors are requested in one buffer, then + * they will have been finished off by the first command. If + * not, then we have a multi-buffer command. + */ + if (CURRENT->nr_sectors) + { + CURRENT->sector += this_count; + CURRENT->errors = 0; + + if (!CURRENT->bh) + { +#ifdef DEBUG + printk("sd%d : handling page request, no buffer\n", + MINOR(CURRENT->dev)); +#endif + +/* + The CURRENT->nr_sectors field is always done in 512 byte sectors, + even if this really isn't the case. +*/ + (char *) CURRENT->buffer += this_count << 9; + } + else + { +#ifdef DEBUG + printk("sd%d : handling linked buffer request\n", MINOR(CURRENT->dev)); +#endif + end_request(1); + } + } + else + end_request(1); + do_sd_request(); + } + +/* + * Of course, the error handling code is a little Fubar down in scsi.c. + * Version 2 of the drivers will fix that, and we will *really* recover + * from errors. + */ + +/* + Now, if we were good little boys and girls, Santa left us a request + sense buffer. We can extract information from this, so we + can choose a block to remap, etc. +*/ + + else if (driver_byte(result) & DRIVER_SENSE) { + if (sugestion(result) == SUGGEST_REMAP) { +#ifdef REMAP +/* + Not yet implemented. A read will fail after being remapped, + a write will call the strategy routine again. +*/ + + if rscsi_disks[DEVICE_NR(CURRENT->dev)].remap + { + result = 0; + } + else + +#endif + } +/* + If we had an ILLEGAL REQUEST returned, then we may have performed + an unsupported command. The only thing this should be would be a ten + byte read where only a six byte read was supportted. Also, on a + system where READ CAPACITY failed, we mave have read past the end of the + disk. +*/ + + else if (sense_buffer[7] == ILLEGAL_REQUEST) { + if (rscsi_disks[DEVICE_NR(CURRENT->dev)].ten) { + rscsi_disks[DEVICE_NR(CURRENT->dev)].ten = 0; + do_sd_request(); + result = 0; + } else { + } + } + } + if (result) { + printk("SCSI disk error : host %d id %d lun %d return code = %03x\n", + rscsi_disks[DEVICE_NR(CURRENT->dev)].device->host_no, + rscsi_disks[DEVICE_NR(CURRENT->dev)].device->id, + rscsi_disks[DEVICE_NR(CURRENT->dev)].device->lun); + + if (driver_byte(result) & DRIVER_SENSE) + printk("\tSense class %x, sense error %x, extended sense %x\n", + sense_class(sense_buffer[0]), + sense_error(sense_buffer[0]), + sense_buffer[2] & 0xf); + + end_request(0); + } +} + +/* + do_sd_request() is the request handler function for the sd driver. + Its function in life is to take block device requests, and translate + them to SCSI commands. +*/ + +static void do_sd_request (void) +{ + int dev, block; + unsigned char cmd[10]; + +repeat: + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + +#ifdef DEBUG + printk("Doing sd request, dev = %d, block = %d\n", dev, block); +#endif + + if (dev >= (NR_SD << 4) || block + CURRENT->nr_sectors > sd[dev].nr_sects) + { + end_request(0); + goto repeat; + } + + block += sd[dev].start_sect; + dev = DEVICE_NR(dev); + +#ifdef DEBUG + printk("sd%d : real dev = /dev/sd%d, block = %d\n", MINOR(CURRENT->dev), dev, block); +#endif + + + if (!CURRENT->bh) + this_count = CURRENT->nr_sectors; + else + this_count = (BLOCK_SIZE / 512); + +#ifdef DEBUG + printk("sd%d : %s %d/%d 512 byte blocks.\n", MINOR(CURRENT->dev), + (CURRENT->cmd == WRITE) ? "writing" : "reading", + this_count, CURRENT->nr_sectors); +#endif + + switch (CURRENT->cmd) + { + case WRITE : + if (!rscsi_disks[dev].device->writeable) + { + end_request(0); + goto repeat; + } + cmd[0] = WRITE_6; + break; + case READ : + cmd[0] = READ_6; + break; + default : + printk ("Unknown sd command %d\r\n", CURRENT->cmd); + panic(""); + } + + cmd[1] = (LUN << 5) & 0xe0; + + if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten) + { + if (this_count > 0xffff) + this_count = 0xffff; + + cmd[0] += READ_10 - READ_6 ; + cmd[2] = (unsigned char) (block >> 24) & 0xff; + cmd[3] = (unsigned char) (block >> 16) & 0xff; + cmd[4] = (unsigned char) (block >> 8) & 0xff; + cmd[5] = (unsigned char) block & 0xff; + cmd[6] = cmd[9] = 0; + cmd[7] = (unsigned char) (this_count >> 8) & 0xff; + cmd[8] = (unsigned char) this_count & 0xff; + } + else + { + if (this_count > 0xff) + this_count = 0xff; + + cmd[1] |= (unsigned char) ((block >> 16) & 0x1f); + cmd[2] = (unsigned char) ((block >> 8) & 0xff); + cmd[3] = (unsigned char) block & 0xff; + cmd[4] = (unsigned char) this_count; + cmd[5] = 0; + } + + scsi_do_cmd (HOST, ID, (void *) cmd, CURRENT->buffer, this_count << 9, + rw_intr, SD_TIMEOUT, sense_buffer, MAX_RETRIES); +} + +static void sd_init_done (int host, int result) +{ + the_result = result; +} + +/* + The sd_init() function looks at all SCSI drives present, determines + their size, and reads partition table entries for them. +*/ + +void sd_init(void) +{ + int i,j; + unsigned char cmd[10]; + unsigned char buffer[513]; + int try_again; + + + for (i = 0; i < NR_SD; ++i) + { + try_again=2; + cmd[0] = READ_CAPACITY; + cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; + memset ((void *) &cmd[2], 0, 8); + +/* + * Super Kludge - since the midlevel error handling code doesn't work + * Version 2 will - it's under development 8^) + * + * We manually retry + */ + + + do { + the_result = -1; +#ifdef DEBUG + printk("sd%d : READ CAPACITY\n ", i); +#endif + scsi_do_cmd (rscsi_disks[i].device->host_no , + rscsi_disks[i].device->id, + (void *) cmd, (void *) buffer, + 512, sd_init_done, SD_TIMEOUT, sense_buffer, + MAX_RETRIES); + + while(the_result < 0); + } while (try_again && the_result); +/* + * The SCSI standard says "READ CAPACITY is necessary for self confuring software" + * While not mandatory, support of READ CAPACITY is strongly encouraged. + * We used to die if we couldn't successfully do a READ CAPACITY. + * But, now we go on about our way. The side effects of this are + * + * 1. We can't know block size with certainty. I have said "512 bytes is it" + * as this is most common. + * + * 2. Recovery from when some one attempts to read past the end of the raw device will + * be slower. + */ + + if (the_result) + { + printk ("sd%d : READ CAPACITY failed.\n" + "sd%d : status = %x, message = %02x, host = %02x, driver = %02x \n", + i,i, + rscsi_disks[i].device->host_no, rscsi_disks[i].device->id, + rscsi_disks[i].device->lun, + status_byte(the_result), + msg_byte(the_result), + host_byte(the_result), + driver_byte(the_result) + ); + if (driver_byte(the_result) & DRIVER_SENSE) + printk("sd%d : extended sense code = %1x \n", i, sense_buffer[2] & 0xf); + else + printk("sd%d : sense not available. \n", i); + + printk("sd%d : block size assumed to be 512 bytes, disk size 1GB. \n", i); + rscsi_disks[i].capacity = 0x1fffff; + rscsi_disks[i].sector_size = 512; + } + else + { + rscsi_disks[i].capacity = (buffer[0] << 24) | + (buffer[1] << 16) | + (buffer[2] << 8) | + buffer[3]; + + if ((rscsi_disks[i].sector_size = (buffer[4] << 24) | + (buffer[5] << 16) | + (buffer[6] << 8) | + buffer[7]) != 512) + { + printk ("sd%d : unsupported sector size %d.\n", + i, rscsi_disks[i].sector_size); + printk ("scsi : deleting disk entry.\n"); + for (j=i; j < NR_SD;) + rscsi_disks[j] = rscsi_disks[++j]; + --i; + continue; + } + } + + rscsi_disks[i].ten = 1; + rscsi_disks[i].remap = 1; + } + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + blk_size[MAJOR_NR] = sd_sizes; + blkdev_fops[MAJOR_NR] = &sd_fops; + sd_gendisk.next = gendisk_head; + gendisk_head = &sd_gendisk; +} +#endif diff --git a/kernel/blk_drv/scsi/sd.h b/kernel/blk_drv/scsi/sd.h new file mode 100644 index 000000000000..aacb01f9e943 --- /dev/null +++ b/kernel/blk_drv/scsi/sd.h @@ -0,0 +1,50 @@ +/* + * sd.h Copyright (C) 1992 Drew Eckhardt + * SCSI disk driver header file by + * Drew Eckhardt + * + * + */ +#ifndef _SD_H + #define _SD_H +/* + $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/sd.h,v 1.1 1992/07/24 06:27:38 root Exp root $ +*/ + +#ifndef _SCSI_H +#include "scsi.h" +#endif + +#ifndef _GENDISK_H +#include +#endif + +/* + This is an arbitrary constant, and may be changed to whatever + suits your purposes. Note that smaller will get you a few bytes + more in kernel space if that is your thing. +*/ + +#define MAX_SD 4 +extern int NR_SD; + +extern struct hd_struct sd[MAX_SD << 4]; + +typedef struct { + unsigned capacity; /* size in blocks */ + unsigned sector_size; /* size in bytes */ + Scsi_Device *device; + unsigned char sector_bit_size; /* sector_size = 2 to the bit size power */ + unsigned char sector_bit_shift; /* power of 2 sectors per FS block */ + unsigned ten:1; /* support ten byte read / write */ + unsigned remap:1; /* support remapping */ + } Scsi_Disk; + +extern Scsi_Disk rscsi_disks[MAX_SD]; + +void sd_init(void); + +#define HOST (rscsi_disks[DEVICE_NR(CURRENT->dev)].device->host_no) +#define ID (rscsi_disks[DEVICE_NR(CURRENT->dev)].device->id) +#define LUN (rscsi_disks[DEVICE_NR(CURRENT->dev)].device->lun) +#endif diff --git a/kernel/blk_drv/scsi/sd_ioctl.c b/kernel/blk_drv/scsi/sd_ioctl.c new file mode 100644 index 000000000000..d2f18d264eac --- /dev/null +++ b/kernel/blk_drv/scsi/sd_ioctl.c @@ -0,0 +1,20 @@ +#include +#ifdef CONFIG_BLK_DEV_SD +#include +#include +#include +#include "scsi.h" +#include "sd.h" + +extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg); + +int sd_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg) +{ + int dev = inode->i_rdev; + + switch (cmd) { + default: + return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device, cmd, (void *) arg); + } +} +#endif diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c new file mode 100644 index 000000000000..e36dbec15661 --- /dev/null +++ b/kernel/blk_drv/scsi/seagate.c @@ -0,0 +1,987 @@ +/* + * seagate.c Copyright (C) 1992 Drew Eckhardt + * low level scsi driver for ST01/ST02 by + * Drew Eckhardt + * + * + */ + +#include + +#if defined(CONFIG_SCSI_SEAGATE) || defined(CONFIG_SCSI_FD_88x) +#include +#include +#include +#include "seagate.h" +#include "scsi.h" +#include "hosts.h" + +extern void seagate_intr(void); +static int internal_command(unsigned char target, const void *cmnd, + void *buff, int bufflen, int reselect); +void (*do_seagate)(void) = NULL; + +static int incommand; /* + set if arbitration has finished and we are + in some command phase. + */ + +static void *base_address = NULL; /* + Where the card ROM starts, + used to calculate memory mapped + register location. + */ +static volatile int abort_confirm = 0; + +static volatile void *st0x_cr_sr; /* + control register write, + status register read. + 256 bytes in length. + + Read is status of SCSI BUS, + as per STAT masks. + + */ + + +static volatile void *st0x_dr; /* + data register, read write + 256 bytes in length. + */ + + +static volatile int st0x_aborted=0; /* + set when we are aborted, ie by a time out, etc. + */ + + /* + In theory, we have a nice auto + detect routine - but this + overides it. + */ + + +#define retcode(result) (((result) << 16) | (message << 8) | status) +#define STATUS (*(unsigned char *) st0x_cr_sr) +#define CONTROL STATUS +#define DATA (*(unsigned char *) st0x_dr) + +#ifndef OVERRIDE +static const char * seagate_bases[] = {(char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000, (char *) 0xce000, (char *) 0xce000, + (char *) 0xdc000, (char *) 0xde000}; +typedef struct + { + char *signature ; + unsigned offset; + unsigned length; + } Signature; + +static const Signature signatures[] = { +#ifdef CONFIG_SCSI_SEAGATE +{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40}, + +/* + The following two lines are NOT mistakes. One detects + ROM revision 3.0.0, the other 3.2. Since seagate + has only one type of SCSI adapter, and this is not + going to change, the "SEAGATE" and "SCSI" together + are probably "good enough" +*/ + +{"SEAGATE SCSI BIOS ",16, 17}, +{"SEAGATE SCSI BIOS ",17, 17}, +#endif + +/* + This is for the Future Domain 88x series. I've been told that + the Seagate controllers are just repackages of these, and seeing + early seagate BIOS bearing the Future Domain copyright, + I believe it. +*/ + +#ifdef CONFIG_SCSI_FD_88x +{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/90", 5, 46}, +#endif +} +; +/* + Note that the last signature handles BIOS revisions 3.0.0 and + 3.2 - the real ID's are + +SEAGATE SCSI BIOS REVISION 3.0.0 +SEAGATE SCSI BIOS REVISION 3.2 + +*/ + +#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature)) +#endif + +/* + * hostno stores the hostnumber, as told to us by the init routine. + */ + +static int hostno = -1; + +int seagate_st0x_detect (int hostnum) + { +#ifndef OVERRIDE + int i,j; +#endif + +/* + * First, we try for the manual override. + */ +#ifdef DEBUG + printk("Autodetecting seagate ST0x\n"); +#endif + + if (hostno != -1) + { + printk ("ERROR : seagate_st0x_detect() called twice.\n"); + return 0; + } + + base_address = NULL; +#ifdef OVERRIDE + base_address = (void *) OVERRIDE; +#ifdef DEBUG + printk("Base address overridden to %x\n", base_address); +#endif +#else +/* + * To detect this card, we simply look for the signature + * from the BIOS version notice in all the possible locations + * of the ROM's. This has a nice sideeffect of not trashing + * any register locations that might be used by something else. + */ + + for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i) + for (j = 0; !base_address && j < NUM_SIGNATURES; ++j) + if (!memcmp ((void *) (seagate_bases[i] + + signatures[j].offset), (void *) signatures[j].signature, + signatures[j].length)) + base_address = (void *) seagate_bases[i]; + #endif + + if (base_address) + { + st0x_cr_sr =(void *) (((unsigned char *) base_address) + 0x1a00); + st0x_dr = (void *) (((unsigned char *) base_address )+ 0x1c00); +#ifdef DEBUG + printk("ST0x detected. Base address = %x, cr = %x, dr = %x\n", base_address, st0x_cr_sr, st0x_dr); +#endif + hostno = hostnum; + +/* + * At all times, we will use IRQ 5. + */ + +#if 1 + set_intr_gate (0x25, seagate_intr); + __asm__(" + inb $0x21, %%al + andb $0xdf, %%al + outb %%al, $0x21"::); +#endif + return -1; + } + else + { +#ifdef DEBUG + printk("ST0x not detected.\n"); +#endif + return 0; + } + } + +char *seagate_st0x_info(void) +{ + static char buffer[] = "Seagate ST-0X SCSI driver by Drew Eckhardt \n" +"$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/seagate.c,v 1.1 1992/07/24 06:27:38 root Exp root $\n"; + return buffer; +} + +/* + * These are our saved pointers for the outstanding command that is + * waiting for a reconnect + */ + +static unsigned char current_target; +static unsigned char *current_cmnd, *current_data; +static int current_bufflen; +static void (*done_fn)(int, int) = NULL; + +/* + * These control weather or not disconnect / reconnect will be attempted, + * or are being attempted. + */ + +#define NO_RECONNECT 0 +#define RECONNECT_NOW 1 +#define CAN_RECONNECT 2 + +/* + * This determines if we are expecting to reconnect or not. + */ + +static int should_reconnect = 0; + +void seagate_unexpected_intr (void) + { + printk("scsi%d: unexpected interrupt.\n", hostno); + } + +/* + * The seagate_reconnect_intr routine is called when a target reselects the + * host adapter. This occurs on the interrupt triggered by the target + * asserting SEL. + */ + +void seagate_reconnect_intr (void) + { + int temp; + +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : seagate_reconnect_intr() called\n", hostno); +#endif + + if (!should_reconnect) + seagate_unexpected_intr(); + else + { + should_reconnect = 0; + +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : internal_command(" + "%d, %08x, %08x, %d, RECONNECT_NOW\n", hostno, + current_target, current_data, current_bufflen); +#endif + + temp = internal_command (current_target, + current_cmnd, current_data, current_bufflen, + RECONNECT_NOW); + + if (msg_byte(temp) != DISCONNECT) + { + if (done_fn) + { +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : done_fn(%d,%08x)", hostno, + hostno, temp); +#endif + done_fn (hostno, temp); + } + else + printk("done_fn() not defined.\n"); + } + } + } + +/* + * The seagate_st0x_queue_command() function provides a queued interface + * to the seagate SCSI driver. Basically, it just passes control onto the + * seagate_command() function, after fixing it so that the done_fn() + * is set to the one passed to the function. + */ + +int seagate_st0x_queue_command (unsigned char target, const void *cmnd, + void *buff, int bufflen, void (*fn)(int, + int)) + { + int result; + + done_fn = fn; + current_target = target; + (const void *) current_cmnd = cmnd; + current_data = buff; + current_bufflen = bufflen; + + result = internal_command (target, cmnd, buff, bufflen, + CAN_RECONNECT); + if (msg_byte(result) == DISCONNECT) + return 0; + else + { + done_fn (hostno, result); + return 1; + } + } + +int seagate_st0x_command (unsigned char target, const void *cmnd, + void *buff, int bufflen) + { + return internal_command (target, cmnd, buff, bufflen, + (int) NO_RECONNECT); + } + +static int internal_command(unsigned char target, const void *cmnd, + void *buff, int bufflen, int reselect) + { + int len; + unsigned char *data; + int clock; + int temp; + + +#if ((DEBUG & PHASE_ETC) || (DEBUG & PRINT_COMMAND) || (DEBUG & PHASE_EXIT)) + int i; +#endif + +#if (DEBUG & PHASE_ETC) + int phase=0, newphase; +#endif + + int done = 0; + unsigned char status = 0; + unsigned char message = 0; + register unsigned char status_read; + + do_seagate = seagate_unexpected_intr; + + len=bufflen; + data=(unsigned char *) buff; + + incommand = 0; + st0x_aborted = 0; + +#if (DEBUG & PRINT_COMMAND) + printk ("scsi%d : target = %d, command = ", hostno, target); + for (i = 0; i < COMMAND_SIZE(((unsigned char *)cmnd)[0]); ++i) + printk("%02x ", ((unsigned char *) cmnd)[i]); + printk("\n"); +#endif + +#if (DEBUG & PHASE_RESELECT) + switch (reselect) + { + case RECONNECT_NOW : + printk("scsi%d : reconnecting\n", hostno); + break; + case CAN_RECONNECT : + printk("scsi%d : allowed to reconnect\n", hostno); + break; + default : + printk("scsi%d : not allowed to reconnect\n", hostno); + } +#endif + + + if (target > 6) + { + if (reselect == RECONNECT_NOW) + eoi(); + return DID_BAD_TARGET; + } + +/* + * We work it differently depending on if this is is "the first time," + * or a reconnect. If this is a reselct phase, then SEL will + * be asserted, and we must skip selection / arbitration phases. + */ + + if (reselect == RECONNECT_NOW) + { +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : phase RESELECT \n", hostno); +#endif + +/* + * At this point, we should find the logical or of our ID and the original + * target's ID on the BUS, with BSY, SEL, and I/O signals asserted. + * + * After ARBITRATION phase is completed, only SEL, BSY, and the + * target ID are asserted. A valid initator ID is not on the bus + * until IO is asserted, so we must wait for that. + */ + + for (clock = jiffies + 10, temp = 0; (jiffies < clock) && + !(STATUS & STAT_IO);); + + if (jiffies >= clock) + { +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : RESELECT timed out while waiting for IO .\n", + hostno); +#endif + eoi(); + return (DID_BAD_INTR << 16); + } + +/* + * After I/O is asserted by the target, we can read our ID and its + * ID off of the BUS. + */ + + if (!((temp = DATA) & 0x80)) + { +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : detected reconnect request to different target.\n" + "\tData bus = %d\n", hostno, temp); +#endif + eoi(); + return (DID_BAD_INTR << 16); + } + + if (!(temp & (1 << current_target))) + { + printk("scsi%d : Unexpected reselect interrupt. Data bus = %d\n", + hostno, temp); + eoi(); + return (DID_BAD_INTR << 16); + } + data=current_data; /* WDE add */ + cmnd=current_cmnd; /* WDE add */ + len=current_bufflen; /* WDE add */ + +/* + * We have determined that we have been selected. At this point, + * we must respond to the reselection by asserting BSY ourselves + */ + + CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY); + +/* + * The target will drop SEL, and raise BSY, at which time we must drop + * BSY. + */ + + for (clock = jiffies + 10; (jiffies < clock) && (STATUS & STAT_SEL);); + + if (jiffies >= clock) + { + CONTROL = (BASE_CMD | CMD_INTR); +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : RESELECT timed out while waiting for SEL.\n", + hostno); +#endif + eoi(); + return (DID_BAD_INTR << 16); + } + + CONTROL = BASE_CMD; + +/* + * At this point, we have connected with the target and can get + * on with our lives. + */ + eoi(); + } + else + { +#if (DEBUG & PHASE_BUS_FREE) + printk ("scsi%d : phase = BUS FREE \n", hostno); +#endif + +/* + * BUS FREE PHASE + * + * On entry, we make sure that the BUS is in a BUS FREE + * phase, by insuring that both BSY and SEL are low for + * at least one bus settle delay. Several reads help + * eliminate wire glitch. + */ + + clock = jiffies + ST0X_BUS_FREE_DELAY; + + while (((STATUS | STATUS | STATUS) & + (STAT_BSY | STAT_SEL)) && + (!st0x_aborted) && (jiffies < clock)); + + if (jiffies > clock) + return retcode(DID_BUS_BUSY); + else if (st0x_aborted) + return retcode(st0x_aborted); + +/* + * Bus free has been detected, within BUS settle. I used to + * support an arbitration phase - however, on the Seagate, this + * degraded performance by a factor > 10 - so it is no more. + */ + +/* + * SELECTION PHASE + * + * Now, we select the disk, giving it the SCSI ID at data + * and a command of PARITY if necessary, and we raise SEL. + */ + +#if (DEBUG & PHASE_SELECTION) + printk("scsi%d : phase = SELECTION\n", hostno); +#endif + + clock = jiffies + ST0X_SELECTION_DELAY; + +/* + * If we wish to disconnect, we should request a MESSAGE OUT + * at this point. Technically, ATTN should be raised before + * SEL = true and BSY = false (from arbitration), but I think this + * should do. + */ + if (reselect) + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | + CMD_ATTN; + +/* + * We must assert both our ID and our target's ID on the bus. + */ + DATA = (unsigned char) ((1 << target) | 0x80); + +/* + * If we are allowing ourselves to reconnect, then I will keep + * ATTN raised so we get MSG OUT. + */ + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | + (reselect ? CMD_ATTN : 0); + +/* + * When the SCSI device decides that we're gawking at it, it will + * respond by asserting BUSY on the bus. + */ + while (!((status_read = STATUS) & STAT_BSY) && + (jiffies < clock) && !st0x_aborted) + +#if (DEBUG & PHASE_SELECTION) + { + temp = clock - jiffies; + + if (!(jiffies % 5)) + printk("seagate_st0x_timeout : %d \r",temp); + + } + printk("Done. \n\r"); + printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n", + hostno, status_read, temp, st0x_aborted); +#else + ; +#endif + + + if ((jiffies > clock) || (!st0x_aborted && + !(status_read & STAT_BSY))) + { +#if (DEBUG & PHASE_SELECT) + printk ("scsi%d : NO CONNECT with target %d, status = %x \n", + hostno, target, STATUS); +#endif + return retcode(DID_NO_CONNECT); + } + +/* + * If we have been aborted, and we have a command in progress, IE the + * target still has BSY asserted, then we will reset the bus, and + * notify the midlevel driver to expect sense. + */ + + if (st0x_aborted) + { + CONTROL = BASE_CMD; + if (STATUS & STAT_BSY) + { + seagate_st0x_reset(); + return retcode(DID_RESET); + } + return retcode(st0x_aborted); + } + } + + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | + ((reselect == CAN_RECONNECT) ? CMD_ATTN : 0) ; + +/* + * INFORMATION TRANSFER PHASE + * + * The nasty looking read / write inline assembler loops we use for + * DATAIN and DATAOUT phases are approximately 4-5 times as fast as + * the 'C' versions - since we're moving 1024 bytes of data, this + * really adds up. + */ + +#if (DEBUG & PHASE_ETC) + printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno); +#endif + + incommand = 1; + + +/* + * Now, we poll the device for status information, + * and handle any requests it makes. Note that since we are unsure of + * how much data will be flowing across the system, etc and cannot + * make reasonable timeouts, that we will instead have the midlevel + * driver handle any timeouts that occur in this phase. + */ + + while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done) + { +#ifdef PARITY + if (status_read & STAT_PARITY) + { + done = 1; + st0x_aborted = DID_PARITY; + } +#endif + + if (status_read & STAT_REQ) + { +#if (DEBUG & PHASE_ETC) + if ((newphase = (status_read & REQ_MASK)) != phase) + { + phase = newphase; + switch (phase) + { + case REQ_DATAOUT: + printk("scsi%d : phase = DATA OUT\n", + hostno); + break; + case REQ_DATAIN : + printk("scsi%d : phase = DATA IN\n", + hostno); + break; + case REQ_CMDOUT : + printk("scsi%d : phase = COMMAND OUT\n", + hostno); + break; + case REQ_STATIN : + printk("scsi%d : phase = STATUS IN\n", + hostno); + break; + case REQ_MSGOUT : + printk("scsi%d : phase = MESSAGE OUT\n", + hostno); + break; + case REQ_MSGIN : + printk("scsi%d : phase = MESSAGE IN\n", + hostno); + break; + default : + printk("scsi%d : phase = UNKNOWN\n", + hostno); + st0x_aborted = 1; + done = 1; + } + } +#endif + + switch (status_read & REQ_MASK) + { + case REQ_DATAOUT : + +/* + * We loop as long as we are in a data out phase, there is data to send, + * and BSY is still active. + */ + __asm__ (" + +/* + Local variables : + len = ecx + data = esi + st0x_cr_sr = ebx + st0x_dr = edi + + Test for any data here at all. +*/ + movl %0, %%esi /* local value of data */ + movl %1, %%ecx /* local value of len */ + orl %%ecx, %%ecx + jz 2f + + cld + + movl _st0x_cr_sr, %%ebx + movl _st0x_dr, %%edi + +1: movb (%%ebx), %%al +/* + Test for BSY +*/ + + test $1, %%al + jz 2f + +/* + Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0. +*/ + test $0xe, %%al + jnz 2f +/* + Test for REQ +*/ + test $0x10, %%al + jz 1b + lodsb + movb %%al, (%%edi) + loop 1b + +2: + movl %%esi, %2 + movl %%ecx, %3 + ": +/* output */ +"=r" (data), "=r" (len) : +/* input */ +"0" (data), "1" (len) : +/* clobbered */ +"ebx", "ecx", "edi", "esi"); + + break; + + case REQ_DATAIN : +/* + * We loop as long as we are in a data in phase, there is room to read, + * and BSY is still active + */ + + __asm__ (" +/* + Local variables : + ecx = len + edi = data + esi = st0x_cr_sr + ebx = st0x_dr + + Test for room to read +*/ + + movl %0, %%edi /* data */ + movl %1, %%ecx /* len */ + orl %%ecx, %%ecx + jz 2f + + cld + movl _st0x_cr_sr, %%esi + movl _st0x_dr, %%ebx + +1: movb (%%esi), %%al +/* + Test for BSY +*/ + + test $1, %%al + jz 2f + +/* + Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, = STAT_IO, which is 4. +*/ + movb $0xe, %%ah + andb %%al, %%ah + cmpb $0x04, %%ah + jne 2f + +/* + Test for REQ +*/ + test $0x10, %%al + jz 1b + + movb (%%ebx), %%al + stosb + loop 1b + +2: movl %%edi, %2 /* data */ + movl %%ecx, %3 /* len */ + ": +/* output */ +"=r" (data), "=r" (len) : +/* input */ +"0" (data), "1" (len) : +/* clobbered */ +"ebx", "ecx", "edi", "esi"); + break; + + case REQ_CMDOUT : + while (((status_read = STATUS) & STAT_BSY) && + ((status_read & REQ_MASK) == REQ_CMDOUT)) + if (status_read & STAT_REQ) + DATA = *(unsigned char *) cmnd ++; + break; + + case REQ_STATIN : + status = DATA; + break; + + case REQ_MSGOUT : +/* + * We can only have sent a MSG OUT if we requested to do this + * by raising ATTN. So, we must drop ATTN. + */ + + CONTROL = BASE_CMD | CMD_DRVR_ENABLE; +/* + * If we are reconecting, then we must send an IDENTIFY message in + * response to MSGOUT. + */ + if (reselect) + { + DATA = IDENTIFY(1,0); +#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT)) + printk("scsi%d : sent IDENTIFY message.\n", hostno); +#endif + } + else + { + DATA = MESSAGE_REJECT; + +#if (DEBUG & PHASE_MSGOUT) + printk("scsi%d : sent MESSAGE REJECT message.\n", hostno); +#endif + } + break; + + case REQ_MSGIN : + switch (message = DATA) + { + case DISCONNECT : + should_reconnect = 1; + current_data = data; /* WDE add */ + current_bufflen = len; /* WDE add */ +#if (DEBUG & (PHASE_RESELECT | PHASE_MSGIN)) + printk("scsi%d : disconnected.\n", hostno); + done=1; + break; +#endif + case COMMAND_COMPLETE : +#if (DEBUG & PHASE_MSGIN) + printk("scsi%d : command complete.\n", hostno); + done=1; + break; +#endif + case ABORT : +#if (DEBUG & PHASE_MSGIN) + printk("scsi%d : abort message.\n", hostno); +#endif + done=1; + break; + case SAVE_POINTERS : + current_data = data; /* WDE mod */ + current_bufflen = len; /* WDE add */ +#if (DEBUG & PHASE_MSGIN) + printk("scsi%d : pointers saved.\n", hostno); +#endif + break; + case RESTORE_POINTERS: + data=current_data; /* WDE mod */ + cmnd=current_cmnd; +#if (DEBUG & PHASE_MSGIN) + printk("scsi%d : pointers restored.\n", hostno); +#endif + break; + default: + +/* + * IDENTIFY distinguishes itself from the other messages by setting the + * high byte. + */ + + if (message & 0x80) + { +#if (DEBUG & PHASE_MSGIN) + printk("scsi%d : IDENTIFY message received from id %d, lun %d.\n", + hostno, target, message & 7); +#endif + } + else + { + +#if (DEBUG & PHASE_MSGIN) + printk("scsi%d : unknown message %d from target %d.\n", + hostno, message, target); +#endif + } + } + break; + + default : + printk("scsi%d : unknown phase.\n", hostno); + st0x_aborted = DID_ERROR; + } + } /* while ends */ + } /* if ends */ + +#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT)) + printk("Transfered %d bytes, allowed %d additional bytes\n", (bufflen - len), len); +#endif + +#if (DEBUG & PHASE_EXIT) + printk("Buffer : \n"); + for (i = 0; i < 20; ++i) + printk ("%02x ", ((unsigned char *) data)[i]); /* WDE mod */ + printk("\n"); + printk("Status = %02x, message = %02x\n", status, message); +#endif + + + if (st0x_aborted) + { + if (STATUS & STAT_BSY) + { + seagate_st0x_reset(); + st0x_aborted = DID_RESET; + } + abort_confirm = 1; + } + + if (should_reconnect) + { +#if (DEBUG & PHASE_RESELECT) + printk("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n", + hostno); +#endif + do_seagate = seagate_reconnect_intr; + CONTROL = BASE_CMD | CMD_INTR ; + } + else + CONTROL = BASE_CMD; + + return retcode (st0x_aborted); + } + +int seagate_st0x_abort (int code) + { + if (code) + st0x_aborted = code; + else + st0x_aborted = DID_ABORT; + + return 0; + } + +/* + the seagate_st0x_reset function resets the SCSI bus +*/ + +int seagate_st0x_reset (void) + { + unsigned clock; + /* + No timeouts - this command is going to fail because + it was reset. + */ + +#ifdef DEBUG + printk("In seagate_st0x_reset()\n"); +#endif + + + /* assert RESET signal on SCSI bus. */ + + CONTROL = BASE_CMD | CMD_RST; + clock=jiffies+2; + + + /* Wait. */ + + while (jiffies < clock); + + CONTROL = BASE_CMD; + + st0x_aborted = DID_RESET; + +#ifdef DEBUG + printk("SCSI bus reset.\n"); +#endif + return 0; + } + +#endif + diff --git a/kernel/blk_drv/scsi/seagate.h b/kernel/blk_drv/scsi/seagate.h new file mode 100644 index 000000000000..0eb3b30da98f --- /dev/null +++ b/kernel/blk_drv/scsi/seagate.h @@ -0,0 +1,131 @@ +/* + * seagate.h Copyright (C) 1992 Drew Eckhardt + * low level scsi driver header for ST01/ST02 by + * Drew Eckhardt + * + * + */ + +#ifndef _SEAGATE_H + #define SEAGATE_H +/* + $Header +*/ +#ifndef ASM +int seagate_st0x_detect(int); +int seagate_st0x_command(unsigned char target, const void *cmnd, void *buff, + int bufflen); +int seagate_st0x_queue_command(unsigned char target, const void *cmnd, + void *buff, int bufflen, void (*done)(int, int)); + +int seagate_st0x_abort(int); +char *seagate_st0x_info(void); +int seagate_st0x_reset(void); + +#ifndef NULL + #define NULL 0 +#endif + +#define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \ + seagate_st0x_info, seagate_st0x_command, \ + seagate_st0x_queue_command, seagate_st0x_abort, \ + seagate_st0x_reset, 1, 7, 0} +#endif + + +/* + defining PARITY causes parity data to be checked +*/ + +#define PARITY + +/* + defining ARBITRATE causes the arbitration sequence to be used. And speed to drop by a + factor of ten. +*/ + +#undef ARBITRATE + + +/* + Thanks to Brian Antoine for the example code in his Messy-Loss ST-01 + driver, and Mitsugu Suzuki for information on the ST-01 + SCSI host. +*/ + +/* + CONTROL defines +*/ + +#define CMD_RST 0x01 +#define CMD_SEL 0x02 +#define CMD_BSY 0x04 +#define CMD_ATTN 0x08 +#define CMD_START_ARB 0x10 +#define CMD_EN_PARITY 0x20 +#define CMD_INTR 0x40 +#define CMD_DRVR_ENABLE 0x80 + +/* + STATUS +*/ + +#define STAT_BSY 0x01 +#define STAT_MSG 0x02 +#define STAT_IO 0x04 +#define STAT_CD 0x08 +#define STAT_REQ 0x10 +#define STAT_SEL 0x20 +#define STAT_PARITY 0x40 +#define STAT_ARB_CMPL 0x80 + +/* + REQUESTS +*/ + +#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG) +#define REQ_DATAOUT 0 +#define REQ_DATAIN STAT_IO +#define REQ_CMDOUT STAT_CD +#define REQ_STATIN (STAT_CD | STAT_IO) +#define REQ_MSGOUT (STAT_MSG | STAT_CD) +#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO) + +extern volatile int seagate_st0x_timeout; + +#ifdef PARITY + #define BASE_CMD CMD_EN_PARITY +#else + #define BASE_CMD 0 +#endif + +/* + Debugging code +*/ + +#define PHASE_BUS_FREE 1 +#define PHASE_ARBITRATION 2 +#define PHASE_SELECTION 4 +#define PHASE_DATAIN 8 +#define PHASE_DATAOUT 0x10 +#define PHASE_CMDOUT 0x20 +#define PHASE_MSGIN 0x40 +#define PHASE_MSGOUT 0x80 +#define PHASE_STATUSIN 0x100 +#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN) +#define PRINT_COMMAND 0x200 +#define PHASE_EXIT 0x400 +#define PHASE_RESELECT 0x800 + +/* + * Control options - these are timeouts specified in .01 seconds. + */ + +#define ST0X_BUS_FREE_DELAY 25 +#define ST0X_SELECTION_DELAY 3 + +#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::) + + +#endif + diff --git a/kernel/blk_drv/scsi/seagate2.s b/kernel/blk_drv/scsi/seagate2.s new file mode 100644 index 000000000000..9b1b6a213d95 --- /dev/null +++ b/kernel/blk_drv/scsi/seagate2.s @@ -0,0 +1,36 @@ +/* + * seagate2.S + * low level scsi driver for ST01/ST02 by + * Drew Eckhardt + * + * + */ +.text +.globl _seagate_intr +_seagate_intr: + cld # GCC thing + + pushal + push %ds + push %es + + mov $0x10, %ax # switch to kernel space + mov %ax, %ds + mov %ax, %es + + + + xor %eax, %eax + xchg _do_seagate, %eax + test %eax, %eax + jnz 1f + mov $_seagate_unexpected_intr, %eax +1: call *%eax + + mov $0x20, %al # non-specific EOI + out %al, $0x20 + + pop %es + pop %ds + popal + iret diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c new file mode 100644 index 000000000000..1f8121771859 --- /dev/null +++ b/kernel/blk_drv/scsi/st.c @@ -0,0 +1,32 @@ +/* + The st.c file is a sub-stub file. I just wanted to have all the detect code, etc in the + mid level driver present and working. If no one else volunteers for this, I'll + do it - but it's low on my list of priorities. +*/ +#include + +#ifdef CONFIG_BLK_DEV_ST +#include "scsi.h" +#include "st.h" + +#define MAJOR_NR 9 +#include +#include +#include +#include "../blk.h" + +Scsi_Tape scsi_tapes[MAX_ST]; +static int st_sizes[MAX_ST]; +int NR_ST=0; + +void do_st_request(void) +{ + panic("There is no st driver.\n\r"); +} + +void st_init(void) +{ + blk_dev[MAJOR_NR].request_fn = do_st_request; + blk_size[MAJOR_NR] = st_sizes; +} +#endif diff --git a/kernel/blk_drv/scsi/st.h b/kernel/blk_drv/scsi/st.h new file mode 100644 index 000000000000..9ae0795bc7ed --- /dev/null +++ b/kernel/blk_drv/scsi/st.h @@ -0,0 +1,26 @@ + +#ifndef _ST_H + #define _ST_H +/* + $Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/st.h,v 1.1 1992/07/24 06:27:38 root Exp root $ +*/ + +#ifndef _SCSI_H +#include "scsi.h" +#endif + +#define MAX_ST 1 + +typedef struct + { + /* + Undecided goodies go here!!! + */ + Scsi_Device* device; + } Scsi_Tape; + + +extern int NR_ST; +extern Scsi_Tape scsi_tapes[MAX_ST]; +void st_init(void); +#endif diff --git a/kernel/blk_drv/scsi/st_ioctl.c b/kernel/blk_drv/scsi/st_ioctl.c new file mode 100644 index 000000000000..478098960d2e --- /dev/null +++ b/kernel/blk_drv/scsi/st_ioctl.c @@ -0,0 +1,19 @@ +#include +#ifdef CONFIG_BLK_DEV_ST +#include +#include +#include +#include "st.h" + +extern int scsi_ioctl(Scsi_Device *dev, int cmd, void *arg); + +int st_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg) +{ + int dev = inode->i_rdev; + + switch (cmd) { + default: + return scsi_ioctl(scsi_tapes[MINOR(dev)].device, cmd, (void *) arg); + } +} +#endif diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c new file mode 100644 index 000000000000..279448f054c0 --- /dev/null +++ b/kernel/blk_drv/scsi/ultrastor.c @@ -0,0 +1,503 @@ +/* + * ultrastor.c Copyright (C) 1991, 1992 David B. Gentzel + * Low-level SCSI driver for UltraStor 14F + * by David B. Gentzel, Whitfield Software Services, Carnegie, PA + * (gentzel@nova.enet.dec.com) + * Thanks to UltraStor for providing the necessary documentation + */ + +/* + * NOTES: + * The UltraStor 14F is an intelligent, high performance ISA SCSI-2 host + * adapter. It is essentially an ISA version of the UltraStor 24F EISA + * adapter. It supports first-party DMA, command queueing, and + * scatter/gather I/O. It can also emulate the standard AT MFM/RLL/IDE + * interface for use with OS's which don't support SCSI. + * + * This driver may also work (with some small changes) with the UltraStor + * 24F. I have no way of confirming this... + * + * Places flagged with a triple question-mark are things which are either + * unfinished, questionable, or wrong. + */ + +/* + * CAVEATS: ??? + * This driver is VERY stupid. It takes no advantage of much of the power + * of the UltraStor controller. We just sit-and-spin while waiting for + * commands to complete. I hope to go back and beat it into shape, but + * PLEASE, anyone else who would like to, please make improvements! + * + * By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature + * of the mid-level SCSI driver. Once I'm satisfied that the queueing + * version is as stable as the non-queueing version, I'll eliminate this + * option. + */ + +#include + +#ifdef CONFIG_SCSI_ULTRASTOR + +#include +#include +#include +#include + +#include +#include + +#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */ +#include "ultrastor.h" +#include "scsi.h" +#include "hosts.h" + +#define VERSION "1.0 beta" + +#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0]) +#define BIT(n) (1ul << (n)) +#define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8))) + +/* Simply using "unsigned long" in these structures won't work as it causes + alignment. Perhaps the "aligned" attribute may be used in GCC 2.0 to get + around this, but for now I use this hack. */ +typedef struct { + unsigned char bytes[4]; +} Longword; + +/* Used to fetch the configuration info from the config i/o registers. We + then store (in a friendlier format) in config. */ +struct config_1 { + unsigned char bios_segment: 3; + unsigned char reserved: 1; + unsigned char interrupt: 2; + unsigned char dma_channel: 2; +}; +struct config_2 { + unsigned char ha_scsi_id: 3; + unsigned char mapping_mode: 2; + unsigned char bios_drive_number: 1; + unsigned char tfr_port: 2; +}; + +/* Used to store configuration info read from config i/o registers. Most of + this is not used yet, but might as well save it. */ +struct config { + unsigned short port_address; + const void *bios_segment; + unsigned char interrupt: 4; + unsigned char dma_channel: 3; + unsigned char ha_scsi_id: 3; + unsigned char heads: 6; + unsigned char sectors: 6; + unsigned char bios_drive_number: 1; +}; + +/* MailBox SCSI Command Packet. Basic command structure for communicating + with controller. */ +struct mscp { + unsigned char opcode: 3; /* type of command */ + unsigned char xdir: 2; /* data transfer direction */ + unsigned char dcn: 1; /* disable disconnect */ + unsigned char ca: 1; /* use cache (if available) */ + unsigned char sg: 1; /* scatter/gather operation */ + unsigned char target_id: 3; /* target SCSI id */ + unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */ + unsigned char lun: 3; /* logical unit number */ + Longword transfer_data; /* transfer data pointer */ + Longword transfer_data_length; /* length in bytes */ + Longword command_link; /* for linking command chains */ + unsigned char scsi_command_link_id; /* identifies command in chain */ + unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */ + unsigned char length_of_sense_byte; + unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */ + unsigned char scsi_cdbs[12]; /* SCSI commands */ + unsigned char adapter_status; /* non-zero indicates HA error */ + unsigned char target_status; /* non-zero indicates target error */ + Longword sense_data; +}; + +/* Allowed BIOS base addresses for 14f (NULL indicates reserved) */ +static const void *const bios_segment_table[8] = { + NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000, + (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000, +}; + +/* Allowed IRQs for 14f */ +static const unsigned char interrupt_table[4] = { 15, 14, 11, 10 }; + +/* Allowed DMA channels for 14f (0 indicates reserved) */ +static const unsigned char dma_channel_table[4] = { 5, 6, 7, 0 }; + +/* Head/sector mappings allowed by 14f */ +static const struct { + unsigned char heads; + unsigned char sectors; +} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } }; + +/* Config info */ +static struct config config; + +/* Our index in the host adapter array maintained by higher-level driver */ +static int host_number; + +/* PORT_ADDRESS is first port address used for i/o of messages. */ +#ifdef PORT_OVERRIDE +# define PORT_ADDRESS PORT_OVERRIDE +#else +# define PORT_ADDRESS (config.port_address) +#endif + +static volatile int aborted = 0; + +#ifndef PORT_OVERRIDE +static const unsigned short ultrastor_ports[] = { + 0x330, 0x340, 0x310, 0x230, 0x240, 0x210, 0x130, 0x140, +}; +#endif + +void ultrastor_interrupt(void); + +static void (*ultrastor_done)(int, int) = 0; + +static const struct { + const char *signature; + size_t offset; + size_t length; +} signatures[] = { + { "SBIOS 1.01 COPYRIGHT (C) UltraStor Corporation,1990-1992.", 0x10, 57 }, +}; + +int ultrastor_14f_detect(int hostnum) +{ + size_t i; + unsigned char in_byte; + struct config_1 config_1; + struct config_2 config_2; + +#if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: called\n"); +#endif + +#ifndef PORT_OVERRIDE + PORT_ADDRESS = 0; + for (i = 0; i < ARRAY_SIZE(ultrastor_ports); i++) { + PORT_ADDRESS = ultrastor_ports[i]; +#endif + +#if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: testing port address %03X\n", PORT_ADDRESS); +#endif + + in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 0)); + if (in_byte != US14F_PRODUCT_ID_0) { +#if (ULTRASTOR_DEBUG & UD_DETECT) +# ifdef PORT_OVERRIDE + printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte); +# else + printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS); +# endif +#endif +#ifdef PORT_OVERRIDE + return FALSE; +#else + continue; +#endif + } + in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 1)); + /* Only upper nibble is defined for Product ID 1 */ + if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) { +#if (ULTRASTOR_DEBUG & UD_DETECT) +# ifdef PORT_OVERRIDE + printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte); +# else + printk("US14F: detect: no adapter at port %03X\n", PORT_ADDRESS); +# endif +#endif +#ifdef PORT_OVERRIDE + return FALSE; +#else + continue; +#endif + } +#ifndef PORT_OVERRIDE + break; + } + if (i == ARRAY_SIZE(ultrastor_ports)) { +# if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: no port address found!\n"); +# endif + return FALSE; + } +#endif + +#if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: adapter found at port address %03X\n", + PORT_ADDRESS); +#endif + + /* All above tests passed, must be the right thing. Get some useful + info. */ + *(char *)&config_1 = inb(CONFIG(PORT_ADDRESS + 0)); + *(char *)&config_2 = inb(CONFIG(PORT_ADDRESS + 1)); + config.bios_segment = bios_segment_table[config_1.bios_segment]; + config.interrupt = interrupt_table[config_1.interrupt]; + config.dma_channel = dma_channel_table[config_1.dma_channel]; + config.ha_scsi_id = config_2.ha_scsi_id; + config.heads = mapping_table[config_2.mapping_mode].heads; + config.sectors = mapping_table[config_2.mapping_mode].sectors; + config.bios_drive_number = config_2.bios_drive_number; + + /* To verify this card, we simply look for the UltraStor SCSI from the + BIOS version notice. */ + if (config.bios_segment != NULL) { + int found = 0; + + for (i = 0; !found && i < ARRAY_SIZE(signatures); i++) + if (memcmp((char *)config.bios_segment + signatures[i].offset, + signatures[i].signature, signatures[i].length)) + found = 1; + if (!found) + config.bios_segment = NULL; + } + if (!config.bios_segment) { +#if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: not detected.\n"); +#endif + return FALSE; + } + + /* Final consistancy check, verify previous info. */ + if (!config.dma_channel || !(config_2.tfr_port & 0x2)) { +#if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: consistancy check failed\n"); +#endif + return FALSE; + } + + /* If we were TRULY paranoid, we could issue a host adapter inquiry + command here and verify the data returned. But frankly, I'm + exhausted! */ + + /* Finally! Now I'm satisfied... */ +#if (ULTRASTOR_DEBUG & UD_DETECT) + printk("US14F: detect: detect succeeded\n" + " Port address: %03X\n" + " BIOS segment: %05X\n" + " Interrupt: %u\n" + " DMA channel: %u\n" + " H/A SCSI ID: %u\n", + PORT_ADDRESS, config.bios_segment, config.interrupt, + config.dma_channel, config.ha_scsi_id); +#endif + host_number = hostnum; + scsi_hosts[hostnum].this_id = config.ha_scsi_id; +#ifndef NO_QUEUEING + set_intr_gate(0x20 + config.interrupt, ultrastor_interrupt); + /* gate to PIC 2 */ + outb_p(inb_p(0x21) & ~BIT(2), 0x21); + /* enable the interrupt */ + outb(inb_p(0xA1) & ~BIT(config.interrupt - 8), 0xA1); +#endif + return TRUE; +} + +const char *ultrastor_14f_info(void) +{ + return "UltraStor 14F SCSI driver version " + VERSION + " by David B. Gentzel\n"; +} + +static struct mscp mscp = { + OP_SCSI, DTD_SCSI, FALSE, TRUE, FALSE /* This stuff doesn't change */ +}; + +int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd, + void *buff, int bufflen, void (*done)(int, int)) +{ + unsigned char in_byte; + +#if (ULTRASTOR_DEBUG & UD_COMMAND) + printk("US14F: queuecommand: called\n"); +#endif + + /* Skip first (constant) byte */ + memset((char *)&mscp + 1, 0, sizeof (struct mscp) - 1); + mscp.target_id = target; + /* mscp.lun = ???; */ + mscp.transfer_data = *(Longword *)&buff; + mscp.transfer_data_length = *(Longword *)&bufflen, + mscp.length_of_scsi_cdbs = ((*(unsigned char *)cmnd <= 0x1F) ? 6 : 10); + memcpy(mscp.scsi_cdbs, cmnd, mscp.length_of_scsi_cdbs); + + /* Find free OGM slot (OGMINT bit is 0) */ + do + in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS)); + while (!aborted && (in_byte & 1)); + if (aborted) { +#if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)) + printk("US14F: queuecommand: aborted\n"); +#endif + /* ??? is this right? */ + return (aborted << 16); + } + + /* Store pointer in OGM address bytes */ + outb_p(BYTE(&mscp, 0), OGM_DATA_PTR(PORT_ADDRESS + 0)); + outb_p(BYTE(&mscp, 1), OGM_DATA_PTR(PORT_ADDRESS + 1)); + outb_p(BYTE(&mscp, 2), OGM_DATA_PTR(PORT_ADDRESS + 2)); + outb_p(BYTE(&mscp, 3), OGM_DATA_PTR(PORT_ADDRESS + 3)); + + /* Issue OGM interrupt */ + outb_p(0x1, LCL_DOORBELL_INTR(PORT_ADDRESS)); + + ultrastor_done = done; + +#if (ULTRASTOR_DEBUG & UD_COMMAND) + printk("US14F: queuecommand: returning\n"); +#endif + + return 0; +} + +#ifdef NO_QUEUEING +int ultrastor_14f_command(unsigned char target, const void *cmnd, + void *buff, int bufflen) +{ + unsigned char in_byte; + +#if (ULTRASTOR_DEBUG & UD_COMMAND) + printk("US14F: command: called\n"); +#endif + + (void)ultrastor_14f_queuecommand(target, cmnd, buff, bufflen, 0); + + /* Wait for ICM interrupt */ + do + in_byte = inb_p(SYS_DOORBELL_INTR(PORT_ADDRESS)); + while (!aborted && !(in_byte & 1)); + if (aborted) { +#if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)) + printk("US14F: command: aborted\n"); +#endif + /* ??? is this right? */ + return (aborted << 16); + } + + /* Clean ICM slot (set ICMINT bit to 0) */ + outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS)); + +#if (ULTRASTOR_DEBUG & UD_COMMAND) + printk("US14F: command: returning %08X\n", + (mscp.adapter_status << 16) | mscp.target_status); +#endif + + /* ??? not right, but okay for now? */ + return (mscp.adapter_status << 16) | mscp.target_status; +} +#endif + +int ultrastor_14f_abort(int code) +{ +#if (ULTRASTOR_DEBUG & UD_ABORT) + printk("US14F: abort: called\n"); +#endif + + aborted = (code ? code : DID_ABORT); + +#if (ULTRASTOR_DEBUG & UD_ABORT) + printk("US14F: abort: returning\n"); +#endif + + return 0; +} + +int ultrastor_14f_reset(void) +{ + unsigned char in_byte; + +#if (ULTRASTOR_DEBUG & UD_RESET) + printk("US14F: reset: called\n"); +#endif + + /* Issue SCSI BUS reset */ + outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS)); + + /* Wait for completion... */ + do + in_byte = inb_p(LCL_DOORBELL_INTR(PORT_ADDRESS)); + while (in_byte & 0x20); + + aborted = DID_RESET; + +#if (ULTRASTOR_DEBUG & UD_RESET) + printk("US14F: reset: returning\n"); +#endif + return 0; +} + +#ifndef NO_QUEUEING +void ultrastor_interrupt_service(void) +{ +#if (ULTRASTOR_DEBUG & UD_INTERRUPT) + printk("US14F: interrupt_service: called: status = %08X\n", + (mscp.adapter_status << 16) | mscp.target_status); +#endif + + if (ultrastor_done == 0) + panic("US14F: interrupt_service: unexpected interrupt!\n"); + else { + void (*done)(int, int); + + /* Save ultrastor_done locally and zero before calling. This is needed + as once we call done, we may get another command queued before this + interrupt service routine can return. */ + done = ultrastor_done; + ultrastor_done = 0; + + /* Clean ICM slot (set ICMINT bit to 0) */ + outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS)); + + /* Let the higher levels know that we're done */ + /* ??? status is wrong here... */ + done(host_number, (mscp.adapter_status << 16) | mscp.target_status); + } + +#if (ULTRASTOR_DEBUG & UD_INTERRUPT) + printk("US14F: interrupt_service: returning\n"); +#endif +} + +__asm__(" +_ultrastor_interrupt: + cld + pushl %eax + pushl %ecx + pushl %edx + push %ds + push %es + push %fs + movl $0x10,%eax + mov %ax,%ds + mov %ax,%es + movl $0x17,%eax + mov %ax,%fs + movb $0x20,%al + outb %al,$0xA0 # EOI to interrupt controller #1 + outb %al,$0x80 # give port chance to breathe + outb %al,$0x80 + outb %al,$0x80 + outb %al,$0x80 + outb %al,$0x20 + call _ultrastor_interrupt_service + pop %fs + pop %es + pop %ds + popl %edx + popl %ecx + popl %eax + iret +"); +#endif + +#endif diff --git a/kernel/blk_drv/scsi/ultrastor.h b/kernel/blk_drv/scsi/ultrastor.h new file mode 100644 index 000000000000..b88141945d8a --- /dev/null +++ b/kernel/blk_drv/scsi/ultrastor.h @@ -0,0 +1,94 @@ +/* + * ultrastor.c (C) 1991 David B. Gentzel + * Low-level scsi driver for UltraStor 14F + * by David B. Gentzel, Whitfield Software Services, Carnegie, PA + * (gentzel@nova.enet.dec.com) + * Thanks to UltraStor for providing the necessary documentation + */ + +#ifndef _ULTRASTOR_H +#define _ULTRASTOR_H + +/* ??? These don't really belong here */ +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +/* ??? This should go eventually, once I'm convinced the queueing stuff is + stable enough... */ +/* #define NO_QUEUEING */ + +int ultrastor_14f_detect(int); +const char *ultrastor_14f_info(void); +int ultrastor_14f_queuecommand(unsigned char target, const void *cmnd, + void *buff, int bufflen, + void (*done)(int, int)); +#ifdef NO_QUEUEING +int ultrastor_14f_command(unsigned char target, const void *cmnd, + void *buff, int bufflen); +#endif +int ultrastor_14f_abort(int); +int ultrastor_14f_reset(void); + +#ifndef NO_QUEUEING +#define ULTRASTOR_14F \ + { "UltraStor 14F", ultrastor_14f_detect, ultrastor_14f_info, 0, \ + ultrastor_14f_queuecommand, ultrastor_14f_abort, ultrastor_14f_reset, \ + 1, 0, 0 } + /* ??? What should can_queue be set to? Currently 1... */ +#else +#define ULTRASTOR_14F \ + { "UltraStor 14F", ultrastor_14f_detect, ultrastor_14f_info, \ + ultrastor_14f_command, 0, ultrastor_14f_abort, ultrastor_14f_reset, \ + 0, 0, 0 } +#endif + +#define UD_ABORT 0x0001 +#define UD_COMMAND 0x0002 +#define UD_DETECT 0x0004 +#define UD_INTERRUPT 0x0008 +#define UD_RESET 0x0010 + +#ifdef ULTRASTOR_PRIVATE + +/* #define PORT_OVERRIDE 0x330 */ + +/* Port addresses (relative to the base address) */ +#define LCL_DOORBELL_MASK(port) ((port) + 0x0) +#define LCL_DOORBELL_INTR(port) ((port) + 0x1) +#define SYS_DOORBELL_MASK(port) ((port) + 0x2) +#define SYS_DOORBELL_INTR(port) ((port) + 0x3) +#define PRODUCT_ID(port) ((port) + 0x4) +#define CONFIG(port) ((port) + 0x6) +#define OGM_DATA_PTR(port) ((port) + 0x8) +#define ICM_DATA_PTR(port) ((port) + 0xC) + +/* Values for the PRODUCT_ID ports for the 14F */ +#define US14F_PRODUCT_ID_0 0x56 +#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */ + +/* MSCP field values */ + +/* Opcode */ +#define OP_HOST_ADAPTER 0x1 +#define OP_SCSI 0x2 +#define OP_RESET 0x4 + +/* Date Transfer Direction */ +#define DTD_SCSI 0x0 +#define DTD_IN 0x1 +#define DTD_OUT 0x2 +#define DTD_NONE 0x3 + +/* Host Adapter command subcodes */ +#define HA_CMD_INQUIRY 0x1 +#define HA_CMD_SELF_DIAG 0x2 +#define HA_CMD_READ_BUFF 0x3 +#define HA_CMD_WRITE_BUFF 0x4 + +#endif + +#endif diff --git a/kernel/chr_drv/Makefile b/kernel/chr_drv/Makefile index 8049f2fcca96..db51601a12c7 100644 --- a/kernel/chr_drv/Makefile +++ b/kernel/chr_drv/Makefile @@ -9,31 +9,22 @@ # parent makes.. # -AR =ar -AS =as -LD =ld -LDFLAGS =-s -x -CC =gcc -nostdinc -I../../include -CPP =cpp -nostdinc -I../../include - .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) -S $< .s.o: $(AS) -c -o $*.o $< .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) -c $< -OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \ - tty_ioctl.o pty.o lp.o vt.o mem.o +OBJS = tty_io.o console.o keyboard.o serial.o \ + tty_ioctl.o pty.o lp.o vt.o mem.o mouse.o chr_drv.a: $(OBJS) $(AR) rcs chr_drv.a $(OBJS) sync -keyboard.s: keyboard.S - $(CPP) $(KEYBOARD) -traditional keyboard.S -o keyboard.s +keyboard.o: keyboard.c + $(CC) $(CFLAGS) $(KEYBOARD) -c -o keyboard.o keyboard.c clean: rm -f core *.o *.a tmp_make keyboard.s @@ -41,61 +32,103 @@ clean: dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ - $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M -DKBD_FINNISH $$i;done >> tmp_make cp tmp_make Makefile ### Dependencies: -console.s console.o : console.c ../../include/linux/sched.h ../../include/linux/head.h \ - ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/timer.h ../../include/linux/tty.h \ - ../../include/termios.h ../../include/linux/config.h ../../include/linux/config_rel.h \ - ../../include/linux/config_ver.h ../../include/asm/io.h ../../include/asm/system.h \ - ../../include/asm/segment.h ../../include/linux/string.h ../../include/errno.h \ - ../../include/sys/kd.h vt_kern.h -lp.s lp.o : lp.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \ - ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \ - ../../include/linux/lp.h ../../include/errno.h ../../include/asm/io.h ../../include/asm/segment.h -mem.s mem.o : mem.c ../../include/errno.h ../../include/sys/types.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/tty.h ../../include/termios.h \ - ../../include/asm/segment.h ../../include/asm/io.h -pty.s pty.o : pty.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \ - ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \ - ../../include/linux/tty.h ../../include/termios.h ../../include/asm/system.h \ - ../../include/asm/io.h -serial.s serial.o : serial.c ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/types.h ../../include/sys/dirent.h ../../include/limits.h \ - ../../include/linux/mm.h ../../include/linux/kernel.h ../../include/signal.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \ - ../../include/linux/timer.h ../../include/linux/tty.h ../../include/termios.h \ - ../../include/asm/system.h ../../include/asm/io.h -tty_io.s tty_io.o : tty_io.c ../../include/linux/ctype.h ../../include/errno.h ../../include/signal.h \ - ../../include/sys/types.h ../../include/unistd.h ../../include/sys/stat.h ../../include/sys/time.h \ - ../../include/time.h ../../include/sys/times.h ../../include/sys/utsname.h ../../include/sys/param.h \ - ../../include/sys/resource.h ../../include/utime.h ../../include/fcntl.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/linux/tty.h ../../include/termios.h ../../include/asm/segment.h \ - ../../include/asm/system.h -tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h ../../include/sys/types.h \ - ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/tty.h ../../include/asm/io.h \ - ../../include/asm/segment.h ../../include/asm/system.h -vt.s vt.o : vt.c ../../include/errno.h ../../include/sys/types.h ../../include/sys/kd.h \ - ../../include/sys/vt.h ../../include/asm/io.h ../../include/asm/segment.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/linux/tty.h ../../include/termios.h \ - vt_kern.h +console.o : console.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/config.h /usr/src/linux/include/linux/config_rel.h \ + /usr/src/linux/include/linux/config_ver.h /usr/src/linux/include/linux/config.dist.h \ + /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/io.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/sys/kd.h vt_kern.h +keyboard.o : keyboard.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/ctype.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/ptrace.h /usr/src/linux/include/asm/io.h +lp.o : lp.c /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/lp.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/io.h \ + /usr/src/linux/include/asm/segment.h +mem.o : mem.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/mouse.h /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/io.h +mouse.o : mouse.c /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/mouse.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/io.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/irq.h +pty.o : pty.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/asm/io.h +serial.o : serial.c /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/segment.h +tty_io.o : tty_io.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/fcntl.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h \ + /usr/src/linux/include/linux/resource.h /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/ctype.h /usr/src/linux/include/asm/io.h \ + /usr/src/linux/include/asm/segment.h /usr/src/linux/include/sys/kd.h vt_kern.h +tty_ioctl.o : tty_ioctl.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/termios.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/segment.h +vt.o : vt.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/tty.h /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/linux/timer.h /usr/src/linux/include/asm/io.h /usr/src/linux/include/asm/segment.h \ + vt_kern.h /usr/src/linux/include/sys/kd.h /usr/src/linux/include/sys/vt.h diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c index 01421e5a5769..cff8124554a1 100644 --- a/kernel/chr_drv/console.c +++ b/kernel/chr_drv/console.c @@ -1,14 +1,14 @@ /* * linux/kernel/console.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* * console.c * * This module implements the console io functions - * 'void con_init(void)' + * 'long con_init(long)' * 'void con_write(struct tty_queue * queue)' * Hopefully this will be a rather complete VT102 implementation. * @@ -30,66 +30,36 @@ * */ +#define KEYBOARD_IRQ 1 + #include #include #include #include #include +#include +#include #include #include #include -#include -#include - #include #include "vt_kern.h" -#define DEF_TERMIOS \ -(struct termios) { \ - ICRNL, \ - OPOST | ONLCR, \ - 0, \ - IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, \ - 0, \ - INIT_C_CC \ -} - -static void blank_screen(void); -static void unblank_screen(void); - -/* - * These are set up by the setup-routine at boot-time: - */ - -#define ORIG_X (*(unsigned char *)0x90000) -#define ORIG_Y (*(unsigned char *)0x90001) -#define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) -#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) -#define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) -#define ORIG_VIDEO_LINES ((*(unsigned short *)0x9000e) & 0xff) -#define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) -#define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) -#define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) - -#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ -#define VIDEO_TYPE_CGA 0x11 /* CGA Display */ -#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ -#define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ - #define NPAR 16 -int NR_CONSOLES = 0; - extern void vt_init(void); -extern void keyboard_interrupt(void); +extern void keyboard_interrupt(int pt_regs); extern void set_leds(void); extern unsigned char kapplic; +extern unsigned char ckmode; +extern unsigned char krepeat; extern unsigned char kleds; extern unsigned char kmode; extern unsigned char kraw; extern unsigned char ke0; +extern unsigned char lfnlmode; unsigned long video_num_columns; /* Number of text columns */ unsigned long video_num_lines; /* Number of test lines */ @@ -101,40 +71,65 @@ static unsigned long video_size_row; /* Bytes per row */ static unsigned char video_page; /* Initial video page */ static unsigned short video_port_reg; /* Video register select port */ static unsigned short video_port_val; /* Video register value port */ -static int can_do_colour = 0; +static int can_do_color = 0; static struct { - unsigned short vc_video_erase_char; - unsigned char vc_attr; - unsigned char vc_def_attr; - int vc_bold_attr; - unsigned long vc_ques; - unsigned long vc_state; - char * vc_restate; - unsigned long vc_checkin; + unsigned short vc_video_erase_char; /* Background erase character */ + unsigned char vc_attr; /* Current attributes */ + unsigned char vc_def_color; /* Default colors */ + unsigned char vc_color; /* Foreground & background */ + unsigned char vc_s_color; /* Saved foreground & background */ + unsigned char vc_ulcolor; /* Colour for underline mode */ + unsigned char vc_halfcolor; /* Colour for half intensity mode */ unsigned long vc_origin; /* Used for EGA/VGA fast scroll */ unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */ unsigned long vc_pos; unsigned long vc_x,vc_y; unsigned long vc_top,vc_bottom; + unsigned long vc_state; unsigned long vc_npar,vc_par[NPAR]; unsigned long vc_video_mem_start; /* Start of video RAM */ unsigned long vc_video_mem_end; /* End of video RAM (sort of) */ - unsigned int vc_saved_x; - unsigned int vc_saved_y; - unsigned int vc_iscolor; - unsigned char vc_kbdapplic; - unsigned char vc_kbdleds; + unsigned long vc_saved_x; + unsigned long vc_saved_y; + /* mode flags */ + unsigned long vc_kbdapplic : 1; /* Application keyboard */ + unsigned long vc_charset : 1; /* Character set G0 / G1 */ + unsigned long vc_s_charset : 1; /* Saved character set */ + unsigned long vc_decckm : 1; /* Cursor Keys Mode */ + unsigned long vc_decscnm : 1; /* Screen Mode */ + unsigned long vc_decom : 1; /* Origin Mode */ + unsigned long vc_decawm : 1; /* Autowrap Mode */ + unsigned long vc_decarm : 1; /* Autorepeat Mode */ + unsigned long vc_deccm : 1; /* Cursor Visible */ + unsigned long vc_decim : 1; /* Insert Mode */ + unsigned long vc_lnm : 1; /* Line feed New line Mode */ + /* attribute flags */ + unsigned long vc_intensity : 2; /* 0=half-bright, 1=normal, 2=bold */ + unsigned long vc_underline : 1; + unsigned long vc_blink : 1; + unsigned long vc_reverse : 1; + unsigned long vc_s_intensity : 2; /* saved rendition */ + unsigned long vc_s_underline : 1; + unsigned long vc_s_blink : 1; + unsigned long vc_s_reverse : 1; + /* misc */ + unsigned long vc_ques : 1; + unsigned long vc_need_wrap : 1; + unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */ unsigned char vc_kbdmode; - unsigned char vc_kbdraw; - unsigned char vc_kbde0; char * vc_translate; -} vc_cons [MAX_CONSOLES]; + char * vc_G0_charset; + char * vc_G1_charset; + char * vc_saved_G0; + char * vc_saved_G1; + /* additional information is in vt_kern.h */ +} vc_cons [NR_CONSOLES]; #define MEM_BUFFER_SIZE (2*80*50*8) -unsigned short *vc_scrbuf[MAX_CONSOLES]; -unsigned short vc_scrmembuf[MEM_BUFFER_SIZE/2]; +unsigned short *vc_scrbuf[NR_CONSOLES]; +static unsigned short * vc_scrmembuf; static int console_blanked = 0; #define origin (vc_cons[currcons].vc_origin) @@ -145,8 +140,6 @@ static int console_blanked = 0; #define x (vc_cons[currcons].vc_x) #define y (vc_cons[currcons].vc_y) #define state (vc_cons[currcons].vc_state) -#define restate (vc_cons[currcons].vc_restate) -#define checkin (vc_cons[currcons].vc_checkin) #define npar (vc_cons[currcons].vc_npar) #define par (vc_cons[currcons].vc_par) #define ques (vc_cons[currcons].vc_ques) @@ -154,27 +147,61 @@ static int console_blanked = 0; #define saved_x (vc_cons[currcons].vc_saved_x) #define saved_y (vc_cons[currcons].vc_saved_y) #define translate (vc_cons[currcons].vc_translate) +#define G0_charset (vc_cons[currcons].vc_G0_charset) +#define G1_charset (vc_cons[currcons].vc_G1_charset) +#define saved_G0 (vc_cons[currcons].vc_saved_G0) +#define saved_G1 (vc_cons[currcons].vc_saved_G1) #define video_mem_start (vc_cons[currcons].vc_video_mem_start) #define video_mem_end (vc_cons[currcons].vc_video_mem_end) -#define def_attr (vc_cons[currcons].vc_def_attr) -#define video_erase_char (vc_cons[currcons].vc_video_erase_char) -#define iscolor (vc_cons[currcons].vc_iscolor) +#define video_erase_char (vc_cons[currcons].vc_video_erase_char) +#define decckm (vc_cons[currcons].vc_decckm) +#define decscnm (vc_cons[currcons].vc_decscnm) +#define decom (vc_cons[currcons].vc_decom) +#define decawm (vc_cons[currcons].vc_decawm) +#define decarm (vc_cons[currcons].vc_decarm) +#define deccm (vc_cons[currcons].vc_deccm) +#define decim (vc_cons[currcons].vc_decim) +#define lnm (vc_cons[currcons].vc_lnm) #define kbdapplic (vc_cons[currcons].vc_kbdapplic) +#define need_wrap (vc_cons[currcons].vc_need_wrap) +#define color (vc_cons[currcons].vc_color) +#define s_color (vc_cons[currcons].vc_s_color) +#define def_color (vc_cons[currcons].vc_def_color) +#define foreground (color & 0x0f) +#define background (color & 0xf0) +#define charset (vc_cons[currcons].vc_charset) +#define s_charset (vc_cons[currcons].vc_s_charset) +#define intensity (vc_cons[currcons].vc_intensity) +#define underline (vc_cons[currcons].vc_underline) +#define blink (vc_cons[currcons].vc_blink) +#define reverse (vc_cons[currcons].vc_reverse) +#define s_intensity (vc_cons[currcons].vc_s_intensity) +#define s_underline (vc_cons[currcons].vc_s_underline) +#define s_blink (vc_cons[currcons].vc_s_blink) +#define s_reverse (vc_cons[currcons].vc_s_reverse) +#define ulcolor (vc_cons[currcons].vc_ulcolor) +#define halfcolor (vc_cons[currcons].vc_halfcolor) #define kbdmode (vc_cons[currcons].vc_kbdmode) -#define kbdraw (vc_cons[currcons].vc_kbdraw) -#define kbde0 (vc_cons[currcons].vc_kbde0) -#define kbdleds (vc_cons[currcons].vc_kbdleds) +#define tab_stop (vc_cons[currcons].vc_tab_stop) +#define kbdraw (vt_cons[currcons].vc_kbdraw) +#define kbdleds (vt_cons[currcons].vc_kbdleds) +#define vtmode (vt_cons[currcons].vt_mode) -int blankinterval = 5*60*HZ; +#define SET(mode,fg,v) \ + (mode) = (v); \ + if (currcons == fg_console) \ + (fg) = (v) + +int blankinterval = 10*60*HZ; static int screen_size = 0; static void sysbeep(void); /* - * this is what the terminal answers to a ESC-Z or csi0c - * query (= vt100 response). + * this is what the terminal answers to a ESC-Z or csi0c query. */ -#define RESPONSE "\033[?1;2c" +#define VT100ID "\033[?1;2c" +#define VT102ID "\033[?6c" static char * translations[] = { /* 8-bit Latin-1 mapped to the PC charater set: '\0' means non-printable */ @@ -196,8 +223,8 @@ static char * translations[] = { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" " !\"#$%&'()*+,-./0123456789:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ " - "\004\261\007\007\007\007\370\361\007\007\275\267\326\323\327\304" - "\304\304\304\304\307\266\320\322\272\363\362\343\007\234\007\0" + "\004\261\007\007\007\007\370\361\040\007\331\277\332\300\305\007" + "\007\304\007\007\303\264\301\302\263\007\007\007\007\007\234\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" @@ -205,27 +232,70 @@ static char * translations[] = { "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341" "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" - "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230" + "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230", +/* IBM grapgics: minimal translations (CR, LF, LL and ESC) */ + "\000\001\002\003\004\005\006\007\010\011\000\013\000\000\016\017" + "\020\021\022\023\024\025\026\027\030\031\032\000\034\035\036\037" + "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057" + "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077" + "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117" + "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137" + "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157" + "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177" + "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217" + "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237" + "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257" + "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277" + "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317" + "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337" + "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357" + "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377" }; #define NORM_TRANS (translations[0]) #define GRAF_TRANS (translations[1]) +#define NULL_TRANS (translations[2]) -/* NOTE! gotoxy thinks x==video_num_columns is ok */ -static inline void gotoxy(int currcons, unsigned int new_x,unsigned int new_y) +static unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, + 8,12,10,14, 9,13,11,15 }; + +/* + * gotoxy() must verify all boundaries, because the arguments + * might also be negative. If the given position is out of + * bounds, the cursor is placed at the nearest margin. + */ +static void gotoxy(int currcons, int new_x, int new_y) { - if (new_x > video_num_columns || new_y >= video_num_lines) - return; - x = new_x; - y = new_y; + int max_y; + + if (new_x < 0) + x = 0; + else + if (new_x >= video_num_columns) + x = video_num_columns - 1; + else + x = new_x; + if (decom) { + new_y += top; + max_y = bottom; + } else + max_y = video_num_lines; + if (new_y < 0) + y = 0; + else + if (new_y >= max_y) + y = max_y - 1; + else + y = new_y; pos = origin + y*video_size_row + (x<<1); + need_wrap = 0; } -static inline void set_origin(int currcons) +static void set_origin(int currcons) { if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM) return; - if (currcons != fg_console) + if (currcons != fg_console || console_blanked || vtmode == KD_GRAPHICS) return; cli(); outb_p(12, video_port_reg); @@ -316,6 +386,7 @@ static void lf(int currcons) return; } else scrup(currcons,top,bottom); + need_wrap = 0; } static void ri(int currcons) @@ -326,20 +397,31 @@ static void ri(int currcons) return; } else scrdown(currcons,top,bottom); + need_wrap = 0; } -static void cr(int currcons) +static inline void cr(int currcons) { pos -= x<<1; - x=0; + need_wrap = x = 0; } -static void del(int currcons) +static inline void bs(int currcons) +{ + if (x) { + pos -= 2; + x--; + need_wrap = 0; + } +} + +static inline void del(int currcons) { if (x) { pos -= 2; x--; *(unsigned short *)pos = video_erase_char; + need_wrap = 0; } } @@ -354,7 +436,7 @@ static void csi_J(int currcons, int vpar) start = pos; break; case 1: /* erase from start to cursor */ - count = (pos-origin)>>1; + count = ((pos-origin)>>1)+1; start = origin; break; case 2: /* erase whole display */ @@ -370,6 +452,7 @@ static void csi_J(int currcons, int vpar) ::"c" (count), "D" (start),"a" (video_erase_char) :"cx","di"); + need_wrap = 0; } static void csi_K(int currcons, int vpar) @@ -379,14 +462,12 @@ static void csi_K(int currcons, int vpar) switch (vpar) { case 0: /* erase from cursor to end of line */ - if (x>=video_num_columns) - return; count = video_num_columns-x; start = pos; break; case 1: /* erase from start of line to cursor */ start = pos - (x<<1); - count = (x> 4) | (attr << 4)) & 0x77); + if (blink) + attr ^= 0x80; + if (intensity == 2) + attr ^= 0x08; + if (!can_do_color) { + if (underline) + attr = (attr & 0xf8) | 0x01; + else if (intensity == 0) + attr = (attr & 0xf0) | 0x08; + } + if (decscnm) + video_erase_char = ((color & 0x88) | (((color >> 4) | + (color << 4)) & 0x77) << 8) | ' '; + else + video_erase_char = (color << 8) | ' '; +} + +static void default_attr(int currcons) { + intensity = 1; + underline = 0; + reverse = 0; + blink = 0; + color = def_color; } -static void csi_m(int currcons ) +static void csi_m(int currcons) { int i; - static int conv_table[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; for (i=0;i<=npar;i++) switch (par[i]) { - case 0: attr=def_attr;break; /* default */ - case 1: attr=(iscolor?attr|0x08:attr|0x0f);break; /* bold */ - /*case 4: attr=attr|0x01;break;*/ /* underline */ - case 4: /* bold */ - if (!iscolor) - attr |= 0x01; - else - { /* check if forground == background */ - if (vc_cons[currcons].vc_bold_attr != -1) - attr = (vc_cons[currcons].vc_bold_attr&0x0f)|(0xf0&(attr)); - else - { short newattr = (attr&0xf0)|(0xf&(~attr)); - attr = ((newattr&0xf)==((attr>>4)&0xf)? - (attr&0xf0)|(((attr&0xf)+1)%0xf): - newattr); - } - } - break; - case 5: attr=attr|0x80;break; /* blinking */ - case 7: attr=(attr&0x88)|((attr<<4)&0x70)| - ((attr>>4)&0x07);break; /* negative */ - case 22: attr=attr&0xf7;break; /* not bold */ - case 24: attr=attr&0xfe;break; /* not underline */ - case 25: attr=attr&0x7f;break; /* not blinking */ - case 27: attr=def_attr;break; /* positive image */ - case 39: attr=(attr & 0xf8)|(def_attr & 0x07); break; - case 49: attr=(attr & 0x8f)|(def_attr & 0x70); break; + case 0: /* all attributes off */ + default_attr(currcons); + break; + case 1: + intensity = 2; + break; + case 2: + intensity = 0; + break; + case 4: + underline = 1; + break; + case 5: + blink = 1; + break; + case 7: + reverse = 1; + break; + case 21: + case 22: + intensity = 1; + break; + case 24: + underline = 0; + break; + case 25: + blink = 0; + break; + case 27: + reverse = 0; + break; + case 39: + color = (def_color & 0x0f) | background; + break; + case 49: + color = (def_color & 0xf0) | foreground; + break; default: - if (!can_do_colour) - break; - iscolor = 1; - if ((par[i]>=30) && (par[i]<=37)) - attr = (attr & 0xf8) | conv_table[par[i]-30]; - else /* Background color */ - if ((par[i]>=40) && (par[i]<=47)) - attr = (attr & 0x8f) | (conv_table[par[i]-40]<<4); - else + if (par[i] >= 30 && par[i] <= 37) + color = color_table[par[i]-30] + | background; + else if (par[i] >= 40 && par[i] <= 47) + color = (color_table[par[i]-40]<<4) + | foreground; break; } -} - -static inline void set_cursor(int currcons) -{ - if (currcons != fg_console) - return; - cli(); - outb_p(14, video_port_reg); - outb_p(0xff&((pos-video_mem_base)>>9), video_port_val); - outb_p(15, video_port_reg); - outb_p(0xff&((pos-video_mem_base)>>1), video_port_val); - sti(); + update_attr(currcons); } static inline void hide_cursor(int currcons) @@ -471,19 +587,149 @@ static inline void hide_cursor(int currcons) outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val); } -static void respond(int currcons, struct tty_struct * tty) +static inline void set_cursor(int currcons) { - char * p = RESPONSE; - + if (currcons != fg_console || console_blanked) + return; cli(); + if (deccm) { + outb_p(14, video_port_reg); + outb_p(0xff&((pos-video_mem_base)>>9), video_port_val); + outb_p(15, video_port_reg); + outb_p(0xff&((pos-video_mem_base)>>1), video_port_val); + } else + hide_cursor(currcons); + sti(); +} + +static void respond_string(char * p, int currcons, struct tty_struct * tty) +{ while (*p) { - PUTCH(*p,tty->read_q); + put_tty_queue(*p,tty->read_q); p++; } - sti(); TTY_READ_FLUSH(tty); } +static void respond_num(unsigned int n, int currcons, struct tty_struct * tty) +{ + char buff[3]; + int i = 0; + + do { + buff[i++] = (n%10)+'0'; + n /= 10; + } while(n && i < 3); /* We'll take no chances */ + while (i--) { + put_tty_queue(buff[i],tty->read_q); + } + /* caller must flush */ +} + +static void cursor_report(int currcons, struct tty_struct * tty) +{ + put_tty_queue('\033', tty->read_q); + put_tty_queue('[', tty->read_q); + respond_num(y + (decom ? top+1 : 1), currcons, tty); + put_tty_queue(';', tty->read_q); + respond_num(x+1, currcons, tty); + put_tty_queue('R', tty->read_q); + TTY_READ_FLUSH(tty); +} + +static inline void status_report(int currcons, struct tty_struct * tty) +{ + respond_string("\033[0n", currcons, tty); /* Terminal ok */ +} + +static inline void respond_ID(int currcons, struct tty_struct * tty) +{ + respond_string(VT102ID, currcons, tty); +} + +static void invert_screen(int currcons) { + unsigned char *p; + + if (can_do_color) + for (p = (unsigned char *)origin+1; p < (unsigned char *)scr_end; p+=2) + *p = (*p & 0x88) | (((*p >> 4) | (*p << 4)) & 0x77); + else + for (p = (unsigned char *)origin+1; p < (unsigned char *)scr_end; p+=2) + *p ^= *p & 0x07 == 1 ? 0x70 : 0x77; +} + +static void set_mode(int currcons, int on_off) +{ + int i; + + for (i=0; i<=npar; i++) + if (ques) switch(par[i]) { /* DEC private modes set/reset */ + case 1: /* Cursor keys send ^[Ox/^[[x */ + SET(decckm,ckmode,on_off); + break; + case 3: /* 80/132 mode switch unimplemented */ + csi_J(currcons,2); + gotoxy(currcons,0,0); + break; + case 5: /* Inverted screen on/off */ + if (decscnm != on_off) { + decscnm = on_off; + invert_screen(currcons); + update_attr(currcons); + } + break; + case 6: /* Origin relative/absolute */ + decom = on_off; + gotoxy(currcons,0,0); + break; + case 7: /* Autowrap on/off */ + decawm = on_off; + break; + case 8: /* Autorepeat on/off */ + SET(decarm,krepeat,on_off); + break; + case 25: /* Cursor on/off */ + deccm = on_off; + set_cursor(currcons); + break; + } else switch(par[i]) { /* ANSI modes set/reset */ + case 4: /* Insert Mode on/off */ + decim = on_off; + break; + case 20: /* Lf, Enter == CrLf/Lf */ + SET(lnm,lfnlmode,on_off); + break; + } +} + +static void setterm_command(int currcons) +{ + switch(par[0]) { + case 1: /* set color for underline mode */ + if (can_do_color && par[1] < 16) { + ulcolor = color_table[par[1]]; + if (underline) + update_attr(currcons); + } + break; + case 2: /* set color for half intensity mode */ + if (can_do_color && par[1] < 16) { + halfcolor = color_table[par[1]]; + if (intensity == 0) + update_attr(currcons); + } + break; + case 8: /* store colors as defaults */ + def_color = attr; + default_attr(currcons); + update_attr(currcons); + break; + case 9: /* set blanking interval */ + blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; + break; + } +} + static void insert_char(int currcons) { unsigned int i = x; @@ -496,11 +742,13 @@ static void insert_char(int currcons) old = tmp; p++; } + need_wrap = 0; } static void insert_line(int currcons) { scrdown(currcons,y,bottom); + need_wrap = 0; } static void delete_char(int currcons) @@ -508,18 +756,18 @@ static void delete_char(int currcons) unsigned int i = x; unsigned short * p = (unsigned short *) pos; - if (x >= video_num_columns) - return; while (++i < video_num_columns) { *p = *(p+1); p++; } *p = video_erase_char; + need_wrap = 0; } static void delete_line(int currcons) { scrup(currcons,y,bottom); + need_wrap = 0; } static void csi_at(int currcons, unsigned int nr) @@ -564,82 +812,164 @@ static void csi_M(int currcons, unsigned int nr) static void save_cur(int currcons) { - saved_x = x; - saved_y = y; + saved_x = x; + saved_y = y; + s_intensity = intensity; + s_underline = underline; + s_blink = blink; + s_reverse = reverse; + s_charset = charset; + s_color = color; + saved_G0 = G0_charset; + saved_G1 = G1_charset; } static void restore_cur(int currcons) { - gotoxy(currcons, saved_x, saved_y); + gotoxy(currcons,saved_x,saved_y); + intensity = s_intensity; + underline = s_underline; + blink = s_blink; + reverse = s_reverse; + charset = s_charset; + color = s_color; + G0_charset = saved_G0; + G1_charset = saved_G1; + translate = charset ? G1_charset : G0_charset; + update_attr(currcons); + need_wrap = 0; } - enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, - ESsetterm, ESsetgraph, ESgraph, ESgresc, ESignore }; + EShash, ESsetG0, ESsetG1, ESignore }; + +static void reset_terminal(int currcons, int do_clear) +{ + top = 0; + bottom = video_num_lines; + state = ESnormal; + ques = 0; + translate = NORM_TRANS; + G0_charset = NORM_TRANS; + G1_charset = GRAF_TRANS; + charset = 0; + need_wrap = 0; + + decscnm = 0; + decom = 0; + decawm = 1; + deccm = 1; + decim = 0; + + if (currcons == fg_console) { + krepeat = 1; + ckmode = 0; + kapplic = 0; + lfnlmode = 0; + kleds = 2; + kmode = 0; + set_leds(); + } else { + decarm = 1; + decckm = 0; + kbdapplic = 0; + lnm = 0; + kbdleds = 2; + kbdmode = 0; + } + + default_attr(currcons); + update_attr(currcons); + + tab_stop[0] = 0x01010100; + tab_stop[1] = + tab_stop[2] = + tab_stop[3] = + tab_stop[4] = 0x01010101; + + if (do_clear) { + gotoxy(currcons,0,0); + csi_J(currcons,2); + save_cur(currcons); + } +} void con_write(struct tty_struct * tty) { - unsigned char c; + int c; unsigned int currcons; wake_up(&tty->write_q->proc_list); currcons = tty - tty_table; - if (currcons >= MAX_CONSOLES) { + if (currcons >= NR_CONSOLES) { printk("con_write: illegal tty\n\r"); return; } - while (!EMPTY(tty->write_q)) { - if (tty->stopped) - break; - GETCH(tty->write_q,c); - if (c == 24 || c == 26) - state = ESnormal; - switch(state) { - case ESnormal: - if (translate[c]) { - c = translate[c]; - while (x >= video_num_columns) { - x -= video_num_columns; - pos -= video_size_row; - lf(currcons); - } - *(char *) pos = c; - *(char *) (pos+1) = attr; - pos += 2; + while (!tty->stopped && (c = get_tty_queue(tty->write_q)) >= 0) { + if (state == ESnormal && translate[c]) { + if (need_wrap) { + cr(currcons); + lf(currcons); + } + if (decim) + insert_char(currcons); + c = translate[c]; + *(char *) pos = c; + *(char *) (pos+1) = attr; + if (x == video_num_columns - 1) + need_wrap = decawm; + else { + x++; + pos+=2; + } + continue; + } + + /* + * Control characters can be used in the _middle_ + * of an escape sequence. + */ + if (c < 32 || c == 127) switch(c) { + case 7: + sysbeep(); + break; + case 8: + bs(currcons); + break; + case 9: + pos -= (x << 1); + while (x < video_num_columns - 1) { x++; - } else if (c == 27) - state = ESesc; - else if (c == 10 || c == 11 || c == 12) - lf(currcons); - else if (c == 13) - cr(currcons); - else if (c == 127) - del(currcons); - else if (c == 8) { - if (x) { - x--; - pos -= 2; - } - } else if (c == 9) { - c = 8-(x&7); - x += c; - pos += c<<1; - if (x > video_num_columns) { - x -= video_num_columns; - pos -= video_size_row; - lf(currcons); - } - c = 9; - } else if (c == 7) - sysbeep(); - else if (c == 14) { - checkin = 1; - translate = restate; - } else if (c == 15) { - translate = NORM_TRANS; - checkin = 0; - } + if (tab_stop[x >> 5] & (1 << (x & 31))) + break; + } + pos += (x << 1); + break; + case 10: case 11: case 12: + lf(currcons); + if (!lfnlmode) + break; + case 13: + cr(currcons); break; + case 14: + charset = 1; + translate = G1_charset; + break; + case 15: + charset = 0; + translate = G0_charset; + break; + case 24: case 26: + state = ESnormal; + break; + case 27: + state = ESesc; + break; + case 127: + del(currcons); + break; + } else switch(state) { case ESesc: state = ESnormal; switch (c) { @@ -647,7 +977,8 @@ void con_write(struct tty_struct * tty) state = ESsquare; break; case 'E': - gotoxy(currcons,0,y+1); + cr(currcons); + lf(currcons); break; case 'M': ri(currcons); @@ -655,8 +986,11 @@ void con_write(struct tty_struct * tty) case 'D': lf(currcons); break; + case 'H': + tab_stop[x >> 5] |= (1 << (x & 31)); + break; case 'Z': - respond(currcons,tty); + respond_ID(currcons,tty); break; case '7': save_cur(currcons); @@ -665,32 +999,22 @@ void con_write(struct tty_struct * tty) restore_cur(currcons); break; case '(': + state = ESsetG0; + break; case ')': - state = ESsetgraph; + state = ESsetG1; break; - case 'P': - state = ESsetterm; - break; case '#': - state = -1; - break; + state = EShash; + break; case 'c': - tty->termios = DEF_TERMIOS; - state = ESnormal; - restate = NORM_TRANS; - checkin = 0; - top = 0; - bottom = video_num_lines; - translate = NORM_TRANS; + reset_terminal(currcons,1); + break; case '>': /* Numeric keypad */ - kbdapplic = 0; - if (currcons == fg_console) - kapplic = 0; - break; + SET(kbdapplic,kapplic,0); + break; case '=': /* Appl. keypad */ - kbdapplic = 1; - if (currcons == fg_console) - kapplic = 1; + SET(kbdapplic,kapplic,1); break; } break; @@ -702,7 +1026,7 @@ void con_write(struct tty_struct * tty) if (c == '[') { /* Function key */ state=ESfunckey; break; - } + } if (ques=(c=='?')) break; case ESgetpars: @@ -716,10 +1040,25 @@ void con_write(struct tty_struct * tty) } else state=ESgotpars; case ESgotpars: state = ESnormal; + switch(c) { + case 'h': + set_mode(currcons,1); + break; + case 'l': + set_mode(currcons,0); + break; + case 'n': + if (!ques) + if (par[0] == 5) + status_report(currcons,tty); + else if (par[0] == 6) + cursor_report(currcons,tty); + break; + } if (ques) { ques = 0; break; - } + } switch(c) { case 'G': case '`': if (par[0]) par[0]--; @@ -773,21 +1112,35 @@ void con_write(struct tty_struct * tty) case 'P': csi_P(currcons,par[0]); break; - case '@': - csi_at(currcons,par[0]); + case 'c': + if (!par[0]) + respond_ID(currcons,tty); + break; + case 'g': + if (!par[0]) + tab_stop[x >> 5] &= ~(1 << (x & 31)); + else if (par[0] == 3) { + tab_stop[0] = + tab_stop[1] = + tab_stop[2] = + tab_stop[3] = + tab_stop[4] = 0; + } break; case 'm': csi_m(currcons); break; case 'r': - if (par[0]) - par[0]--; + if (!par[0]) + par[0]++; if (!par[1]) par[1] = video_num_lines; + /* Minimum allowed region is 2 lines */ if (par[0] < par[1] && par[1] <= video_num_lines) { - top=par[0]; + top=par[0]-1; bottom=par[1]; + gotoxy(currcons,0,0); } break; case 's': @@ -796,61 +1149,65 @@ void con_write(struct tty_struct * tty) case 'u': restore_cur(currcons); break; - case 'l': /* blank interval */ - case 'b': /* bold attribute */ - if (!((npar >= 2) && - ((par[1]-13) == par[0]) && - ((par[2]-17) == par[0]))) - break; - if ((c=='l') && (par[0]<=60)) { - blankinterval = HZ*60*par[0]; - } - if (c=='b') - vc_cons[currcons].vc_bold_attr - = par[0]; + case '@': + csi_at(currcons,par[0]); + break; + case ']': /* setterm functions */ + setterm_command(currcons); + break; } break; case ESfunckey: state = ESnormal; break; - case ESsetterm: /* Setterm functions. */ + case EShash: + state = ESnormal; + if (c == '8') { + /* DEC screen alignment test. kludge :-) */ + video_erase_char = + (video_erase_char & 0xff00) | 'E'; + csi_J(currcons, 2); + video_erase_char = + (video_erase_char & 0xff00) | ' '; + } + break; + case ESsetG0: + if (c == '0') + G0_charset = GRAF_TRANS; + else if (c == 'B') + G0_charset = NORM_TRANS; + else if (c == 'U') + G0_charset = NULL_TRANS; + if (charset == 0) + translate = G0_charset; state = ESnormal; - if (c == 'S') { - def_attr = attr; - video_erase_char = (video_erase_char&0x0ff) | (def_attr<<8); - } else if (c == 'L') - /*linewrap on*/; - else if (c == 'l') - /*linewrap off*/; break; - case ESsetgraph: - if (c == '0') { - if (checkin) - translate = GRAF_TRANS; - restate = GRAF_TRANS; - } else if (c == 'B') - translate = restate = NORM_TRANS; + case ESsetG1: + if (c == '0') + G1_charset = GRAF_TRANS; + else if (c == 'B') + G1_charset = NORM_TRANS; + else if (c == 'U') + G1_charset = NULL_TRANS; + if (charset == 1) + translate = G1_charset; state = ESnormal; break; default: state = ESnormal; } } + if (vtmode == KD_GRAPHICS) + return; set_cursor(currcons); - timer_active &= ~(1< MAX_CONSOLES) - NR_CONSOLES = MAX_CONSOLES; - if (!NR_CONSOLES) - NR_CONSOLES = 1; - video_memory = screen_size; - + /* Initialize the variables used for scrolling (mostly EGA/VGA) */ - - base = origin = video_mem_start = video_mem_base; - term = video_mem_end = base + video_memory; - scr_end = video_mem_start + screen_size; - top = 0; - bottom = video_num_lines; - attr = 0x07; - def_attr = 0x07; - restate = NORM_TRANS; - state = ESnormal; - checkin = 0; - ques = 0; - iscolor = 0; - translate = NORM_TRANS; - kbdleds = 2; - kbdmode = 0; - kbdraw = 0; - kbde0 = 0; - kbdapplic = 0; - vc_cons[0].vc_bold_attr = -1; - - gotoxy(currcons,ORIG_X,ORIG_Y); - for (currcons = 1; currconsNR_CONSOLES)) + if ((currcons<0) || (currcons>=NR_CONSOLES)) return -EIO; put_fs_byte((char)(video_num_lines),buf++); put_fs_byte((char)(video_num_columns),buf++); @@ -1138,27 +1467,32 @@ void console_print(const char * b) if (currcons<0 || currcons>=NR_CONSOLES) currcons = 0; - if (vt_info[currcons].mode == KD_GRAPHICS) - return; /* no output in graphics mode */ while (c = *(b++)) { - if (c == 10) { - cr(currcons); - lf(currcons); - continue; - } - if (c == 13) { + if (c == 10 || c == 13 || need_wrap) { + if (c != 13) + lf(currcons); cr(currcons); - continue; - } - while (x >= video_num_columns) { - x -= video_num_columns; - pos -= video_size_row; - lf(currcons); + if (c == 10 || c == 13) + continue; } *(char *) pos = c; *(char *) (pos+1) = attr; - pos += 2; + if (x == video_num_columns - 1) { + need_wrap = 1; + continue; + } x++; + pos+=2; } set_cursor(currcons); + if (vt_cons[fg_console].vt_mode == KD_GRAPHICS) + return; + timer_active &= ~(1< - .fill 10,1,0 - -alt_map: - .byte 0,0 - .ascii "\0@\243$\0\0{[]}\\\0" - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte '~,13,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0,0,0 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte 0,0,0,0,0 /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '| - .fill 10,1,0 - -#elif defined(KBD_US) - -key_map: - .byte 0,27 - .ascii "1234567890-=" - .byte 127,9 - .ascii "qwertyuiop[]" - .byte 13,0 - .ascii "asdfghjkl;'" - .byte '`,0 - .ascii "\\zxcvbnm,./" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '< - .fill 10,1,0 - -shift_map: - .byte 0,27 - .ascii "!@#$%^&*()_+" - .byte 127,9 - .ascii "QWERTYUIOP{}" - .byte 13,0 - .ascii "ASDFGHJKL:\"" - .byte '~,0 - .ascii "|ZXCVBNM<>?" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '> - .fill 10,1,0 - -alt_map: - .byte 0,0 - .ascii "\0@\0$\0\0{[]}\\\0" - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte '~,13,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0,0,0 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte 0,0,0,0,0 /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '| - .fill 10,1,0 - -#elif defined(KBD_UK) - -key_map: - .byte 0,27 - .ascii "1234567890-=" - .byte 127,9 - .ascii "qwertyuiop[]" - .byte 13,0 - .ascii "asdfghjkl;'" - .byte '`,0 - .ascii "#zxcvbnm,./" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .ascii "\\" - .fill 10,1,0 - -shift_map: - .byte 0,27 - .ascii "!\"\243$%^&*()_+" - .byte 127,9 - .ascii "QWERTYUIOP{}" - .byte 13,0 - .ascii "ASDFGHJKL:@" - .byte '~,0 - .ascii "~ZXCVBNM<>?" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '| - .fill 10,1,0 - -alt_map: - .byte 0,0 - .ascii "\0@\0$\0\0{[]}\\\0" - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte '~,13,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0,0,0 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte 0,0,0,0,0 /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '| - .fill 10,1,0 - -#elif defined(KBD_GR) - -key_map: - .byte 0,27 - .ascii "1234567890\\'" - .byte 127,9 - .ascii "qwertzuiop@+" - .byte 13,0 - .ascii "asdfghjkl[]^" - .byte 0,'# - .ascii "yxcvbnm,.-" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '< - .fill 10,1,0 - -shift_map: - .byte 0,27 - .ascii "!\"#$%&/()=?`" - .byte 127,9 - .ascii "QWERTZUIOP\\*" - .byte 13,0 - .ascii "ASDFGHJKL{}~" - .byte 0,'' - .ascii "YXCVBNM;:_" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '> - .fill 10,1,0 - -alt_map: - .byte 0,0 - .ascii "\0@\0$\0\0{[]}\\\0" - .byte 0,0 - .byte '@,0,0,0,0,0,0,0,0,0,0 - .byte '~,13,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0,0,0 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte 0,0,0,0,0 /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '| - .fill 10,1,0 - -#elif defined(KBD_FR) - -key_map: - .byte 0,27 - .ascii "&{\"'(-}_/@)=" - .byte 127,9 - .ascii "azertyuiop^$" - .byte 13,0 - .ascii "qsdfghjklm|" - .byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */ - .ascii "wxcvbn,;:!" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '< - .fill 10,1,0 - -shift_map: - .byte 0,27 - .ascii "1234567890]+" - .byte 127,9 - .ascii "AZERTYUIOP<>" - .byte 13,0 - .ascii "QSDFGHJKLM%" - .byte '~,0,'# - .ascii "WXCVBN?./\\" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '> - .fill 10,1,0 - -alt_map: - .byte 0,0 - .ascii "\0~#{[|`\\^@]}" - .byte 0,0 - .byte '@,0,0,0,0,0,0,0,0,0,0 - .byte '~,13,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0,0,0 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte 0,0,0,0,0 /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '| - .fill 10,1,0 - -#elif defined(KBD_DK) - -key_map: - .byte 0,27 - .ascii "1234567890+'" - .byte 127,9 - .ascii "qwertyuiop" - .byte 134,0,13,0 /* This is IBM-PC, change it to latin-1 */ - .ascii "asdfghjkl" - .byte 145,155,0,0 - .ascii "'zxcvbnm,.-" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '< - .fill 10,1,0 - -shift_map: - .byte 0,27 - .ascii "!\"#$%&/()=?`" - .byte 127,9 - .ascii "QWERTYUIOP" - .byte 143,94,13,0 - .ascii "ASDFGHJKL" - .byte 146,157,0,0 - .ascii "*ZXCVBNM;:_" - .byte 0,'*,0,32 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte '-,0,0,0,'+ /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .byte '> - .fill 10,1,0 - -alt_map: - .byte 0,0 - .ascii "\0@\0$\0\0{[]}\0" - .byte '|,0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte '~,13,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0 - .byte 0,0,0,0,0,0,0,0,0,0,0 - .byte 0,0,0,0 /* 36-39 */ - .fill 16,1,0 /* 3A-49 */ - .byte 0,0,0,0,0 /* 4A-4E */ - .byte 0,0,0,0,0,0,0 /* 4F-55 */ - .ascii "\\" - .fill 10,1,0 - -#else -#error "KBD-type not defined" -#endif -/* - * do_self handles "normal" keys, ie keys that don't change meaning - * and which have just one character returns. - */ -do_self: - lea alt_map,%ebx - testb $0x20,_kmode /* alt-gr */ - jne 1f - lea shift_map,%ebx - testb $0x0f,_kmode - jne 1f - lea key_map,%ebx -1: movb (%ebx,%eax),%al - orb %al,%al - je none - testb $0x4c,_kmode /* ctrl or caps */ - je 2f - cmpb $'a,%al - jb 2f - cmpb $'},%al - ja 2f - subb $32,%al -2: testb $0x0c,_kmode /* ctrl */ - je 3f - andb $0x1f,%al -3: testb $0x10,_kmode /* left alt */ - je 4f - orb $0x80,%al -4: andl $0xff,%eax - xorl %ebx,%ebx - call put_queue -none: ret - -/* - * slash and star have routines of their own, as a 'E0h' before - * the scan code for slash means that the numeric keypad - * slash was pushed. - */ -slash: cmpb $1,_ke0 - jne do_self - cmpb $1,_kapplic - jne notmapplic - movw $'Q,%ax - jmp applkey - -notmapplic: - movl $'/,%eax - xorl %ebx,%ebx - jmp put_queue - -star: cmpb $1,_kapplic - jne do_self - movw $'R,%ax - jmp applkey - -notsapplic: - movl $'*,%eax - xorl %ebx,%ebx - jmp put_queue - -enter: cmpb $1,_ke0 - jne do_self - cmpb $1,_kapplic - jne do_self - movw $'M,%ax - jmp applkey - -minus: cmpb $1,_kapplic - jne do_self - movw $'S,%ax - jmp applkey - -plus: cmpb $1,_kapplic - jne do_self - movw $'l,%ax - jmp applkey - -/* - * This table decides which routine to call when a scan-code has been - * gotten. Most routines just call do_self, or none, depending if - * they are make or break. - */ -key_table: - .long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */ - .long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */ - .long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */ - .long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */ - .long do_self,do_self,do_self,do_self /* 10-13 q w e r */ - .long do_self,do_self,do_self,do_self /* 14-17 t y u i */ - .long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */ - .long enter,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */ - .long do_self,do_self,do_self,do_self /* 20-23 d f g h */ - .long do_self,do_self,do_self,do_self /* 24-27 j k l | */ - .long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */ - .long do_self,do_self,do_self,do_self /* 2C-2F z x c v */ - .long do_self,do_self,do_self,do_self /* 30-33 b n m , */ - .long do_self,slash,rshift,star /* 34-37 . - rshift * */ - .long alt,do_self,caps,func /* 38-3B alt sp caps f1 */ - .long func,func,func,func /* 3C-3F f2 f3 f4 f5 */ - .long func,func,func,func /* 40-43 f6 f7 f8 f9 */ - .long func,num,scroll,cursor /* 44-47 f10 num scr home */ - .long cursor,cursor,minus,cursor /* 48-4B up pgup - left */ - .long cursor,cursor,plus,cursor /* 4C-4F n5 right + end */ - .long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */ - .long none,none,do_self,func /* 54-57 sysreq ? < f11 */ - .long func,none,none,none /* 58-5B f12 ? ? ? */ - .long none,none,none,none /* 5C-5F ? ? ? ? */ - .long none,none,none,none /* 60-63 ? ? ? ? */ - .long none,none,none,none /* 64-67 ? ? ? ? */ - .long none,none,none,none /* 68-6B ? ? ? ? */ - .long none,none,none,none /* 6C-6F ? ? ? ? */ - .long none,none,none,none /* 70-73 ? ? ? ? */ - .long none,none,none,none /* 74-77 ? ? ? ? */ - .long none,none,none,none /* 78-7B ? ? ? ? */ - .long none,none,none,none /* 7C-7F ? ? ? ? */ - .long none,none,none,none /* 80-83 ? br br br */ - .long none,none,none,none /* 84-87 br br br br */ - .long none,none,none,none /* 88-8B br br br br */ - .long none,none,none,none /* 8C-8F br br br br */ - .long none,none,none,none /* 90-93 br br br br */ - .long none,none,none,none /* 94-97 br br br br */ - .long none,none,none,none /* 98-9B br br br br */ - .long none,unctrl,none,none /* 9C-9F br unctrl br br */ - .long none,none,none,none /* A0-A3 br br br br */ - .long none,none,none,none /* A4-A7 br br br br */ - .long none,none,unlshift,none /* A8-AB br br unlshift br */ - .long none,none,none,none /* AC-AF br br br br */ - .long none,none,none,none /* B0-B3 br br br br */ - .long none,none,unrshift,none /* B4-B7 br br unrshift br */ - .long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */ - .long none,none,none,none /* BC-BF br br br br */ - .long none,none,none,none /* C0-C3 br br br br */ - .long none,none,none,none /* C4-C7 br br br br */ - .long none,none,none,none /* C8-CB br br br br */ - .long none,none,none,none /* CC-CF br br br br */ - .long none,none,none,none /* D0-D3 br br br br */ - .long none,none,none,none /* D4-D7 br br br br */ - .long none,none,none,none /* D8-DB br ? ? ? */ - .long none,none,none,none /* DC-DF ? ? ? ? */ - .long none,none,none,none /* E0-E3 e0 e1 ? ? */ - .long none,none,none,none /* E4-E7 ? ? ? ? */ - .long none,none,none,none /* E8-EB ? ? ? ? */ - .long none,none,none,none /* EC-EF ? ? ? ? */ - .long none,none,none,none /* F0-F3 ? ? ? ? */ - .long none,none,none,none /* F4-F7 ? ? ? ? */ - .long none,none,none,none /* F8-FB ? ? ? ? */ - .long none,none,none,none /* FC-FF ? ? ? ? */ - -/* - * kb_wait waits for the keyboard controller buffer to empty. - */ -kb_wait: - pushl %eax - pushl %ebx - movl $10000,%ebx -1: inb $0x64,%al - testb $0x02,%al - je 2f - decl %ebx - jne 1b -2: popl %ebx - popl %eax - ret - -no_idt: - .long 0,0 -/* - * This routine reboots the machine by asking the keyboard - * controller to pulse the reset-line low. We try that for a while, - * and if it doesn't work, we do some other stupid things. - */ -_hard_reset_now: - sti - movl $100,%ebx -1: call kb_wait - movw $0x1234,0x472 /* don't do memory check */ - movb $0xfe,%al /* pulse reset low */ - outb %al,$0x64 - decl %ebx - jne 1b - lidt no_idt /* zero-length idt: should triple-fault */ - jmp _hard_reset_now diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c new file mode 100644 index 000000000000..8e4e52130ce3 --- /dev/null +++ b/kernel/chr_drv/keyboard.c @@ -0,0 +1,1307 @@ +/* + * linux/kernel/chr_drv/keyboard.c + * + * Keyboard driver for Linux v0.96 using Latin-1. + * + * Written for linux by Johan Myreen as a translation from + * the assembly version by Linus (with diacriticals added) + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define LSHIFT 0x01 +#define RSHIFT 0x02 +#define LCTRL 0x04 +#define RCTRL 0x08 +#define ALT 0x10 +#define ALTGR 0x20 +#define CAPS 0x40 +#define CAPSDOWN 0x80 + +#define SCRLED 0x01 +#define NUMLED 0x02 +#define CAPSLED 0x04 + +#define NO_META_BIT 0x80 + +unsigned char kapplic = 0; +unsigned char ckmode = 0; +unsigned char krepeat = 1; +unsigned char kmode = 0; +unsigned char kleds = NUMLED; +unsigned char ke0 = 0; +unsigned char kraw = 0; +unsigned char kbd_flags = KBDFLAGS; +unsigned char lfnlmode = 0; + +extern void do_keyboard_interrupt(void); +extern void ctrl_alt_del(void); +extern void change_console(unsigned int new_console); +extern struct tty_queue *table_list[]; + +typedef void (*fptr)(int); + +static unsigned char old_leds = 2; +static int diacr = -1; +static int npadch = 0; +fptr key_table[]; + +static void put_queue(int); +void set_leds(void); +static void applkey(int); +static void cur(int); +static void kb_wait(void), kb_ack(void); +static unsigned int handle_diacr(unsigned int); + +static struct pt_regs * pt_regs; + +void keyboard_interrupt(int int_pt_regs) +{ + static unsigned char rep = 0xff, repke0 = 0; + unsigned char scancode, x; + struct tty_struct * tty = TTY_TABLE(0); + + pt_regs = (struct pt_regs *) int_pt_regs; + scancode=inb_p(0x60); + x=inb_p(0x61); + outb_p(x|0x80, 0x61); + outb_p(x&0x7f, 0x61); + outb(0x20, 0x20); + sti(); + + if (kraw) { + put_queue(scancode); + do_keyboard_interrupt(); + return; + } + if (scancode == 0xe0) { + ke0 = 1; + return; + } + if (scancode == 0xe1) { + ke0 = 2; + return; + } + /* + * The keyboard maintains its own internal caps lock and num lock + * statuses. In caps lock mode E0 AA precedes make code and E0 2A + * follows break code. In num lock mode, E0 2A precedes make + * code and E0 AA follows break code. We do our own book-keeping, + * so we will just ignore these. + */ + if (ke0 == 1 && (scancode == 0x2a || scancode == 0xaa)) { + ke0 = 0; + return; + } + /* + * Repeat a key only if the input buffers are empty or the + * characters get echoed locally. This makes key repeat usable + * with slow applications and unders heavy loads. + */ + if (rep == 0xff) { + if (scancode < 0x80) { + rep = scancode; + repke0 = ke0; + } + } else if (ke0 == repke0 && (scancode & 0x7f) == rep) + if (scancode & 0x80) + rep = 0xff; + else if (!(krepeat && (L_ECHO(tty) || (EMPTY(tty->secondary) && + EMPTY(tty->read_q))))) { + ke0 = 0; + return; + } + key_table[scancode](scancode); + do_keyboard_interrupt(); + ke0 = 0; +} + +static void put_queue(int ch) +{ + register struct tty_queue *qp = table_list[0]; + unsigned long new_head; + + qp->buf[qp->head]=ch; + if ((new_head=(qp->head+1)&(TTY_BUF_SIZE-1)) != qp->tail) + qp->head=new_head; + wake_up(&qp->proc_list); +} + +static void puts_queue(char *cp) +{ + register struct tty_queue *qp = table_list[0]; + unsigned long new_head; + char ch; + + while (ch=*cp++) { + qp->buf[qp->head]=ch; + if ((new_head=(qp->head+1)&(TTY_BUF_SIZE-1)) + != qp->tail) + qp->head=new_head; + } + wake_up(&qp->proc_list); +} + +static void ctrl(int sc) +{ + if (ke0) + kmode|=RCTRL; + else + kmode|=LCTRL; +} + +static void alt(int sc) +{ + if (ke0) + kmode|=ALTGR; + else + kmode|=ALT; +} + +static void unctrl(int sc) +{ + if (ke0) + kmode&=(~RCTRL); + else + kmode&=(~LCTRL); +} + +static void unalt(int sc) +{ + if (ke0) + kmode&=(~ALTGR); + else { + kmode&=(~ALT); + if (npadch != 0) { + put_queue(npadch); + npadch=0; + } + } +} + +static void lshift(int sc) +{ + kmode|=LSHIFT; +} + +static void unlshift(int sc) +{ + kmode&=(~LSHIFT); +} + +static void rshift(int sc) +{ + kmode|=RSHIFT; +} + +static void unrshift(int sc) +{ + kmode&=(~RSHIFT); +} + +static void caps(int sc) +{ + if (!(kmode & CAPSDOWN)) { + kleds ^= CAPSLED; + kmode ^= CAPS; + kmode |= CAPSDOWN; + set_leds(); + } +} + +void set_leds(void) +{ + if (kleds != old_leds) { + old_leds = kleds; + kb_wait(); + outb(0xed, 0x60); /* set leds command */ + kb_ack(); + kb_wait(); + outb(kleds, 0x60); + kb_ack(); + } +} + +static void uncaps(int sc) +{ + kmode &= ~CAPSDOWN; +} + +static void show_ptregs(void) +{ + printk("\nEIP: %04x:%08x",0xffff & pt_regs->cs,pt_regs->eip); + if (pt_regs->cs & 3) + printk(" ESP: %04x:%08x",0xffff & pt_regs->cs,pt_regs->eip); + printk(" EFLAGS: %08x",pt_regs->eflags); + printk("\nEAX: %08x EBX: %08x ECX: %08x EDX: %08x", + pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx); + printk("\nESI: %08x EDI: %08x EBP: %08x", + pt_regs->esi, pt_regs->edi, pt_regs->ebp); + printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n", + 0xffff & pt_regs->ds,0xffff & pt_regs->es, + 0xffff & pt_regs->fs,0xffff & pt_regs->gs); +} + +static void scroll(int sc) +{ + if (kmode & (LSHIFT | RSHIFT)) + show_mem(); + else if (kmode & (ALT | ALTGR)) + show_ptregs(); + else if (kmode & (LCTRL | RCTRL)) + show_state(); + kleds ^= SCRLED; + set_leds(); +} + +static void num(int sc) +{ + if (kapplic) + applkey(0x50); + else { + kleds ^= NUMLED; + set_leds(); + } +} + +static void applkey(int key) +{ + char buf[] = { 0x1b, 0x4f, 0x00, 0x00 }; + + buf[2] = key; + puts_queue(buf); +} + + +#if defined KBD_FINNISH + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\'', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '}', 0, 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|', + '{', 0, 0, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '\"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', ']', '^', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\', + '[', 0, 0, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 163, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_FINNISH_LATIN1 + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', 180, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 229, 168, 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246, + 228, 167, 0, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 197, '^', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214, + 196, 189, 0, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 163, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_US + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', '0', '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 0, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_UK + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0, '#', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '\\', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '"', 163, '$', '%', '^', + '&', '*', '(', ')', '_', '+', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '@', '~', '0', '~', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 0, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_GR + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\\', '\'', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', '@', '+', 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '[', + ']', '^', 0, '#', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', '\\', '*', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '{', + '}', '~', 0, '\'', 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 0, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + '@', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_GR_LATIN1 + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 223, 180, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 252, '+', 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246, + 228, 94, 0, '#', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '"', 167, '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 220, '*', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214, + 196, 176, 0, '\'', 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, 178, 179, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + '@', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 181, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_FR + +static unsigned char key_map[] = { + 0, 27, '&', '{', '"', '\'', '(', '-', + '}', '_', '/', '@', ')', '=', 127, 9, + 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '^', '$', 13, 0, 'q', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', + '|', '`', 0, 42, 'w', 'x', 'c', 'v', + 'b', 'n', ',', ';', ':', '!', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', ']', '+', 127, 9, + 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '<', '>', 13, 0, 'Q', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + '%', '~', 0, '#', 'W', 'X', 'C', 'V', + 'B', 'N', '?', '.', '/', '\\', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '~', '#', '{', '[', '|', + '`', '\\', '^', '@', ']', '}', 0, 0, + '@', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_FR_LATIN1 + +static unsigned char key_map[] = { + 0, 27, '&', 233, '"', '\'', '(', '-', + 232, '_', 231, 224, ')', '=', 127, 9, + 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '^', '$', 13, 0, 'q', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', + 249, 178, 0, 42, 'w', 'x', 'c', 'v', + 'b', 'n', ',', ';', ':', '!', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 176, '+', 127, 9, + 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 168, 163, 13, 0, 'Q', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + '%', 0, 0, 181, 'W', 'X', 'C', 'V', + 'B', 'N', '?', '.', '/', 167, 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '~', '#', '{', '[', '|', + '`', '\\', '^', '@', ']', '}', 0, 0, + '@', 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 164, 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_DK + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\'', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 229, 0, 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 230, + 162, 0, 0, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '\"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 197, '^', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 198, + 165, 0, 0, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 163, '$', 0, 0, + '{', '[', ']', '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '\\', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_DK_LATIN1 + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', 180, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 229, 168, 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 230, + 162, 189, 0, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '\"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 197, '^', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 198, + 165, 167, 0, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 163, '$', 0, 0, + '{', '[', ']', '}', 0, '|', 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '\\', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_DVORAK + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\\', '=', 127, 9, + '\'', ',', '.', 'p', 'y', 'f', 'g', 'c', + 'r', 'l', '/', ']', 13, 0, 'a', 'o', + 'e', 'u', 'i', 'd', 'h', 't', 'n', 's', + '-', '`', 0, '[', ';', 'q', 'j', 'k', + 'x', 'b', 'm', 'w', 'v', 'z', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '|', '+', 127, 9, + '"', '<', '>', 'P', 'Y', 'F', 'G', 'C', + 'R', 'L', '?', '}', 13, 0, 'A', 'O', + 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S', + '_', '~', 0, '{', ':', 'Q', 'J', 'K', + 'X', 'B', 'M', 'W', 'V', 'Z', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 0, '$', 0, 0, + '{', '[', ']', '}', '\\', 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '|', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_SG + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0, 0, 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0, + 0, 0, 0, '$', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '+', '"', '*', 0, '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0, '!', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0, + 0, 0, 0, 0, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', '#', 0, 0, 0, + '|', 0, 0, 0, '\'', '~', 0, 0, + '@', 0, 0, 0, 0, 0, 0, 0, + 0, 0, '[', ']', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + '{', 0, 0, '}', 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '\\', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_SG_LATIN1 + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 252, 0, 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 246, + 228, 167, 0, '$', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '+', '"', '*', 231, '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 220, '!', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 214, + 196, 176, 0, 163, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', '#', 0, 0, 172, + '|', 162, 0, 0, '\'', '~', 0, 0, + '@', 0, 0, 0, 0, 0, 0, 0, + 0, 0, '[', ']', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 233, + '{', 0, 0, '}', 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, '\\', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#elif defined KBD_NO + +static unsigned char key_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\\', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '}', '~', 13, 0, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|', + '{', '|', 0, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '<', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char shift_map[] = { + 0, 27, '!', '\"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', ']', '^', 13, 0, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\', + '[', 0, 0, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', 0, '*', + 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, '-', 0, 0, 0, '+', 0, + 0, 0, 0, 0, 0, 0, '>', 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +static unsigned char alt_map[] = { + 0, 0, 0, '@', 0, '$', 0, 0, + '{', '[', ']', '}', 0, '\'', 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, '~', 13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0 }; + +#else +#error "KBD-type not defined" +#endif + +static void do_self(int sc) +{ + unsigned char ch; + + if (kmode & ALTGR) + ch = alt_map[sc]; + else if (kmode & (LSHIFT | RSHIFT | LCTRL | RCTRL)) + ch = shift_map[sc]; + else + ch = key_map[sc]; + + if (ch == 0) + return; + + if ((ch = handle_diacr(ch)) == 0) + return; + + if (kmode & (LCTRL | RCTRL | CAPS)) /* ctrl or caps */ + if ((ch >= 'a' && ch <= 'z') || (ch >= 224 && ch <= 254)) + ch -= 32; + if (kmode & (LCTRL | RCTRL)) /* ctrl */ + ch &= 0x1f; + + if (kmode & ALT) + if (kbd_flags & NO_META_BIT) { + put_queue('\033'); + put_queue(ch); + } else + put_queue(ch|0x80); + else + put_queue(ch); +} + +unsigned char accent_table[5][64] = { + " \300BCD\310FGH\314JKLMN\322PQRST\331VWXYZ[\\]^_" + "`\340bcd\350fgh\354jklmn\362pqrst\371vwxyz{|}~", /* accent grave */ + + " \301BCD\311FGH\315JKLMN\323PQRST\332VWX\335Z[\\]^_" + "`\341bcd\351fgh\355jklmn\363pqrst\372vwxyz{|}~", /* accent acute */ + + " \302BCD\312FGH\316JKLMN\324PQRST\333VWXYZ[\\]^_" + "`\342bcd\352fgh\356jklmn\364pqrst\373vwxyz{|}~", /* circumflex */ + + " \303BCDEFGHIJKLMN\325PQRSTUVWXYZ[\\]^_" + "`\343bcdefghijklm\361\365pqrstuvwxyz{|}~", /* tilde */ + + " \304BCD\313FGH\316JKLMN\326PQRST\334VWXYZ[\\]^_" + "`\344bcd\353fgh\357jklmn\366pqrst\374vwx\377z{|}~" /* dieresis */ +}; + + +/* + * Check if dead key pressed. If so, check if same key pressed twice; + * in that case return the char, otherwise store char and return 0. + * If dead key not pressed, check if accented character pending. If + * not: return the char, otherwise check if char is a space. If it is + * a space return the diacritical. Else combine char with diacritical + * mark and return. + */ + +unsigned int handle_diacr(unsigned int ch) +{ + static unsigned char diacr_table[] = + {'`', 180, '^', '~', 168, 0}; /* Must end with 0 */ + int i; + + for(i=0; diacr_table[i]; i++) + if (ch==diacr_table[i] && ((1<122) { + diacr=-1; + return ch; + } else { + ch=accent_table[diacr][ch-64]; + diacr=-1; + return ch; + } +} + + +#if defined KBD_FR || defined KBD_US || defined KBD_UK +static unsigned char num_table[] = "789-456+1230."; +#else +static unsigned char num_table[] = "789-456+1230,"; +#endif + +static unsigned char cur_table[] = "HA5-DGC+YB623"; +static unsigned int pad_table[] = { 7,8,9,0,4,5,6,0,1,2,3,0,0 }; + +/* + Keypad / 35 B7 Q + Keypad * (PrtSc) 37 B7 R + Keypad NumLock 45 ?? P + Keypad 7 (Home) 47 C7 w + Keypad 8 (Up arrow) 48 C8 x + Keypad 9 (PgUp) 49 C9 y + Keypad - 4A CA S + Keypad 4 (Left arrow) 4B CB t + Keypad 5 4C CC u + Keypad 6 (Right arrow) 4D CD v + Keypad + 4E CE l + Keypad 1 (End) 4F CF q + Keypad 2 (Down arrow) 50 D0 r + Keypad 3 (PgDn) 51 D1 s + Keypad 0 (Ins) 52 D2 p + Keypad . (Del) 53 D3 n +*/ + +static unsigned char appl_table[] = "wxyStuvlqrspn"; + +static char *func_table[] = { + "\033[[A", "\033[[B", "\033[[C", "\033[[D", + "\033[[E", "\033[[F", "\033[[G", "\033[[H", + "\033[[I", "\033[[J", "\033[[K", "\033[[L" +}; + + +static void cursor(int sc) +{ + if (sc < 0x47 || sc > 0x53) + return; + sc-=0x47; + if (sc == 12 && (kmode&(LCTRL|RCTRL)) && (kmode&(ALT|ALTGR))) { + ctrl_alt_del(); + return; + } + if (ke0 == 1) { + cur(sc); + return; + } + + if ((kmode&ALT) && sc!=12) { /* Alt-numpad */ + npadch=npadch*10+pad_table[sc]; + return; + } + + if (kapplic && !(kmode&(LSHIFT|RSHIFT))) { /* shift forces cursor */ + applkey(appl_table[sc]); + return; + } + + if (kleds&NUMLED) { + put_queue(num_table[sc]); + } else + cur(sc); +} + +static void cur(int sc) +{ + char buf[] = { 0x1b, '[', 0, 0, 0 }; /* must not be static */ + + buf[2]=cur_table[sc]; + if (buf[2] < '9') + buf[3]='~'; + if ((buf[2] >= 'A' && buf[2] <= 'D') ? ckmode : kapplic) + buf[1]='O'; + puts_queue(buf); +} + +static void func(int sc) +{ + if (sc < 0x3b) + return; + sc-=0x3b; + if (sc > 9) { + sc-=18; + if (sc < 10 || sc > 11) + return; + } + if (kmode&ALT) + change_console(sc); + else + puts_queue(func_table[sc]); +} + + +static void slash(int sc) +{ + if (ke0 != 1) + do_self(sc); + else if (kapplic) + applkey('Q'); + else + put_queue('/'); +} + +static void star(int sc) +{ + if (kapplic) + applkey('R'); + else + do_self(sc); +} + +static void enter(int sc) +{ + if (ke0 == 1 && kapplic) + applkey('M'); + else { + put_queue(13); + if (lfnlmode) + put_queue(10); + } +} + +static void minus(int sc) +{ + if (kapplic) + applkey('S'); + else + do_self(sc); +} + +static void plus(int sc) +{ + if (kapplic) + applkey('l'); + else + do_self(sc); +} + + +static void none(int sc) +{ +} + + +/* + * kb_wait waits for the keyboard controller buffer to empty. + */ + +static void kb_wait(void) +{ + int i; + + for (i=0; i<0x10000; i++) + if ((inb(0x64)&0x02) == 0) + break; +} + +/* + * kb_ack waits for 0xfa to appear in port 0x60 + * + * Suggested by Bruce Evans + * Added by Niels Skou Olsen [NSO] + * April 21, 1992 + * + * Heavily inspired by kb_wait :-) + * I don't know how much waiting actually is required, + * but this seems to work + */ + +void kb_ack(void) +{ + int i; + + for(i=0; i<0x10000; i++) + if (inb(0x60) == 0xfa) + break; +} + +long no_idt[2] = {0, 0}; + +/* + * This routine reboots the machine by asking the keyboard + * controller to pulse the reset-line low. We try that for a while, + * and if it doesn't work, we do some other stupid things. + */ +void hard_reset_now(void) +{ + int i; + + sti(); + for (;;) { + for (i=0; i<100; i++) { + kb_wait(); + *((unsigned short *)0x472)=0x1234; + outb(0xfe,0x64); /* pulse reset low */ + } + __asm__("\tlidt _no_idt"::); + } +} + + +static fptr key_table[] = { + none,do_self,do_self,do_self, /* 00-03 s0 esc 1 2 */ + do_self,do_self,do_self,do_self, /* 04-07 3 4 5 6 */ + do_self,do_self,do_self,do_self, /* 08-0B 7 8 9 0 */ + do_self,do_self,do_self,do_self, /* 0C-0F + ' bs tab */ + do_self,do_self,do_self,do_self, /* 10-13 q w e r */ + do_self,do_self,do_self,do_self, /* 14-17 t y u i */ + do_self,do_self,do_self,do_self, /* 18-1B o p } ^ */ + enter,ctrl,do_self,do_self, /* 1C-1F enter ctrl a s */ + do_self,do_self,do_self,do_self, /* 20-23 d f g h */ + do_self,do_self,do_self,do_self, /* 24-27 j k l | */ + do_self,do_self,lshift,do_self, /* 28-2B { para lshift , */ + do_self,do_self,do_self,do_self, /* 2C-2F z x c v */ + do_self,do_self,do_self,do_self, /* 30-33 b n m , */ + do_self,slash,rshift,star, /* 34-37 . - rshift * */ + alt,do_self,caps,func, /* 38-3B alt sp caps f1 */ + func,func,func,func, /* 3C-3F f2 f3 f4 f5 */ + func,func,func,func, /* 40-43 f6 f7 f8 f9 */ + func,num,scroll,cursor, /* 44-47 f10 num scr home */ + cursor,cursor,minus,cursor, /* 48-4B up pgup - left */ + cursor,cursor,plus,cursor, /* 4C-4F n5 right + end */ + cursor,cursor,cursor,cursor, /* 50-53 dn pgdn ins del */ + none,none,do_self,func, /* 54-57 sysreq ? < f11 */ + func,none,none,none, /* 58-5B f12 ? ? ? */ + none,none,none,none, /* 5C-5F ? ? ? ? */ + none,none,none,none, /* 60-63 ? ? ? ? */ + none,none,none,none, /* 64-67 ? ? ? ? */ + none,none,none,none, /* 68-6B ? ? ? ? */ + none,none,none,none, /* 6C-6F ? ? ? ? */ + none,none,none,none, /* 70-73 ? ? ? ? */ + none,none,none,none, /* 74-77 ? ? ? ? */ + none,none,none,none, /* 78-7B ? ? ? ? */ + none,none,none,none, /* 7C-7F ? ? ? ? */ + none,none,none,none, /* 80-83 ? br br br */ + none,none,none,none, /* 84-87 br br br br */ + none,none,none,none, /* 88-8B br br br br */ + none,none,none,none, /* 8C-8F br br br br */ + none,none,none,none, /* 90-93 br br br br */ + none,none,none,none, /* 94-97 br br br br */ + none,none,none,none, /* 98-9B br br br br */ + none,unctrl,none,none, /* 9C-9F br unctrl br br */ + none,none,none,none, /* A0-A3 br br br br */ + none,none,none,none, /* A4-A7 br br br br */ + none,none,unlshift,none, /* A8-AB br br unlshift br */ + none,none,none,none, /* AC-AF br br br br */ + none,none,none,none, /* B0-B3 br br br br */ + none,none,unrshift,none, /* B4-B7 br br unrshift br */ + unalt,none,uncaps,none, /* B8-BB unalt br uncaps br */ + none,none,none,none, /* BC-BF br br br br */ + none,none,none,none, /* C0-C3 br br br br */ + none,none,none,none, /* C4-C7 br br br br */ + none,none,none,none, /* C8-CB br br br br */ + none,none,none,none, /* CC-CF br br br br */ + none,none,none,none, /* D0-D3 br br br br */ + none,none,none,none, /* D4-D7 br br br br */ + none,none,none,none, /* D8-DB br ? ? ? */ + none,none,none,none, /* DC-DF ? ? ? ? */ + none,none,none,none, /* E0-E3 e0 e1 ? ? */ + none,none,none,none, /* E4-E7 ? ? ? ? */ + none,none,none,none, /* E8-EB ? ? ? ? */ + none,none,none,none, /* EC-EF ? ? ? ? */ + none,none,none,none, /* F0-F3 ? ? ? ? */ + none,none,none,none, /* F4-F7 ? ? ? ? */ + none,none,none,none, /* F8-FB ? ? ? ? */ + none,none,none,none /* FC-FF ? ? ? ? */ +}; diff --git a/kernel/chr_drv/lp.c b/kernel/chr_drv/lp.c index 53d4c9be564a..4270b2d5ca19 100644 --- a/kernel/chr_drv/lp.c +++ b/kernel/chr_drv/lp.c @@ -9,7 +9,6 @@ */ #include -#define __LP_C__ #include static int lp_reset(int minor) @@ -53,13 +52,6 @@ static int lp_write(struct inode * inode, struct file * file, char * buf, int co unsigned int minor = MINOR(inode->i_rdev); char c, *temp = buf; - if (minor >= LP_NO) - return -ENODEV; - if ((LP_F(minor) & LP_EXIST) == 0) - return -ENODEV; - LP_T(minor) = current->pid; - LP_F(minor) |= LP_BUSY; - LP_R(minor) = count; temp = buf; while (count > 0) { c = get_fs_byte(temp++); @@ -97,22 +89,39 @@ static int lp_lseek(struct inode * inode, struct file * file, off_t offset, int return -EINVAL; } -static int lp_readdir(struct inode * inode, struct file * file, struct dirent * de, int count) +static int lp_open(struct inode * inode, struct file * file) { - return -ENOTDIR; + unsigned int minor = MINOR(inode->i_rdev); + + if (minor >= LP_NO) + return -ENODEV; + if ((LP_F(minor) & LP_EXIST) == 0) + return -ENODEV; + if (LP_F(minor) & LP_BUSY) + return -EBUSY; + LP_F(minor) |= LP_BUSY; + return 0; +} + +static void lp_release(struct inode * inode, struct file * file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + LP_F(minor) &= ~LP_BUSY; } static struct file_operations lp_fops = { lp_lseek, lp_read, lp_write, - lp_readdir, - NULL, /* lp_close */ + NULL, /* lp_readdir */ NULL, /* lp_select */ - NULL /* lp_ioctl */ + NULL, /* lp_ioctl */ + lp_open, + lp_release }; -void lp_init(void) +long lp_init(long kmem_start) { int offset = 0; unsigned int testvalue = 0; @@ -135,4 +144,5 @@ void lp_init(void) } if (count == 0) printk("lp_init: no lp devices found\n"); + return kmem_start; } diff --git a/kernel/chr_drv/mem.c b/kernel/chr_drv/mem.c index db3138f5acb8..14962393b921 100644 --- a/kernel/chr_drv/mem.c +++ b/kernel/chr_drv/mem.c @@ -1,15 +1,15 @@ /* * linux/kernel/chr_drv/mem.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include -#include - +#include +#include #include #include #include +#include #include #include @@ -36,17 +36,17 @@ static int read_mem(struct inode * inode, struct file * file,char * buf, int cou addr = file->f_pos; tmp = buf; while (count > 0) { + if (current->signal & ~current->blocked) + break; pde = (unsigned long) pg_dir + (addr >> 20 & 0xffc); - if (!((pte = *((unsigned long *) pde)) & 1)) + pte = *(unsigned long *) pde; + if (!(pte & PAGE_PRESENT)) break; pte &= 0xfffff000; pte += (addr >> 10) & 0xffc; - if (((page = *((unsigned long *) pte)) & 1) == 0) + page = *(unsigned long *) pte; + if (!(page & 1)) break; -/* - if ((page & 2) == 0) - un_wp_page((unsigned long *) pte); -*/ page &= 0xfffff000; page += addr & 0xfff; i = 4096-(addr & 0xfff); @@ -73,15 +73,21 @@ static int write_mem(struct inode * inode, struct file * file,char * buf, int co addr = file->f_pos; tmp = buf; while (count > 0) { + if (current->signal & ~current->blocked) + break; pde = (unsigned long) pg_dir + (addr >> 20 & 0xffc); - if (!((pte = *((unsigned long *) pde)) & 1)) + pte = *(unsigned long *) pde; + if (!(pte & PAGE_PRESENT)) break; pte &= 0xfffff000; pte += (addr >> 10) & 0xffc; - if (((page = *((unsigned long *) pte)) & 1) == 0) + page = *(unsigned long *) pte; + if (!(page & PAGE_PRESENT)) break; - if ((page & 2) == 0) - un_wp_page((unsigned long *) pte); + if (!(page & 2)) { + do_wp_page(0,addr,current,0); + continue; + } page &= 0xfffff000; page += addr & 0xfff; i = 4096-(addr & 0xfff); @@ -93,7 +99,11 @@ static int write_mem(struct inode * inode, struct file * file,char * buf, int co count -= i; } file->f_pos = addr; - return tmp-buf; + if (tmp != buf) + return tmp-buf; + if (current->signal & ~current->blocked) + return -ERESTARTSYS; + return 0; } static int read_kmem(struct inode * inode, struct file * file,char * buf, int count) @@ -102,10 +112,10 @@ static int read_kmem(struct inode * inode, struct file * file,char * buf, int co if (count < 0) return -EINVAL; - if (p >= HIGH_MEMORY) + if (p >= high_memory) return 0; - if (count > HIGH_MEMORY - p) - count = HIGH_MEMORY - p; + if (count > high_memory - p) + count = high_memory - p; memcpy_tofs(buf,(void *) p,count); file->f_pos += count; return count; @@ -117,10 +127,10 @@ static int write_kmem(struct inode * inode, struct file * file,char * buf, int c if (count < 0) return -EINVAL; - if (p >= HIGH_MEMORY) + if (p >= high_memory) return 0; - if (count > HIGH_MEMORY - p) - count = HIGH_MEMORY - p; + if (count > high_memory - p) + count = high_memory - p; memcpy_fromfs((void *) p,buf,count); file->f_pos += count; return count; @@ -154,6 +164,17 @@ static int write_port(struct inode * inode,struct file * file,char * buf, int co return tmp-buf; } +static int read_zero(struct inode *node,struct file *file,char *buf,int count) +{ + int left; + + for (left = count; left > 0; left--) { + put_fs_byte(0,buf); + buf++; + } + return count; +} + /* * The memory devices use the full 32 bits of the offset, and so we cannot * check against negative addresses: they are ok. The return value is weird, @@ -192,6 +213,8 @@ static int mem_read(struct inode * inode, struct file * file, char * buf, int co return 0; /* /dev/null */ case 4: return read_port(inode,file,buf,count); + case 5: + return read_zero(inode,file,buf,count); default: return -ENODEV; } @@ -210,29 +233,29 @@ static int mem_write(struct inode * inode, struct file * file, char * buf, int c return count; /* /dev/null */ case 4: return write_port(inode,file,buf,count); + case 5: + return count; /* /dev/zero */ default: return -ENODEV; } } -static int mem_readdir(struct inode * inode, struct file * file, struct dirent * de, int count) -{ - return -ENOTDIR; -} - static struct file_operations mem_fops = { mem_lseek, mem_read, mem_write, - mem_readdir, - NULL, /* mem_close */ + NULL, /* mem_readdir */ NULL, /* mem_select */ - NULL /* mem_ioctl */ + NULL, /* mem_ioctl */ + NULL, /* no special open code */ + NULL /* no special release code */ }; -void chr_dev_init(void) +long chr_dev_init(long mem_start, long mem_end) { chrdev_fops[1] = &mem_fops; - tty_init(); - lp_init(); + mem_start = tty_init(mem_start); + mem_start = lp_init(mem_start); + mem_start = mouse_init(mem_start); + return mem_start; } diff --git a/kernel/chr_drv/mouse.c b/kernel/chr_drv/mouse.c new file mode 100644 index 000000000000..396e6c243219 --- /dev/null +++ b/kernel/chr_drv/mouse.c @@ -0,0 +1,182 @@ +/* + * Logitech Bus Mouse Driver for Linux + * by James Banks + * + * Heavily modified by David Giller + * changed from queue- to counter- driven + * hacked out a (probably incorrect) mouse_select + * + * Modified again by Nathan Laredo to interface with + * 0.96c-pl1 IRQ handling changes (13JUL92) + * didn't bother touching select code. + * + * Modified the select() code blindly to conform to the VFS + * requirements. 92.07.14 - Linus. Somebody should test it out. + * + * version 0.1 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct mouse_status mouse; + +static void mouse_interrupt(int unused) +{ + char dx, dy, buttons; + + MSE_INT_OFF(); + + outb(MSE_READ_X_LOW, MSE_CONTROL_PORT); + dx = (inb(MSE_DATA_PORT) & 0xf); + + outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT); + dx |= (inb(MSE_DATA_PORT) & 0xf) << 4; + + outb(MSE_READ_Y_LOW, MSE_CONTROL_PORT ); + dy = (inb(MSE_DATA_PORT) & 0xf); + + outb(MSE_READ_Y_HIGH, MSE_CONTROL_PORT); + buttons = inb(MSE_DATA_PORT); + + dy |= (buttons & 0xf) << 4; + buttons = ((buttons >> 5) & 0x07); + + mouse.buttons = buttons; + mouse.latch_buttons |= buttons; + mouse.dx += dx; + mouse.dy += dy; + mouse.ready = 1; + if (mouse.inode && mouse.inode->i_wait) + wake_up(&mouse.inode->i_wait); + + MSE_INT_ON(); +} + +static void release_mouse(struct inode * inode, struct file * file) +{ + MSE_INT_OFF(); + mouse.active = 0; + mouse.ready = 0; + mouse.inode = NULL; + free_irq(MOUSE_IRQ); +} + +static int open_mouse(struct inode * inode, struct file * file) +{ + if (mouse.active) + return -EBUSY; + if (!mouse.present) + return -EINVAL; + mouse.active = 1; + mouse.ready = 0; + mouse.inode = inode; + mouse.dx = 0; + mouse.dy = 0; + mouse.buttons = mouse.latch_buttons = 0x80; + MSE_INT_ON(); + if (request_irq(MOUSE_IRQ, mouse_interrupt)) { + MSE_INT_OFF(); + mouse.active = 0; + mouse.ready = 0; + mouse.inode = NULL; + return -EBUSY; + } + return 0; +} + +static int write_mouse(struct inode * inode, struct file * file, char * buffer, int count) +{ + return -EINVAL; +} + +static int read_mouse(struct inode * inode, struct file * file, char * buffer, int count) +{ + int i; + + if (count < 3) return -EINVAL; + if (!mouse.ready) return -EAGAIN; + + MSE_INT_OFF(); + + put_fs_byte(mouse.latch_buttons | 0x80, buffer); + + if (mouse.dx < -127) mouse.dx = -127; + if (mouse.dx > 127) mouse.dx = 127; + + put_fs_byte((char)mouse.dx, buffer + 1); + + if (mouse.dy < -127) mouse.dy = -127; + if (mouse.dy > 127) mouse.dy = 127; + + put_fs_byte((char) -mouse.dy, buffer + 2); + + for (i = 3; i < count; i++) + put_fs_byte(0x00, buffer + i); + + mouse.dx = 0; + mouse.dy = 0; + mouse.latch_buttons = mouse.buttons; + mouse.ready = 0; + + MSE_INT_ON(); + return i; +} + +static int mouse_select(struct inode *inode, struct file *file, int sel_type, select_table * wait) +{ + if (sel_type != SEL_IN) + return 0; + if (mouse.ready) + return 1; + select_wait(&inode->i_wait,wait); + return 0; +} + +static struct file_operations mouse_fops = { + NULL, /* mouse_seek */ + read_mouse, + write_mouse, + NULL, /* mouse_readdir */ + mouse_select, /* mouse_select */ + NULL, /* mouse_ioctl */ + open_mouse, + release_mouse, +}; + +long mouse_init(long kmem_start) +{ + int i; + + outb(MSE_CONFIG_BYTE, MSE_CONFIG_PORT); + outb(MSE_SIGNATURE_BYTE, MSE_SIGNATURE_PORT); + + for (i = 0; i < 100000; i++); /* busy loop */ + if (inb(MSE_SIGNATURE_PORT) != MSE_SIGNATURE_BYTE) { + printk("No bus mouse detected.\n"); + mouse.present = 0; + return kmem_start; + } + chrdev_fops[10] = &mouse_fops; + outb(MSE_DEFAULT_MODE, MSE_CONFIG_PORT); + + MSE_INT_OFF(); + + mouse.present = 1; + mouse.active = 0; + mouse.ready = 0; + mouse.buttons = mouse.latch_buttons = 0x80; + mouse.dx = 0; + mouse.dy = 0; + printk("Bus mouse detected and installed.\n"); + return kmem_start; +} diff --git a/kernel/chr_drv/pty.c b/kernel/chr_drv/pty.c index 0647ce821457..76a8915749eb 100644 --- a/kernel/chr_drv/pty.c +++ b/kernel/chr_drv/pty.c @@ -1,7 +1,7 @@ /* * linux/kernel/chr_drv/pty.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -12,15 +12,47 @@ * void spty_write(struct tty_struct * queue); */ +#include #include #include +#include #include #include +int pty_open(unsigned int dev, struct file * filp) +{ + struct tty_struct * tty; + + tty = tty_table + dev; + if (!tty->link) + return -ENODEV; + wake_up(&tty->read_q->proc_list); + if (filp->f_flags & O_NDELAY) + return 0; + while (!tty->link->count && !(current->signal & ~current->blocked)) + interruptible_sleep_on(&tty->link->read_q->proc_list); + if (!tty->link->count) + return -ERESTARTSYS; + return 0; +} + +void pty_close(unsigned int dev, struct file * filp) +{ + struct tty_struct * tty; + + tty = tty_table + dev; + wake_up(&tty->read_q->proc_list); + wake_up(&tty->link->write_q->proc_list); + if (IS_A_PTY_MASTER(dev)) { + if (tty->link->pgrp > 0) + kill_pg(tty->link->pgrp,SIGHUP,1); + } +} + static inline void pty_copy(struct tty_struct * from, struct tty_struct * to) { - char c; + int c; while (!from->stopped && !EMPTY(from->write_q)) { if (FULL(to->read_q)) { @@ -29,8 +61,8 @@ static inline void pty_copy(struct tty_struct * from, struct tty_struct * to) TTY_READ_FLUSH(to); continue; } - GETCH(from->write_q,c); - PUTCH(c,to->read_q); + c = get_tty_queue(from->write_q); + put_tty_queue(c,to->read_q); if (current->signal & ~current->blocked) break; } @@ -45,20 +77,12 @@ static inline void pty_copy(struct tty_struct * from, struct tty_struct * to) */ void mpty_write(struct tty_struct * tty) { - int nr = tty - tty_table; - - if ((nr >> 6) != 2) - printk("bad mpty\n\r"); - else - pty_copy(tty,tty+64); + if (tty->link) + pty_copy(tty,tty->link); } void spty_write(struct tty_struct * tty) { - int nr = tty - tty_table; - - if ((nr >> 6) != 3) - printk("bad spty\n\r"); - else - pty_copy(tty,tty-64); + if (tty->link) + pty_copy(tty,tty->link); } diff --git a/kernel/chr_drv/rs_io.s b/kernel/chr_drv/rs_io.s deleted file mode 100644 index e74dcd3f329c..000000000000 --- a/kernel/chr_drv/rs_io.s +++ /dev/null @@ -1,164 +0,0 @@ -/* - * linux/kernel/rs_io.s - * - * (C) 1991 Linus Torvalds - */ - -/* - * rs_io.s - * - * This module implements the rs232 io interrupts. - */ - -.text -.globl _rs1_interrupt,_rs2_interrupt - -size = 2048 /* must be power of two ! - and must match the value - in tty_io.c!!! */ - -/* these are the offsets into the read/write buffer structures */ -rs_addr = 0 -head = 4 -tail = 8 -proc_list = 12 -buf = 16 - -startup = 256 /* chars left in write queue when we restart it */ - -/* - * These are the actual interrupt routines. They look where - * the interrupt is coming from, and take appropriate action. - * - * rs1_interrupt (IRQ 4) takes care of com1 and com3 - * rs2_interrupt (IRQ 3) takes care of com2 and com4 - */ -.align 2 -_rs1_interrupt: - pushl $_table_list+8 - pushl $_table_list+24 - jmp rs_int - -.align 2 -_rs2_interrupt: - pushl $_table_list+16 - pushl $_table_list+32 -rs_int: cld - pushl %edx - pushl %ecx - pushl %ebx - pushl %eax - push %es - push %ds /* as this is an interrupt, we cannot */ - pushl $0x10 /* know that bs is ok. Load it */ - pop %ds - pushl $0x10 - pop %es - movl 24(%esp),%edx - call do_rs_intr - movl 28(%esp),%edx - call do_rs_intr - movb $0x20,%al - outb %al,$0x20 /* EOI */ - pop %ds - pop %es - popl %eax - popl %ebx - popl %ecx - popl %edx - addl $8,%esp # jump over the _table_list entries - iret - -do_rs_intr: - pushl %edx - movl (%edx),%edx - movl rs_addr(%edx),%edx - addl $2,%edx /* interrupt ident. reg */ -1: xorl %eax,%eax - inb %dx,%al - testb $1,%al - jne 2f - cmpb $6,%al /* this shouldn't happen, but ... */ - ja 2f - movl (%esp),%ecx - pushl %edx - subl $2,%edx - call jmp_table(,%eax,2) /* NOTE! not *4, bit0 is 0 already */ - popl %edx - jmp 1b -2: popl %edx - ret - -jmp_table: - .long modem_status,write_char,read_char,line_status - -.align 2 -modem_status: - addl $6,%edx /* clear intr by reading modem status reg */ - inb %dx,%al - ret - -.align 2 -line_status: - addl $5,%edx /* clear intr by reading line status reg. */ - inb %dx,%al - ret - -.align 2 -read_char: - inb %dx,%al - movl %ecx,%edx - subl $_table_list,%edx - shrl $3,%edx - movl (%ecx),%ecx # read-queue - movl head(%ecx),%ebx - movb %al,buf(%ecx,%ebx) - incl %ebx - andl $size-1,%ebx - cmpl tail(%ecx),%ebx - je 1f - movl %ebx,head(%ecx) -1: movl mask_table(,%edx,4),%edx - orl %edx,_timer_active - ret - -.align 2 -mask_table: - .long 0,4,8,16,32 - -.align 2 -write_char: - movl 4(%ecx),%ecx # write-queue - movl head(%ecx),%ebx - subl tail(%ecx),%ebx - andl $size-1,%ebx # nr chars in queue - je write_buffer_empty - cmpl $startup,%ebx - ja 1f - movl proc_list(%ecx),%ebx # wake up sleeping process - testl %ebx,%ebx # is there any? - je 1f - movl $0,(%ebx) -1: movl tail(%ecx),%ebx - movb buf(%ecx,%ebx),%al - outb %al,%dx - incl %ebx - andl $size-1,%ebx - movl %ebx,tail(%ecx) - cmpl head(%ecx),%ebx - je write_buffer_empty - ret - -.align 2 -write_buffer_empty: - movl proc_list(%ecx),%ebx # wake up sleeping process - testl %ebx,%ebx # is there any? - je 1f - movl $0,(%ebx) -1: incl %edx - inb %dx,%al - jmp 1f -1: jmp 1f -1: andb $0xd,%al # disable transmit interrupt - outb %al,%dx - ret diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c index f6260714eaf5..13e15b8b34eb 100644 --- a/kernel/chr_drv/serial.c +++ b/kernel/chr_drv/serial.c @@ -1,7 +1,7 @@ /* * linux/kernel/serial.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -9,22 +9,190 @@ * * This module implements the rs232 io functions * void rs_write(struct tty_struct * queue); - * void rs_init(void); + * long rs_init(long); * and all interrupts pertaining to serial IO. */ +#include +#include #include #include #include #include #include +#include -#define WAKEUP_CHARS (TTY_BUF_SIZE/4) +#define WAKEUP_CHARS (3*TTY_BUF_SIZE/4) -extern void rs1_interrupt(void); -extern void rs2_interrupt(void); +struct serial_struct serial_table[NR_SERIALS] = { + { PORT_UNKNOWN, 0, 0x3F8, 4, NULL}, + { PORT_UNKNOWN, 1, 0x2F8, 3, NULL}, + { PORT_UNKNOWN, 2, 0x3E8, 4, NULL}, + { PORT_UNKNOWN, 3, 0x2E8, 3, NULL}, +}; +void send_break(unsigned int line) +{ + unsigned short port; + struct serial_struct * info; + + if (line >= NR_SERIALS) + return; + info = serial_table + line; + if (!(port = info->port)) + return; + port += 3; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 25; + outb_p(inb_p(port) | 0x40,port); + schedule(); + outb_p(inb_p(port) & 0xbf,port); +} + +/* + * There are several races here: we avoid most of them by disabling timer_active + * for the crucial part of the process.. That's a good idea anyway. + * + * The problem is that we have to output characters /both/ from interrupts + * and from the normal write: the latter to be sure the interrupts start up + * again. With serial lines, the interrupts can happen so often that the + * races actually are noticeable. + */ +static void send_intr(struct serial_struct * info) +{ + unsigned short port = info->port; + unsigned int timer = SER1_TIMEOUT + info->line; + struct tty_queue * queue = info->tty->write_q; + int c, i = 0; + + if (info->tty->stopped) return; + + timer_active &= ~(1 << timer); + while (inb_p(info->port+5) & 0x20) { + if (queue->tail == queue->head) + goto end_send; + c = queue->buf[queue->tail]; + queue->tail++; + queue->tail &= TTY_BUF_SIZE-1; + outb(c,port); + if ((info->type != PORT_16550A) || (++i >= 14) || info->tty->stopped) + break; + } + timer_table[timer].expires = jiffies + 10; + timer_active |= 1 << timer; +end_send: + if (LEFT(queue) > WAKEUP_CHARS) + wake_up(&queue->proc_list); +} + +static void receive_intr(struct serial_struct * info) +{ + unsigned short port = info->port; + struct tty_queue * queue = info->tty->read_q; + int head = queue->head; + int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1); + + timer_active &= ~((1<line); + do { + queue->buf[head] = inb(port); + if (head != maxhead) { + head++; + head &= TTY_BUF_SIZE-1; + } + } while (inb(port+5) & 1); + queue->head = head; + timer_active |= (1<line; +} + +static void line_status_intr(struct serial_struct * info) +{ + unsigned char status = inb(info->port+5); + +/* printk("line status: %02x\n",status); */ +} + +static void modem_status_intr(struct serial_struct * info) +{ + unsigned char status = inb(info->port+6); + + if (!(info->tty->termios.c_cflag & CLOCAL)) { + if ((status & 0x88) == 0x08 && info->tty->pgrp > 0) + kill_pg(info->tty->pgrp,SIGHUP,1); + + if (info->tty->termios.c_cflag & CRTSCTS) + info->tty->stopped = !(status & 0x10); + + if (!info->tty->stopped) + send_intr(info); + } +} + +static void (*jmp_table[4])(struct serial_struct *) = { + modem_status_intr, + send_intr, + receive_intr, + line_status_intr +}; + +static void check_tty(struct serial_struct * info) +{ + unsigned char ident; + + if (!info || !info->tty || !info->port) + return; + while (1) { + ident = inb(info->port+2) & 7; + if (ident & 1) + return; + ident >>= 1; + if (ident > 3) + return; + jmp_table[ident](info); + } +} + +/* + * Again, we disable interrupts to be sure there aren't any races: + * see send_intr for details. + */ +static inline void do_rs_write(struct serial_struct * info) +{ + if (!info->tty || !info->port) + return; + if (!info->tty->write_q || EMPTY(info->tty->write_q)) + return; + cli(); + send_intr(info); + sti(); +} + +/* + * IRQ routines: one per line + */ +static void com1_IRQ(int unused) +{ + check_tty(serial_table+0); +} + +static void com2_IRQ(int unused) +{ + check_tty(serial_table+1); +} + +static void com3_IRQ(int unused) +{ + check_tty(serial_table+2); +} + +static void com4_IRQ(int unused) +{ + check_tty(serial_table+3); +} + +/* + * Receive timer routines: one per line + */ static void com1_timer(void) { TTY_READ_FLUSH(tty_table+64); @@ -45,89 +213,242 @@ static void com4_timer(void) TTY_READ_FLUSH(tty_table+67); } -static inline void do_rs_write(unsigned int port) -{ - char c; - -#define TTY (tty_table[64+port].write_q) -#define TIMER (SER1_TIMEOUT+port) - cli(); - if (!EMPTY(TTY)) { - outb_p(inb_p(TTY->data+1)|0x02,TTY->data+1); - if (inb(TTY->data+5) & 0x20) { - GETCH(TTY,c); - outb(c,TTY->data); - } - timer_table[TIMER].expires = jiffies + 50; - timer_active |= 1 << TIMER; - } else - timer_active &= ~(1 << TIMER); - sti(); -#undef TIMER -#undef TTY -} - +/* + * Send timeout routines: one per line + */ static void com1_timeout(void) { - do_rs_write(0); + do_rs_write(serial_table); } static void com2_timeout(void) { - do_rs_write(1); + do_rs_write(serial_table + 1); } static void com3_timeout(void) { - do_rs_write(2); + do_rs_write(serial_table + 2); } static void com4_timeout(void) { - do_rs_write(3); + do_rs_write(serial_table + 3); } -static void init(int port) +static void init(struct serial_struct * info) { + unsigned char status1, status2, scratch; + unsigned short port = info->port; + + if (inb(port+5) == 0xff) { + info->type = PORT_UNKNOWN; + return; + } + + scratch = inb(port+7); + outb_p(0xa5, port+7); + status1 = inb(port+7); + outb_p(0x5a, port+7); + status2 = inb(port+7); + if (status1 == 0xa5 && status2 == 0x5a) { + outb_p(scratch, port+7); + outb_p(0x01, port+2); + scratch = inb(port+2) >> 6; + switch (scratch) { + case 0: + info->type = PORT_16450; + break; + case 1: + info->type = PORT_UNKNOWN; + break; + case 2: + info->type = PORT_16550; + outb_p(0x00, port+2); + break; + case 3: + info->type = PORT_16550A; + outb_p(0xc7, port+2); + break; + } + } else + info->type = PORT_8250; outb_p(0x80,port+3); /* set DLAB of line control reg */ - outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps */ + outb_p(0x30,port); /* LS of divisor (48 -> 2400 bps) */ outb_p(0x00,port+1); /* MS of divisor */ outb_p(0x03,port+3); /* reset DLAB */ outb_p(0x00,port+4); /* reset DTR,RTS, OUT_2 */ - outb_p(0x0d,port+1); /* enable all intrs but writes */ + outb_p(0x00,port+1); /* disable all intrs */ (void)inb(port); /* read data port to reset things (?) */ } -/* - * this routine enables interrupts on 'line', and disables them on - * 'line ^ 2', as they share the same IRQ. Braindamaged AT hardware. - */ -void serial_open(unsigned int line) +void serial_close(unsigned line, struct file * filp) { - unsigned short port; - unsigned short port2; + struct serial_struct * info; + int irq; - if (line>3) + if (line >= NR_SERIALS) return; - port = tty_table[64+line].read_q->data; - if (!port) + info = serial_table + line; + if (!info->port) return; - port2 = tty_table[64+(line ^ 2)].read_q->data; - cli(); - if (port2) - outb_p(0x00,port2+4); + outb(0x00,info->port+4); /* reset DTR, RTS, */ + irq = info->irq; + if (irq == 2) + irq = 9; + free_irq(irq); +} + +static void startup(unsigned short port) +{ + int i; + outb_p(0x03,port+3); /* reset DLAB */ - outb_p(0x0f,port+4); /* set DTR,RTS, OUT_2 */ - outb_p(0x0d,port+1); /* enable all intrs but writes */ + outb_p(0x0b,port+4); /* set DTR,RTS, OUT_2 */ + outb_p(0x0f,port+1); /* enable all intrs */ + inb_p(port+2); + inb_p(port+6); + inb_p(port+2); + inb_p(port+5); + for (i = 0; i < 16 ; i++) { + inb_p(port+0); + if (!(inb_p(port+5) & 1)) + break; + } + inb_p(port+2); inb_p(port+5); - inb_p(port+0); - inb(port+6); - inb(port+2); +} + +void change_speed(unsigned int line) +{ + struct serial_struct * info; + unsigned short port,quot; + unsigned cflag,cval; + static unsigned short quotient[] = { + 0, 2304, 1536, 1047, 857, + 768, 576, 384, 192, 96, + 64, 48, 24, 12, 6, 3 + }; + + if (line >= NR_SERIALS) + return; + info = serial_table + line; + cflag = info->tty->termios.c_cflag; + if (!(port = info->port)) + return; + quot = quotient[cflag & CBAUD]; + if (!quot) + outb(0x00,port+4); + else if (!inb(port+4)) + startup(port); +/* byte size and parity */ + cval = cflag & (CSIZE | CSTOPB); + cval >>= 4; + if (cflag & PARENB) + cval |= 8; + if (!(cflag & PARODD)) + cval |= 16; + cli(); + outb_p(cval | 0x80,port+3); /* set DLAB */ + outb_p(quot & 0xff,port); /* LS of divisor */ + outb_p(quot >> 8,port+1); /* MS of divisor */ + outb(cval,port+3); /* reset DLAB */ + sti(); +} + +static void (*serial_handler[NR_SERIALS])(int) = { + com1_IRQ,com2_IRQ,com3_IRQ,com4_IRQ +}; + +/* + * this routine enables interrupts on 'line', and disables them for any + * other serial line that shared the same IRQ. Braindamaged AT hardware. + */ +int serial_open(unsigned line, struct file * filp) +{ + struct serial_struct * info; + int irq,retval; + unsigned short port; + struct sigaction sa; + + sa.sa_handler = serial_handler[line]; + sa.sa_flags = SA_INTERRUPT; + sa.sa_mask = 0; + sa.sa_restorer = NULL; + if (line >= NR_SERIALS) + return -ENODEV; + info = serial_table + line; + if (!(port = info->port)) + return -ENODEV; + irq = info->irq; + if (irq == 2) + irq = 9; + if (retval = irqaction(irq,&sa)) + return retval; + startup(port); + return 0; +} + +int get_serial_info(unsigned int line, struct serial_struct * info) +{ + if (line >= NR_SERIALS) + return -ENODEV; + if (!info) + return -EFAULT; + memcpy_tofs(info,serial_table+line,sizeof(*info)); + return 0; +} + +int set_serial_info(unsigned int line, struct serial_struct * info) +{ + struct serial_struct tmp; + unsigned new_port; + unsigned irq,new_irq; + int retval; + void (*handler)(int) = serial_handler[line]; + + if (!suser()) + return -EPERM; + if (line >= NR_SERIALS) + return -ENODEV; + if (!info) + return -EFAULT; + memcpy_fromfs(&tmp,info,sizeof(tmp)); + info = serial_table + line; + if (!(new_port = tmp.port)) + new_port = info->port; + if (!(new_irq = tmp.irq)) + new_irq = info->irq; + if (new_irq > 15 || new_port > 0xffff) + return -EINVAL; + if (new_irq == 2) + new_irq = 9; + irq = info->irq; + if (irq == 2) + irq = 9; + if (irq != new_irq) { + retval = request_irq(new_irq,handler); + if (retval) + return retval; + info->irq = new_irq; + free_irq(irq); + } + cli(); + if (new_port != info->port) { + outb(0x00,info->port+4); /* reset DTR, RTS, */ + info->port = new_port; + init(info); + startup(new_port); + } sti(); + return 0; } -void rs_init(void) +long rs_init(long kmem_start) { + int i; + struct serial_struct * info; + /* SERx_TIMER timers are used for receiving: timeout is always 0 (immediate) */ timer_table[SER1_TIMER].fn = com1_timer; timer_table[SER1_TIMER].expires = 0; @@ -146,13 +467,31 @@ void rs_init(void) timer_table[SER3_TIMEOUT].expires = 0; timer_table[SER4_TIMEOUT].fn = com4_timeout; timer_table[SER4_TIMEOUT].expires = 0; - set_intr_gate(0x24,rs1_interrupt); - set_intr_gate(0x23,rs2_interrupt); - init(tty_table[64].read_q->data); - init(tty_table[65].read_q->data); - init(tty_table[66].read_q->data); - init(tty_table[67].read_q->data); - outb(inb_p(0x21)&0xE7,0x21); + for (i = 0, info = serial_table; i < NR_SERIALS; i++,info++) { + info->tty = (tty_table+64) + i; + init(info); + if (info->type == PORT_UNKNOWN) + continue; + printk("serial port at 0x%04x (irq = %d)",info->port,info->irq); + switch (info->type) { + case PORT_8250: + printk(" is a 8250\n"); + break; + case PORT_16450: + printk(" is a 16450\n"); + break; + case PORT_16550: + printk(" is a 16550\n"); + break; + case PORT_16550A: + printk(" is a 16550A\n"); + break; + default: + printk("\n"); + break; + } + } + return kmem_start; } /* @@ -164,9 +503,7 @@ void rs_init(void) */ void rs_write(struct tty_struct * tty) { - cli(); - if (!EMPTY(tty->write_q)) - outb_p(inb_p(tty->write_q->data+1)|0x02,tty->write_q->data+1); - timer_active |= 15 << SER1_TIMEOUT; - sti(); + int line = tty - tty_table - 64; + + do_rs_write(serial_table+line); } diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c index 8e376bb82cbf..67d4153cd059 100644 --- a/kernel/chr_drv/tty_io.c +++ b/kernel/chr_drv/tty_io.c @@ -1,7 +1,7 @@ /* * linux/kernel/tty_io.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -11,88 +11,103 @@ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. */ -#include -#include -#include -#include -#include - -#define ALRMMASK (1<<(SIGALRM-1)) - +#include +#include +#include +#include #include #include +#include + +#include #include #include -int kill_pg(int pgrp, int sig, int priv); -int is_orphaned_pgrp(int pgrp); - -extern void lp_init(void); - -#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f) -#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f) -#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f) - -#define L_CANON(tty) _L_FLAG((tty),ICANON) -#define L_ISIG(tty) _L_FLAG((tty),ISIG) -#define L_ECHO(tty) _L_FLAG((tty),ECHO) -#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) -#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) -#define L_ECHONL(tty) _L_FLAG((tty),ECHONL) -#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) -#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) -#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) - -#define I_UCLC(tty) _I_FLAG((tty),IUCLC) -#define I_NLCR(tty) _I_FLAG((tty),INLCR) -#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_STRP(tty) _I_FLAG((tty),ISTRIP) - -#define O_POST(tty) _O_FLAG((tty),OPOST) -#define O_NLCR(tty) _O_FLAG((tty),ONLCR) -#define O_CRNL(tty) _O_FLAG((tty),OCRNL) -#define O_NLRET(tty) _O_FLAG((tty),ONLRET) -#define O_LCUC(tty) _O_FLAG((tty),OLCUC) - -#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD) -#define C_HUP(tty) (C_SPEED((tty)) == B0) - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#define QUEUES (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS)) -static struct tty_queue tty_queues[QUEUES]; +#include +#include "vt_kern.h" + +#define QUEUES (3*(NR_CONSOLES+NR_SERIALS+2*NR_PTYS)) +static struct tty_queue * tty_queues; struct tty_struct tty_table[256]; #define con_queues tty_queues -#define rs_queues ((3*MAX_CONSOLES) + tty_queues) -#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues) -#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues) +#define rs_queues ((3*NR_CONSOLES) + tty_queues) +#define mpty_queues ((3*(NR_CONSOLES+NR_SERIALS)) + tty_queues) +#define spty_queues ((3*(NR_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues) #define con_table tty_table #define rs_table (64+tty_table) #define mpty_table (128+tty_table) #define spty_table (192+tty_table) +/* + * fg_console is the current virtual console, + * redirect is the pseudo-tty that console output + * is redirected to if asked by TIOCCONS. + */ int fg_console = 0; +struct tty_struct * redirect = NULL; /* * these are the tables used by the machine code handlers. * you can implement virtual consoles. */ -struct tty_queue * table_list[]={ - con_queues + 0, con_queues + 1, - rs_queues + 0, rs_queues + 1, - rs_queues + 3, rs_queues + 4, - rs_queues + 6, rs_queues + 7, - rs_queues + 9, rs_queues + 10 - }; +struct tty_queue * table_list[] = { NULL, NULL }; + +void inline put_tty_queue(char c, struct tty_queue * queue) +{ + int head; + unsigned long flags; + + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + head = (queue->head + 1) & (TTY_BUF_SIZE-1); + if (head != queue->tail) { + queue->buf[queue->head] = c; + queue->head = head; + } + __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags)); +} + +int inline get_tty_queue(struct tty_queue * queue) +{ + int result = -1; + unsigned long flags; + + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + if (queue->tail != queue->head) { + result = 0xff & queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (TTY_BUF_SIZE-1); + } + __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags)); + return result; +} + +void inline tty_write_flush(struct tty_struct * tty) +{ + if (EMPTY(tty->write_q)) + return; + if (set_bit(TTY_WRITE_BUSY,&tty->flags)) + return; + tty->write(tty); + if (clear_bit(TTY_WRITE_BUSY,&tty->flags)) + printk("tty_write_flush: bit already cleared\n"); +} + +void tty_read_flush(struct tty_struct * tty) +{ + if (EMPTY(tty->read_q)) + return; + if (set_bit(TTY_READ_BUSY, &tty->flags)) + return; + copy_to_cooked(tty); + if (clear_bit(TTY_READ_BUSY, &tty->flags)) + printk("tty_read_flush: bit already cleared\n"); +} void change_console(unsigned int new_console) { + if (vt_cons[fg_console].vt_mode == KD_GRAPHICS) + return; if (new_console == fg_console || new_console >= NR_CONSOLES) return; table_list[0] = con_queues + 0 + new_console*3; @@ -108,24 +123,15 @@ static void sleep_if_empty(struct tty_queue * queue) sti(); } -static void sleep_if_full(struct tty_queue * queue) -{ - if (!FULL(queue)) - return; - cli(); - while (!(current->signal & ~current->blocked) && LEFT(queue)<128) - interruptible_sleep_on(&queue->proc_list); - sti(); -} - void wait_for_keypress(void) { sleep_if_empty(tty_table[fg_console].secondary); + flush_input(&tty_table[fg_console]); } void copy_to_cooked(struct tty_struct * tty) { - unsigned char c; + int c; if (!(tty && tty->write && tty->read_q && tty->write_q && tty->secondary)) { @@ -133,15 +139,11 @@ void copy_to_cooked(struct tty_struct * tty) return; } while (1) { - if (EMPTY(tty->read_q)) + if (FULL(tty->secondary)) break; - if (FULL(tty->secondary)) { - if (tty->secondary->proc_list) - if (tty->secondary->proc_list != current) - current->counter = 0; + c = get_tty_queue(tty->read_q); + if (c < 0) break; - } - GETCH(tty->read_q,c); if (I_STRP(tty)) c &= 0x7f; if (c==13) { @@ -154,94 +156,108 @@ void copy_to_cooked(struct tty_struct * tty) if (I_UCLC(tty)) c=tolower(c); if (L_CANON(tty)) { - if ((KILL_CHAR(tty) != _POSIX_VDISABLE) && + if ((KILL_CHAR(tty) != __DISABLED_CHAR) && (c==KILL_CHAR(tty))) { /* deal with killing the input line */ while(!(EMPTY(tty->secondary) || (c=LAST(tty->secondary))==10 || - ((EOF_CHAR(tty) != _POSIX_VDISABLE) && + ((EOF_CHAR(tty) != __DISABLED_CHAR) && (c==EOF_CHAR(tty))))) { if (L_ECHO(tty)) { - if (c<32) - PUTCH(127,tty->write_q); - PUTCH(127,tty->write_q); - TTY_WRITE_FLUSH(tty); + if (c<32) { + put_tty_queue(8,tty->write_q); + put_tty_queue(' ',tty->write_q); + put_tty_queue(8,tty->write_q); + } + put_tty_queue(8,tty->write_q); + put_tty_queue(' ',tty->write_q); + put_tty_queue(8,tty->write_q); } DEC(tty->secondary->head); } continue; } - if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) && + if ((ERASE_CHAR(tty) != __DISABLED_CHAR) && (c==ERASE_CHAR(tty))) { if (EMPTY(tty->secondary) || (c=LAST(tty->secondary))==10 || - ((EOF_CHAR(tty) != _POSIX_VDISABLE) && + ((EOF_CHAR(tty) != __DISABLED_CHAR) && (c==EOF_CHAR(tty)))) continue; if (L_ECHO(tty)) { - if (c<32) - PUTCH(127,tty->write_q); - PUTCH(127,tty->write_q); - TTY_WRITE_FLUSH(tty); + if (c<32) { + put_tty_queue(8,tty->write_q); + put_tty_queue(' ',tty->write_q); + put_tty_queue(8,tty->write_q); + } + put_tty_queue(8,tty->write_q); + put_tty_queue(32,tty->write_q); + put_tty_queue(8,tty->write_q); } DEC(tty->secondary->head); continue; } } if (I_IXON(tty)) { - if ((STOP_CHAR(tty) != _POSIX_VDISABLE) && + if ((STOP_CHAR(tty) != __DISABLED_CHAR) && (c==STOP_CHAR(tty))) { tty->stopped=1; continue; } - if ((START_CHAR(tty) != _POSIX_VDISABLE) && + if ((START_CHAR(tty) != __DISABLED_CHAR) && (c==START_CHAR(tty))) { tty->stopped=0; - TTY_WRITE_FLUSH(tty); continue; } } if (L_ISIG(tty)) { - if ((INTR_CHAR(tty) != _POSIX_VDISABLE) && + if ((INTR_CHAR(tty) != __DISABLED_CHAR) && (c==INTR_CHAR(tty))) { kill_pg(tty->pgrp, SIGINT, 1); + flush_input(tty); continue; } - if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) && + if ((QUIT_CHAR(tty) != __DISABLED_CHAR) && (c==QUIT_CHAR(tty))) { kill_pg(tty->pgrp, SIGQUIT, 1); + flush_input(tty); continue; } - if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) && + if ((SUSPEND_CHAR(tty) != __DISABLED_CHAR) && (c==SUSPEND_CHAR(tty))) { if (!is_orphaned_pgrp(tty->pgrp)) kill_pg(tty->pgrp, SIGTSTP, 1); continue; } } - if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE && + if (c==10 || (EOF_CHAR(tty) != __DISABLED_CHAR && c==EOF_CHAR(tty))) tty->secondary->data++; - if ((L_ECHO(tty) || L_ECHONL(tty)) && (c==10)) { - PUTCH(10,tty->write_q); - PUTCH(13,tty->write_q); + if ((c==10) && (L_ECHO(tty) || (L_CANON(tty) && L_ECHONL(tty)))) { + put_tty_queue(10,tty->write_q); + put_tty_queue(13,tty->write_q); } else if (L_ECHO(tty)) { if (c<32 && L_ECHOCTL(tty)) { - PUTCH('^',tty->write_q); - PUTCH(c+64,tty->write_q); + put_tty_queue('^',tty->write_q); + put_tty_queue(c+64,tty->write_q); } else - PUTCH(c,tty->write_q); + put_tty_queue(c,tty->write_q); } - PUTCH(c,tty->secondary); - TTY_WRITE_FLUSH(tty); + put_tty_queue(c,tty->secondary); } TTY_WRITE_FLUSH(tty); if (!EMPTY(tty->secondary)) wake_up(&tty->secondary->proc_list); - if (LEFT(tty->write_q) > TTY_BUF_SIZE/2) + if (tty->write_q->proc_list && LEFT(tty->write_q) > TTY_BUF_SIZE/2) wake_up(&tty->write_q->proc_list); } +int is_ignored(int sig) +{ + return ((current->blocked & (1<<(sig-1))) || + (current->sigaction[sig-1].sa_handler == SIG_IGN)); +} + /* * Called when we need to send a SIGTTIN or SIGTTOU to our process * group @@ -259,24 +275,36 @@ void copy_to_cooked(struct tty_struct * tty) */ int tty_signal(int sig, struct tty_struct *tty) { - if (is_orphaned_pgrp(current->pgrp)) - return -EIO; /* don't stop an orphaned pgrp */ (void) kill_pg(current->pgrp,sig,1); - if ((current->blocked & (1<<(sig-1))) || - ((int) current->sigaction[sig-1].sa_handler == 1)) - return -EIO; /* Our signal will be ignored */ - else if (current->sigaction[sig-1].sa_handler) - return -EINTR; /* We _will_ be interrupted :-) */ - else - return -ERESTARTSYS; /* We _will_ be interrupted :-) */ - /* (but restart after we continue) */ + return -ERESTARTSYS; +} + +static void wait_for_canon_input(struct tty_struct * tty) +{ + while (1) { + TTY_READ_FLUSH(tty); + if (tty->link) + if (tty->link->count) + TTY_WRITE_FLUSH(tty->link); + else + return; + if (current->signal & ~current->blocked) + return; + if (FULL(tty->read_q)) + return; + if (tty->secondary->data) + return; + cli(); + if (!tty->secondary->data) + interruptible_sleep_on(&tty->secondary->proc_list); + sti(); + } } static int read_chan(unsigned int channel, struct file * file, char * buf, int nr) { struct tty_struct * tty; - struct tty_struct * other_tty = NULL; - unsigned char c; + int c; char * b=buf; int minimum,time; @@ -285,71 +313,71 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n tty = TTY_TABLE(channel); if (!(tty->read_q && tty->secondary)) return -EIO; - if ((tty->pgrp > 0) && (current->tty == channel) && + if ((tty->pgrp > 0) && + (current->tty == channel) && (tty->pgrp != current->pgrp)) - return(tty_signal(SIGTTIN, tty)); - if (channel & 0x80) - other_tty = tty_table + (channel ^ 0x40); - time = 10L*tty->termios.c_cc[VTIME]; - minimum = tty->termios.c_cc[VMIN]; - if (L_CANON(tty)) { - minimum = nr; - current->timeout = 0xffffffff; - time = 0; - } else if (minimum) - current->timeout = 0xffffffff; + if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp)) + return -EIO; + else + return(tty_signal(SIGTTIN, tty)); + if (L_CANON(tty)) + minimum = time = current->timeout = 0; else { - minimum = nr; - if (time) - current->timeout = time + jiffies; - time = 0; + time = 10L*tty->termios.c_cc[VTIME]; + minimum = tty->termios.c_cc[VMIN]; + if (minimum) + current->timeout = 0xffffffff; + else { + if (time) + current->timeout = time + jiffies; + else + current->timeout = 0; + time = 0; + minimum = 1; + } } if (file->f_flags & O_NONBLOCK) time = current->timeout = 0; + else if (L_CANON(tty)) + wait_for_canon_input(tty); if (minimum>nr) minimum = nr; - TTY_READ_FLUSH(tty); while (nr>0) { - if (other_tty && other_tty->write) - TTY_WRITE_FLUSH(other_tty); - cli(); - if (EMPTY(tty->secondary) || (L_CANON(tty) && - !FULL(tty->read_q) && !tty->secondary->data)) { - if (!current->timeout) - break; - if (current->signal & ~current->blocked) - break; - if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty)) - break; - interruptible_sleep_on(&tty->secondary->proc_list); - sti(); - TTY_READ_FLUSH(tty); - continue; - } - sti(); - do { - GETCH(tty->secondary,c); - if ((EOF_CHAR(tty) != _POSIX_VDISABLE && + TTY_READ_FLUSH(tty); + if (tty->link) + TTY_WRITE_FLUSH(tty->link); + while (nr > 0 && ((c = get_tty_queue(tty->secondary)) >= 0)) { + if ((EOF_CHAR(tty) != __DISABLED_CHAR && c==EOF_CHAR(tty)) || c==10) tty->secondary->data--; - if ((EOF_CHAR(tty) != _POSIX_VDISABLE && + if ((EOF_CHAR(tty) != __DISABLED_CHAR && c==EOF_CHAR(tty)) && L_CANON(tty)) break; - else { - put_fs_byte(c,b++); - if (!--nr) - break; - } + put_fs_byte(c,b++); + nr--; + if (time) + current->timeout = time+jiffies; if (c==10 && L_CANON(tty)) break; - } while (nr>0 && !EMPTY(tty->secondary)); + }; wake_up(&tty->read_q->proc_list); - if (L_CANON(tty) || b-buf >= minimum) + if (b-buf >= minimum || !current->timeout) + break; + if (current->signal & ~current->blocked) break; - if (time) - current->timeout = time+jiffies; + if (tty->link && !tty->link->count) + break; + TTY_READ_FLUSH(tty); + if (tty->link) + TTY_WRITE_FLUSH(tty->link); + cli(); + if (EMPTY(tty->secondary)) + interruptible_sleep_on(&tty->secondary->proc_list); + sti(); } - sti(); + TTY_READ_FLUSH(tty); + if (tty->link && tty->link->write) + TTY_WRITE_FLUSH(tty->link); current->timeout = 0; if (b-buf) return b-buf; @@ -362,26 +390,42 @@ static int read_chan(unsigned int channel, struct file * file, char * buf, int n static int write_chan(unsigned int channel, struct file * file, char * buf, int nr) { - static cr_flag=0; struct tty_struct * tty; char c, *b=buf; if (channel > 255) return -EIO; tty = TTY_TABLE(channel); - if (!(tty->write_q && tty->write)) - return -EIO; - if (L_TOSTOP(tty) && (tty->pgrp > 0) && - (current->tty == channel) && (tty->pgrp != current->pgrp)) - return(tty_signal(SIGTTOU, tty)); + if (L_TOSTOP(tty) && (tty->pgrp > 0) && + (current->tty == channel) && (tty->pgrp != current->pgrp)) { + if (is_orphaned_pgrp(tty->pgrp)) + return -EIO; + if (!is_ignored(SIGTTOU)) + return tty_signal(SIGTTOU, tty); + } if (nr < 0) return -EINVAL; if (!nr) return 0; + if (redirect && tty == TTY_TABLE(0)) + tty = redirect; + if (!(tty->write_q && tty->write)) + return -EIO; while (nr>0) { - sleep_if_full(tty->write_q); if (current->signal & ~current->blocked) break; + if (tty->link && !tty->link->count) { + send_sig(SIGPIPE,current,0); + break; + } + if (FULL(tty->write_q)) { + TTY_WRITE_FLUSH(tty); + cli(); + if (FULL(tty->write_q)) + interruptible_sleep_on(&tty->write_q->proc_list); + sti(); + continue; + } while (nr>0 && !FULL(tty->write_q)) { c=get_fs_byte(b); if (O_POST(tty)) { @@ -389,24 +433,26 @@ static int write_chan(unsigned int channel, struct file * file, char * buf, int c='\n'; else if (c=='\n' && O_NLRET(tty)) c='\r'; - if (c=='\n' && !cr_flag && O_NLCR(tty)) { - cr_flag = 1; - PUTCH(13,tty->write_q); + if (c=='\n' && O_NLCR(tty) && + !set_bit(TTY_CR_PENDING,&tty->flags)) { + put_tty_queue(13,tty->write_q); continue; } if (O_LCUC(tty)) c=toupper(c); } b++; nr--; - cr_flag = 0; - PUTCH(c,tty->write_q); + clear_bit(TTY_CR_PENDING,&tty->flags); + put_tty_queue(c,tty->write_q); } - TTY_WRITE_FLUSH(tty); if (nr>0) schedule(); } + TTY_WRITE_FLUSH(tty); if (b-buf) return b-buf; + if (tty->link && !tty->link->count) + return -EPIPE; if (current->signal & ~current->blocked) return -ERESTARTSYS; return 0; @@ -414,78 +460,190 @@ static int write_chan(unsigned int channel, struct file * file, char * buf, int static int tty_read(struct inode * inode, struct file * file, char * buf, int count) { - return read_chan(current->tty,file,buf,count); + int i; + + if (MAJOR(file->f_rdev) != 4) { + printk("tty_read: pseudo-major != 4\n"); + return -EINVAL; + } + i = read_chan(MINOR(file->f_rdev),file,buf,count); + if (i > 0) + inode->i_atime = CURRENT_TIME; + return i; } -static int ttyx_read(struct inode * inode, struct file * file, char * buf, int count) +static int tty_write(struct inode * inode, struct file * file, char * buf, int count) { - return read_chan(MINOR(inode->i_rdev),file,buf,count); + int i; + + if (MAJOR(file->f_rdev) != 4) { + printk("tty_write: pseudo-major != 4\n"); + return -EINVAL; + } + i = write_chan(MINOR(file->f_rdev),file,buf,count); + if (i > 0) + inode->i_mtime = CURRENT_TIME; + return i; } -static int tty_write(struct inode * inode, struct file * file, char * buf, int count) +static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig) { - return write_chan(current->tty,file,buf,count); + return -EBADF; } -static int ttyx_write(struct inode * inode, struct file * file, char * buf, int count) +/* + * tty_open and tty_release keep up the tty count that contains the + * number of opens done on a tty. We cannot use the inode-count, as + * different inodes might point to the same tty. + * + * Open-counting is needed for pty masters, as well as for keeping + * track of serial lines: DTR is dropped when the last close happens. + */ +static int tty_open(struct inode * inode, struct file * filp) { - return write_chan(MINOR(inode->i_rdev),file,buf,count); + struct tty_struct *tty; + int dev, retval; + + dev = inode->i_rdev; + if (MAJOR(dev) == 5) + dev = current->tty; + else + dev = MINOR(dev); + if (dev < 0) + return -ENODEV; + filp->f_rdev = 0x0400 | dev; + tty = TTY_TABLE(dev); + if (!tty->count && !(tty->link && tty->link->count)) { + flush_input(tty); + flush_output(tty); + tty->stopped = 0; + } + if (IS_A_PTY_MASTER(dev)) { + if (tty->count) + return -EAGAIN; + if (tty->link) + tty->link->count++; + } + tty->count++; + retval = 0; + if (!(filp->f_flags & O_NOCTTY) && + current->leader && + current->tty<0 && + tty->session==0) { + current->tty = dev; + tty->session = current->session; + tty->pgrp = current->pgrp; + } + if (IS_A_SERIAL(dev) && tty->count < 2) + retval = serial_open(dev-64,filp); + else if (IS_A_PTY(dev)) + retval = pty_open(dev,filp); + if (retval) { + tty->count--; + if (IS_A_PTY_MASTER(dev) && tty->link) + tty->link->count--; + } + return retval; } -static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig) +/* + * Note that releasing a pty master also releases the child, so + * we have to make the redirection checks after that and on both + * sides of a pty. + */ +static void tty_release(struct inode * inode, struct file * filp) { - return -EBADF; + int dev; + struct tty_struct * tty; + + dev = filp->f_rdev; + if (MAJOR(dev) != 4) { + printk("tty_close: tty pseudo-major != 4\n"); + return; + } + dev = MINOR(filp->f_rdev); + tty = TTY_TABLE(dev); + if (IS_A_PTY_MASTER(dev) && tty->link) + tty->link->count--; + tty->count--; + if (tty->count) + return; + if (IS_A_SERIAL(dev)) { + wait_until_sent(tty); + serial_close(dev-64,filp); + } else if (IS_A_PTY(dev)) + pty_close(dev,filp); + if (!tty->count && (tty == redirect)) + redirect = NULL; + if (tty = tty->link) + if (!tty->count && (tty == redirect)) + redirect = NULL; } -static int tty_readdir(struct inode * inode, struct file * file, struct dirent * de, int count) +static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait) { - return -ENOTDIR; + int dev; + struct tty_struct * tty; + + dev = filp->f_rdev; + if (MAJOR(dev) != 4) { + printk("tty_select: tty pseudo-major != 4\n"); + return 0; + } + dev = MINOR(filp->f_rdev); + tty = TTY_TABLE(dev); + switch (sel_type) { + case SEL_IN: + if (!EMPTY(tty->secondary)) + return 1; + if (tty->link && !tty->link->count) + return 1; + select_wait(&tty->secondary->proc_list, wait); + return 0; + case SEL_OUT: + if (!FULL(tty->write_q)) + return 1; + select_wait(&tty->write_q->proc_list, wait); + return 0; + case SEL_EX: + if (tty->link && !tty->link->count) + return 1; + return 0; + } + return 0; } static struct file_operations tty_fops = { tty_lseek, tty_read, tty_write, - tty_readdir, - NULL, /* tty_close */ - NULL, /* tty_select */ - tty_ioctl /* tty_ioctl */ -}; - -static struct file_operations ttyx_fops = { - tty_lseek, - ttyx_read, - ttyx_write, - tty_readdir, - NULL, /* ttyx_close */ - NULL, /* ttyx_select */ - tty_ioctl /* ttyx_ioctl */ + NULL, /* tty_readdir */ + tty_select, + tty_ioctl, + tty_open, + tty_release }; -void tty_init(void) +long tty_init(long kmem_start) { int i; - chrdev_fops[4] = &ttyx_fops; + tty_queues = (struct tty_queue *) kmem_start; + kmem_start += QUEUES * (sizeof (struct tty_queue)); + table_list[0] = con_queues + 0; + table_list[1] = con_queues + 1; + chrdev_fops[4] = &tty_fops; chrdev_fops[5] = &tty_fops; for (i=0 ; i < QUEUES ; i++) tty_queues[i] = (struct tty_queue) {0,0,0,0,""}; - rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""}; - rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""}; - rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""}; - rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""}; - rs_queues[6] = (struct tty_queue) {0x3e8,0,0,0,""}; - rs_queues[7] = (struct tty_queue) {0x3e8,0,0,0,""}; - rs_queues[9] = (struct tty_queue) {0x2e8,0,0,0,""}; - rs_queues[10] = (struct tty_queue) {0x2e8,0,0,0,""}; for (i=0 ; i<256 ; i++) { tty_table[i] = (struct tty_struct) { {0, 0, 0, 0, 0, INIT_C_CC}, - -1, 0, 0, 0, {0,0,0,0}, - NULL, NULL, NULL, NULL + -1, 0, 0, 0, 0, {0,0,0,0}, + NULL, NULL, NULL, NULL, NULL }; } - con_init(); + kmem_start = con_init(kmem_start); for (i = 0 ; i -#include -#include - +#include +#include +#include #include #include #include +#include #include #include @@ -19,30 +19,8 @@ extern int session_of_pgrp(int pgrp); extern int do_screendump(int arg); extern int kill_pg(int pgrp, int sig, int priv); -extern int tty_signal(int sig, struct tty_struct *tty); extern int vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg); -static unsigned short quotient[] = { - 0, 2304, 1536, 1047, 857, - 768, 576, 384, 192, 96, - 64, 48, 24, 12, 6, 3 -}; - -static void change_speed(struct tty_struct * tty) -{ - unsigned short port,quot; - - if (!(port = tty->read_q->data)) - return; - quot = quotient[tty->termios.c_cflag & CBAUD]; - cli(); - outb_p(0x80,port+3); /* set DLAB */ - outb_p(quot & 0xff,port); /* LS of divisor */ - outb_p(quot >> 8,port+1); /* MS of divisor */ - outb(0x03,port+3); /* reset DLAB */ - sti(); -} - static void flush(struct tty_queue * queue) { if (queue) { @@ -53,19 +31,53 @@ static void flush(struct tty_queue * queue) } } -static void wait_until_sent(struct tty_struct * tty) +void flush_input(struct tty_struct * tty) { - cli(); - while (!(current->signal & ~current->blocked) && !EMPTY(tty->write_q)) { - current->counter = 0; - interruptible_sleep_on(&tty->write_q->proc_list); + if (tty->read_q) { + flush(tty->read_q); + wake_up(&tty->read_q->proc_list); + } + if (tty->secondary) { + flush(tty->secondary); + tty->secondary->data = 0; + } + if ((tty = tty->link) && tty->write_q) { + flush(tty->write_q); + wake_up(&tty->write_q->proc_list); } - sti(); } -static void send_break(struct tty_struct * tty) +void flush_output(struct tty_struct * tty) { - /* do nothing - not implemented */ + if (tty->write_q) { + flush(tty->write_q); + wake_up(&tty->write_q->proc_list); + } + if (tty = tty->link) { + if (tty->read_q) { + flush(tty->read_q); + wake_up(&tty->read_q->proc_list); + } + if (tty->secondary) { + flush(tty->secondary); + tty->secondary->data = 0; + } + } +} + +void wait_until_sent(struct tty_struct * tty) +{ + while (!(current->signal & ~current->blocked) && !EMPTY(tty->write_q)) { + TTY_WRITE_FLUSH(tty); + current->counter = 0; + cli(); + if (EMPTY(tty->write_q)) + break; + else + interruptible_sleep_on(&tty->write_q->proc_list); + sti(); + } + sti(); } static int do_get_ps_info(int arg) @@ -109,19 +121,23 @@ static int get_termios(struct tty_struct * tty, struct termios * termios) static int set_termios(struct tty_struct * tty, struct termios * termios, int channel) { - int i, retsig; + int i; + unsigned short old_cflag = tty->termios.c_cflag; /* If we try to set the state of terminal and we're not in the foreground, send a SIGTTOU. If the signal is blocked or ignored, go ahead and perform the operation. POSIX 7.2) */ - if ((current->tty == channel) && (tty->pgrp != current->pgrp)) { - retsig = tty_signal(SIGTTOU, tty); - if (retsig == -ERESTARTSYS || retsig == -EINTR) - return retsig; + if ((current->tty == channel) && + (tty->pgrp != current->pgrp)) { + if (is_orphaned_pgrp(current->pgrp)) + return -EIO; + if (!is_ignored(SIGTTOU)) + return tty_signal(SIGTTOU, tty); } for (i=0 ; i< (sizeof (*termios)) ; i++) ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios); - change_speed(tty); + if (IS_A_SERIAL(channel) && tty->termios.c_cflag != old_cflag) + change_speed(channel-64); return 0; } @@ -149,13 +165,17 @@ static int get_termio(struct tty_struct * tty, struct termio * termio) static int set_termio(struct tty_struct * tty, struct termio * termio, int channel) { - int i, retsig; + int i; struct termio tmp_termio; + unsigned short old_cflag = tty->termios.c_cflag; - if ((current->tty == channel) && (tty->pgrp != current->pgrp)) { - retsig = tty_signal(SIGTTOU, tty); - if (retsig == -ERESTARTSYS || retsig == -EINTR) - return retsig; + if ((current->tty == channel) && + (tty->pgrp > 0) && + (tty->pgrp != current->pgrp)) { + if (is_orphaned_pgrp(current->pgrp)) + return -EIO; + if (!is_ignored(SIGTTOU)) + return tty_signal(SIGTTOU, tty); } for (i=0 ; i< (sizeof (*termio)) ; i++) ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio); @@ -166,7 +186,8 @@ static int set_termio(struct tty_struct * tty, struct termio * termio, tty->termios.c_line = tmp_termio.c_line; for(i=0 ; i < NCC ; i++) tty->termios.c_cc[i] = tmp_termio.c_cc[i]; - change_speed(tty); + if (IS_A_SERIAL(channel) && tty->termios.c_cflag != old_cflag) + change_speed(channel-64); return 0; } @@ -213,12 +234,11 @@ int tty_ioctl(struct inode * inode, struct file * file, int pgrp; int dev; - if (MAJOR(inode->i_rdev) == 5) { - dev = current->tty; - if (dev<0) - return -EINVAL; - } else - dev = MINOR(inode->i_rdev); + if (MAJOR(file->f_rdev) != 4) { + printk("tty_ioctl: tty pseudo-major != 4\n"); + return -EINVAL; + } + dev = MINOR(file->f_rdev); tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console); if (IS_A_PTY(dev)) @@ -232,10 +252,7 @@ int tty_ioctl(struct inode * inode, struct file * file, case TCGETS: return get_termios(tty,(struct termios *) arg); case TCSETSF: - flush(tty->read_q); - flush(tty->secondary); - if (other_tty) - flush(other_tty->write_q); + flush_input(tty); /* fallthrough */ case TCSETSW: wait_until_sent(tty); @@ -245,20 +262,18 @@ int tty_ioctl(struct inode * inode, struct file * file, case TCGETA: return get_termio(tty,(struct termio *) arg); case TCSETAF: - flush(tty->read_q); - flush(tty->secondary); - if (other_tty) - flush(other_tty->write_q); + flush_input(tty); /* fallthrough */ case TCSETAW: wait_until_sent(tty); /* fallthrough */ case TCSETA: return set_termio(tty,(struct termio *) arg, dev); case TCSBRK: - if (!arg) { - wait_until_sent(tty); - send_break(tty); - } + if (!IS_A_SERIAL(dev)) + return -EINVAL; + wait_until_sent(tty); + if (!arg) + send_break(dev-64); return 0; case TCXONC: switch (arg) { @@ -272,28 +287,22 @@ int tty_ioctl(struct inode * inode, struct file * file, return 0; case TCIOFF: if (STOP_CHAR(tty)) - PUTCH(STOP_CHAR(tty),tty->write_q); + put_tty_queue(STOP_CHAR(tty),tty->write_q); return 0; case TCION: if (START_CHAR(tty)) - PUTCH(START_CHAR(tty),tty->write_q); + put_tty_queue(START_CHAR(tty),tty->write_q); return 0; } return -EINVAL; /* not implemented */ case TCFLSH: - if (arg==0) { - flush(tty->read_q); - flush(tty->secondary); - if (other_tty) - flush(other_tty->write_q); - } else if (arg==1) - flush(tty->write_q); + if (arg==0) + flush_input(tty); + else if (arg==1) + flush_output(tty); else if (arg==2) { - flush(tty->read_q); - flush(tty->secondary); - flush(tty->write_q); - if (other_tty) - flush(other_tty->write_q); + flush_input(tty); + flush_output(tty); } else return -EINVAL; return 0; @@ -325,15 +334,18 @@ int tty_ioctl(struct inode * inode, struct file * file, return 0; case TIOCINQ: verify_area((void *) arg,4); - put_fs_long(CHARS(tty->secondary), - (unsigned long *) arg); + if (L_CANON(tty) && !tty->secondary->data) + put_fs_long(0, (unsigned long *) arg); + else + put_fs_long(CHARS(tty->secondary), + (unsigned long *) arg); return 0; case TIOCSTI: return -EINVAL; /* not implemented */ case TIOCGWINSZ: return get_window_size(tty,(struct winsize *) arg); case TIOCSWINSZ: - if (other_tty) + if (IS_A_PTY_MASTER(dev)) set_window_size(other_tty,(struct winsize *) arg); return set_window_size(tty,(struct winsize *) arg); case TIOCMGET: @@ -358,6 +370,44 @@ int tty_ioctl(struct inode * inode, struct file * file, default: return -EINVAL; } + case TIOCCONS: + if (!IS_A_PTY(dev)) + return -EINVAL; + if (redirect) + return -EBUSY; + if (!suser()) + return -EPERM; + if (IS_A_PTY_MASTER(dev)) + redirect = other_tty; + else + redirect = tty; + return 0; + case TIOCGSERIAL: + if (!IS_A_SERIAL(dev)) + return -EINVAL; + verify_area((void *) arg,sizeof(struct serial_struct)); + return get_serial_info(dev-64,(struct serial_struct *) arg); + case TIOCSSERIAL: + if (!IS_A_SERIAL(dev)) + return -EINVAL; + return set_serial_info(dev-64,(struct serial_struct *) arg); + case FIONBIO: + if (arg) + file->f_flags |= O_NONBLOCK; + else + file->f_flags &= ~O_NONBLOCK; + return 0; + case TIOCNOTTY: + if (MINOR(file->f_rdev) != current->tty) + return -EINVAL; + current->tty = -1; + if (current->leader) { + if (tty->pgrp > 0) + kill_pg(tty->pgrp, SIGHUP, 0); + tty->pgrp = -1; + tty->session = 0; + } + return 0; default: return vt_ioctl(tty, dev, cmd, arg); } diff --git a/kernel/chr_drv/vt.c b/kernel/chr_drv/vt.c index 549cb93fa731..580e6b5c6293 100644 --- a/kernel/chr_drv/vt.c +++ b/kernel/chr_drv/vt.c @@ -1,31 +1,29 @@ /* * kernel/chr_drv/vt.c * - * (C) 1992 obz under the linux copyright + * Copyright (C) 1992 obz under the linux copyright */ -#include - -#include -#include -#include - -#include -#include - +#include +#include #include #include +#include #include +#include +#include + #include "vt_kern.h" +#include +#include /* * console (vt and kd) routines, as defined by usl svr4 manual */ -struct vt_info vt_info[MAX_CONSOLES]; +struct vt_cons vt_cons[NR_CONSOLES]; -extern int NR_CONSOLES; extern unsigned char kleds; extern unsigned char kraw; extern unsigned char ke0; @@ -64,14 +62,27 @@ kiocsound(unsigned int freq) return 0; } +/* + * all the vt ioctls affect only consoles, so we reject all other ttys. + * we also have the capability to modify any console, not just the fg_console. + */ int vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg) { + int console = dev ? dev - 1 : fg_console; + unsigned char ucval; + + if (!IS_A_CONSOLE(dev) || console < 0 || console >= NR_CONSOLES) + return -EINVAL; + switch (cmd) { case KIOCSOUND: return kiocsound((unsigned int)arg); case KDGKBTYPE: + /* + * this is naive. + */ verify_area((void *) arg, sizeof(unsigned char)); put_fs_byte(KB_101, (unsigned char *) arg); return 0; @@ -88,7 +99,8 @@ vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg) case KDENABIO: case KDDISABIO: - return sys_ioperm(GPFIRST, GPNUM, (cmd == KDENABIO)) ? -ENXIO : 0; + return sys_ioperm(GPFIRST, GPNUM, + (cmd == KDENABIO)) ? -ENXIO : 0; case KDSETMODE: /* @@ -107,11 +119,21 @@ vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg) default: return -EINVAL; } - vt_info[fg_console].mode = arg; + if (vt_cons[console].vt_mode == (unsigned char) arg) + return 0; + vt_cons[console].vt_mode = (unsigned char) arg; + if (console != fg_console) + return 0; + if (arg == KD_TEXT) + unblank_screen(); + else { + timer_active &= 1< -#include -#include - +#include +#include +#include #include #include +#include #include + #include int sys_close(int fd); -inline int send_sig(long sig,struct task_struct * p,int priv) +int send_sig(long sig,struct task_struct * p,int priv) { if (!p || (sig < 0) || (sig > 32)) return -EINVAL; - if (!priv && (current->euid!=p->euid) && !suser()) + if (!priv && ((sig != SIGCONT) || (current->session != p->session)) && + (current->euid != p->euid) && (current->uid != p->uid) && !suser()) return -EPERM; if (!sig) return 0; @@ -41,7 +43,7 @@ inline int send_sig(long sig,struct task_struct * p,int priv) /* save the signal number for wait. */ p->exit_code = sig; - /* we have to make sure the parent is awake. */ + /* we have to make sure the parent process is awake. */ if (p->p_pptr != NULL && p->p_pptr->state == TASK_INTERRUPTIBLE) p->p_pptr->state = TASK_RUNNING; @@ -65,13 +67,7 @@ void release(struct task_struct * p) for (i=1 ; ip_osptr) - p->p_osptr->p_ysptr = p->p_ysptr; - if (p->p_ysptr) - p->p_ysptr->p_osptr = p->p_osptr; - else - p->p_pptr->p_cptr = p->p_osptr; + REMOVE_LINKS(p); free_page((long) p); return; } @@ -167,14 +163,26 @@ void audit_ptree() } #endif /* DEBUG_PROC_TREE */ +/* + * This checks not only the pgrp, but falls back on the pid if no + * satisfactory prgp is found. I dunno - gdb doesn't work correctly + * without this... + */ int session_of_pgrp(int pgrp) { struct task_struct **p; + int fallback; - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) + fallback = -1; + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if (!*p || (*p)->session <= 0) + continue; if ((*p)->pgrp == pgrp) - return((*p)->session); - return -1; + return (*p)->session; + if ((*p)->pid == pgrp) + fallback = (*p)->session; + } + return fallback; } int kill_pg(int pgrp, int sig, int priv) @@ -186,7 +194,7 @@ int kill_pg(int pgrp, int sig, int priv) if (sig<0 || sig>32 || pgrp<=0) return -EINVAL; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if ((*p)->pgrp == pgrp) { + if (*p && (*p)->pgrp == pgrp) { if (sig && (err = send_sig(sig,*p,priv))) retval = err; else @@ -202,7 +210,7 @@ int kill_proc(int pid, int sig, int priv) if (sig<0 || sig>32) return -EINVAL; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if ((*p)->pid == pid) + if (*p && (*p)->pid == pid) return(sig ? send_sig(sig,*p,priv) : 0); return(-ESRCH); } @@ -220,7 +228,7 @@ int sys_kill(int pid,int sig) return(kill_pg(current->pgrp,sig,0)); if (pid == -1) { while (--p > &FIRST_TASK) - if ((*p)->pid > 1 && *p != current) { + if (*p && (*p)->pid > 1 && *p != current) { ++count; if ((err = send_sig(sig,*p,0)) != -EPERM) retval = err; @@ -263,7 +271,7 @@ static int has_stopped_jobs(int pgrp) struct task_struct ** p; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if ((*p)->pgrp != pgrp) + if (!*p || (*p)->pgrp != pgrp) continue; if ((*p)->state == TASK_STOPPED) return(1); @@ -271,23 +279,37 @@ static int has_stopped_jobs(int pgrp) return(0); } +static void forget_original_parent(struct task_struct * father) +{ + struct task_struct ** p; + + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) + if (*p && (*p)->p_opptr == father) + if (task[1]) + (*p)->p_opptr = task[1]; + else + (*p)->p_opptr = task[0]; +} + volatile void do_exit(long code) { struct task_struct *p; int i; +fake_volatile: free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); for (i=0 ; ifilp[i]) sys_close(i); + forget_original_parent(current); iput(current->pwd); current->pwd = NULL; iput(current->root); current->root = NULL; iput(current->executable); current->executable = NULL; - for (i=0; inumlibraries; i++) { + for (i=0; i < current->numlibraries; i++) { iput(current->libraries[i].library); current->libraries[i].library = NULL; } @@ -319,18 +341,21 @@ volatile void do_exit(long code) * A. Make init inherit all the child processes * B. Check to see if any process groups have become orphaned * as a result of our exiting, and if they have any stopped - * jons, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2) + * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) */ while (p = current->p_cptr) { current->p_cptr = p->p_osptr; p->p_ysptr = NULL; - p->flags &= ~PF_PTRACED; - p->p_pptr = task[1]; - p->p_osptr = task[1]->p_cptr; - task[1]->p_cptr->p_ysptr = p; - task[1]->p_cptr = p; + p->flags &= ~PF_PTRACED; + if (task[1]) + p->p_pptr = task[1]; + else + p->p_pptr = task[0]; + p->p_osptr = p->p_pptr->p_cptr; + p->p_osptr->p_ysptr = p; + p->p_pptr->p_cptr = p; if (p->state == TASK_ZOMBIE) - task[1]->signal |= (1<<(SIGCHLD-1)); + send_sig(SIGCHLD,p->p_pptr,1); /* * process group orphan check * Case ii: Our child is in a different pgrp @@ -357,7 +382,7 @@ volatile void do_exit(long code) tty->session = 0; } for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) - if ((*p)->session == current->session) + if (*p && (*p)->session == current->session) (*p)->tty = -1; } if (last_task_used_math == current) @@ -366,6 +391,20 @@ volatile void do_exit(long code) audit_ptree(); #endif schedule(); +/* + * In order to get rid of the "volatile function does return" message + * I did this little loop that confuses gcc to think do_exit really + * is volatile. In fact it's schedule() that is volatile in some + * circumstances: when current->state = ZOMBIE, schedule() never + * returns. + * + * In fact the natural way to do all this is to have the label and the + * goto right after each other, but I put the fake_volatile label at + * the start of the function just in case something /really/ bad + * happens, and the schedule returns. This way we can try again. I'm + * not paranoid: it's just that everybody is out to get me. + */ + goto fake_volatile; } int sys_exit(int error_code) @@ -382,8 +421,9 @@ int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options) if (stat_addr) verify_area(stat_addr,4); repeat: + current->signal &= ~(1<<(SIGCHLD-1)); flag=0; - for (p = current->p_cptr ; p ; p = p->p_osptr) { + for (p = current->p_cptr ; p ; p = p->p_osptr) { if (pid>0) { if (p->pid != pid) continue; @@ -396,8 +436,9 @@ repeat: } switch (p->state) { case TASK_STOPPED: - if (!(options & WUNTRACED) || - !p->exit_code) + if (!p->exit_code) + continue; + if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED)) continue; if (stat_addr) put_fs_long((p->exit_code << 8) | 0x7f, @@ -412,7 +453,13 @@ repeat: flag = p->pid; if (stat_addr) put_fs_long(p->exit_code, stat_addr); - release(p); + if (p->p_opptr != p->p_pptr) { + REMOVE_LINKS(p); + p->p_pptr = p->p_opptr; + SET_LINKS(p); + send_sig(SIGCHLD,p->p_pptr,1); + } else + release(p); #ifdef DEBUG_PROC_TREE audit_ptree(); #endif @@ -437,5 +484,3 @@ repeat: } return -ECHILD; } - - diff --git a/kernel/fork.c b/kernel/fork.c index 89277ab86bc3..e80abe8f7050 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1,7 +1,7 @@ /* * linux/kernel/fork.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -10,15 +10,17 @@ * Fork is rather simple, once you get the hang of it, but the memory * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' */ -#include -#include +#include #include #include +#include +#include + #include #include -extern void write_verify(unsigned long address); +#define MAX_TASKS_PER_USER ((NR_TASKS/4)*3) long last_pid=0; @@ -42,8 +44,8 @@ int copy_mem(int nr,struct task_struct * p) unsigned long old_data_base,new_data_base,data_limit; unsigned long old_code_base,new_code_base,code_limit; - code_limit=get_limit(0x0f); - data_limit=get_limit(0x17); + code_limit = get_limit(0x0f); + data_limit = get_limit(0x17); old_code_base = get_base(current->ldt[1]); old_data_base = get_base(current->ldt[2]); if (old_data_base != old_code_base) { @@ -67,17 +69,32 @@ int copy_mem(int nr,struct task_struct * p) static int find_empty_process(void) { - int i; + int i, task_nr; + int this_user_tasks = 0; - repeat: - if ((++last_pid)<0) last_pid=1; - for(i=0 ; ipid == last_pid) || - (task[i]->pgrp == last_pid))) - goto repeat; +repeat: + if ((++last_pid) & 0xffff0000) + last_pid=1; + for(i=0 ; i < NR_TASKS ; i++) { + if (!task[i]) + continue; + if (task[i]->uid == current->uid) + this_user_tasks++; + if (task[i]->pid == last_pid || task[i]->pgrp == last_pid) + goto repeat; + } + if (this_user_tasks > MAX_TASKS_PER_USER && !suser()) + return -EAGAIN; +/* Only the super-user can fill the last available slot */ + task_nr = 0; for(i=1 ; iwait.task = p; + p->wait.next = NULL; p->state = TASK_UNINTERRUPTIBLE; + p->flags &= ~PF_PTRACED; p->pid = last_pid; - p->p_pptr = current; + if (p->pid > 1) + p->swappable = 1; + p->p_pptr = p->p_opptr = current; p->p_cptr = NULL; - p->p_ysptr = NULL; - if (p->p_osptr = current->p_cptr) - p->p_osptr->p_ysptr = p; - current->p_cptr = p; + SET_LINKS(p); p->counter = p->priority; p->signal = 0; - p->alarm = 0; + p->it_real_value = p->it_virt_value = p->it_prof_value = 0; + p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0; p->leader = 0; /* process leadership doesn't inherit */ p->utime = p->stime = 0; p->cutime = p->cstime = 0; @@ -126,7 +146,7 @@ int sys_fork(long ebx,long ecx,long edx, p->tss.esp0 = PAGE_SIZE + (long) p; p->tss.ss0 = 0x10; p->tss.eip = eip; - p->tss.eflags = eflags; + p->tss.eflags = eflags & 0xffffcfff; /* iopl is always 0 for a new process */ p->tss.eax = 0; p->tss.ecx = ecx; p->tss.edx = edx; @@ -149,12 +169,7 @@ int sys_fork(long ebx,long ecx,long edx, __asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387)); if (copy_mem(nr,p)) { task[nr] = NULL; - if (p->p_pptr->p_cptr == p) - p->p_pptr->p_cptr = p->p_osptr; - if (p->p_osptr) - p->p_osptr->p_ysptr = p->p_ysptr; - if (p->p_ysptr) - p->p_ysptr->p_osptr = p->p_osptr; + REMOVE_LINKS(p); free_page((long) p); return -EAGAIN; } diff --git a/kernel/ioport.c b/kernel/ioport.c index cdca95af5021..0fde69667f5a 100644 --- a/kernel/ioport.c +++ b/kernel/ioport.c @@ -7,9 +7,8 @@ #include #include - -#include -#include +#include +#include #define _IODEBUG @@ -92,3 +91,30 @@ int sys_ioperm(unsigned long from, unsigned long num, int turn_on) } return 0; } + +unsigned int *stack; + +/* + * sys_iopl has to be used when you want to access the IO ports + * beyond the 0x3ff range: to get the full 65536 ports bitmapped + * you'd need 8kB of bitmaps/process, which is a bit excessive. + * + * Here we just change the eflags value on the stack: we allow + * only the super-user to do it. This depends on the stack-layout + * on system-call entry - see also fork() and the signal handling + * code. + */ +int sys_iopl(long ebx,long ecx,long edx, + long esi, long edi, long ebp, long eax, long ds, + long es, long fs, long gs, long orig_eax, + long eip,long cs,long eflags,long esp,long ss) +{ + unsigned int level = ebx; + + if (level > 3) + return -EINVAL; + if (!suser()) + return -EPERM; + *(&eflags) = (eflags & 0xffffcfff) | (level << 12); + return 0; +} diff --git a/kernel/irq.c b/kernel/irq.c new file mode 100644 index 000000000000..438fb680e7c2 --- /dev/null +++ b/kernel/irq.c @@ -0,0 +1,236 @@ +/* + * linux/kernel/irq.c + * + * Copyright (C) 1992 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * The same sigaction struct is used, and with similar semantics (ie there + * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there + * are similarities. + * + * sa_handler(int irq_NR) is the default function called. + * sa_mask is 0 if nothing uses this IRQ + * sa_flags contains various info: SA_INTERRUPT etc + * sa_restorer is the unused + */ + +#include +#include +#include +#include + +#include +#include +#include + +void irq13(void); + +/* + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that do all + * the operations that are needed to keep the AT interrupt-controller + * happy. They are also written to be fast - and to disable interrupts + * as little as humanly possible. + * + * NOTE! These macros expand to three different handlers for each line: one + * complete handler that does all the fancy stuff (including signal handling), + * and one fast handler that is meant for simple IRQ's that want to be + * atomic. The specific handler is chosen depending on the SA_INTERRUPT + * flag when installing a handler. Finally, one "bad interrupt" handler, that + * is used when no handler is present. + */ +BUILD_IRQ(FIRST,0,0x01) +BUILD_IRQ(FIRST,1,0x02) +BUILD_IRQ(FIRST,2,0x04) +BUILD_IRQ(FIRST,3,0x08) +BUILD_IRQ(FIRST,4,0x10) +BUILD_IRQ(FIRST,5,0x20) +BUILD_IRQ(FIRST,6,0x40) +BUILD_IRQ(FIRST,7,0x80) +BUILD_IRQ(SECOND,8,0x01) +BUILD_IRQ(SECOND,9,0x02) +BUILD_IRQ(SECOND,10,0x04) +BUILD_IRQ(SECOND,11,0x08) +BUILD_IRQ(SECOND,12,0x10) +BUILD_IRQ(SECOND,13,0x20) +BUILD_IRQ(SECOND,14,0x40) +BUILD_IRQ(SECOND,15,0x80) + +/* + * Pointers to the low-level handlers: first the general ones, then the + * fast ones, then the bad ones. + */ +static void (*interrupt[16])(void) = { + IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt, + IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, + IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, + IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt +}; + +static void (*fast_interrupt[16])(void) = { + fast_IRQ0_interrupt, fast_IRQ1_interrupt, + fast_IRQ2_interrupt, fast_IRQ3_interrupt, + fast_IRQ4_interrupt, fast_IRQ5_interrupt, + fast_IRQ6_interrupt, fast_IRQ7_interrupt, + fast_IRQ8_interrupt, fast_IRQ9_interrupt, + fast_IRQ10_interrupt, fast_IRQ11_interrupt, + fast_IRQ12_interrupt, fast_IRQ13_interrupt, + fast_IRQ14_interrupt, fast_IRQ15_interrupt +}; + +static void (*bad_interrupt[16])(void) = { + bad_IRQ0_interrupt, bad_IRQ1_interrupt, + bad_IRQ2_interrupt, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + bad_IRQ14_interrupt, bad_IRQ15_interrupt +}; + +/* + * Initial irq handlers. + */ +static struct sigaction irq_sigaction[16] = { + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } +}; + +/* + * do_IRQ handles IRQ's that have been installed without the + * SA_INTERRUPT flag: it uses the full signal-handling return + * and runs with other interrupts disabled. All relatively slow + * IRQ's should use this format: notably the keyboard/timer + * routines. + */ +int do_IRQ(int irq, struct pt_regs * regs) +{ + struct sigaction * sa = irq + irq_sigaction; + + sa->sa_handler((int) regs); + return 0; /* re-enable the irq when returning */ +} + +/* + * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return + * stuff - the handler is also running with interrupts disabled unless + * it explicitly enables them later. + */ +int do_fast_IRQ(int irq) +{ + struct sigaction * sa = irq + irq_sigaction; + + sa->sa_handler(0); + return 0; /* re-enable the irq when returning */ +} + +int irqaction(unsigned int irq, struct sigaction * new) +{ + struct sigaction * sa; + unsigned long flags; + + if (irq > 15) + return -EINVAL; + sa = irq + irq_sigaction; + if (sa->sa_mask) + return -EBUSY; + if (!new->sa_handler) + return -EINVAL; + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + *sa = *new; + sa->sa_mask = 1; + if (sa->sa_flags & SA_INTERRUPT) + set_intr_gate(0x20+irq,fast_interrupt[irq]); + else + set_intr_gate(0x20+irq,interrupt[irq]); + if (irq < 8) + outb(inb_p(0x21) & ~(1< 15) { + printk("Trying to free IRQ%d\n",irq); + return; + } + if (!sa->sa_mask) { + printk("Trying to free free IRQ%d\n",irq); + return; + } + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + if (irq < 8) + outb(inb_p(0x21) | (1<sa_handler = NULL; + sa->sa_flags = 0; + sa->sa_mask = 0; + sa->sa_restorer = NULL; + __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags)); +} + +extern void do_coprocessor_error(long,long); + +static void math_error_irq(int cpl) +{ + outb(0,0xF0); + do_coprocessor_error(0,0); +} + +static void no_action(int cpl) { } + +static struct sigaction ignore_IRQ = { + no_action, + 0, + SA_INTERRUPT, + NULL +}; + +void init_IRQ(void) +{ + int i; + + for (i = 0; i < 16 ; i++) + set_intr_gate(0x20+i,bad_interrupt[i]); + if (irqaction(2,&ignore_IRQ)) + printk("Unable to get IRQ2 for cascade\n"); + if (request_irq(13,math_error_irq)) + printk("Unable to get IRQ13 for math-error handler\n"); +} diff --git a/kernel/itimer.c b/kernel/itimer.c new file mode 100644 index 000000000000..b69e1daa6396 --- /dev/null +++ b/kernel/itimer.c @@ -0,0 +1,114 @@ +/* + * linux/kernel/itimer.c + * + * Copyright (C) 1992 Darren Senn + */ + +/* These are all the functions necessary to implement itimers */ + +#include +#include +#include +#include +#include + +#include + +static unsigned long tvtojiffies(struct timeval *value) +{ + return((unsigned long )value->tv_sec * HZ + + (unsigned long )(value->tv_usec + (1000000 / HZ - 1)) / + (1000000 / HZ)); +} + +static void jiffiestotv(unsigned long jiffies, struct timeval *value) +{ + value->tv_usec = (jiffies % HZ) * (1000000 / HZ); + value->tv_sec = jiffies / HZ; + return; +} + +int _getitimer(int which, struct itimerval *value) +{ + register unsigned long val, interval; + + switch (which) { + case ITIMER_REAL: + val = current->it_real_value; + interval = current->it_real_incr; + break; + case ITIMER_VIRTUAL: + val = current->it_virt_value; + interval = current->it_virt_incr; + break; + case ITIMER_PROF: + val = current->it_prof_value; + interval = current->it_prof_incr; + break; + default: + return(-EINVAL); + } + jiffiestotv(val, &value->it_value); + jiffiestotv(interval, &value->it_interval); + return(0); +} + +int sys_getitimer(int which, struct itimerval *value) +{ + struct itimerval get_buffer; + int k; + + if (!value) + return -EFAULT; + k = _getitimer(which, &get_buffer); + if (k < 0) + return k; + verify_area(value, sizeof(struct itimerval)); + memcpy_tofs(value, &get_buffer, sizeof(get_buffer)); + return 0; +} + +int _setitimer(int which, struct itimerval *value, struct itimerval *ovalue) +{ + register unsigned long i, j; + int k; + + i = tvtojiffies(&value->it_interval); + j = tvtojiffies(&value->it_value); + if (ovalue && (k = _getitimer(which, ovalue)) < 0) + return k; + switch (which) { + case ITIMER_REAL: + current->it_real_value = j; + current->it_real_incr = i; + break; + case ITIMER_VIRTUAL: + current->it_virt_value = j; + current->it_virt_incr = i; + break; + case ITIMER_PROF: + current->it_prof_value = j; + current->it_prof_incr = i; + break; + default: + return -EINVAL; + } + return 0; +} + +int sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) +{ + struct itimerval set_buffer, get_buffer; + int k; + + if (!value) + memset((char *) &set_buffer, 0, sizeof(set_buffer)); + else + memcpy_fromfs(&set_buffer, value, sizeof(set_buffer)); + k = _setitimer(which, &set_buffer, ovalue ? &get_buffer : 0); + if (k < 0 || !ovalue) + return k; + verify_area(ovalue, sizeof(struct itimerval)); + memcpy_tofs(ovalue, &get_buffer, sizeof(get_buffer)); + return 0; +} diff --git a/kernel/math/Makefile b/kernel/math/Makefile index 79e3276a063b..5b2a163f4583 100644 --- a/kernel/math/Makefile +++ b/kernel/math/Makefile @@ -6,24 +6,15 @@ # unless it's something special (ie not a .c file). # -AR =ar -AS =as -LD =ld -LDFLAGS =-s -x -CC =gcc -nostdinc -I../../include -CPP =cpp -nostdinc -I../../include - .c.s: - $(CC) $(CFLAGS) $(MATH_EMULATION) \ - -S -o $*.s $< + $(CC) $(CFLAGS) $(MATH_EMULATION) -S $< .s.o: $(AS) -c -o $*.o $< .c.o: - $(CC) $(CFLAGS) $(MATH_EMULATION) \ - -c -o $*.o $< + $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< -OBJS = emulate.o error.o convert.o ea.o get_put.o \ - add.o mul.o div.o compare.o +OBJS = emulate.o convert.o ea.o get_put.o \ + add.o mul.o div.o compare.o sqrt.o math.a: $(OBJS) $(AR) rcs math.a $(OBJS) @@ -35,51 +26,72 @@ clean: dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ - $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile ### Dependencies: -add.s add.o : add.c ../../include/linux/math_emu.h ../../include/linux/sched.h ../../include/linux/head.h \ - ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h -compare.s compare.o : compare.c ../../include/linux/math_emu.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h -convert.s convert.o : convert.c ../../include/linux/math_emu.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h -div.s div.o : div.c ../../include/linux/math_emu.h ../../include/linux/sched.h ../../include/linux/head.h \ - ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h -ea.s ea.o : ea.c ../../include/stddef.h ../../include/linux/math_emu.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/types.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h ../../include/asm/segment.h -emulate.s emulate.o : emulate.c ../../include/signal.h ../../include/sys/types.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h -error.s error.o : error.c ../../include/signal.h ../../include/sys/types.h ../../include/linux/sched.h \ - ../../include/linux/head.h ../../include/linux/fs.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h -get_put.s get_put.o : get_put.c ../../include/signal.h ../../include/sys/types.h ../../include/linux/math_emu.h \ - ../../include/linux/sched.h ../../include/linux/head.h ../../include/linux/fs.h \ - ../../include/sys/dirent.h ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h ../../include/sys/resource.h \ - ../../include/asm/segment.h -mul.s mul.o : mul.c ../../include/linux/math_emu.h ../../include/linux/sched.h ../../include/linux/head.h \ - ../../include/linux/fs.h ../../include/sys/types.h ../../include/sys/dirent.h \ - ../../include/limits.h ../../include/linux/mm.h ../../include/linux/kernel.h \ - ../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \ - ../../include/sys/resource.h +add.o : add.c /usr/src/linux/include/linux/math_emu.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +compare.o : compare.c /usr/src/linux/include/linux/math_emu.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +convert.o : convert.c /usr/src/linux/include/linux/math_emu.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +div.o : div.c /usr/src/linux/include/linux/math_emu.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +ea.o : ea.c /usr/src/linux/include/linux/stddef.h /usr/src/linux/include/linux/math_emu.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/asm/segment.h +emulate.o : emulate.c /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +get_put.o : get_put.c /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/math_emu.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/asm/segment.h +mul.o : mul.c /usr/src/linux/include/linux/math_emu.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h +sqrt.o : sqrt.c /usr/src/linux/include/linux/math_emu.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h diff --git a/kernel/math/add.c b/kernel/math/add.c index 5cf84ef6bd96..563ec6965792 100644 --- a/kernel/math/add.c +++ b/kernel/math/add.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/add.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* diff --git a/kernel/math/compare.c b/kernel/math/compare.c index 4f1dfac088ab..e3d676cc4815 100644 --- a/kernel/math/compare.c +++ b/kernel/math/compare.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/compare.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* diff --git a/kernel/math/convert.c b/kernel/math/convert.c index e9383242bf31..3f5bbdfd3c2b 100644 --- a/kernel/math/convert.c +++ b/kernel/math/convert.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/convert.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include diff --git a/kernel/math/div.c b/kernel/math/div.c index e485fd9721dc..55ee71110f65 100644 --- a/kernel/math/div.c +++ b/kernel/math/div.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/div.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* diff --git a/kernel/math/ea.c b/kernel/math/ea.c index 85a1131c9ded..dba41ffeedce 100644 --- a/kernel/math/ea.c +++ b/kernel/math/ea.c @@ -1,16 +1,16 @@ /* * linux/kernel/math/ea.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* * Calculate the effective address. */ -#include - +#include #include + #include static int __regoffset[] = { @@ -58,7 +58,7 @@ static char * sib(struct info * info, int mod) char * ea(struct info * info, unsigned short code) { unsigned char mod,rm; - long * tmp = &EAX; + long * tmp; int offset = 0; mod = (code >> 6) & 3; @@ -84,7 +84,7 @@ char * ea(struct info * info, unsigned short code) EIP += 4; break; case 3: - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); } I387.foo = offset; I387.fos = 0x17; diff --git a/kernel/math/emulate.c b/kernel/math/emulate.c index b8e3ac2089ef..1df0691ba030 100644 --- a/kernel/math/emulate.c +++ b/kernel/math/emulate.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/emulate.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -32,7 +32,7 @@ #ifdef KERNEL_MATH_EMULATION -#include +#include #define __ALIGNED_TEMP_REAL 1 #include @@ -79,7 +79,7 @@ static void do_emu(struct info * info) return; case 0x1d1: case 0x1d2: case 0x1d3: case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7: - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); case 0x1e0: ST(0).exponent ^= 0x8000; return; @@ -87,15 +87,15 @@ static void do_emu(struct info * info) ST(0).exponent &= 0x7fff; return; case 0x1e2: case 0x1e3: - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); case 0x1e4: ftst(PST(0)); return; case 0x1e5: printk("fxam not implemented\n\r"); - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); case 0x1e6: case 0x1e7: - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); case 0x1e8: fpush(); ST(0) = CONST1; @@ -125,13 +125,17 @@ static void do_emu(struct info * info) ST(0) = CONSTZ; return; case 0x1ef: - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); + case 0x1fa: + fsqrt(PST(0),&tmp); + real_to_real(&tmp,&ST(0)); + return; case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: - case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb: - case 0x1fd: case 0x1fe: case 0x1ff: + case 0x1f8: case 0x1f9: case 0x1fb: case 0x1fd: + case 0x1fe: case 0x1ff: printk("%04x fxxx not implemented\n\r",code + 0xd800); - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); case 0x1fc: frndint(PST(0),&tmp); real_to_real(&tmp,&ST(0)); @@ -242,7 +246,7 @@ static void do_emu(struct info * info) return; case 0xb8: printk("ffree not implemented\n\r"); - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); case 0xb9: fxchg(&ST(0),&ST(code & 7)); return; @@ -299,7 +303,7 @@ static void do_emu(struct info * info) return; case 0xf8: printk("ffree not implemented\n\r"); - math_abort(info,1<<(SIGILL-1)); + math_abort(info,SIGILL); fpop(); return; case 0xf9: @@ -474,7 +478,7 @@ static void do_emu(struct info * info) return; } printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code); - math_abort(info,1<<(SIGFPE-1)); + math_abort(info,SIGFPE); } void math_emulate(long ___false) @@ -491,7 +495,7 @@ void math_emulate(long ___false) void __math_abort(struct info * info, unsigned int signal) { EIP = ORIG_EIP; - current->signal |= signal; + send_sig(signal,current,1); __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4)); } @@ -533,12 +537,12 @@ static temp_real_unaligned * __st(int i) #else /* no math emulation */ -#include +#include #include void math_emulate(long ___false) { - current->signal |= 1<<(SIGFPE-1); + send_sig(SIGFPE,current,1); schedule(); } diff --git a/kernel/math/error.c b/kernel/math/error.c deleted file mode 100644 index 5f1c1c2ca4c2..000000000000 --- a/kernel/math/error.c +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/kernel/math/error.c - * - * (C) 1991 Linus Torvalds - */ - -#include - -#include - -void math_error(void) -{ - if (last_task_used_math) - last_task_used_math->signal |= 1<<(SIGFPE-1); - __asm__("fnclex"); -} diff --git a/kernel/math/get_put.c b/kernel/math/get_put.c index 39063fee899a..bb603cbf6a96 100644 --- a/kernel/math/get_put.c +++ b/kernel/math/get_put.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/get_put.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -9,8 +9,7 @@ * ints/reals/BCD etc. This is the only part that concerns itself with * other than temporary real format. All other cals are strictly temp_real. */ -#include - +#include #include #include #include diff --git a/kernel/math/mul.c b/kernel/math/mul.c index ae85e70581d0..506f41847d4b 100644 --- a/kernel/math/mul.c +++ b/kernel/math/mul.c @@ -1,7 +1,7 @@ /* * linux/kernel/math/mul.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* diff --git a/kernel/math/sqrt.c b/kernel/math/sqrt.c new file mode 100644 index 000000000000..852222472dc2 --- /dev/null +++ b/kernel/math/sqrt.c @@ -0,0 +1,95 @@ +/* + * linux/kernel/math/sqrt.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * simple and stupid temporary real fsqrt() routine + * + * There are probably better ways to do this, but this should work ok. + */ + +#include +#include + +static void shift_right(int * c) +{ + __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)" + ::"r" ((long) c)); +} + +static int sqr64(unsigned long * a, unsigned long * b) +{ + unsigned long tmp[4]; + + __asm__("movl (%0),%%eax ; mull %%eax\n\t" + "movl %%eax,(%1) ; movl %%edx,4(%1)\n\t" + "movl 4(%0),%%eax ; mull %%eax\n\t" + "movl %%eax,8(%1) ; movl %%edx,12(%1)\n\t" + "movl (%0),%%eax ; mull 4(%0)\n\t" + "addl %%eax,%%eax ; adcl %%edx,%%edx\n\t" + "adcl $0,12(%1) ; addl %%eax,4(%1)\n\t" + "adcl %%edx,8(%1) ; adcl $0,12(%1)" + ::"b" ((long) a),"c" ((long) tmp) + :"ax","bx","cx","dx"); + if (tmp[3] > b[3] || + (tmp[3] == b[3] && (tmp[2] > b[2] || + (tmp[2] == b[2] && (tmp[1] > b[1] || + (tmp[1] == b[1] && tmp[0] > b[0])))))) + return 0; + return 1; +} + +void fsqrt(const temp_real * s, temp_real * d) +{ + unsigned long src[4]; + unsigned long res[2]; + int exponent; + unsigned long mask, *c; + int i; + + exponent = s->exponent; + src[0] = src[1] = 0; + src[2] = s->a; + src[3] = s->b; + d->exponent = 0; + d->a = d->b = 0; + if (!exponent) /* fsqrt(0.0) = 0.0 */ + return; + if (!src[2] && !src[3]) + return; + if (exponent & 0x8000) { + send_sig(SIGFPE,current,0); + return; + } + if (exponent & 1) { + shift_right(src); + exponent++; + } + exponent >>= 1; + exponent += 0x1fff; + c = res + 2; + mask = 0; + for (i = 64 ; i > 0 ; i--) { + if (!(mask >>= 1)) { + c--; + mask = 0x80000000; + } + res[0] = d->a; res[1] = d->b; + *c |= mask; + if (sqr64(res,src)) { + d->a = res[0]; + d->b = res[1]; + } + } + if (!d->a && !d->b) + return; + while (!(d->b & 0x80000000)) { + __asm__("addl %%eax,%%eax ; adcl %%edx,%%edx" + :"=a" (d->a),"=d" (d->b) + :"0" (d->a),"1" (d->b)); + exponent--; + } + d->exponent = exponent; +} diff --git a/kernel/mktime.c b/kernel/mktime.c index a67db96f4b18..5de9c67b0050 100644 --- a/kernel/mktime.c +++ b/kernel/mktime.c @@ -1,7 +1,7 @@ /* * linux/kernel/mktime.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include diff --git a/kernel/panic.c b/kernel/panic.c index 7d8a06ba96b9..2459e762f5a0 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -1,7 +1,7 @@ /* * linux/kernel/panic.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* diff --git a/kernel/printk.c b/kernel/printk.c index ebce88c6d6d2..2e0a7a1ecd07 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1,31 +1,103 @@ /* * linux/kernel/printk.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -/* - * When in kernel-mode, we cannot use printf, as fs is liable to - * point to 'interesting' things. Make a printf with fs-saving, and - * all is well. - */ #include -#include +#include +#include + +#include +#include #include static char buf[1024]; extern int vsprintf(char * buf, const char * fmt, va_list args); +extern void console_print(const char *); + +static unsigned long log_page = 0; +static unsigned long log_start = 0; +static unsigned long log_size = 0; +static struct wait_queue * log_wait = NULL; + +int sys_syslog(int type, char * buf, int len) +{ + unsigned long i; + char c; + + if (!suser()) + return -EPERM; + switch (type) { + case 0: + i = log_page; + log_page = 0; + free_page(i); + wake_up(&log_wait); + return 0; + case 1: + i = get_free_page(GFP_KERNEL); + if (log_page) { + free_page(i); + return 0; + } else if (log_page = i) { + log_start = log_size = 0; + return 0; + } + return -ENOMEM; + case 2: + if (!buf || len < 0) + return -EINVAL; + if (!len) + return 0; + verify_area(buf,len); + while (!log_size) { + if (!log_page) + return -EIO; + if (current->signal & ~current->blocked) + return -ERESTARTSYS; + cli(); + if (!log_size) + interruptible_sleep_on(&log_wait); + sti(); + } + i = 0; + while (log_size && len) { + c = *((char *) log_page+log_start); + log_start++; + log_size--; + log_start &= 4095; + put_fs_byte(c,buf); + buf++; + i++; + } + return i; + } + return -EINVAL; +} + int printk(const char *fmt, ...) { va_list args; - int i; + int i,j; + char * p; va_start(args, fmt); i=vsprintf(buf,fmt,args); va_end(args); + for (j = 0; j < i && log_page ; j++) { + p = (char *) log_page + (4095 & (log_start+log_size)); + *p = buf[j]; + if (log_size < 4096) + log_size++; + else + log_start++; + } + if (log_page) + wake_up(&log_wait); console_print(buf); return i; } diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 4602fc2ab966..07f3a8d5f48c 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -6,10 +6,11 @@ #include #include #include -#include +#include +#include + #include #include -#include /* * does not yet catch signals sent when the child dies. @@ -29,19 +30,16 @@ */ #define MAGICNUMBER 68 -void do_no_page(unsigned long, unsigned long, struct task_struct *); -void write_verify(unsigned long); - /* change a pid into a task struct. */ -static inline int get_task(int pid) +static inline struct task_struct * get_task(int pid) { int i; - for (i = 0; i < NR_TASKS; i++) { + for (i = 1; i < NR_TASKS; i++) { if (task[i] != NULL && (task[i]->pid == pid)) - return i; + return task[i]; } - return -1; + return NULL; } /* @@ -100,7 +98,7 @@ repeat: page = *((unsigned long *) page); } if (!(page & PAGE_PRESENT)) { - do_no_page(0,addr,tsk); + do_no_page(0,addr,tsk,0); goto repeat; } page &= 0xfffff000; @@ -129,11 +127,11 @@ repeat: page = *((unsigned long *) page); } if (!(page & PAGE_PRESENT)) { - do_no_page(0,addr,tsk); + do_no_page(0,addr,tsk,0); goto repeat; } if (!(page & PAGE_RW)) { - write_verify(addr); + do_wp_page(0,addr,tsk,0); goto repeat; } page &= 0xfffff000; @@ -222,32 +220,54 @@ static int write_long(struct task_struct * tsk, unsigned long addr, int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; - int childno; - if (request == 0) { + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->flags & PF_PTRACED) + return -EPERM; /* set the ptrace bit in the proccess flags. */ current->flags |= PF_PTRACED; return 0; } - - childno = get_task(pid); - - if (childno < 0) + if (!(child = get_task(pid))) return -ESRCH; - else - child = task[childno]; - - if (child->p_pptr != current || !(child->flags & PF_PTRACED) || - child->state != TASK_STOPPED) + if (request == PTRACE_ATTACH) { + long tmp; + + if (child == current) + return -EPERM; + if ((!child->dumpable || (current->uid != child->euid) || + (current->gid != child->egid)) && !suser()) + return -EPERM; + /* the same process cannot be attached many times */ + if (child->flags & PF_PTRACED) + return -EPERM; + child->flags |= PF_PTRACED; + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG; + put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); + if (child->state == TASK_INTERRUPTIBLE || + child->state == TASK_STOPPED) + child->state = TASK_RUNNING; + child->signal = 0; + return 0; + } + if (!(child->flags & PF_PTRACED) || child->state != TASK_STOPPED) + return -ESRCH; + if (child->p_pptr != current) return -ESRCH; switch (request) { /* when I and D space are seperate, these will need to be fixed. */ - case 1: /* read word at location addr. */ - case 2: { + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { int tmp,res; - res = read_long(task[childno], addr, &tmp); + res = read_long(child, addr, &tmp); if (res < 0) return res; verify_area((void *) data, 4); @@ -256,7 +276,7 @@ int sys_ptrace(long request, long pid, long addr, long data) } /* read the word at location addr in the USER area. */ - case 3: { + case PTRACE_PEEKUSR: { int tmp; addr = addr >> 2; /* temporary hack. */ if (addr < 0 || addr >= 17) @@ -268,11 +288,11 @@ int sys_ptrace(long request, long pid, long addr, long data) } /* when I and D space are seperate, this will have to be fixed. */ - case 4: /* write the word at location addr. */ - case 5: - return write_long(task[childno],addr,data); + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + return write_long(child,addr,data); - case 6: /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ addr = addr >> 2; /* temproary hack. */ if (addr < 0 || addr >= 17) return -EIO; @@ -286,13 +306,13 @@ int sys_ptrace(long request, long pid, long addr, long data) return -EIO; return 0; - case 7: { /* restart after signal. */ + case PTRACE_CONT: { /* restart after signal. */ long tmp; - child->signal=0; + child->signal = 0; if (data > 0 && data <= NSIG) child->signal = 1<<(data-1); - child->state = 0; + child->state = TASK_RUNNING; /* make sure the single step bit is not set. */ tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG; put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); @@ -304,10 +324,10 @@ int sys_ptrace(long request, long pid, long addr, long data) * perhaps it should be put in the status that it want's to * exit. */ - case 8: { + case PTRACE_KILL: { long tmp; - child->state = 0; + child->state = TASK_RUNNING; child->signal = 1 << (SIGKILL-1); /* make sure the single step bit is not set. */ tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG; @@ -315,19 +335,34 @@ int sys_ptrace(long request, long pid, long addr, long data) return 0; } - case 9: { /* set the trap flag. */ + case PTRACE_SINGLESTEP: { /* set the trap flag. */ long tmp; tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG; put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); - child->state = 0; + child->state = TASK_RUNNING; child->signal = 0; - if (data > 0 && data 0 && data <= NSIG) child->signal= 1<<(data-1); /* give it a chance to run. */ return 0; } + case PTRACE_DETACH: { /* detach a process that was attached. */ + long tmp; + + child->flags &= ~PF_PTRACED; + child->signal=0; + child->state = 0; + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG; + put_stack_long(child, 4*EFL-MAGICNUMBER,tmp); + return 0; + } + default: return -EIO; } diff --git a/kernel/sched.c b/kernel/sched.c index 098d096154ea..208aefb3e0d7 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1,7 +1,7 @@ /* * linux/kernel/sched.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -10,26 +10,33 @@ * call functions (type getpid(), which just extracts a field from * current-task */ + +#define TIMER_IRQ 0 + +#include #include #include #include #include #include +#include +#include +#include + #include #include #include -#include -#include +int need_resched = 0; #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) -void show_task(int nr,struct task_struct * p) +static void show_task(int nr,struct task_struct * p) { int i,j = 4096-sizeof(struct task_struct); - printk("%d: pid=%d, state=%d, father=%d, child=%d, ",nr,p->pid, + printk("%d: pid=%d, state=%d, father=%d, child=%d, ",(p == current)?-nr:nr,p->pid, p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1); i=0; while (i &FIRST_TASK ; --p) if (*p) { if ((*p)->timeout && (*p)->timeout < jiffies) if ((*p)->state == TASK_INTERRUPTIBLE) { (*p)->timeout = 0; - (*p)->state = TASK_RUNNING; + wake_one_task(*p); } - if ((*p)->alarm && (*p)->alarm < jiffies) { - (*p)->signal |= (1<<(SIGALRM-1)); - (*p)->alarm = 0; - } if (((*p)->signal & ~(*p)->blocked) && - (*p)->state==TASK_INTERRUPTIBLE) - (*p)->state=TASK_RUNNING; + (*p)->state==TASK_INTERRUPTIBLE) + wake_one_task(*p); } /* this is the scheduler proper: */ @@ -178,54 +182,68 @@ int sys_pause(void) return -EINTR; } -void wake_up(struct task_struct **p) +void wake_one_task(struct task_struct * p) +{ + p->state = TASK_RUNNING; + if (p->counter > current->counter) + need_resched = 1; +} + +/* + * wake_up doesn't wake up stopped processes - they have to be awakened + * with signals or similar. + */ +void wake_up(struct wait_queue **q) { - struct task_struct * wakeup_ptr, * tmp; - - if (p && *p) { - wakeup_ptr = *p; - *p = NULL; - while (wakeup_ptr && wakeup_ptr != task[0]) { - if (wakeup_ptr->state == TASK_STOPPED) - printk("wake_up: TASK_STOPPED\n"); - else if (wakeup_ptr->state == TASK_ZOMBIE) + struct wait_queue *tmp, *next; + struct task_struct * p; + unsigned long flags; + + if (!q || !(next = *q)) + return; + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); + do { + tmp = next; + next = tmp->next; + if (p = tmp->task) { + if (p->state == TASK_ZOMBIE) printk("wake_up: TASK_ZOMBIE\n"); - else - wakeup_ptr->state = TASK_RUNNING; - tmp = wakeup_ptr->next_wait; - wakeup_ptr->next_wait = task[0]; - wakeup_ptr = tmp; + else if (p->state != TASK_STOPPED) { + p->state = TASK_RUNNING; + if (p->counter > current->counter) + need_resched = 1; + } } - } + tmp->next = NULL; + } while (next && next != *q); + __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags)); } -static inline void __sleep_on(struct task_struct **p, int state) +static inline void __sleep_on(struct wait_queue **p, int state) { - unsigned int flags; + unsigned long flags; if (!p) return; if (current == task[0]) panic("task[0] trying to sleep"); - __asm__("pushfl ; popl %0":"=r" (flags)); - current->next_wait = *p; - task[0]->next_wait = NULL; - *p = current; + if (current->wait.next) + printk("__sleep_on: wait->next exists\n"); + __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags)); current->state = state; + add_wait_queue(p,¤t->wait); sti(); schedule(); - if (current->next_wait != task[0]) - wake_up(p); - current->next_wait = NULL; + remove_wait_queue(p,¤t->wait); __asm__("pushl %0 ; popfl"::"r" (flags)); } -void interruptible_sleep_on(struct task_struct **p) +void interruptible_sleep_on(struct wait_queue **p) { __sleep_on(p,TASK_INTERRUPTIBLE); } -void sleep_on(struct task_struct **p) +void sleep_on(struct wait_queue **p) { __sleep_on(p,TASK_UNINTERRUPTIBLE); } @@ -235,7 +253,7 @@ void sleep_on(struct task_struct **p) * proper. They are here because the floppy needs a timer, and this * was the easiest way of doing it. */ -static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL}; +static struct wait_queue * wait_motor[4] = {NULL,NULL,NULL,NULL}; static int mon_timer[4]={0,0,0,0}; static int moff_timer[4]={0,0,0,0}; unsigned char current_DOR = 0x0C; @@ -333,14 +351,82 @@ void add_timer(long jiffies, void (*fn)(void)) sti(); } +#define FSHIFT 11 +#define FSCALE (1< &FIRST_TASK; --p) + if (*p && ((*p)->state == TASK_RUNNING || + (*p)->state == TASK_UNINTERRUPTIBLE)) + ++n; + + for (i = 0; i < 3; ++i) + averunnable[i] = (cexp[i] * averunnable[i] + + n * FSCALE * (FSCALE - cexp[i])) >> FSHIFT; +} + unsigned long timer_active = 0; struct timer_struct timer_table[32]; -void do_timer(long cpl) +/* + * The int argument is really a (struct pt_regs *), in case the + * interrupt wants to know from where it was called. The timer + * irq uses this to decide if it should update the user or system + * times. + */ +static void do_timer(int regs) { unsigned long mask; struct timer_struct *tp = timer_table+0; + struct task_struct ** task_p; + static int avg_cnt = 0; + jiffies++; + if (3 & ((struct pt_regs *) regs)->cs) + current->utime++; + else { + current->stime++; + /* Update ITIMER_VIRT for current task if not in a system call */ + if (current->it_virt_value && !(--current->it_virt_value)) { + current->it_virt_value = current->it_virt_incr; + send_sig(SIGVTALRM,current,1); + } + } + if (--avg_cnt < 0) { + avg_cnt = 500; + update_avg(); + } + if ((--current->counter)<=0) { + current->counter=0; + need_resched = 1; + } + /* Update ITIMER_REAL for every task */ + for (task_p = &LAST_TASK; task_p >= &FIRST_TASK; task_p--) + if (*task_p && (*task_p)->it_real_value + && !(--(*task_p)->it_real_value)) { + send_sig(SIGALRM,*task_p,1); + (*task_p)->it_real_value = (*task_p)->it_real_incr; + need_resched = 1; + } + /* Update ITIMER_PROF for the current task */ + if (current->it_prof_value && !(--current->it_prof_value)) { + current->it_prof_value = current->it_prof_incr; + send_sig(SIGPROF,current,1); + } for (mask = 1 ; mask ; tp++,mask += mask) { if (mask > timer_active) break; @@ -350,13 +436,8 @@ void do_timer(long cpl) continue; timer_active &= ~mask; tp->fn(); + sti(); } - - if (cpl) - current->utime++; - else - current->stime++; - if (next_timer) { next_timer->jiffies--; while (next_timer && next_timer->jiffies <= 0) { @@ -370,20 +451,18 @@ void do_timer(long cpl) } if (current_DOR & 0xf0) do_floppy_timer(); - if ((--current->counter)>0) return; - current->counter=0; - if (!cpl) return; - schedule(); } int sys_alarm(long seconds) { - int old = current->alarm; - - if (old) - old = (old - jiffies) / HZ; - current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; - return (old); + extern int _setitimer(int, struct itimerval *, struct itimerval *); + struct itimerval new, old; + + new.it_interval.tv_sec = new.it_interval.tv_usec = 0; + new.it_value.tv_sec = seconds; + new.it_value.tv_usec = 0; + _setitimer(ITIMER_REAL, &new, &old); + return(old.it_value.tv_sec + (old.it_value.tv_usec / 1000000)); } int sys_getpid(void) @@ -420,7 +499,7 @@ int sys_nice(long increment) { if (increment < 0 && !suser()) return -EPERM; - if (increment > current->priority) + if (increment >= current->priority) increment = current->priority-1; current->priority -= increment; return 0; @@ -435,6 +514,7 @@ void sched_init(void) panic("Struct sigaction MUST be 16 bytes"); set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); + set_system_gate(0x80,&system_call); p = gdt+2+FIRST_TSS_ENTRY; for(i=1 ; i> 8 , 0x40); /* MSB */ - set_intr_gate(0x20,&timer_interrupt); - outb(inb_p(0x21)&~0x01,0x21); - set_system_gate(0x80,&system_call); + request_irq(TIMER_IRQ,do_timer); } diff --git a/kernel/signal.c b/kernel/signal.c index cf0aa01baa96..ca850fa7b121 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1,19 +1,20 @@ /* * linux/kernel/signal.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include #include +#include +#include +#include +#include + #include -#include -#include -#include - -int send_sig (int, struct task_struct *, int); - +extern int core_dump(long signr,struct pt_regs * regs); + int sys_sgetmask() { return current->blocked; @@ -93,7 +94,7 @@ int sys_signal(int signum, long handler, long restorer) return -EINVAL; tmp.sa_handler = (void (*)(int)) handler; tmp.sa_mask = 0; - tmp.sa_flags = SA_ONESHOT | SA_NOMASK; + tmp.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT; tmp.sa_restorer = (void (*)(void)) restorer; handler = (long) current->sigaction[signum-1].sa_handler; current->sigaction[signum-1] = tmp; @@ -119,47 +120,37 @@ int sys_sigaction(int signum, const struct sigaction * action, return 0; } -/* - * Routine writes a core dump image in the current directory. - * Currently not implemented. - */ -int core_dump(long signr) -{ - return(0); /* We didn't do a dump */ -} - extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); -int do_signal(long signr,long ebx, long ecx, long edx, - long esi, long edi, long ebp, long eax, - long ds, long es, long fs, long gs, - long orig_eax, - long eip, long cs, long eflags, - unsigned long * esp, long ss) +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +int do_signal(long signr,struct pt_regs * regs) { unsigned long sa_handler; - long old_eip=eip; + long old_eip = regs->eip; struct sigaction * sa = current->sigaction + signr - 1; int longs; - unsigned long * tmp_esp; #ifdef notdef printk("pid: %d, signr: %x, eax=%d, oeax = %d, int=%d\n", - current->pid, signr, eax, orig_eax, + current->pid, signr, regs->eax, regs->orig_eax, sa->sa_flags & SA_INTERRUPT); #endif - if ((orig_eax != -1) && - ((eax == -ERESTARTSYS) || (eax == -ERESTARTNOINTR))) { - if ((eax == -ERESTARTSYS) && ((sa->sa_flags & SA_INTERRUPT) || - signr < SIGCONT || signr > SIGTTOU)) - *(&eax) = -EINTR; + sa_handler = (unsigned long) sa->sa_handler; + if ((regs->orig_eax != -1) && + ((regs->eax == -ERESTARTSYS) || (regs->eax == -ERESTARTNOINTR))) { + if ((sa_handler > 1) && (regs->eax == -ERESTARTSYS) && + (sa->sa_flags & SA_INTERRUPT)) + regs->eax = -EINTR; else { - *(&eax) = orig_eax; - *(&eip) = old_eip -= 2; + regs->eax = regs->orig_eax; + regs->eip = old_eip -= 2; } } - sa_handler = (unsigned long) sa->sa_handler; if (sa_handler==1) { /* check for SIGCHLD: it's special */ if (signr == SIGCHLD) @@ -168,6 +159,8 @@ int do_signal(long signr,long ebx, long ecx, long edx, return(1); /* Ignore, see if there are more signals... */ } if (!sa_handler) { + if (current->pid == 1) + return 1; switch (signr) { case SIGCONT: case SIGCHLD: @@ -182,9 +175,7 @@ int do_signal(long signr,long ebx, long ecx, long edx, current->exit_code = signr; if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - send_sig(SIGCHLD, current->p_pptr, 1); -/* current->p_pptr->signal |= (1<<(SIGCHLD-1));*/ - + send_sig(SIGCHLD, current->p_pptr, 1); return(1); /* Reschedule another event */ case SIGQUIT: @@ -193,7 +184,7 @@ int do_signal(long signr,long ebx, long ecx, long edx, case SIGIOT: case SIGFPE: case SIGSEGV: - if (core_dump(signr)) + if (core_dump(signr,regs)) do_exit(signr|0x80); /* fall through */ default: @@ -205,19 +196,19 @@ int do_signal(long signr,long ebx, long ecx, long edx, */ if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - *(&eip) = sa_handler; - longs = (sa->sa_flags & SA_NOMASK)?7:8; - *(&esp) -= longs; - verify_area(esp,longs*4); - tmp_esp=esp; + regs->eip = sa_handler; + longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4); + regs->esp -= longs; + tmp_esp = (unsigned long *) regs->esp; + verify_area(tmp_esp,longs); put_fs_long((long) sa->sa_restorer,tmp_esp++); put_fs_long(signr,tmp_esp++); if (!(sa->sa_flags & SA_NOMASK)) put_fs_long(current->blocked,tmp_esp++); - put_fs_long(eax,tmp_esp++); - put_fs_long(ecx,tmp_esp++); - put_fs_long(edx,tmp_esp++); - put_fs_long(eflags,tmp_esp++); + put_fs_long(regs->eax,tmp_esp++); + put_fs_long(regs->ecx,tmp_esp++); + put_fs_long(regs->edx,tmp_esp++); + put_fs_long(regs->eflags,tmp_esp++); put_fs_long(old_eip,tmp_esp++); current->blocked |= sa->sa_mask; /* force a supervisor-mode page-in of the signal handler to reduce races */ diff --git a/kernel/sys.c b/kernel/sys.c index a9190287803d..94a8de54e765 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1,22 +1,22 @@ /* * linux/kernel/sys.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ -#include - +#include #include #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include +#include + /* * this indicates wether you can reboot with ctrl-alt-del: the deault is yes */ @@ -30,14 +30,72 @@ struct timezone sys_tz = { 0, 0}; extern int session_of_pgrp(int pgrp); -int sys_getpriority() +#define PZERO 15 + +static int proc_sel(struct task_struct *p, int which, int who) +{ + switch (which) { + case PRIO_PROCESS: + if (!who && p == current) + return 1; + return(p->pid == who); + case PRIO_PGRP: + if (!who) + who = current->pgrp; + return(p->pgrp == who); + case PRIO_USER: + if (!who) + who = current->uid; + return(p->uid == who); + } + return 0; +} + +int sys_setpriority(int which, int who, int niceval) { - return -ENOSYS; + struct task_struct **p; + int error = ESRCH; + int priority; + + if (which > 2 || which < 0) + return -EINVAL; + + if ((priority = PZERO - niceval) <= 0) + priority = 1; + + for(p = &LAST_TASK; p > &FIRST_TASK; --p) { + if (!*p || !proc_sel(*p, which, who)) + continue; + if ((*p)->uid != current->euid && + (*p)->uid != current->uid && !suser()) { + error = EPERM; + continue; + } + if (error == ESRCH) + error = 0; + if (priority > (*p)->priority && !suser()) + error = EACCES; + else + (*p)->priority = priority; + } + return -error; } -int sys_setpriority() +int sys_getpriority(int which, int who) { - return -ENOSYS; + struct task_struct **p; + int max_prio = 0; + + if (which > 2 || which < 0) + return -EINVAL; + + for(p = &LAST_TASK; p > &FIRST_TASK; --p) { + if (!*p || !proc_sel(*p, which, who)) + continue; + if ((*p)->priority > max_prio) + max_prio = (*p)->priority; + } + return(max_prio ? max_prio : -ESRCH); } int sys_profil() @@ -106,6 +164,8 @@ void ctrl_alt_del(void) { if (C_A_D) hard_reset_now(); + else + send_sig(SIGINT,task[1],1); } @@ -122,14 +182,14 @@ void ctrl_alt_del(void) */ int sys_setregid(int rgid, int egid) { - if (rgid>0) { + if (rgid >= 0) { if ((current->gid == rgid) || suser()) current->gid = rgid; else return(-EPERM); } - if (egid>0) { + if (egid >= 0) { if ((current->gid == egid) || (current->egid == egid) || suser()) { @@ -209,17 +269,17 @@ int sys_setreuid(int ruid, int euid) { int old_ruid = current->uid; - if (ruid>0) { + if (ruid >= 0) { if ((current->euid==ruid) || - (old_ruid == ruid) || + (old_ruid == ruid) || suser()) current->uid = ruid; else return(-EPERM); } - if (euid>0) { + if (euid >= 0) { if ((old_ruid == euid) || - (current->euid == euid) || + (current->euid == euid) || suser()) { current->euid = euid; current->suid = euid; @@ -385,19 +445,34 @@ int in_group_p(gid_t grp) return 0; } -static struct utsname thisname = { +static struct new_utsname thisname = { UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, UTS_MACHINE }; -int sys_uname(struct utsname * name) +int sys_newuname(struct new_utsname * name) { - int i; + if (!name) + return -EFAULT; + verify_area(name, sizeof *name); + memcpy_tofs(name,&thisname,sizeof *name); + return 0; +} +int sys_uname(struct old_utsname * name) +{ if (!name) return -EINVAL; verify_area(name,sizeof *name); - for(i=0;isysname,&thisname.sysname,__OLD_UTS_LEN); + put_fs_byte(0,name->sysname+__OLD_UTS_LEN); + memcpy_tofs(&name->nodename,&thisname.nodename,__OLD_UTS_LEN); + put_fs_byte(0,name->nodename+__OLD_UTS_LEN); + memcpy_tofs(&name->release,&thisname.release,__OLD_UTS_LEN); + put_fs_byte(0,name->release+__OLD_UTS_LEN); + memcpy_tofs(&name->version,&thisname.version,__OLD_UTS_LEN); + put_fs_byte(0,name->version+__OLD_UTS_LEN); + memcpy_tofs(&name->machine,&thisname.machine,__OLD_UTS_LEN); + put_fs_byte(0,name->machine+__OLD_UTS_LEN); return 0; } @@ -410,15 +485,13 @@ int sys_sethostname(char *name, int len) if (!suser()) return -EPERM; - if (len > MAXHOSTNAMELEN) + if (len > __NEW_UTS_LEN) return -EINVAL; for (i=0; i < len; i++) { if ((thisname.nodename[i] = get_fs_byte(name+i)) == 0) - break; - } - if (thisname.nodename[i]) { - thisname.nodename[i>MAXHOSTNAMELEN ? MAXHOSTNAMELEN : i] = 0; + return 0; } + thisname.nodename[i] = 0; return 0; } diff --git a/kernel/sys_call.S b/kernel/sys_call.S index e6f77667483c..735a795d9510 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -1,7 +1,7 @@ /* * linux/kernel/sys_call.S * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -37,8 +37,6 @@ * 40(%esp) - %oldss */ -SIG_CHLD = 17 - EBX = 0x00 ECX = 0x04 EDX = 0x08 @@ -81,14 +79,14 @@ ENOSYS = 38 * Ok, I get parallel printer interrupts while using the floppy for some * strange reason. Urgel. Now I just ignore them. */ -.globl _system_call,_timer_interrupt,_sys_execve +.globl _system_call,_sys_execve .globl _device_not_available, _coprocessor_error .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op .globl _double_fault,_coprocessor_segment_overrun .globl _invalid_TSS,_segment_not_present,_stack_segment -.globl _general_protection,_irq13,_reserved +.globl _general_protection,_reserved .globl _alignment_check,_page_fault -.globl _keyboard_interrupt +.globl ret_from_sys_call #define SAVE_ALL \ cld; \ @@ -109,59 +107,17 @@ ENOSYS = 38 movl $0x17,%edx; \ mov %dx,%fs -#define ACK_FIRST(mask) \ - inb $0x21,%al; \ - jmp 1f; \ -1: jmp 1f; \ -1: orb $(mask),%al; \ - outb %al,$0x21; \ - jmp 1f; \ -1: jmp 1f; \ -1: movb $0x20,%al; \ - outb %al,$0x20 - -#define ACK_SECOND(mask) \ - inb $0xA1,%al; \ - jmp 1f; \ -1: jmp 1f; \ -1: orb $mask,%al; \ - outb %al,$0xA1; \ - jmp 1f; \ -1: jmp 1f; \ -1: movb $0x20,%al; \ - outb %al,$0x20 - jmp 1f; \ -1: jmp 1f; \ -1: outb %al,$0xA0 - -#define UNBLK_FIRST(mask) \ - inb $0x21,%al; \ - jmp 1f; \ -1: jmp 1f; \ -1: andb $~(mask),%al; \ - outb %al,$0x21 - -#define UNBLK_SECOND(mask) \ - inb $0xA1,%al; \ - jmp 1f; \ -1: jmp 1f; \ -1: andb $~(mask),%al; \ - outb %al,$0xA1 - -.align 2 -bad_sys_call: - movl $-ENOSYS,EAX(%esp) - jmp ret_from_sys_call .align 2 reschedule: pushl $ret_from_sys_call jmp _schedule .align 2 _system_call: - pushl %eax # save orig_eax + pushl %eax # save orig_eax SAVE_ALL + movl $-ENOSYS,EAX(%esp) cmpl _NR_syscalls,%eax - jae bad_sys_call + jae ret_from_sys_call call _sys_call_table(,%eax,4) movl %eax,EAX(%esp) # save the return value ret_from_sys_call: @@ -169,13 +125,17 @@ ret_from_sys_call: jne 2f cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 2f -1: movl _current,%eax - cmpl _task,%eax # task[0] cannot have signals - je 2f +1: cmpl $0,_need_resched + jne reschedule + movl _current,%eax cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je reschedule + movl $1,_need_resched + cmpl _task,%eax # task[0] cannot have signals + je 2f + movl $0,_need_resched movl signal(%eax),%ebx movl blocked(%eax),%ecx notl %ecx @@ -184,10 +144,13 @@ ret_from_sys_call: je 2f btrl %ecx,%ebx movl %ebx,signal(%eax) + movl %esp,%ebx + pushl %ebx incl %ecx pushl %ecx call _do_signal popl %ecx + popl %ebx testl %eax, %eax jne 1b # see if we need to switch tasks, or do more signals 2: popl %ebx @@ -204,62 +167,6 @@ ret_from_sys_call: addl $4,%esp # skip the orig_eax iret -.align 2 -_irq13: - pushl %eax - xorb %al,%al - outb %al,$0xF0 - movb $0x20,%al - outb %al,$0x20 - jmp 1f -1: jmp 1f -1: outb %al,$0xA0 - popl %eax -_coprocessor_error: - pushl $-1 # mark this as an int. - SAVE_ALL - pushl $ret_from_sys_call - jmp _math_error - -.align 2 -_device_not_available: - pushl $-1 # mark this as an int - SAVE_ALL - pushl $ret_from_sys_call - clts # clear TS so that we can use math - movl %cr0,%eax - testl $0x4,%eax # EM (math emulation bit) - je _math_state_restore - pushl $0 # temporary storage for ORIG_EIP - call _math_emulate - addl $4,%esp - ret - -.align 2 -_keyboard_interrupt: - pushl $-1 - SAVE_ALL - ACK_FIRST(2) - sti - call _do_keyboard - cli - UNBLK_FIRST(2) - jmp ret_from_sys_call - -.align 2 -_timer_interrupt: - pushl $-1 # mark this as an int - SAVE_ALL - incl _jiffies - movb $0x20,%al # EOI to interrupt controller #1 - outb %al,$0x20 - movl CS(%esp),%eax - andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor) - pushl %eax - call _do_timer # 'do_timer(long CPL)' does everything from - addl $4,%esp # task switching to accounting ... - jmp ret_from_sys_call - .align 2 _sys_execve: lea (EIP+4)(%esp),%eax # don't forget about the return address. @@ -300,9 +207,29 @@ error_code: addl $8,%esp jmp ret_from_sys_call +.align 2 +_coprocessor_error: + pushl $0 + pushl $_do_coprocessor_error + jmp error_code + +.align 2 +_device_not_available: + pushl $-1 # mark this as an int + SAVE_ALL + pushl $ret_from_sys_call + clts # clear TS so that we can use math + movl %cr0,%eax + testl $0x4,%eax # EM (math emulation bit) + je _math_state_restore + pushl $0 # temporary storage for ORIG_EIP + call _math_emulate + addl $4,%esp + ret + _debug: pushl $0 - pushl $_do_int3 # _do_debug + pushl $_do_debug jmp error_code _nmi: diff --git a/kernel/traps.c b/kernel/traps.c index 89b65f3b0bca..8d7d0397f122 100644 --- a/kernel/traps.c +++ b/kernel/traps.c @@ -1,7 +1,7 @@ /* * linux/kernel/traps.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -10,16 +10,15 @@ * to mainly kill the offending process (probably by giving it a signal, * but possibly by killing it outright if necessary). */ -#include - #include #include #include +#include +#include + #include #include #include -#include - #define get_seg_byte(seg,addr) ({ \ register char __res; \ @@ -57,121 +56,126 @@ void general_protection(void); void page_fault(void); void coprocessor_error(void); void reserved(void); -void parallel_interrupt(void); -void irq13(void); void alignment_check(void); -int send_sig(long, struct task_struct *, int); -static void die(char * str,long esp_ptr,long nr) +static void die_if_kernel(char * str,long esp_ptr,long nr) { long * esp = (long *) esp_ptr; int i; + if ((0xffff & esp[1]) == 0xf) + return; printk("%s: %04x\n\r",str,nr&0xffff); printk("EIP: %04x:%p\nEFLAGS: %p\n", 0xffff & esp[1],esp[0],esp[2]); - if ((0xffff & esp[1]) == 0xf) - printk("ESP: %04x:%p\n",0xffff & esp[4],esp[3]); printk("fs: %04x\n",_fs()); printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17)); - if ((0xffff & esp[1]) == 0xf) { - printk("Stack: "); - for (i=0;i<4;i++) - printk("%p ",get_seg_long(0x17,i+(long *)esp[3])); - printk("\n"); - } str(i); printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i); for(i=0;i<10;i++) printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0]))); printk("\n\r"); - do_exit(11); /* play segment exception */ + do_exit(SIGSEGV); } void do_double_fault(long esp, long error_code) { - die("double fault",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("double fault",esp,error_code); } void do_general_protection(long esp, long error_code) { - die("general protection",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("general protection",esp,error_code); } void do_alignment_check(long esp, long error_code) { - die("alignment check",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("alignment check",esp,error_code); } void do_divide_error(long esp, long error_code) { - die("divide error",esp,error_code); + send_sig(SIGFPE, current, 1); + die_if_kernel("divide error",esp,error_code); } void do_int3(long esp, long error_code) { - send_sig(SIGTRAP, current, 0); + send_sig(SIGTRAP, current, 1); + die_if_kernel("int3",esp,error_code); } void do_nmi(long esp, long error_code) { - die("nmi",esp,error_code); + printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); } void do_debug(long esp, long error_code) { - send_sig(SIGTRAP, current, 0); + send_sig(SIGTRAP, current, 1); + die_if_kernel("debug",esp,error_code); } void do_overflow(long esp, long error_code) { - die("overflow",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("overflow",esp,error_code); } void do_bounds(long esp, long error_code) { - die("bounds",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("bounds",esp,error_code); } void do_invalid_op(long esp, long error_code) { - die("invalid operand",esp,error_code); + send_sig(SIGILL, current, 1); + die_if_kernel("invalid operand",esp,error_code); } void do_device_not_available(long esp, long error_code) { - die("device not available",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("device not available",esp,error_code); } void do_coprocessor_segment_overrun(long esp, long error_code) { - die("coprocessor segment overrun",esp,error_code); + send_sig(SIGFPE, last_task_used_math, 1); + die_if_kernel("coprocessor segment overrun",esp,error_code); } void do_invalid_TSS(long esp,long error_code) { - die("invalid TSS",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("invalid TSS",esp,error_code); } void do_segment_not_present(long esp,long error_code) { - die("segment not present",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("segment not present",esp,error_code); } void do_stack_segment(long esp,long error_code) { - die("stack segment",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("stack segment",esp,error_code); } void do_coprocessor_error(long esp, long error_code) { - if (last_task_used_math != current) - return; - die("coprocessor error",esp,error_code); + send_sig(SIGFPE, last_task_used_math, 1); + __asm__("fnclex"); } void do_reserved(long esp, long error_code) { - die("reserved (15,17-47) error",esp,error_code); + send_sig(SIGSEGV, current, 1); + die_if_kernel("reserved (15,17-47) error",esp,error_code); } void trap_init(void) @@ -198,8 +202,4 @@ void trap_init(void) set_trap_gate(17,&alignment_check); for (i=18;i<48;i++) set_trap_gate(i,&reserved); - set_trap_gate(45,&irq13); - outb_p(inb_p(0x21)&0xfb,0x21); - outb(inb_p(0xA1)&0xdf,0xA1); - set_trap_gate(39,¶llel_interrupt); } diff --git a/kernel/vsprintf.c b/kernel/vsprintf.c index 8ce9873fa1c5..3613e3817a0e 100644 --- a/kernel/vsprintf.c +++ b/kernel/vsprintf.c @@ -1,7 +1,7 @@ /* * linux/kernel/vsprintf.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ @@ -10,6 +10,7 @@ */ #include +#include #include /* we use this so that we can do without the ctype library */ diff --git a/lib/Makefile b/lib/Makefile index 78afc1ea569f..b86a6d492be7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,21 +6,12 @@ # unless it's something special (ie not a .c file). # -AR =ar -AS =as -LD =ld -LDFLAGS =-s -x -CC =gcc -nostdinc -I../include -CPP =gcc -E -nostdinc -I../include - .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) -S $< .s.o: $(AS) -c -o $*.o $< .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) -c $< OBJS = ctype.o _exit.o open.o close.o errno.o write.o dup.o setsid.o \ execve.o wait.o string.o malloc.o @@ -35,38 +26,24 @@ clean: dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ - $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile ### Dependencies: -_exit.s _exit.o : _exit.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h -close.s close.o : close.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h -ctype.s ctype.o : ctype.c ../include/linux/ctype.h -dup.s dup.o : dup.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h -errno.s errno.o : errno.c -execve.s execve.o : execve.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h -malloc.s malloc.o : malloc.c ../include/linux/kernel.h ../include/linux/mm.h ../include/linux/fs.h \ - ../include/sys/types.h ../include/sys/dirent.h ../include/limits.h ../include/signal.h \ - ../include/asm/system.h -open.s open.o : open.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/stdarg.h -setsid.s setsid.o : setsid.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h -string.s string.o : string.c ../include/linux/string.h -wait.s wait.o : wait.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h ../include/sys/wait.h -write.s write.o : write.c ../include/unistd.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/sys/time.h ../include/time.h ../include/sys/times.h ../include/sys/utsname.h \ - ../include/sys/param.h ../include/sys/resource.h ../include/utime.h +_exit.o : _exit.c /usr/src/linux/include/linux/unistd.h +close.o : close.c /usr/src/linux/include/linux/unistd.h +ctype.o : ctype.c /usr/src/linux/include/linux/ctype.h +dup.o : dup.c /usr/src/linux/include/linux/unistd.h +errno.o : errno.c +execve.o : execve.c /usr/src/linux/include/linux/unistd.h +malloc.o : malloc.c /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/mm.h \ + /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h \ + /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h \ + /usr/src/linux/include/linux/minix_fs_sb.h /usr/src/linux/include/linux/ext_fs_sb.h \ + /usr/src/linux/include/linux/msdos_fs_sb.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/asm/system.h +open.o : open.c /usr/src/linux/include/linux/unistd.h /usr/src/linux/include/stdarg.h +setsid.o : setsid.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/unistd.h +string.o : string.c /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/string.h +wait.o : wait.c /usr/src/linux/include/linux/unistd.h /usr/src/linux/include/linux/types.h +write.o : write.c /usr/src/linux/include/linux/unistd.h /usr/src/linux/include/linux/types.h diff --git a/lib/_exit.c b/lib/_exit.c index ac48d1aaf7b9..b0b77e75794a 100644 --- a/lib/_exit.c +++ b/lib/_exit.c @@ -1,14 +1,16 @@ /* * linux/lib/_exit.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include volatile void _exit(int exit_code) { +fake_volatile: __asm__("movl %1,%%ebx\n\t" "int $0x80"::"a" (__NR_exit),"g" (exit_code)); + goto fake_volatile; } diff --git a/lib/close.c b/lib/close.c index afd8364f6bec..2c3f4363e8ee 100644 --- a/lib/close.c +++ b/lib/close.c @@ -1,10 +1,11 @@ /* * linux/lib/close.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include _syscall1(int,close,int,fd) + diff --git a/lib/ctype.c b/lib/ctype.c index f1103a9fd4c8..d3613be6ffd9 100644 --- a/lib/ctype.c +++ b/lib/ctype.c @@ -1,7 +1,7 @@ /* * linux/lib/ctype.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include diff --git a/lib/dup.c b/lib/dup.c index dd13414adf60..6d5ed119ec38 100644 --- a/lib/dup.c +++ b/lib/dup.c @@ -1,10 +1,11 @@ /* * linux/lib/dup.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include _syscall1(int,dup,int,fd) + diff --git a/lib/errno.c b/lib/errno.c index 50aca2eebcd5..41cb9d76c052 100644 --- a/lib/errno.c +++ b/lib/errno.c @@ -1,7 +1,7 @@ /* * linux/lib/errno.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ int errno; diff --git a/lib/execve.c b/lib/execve.c index a89726dec770..32a93f0f1b87 100644 --- a/lib/execve.c +++ b/lib/execve.c @@ -1,10 +1,11 @@ /* * linux/lib/execve.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include _syscall3(int,execve,const char *,file,char **,argv,char **,envp) + diff --git a/lib/malloc.c b/lib/malloc.c index 42723a76d6a2..e84168983a69 100644 --- a/lib/malloc.c +++ b/lib/malloc.c @@ -99,7 +99,7 @@ static inline void init_bucket_desc() struct bucket_desc *bdesc, *first; int i; - first = bdesc = (struct bucket_desc *) get_free_page(); + first = bdesc = (struct bucket_desc *) get_free_page(GFP_KERNEL); if (!bdesc) panic("Out of memory in init_bucket_desc()"); for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) { @@ -153,7 +153,7 @@ void *malloc(unsigned int len) free_bucket_desc = bdesc->next; bdesc->refcnt = 0; bdesc->bucket_size = bdir->size; - bdesc->page = bdesc->freeptr = (void *) cp = get_free_page(); + bdesc->page = bdesc->freeptr = (void *) cp = get_free_page(GFP_KERNEL); if (!cp) panic("Out of memory in kernel malloc()"); /* Set up the chain of free objects */ diff --git a/lib/open.c b/lib/open.c index 59728071612b..b69d2b548630 100644 --- a/lib/open.c +++ b/lib/open.c @@ -1,11 +1,11 @@ /* * linux/lib/open.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include #include int open(const char * filename, int flag, ...) diff --git a/lib/setsid.c b/lib/setsid.c index 68516c7e579d..c157b7135a82 100644 --- a/lib/setsid.c +++ b/lib/setsid.c @@ -1,10 +1,12 @@ /* * linux/lib/setsid.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include +#include _syscall0(pid_t,setsid) + diff --git a/lib/string.c b/lib/string.c index ebbdffff404c..4f4722f771fb 100644 --- a/lib/string.c +++ b/lib/string.c @@ -1,13 +1,15 @@ /* * linux/lib/string.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #ifndef __GNUC__ #error I want gcc! #endif +#include + #define extern #define inline #define __LIBRARY__ diff --git a/lib/wait.c b/lib/wait.c index 2815c16aeeb5..727a2716231b 100644 --- a/lib/wait.c +++ b/lib/wait.c @@ -1,12 +1,12 @@ /* * linux/lib/wait.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include -#include +#include +#include _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) @@ -14,3 +14,4 @@ pid_t wait(int * wait_stat) { return waitpid(-1,wait_stat,0); } + diff --git a/lib/write.c b/lib/write.c index df52e74d6ede..9336ed9d82c7 100644 --- a/lib/write.c +++ b/lib/write.c @@ -1,10 +1,12 @@ /* * linux/lib/write.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #define __LIBRARY__ -#include +#include +#include _syscall3(int,write,int,fd,const char *,buf,off_t,count) + diff --git a/mm/Makefile b/mm/Makefile index c60e25bc8284..9523711e6a12 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -7,20 +7,12 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -AS =as -AR =ar -LD =ld -CC =gcc -nostdinc -I../include -CPP =cpp -nostdinc -I../include - .c.o: - $(CC) $(CFLAGS) \ - -c -o $*.o $< + $(CC) $(CFLAGS) -c $< .s.o: $(AS) -o $*.o $< .c.s: - $(CC) $(CFLAGS) \ - -S -o $*.s $< + $(CC) $(CFLAGS) -S $< OBJS = memory.o swap.o mmap.o @@ -33,21 +25,32 @@ clean: dep: sed '/\#\#\# Dependencies/q' < Makefile > tmp_make - (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make cp tmp_make Makefile ### Dependencies: -memory.o : memory.c ../include/signal.h ../include/sys/types.h ../include/asm/system.h \ - ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h \ - ../include/limits.h ../include/linux/mm.h ../include/linux/kernel.h ../include/sys/param.h \ - ../include/sys/time.h ../include/time.h ../include/sys/resource.h -mmap.o : mmap.c ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/linux/fs.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \ - ../include/sys/time.h ../include/time.h ../include/sys/resource.h ../include/asm/segment.h \ - ../include/asm/system.h ../include/errno.h ../include/sys/mman.h -swap.o : swap.c ../include/errno.h ../include/sys/stat.h ../include/sys/types.h \ - ../include/linux/mm.h ../include/linux/fs.h ../include/sys/dirent.h ../include/limits.h \ - ../include/linux/kernel.h ../include/signal.h ../include/linux/string.h ../include/linux/sched.h \ - ../include/linux/head.h ../include/sys/param.h ../include/sys/time.h ../include/time.h \ - ../include/sys/resource.h +memory.o : memory.c /usr/src/linux/include/asm/system.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/string.h +mmap.o : mmap.c /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/time.h /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/asm/segment.h /usr/src/linux/include/asm/system.h \ + /usr/src/linux/include/sys/mman.h +swap.o : swap.c /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/signal.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/stat.h diff --git a/mm/memory.c b/mm/memory.c index 866a5cb64ce5..209641e422a6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1,7 +1,7 @@ /* * linux/mm/memory.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -28,43 +28,59 @@ * 20.12.91 - Ok, making the swap-device changeable like the root. */ -#include - #include +#include #include #include #include +#include #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \ current->start_code + current->end_code) -unsigned long HIGH_MEMORY = 0; +unsigned long low_memory = 0; +unsigned long high_memory = 0; +unsigned long paging_pages = 0; #define copy_page(from,to) \ __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si") -#define CHECK_LAST_NR 16 - -static unsigned long last_pages[CHECK_LAST_NR] = { 0, }; +unsigned char * mem_map = NULL; -unsigned char mem_map [ PAGING_PAGES ] = {0,}; +/* + * oom() prints a message (so that the user knows why the process died), + * and gives the process an untrappable SIGSEGV. + */ +void oom(struct task_struct * task) +{ + printk("\nout of memory\n"); + task->sigaction[SIGSEGV-1].sa_handler = NULL; + task->blocked &= ~(1<<(SIGSEGV-1)); + send_sig(SIGSEGV,task,1); +} +int nr_free_pages = 0; /* * Free a page of memory at physical address 'addr'. Used by * 'free_page_tables()' */ void free_page(unsigned long addr) { - if (addr < LOW_MEM) return; - if (addr < HIGH_MEMORY) { - addr -= LOW_MEM; - addr >>= 12; - if (mem_map[addr]--) + unsigned long i; + + if (addr < low_memory) + return; + if (addr < high_memory) { + i = addr - low_memory; + i >>= 12; + if (mem_map[i] == 1) + ++nr_free_pages; + if (mem_map[i]--) return; - mem_map[addr]=0; + mem_map[i] = 0; } - printk("trying to free free page: memory probably corrupted"); + printk("trying to free free page (%08x): memory probably corrupted\n",addr); } /* @@ -105,8 +121,6 @@ int free_page_tables(unsigned long from,unsigned long size) free_page(0xfffff000 & page_dir); } invalidate(); - for (page = 0; page < CHECK_LAST_NR ; page++) - last_pages[page] = 0; return 0; } @@ -154,30 +168,36 @@ int copy_page_tables(unsigned long from,unsigned long to,long size) continue; } from_page_table = (unsigned long *) (0xfffff000 & *from_dir); - if (!(to_page_table = (unsigned long *) get_free_page())) + if (!(to_page_table = (unsigned long *) get_free_page(GFP_KERNEL))) return -1; /* Out of memory, see freeing */ *to_dir = ((unsigned long) to_page_table) | 7; nr = (from==0)?0xA0:1024; for ( ; nr-- > 0 ; from_page_table++,to_page_table++) { +repeat: this_page = *from_page_table; if (!this_page) continue; if (!(1 & this_page)) { - if (!(new_page = get_free_page())) + if (!(new_page = get_free_page(GFP_KERNEL))) return -1; ++current->rss; read_swap_page(this_page>>1, (char *) new_page); + if (*from_page_table != this_page) { + free_page(new_page); + goto repeat; + } *to_page_table = this_page; *from_page_table = new_page | (PAGE_DIRTY | 7); continue; } this_page &= ~2; *to_page_table = this_page; - if (this_page > LOW_MEM) { + if (this_page > low_memory) { *from_page_table = this_page; - this_page -= LOW_MEM; + this_page -= low_memory; this_page >>= 12; - mem_map[this_page]++; + if (!mem_map[this_page]++) + --nr_free_pages; } } } @@ -189,8 +209,7 @@ int copy_page_tables(unsigned long from,unsigned long to,long size) * a more complete version of free_page_tables which performs with page * granularity. */ -int -unmap_page_range(unsigned long from, unsigned long size) +int unmap_page_range(unsigned long from, unsigned long size) { unsigned long page, page_dir; unsigned long *page_table, *dir; @@ -237,8 +256,6 @@ unmap_page_range(unsigned long from, unsigned long size) } } invalidate(); - for (page = 0; page < CHECK_LAST_NR ; page++) - last_pages[page] = 0; return 0; } @@ -256,12 +273,12 @@ unmap_page_range(unsigned long from, unsigned long size) * write/copy: yes/copy copy/copy * exec: yes yes */ -int -remap_page_range(unsigned long from, unsigned long to, unsigned long size, +int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int permiss) { unsigned long *page_table, *dir; unsigned long poff, pcnt; + unsigned long page; if ((from & 0xfff) || (to & 0xfff)) panic("remap_page_range called with wrong alignment"); @@ -273,7 +290,7 @@ remap_page_range(unsigned long from, unsigned long to, unsigned long size, while (size > 0) { if (!(1 & *dir)) { - if (!(page_table = (unsigned long *)get_free_page())) { + if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) { invalidate(); return -1; } @@ -301,12 +318,13 @@ remap_page_range(unsigned long from, unsigned long to, unsigned long size, if (permiss & 4) mask |= 1; - if (*page_table) { + if (page = *page_table) { + *page_table = 0; --current->rss; - if (1 & *page_table) - free_page(0xfffff000 & *page_table); + if (1 & page) + free_page(0xfffff000 & page); else - swap_free(*page_table >> 1); + swap_free(page >> 1); } /* @@ -316,16 +334,17 @@ remap_page_range(unsigned long from, unsigned long to, unsigned long size, * when the page is referenced. current assumptions * cause it to be treated as demand allocation. */ - if (mask == 4 || to >= HIGH_MEMORY) + if (mask == 4 || to >= high_memory) *page_table++ = 0; /* not present */ else { ++current->rss; *page_table++ = (to | mask); - if (to > LOW_MEM) { + if (to > low_memory) { unsigned long frame; - frame = to - LOW_MEM; + frame = to - low_memory; frame >>= 12; - mem_map[frame]++; + if (!mem_map[frame]++) + --nr_free_pages; } } to += PAGE_SIZE; @@ -333,8 +352,6 @@ remap_page_range(unsigned long from, unsigned long to, unsigned long size, pcnt = (size > 1024 ? 1024 : size); } invalidate(); - for (to = 0; to < CHECK_LAST_NR ; to++) - last_pages[to] = 0; return 0; } @@ -350,22 +367,25 @@ static unsigned long put_page(unsigned long page,unsigned long address) /* NOTE !!! This uses the fact that _pg_dir=0 */ - if (page < LOW_MEM || page >= HIGH_MEMORY) { + if (page >= high_memory) { printk("put_page: trying to put page %p at %p\n",page,address); return 0; } - if (mem_map[(page-LOW_MEM)>>12] != 1) { - printk("mem_map disagrees with %p at %p\n",page,address); + if (page >= low_memory && mem_map[(page-low_memory)>>12] != 1) { + printk("put_page: mem_map disagrees with %p at %p\n",page,address); return 0; } page_table = (unsigned long *) ((address>>20) & 0xffc); if ((*page_table)&1) page_table = (unsigned long *) (0xfffff000 & *page_table); else { - if (!(tmp=get_free_page())) - return 0; + tmp = get_free_page(GFP_KERNEL); + if (!tmp) { + oom(current); + tmp = BAD_PAGETABLE; + } *page_table = tmp | 7; - page_table = (unsigned long *) tmp; + return 0; } page_table += (address>>12) & 0x3ff; if (*page_table) { @@ -390,15 +410,15 @@ unsigned long put_dirty_page(unsigned long page, unsigned long address) /* NOTE !!! This uses the fact that _pg_dir=0 */ - if (page < LOW_MEM || page >= HIGH_MEMORY) + if (page < low_memory || page >= high_memory) printk("put_dirty_page: trying to put page %p at %p\n",page,address); - if (mem_map[(page-LOW_MEM)>>12] != 1) + if (mem_map[(page-low_memory)>>12] != 1) printk("mem_map disagrees with %p at %p\n",page,address); page_table = (unsigned long *) ((address>>20) & 0xffc); if ((*page_table)&1) page_table = (unsigned long *) (0xfffff000 & *page_table); else { - if (!(tmp=get_free_page())) + if (!(tmp=get_free_page(GFP_KERNEL))) return 0; *page_table = tmp|7; page_table = (unsigned long *) tmp; @@ -414,7 +434,7 @@ unsigned long put_dirty_page(unsigned long page, unsigned long address) return page; } -void un_wp_page(unsigned long * table_entry) +static void un_wp_page(unsigned long * table_entry, struct task_struct * task) { unsigned long old_page; unsigned long new_page = 0; @@ -422,32 +442,36 @@ void un_wp_page(unsigned long * table_entry) repeat: old_page = *table_entry; - dirty = old_page & PAGE_DIRTY; if (!(old_page & 1)) { if (new_page) free_page(new_page); return; } + dirty = old_page & PAGE_DIRTY; old_page &= 0xfffff000; - if (old_page >= HIGH_MEMORY) { + if (old_page >= high_memory) { if (new_page) free_page(new_page); printk("bad page address\n\r"); - do_exit(SIGSEGV); + send_sig(SIGSEGV, task, 1); + *table_entry = BAD_PAGE | 7; + return; } - if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) { + if (old_page >= low_memory && mem_map[MAP_NR(old_page)]==1) { *table_entry |= 2; invalidate(); if (new_page) free_page(new_page); return; } - if (!new_page) { - if (!(new_page=get_free_page())) - oom(); + if (!new_page && (new_page=get_free_page(GFP_KERNEL))) goto repeat; + if (new_page) + copy_page(old_page,new_page); + else { + new_page = BAD_PAGE; + send_sig(SIGSEGV,task,1); } - copy_page(old_page,new_page); *table_entry = new_page | dirty | 7; free_page(old_page); invalidate(); @@ -460,43 +484,63 @@ repeat: * * If it's in code space we exit with a segment error. */ -void do_wp_page(unsigned long error_code,unsigned long address) +void do_wp_page(unsigned long error_code, unsigned long address, + struct task_struct * tsk, unsigned long user_esp) { + unsigned long pde, pte, page; + + pde = (address>>20) & 0xffc; + pte = *(unsigned long *) pde; + if ((pte & 3) != 3) { + printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte); + *(unsigned long *) pde = BAD_PAGETABLE | 7; + send_sig(SIGSEGV, tsk, 1); + return; + } if (address < TASK_SIZE) { - printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r"); - do_exit(SIGSEGV); + printk("do_wp_page: kernel WP error at address %08x (%08x)\n",address,pte); + *(unsigned long *) pde = BAD_PAGETABLE | 7; + send_sig(SIGSEGV, tsk, 1); + return; } - if (address - current->start_code >= TASK_SIZE) { - printk("Bad things happen: page error in do_wp_page\n\r"); - do_exit(SIGSEGV); + pte &= 0xfffff000; + pte += (address>>10) & 0xffc; + page = *(unsigned long *) pte; + if ((page & 3) != 1) { + printk("do_wp_page: bogus page at address %08x (%08x)\n",address,page); + *(unsigned long *) pte = BAD_PAGE | 7; + send_sig(SIGSEGV, tsk, 1); + return; } ++current->min_flt; - un_wp_page((unsigned long *) - (((address>>10) & 0xffc) + (0xfffff000 & - *((unsigned long *) ((address>>20) &0xffc))))); + un_wp_page((unsigned long *) pte, tsk); } void write_verify(unsigned long address) { unsigned long page; - if (!( (page = *((unsigned long *) ((address>>20) & 0xffc)) )&1)) + page = *(unsigned long *) ((address>>20) & 0xffc); + if (!(page & PAGE_PRESENT)) return; page &= 0xfffff000; page += ((address>>10) & 0xffc); if ((3 & *(unsigned long *) page) == 1) /* non-writeable, present */ - un_wp_page((unsigned long *) page); + un_wp_page((unsigned long *) page, current); return; } -void get_empty_page(unsigned long address) +static void get_empty_page(unsigned long address) { unsigned long tmp; - if (!(tmp=get_free_page()) || !put_page(tmp,address)) { - free_page(tmp); /* 0 is ok - ignored */ - oom(); + tmp = get_free_page(GFP_KERNEL); + if (!tmp) { + oom(current); + tmp = BAD_PAGE; } + if (!put_page(tmp,address)) + free_page(tmp); } /* @@ -529,14 +573,14 @@ static int try_to_share(unsigned long address, struct task_struct * p) if ((phys_addr & 0x41) != 0x01) return 0; phys_addr &= 0xfffff000; - if (phys_addr >= HIGH_MEMORY || phys_addr < LOW_MEM) + if (phys_addr >= high_memory || phys_addr < low_memory) return 0; to = *(unsigned long *) to_page; if (!(to & 1)) { - if (to = get_free_page()) - *(unsigned long *) to_page = to | 7; - else - oom(); + to = get_free_page(GFP_KERNEL); + if (!to) + return 0; + *(unsigned long *) to_page = to | 7; } to &= 0xfffff000; to_page = to + ((address>>10) & 0xffc); @@ -546,9 +590,10 @@ static int try_to_share(unsigned long address, struct task_struct * p) *(unsigned long *) from_page &= ~2; *(unsigned long *) to_page = *(unsigned long *) from_page; invalidate(); - phys_addr -= LOW_MEM; + phys_addr -= low_memory; phys_addr >>= 12; - mem_map[phys_addr]++; + if (!mem_map[phys_addr]++) + --nr_free_pages; return 1; } @@ -565,7 +610,7 @@ static int share_page(struct inode * inode, unsigned long address) struct task_struct ** p; int i; - if (inode->i_count < 2 || !inode) + if (!inode || inode->i_count < 2) return 0; for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!*p) @@ -588,26 +633,42 @@ static int share_page(struct inode * inode, unsigned long address) return 0; } +/* + * fill in an empty page-table if none exists + */ +static unsigned long get_empty_pgtable(unsigned long * p) +{ + unsigned long page = 0; + +repeat: + if (1 & *p) { + free_page(page); + return *p; + } + if (*p) { + printk("get_empty_pgtable: bad page-directory entry \n"); + *p = 0; + } + if (page) { + *p = page | 7; + return *p; + } + if (page = get_free_page(GFP_KERNEL)) + goto repeat; + oom(current); + *p = BAD_PAGETABLE | 7; + return 0; +} + void do_no_page(unsigned long error_code, unsigned long address, - struct task_struct *tsk) + struct task_struct *tsk, unsigned long user_esp) { - static unsigned int last_checked = 0; int nr[4]; unsigned long tmp; unsigned long page; - int block,i; + unsigned int block,i; struct inode * inode; - /* Thrashing ? Make it interruptible, but don't penalize otherwise */ - for (i = 0; i < CHECK_LAST_NR; i++) - if ((address & 0xfffff000) == last_pages[i]) { - current->counter = 0; - schedule(); - } - last_checked++; - if (last_checked >= CHECK_LAST_NR) - last_checked = 0; - last_pages[last_checked] = address & 0xfffff000; if (address < TASK_SIZE) { printk("\n\rBAD!! KERNEL PAGE MISSING\n\r"); do_exit(SIGSEGV); @@ -616,54 +677,52 @@ void do_no_page(unsigned long error_code, unsigned long address, printk("Bad things happen: nonexistent page error in do_no_page\n\r"); do_exit(SIGSEGV); } + page = get_empty_pgtable((unsigned long *) ((address >> 20) & 0xffc)); + if (!page) + return; + page &= 0xfffff000; + page += (address >> 10) & 0xffc; + tmp = *(unsigned long *) page; + if (tmp & 1) { + printk("bogus do_no_page\n"); + return; + } ++tsk->rss; - page = *(unsigned long *) ((address >> 20) & 0xffc); -/* check the page directory: make a page dir entry if no such exists */ - if (page & 1) { - page &= 0xfffff000; - page += (address >> 10) & 0xffc; - tmp = *(unsigned long *) page; - if (tmp && !(1 & tmp)) { - ++tsk->maj_flt; - swap_in((unsigned long *) page); - return; - } - } else { - if (page) - printk("do_no_page: bad page directory\n"); - if (!(page = get_free_page())) - oom(); - page |= 7; - *(unsigned long *) ((address >> 20) & 0xffc) = page; + if (tmp) { + ++tsk->maj_flt; + swap_in((unsigned long *) page); + return; } address &= 0xfffff000; tmp = address - tsk->start_code; - if (tmp >= LIBRARY_OFFSET ) { - inode = NULL; - block = 1; - i = tsk->numlibraries; - while (i-- > 0) { - if (tmp < (tsk->libraries[i].start + - tsk->libraries[i].length)) { - inode = tsk->libraries[i].library; - block = 1 + (tmp - tsk->libraries[i].start) / - BLOCK_SIZE; - break; - } - } - } else if (tmp < tsk->end_data) { + inode = NULL; + block = 0; + if (tmp < tsk->end_data) { inode = tsk->executable; block = 1 + tmp / BLOCK_SIZE; } else { - inode = NULL; - block = 0; + i = tsk->numlibraries; + while (i-- > 0) { + if (tmp < tsk->libraries[i].start) + continue; + block = tmp - tsk->libraries[i].start; + if (block >= tsk->libraries[i].length) + continue; + inode = tsk->libraries[i].library; + block = 1 + block / BLOCK_SIZE; + break; + } } if (!inode) { ++tsk->min_flt; - if (tmp < LIBRARY_OFFSET && tmp > tsk->brk && tsk == current && - LIBRARY_OFFSET - tmp > tsk->rlim[RLIMIT_STACK].rlim_max) - do_exit(SIGSEGV); get_empty_page(address); + if (tsk != current) + return; + if (tmp >= LIBRARY_OFFSET || tmp < tsk->brk) + return; + if (tmp+8192 >= (user_esp & 0xfffff000)) + return; + send_sig(SIGSEGV,tsk,1); return; } if (tsk == current) @@ -672,9 +731,12 @@ void do_no_page(unsigned long error_code, unsigned long address, return; } ++tsk->maj_flt; - if (!(page = get_free_page())) - oom(); -/* remember that 1 block is used for header */ + page = get_free_page(GFP_KERNEL); + if (!page) { + oom(current); + put_page(BAD_PAGE,address); + return; + } for (i=0 ; i<4 ; block++,i++) nr[i] = bmap(inode,block); bread_page(page,inode->i_dev,nr); @@ -682,30 +744,14 @@ void do_no_page(unsigned long error_code, unsigned long address, if (i>4095) i = 0; tmp = page + 4096; - while (i-- > 0) { + while (i--) { tmp--; *(char *)tmp = 0; } if (put_page(page,address)) return; free_page(page); - oom(); -} - -void mem_init(long start_mem, long end_mem) -{ - int i; - - swap_device = 0; - swap_file = NULL; - HIGH_MEMORY = end_mem; - for (i=0 ; i>= 12; - while (end_mem-->0) - mem_map[i++]=0; + oom(current); } void show_mem(void) @@ -715,9 +761,10 @@ void show_mem(void) unsigned long * pg_tbl; printk("Mem-info:\n\r"); - for(i=0 ; iHIGH_MEMORY) { + if (pg_dir[i]>high_memory) { printk("page directory[%d]: %08X\n\r", i,pg_dir[i]); i++; continue; } - if (pg_dir[i]>LOW_MEM) + if (pg_dir[i]>low_memory) free++,k++; pg_tbl=(unsigned long *) (0xfffff000 & pg_dir[i]); for(j=0 ; j<1024 ; j++) - if ((pg_tbl[j]&1) && pg_tbl[j]>LOW_MEM) - if (pg_tbl[j]>HIGH_MEMORY) + if ((pg_tbl[j]&1) && pg_tbl[j]>low_memory) + if (pg_tbl[j]>high_memory) printk("page_dir[%d][%d]: %08X\n\r", i,j, pg_tbl[j]); else @@ -763,14 +811,37 @@ void show_mem(void) void do_page_fault(unsigned long *esp, unsigned long error_code) { unsigned long address; - /* get the address */ + unsigned long user_esp; + if ((0xffff & esp[1]) == 0xf) + user_esp = esp[3]; + else + user_esp = 0; + /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); if (!(error_code & 1)) { - do_no_page(error_code, address, current); + do_no_page(error_code, address, current, user_esp); return; } else { - do_wp_page(error_code, address); + do_wp_page(error_code, address, current, user_esp); return; } } + +unsigned long mem_init(unsigned long start_mem, unsigned long end_mem) +{ + end_mem &= 0xfffff000; + high_memory = end_mem; + mem_map = (char *) start_mem; + paging_pages = (end_mem - start_mem) >> 12; + start_mem += paging_pages; + start_mem += 0xfff; + start_mem &= 0xfffff000; + low_memory = start_mem; + paging_pages = (high_memory - low_memory) >> 12; + swap_device = 0; + swap_file = NULL; + memset(mem_map,0,paging_pages); + nr_free_pages = paging_pages; + return start_mem; +} diff --git a/mm/mmap.c b/mm/mmap.c index a8ea62805d3a..38dc07db45d1 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -3,12 +3,15 @@ * * Written by obz. */ -#include +#include #include #include +#include +#include + #include #include -#include + #include /* @@ -37,16 +40,11 @@ #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \ current->start_code + current->end_code) -extern int remap_page_range(unsigned long from, unsigned long to, - unsigned long size, int permiss); -extern int unmap_page_range(unsigned long from, unsigned long size); - static caddr_t mmap_chr(unsigned long addr, size_t len, int prot, int flags, struct inode *inode, unsigned long off) { int major, minor; - extern unsigned long HIGH_MEMORY; major = MAJOR(inode->i_rdev); minor = MINOR(inode->i_rdev); @@ -61,7 +59,7 @@ mmap_chr(unsigned long addr, size_t len, int prot, int flags, return (caddr_t)-ENODEV; /* - * we only allow mappings from address 0 to HIGH_MEMORY, since thats + * we only allow mappings from address 0 to high_memory, since thats * the range of our memory [actually this is a lie. the buffer cache * and ramdisk occupy higher memory, but the paging stuff won't * let us map to it anyway, so we break it here]. @@ -75,7 +73,7 @@ mmap_chr(unsigned long addr, size_t len, int prot, int flags, * truly useful. */ - if (len > HIGH_MEMORY || off > HIGH_MEMORY - len) /* avoid overflow */ + if (len > high_memory || off > high_memory - len) /* avoid overflow */ return (caddr_t)-ENXIO; if (remap_page_range(addr, off, len, PERMISS(flags, prot))) diff --git a/mm/swap.c b/mm/swap.c index ba4139946030..b582eb4cb6fe 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -1,7 +1,7 @@ /* * linux/mm/swap.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -9,14 +9,22 @@ * Started 18.12.91 */ -#include -#include - #include -#include #include #include #include +#include +#include +#include + +static int lowest_bit = 0; +static int highest_bit = 0; + +/* + * The following are used to make sure we don't thrash too much... + */ +#define NR_LAST_FREE_PAGES 32 +static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {0,}; #define SWAP_BITS (4096<<3) @@ -35,98 +43,118 @@ bitop(setbit,"s") bitop(clrbit,"r") static char * swap_bitmap = NULL; +static char * swap_lockmap = NULL; unsigned int swap_device = 0; struct inode * swap_file = NULL; void rw_swap_page(int rw, unsigned int nr, char * buf) { - unsigned int zones[4]; - int i; + static struct wait_queue * lock_queue = NULL; - if (swap_device) { - ll_rw_page(rw,swap_device,nr,buf); + if (!swap_lockmap) { + printk("No swap lock-map\n"); return; } - if (swap_file) { - nr <<= 2; + while (setbit(swap_lockmap,nr)) + sleep_on(&lock_queue); + if (swap_device) { + ll_rw_page(rw,swap_device,nr,buf); + } else if (swap_file) { + unsigned int zones[4]; + unsigned int block = nr << 2; + int i; + for (i = 0; i < 4; i++) - if (!(zones[i] = bmap(swap_file,nr++))) { + if (!(zones[i] = bmap(swap_file,block++))) { printk("rw_swap_page: bad swap file\n"); return; } ll_rw_swap_file(rw,swap_file->i_dev, zones,4,buf); - return; - } - printk("ll_swap_page: no swap file or device\n"); + } else + printk("re_swap_page: no swap file or device\n"); + if (!clrbit(swap_lockmap,nr)) + printk("rw_swap_page: lock already cleared\n"); + wake_up(&lock_queue); } -/* - * We never page the pages in task[0] - kernel memory. - * We page all other pages. - */ -#define FIRST_VM_PAGE (TASK_SIZE>>12) -#define LAST_VM_PAGE (1024*1024) -#define VM_PAGES (LAST_VM_PAGE - FIRST_VM_PAGE) - -static int get_swap_page(void) +static unsigned int get_swap_page(void) { - int nr; + unsigned int nr; if (!swap_bitmap) return 0; - for (nr = 1; nr < SWAP_BITS ; nr++) - if (clrbit(swap_bitmap,nr)) - return nr; + for (nr = lowest_bit; nr <= highest_bit ; nr++) + if (clrbit(swap_bitmap,nr)) { + if (nr == highest_bit) + highest_bit--; + return lowest_bit = nr; + } return 0; } -void swap_free(int swap_nr) +void swap_free(unsigned int swap_nr) { if (!swap_nr) return; - if (swap_bitmap && swap_nr < SWAP_BITS) + if (swap_bitmap && swap_nr < SWAP_BITS) { + if (swap_nr < lowest_bit) + lowest_bit = swap_nr; + if (swap_nr > highest_bit) + highest_bit = swap_nr; if (!setbit(swap_bitmap,swap_nr)) return; - printk("swap_free: swap-space bitmap bad\n"); + } + printk("swap_free: swap-space bitmap bad (bit %d)\n",swap_nr); return; } void swap_in(unsigned long *table_ptr) { - int swap_nr; + unsigned long swap_nr; unsigned long page; - if (!swap_bitmap) { - printk("Trying to swap in without swap bit-map"); - return; - } - if (1 & *table_ptr) { + swap_nr = *table_ptr; + if (1 & swap_nr) { printk("trying to swap in present page\n\r"); return; } - swap_nr = *table_ptr >> 1; if (!swap_nr) { printk("No swap page in swap_in\n\r"); return; } - if (!(page = get_free_page())) - oom(); - read_swap_page(swap_nr, (char *) page); - if (setbit(swap_bitmap,swap_nr)) - printk("swapping in multiply from same page\n\r"); + if (!swap_bitmap) { + printk("Trying to swap in without swap bit-map"); + *table_ptr = BAD_PAGE; + return; + } + page = get_free_page(GFP_KERNEL); + if (!page) { + oom(current); + page = BAD_PAGE; + } else + read_swap_page(swap_nr>>1, (char *) page); + if (*table_ptr != swap_nr) { + free_page(page); + return; + } + swap_free(swap_nr>>1); *table_ptr = page | (PAGE_DIRTY | 7); } int try_to_swap_out(unsigned long * table_ptr) { + int i; unsigned long page; unsigned long swap_nr; page = *table_ptr; if (!(PAGE_PRESENT & page)) return 0; - if (page - LOW_MEM > PAGING_MEMORY) + if (page < low_memory || page >= high_memory) return 0; + for (i = 0; i < NR_LAST_FREE_PAGES; i++) + if (last_free_pages[i] == (page & 0xfffff000)) + return 0; if (PAGE_DIRTY & page) { page &= 0xfffff000; if (mem_map[MAP_NR(page)] != 1) @@ -146,15 +174,21 @@ int try_to_swap_out(unsigned long * table_ptr) return 1; } +/* + * We never page the pages in task[0] - kernel memory. + * We page all other pages. + */ +#define FIRST_VM_PAGE (TASK_SIZE>>12) +#define LAST_VM_PAGE (1024*1024) +#define VM_PAGES (LAST_VM_PAGE - FIRST_VM_PAGE) + /* * Go through the page tables, searching for a user page that * we can swap out. * - * Here it's easy to add a check for tasks that may not be swapped out: - * loadable device drivers or similar. Just add an entry to the task-struct - * and check it at the same time you check for the existence of the task. - * The code assumes tasks are page-table aligned, but so do other parts - * of the memory manager... + * We now check that the process is swappable (normally only 'init' + * is un-swappable), allowing high-priority processes which cannot be + * swapped out (things like user-level device drivers (Not implemented)). */ int swap_out(void) { @@ -195,8 +229,9 @@ check_table: dir_entry++; goto check_dir; } - if (try_to_swap_out(page_entry + (unsigned long *) pg_table)) { + if (p->swappable && try_to_swap_out(page_entry + (unsigned long *) pg_table)) { p->rss--; + dir_entry++; return 1; } goto check_table; @@ -209,9 +244,10 @@ no_swap: * Get physical address of first (actually last :-) free page, and mark it * used. If no free pages left, return 0. */ -unsigned long get_free_page(void) +unsigned long get_free_page(int priority) { unsigned long result; + static unsigned long index = 0; repeat: __asm__("std ; repne ; scasb\n\t" @@ -226,18 +262,39 @@ repeat: "movl %%edx,%%eax\n" "1:\tcld" :"=a" (result) - :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), - "D" (mem_map+PAGING_PAGES-1) + :"0" (0),"b" (low_memory),"c" (paging_pages), + "D" (mem_map+paging_pages-1) :"di","cx","dx"); - if (result >= HIGH_MEMORY) + if (result >= high_memory) goto repeat; - if ((result && result < LOW_MEM) || (result & 0xfff)) { + if ((result && result < low_memory) || (result & 0xfff)) { printk("weird result: %08x\n",result); result = 0; } - if (!result && swap_out()) + if (result) { + --nr_free_pages; + if (index >= NR_LAST_FREE_PAGES) + index = 0; + last_free_pages[index] = result; + index++; + return result; + } + if (nr_free_pages) { + printk("Damn. mm_free_page count is off by %d\r\n", + nr_free_pages); + nr_free_pages = 0; + } + if (priority <= GFP_BUFFER) + return 0; + if (shrink_buffers()) { + schedule(); + goto repeat; + } + if (swap_out()) { + schedule(); goto repeat; - return result; + } + return 0; } /* @@ -245,17 +302,17 @@ repeat: * * The swapon system call */ - int sys_swapon(const char * specialfile) { struct inode * swap_inode; + char * tmp; int i,j; if (!suser()) return -EPERM; if (!(swap_inode = namei(specialfile))) return -ENOENT; - if (swap_file || swap_device || swap_bitmap) { + if (swap_file || swap_device || swap_bitmap || swap_lockmap) { iput(swap_inode); return -EBUSY; } @@ -268,38 +325,54 @@ int sys_swapon(const char * specialfile) iput(swap_inode); return -EINVAL; } - swap_bitmap = (char *) get_free_page(); - if (!swap_bitmap) { + tmp = (char *) get_free_page(GFP_USER); + swap_lockmap = (char *) get_free_page(GFP_USER); + if (!tmp || !swap_lockmap) { + printk("Unable to start swapping: out of memory :-)\n"); + free_page((long) tmp); + free_page((long) swap_lockmap); iput(swap_file); swap_device = 0; swap_file = NULL; - printk("Unable to start swapping: out of memory :-)\n"); + swap_bitmap = NULL; + swap_lockmap = NULL; return -ENOMEM; } - read_swap_page(0,swap_bitmap); - if (strncmp("SWAP-SPACE",swap_bitmap+4086,10)) { + read_swap_page(0,tmp); + if (strncmp("SWAP-SPACE",tmp+4086,10)) { printk("Unable to find swap-space signature\n\r"); - free_page((long) swap_bitmap); + free_page((long) tmp); + free_page((long) swap_lockmap); iput(swap_file); swap_device = 0; swap_file = NULL; swap_bitmap = NULL; + swap_lockmap = NULL; return -EINVAL; } - memset(swap_bitmap+4086,0,10); + memset(tmp+4086,0,10); j = 0; + lowest_bit = 0; + highest_bit = 0; for (i = 1 ; i < SWAP_BITS ; i++) - if (bit(swap_bitmap,i)) + if (bit(tmp,i)) { + if (!lowest_bit) + lowest_bit = i; + highest_bit = i; j++; + } if (!j) { printk("Empty swap-file\n"); - free_page((long) swap_bitmap); + free_page((long) tmp); + free_page((long) swap_lockmap); iput(swap_file); swap_device = 0; swap_file = NULL; swap_bitmap = NULL; + swap_lockmap = NULL; return -EINVAL; } + swap_bitmap = tmp; printk("Adding Swap: %d pages (%d bytes) swap-space\n\r",j,j*4096); return 0; } diff --git a/net/Makefile b/net/Makefile new file mode 100644 index 000000000000..daada7072d9c --- /dev/null +++ b/net/Makefile @@ -0,0 +1,52 @@ +# +# Makefile for the linux networking. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< +.c.s: + $(CC) $(CFLAGS) -S $< + +OBJS = socket.o unix.o + +net.o: $(OBJS) + $(LD) -r -o net.o $(OBJS) + +clean: + rm -f core *.o *.a tmp_make + for i in *.c;do rm -f `basename $$i .c`.s;done + +dep: + sed '/\#\#\# Dependencies/q' < Makefile > tmp_make + for i in *.c;do $(CPP) -M $$i;done >> tmp_make + cp tmp_make Makefile + +### Dependencies: +socket.o : socket.c /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/errno.h \ + /usr/src/linux/include/linux/sched.h /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h \ + /usr/src/linux/include/linux/limits.h /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h \ + /usr/src/linux/include/linux/dirent.h /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/stat.h /usr/src/linux/include/linux/socket.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/segment.h \ + kern_sock.h socketcall.h +unix.o : unix.c /usr/src/linux/include/linux/signal.h /usr/src/linux/include/linux/sched.h \ + /usr/src/linux/include/linux/head.h /usr/src/linux/include/linux/fs.h /usr/src/linux/include/linux/limits.h \ + /usr/src/linux/include/linux/wait.h /usr/src/linux/include/linux/types.h /usr/src/linux/include/linux/dirent.h \ + /usr/src/linux/include/linux/vfs.h /usr/src/linux/include/linux/minix_fs_sb.h \ + /usr/src/linux/include/linux/ext_fs_sb.h /usr/src/linux/include/linux/msdos_fs_sb.h \ + /usr/src/linux/include/linux/mm.h /usr/src/linux/include/linux/kernel.h /usr/src/linux/include/linux/time.h \ + /usr/src/linux/include/linux/param.h /usr/src/linux/include/linux/resource.h \ + /usr/src/linux/include/linux/errno.h /usr/src/linux/include/linux/string.h /usr/src/linux/include/linux/stat.h \ + /usr/src/linux/include/linux/socket.h /usr/src/linux/include/linux/un.h /usr/src/linux/include/linux/fcntl.h \ + /usr/src/linux/include/linux/termios.h /usr/src/linux/include/asm/system.h /usr/src/linux/include/asm/segment.h \ + kern_sock.h diff --git a/net/kern_sock.h b/net/kern_sock.h new file mode 100644 index 000000000000..ccbd5e49f87b --- /dev/null +++ b/net/kern_sock.h @@ -0,0 +1,67 @@ +#ifndef _KERN_SOCK_H +#define _KERN_SOCK_H + +#define NSOCKETS 128 /* should be dynamic, later... */ + +typedef enum { + SS_FREE = 0, /* not allocated */ + SS_UNCONNECTED, /* unconnected to any socket */ + SS_CONNECTING, /* in process of connecting */ + SS_CONNECTED, /* connected to socket */ + SS_DISCONNECTING, /* in process of disconnecting */ +} socket_state; + +#define SO_ACCEPTCON (1<<16) /* performed a listen */ + +/* + * internel representation of a socket. not all the fields are used by + * all configurations: + * + * server client + * conn client connected to server connected to + * iconn list of clients -unused- + * awaiting connections + * wait sleep for clients, sleep for connection, + * sleep for i/o sleep for i/o + */ +struct socket { + short type; /* SOCK_STREAM, ... */ + socket_state state; + long flags; + struct proto_ops *ops; /* protocols do most everything */ + char *data; /* protocol data */ + struct socket *conn; /* server socket connected to */ + struct socket *iconn; /* incomplete client connections */ + struct socket *next; + struct wait_queue **wait; /* ptr to place to wait on */ + void *dummy; +}; + +struct proto_ops { + int (*init)(void); + int (*create)(struct socket *sock, int protocol); + int (*dup)(struct socket *newsock, struct socket *oldsock); + int (*release)(struct socket *sock, struct socket *peer); + int (*bind)(struct socket *sock, struct sockaddr *umyaddr, + int sockaddr_len); + int (*connect)(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len); + int (*socketpair)(struct socket *sock1, struct socket *sock2); + int (*accept)(struct socket *sock, struct socket *newsock); + int (*getname)(struct socket *sock, struct sockaddr *uaddr, + int *usockaddr_len, int peer); + int (*read)(struct socket *sock, char *ubuf, int size, int nonblock); + int (*write)(struct socket *sock, char *ubuf, int size, int nonblock); + int (*select)(struct socket *sock, int sel_type, select_table * wait); + int (*ioctl)(struct socket *sock, unsigned int cmd, unsigned long arg); +}; + +extern int sock_awaitconn(struct socket *mysock, struct socket *servsock); + +#ifdef SOCK_DEBUG +#define PRINTK printk +#else +#define PRINTK (void) +#endif + +#endif /* _KERN_SOCK_H */ diff --git a/net/socket.c b/net/socket.c new file mode 100644 index 000000000000..ffea8a2f4085 --- /dev/null +++ b/net/socket.c @@ -0,0 +1,766 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kern_sock.h" +#include "socketcall.h" + +extern int sys_close(int fd); + +extern struct proto_ops unix_proto_ops; + +static struct { + short family; + char *name; + struct proto_ops *ops; +} proto_table[] = { + AF_UNIX, "AF_UNIX", &unix_proto_ops +}; +#define NPROTO (sizeof(proto_table) / sizeof(proto_table[0])) + +static char * +family_name(int family) +{ + int i; + + for (i = 0; i < NPROTO; ++i) + if (proto_table[i].family == family) + return proto_table[i].name; + return "UNKNOWN"; +} + +static int sock_lseek(struct inode *inode, struct file *file, off_t offset, + int whence); +static int sock_read(struct inode *inode, struct file *file, char *buf, + int size); +static int sock_write(struct inode *inode, struct file *file, char *buf, + int size); +static int sock_readdir(struct inode *inode, struct file *file, + struct dirent *dirent, int count); +static void sock_close(struct inode *inode, struct file *file); +static int sock_select(struct inode *inode, struct file *file, int which, select_table *seltable); +static int sock_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned int arg); + +static struct file_operations socket_file_ops = { + sock_lseek, + sock_read, + sock_write, + sock_readdir, + sock_select, /* not in vfs yet */ + sock_ioctl, + NULL, /* no special open code... */ + sock_close +}; + +#define SOCK_INODE(S) ((struct inode *)(S)->dummy) + +static struct socket sockets[NSOCKETS]; +#define last_socket (sockets + NSOCKETS - 1) +static struct wait_queue *socket_wait_free = NULL; + +/* + * obtains the first available file descriptor and sets it up for use + */ +static int +get_fd(struct inode *inode) +{ + int fd, i; + struct file *file; + + /* + * find a file descriptor suitable for return to the user. + */ + for (fd = 0; fd < NR_OPEN; ++fd) + if (!current->filp[fd]) + break; + if (fd == NR_OPEN) + return -1; + current->close_on_exec &= ~(1 << fd); + for (file = file_table, i = 0; i < NR_FILE; ++i, ++file) + if (!file->f_count) + break; + if (i == NR_FILE) + return -1; + current->filp[fd] = file; + file->f_op = &socket_file_ops; + file->f_mode = 3; + file->f_flags = 0; + file->f_count = 1; + file->f_inode = inode; + file->f_pos = 0; + return fd; +} + +/* + * reverses the action of get_fd() by releasing the file. it closes the + * descriptor, but makes sure it does nothing more. called when an incomplete + * socket must be closed, along with sock_release(). + */ +static inline void +toss_fd(int fd) +{ + current->filp[fd]->f_inode = NULL; /* safe from iput */ + sys_close(fd); +} + +static inline struct socket * +socki_lookup(struct inode *inode) +{ + struct socket *sock; + + for (sock = sockets; sock <= last_socket; ++sock) + if (sock->state != SS_FREE && SOCK_INODE(sock) == inode) + return sock; + return NULL; +} + +static inline struct socket * +sockfd_lookup(int fd, struct file **pfile) +{ + struct file *file; + + if (fd < 0 || fd >= NR_OPEN || !(file = current->filp[fd])) + return NULL; + if (pfile) + *pfile = file; + return socki_lookup(file->f_inode); +} + +static struct socket * +sock_alloc(int wait) +{ + struct socket *sock; + + while (1) { + cli(); + for (sock = sockets; sock <= last_socket; ++sock) + if (sock->state == SS_FREE) { + sock->state = SS_UNCONNECTED; + sti(); + sock->flags = 0; + sock->ops = NULL; + sock->data = NULL; + sock->conn = NULL; + sock->iconn = NULL; + /* + * this really shouldn't be necessary, but + * everything else depends on inodes, so we + * grab it. + * sleeps are also done on the i_wait member + * of this inode. + * the close system call will iput this inode + * for us. + */ + if (!(SOCK_INODE(sock) = get_empty_inode())) { + printk("sock_alloc: no more inodes\n"); + sock->state = SS_FREE; + return NULL; + } + SOCK_INODE(sock)->i_mode = S_IFSOCK; + sock->wait = &SOCK_INODE(sock)->i_wait; + PRINTK("sock_alloc: socket 0x%x, inode 0x%x\n", + sock, SOCK_INODE(sock)); + return sock; + } + sti(); + if (!wait) + return NULL; + PRINTK("sock_alloc: no free sockets, sleeping...\n"); + interruptible_sleep_on(&socket_wait_free); + if (current->signal & ~current->blocked) { + PRINTK("sock_alloc: sleep was interrupted\n"); + return NULL; + } + PRINTK("sock_alloc: wakeup... trying again...\n"); + } +} + +static inline void +sock_release_peer(struct socket *peer) +{ + peer->state = SS_DISCONNECTING; + wake_up(peer->wait); +} + +static void +sock_release(struct socket *sock) +{ + int oldstate; + struct socket *peersock, *nextsock; + + PRINTK("sock_release: socket 0x%x, inode 0x%x\n", sock, + SOCK_INODE(sock)); + if ((oldstate = sock->state) != SS_UNCONNECTED) + sock->state = SS_DISCONNECTING; + /* + * wake up anyone waiting for connections + */ + for (peersock = sock->iconn; peersock; peersock = nextsock) { + nextsock = peersock->next; + sock_release_peer(peersock); + } + /* + * wake up anyone we're connected to. first, we release the + * protocol, to give it a chance to flush data, etc. + */ + peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL; + if (sock->ops) + sock->ops->release(sock, peersock); + if (peersock) + sock_release_peer(peersock); + sock->state = SS_FREE; /* this really releases us */ + wake_up(&socket_wait_free); +} + +static int +sock_lseek(struct inode *inode, struct file *file, off_t offset, int whence) +{ + PRINTK("sock_lseek: huh?\n"); + return -EBADF; +} + +static int +sock_read(struct inode *inode, struct file *file, char *ubuf, int size) +{ + struct socket *sock; + + PRINTK("sock_read: buf=0x%x, size=%d\n", ubuf, size); + if (!(sock = socki_lookup(inode))) { + printk("sock_read: can't find socket for inode!\n"); + return -EBADF; + } + if (sock->flags & SO_ACCEPTCON) + return -EINVAL; + return sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)); +} + +static int +sock_write(struct inode *inode, struct file *file, char *ubuf, int size) +{ + struct socket *sock; + + PRINTK("sock_write: buf=0x%x, size=%d\n", ubuf, size); + if (!(sock = socki_lookup(inode))) { + printk("sock_write: can't find socket for inode!\n"); + return -EBADF; + } + if (sock->flags & SO_ACCEPTCON) + return -EINVAL; + return sock->ops->write(sock, ubuf, size,(file->f_flags & O_NONBLOCK)); +} + +static int +sock_readdir(struct inode *inode, struct file *file, struct dirent *dirent, + int count) +{ + PRINTK("sock_readdir: huh?\n"); + return -EBADF; +} + +int +sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned int arg) +{ + struct socket *sock; + + PRINTK("sock_ioctl: inode=0x%x cmd=0x%x arg=%d\n", inode, cmd, arg); + if (!(sock = socki_lookup(inode))) { + printk("sock_ioctl: can't find socket for inode!\n"); + return -EBADF; + } + switch (cmd) { + case TIOCINQ: + case TIOCOUTQ: + if (sock->flags & SO_ACCEPTCON) + return -EINVAL; + break; + + default: + return -EINVAL; + } + return sock->ops->ioctl(sock, cmd, arg); +} + +static int +sock_select(struct inode *inode, struct file *file, int sel_type, select_table * wait) +{ + struct socket *sock; + + PRINTK("sock_select: inode = 0x%x, kind = %s\n", inode, + (sel_type == SEL_IN) ? "in" : + (sel_type == SEL_OUT) ? "out" : "ex"); + if (!(sock = socki_lookup(inode))) { + printk("sock_select: can't find socket for inode!\n"); + return 0; + } + + /* + * handle server sockets specially + */ + if (sock->flags & SO_ACCEPTCON) { + if (sel_type == SEL_IN) { + PRINTK("sock_select: %sconnections pending\n", + sock->iconn ? "" : "no "); + if (sock->iconn) + return 1; + select_wait(&inode->i_wait, wait); + return sock->iconn ? 1 : 0; + } + PRINTK("sock_select: nothing else for server socket\n"); + select_wait(&inode->i_wait, wait); + return 0; + } + /* + * we can't return errors to select, so its either yes or no. + */ + if (sock->ops && sock->ops->select) + return sock->ops->select(sock, sel_type, wait); + return 0; +} + +void +sock_close(struct inode *inode, struct file *file) +{ + struct socket *sock; + + PRINTK("sock_close: inode=0x%x (cnt=%d)\n", inode, inode->i_count); + /* + * it's possible the inode is NULL if we're closing an unfinished + * socket. + */ + if (!inode) + return; + if (!(sock = socki_lookup(inode))) { + printk("sock_close: can't find socket for inode!\n"); + return; + } + sock_release(sock); +} + +int +sock_awaitconn(struct socket *mysock, struct socket *servsock) +{ + struct socket *last; + + PRINTK("sock_awaitconn: trying to connect socket 0x%x to 0x%x\n", + mysock, servsock); + if (!(servsock->flags & SO_ACCEPTCON)) { + PRINTK("sock_awaitconn: server not accepting connections\n"); + return -EINVAL; + } + + /* + * put ourselves on the server's incomplete connection queue. + */ + mysock->next = NULL; + cli(); + if (!(last = servsock->iconn)) + servsock->iconn = mysock; + else { + while (last->next) + last = last->next; + last->next = mysock; + } + mysock->state = SS_CONNECTING; + mysock->conn = servsock; + sti(); + + /* + * wake up server, then await connection. server will set state to + * SS_CONNECTED if we're connected. + */ + wake_up(servsock->wait); + if (mysock->state != SS_CONNECTED) { + interruptible_sleep_on(mysock->wait); + if (mysock->state != SS_CONNECTED) { + /* + * if we're not connected we could have been + * 1) interrupted, so we need to remove ourselves + * from the server list + * 2) rejected (mysock->conn == NULL), and have + * already been removed from the list + */ + if (mysock->conn == servsock) { + cli(); + if ((last = servsock->iconn) == mysock) + servsock->iconn = mysock->next; + else { + while (last->next != mysock) + last = last->next; + last->next = mysock->next; + } + sti(); + } + return mysock->conn ? -EINTR : -EACCES; + } + } + return 0; +} + +/* + * perform the socket system call. we locate the appropriate family, then + * create a fresh socket. + */ +static int +sock_socket(int family, int type, int protocol) +{ + int i, fd; + struct socket *sock; + struct proto_ops *ops; + + PRINTK("sys_socket: family = %d (%s), type = %d, protocol = %d\n", + family, family_name(family), type, protocol); + + /* + * locate the correct protocol family + */ + for (i = 0; i < NPROTO; ++i) + if (proto_table[i].family == family) + break; + if (i == NPROTO) { + PRINTK("sys_socket: family not found\n"); + return -EINVAL; + } + ops = proto_table[i].ops; + + /* + * check that this is a type that we know how to manipulate and + * the protocol makes sense here. the family can still reject the + * protocol later. + */ + if ((type != SOCK_STREAM && + type != SOCK_DGRAM && + type != SOCK_SEQPACKET && + type != SOCK_RAW) || + protocol < 0) + return -EINVAL; + + /* + * allocate the socket and allow the family to set things up. if + * the protocol is 0, the family is instructed to select an appropriate + * default. + */ + if (!(sock = sock_alloc(1))) { + printk("sys_socket: no more sockets\n"); + return -EAGAIN; + } + sock->type = type; + sock->ops = ops; + if ((i = sock->ops->create(sock, protocol)) < 0) { + sock_release(sock); + return i; + } + + if ((fd = get_fd(SOCK_INODE(sock))) < 0) { + sock_release(sock); + return -EINVAL; + } + + return fd; +} + +static int +sock_socketpair(int family, int type, int protocol, int usockvec[2]) +{ + int fd1, fd2, i; + struct socket *sock1, *sock2; + + PRINTK("sys_socketpair: family = %d, type = %d, protocol = %d\n", + family, type, protocol); + + /* + * obtain the first socket and check if the underlying protocol + * supports the socketpair call + */ + if ((fd1 = sock_socket(family, type, protocol)) < 0) + return fd1; + sock1 = sockfd_lookup(fd1, NULL); + if (!sock1->ops->socketpair) { + sys_close(fd1); + return -EINVAL; + } + + /* + * now grab another socket and try to connect the two together + */ + if ((fd2 = sock_socket(family, type, protocol)) < 0) { + sys_close(fd1); + return -EINVAL; + } + sock2 = sockfd_lookup(fd2, NULL); + if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) { + sys_close(fd1); + sys_close(fd2); + return i; + } + sock1->conn = sock2; + sock2->conn = sock1; + sock1->state = SS_CONNECTED; + sock2->state = SS_CONNECTED; + + verify_area(usockvec, 2 * sizeof(int)); + put_fs_long(fd1, &usockvec[0]); + put_fs_long(fd2, &usockvec[1]); + + return 0; +} + +/* + * binds a name to a socket. nothing much to do here since its the + * protocol's responsibility to handle the local address + */ +static int +sock_bind(int fd, struct sockaddr *umyaddr, int addrlen) +{ + struct socket *sock; + int i; + + PRINTK("sys_bind: fd = %d\n", fd); + if (!(sock = sockfd_lookup(fd, NULL))) + return -EBADF; + if ((i = sock->ops->bind(sock, umyaddr, addrlen)) < 0) { + PRINTK("sys_bind: bind failed\n"); + return i; + } + return 0; +} + +/* + * perform a listen. basically, we allow the protocol to do anything + * necessary for a listen, and if that works, we mark the socket as + * ready for listening. + */ +static int +sock_listen(int fd, int backlog) +{ + struct socket *sock; + + PRINTK("sys_listen: fd = %d\n", fd); + if (!(sock = sockfd_lookup(fd, NULL))) + return -EBADF; + if (sock->state != SS_UNCONNECTED) { + PRINTK("sys_listen: socket isn't unconnected\n"); + return -EINVAL; + } + if (sock->flags & SO_ACCEPTCON) { + PRINTK("sys_listen: socket already accepting connections!\n"); + return -EINVAL; + } + sock->flags |= SO_ACCEPTCON; + return 0; +} + +/* + * for accept, we attempt to create a new socket, set up the link with the + * client, wake up the client, then return the new connected fd. + */ +static int +sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen) +{ + struct file *file; + struct socket *sock, *clientsock, *newsock; + int i; + + PRINTK("sys_accept: fd = %d\n", fd); + if (!(sock = sockfd_lookup(fd, &file))) + return -EBADF; + if (sock->state != SS_UNCONNECTED) { + PRINTK("sys_accept: socket isn't unconnected\n"); + return -EINVAL; + } + if (!(sock->flags & SO_ACCEPTCON)) { + PRINTK("sys_accept: socket not accepting connections!\n"); + return -EINVAL; + } + + /* + * if there aren't any sockets awaiting connection, then wait for + * one, unless nonblocking + */ + while (!(clientsock = sock->iconn)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) { + PRINTK("sys_accept: sleep was interrupted\n"); + return -ERESTARTSYS; + } + } + + if (!(newsock = sock_alloc(0))) { + printk("sys_accept: no more sockets\n"); + return -EINVAL; + } + newsock->type = sock->type; + newsock->ops = sock->ops; + if ((i = sock->ops->dup(newsock, sock)) < 0) { + sock_release(newsock); + return i; + } + + if ((fd = get_fd(SOCK_INODE(newsock))) < 0) { + sock_release(newsock); + return -EINVAL; + } + + /* + * great. finish the connection relative to server and client, + * wake up the client and return the new fd to the server + */ + sock->iconn = clientsock->next; + clientsock->next = NULL; + newsock->conn = clientsock; + clientsock->conn = newsock; + clientsock->state = SS_CONNECTED; + newsock->state = SS_CONNECTED; + newsock->ops->accept(sock, newsock); + PRINTK("sys_accept: connected socket 0x%x via 0x%x to 0x%x\n", + sock, newsock, clientsock); + if (upeer_sockaddr) + newsock->ops->getname(newsock, upeer_sockaddr, + upeer_addrlen, 1); + wake_up(clientsock->wait); + + return fd; +} + +/* + * attempt to connect to a socket with the server address. + */ +static int +sock_connect(int fd, struct sockaddr *uservaddr, int addrlen) +{ + struct socket *sock; + int i; + + PRINTK("sys_connect: fd = %d\n", fd); + if (!(sock = sockfd_lookup(fd, NULL))) + return -EBADF; + if (sock->state != SS_UNCONNECTED) { + PRINTK("sys_connect: socket not unconnected\n"); + return -EINVAL; + } + if ((i = sock->ops->connect(sock, uservaddr, addrlen)) < 0) { + PRINTK("sys_connect: connect failed\n"); + return i; + } + return 0; +} + +static int +sock_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len) +{ + struct socket *sock; + + PRINTK("sys_getsockname: fd = %d\n", fd); + if (!(sock = sockfd_lookup(fd, NULL))) + return -EBADF; + return sock->ops->getname(sock, usockaddr, usockaddr_len, 0); +} + +static int +sock_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len) +{ + struct socket *sock; + + PRINTK("sys_getpeername: fd = %d\n", fd); + if (!(sock = sockfd_lookup(fd, NULL))) + return -EBADF; + return sock->ops->getname(sock, usockaddr, usockaddr_len, 1); +} + +/* + * system call vectors. since i want to rewrite sockets as streams, we have + * this level of indirection. not a lot of overhead, since more of the work is + * done via read/write/select directly + */ +int +sys_socketcall(int call, unsigned long *args) +{ + switch (call) { + case SYS_SOCKET: + verify_area(args, 3 * sizeof(long)); + return sock_socket(get_fs_long(args+0), + get_fs_long(args+1), + get_fs_long(args+2)); + + case SYS_BIND: + verify_area(args, 3 * sizeof(long)); + return sock_bind(get_fs_long(args+0), + (struct sockaddr *)get_fs_long(args+1), + get_fs_long(args+2)); + + case SYS_CONNECT: + verify_area(args, 3 * sizeof(long)); + return sock_connect(get_fs_long(args+0), + (struct sockaddr *)get_fs_long(args+1), + get_fs_long(args+2)); + + case SYS_LISTEN: + verify_area(args, 2 * sizeof(long)); + return sock_listen(get_fs_long(args+0), + get_fs_long(args+1)); + + case SYS_ACCEPT: + verify_area(args, 3 * sizeof(long)); + return sock_accept(get_fs_long(args+0), + (struct sockaddr *)get_fs_long(args+1), + (int *)get_fs_long(args+2)); + + case SYS_GETSOCKNAME: + verify_area(args, 3 * sizeof(long)); + return sock_getsockname(get_fs_long(args+0), + (struct sockaddr *)get_fs_long(args+1), + (int *)get_fs_long(args+2)); + + case SYS_GETPEERNAME: + verify_area(args, 3 * sizeof(long)); + return sock_getpeername(get_fs_long(args+0), + (struct sockaddr *)get_fs_long(args+1), + (int *)get_fs_long(args+2)); + + case SYS_SOCKETPAIR: + verify_area(args, 4 * sizeof(long)); + return sock_socketpair(get_fs_long(args+0), + get_fs_long(args+1), + get_fs_long(args+2), + (int *)get_fs_long(args+3)); + + default: + return -EINVAL; + } +} + +void +sock_init(void) +{ + struct socket *sock; + int i, ok; + + for (sock = sockets; sock <= last_socket; ++sock) + sock->state = SS_FREE; + for (i = ok = 0; i < NPROTO; ++i) { + printk("sock_init: initializing family %d (%s)\n", + proto_table[i].family, proto_table[i].name); + if ((*proto_table[i].ops->init)() < 0) { + printk("sock_init: init failed.\n", + proto_table[i].family); + proto_table[i].family = -1; + } + else + ++ok; + } + if (!ok) + printk("sock_init: warning: no protocols initialized\n"); + return; +} + diff --git a/net/socketcall.h b/net/socketcall.h new file mode 100644 index 000000000000..4b48562de73a --- /dev/null +++ b/net/socketcall.h @@ -0,0 +1,13 @@ +#ifndef _SOCKETCALL_ +#define _SOCKETCALL_ + +#define SYS_SOCKET 1 +#define SYS_BIND 2 +#define SYS_CONNECT 3 +#define SYS_LISTEN 4 +#define SYS_ACCEPT 5 +#define SYS_GETSOCKNAME 6 +#define SYS_GETPEERNAME 7 +#define SYS_SOCKETPAIR 8 + +#endif _SOCKETCALL_ diff --git a/net/unix.c b/net/unix.c new file mode 100644 index 000000000000..8e956b55313f --- /dev/null +++ b/net/unix.c @@ -0,0 +1,600 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kern_sock.h" + +static struct unix_proto_data { + int refcnt; /* cnt of reference 0=free */ + struct socket *socket; /* socket we're bound to */ + int protocol; + struct sockaddr_un sockaddr_un; + short sockaddr_len; /* >0 if name bound */ + char *buf; + int bp_head, bp_tail; + struct inode *inode; + struct unix_proto_data *peerupd; +} unix_datas[NSOCKETS]; +#define last_unix_data (unix_datas + NSOCKETS - 1) + +#define UN_DATA(SOCK) ((struct unix_proto_data *)(SOCK)->data) +#define UN_PATH_OFFSET ((unsigned long)((struct sockaddr_un *)0)->sun_path) + +/* + * buffer size must be power of 2. buffer mgmt inspired by pipe code. + * note that buffer contents can wraparound, and we can write one byte less + * than full size to discern full vs empty. + */ +#define BUF_SIZE PAGE_SIZE +#define UN_BUF_AVAIL(UPD) (((UPD)->bp_head - (UPD)->bp_tail) & (BUF_SIZE-1)) +#define UN_BUF_SPACE(UPD) ((BUF_SIZE-1) - UN_BUF_AVAIL(UPD)) + +static int unix_proto_init(void); +static int unix_proto_create(struct socket *sock, int protocol); +static int unix_proto_dup(struct socket *newsock, struct socket *oldsock); +static int unix_proto_release(struct socket *sock, struct socket *peer); +static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, + int sockaddr_len); +static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len); +static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2); +static int unix_proto_accept(struct socket *sock, struct socket *newsock); +static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, + int *usockaddr_len, int peer); +static int unix_proto_read(struct socket *sock, char *ubuf, int size, + int nonblock); +static int unix_proto_write(struct socket *sock, char *ubuf, int size, + int nonblock); +static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait); +static int unix_proto_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg); + +struct proto_ops unix_proto_ops = { + unix_proto_init, + unix_proto_create, + unix_proto_dup, + unix_proto_release, + unix_proto_bind, + unix_proto_connect, + unix_proto_socketpair, + unix_proto_accept, + unix_proto_getname, + unix_proto_read, + unix_proto_write, + unix_proto_select, + unix_proto_ioctl +}; + +#ifdef SOCK_DEBUG +void +sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len) +{ + char buf[sizeof(sockun->sun_path) + 1]; + + sockaddr_len -= UN_PATH_OFFSET; + if (sockun->sun_family != AF_UNIX) + printk("sockaddr_un: \n", sockun->sun_family); + else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf)-1) + printk("sockaddr_un: \n", sockaddr_len); + else { + memcpy(buf, sockun->sun_path, sockaddr_len); + buf[sockaddr_len] = '\0'; + printk("sockaddr_un: '%s'[%d]\n", buf, + sockaddr_len + UN_PATH_OFFSET); + } +} +#endif + +static struct unix_proto_data * +unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len) +{ + struct unix_proto_data *upd; + + for (upd = unix_datas; upd <= last_unix_data; ++upd) { + if (upd->refcnt && upd->socket && + upd->sockaddr_len == sockaddr_len && + memcmp(&upd->sockaddr_un, sockun, sockaddr_len) == 0) + return upd; + } + return NULL; +} + +static struct unix_proto_data * +unix_data_alloc(void) +{ + struct unix_proto_data *upd; + + cli(); + for (upd = unix_datas; upd <= last_unix_data; ++upd) { + if (!upd->refcnt) { + upd->refcnt = 1; + sti(); + upd->socket = NULL; + upd->sockaddr_len = 0; + upd->buf = NULL; + upd->bp_head = upd->bp_tail = 0; + upd->inode = NULL; + upd->peerupd = NULL; + return upd; + } + } + sti(); + return NULL; +} + +static inline void +unix_data_ref(struct unix_proto_data *upd) +{ + ++upd->refcnt; + PRINTK("unix_data_ref: refing data 0x%x (%d)\n", upd, upd->refcnt); +} + +static void +unix_data_deref(struct unix_proto_data *upd) +{ + if (upd->refcnt == 1) { + PRINTK("unix_data_deref: releasing data 0x%x\n", upd); + if (upd->buf) { + free_page((unsigned long)upd->buf); + upd->buf = NULL; + upd->bp_head = upd->bp_tail = 0; + } + } + --upd->refcnt; +} + +/* + * upon a create, we allocate an empty protocol data, and grab a page to + * buffer writes + */ +static int +unix_proto_create(struct socket *sock, int protocol) +{ + struct unix_proto_data *upd; + + PRINTK("unix_proto_create: socket 0x%x, proto %d\n", sock, protocol); + if (protocol != 0) { + PRINTK("unix_proto_create: protocol != 0\n"); + return -EINVAL; + } + if (!(upd = unix_data_alloc())) { + printk("unix_proto_create: can't allocate buffer\n"); + return -ENOMEM; + } + if (!(upd->buf = (char *)get_free_page(GFP_USER))) { + printk("unix_proto_create: can't get page!\n"); + unix_data_deref(upd); + return -ENOMEM; + } + upd->protocol = protocol; + upd->socket = sock; + UN_DATA(sock) = upd; + PRINTK("unix_proto_create: allocated data 0x%x\n", upd); + return 0; +} + +static int +unix_proto_dup(struct socket *newsock, struct socket *oldsock) +{ + struct unix_proto_data *upd = UN_DATA(oldsock); + + return unix_proto_create(newsock, upd->protocol); +} + +static int +unix_proto_release(struct socket *sock, struct socket *peer) +{ + struct unix_proto_data *upd = UN_DATA(sock); + + PRINTK("unix_proto_release: socket 0x%x, unix_data 0x%x\n", + sock, upd); + if (!upd) + return 0; + if (upd->socket != sock) { + printk("unix_proto_release: socket link mismatch!\n"); + return -EINVAL; + } + if (upd->inode) { + PRINTK("unix_proto_release: releasing inode 0x%x\n", + upd->inode); + iput(upd->inode); + upd->inode = NULL; + } + UN_DATA(sock) = NULL; + upd->socket = NULL; + if (upd->peerupd) + unix_data_deref(upd->peerupd); + unix_data_deref(upd); + return 0; +} + +/* + * bind a name to a socket. this is where much of the work is done. we + * allocate a fresh page for the buffer, grab the appropriate inode and + * set things up. + * + * XXX what should we do if an address is already bound? here we return + * EINVAL, but it may be necessary to re-bind. i think thats what bsd does + * in the case of datagram sockets + */ +static int +unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr, + int sockaddr_len) +{ + struct unix_proto_data *upd = UN_DATA(sock); + char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1]; + int i; + unsigned long old_fs; + + PRINTK("unix_proto_bind: socket 0x%x, len=%d\n", sock, + sockaddr_len); + if (sockaddr_len <= UN_PATH_OFFSET || + sockaddr_len >= sizeof(struct sockaddr_un)) { + PRINTK("unix_proto_bind: bad length %d\n", sockaddr_len); + return -EINVAL; + } + if (upd->sockaddr_len || upd->inode) { + printk("unix_proto_bind: already bound!\n"); + return -EINVAL; + } + verify_area(umyaddr, sockaddr_len); + memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len); + if (upd->sockaddr_un.sun_family != AF_UNIX) { + PRINTK("unix_proto_bind: family is %d, not AF_UNIX (%d)\n", + upd->sockaddr_un.sun_family, AF_UNIX); + return -EINVAL; + } + + memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET); + fname[sockaddr_len-UN_PATH_OFFSET] = '\0'; + old_fs = get_fs(); + set_fs(get_ds()); + i = do_mknod(fname, S_IFSOCK | 0777, 0); + if (i == 0) + i = open_namei(fname, 0, S_IFSOCK, &upd->inode); + set_fs(old_fs); + if (i < 0) { + printk("unix_proto_bind: can't open socket %s\n", fname); + return i; + } + + upd->sockaddr_len = sockaddr_len; /* now its legal */ + PRINTK("unix_proto_bind: bound socket address: "); +#ifdef SOCK_DEBUG + sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len); +#endif + return 0; +} + +/* + * perform a connection. we can only connect to unix sockets (i can't for + * the life of me find an application where that wouldn't be the case!) + */ +static int +unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr, + int sockaddr_len) +{ + int i; + struct unix_proto_data *serv_upd; + struct sockaddr_un sockun; + + PRINTK("unix_proto_connect: socket 0x%x, servlen=%d\n", sock, + sockaddr_len); + if (sockaddr_len <= UN_PATH_OFFSET || + sockaddr_len >= sizeof(struct sockaddr_un)) { + PRINTK("unix_proto_connect: bad length %d\n", sockaddr_len); + return -EINVAL; + } + verify_area(uservaddr, sockaddr_len); + memcpy_fromfs(&sockun, uservaddr, sockaddr_len); + if (sockun.sun_family != AF_UNIX) { + PRINTK("unix_proto_connect: family is %d, not AF_UNIX (%d)\n", + sockun.sun_family, AF_UNIX); + return -EINVAL; + } + if (!(serv_upd = unix_data_lookup(&sockun, sockaddr_len))) { + PRINTK("unix_proto_connect: can't locate peer\n"); + return -EINVAL; + } + if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) { + PRINTK("unix_proto_connect: can't await connection\n"); + return i; + } + unix_data_ref(UN_DATA(sock->conn)); + UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */ + return 0; +} + +/* + * to do a socketpair, we make just connect the two datas, easy! since we + * always wait on the socket inode, they're no contention for a wait area, + * and deadlock prevention in the case of a process writing to itself is, + * ignored, in true unix fashion! + */ +static int +unix_proto_socketpair(struct socket *sock1, struct socket *sock2) +{ + struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2); + + unix_data_ref(upd1); + unix_data_ref(upd2); + upd1->peerupd = upd2; + upd2->peerupd = upd1; + return 0; +} + +/* + * on accept, we ref the peer's data for safe writes + */ +static int +unix_proto_accept(struct socket *sock, struct socket *newsock) +{ + PRINTK("unix_proto_accept: socket 0x%x accepted via socket 0x%x\n", + sock, newsock); + unix_data_ref(UN_DATA(newsock->conn)); + UN_DATA(newsock)->peerupd = UN_DATA(newsock->conn); + return 0; +} + +/* + * gets the current name or the name of the connected socket. + */ +static int +unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr, + int *usockaddr_len, int peer) +{ + struct unix_proto_data *upd; + int len; + + PRINTK("unix_proto_getname: socket 0x%x for %s\n", sock, + peer ? "peer" : "self"); + if (peer) { + if (sock->state != SS_CONNECTED) { + PRINTK("unix_proto_getname: socket not connected\n"); + return -EINVAL; + } + upd = UN_DATA(sock->conn); + } + else + upd = UN_DATA(sock); + verify_area(usockaddr_len, sizeof(*usockaddr_len)); + if ((len = get_fs_long(usockaddr_len)) <= 0) + return -EINVAL; + if (len > upd->sockaddr_len) + len = upd->sockaddr_len; + if (len) { + verify_area(usockaddr, len); + memcpy_tofs(usockaddr, &upd->sockaddr_un, len); + } + put_fs_long(len, usockaddr_len); + return 0; +} + +/* + * we read from our own buf. + */ +static int +unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) +{ + struct unix_proto_data *upd; + int todo, avail; + + if ((todo = size) <= 0) + return 0; + upd = UN_DATA(sock); + while (!(avail = UN_BUF_AVAIL(upd))) { + if (sock->state != SS_CONNECTED) { + PRINTK("unix_proto_read: socket not connected\n"); + return (sock->state == SS_DISCONNECTING) ? 0 : -EINVAL; + } + PRINTK("unix_proto_read: no data available...\n"); + if (nonblock) + return -EAGAIN; + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) { + PRINTK("unix_proto_read: interrupted\n"); + return -ERESTARTSYS; + } + if (sock->state == SS_DISCONNECTING) { + PRINTK("unix_proto_read: disconnected\n"); + return 0; + } + } + + /* + * copy from the read buffer into the user's buffer, watching for + * wraparound. then we wake up the writer + */ + do { + int part, cando; + + if (avail <= 0) { + PRINTK("unix_proto_read: AVAIL IS NEGATIVE!!!\n"); + send_sig(SIGKILL,current,1); + return -EINTR; + } + + if ((cando = todo) > avail) + cando = avail; + if (cando > (part = BUF_SIZE - upd->bp_tail)) + cando = part; + PRINTK("unix_proto_read: avail=%d, todo=%d, cando=%d\n", + avail, todo, cando); + verify_area(ubuf, cando); + memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando); + upd->bp_tail = (upd->bp_tail + cando) & (BUF_SIZE-1); + ubuf += cando; + todo -= cando; + if (sock->state == SS_CONNECTED) + wake_up(sock->conn->wait); + avail = UN_BUF_AVAIL(upd); + } while (todo && avail); + return size - todo; +} + +/* + * we write to our peer's buf. when we connected we ref'd this peer so we + * are safe that the buffer remains, even after the peer has disconnected, + * which we check other ways. + */ +static int +unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) +{ + struct unix_proto_data *pupd; + int todo, space; + + if ((todo = size) <= 0) + return 0; + if (sock->state != SS_CONNECTED) { + PRINTK("unix_proto_write: socket not connected\n"); + if (sock->state == SS_DISCONNECTING) { + send_sig(SIGPIPE,current,1); + return -EINTR; + } + return -EINVAL; + } + pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */ + + while (!(space = UN_BUF_SPACE(pupd))) { + PRINTK("unix_proto_write: no space left...\n"); + if (nonblock) + return -EAGAIN; + interruptible_sleep_on(sock->wait); + if (current->signal & ~current->blocked) { + PRINTK("unix_proto_write: interrupted\n"); + return -ERESTARTSYS; + } + if (sock->state == SS_DISCONNECTING) { + PRINTK("unix_proto_write: disconnected (SIGPIPE)\n"); + send_sig(SIGPIPE,current,1); + return -EINTR; + } + } + + /* + * copy from the user's buffer to the write buffer, watching for + * wraparound. then we wake up the reader + */ + do { + int part, cando; + + if (space <= 0) { + PRINTK("unix_proto_write: SPACE IS NEGATIVE!!!\n"); + send_sig(SIGKILL,current,1); + return -EINTR; + } + + /* + * we may become disconnected inside this loop, so watch + * for it (peerupd is safe until we close) + */ + if (sock->state == SS_DISCONNECTING) { + send_sig(SIGPIPE,current,1); + return -EINTR; + } + if ((cando = todo) > space) + cando = space; + if (cando > (part = BUF_SIZE - pupd->bp_head)) + cando = part; + PRINTK("unix_proto_write: space=%d, todo=%d, cando=%d\n", + space, todo, cando); + verify_area(ubuf, cando); + memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando); + pupd->bp_head = (pupd->bp_head + cando) & (BUF_SIZE-1); + ubuf += cando; + todo -= cando; + if (sock->state == SS_CONNECTED) + wake_up(sock->conn->wait); + space = UN_BUF_SPACE(pupd); + } while (todo && space); + return size - todo; +} + +static int +unix_proto_select(struct socket *sock, int sel_type, select_table * wait) +{ + struct unix_proto_data *upd, *peerupd; + + if (sel_type == SEL_IN) { + upd = UN_DATA(sock); + PRINTK("unix_proto_select: there is%s data available\n", + UN_BUF_AVAIL(upd) ? "" : " no"); + if (UN_BUF_AVAIL(upd)) /* even if disconnected */ + return 1; + else if (sock->state != SS_CONNECTED) { + PRINTK("unix_proto_select: socket not connected (read EOF)\n"); + return 1; + } + select_wait(sock->wait,wait); + return 0; + } + if (sel_type == SEL_OUT) { + if (sock->state != SS_CONNECTED) { + PRINTK("unix_proto_select: socket not connected (write EOF)\n"); + return 1; + } + peerupd = UN_DATA(sock->conn); + PRINTK("unix_proto_select: there is%s space available\n", + UN_BUF_SPACE(peerupd) ? "" : " no"); + if (UN_BUF_SPACE(peerupd) > 0) + return 1; + select_wait(sock->wait,wait); + return 0; + } + /* SEL_EX */ + PRINTK("unix_proto_select: there are no exceptions here?!\n"); + return 0; +} + +static int +unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct unix_proto_data *upd, *peerupd; + + upd = UN_DATA(sock); + peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL; + + switch (cmd) { + case TIOCINQ: + verify_area((void *)arg, sizeof(unsigned long)); + if (UN_BUF_AVAIL(upd) || peerupd) + put_fs_long(UN_BUF_AVAIL(upd), (unsigned long *)arg); + else + put_fs_long(1, (unsigned long *)arg); /* read EOF */ + break; + + case TIOCOUTQ: + verify_area((void *)arg, sizeof(unsigned long)); + if (peerupd) + put_fs_long(UN_BUF_SPACE(peerupd), + (unsigned long *)arg); + else + put_fs_long(0, (unsigned long *)arg); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int +unix_proto_init(void) +{ + struct unix_proto_data *upd; + + PRINTK("unix_proto_init: initializing...\n"); + for (upd = unix_datas; upd <= last_unix_data; ++upd) + upd->refcnt = 0; + return 0; +} diff --git a/tools/build.c b/tools/build.c index 1fa477bad97a..5169ce3c19d3 100644 --- a/tools/build.c +++ b/tools/build.c @@ -1,7 +1,7 @@ /* * linux/tools/build.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ /* @@ -25,14 +25,14 @@ #include /* contains exit */ #include /* unistd.h needs this */ #include -#include +#include #include /* contains read/write */ #include #define MINIX_HEADER 32 #define GCC_HEADER 1024 -#define SYS_SIZE 0x4000 +#define SYS_SIZE 0x5000 #define DEFAULT_MAJOR_ROOT 0 #define DEFAULT_MINOR_ROOT 0 @@ -69,8 +69,8 @@ int main(int argc, char ** argv) perror(argv[4]); die("Couldn't stat root device."); } - major_root = MAJOR(sb.st_rdev); - minor_root = MINOR(sb.st_rdev); + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); } else { major_root = 0; minor_root = 0; @@ -81,7 +81,7 @@ int main(int argc, char ** argv) } fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); if ((major_root != 2) && (major_root != 3) && - (major_root != 0)) { + (major_root != 8) && (major_root != 0)) { fprintf(stderr, "Illegal root device (major = %d)\n", major_root); die("Bad root device --- major #"); -- 2.39.5