aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Benedict Torvalds <torvalds@cc.helsinki.fi>1992-01-15 02:13:40 +0200
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:02 -0400
commit26d73589f54a0ffa39ca6b16cfe4a46a63f6b781 (patch)
treecf5abfad6cf200ada585f1fb37b894f5759fe8ce
parent7aadf253807264ad5561479e2a8b9de9602f966d (diff)
downloadarchive-26d73589f54a0ffa39ca6b16cfe4a46a63f6b781.tar.gz
linux-0.12 is availablev0.12
Ok, the subject says it all. The kernel source and the disk-images are available on nic.funet.fi, and I'm assuming they will migrate to other places in a couple of days. There is a RELNOTES-0.12, but installation is similar to 0.11, so the installinfo in relnotes is pretty minimal. Note that even users of 0.11 should boot up from floppy first and copy all the binaries to their proper places on the harddisk partition: fsck, ls etc have changed with symlinks, and bash (/bin/sh) due to job control. Also note that I've decided to change the copyright to have the same set of rules as the GNU copyleft - I got some mail asking about it, and I agree. The old copyright still holds for now - I want to make sure none of the "co-authors" would mind, but I'm pretty sure they won't. It won't actually change the copyright very much: it's the handling-costs extra etc. I'll send some additional files (the complete fileutils, the new library and libc.a etc) tomorrow. Linus <RELNOTES-0.12> RELEASE NOTES FOR LINUX v0.12 This is file mostly contains info on changed features of Linux, and using old versions as a help-reference might be a good idea. COPYRIGHT The Linux copyright will change: I've had a couple of requests to make it compatible with the GNU copyleft, removing the "you may not distribute it for money" condition. I agree. I propose that the copyright be changed so that it confirms to GNU - pending approval of the persons who have helped write code. I assume this is going to be no problem for anybody: If you have grievances ("I wrote that code assuming the copyright would stay the same") mail me. Otherwise The GNU copyleft takes effect as of the first of February. If you do not know the gist of the GNU copyright - read it. INSTALLATION This is a SHORT install-note. The installation is very similar to 0.11, so read that (INSTALL-0.11) too. There are a couple of programs you will need to install linux: something that writes disk images (rawrite.exe or NU or...) and something that can create harddisk partitions (fdisk under xenix or older versions of dos, edpart.exe or something like that). NOTE! Repartitioning your harddisk will destroy all data on it (well, not exactly, but if you know enough to get back the data you probably didn't need this warning). So be careful. READ THIS THROUGH, THEN READ INSTALL-0.11, AND IF YOU ARE SURE YOU KNOW WHAT YOU ARE DOING, CONTINUE. OTHERWISE, PANIC. OR WRITE ME FOR EXPLANATIONS. OR DO ANYTHING BUT INSTALL LINUX - IT'S VERY SIMPLE, BUT IF YOU DON'T KNOW WHAT YOU ARE DOING YOU'LL PROBABLY BE SORRY. I'D RATHER ANSWER A FEW UNNECESSARY MAILS THAN GET MAIL SAYING "YOU KILLED MY HARDDISK, BASTARD. I'M GOING TO FIND YOU, AND YOU'LL BE SORRY WHEN I DO". 1) back up everything you have on your harddisk - linux-0.12 is still in beta and might do weird things. The only thing I guarantee is that it has worked fine on /my/ machine - for all I know it might eat your harddisk and spit it out in small pieces on any other hardware. 2) Test out the linux boot-disk with the root file system. If it doesn't work, check the hardware requirements, and mail me if you still think it should work. I might not be able to help you, but your bug-report would still be appreciated. Test that linux can read your harddisk at least partly: run the fdisk program on the root-disk, and see if it barfs. If it tells you about any partitions at all, linux can successfully read at least part of your harddisk. 3) Make sure that you have a free /primary/ partition. There can be 4 primary partitions per drive: newer DOS fdisks seem to be able to create only 2 (one primary and one extended). In that case use some other partitioning software: edpart.exe etc. Linux fdisk currently only tells you the partition info - it doesn't write to the disk. Remember to check how big your partition was, as that can be used to tell which device Linux thinks it is. 4) Boot up linux again, fdisk to make sure you now have the new partition, and use mkfs to make a filesystem on one of the partitions fdisk reports. Write "mkfs -c /dev/hdX nnn" where X is the device number reported by linux fdisk, and nnn is the size - also reported by fdisk. nnn is the size in /blocks/, ie kilobytes. You should be able to use the size info to determine which partition is represented by which device name. 5) Mount the new disk partition: "mount /dev/hdX /user". Copy over the root filesystem to the harddisk, eg like this: # for i in bin dev etc usr tmp # do # cp +recursive /$i /user # done You caanot use just "cp +recursive / /user", as that will result in a loop. 6) Sync the filesystem after you have played around enough, and reboot. # sync <wait for it to sync> ctrl-alt-del The folklore says you should do this three times before rebooting: once should be enough, but I admit I do it three times anyway :) THIS IS IMPORTANT! NEVER EVER FORGET TO SYNC BEFORE KILLING THE MACHINE. 7) Change the bootdisk to understand which partition it should use as a root filesystem. See INSTALL-0.11: it's still the word at offset 508 into the image. You should be up and running. That's it. Go back and read the INSTALL-0.11 New features of 0.12, in order of appearance (ie in the order you see them) Linux now prints cute dots when loading WoW. Run, don't walk, to see this :). Seriously, it should hopefully now load even on machines that never got off the ground before, but otherwise the loading hasn't changed. Implemented by drew. Super-VGA detection for extended alphamun modes I cannot guarantee it, I didn't write it, but it works great on a ET400 SVGA card. I'm addicted to the new look with 100x40 character editing, instead of a cramped 80x25. This only works on VGA-cards that support higher text-resolutions, and which are correctly identified. Implemented by d88-man. Job Control. Ok, everybody used to typing ^Z after they started a long command, and forgot to put it in the background - now it works on linux too. Bash knows the usualy job-control commands: bg, fg, jobs & kill. I hope there will be no nasty surprises. Job control was implemented by tytso@athena.mit.edu. Virtual consoles on EGA/VGA screens. You can select one of several consoles by pressing the left alt-key and a function key at the same time. Linux should report the number of virtual consoles available upon bootup. /dev/tty0 is now "the current" screen, /dev/tty1 is the main console, and /dev/tty2-8 can exist depending on your text-mode or card. NOTE! Scrolling is noticeably much slower with virtual consoles on a EGA/VGA. The reason is that no longer does linux use all the screen memory as a long buffer, but crams in several consoles in it. I think it's worth it. The virtual consoles also have some new screen-handling commands: they confirm even better to vt200 control codes than 0.11. Special graphic characters etc: you can well use them as terminals to VMS (although that's a shameful waste of resources). pty's Ok. I have to admit that I didn't get the hangup-code working correctly, but that should be easy to add. The general things are there. select I've never used it, so I cannot say how well it works. My minor testing seems to indicate that it works ok. vc's, pty's and select were implemented by pmacdona, although I hacked it heavily. 387-emulation. It's not complete, but it works well enough to run those gcc2.0 compiled programs I tested (few). None of the "heavy" math-functions are implemented yet. Symbolic links. Try out a few "ln -s xx yy", and ls -l. Note that I think tar should be recompiled to know anout them, and probably some other programs too. The 0.12 rootimage-disk has most of the recompiled fileutilities. Virtual memory. In addition to the "mkfs" program, there is now a "mkswap" program on the root disk. The syntax is identical: "mkswap -c /dev/hdX nnn", and again: this writes over the partition, so be careful. Swapping can then be enabled by changing the word at offset 506 in the bootimage to the desired device. Use the same program as for setting the root file system (but change the 508 offset to 506 of course). NOTE! This has been tested by Robert Blum, who has a 2M machine, and it allows you to run gcc without much memory. HOWEVER, I had to stop using it, as my diskspace was eaten up by the beta-gcc-2.0, so I'd like to hear that it still works: I've been totally unable to make a swap-partition for even rudimentary testing since about christmastime. Thus the new changes could possibly just have backfired on the VM, but I doubt it. And that's it, I think. Happy hacking. Linus
-rw-r--r--Makefile30
-rw-r--r--boot/bootsect.S (renamed from boot/bootsect.s)222
-rw-r--r--boot/setup.S631
-rw-r--r--boot/setup.s231
-rw-r--r--fs/Makefile99
-rw-r--r--fs/bitmap.c15
-rw-r--r--fs/block_dev.c16
-rw-r--r--fs/exec.c63
-rw-r--r--fs/inode.c10
-rw-r--r--fs/ioctl.c3
-rw-r--r--fs/namei.c228
-rw-r--r--fs/open.c51
-rw-r--r--fs/pipe.c29
-rw-r--r--fs/select.c278
-rw-r--r--fs/stat.c45
-rw-r--r--fs/super.c1
-rw-r--r--fs/truncate.c64
-rw-r--r--include/asm/system.h8
-rw-r--r--include/errno.h4
-rw-r--r--include/linux/config.h25
-rw-r--r--include/linux/fs.h16
-rw-r--r--include/linux/kernel.h11
-rw-r--r--include/linux/math_emu.h185
-rw-r--r--include/linux/mm.h37
-rw-r--r--include/linux/sched.h71
-rw-r--r--include/linux/sys.h27
-rw-r--r--include/linux/tty.h42
-rw-r--r--include/signal.h7
-rw-r--r--include/stddef.h1
-rw-r--r--include/sys/param.h12
-rw-r--r--include/sys/resource.h63
-rw-r--r--include/sys/stat.h2
-rw-r--r--include/sys/time.h63
-rw-r--r--include/sys/types.h8
-rw-r--r--include/sys/utsname.h3
-rw-r--r--include/sys/wait.h3
-rw-r--r--include/termios.h26
-rw-r--r--include/time.h7
-rw-r--r--include/unistd.h31
-rw-r--r--init/main.c42
-rw-r--r--kernel/Makefile40
-rw-r--r--kernel/asm.s5
-rw-r--r--kernel/blk_drv/Makefile27
-rw-r--r--kernel/blk_drv/blk.h26
-rw-r--r--kernel/blk_drv/floppy.c15
-rw-r--r--kernel/blk_drv/hd.c77
-rw-r--r--kernel/blk_drv/ll_rw_blk.c55
-rw-r--r--kernel/chr_drv/Makefile56
-rw-r--r--kernel/chr_drv/console.c569
-rw-r--r--kernel/chr_drv/keyboard.S31
-rw-r--r--kernel/chr_drv/pty.c63
-rw-r--r--kernel/chr_drv/rs_io.s3
-rw-r--r--kernel/chr_drv/serial.c6
-rw-r--r--kernel/chr_drv/tty_io.c389
-rw-r--r--kernel/chr_drv/tty_ioctl.c79
-rw-r--r--kernel/exit.c403
-rw-r--r--kernel/fork.c20
-rw-r--r--kernel/math/Makefile42
-rw-r--r--kernel/math/add.c92
-rw-r--r--kernel/math/compare.c60
-rw-r--r--kernel/math/convert.c185
-rw-r--r--kernel/math/div.c109
-rw-r--r--kernel/math/ea.c92
-rw-r--r--kernel/math/error.c16
-rw-r--r--kernel/math/get_put.c240
-rw-r--r--kernel/math/math_emulate.c525
-rw-r--r--kernel/math/mul.c73
-rw-r--r--kernel/printk.c12
-rw-r--r--kernel/sched.c96
-rw-r--r--kernel/signal.c118
-rw-r--r--kernel/sys.c314
-rw-r--r--kernel/sys_call.s (renamed from kernel/system_call.s)65
-rw-r--r--kernel/traps.c11
-rw-r--r--mm/Makefile11
-rw-r--r--mm/memory.c226
-rw-r--r--mm/swap.c253
-rw-r--r--tools/build.c36
77 files changed, 6009 insertions, 1141 deletions
diff --git a/Makefile b/Makefile
index 9de9f4f..1cb2861 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,7 @@ CPP =cpp -nostdinc -Iinclude
# default of /dev/hd6 is used by 'build'.
#
ROOT_DEV=/dev/hd6
+SWAP_DEV=/dev/hd2
ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
@@ -39,7 +40,8 @@ LIBS =lib/lib.a
all: Image
Image: boot/bootsect boot/setup tools/system tools/build
- tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
+ tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) \
+ $(SWAP_DEV) > Image
sync
disk: Image
@@ -85,17 +87,19 @@ 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
+
+boot/bootsect.s: boot/bootsect.S include/linux/config.h
+ $(CPP) -traditional boot/bootsect.S -o boot/bootsect.s
+
boot/bootsect: boot/bootsect.s
$(AS86) -o boot/bootsect.o boot/bootsect.s
$(LD86) -s -o boot/bootsect boot/bootsect.o
-tmp.s: boot/bootsect.s tools/system
- (echo -n "SYSSIZE = (";ls -l tools/system | grep system \
- | cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s
- cat boot/bootsect.s >> tmp.s
-
clean:
- rm -f Image System.map tmp_make core boot/bootsect boot/setup
+ rm -f Image System.map tmp_make core boot/bootsect boot/setup \
+ boot/bootsect.s boot/setup.s
rm -f init/*.o tools/system tools/build boot/*.o
(cd mm;make clean)
(cd fs;make clean)
@@ -116,8 +120,10 @@ dep:
### Dependencies:
init/main.o : init/main.c include/unistd.h include/sys/stat.h \
- include/sys/types.h include/sys/times.h include/sys/utsname.h \
- include/utime.h include/time.h include/linux/tty.h include/termios.h \
- include/linux/sched.h include/linux/head.h include/linux/fs.h \
- include/linux/mm.h include/signal.h include/asm/system.h include/asm/io.h \
- include/stddef.h include/stdarg.h include/fcntl.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/tty.h include/termios.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/asm/system.h \
+ include/asm/io.h include/stddef.h include/stdarg.h include/fcntl.h \
+ include/string.h
diff --git a/boot/bootsect.s b/boot/bootsect.S
index 711f103..1df6c23 100644
--- a/boot/bootsect.s
+++ b/boot/bootsect.S
@@ -3,9 +3,11 @@
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
-SYSSIZE = 0x3000
+#include <linux/config.h>
+SYSSIZE = DEF_SYSSIZE
!
! bootsect.s (C) 1991 Linus Torvalds
+! modified by Drew Eckhardt
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
@@ -33,14 +35,14 @@ begbss:
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
-INITSEG = 0x9000 ! we move boot here - out of the way
-SETUPSEG = 0x9020 ! setup starts here
-SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
+INITSEG = DEF_INITSEG ! we move boot here - out of the way
+SETUPSEG = DEF_SETUPSEG ! setup starts here
+SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
-! ROOT_DEV: 0x000 - same type of floppy as boot.
-! 0x301 - first partition on first drive etc
-ROOT_DEV = 0x306
+! ROOT_DEV & SWAP_DEV are now written by "build".
+ROOT_DEV = 0
+SWAP_DEV = 0
entry start
start:
@@ -54,25 +56,83 @@ start:
rep
movw
jmpi go,INITSEG
-go: mov ax,cs
+
+go: mov ax,cs
+ mov dx,#0xfef4 ! arbitrary value >>512 - disk parm size
+
mov ds,ax
mov es,ax
-! put stack at 0x9ff00.
- mov ss,ax
- mov sp,#0xFF00 ! arbitrary value >>512
+ push ax
+
+ mov ss,ax ! put stack at 0x9ff00 - 12.
+ mov sp,dx
+/*
+ * Many BIOS's default disk parameter tables will not
+ * recognize multi-sector reads beyond the maximum sector number
+ * specified in the default diskette parameter tables - this may
+ * mean 7 sectors in some cases.
+ *
+ * Since single sector reads are slow and out of the question,
+ * we must take care of this by creating new parameter tables
+ * (for the first disk) in RAM. We will set the maximum sector
+ * count to 18 - the most we will encounter on an HD 1.44.
+ *
+ * High doesn't hurt. Low does.
+ *
+ * Segments are as follows: ds=es=ss=cs - INITSEG,
+ * fs = 0, gs = parameter table segment
+ */
+
+
+ push #0
+ pop fs
+ mov bx,#0x78 ! fs:bx is parameter table address
+ seg fs
+ lgs si,(bx) ! gs:si is source
+
+ mov di,dx ! es:di is destination
+ mov cx,#6 ! copy 12 bytes
+ cld
+
+ rep
+ seg gs
+ movw
+
+ mov di,dx
+ movb 4(di),*18 ! patch sector count
+
+ seg fs
+ mov (bx),di
+ seg fs
+ mov 2(bx),es
+
+ pop ax
+ mov fs,ax
+ mov gs,ax
+
+ xor ah,ah ! reset FDC
+ xor dl,dl
+ int 0x13
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
load_setup:
- mov dx,#0x0000 ! drive 0, head 0
+ xor dx, dx ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
- mov dx,#0x0000
- mov ax,#0x0000 ! reset the diskette
+
+ push ax ! dump error code
+ call print_nl
+ mov bp, sp
+ call print_hex
+ pop ax
+
+ xor dl, dl ! reset FDC
+ xor ah, ah
int 0x13
j load_setup
@@ -80,10 +140,10 @@ ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
- mov dl,#0x00
- mov ax,#0x0800 ! AH=8 is get drive parameters
+ xor dl,dl
+ mov ah,#0x08 ! AH=8 is get drive parameters
int 0x13
- mov ch,#0x00
+ xor ch,ch
seg cs
mov sectors,cx
mov ax,#INITSEG
@@ -95,7 +155,7 @@ ok_load_setup:
xor bh,bh
int 0x10
- mov cx,#24
+ mov cx,#9
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
@@ -108,6 +168,7 @@ ok_load_setup:
mov es,ax ! segment of 0x010000
call read_it
call kill_motor
+ call print_nl
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
@@ -116,7 +177,7 @@ ok_load_setup:
seg cs
mov ax,root_dev
- cmp ax,#0
+ or ax,ax
jne root_defined
seg cs
mov bx,sectors
@@ -190,42 +251,123 @@ ok3_read:
add bx,cx
jnc rp_read
mov ax,es
- add ax,#0x1000
+ add ah,#0x10
mov es,ax
xor bx,bx
jmp rp_read
read_track:
- push ax
- push bx
- push cx
- push dx
+ pusha
+ pusha
+ mov ax, #0xe2e ! loading... message 2e = .
+ mov bx, #7
+ int 0x10
+ popa
+
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
- mov dl,#0
and dx,#0x0100
mov ah,#2
+
+ push dx ! save for error dump
+ push cx
+ push bx
+ push ax
+
int 0x13
jc bad_rt
- pop dx
- pop cx
- pop bx
- pop ax
+ add sp, #8
+ popa
ret
-bad_rt: mov ax,#0
- mov dx,#0
+
+bad_rt: push ax ! save error code
+ call print_all ! ah = error, al = read
+
+
+ xor ah,ah
+ xor dl,dl
int 0x13
- pop dx
- pop cx
- pop bx
- pop ax
+
+
+ add sp, #10
+ popa
jmp read_track
/*
+ * print_all is for debugging purposes.
+ * It will print out all of the registers. The assumption is that this is
+ * called from a routine, with a stack frame like
+ * dx
+ * cx
+ * bx
+ * ax
+ * error
+ * ret <- sp
+ *
+*/
+
+print_all:
+ mov cx, #5 ! error code + 4 registers
+ mov bp, sp
+
+print_loop:
+ push cx ! save count left
+ call print_nl ! nl for readability
+ jae no_reg ! see if register name is needed
+
+ mov ax, #0xe05 + 0x41 - 1
+ sub al, cl
+ int 0x10
+
+ mov al, #0x58 ! X
+ int 0x10
+
+ mov al, #0x3a ! :
+ int 0x10
+
+no_reg:
+ add bp, #2 ! next register
+ call print_hex ! print it
+ pop cx
+ loop print_loop
+ ret
+
+print_nl:
+ mov ax, #0xe0d ! CR
+ int 0x10
+ mov al, #0xa ! LF
+ int 0x10
+ ret
+
+/*
+ * print_hex is for debugging purposes, and prints the word
+ * pointed to by ss:bp in hexadecmial.
+*/
+
+print_hex:
+ mov cx, #4 ! 4 hex digits
+ mov dx, (bp) ! load word into dx
+print_digit:
+ rol dx, #4 ! rotate so that lowest 4 bits are used
+ mov ah, #0xe
+ mov al, dl ! mask off so we have only next nibble
+ and al, #0xf
+ add al, #0x30 ! convert to 0 based digit, '0'
+ cmp al, #0x39 ! check for overflow
+ jbe good_digit
+ add al, #0x41 - 0x30 - 0xa ! 'A' - '0' - 0xa
+
+good_digit:
+ int 0x10
+ loop print_digit
+ ret
+
+
+/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
@@ -233,7 +375,7 @@ bad_rt: mov ax,#0
kill_motor:
push dx
mov dx,#0x3f2
- mov al,#0
+ xor al, al
outb
pop dx
ret
@@ -243,10 +385,11 @@ sectors:
msg1:
.byte 13,10
- .ascii "Loading system ..."
- .byte 13,10,13,10
+ .ascii "Loading"
-.org 508
+.org 506
+swap_dev:
+ .word SWAP_DEV
root_dev:
.word ROOT_DEV
boot_flag:
@@ -258,3 +401,4 @@ endtext:
enddata:
.bss
endbss:
+
diff --git a/boot/setup.S b/boot/setup.S
new file mode 100644
index 0000000..093f96c
--- /dev/null
+++ b/boot/setup.S
@@ -0,0 +1,631 @@
+!
+! setup.s (C) 1991 Linus Torvalds
+!
+! setup.s is responsible for getting the system data from the BIOS,
+! and putting them into the appropriate places in system memory.
+! both setup.s and system has been loaded by the bootblock.
+!
+! This code asks the bios for memory/disk/other parameters, and
+! puts them in a "safe" place: 0x90000-0x901FF, ie where the
+! boot-block used to be. It is then up to the protected mode
+! system to read them from there before the area is overwritten
+! for buffer-blocks.
+!
+
+! NOTE! These had better be the same as in bootsect.s!
+#include <linux/config.h>
+
+INITSEG = DEF_INITSEG ! we move boot here - out of the way
+SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
+SETUPSEG = DEF_SETUPSEG ! this is the current segment
+
+.globl begtext, begdata, begbss, endtext, enddata, endbss
+.text
+begtext:
+.data
+begdata:
+.bss
+begbss:
+.text
+
+entry start
+start:
+
+! ok, the read went well so we get current cursor position and save it for
+! posterity.
+
+ mov ax,#INITSEG ! this is done in bootsect already, but...
+ mov ds,ax
+
+! Get memory size (extended mem, kB)
+
+ mov ah,#0x88
+ int 0x15
+ mov [2],ax
+
+! check for EGA/VGA and some config parameters
+
+ mov ah,#0x12
+ mov bl,#0x10
+ int 0x10
+ mov [8],ax
+ mov [10],bx
+ mov [12],cx
+ mov ax,#0x5019
+ cmp bl,#0x10
+ je novga
+ call chsvga
+novga: mov [14],ax
+ mov ah,#0x03 ! read cursor pos
+ xor bh,bh
+ int 0x10 ! save it in known place, con_init fetches
+ mov [0],dx ! it from 0x90000.
+
+! Get video-card data:
+
+ mov ah,#0x0f
+ int 0x10
+ mov [4],bx ! bh = display page
+ mov [6],ax ! al = video mode, ah = window width
+
+! Get hd0 data
+
+ mov ax,#0x0000
+ mov ds,ax
+ lds si,[4*0x41]
+ mov ax,#INITSEG
+ mov es,ax
+ mov di,#0x0080
+ mov cx,#0x10
+ rep
+ movsb
+
+! Get hd1 data
+
+ mov ax,#0x0000
+ mov ds,ax
+ lds si,[4*0x46]
+ mov ax,#INITSEG
+ mov es,ax
+ mov di,#0x0090
+ mov cx,#0x10
+ rep
+ movsb
+
+! Check that there IS a hd1 :-)
+
+ mov ax,#0x01500
+ mov dl,#0x81
+ int 0x13
+ jc no_disk1
+ cmp ah,#3
+ je is_disk1
+no_disk1:
+ mov ax,#INITSEG
+ mov es,ax
+ mov di,#0x0090
+ mov cx,#0x10
+ mov ax,#0x00
+ rep
+ stosb
+is_disk1:
+
+! now we want to move to protected mode ...
+
+ cli ! no interrupts allowed !
+
+! first we move the system to it's rightful place
+
+ mov ax,#0x0000
+ cld ! 'direction'=0, movs moves forward
+do_move:
+ mov es,ax ! destination segment
+ add ax,#0x1000
+ cmp ax,#0x9000
+ jz end_move
+ mov ds,ax ! source segment
+ sub di,di
+ sub si,si
+ mov cx,#0x8000
+ rep
+ movsw
+ jmp do_move
+
+! then we load the segment descriptors
+
+end_move:
+ mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
+ mov ds,ax
+ lidt idt_48 ! load idt with 0,0
+ lgdt gdt_48 ! load gdt with whatever appropriate
+
+! that was painless, now we enable A20
+
+ call empty_8042
+ mov al,#0xD1 ! command write
+ out #0x64,al
+ call empty_8042
+ mov al,#0xDF ! A20 on
+ out #0x60,al
+ call empty_8042
+
+! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
+! we put them right after the intel-reserved hardware interrupts, at
+! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
+! messed this up with the original PC, and they haven't been able to
+! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
+! which is used for the internal hardware interrupts as well. We just
+! have to reprogram the 8259's, and it isn't fun.
+
+ mov al,#0x11 ! initialization sequence
+ out #0x20,al ! send it to 8259A-1
+ .word 0x00eb,0x00eb ! jmp $+2, jmp $+2
+ out #0xA0,al ! and to 8259A-2
+ .word 0x00eb,0x00eb
+ mov al,#0x20 ! start of hardware int's (0x20)
+ out #0x21,al
+ .word 0x00eb,0x00eb
+ mov al,#0x28 ! start of hardware int's 2 (0x28)
+ out #0xA1,al
+ .word 0x00eb,0x00eb
+ mov al,#0x04 ! 8259-1 is master
+ out #0x21,al
+ .word 0x00eb,0x00eb
+ mov al,#0x02 ! 8259-2 is slave
+ out #0xA1,al
+ .word 0x00eb,0x00eb
+ mov al,#0x01 ! 8086 mode for both
+ out #0x21,al
+ .word 0x00eb,0x00eb
+ out #0xA1,al
+ .word 0x00eb,0x00eb
+ mov al,#0xFF ! mask off all interrupts for now
+ out #0x21,al
+ .word 0x00eb,0x00eb
+ out #0xA1,al
+
+! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
+! need no steenking BIOS anyway (except for the initial loading :-).
+! The BIOS-routine wants lots of unnecessary data, and it's less
+! "interesting" anyway. This is how REAL programmers do it.
+!
+! Well, now's the time to actually move into protected mode. To make
+! things as simple as possible, we do no register set-up or anything,
+! we let the gnu-compiled 32-bit programs do that. We just jump to
+! absolute address 0x00000, in 32-bit protected mode.
+
+ mov ax,#0x0001 ! protected mode (PE) bit
+ lmsw ax ! This is it!
+ jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
+
+! This routine checks that the keyboard command queue is empty
+! No timeout is used - if this hangs there is something wrong with
+! the machine, and we probably couldn't proceed anyway.
+empty_8042:
+ .word 0x00eb,0x00eb
+ in al,#0x64 ! 8042 status port
+ test al,#2 ! is input buffer full?
+ jnz empty_8042 ! yes - loop
+ ret
+
+! Routine trying to recognize type of SVGA-board present (if any)
+! and if it recognize one gives the choices of resolution it offers.
+! If one is found the resolution chosen is given by al,ah (rows,cols).
+
+chsvga: cld
+ push ds
+ push cs
+ pop ds
+ mov ax,#0xc000
+ mov es,ax
+ lea si,msg1
+ call prtstr
+nokey: in al,#0x60
+ cmp al,#0x82
+ jb nokey
+ cmp al,#0xe0
+ ja nokey
+ cmp al,#0x9c
+ je svga
+ mov ax,#0x5019
+ pop ds
+ ret
+svga: lea si,idati ! Check ATI 'clues'
+ mov di,#0x31
+ mov cx,#0x09
+ repe
+ cmpsb
+ jne noati
+ lea si,dscati
+ lea di,moati
+ lea cx,selmod
+ jmp cx
+noati: mov ax,#0x200f ! Check Ahead 'clues'
+ mov dx,#0x3ce
+ out dx,ax
+ inc dx
+ in al,dx
+ cmp al,#0x20
+ je isahed
+ cmp al,#0x21
+ jne noahed
+isahed: lea si,dscahead
+ lea di,moahead
+ lea cx,selmod
+ jmp cx
+noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues'
+ in al,dx
+ or al,#0x10
+ out dx,al
+ mov dx,#0x104
+ in al,dx
+ mov bl,al
+ mov dx,#0x3c3
+ in al,dx
+ and al,#0xef
+ out dx,al
+ cmp bl,[idcandt]
+ jne nocant
+ lea si,dsccandt
+ lea di,mocandt
+ lea cx,selmod
+ jmp cx
+nocant: mov dx,#0x3d4 ! Check Cirrus 'clues'
+ mov al,#0x0c
+ out dx,al
+ inc dx
+ in al,dx
+ mov bl,al
+ xor al,al
+ out dx,al
+ dec dx
+ mov al,#0x1f
+ out dx,al
+ inc dx
+ in al,dx
+ mov bh,al
+ xor ah,ah
+ shl al,#4
+ mov cx,ax
+ mov al,bh
+ shr al,#4
+ add cx,ax
+ shl cx,#8
+ add cx,#6
+ mov ax,cx
+ mov dx,#0x3c4
+ out dx,ax
+ inc dx
+ in al,dx
+ and al,al
+ jnz nocirr
+ mov al,bh
+ out dx,al
+ in al,dx
+ cmp al,#0x01
+ jne nocirr
+ call rst3d4
+ lea si,dsccirrus
+ lea di,mocirrus
+ lea cx,selmod
+ jmp cx
+rst3d4: mov dx,#0x3d4
+ mov al,bl
+ xor ah,ah
+ shl ax,#8
+ add ax,#0x0c
+ out dx,ax
+ ret
+nocirr: call rst3d4 ! Check Everex 'clues'
+ mov ax,#0x7000
+ xor bx,bx
+ int 0x10
+ cmp al,#0x70
+ jne noevrx
+ shr dx,#4
+ cmp dx,#0x678
+ je istrid
+ cmp dx,#0x236
+ je istrid
+ lea si,dsceverex
+ lea di,moeverex
+ lea cx,selmod
+ jmp cx
+istrid: lea cx,ev2tri
+ jmp cx
+noevrx: lea si,idgenoa ! Check Genoa 'clues'
+ xor ax,ax
+ seg es
+ mov al,[0x37]
+ mov di,ax
+ mov cx,#0x04
+ dec si
+ dec di
+l1: inc si
+ inc di
+ mov al,(si)
+ seg es
+ and al,(di)
+ cmp al,(si)
+ loope l1
+ cmp cx,#0x00
+ jne nogen
+ lea si,dscgenoa
+ lea di,mogenoa
+ lea cx,selmod
+ jmp cx
+nogen: lea si,idparadise ! Check Paradise 'clues'
+ mov di,#0x7d
+ mov cx,#0x04
+ repe
+ cmpsb
+ jne nopara
+ lea si,dscparadise
+ lea di,moparadise
+ lea cx,selmod
+ jmp cx
+nopara: mov dx,#0x3c4 ! Check Trident 'clues'
+ mov al,#0x0e
+ out dx,al
+ inc dx
+ in al,dx
+ xchg ah,al
+ mov al,#0x00
+ out dx,al
+ in al,dx
+ xchg al,ah
+ mov bl,al ! Strange thing ... in the book this wasn't
+ and bl,#0x02 ! necessary but it worked on my card which
+ jz setb2 ! is a trident. Without it the screen goes
+ and al,#0xfd ! blurred ...
+ jmp clrb2 !
+setb2: or al,#0x02 !
+clrb2: out dx,al
+ and ah,#0x0f
+ cmp ah,#0x02
+ jne notrid
+ev2tri: lea si,dsctrident
+ lea di,motrident
+ lea cx,selmod
+ jmp cx
+notrid: mov dx,#0x3cd ! Check Tseng 'clues'
+ in al,dx ! Could things be this simple ! :-)
+ mov bl,al
+ mov al,#0x55
+ out dx,al
+ in al,dx
+ mov ah,al
+ mov al,bl
+ out dx,al
+ cmp ah,#0x55
+ jne notsen
+ lea si,dsctseng
+ lea di,motseng
+ lea cx,selmod
+ jmp cx
+notsen: mov dx,#0x3cc ! Check Video7 'clues'
+ in al,dx
+ mov dx,#0x3b4
+ and al,#0x01
+ jz even7
+ mov dx,#0x3d4
+even7: mov al,#0x0c
+ out dx,al
+ inc dx
+ in al,dx
+ mov bl,al
+ mov al,#0x55
+ out dx,al
+ in al,dx
+ dec dx
+ mov al,#0x1f
+ out dx,al
+ inc dx
+ in al,dx
+ mov bh,al
+ dec dx
+ mov al,#0x0c
+ out dx,al
+ inc dx
+ mov al,bl
+ out dx,al
+ mov al,#0x55
+ xor al,#0xea
+ cmp al,bh
+ jne novid7
+ lea si,dscvideo7
+ lea di,movideo7
+selmod: push si
+ lea si,msg2
+ call prtstr
+ xor cx,cx
+ mov cl,(di)
+ pop si
+ push si
+ push cx
+tbl: pop bx
+ push bx
+ mov al,bl
+ sub al,cl
+ call dprnt
+ call spcing
+ lodsw
+ xchg al,ah
+ call dprnt
+ xchg ah,al
+ push ax
+ mov al,#0x78
+ call prnt1
+ pop ax
+ call dprnt
+ call docr
+ loop tbl
+ pop cx
+ call docr
+ lea si,msg3
+ call prtstr
+ pop si
+ add cl,#0x80
+nonum: in al,#0x60 ! Quick and dirty...
+ cmp al,#0x82
+ jb nonum
+ cmp al,#0x8b
+ je zero
+ cmp al,cl
+ ja nonum
+ jmp nozero
+zero: sub al,#0x0a
+nozero: sub al,#0x80
+ dec al
+ xor ah,ah
+ add di,ax
+ inc di
+ push ax
+ mov al,(di)
+ int 0x10
+ pop ax
+ shl ax,#1
+ add si,ax
+ lodsw
+ pop ds
+ ret
+novid7: pop ds ! Here could be code to support standard 80x50,80x30
+ mov ax,#0x5019
+ ret
+
+! Routine that 'tabs' to next col.
+
+spcing: mov al,#0x2e
+ call prnt1
+ mov al,#0x20
+ call prnt1
+ mov al,#0x20
+ call prnt1
+ mov al,#0x20
+ call prnt1
+ mov al,#0x20
+ call prnt1
+ ret
+
+! Routine to print asciiz-string at DS:SI
+
+prtstr: lodsb
+ and al,al
+ jz fin
+ call prnt1
+ jmp prtstr
+fin: ret
+
+! Routine to print a decimal value on screen, the value to be
+! printed is put in al (i.e 0-255).
+
+dprnt: push ax
+ push cx
+ mov ah,#0x00
+ mov cl,#0x0a
+ idiv cl
+ cmp al,#0x09
+ jbe lt100
+ call dprnt
+ jmp skip10
+lt100: add al,#0x30
+ call prnt1
+skip10: mov al,ah
+ add al,#0x30
+ call prnt1
+ pop cx
+ pop ax
+ ret
+
+! Part of above routine, this one just prints ascii al
+
+prnt1: push ax
+ push cx
+ mov bh,#0x00
+ mov cx,#0x01
+ mov ah,#0x0e
+ int 0x10
+ pop cx
+ pop ax
+ ret
+
+! Prints <CR> + <LF>
+
+docr: push ax
+ push cx
+ mov bh,#0x00
+ mov ah,#0x0e
+ mov al,#0x0a
+ mov cx,#0x01
+ int 0x10
+ mov al,#0x0d
+ int 0x10
+ pop cx
+ pop ax
+ ret
+
+gdt:
+ .word 0,0,0,0 ! dummy
+
+ .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
+ .word 0x0000 ! base address=0
+ .word 0x9A00 ! code read/exec
+ .word 0x00C0 ! granularity=4096, 386
+
+ .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
+ .word 0x0000 ! base address=0
+ .word 0x9200 ! data read/write
+ .word 0x00C0 ! granularity=4096, 386
+
+idt_48:
+ .word 0 ! idt limit=0
+ .word 0,0 ! idt base=0L
+
+gdt_48:
+ .word 0x800 ! gdt limit=2048, 256 GDT entries
+ .word 512+gdt,0x9 ! gdt base = 0X9xxxx
+
+msg1: .ascii "Press <RETURN> to see SVGA-modes available or any other key to continue."
+ db 0x0d, 0x0a, 0x0a, 0x00
+msg2: .ascii "Mode: COLSxROWS:"
+ db 0x0d, 0x0a, 0x0a, 0x00
+msg3: .ascii "Choose mode by pressing the corresponding number."
+ db 0x0d, 0x0a, 0x00
+
+idati: .ascii "761295520"
+idcandt: .byte 0xa5
+idgenoa: .byte 0x77, 0x00, 0x66, 0x99
+idparadise: .ascii "VGA="
+
+! Manufacturer: Numofmodes: Mode:
+
+moati: .byte 0x02, 0x23, 0x33
+moahead: .byte 0x05, 0x22, 0x23, 0x24, 0x2f, 0x34
+mocandt: .byte 0x02, 0x60, 0x61
+mocirrus: .byte 0x04, 0x1f, 0x20, 0x22, 0x31
+moeverex: .byte 0x0a, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
+mogenoa: .byte 0x0a, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
+moparadise: .byte 0x02, 0x55, 0x54
+motrident: .byte 0x07, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
+motseng: .byte 0x05, 0x26, 0x2a, 0x23, 0x24, 0x22
+movideo7: .byte 0x06, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45
+
+! msb = Cols lsb = Rows:
+
+dscati: .word 0x8419, 0x842c
+dscahead: .word 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
+dsccandt: .word 0x8419, 0x8432
+dsccirrus: .word 0x8419, 0x842c, 0x841e, 0x6425
+dsceverex: .word 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
+dscgenoa: .word 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
+dscparadise: .word 0x8419, 0x842b
+dsctrident: .word 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
+dsctseng: .word 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
+dscvideo7: .word 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
+
+.text
+endtext:
+.data
+enddata:
+.bss
+endbss:
diff --git a/boot/setup.s b/boot/setup.s
deleted file mode 100644
index 2329d00..0000000
--- a/boot/setup.s
+++ /dev/null
@@ -1,231 +0,0 @@
-!
-! setup.s (C) 1991 Linus Torvalds
-!
-! setup.s is responsible for getting the system data from the BIOS,
-! and putting them into the appropriate places in system memory.
-! both setup.s and system has been loaded by the bootblock.
-!
-! This code asks the bios for memory/disk/other parameters, and
-! puts them in a "safe" place: 0x90000-0x901FF, ie where the
-! boot-block used to be. It is then up to the protected mode
-! system to read them from there before the area is overwritten
-! for buffer-blocks.
-!
-
-! NOTE! These had better be the same as in bootsect.s!
-
-INITSEG = 0x9000 ! we move boot here - out of the way
-SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
-SETUPSEG = 0x9020 ! this is the current segment
-
-.globl begtext, begdata, begbss, endtext, enddata, endbss
-.text
-begtext:
-.data
-begdata:
-.bss
-begbss:
-.text
-
-entry start
-start:
-
-! ok, the read went well so we get current cursor position and save it for
-! posterity.
-
- mov ax,#INITSEG ! this is done in bootsect already, but...
- mov ds,ax
- mov ah,#0x03 ! read cursor pos
- xor bh,bh
- int 0x10 ! save it in known place, con_init fetches
- mov [0],dx ! it from 0x90000.
-
-! Get memory size (extended mem, kB)
-
- mov ah,#0x88
- int 0x15
- mov [2],ax
-
-! Get video-card data:
-
- mov ah,#0x0f
- int 0x10
- mov [4],bx ! bh = display page
- mov [6],ax ! al = video mode, ah = window width
-
-! check for EGA/VGA and some config parameters
-
- mov ah,#0x12
- mov bl,#0x10
- int 0x10
- mov [8],ax
- mov [10],bx
- mov [12],cx
-
-! Get hd0 data
-
- mov ax,#0x0000
- mov ds,ax
- lds si,[4*0x41]
- mov ax,#INITSEG
- mov es,ax
- mov di,#0x0080
- mov cx,#0x10
- rep
- movsb
-
-! Get hd1 data
-
- mov ax,#0x0000
- mov ds,ax
- lds si,[4*0x46]
- mov ax,#INITSEG
- mov es,ax
- mov di,#0x0090
- mov cx,#0x10
- rep
- movsb
-
-! Check that there IS a hd1 :-)
-
- mov ax,#0x01500
- mov dl,#0x81
- int 0x13
- jc no_disk1
- cmp ah,#3
- je is_disk1
-no_disk1:
- mov ax,#INITSEG
- mov es,ax
- mov di,#0x0090
- mov cx,#0x10
- mov ax,#0x00
- rep
- stosb
-is_disk1:
-
-! now we want to move to protected mode ...
-
- cli ! no interrupts allowed !
-
-! first we move the system to it's rightful place
-
- mov ax,#0x0000
- cld ! 'direction'=0, movs moves forward
-do_move:
- mov es,ax ! destination segment
- add ax,#0x1000
- cmp ax,#0x9000
- jz end_move
- mov ds,ax ! source segment
- sub di,di
- sub si,si
- mov cx,#0x8000
- rep
- movsw
- jmp do_move
-
-! then we load the segment descriptors
-
-end_move:
- mov ax,#SETUPSEG ! right, forgot this at first. didn't work :-)
- mov ds,ax
- lidt idt_48 ! load idt with 0,0
- lgdt gdt_48 ! load gdt with whatever appropriate
-
-! that was painless, now we enable A20
-
- call empty_8042
- mov al,#0xD1 ! command write
- out #0x64,al
- call empty_8042
- mov al,#0xDF ! A20 on
- out #0x60,al
- call empty_8042
-
-! well, that went ok, I hope. Now we have to reprogram the interrupts :-(
-! we put them right after the intel-reserved hardware interrupts, at
-! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
-! messed this up with the original PC, and they haven't been able to
-! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
-! which is used for the internal hardware interrupts as well. We just
-! have to reprogram the 8259's, and it isn't fun.
-
- mov al,#0x11 ! initialization sequence
- out #0x20,al ! send it to 8259A-1
- .word 0x00eb,0x00eb ! jmp $+2, jmp $+2
- out #0xA0,al ! and to 8259A-2
- .word 0x00eb,0x00eb
- mov al,#0x20 ! start of hardware int's (0x20)
- out #0x21,al
- .word 0x00eb,0x00eb
- mov al,#0x28 ! start of hardware int's 2 (0x28)
- out #0xA1,al
- .word 0x00eb,0x00eb
- mov al,#0x04 ! 8259-1 is master
- out #0x21,al
- .word 0x00eb,0x00eb
- mov al,#0x02 ! 8259-2 is slave
- out #0xA1,al
- .word 0x00eb,0x00eb
- mov al,#0x01 ! 8086 mode for both
- out #0x21,al
- .word 0x00eb,0x00eb
- out #0xA1,al
- .word 0x00eb,0x00eb
- mov al,#0xFF ! mask off all interrupts for now
- out #0x21,al
- .word 0x00eb,0x00eb
- out #0xA1,al
-
-! well, that certainly wasn't fun :-(. Hopefully it works, and we don't
-! need no steenking BIOS anyway (except for the initial loading :-).
-! The BIOS-routine wants lots of unnecessary data, and it's less
-! "interesting" anyway. This is how REAL programmers do it.
-!
-! Well, now's the time to actually move into protected mode. To make
-! things as simple as possible, we do no register set-up or anything,
-! we let the gnu-compiled 32-bit programs do that. We just jump to
-! absolute address 0x00000, in 32-bit protected mode.
-
- mov ax,#0x0001 ! protected mode (PE) bit
- lmsw ax ! This is it!
- jmpi 0,8 ! jmp offset 0 of segment 8 (cs)
-
-! This routine checks that the keyboard command queue is empty
-! No timeout is used - if this hangs there is something wrong with
-! the machine, and we probably couldn't proceed anyway.
-empty_8042:
- .word 0x00eb,0x00eb
- in al,#0x64 ! 8042 status port
- test al,#2 ! is input buffer full?
- jnz empty_8042 ! yes - loop
- ret
-
-gdt:
- .word 0,0,0,0 ! dummy
-
- .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
- .word 0x0000 ! base address=0
- .word 0x9A00 ! code read/exec
- .word 0x00C0 ! granularity=4096, 386
-
- .word 0x07FF ! 8Mb - limit=2047 (2048*4096=8Mb)
- .word 0x0000 ! base address=0
- .word 0x9200 ! data read/write
- .word 0x00C0 ! granularity=4096, 386
-
-idt_48:
- .word 0 ! idt limit=0
- .word 0,0 ! idt base=0L
-
-gdt_48:
- .word 0x800 ! gdt limit=2048, 256 GDT entries
- .word 512+gdt,0x9 ! gdt base = 0X9xxxx
-
-.text
-endtext:
-.data
-enddata:
-.bss
-endbss:
diff --git a/fs/Makefile b/fs/Makefile
index 97a388e..96d8de1 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -3,7 +3,7 @@ AS =gas
CC =gcc
LD =gld
CFLAGS =-Wall -O -fstrength-reduce -fcombine-regs -fomit-frame-pointer \
- -mstring-insns -nostdinc -I../include
+ -fno-defer-pop -mstring-insns -nostdinc -I../include
CPP =gcc -E -nostdinc -I../include
.c.s:
@@ -17,7 +17,7 @@ CPP =gcc -E -nostdinc -I../include
OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \
block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \
- bitmap.o fcntl.o ioctl.o truncate.o
+ bitmap.o fcntl.o ioctl.o truncate.o select.o
fs.o: $(OBJS)
$(LD) -r -o fs.o $(OBJS)
@@ -34,67 +34,96 @@ dep:
### Dependencies:
bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.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
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/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
- ../include/asm/segment.h ../include/asm/system.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/sched.h ../include/linux/head.h ../include/linux/fs.h \
- ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \
- ../include/linux/kernel.h ../include/asm/system.h ../include/asm/io.h
+ ../include/sys/types.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
char_dev.o : char_dev.c ../include/errno.h ../include/sys/types.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
- ../include/asm/segment.h ../include/asm/io.h
-exec.o : exec.c ../include/errno.h ../include/string.h \
- ../include/sys/stat.h ../include/sys/types.h ../include/a.out.h \
- ../include/linux/fs.h ../include/linux/sched.h ../include/linux/head.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
- ../include/asm/segment.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/io.h
+exec.o : exec.c ../include/signal.h ../include/sys/types.h \
+ ../include/errno.h ../include/string.h ../include/sys/stat.h \
+ ../include/a.out.h ../include/linux/fs.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/string.h ../include/errno.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
- ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \
- ../include/linux/kernel.h ../include/asm/segment.h ../include/fcntl.h \
- ../include/sys/stat.h
+ ../include/sys/types.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_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \
../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
- ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \
- ../include/linux/kernel.h ../include/asm/segment.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/asm/segment.h
file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h
inode.o : inode.c ../include/string.h ../include/sys/stat.h \
../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
- ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \
- ../include/linux/kernel.h ../include/asm/system.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/asm/system.h
ioctl.o : ioctl.c ../include/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/linux/mm.h \
- ../include/signal.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/linux/mm.h \
- ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h \
- ../include/string.h ../include/fcntl.h ../include/errno.h \
- ../include/const.h ../include/sys/stat.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/string.h ../include/fcntl.h \
+ ../include/errno.h ../include/const.h ../include/sys/stat.h
open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \
../include/sys/types.h ../include/utime.h ../include/sys/stat.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \
- ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.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
pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \
- ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
- ../include/linux/mm.h ../include/asm/segment.h
+ ../include/errno.h ../include/termios.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.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
read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \
../include/errno.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/asm/segment.h
+ ../include/signal.h ../include/sys/param.h ../include/sys/time.h \
+ ../include/time.h ../include/sys/resource.h ../include/asm/segment.h
+select.o : select.c ../include/linux/fs.h ../include/sys/types.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/asm/segment.h \
+ ../include/asm/system.h ../include/sys/stat.h ../include/string.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/linux/sched.h \
- ../include/linux/head.h ../include/linux/mm.h ../include/signal.h \
- ../include/linux/kernel.h ../include/asm/segment.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/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
- ../include/asm/system.h ../include/errno.h ../include/sys/stat.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/errno.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/linux/mm.h \
- ../include/signal.h ../include/sys/stat.h
+ ../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
+ ../include/sys/time.h ../include/time.h ../include/sys/resource.h \
+ ../include/sys/stat.h
diff --git a/fs/bitmap.c b/fs/bitmap.c
index a3fc362..ec733d0 100644
--- a/fs/bitmap.c
+++ b/fs/bitmap.c
@@ -44,7 +44,7 @@ __asm__("cld\n" \
:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
__res;})
-void free_block(int dev, int block)
+int free_block(int dev, int block)
{
struct super_block * sb;
struct buffer_head * bh;
@@ -55,21 +55,22 @@ void free_block(int dev, int block)
panic("trying to free block not in datazone");
bh = get_hash_table(dev,block);
if (bh) {
- if (bh->b_count != 1) {
- printk("trying to free block (%04x:%d), count=%d\n",
- dev,block,bh->b_count);
- return;
+ if (bh->b_count > 1) {
+ brelse(bh);
+ return 0;
}
bh->b_dirt=0;
bh->b_uptodate=0;
- brelse(bh);
+ if (bh->b_count)
+ brelse(bh);
}
block -= sb->s_firstdatazone - 1 ;
if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
- panic("free_block: bit already cleared");
+ printk("free_block: bit already cleared\n");
}
sb->s_zmap[block/8192]->b_dirt = 1;
+ return 1;
}
int new_block(int dev)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index a50ae3f..af7cbda 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -11,16 +11,25 @@
#include <asm/segment.h>
#include <asm/system.h>
+extern int *blk_size[];
+
int block_write(int dev, long * pos, char * buf, int count)
{
int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos & (BLOCK_SIZE-1);
int chars;
int written = 0;
+ int size;
struct buffer_head * bh;
register char * p;
+ if (blk_size[MAJOR(dev)])
+ size = blk_size[MAJOR(dev)][MINOR(dev)];
+ else
+ size = 0x7fffffff;
while (count>0) {
+ if (block >= size)
+ return written?written:-EIO;
chars = BLOCK_SIZE - offset;
if (chars > count)
chars=count;
@@ -49,11 +58,18 @@ int block_read(int dev, unsigned long * pos, char * buf, int count)
int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos & (BLOCK_SIZE-1);
int chars;
+ int size;
int read = 0;
struct buffer_head * bh;
register char * p;
+ if (blk_size[MAJOR(dev)])
+ size = blk_size[MAJOR(dev)][MINOR(dev)];
+ else
+ size = 0x7fffffff;
while (count>0) {
+ if (block >= size)
+ return read?read:-EIO;
chars = BLOCK_SIZE-offset;
if (chars > count)
chars = count;
diff --git a/fs/exec.c b/fs/exec.c
index 419f248..d5136f2 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -17,6 +17,7 @@
* was less than 2 hours work to get demand-loading completely implemented.
*/
+#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
@@ -38,6 +39,28 @@ extern int sys_close(int fd);
*/
#define MAX_ARG_PAGES 32
+int sys_uselib(const char * library)
+{
+ struct m_inode * inode;
+ unsigned long base;
+
+ if (get_limit(0x17) != TASK_SIZE)
+ return -EINVAL;
+ if (library) {
+ if (!(inode=namei(library))) /* get library inode */
+ return -ENOENT;
+ } else
+ inode = NULL;
+/* we should check filetypes (headers etc), but we don't */
+ iput(current->library);
+ current->library = NULL;
+ base = get_base(current->ldt[2]);
+ base += LIBRARY_OFFSET;
+ free_page_tables(base,LIBRARY_SIZE);
+ current->library = inode;
+ return 0;
+}
+
/*
* create_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
@@ -156,9 +179,8 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
unsigned long code_limit,data_limit,code_base,data_base;
int i;
- code_limit = text_size+PAGE_SIZE -1;
- code_limit &= 0xFFFFF000;
- data_limit = 0x4000000;
+ code_limit = TASK_SIZE;
+ data_limit = TASK_SIZE;
code_base = get_base(current->ldt[1]);
data_base = code_base;
set_base(current->ldt[1],code_base);
@@ -167,17 +189,20 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
set_limit(current->ldt[2],data_limit);
/* make sure fs points to the NEW data segment */
__asm__("pushl $0x17\n\tpop %%fs"::);
- data_base += data_limit;
+ data_base += data_limit - LIBRARY_SIZE;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
if (page[i])
- put_page(page[i],data_base);
+ put_dirty_page(page[i],data_base);
}
return data_limit;
}
/*
* 'do_execve()' executes a new program.
+ *
+ * NOTE! We leave 4MB free at the top of the data-area for a loadable
+ * library.
*/
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
@@ -211,7 +236,7 @@ restart_interp:
e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
if (current->euid == inode->i_uid)
i >>= 6;
- else if (current->egid == inode->i_gid)
+ else if (in_group_p(inode->i_gid))
i >>= 3;
if (!(i & 1) &&
!((inode->i_mode & 0111) && suser())) {
@@ -229,13 +254,13 @@ restart_interp:
* Sorta complicated, but hopefully it will work. -TYT
*/
- char buf[1023], *cp, *interp, *i_name, *i_arg;
+ char buf[128], *cp, *interp, *i_name, *i_arg;
unsigned long old_fs;
- strncpy(buf, bh->b_data+2, 1022);
+ strncpy(buf, bh->b_data+2, 127);
brelse(bh);
iput(inode);
- buf[1022] = '\0';
+ buf[127] = '\0';
if (cp = strchr(buf, '\n')) {
*cp = '\0';
for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
@@ -316,11 +341,17 @@ restart_interp:
}
}
/* OK, This is the point of no return */
+/* note that current->library stays unchanged by an exec */
if (current->executable)
iput(current->executable);
current->executable = inode;
- for (i=0 ; i<32 ; i++)
- current->sigaction[i].sa_handler = NULL;
+ current->signal = 0;
+ for (i=0 ; i<32 ; i++) {
+ current->sigaction[i].sa_mask = 0;
+ current->sigaction[i].sa_flags = 0;
+ if (current->sigaction[i].sa_handler != SIG_IGN)
+ current->sigaction[i].sa_handler = NULL;
+ }
for (i=0 ; i<NR_OPEN ; i++)
if ((current->close_on_exec>>i)&1)
sys_close(i);
@@ -330,17 +361,15 @@ restart_interp:
if (last_task_used_math == current)
last_task_used_math = NULL;
current->used_math = 0;
- p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
+ p += change_ldt(ex.a_text,page);
+ p -= LIBRARY_SIZE + MAX_ARG_PAGES*PAGE_SIZE;
p = (unsigned long) create_tables((char *)p,argc,envc);
current->brk = ex.a_bss +
(current->end_data = ex.a_data +
(current->end_code = ex.a_text));
current->start_stack = p & 0xfffff000;
- current->euid = e_uid;
- current->egid = e_gid;
- i = ex.a_text+ex.a_data;
- while (i&0xfff)
- put_fs_byte(0,(char *) (i++));
+ current->suid = current->euid = e_uid;
+ current->sgid = current->egid = e_gid;
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
return 0;
diff --git a/fs/inode.c b/fs/inode.c
index 85c4107..5c56663 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -12,6 +12,8 @@
#include <linux/mm.h>
#include <asm/system.h>
+extern int *blk_size[];
+
struct m_inode inode_table[NR_INODE]={{0,},};
static void read_inode(struct m_inode * inode);
@@ -156,6 +158,7 @@ void iput(struct m_inode * inode)
panic("iput: trying to free free inode");
if (inode->i_pipe) {
wake_up(&inode->i_wait);
+ wake_up(&inode->i_wait2);
if (--inode->i_count)
return;
free_page(inode->i_size);
@@ -308,6 +311,13 @@ static void read_inode(struct m_inode * inode)
((struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK];
brelse(bh);
+ if (S_ISBLK(inode->i_mode)) {
+ int i = inode->i_zone[0];
+ if (blk_size[MAJOR(i)])
+ inode->i_size = 1024*blk_size[MAJOR(i)][MINOR(i)];
+ else
+ inode->i_size = 0x7fffffff;
+ }
unlock_inode(inode);
}
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 36fc976..c7b41eb 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -11,6 +11,7 @@
#include <linux/sched.h>
extern int tty_ioctl(int dev, int cmd, int arg);
+extern int pipe_ioctl(struct m_inode *pino, int cmd, int arg);
typedef int (*ioctl_ptr)(int dev,int cmd,int arg);
@@ -34,6 +35,8 @@ int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
+ if (filp->f_inode->i_pipe)
+ return (filp->f_mode&1)?pipe_ioctl(filp->f_inode,cmd,arg):-EBADF;
mode=filp->f_inode->i_mode;
if (!S_ISCHR(mode) && !S_ISBLK(mode))
return -EINVAL;
diff --git a/fs/namei.c b/fs/namei.c
index fe2425c..8b99d70 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -18,6 +18,9 @@
#include <const.h>
#include <sys/stat.h>
+static struct m_inode * _namei(const char * filename, struct m_inode * base,
+ int follow_links);
+
#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
/*
@@ -46,7 +49,7 @@ static int permission(struct m_inode * inode,int mask)
return 0;
else if (current->euid==inode->i_uid)
mode >>= 6;
- else if (current->egid==inode->i_gid)
+ else if (in_group_p(inode->i_gid))
mode >>= 3;
if (((mode & mask & 0007) == mask) || suser())
return 1;
@@ -66,6 +69,9 @@ static int match(int len,const char * name,struct dir_entry * de)
if (!de || !de->inode || len > 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 < NAME_LEN && de->name[len])
return 0;
__asm__("cld\n\t"
@@ -106,8 +112,6 @@ static struct buffer_head * find_entry(struct m_inode ** dir,
#endif
entries = (*dir)->i_size / (sizeof (struct dir_entry));
*res_dir = NULL;
- if (!namelen)
- return NULL;
/* check for '..', as we might have to do some "magic" for it */
if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
/* '..' in a pseudo-root results in a faked '.' (just change namelen) */
@@ -219,33 +223,63 @@ static struct buffer_head * add_entry(struct m_inode * dir,
return NULL;
}
+static struct m_inode * follow_link(struct m_inode * dir, struct m_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 (fs != 0x17 || !inode->i_zone[0] ||
+ !(bh = bread(inode->i_dev, inode->i_zone[0]))) {
+ iput(dir);
+ iput(inode);
+ return NULL;
+ }
+ iput(inode);
+ __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
+ inode = _namei(bh->b_data,dir,0);
+ __asm__("mov %0,%%fs"::"r" (fs));
+ brelse(bh);
+ return inode;
+}
+
/*
* get_dir()
*
* Getdir traverses the pathname until it hits the topmost directory.
* It returns NULL on failure.
*/
-static struct m_inode * get_dir(const char * pathname)
+static struct m_inode * get_dir(const char * pathname, struct m_inode * inode)
{
char c;
const char * thisname;
- struct m_inode * inode;
struct buffer_head * bh;
- int namelen,inr,idev;
+ int namelen,inr;
struct dir_entry * de;
+ struct m_inode * dir;
- if (!current->root || !current->root->i_count)
- panic("No root inode");
- if (!current->pwd || !current->pwd->i_count)
- panic("No cwd inode");
+ if (!inode) {
+ inode = current->pwd;
+ inode->i_count++;
+ }
if ((c=get_fs_byte(pathname))=='/') {
+ iput(inode);
inode = current->root;
pathname++;
- } else if (c)
- inode = current->pwd;
- else
- return NULL; /* empty name is bad */
- inode->i_count++;
+ inode->i_count++;
+ }
while (1) {
thisname = pathname;
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
@@ -261,10 +295,13 @@ static struct m_inode * get_dir(const char * pathname)
return NULL;
}
inr = de->inode;
- idev = inode->i_dev;
brelse(bh);
- iput(inode);
- if (!(inode = iget(idev,inr)))
+ dir = inode;
+ if (!(inode = iget(dir->i_dev,inr))) {
+ iput(dir);
+ return NULL;
+ }
+ if (!(inode = follow_link(dir,inode)))
return NULL;
}
}
@@ -276,13 +313,13 @@ static struct m_inode * get_dir(const char * pathname)
* specified name, and the name within that directory.
*/
static struct m_inode * dir_namei(const char * pathname,
- int * namelen, const char ** name)
+ int * namelen, const char ** name, struct m_inode * base)
{
char c;
const char * basename;
struct m_inode * dir;
- if (!(dir = get_dir(pathname)))
+ if (!(dir = get_dir(pathname,base)))
return NULL;
basename = pathname;
while (c=get_fs_byte(pathname++))
@@ -293,40 +330,54 @@ static struct m_inode * dir_namei(const char * pathname,
return dir;
}
-/*
- * namei()
- *
- * is used by most simple commands to get the inode of a specified name.
- * Open, link etc use their own routines, but this is enough for things
- * like 'chmod' etc.
- */
-struct m_inode * namei(const char * pathname)
+struct m_inode * _namei(const char * pathname, struct m_inode * base,
+ int follow_links)
{
const char * basename;
- int inr,dev,namelen;
- struct m_inode * dir;
+ int inr,namelen;
+ struct m_inode * inode;
struct buffer_head * bh;
struct dir_entry * de;
- if (!(dir = dir_namei(pathname,&namelen,&basename)))
+ if (!(base = dir_namei(pathname,&namelen,&basename,base)))
return NULL;
if (!namelen) /* special case: '/usr/' etc */
- return dir;
- bh = find_entry(&dir,basename,namelen,&de);
+ return base;
+ bh = find_entry(&base,basename,namelen,&de);
if (!bh) {
- iput(dir);
+ iput(base);
return NULL;
}
inr = de->inode;
- dev = dir->i_dev;
brelse(bh);
- iput(dir);
- dir=iget(dev,inr);
- if (dir) {
- dir->i_atime=CURRENT_TIME;
- dir->i_dirt=1;
+ if (!(inode = iget(base->i_dev,inr))) {
+ iput(base);
+ return NULL;
}
- return dir;
+ if (follow_links)
+ inode = follow_link(base,inode);
+ else
+ iput(base);
+ inode->i_atime=CURRENT_TIME;
+ inode->i_dirt=1;
+ return inode;
+}
+
+struct m_inode * lnamei(const char * pathname)
+{
+ return _namei(pathname, NULL, 0);
+}
+
+/*
+ * namei()
+ *
+ * is used by most simple commands to get the inode of a specified name.
+ * Open, link etc use their own routines, but this is enough for things
+ * like 'chmod' etc.
+ */
+struct m_inode * namei(const char * pathname)
+{
+ return _namei(pathname,NULL,1);
}
/*
@@ -347,7 +398,7 @@ int open_namei(const char * pathname, int flag, int mode,
flag |= O_WRONLY;
mode &= 0777 & ~current->umask;
mode |= I_REGULAR;
- if (!(dir = dir_namei(pathname,&namelen,&basename)))
+ if (!(dir = dir_namei(pathname,&namelen,&basename,NULL)))
return -ENOENT;
if (!namelen) { /* special case: '/usr/' etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
@@ -392,10 +443,11 @@ int open_namei(const char * pathname, int flag, int mode,
inr = de->inode;
dev = dir->i_dev;
brelse(bh);
- iput(dir);
- if (flag & O_EXCL)
+ if (flag & O_EXCL) {
+ iput(dir);
return -EEXIST;
- if (!(inode=iget(dev,inr)))
+ }
+ if (!(inode = follow_link(dir,iget(dev,inr))))
return -EACCES;
if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
!permission(inode,ACC_MODE(flag))) {
@@ -419,7 +471,7 @@ int sys_mknod(const char * filename, int mode, int dev)
if (!suser())
return -EPERM;
- if (!(dir = dir_namei(filename,&namelen,&basename)))
+ if (!(dir = dir_namei(filename,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
@@ -468,9 +520,7 @@ int sys_mkdir(const char * pathname, int mode)
struct buffer_head * bh, *dir_block;
struct dir_entry * de;
- if (!suser())
- return -EPERM;
- if (!(dir = dir_namei(pathname,&namelen,&basename)))
+ if (!(dir = dir_namei(pathname,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
@@ -503,7 +553,6 @@ int sys_mkdir(const char * pathname, int mode)
inode->i_dirt = 1;
if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
iput(dir);
- free_block(inode->i_dev,inode->i_zone[0]);
inode->i_nlinks--;
iput(inode);
return -ERROR;
@@ -522,7 +571,6 @@ int sys_mkdir(const char * pathname, int mode)
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
- free_block(inode->i_dev,inode->i_zone[0]);
inode->i_nlinks=0;
iput(inode);
return -ENOSPC;
@@ -592,9 +640,7 @@ int sys_rmdir(const char * name)
struct buffer_head * bh;
struct dir_entry * de;
- if (!suser())
- return -EPERM;
- if (!(dir = dir_namei(name,&namelen,&basename)))
+ if (!(dir = dir_namei(name,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
@@ -668,7 +714,7 @@ int sys_unlink(const char * name)
struct buffer_head * bh;
struct dir_entry * de;
- if (!(dir = dir_namei(name,&namelen,&basename)))
+ if (!(dir = dir_namei(name,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
@@ -718,6 +764,76 @@ int sys_unlink(const char * name)
return 0;
}
+int sys_symlink(const char * oldname, const char * newname)
+{
+ struct dir_entry * de;
+ struct m_inode * dir, * inode;
+ struct buffer_head * bh, * name_block;
+ const char * basename;
+ int namelen, i;
+ char c;
+
+ dir = dir_namei(newname,&namelen,&basename, NULL);
+ if (!dir)
+ return -EACCES;
+ if (!namelen) {
+ iput(dir);
+ return -EPERM;
+ }
+ if (!permission(dir,MAY_WRITE)) {
+ iput(dir);
+ return -EACCES;
+ }
+ if (!(inode = new_inode(dir->i_dev))) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = S_IFLNK | (0777 & ~current->umask);
+ inode->i_dirt = 1;
+ if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
+ iput(dir);
+ inode->i_nlinks--;
+ iput(inode);
+ return -ENOSPC;
+ }
+ inode->i_dirt = 1;
+ if (!(name_block=bread(inode->i_dev,inode->i_zone[0]))) {
+ iput(dir);
+ inode->i_nlinks--;
+ iput(inode);
+ return -ERROR;
+ }
+ i = 0;
+ while (i < 1023 && (c=get_fs_byte(oldname++)))
+ 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 = find_entry(&dir,basename,namelen,&de);
+ if (bh) {
+ inode->i_nlinks--;
+ iput(inode);
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ bh = add_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ inode->i_nlinks--;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_num;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
int sys_link(const char * oldname, const char * newname)
{
struct dir_entry * de;
@@ -733,7 +849,7 @@ int sys_link(const char * oldname, const char * newname)
iput(oldinode);
return -EPERM;
}
- dir = dir_namei(newname,&namelen,&basename);
+ dir = dir_namei(newname,&namelen,&basename, NULL);
if (!dir) {
iput(oldinode);
return -EACCES;
diff --git a/fs/open.c b/fs/open.c
index 3695ff1..92c8973 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
+
#include <asm/segment.h>
int sys_ustat(int dev, struct ustat * ubuf)
@@ -135,6 +136,38 @@ int sys_chown(const char * filename,int uid,int gid)
return 0;
}
+static int check_char_dev(struct m_inode * inode, int dev, int flag)
+{
+ struct tty_struct *tty;
+ int min;
+
+ 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 (!(flag & O_NOCTTY) &&
+ current->leader &&
+ current->tty<0 &&
+ tty->session==0) {
+ current->tty = min;
+ tty->session= current->session;
+ tty->pgrp = current->pgrp;
+ }
+ if (flag & O_NONBLOCK) {
+ TTY_TABLE(min)->termios.c_cc[VMIN] =0;
+ TTY_TABLE(min)->termios.c_cc[VTIME] =0;
+ TTY_TABLE(min)->termios.c_lflag &= ~ICANON;
+ }
+ }
+ return 0;
+}
+
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
@@ -161,18 +194,12 @@ int sys_open(const char * filename,int flag,int mode)
}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
if (S_ISCHR(inode->i_mode))
- if (MAJOR(inode->i_zone[0])==4) {
- if (current->leader && current->tty<0) {
- current->tty = MINOR(inode->i_zone[0]);
- tty_table[current->tty].pgrp = current->pgrp;
- }
- } else if (MAJOR(inode->i_zone[0])==5)
- if (current->tty<0) {
- iput(inode);
- current->filp[fd]=NULL;
- f->f_count=0;
- return -EPERM;
- }
+ if (check_char_dev(inode,inode->i_zone[0],flag)) {
+ iput(inode);
+ current->filp[fd]=NULL;
+ f->f_count=0;
+ return -EAGAIN;
+ }
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
diff --git a/fs/pipe.c b/fs/pipe.c
index dfc4480..38ea99d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -5,10 +5,13 @@
*/
#include <signal.h>
+#include <errno.h>
+#include <termios.h>
#include <linux/sched.h>
#include <linux/mm.h> /* for get_free_page */
#include <asm/segment.h>
+#include <linux/kernel.h>
int read_pipe(struct m_inode * inode, char * buf, int count)
{
@@ -16,10 +19,12 @@ int read_pipe(struct m_inode * inode, char * buf, int count)
while (count>0) {
while (!(size=PIPE_SIZE(*inode))) {
- wake_up(&inode->i_wait);
+ wake_up(& PIPE_WRITE_WAIT(*inode));
if (inode->i_count != 2) /* are there any writers? */
return read;
- sleep_on(&inode->i_wait);
+ if (current->signal & ~current->blocked)
+ return read?read:-ERESTARTSYS;
+ interruptible_sleep_on(& PIPE_READ_WAIT(*inode));
}
chars = PAGE_SIZE-PIPE_TAIL(*inode);
if (chars > count)
@@ -34,7 +39,7 @@ int read_pipe(struct m_inode * inode, char * buf, int count)
while (chars-->0)
put_fs_byte(((char *)inode->i_size)[size++],buf++);
}
- wake_up(&inode->i_wait);
+ wake_up(& PIPE_WRITE_WAIT(*inode));
return read;
}
@@ -44,12 +49,12 @@ int write_pipe(struct m_inode * inode, char * buf, int count)
while (count>0) {
while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) {
- wake_up(&inode->i_wait);
+ wake_up(& PIPE_READ_WAIT(*inode));
if (inode->i_count != 2) { /* no readers */
current->signal |= (1<<(SIGPIPE-1));
return written?written:-1;
}
- sleep_on(&inode->i_wait);
+ sleep_on(& PIPE_WRITE_WAIT(*inode));
}
chars = PAGE_SIZE-PIPE_HEAD(*inode);
if (chars > count)
@@ -64,7 +69,7 @@ int write_pipe(struct m_inode * inode, char * buf, int count)
while (chars-->0)
((char *)inode->i_size)[size++]=get_fs_byte(buf++);
}
- wake_up(&inode->i_wait);
+ wake_up(& PIPE_READ_WAIT(*inode));
return written;
}
@@ -109,3 +114,15 @@ int sys_pipe(unsigned long * fildes)
put_fs_long(fd[1],1+fildes);
return 0;
}
+
+int pipe_ioctl(struct m_inode *pino, int cmd, int arg)
+{
+ switch (cmd) {
+ case FIONREAD:
+ verify_area((void *) arg,4);
+ put_fs_long(PIPE_SIZE(*pino),(unsigned long *) arg);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/fs/select.c b/fs/select.c
new file mode 100644
index 0000000..24c84ef
--- /dev/null
+++ b/fs/select.c
@@ -0,0 +1,278 @@
+/*
+ * This file contains the procedures for the handling of select
+ *
+ * Created for Linux based loosely upon Mathius Lattner's minix
+ * patches by Peter MacDonald. Heavily edited by Linus.
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+#include <const.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <signal.h>
+
+/*
+ * Ok, Peter made a complicated, but straightforward multiple_wait() function.
+ * I have rewritten this, taking some shortcuts: This code may not be easy to
+ * follow, but it should be free of race-conditions, and it's practical. If you
+ * 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.
+ */
+
+typedef struct {
+ struct task_struct * old_task;
+ struct task_struct ** wait_address;
+} wait_entry;
+
+typedef struct {
+ int nr;
+ wait_entry entry[NR_OPEN*3];
+} select_table;
+
+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;
+ p->entry[p->nr].wait_address = wait_address;
+ p->entry[p->nr].old_task = * wait_address;
+ *wait_address = current;
+ p->nr++;
+}
+
+static void free_wait(select_table * p)
+{
+ int i;
+ struct task_struct ** tpp;
+
+ for (i = 0; i < p->nr ; i++) {
+ tpp = p->entry[i].wait_address;
+ while (*tpp && *tpp != current) {
+ (*tpp)->state = 0;
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule();
+ }
+ if (!*tpp)
+ printk("free_wait: NULL");
+ if (*tpp = p->entry[i].old_task)
+ (**tpp).state = 0;
+ }
+ p->nr = 0;
+}
+
+static struct tty_struct * get_tty(struct m_inode * inode)
+{
+ int major, minor;
+
+ if (!S_ISCHR(inode->i_mode))
+ return NULL;
+ if ((major = MAJOR(inode->i_zone[0])) != 5 && major != 4)
+ return NULL;
+ if (major == 5)
+ minor = current->tty;
+ else
+ minor = MINOR(inode->i_zone[0]);
+ 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)
+ */
+static int check_in(select_table * wait, struct m_inode * inode)
+{
+ 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))
+ return 1;
+ else
+ add_wait(&inode->i_wait, wait);
+ return 0;
+}
+
+static int check_out(select_table * wait, struct m_inode * inode)
+{
+ 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);
+ return 0;
+}
+
+static int check_ex(select_table * wait, struct m_inode * inode)
+{
+ 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);
+ return 0;
+}
+
+int do_select(fd_set in, fd_set out, fd_set ex,
+ fd_set *inp, fd_set *outp, fd_set *exp)
+{
+ int count;
+ select_table wait_table;
+ int i;
+ fd_set mask;
+
+ mask = in | out | ex;
+ for (i = 0 ; i < NR_OPEN ; i++,mask >>= 1) {
+ if (!(mask & 1))
+ continue;
+ if (!current->filp[i])
+ return -EBADF;
+ if (!current->filp[i]->f_inode)
+ return -EBADF;
+ if (current->filp[i]->f_inode->i_pipe)
+ continue;
+ if (S_ISCHR(current->filp[i]->f_inode->i_mode))
+ continue;
+ if (S_ISFIFO(current->filp[i]->f_inode->i_mode))
+ continue;
+ return -EBADF;
+ }
+repeat:
+ wait_table.nr = 0;
+ *inp = *outp = *exp = 0;
+ count = 0;
+ mask = 1;
+ for (i = 0 ; i < NR_OPEN ; i++, mask += mask) {
+ if (mask & in)
+ if (check_in(&wait_table,current->filp[i]->f_inode)) {
+ *inp |= mask;
+ count++;
+ }
+ if (mask & out)
+ if (check_out(&wait_table,current->filp[i]->f_inode)) {
+ *outp |= mask;
+ count++;
+ }
+ if (mask & ex)
+ if (check_ex(&wait_table,current->filp[i]->f_inode)) {
+ *exp |= mask;
+ count++;
+ }
+ }
+ if (!(current->signal & ~current->blocked) &&
+ (wait_table.nr || current->timeout) && !count) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ free_wait(&wait_table);
+ goto repeat;
+ }
+ free_wait(&wait_table);
+ 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 ...
+ */
+int sys_select( unsigned long *buffer )
+{
+/* Perform the select(nd, in, out, ex, tv) system call. */
+ int i;
+ fd_set res_in, in = 0, *inp;
+ fd_set res_out, out = 0, *outp;
+ fd_set res_ex, ex = 0, *exp;
+ fd_set mask;
+ struct timeval *tvp;
+ unsigned long timeout;
+
+ mask = ~((~0) << get_fs_long(buffer++));
+ inp = (fd_set *) get_fs_long(buffer++);
+ outp = (fd_set *) get_fs_long(buffer++);
+ exp = (fd_set *) get_fs_long(buffer++);
+ tvp = (struct timeval *) get_fs_long(buffer);
+
+ if (inp)
+ in = mask & get_fs_long(inp);
+ if (outp)
+ out = mask & get_fs_long(outp);
+ if (exp)
+ ex = mask & get_fs_long(exp);
+ timeout = 0xffffffff;
+ if (tvp) {
+ timeout = get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ);
+ timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
+ 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 (i < 0)
+ return i;
+ if (inp) {
+ verify_area(inp, 4);
+ put_fs_long(res_in,inp);
+ }
+ if (outp) {
+ verify_area(outp,4);
+ put_fs_long(res_out,outp);
+ }
+ if (exp) {
+ 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 && (current->signal & ~current->blocked))
+ return -EINTR;
+ return i;
+}
diff --git a/fs/stat.c b/fs/stat.c
index 61a4ceb..b4f36fe 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -17,7 +17,7 @@ static void cp_stat(struct m_inode * inode, struct stat * statbuf)
struct stat tmp;
int i;
- verify_area(statbuf,sizeof (* statbuf));
+ verify_area(statbuf,sizeof (struct stat));
tmp.st_dev = inode->i_dev;
tmp.st_ino = inode->i_num;
tmp.st_mode = inode->i_mode;
@@ -30,7 +30,7 @@ static void cp_stat(struct m_inode * inode, struct stat * statbuf)
tmp.st_mtime = inode->i_mtime;
tmp.st_ctime = inode->i_ctime;
for (i=0 ; i<sizeof (tmp) ; i++)
- put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]);
+ put_fs_byte(((char *) &tmp)[i],i + (char *) statbuf);
}
int sys_stat(char * filename, struct stat * statbuf)
@@ -44,6 +44,17 @@ int sys_stat(char * filename, struct stat * statbuf)
return 0;
}
+int sys_lstat(char * filename, struct stat * statbuf)
+{
+ struct m_inode * inode;
+
+ if (!(inode = lnamei(filename)))
+ return -ENOENT;
+ cp_stat(inode,statbuf);
+ iput(inode);
+ return 0;
+}
+
int sys_fstat(unsigned int fd, struct stat * statbuf)
{
struct file * f;
@@ -54,3 +65,33 @@ int sys_fstat(unsigned int fd, struct stat * statbuf)
cp_stat(inode,statbuf);
return 0;
}
+
+int sys_readlink(const char * path, char * buf, int bufsiz)
+{
+ struct m_inode * inode;
+ struct buffer_head * bh;
+ int i;
+ char c;
+
+ if (bufsiz <= 0)
+ return -EBADF;
+ if (bufsiz > 1023)
+ bufsiz = 1023;
+ verify_area(buf,bufsiz);
+ if (!(inode = lnamei(path)))
+ return -ENOENT;
+ if (inode->i_zone[0])
+ bh = bread(inode->i_dev, inode->i_zone[0]);
+ else
+ bh = NULL;
+ iput(inode);
+ if (!bh)
+ return 0;
+ i = 0;
+ while (i<bufsiz && (c = bh->b_data[i])) {
+ i++;
+ put_fs_byte(c,buf++);
+ }
+ brelse(bh);
+ return i;
+}
diff --git a/fs/super.c b/fs/super.c
index b73fdca..20c1c9b 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -74,7 +74,6 @@ struct super_block * get_super(int dev)
void put_super(int dev)
{
struct super_block * sb;
- struct m_inode * inode;
int i;
if (dev == ROOT_DEV) {
diff --git a/fs/truncate.c b/fs/truncate.c
index 36f3ea2..32bc19d 100644
--- a/fs/truncate.c
+++ b/fs/truncate.c
@@ -8,58 +8,92 @@
#include <sys/stat.h>
-static void free_ind(int dev,int block)
+static int free_ind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
+ int block_busy;
if (!block)
- return;
+ return 1;
+ block_busy = 0;
if (bh=bread(dev,block)) {
p = (unsigned short *) bh->b_data;
for (i=0;i<512;i++,p++)
if (*p)
- free_block(dev,*p);
+ if (free_block(dev,*p)) {
+ *p = 0;
+ bh->b_dirt = 1;
+ } else
+ block_busy = 1;
brelse(bh);
}
- free_block(dev,block);
+ if (block_busy)
+ return 0;
+ else
+ return free_block(dev,block);
}
-static void free_dind(int dev,int block)
+static int free_dind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
+ int block_busy;
if (!block)
- return;
+ return 1;
+ block_busy = 0;
if (bh=bread(dev,block)) {
p = (unsigned short *) bh->b_data;
for (i=0;i<512;i++,p++)
if (*p)
- free_ind(dev,*p);
+ if (free_ind(dev,*p)) {
+ *p = 0;
+ bh->b_dirt = 1;
+ } else
+ block_busy = 1;
brelse(bh);
}
- free_block(dev,block);
+ if (block_busy)
+ return 0;
+ else
+ return free_block(dev,block);
}
void truncate(struct m_inode * inode)
{
int i;
+ int block_busy;
- if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
return;
+repeat:
+ block_busy = 0;
for (i=0;i<7;i++)
if (inode->i_zone[i]) {
- free_block(inode->i_dev,inode->i_zone[i]);
- inode->i_zone[i]=0;
+ if (free_block(inode->i_dev,inode->i_zone[i]))
+ inode->i_zone[i]=0;
+ else
+ block_busy = 1;
}
- free_ind(inode->i_dev,inode->i_zone[7]);
- free_dind(inode->i_dev,inode->i_zone[8]);
- inode->i_zone[7] = inode->i_zone[8] = 0;
- inode->i_size = 0;
+ if (free_ind(inode->i_dev,inode->i_zone[7]))
+ inode->i_zone[7] = 0;
+ else
+ block_busy = 1;
+ if (free_dind(inode->i_dev,inode->i_zone[8]))
+ inode->i_zone[8] = 0;
+ else
+ block_busy = 1;
inode->i_dirt = 1;
+ if (block_busy) {
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
+ inode->i_size = 0;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
diff --git a/include/asm/system.h b/include/asm/system.h
index 0b5a21d..c112e10 100644
--- a/include/asm/system.h
+++ b/include/asm/system.h
@@ -7,10 +7,10 @@ __asm__ ("movl %%esp,%%eax\n\t" \
"pushl $1f\n\t" \
"iret\n" \
"1:\tmovl $0x17,%%eax\n\t" \
- "movw %%ax,%%ds\n\t" \
- "movw %%ax,%%es\n\t" \
- "movw %%ax,%%fs\n\t" \
- "movw %%ax,%%gs" \
+ "mov %%ax,%%ds\n\t" \
+ "mov %%ax,%%es\n\t" \
+ "mov %%ax,%%fs\n\t" \
+ "mov %%ax,%%gs" \
:::"ax")
#define sti() __asm__ ("sti"::)
diff --git a/include/errno.h b/include/errno.h
index c282f69..6f35d03 100644
--- a/include/errno.h
+++ b/include/errno.h
@@ -57,4 +57,8 @@ extern int errno;
#define ENOSYS 38
#define ENOTEMPTY 39
+/* Should never be seen by user programs */
+#define ERESTARTSYS 512
+#define ERESTARTNOINTR 513
+
#endif
diff --git a/include/linux/config.h b/include/linux/config.h
index b706122..a30130e 100644
--- a/include/linux/config.h
+++ b/include/linux/config.h
@@ -2,21 +2,28 @@
#define _CONFIG_H
/*
+ * Defines for what uname() should return
+ */
+#define UTS_SYSNAME "Linux"
+#define UTS_NODENAME "(none)" /* set by sethostname() */
+#define UTS_RELEASE "0" /* patchlevel */
+#define UTS_VERSION "0.12"
+#define UTS_MACHINE "i386" /* hardware type */
+
+/* Don't touch these, unless you really know what your doing. */
+#define DEF_INITSEG 0x9000
+#define DEF_SYSSEG 0x1000
+#define DEF_SETUPSEG 0x9020
+#define DEF_SYSSIZE 0x3000
+
+/*
* The root-device is no longer hard-coded. You can change the default
* root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s
*/
/*
- * define your keyboard here -
- * KBD_FINNISH for Finnish keyboards
- * KBD_US for US-type
- * KBD_GR for German keyboards
- * KBD_FR for Frech keyboard
+ * The keyboard is now defined in kernel/chr_dev/keyboard.S
*/
-/*#define KBD_US */
-/*#define KBD_GR */
-/*#define KBD_FR */
-#define KBD_FINNISH
/*
* Normally, Linux can get the drive parameters from the BIOS at
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 7a90b10..5e6768f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -41,7 +41,7 @@ void buffer_init(long buffer_end);
#define SUPER_MAGIC 0x137F
#define NR_OPEN 20
-#define NR_INODE 32
+#define NR_INODE 64
#define NR_FILE 64
#define NR_SUPER 8
#define NR_HASH 307
@@ -55,13 +55,18 @@ void buffer_init(long buffer_end);
#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))
+#define PIPE_READ_WAIT(inode) ((inode).i_wait)
+#define PIPE_WRITE_WAIT(inode) ((inode).i_wait2)
#define PIPE_HEAD(inode) ((inode).i_zone[0])
#define PIPE_TAIL(inode) ((inode).i_zone[1])
#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))
-#define INC_PIPE(head) \
-__asm__("incl %0\n\tandl $4095,%0"::"m" (head))
+
+#define NIL_FILP ((struct file *)0)
+#define SEL_IN 1
+#define SEL_OUT 2
+#define SEL_EX 4
typedef char buffer_block[BLOCK_SIZE];
@@ -100,6 +105,7 @@ struct m_inode {
unsigned short i_zone[9];
/* these are in memory also */
struct task_struct * i_wait;
+ struct task_struct * i_wait2; /* for pipes */
unsigned long i_atime;
unsigned long i_ctime;
unsigned short i_dev;
@@ -176,6 +182,7 @@ extern void wait_on(struct m_inode * inode);
extern int bmap(struct m_inode * inode,int block);
extern int create_block(struct m_inode * inode,int block);
extern struct m_inode * namei(const char * pathname);
+extern struct m_inode * lnamei(const char * pathname);
extern int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode);
extern void iput(struct m_inode * inode);
@@ -185,12 +192,13 @@ extern struct m_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 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 brelse(struct buffer_head * buf);
extern struct buffer_head * bread(int dev,int block);
extern void bread_page(unsigned long addr,int dev,int b[4]);
extern struct buffer_head * breada(int dev,int block,...);
extern int new_block(int dev);
-extern void free_block(int dev, int block);
+extern int free_block(int dev, int block);
extern struct m_inode * new_inode(int dev);
extern void free_inode(struct m_inode * inode);
extern int sync_dev(int dev);
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index cb40dd5..d294d85 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -3,11 +3,22 @@
*/
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);
int tty_write(unsigned ch,char * buf,int count);
void * malloc(unsigned int size);
void free_s(void * obj, int size);
+extern void hd_times_out(void);
+extern void sysbeepstop(void);
+extern void blank_screen(void);
+extern void unblank_screen(void);
+
+extern int beepcount;
+extern int hd_timeout;
+extern int blankinterval;
+extern int blankcount;
#define free(x) free_s((x), 0)
diff --git a/include/linux/math_emu.h b/include/linux/math_emu.h
new file mode 100644
index 0000000..abe0cc2
--- /dev/null
+++ b/include/linux/math_emu.h
@@ -0,0 +1,185 @@
+/*
+ * linux/include/linux/math_emu.h
+ *
+ * (C) 1991 Linus Torvalds
+ */
+#ifndef _LINUX_MATH_EMU_H
+#define _LINUX_MATH_EMU_H
+
+#include <linux/sched.h>
+
+struct info {
+ long ___math_ret;
+ long ___orig_eip;
+ long ___edi;
+ long ___esi;
+ long ___ebp;
+ long ___sys_call_ret;
+ long ___eax;
+ long ___ebx;
+ long ___ecx;
+ long ___edx;
+ long ___orig_eax;
+ long ___fs;
+ long ___es;
+ long ___ds;
+ long ___eip;
+ long ___cs;
+ long ___eflags;
+ long ___esp;
+ long ___ss;
+};
+
+#define EAX (info->___eax)
+#define EBX (info->___ebx)
+#define ECX (info->___ecx)
+#define EDX (info->___edx)
+#define ESI (info->___esi)
+#define EDI (info->___edi)
+#define EBP (info->___ebp)
+#define ESP (info->___esp)
+#define EIP (info->___eip)
+#define ORIG_EIP (info->___orig_eip)
+#define EFLAGS (info->___eflags)
+#define DS (*(unsigned short *) &(info->___ds))
+#define ES (*(unsigned short *) &(info->___es))
+#define FS (*(unsigned short *) &(info->___fs))
+#define CS (*(unsigned short *) &(info->___cs))
+#define SS (*(unsigned short *) &(info->___ss))
+
+void __math_abort(struct info *, unsigned int);
+
+#define math_abort(x,y) \
+(((volatile void (*)(struct info *,unsigned int)) __math_abort)((x),(y)))
+
+/*
+ * Gcc forces this stupid alignment problem: I want to use only two longs
+ * for the temporary real 64-bit mantissa, but then gcc aligns out the
+ * structure to 12 bytes which breaks things in math_emulate.c. Shit. I
+ * want some kind of "no-alignt" pragma or something.
+ */
+
+typedef struct {
+ long a,b;
+ short exponent;
+} temp_real;
+
+typedef struct {
+ short m0,m1,m2,m3;
+ short exponent;
+} temp_real_unaligned;
+
+#define real_to_real(a,b) \
+((*(long long *) (b) = *(long long *) (a)),((b)->exponent = (a)->exponent))
+
+typedef struct {
+ long a,b;
+} long_real;
+
+typedef long short_real;
+
+typedef struct {
+ long a,b;
+ short sign;
+} temp_int;
+
+struct swd {
+ int ie:1;
+ int de:1;
+ int ze:1;
+ int oe:1;
+ int ue:1;
+ int pe:1;
+ int sf:1;
+ int ir:1;
+ int c0:1;
+ int c1:1;
+ int c2:1;
+ int top:3;
+ int c3:1;
+ int b:1;
+};
+
+#define I387 (current->tss.i387)
+#define SWD (*(struct swd *) &I387.swd)
+#define ROUNDING ((I387.cwd >> 10) & 3)
+#define PRECISION ((I387.cwd >> 8) & 3)
+
+#define BITS24 0
+#define BITS53 2
+#define BITS64 3
+
+#define ROUND_NEAREST 0
+#define ROUND_DOWN 1
+#define ROUND_UP 2
+#define ROUND_0 3
+
+#define CONSTZ (temp_real_unaligned) {0x0000,0x0000,0x0000,0x0000,0x0000}
+#define CONST1 (temp_real_unaligned) {0x0000,0x0000,0x0000,0x8000,0x3FFF}
+#define CONSTPI (temp_real_unaligned) {0xC235,0x2168,0xDAA2,0xC90F,0x4000}
+#define CONSTLN2 (temp_real_unaligned) {0x79AC,0xD1CF,0x17F7,0xB172,0x3FFE}
+#define CONSTLG2 (temp_real_unaligned) {0xF799,0xFBCF,0x9A84,0x9A20,0x3FFD}
+#define CONSTL2E (temp_real_unaligned) {0xF0BC,0x5C17,0x3B29,0xB8AA,0x3FFF}
+#define CONSTL2T (temp_real_unaligned) {0x8AFE,0xCD1B,0x784B,0xD49A,0x4000}
+
+#define set_IE() (I387.swd |= 1)
+#define set_DE() (I387.swd |= 2)
+#define set_ZE() (I387.swd |= 4)
+#define set_OE() (I387.swd |= 8)
+#define set_UE() (I387.swd |= 16)
+#define set_PE() (I387.swd |= 32)
+
+#define set_C0() (I387.swd |= 0x0100)
+#define set_C1() (I387.swd |= 0x0200)
+#define set_C2() (I387.swd |= 0x0400)
+#define set_C3() (I387.swd |= 0x4000)
+
+/* ea.c */
+
+char * ea(struct info * __info, unsigned short __code);
+
+/* convert.c */
+
+void short_to_temp(const short_real * __a, temp_real * __b);
+void long_to_temp(const long_real * __a, temp_real * __b);
+void temp_to_short(const temp_real * __a, short_real * __b);
+void temp_to_long(const temp_real * __a, long_real * __b);
+void real_to_int(const temp_real * __a, temp_int * __b);
+void int_to_real(const temp_int * __a, temp_real * __b);
+
+/* get_put.c */
+
+void get_short_real(temp_real *, struct info *, unsigned short);
+void get_long_real(temp_real *, struct info *, unsigned short);
+void get_temp_real(temp_real *, struct info *, unsigned short);
+void get_short_int(temp_real *, struct info *, unsigned short);
+void get_long_int(temp_real *, struct info *, unsigned short);
+void get_longlong_int(temp_real *, struct info *, unsigned short);
+void get_BCD(temp_real *, struct info *, unsigned short);
+void put_short_real(const temp_real *, struct info *, unsigned short);
+void put_long_real(const temp_real *, struct info *, unsigned short);
+void put_temp_real(const temp_real *, struct info *, unsigned short);
+void put_short_int(const temp_real *, struct info *, unsigned short);
+void put_long_int(const temp_real *, struct info *, unsigned short);
+void put_longlong_int(const temp_real *, struct info *, unsigned short);
+void put_BCD(const temp_real *, struct info *, unsigned short);
+
+/* add.c */
+
+void fadd(const temp_real *, const temp_real *, temp_real *);
+
+/* mul.c */
+
+void fmul(const temp_real *, const temp_real *, temp_real *);
+
+/* div.c */
+
+void fdiv(const temp_real *, const temp_real *, temp_real *);
+
+/* compare.c */
+
+void fcom(const temp_real *, const temp_real *);
+void fucom(const temp_real *, const temp_real *);
+void ftst(const temp_real *);
+
+#endif
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 5a160f3..94c3713 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3,8 +3,43 @@
#define PAGE_SIZE 4096
+#include <linux/kernel.h>
+#include <signal.h>
+
+extern int SWAP_DEV;
+
+#define read_swap_page(nr,buffer) ll_rw_page(READ,SWAP_DEV,(nr),(buffer));
+#define write_swap_page(nr,buffer) ll_rw_page(WRITE,SWAP_DEV,(nr),(buffer));
+
extern unsigned long get_free_page(void);
-extern unsigned long put_page(unsigned long page,unsigned long address);
+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 inline volatile void oom(void)
+{
+ printk("out of memory\n\r");
+ do_exit(SIGSEGV);
+}
+
+#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)
+#define USED 100
+
+extern unsigned char mem_map [ PAGING_PAGES ];
+
+#define PAGE_DIRTY 0x40
+#define PAGE_ACCESSED 0x20
+#define PAGE_USER 0x04
+#define PAGE_RW 0x02
+#define PAGE_PRESENT 0x01
#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 3407bb9..8f1ef9b 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1,19 +1,46 @@
#ifndef _SCHED_H
#define _SCHED_H
-#define NR_TASKS 64
#define HZ 100
+#define NR_TASKS 64
+#define TASK_SIZE 0x04000000
+#define LIBRARY_SIZE 0x00400000
+
+#if (TASK_SIZE & 0x3fffff)
+#error "TASK_SIZE must be multiple of 4M"
+#endif
+
+#if (LIBRARY_SIZE & 0x3fffff)
+#error "LIBRARY_SIZE must be a multiple of 4M"
+#endif
+
+#if (LIBRARY_SIZE >= (TASK_SIZE/2))
+#error "LIBRARY_SIZE too damn big!"
+#endif
+
+#if (((TASK_SIZE>>16)*NR_TASKS) != 0x10000)
+#error "TASK_SIZE*NR_TASKS must be 4GB"
+#endif
+
+#define LIBRARY_OFFSET (TASK_SIZE - LIBRARY_SIZE)
+
+#define CT_TO_SECS(x) ((x) / HZ)
+#define CT_TO_USECS(x) (((x) % HZ) * 1000000/HZ)
+
#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]
#include <linux/head.h>
#include <linux/fs.h>
#include <linux/mm.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <signal.h>
#if (NR_OPEN > 32)
-#error "Currently the close-on-exec-flags are in one word, max 32 files/proc"
+#error "Currently the close-on-exec-flags and select masks are in one long, max 32 files/proc"
#endif
#define TASK_RUNNING 0
@@ -86,11 +113,20 @@ struct task_struct {
/* various fields */
int exit_code;
unsigned long start_code,end_code,end_data,brk,start_stack;
- long pid,father,pgrp,session,leader;
+ long pid,pgrp,session,leader;
+ int groups[NGROUPS];
+ /*
+ * pointers to 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;
unsigned short uid,euid,suid;
unsigned short gid,egid,sgid;
- long alarm;
+ unsigned long timeout,alarm;
long utime,stime,cutime,cstime,start_time;
+ struct rlimit rlim[RLIM_NLIMITS];
+ unsigned int flags; /* per process flags, defined below */
unsigned short used_math;
/* file system info */
int tty; /* -1 if no tty, so it must be signed */
@@ -98,6 +134,7 @@ struct task_struct {
struct m_inode * pwd;
struct m_inode * root;
struct m_inode * executable;
+ struct m_inode * library;
unsigned long close_on_exec;
struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
@@ -107,6 +144,12 @@ struct task_struct {
};
/*
+ * Per process flags
+ */
+#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */
+ /* Not implemented yet, only for 486*/
+
+/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x9ffff (=640kB)
*/
@@ -114,11 +157,17 @@ struct task_struct {
/* state etc */ { 0,15,15, \
/* signals */ 0,{{},},0, \
/* ec,brk... */ 0,0,0,0,0,0, \
-/* pid etc.. */ 0,-1,0,0,0, \
+/* pid etc.. */ 0,0,0,0, \
+/* suppl grps*/ {NOGROUP,}, \
+/* proc links*/ &init_task.task,0,0,0, \
/* uid etc */ 0,0,0,0,0,0, \
-/* alarm */ 0,0,0,0,0,0, \
+/* timeout */ 0,0,0,0,0,0,0, \
+/* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
+ {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
+ {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \
+/* flags */ 0, \
/* math */ 0, \
-/* fs info */ -1,0022,NULL,NULL,NULL,0, \
+/* fs info */ -1,0022,NULL,NULL,NULL,NULL,0, \
/* filp */ {NULL,}, \
{ \
{0,0}, \
@@ -136,15 +185,17 @@ struct task_struct {
extern struct task_struct *task[NR_TASKS];
extern struct task_struct *last_task_used_math;
extern struct task_struct *current;
-extern long volatile jiffies;
-extern long startup_time;
+extern unsigned long volatile jiffies;
+extern unsigned long startup_time;
+extern int jiffies_offset;
-#define CURRENT_TIME (startup_time+jiffies/HZ)
+#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 int in_group_p(gid_t grp);
/*
* Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall
diff --git a/include/linux/sys.h b/include/linux/sys.h
index c538fc1..2a1c8be 100644
--- a/include/linux/sys.h
+++ b/include/linux/sys.h
@@ -1,3 +1,7 @@
+/*
+ * Why isn't this a .c file? Enquiring minds....
+ */
+
extern int sys_setup();
extern int sys_exit();
extern int sys_fork();
@@ -70,6 +74,21 @@ extern int sys_sgetmask();
extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();
+extern int sys_sigpending();
+extern int sys_sigsuspend();
+extern int sys_sethostname();
+extern int sys_setrlimit();
+extern int sys_getrlimit();
+extern int sys_getrusage();
+extern int sys_gettimeofday();
+extern int sys_settimeofday();
+extern int sys_getgroups();
+extern int sys_setgroups();
+extern int sys_select();
+extern int sys_symlink();
+extern int sys_lstat();
+extern int sys_readlink();
+extern int sys_uselib();
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,
@@ -83,4 +102,10 @@ sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
-sys_setreuid,sys_setregid };
+sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname,
+sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday,
+sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink,
+sys_lstat, sys_readlink, sys_uselib };
+
+/* 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/linux/tty.h b/include/linux/tty.h
index ad846b3..c3d7af6 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -9,6 +9,12 @@
#ifndef _TTY_H
#define _TTY_H
+#define MAX_CONSOLES 8
+#define NR_SERIALS 2
+#define NR_PTYS 4
+
+extern int NR_CONSOLES;
+
#include <termios.h>
#define TTY_BUF_SIZE 1024
@@ -21,17 +27,24 @@ struct tty_queue {
char buf[TTY_BUF_SIZE];
};
+#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00)
+#define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40)
+#define IS_A_PTY(min) ((min) & 0x80)
+#define IS_A_PTY_MASTER(min) (((min) & 0xC0) == 0x80)
+#define IS_A_PTY_SLAVE(min) (((min) & 0xC0) == 0xC0)
+#define PTY_OTHER(min) ((min) ^ 0x40)
+
#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
-#define EMPTY(a) ((a).head == (a).tail)
-#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1))
-#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)])
+#define EMPTY(a) ((a)->head == (a)->tail)
+#define LEFT(a) (((a)->tail-(a)->head-1)&(TTY_BUF_SIZE-1))
+#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 CHARS(a) (((a)->head-(a)->tail)&(TTY_BUF_SIZE-1))
#define GETCH(queue,c) \
-(void)({c=(queue).buf[(queue).tail];INC((queue).tail);})
+(void)({c=(queue)->buf[(queue)->tail];INC((queue)->tail);})
#define PUTCH(c,queue) \
-(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})
+(void)({(queue)->buf[(queue)->head]=(c);INC((queue)->head);})
#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR])
#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT])
@@ -45,14 +58,19 @@ struct tty_queue {
struct tty_struct {
struct termios termios;
int pgrp;
+ int session;
int stopped;
void (*write)(struct tty_struct * tty);
- struct tty_queue read_q;
- struct tty_queue write_q;
- struct tty_queue secondary;
+ struct tty_queue *read_q;
+ struct tty_queue *write_q;
+ struct tty_queue *secondary;
};
extern struct tty_struct tty_table[];
+extern int fg_console;
+
+#define TTY_TABLE(nr) \
+(tty_table + ((nr) ? (((nr) < 64)? (nr)-1:(nr)) : fg_console))
/* intr=^C quit=^| erase=del kill=^U
eof=^D vtime=\0 vmin=\1 sxtc=\0
@@ -69,9 +87,13 @@ void tty_init(void);
int tty_read(unsigned c, char * buf, int n);
int tty_write(unsigned c, char * buf, int n);
-void rs_write(struct tty_struct * tty);
void con_write(struct tty_struct * tty);
+void rs_write(struct tty_struct * tty);
+void mpty_write(struct tty_struct * tty);
+void spty_write(struct tty_struct * tty);
void copy_to_cooked(struct tty_struct * tty);
+void update_screen(void);
+
#endif
diff --git a/include/signal.h b/include/signal.h
index 0eea9a3..462556d 100644
--- a/include/signal.h
+++ b/include/signal.h
@@ -35,6 +35,7 @@ typedef unsigned int sigset_t; /* 32 bits */
/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
#define SA_NOCLDSTOP 1
+#define SA_INTERRUPT 0x20000000
#define SA_NOMASK 0x40000000
#define SA_ONESHOT 0x80000000
@@ -44,6 +45,12 @@ typedef unsigned int sigset_t; /* 32 bits */
#define SIG_DFL ((void (*)(int))0) /* default signal handling */
#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);
diff --git a/include/stddef.h b/include/stddef.h
index 97f72ff..697c4f4 100644
--- a/include/stddef.h
+++ b/include/stddef.h
@@ -15,5 +15,4 @@ typedef unsigned long size_t;
#define NULL ((void *)0)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-
#endif
diff --git a/include/sys/param.h b/include/sys/param.h
new file mode 100644
index 0000000..c864701
--- /dev/null
+++ b/include/sys/param.h
@@ -0,0 +1,12 @@
+#ifndef _SYS_PARAM_H
+#define _SYS_PARAM_H
+
+#define HZ 100
+#define EXEC_PAGESIZE 4096
+
+#define NGROUPS 32 /* Max number of groups per user */
+#define NOGROUP -1
+
+#define MAXHOSTNAMELEN 8
+
+#endif
diff --git a/include/sys/resource.h b/include/sys/resource.h
new file mode 100644
index 0000000..e23ec63
--- /dev/null
+++ b/include/sys/resource.h
@@ -0,0 +1,63 @@
+/*
+ * 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
+ *
+ * We don't support all of these yet, but we might as well have them....
+ * Otherwise, each time we add new items, programs which depend on this
+ * structure will lose. This reduces the chances of that happening.
+ */
+#define RUSAGE_SELF 0
+#define RUSAGE_CHILDREN -1
+
+struct rusage {
+ struct timeval ru_utime; /* user time used */
+ struct timeval ru_stime; /* system time used */
+ long ru_maxrss; /* maximum resident set size */
+ long ru_ixrss; /* integral shared memory size */
+ long ru_idrss; /* integral unshared data size */
+ long ru_isrss; /* integral unshared stack size */
+ long ru_minflt; /* page reclaims */
+ long ru_majflt; /* page faults */
+ long ru_nswap; /* swaps */
+ long ru_inblock; /* block input operations */
+ long ru_oublock; /* block output operations */
+ long ru_msgsnd; /* messages sent */
+ long ru_msgrcv; /* messages received */
+ long ru_nsignals; /* signals received */
+ long ru_nvcsw; /* voluntary context switches */
+ long ru_nivcsw; /* involuntary " */
+};
+
+/*
+ * Resource limits
+ */
+
+#define RLIMIT_CPU 0 /* CPU time in ms */
+#define RLIMIT_FSIZE 1 /* Maximum filesize */
+#define RLIMIT_DATA 2 /* max data size */
+#define RLIMIT_STACK 3 /* max stack size */
+#define RLIMIT_CORE 4 /* max core file size */
+#define RLIMIT_RSS 5 /* max resident set size */
+
+#ifdef notdef
+#define RLIMIT_MEMLOCK 6 /* max locked-in-memory address space*/
+#define RLIMIT_NPROC 7 /* max number of processes */
+#define RLIMIT_OFILE 8 /* max number of open files */
+#endif
+
+#define RLIM_NLIMITS 6
+
+#define RLIM_INFINITY 0x7fffffff
+
+struct rlimit {
+ int rlim_cur;
+ int rlim_max;
+};
+
+#endif /* _SYS_RESOURCE_H */
diff --git a/include/sys/stat.h b/include/sys/stat.h
index 41c3840..e917e9c 100644
--- a/include/sys/stat.h
+++ b/include/sys/stat.h
@@ -18,6 +18,7 @@ struct stat {
};
#define S_IFMT 00170000
+#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
@@ -27,6 +28,7 @@ struct stat {
#define S_ISGID 0002000
#define S_ISVTX 0001000
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
diff --git a/include/sys/time.h b/include/sys/time.h
new file mode 100644
index 0000000..7d56038
--- /dev/null
+++ b/include/sys/time.h
@@ -0,0 +1,63 @@
+#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_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 <time.h>
+#include <sys/types.h>
+
+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/types.h b/include/sys/types.h
index 557aa31..aa260c8 100644
--- a/include/sys/types.h
+++ b/include/sys/types.h
@@ -22,7 +22,7 @@ typedef long ptrdiff_t;
typedef int pid_t;
typedef unsigned short uid_t;
-typedef unsigned char gid_t;
+typedef unsigned short gid_t;
typedef unsigned short dev_t;
typedef unsigned short ino_t;
typedef unsigned short mode_t;
@@ -33,6 +33,12 @@ typedef long off_t;
typedef unsigned char u_char;
typedef unsigned short ushort;
+typedef unsigned char cc_t;
+typedef unsigned int speed_t;
+typedef unsigned long tcflag_t;
+
+typedef unsigned long fd_set;
+
typedef struct { int quot,rem; } div_t;
typedef struct { long quot,rem; } ldiv_t;
diff --git a/include/sys/utsname.h b/include/sys/utsname.h
index 0a1c5a0..0e6aef8 100644
--- a/include/sys/utsname.h
+++ b/include/sys/utsname.h
@@ -2,10 +2,11 @@
#define _SYS_UTSNAME_H
#include <sys/types.h>
+#include <sys/param.h>
struct utsname {
char sysname[9];
- char nodename[9];
+ char nodename[MAXHOSTNAMELEN+1];
char release[9];
char version[9];
char machine[9];
diff --git a/include/sys/wait.h b/include/sys/wait.h
index 53190c2..d6c3360 100644
--- a/include/sys/wait.h
+++ b/include/sys/wait.h
@@ -10,10 +10,11 @@
#define WNOHANG 1
#define WUNTRACED 2
-#define WIFEXITED(s) (!((s)&0xFF)
+#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)
diff --git a/include/termios.h b/include/termios.h
index 2b7b913..3c2937b 100644
--- a/include/termios.h
+++ b/include/termios.h
@@ -1,6 +1,8 @@
#ifndef _TERMIOS_H
#define _TERMIOS_H
+#include <sys/types.h>
+
#define TTY_BUF_SIZE 1024
/* 0x54 is just a magic number to make these relatively uniqe ('T') */
@@ -31,7 +33,8 @@
#define TIOCMSET 0x5418
#define TIOCGSOFTCAR 0x5419
#define TIOCSSOFTCAR 0x541A
-#define TIOCINQ 0x541B
+#define FIONREAD 0x541B
+#define TIOCINQ FIONREAD
struct winsize {
unsigned short ws_row;
@@ -52,12 +55,12 @@ struct termio {
#define NCCS 17
struct termios {
- unsigned long c_iflag; /* input mode flags */
- unsigned long c_oflag; /* output mode flags */
- unsigned long c_cflag; /* control mode flags */
- unsigned long c_lflag; /* local mode flags */
- unsigned char c_line; /* line discipline */
- unsigned char c_cc[NCCS]; /* control characters */
+ tcflag_t c_iflag; /* input mode flags */
+ tcflag_t c_oflag; /* output mode flags */
+ tcflag_t c_cflag; /* control mode flags */
+ tcflag_t c_lflag; /* local mode flags */
+ cc_t c_line; /* line discipline */
+ cc_t c_cc[NCCS]; /* control characters */
};
/* c_cc characters */
@@ -155,16 +158,13 @@ struct termios {
#define CS8 0000060
#define CSTOPB 0000100
#define CREAD 0000200
-#define CPARENB 0000400
-#define CPARODD 0001000
+#define PARENB 0000400
+#define PARODD 0001000
#define HUPCL 0002000
#define CLOCAL 0004000
#define CIBAUD 03600000 /* input baud rate (not used) */
#define CRTSCTS 020000000000 /* flow control */
-#define PARENB CPARENB
-#define PARODD CPARODD
-
/* c_lflag bits */
#define ISIG 0000001
#define ICANON 0000002
@@ -211,8 +211,6 @@ struct termios {
#define TCSADRAIN 1
#define TCSAFLUSH 2
-typedef int speed_t;
-
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);
diff --git a/include/time.h b/include/time.h
index d0a765d..fbb4e73 100644
--- a/include/time.h
+++ b/include/time.h
@@ -11,6 +11,10 @@ typedef long time_t;
typedef unsigned int size_t;
#endif
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
#define CLOCKS_PER_SEC 100
typedef long clock_t;
@@ -27,6 +31,9 @@ struct tm {
int tm_isdst;
};
+#define __isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 1000 == 0))
+
clock_t clock(void);
time_t time(time_t * tp);
double difftime(time_t time2, time_t time1);
diff --git a/include/unistd.h b/include/unistd.h
index bf71dcb..b72f6b0 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -7,8 +7,8 @@
#define _POSIX_CHOWN_RESTRICTED /* only root can do a chown (I think..) */
#define _POSIX_NO_TRUNC /* no pathname truncation (but see in kernel) */
#define _POSIX_VDISABLE '\0' /* character to disable things like ^C */
-/*#define _POSIX_SAVED_IDS */ /* we'll get to this yet */
-/*#define _POSIX_JOB_CONTROL */ /* we aren't there quite yet. Soon hopefully */
+#define _POSIX_JOB_CONTROL
+#define _POSIX_SAVED_IDS /* Implemented, for whatever good it is */
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
@@ -51,8 +51,10 @@
#define _PC_CHOWN_RESTRICTED 9
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/times.h>
#include <sys/utsname.h>
+#include <sys/resource.h>
#include <utime.h>
#ifdef __LIBRARY__
@@ -129,6 +131,21 @@
#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 _syscall0(type,name) \
type name(void) \
@@ -249,5 +266,15 @@ 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);
#endif
diff --git a/init/main.c b/init/main.c
index 25de9e0..5fd2cdb 100644
--- a/init/main.c
+++ b/init/main.c
@@ -39,8 +39,11 @@ static inline _syscall0(int,sync)
#include <linux/fs.h>
+#include <string.h>
+
static char printbuf[1024];
+extern char *strcpy();
extern int vsprintf();
extern void init(void);
extern void blk_dev_init(void);
@@ -50,14 +53,27 @@ extern void floppy_init(void);
extern void mem_init(long start, long end);
extern long rd_init(long mem_start, int length);
extern long kernel_mktime(struct tm * tm);
-extern long startup_time;
+
+static int sprintf(char * str, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsprintf(str, fmt, args);
+ va_end(args);
+ return i;
+}
/*
* 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 ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
+#define ORIG_SWAP_DEV (*(unsigned short *)0x901FA)
/*
* Yeah, yeah, it's ugly, but I cannot find how to do this correctly
@@ -98,6 +114,13 @@ static void time_init(void)
static long memory_end = 0;
static long buffer_memory_end = 0;
static long main_memory_start = 0;
+static char term[32];
+
+static char * argv_rc[] = { "/bin/sh", NULL };
+static char * envp_rc[] = { "HOME=/", NULL ,NULL };
+
+static char * argv[] = { "-/bin/sh",NULL };
+static char * envp[] = { "HOME=/usr/root", NULL, NULL };
struct drive_info { char dummy[32]; } drive_info;
@@ -108,6 +131,10 @@ void main(void) /* This really IS void, no error here. */
* enable them
*/
ROOT_DEV = ORIG_ROOT_DEV;
+ SWAP_DEV = ORIG_SWAP_DEV;
+ sprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS);
+ envp[1] = term;
+ envp_rc[1] = term;
drive_info = DRIVE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
@@ -145,7 +172,8 @@ void main(void) /* This really IS void, no error here. */
* can run). For task0 'pause()' just means we go check if some other
* task can run, and if not we return here.
*/
- for(;;) pause();
+ for(;;)
+ __asm__("int $0x80"::"a" (__NR_pause):"ax");
}
static int printf(const char *fmt, ...)
@@ -159,18 +187,12 @@ static int printf(const char *fmt, ...)
return i;
}
-static char * argv_rc[] = { "/bin/sh", NULL };
-static char * envp_rc[] = { "HOME=/", NULL };
-
-static char * argv[] = { "-/bin/sh",NULL };
-static char * envp[] = { "HOME=/usr/root", NULL };
-
void init(void)
{
int pid,i;
setup((void *) &drive_info);
- (void) open("/dev/tty0",O_RDWR,0);
+ (void) open("/dev/tty1",O_RDWR,0);
(void) dup(0);
(void) dup(0);
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
@@ -194,7 +216,7 @@ void init(void)
if (!pid) {
close(0);close(1);close(2);
setsid();
- (void) open("/dev/tty0",O_RDWR,0);
+ (void) open("/dev/tty1",O_RDWR,0);
(void) dup(0);
(void) dup(0);
_exit(execve("/bin/sh",argv,envp));
diff --git a/kernel/Makefile b/kernel/Makefile
index 07d7f61..5f5685f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -24,7 +24,7 @@ CPP =gcc -E -nostdinc -I../include
$(CC) $(CFLAGS) \
-c -o $*.o $<
-OBJS = sched.o system_call.o traps.o asm.o fork.o \
+OBJS = sched.o sys_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o
@@ -51,33 +51,43 @@ dep:
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/linux/mm.h \
- ../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
- ../include/asm/segment.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/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
- ../include/asm/segment.h ../include/asm/system.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
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/linux/mm.h ../include/signal.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
sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
- ../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \
- ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \
- ../include/asm/segment.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/sys.h ../include/linux/fdreg.h ../include/asm/system.h \
+ ../include/asm/io.h ../include/asm/segment.h
signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
- ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.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/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/linux/mm.h ../include/signal.h ../include/linux/tty.h \
- ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \
- ../include/sys/times.h ../include/sys/utsname.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/asm/segment.h ../include/sys/times.h \
+ ../include/sys/utsname.h ../include/string.h
traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \
../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \
- ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
- ../include/asm/system.h ../include/asm/segment.h ../include/asm/io.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
vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h
diff --git a/kernel/asm.s b/kernel/asm.s
index e72a9ab..b3bbf8b 100644
--- a/kernel/asm.s
+++ b/kernel/asm.s
@@ -15,6 +15,7 @@
.globl _double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_coprocessor_error,_irq13,_reserved
+.globl _alignment_check
_divide_error:
pushl $_do_divide_error
@@ -144,3 +145,7 @@ _general_protection:
pushl $_do_general_protection
jmp error_code
+_alignment_check:
+ pushl $_do_alignment_check
+ jmp error_code
+
diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile
index 0fd7cb0..e1393ac 100644
--- a/kernel/blk_drv/Makefile
+++ b/kernel/blk_drv/Makefile
@@ -43,16 +43,29 @@ dep:
### Dependencies:
floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
- ../../include/signal.h ../../include/linux/kernel.h \
- ../../include/linux/fdreg.h ../../include/asm/system.h \
- ../../include/asm/io.h ../../include/asm/segment.h blk.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/linux/config.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
- ../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \
- ../../include/linux/kernel.h ../../include/linux/hdreg.h \
+ ../../include/sys/types.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/hdreg.h \
../../include/asm/system.h ../../include/asm/io.h \
../../include/asm/segment.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/linux/mm.h ../../include/signal.h \
- ../../include/linux/kernel.h ../../include/asm/system.h blk.h
+ ../../include/sys/types.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/string.h ../../include/linux/config.h \
+ ../../include/linux/sched.h ../../include/linux/head.h \
+ ../../include/linux/fs.h ../../include/sys/types.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/memory.h blk.h
diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h
index c4a7cde..0c62c82 100644
--- a/kernel/blk_drv/blk.h
+++ b/kernel/blk_drv/blk.h
@@ -51,6 +51,8 @@ extern struct blk_dev_struct blk_dev[NR_BLK_DEV];
extern struct request request[NR_REQUEST];
extern struct task_struct * wait_for_request;
+extern int * blk_size[NR_BLK_DEV];
+
#ifdef MAJOR_NR
/*
@@ -79,6 +81,7 @@ extern struct task_struct * wait_for_request;
/* harddisk */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
+#define DEVICE_TIMEOUT hd_timeout
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)/5)
#define DEVICE_ON(device)
@@ -96,6 +99,12 @@ extern struct task_struct * wait_for_request;
#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;
#endif
+#ifdef DEVICE_TIMEOUT
+int DEVICE_TIMEOUT = 0;
+#define SET_INTR(x) (DEVICE_INTR = (x),DEVICE_TIMEOUT = 200)
+#else
+#define SET_INTR(x) (DEVICE_INTR = (x))
+#endif
static void (DEVICE_REQUEST)(void);
extern inline void unlock_buffer(struct buffer_head * bh)
@@ -124,10 +133,25 @@ extern inline void end_request(int uptodate)
CURRENT = CURRENT->next;
}
+#ifdef DEVICE_TIMEOUT
+#define CLEAR_DEVICE_TIMEOUT DEVICE_TIMEOUT = 0;
+#else
+#define CLEAR_DEVICE_TIMEOUT
+#endif
+
+#ifdef DEVICE_INTR
+#define CLEAR_DEVICE_INTR DEVICE_INTR = 0;
+#else
+#define CLEAR_DEVICE_INTR
+#endif
+
#define INIT_REQUEST \
repeat: \
- if (!CURRENT) \
+ if (!CURRENT) {\
+ CLEAR_DEVICE_INTR \
+ CLEAR_DEVICE_TIMEOUT \
return; \
+ } \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index 2500a88..3bccac0 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -92,6 +92,7 @@ static struct floppy_struct {
{ 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */
{ 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */
};
+
/*
* Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
* Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
@@ -141,7 +142,7 @@ int floppy_change(unsigned int nr)
repeat:
floppy_on(nr);
while ((current_DOR & 3) != nr && selected)
- interruptible_sleep_on(&wait_on_floppy_select);
+ sleep_on(&wait_on_floppy_select);
if ((current_DOR & 3) != nr)
goto repeat;
if (inb(FD_DIR) & 0x80) {
@@ -454,8 +455,20 @@ 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 floppy_init(void)
{
+ blk_size[MAJOR_NR] = floppy_sizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
set_trap_gate(0x26,&floppy_interrupt);
outb(inb_p(0x21)&~0x40,0x21);
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index 23c58d4..563be0a 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -35,9 +35,10 @@ inb_p(0x71); \
#define MAX_HD 2
static void recal_intr(void);
+static void bad_rw_intr(void);
-static int recalibrate = 1;
-static int reset = 1;
+static int recalibrate = 0;
+static int reset = 0;
/*
* This struct defines the HD's and their types.
@@ -58,6 +59,8 @@ static struct hd_struct {
long nr_sects;
} hd[5*MAX_HD]={{0,0},};
+static int hd_sizes[5*MAX_HD] = {0, };
+
#define port_read(port,buf,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
@@ -151,16 +154,20 @@ int sys_setup(void * BIOS)
}
brelse(bh);
}
+ for (i=0 ; i<5*MAX_HD ; i++)
+ hd_sizes[i] = hd[i].nr_sects>>1 ;
+ blk_size[MAJOR_NR] = hd_sizes;
if (NR_HD)
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
rd_load();
+ init_swapping();
mount_root();
return (0);
}
static int controller_ready(void)
{
- int retries=10000;
+ int retries = 100000;
while (--retries && (inb_p(HD_STATUS)&0xc0)!=0x40);
return (retries);
@@ -187,7 +194,7 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
panic("Trying to write bad sector");
if (!controller_ready())
panic("HD controller not ready");
- do_hd = intr_addr;
+ SET_INTR(intr_addr);
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
@@ -202,14 +209,14 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
static int drive_busy(void)
{
unsigned int i;
+ unsigned char c;
- for (i = 0; i < 10000; i++)
- if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT)))
- break;
- i = inb(HD_STATUS);
- i &= BUSY_STAT | READY_STAT | SEEK_STAT;
- if (i == READY_STAT | SEEK_STAT)
- return(0);
+ for (i = 0; i < 50000; i++) {
+ c = inb_p(HD_STATUS);
+ c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
+ if (c == (READY_STAT | SEEK_STAT))
+ return 0;
+ }
printk("HD controller times out\n\r");
return(1);
}
@@ -219,7 +226,7 @@ static void reset_controller(void)
int i;
outb(4,HD_CMD);
- for(i = 0; i < 100; i++) nop();
+ for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
if (drive_busy())
printk("HD-controller still busy\n\r");
@@ -227,16 +234,33 @@ static void reset_controller(void)
printk("HD-controller reset failed: %02x\n\r",i);
}
-static void reset_hd(int nr)
+static void reset_hd(void)
{
- reset_controller();
- hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1,
- hd_info[nr].cyl,WIN_SPECIFY,&recal_intr);
+ static int i;
+
+repeat:
+ if (reset) {
+ reset = 0;
+ i = -1;
+ reset_controller();
+ } else if (win_result()) {
+ bad_rw_intr();
+ if (reset)
+ goto repeat;
+ }
+ i++;
+ if (i < NR_HD) {
+ hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
+ hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
+ } else
+ do_hd_request();
}
void unexpected_hd_interrupt(void)
{
printk("Unexpected HD interrupt\n\r");
+ reset = 1;
+ do_hd_request();
}
static void bad_rw_intr(void)
@@ -259,7 +283,7 @@ static void read_intr(void)
CURRENT->buffer += 512;
CURRENT->sector++;
if (--CURRENT->nr_sectors) {
- do_hd = &read_intr;
+ SET_INTR(&read_intr);
return;
}
end_request(1);
@@ -276,7 +300,7 @@ static void write_intr(void)
if (--CURRENT->nr_sectors) {
CURRENT->sector++;
CURRENT->buffer += 512;
- do_hd = &write_intr;
+ SET_INTR(&write_intr);
port_write(HD_DATA,CURRENT->buffer,256);
return;
}
@@ -291,6 +315,18 @@ static void recal_intr(void)
do_hd_request();
}
+void hd_times_out(void)
+{
+ if (!CURRENT)
+ return;
+ printk("HD timeout");
+ if (++CURRENT->errors >= MAX_ERRORS)
+ end_request(0);
+ SET_INTR(NULL);
+ reset = 1;
+ do_hd_request();
+}
+
void do_hd_request(void)
{
int i,r;
@@ -314,9 +350,8 @@ void do_hd_request(void)
sec++;
nsect = CURRENT->nr_sectors;
if (reset) {
- reset = 0;
recalibrate = 1;
- reset_hd(CURRENT_DEV);
+ reset_hd();
return;
}
if (recalibrate) {
@@ -327,7 +362,7 @@ void do_hd_request(void)
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
- for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
+ for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
/* nothing */ ;
if (!r) {
bad_rw_intr();
diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c
index f57d998..9506a06 100644
--- a/kernel/blk_drv/ll_rw_blk.c
+++ b/kernel/blk_drv/ll_rw_blk.c
@@ -39,6 +39,15 @@ struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
{ NULL, NULL } /* dev lp */
};
+/*
+ * blk_size contains the size of all block-devices:
+ *
+ * blk_size[MAJOR][MINOR]
+ *
+ * if (!blk_size[MAJOR]) then no minor size checking is done.
+ */
+int * blk_size[NR_BLK_DEV] = { NULL, NULL, };
+
static inline void lock_buffer(struct buffer_head * bh)
{
cli();
@@ -60,6 +69,9 @@ static inline void unlock_buffer(struct buffer_head * bh)
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
+ *
+ * Note that swapping requests always go before other requests,
+ * and are done in the order they appear.
*/
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
@@ -75,11 +87,17 @@ static void add_request(struct blk_dev_struct * dev, struct request * req)
(dev->request_fn)();
return;
}
- for ( ; tmp->next ; tmp=tmp->next)
+ for ( ; tmp->next ; tmp=tmp->next) {
+ if (!req->bh)
+ if (tmp->next->bh)
+ break;
+ else
+ continue;
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
+ }
req->next=tmp->next;
tmp->next=req;
sti();
@@ -142,6 +160,41 @@ repeat:
add_request(major+blk_dev,req);
}
+void ll_rw_page(int rw, int dev, int page, char * buffer)
+{
+ struct request * req;
+ unsigned int major = MAJOR(dev);
+
+ if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) {
+ printk("Trying to read nonexistent block-device\n\r");
+ return;
+ }
+ if (rw!=READ && rw!=WRITE)
+ panic("Bad block dev command, must be R/W");
+repeat:
+ req = request+NR_REQUEST;
+ while (--req >= request)
+ if (req->dev<0)
+ break;
+ if (req < request) {
+ sleep_on(&wait_for_request);
+ goto repeat;
+ }
+/* fill up the request-info, and add it to the queue */
+ req->dev = dev;
+ req->cmd = rw;
+ req->errors = 0;
+ req->sector = page<<3;
+ req->nr_sectors = 8;
+ req->buffer = buffer;
+ req->waiting = current;
+ req->bh = NULL;
+ req->next = NULL;
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_request(major+blk_dev,req);
+ schedule();
+}
+
void ll_rw_block(int rw, struct buffer_head * bh)
{
unsigned int major;
diff --git a/kernel/chr_drv/Makefile b/kernel/chr_drv/Makefile
index 9e2b848..66f4cec 100644
--- a/kernel/chr_drv/Makefile
+++ b/kernel/chr_drv/Makefile
@@ -25,13 +25,13 @@ CPP =gcc -E -nostdinc -I../../include
-c -o $*.o $<
OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \
- tty_ioctl.o
+ tty_ioctl.o pty.o
chr_drv.a: $(OBJS)
$(AR) rcs chr_drv.a $(OBJS)
sync
-keyboard.s: keyboard.S ../../include/linux/config.h
+keyboard.s: keyboard.S
$(CPP) -traditional keyboard.S -o keyboard.s
clean:
@@ -47,22 +47,42 @@ dep:
### Dependencies:
console.s console.o : console.c ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
- ../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \
- ../../include/linux/tty.h ../../include/termios.h ../../include/asm/io.h \
- ../../include/asm/system.h
+ ../../include/sys/types.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/asm/io.h \
+ ../../include/asm/system.h ../../include/asm/segment.h \
+ ../../include/string.h ../../include/errno.h
+pty.s pty.o : pty.c ../../include/linux/tty.h ../../include/termios.h \
+ ../../include/sys/types.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/asm/system.h ../../include/asm/io.h
serial.s serial.o : serial.c ../../include/linux/tty.h ../../include/termios.h \
- ../../include/linux/sched.h ../../include/linux/head.h \
- ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
- ../../include/signal.h ../../include/asm/system.h ../../include/asm/io.h
+ ../../include/sys/types.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/asm/system.h ../../include/asm/io.h
tty_io.s tty_io.o : tty_io.c ../../include/ctype.h ../../include/errno.h \
- ../../include/signal.h ../../include/sys/types.h \
- ../../include/linux/sched.h ../../include/linux/head.h \
- ../../include/linux/fs.h ../../include/linux/mm.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/linux/sched.h ../../include/linux/head.h \
- ../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
- ../../include/signal.h ../../include/linux/kernel.h \
- ../../include/linux/tty.h ../../include/asm/io.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/linux/sched.h \
+ ../../include/linux/head.h ../../include/linux/fs.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/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
diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c
index ae6bbde..781fdab 100644
--- a/kernel/chr_drv/console.c
+++ b/kernel/chr_drv/console.c
@@ -13,6 +13,9 @@
* Hopefully this will be a rather complete VT102 implementation.
*
* Beeping thanks to John T Kohl.
+ *
+ * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
+ * Chars, and VT100 enhancements by Peter MacDonald.
*/
/*
@@ -29,8 +32,26 @@
#include <linux/sched.h>
#include <linux/tty.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+
#include <asm/io.h>
#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <string.h>
+#include <errno.h>
+
+#define DEF_TERMIOS \
+(struct termios) { \
+ ICRNL, \
+ OPOST | ONLCR, \
+ 0, \
+ IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, \
+ 0, \
+ INIT_C_CC \
+}
+
/*
* These are set up by the setup-routine at boot-time:
@@ -41,7 +62,7 @@
#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 (25)
+#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)
@@ -53,28 +74,69 @@
#define NPAR 16
+int NR_CONSOLES = 0;
+
extern void keyboard_interrupt(void);
static unsigned char video_type; /* Type of display being used */
static unsigned long video_num_columns; /* Number of text columns */
+static unsigned long video_mem_base; /* Base of video memory */
+static unsigned long video_mem_term; /* End of video memory */
static unsigned long video_size_row; /* Bytes per row */
static unsigned long video_num_lines; /* Number of test lines */
static unsigned char video_page; /* Initial video page */
-static unsigned long video_mem_start; /* Start of video RAM */
-static unsigned long video_mem_end; /* End of video RAM (sort of) */
static unsigned short video_port_reg; /* Video register select port */
static unsigned short video_port_val; /* Video register value port */
-static unsigned short video_erase_char; /* Char+Attrib to erase with */
-
-static unsigned long origin; /* Used for EGA/VGA fast scroll */
-static unsigned long scr_end; /* Used for EGA/VGA fast scroll */
-static unsigned long pos;
-static unsigned long x,y;
-static unsigned long top,bottom;
-static unsigned long state=0;
-static unsigned long npar,par[NPAR];
-static unsigned long ques=0;
-static unsigned char attr=0x07;
+static int can_do_colour = 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;
+ unsigned long vc_restate;
+ unsigned long vc_checkin;
+ 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_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;
+ char * vc_translate;
+} vc_cons [MAX_CONSOLES];
+
+#define origin (vc_cons[currcons].vc_origin)
+#define scr_end (vc_cons[currcons].vc_scr_end)
+#define pos (vc_cons[currcons].vc_pos)
+#define top (vc_cons[currcons].vc_top)
+#define bottom (vc_cons[currcons].vc_bottom)
+#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)
+#define attr (vc_cons[currcons].vc_attr)
+#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 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)
+
+int blankinterval = 0;
+int blankcount = 0;
static void sysbeep(void);
@@ -84,28 +146,49 @@ static void sysbeep(void);
*/
#define RESPONSE "\033[?1;2c"
+static char * translations[] = {
+/* normal 7-bit ascii */
+ " !\"#$%&'()*+,-./0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~ ",
+/* vt100 graphics */
+ " !\"#$%&'()*+,-./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 "
+};
+
+#define NORM_TRANS (translations[0])
+#define GRAF_TRANS (translations[1])
+
/* NOTE! gotoxy thinks x==video_num_columns is ok */
-static inline void gotoxy(unsigned int new_x,unsigned int new_y)
+static inline void gotoxy(int currcons, int new_x,unsigned int new_y)
{
if (new_x > video_num_columns || new_y >= video_num_lines)
return;
- x=new_x;
- y=new_y;
- pos=origin + y*video_size_row + (x<<1);
+ x = new_x;
+ y = new_y;
+ pos = origin + y*video_size_row + (x<<1);
}
-static inline void set_origin(void)
+static inline void set_origin(int currcons)
{
+ if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
+ return;
+ if (currcons != fg_console)
+ return;
cli();
outb_p(12, video_port_reg);
- outb_p(0xff&((origin-video_mem_start)>>9), video_port_val);
+ outb_p(0xff&((origin-video_mem_base)>>9), video_port_val);
outb_p(13, video_port_reg);
- outb_p(0xff&((origin-video_mem_start)>>1), video_port_val);
+ outb_p(0xff&((origin-video_mem_base)>>1), video_port_val);
sti();
}
-static void scrup(void)
+static void scrup(int currcons)
{
+ if (bottom<=top)
+ return;
if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)
{
if (!top && bottom == video_num_lines) {
@@ -136,7 +219,7 @@ static void scrup(void)
"D" (scr_end-video_size_row)
:"cx","di");
}
- set_origin();
+ set_origin(currcons);
} else {
__asm__("cld\n\t"
"rep\n\t"
@@ -167,8 +250,10 @@ static void scrup(void)
}
}
-static void scrdown(void)
+static void scrdown(int currcons)
{
+ if (bottom <= top)
+ return;
if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)
{
__asm__("std\n\t"
@@ -201,33 +286,33 @@ static void scrdown(void)
}
}
-static void lf(void)
+static void lf(int currcons)
{
if (y+1<bottom) {
y++;
pos += video_size_row;
return;
}
- scrup();
+ scrup(currcons);
}
-static void ri(void)
+static void ri(int currcons)
{
if (y>top) {
y--;
pos -= video_size_row;
return;
}
- scrdown();
+ scrdown(currcons);
}
-static void cr(void)
+static void cr(int currcons)
{
pos -= x<<1;
x=0;
}
-static void del(void)
+static void del(int currcons)
{
if (x) {
pos -= 2;
@@ -236,12 +321,12 @@ static void del(void)
}
}
-static void csi_J(int par)
+static void csi_J(int currcons, int vpar)
{
long count __asm__("cx");
long start __asm__("di");
- switch (par) {
+ switch (vpar) {
case 0: /* erase from cursor to end of display */
count = (scr_end-pos)>>1;
start = pos;
@@ -265,12 +350,12 @@ static void csi_J(int par)
:"cx","di");
}
-static void csi_K(int par)
+static void csi_K(int currcons, int vpar)
{
long count __asm__("cx");
long start __asm__("di");
- switch (par) {
+ switch (vpar) {
case 0: /* erase from cursor to end of line */
if (x>=video_num_columns)
return;
@@ -296,31 +381,74 @@ static void csi_K(int par)
:"cx","di");
}
-void csi_m(void)
+void csi_m(int currcons )
{
int i;
for (i=0;i<=npar;i++)
switch (par[i]) {
- case 0:attr=0x07;break;
- case 1:attr=0x0f;break;
- case 4:attr=0x0f;break;
- case 7:attr=0x70;break;
- case 27:attr=0x07;break;
+ 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<<4)|(attr>>4);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 & 0xf0)|(def_attr & 0x0f); break;
+ case 49: attr=(attr & 0x0f)|(def_attr & 0xf0); break;
+ default:
+ if (!can_do_colour)
+ break;
+ iscolor = 1;
+ if ((par[i]>=30) && (par[i]<=38))
+ attr = (attr & 0xf0) | (par[i]-30);
+ else /* Background color */
+ if ((par[i]>=40) && (par[i]<=48))
+ attr = (attr & 0x0f) | ((par[i]-40)<<4);
+ else
+ break;
}
}
-static inline void set_cursor(void)
+static inline void set_cursor(int currcons)
{
+ blankcount = blankinterval;
+ if (currcons != fg_console)
+ return;
cli();
outb_p(14, video_port_reg);
- outb_p(0xff&((pos-video_mem_start)>>9), video_port_val);
+ outb_p(0xff&((pos-video_mem_base)>>9), video_port_val);
outb_p(15, video_port_reg);
- outb_p(0xff&((pos-video_mem_start)>>1), video_port_val);
+ outb_p(0xff&((pos-video_mem_base)>>1), video_port_val);
sti();
}
-static void respond(struct tty_struct * tty)
+static inline void hide_cursor(int currcons)
+{
+ outb_p(14, video_port_reg);
+ outb_p(0xff&((scr_end-video_mem_base)>>9), video_port_val);
+ outb_p(15, video_port_reg);
+ outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val);
+}
+
+static void respond(int currcons, struct tty_struct * tty)
{
char * p = RESPONSE;
@@ -333,7 +461,7 @@ static void respond(struct tty_struct * tty)
copy_to_cooked(tty);
}
-static void insert_char(void)
+static void insert_char(int currcons)
{
int i=x;
unsigned short tmp, old = video_erase_char;
@@ -347,7 +475,7 @@ static void insert_char(void)
}
}
-static void insert_line(void)
+static void insert_line(int currcons)
{
int oldtop,oldbottom;
@@ -355,12 +483,12 @@ static void insert_line(void)
oldbottom=bottom;
top=y;
bottom = video_num_lines;
- scrdown();
+ scrdown(currcons);
top=oldtop;
bottom=oldbottom;
}
-static void delete_char(void)
+static void delete_char(int currcons)
{
int i;
unsigned short * p = (unsigned short *) pos;
@@ -375,7 +503,7 @@ static void delete_char(void)
*p = video_erase_char;
}
-static void delete_line(void)
+static void delete_line(int currcons)
{
int oldtop,oldbottom;
@@ -383,95 +511,107 @@ static void delete_line(void)
oldbottom=bottom;
top=y;
bottom = video_num_lines;
- scrup();
+ scrup(currcons);
top=oldtop;
bottom=oldbottom;
}
-static void csi_at(unsigned int nr)
+static void csi_at(int currcons, unsigned int nr)
{
if (nr > video_num_columns)
nr = video_num_columns;
else if (!nr)
nr = 1;
while (nr--)
- insert_char();
+ insert_char(currcons);
}
-static void csi_L(unsigned int nr)
+static void csi_L(int currcons, unsigned int nr)
{
if (nr > video_num_lines)
nr = video_num_lines;
else if (!nr)
nr = 1;
while (nr--)
- insert_line();
+ insert_line(currcons);
}
-static void csi_P(unsigned int nr)
+static void csi_P(int currcons, unsigned int nr)
{
if (nr > video_num_columns)
nr = video_num_columns;
else if (!nr)
nr = 1;
while (nr--)
- delete_char();
+ delete_char(currcons);
}
-static void csi_M(unsigned int nr)
+static void csi_M(int currcons, unsigned int nr)
{
if (nr > video_num_lines)
nr = video_num_lines;
else if (!nr)
nr=1;
while (nr--)
- delete_line();
+ delete_line(currcons);
}
-static int saved_x=0;
-static int saved_y=0;
-
-static void save_cur(void)
+static void save_cur(int currcons)
{
saved_x=x;
saved_y=y;
}
-static void restore_cur(void)
+static void restore_cur(int currcons)
{
- gotoxy(saved_x, saved_y);
+ gotoxy(currcons,saved_x, saved_y);
}
+
+enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
+ ESsetterm, ESsetgraph };
+
void con_write(struct tty_struct * tty)
{
int nr;
char c;
-
+ int currcons;
+
+ currcons = tty - tty_table;
+ if ((currcons>=MAX_CONSOLES) || (currcons<0))
+ panic("con_write: illegal tty");
+
nr = CHARS(tty->write_q);
while (nr--) {
+ if (tty->stopped)
+ break;
GETCH(tty->write_q,c);
+ if (c == 24 || c == 26)
+ state = ESnormal;
switch(state) {
- case 0:
+ case ESnormal:
if (c>31 && c<127) {
if (x>=video_num_columns) {
x -= video_num_columns;
pos -= video_size_row;
- lf();
+ lf(currcons);
}
- __asm__("movb _attr,%%ah\n\t"
+ __asm__("movb %2,%%ah\n\t"
"movw %%ax,%1\n\t"
- ::"a" (c),"m" (*(short *)pos)
+ ::"a" (translate[c-32]),
+ "m" (*(short *)pos),
+ "m" (attr)
:"ax");
pos += 2;
x++;
} else if (c==27)
- state=1;
+ state=ESesc;
else if (c==10 || c==11 || c==12)
- lf();
+ lf(currcons);
else if (c==13)
- cr();
+ cr(currcons);
else if (c==ERASE_CHAR(tty))
- del();
+ del(currcons);
else if (c==8) {
if (x) {
x--;
@@ -484,104 +624,144 @@ void con_write(struct tty_struct * tty)
if (x>video_num_columns) {
x -= video_num_columns;
pos -= video_size_row;
- lf();
+ lf(currcons);
}
c=9;
} else if (c==7)
sysbeep();
+ else if (c == 14)
+ translate = GRAF_TRANS;
+ else if (c == 15)
+ translate = NORM_TRANS;
break;
- case 1:
- state=0;
- if (c=='[')
- state=2;
- else if (c=='E')
- gotoxy(0,y+1);
- else if (c=='M')
- ri();
- else if (c=='D')
- lf();
- else if (c=='Z')
- respond(tty);
- else if (x=='7')
- save_cur();
- else if (x=='8')
- restore_cur();
+ case ESesc:
+ state = ESnormal;
+ switch (c)
+ {
+ case '[':
+ state=ESsquare;
+ break;
+ case 'E':
+ gotoxy(currcons,0,y+1);
+ break;
+ case 'M':
+ ri(currcons);
+ break;
+ case 'D':
+ lf(currcons);
+ break;
+ case 'Z':
+ respond(currcons,tty);
+ break;
+ case '7':
+ save_cur(currcons);
+ break;
+ case '8':
+ restore_cur(currcons);
+ break;
+ case '(': case ')':
+ state = ESsetgraph;
+ break;
+ case 'P':
+ state = ESsetterm;
+ break;
+ case '#':
+ state = -1;
+ break;
+ case 'c':
+ tty->termios = DEF_TERMIOS;
+ state = restate = ESnormal;
+ checkin = 0;
+ top = 0;
+ bottom = video_num_lines;
+ break;
+ /* case '>': Numeric keypad */
+ /* case '=': Appl. keypad */
+ }
break;
- case 2:
+ case ESsquare:
for(npar=0;npar<NPAR;npar++)
par[npar]=0;
npar=0;
- state=3;
+ state=ESgetpars;
+ if (c =='[') /* Function key */
+ { state=ESfunckey;
+ break;
+ }
if (ques=(c=='?'))
break;
- case 3:
+ case ESgetpars:
if (c==';' && npar<NPAR-1) {
npar++;
break;
} else if (c>='0' && c<='9') {
par[npar]=10*par[npar]+c-'0';
break;
- } else state=4;
- case 4:
- state=0;
+ } else state=ESgotpars;
+ case ESgotpars:
+ state = ESnormal;
+ if (ques)
+ { ques =0;
+ break;
+ }
switch(c) {
case 'G': case '`':
if (par[0]) par[0]--;
- gotoxy(par[0],y);
+ gotoxy(currcons,par[0],y);
break;
case 'A':
if (!par[0]) par[0]++;
- gotoxy(x,y-par[0]);
+ gotoxy(currcons,x,y-par[0]);
break;
case 'B': case 'e':
if (!par[0]) par[0]++;
- gotoxy(x,y+par[0]);
+ gotoxy(currcons,x,y+par[0]);
break;
case 'C': case 'a':
if (!par[0]) par[0]++;
- gotoxy(x+par[0],y);
+ gotoxy(currcons,x+par[0],y);
break;
case 'D':
if (!par[0]) par[0]++;
- gotoxy(x-par[0],y);
+ gotoxy(currcons,x-par[0],y);
break;
case 'E':
if (!par[0]) par[0]++;
- gotoxy(0,y+par[0]);
+ gotoxy(currcons,0,y+par[0]);
break;
case 'F':
if (!par[0]) par[0]++;
- gotoxy(0,y-par[0]);
+ gotoxy(currcons,0,y-par[0]);
break;
case 'd':
if (par[0]) par[0]--;
- gotoxy(x,par[0]);
+ gotoxy(currcons,x,par[0]);
break;
case 'H': case 'f':
if (par[0]) par[0]--;
if (par[1]) par[1]--;
- gotoxy(par[1],par[0]);
+ gotoxy(currcons,par[1],par[0]);
break;
case 'J':
- csi_J(par[0]);
+ csi_J(currcons,par[0]);
break;
case 'K':
- csi_K(par[0]);
+ csi_K(currcons,par[0]);
break;
case 'L':
- csi_L(par[0]);
+ csi_L(currcons,par[0]);
break;
case 'M':
- csi_M(par[0]);
+ csi_M(currcons,par[0]);
break;
case 'P':
- csi_P(par[0]);
+ csi_P(currcons,par[0]);
break;
case '@':
- csi_at(par[0]);
+ csi_at(currcons,par[0]);
break;
case 'm':
- csi_m();
+ csi_m(currcons);
break;
case 'r':
if (par[0]) par[0]--;
@@ -593,15 +773,52 @@ void con_write(struct tty_struct * tty)
}
break;
case 's':
- save_cur();
+ save_cur(currcons);
break;
case 'u':
- restore_cur();
+ 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]>=0)&&(par[0]<=60))
+ {
+ blankinterval = HZ*60*par[0];
+ blankcount = blankinterval;
+ }
+ if (c=='b')
+ vc_cons[currcons].vc_bold_attr
+ = par[0];
}
+ break;
+ case ESfunckey:
+ state = ESnormal;
+ break;
+ case ESsetterm: /* Setterm functions. */
+ 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:
+ state = ESnormal;
+ if (c == '0')
+ translate = GRAF_TRANS;
+ else if (c == 'B')
+ translate = NORM_TRANS;
+ break;
+ default:
+ state = ESnormal;
}
}
- set_cursor();
+ set_cursor(currcons);
}
/*
@@ -619,53 +836,65 @@ void con_init(void)
register unsigned char a;
char *display_desc = "????";
char *display_ptr;
+ int currcons = 0;
+ long base, term;
+ long video_memory;
video_num_columns = ORIG_VIDEO_COLS;
video_size_row = video_num_columns * 2;
video_num_lines = ORIG_VIDEO_LINES;
video_page = ORIG_VIDEO_PAGE;
video_erase_char = 0x0720;
+ blankcount = blankinterval;
- if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
+ if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
{
- video_mem_start = 0xb0000;
+ video_mem_base = 0xb0000;
video_port_reg = 0x3b4;
video_port_val = 0x3b5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAM;
- video_mem_end = 0xb8000;
+ video_mem_term = 0xb8000;
display_desc = "EGAm";
}
else
{
video_type = VIDEO_TYPE_MDA;
- video_mem_end = 0xb2000;
+ video_mem_term = 0xb2000;
display_desc = "*MDA";
}
}
- else /* If not, it is color. */
+ else /* If not, it is color. */
{
- video_mem_start = 0xb8000;
+ can_do_colour = 1;
+ video_mem_base = 0xb8000;
video_port_reg = 0x3d4;
video_port_val = 0x3d5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAC;
- video_mem_end = 0xbc000;
+ video_mem_term = 0xc0000;
display_desc = "EGAc";
}
else
{
video_type = VIDEO_TYPE_CGA;
- video_mem_end = 0xba000;
+ video_mem_term = 0xba000;
display_desc = "*CGA";
}
}
+ video_memory = video_mem_term - video_mem_base;
+ NR_CONSOLES = video_memory / (video_num_lines * video_size_row);
+ if (NR_CONSOLES > MAX_CONSOLES)
+ NR_CONSOLES = MAX_CONSOLES;
+ if (!NR_CONSOLES)
+ NR_CONSOLES = 1;
+ video_memory /= NR_CONSOLES;
/* Let the user known what kind of display driver we are using */
- display_ptr = ((char *)video_mem_start) + video_size_row - 8;
+ display_ptr = ((char *)video_mem_base) + video_size_row - 8;
while (*display_desc)
{
*display_ptr++ = *display_desc++;
@@ -674,18 +903,42 @@ void con_init(void)
/* Initialize the variables used for scrolling (mostly EGA/VGA) */
- origin = video_mem_start;
+ base = origin = video_mem_start = video_mem_base;
+ term = video_mem_end = base + video_memory;
scr_end = video_mem_start + video_num_lines * video_size_row;
top = 0;
bottom = video_num_lines;
+ attr = 0x07;
+ def_attr = 0x07;
+ restate = state = ESnormal;
+ checkin = 0;
+ ques = 0;
+ iscolor = 0;
+ translate = NORM_TRANS;
+ vc_cons[0].vc_bold_attr = -1;
- gotoxy(ORIG_X,ORIG_Y);
+ gotoxy(currcons,ORIG_X,ORIG_Y);
+ for (currcons = 1; currcons<NR_CONSOLES; currcons++) {
+ vc_cons[currcons] = vc_cons[0];
+ origin = video_mem_start = (base += video_memory);
+ scr_end = origin + video_num_lines * video_size_row;
+ video_mem_end = (term += video_memory);
+ gotoxy(currcons,0,0);
+ }
+ update_screen();
set_trap_gate(0x21,&keyboard_interrupt);
outb_p(inb_p(0x21)&0xfd,0x21);
a=inb_p(0x61);
outb_p(a|0x80,0x61);
- outb(a,0x61);
+ outb_p(a,0x61);
}
+
+void update_screen(void)
+{
+ set_origin(fg_console);
+ set_cursor(fg_console);
+}
+
/* from bsd-net-2: */
void sysbeepstop(void)
@@ -708,3 +961,65 @@ static void sysbeep(void)
/* 1/8 second */
beepcount = HZ/8;
}
+
+int do_screendump(int arg)
+{
+ char *sptr, *buf = (char *)arg;
+ int currcons, l;
+
+ verify_area(buf,video_num_columns*video_num_lines);
+ currcons = get_fs_byte(buf);
+ if ((currcons<1) || (currcons>NR_CONSOLES))
+ return -EIO;
+ currcons--;
+ sptr = (char *) origin;
+ for (l=video_num_lines*video_num_columns; l>0 ; l--)
+ put_fs_byte(*sptr++,buf++);
+ return(0);
+}
+
+void blank_screen()
+{
+ if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
+ return;
+/* blank here. I can't find out how to do it, though */
+}
+
+void unblank_screen()
+{
+ if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
+ return;
+/* unblank here */
+}
+
+void console_print(const char * b)
+{
+ int currcons = fg_console;
+ char c;
+
+ while (c = *(b++)) {
+ if (c == 10) {
+ cr(currcons);
+ lf(currcons);
+ continue;
+ }
+ if (c == 13) {
+ cr(currcons);
+ continue;
+ }
+ if (x>=video_num_columns) {
+ x -= video_num_columns;
+ pos -= video_size_row;
+ lf(currcons);
+ }
+ __asm__("movb %2,%%ah\n\t"
+ "movw %%ax,%1\n\t"
+ ::"a" (c),
+ "m" (*(short *)pos),
+ "m" (attr)
+ :"ax");
+ pos += 2;
+ x++;
+ }
+ set_cursor(currcons);
+}
diff --git a/kernel/chr_drv/keyboard.S b/kernel/chr_drv/keyboard.S
index 709ef9a..5995738 100644
--- a/kernel/chr_drv/keyboard.S
+++ b/kernel/chr_drv/keyboard.S
@@ -10,7 +10,12 @@
* Marc Corsini for the French keyboard
*/
-#include <linux/config.h>
+/* KBD_FINNISH for Finnish keyboards
+ * KBD_US for US-type
+ * KBD_GR for German keyboards
+ * KBD_FR for Frech keyboard
+ */
+#define KBD_FINNISH
.text
.globl _keyboard_interrupt
@@ -44,7 +49,9 @@ _keyboard_interrupt:
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
- xorl %al,%al /* %eax is scan code */
+ movl _blankinterval,%eax
+ movl %eax,_blankcount
+ xorl %eax,%eax /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
@@ -155,7 +162,12 @@ set_leds:
uncaps: andb $0x7f,mode
ret
scroll:
- xorb $1,leds
+ testb $0x03,mode
+ je 1f
+ call _show_mem
+ jmp 2f
+1: call _show_state
+2: xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
@@ -208,13 +220,6 @@ cur_table:
* this routine handles function keys
*/
func:
- pushl %eax
- pushl %ecx
- pushl %edx
- call _show_stat
- popl %edx
- popl %ecx
- popl %eax
subb $0x3B,%al
jb end_func
cmpb $9,%al
@@ -225,11 +230,17 @@ func:
cmpb $11,%al
ja end_func
ok_func:
+ testb $0x10,mode
+ jne alt_func
cmpl $4,%ecx /* check that there is enough room */
jl end_func
movl func_table(,%eax,4),%eax
xorl %ebx,%ebx
jmp put_queue
+alt_func:
+ pushl %eax
+ call _change_console
+ popl %eax
end_func:
ret
diff --git a/kernel/chr_drv/pty.c b/kernel/chr_drv/pty.c
new file mode 100644
index 0000000..43407dc
--- /dev/null
+++ b/kernel/chr_drv/pty.c
@@ -0,0 +1,63 @@
+/*
+ * linux/kernel/chr_drv/pty.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * pty.c
+ *
+ * This module implements the pty functions
+ * void mpty_write(struct tty_struct * queue);
+ * void spty_write(struct tty_struct * queue);
+ */
+
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
+{
+ char c;
+
+ while (!from->stopped && !EMPTY(from->write_q)) {
+ if (FULL(to->read_q)) {
+ if (FULL(to->secondary))
+ break;
+ copy_to_cooked(to);
+ continue;
+ }
+ GETCH(from->write_q,c);
+ PUTCH(c,to->read_q);
+ if (current->signal & ~current->blocked)
+ break;
+ }
+ copy_to_cooked(to);
+ wake_up(&from->write_q->proc_list);
+}
+
+/*
+ * This routine gets called when tty_write has put something into
+ * the write_queue. It copies the input to the output-queue of it's
+ * slave.
+ */
+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);
+}
+
+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);
+}
diff --git a/kernel/chr_drv/rs_io.s b/kernel/chr_drv/rs_io.s
index b300643..67baadb 100644
--- a/kernel/chr_drv/rs_io.s
+++ b/kernel/chr_drv/rs_io.s
@@ -105,7 +105,8 @@ read_char:
cmpl tail(%ecx),%ebx
je 1f
movl %ebx,head(%ecx)
-1: pushl %edx
+1: addl $63,%edx
+ pushl %edx
call _do_tty_interrupt
addl $4,%esp
ret
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index aba25df..a78b1e5 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -38,8 +38,8 @@ void rs_init(void)
{
set_intr_gate(0x24,rs1_interrupt);
set_intr_gate(0x23,rs2_interrupt);
- init(tty_table[1].read_q.data);
- init(tty_table[2].read_q.data);
+ init(tty_table[64].read_q->data);
+ init(tty_table[65].read_q->data);
outb(inb_p(0x21)&0xE7,0x21);
}
@@ -54,6 +54,6 @@ void rs_write(struct tty_struct * tty)
{
cli();
if (!EMPTY(tty->write_q))
- outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1);
+ outb(inb_p(tty->write_q->data+1)|0x02,tty->write_q->data+1);
sti();
}
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index b8da643..6551da1 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -8,23 +8,24 @@
* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
* or rs-channels. It also implements echoing, cooked mode etc.
*
- * Kill-line thanks to John T Kohl.
+ * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
*/
+
#include <ctype.h>
#include <errno.h>
#include <signal.h>
+#include <unistd.h>
#define ALRMMASK (1<<(SIGALRM-1))
-#define KILLMASK (1<<(SIGKILL-1))
-#define INTMASK (1<<(SIGINT-1))
-#define QUITMASK (1<<(SIGQUIT-1))
-#define TSTPMASK (1<<(SIGTSTP-1))
#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/segment.h>
#include <asm/system.h>
+int kill_pg(int pgrp, int sig, int priv);
+int is_orphaned_pgrp(int pgrp);
+
#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)
@@ -36,11 +37,13 @@
#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
#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 O_POST(tty) _O_FLAG((tty),OPOST)
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
@@ -48,136 +51,119 @@
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
-struct tty_struct tty_table[] = {
- {
- {ICRNL, /* change incoming CR to NL */
- OPOST|ONLCR, /* change outgoing NL to CRNL */
- 0,
- ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
- 0, /* console termio */
- INIT_C_CC},
- 0, /* initial pgrp */
- 0, /* initial stopped */
- con_write,
- {0,0,0,0,""}, /* console read-queue */
- {0,0,0,0,""}, /* console write-queue */
- {0,0,0,0,""} /* console secondary queue */
- },{
- {0, /* no translation */
- 0, /* no translation */
- B2400 | CS8,
- 0,
- 0,
- INIT_C_CC},
- 0,
- 0,
- rs_write,
- {0x3f8,0,0,0,""}, /* rs 1 */
- {0x3f8,0,0,0,""},
- {0,0,0,0,""}
- },{
- {0, /* no translation */
- 0, /* no translation */
- B2400 | CS8,
- 0,
- 0,
- INIT_C_CC},
- 0,
- 0,
- rs_write,
- {0x2f8,0,0,0,""}, /* rs 2 */
- {0x2f8,0,0,0,""},
- {0,0,0,0,""}
- }
-};
+#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];
+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 con_table tty_table
+#define rs_table (64+tty_table)
+#define mpty_table (128+tty_table)
+#define spty_table (192+tty_table)
+
+int fg_console = 0;
/*
* these are the tables used by the machine code handlers.
- * you can implement pseudo-tty's or something by changing
- * them. Currently not done.
+ * you can implement virtual consoles.
*/
struct tty_queue * table_list[]={
- &tty_table[0].read_q, &tty_table[0].write_q,
- &tty_table[1].read_q, &tty_table[1].write_q,
- &tty_table[2].read_q, &tty_table[2].write_q
+ con_queues + 0, con_queues + 1,
+ rs_queues + 0, rs_queues + 1,
+ rs_queues + 3, rs_queues + 4
};
-void tty_init(void)
+void change_console(unsigned int new_console)
{
- rs_init();
- con_init();
-}
-
-void tty_intr(struct tty_struct * tty, int mask)
-{
- int i;
-
- if (tty->pgrp <= 0)
+ if (new_console == fg_console || new_console >= NR_CONSOLES)
return;
- for (i=0;i<NR_TASKS;i++)
- if (task[i] && task[i]->pgrp==tty->pgrp)
- task[i]->signal |= mask;
+ fg_console = new_console;
+ table_list[0] = con_queues + 0 + fg_console*3;
+ table_list[1] = con_queues + 1 + fg_console*3;
+ update_screen();
}
static void sleep_if_empty(struct tty_queue * queue)
{
cli();
- while (!current->signal && EMPTY(*queue))
+ while (!(current->signal & ~current->blocked) && EMPTY(queue))
interruptible_sleep_on(&queue->proc_list);
sti();
}
static void sleep_if_full(struct tty_queue * queue)
{
- if (!FULL(*queue))
+ if (!FULL(queue))
return;
cli();
- while (!current->signal && LEFT(*queue)<128)
+ 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[0].secondary);
+ sleep_if_empty(tty_table[fg_console].secondary);
}
void copy_to_cooked(struct tty_struct * tty)
{
signed char c;
- while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {
+ if (!(tty->read_q || tty->write_q || tty->secondary)) {
+ printk("copy_to_cooked: missing queues\n\r");
+ return;
+ }
+ while (1) {
+ if (EMPTY(tty->read_q))
+ break;
+ if (FULL(tty->secondary))
+ break;
GETCH(tty->read_q,c);
- if (c==13)
+ if (c==13) {
if (I_CRNL(tty))
c=10;
else if (I_NOCR(tty))
continue;
- else ;
- else if (c==10 && I_NLCR(tty))
+ } else if (c==10 && I_NLCR(tty))
c=13;
if (I_UCLC(tty))
c=tolower(c);
if (L_CANON(tty)) {
- if (c==KILL_CHAR(tty)) {
+ if ((KILL_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==KILL_CHAR(tty))) {
/* deal with killing the input line */
while(!(EMPTY(tty->secondary) ||
(c=LAST(tty->secondary))==10 ||
- c==EOF_CHAR(tty))) {
+ ((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==EOF_CHAR(tty))))) {
if (L_ECHO(tty)) {
if (c<32)
PUTCH(127,tty->write_q);
PUTCH(127,tty->write_q);
tty->write(tty);
}
- DEC(tty->secondary.head);
+ DEC(tty->secondary->head);
}
continue;
}
- if (c==ERASE_CHAR(tty)) {
+ if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==ERASE_CHAR(tty))) {
if (EMPTY(tty->secondary) ||
(c=LAST(tty->secondary))==10 ||
- c==EOF_CHAR(tty))
+ ((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==EOF_CHAR(tty))))
continue;
if (L_ECHO(tty)) {
if (c<32)
@@ -185,30 +171,45 @@ void copy_to_cooked(struct tty_struct * tty)
PUTCH(127,tty->write_q);
tty->write(tty);
}
- DEC(tty->secondary.head);
+ DEC(tty->secondary->head);
continue;
}
- if (c==STOP_CHAR(tty)) {
+ }
+ if (I_IXON(tty)) {
+ if ((STOP_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==STOP_CHAR(tty))) {
tty->stopped=1;
+ tty->write(tty);
continue;
}
- if (c==START_CHAR(tty)) {
+ if ((START_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==START_CHAR(tty))) {
tty->stopped=0;
+ tty->write(tty);
continue;
}
}
if (L_ISIG(tty)) {
- if (c==INTR_CHAR(tty)) {
- tty_intr(tty,INTMASK);
+ if ((INTR_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==INTR_CHAR(tty))) {
+ kill_pg(tty->pgrp, SIGINT, 1);
+ continue;
+ }
+ if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==QUIT_CHAR(tty))) {
+ kill_pg(tty->pgrp, SIGQUIT, 1);
continue;
}
- if (c==QUIT_CHAR(tty)) {
- tty_intr(tty,QUITMASK);
+ if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) &&
+ (c==SUSPEND_CHAR(tty))) {
+ if (!is_orphaned_pgrp(tty->pgrp))
+ kill_pg(tty->pgrp, SIGTSTP, 1);
continue;
}
}
- if (c==10 || c==EOF_CHAR(tty))
- tty->secondary.data++;
+ if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE &&
+ c==EOF_CHAR(tty)))
+ tty->secondary->data++;
if (L_ECHO(tty)) {
if (c==10) {
PUTCH(10,tty->write_q);
@@ -224,66 +225,114 @@ void copy_to_cooked(struct tty_struct * tty)
}
PUTCH(c,tty->secondary);
}
- wake_up(&tty->secondary.proc_list);
+ wake_up(&tty->secondary->proc_list);
+}
+
+/*
+ * Called when we need to send a SIGTTIN or SIGTTOU to our process
+ * group
+ *
+ * We only request that a system call be restarted if there was if the
+ * default signal handler is being used. The reason for this is that if
+ * a job is catching SIGTTIN or SIGTTOU, the signal handler may not want
+ * the system call to be restarted blindly. If there is no way to reset the
+ * terminal pgrp back to the current pgrp (perhaps because the controlling
+ * tty has been released on logout), we don't want to be in an infinite loop
+ * while restarting the system call, and have it always generate a SIGTTIN
+ * or SIGTTOU. The default signal handler will cause the process to stop
+ * thus avoiding the infinite loop problem. Presumably the job-control
+ * cognizant parent will fix things up before continuging its child process.
+ */
+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) */
}
int tty_read(unsigned channel, char * buf, int nr)
{
struct tty_struct * tty;
+ struct tty_struct * other_tty = NULL;
char c, * b=buf;
- int minimum,time,flag=0;
- long oldalarm;
+ int minimum,time;
- if (channel>2 || nr<0) return -1;
- tty = &tty_table[channel];
- oldalarm = current->alarm;
+ if (channel > 255)
+ return -EIO;
+ tty = TTY_TABLE(channel);
+ if (!(tty->write_q || tty->read_q || tty->secondary))
+ return -EIO;
+ if ((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 (time && !minimum) {
- minimum=1;
- if (flag=(!oldalarm || time+jiffies<oldalarm))
- current->alarm = time+jiffies;
+ if (L_CANON(tty)) {
+ minimum = nr;
+ current->timeout = 0xffffffff;
+ time = 0;
+ } else if (minimum)
+ current->timeout = 0xffffffff;
+ else {
+ minimum = nr;
+ if (time)
+ current->timeout = time + jiffies;
+ time = 0;
}
if (minimum>nr)
- minimum=nr;
+ minimum = nr;
while (nr>0) {
- if (flag && (current->signal & ALRMMASK)) {
- current->signal &= ~ALRMMASK;
- break;
- }
- if (current->signal)
- break;
+ if (other_tty)
+ other_tty->write(other_tty);
+ cli();
if (EMPTY(tty->secondary) || (L_CANON(tty) &&
- !tty->secondary.data && LEFT(tty->secondary)>20)) {
- sleep_if_empty(&tty->secondary);
+ !FULL(tty->read_q) && !tty->secondary->data)) {
+ if (!current->timeout ||
+ (current->signal & ~current->blocked)) {
+ sti();
+ break;
+ }
+ if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty))
+ break;
+ interruptible_sleep_on(&tty->secondary->proc_list);
+ sti();
continue;
}
+ sti();
do {
GETCH(tty->secondary,c);
- if (c==EOF_CHAR(tty) || c==10)
- tty->secondary.data--;
- if (c==EOF_CHAR(tty) && L_CANON(tty))
- return (b-buf);
+ if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
+ c==EOF_CHAR(tty)) || c==10)
+ tty->secondary->data--;
+ if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
+ c==EOF_CHAR(tty)) && L_CANON(tty))
+ break;
else {
put_fs_byte(c,b++);
if (!--nr)
break;
}
- } while (nr>0 && !EMPTY(tty->secondary));
- if (time && !L_CANON(tty))
- if (flag=(!oldalarm || time+jiffies<oldalarm))
- current->alarm = time+jiffies;
- else
- current->alarm = oldalarm;
- if (L_CANON(tty)) {
- if (b-buf)
+ if (c==10 && L_CANON(tty))
break;
- } else if (b-buf >= minimum)
+ } while (nr>0 && !EMPTY(tty->secondary));
+ wake_up(&tty->read_q->proc_list);
+ if (time)
+ current->timeout = time+jiffies;
+ if (L_CANON(tty) || b-buf >= minimum)
break;
}
- current->alarm = oldalarm;
- if (current->signal && !(b-buf))
- return -EINTR;
+ current->timeout = 0;
+ if ((current->signal & ~current->blocked) && !(b-buf))
+ return -ERESTARTSYS;
return (b-buf);
}
@@ -293,11 +342,17 @@ int tty_write(unsigned channel, char * buf, int nr)
struct tty_struct * tty;
char c, *b=buf;
- if (channel>2 || nr<0) return -1;
- tty = channel + tty_table;
+ if (channel > 255)
+ return -EIO;
+ tty = TTY_TABLE(channel);
+ if (!(tty->write_q || tty->read_q || tty->secondary))
+ return -EIO;
+ if (L_TOSTOP(tty) &&
+ (current->tty == channel) && (tty->pgrp != current->pgrp))
+ return(tty_signal(SIGTTOU, tty));
while (nr>0) {
- sleep_if_full(&tty->write_q);
- if (current->signal)
+ sleep_if_full(tty->write_q);
+ if (current->signal & ~current->blocked)
break;
while (nr>0 && !FULL(tty->write_q)) {
c=get_fs_byte(b);
@@ -341,9 +396,89 @@ int tty_write(unsigned channel, char * buf, int nr)
*/
void do_tty_interrupt(int tty)
{
- copy_to_cooked(tty_table+tty);
+ copy_to_cooked(TTY_TABLE(tty));
}
void chr_dev_init(void)
{
}
+
+void tty_init(void)
+{
+ int i;
+
+ 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,""};
+ for (i=0 ; i<256 ; i++) {
+ tty_table[i] = (struct tty_struct) {
+ {0, 0, 0, 0, 0, INIT_C_CC},
+ 0, 0, 0, NULL, NULL, NULL, NULL
+ };
+ }
+ con_init();
+ for (i = 0 ; i<NR_CONSOLES ; i++) {
+ con_table[i] = (struct tty_struct) {
+ {ICRNL, /* change incoming CR to NL */
+ OPOST|ONLCR, /* change outgoing NL to CRNL */
+ 0,
+ IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
+ 0, /* console termio */
+ INIT_C_CC},
+ 0, /* initial pgrp */
+ 0, /* initial session */
+ 0, /* initial stopped */
+ con_write,
+ con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
+ };
+ }
+ for (i = 0 ; i<NR_SERIALS ; i++) {
+ rs_table[i] = (struct tty_struct) {
+ {0, /* no translation */
+ 0, /* no translation */
+ B2400 | CS8,
+ 0,
+ 0,
+ INIT_C_CC},
+ 0,
+ 0,
+ 0,
+ rs_write,
+ rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
+ };
+ }
+ for (i = 0 ; i<NR_PTYS ; i++) {
+ mpty_table[i] = (struct tty_struct) {
+ {0, /* no translation */
+ 0, /* no translation */
+ B9600 | CS8,
+ 0,
+ 0,
+ INIT_C_CC},
+ 0,
+ 0,
+ 0,
+ mpty_write,
+ mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
+ };
+ spty_table[i] = (struct tty_struct) {
+ {0, /* no translation */
+ 0, /* no translation */
+ B9600 | CS8,
+ IXON | ISIG | ICANON,
+ 0,
+ INIT_C_CC},
+ 0,
+ 0,
+ 0,
+ spty_write,
+ spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
+ };
+ }
+ rs_init();
+ printk("%d virtual consoles\n\r",NR_CONSOLES);
+ printk("%d pty's\n\r",NR_PTYS);
+}
diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c
index e4e3745..c3ac9a1 100644
--- a/kernel/chr_drv/tty_ioctl.c
+++ b/kernel/chr_drv/tty_ioctl.c
@@ -15,6 +15,9 @@
#include <asm/segment.h>
#include <asm/system.h>
+extern int session_of_pgrp(int pgrp);
+extern int tty_signal(int sig, struct tty_struct *tty);
+
static unsigned short quotient[] = {
0, 2304, 1536, 1047, 857,
768, 576, 384, 192, 96,
@@ -25,7 +28,7 @@ static void change_speed(struct tty_struct * tty)
{
unsigned short port,quot;
- if (!(port = tty->read_q.data))
+ if (!(port = tty->read_q->data))
return;
quot = quotient[tty->termios.c_cflag & CBAUD];
cli();
@@ -63,10 +66,19 @@ static int get_termios(struct tty_struct * tty, struct termios * termios)
return 0;
}
-static int set_termios(struct tty_struct * tty, struct termios * termios)
+static int set_termios(struct tty_struct * tty, struct termios * termios,
+ int channel)
{
- int i;
-
+ int i, retsig;
+
+ /* 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;
+ }
for (i=0 ; i< (sizeof (*termios)) ; i++)
((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
change_speed(tty);
@@ -94,11 +106,17 @@ static int get_termio(struct tty_struct * tty, struct termio * termio)
/*
* This only works as the 386 is low-byt-first
*/
-static int set_termio(struct tty_struct * tty, struct termio * termio)
+static int set_termio(struct tty_struct * tty, struct termio * termio,
+ int channel)
{
- int i;
+ int i, retsig;
struct termio tmp_termio;
+ if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
+ retsig = tty_signal(SIGTTOU, tty);
+ if (retsig == -ERESTARTSYS || retsig == -EINTR)
+ return retsig;
+ }
for (i=0 ; i< (sizeof (*termio)) ; i++)
((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
*(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
@@ -115,30 +133,32 @@ static int set_termio(struct tty_struct * tty, struct termio * termio)
int tty_ioctl(int dev, int cmd, int arg)
{
struct tty_struct * tty;
+ int pgrp;
+
if (MAJOR(dev) == 5) {
dev=current->tty;
if (dev<0)
panic("tty_ioctl: dev<0");
} else
dev=MINOR(dev);
- tty = dev + tty_table;
+ tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console);
switch (cmd) {
case TCGETS:
return get_termios(tty,(struct termios *) arg);
case TCSETSF:
- flush(&tty->read_q); /* fallthrough */
+ flush(tty->read_q); /* fallthrough */
case TCSETSW:
wait_until_sent(tty); /* fallthrough */
case TCSETS:
- return set_termios(tty,(struct termios *) arg);
+ return set_termios(tty,(struct termios *) arg, dev);
case TCGETA:
return get_termio(tty,(struct termio *) arg);
case TCSETAF:
- flush(&tty->read_q); /* fallthrough */
+ flush(tty->read_q); /* fallthrough */
case TCSETAW:
wait_until_sent(tty); /* fallthrough */
case TCSETA:
- return set_termio(tty,(struct termio *) arg);
+ return set_termio(tty,(struct termio *) arg, dev);
case TCSBRK:
if (!arg) {
wait_until_sent(tty);
@@ -146,15 +166,33 @@ int tty_ioctl(int dev, int cmd, int arg)
}
return 0;
case TCXONC:
+ switch (arg) {
+ case TCOOFF:
+ tty->stopped = 1;
+ tty->write(tty);
+ return 0;
+ case TCOON:
+ tty->stopped = 0;
+ tty->write(tty);
+ return 0;
+ case TCIOFF:
+ if (STOP_CHAR(tty))
+ PUTCH(STOP_CHAR(tty),tty->write_q);
+ return 0;
+ case TCION:
+ if (START_CHAR(tty))
+ PUTCH(START_CHAR(tty),tty->write_q);
+ return 0;
+ }
return -EINVAL; /* not implemented */
case TCFLSH:
if (arg==0)
- flush(&tty->read_q);
+ flush(tty->read_q);
else if (arg==1)
- flush(&tty->write_q);
+ flush(tty->write_q);
else if (arg==2) {
- flush(&tty->read_q);
- flush(&tty->write_q);
+ flush(tty->read_q);
+ flush(tty->write_q);
} else
return -EINVAL;
return 0;
@@ -169,7 +207,16 @@ int tty_ioctl(int dev, int cmd, int arg)
put_fs_long(tty->pgrp,(unsigned long *) arg);
return 0;
case TIOCSPGRP:
- tty->pgrp=get_fs_long((unsigned long *) arg);
+ if ((current->tty < 0) ||
+ (current->tty != dev) ||
+ (tty->session != current->session))
+ return -ENOTTY;
+ pgrp=get_fs_long((unsigned long *) arg);
+ if (pgrp < 0)
+ return -EINVAL;
+ if (session_of_pgrp(pgrp) != current->session)
+ return -EPERM;
+ tty->pgrp = pgrp;
return 0;
case TIOCOUTQ:
verify_area((void *) arg,4);
diff --git a/kernel/exit.c b/kernel/exit.c
index 2406ebe..381995b 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -4,6 +4,8 @@
* (C) 1991 Linus Torvalds
*/
+#define DEBUG_PROC_TREE
+
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
@@ -22,9 +24,20 @@ void release(struct task_struct * p)
if (!p)
return;
+ if (p == current) {
+ printk("task releasing itself\n\r");
+ return;
+ }
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
+ /* Update links */
+ 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;
free_page((long)p);
schedule();
return;
@@ -32,149 +45,366 @@ void release(struct task_struct * p)
panic("trying to release non-existent task");
}
+#ifdef DEBUG_PROC_TREE
+/*
+ * Check to see if a task_struct pointer is present in the task[] array
+ * Return 0 if found, and 1 if not found.
+ */
+int bad_task_ptr(struct task_struct *p)
+{
+ int i;
+
+ if (!p)
+ return 0;
+ for (i=0 ; i<NR_TASKS ; i++)
+ if (task[i] == p)
+ return 0;
+ return 1;
+}
+
+/*
+ * This routine scans the pid tree and make sure the rep invarient still
+ * holds. Used for debugging only, since it's very slow....
+ *
+ * It looks a lot scarier than it really is.... we're doing ænothing more
+ * than verifying the doubly-linked list foundæin p_ysptr and p_osptr,
+ * and checking it corresponds with the process tree defined by p_cptr and
+ * p_pptr;
+ */
+void audit_ptree()
+{
+ int i;
+
+ for (i=1 ; i<NR_TASKS ; i++) {
+ if (!task[i])
+ continue;
+ if (bad_task_ptr(task[i]->p_pptr))
+ printk("Warning, pid %d's parent link is bad\n",
+ task[i]->pid);
+ if (bad_task_ptr(task[i]->p_cptr))
+ printk("Warning, pid %d's child link is bad\n",
+ task[i]->pid);
+ if (bad_task_ptr(task[i]->p_ysptr))
+ printk("Warning, pid %d's ys link is bad\n",
+ task[i]->pid);
+ if (bad_task_ptr(task[i]->p_osptr))
+ printk("Warning, pid %d's os link is bad\n",
+ task[i]->pid);
+ if (task[i]->p_pptr == task[i])
+ printk("Warning, pid %d parent link points to self\n");
+ if (task[i]->p_cptr == task[i])
+ printk("Warning, pid %d child link points to self\n");
+ if (task[i]->p_ysptr == task[i])
+ printk("Warning, pid %d ys link points to self\n");
+ if (task[i]->p_osptr == task[i])
+ printk("Warning, pid %d os link points to self\n");
+ if (task[i]->p_osptr) {
+ if (task[i]->p_pptr != task[i]->p_osptr->p_pptr)
+ printk(
+ "Warning, pid %d older sibling %d parent is %d\n",
+ task[i]->pid, task[i]->p_osptr->pid,
+ task[i]->p_osptr->p_pptr->pid);
+ if (task[i]->p_osptr->p_ysptr != task[i])
+ printk(
+ "Warning, pid %d older sibling %d has mismatched ys link\n",
+ task[i]->pid, task[i]->p_osptr->pid);
+ }
+ if (task[i]->p_ysptr) {
+ if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr)
+ printk(
+ "Warning, pid %d younger sibling %d parent is %d\n",
+ task[i]->pid, task[i]->p_osptr->pid,
+ task[i]->p_osptr->p_pptr->pid);
+ if (task[i]->p_ysptr->p_osptr != task[i])
+ printk(
+ "Warning, pid %d younger sibling %d has mismatched os link\n",
+ task[i]->pid, task[i]->p_ysptr->pid);
+ }
+ if (task[i]->p_cptr) {
+ if (task[i]->p_cptr->p_pptr != task[i])
+ printk(
+ "Warning, pid %d youngest child %d has mismatched parent link\n",
+ task[i]->pid, task[i]->p_cptr->pid);
+ if (task[i]->p_cptr->p_ysptr)
+ printk(
+ "Warning, pid %d youngest child %d has non-NULL ys link\n",
+ task[i]->pid, task[i]->p_cptr->pid);
+ }
+ }
+}
+#endif /* DEBUG_PROC_TREE */
+
static inline int send_sig(long sig,struct task_struct * p,int priv)
{
- if (!p || sig<1 || sig>32)
+ if (!p)
return -EINVAL;
- if (priv || (current->euid==p->euid) || suser())
- p->signal |= (1<<(sig-1));
- else
+ if (!priv && (current->euid!=p->euid) && !suser())
return -EPERM;
+ if ((sig == SIGKILL) || (sig == SIGCONT)) {
+ if (p->state == TASK_STOPPED)
+ p->state = TASK_RUNNING;
+ p->exit_code = 0;
+ p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
+ (1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
+ }
+ /* If the signal will be ignored, don't even post it */
+ if ((int) p->sigaction[sig-1].sa_handler == 1)
+ return 0;
+ /* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
+ if ((sig >= SIGSTOP) && (sig <= SIGTTOU))
+ p->signal &= ~(1<<(SIGCONT-1));
+ /* Actually deliver the signal */
+ p->signal |= (1<<(sig-1));
return 0;
}
-static void kill_session(void)
+int session_of_pgrp(int pgrp)
{
- struct task_struct **p = NR_TASKS + task;
-
- while (--p > &FIRST_TASK) {
- if (*p && (*p)->session == current->session)
- (*p)->signal |= 1<<(SIGHUP-1);
- }
+ struct task_struct **p;
+
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+ if ((*p)->pgrp == pgrp)
+ return((*p)->session);
+ return -1;
+}
+
+int kill_pg(int pgrp, int sig, int priv)
+{
+ struct task_struct **p;
+ int err,retval = -ESRCH;
+ int found = 0;
+
+ if (sig<1 || sig>32 || pgrp<=0)
+ return -EINVAL;
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+ if ((*p)->pgrp == pgrp) {
+ if (sig && (err = send_sig(sig,*p,priv)))
+ retval = err;
+ else
+ found++;
+ }
+ return(found ? 0 : retval);
+}
+
+int kill_proc(int pid, int sig, int priv)
+{
+ struct task_struct **p;
+
+ if (sig<1 || sig>32)
+ return -EINVAL;
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+ if ((*p)->pid == pid)
+ return(sig ? send_sig(sig,*p,priv) : 0);
+ return(-ESRCH);
}
/*
- * XXX need to check permissions needed to send signals to process
- * groups, etc. etc. kill() permissions semantics are tricky!
+ * POSIX specifies that kill(-1,sig) is unspecified, but what we have
+ * is probably wrong. Should make it like BSD or SYSV.
*/
int sys_kill(int pid,int sig)
{
struct task_struct **p = NR_TASKS + task;
int err, retval = 0;
- if (!pid) while (--p > &FIRST_TASK) {
- if (*p && (*p)->pgrp == current->pid)
- if (err=send_sig(sig,*p,1))
- retval = err;
- } else if (pid>0) while (--p > &FIRST_TASK) {
- if (*p && (*p)->pid == pid)
- if (err=send_sig(sig,*p,0))
- retval = err;
- } else if (pid == -1) while (--p > &FIRST_TASK)
- if (err = send_sig(sig,*p,0))
- retval = err;
- else while (--p > &FIRST_TASK)
- if (*p && (*p)->pgrp == -pid)
+ if (!pid)
+ return(kill_pg(current->pid,sig,0));
+ if (pid == -1) {
+ while (--p > &FIRST_TASK)
if (err = send_sig(sig,*p,0))
retval = err;
- return retval;
+ return(retval);
+ }
+ if (pid < 0)
+ return(kill_pg(-pid,sig,0));
+ /* Normal kill */
+ return(kill_proc(pid,sig,0));
}
-static void tell_father(int pid)
+/*
+ * Determine if a process group is "orphaned", according to the POSIX
+ * definition in 2.2.2.52. Orphaned process groups are not to be affected
+ * by terminal-generated stop signals. Newly orphaned process groups are
+ * to receive a SIGHUP and a SIGCONT.
+ *
+ * "I ask you, have you ever known what it is to be an orphan?"
+ */
+int is_orphaned_pgrp(int pgrp)
{
- int i;
+ struct task_struct **p;
- if (pid)
- for (i=0;i<NR_TASKS;i++) {
- if (!task[i])
- continue;
- if (task[i]->pid != pid)
- continue;
- task[i]->signal |= (1<<(SIGCHLD-1));
- return;
- }
-/* if we don't find any fathers, we just release ourselves */
-/* This is not really OK. Must change it to make father 1 */
- printk("BAD BAD - no father found\n\r");
- release(current);
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ if (!(*p) ||
+ ((*p)->pgrp != pgrp) ||
+ ((*p)->state == TASK_ZOMBIE) ||
+ ((*p)->p_pptr->pid == 1))
+ continue;
+ if (((*p)->p_pptr->pgrp != pgrp) &&
+ ((*p)->p_pptr->session == (*p)->session))
+ return 0;
+ }
+ return(1); /* (sighing) "Often!" */
+}
+
+static int has_stopped_jobs(int pgrp)
+{
+ struct task_struct ** p;
+
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ if ((*p)->pgrp != pgrp)
+ continue;
+ if ((*p)->state == TASK_STOPPED)
+ return(1);
+ }
+ return(0);
}
-int do_exit(long code)
+volatile void do_exit(long code)
{
+ struct task_struct *p;
int i;
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 ; i<NR_TASKS ; i++)
- if (task[i] && task[i]->father == current->pid) {
- task[i]->father = 1;
- if (task[i]->state == TASK_ZOMBIE)
- /* assumption task[1] is always init */
- (void) send_sig(SIGCHLD, task[1], 1);
- }
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
sys_close(i);
iput(current->pwd);
- current->pwd=NULL;
+ current->pwd = NULL;
iput(current->root);
- current->root=NULL;
+ current->root = NULL;
iput(current->executable);
- current->executable=NULL;
- if (current->leader && current->tty >= 0)
- tty_table[current->tty].pgrp = 0;
- if (last_task_used_math == current)
- last_task_used_math = NULL;
- if (current->leader)
- kill_session();
+ current->executable = NULL;
+ iput(current->library);
+ current->library = NULL;
current->state = TASK_ZOMBIE;
current->exit_code = code;
- tell_father(current->father);
+ /*
+ * Check to see if any process groups have become orphaned
+ * as a result of our exiting, and if they have any stopped
+ * jobs, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2)
+ *
+ * Case i: Our father is in a different pgrp than we are
+ * and we were the only connection outside, so our pgrp
+ * is about to become orphaned.
+ */
+ if ((current->p_pptr->pgrp != current->pgrp) &&
+ (current->p_pptr->session == current->session) &&
+ is_orphaned_pgrp(current->pgrp) &&
+ has_stopped_jobs(current->pgrp)) {
+ kill_pg(current->pgrp,SIGHUP,1);
+ kill_pg(current->pgrp,SIGCONT,1);
+ }
+ /* Let father know we died */
+ current->p_pptr->signal |= (1<<(SIGCHLD-1));
+
+ /*
+ * This loop does two things:
+ *
+ * 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)
+ */
+ if (p = current->p_cptr) {
+ while (1) {
+ p->p_pptr = task[1];
+ if (p->state == TASK_ZOMBIE)
+ task[1]->signal |= (1<<(SIGCHLD-1));
+ /*
+ * process group orphan check
+ * Case ii: Our child is in a different pgrp
+ * than we are, and it was the only connection
+ * outside, so the child pgrp is now orphaned.
+ */
+ if ((p->pgrp != current->pgrp) &&
+ (p->session == current->session) &&
+ is_orphaned_pgrp(p->pgrp) &&
+ has_stopped_jobs(p->pgrp)) {
+ kill_pg(p->pgrp,SIGHUP,1);
+ kill_pg(p->pgrp,SIGCONT,1);
+ }
+ if (p->p_osptr) {
+ p = p->p_osptr;
+ continue;
+ }
+ /*
+ * This is it; link everything into init's children
+ * and leave
+ */
+ p->p_osptr = task[1]->p_cptr;
+ task[1]->p_cptr->p_ysptr = p;
+ task[1]->p_cptr = current->p_cptr;
+ current->p_cptr = 0;
+ break;
+ }
+ }
+ if (current->leader) {
+ struct task_struct **p;
+ struct tty_struct *tty;
+
+ if (current->tty >= 0) {
+ tty = TTY_TABLE(current->tty);
+ if (tty->pgrp>0)
+ kill_pg(tty->pgrp, SIGHUP, 1);
+ tty->pgrp = 0;
+ tty->session = 0;
+ }
+ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
+ if ((*p)->session == current->session)
+ (*p)->tty = -1;
+ }
+ if (last_task_used_math == current)
+ last_task_used_math = NULL;
+#ifdef DEBUG_PROC_TREE
+ audit_ptree();
+#endif
schedule();
- return (-1); /* just to suppress warnings */
}
int sys_exit(int error_code)
{
- return do_exit((error_code&0xff)<<8);
+ do_exit((error_code&0xff)<<8);
}
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
- int flag, code;
- struct task_struct ** p;
+ int flag;
+ struct task_struct *p;
+ unsigned long oldblocked;
verify_area(stat_addr,4);
repeat:
flag=0;
- for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if (!*p || *p == current)
- continue;
- if ((*p)->father != current->pid)
- continue;
+ for (p = current->p_cptr ; p ; p = p->p_osptr) {
if (pid>0) {
- if ((*p)->pid != pid)
+ if (p->pid != pid)
continue;
} else if (!pid) {
- if ((*p)->pgrp != current->pgrp)
+ if (p->pgrp != current->pgrp)
continue;
} else if (pid != -1) {
- if ((*p)->pgrp != -pid)
+ if (p->pgrp != -pid)
continue;
}
- switch ((*p)->state) {
+ switch (p->state) {
case TASK_STOPPED:
- if (!(options & WUNTRACED))
+ if (!(options & WUNTRACED) ||
+ !p->exit_code)
continue;
- put_fs_long(0x7f,stat_addr);
- return (*p)->pid;
+ put_fs_long((p->exit_code << 8) | 0x7f,
+ stat_addr);
+ p->exit_code = 0;
+ return p->pid;
case TASK_ZOMBIE:
- current->cutime += (*p)->utime;
- current->cstime += (*p)->stime;
- flag = (*p)->pid;
- code = (*p)->exit_code;
- release(*p);
- put_fs_long(code,stat_addr);
+ current->cutime += p->utime;
+ current->cstime += p->stime;
+ flag = p->pid;
+ put_fs_long(p->exit_code, stat_addr);
+ release(p);
+#ifdef DEBUG_PROC_TREE
+ audit_ptree();
+#endif
return flag;
default:
flag=1;
@@ -185,11 +415,14 @@ repeat:
if (options & WNOHANG)
return 0;
current->state=TASK_INTERRUPTIBLE;
+ oldblocked = current->blocked;
+ current->blocked &= ~(1<<(SIGCHLD-1));
schedule();
- if (!(current->signal &= ~(1<<(SIGCHLD-1))))
- goto repeat;
+ current->blocked = oldblocked;
+ if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1))))
+ return -ERESTARTSYS;
else
- return -EINTR;
+ goto repeat;
}
return -ECHILD;
}
diff --git a/kernel/fork.c b/kernel/fork.c
index 38a997a..4c076be 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -49,7 +49,7 @@ int copy_mem(int nr,struct task_struct * p)
panic("We don't support separate I&D");
if (data_limit < code_limit)
panic("Bad data_limit");
- new_data_base = new_code_base = nr * 0x4000000;
+ new_data_base = new_code_base = nr * TASK_SIZE;
p->start_code = new_code_base;
set_base(p->ldt[1],new_code_base);
set_base(p->ldt[2],new_data_base);
@@ -66,7 +66,7 @@ int copy_mem(int nr,struct task_struct * p)
* also copies the data segment in it's entirety.
*/
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
- long ebx,long ecx,long edx,
+ long ebx,long ecx,long edx, long orig_eax,
long fs,long es,long ds,
long eip,long cs,long eflags,long esp,long ss)
{
@@ -81,7 +81,6 @@ int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
*p = *current; /* NOTE! this doesn't copy the supervisor stack */
p->state = TASK_UNINTERRUPTIBLE;
p->pid = last_pid;
- p->father = current->pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
@@ -111,7 +110,7 @@ int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
- __asm__("clts ; fnsave %0"::"m" (p->tss.i387));
+ __asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
@@ -126,8 +125,17 @@ int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
+ if (current->library)
+ current->library->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
+ p->p_pptr = current;
+ p->p_cptr = 0;
+ p->p_ysptr = 0;
+ p->p_osptr = current->p_cptr;
+ if (p->p_osptr)
+ p->p_osptr->p_ysptr = p;
+ current->p_cptr = p;
p->state = TASK_RUNNING; /* do this last, just in case */
return last_pid;
}
@@ -139,7 +147,9 @@ int find_empty_process(void)
repeat:
if ((++last_pid)<0) last_pid=1;
for(i=0 ; i<NR_TASKS ; i++)
- if (task[i] && task[i]->pid == last_pid) goto repeat;
+ if (task[i] && ((task[i]->pid == last_pid) ||
+ (task[i]->pgrp == last_pid)))
+ goto repeat;
for(i=1 ; i<NR_TASKS ; i++)
if (!task[i])
return i;
diff --git a/kernel/math/Makefile b/kernel/math/Makefile
index 7e0b4c4..834283d 100644
--- a/kernel/math/Makefile
+++ b/kernel/math/Makefile
@@ -24,7 +24,8 @@ CPP =gcc -E -nostdinc -I../../include
$(CC) $(CFLAGS) \
-c -o $*.o $<
-OBJS = math_emulate.o
+OBJS = math_emulate.o error.o convert.o ea.o get_put.o \
+ add.o mul.o div.o compare.o
math.a: $(OBJS)
$(AR) rcs math.a $(OBJS)
@@ -41,3 +42,42 @@ dep:
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/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.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/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.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/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.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/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.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/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.h \
+ ../../include/asm/segment.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/linux/mm.h \
+ ../../include/linux/kernel.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/linux/mm.h ../../include/linux/kernel.h \
+ ../../include/asm/segment.h
+math_emulate.s math_emulate.o : math_emulate.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/linux/mm.h \
+ ../../include/linux/kernel.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/linux/mm.h \
+ ../../include/linux/kernel.h ../../include/signal.h
diff --git a/kernel/math/add.c b/kernel/math/add.c
new file mode 100644
index 0000000..5cf84ef
--- /dev/null
+++ b/kernel/math/add.c
@@ -0,0 +1,92 @@
+/*
+ * linux/kernel/math/add.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real addition routine.
+ *
+ * NOTE! These aren't exact: they are only 62 bits wide, and don't do
+ * correct rounding. Fast hack. The reason is that we shift right the
+ * values by two, in order not to have overflow (1 bit), and to be able
+ * to move the sign into the mantissa (1 bit). Much simpler algorithms,
+ * and 62 bits (61 really - no rounding) accuracy is usually enough. The
+ * only time you should notice anything weird is when adding 64-bit
+ * integers together. When using doubles (52 bits accuracy), the
+ * 61-bit accuracy never shows at all.
+ */
+
+#include <linux/math_emu.h>
+
+#define NEGINT(a) \
+__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \
+ :"=r" (a->a),"=r" (a->b) \
+ :"0" (a->a),"1" (a->b))
+
+static void signify(temp_real * a)
+{
+ a->exponent += 2;
+ __asm__("shrdl $2,%1,%0 ; shrl $2,%1"
+ :"=r" (a->a),"=r" (a->b)
+ :"0" (a->a),"1" (a->b));
+ if (a->exponent < 0)
+ NEGINT(a);
+ a->exponent &= 0x7fff;
+}
+
+static void unsignify(temp_real * a)
+{
+ if (!(a->a || a->b)) {
+ a->exponent = 0;
+ return;
+ }
+ a->exponent &= 0x7fff;
+ if (a->b < 0) {
+ NEGINT(a);
+ a->exponent |= 0x8000;
+ }
+ while (a->b >= 0) {
+ a->exponent--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (a->a),"=r" (a->b)
+ :"0" (a->a),"1" (a->b));
+ }
+}
+
+void fadd(const temp_real * src1, const temp_real * src2, temp_real * result)
+{
+ temp_real a,b;
+ int x1,x2,shift;
+
+ x1 = src1->exponent & 0x7fff;
+ x2 = src2->exponent & 0x7fff;
+ if (x1 > x2) {
+ a = *src1;
+ b = *src2;
+ shift = x1-x2;
+ } else {
+ a = *src2;
+ b = *src1;
+ shift = x2-x1;
+ }
+ if (shift >= 64) {
+ *result = a;
+ return;
+ }
+ if (shift >= 32) {
+ b.a = b.b;
+ b.b = 0;
+ shift -= 32;
+ }
+ __asm__("shrdl %4,%1,%0 ; shrl %4,%1"
+ :"=r" (b.a),"=r" (b.b)
+ :"0" (b.a),"1" (b.b),"c" ((char) shift));
+ signify(&a);
+ signify(&b);
+ __asm__("addl %4,%0 ; adcl %5,%1"
+ :"=r" (a.a),"=r" (a.b)
+ :"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));
+ unsignify(&a);
+ *result = a;
+}
diff --git a/kernel/math/compare.c b/kernel/math/compare.c
new file mode 100644
index 0000000..4f1dfac
--- /dev/null
+++ b/kernel/math/compare.c
@@ -0,0 +1,60 @@
+/*
+ * linux/kernel/math/compare.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real comparison routines
+ */
+
+#include <linux/math_emu.h>
+
+#define clear_Cx() (I387.swd &= ~0x4500)
+
+static void normalize(temp_real * a)
+{
+ int i = a->exponent & 0x7fff;
+ int sign = a->exponent & 0x8000;
+
+ if (!(a->a || a->b)) {
+ a->exponent = 0;
+ return;
+ }
+ while (i && a->b >= 0) {
+ i--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (a->a),"=r" (a->b)
+ :"0" (a->a),"1" (a->b));
+ }
+ a->exponent = i | sign;
+}
+
+void ftst(const temp_real * a)
+{
+ temp_real b;
+
+ clear_Cx();
+ b = *a;
+ normalize(&b);
+ if (b.a || b.b || b.exponent) {
+ if (b.exponent < 0)
+ set_C0();
+ } else
+ set_C3();
+}
+
+void fcom(const temp_real * src1, const temp_real * src2)
+{
+ temp_real a;
+
+ a = *src1;
+ a.exponent ^= 0x8000;
+ fadd(&a,src2,&a);
+ ftst(&a);
+}
+
+void fucom(const temp_real * src1, const temp_real * src2)
+{
+ fcom(src1,src2);
+}
diff --git a/kernel/math/convert.c b/kernel/math/convert.c
new file mode 100644
index 0000000..5e3d1a5
--- /dev/null
+++ b/kernel/math/convert.c
@@ -0,0 +1,185 @@
+/*
+ * linux/kernel/math/convert.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+#include <linux/math_emu.h>
+
+/*
+ * NOTE!!! There is some "non-obvious" optimisations in the temp_to_long
+ * and temp_to_short conversion routines: don't touch them if you don't
+ * know what's going on. They are the adding of one in the rounding: the
+ * overflow bit is also used for adding one into the exponent. Thus it
+ * looks like the overflow would be incorrectly handled, but due to the
+ * way the IEEE numbers work, things are correct.
+ *
+ * There is no checking for total overflow in the conversions, though (ie
+ * if the temp-real number simply won't fit in a short- or long-real.)
+ */
+
+void short_to_temp(const short_real * a, temp_real * b)
+{
+ if (!(*a & 0x7fffffff)) {
+ b->a = b->b = 0;
+ if (*a)
+ b->exponent = 0x8000;
+ else
+ b->exponent = 0;
+ return;
+ }
+ b->exponent = ((*a>>23) & 0xff)-127+16383;
+ if (*a<0)
+ b->exponent |= 0x8000;
+ b->b = (*a<<8) | 0x80000000;
+ b->a = 0;
+}
+
+void long_to_temp(const long_real * a, temp_real * b)
+{
+ if (!a->a && !(a->b & 0x7fffffff)) {
+ b->a = b->b = 0;
+ if (a->b)
+ b->exponent = 0x8000;
+ else
+ b->exponent = 0;
+ return;
+ }
+ b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383;
+ if (a->b<0)
+ b->exponent |= 0x8000;
+ b->b = 0x80000000 | (a->b<<11) | (((unsigned long)a->a)>>21);
+ b->a = a->a<<11;
+}
+
+void temp_to_short(const temp_real * a, short_real * b)
+{
+ if (!(a->exponent & 0x7fff)) {
+ *b = (a->exponent)?0x80000000:0;
+ return;
+ }
+ *b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000;
+ if (a->exponent < 0)
+ *b |= 0x80000000;
+ *b |= (a->b >> 8) & 0x007fffff;
+ switch (ROUNDING) {
+ case ROUND_NEAREST:
+ if ((a->b & 0xff) > 0x80)
+ ++*b;
+ break;
+ case ROUND_DOWN:
+ if ((a->exponent & 0x8000) && (a->b & 0xff))
+ ++*b;
+ break;
+ case ROUND_UP:
+ if (!(a->exponent & 0x8000) && (a->b & 0xff))
+ ++*b;
+ break;
+ }
+}
+
+void temp_to_long(const temp_real * a, long_real * b)
+{
+ if (!(a->exponent & 0x7fff)) {
+ b->a = 0;
+ b->b = (a->exponent)?0x80000000:0;
+ return;
+ }
+ b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000;
+ if (a->exponent < 0)
+ b->b |= 0x80000000;
+ b->b |= (a->b >> 11) & 0x000fffff;
+ b->a = a->b << 21;
+ b->a |= (a->a >> 11) & 0x001fffff;
+ switch (ROUNDING) {
+ case ROUND_NEAREST:
+ if ((a->a & 0x7ff) > 0x400)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_DOWN:
+ if ((a->exponent & 0x8000) && (a->b & 0xff))
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_UP:
+ if (!(a->exponent & 0x8000) && (a->b & 0xff))
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ }
+}
+
+void real_to_int(const temp_real * a, temp_int * b)
+{
+ int shift = 16383 + 63 - (a->exponent & 0x7fff);
+ unsigned long underflow;
+
+ b->a = b->b = underflow = 0;
+ b->sign = (a->exponent < 0);
+ if (shift < 0) {
+ set_OE();
+ return;
+ }
+ if (shift < 32) {
+ b->b = a->b; b->a = a->a;
+ } else if (shift < 64) {
+ b->a = a->b; underflow = a->a;
+ shift -= 32;
+ } else if (shift < 96) {
+ underflow = a->b;
+ shift -= 64;
+ } else
+ return;
+ __asm__("shrdl %2,%1,%0"
+ :"=r" (underflow),"=r" (b->a)
+ :"c" ((char) shift),"0" (underflow),"1" (b->a));
+ __asm__("shrdl %2,%1,%0"
+ :"=r" (b->a),"=r" (b->b)
+ :"c" ((char) shift),"0" (b->a),"1" (b->b));
+ __asm__("shrl %1,%0"
+ :"=r" (b->b)
+ :"c" ((char) shift),"0" (b->b));
+ switch (ROUNDING) {
+ case ROUND_NEAREST:
+ __asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b)
+ ,"r" (0x7fffffff + (b->a & 1))
+ ,"m" (*&underflow));
+ break;
+ case ROUND_UP:
+ if (!b->sign && underflow)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ case ROUND_DOWN:
+ if (b->sign && underflow)
+ __asm__("addl $1,%0 ; adcl $0,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ break;
+ }
+}
+
+void int_to_real(const temp_int * a, temp_real * b)
+{
+ b->a = a->a;
+ b->b = a->b;
+ if (b->a || b->b)
+ b->exponent = 16383 + 63 + (a->sign? 0x8000:0);
+ else {
+ b->exponent = 0;
+ return;
+ }
+ while (b->b >= 0) {
+ b->exponent--;
+ __asm__("addl %0,%0 ; adcl %1,%1"
+ :"=r" (b->a),"=r" (b->b)
+ :"0" (b->a),"1" (b->b));
+ }
+}
diff --git a/kernel/math/div.c b/kernel/math/div.c
new file mode 100644
index 0000000..e485fd9
--- /dev/null
+++ b/kernel/math/div.c
@@ -0,0 +1,109 @@
+/*
+ * linux/kernel/math/div.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real division routine.
+ */
+
+#include <linux/math_emu.h>
+
+static void shift_left(int * c)
+{
+ __asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
+ "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
+ "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
+ "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
+ ::"r" ((long) c):"ax");
+}
+
+static void shift_right(int * c)
+{
+ __asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
+ ::"r" ((long) c));
+}
+
+static int try_sub(int * a, int * b)
+{
+ char ok;
+
+ __asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t"
+ "movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t"
+ "movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t"
+ "movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t"
+ "setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b));
+ return ok;
+}
+
+static void div64(int * a, int * b, int * c)
+{
+ int tmp[4];
+ int i;
+ unsigned int mask = 0;
+
+ c += 4;
+ for (i = 0 ; i<64 ; i++) {
+ if (!(mask >>= 1)) {
+ c--;
+ mask = 0x80000000;
+ }
+ tmp[0] = a[0]; tmp[1] = a[1];
+ tmp[2] = a[2]; tmp[3] = a[3];
+ if (try_sub(b,tmp)) {
+ *c |= mask;
+ a[0] = tmp[0]; a[1] = tmp[1];
+ a[2] = tmp[2]; a[3] = tmp[3];
+ }
+ shift_right(b);
+ }
+}
+
+void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
+{
+ int i,sign;
+ int a[4],b[4],tmp[4] = {0,0,0,0};
+
+ sign = (src1->exponent ^ src2->exponent) & 0x8000;
+ if (!(src2->a || src2->b)) {
+ set_ZE();
+ return;
+ }
+ i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383;
+ if (i<0) {
+ set_UE();
+ result->exponent = sign;
+ result->a = result->b = 0;
+ return;
+ }
+ a[0] = a[1] = 0;
+ a[2] = src1->a;
+ a[3] = src1->b;
+ b[0] = b[1] = 0;
+ b[2] = src2->a;
+ b[3] = src2->b;
+ while (b[3] >= 0) {
+ i++;
+ shift_left(b);
+ }
+ div64(a,b,tmp);
+ if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
+ while (i && tmp[3] >= 0) {
+ i--;
+ shift_left(tmp);
+ }
+ if (tmp[3] >= 0)
+ set_DE();
+ } else
+ i = 0;
+ if (i>0x7fff) {
+ set_OE();
+ return;
+ }
+ if (tmp[0] || tmp[1])
+ set_PE();
+ result->exponent = i | sign;
+ result->a = tmp[2];
+ result->b = tmp[3];
+}
diff --git a/kernel/math/ea.c b/kernel/math/ea.c
new file mode 100644
index 0000000..85a1131
--- /dev/null
+++ b/kernel/math/ea.c
@@ -0,0 +1,92 @@
+/*
+ * linux/kernel/math/ea.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * Calculate the effective address.
+ */
+
+#include <stddef.h>
+
+#include <linux/math_emu.h>
+#include <asm/segment.h>
+
+static int __regoffset[] = {
+ offsetof(struct info,___eax),
+ offsetof(struct info,___ecx),
+ offsetof(struct info,___edx),
+ offsetof(struct info,___ebx),
+ offsetof(struct info,___esp),
+ offsetof(struct info,___ebp),
+ offsetof(struct info,___esi),
+ offsetof(struct info,___edi)
+};
+
+#define REG(x) (*(long *)(__regoffset[(x)]+(char *) info))
+
+static char * sib(struct info * info, int mod)
+{
+ unsigned char ss,index,base;
+ long offset = 0;
+
+ base = get_fs_byte((char *) EIP);
+ EIP++;
+ ss = base >> 6;
+ index = (base >> 3) & 7;
+ base &= 7;
+ if (index == 4)
+ offset = 0;
+ else
+ offset = REG(index);
+ offset <<= ss;
+ if (mod || base != 5)
+ offset += REG(base);
+ if (mod == 1) {
+ offset += (signed char) get_fs_byte((char *) EIP);
+ EIP++;
+ } else if (mod == 2 || base == 5) {
+ offset += (signed) get_fs_long((unsigned long *) EIP);
+ EIP += 4;
+ }
+ I387.foo = offset;
+ I387.fos = 0x17;
+ return (char *) offset;
+}
+
+char * ea(struct info * info, unsigned short code)
+{
+ unsigned char mod,rm;
+ long * tmp = &EAX;
+ int offset = 0;
+
+ mod = (code >> 6) & 3;
+ rm = code & 7;
+ if (rm == 4 && mod != 3)
+ return sib(info,mod);
+ if (rm == 5 && !mod) {
+ offset = get_fs_long((unsigned long *) EIP);
+ EIP += 4;
+ I387.foo = offset;
+ I387.fos = 0x17;
+ return (char *) offset;
+ }
+ tmp = & REG(rm);
+ switch (mod) {
+ case 0: offset = 0; break;
+ case 1:
+ offset = (signed char) get_fs_byte((char *) EIP);
+ EIP++;
+ break;
+ case 2:
+ offset = (signed) get_fs_long((unsigned long *) EIP);
+ EIP += 4;
+ break;
+ case 3:
+ math_abort(info,1<<(SIGILL-1));
+ }
+ I387.foo = offset;
+ I387.fos = 0x17;
+ return offset + (char *) *tmp;
+}
diff --git a/kernel/math/error.c b/kernel/math/error.c
new file mode 100644
index 0000000..1de404a
--- /dev/null
+++ b/kernel/math/error.c
@@ -0,0 +1,16 @@
+/*
+ * linux/kernel/math/error.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+#include <signal.h>
+
+#include <linux/sched.h>
+
+void math_error(void)
+{
+ __asm__("fnclex");
+ if (last_task_used_math)
+ last_task_used_math->signal |= 1<<(SIGFPE-1);
+}
diff --git a/kernel/math/get_put.c b/kernel/math/get_put.c
new file mode 100644
index 0000000..5f8167b
--- /dev/null
+++ b/kernel/math/get_put.c
@@ -0,0 +1,240 @@
+/*
+ * linux/kernel/math/get_put.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * This file handles all accesses to user memory: getting and putting
+ * ints/reals/BCD etc. This is the only part that concerns itself with
+ * other than temporary real format. All other cals are strictly temp_real.
+ */
+#include <signal.h>
+
+#include <linux/math_emu.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+void get_short_real(temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ short_real sr;
+
+ addr = ea(info,code);
+ sr = get_fs_long((unsigned long *) addr);
+ short_to_temp(&sr,tmp);
+}
+
+void get_long_real(temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ long_real lr;
+
+ addr = ea(info,code);
+ lr.a = get_fs_long((unsigned long *) addr);
+ lr.b = get_fs_long(1 + (unsigned long *) addr);
+ long_to_temp(&lr,tmp);
+}
+
+void get_temp_real(temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+
+ addr = ea(info,code);
+ tmp->a = get_fs_long((unsigned long *) addr);
+ tmp->b = get_fs_long(1 + (unsigned long *) addr);
+ tmp->exponent = get_fs_word(4 + (unsigned short *) addr);
+}
+
+void get_short_int(temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ ti.a = (signed short) get_fs_word((unsigned short *) addr);
+ ti.b = 0;
+ if (ti.sign = (ti.a < 0))
+ ti.a = - ti.a;
+ int_to_real(&ti,tmp);
+}
+
+void get_long_int(temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ ti.a = get_fs_long((unsigned long *) addr);
+ ti.b = 0;
+ if (ti.sign = (ti.a < 0))
+ ti.a = - ti.a;
+ int_to_real(&ti,tmp);
+}
+
+void get_longlong_int(temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ ti.a = get_fs_long((unsigned long *) addr);
+ ti.b = get_fs_long(1 + (unsigned long *) addr);
+ if (ti.sign = (ti.b < 0))
+ __asm__("notl %0 ; notl %1\n\t"
+ "addl $1,%0 ; adcl $0,%1"
+ :"=r" (ti.a),"=r" (ti.b)
+ :"0" (ti.a),"1" (ti.b));
+ int_to_real(&ti,tmp);
+}
+
+#define MUL10(low,high) \
+__asm__("addl %0,%0 ; adcl %1,%1\n\t" \
+"movl %0,%%ecx ; movl %1,%%ebx\n\t" \
+"addl %0,%0 ; adcl %1,%1\n\t" \
+"addl %0,%0 ; adcl %1,%1\n\t" \
+"addl %%ecx,%0 ; adcl %%ebx,%1" \
+:"=a" (low),"=d" (high) \
+:"0" (low),"1" (high):"cx","bx")
+
+#define ADD64(val,low,high) \
+__asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \
+:"0" (low),"1" (high),"r" ((unsigned long) (val)))
+
+void get_BCD(temp_real * tmp, struct info * info, unsigned short code)
+{
+ int k;
+ char * addr;
+ temp_int i;
+ unsigned char c;
+
+ addr = ea(info,code);
+ addr += 9;
+ i.sign = 0x80 & get_fs_byte(addr--);
+ i.a = i.b = 0;
+ for (k = 0; k < 9; k++) {
+ c = get_fs_byte(addr--);
+ MUL10(i.a, i.b);
+ ADD64((c>>4), i.a, i.b);
+ MUL10(i.a, i.b);
+ ADD64((c&0xf), i.a, i.b);
+ }
+ int_to_real(&i,tmp);
+}
+
+void put_short_real(const temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ short_real sr;
+
+ addr = ea(info,code);
+ verify_area(addr,4);
+ temp_to_short(tmp,&sr);
+ put_fs_long(sr,(unsigned long *) addr);
+}
+
+void put_long_real(const temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ long_real lr;
+
+ addr = ea(info,code);
+ verify_area(addr,8);
+ temp_to_long(tmp,&lr);
+ put_fs_long(lr.a, (unsigned long *) addr);
+ put_fs_long(lr.b, 1 + (unsigned long *) addr);
+}
+
+void put_temp_real(const temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+
+ addr = ea(info,code);
+ verify_area(addr,10);
+ put_fs_long(tmp->a, (unsigned long *) addr);
+ put_fs_long(tmp->b, 1 + (unsigned long *) addr);
+ put_fs_word(tmp->exponent, 4 + (short *) addr);
+}
+
+void put_short_int(const temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ real_to_int(tmp,&ti);
+ verify_area(addr,2);
+ if (ti.sign)
+ ti.a = -ti.a;
+ put_fs_word(ti.a,(short *) addr);
+}
+
+void put_long_int(const temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ real_to_int(tmp,&ti);
+ verify_area(addr,4);
+ if (ti.sign)
+ ti.a = -ti.a;
+ put_fs_long(ti.a,(unsigned long *) addr);
+}
+
+void put_longlong_int(const temp_real * tmp,
+ struct info * info, unsigned short code)
+{
+ char * addr;
+ temp_int ti;
+
+ addr = ea(info,code);
+ real_to_int(tmp,&ti);
+ verify_area(addr,8);
+ if (ti.sign)
+ __asm__("notl %0 ; notl %1\n\t"
+ "addl $1,%0 ; adcl $0,%1"
+ :"=r" (ti.a),"=r" (ti.b)
+ :"0" (ti.a),"1" (ti.b));
+ put_fs_long(ti.a,(unsigned long *) addr);
+ put_fs_long(ti.b,1 + (unsigned long *) addr);
+}
+
+#define DIV10(low,high,rem) \
+__asm__("divl %6 ; xchgl %1,%2 ; divl %6" \
+ :"=d" (rem),"=a" (low),"=b" (high) \
+ :"0" (0),"1" (high),"2" (low),"c" (10))
+
+void put_BCD(const temp_real * tmp,struct info * info, unsigned short code)
+{
+ int k,rem;
+ char * addr;
+ temp_int i;
+ unsigned char c;
+
+ addr = ea(info,code);
+ verify_area(addr,10);
+ real_to_int(tmp,&i);
+ if (i.sign)
+ put_fs_byte(0x80, addr+9);
+ else
+ put_fs_byte(0, addr+9);
+ for (k = 0; k < 9; k++) {
+ DIV10(i.a,i.b,rem);
+ c = rem;
+ DIV10(i.a,i.b,rem);
+ c += rem<<4;
+ put_fs_byte(c,addr++);
+ }
+}
diff --git a/kernel/math/math_emulate.c b/kernel/math/math_emulate.c
index 825e528..f79fd73 100644
--- a/kernel/math/math_emulate.c
+++ b/kernel/math/math_emulate.c
@@ -5,38 +5,525 @@
*/
/*
- * This directory should contain the math-emulation code.
- * Currently only results in a signal.
+ * Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
+ * even for soft-float, unless you use bruce evans' patches. The patches
+ * are great, but they have to be re-applied for every version, and the
+ * library is different for soft-float and 80387. So emulation is more
+ * practical, even though it's slower.
+ *
+ * 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
+ * about add/sub/mul/div. Urgel. I should find some good source, but I'll
+ * just fake up something.
+ *
+ * 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
+ * test every possible combination.
+ */
+
+/*
+ * This file is full of ugly macros etc: one problem was that gcc simply
+ * didn't want to make the structures as they should be: it has to try to
+ * align them. Sickening code, but at least I've hidden the ugly things
+ * in this one file: the other files don't need to know about these things.
+ *
+ * The other files also don't care about ST(x) etc - they just get addresses
+ * to 80-bit temporary reals, and do with them as they please. I wanted to
+ * hide most of the 387-specific things here.
*/
#include <signal.h>
-#include <linux/sched.h>
+#define __ALIGNED_TEMP_REAL 1
+#include <linux/math_emu.h>
#include <linux/kernel.h>
#include <asm/segment.h>
-void math_emulate(long edi, long esi, long ebp, long sys_call_ret,
- long eax,long ebx,long ecx,long edx,
- unsigned short fs,unsigned short es,unsigned short ds,
- unsigned long eip,unsigned short cs,unsigned long eflags,
- unsigned short ss, unsigned long esp)
+#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
+#define ST(x) (*__st((x)))
+#define PST(x) ((const temp_real *) __st((x)))
+
+/*
+ * We don't want these inlined - it gets too messy in the machine-code.
+ */
+static void fpop(void);
+static void fpush(void);
+static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
+static temp_real_unaligned * __st(int i);
+
+static void do_emu(struct info * info)
{
- unsigned char first, second;
+ unsigned short code;
+ temp_real tmp;
+ char * address;
+ if (I387.cwd & I387.swd & 0x3f)
+ I387.swd |= 0x8000;
+ else
+ I387.swd &= 0x7fff;
+ ORIG_EIP = EIP;
/* 0x0007 means user code space */
- if (cs != 0x000F) {
- printk("math_emulate: %04x:%08x\n\r",cs,eip);
+ if (CS != 0x000F) {
+ printk("math_emulate: %04x:%08x\n\r",CS,EIP);
panic("Math emulation needed in kernel");
}
- first = get_fs_byte((char *)((*&eip)++));
- second = get_fs_byte((char *)((*&eip)++));
- printk("%04x:%08x %02x %02x\n\r",cs,eip-2,first,second);
- current->signal |= 1<<(SIGFPE-1);
+ code = get_fs_word((unsigned short *) EIP);
+ bswapw(code);
+ code &= 0x7ff;
+ I387.fip = EIP;
+ *(unsigned short *) &I387.fcs = CS;
+ *(1+(unsigned short *) &I387.fcs) = code;
+ EIP += 2;
+ switch (code) {
+ case 0x1d0: /* fnop */
+ return;
+ case 0x1d1: case 0x1d2: case 0x1d3:
+ case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
+ math_abort(info,1<<(SIGILL-1));
+ case 0x1e0:
+ ST(0).exponent ^= 0x8000;
+ return;
+ case 0x1e1:
+ ST(0).exponent &= 0x7fff;
+ return;
+ case 0x1e2: case 0x1e3:
+ math_abort(info,1<<(SIGILL-1));
+ case 0x1e4:
+ ftst(PST(0));
+ return;
+ case 0x1e5:
+ printk("fxam not implemented\n\r");
+ math_abort(info,1<<(SIGILL-1));
+ case 0x1e6: case 0x1e7:
+ math_abort(info,1<<(SIGILL-1));
+ case 0x1e8:
+ fpush();
+ ST(0) = CONST1;
+ return;
+ case 0x1e9:
+ fpush();
+ ST(0) = CONSTL2T;
+ return;
+ case 0x1ea:
+ fpush();
+ ST(0) = CONSTL2E;
+ return;
+ case 0x1eb:
+ fpush();
+ ST(0) = CONSTPI;
+ return;
+ case 0x1ec:
+ fpush();
+ ST(0) = CONSTLG2;
+ return;
+ case 0x1ed:
+ fpush();
+ ST(0) = CONSTLN2;
+ return;
+ case 0x1ee:
+ fpush();
+ ST(0) = CONSTZ;
+ return;
+ case 0x1ef:
+ math_abort(info,1<<(SIGILL-1));
+ 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 0x1fc: case 0x1fd: case 0x1fe: case 0x1ff:
+ printk("%04x fxxx not implemented\n\r",code + 0xc800);
+ math_abort(info,1<<(SIGILL-1));
+ case 0x2e9:
+ fucom(PST(1),PST(0));
+ fpop(); fpop();
+ return;
+ case 0x3d0: case 0x3d1:
+ return;
+ case 0x3e2:
+ I387.swd &= 0x7f00;
+ return;
+ case 0x3e3:
+ I387.cwd = 0x037f;
+ I387.swd = 0x0000;
+ I387.twd = 0x0000;
+ return;
+ case 0x3e4:
+ return;
+ case 0x6d9:
+ fcom(PST(1),PST(0));
+ fpop(); fpop();
+ return;
+ case 0x7e0:
+ *(short *) &EAX = I387.swd;
+ return;
+ }
+ switch (code >> 3) {
+ case 0x18:
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x19:
+ fmul(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x1a:
+ fcom(PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x1b:
+ fcom(PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ fpop();
+ return;
+ case 0x1c:
+ real_to_real(&ST(code & 7),&tmp);
+ tmp.exponent ^= 0x8000;
+ fadd(PST(0),&tmp,&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x1d:
+ ST(0).exponent ^= 0x8000;
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x1e:
+ fdiv(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x1f:
+ fdiv(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x38:
+ fpush();
+ ST(0) = ST((code & 7)+1);
+ return;
+ case 0x39:
+ fxchg(&ST(0),&ST(code & 7));
+ return;
+ case 0x3b:
+ ST(code & 7) = ST(0);
+ fpop();
+ return;
+ case 0x98:
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return;
+ case 0x99:
+ fmul(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return;
+ case 0x9a:
+ fcom(PST(code & 7),PST(0));
+ return;
+ case 0x9b:
+ fcom(PST(code & 7),PST(0));
+ fpop();
+ return;
+ case 0x9c:
+ ST(code & 7).exponent ^= 0x8000;
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return;
+ case 0x9d:
+ real_to_real(&ST(0),&tmp);
+ tmp.exponent ^= 0x8000;
+ fadd(PST(code & 7),&tmp,&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return;
+ case 0x9e:
+ fdiv(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return;
+ case 0x9f:
+ fdiv(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ return;
+ case 0xb8:
+ printk("ffree not implemented\n\r");
+ math_abort(info,1<<(SIGILL-1));
+ case 0xb9:
+ fxchg(&ST(0),&ST(code & 7));
+ return;
+ case 0xba:
+ ST(code & 7) = ST(0);
+ return;
+ case 0xbb:
+ ST(code & 7) = ST(0);
+ fpop();
+ return;
+ case 0xbc:
+ fucom(PST(code & 7),PST(0));
+ return;
+ case 0xbd:
+ fucom(PST(code & 7),PST(0));
+ fpop();
+ return;
+ case 0xd8:
+ fadd(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return;
+ case 0xd9:
+ fmul(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return;
+ case 0xda:
+ fcom(PST(code & 7),PST(0));
+ fpop();
+ return;
+ case 0xdc:
+ ST(code & 7).exponent ^= 0x8000;
+ fadd(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return;
+ case 0xdd:
+ real_to_real(&ST(0),&tmp);
+ tmp.exponent ^= 0x8000;
+ fadd(PST(code & 7),&tmp,&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return;
+ case 0xde:
+ fdiv(PST(0),PST(code & 7),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return;
+ case 0xdf:
+ fdiv(PST(code & 7),PST(0),&tmp);
+ real_to_real(&tmp,&ST(code & 7));
+ fpop();
+ return;
+ case 0xf8:
+ printk("ffree not implemented\n\r");
+ math_abort(info,1<<(SIGILL-1));
+ fpop();
+ return;
+ case 0xf9:
+ fxchg(&ST(0),&ST(code & 7));
+ return;
+ case 0xfa:
+ case 0xfb:
+ ST(code & 7) = ST(0);
+ fpop();
+ return;
+ }
+ switch ((code>>3) & 0xe7) {
+ case 0x22:
+ put_short_real(PST(0),info,code);
+ return;
+ case 0x23:
+ put_short_real(PST(0),info,code);
+ fpop();
+ return;
+ case 0x24:
+ address = ea(info,code);
+ for (code = 0 ; code < 7 ; code++) {
+ ((long *) & I387)[code] =
+ get_fs_long((unsigned long *) address);
+ address += 4;
+ }
+ return;
+ case 0x25:
+ address = ea(info,code);
+ *(unsigned short *) &I387.cwd =
+ get_fs_word((unsigned short *) address);
+ return;
+ case 0x26:
+ address = ea(info,code);
+ verify_area(address,28);
+ for (code = 0 ; code < 7 ; code++) {
+ put_fs_long( ((long *) & I387)[code],
+ (unsigned long *) address);
+ address += 4;
+ }
+ return;
+ case 0x27:
+ address = ea(info,code);
+ verify_area(address,2);
+ put_fs_word(I387.cwd,(short *) address);
+ return;
+ case 0x62:
+ put_long_int(PST(0),info,code);
+ return;
+ case 0x63:
+ put_long_int(PST(0),info,code);
+ fpop();
+ return;
+ case 0x65:
+ fpush();
+ get_temp_real(&tmp,info,code);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0x67:
+ put_temp_real(PST(0),info,code);
+ fpop();
+ return;
+ case 0xa2:
+ put_long_real(PST(0),info,code);
+ return;
+ case 0xa3:
+ put_long_real(PST(0),info,code);
+ fpop();
+ return;
+ case 0xa4:
+ address = ea(info,code);
+ for (code = 0 ; code < 27 ; code++) {
+ ((long *) & I387)[code] =
+ get_fs_long((unsigned long *) address);
+ address += 4;
+ }
+ return;
+ case 0xa6:
+ address = ea(info,code);
+ verify_area(address,108);
+ for (code = 0 ; code < 27 ; code++) {
+ put_fs_long( ((long *) & I387)[code],
+ (unsigned long *) address);
+ address += 4;
+ }
+ I387.cwd = 0x037f;
+ I387.swd = 0x0000;
+ I387.twd = 0x0000;
+ return;
+ case 0xa7:
+ address = ea(info,code);
+ verify_area(address,2);
+ put_fs_word(I387.swd,(short *) address);
+ return;
+ case 0xe2:
+ put_short_int(PST(0),info,code);
+ return;
+ case 0xe3:
+ put_short_int(PST(0),info,code);
+ fpop();
+ return;
+ case 0xe4:
+ fpush();
+ get_BCD(&tmp,info,code);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0xe5:
+ fpush();
+ get_longlong_int(&tmp,info,code);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 0xe6:
+ put_BCD(PST(0),info,code);
+ fpop();
+ return;
+ case 0xe7:
+ put_longlong_int(PST(0),info,code);
+ fpop();
+ return;
+ }
+ switch (code >> 9) {
+ case 0:
+ get_short_real(&tmp,info,code);
+ break;
+ case 1:
+ get_long_int(&tmp,info,code);
+ break;
+ case 2:
+ get_long_real(&tmp,info,code);
+ break;
+ case 4:
+ get_short_int(&tmp,info,code);
+ }
+ switch ((code>>3) & 0x27) {
+ case 0:
+ fadd(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 1:
+ fmul(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 2:
+ fcom(&tmp,PST(0));
+ return;
+ case 3:
+ fcom(&tmp,PST(0));
+ fpop();
+ return;
+ case 4:
+ tmp.exponent ^= 0x8000;
+ fadd(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 5:
+ ST(0).exponent ^= 0x8000;
+ fadd(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 6:
+ fdiv(PST(0),&tmp,&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ case 7:
+ fdiv(&tmp,PST(0),&tmp);
+ real_to_real(&tmp,&ST(0));
+ return;
+ }
+ if ((code & 0x138) == 0x100) {
+ fpush();
+ real_to_real(&tmp,&ST(0));
+ return;
+ }
+ printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
+ math_abort(info,1<<(SIGFPE-1));
+}
+
+void math_emulate(long ___false)
+{
+ if (!current->used_math) {
+ current->used_math = 1;
+ I387.cwd = 0x037f;
+ I387.swd = 0x0000;
+ I387.twd = 0x0000;
+ }
+/* &___false points to info->___orig_eip, so subtract 1 to get info */
+ do_emu((struct info *) ((&___false) - 1));
+}
+
+void __math_abort(struct info * info, unsigned int signal)
+{
+ EIP = ORIG_EIP;
+ current->signal |= signal;
+ __asm__("movl %0,%%esp ; ret"::"g" ((long) info));
+}
+
+static void fpop(void)
+{
+ unsigned long tmp;
+
+ tmp = I387.swd & 0xffffc7ff;
+ I387.swd += 0x00000800;
+ I387.swd &= 0x00003800;
+ I387.swd |= tmp;
+}
+
+static void fpush(void)
+{
+ unsigned long tmp;
+
+ tmp = I387.swd & 0xffffc7ff;
+ I387.swd += 0x00003800;
+ I387.swd &= 0x00003800;
+ I387.swd |= tmp;
+}
+
+static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
+{
+ temp_real_unaligned c;
+
+ c = *a;
+ *a = *b;
+ *b = c;
}
-void math_error(void)
+static temp_real_unaligned * __st(int i)
{
- __asm__("fnclex");
- if (last_task_used_math)
- last_task_used_math->signal |= 1<<(SIGFPE-1);
+ i += I387.swd >> 11;
+ i &= 7;
+ return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
}
diff --git a/kernel/math/mul.c b/kernel/math/mul.c
new file mode 100644
index 0000000..94f7c52
--- /dev/null
+++ b/kernel/math/mul.c
@@ -0,0 +1,73 @@
+/*
+ * linux/kernel/math/mul.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * temporary real multiplication routine.
+ */
+
+#include <linux/math_emu.h>
+
+static void shift(int * c)
+{
+ __asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
+ "movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
+ "movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
+ "movl 12(%0),%%eax ; adcl %%eax,12(%0)"
+ ::"r" ((long) c):"ax");
+}
+
+static void mul64(const temp_real * a, const temp_real * b, int * c)
+{
+ __asm__("movl (%0),%%eax\n\t"
+ "mull (%1)\n\t"
+ "movl %%eax,(%2)\n\t"
+ "movl %%edx,4(%2)\n\t"
+ "movl 4(%0),%%eax\n\t"
+ "mull 4(%1)\n\t"
+ "movl %%eax,8(%2)\n\t"
+ "movl %%edx,12(%2)\n\t"
+ "movl (%0),%%eax\n\t"
+ "mull 4(%1)\n\t"
+ "addl %%eax,4(%2)\n\t"
+ "adcl %%edx,8(%2)\n\t"
+ "adcl $0,12(%2)\n\t"
+ "movl 4(%0),%%eax\n\t"
+ "mull (%1)\n\t"
+ "addl %%eax,4(%2)\n\t"
+ "adcl %%edx,8(%2)\n\t"
+ "adcl $0,12(%2)"
+ ::"b" ((long) a),"c" ((long) b),"D" ((long) c)
+ :"ax","dx");
+}
+
+void fmul(const temp_real * src1, const temp_real * src2, temp_real * result)
+{
+ int i,sign;
+ int tmp[4] = {0,0,0,0};
+
+ sign = (src1->exponent ^ src2->exponent) & 0x8000;
+ i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1;
+ if (i<0) {
+ result->exponent = sign;
+ result->a = result->b = 0;
+ return;
+ }
+ if (i>0x7fff) {
+ set_OE();
+ return;
+ }
+ mul64(src1,src2,tmp);
+ if (tmp[0] || tmp[1] || tmp[2] || tmp[3])
+ while (i && tmp[3] >= 0) {
+ i--;
+ shift(tmp);
+ }
+ else
+ i = 0;
+ result->exponent = i | sign;
+ result->a = tmp[2];
+ result->b = tmp[3];
+}
diff --git a/kernel/printk.c b/kernel/printk.c
index c464d43..ebce88c 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -26,16 +26,6 @@ int printk(const char *fmt, ...)
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
- __asm__("push %%fs\n\t"
- "push %%ds\n\t"
- "pop %%fs\n\t"
- "pushl %0\n\t"
- "pushl $_buf\n\t"
- "pushl $0\n\t"
- "call _tty_write\n\t"
- "addl $8,%%esp\n\t"
- "popl %0\n\t"
- "pop %%fs"
- ::"r" (i):"ax","cx","dx");
+ console_print(buf);
return i;
}
diff --git a/kernel/sched.c b/kernel/sched.c
index 15d839b..8de11a1 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -27,17 +27,26 @@ void show_task(int nr,struct task_struct * p)
{
int i,j = 4096-sizeof(struct task_struct);
- printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
+ printk("%d: pid=%d, state=%d, father=%d, child=%d, ",nr,p->pid,
+ p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
i=0;
while (i<j && !((char *)(p+1))[i])
i++;
- printk("%d (of %d) chars free in kernel stack\n\r",i,j);
+ printk("%d/%d chars free in kstack\n\r",i,j);
+ printk(" PC=%08X.", *(1019 + (unsigned long *) p));
+ if (p->p_ysptr || p->p_osptr)
+ printk(" Younger sib=%d, older sib=%d\n\r",
+ p->p_ysptr ? p->p_ysptr->pid : -1,
+ p->p_osptr ? p->p_osptr->pid : -1);
+ else
+ printk("\n\r");
}
-void show_stat(void)
+void show_state(void)
{
int i;
+ printk("\rTask-info:\n\r");
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
@@ -57,8 +66,14 @@ union task_union {
static union task_union init_task = {INIT_TASK,};
-long volatile jiffies=0;
-long startup_time=0;
+unsigned long volatile jiffies=0;
+unsigned long startup_time=0;
+int jiffies_offset = 0; /* # clock ticks to add to get "true
+ time". Should always be less than
+ 1 second's worth. For time fanatics
+ who like to syncronize their machines
+ to WWV :-) */
+
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
@@ -110,10 +125,15 @@ void schedule(void)
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
+ if ((*p)->timeout && (*p)->timeout < jiffies) {
+ (*p)->timeout = 0;
+ if ((*p)->state == TASK_INTERRUPTIBLE)
+ (*p)->state = TASK_RUNNING;
+ }
if ((*p)->alarm && (*p)->alarm < jiffies) {
- (*p)->signal |= (1<<(SIGALRM-1));
- (*p)->alarm = 0;
- }
+ (*p)->signal |= (1<<(SIGALRM-1));
+ (*p)->alarm = 0;
+ }
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
@@ -148,7 +168,7 @@ int sys_pause(void)
return 0;
}
-void sleep_on(struct task_struct **p)
+static inline void __sleep_on(struct task_struct **p, int state)
{
struct task_struct *tmp;
@@ -158,38 +178,37 @@ void sleep_on(struct task_struct **p)
panic("task[0] trying to sleep");
tmp = *p;
*p = current;
- current->state = TASK_UNINTERRUPTIBLE;
- schedule();
- if (tmp)
+ current->state = state;
+repeat: schedule();
+ if (*p && *p != current) {
+ (**p).state = 0;
+ current->state = TASK_UNINTERRUPTIBLE;
+ goto repeat;
+ }
+ if (!*p)
+ printk("Warning: *P = NULL\n\r");
+ if (*p = tmp)
tmp->state=0;
}
void interruptible_sleep_on(struct task_struct **p)
{
- struct task_struct *tmp;
+ __sleep_on(p,TASK_INTERRUPTIBLE);
+}
- if (!p)
- return;
- if (current == &(init_task.task))
- panic("task[0] trying to sleep");
- tmp=*p;
- *p=current;
-repeat: current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (*p && *p != current) {
- (**p).state=0;
- goto repeat;
- }
- *p=NULL;
- if (tmp)
- tmp->state=0;
+void sleep_on(struct task_struct **p)
+{
+ __sleep_on(p,TASK_UNINTERRUPTIBLE);
}
void wake_up(struct task_struct **p)
{
if (p && *p) {
+ if ((**p).state == TASK_STOPPED)
+ printk("wake_up: TASK_STOPPED");
+ if ((**p).state == TASK_ZOMBIE)
+ printk("wake_up: TASK_ZOMBIE");
(**p).state=0;
- *p=NULL;
}
}
@@ -304,8 +323,21 @@ void add_timer(long jiffies, void (*fn)(void))
void do_timer(long cpl)
{
- extern int beepcount;
- extern void sysbeepstop(void);
+ static int blanked = 0;
+
+ if (blankcount || !blankinterval) {
+ if (blanked)
+ unblank_screen();
+ if (blankcount)
+ blankcount--;
+ blanked = 0;
+ } else if (!blanked) {
+ blank_screen();
+ blanked = 1;
+ }
+ if (hd_timeout)
+ if (!--hd_timeout)
+ hd_times_out();
if (beepcount)
if (!--beepcount)
@@ -352,7 +384,7 @@ int sys_getpid(void)
int sys_getppid(void)
{
- return current->father;
+ return current->p_pptr->pid;
}
int sys_getuid(void)
diff --git a/kernel/signal.c b/kernel/signal.c
index 055fc20..b326435 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -9,9 +9,8 @@
#include <asm/segment.h>
#include <signal.h>
-
-volatile void do_exit(int error_code);
-
+#include <errno.h>
+
int sys_sgetmask()
{
return current->blocked;
@@ -21,10 +20,48 @@ int sys_ssetmask(int newmask)
{
int old=current->blocked;
- current->blocked = newmask & ~(1<<(SIGKILL-1));
+ current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1));
return old;
}
+int sys_sigpending(sigset_t *set)
+{
+ /* fill in "set" with signals pending but blocked. */
+ verify_area(set,4);
+ put_fs_long(current->blocked & current->signal, (unsigned long *)set);
+ return 0;
+}
+
+/* atomically swap in the new signal mask, and wait for a signal.
+ *
+ * we need to play some games with syscall restarting. We get help
+ * from the syscall library interface. Note that we need to coordinate
+ * the calling convention with the libc routine.
+ *
+ * "set" is just the sigmask as described in 1003.1-1988, 3.3.7.
+ * It is assumed that sigset_t can be passed as a 32 bit quantity.
+ *
+ * "restart" holds a restart indication. If it's non-zero, then we
+ * install the old mask, and return normally. If it's zero, we store
+ * the current mask in old_mask and block until a signal comes in.
+ */
+int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set)
+{
+ extern int sys_pause(void);
+
+ if (restart) {
+ /* we're restarting */
+ current->blocked = old_mask;
+ return -EINTR;
+ }
+ /* we're not restarting. do the work */
+ *(&restart) = 1;
+ *(&old_mask) = current->blocked;
+ current->blocked = set;
+ (void) sys_pause(); /* return after a signal arrives */
+ return -ERESTARTNOINTR; /* handle the signal, and come back */
+}
+
static inline void save_old(char * from,char * to)
{
int i;
@@ -49,8 +86,8 @@ int sys_signal(int signum, long handler, long restorer)
{
struct sigaction tmp;
- if (signum<1 || signum>32 || signum==SIGKILL)
- return -1;
+ if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
+ return -EINVAL;
tmp.sa_handler = (void (*)(int)) handler;
tmp.sa_mask = 0;
tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
@@ -65,8 +102,8 @@ int sys_sigaction(int signum, const struct sigaction * action,
{
struct sigaction tmp;
- if (signum<1 || signum>32 || signum==SIGKILL)
- return -1;
+ if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
+ return -EINVAL;
tmp = current->sigaction[signum-1];
get_new((char *) action,
(char *) (signum-1+current->sigaction));
@@ -79,7 +116,16 @@ int sys_sigaction(int signum, const struct sigaction * action,
return 0;
}
-void do_signal(long signr,long eax, long ebx, long ecx, long edx,
+/*
+ * 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 */
+}
+
+int do_signal(long signr,long eax,long ebx, long ecx, long edx, long orig_eax,
long fs, long es, long ds,
long eip, long cs, long eflags,
unsigned long * esp, long ss)
@@ -88,17 +134,60 @@ void do_signal(long signr,long eax, long ebx, long ecx, long edx,
long old_eip=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,
+ 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;
+ else {
+ *(&eax) = orig_eax;
+ *(&eip) = old_eip -= 2;
+ }
+ }
sa_handler = (unsigned long) sa->sa_handler;
if (sa_handler==1)
- return;
+ return(1); /* Ignore, see if there are more signals... */
if (!sa_handler) {
- if (signr==SIGCHLD)
- return;
- else
- do_exit(1<<(signr-1));
+ switch (signr) {
+ case SIGCONT:
+ case SIGCHLD:
+ return(1); /* Ignore, ... */
+
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ current->state = TASK_STOPPED;
+ current->exit_code = signr;
+ if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
+ SA_NOCLDSTOP))
+ current->p_pptr->signal |= (1<<(SIGCHLD-1));
+ return(1); /* Reschedule another event */
+
+ case SIGQUIT:
+ case SIGILL:
+ case SIGTRAP:
+ case SIGIOT:
+ case SIGFPE:
+ case SIGSEGV:
+ if (core_dump(signr))
+ do_exit(signr|0x80);
+ /* fall through */
+ default:
+ do_exit(signr);
+ }
}
+ /*
+ * OK, we're invoking a handler
+ */
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
*(&eip) = sa_handler;
@@ -116,4 +205,5 @@ void do_signal(long signr,long eax, long ebx, long ecx, long edx,
put_fs_long(eflags,tmp_esp++);
put_fs_long(old_eip,tmp_esp++);
current->blocked |= sa->sa_mask;
+ return(0); /* Continue, execute handler */
}
diff --git a/kernel/sys.c b/kernel/sys.c
index 2c01e67..fd85818 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -9,9 +9,21 @@
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
+#include <linux/config.h>
#include <asm/segment.h>
#include <sys/times.h>
#include <sys/utsname.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <string.h>
+
+/*
+ * The timezone where the local system is located. Used as a default by some
+ * programs who obtain this value by using gettimeofday.
+ */
+struct timezone sys_tz = { 0, 0};
+
+extern int session_of_pgrp(int pgrp);
int sys_ftime()
{
@@ -48,6 +60,17 @@ int sys_prof()
return -ENOSYS;
}
+/*
+ * This is done BSD-style, with no consideration of the saved gid, except
+ * that if you set the effective gid, it sets the saved gid too. This
+ * makes it possible for a setgid program to completely drop its privileges,
+ * which is often a useful assertion to make when you are doing a security
+ * audit over a program.
+ *
+ * The general idea is that a program which uses just setregid() will be
+ * 100% compatible with BSD. A program which uses just setgid() will be
+ * 100% compatible with POSIX w/ Saved ID's.
+ */
int sys_setregid(int rgid, int egid)
{
if (rgid>0) {
@@ -60,18 +83,27 @@ int sys_setregid(int rgid, int egid)
if (egid>0) {
if ((current->gid == egid) ||
(current->egid == egid) ||
- (current->sgid == egid) ||
- suser())
+ suser()) {
current->egid = egid;
- else
+ current->sgid = egid;
+ } else
return(-EPERM);
}
return 0;
}
+/*
+ * setgid() is implemeneted like SysV w/ SAVED_IDS
+ */
int sys_setgid(int gid)
{
- return(sys_setregid(gid, gid));
+ if (suser())
+ current->gid = current->egid = current->sgid = gid;
+ else if ((gid == current->gid) || (gid == current->sgid))
+ current->egid = gid;
+ else
+ return -EPERM;
+ return 0;
}
int sys_acct()
@@ -113,7 +145,16 @@ int sys_time(long * tloc)
/*
* Unprivileged users may change the real user id to the effective uid
- * or vice versa.
+ * or vice versa. (BSD-style)
+ *
+ * When you set the effective uid, it sets the saved uid too. This
+ * makes it possible for a setuid program to completely drop its privileges,
+ * which is often a useful assertion to make when you are doing a security
+ * audit over a program.
+ *
+ * The general idea is that a program which uses just setreuid() will be
+ * 100% compatible with BSD. A program which uses just setuid() will be
+ * 100% compatible with POSIX w/ Saved ID's.
*/
int sys_setreuid(int ruid, int euid)
{
@@ -130,9 +171,10 @@ int sys_setreuid(int ruid, int euid)
if (euid>0) {
if ((old_ruid == euid) ||
(current->euid == euid) ||
- suser())
+ suser()) {
current->euid = euid;
- else {
+ current->suid = euid;
+ } else {
current->uid = old_ruid;
return(-EPERM);
}
@@ -140,9 +182,26 @@ int sys_setreuid(int ruid, int euid)
return 0;
}
+/*
+ * setuid() is implemeneted like SysV w/ SAVED_IDS
+ *
+ * Note that SAVED_ID's is deficient in that a setuid root program
+ * like sendmail, for example, cannot set its uid to be a normal
+ * user and then switch back, because if you're root, setuid() sets
+ * the saved uid too. If you don't like this, blame the bright people
+ * in the POSIX commmittee and/or USG. Note that the BSD-style setreuid()
+ * will allow a root program to temporarily drop privileges and be able to
+ * regain them by swapping the real and effective uid.
+ */
int sys_setuid(int uid)
{
- return(sys_setreuid(uid, uid));
+ if (suser())
+ current->uid = current->euid = current->suid = uid;
+ else if ((uid == current->uid) || (uid == current->suid))
+ current->euid = uid;
+ else
+ return -EPERM;
+ return(0);
}
int sys_stime(long * tptr)
@@ -150,6 +209,7 @@ int sys_stime(long * tptr)
if (!suser())
return -EPERM;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
+ jiffies_offset = 0;
return 0;
}
@@ -177,20 +237,30 @@ int sys_brk(unsigned long end_data_seg)
* This needs some heave checking ...
* I just haven't get the stomach for it. I also don't fully
* understand sessions/pgrp etc. Let somebody who does explain it.
+ *
+ * OK, I think I have the protection semantics right.... this is really
+ * only important on a multi-user system anyway, to make sure one user
+ * can't send a signal to a process owned by another. -TYT, 12/12/91
*/
int sys_setpgid(int pid, int pgid)
{
- int i;
+ int i;
if (!pid)
pid = current->pid;
if (!pgid)
pgid = current->pid;
+ if (pgid < 0)
+ return -EINVAL;
for (i=0 ; i<NR_TASKS ; i++)
- if (task[i] && task[i]->pid==pid) {
+ if (task[i] && (task[i]->pid == pid) &&
+ ((task[i]->p_pptr == current) ||
+ (task[i] == current))) {
if (task[i]->leader)
return -EPERM;
- if (task[i]->session != current->session)
+ if ((task[i]->session != current->session) ||
+ ((pgid != pid) &&
+ (session_of_pgrp(pgid) != current->session)))
return -EPERM;
task[i]->pgrp = pgid;
return 0;
@@ -213,11 +283,65 @@ int sys_setsid(void)
return current->pgrp;
}
+/*
+ * Supplementary group ID's
+ */
+int sys_getgroups(int gidsetsize, gid_t *grouplist)
+{
+ int i;
+
+ if (gidsetsize)
+ verify_area(grouplist, sizeof(gid_t) * gidsetsize);
+
+ for (i = 0; (i < NGROUPS) && (current->groups[i] != NOGROUP);
+ i++, grouplist++) {
+ if (gidsetsize) {
+ if (i >= gidsetsize)
+ return -EINVAL;
+ put_fs_word(current->groups[i], (short *) grouplist);
+ }
+ }
+ return(i);
+}
+
+int sys_setgroups(int gidsetsize, gid_t *grouplist)
+{
+ int i;
+
+ if (!suser())
+ return -EPERM;
+ if (gidsetsize > NGROUPS)
+ return -EINVAL;
+ for (i = 0; i < gidsetsize; i++, grouplist++) {
+ current->groups[i] = get_fs_word((unsigned short *) grouplist);
+ }
+ if (i < NGROUPS)
+ current->groups[i] = NOGROUP;
+ return 0;
+}
+
+int in_group_p(gid_t grp)
+{
+ int i;
+
+ if (grp == current->egid)
+ return 1;
+
+ for (i = 0; i < NGROUPS; i++) {
+ if (current->groups[i] == NOGROUP)
+ break;
+ if (current->groups[i] == grp)
+ return 1;
+ }
+ return 0;
+}
+
+static struct utsname thisname = {
+ UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, UTS_MACHINE
+};
+
int sys_uname(struct utsname * name)
{
- static struct utsname thisname = {
- "linux .0","nodename","release ","version ","machine "
- };
int i;
if (!name) return -ERROR;
@@ -227,6 +351,167 @@ int sys_uname(struct utsname * name)
return 0;
}
+/*
+ * Only sethostname; gethostname can be implemented by calling uname()
+ */
+int sys_sethostname(char *name, int len)
+{
+ int i;
+
+ if (!suser())
+ return -EPERM;
+ if (len > MAXHOSTNAMELEN)
+ 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;
+}
+
+int sys_getrlimit(int resource, struct rlimit *rlim)
+{
+ if (resource >= RLIM_NLIMITS)
+ return -EINVAL;
+ verify_area(rlim,sizeof *rlim);
+ put_fs_long(current->rlim[resource].rlim_cur,
+ (unsigned long *) rlim);
+ put_fs_long(current->rlim[resource].rlim_max,
+ ((unsigned long *) rlim)+1);
+ return 0;
+}
+
+int sys_setrlimit(int resource, struct rlimit *rlim)
+{
+ struct rlimit new, *old;
+
+ if (resource >= RLIM_NLIMITS)
+ return -EINVAL;
+ old = current->rlim + resource;
+ new.rlim_cur = get_fs_long((unsigned long *) rlim);
+ new.rlim_max = get_fs_long(((unsigned long *) rlim)+1);
+ if (((new.rlim_cur > old->rlim_max) ||
+ (new.rlim_max > old->rlim_max)) &&
+ !suser())
+ return -EPERM;
+ *old = new;
+ return 0;
+}
+
+/*
+ * It would make sense to put struct rusuage in the task_struct,
+ * except that would make the task_struct be *really big*. After
+ * task_struct gets moved into malloc'ed memory, it would
+ * make sense to do this. It will make moving the rest of the information
+ * a lot simpler! (Which we're not doing right now because we're not
+ * measuring them yet).
+ */
+int sys_getrusage(int who, struct rusage *ru)
+{
+ struct rusage r;
+ unsigned long *lp, *lpend, *dest;
+
+ if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
+ return -EINVAL;
+ verify_area(ru, sizeof *ru);
+ memset((char *) &r, 0, sizeof(r));
+ if (who == RUSAGE_SELF) {
+ r.ru_utime.tv_sec = CT_TO_SECS(current->utime);
+ r.ru_utime.tv_usec = CT_TO_USECS(current->utime);
+ r.ru_stime.tv_sec = CT_TO_SECS(current->stime);
+ r.ru_stime.tv_usec = CT_TO_USECS(current->stime);
+ } else {
+ r.ru_utime.tv_sec = CT_TO_SECS(current->cutime);
+ r.ru_utime.tv_usec = CT_TO_USECS(current->cutime);
+ r.ru_stime.tv_sec = CT_TO_SECS(current->cstime);
+ r.ru_stime.tv_usec = CT_TO_USECS(current->cstime);
+ }
+ lp = (unsigned long *) &r;
+ lpend = (unsigned long *) (&r+1);
+ dest = (unsigned long *) ru;
+ for (; lp < lpend; lp++, dest++)
+ put_fs_long(*lp, dest);
+ return(0);
+}
+
+int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+ if (tv) {
+ verify_area(tv, sizeof *tv);
+ put_fs_long(startup_time + CT_TO_SECS(jiffies+jiffies_offset),
+ (unsigned long *) tv);
+ put_fs_long(CT_TO_USECS(jiffies+jiffies_offset),
+ ((unsigned long *) tv)+1);
+ }
+ if (tz) {
+ verify_area(tz, sizeof *tz);
+ put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz);
+ put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1);
+ }
+ return 0;
+}
+
+/*
+ * The first time we set the timezone, we will warp the clock so that
+ * it is ticking GMT time instead of local time. Presumably,
+ * if someone is setting the timezone then we are running in an
+ * environment where the programs understand about timezones.
+ * This should be done at boot time in the /etc/rc script, as
+ * soon as possible, so that the clock can be set right. Otherwise,
+ * various programs will get confused when the clock gets warped.
+ */
+int sys_settimeofday(struct timeval *tv, struct timezone *tz)
+{
+ static int firsttime = 1;
+ void adjust_clock();
+
+ if (!suser())
+ return -EPERM;
+ if (tz) {
+ sys_tz.tz_minuteswest = get_fs_long((unsigned long *) tz);
+ sys_tz.tz_dsttime = get_fs_long(((unsigned long *) tz)+1);
+ if (firsttime) {
+ firsttime = 0;
+ if (!tv)
+ adjust_clock();
+ }
+ }
+ if (tv) {
+ int sec, usec;
+
+ sec = get_fs_long((unsigned long *)tv);
+ usec = get_fs_long(((unsigned long *)tv)+1);
+
+ startup_time = sec - jiffies/HZ;
+ jiffies_offset = usec * HZ / 1000000 - jiffies%HZ;
+ }
+ return 0;
+}
+
+/*
+ * Adjust the time obtained from the CMOS to be GMT time instead of
+ * local time.
+ *
+ * This is ugly, but preferable to the alternatives. Otherwise we
+ * would either need to write a program to do it in /etc/rc (and risk
+ * confusion if the program gets run more than once; it would also be
+ * hard to make the program warp the clock precisely n hours) or
+ * compile in the timezone information into the kernel. Bad, bad....
+ *
+ * XXX Currently does not adjust for daylight savings time. May not
+ * need to do anything, depending on how smart (dumb?) the BIOS
+ * is. Blast it all.... the best thing to do not depend on the CMOS
+ * clock at all, but get the time via NTP or timed if you're on a
+ * network.... - TYT, 1/1/92
+ */
+void adjust_clock()
+{
+ startup_time += sys_tz.tz_minuteswest*60;
+}
+
int sys_umask(int mask)
{
int old = current->umask;
@@ -234,3 +519,4 @@ int sys_umask(int mask)
current->umask = mask & 0777;
return (old);
}
+
diff --git a/kernel/system_call.s b/kernel/sys_call.s
index c96826c..f85ddf3 100644
--- a/kernel/system_call.s
+++ b/kernel/sys_call.s
@@ -20,14 +20,15 @@
* 4(%esp) - %ebx
* 8(%esp) - %ecx
* C(%esp) - %edx
- * 10(%esp) - %fs
- * 14(%esp) - %es
- * 18(%esp) - %ds
- * 1C(%esp) - %eip
- * 20(%esp) - %cs
- * 24(%esp) - %eflags
- * 28(%esp) - %oldesp
- * 2C(%esp) - %oldss
+ * 10(%esp) - original %eax (-1 if not system call)
+ * 14(%esp) - %fs
+ * 18(%esp) - %es
+ * 1C(%esp) - %ds
+ * 20(%esp) - %eip
+ * 24(%esp) - %cs
+ * 28(%esp) - %eflags
+ * 2C(%esp) - %oldesp
+ * 30(%esp) - %oldss
*/
SIG_CHLD = 17
@@ -36,14 +37,15 @@ EAX = 0x00
EBX = 0x04
ECX = 0x08
EDX = 0x0C
-FS = 0x10
-ES = 0x14
-DS = 0x18
-EIP = 0x1C
-CS = 0x20
-EFLAGS = 0x24
-OLDESP = 0x28
-OLDSS = 0x2C
+ORIG_EAX = 0x10
+FS = 0x14
+ES = 0x18
+DS = 0x1C
+EIP = 0x20
+CS = 0x24
+EFLAGS = 0x28
+OLDESP = 0x2C
+OLDSS = 0x30
state = 0 # these are offsets into the task-struct.
counter = 4
@@ -58,7 +60,9 @@ sa_mask = 4
sa_flags = 8
sa_restorer = 12
-nr_system_calls = 72
+nr_system_calls = 82
+
+ENOSYS = 38
/*
* Ok, I get parallel printer interrupts while using the floppy for some
@@ -70,20 +74,19 @@ nr_system_calls = 72
.align 2
bad_sys_call:
- movl $-1,%eax
- iret
+ pushl $-ENOSYS
+ jmp ret_from_sys_call
.align 2
reschedule:
pushl $ret_from_sys_call
jmp _schedule
.align 2
_system_call:
- cmpl $nr_system_calls-1,%eax
- ja bad_sys_call
push %ds
push %es
push %fs
- pushl %edx
+ pushl %eax # save the orig_eax
+ pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
@@ -91,16 +94,19 @@ _system_call:
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
+ cmpl _NR_syscalls,%eax
+ jae bad_sys_call
call _sys_call_table(,%eax,4)
pushl %eax
+2:
movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
ret_from_sys_call:
- movl _current,%eax # task[0] cannot have signals
- cmpl _task,%eax
+ movl _current,%eax
+ cmpl _task,%eax # task[0] cannot have signals
je 3f
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
@@ -117,11 +123,14 @@ ret_from_sys_call:
incl %ecx
pushl %ecx
call _do_signal
- popl %eax
+ popl %ecx
+ testl %eax, %eax
+ jne 2b # see if we need to switch tasks, or do more signals
3: popl %eax
popl %ebx
popl %ecx
popl %edx
+ addl $4, %esp # skip orig_eax
pop %fs
pop %es
pop %ds
@@ -132,6 +141,7 @@ _coprocessor_error:
push %ds
push %es
push %fs
+ pushl $-1 # fill in -1 for orig_eax
pushl %edx
pushl %ecx
pushl %ebx
@@ -149,6 +159,7 @@ _device_not_available:
push %ds
push %es
push %fs
+ pushl $-1 # fill in -1 for orig_eax
pushl %edx
pushl %ecx
pushl %ebx
@@ -166,7 +177,9 @@ _device_not_available:
pushl %ebp
pushl %esi
pushl %edi
+ pushl $0 # temporary storage for ORIG_EIP
call _math_emulate
+ addl $4,%esp
popl %edi
popl %esi
popl %ebp
@@ -177,6 +190,7 @@ _timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
+ pushl $-1 # fill in -1 for orig_eax
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
@@ -235,6 +249,7 @@ _hd_interrupt:
jmp 1f # give port chance to breathe
1: jmp 1f
1: xorl %edx,%edx
+ movl %edx,_hd_timeout
xchgl _do_hd,%edx
testl %edx,%edx
jne 1f
diff --git a/kernel/traps.c b/kernel/traps.c
index f9bd8f1..ae9f8cb 100644
--- a/kernel/traps.c
+++ b/kernel/traps.c
@@ -36,8 +36,6 @@ register unsigned short __res; \
__asm__("mov %%fs,%%ax":"=a" (__res):); \
__res;})
-int do_exit(long code);
-
void page_exception(void);
void divide_error(void);
@@ -59,6 +57,7 @@ void coprocessor_error(void);
void reserved(void);
void parallel_interrupt(void);
void irq13(void);
+void alignment_check(void);
static void die(char * str,long esp_ptr,long nr)
{
@@ -94,6 +93,11 @@ void do_general_protection(long esp, long error_code)
die("general protection",esp,error_code);
}
+void do_alignment_check(long esp, long error_code)
+{
+ die("alignment check",esp,error_code);
+}
+
void do_divide_error(long esp, long error_code)
{
die("divide error",esp,error_code);
@@ -199,7 +203,8 @@ void trap_init(void)
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
- for (i=17;i<48;i++)
+ 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);
diff --git a/mm/Makefile b/mm/Makefile
index d431718..bab2e4c 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -15,7 +15,7 @@ CPP =gcc -E -nostdinc -I../include
$(CC) $(CFLAGS) \
-S -o $*.s $<
-OBJS = memory.o page.o
+OBJS = memory.o swap.o page.o
all: mm.o
@@ -34,4 +34,11 @@ dep:
### 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/linux/mm.h ../include/linux/kernel.h
+ ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
+ ../include/sys/param.h ../include/sys/time.h ../include/time.h \
+ ../include/sys/resource.h
+swap.o : swap.c ../include/string.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.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
diff --git a/mm/memory.c b/mm/memory.c
index a27cd78..b7815bb 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -20,6 +20,14 @@
* Also corrected some "invalidate()"s - I wasn't doing enough of them.
*/
+/*
+ * Real VM (paging to/from disk) started 18.12.91. Much more work and
+ * thought has to go into this. Oh, well..
+ * 19.12.91 - works, somewhat. Sometimes I get faults, don't know why.
+ * Found it. Everything seems to work now.
+ * 20.12.91 - Ok, making the swap-device changeable like the root.
+ */
+
#include <signal.h>
#include <asm/system.h>
@@ -28,59 +36,15 @@
#include <linux/head.h>
#include <linux/kernel.h>
-volatile void do_exit(long code);
-
-static inline volatile void oom(void)
-{
- printk("out of memory\n\r");
- do_exit(SIGSEGV);
-}
-
-#define invalidate() \
-__asm__("movl %%eax,%%cr3"::"a" (0))
-
-/* these are not to be changed without changing head.s etc */
-#define LOW_MEM 0x100000
-#define PAGING_MEMORY (15*1024*1024)
-#define PAGING_PAGES (PAGING_MEMORY>>12)
-#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
-#define USED 100
-
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
current->start_code + current->end_code)
-static long HIGH_MEMORY = 0;
+unsigned long HIGH_MEMORY = 0;
#define copy_page(from,to) \
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
-static unsigned char mem_map [ PAGING_PAGES ] = {0,};
-
-/*
- * 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)
-{
-register unsigned long __res asm("ax");
-
-__asm__("std ; repne ; scasb\n\t"
- "jne 1f\n\t"
- "movb $1,1(%%edi)\n\t"
- "sall $12,%%ecx\n\t"
- "addl %2,%%ecx\n\t"
- "movl %%ecx,%%edx\n\t"
- "movl $1024,%%ecx\n\t"
- "leal 4092(%%edx),%%edi\n\t"
- "rep ; stosl\n\t"
- "movl %%edx,%%eax\n"
- "1:"
- :"=a" (__res)
- :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
- "D" (mem_map+PAGING_PAGES-1)
- :"di","cx","dx");
-return __res;
-}
+unsigned char mem_map [ PAGING_PAGES ] = {0,};
/*
* Free a page of memory at physical address 'addr'. Used by
@@ -118,9 +82,13 @@ int free_page_tables(unsigned long from,unsigned long size)
continue;
pg_table = (unsigned long *) (0xfffff000 & *dir);
for (nr=0 ; nr<1024 ; nr++) {
- if (1 & *pg_table)
- free_page(0xfffff000 & *pg_table);
- *pg_table = 0;
+ if (*pg_table) {
+ if (1 & *pg_table)
+ free_page(0xfffff000 & *pg_table);
+ else
+ swap_free(*pg_table >> 1);
+ *pg_table = 0;
+ }
pg_table++;
}
free_page(0xfffff000 & *dir);
@@ -153,6 +121,7 @@ int copy_page_tables(unsigned long from,unsigned long to,long size)
unsigned long * to_page_table;
unsigned long this_page;
unsigned long * from_dir, * to_dir;
+ unsigned long new_page;
unsigned long nr;
if ((from&0x3fffff) || (to&0x3fffff))
@@ -172,8 +141,16 @@ int copy_page_tables(unsigned long from,unsigned long to,long size)
nr = (from==0)?0xA0:1024;
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table;
- if (!(1 & this_page))
+ if (!this_page)
+ continue;
+ if (!(1 & this_page)) {
+ if (!(new_page = get_free_page()))
+ return -1;
+ read_swap_page(this_page>>1, (char *) new_page);
+ *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) {
@@ -194,7 +171,7 @@ int copy_page_tables(unsigned long from,unsigned long to,long size)
* out of memory (either when trying to access page-table or
* page.)
*/
-unsigned long put_page(unsigned long page,unsigned long address)
+static unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
@@ -210,7 +187,7 @@ unsigned long put_page(unsigned long page,unsigned long address)
else {
if (!(tmp=get_free_page()))
return 0;
- *page_table = tmp|7;
+ *page_table = tmp | 7;
page_table = (unsigned long *) tmp;
}
page_table[(address>>12) & 0x3ff] = page | 7;
@@ -218,6 +195,36 @@ unsigned long put_page(unsigned long page,unsigned long address)
return page;
}
+/*
+ * The previous function doesn't work very well if you also want to mark
+ * the page dirty: exec.c wants this, as it has earlier changed the page,
+ * and we want the dirty-status to be correct (for VM). Thus the same
+ * routine, but this time we mark it dirty too.
+ */
+unsigned long put_dirty_page(unsigned long page, unsigned long address)
+{
+ unsigned long tmp, *page_table;
+
+/* NOTE !!! This uses the fact that _pg_dir=0 */
+
+ if (page < LOW_MEM || page >= HIGH_MEMORY)
+ printk("Trying to put page %p at %p\n",page,address);
+ if (mem_map[(page-LOW_MEM)>>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()))
+ return 0;
+ *page_table = tmp|7;
+ page_table = (unsigned long *) tmp;
+ }
+ page_table[(address>>12) & 0x3ff] = page | (PAGE_DIRTY | 7);
+/* no need for invalidate */
+ return page;
+}
+
void un_wp_page(unsigned long * table_entry)
{
unsigned long old_page,new_page;
@@ -232,9 +239,9 @@ void un_wp_page(unsigned long * table_entry)
oom();
if (old_page >= LOW_MEM)
mem_map[MAP_NR(old_page)]--;
+ copy_page(old_page,new_page);
*table_entry = new_page | 7;
invalidate();
- copy_page(old_page,new_page);
}
/*
@@ -246,6 +253,12 @@ void un_wp_page(unsigned long * table_entry)
*/
void do_wp_page(unsigned long error_code,unsigned long address)
{
+ if (address < TASK_SIZE)
+ printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r");
+ if (address - current->start_code > TASK_SIZE) {
+ printk("Bad things happen: page error in do_wp_page\n\r");
+ do_exit(SIGSEGV);
+ }
#if 0
/* we cannot do this yet: the estdio library writes to code space */
/* stupid, stupid. I really want the libc.a from GNU */
@@ -287,7 +300,7 @@ void get_empty_page(unsigned long address)
* task.
*
* NOTE! This assumes we have checked that p != current, and that they
- * share the same executable.
+ * share the same executable or library.
*/
static int try_to_share(unsigned long address, struct task_struct * p)
{
@@ -341,21 +354,24 @@ static int try_to_share(unsigned long address, struct task_struct * p)
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
-static int share_page(unsigned long address)
+static int share_page(struct m_inode * inode, unsigned long address)
{
struct task_struct ** p;
- if (!current->executable)
- return 0;
- if (current->executable->i_count < 2)
+ if (inode->i_count < 2 || !inode)
return 0;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!*p)
continue;
if (current == *p)
continue;
- if ((*p)->executable != current->executable)
- continue;
+ if (address < LIBRARY_OFFSET) {
+ if (inode != (*p)->executable)
+ continue;
+ } else {
+ if (inode != (*p)->library)
+ continue;
+ }
if (try_to_share(address,*p))
return 1;
}
@@ -368,23 +384,51 @@ void do_no_page(unsigned long error_code,unsigned long address)
unsigned long tmp;
unsigned long page;
int block,i;
+ struct m_inode * inode;
+ if (address < TASK_SIZE)
+ printk("\n\rBAD!! KERNEL PAGE MISSING\n\r");
+ if (address - current->start_code > TASK_SIZE) {
+ printk("Bad things happen: nonexistent page error in do_no_page\n\r");
+ do_exit(SIGSEGV);
+ }
+ page = *(unsigned long *) ((address >> 20) & 0xffc);
+ if (page & 1) {
+ page &= 0xfffff000;
+ page += (address >> 10) & 0xffc;
+ tmp = *(unsigned long *) page;
+ if (tmp && !(1 & tmp)) {
+ swap_in((unsigned long *) page);
+ return;
+ }
+ }
address &= 0xfffff000;
tmp = address - current->start_code;
- if (!current->executable || tmp >= current->end_data) {
+ if (tmp >= LIBRARY_OFFSET ) {
+ inode = current->library;
+ block = 1 + (tmp-LIBRARY_OFFSET) / BLOCK_SIZE;
+ } else if (tmp < current->end_data) {
+ inode = current->executable;
+ block = 1 + tmp / BLOCK_SIZE;
+ } else {
+ inode = NULL;
+ block = 0;
+ }
+ if (!inode) {
get_empty_page(address);
return;
}
- if (share_page(tmp))
+ if (share_page(inode,tmp))
return;
if (!(page = get_free_page()))
oom();
/* remember that 1 block is used for header */
- block = 1 + tmp/BLOCK_SIZE;
for (i=0 ; i<4 ; block++,i++)
- nr[i] = bmap(current->executable,block);
- bread_page(page,current->executable->i_dev,nr);
+ nr[i] = bmap(inode,block);
+ bread_page(page,inode->i_dev,nr);
i = tmp + 4096 - current->end_data;
+ if (i>4095)
+ i = 0;
tmp = page + 4096;
while (i-- > 0) {
tmp--;
@@ -410,21 +454,49 @@ void mem_init(long start_mem, long end_mem)
mem_map[i++]=0;
}
-void calc_mem(void)
+void show_mem(void)
{
- int i,j,k,free=0;
- long * pg_tbl;
+ int i,j,k,free=0,total=0;
+ int shared=0;
+ unsigned long * pg_tbl;
- for(i=0 ; i<PAGING_PAGES ; i++)
- if (!mem_map[i]) free++;
- printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
- for(i=2 ; i<1024 ; i++) {
+ printk("Mem-info:\n\r");
+ for(i=0 ; i<PAGING_PAGES ; i++) {
+ if (mem_map[i] == USED)
+ continue;
+ total++;
+ if (!mem_map[i])
+ free++;
+ else
+ shared += mem_map[i]-1;
+ }
+ printk("%d free pages of %d\n\r",free,total);
+ printk("%d pages shared\n\r",shared);
+ k = 0;
+ for(i=4 ; i<1024 ;) {
if (1&pg_dir[i]) {
- pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
- for(j=k=0 ; j<1024 ; j++)
- if (pg_tbl[j]&1)
- k++;
- printk("Pg-dir[%d] uses %d pages\n",i,k);
+ if (pg_dir[i]>HIGH_MEMORY) {
+ printk("page directory[%d]: %08X\n\r",
+ i,pg_dir[i]);
+ continue;
+ }
+ if (pg_dir[i]>LOW_MEM)
+ 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)
+ printk("page_dir[%d][%d]: %08X\n\r",
+ i,j, pg_tbl[j]);
+ else
+ k++,free++;
+ }
+ i++;
+ if (!(i&15) && k) {
+ k++,free++; /* one page/process for task_struct */
+ printk("Process %d: %d pages\n\r",(i>>4)-1,k);
+ k = 0;
}
}
+ printk("Memory found: %d (%d)\n\r",free-shared,total);
}
diff --git a/mm/swap.c b/mm/swap.c
new file mode 100644
index 0000000..327259b
--- /dev/null
+++ b/mm/swap.c
@@ -0,0 +1,253 @@
+/*
+ * linux/mm/swap.c
+ *
+ * (C) 1991 Linus Torvalds
+ */
+
+/*
+ * This file should contain most things doing the swapping from/to disk.
+ * Started 18.12.91
+ */
+
+#include <string.h>
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+
+#define SWAP_BITS (4096<<3)
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+static char * swap_bitmap = NULL;
+int SWAP_DEV = 0;
+
+/*
+ * 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)
+{
+ int nr;
+
+ if (!swap_bitmap)
+ return 0;
+ for (nr = 1; nr < 32768 ; nr++)
+ if (clrbit(swap_bitmap,nr))
+ return nr;
+ return 0;
+}
+
+void swap_free(int swap_nr)
+{
+ if (!swap_nr)
+ return;
+ if (swap_bitmap && swap_nr < SWAP_BITS)
+ if (!setbit(swap_bitmap,swap_nr))
+ return;
+ printk("Swap-space bad (swap_free())\n\r");
+ return;
+}
+
+void swap_in(unsigned long *table_ptr)
+{
+ int swap_nr;
+ unsigned long page;
+
+ if (!swap_bitmap) {
+ printk("Trying to swap in without swap bit-map");
+ return;
+ }
+ if (1 & *table_ptr) {
+ 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");
+ *table_ptr = page | (PAGE_DIRTY | 7);
+}
+
+int try_to_swap_out(unsigned long * table_ptr)
+{
+ unsigned long page;
+ unsigned long swap_nr;
+
+ page = *table_ptr;
+ if (!(PAGE_PRESENT & page))
+ return 0;
+ if (page - LOW_MEM > PAGING_MEMORY)
+ return 0;
+ if (PAGE_DIRTY & page) {
+ page &= 0xfffff000;
+ if (mem_map[MAP_NR(page)] != 1)
+ return 0;
+ if (!(swap_nr = get_swap_page()))
+ return 0;
+ *table_ptr = swap_nr<<1;
+ invalidate();
+ write_swap_page(swap_nr, (char *) page);
+ free_page(page);
+ return 1;
+ }
+ *table_ptr = 0;
+ invalidate();
+ free_page(page);
+ return 1;
+}
+
+/*
+ * Ok, this has a rather intricate logic - the idea is to make good
+ * and fast machine code. If we didn't worry about that, things would
+ * be easier.
+ */
+int swap_out(void)
+{
+ static int dir_entry = FIRST_VM_PAGE>>10;
+ static int page_entry = -1;
+ int counter = VM_PAGES;
+ int pg_table;
+
+ while (counter>0) {
+ pg_table = pg_dir[dir_entry];
+ if (pg_table & 1)
+ break;
+ counter -= 1024;
+ dir_entry++;
+ if (dir_entry >= 1024)
+ dir_entry = FIRST_VM_PAGE>>10;
+ }
+ pg_table &= 0xfffff000;
+ while (counter-- > 0) {
+ page_entry++;
+ if (page_entry >= 1024) {
+ page_entry = 0;
+ repeat:
+ dir_entry++;
+ if (dir_entry >= 1024)
+ dir_entry = FIRST_VM_PAGE>>10;
+ pg_table = pg_dir[dir_entry];
+ if (!(pg_table&1))
+ if ((counter -= 1024) > 0)
+ goto repeat;
+ else
+ break;
+ pg_table &= 0xfffff000;
+ }
+ if (try_to_swap_out(page_entry + (unsigned long *) pg_table))
+ return 1;
+ }
+ printk("Out of swap-memory\n\r");
+ return 0;
+}
+
+/*
+ * 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)
+{
+register unsigned long __res asm("ax");
+
+repeat:
+ __asm__("std ; repne ; scasb\n\t"
+ "jne 1f\n\t"
+ "movb $1,1(%%edi)\n\t"
+ "sall $12,%%ecx\n\t"
+ "addl %2,%%ecx\n\t"
+ "movl %%ecx,%%edx\n\t"
+ "movl $1024,%%ecx\n\t"
+ "leal 4092(%%edx),%%edi\n\t"
+ "rep ; stosl\n\t"
+ "movl %%edx,%%eax\n"
+ "1:"
+ :"=a" (__res)
+ :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
+ "D" (mem_map+PAGING_PAGES-1)
+ :"di","cx","dx");
+ if (__res >= HIGH_MEMORY)
+ goto repeat;
+ if (!__res && swap_out())
+ goto repeat;
+ return __res;
+}
+
+void init_swapping(void)
+{
+ extern int *blk_size[];
+ int swap_size,i,j;
+
+ if (!SWAP_DEV)
+ return;
+ if (!blk_size[MAJOR(SWAP_DEV)]) {
+ printk("Unable to get size of swap device\n\r");
+ return;
+ }
+ swap_size = blk_size[MAJOR(SWAP_DEV)][MINOR(SWAP_DEV)];
+ if (!swap_size)
+ return;
+ if (swap_size < 100) {
+ printk("Swap device too small (%d blocks)\n\r",swap_size);
+ return;
+ }
+ swap_size >>= 2;
+ if (swap_size > SWAP_BITS)
+ swap_size = SWAP_BITS;
+ swap_bitmap = (char *) get_free_page();
+ if (!swap_bitmap) {
+ printk("Unable to start swapping: out of memory :-)\n\r");
+ return;
+ }
+ read_swap_page(0,swap_bitmap);
+ if (strncmp("SWAP-SPACE",swap_bitmap+4086,10)) {
+ printk("Unable to find swap-space signature\n\r");
+ free_page((long) swap_bitmap);
+ swap_bitmap = NULL;
+ return;
+ }
+ memset(swap_bitmap+4086,0,10);
+ for (i = 0 ; i < SWAP_BITS ; i++) {
+ if (i == 1)
+ i = swap_size;
+ if (bit(swap_bitmap,i)) {
+ printk("Bad swap-space bit-map\n\r");
+ free_page((long) swap_bitmap);
+ swap_bitmap = NULL;
+ return;
+ }
+ }
+ j = 0;
+ for (i = 1 ; i < swap_size ; i++)
+ if (bit(swap_bitmap,i))
+ j++;
+ if (!j) {
+ free_page((long) swap_bitmap);
+ swap_bitmap = NULL;
+ return;
+ }
+ printk("Swap device ok: %d pages (%d bytes) swap-space\n\r",j,j*4096);
+}
diff --git a/tools/build.c b/tools/build.c
index 69e25d0..32d2efb 100644
--- a/tools/build.c
+++ b/tools/build.c
@@ -18,6 +18,8 @@
/*
* Changes by tytso to allow root device specification
+ *
+ * Added swap-device specification: Linux 20.12.91
*/
#include <stdio.h> /* fprintf */
@@ -32,11 +34,14 @@
#define MINIX_HEADER 32
#define GCC_HEADER 1024
-#define SYS_SIZE 0x2000
+#define SYS_SIZE 0x3000
#define DEFAULT_MAJOR_ROOT 3
#define DEFAULT_MINOR_ROOT 6
+#define DEFAULT_MAJOR_SWAP 0
+#define DEFAULT_MINOR_SWAP 0
+
/* max nr of sectors of setup: don't change unless you also change
* bootsect etc */
#define SETUP_SECTS 4
@@ -59,11 +64,12 @@ int main(int argc, char ** argv)
int i,c,id;
char buf[1024];
char major_root, minor_root;
+ char major_swap, minor_swap;
struct stat sb;
- if ((argc != 4) && (argc != 5))
+ if ((argc < 4) || (argc > 6))
usage();
- if (argc == 5) {
+ if (argc > 4) {
if (strcmp(argv[4], "FLOPPY")) {
if (stat(argv[4], &sb)) {
perror(argv[4]);
@@ -79,13 +85,35 @@ int main(int argc, char ** argv)
major_root = DEFAULT_MAJOR_ROOT;
minor_root = DEFAULT_MINOR_ROOT;
}
+ if (argc == 6) {
+ if (strcmp(argv[5], "NONE")) {
+ if (stat(argv[5], &sb)) {
+ perror(argv[5]);
+ die("Couldn't stat root device.");
+ }
+ major_swap = MAJOR(sb.st_rdev);
+ minor_swap = MINOR(sb.st_rdev);
+ } else {
+ major_swap = 0;
+ minor_swap = 0;
+ }
+ } else {
+ major_swap = DEFAULT_MAJOR_SWAP;
+ minor_swap = DEFAULT_MINOR_SWAP;
+ }
fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
+ fprintf(stderr, "Swap device is (%d, %d)\n", major_swap, minor_swap);
if ((major_root != 2) && (major_root != 3) &&
(major_root != 0)) {
fprintf(stderr, "Illegal root device (major = %d)\n",
major_root);
die("Bad root device --- major #");
}
+ if (major_swap && major_swap != 3) {
+ fprintf(stderr, "Illegal swap device (major = %d)\n",
+ major_swap);
+ die("Bad root device --- major #");
+ }
for (i=0;i<sizeof buf; i++) buf[i]=0;
if ((id=open(argv[1],O_RDONLY,0))<0)
die("Unable to open 'boot'");
@@ -109,6 +137,8 @@ int main(int argc, char ** argv)
die("Boot block must be exactly 512 bytes");
if ((*(unsigned short *)(buf+510)) != 0xAA55)
die("Boot block hasn't got boot flag (0xAA55)");
+ buf[506] = (char) minor_swap;
+ buf[507] = (char) major_swap;
buf[508] = (char) minor_root;
buf[509] = (char) major_root;
i=write(1,buf,512);