Commit fa1ec100 authored by Linus Torvalds's avatar Linus Torvalds

Linux 0.10 (November 11, 1991 ???)

Likely correct 0.10: these were re-created from the RCS tree that Ted
Ts'o had, no known pristine 0.10 tree (or, sadly, 0.02 and 0.03 trees)
exist any more.

Linux-0.10 was actually a major step.  It was _almost_ able to host
itself, and if I remember correctly, a small patch I posted to the
newsgroup a few days later got the buffer cache handling stable enough
that Linux could now compile itself under itself without running out of
memory due to a memory leak.

Apart from bugfixes, the major update here is the support for
mount/umount.  But you can also tell that others are starting to test
out this thing, since the harddisk geometry is now auto-sensed, and we
support the US keyboard layout in addition to the Finnish one.

(This is also the first actual thing from the outside: the US keyboard
layout tables came from Alfred Leung, although with major editing by me.)

 - add copyright messages ("(C) 1991  Linus Torvalds")

   Nobody else is really doing coding (yet..) but clearly I'm starting
   to be a lot more aware of other people here.

 - split up boot/boot.s into boot/bootsect.s and boot/setup.s
 - autodetect floppy type for booting
 - make root device and boot device configurable

 - support up to 16MB of physical memory (instead of just 8MB ;)

   Whee. We're clearly moving into the "big iron" phase of Linux.

 - move drivers around.  We now have separate subdirectories for
   character device drivers (tty and memory) and block device drivers.

 - initial floppy driver support!

   You can see how the "block layer" interfaces evolved directly from
   moving parts of the original hd.c driver into ll_rw_block.c and
   making them "generic".

 - make file reading do simple read-ahead
 - make file writing avoid reading in blocks that are totally overwritten
 - add support for /dev/port and /dev/null (!!)
 - improve pipe throughput

 - add support for sigaction(), not just old-style signal()

   This also rewrites most of the signal code in C rather than assembly.

 - add "mknod()" and "mount()"/"umount()" system calls, and support
   for traversing over mount-points.

 - add "sessions" and setsid(), so that we get proper SIGHUP's
parent bb441db1
# ROOTDEV= /dev/hd3
# Makefile for linux.
# If you don't have '-mstring-insns' in your gcc (and nobody but me has :-)
# remove them from the CFLAGS defines.
#
AS86 =as -0 -a AS86 =as -0 -a
CC86 =cc -0 CC86 =cc -0
...@@ -13,9 +9,10 @@ LD =gld ...@@ -13,9 +9,10 @@ LD =gld
LDFLAGS =-s -x -M LDFLAGS =-s -x -M
CC =gcc CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs
CPP =gcc -E -nostdinc -Iinclude CPP =cpp -nostdinc -Iinclude
ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
LIBS =lib/lib.a LIBS =lib/lib.a
.c.s: .c.s:
...@@ -29,8 +26,8 @@ LIBS =lib/lib.a ...@@ -29,8 +26,8 @@ LIBS =lib/lib.a
all: Image all: Image
Image: boot/boot tools/system tools/build Image: boot/bootsect boot/setup tools/system tools/build
tools/build boot/boot tools/system > Image tools/build boot/bootsect boot/setup tools/system $(ROOTDEV) > Image
sync sync
tools/build: tools/build.c tools/build: tools/build.c
...@@ -41,12 +38,19 @@ tools/build: tools/build.c ...@@ -41,12 +38,19 @@ tools/build: tools/build.c
boot/head.o: boot/head.s boot/head.o: boot/head.s
tools/system: boot/head.o init/main.o \ tools/system: boot/head.o init/main.o \
$(ARCHIVES) $(LIBS) $(ARCHIVES) $(DRIVERS) $(LIBS)
$(LD) $(LDFLAGS) boot/head.o init/main.o \ $(LD) $(LDFLAGS) boot/head.o init/main.o \
$(ARCHIVES) \ $(ARCHIVES) \
$(DRIVERS) \
$(LIBS) \ $(LIBS) \
-o tools/system > System.map -o tools/system > System.map
kernel/blk_drv/blk_drv.a:
(cd kernel/blk_drv; make)
kernel/chr_drv/chr_drv.a:
(cd kernel/chr_drv; make)
kernel/kernel.o: kernel/kernel.o:
(cd kernel; make) (cd kernel; make)
...@@ -59,16 +63,22 @@ fs/fs.o: ...@@ -59,16 +63,22 @@ fs/fs.o:
lib/lib.a: lib/lib.a:
(cd lib; make) (cd lib; make)
boot/boot: boot/boot.s tools/system #boot/setup: boot/setup.s
(echo -n "SYSSIZE = (";ls -l tools/system | grep system \ # $(AS86) -o boot/setup.o boot/setup.s
| cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s # $(LD86) -s -o boot/setup boot/setup.o
cat boot/boot.s >> tmp.s
$(AS86) -o boot/boot.o tmp.s #boot/bootsect: tmp.s
rm -f tmp.s # $(AS86) -o boot/bootsect.o tmp.s
$(LD86) -s -o boot/boot boot/boot.o # rm -f tmp.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: clean:
rm -f Image System.map tmp_make boot/boot core rm -f Image System.map tmp_make core
rm -f init/*.o boot/*.o tools/system tools/build rm -f init/*.o boot/*.o tools/system tools/build
(cd mm;make clean) (cd mm;make clean)
(cd fs;make clean) (cd fs;make clean)
...@@ -92,5 +102,5 @@ init/main.o : init/main.c include/unistd.h include/sys/stat.h \ ...@@ -92,5 +102,5 @@ 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/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/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/sched.h include/linux/head.h include/linux/fs.h \
include/linux/mm.h include/asm/system.h include/asm/io.h include/stddef.h \ include/linux/mm.h include/signal.h include/asm/system.h include/asm/io.h \
include/stdarg.h include/fcntl.h include/stddef.h include/stdarg.h include/fcntl.h
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @# @;
1.2
date 91.11.11.15.02.48; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.11.14.43.04; author tytso; state Exp;
branches ;
next ;
desc
@Top level makefile for the Linux kernel
@
1.2
log
@Comment out code to build and clean out 16 bit binaries.
Modify rule to specify the appropriate root device to the build program
@
text
@ROOTDEV= /dev/hd3
AS86 =as -0 -a
CC86 =cc -0
LD86 =ld -0
AS =gas
LD =gld
LDFLAGS =-s -x -M
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs
CPP =cpp -nostdinc -Iinclude
ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
LIBS =lib/lib.a
.c.s:
$(CC) $(CFLAGS) \
-nostdinc -Iinclude -S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-nostdinc -Iinclude -c -o $*.o $<
all: Image
Image: boot/bootsect boot/setup tools/system tools/build
tools/build boot/bootsect boot/setup tools/system $(ROOTDEV) > Image
sync
tools/build: tools/build.c
$(CC) $(CFLAGS) \
-o tools/build tools/build.c
chmem +65000 tools/build
boot/head.o: boot/head.s
tools/system: boot/head.o init/main.o \
$(ARCHIVES) $(DRIVERS) $(LIBS)
$(LD) $(LDFLAGS) boot/head.o init/main.o \
$(ARCHIVES) \
$(DRIVERS) \
$(LIBS) \
-o tools/system > System.map
kernel/blk_drv/blk_drv.a:
(cd kernel/blk_drv; make)
kernel/chr_drv/chr_drv.a:
(cd kernel/chr_drv; make)
kernel/kernel.o:
(cd kernel; make)
mm/mm.o:
(cd mm; make)
fs/fs.o:
(cd fs; make)
lib/lib.a:
(cd lib; make)
#boot/setup: boot/setup.s
# $(AS86) -o boot/setup.o boot/setup.s
# $(LD86) -s -o boot/setup boot/setup.o
#boot/bootsect: tmp.s
# $(AS86) -o boot/bootsect.o tmp.s
# rm -f tmp.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
rm -f init/*.o boot/*.o tools/system tools/build
(cd mm;make clean)
(cd fs;make clean)
(cd kernel;make clean)
(cd lib;make clean)
backup: clean
(cd .. ; tar cf - linux | compress16 - > backup.Z)
sync
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
(cd fs; make dep)
(cd kernel; make dep)
(cd mm; make dep)
### 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
@
1.1
log
@Initial revision
@
text
@d1 2
d30 1
a30 1
tools/build boot/bootsect boot/setup tools/system > Image
d66 3
a68 3
boot/setup: boot/setup.s
$(AS86) -o boot/setup.o boot/setup.s
$(LD86) -s -o boot/setup boot/setup.o
d70 4
a73 4
boot/bootsect: tmp.s
$(AS86) -o boot/bootsect.o tmp.s
rm -f tmp.s
$(LD86) -s -o boot/bootsect boot/bootsect.o
d75 4
a78 4
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
d81 2
a82 2
rm -f Image System.map tmp_make boot/bootsect core
rm -f boot/setup init/*.o boot/*.o tools/system tools/build
@
|
| bootsect.s (C) 1991 Linus Torvalds
|
| bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
| iself out of the way to address 0x90000, and jumps there.
|
| It then loads 'setup' directly after itself (0x90200), and the system
| at 0x10000, using BIOS interrupts.
|
| NOTE! currently system is at most 8*65536 bytes long. This should be no
| problem, even in the future. I want to keep it simple. This 512 kB
| kernel size should be enough, especially as this doesn't contain the
| buffer cache as in minix
|
| The loader has been made as simple as possible, and continuos
| read errors will result in a unbreakable loop. Reboot by hand. It
| loads pretty fast by getting whole sectors at a time whenever possible.
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
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).
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 = 0 | 0x306
entry start
start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG
go: mov ax,cs
mov ds,ax
mov es,ax
| put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 | arbitrary value >>512
| load the setup-sectors directly after the bootblock.
| Note that 'es' is already set up.
load_setup:
mov dx,#0x0000 | 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
int 0x13
j load_setup
ok_load_setup:
| Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00
mov ax,#0x0800 | AH=8 is get drive parameters
int 0x13
mov ch,#0x00
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax
| Print some inane message
mov ah,#0x03 | read cursor pos
xor bh,bh
int 0x10
mov cx,#24
mov bx,#0x0007 | page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 | write string, move cursor
int 0x10
| ok, we've written the message, now
| we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax | segment of 0x010000
call read_it
call kill_motor
| After that we check which root-device to use. If the device is
| defined (!= 0), nothing is done and the given device is used.
| Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
| on the number of sectors that the BIOS reports currently.
seg cs
mov ax,root_dev
cmp ax,#0
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208 | /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov ax,#0x021c | /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
undef_root:
jmp undef_root
root_defined:
seg cs
mov root_dev,ax
| after that (everyting loaded), we jump to
| the setup-routine loaded directly after
| the bootblock:
jmpi 0,SETUPSEG
| This routine loads the system at address 0x10000, making sure
| no 64kB boundaries are crossed. We try to load it as fast as
| possible, loading whole tracks whenever we can.
|
| in: es - starting address segment (normally 0x1000)
|
sread: .word 1+SETUPLEN | sectors read of current track
head: .word 0 | current head
track: .word 0 | current track
read_it:
mov ax,es
test ax,#0x0fff
die: jne die | es must be at 64kB boundary
xor bx,bx | bx is starting address within segment
rp_read:
mov ax,es
cmp ax,#ENDSEG | have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg cs
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,#9
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
seg cs
cmp ax,sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#9
add bx,cx
jnc rp_read
mov ax,es
add ax,#0x1000
mov es,ax
xor bx,bx
jmp rp_read
read_track:
push ax
push bx
push cx
push dx
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
int 0x13
jc bad_rt
pop dx
pop cx
pop bx
pop ax
ret
bad_rt: mov ax,#0
mov dx,#0
int 0x13
pop dx
pop cx
pop bx
pop ax
jmp read_track
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
push dx
mov dx,#0x3f2
mov al,#0
outb
pop dx
ret
sectors:
.word 0
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
/*
*
* bootsect.s (C) 1991 Linus Torvalds
*
* bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
* iself out of the way to address 0x90000, and jumps there.
*
* It then loads 'setup' directly after itself (0x90200), and the system
* at 0x10000, using BIOS interrupts.
*
* NOTE! currently system is at most 8*65536 bytes long. This should be no
* problem, even in the future. I want to keep it simple. This 512 kB
* kernel size should be enough, especially as this doesn't contain the
* buffer cache as in minix
*
* The loader has been made as simple as possible, and continuos
* read errors will result in a unbreakable loop. Reboot by hand. It
* loads pretty fast by getting whole sectors at a time whenever possible.
*/
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
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).
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 = 0 # 0x306
entry start
start:
mov $BOOTSEG,%ax
mov %ax,%ds
mov $INITSEG,%ax
mov %ax,%es
mov $256,%cx
sub %si,%si
sub %di,%di
rep
movw
jmpi go,INITSEG
go: mov %cs,%ax
mov %ax,%ds
mov %ax,%es
/*
* put stack at 0x9ff00.
*/
mov %ax,%ss
mov $0xFF00,%sp # arbitrary value >>512
/*
* load the setup-sectors directly after the bootblock.
* Note that 'es' is already set up.
*/
load_setup:
mov $0x0000,%dx # drive 0, head 0
mov $0x0002,%cx # sector 2, track 0
mov $0x0200,%bx # address = 512, in INITSEG
mov $0x0200,%ax+SETUPLEN # service 2, nr of sectors
int 0x13 # read it
jnc ok_load_setup # ok - continue
mov $0x0000,%dx
mov $0x0000,%ax # reset the diskette
int 0x13
j load_setup
ok_load_setup:
/*
* Get disk drive parameters, specifically nr of sectors/track
*/
mov $0x00,%dl
mov $0x0800,%ax # AH=8 is get drive parameters
int 0x13
mov $0x00,%ch
seg %cs
mov %cx,sectors
mov $INITSEG,%ax
mov %ax,%es
/*
* Print some inane message
*/
mov $0x03,%ah # read cursor pos
xor %bh,%bh
int 0x10
mov $24,%cx
mov $0x0007,%bx # page 0, attribute 7 (normal)
mov $msg1,%bp
mov $0x1301,%ax # write string, move cursor
int 0x10
/*
* ok, we've written the message, now
* we want to load the system (at 0x10000)
*/
mov $SYSSEG,%ax
mov %ax,%es # segment of 0x010000
call read_it
call kill_motor
/*
* After that we check which root-device to use. If the device is
* defined (!= 0), nothing is done and the given device is used.
* Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
* on the number of sectors that the BIOS reports currently.
*/
seg %cs
mov root,%ax_dev
cmp %ax,$0
jne root_defined
seg %cs
mov sectors,%bx
mov $0x0208,%ax # /dev/ps0 - 1.2Mb
cmp %bx,$15
je root_defined
mov $0x021c,%ax # /dev/PS0 - 1.44Mb
cmp %bx,$18
je root_defined
undef_root:
jmp undef_root
root_defined:
seg %cs
mov root_%ax,dev
/*
* after that (everyting loaded), we jump to
* the setup-routine loaded directly after
* the bootblock:
*/
jmpi 0,SETUPSEG
/*
* This routine loads the system at address 0x10000, making sure
* no 64kB boundaries are crossed. We try to load it as fast as
* possible, loading whole tracks whenever we can.
*
* in: es - starting address segment (normally 0x1000)
*
*/
sread: .word 1+SETUPLEN # sectors read of current track
head: .word 0 # current head
track: .word 0 # current track
read_it:
mov %es,%ax
test %ax,$0x0fff
die: jne die # %es must be at 64kB boundary
xor %bx,%bx # %bx is starting address within segment
rp_read:
mov %es,%ax
cmp %ax,$ENDSEG # have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg %cs
mov sectors,%ax
sub sread,%ax
mov %ax,%cx
shl $9,%cx
add %bx,%cx
jnc ok2_read
je ok2_read
xor %ax,%ax
sub %bx,%ax
shr $9,%ax
ok2_read:
call read_track
mov %ax,%cx
add sread,%ax
seg %cs
cmp %ax,sectors
jne ok3_read
mov $1,%ax
sub head,%ax
jne ok4_read
inc track
ok4_read:
mov %ax,head
xor %ax,%ax
ok3_read:
mov %ax,sread
shl $9,%cx
add %cx,%bx
jnc rp_read
mov %es,%ax
add $0x1000,%ax
mov %ax,%es
xor %bx,%bx
jmp rp_read
read_track:
push %ax
push %bx
push %cx
push %dx
mov track,%dx
mov sread,%cx
inc %cx
mov %dl,%ch
mov head,%dx
mov %dl,%dh
mov $0,%dl
and $0x0100,%dx
mov $2,%ah
int 0x13
jc bad_rt
pop %dx
pop %cx
pop %bx
pop %ax
ret
bad_rt: mov %ax,$0
mov $0,%dx
int 0x13
pop %dx
pop %cx
pop %bx
pop %ax
jmp read_track
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
push %dx
mov $0x3f2,%dx
mov $0,%al
outb
pop %dx
ret
sectors:
.word 0
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
#!/afs/net/tools/@sys/perl
#
#
$in_block_comment = 0;
while (<>) {
if (/^\|/) {
if (! $in_block_comment) {
print "/* \n";
$in_block_comment = 1;
}
s/\|/ */;
print;
next;
} else {
if ($in_block_comment) {
print " */\n";
$in_block_comment = 0;
}
}
s/#/$/; # Convert immediate references
s/\|/#/; # Convert in-line comments
s/(\b|,)([abcd][xhl])(\b|,|$)/\1%\2\3/g;
s/(\b|,)([cdsefg]s)(\b|,|$)/\1%\2\3/g;
s/(\b|,)([sd]i)(\b|,|$)/\1%\2\3/g;
s/(\b|,)([sb]p)(\b|,|$)/\1%\2\3/g;
s/(\b|,)(e[abcd]x)(\b|,|$)/\1%\2\3/g;
if (/^(([a-zA-Z]+:[ \t]+)|[ \t]+)([a-zA-Z]+)/) {
$op = $3;
if (($op eq "mov") || ($op eq "add") || ($op eq "sub") ||
($op eq "xor") || ($op eq "and") || ($op eq "shr") ||
($op eq "shl") || ($op eq "in") || ($op eq "out")) {
#
# We need to swap arguments...
#
s/([0-9a-zA-Z%\$]+)(,)([0-9a-zA-Z%\$]+)/\3\2\1/;
}
}
print;
}
/*
* linux/boot/head.s
*
* (C) 1991 Linus Torvalds
*/
/* /*
* head.s contains the 32-bit startup code. * head.s contains the 32-bit startup code.
* *
...@@ -6,7 +12,7 @@ ...@@ -6,7 +12,7 @@
* the page directory. * the page directory.
*/ */
.text .text
.globl _idt,_gdt,_pg_dir .globl _idt,_gdt,_pg_dir,_tmp_floppy_area
_pg_dir: _pg_dir:
startup_32: startup_32:
movl $0x10,%eax movl $0x10,%eax
...@@ -25,14 +31,22 @@ startup_32: ...@@ -25,14 +31,22 @@ startup_32:
lss _stack_start,%esp lss _stack_start,%esp
xorl %eax,%eax xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled 1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000 movl %eax,0x000000 # loop forever if it isn't
cmpl %eax,0x100000 cmpl %eax,0x100000
je 1b je 1b
/*
* NOTE! 486 should set bit 16, to check for write-protect in supervisor
* mode. Then it would be unnecessary with the "verify_area()"-calls.
* 486 users probably want to set the NE (#5) bit also, so as to use
* int 16 for math errors.
*/
movl %cr0,%eax # check math chip movl %cr0,%eax # check math chip
andl $0x80000011,%eax # Save PG,ET,PE andl $0x80000011,%eax # Save PG,ET,PE
/* "orl $0x10020,%eax" here for 486 might be good */
orl $2,%eax # set MP
testl $0x10,%eax testl $0x10,%eax
jne 1f # ET is set - 387 is present jne 1f # ET is set - 387 is present
orl $4,%eax # else set emulate bit xorl $6,%eax # else reset MP and set EM
1: movl %eax,%cr0 1: movl %eax,%cr0
jmp after_page_tables jmp after_page_tables
...@@ -78,6 +92,11 @@ setup_gdt: ...@@ -78,6 +92,11 @@ setup_gdt:
lgdt gdt_descr lgdt gdt_descr
ret ret
/*
* I put the kernel page tables right after the page directory,
* using 4 of them to span 16 Mb of physical memory. People with
* more than 16MB will have to expand this.
*/
.org 0x1000 .org 0x1000
pg0: pg0:
...@@ -85,11 +104,20 @@ pg0: ...@@ -85,11 +104,20 @@ pg0:
pg1: pg1:
.org 0x3000 .org 0x3000
pg2: # This is not used yet, but if you pg2:
# want to expand past 8 Mb, you'll have
# to use it.
.org 0x4000 .org 0x4000
pg3:
.org 0x5000
/*
* tmp_floppy_area is used by the floppy-driver when DMA cannot
* reach to a buffer-block. It needs to be aligned, so that it isn't
* on a 64kB border.
*/
_tmp_floppy_area:
.fill 1024,1,0
after_page_tables: after_page_tables:
pushl $0 # These are the parameters to main :-) pushl $0 # These are the parameters to main :-)
pushl $0 pushl $0
...@@ -102,11 +130,30 @@ L6: ...@@ -102,11 +130,30 @@ L6:
# just in case, we know what happens. # just in case, we know what happens.
/* This is the default interrupt "handler" :-) */ /* This is the default interrupt "handler" :-) */
int_msg:
.asciz "Unknown interrupt\n\r"
.align 2 .align 2
ignore_int: ignore_int:
incb 0xb8000+160 # put something on the screen pushl %eax
movb $2,0xb8000+161 # so that we know something pushl %ecx
iret # happened pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
pushl $int_msg
call _printk
popl %eax
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
/* /*
...@@ -114,7 +161,7 @@ ignore_int: ...@@ -114,7 +161,7 @@ ignore_int:
* *
* This routine sets up paging by setting the page bit * This routine sets up paging by setting the page bit
* in cr0. The page tables are set up, identity-mapping * in cr0. The page tables are set up, identity-mapping
* the first 8MB. The pager assumes that no illegal * the first 16MB. The pager assumes that no illegal
* addresses are produced (ie >4Mb on a 4Mb machine). * addresses are produced (ie >4Mb on a 4Mb machine).
* *
* NOTE! Although all physical memory should be identity * NOTE! Although all physical memory should be identity
...@@ -124,25 +171,27 @@ ignore_int: ...@@ -124,25 +171,27 @@ ignore_int:
* will be mapped to some other place - mm keeps track of * will be mapped to some other place - mm keeps track of
* that. * that.
* *
* For those with more memory than 8 Mb - tough luck. I've * For those with more memory than 16 Mb - tough luck. I've
* not got it, why should you :-) The source is here. Change * not got it, why should you :-) The source is here. Change
* it. (Seriously - it shouldn't be too difficult. Mostly * it. (Seriously - it shouldn't be too difficult. Mostly
* change some constants etc. I left it at 8Mb, as my machine * change some constants etc. I left it at 16Mb, as my machine
* even cannot be extended past that (ok, but it was cheap :-) * even cannot be extended past that (ok, but it was cheap :-)
* I've tried to show which constants to change by having * I've tried to show which constants to change by having
* some kind of marker at them (search for "8Mb"), but I * some kind of marker at them (search for "16Mb"), but I
* won't guarantee that's all :-( ) * won't guarantee that's all :-( )
*/ */
.align 2 .align 2
setup_paging: setup_paging:
movl $1024*3,%ecx movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */
xorl %eax,%eax xorl %eax,%eax
xorl %edi,%edi /* pg_dir is at 0x000 */ xorl %edi,%edi /* pg_dir is at 0x000 */
cld;rep;stosl cld;rep;stosl
movl $pg0+7,_pg_dir /* set present bit/user r/w */ movl $pg0+7,_pg_dir /* set present bit/user r/w */
movl $pg1+7,_pg_dir+4 /* --------- " " --------- */ movl $pg1+7,_pg_dir+4 /* --------- " " --------- */
movl $pg1+4092,%edi movl $pg2+7,_pg_dir+8 /* --------- " " --------- */
movl $0x7ff007,%eax /* 8Mb - 4096 + 7 (r/w user,p) */ movl $pg3+7,_pg_dir+12 /* --------- " " --------- */
movl $pg3+4092,%edi
movl $0xfff007,%eax /* 16Mb - 4096 + 7 (r/w user,p) */
std std
1: stosl /* fill pages backwards - more efficient :-) */ 1: stosl /* fill pages backwards - more efficient :-) */
subl $0x1000,%eax subl $0x1000,%eax
...@@ -169,7 +218,7 @@ gdt_descr: ...@@ -169,7 +218,7 @@ gdt_descr:
_idt: .fill 256,8,0 # idt is uninitialized _idt: .fill 256,8,0 # idt is uninitialized
_gdt: .quad 0x0000000000000000 /* NULL descriptor */ _gdt: .quad 0x0000000000000000 /* NULL descriptor */
.quad 0x00c09a00000007ff /* 8Mb */ .quad 0x00c09a0000000fff /* 16Mb */
.quad 0x00c09200000007ff /* 8Mb */ .quad 0x00c0920000000fff /* 16Mb */
.quad 0x0000000000000000 /* TEMPORARY - don't use */ .quad 0x0000000000000000 /* TEMPORARY - don't use */
.fill 252,8,0 /* space for LDT's and TSS's etc */ .fill 252,8,0 /* space for LDT's and TSS's etc */
| |
| boot.s | setup.s (C) 1991 Linus Torvalds
| |
| boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself | setup.s is responsible for getting the system data from the BIOS,
| out of the way to address 0x90000, and jumps there. | and putting them into the appropriate places in system memory.
| both setup.s and system has been loaded by the bootblock.
| |
| It then loads the system at 0x10000, using BIOS interrupts. Thereafter | This code asks the bios for memory/disk/other parameters, and
| it disables all interrupts, moves the system down to 0x0000, changes | puts them in a "safe" place: 0x90000-0x901FF, ie where the
| to protected mode, and calls the start of system. System then must | boot-block used to be. It is then up to the protected mode
| RE-initialize the protected mode in it's own tables, and enable | system to read them from there before the area is overwritten
| interrupts as needed. | for buffer-blocks.
| |
| NOTE! currently system is at most 8*65536 bytes long. This should be no
| problem, even in the future. I want to keep it simple. This 512 kB | NOTE! These had better be the same as in bootsect.s!
| kernel size should be enough - in fact more would mean we'd have to move
| not just these start-up routines, but also do something about the cache- INITSEG = 0x9000 | we move boot here - out of the way
| memory (block IO devices). The area left over in the lower 640 kB is meant SYSSEG = 0x1000 | system loaded at 0x10000 (65536).
| for these. No other memory is assumed to be "physical", ie all memory SETUPSEG = 0x9020 | this is the current segment
| over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match
| their physical addresses.
|
| NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated
| above the 1Mb mark as well as below. Otherwise it is mainly correct.
|
| NOTE 2! The boot disk type must be set at compile-time, by setting
| the following equ. Having the boot-up procedure hunt for the right
| disk type is severe brain-damage.
| The loader has been made as simple as possible (had to, to get it
| in 512 bytes with the code to move to protected mode), and continuos
| read errors will result in a unbreakable loop. Reboot by hand. It
| loads pretty fast by getting whole sectors at a time whenever possible.
| 1.44Mb disks:
sectors = 18
| 1.2Mb disks:
| sectors = 15
| 720kB disks:
| sectors = 9
.globl begtext, begdata, begbss, endtext, enddata, endbss .globl begtext, begdata, begbss, endtext, enddata, endbss
.text .text
...@@ -46,55 +27,67 @@ begdata: ...@@ -46,55 +27,67 @@ begdata:
begbss: begbss:
.text .text
BOOTSEG = 0x07c0
INITSEG = 0x9000
SYSSEG = 0x1000 | system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE
entry start entry start
start: start:
mov ax,#BOOTSEG
| 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 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 hd0 data
mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax,#INITSEG mov ax,#INITSEG
mov es,ax mov es,ax
mov cx,#256 mov di,#0x0080
sub si,si mov cx,#0x10
sub di,di
rep rep
movw movsb
jmpi go,INITSEG
go: mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,#0x400 | arbitrary value >>512
mov ah,#0x03 | read cursor pos | Get hd1 data
xor bh,bh
int 0x10
mov cx,#24
mov bx,#0x0007 | page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 | write string, move cursor
int 0x10
| ok, we've written the message, now mov ax,#0x0000
| we want to load the system (at 0x10000) mov ds,ax
lds si,[4*0x46]
mov ax,#INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
rep
movsb
mov ax,#SYSSEG | Check that there IS a hd1 :-)
mov es,ax | segment of 0x010000
call read_it
call kill_motor
| if the read went well we get current cursor position ans save it for mov ax,#0x01500
| posterity. 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:
mov ah,#0x03 | read cursor pos
xor bh,bh
int 0x10 | save it in known place, con_init fetches
mov [510],dx | it from 0x90510.
| now we want to move to protected mode ... | now we want to move to protected mode ...
cli | no interrupts allowed ! cli | no interrupts allowed !
...@@ -114,13 +107,12 @@ do_move: ...@@ -114,13 +107,12 @@ do_move:
mov cx,#0x8000 mov cx,#0x8000
rep rep
movsw movsw
j do_move jmp do_move
| then we load the segment descriptors | then we load the segment descriptors
end_move: end_move:
mov ax,#SETUPSEG | right, forgot this at first. didn't work :-)
mov ax,cs | right, forgot this at first. didn't work :-)
mov ds,ax mov ds,ax
lidt idt_48 | load idt with 0,0 lidt idt_48 | load idt with 0,0
lgdt gdt_48 | load gdt with whatever appropriate lgdt gdt_48 | load gdt with whatever appropriate
...@@ -194,107 +186,6 @@ empty_8042: ...@@ -194,107 +186,6 @@ empty_8042:
jnz empty_8042 | yes - loop jnz empty_8042 | yes - loop
ret ret
| This routine loads the system at address 0x10000, making sure
| no 64kB boundaries are crossed. We try to load it as fast as
| possible, loading whole tracks whenever we can.
|
| in: es - starting address segment (normally 0x1000)
|
| This routine has to be recompiled to fit another drive type,
| just change the "sectors" variable at the start of the file
| (originally 18, for a 1.44Mb drive)
|
sread: .word 1 | sectors read of current track
head: .word 0 | current head
track: .word 0 | current track
read_it:
mov ax,es
test ax,#0x0fff
die: jne die | es must be at 64kB boundary
xor bx,bx | bx is starting address within segment
rp_read:
mov ax,es
cmp ax,#ENDSEG | have we loaded all yet?
jb ok1_read
ret
ok1_read:
mov ax,#sectors
sub ax,sread
mov cx,ax
shl cx,#9
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
cmp ax,#sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#9
add bx,cx
jnc rp_read
mov ax,es
add ax,#0x1000
mov es,ax
xor bx,bx
jmp rp_read
read_track:
push ax
push bx
push cx
push dx
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
int 0x13
jc bad_rt
pop dx
pop cx
pop bx
pop ax
ret
bad_rt: mov ax,#0
mov dx,#0
int 0x13
pop dx
pop cx
pop bx
pop ax
jmp read_track
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
* don't have to worry about it later.
*/
kill_motor:
push dx
mov dx,#0x3f2
mov al,#0
outb
pop dx
ret
gdt: gdt:
.word 0,0,0,0 | dummy .word 0,0,0,0 | dummy
...@@ -314,13 +205,8 @@ idt_48: ...@@ -314,13 +205,8 @@ idt_48:
gdt_48: gdt_48:
.word 0x800 | gdt limit=2048, 256 GDT entries .word 0x800 | gdt limit=2048, 256 GDT entries
.word gdt,0x9 | gdt base = 0X9xxxx .word 512+gdt,0x9 | gdt base = 0X9xxxx
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
.text .text
endtext: endtext:
.data .data
......
/*
*
* 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 $INITSEG,%ax # this is done in bootsect already, but...
mov %ax,%ds
mov $0x03,%ah # 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 $0x88,%ah
int 0x15
mov [2],%ax
/*
* Get hd0 data
*/
mov $0x0000,%ax
mov %ax,%ds
lds %si,[4*0x41]
mov $INITSEG,%ax
mov %ax,%es
mov $0x0080,%di
mov $0x10,%cx
rep
movsb
/*
* Get hd1 data
*/
mov $0x0000,%ax
mov %ax,%ds
lds %si,[4*0x46]
mov $INITSEG,%ax
mov %ax,%es
mov $0x0090,%di
mov $0x10,%cx
rep
movsb
/*
* Check that there IS a hd1 :-)
*/
mov $0x01500,%ax
mov $0x81,%dl
int 0x13
jc no_disk1
cmp %ah,$3
je is_disk1
no_disk1:
mov $INITSEG,%ax
mov %ax,%es
mov $0x0090,%di
mov $0x10,%cx
mov $0x00,%ax
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 $0x0000,%ax
cld # 'direction'=0, movs moves forward
do_move:
mov %ax,%es # destination segment
add $0x1000,%ax
cmp %ax,$0x9000
jz end_move
mov %ax,%ds # source segment
sub %di,%di
sub %si,%si
mov $0x8000,%cx
rep
movsw
jmp do_move
/*
* then we load the segment descriptors
*/
end_move:
mov $SETUPSEG,%ax # right, forgot this at first. didn't work :-)
mov %ax,%ds
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 $0xD1,%al # command write
out %al,$0x64
call empty_8042
mov $0xDF,%al # A20 on
out %al,$0x60
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 $0x11,%al # initialization sequence
out %al,$0x20 # send it to 8259A-1
.word 0x00eb,0x00eb # jmp $+2, jmp $+2
out %al,$0xA0 # and to 8259A-2
.word 0x00eb,0x00eb
mov $0x20,%al # start of hardware int's (0x20)
out %al,$0x21
.word 0x00eb,0x00eb
mov $0x28,%al # start of hardware int's 2 (0x28)
out %al,$0xA1
.word 0x00eb,0x00eb
mov $0x04,%al # 8259-1 is master
out %al,$0x21
.word 0x00eb,0x00eb
mov $0x02,%al # 8259-2 is slave
out %al,$0xA1
.word 0x00eb,0x00eb
mov $0x01,%al # 8086 mode for both
out %al,$0x21
.word 0x00eb,0x00eb
out %al,$0xA1
.word 0x00eb,0x00eb
mov $0xFF,%al # mask off all interrupts for now
out %al,$0x21
.word 0x00eb,0x00eb
out %al,$0xA1
/*
* 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 $0x0001,%ax # 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 $0x64,%al # 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:
...@@ -34,62 +34,71 @@ dep: ...@@ -34,62 +34,71 @@ dep:
### Dependencies: ### Dependencies:
bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \ 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/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h
block_dev.o : block_dev.c ../include/errno.h ../include/linux/fs.h \ block_dev.o : block_dev.c ../include/errno.h ../include/linux/sched.h \
../include/sys/types.h ../include/linux/kernel.h ../include/asm/segment.h
buffer.o : buffer.c ../include/linux/config.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
char_dev.o : char_dev.c ../include/errno.h ../include/linux/sched.h \ ../include/asm/segment.h ../include/asm/system.h
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ buffer.o : buffer.c ../include/stdarg.h ../include/linux/config.h \
../include/linux/mm.h ../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/kernel.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/sys/stat.h \ exec.o : exec.c ../include/errno.h ../include/sys/stat.h \
../include/sys/types.h ../include/a.out.h ../include/linux/fs.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/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/asm/segment.h ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h
fcntl.o : fcntl.c ../include/string.h ../include/errno.h \ fcntl.o : fcntl.c ../include/string.h ../include/errno.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.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/sys/types.h ../include/linux/mm.h ../include/signal.h \
../include/asm/segment.h ../include/fcntl.h ../include/sys/stat.h ../include/linux/kernel.h ../include/asm/segment.h ../include/fcntl.h \
../include/sys/stat.h
file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.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/sys/types.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/signal.h \
../include/asm/segment.h ../include/linux/kernel.h ../include/asm/segment.h
file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h
inode.o : inode.c ../include/string.h ../include/linux/sched.h \ inode.o : inode.c ../include/string.h ../include/sys/stat.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \ ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h ../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \
../include/linux/kernel.h ../include/asm/system.h
ioctl.o : ioctl.c ../include/string.h ../include/errno.h \ ioctl.o : ioctl.c ../include/string.h ../include/errno.h \
../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.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/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/signal.h
namei.o : namei.c ../include/linux/sched.h ../include/linux/head.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/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/asm/segment.h ../include/string.h \ ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h \
../include/fcntl.h ../include/errno.h ../include/const.h \ ../include/string.h ../include/fcntl.h ../include/errno.h \
../include/sys/stat.h ../include/const.h ../include/sys/stat.h
open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.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/sys/types.h ../include/utime.h ../include/sys/stat.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.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/linux/mm.h ../include/signal.h ../include/linux/tty.h \
../include/linux/kernel.h ../include/asm/segment.h ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h
pipe.o : pipe.c ../include/signal.h ../include/sys/types.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/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/linux/mm.h ../include/asm/segment.h ../include/linux/mm.h ../include/asm/segment.h
read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.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/errno.h ../include/linux/kernel.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \ ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/asm/segment.h ../include/signal.h ../include/asm/segment.h
stat.o : stat.c ../include/errno.h ../include/sys/stat.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/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \ ../include/linux/head.h ../include/linux/mm.h ../include/signal.h \
../include/asm/segment.h ../include/linux/kernel.h ../include/asm/segment.h
super.o : super.c ../include/linux/config.h ../include/linux/sched.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/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/system.h ../include/errno.h ../include/sys/stat.h
truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.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/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/sys/stat.h ../include/signal.h ../include/sys/stat.h
tty_ioctl.o : tty_ioctl.c ../include/errno.h ../include/termios.h \ 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/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \ ../include/sys/types.h ../include/linux/mm.h ../include/signal.h \
../include/linux/tty.h ../include/asm/segment.h ../include/asm/system.h ../include/linux/kernel.h ../include/linux/tty.h ../include/asm/segment.h \
../include/asm/system.h
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.11.20.00.10.40; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.12.15.49.42; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Used new version of buffer.c supplied from Linus.
@
text
@/*
* linux/fs/buffer.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'buffer.c' implements the buffer-cache functions. Race-conditions have
* been avoided by NEVER letting a interrupt change a buffer (except for the
* data, of course), but instead letting the caller do it. NOTE! As interrupts
* can wake up a caller, some cli-sti sequences are needed to check for
* sleep-on-calls. These should be extremely quick, though (I hope).
*/
/*
* NOTE! There is one discordant note here: checking floppies for
* disk change. This is where it fits best, I think, as it should
* invalidate changed floppy-disk-caches.
*/
#include <stdarg.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <asm/io.h>
extern int end;
struct buffer_head * start_buffer = (struct buffer_head *) &end;
struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list;
static struct task_struct * buffer_wait = NULL;
int NR_BUFFERS = 0;
static inline void wait_on_buffer(struct buffer_head * bh)
{
cli();
while (bh->b_lock)
sleep_on(&bh->b_wait);
sti();
}
int sys_sync(void)
{
int i;
struct buffer_head * bh;
sync_inodes(); /* write out inodes into buffers */
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
wait_on_buffer(bh);
if (bh->b_dirt)
ll_rw_block(WRITE,bh);
}
return 0;
}
int sync_dev(int dev)
{
int i;
struct buffer_head * bh;
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh);
}
sync_inodes();
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh);
}
return 0;
}
/*
* This routine checks whether a floppy has been changed, and
* invalidates all buffer-cache-entries in that case. This
* is a relatively slow routine, so we have to try to minimize using
* it. Thus it is called only upon a 'mount' or 'open'. This
* is the best way of combining speed and utility, I think.
* People changing diskettes in the middle of an operation deserve
* to loose :-)
*
* NOTE! Although currently this is only for floppies, the idea is
* that any additional removable block-device will use this routine,
* and that mount/open needn't know that floppies/whatever are
* special.
*/
void check_disk_change(int dev)
{
int i;
struct buffer_head * bh;
if (MAJOR(dev) != 2)
return;
dev=MINOR(dev) & 0x03; /* which floppy is it? */
if (!floppy_change(dev))
return;
dev |= 0x200;
for (i=0 ; i<NR_SUPER ; i++)
if ((super_block[i].s_dev & 0xff03)==dev)
put_super(super_block[i].s_dev);
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if ((bh->b_dev & 0xff03) != dev)
continue;
wait_on_buffer(bh);
if ((bh->b_dev & 0xff03) == dev)
bh->b_uptodate = bh->b_dirt = 0;
}
}
#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
#define hash(dev,block) hash_table[_hashfn(dev,block)]
static inline void remove_from_queues(struct buffer_head * bh)
{
/* remove from hash-queue */
if (bh->b_next)
bh->b_next->b_prev = bh->b_prev;
if (bh->b_prev)
bh->b_prev->b_next = bh->b_next;
if (hash(bh->b_dev,bh->b_blocknr) == bh)
hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
/* remove from free list */
if (!(bh->b_prev_free) || !(bh->b_next_free))
panic("Free block list corrupted");
bh->b_prev_free->b_next_free = bh->b_next_free;
bh->b_next_free->b_prev_free = bh->b_prev_free;
if (free_list == bh)
free_list = bh->b_next_free;
}
static inline void insert_into_queues(struct buffer_head * bh)
{
/* put at end of free list */
bh->b_next_free = free_list;
bh->b_prev_free = free_list->b_prev_free;
free_list->b_prev_free->b_next_free = bh;
free_list->b_prev_free = bh;
/* put the buffer in new hash-queue if it has a device */
bh->b_prev = NULL;
bh->b_next = NULL;
if (!bh->b_dev)
return;
bh->b_next = hash(bh->b_dev,bh->b_blocknr);
hash(bh->b_dev,bh->b_blocknr) = bh;
bh->b_next->b_prev = bh;
}
static struct buffer_head * find_buffer(int dev, int block)
{
struct buffer_head * tmp;
for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
if (tmp->b_dev==dev && tmp->b_blocknr==block)
return tmp;
return NULL;
}
/*
* Why like this, I hear you say... The reason is race-conditions.
* As we don't lock buffers (unless we are readint them, that is),
* something might happen to it while we sleep (ie a read-error
* will force it bad). This shouldn't really happen currently, but
* the code is ready.
*/
struct buffer_head * get_hash_table(int dev, int block)
{
struct buffer_head * bh;
for (;;) {
if (!(bh=find_buffer(dev,block)))
return NULL;
bh->b_count++;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_blocknr == block)
return bh;
bh->b_count--;
}
}
/*
* Ok, this is getblk, and it isn't very clear, again to hinder
* race-conditions. Most of the code is seldom used, (ie repeating),
* so it should be much more efficient than it looks.
*
* The algoritm is changed: better, and an elusive bug removed.
* LBT 11.11.91
*/
#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
struct buffer_head * getblk(int dev,int block)
{
struct buffer_head * tmp, * bh;
repeat:
if (bh = get_hash_table(dev,block))
return bh;
tmp = free_list;
do {
if (tmp->b_count)
continue;
if (!bh || BADNESS(tmp)<BADNESS(bh)) {
bh = tmp;
if (!BADNESS(tmp))
break;
}
} while ((tmp = tmp->b_next_free) != free_list);
if (!bh) {
sleep_on(&buffer_wait);
goto repeat;
}
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
while (bh->b_dirt) {
sync_dev(bh->b_dev);
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
}
/* NOTE!! While we slept waiting for this block, somebody else might */
/* already have added "this" block to the cache. check it */
if (find_buffer(dev,block))
goto repeat;
/* OK, FINALLY we know that this buffer is the only one of it's kind, */
/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
bh->b_count=1;
bh->b_dirt=0;
bh->b_uptodate=0;
remove_from_queues(bh);
bh->b_dev=dev;
bh->b_blocknr=block;
insert_into_queues(bh);
return bh;
}
void brelse(struct buffer_head * buf)
{
if (!buf)
return;
wait_on_buffer(buf);
if (!(buf->b_count--))
panic("Trying to free free buffer");
wake_up(&buffer_wait);
}
/*
* bread() reads a specified block and returns the buffer that contains
* it. It returns NULL if the block was unreadable.
*/
struct buffer_head * bread(int dev,int block)
{
struct buffer_head * bh;
if (!(bh=getblk(dev,block)))
panic("bread: getblk returned NULL\n");
if (bh->b_uptodate)
return bh;
ll_rw_block(READ,bh);
wait_on_buffer(bh);
if (bh->b_uptodate)
return bh;
brelse(bh);
return NULL;
}
/*
* Ok, breada can be used as bread, but additionally to mark other
* blocks for reading as well. End the argument list with a negative
* number.
*/
struct buffer_head * breada(int dev,int first, ...)
{
va_list args;
struct buffer_head * bh, *tmp;
va_start(args,first);
if (!(bh=getblk(dev,first)))
panic("bread: getblk returned NULL\n");
if (!bh->b_uptodate)
ll_rw_block(READ,bh);
while ((first=va_arg(args,int))>=0) {
tmp=getblk(dev,first);
if (tmp) {
if (!tmp->b_uptodate)
ll_rw_block(READA,bh);
tmp->b_count--;
}
}
va_end(args);
wait_on_buffer(bh);
if (bh->b_uptodate)
return bh;
brelse(bh);
return (NULL);
}
void buffer_init(long buffer_end)
{
struct buffer_head * h = start_buffer;
void * b;
int i;
if (buffer_end == 1<<20)
b = (void *) (640*1024);
else
b = (void *) buffer_end;
while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
h->b_dev = 0;
h->b_dirt = 0;
h->b_count = 0;
h->b_lock = 0;
h->b_uptodate = 0;
h->b_wait = NULL;
h->b_next = NULL;
h->b_prev = NULL;
h->b_data = (char *) b;
h->b_prev_free = h-1;
h->b_next_free = h+1;
h++;
NR_BUFFERS++;
if (b == (void *) 0x100000)
b = (void *) 0xA0000;
}
h--;
free_list = start_buffer;
free_list->b_prev_free = h;
h->b_next_free = free_list;
for (i=0;i<NR_HASH;i++)
hash_table[i]=NULL;
}
@
1.1
log
@Initial revision
@
text
@d72 9
d196 3
d200 1
d203 1
a203 1
struct buffer_head * tmp;
d206 2
a207 2
if (tmp=get_hash_table(dev,block))
return tmp;
d209 6
a214 4
for (;;) {
if (!tmp->b_count) {
wait_on_buffer(tmp); /* we still have to wait */
if (!tmp->b_count) /* on it, it might be dirty */
d217 12
a228 3
tmp = tmp->b_next_free;
if (tmp == free_list) {
sleep_on(&buffer_wait);
a229 1
}
d231 3
a233 23
tmp->b_count++;
remove_from_queues(tmp);
/*
* Now, when we know nobody can get to this node (as it's removed from the
* free list), we write it out. We can sleep here without fear of race-
* conditions.
*/
while (tmp->b_dirt) {
sync_dev(tmp->b_dev);
wait_on_buffer(tmp);
}
/* update buffer contents */
tmp->b_dev=dev;
tmp->b_blocknr=block;
tmp->b_dirt=0;
tmp->b_uptodate=0;
/* NOTE!! While we possibly slept in sync_dev(), somebody else might have */
/* added "this" block already, so check for that. Thank God for goto's. */
if (find_buffer(dev,block)) {
tmp->b_dev=0; /* ok, someone else has beaten us */
tmp->b_blocknr=0; /* to it - free this block and */
tmp->b_count=0; /* try again */
insert_into_queues(tmp);
d235 10
a244 4
}
/* and then insert into correct position */
insert_into_queues(tmp);
return tmp;
@
head 1.2;
branch ;
access ;
symbols ;
locks tytso:1.2; strict;
comment @ * @;
1.2
date 91.12.01.09.22.01; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.21.23.19.36; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Patches sent to Linus
@
text
@/*
* linux/fs/exec.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <a.out.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/segment.h>
extern int sys_exit(int exit_code);
extern int sys_close(int fd);
/*
* MAX_ARG_PAGES defines the number of pages allocated for arguments
* and envelope for the new program. 32 should suffice, this gives
* a maximum env+arg of 128kB !
*/
#define MAX_ARG_PAGES 32
#define cp_block(from,to) \
__asm__("pushl $0x10\n\t" \
"pushl $0x17\n\t" \
"pop %%es\n\t" \
"cld\n\t" \
"rep\n\t" \
"movsl\n\t" \
"pop %%es" \
::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
:"cx","di","si")
/*
* read_head() reads blocks 1-6 (not 0). Block 0 has already been
* read for header information.
*/
int read_head(struct m_inode * inode,int blocks)
{
struct buffer_head * bh;
int count;
if (blocks>6)
blocks=6;
for(count = 0 ; count<blocks ; count++) {
if (!inode->i_zone[count+1])
continue;
if (!(bh=bread(inode->i_dev,inode->i_zone[count+1])))
return -1;
cp_block(bh->b_data,count*BLOCK_SIZE);
brelse(bh);
}
return 0;
}
int read_ind(int dev,int ind,long size,unsigned long offset)
{
struct buffer_head * ih, * bh;
unsigned short * table,block;
if (size<=0)
panic("size<=0 in read_ind");
if (size>512*BLOCK_SIZE)
size=512*BLOCK_SIZE;
if (!ind)
return 0;
if (!(ih=bread(dev,ind)))
return -1;
table = (unsigned short *) ih->b_data;
while (size>0) {
if (block=*(table++))
if (!(bh=bread(dev,block))) {
brelse(ih);
return -1;
} else {
cp_block(bh->b_data,offset);
brelse(bh);
}
size -= BLOCK_SIZE;
offset += BLOCK_SIZE;
}
brelse(ih);
return 0;
}
/*
* read_area() reads an area into %fs:mem.
*/
int read_area(struct m_inode * inode,long size)
{
struct buffer_head * dind;
unsigned short * table;
int i,count;
if ((i=read_head(inode,(size+BLOCK_SIZE-1)/BLOCK_SIZE)) ||
(size -= BLOCK_SIZE*6)<=0)
return i;
if ((i=read_ind(inode->i_dev,inode->i_zone[7],size,BLOCK_SIZE*6)) ||
(size -= BLOCK_SIZE*512)<=0)
return i;
if (!(i=inode->i_zone[8]))
return 0;
if (!(dind = bread(inode->i_dev,i)))
return -1;
table = (unsigned short *) dind->b_data;
for(count=0 ; count<512 ; count++)
if ((i=read_ind(inode->i_dev,*(table++),size,
BLOCK_SIZE*(518+count))) || (size -= BLOCK_SIZE*512)<=0)
return i;
panic("Impossibly long executable");
}
/*
* create_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
* addresses on the "stack", returning the new stack pointer value.
*/
static unsigned long * create_tables(char * p,int argc,int envc)
{
unsigned long *argv,*envp;
unsigned long * sp;
sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
sp -= envc+1;
envp = sp;
sp -= argc+1;
argv = sp;
put_fs_long((unsigned long)envp,--sp);
put_fs_long((unsigned long)argv,--sp);
put_fs_long((unsigned long)argc,--sp);
while (argc-->0) {
put_fs_long((unsigned long) p,argv++);
while (get_fs_byte(p++)) /* nothing */ ;
}
put_fs_long(0,argv);
while (envc-->0) {
put_fs_long((unsigned long) p,envp++);
while (get_fs_byte(p++)) /* nothing */ ;
}
put_fs_long(0,envp);
return sp;
}
/*
* count() counts the number of arguments/envelopes
*/
static int count(char ** argv)
{
int i=0;
char ** tmp;
if (tmp = argv)
while (get_fs_long((unsigned long *) (tmp++)))
i++;
return i;
}
/*
* 'copy_string()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory.
*
* Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
* whether the string and the string array are from user or kernel segments:
*
* from_kmem argv * argv **
* 0 user space user space
* 1 kernel space user space
* 2 kernel space kernel space
*
* We do this by playing games with the fs segment register. Since it
* it is expensive to load a segment register, we try to avoid calling
* set_fs() unless we absolutely have to.
*/
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
unsigned long p, int from_kmem)
{
char *tmp, *pag;
int len, offset = 0;
unsigned long old_fs, new_fs;
if (!p)
return 0; /* bullet-proofing */
new_fs = get_ds();
old_fs = get_fs();
if (from_kmem==2)
set_fs(new_fs);
while (argc-- > 0) {
if (from_kmem == 1)
set_fs(new_fs);
if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
panic("argc is wrong");
if (from_kmem == 1)
set_fs(old_fs);
len=0; /* remember zero-padding */
do {
len++;
} while (get_fs_byte(tmp++));
if (p-len < 0) { /* this shouldn't happen - 128kB */
set_fs(old_fs);
return 0;
}
while (len) {
--p; --tmp; --len;
if (--offset < 0) {
offset = p % PAGE_SIZE;
if (from_kmem==2)
set_fs(old_fs);
if (!(pag = (char *) page[p/PAGE_SIZE]) &&
!(pag = (char *) page[p/PAGE_SIZE] =
(unsigned long *) get_free_page()))
return 0;
if (from_kmem==2)
set_fs(new_fs);
}
*(pag + offset) = get_fs_byte(tmp);
}
}
if (from_kmem==2)
set_fs(old_fs);
return p;
}
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_base = get_base(current->ldt[1]);
data_base = code_base;
set_base(current->ldt[1],code_base);
set_limit(current->ldt[1],code_limit);
set_base(current->ldt[2],data_base);
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;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
if (page[i])
put_page(page[i],data_base);
}
return data_limit;
}
/*
* 'do_execve()' executes a new program.
*/
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
{
struct m_inode * inode;
struct buffer_head * bh;
struct exec ex;
unsigned long page[MAX_ARG_PAGES];
int i,argc,envc;
int e_uid, e_gid;
int retval;
int sh_bang = 0;
unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
if ((0xffff & eip[1]) != 0x000f)
panic("execve called from supervisor mode");
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
page[i]=0;
if (!(inode=namei(filename))) /* get executables inode */
return -ENOENT;
argc = count(argv);
envc = count(envp);
restart_interp:
if (!S_ISREG(inode->i_mode)) { /* must be regular file */
retval = -EACCES;
goto exec_error2;
}
i = inode->i_mode;
e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
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)
i >>= 3;
if (!(i & 1) &&
!((inode->i_mode & 0111) && suser())) {
retval = -ENOEXEC;
goto exec_error2;
}
if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
retval = -EACCES;
goto exec_error2;
}
ex = *((struct exec *) bh->b_data); /* read exec-header */
if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {
/*
* This section does the #! interpretation.
* Sorta complicated, but hopefully it will work. -TYT
*/
char buf[1023], *cp, *interp, *i_name, *i_arg;
unsigned long old_fs;
strncpy(buf, bh->b_data+2, 1022);
brelse(bh);
iput(inode);
buf[1022] = '\0';
if (cp = strchr(buf, '\n')) {
*cp = '\0';
for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
}
if (!cp || *cp == '\0') {
retval = -ENOEXEC; /* No interpreter name found */
goto exec_error1;
}
interp = i_name = cp;
i_arg = 0;
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
if (*cp == '/')
i_name = cp+1;
}
if (*cp) {
*cp++ = '\0';
i_arg = cp;
}
/*
* OK, we've parsed out the interpreter name and
* (optional) argument.
*/
if (sh_bang++ == 0) {
p = copy_strings(envc, envp, page, p, 0);
p = copy_strings(--argc, argv+1, page, p, 0);
}
/*
* Splice in (1) the interpreter's name for argv[0]
* (2) (optional) argument to interpreter
* (3) filename of shell script
*
* This is done in reverse order, because of how the
* user environment and arguments are stored.
*/
p = copy_strings(1, &filename, page, p, 1);
argc++;
if (i_arg) {
p = copy_strings(1, &i_arg, page, p, 2);
argc++;
}
p = copy_strings(1, &i_name, page, p, 2);
argc++;
if (!p) {
retval = -ENOMEM;
goto exec_error1;
}
/*
* OK, now restart the process with the interpreter's inode.
*/
old_fs = get_fs();
set_fs(get_ds());
if (!(inode=namei(interp))) { /* get executables inode */
set_fs(old_fs);
retval = -ENOENT;
goto exec_error1;
}
set_fs(old_fs);
goto restart_interp;
}
brelse(bh);
if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
retval = -ENOEXEC;
goto exec_error2;
}
if (N_TXTOFF(ex) != BLOCK_SIZE) {
printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
retval = -ENOEXEC;
goto exec_error2;
}
if (!sh_bang) {
p = copy_strings(envc,envp,page,p,0);
p = copy_strings(argc,argv,page,p,0);
if (!p) {
retval = -ENOMEM;
goto exec_error2;
}
}
/* OK, This is the point of no return */
for (i=0 ; i<32 ; i++)
current->sigaction[i].sa_handler = NULL;
for (i=0 ; i<NR_OPEN ; i++)
if ((current->close_on_exec>>i)&1)
sys_close(i);
current->close_on_exec = 0;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
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 = (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 = read_area(inode,ex.a_text+ex.a_data);
iput(inode);
if (i<0)
sys_exit(-1);
i = ex.a_text+ex.a_data;
while (i&0xfff)
put_fs_byte(0,(char *) (i++));
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
return 0;
exec_error2:
iput(inode);
exec_error1:
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(page[i]);
return(retval);
}
@
1.1
log
@Initial revision
@
text
@d8 1
d168 12
d182 1
a182 1
unsigned long p)
d184 3
a186 2
int len,i;
char *tmp;
d188 6
d195 3
a197 1
if (!(tmp = (char *)get_fs_long(((unsigned long *) argv)+argc)))
d199 2
d205 2
a206 1
if (p-len < 0) /* this shouldn't happen - 128kB */
a207 5
i = ((unsigned) (p-len)) >> 12;
while (i<MAX_ARG_PAGES && !page[i]) {
if (!(page[i]=get_free_page()))
return 0;
i++;
d209 16
a224 7
do {
--p;
if (!page[p/PAGE_SIZE])
panic("nonexistent page in exec.c");
((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] =
get_fs_byte(--tmp);
} while (--len);
d226 2
d267 4
a270 1
unsigned long p;
d278 4
d283 2
a284 2
iput(inode);
return -EACCES;
d287 10
a296 10
if (current->uid && current->euid) {
if (current->euid == inode->i_uid)
i >>= 6;
else if (current->egid == inode->i_gid)
i >>= 3;
} else if (i & 0111)
i=1;
if (!(i & 1)) {
iput(inode);
return -ENOEXEC;
d299 15
d315 59
a373 1
return -EACCES;
a374 1
ex = *((struct exec *) bh->b_data); /* read exec-header */
d379 2
a380 2
iput(inode);
return -ENOEXEC;
d382 4
a385 11
if (N_TXTOFF(ex) != BLOCK_SIZE)
panic("N_TXTOFF != BLOCK_SIZE. See a.out.h.");
argc = count(argv);
envc = count(envp);
p = copy_strings(envc,envp,page,PAGE_SIZE*MAX_ARG_PAGES-4);
p = copy_strings(argc,argv,page,p);
if (!p) {
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(page[i]);
iput(inode);
return -1;
d387 8
d413 2
d425 6
@
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.12.01.09.22.07; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.21.09.55.14; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Patches sent to Linus
@
text
@/*
* linux/fs/namei.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.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>
#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
/*
* comment out this line if you want names > NAME_LEN chars to be
* truncated. Else they will be disallowed.
*/
/* #define NO_TRUNCATE */
#define MAY_EXEC 1
#define MAY_WRITE 2
#define MAY_READ 4
/*
* permission()
*
* is used to check for read/write/execute permissions on a file.
* I don't know if we should look at just the euid or both euid and
* uid, but that should be easily changed.
*/
static int permission(struct m_inode * inode,int mask)
{
int mode = inode->i_mode;
/* special case: not even root can read/write a deleted file */
if (inode->i_dev && !inode->i_nlinks)
return 0;
else if (current->euid==inode->i_uid)
mode >>= 6;
else if (current->egid==inode->i_gid)
mode >>= 3;
if (((mode & mask & 0007) == mask) || suser())
return 1;
return 0;
}
/*
* ok, we cannot use strncmp, as the name is not in our data space.
* Thus we'll have to use match. No big problem. Match also makes
* some sanity tests.
*
* NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
*/
static int match(int len,const char * name,struct dir_entry * de)
{
register int same __asm__("ax");
if (!de || !de->inode || len > NAME_LEN)
return 0;
if (len < NAME_LEN && de->name[len])
return 0;
__asm__("cld\n\t"
"fs ; repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
:"cx","di","si");
return same;
}
/*
* find_entry()
*
* finds and entry in the specified directory with the wanted name. It
* returns the cache buffer in which the entry was found, and the entry
* itself (as a parameter - res_dir). It does NOT read the inode of the
* entry - you'll have to do that yourself if you want to.
*
* This also takes care of the few special cases due to '..'-traversal
* over a pseudo-root and a mount point.
*/
static struct buffer_head * find_entry(struct m_inode ** dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int entries;
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
struct super_block * sb;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#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) */
if ((*dir) == current->root)
namelen=1;
else if ((*dir)->i_num == ROOT_INO) {
/* '..' over a mount-point results in 'dir' being exchanged for the mounted
directory-inode. NOTE! We set mounted, so that we can iput the new dir */
sb=get_super((*dir)->i_dev);
if (sb->s_imount) {
iput(*dir);
(*dir)=sb->s_imount;
(*dir)->i_count++;
}
}
}
if (!(block = (*dir)->i_zone[0]))
return NULL;
if (!(bh = bread((*dir)->i_dev,block)))
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;
while (i < entries) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
!(bh = bread((*dir)->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if (match(namelen,name,de)) {
*res_dir = de;
return bh;
}
de++;
i++;
}
brelse(bh);
return NULL;
}
/*
* add_entry()
*
* adds a file entry to the specified directory, using the same
* semantics as find_entry(). It returns NULL if it failed.
*
* NOTE!! The inode part of 'de' is left at 0 - which means you
* may not sleep between calling this and putting something into
* the entry, as someone else might have used it while you slept.
*/
static struct buffer_head * add_entry(struct m_inode * dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
*res_dir = NULL;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
if (!namelen)
return NULL;
if (!(block = dir->i_zone[0]))
return NULL;
if (!(bh = bread(dir->i_dev,block)))
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;
while (1) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
if (!block)
return NULL;
if (!(bh = bread(dir->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if (i*sizeof(struct dir_entry) >= dir->i_size) {
de->inode=0;
dir->i_size = (i+1)*sizeof(struct dir_entry);
dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME;
}
if (!de->inode) {
dir->i_mtime = CURRENT_TIME;
for (i=0; i < NAME_LEN ; i++)
de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
bh->b_dirt = 1;
*res_dir = de;
return bh;
}
de++;
i++;
}
brelse(bh);
return NULL;
}
/*
* 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)
{
char c;
const char * thisname;
struct m_inode * inode;
struct buffer_head * bh;
int namelen,inr,idev;
struct dir_entry * de;
if (!current->root || !current->root->i_count)
panic("No root inode");
if (!current->pwd || !current->pwd->i_count)
panic("No cwd inode");
if ((c=get_fs_byte(pathname))=='/') {
inode = current->root;
pathname++;
} else if (c)
inode = current->pwd;
else
return NULL; /* empty name is bad */
inode->i_count++;
while (1) {
thisname = pathname;
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
iput(inode);
return NULL;
}
for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
/* nothing */ ;
if (!c)
return inode;
if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
iput(inode);
return NULL;
}
inr = de->inode;
idev = inode->i_dev;
brelse(bh);
iput(inode);
if (!(inode = iget(idev,inr)))
return NULL;
}
}
/*
* dir_namei()
*
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name)
{
char c;
const char * basename;
struct m_inode * dir;
if (!(dir = get_dir(pathname)))
return NULL;
basename = pathname;
while (c=get_fs_byte(pathname++))
if (c=='/')
basename=pathname;
*namelen = pathname-basename-1;
*name = basename;
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)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return NULL;
if (!namelen) /* special case: '/usr/' etc */
return dir;
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
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;
}
return dir;
}
/*
* open_namei()
*
* namei for open - this is in fact almost the whole open-routine.
*/
int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir, *inode;
struct buffer_head * bh;
struct dir_entry * de;
if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
flag |= O_WRONLY;
mode &= 0777 & ~current->umask;
mode |= I_REGULAR;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen) { /* special case: '/usr/' etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
*res_inode=dir;
return 0;
}
iput(dir);
return -EISDIR;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
if (!(flag & O_CREAT)) {
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EACCES;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_uid = current->euid;
inode->i_mode = mode;
inode->i_dirt = 1;
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);
*res_inode = inode;
return 0;
}
inr = de->inode;
dev = dir->i_dev;
brelse(bh);
iput(dir);
if (flag & O_EXCL)
return -EEXIST;
if (!(inode=iget(dev,inr)))
return -EACCES;
if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
!permission(inode,ACC_MODE(flag))) {
iput(inode);
return -EPERM;
}
inode->i_atime = CURRENT_TIME;
if (flag & O_TRUNC)
truncate(inode);
*res_inode = inode;
return 0;
}
int sys_mknod(const char * filename, int mode, int dev)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(filename,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
return -EEXIST;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_mode = mode;
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_zone[0] = dev;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
inode->i_nlinks=0;
iput(inode);
return -ENOSPC;
}
de->inode = inode->i_num;
bh->b_dirt = 1;
iput(dir);
iput(inode);
brelse(bh);
return 0;
}
int sys_mkdir(const char * pathname, int mode)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh, *dir_block;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
return -EEXIST;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_size = 32;
inode->i_dirt = 1;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
iput(dir);
inode->i_nlinks--;
iput(inode);
return -ENOSPC;
}
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;
}
de = (struct dir_entry *) dir_block->b_data;
de->inode=inode->i_num;
strcpy(de->name,".");
de++;
de->inode = dir->i_num;
strcpy(de->name,"..");
inode->i_nlinks = 2;
dir_block->b_dirt = 1;
brelse(dir_block);
inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
inode->i_dirt = 1;
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;
}
de->inode = inode->i_num;
bh->b_dirt = 1;
dir->i_nlinks++;
dir->i_dirt = 1;
iput(dir);
iput(inode);
brelse(bh);
return 0;
}
/*
* routine to check that the specified directory is empty (for rmdir)
*/
static int empty_dir(struct m_inode * inode)
{
int nr,block;
int len;
struct buffer_head * bh;
struct dir_entry * de;
len = inode->i_size / sizeof (struct dir_entry);
if (len<2 || !inode->i_zone[0] ||
!(bh=bread(inode->i_dev,inode->i_zone[0]))) {
printk("warning - bad directory on dev %04x\n",inode->i_dev);
return 0;
}
de = (struct dir_entry *) bh->b_data;
if (de[0].inode != inode->i_num || !de[1].inode ||
strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
printk("warning - bad directory on dev %04x\n",inode->i_dev);
return 0;
}
nr = 2;
de += 2;
while (nr<len) {
if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
brelse(bh);
block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
if (!block) {
nr += DIR_ENTRIES_PER_BLOCK;
continue;
}
if (!(bh=bread(inode->i_dev,block)))
return 0;
de = (struct dir_entry *) bh->b_data;
}
if (de->inode) {
brelse(bh);
return 0;
}
de++;
nr++;
}
brelse(bh);
return 1;
}
int sys_rmdir(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
brelse(bh);
return -EPERM;
}
if (!(inode = iget(dir->i_dev, de->inode))) {
iput(dir);
brelse(bh);
return -EPERM;
}
if (inode == dir) { /* we may not delete ".", but "../dir" is ok */
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
iput(dir);
brelse(bh);
return -ENOTDIR;
}
if (!empty_dir(inode)) {
iput(inode);
iput(dir);
brelse(bh);
return -ENOTEMPTY;
}
if (inode->i_nlinks != 2)
printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
de->inode = 0;
bh->b_dirt = 1;
brelse(bh);
inode->i_nlinks=0;
inode->i_dirt=1;
dir->i_nlinks--;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt=1;
iput(dir);
iput(inode);
return 0;
}
int sys_unlink(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
return -ENOENT;
}
inode = iget(dir->i_dev, de->inode);
if (!inode) {
printk("iget failed in delete (%04x:%d)",dir->i_dev,de->inode);
iput(dir);
brelse(bh);
return -ENOENT;
}
if (S_ISDIR(inode->i_mode)) {
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
/*
* If the directory has the sticky bit, the user must either
* own the file or own the directory or be the superuser to
* delete a file in that directory. This is typically used
* for /tmp and /usr/tmp.
*/
if ((dir->i_mode & S_ISVTX) && (current->euid != inode->i_uid) &&
(current->euid != dir->i_uid) && !suser()) {
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
if (!inode->i_nlinks) {
printk("Deleting nonexistent file (%04x:%d), %d\n",
inode->i_dev,inode->i_num,inode->i_nlinks);
inode->i_nlinks=1;
}
de->inode = 0;
bh->b_dirt = 1;
brelse(bh);
inode->i_nlinks--;
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
iput(inode);
iput(dir);
return 0;
}
int sys_link(const char * oldname, const char * newname)
{
struct dir_entry * de;
struct m_inode * oldinode, * dir;
struct buffer_head * bh;
const char * basename;
int namelen;
oldinode=namei(oldname);
if (!oldinode)
return -ENOENT;
if (S_ISDIR(oldinode->i_mode)) {
iput(oldinode);
return -EPERM;
}
dir = dir_namei(newname,&namelen,&basename);
if (!dir) {
iput(oldinode);
return -EACCES;
}
if (!namelen) {
iput(oldinode);
iput(dir);
return -EPERM;
}
if (dir->i_dev != oldinode->i_dev) {
iput(dir);
iput(oldinode);
return -EXDEV;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
iput(oldinode);
return -EACCES;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
iput(oldinode);
return -EEXIST;
}
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
iput(oldinode);
return -ENOSPC;
}
de->inode = oldinode->i_num;
bh->b_dirt = 1;
brelse(bh);
iput(dir);
oldinode->i_nlinks++;
oldinode->i_ctime = CURRENT_TIME;
oldinode->i_dirt = 1;
iput(oldinode);
return 0;
}
@
1.1
log
@Initial revision
@
text
@d43 1
a43 3
if (!(current->uid && current->euid))
mode=0777;
else if (current->uid==inode->i_uid || current->euid==inode->i_uid)
d45 1
a45 1
else if (current->gid==inode->i_gid || current->egid==inode->i_gid)
d47 3
a49 1
return mode & mask & 0007;
d371 1
d397 1
a397 1
permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) {
d416 1
a416 1
if (current->euid && current->uid)
d467 1
a467 1
if (current->euid && current->uid)
d591 1
a591 1
if (current->euid && current->uid)
d683 13
@
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.12.01.09.22.10; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.21.09.38.53; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Patches sent to Linus
@
text
@/*
* linux/fs/open.c
*
* (C) 1991 Linus Torvalds
*/
#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/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
int sys_ustat(int dev, struct ustat * ubuf)
{
return -ENOSYS;
}
int sys_utime(char * filename, struct utimbuf * times)
{
struct m_inode * inode;
long actime,modtime;
if (!(inode=namei(filename)))
return -ENOENT;
if (times) {
actime = get_fs_long((unsigned long *) &times->actime);
modtime = get_fs_long((unsigned long *) &times->modtime);
} else
actime = modtime = CURRENT_TIME;
inode->i_atime = actime;
inode->i_mtime = modtime;
inode->i_dirt = 1;
iput(inode);
return 0;
}
/*
* XXX should we use the real or effective uid? BSD uses the real uid,
* so as to make this call useful to setuid programs.
*/
int sys_access(const char * filename,int mode)
{
struct m_inode * inode;
int res, i_mode;
mode &= 0007;
if (!(inode=namei(filename)))
return -EACCES;
i_mode = res = inode->i_mode & 0777;
iput(inode);
if (current->uid == inode->i_uid)
res >>= 6;
else if (current->gid == inode->i_gid)
res >>= 6;
if ((res & 0007 & mode) == mode)
return 0;
/*
* XXX we are doing this test last because we really should be
* swapping the effective with the real user id (temporarily),
* and then calling suser() routine. If we do call the
* suser() routine, it needs to be called last.
*/
if ((!current->uid) &&
(!(mode & 1) || (i_mode & 0111)))
return 0;
return -EACCES;
}
int sys_chdir(const char * filename)
{
struct m_inode * inode;
if (!(inode = namei(filename)))
return -ENOENT;
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
return -ENOTDIR;
}
iput(current->pwd);
current->pwd = inode;
return (0);
}
int sys_chroot(const char * filename)
{
struct m_inode * inode;
if (!(inode=namei(filename)))
return -ENOENT;
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
return -ENOTDIR;
}
iput(current->root);
current->root = inode;
return (0);
}
int sys_chmod(const char * filename,int mode)
{
struct m_inode * inode;
if (!(inode=namei(filename)))
return -ENOENT;
if ((current->euid != inode->i_uid) && !suser()) {
iput(inode);
return -EACCES;
}
inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
inode->i_dirt = 1;
iput(inode);
return 0;
}
int sys_chown(const char * filename,int uid,int gid)
{
struct m_inode * inode;
if (!(inode=namei(filename)))
return -ENOENT;
if (!suser()) {
iput(inode);
return -EACCES;
}
inode->i_uid=uid;
inode->i_gid=gid;
inode->i_dirt=1;
iput(inode);
return 0;
}
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
struct file * f;
int i,fd;
mode &= 0777 & ~current->umask;
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])
break;
if (fd>=NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
f=0+file_table;
for (i=0 ; i<NR_FILE ; i++,f++)
if (!f->f_count) break;
if (i>=NR_FILE)
return -EINVAL;
(current->filp[fd]=f)->f_count++;
if ((i=open_namei(filename,flag,mode,&inode))<0) {
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
/* 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;
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode;
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
return (fd);
}
int sys_creat(const char * pathname, int mode)
{
return sys_open(pathname, O_CREAT | O_TRUNC, mode);
}
int sys_close(unsigned int fd)
{
struct file * filp;
if (fd >= NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
if (!(filp = current->filp[fd]))
return -EINVAL;
current->filp[fd] = NULL;
if (filp->f_count == 0)
panic("Close: file count is 0");
if (--filp->f_count)
return (0);
iput(filp->f_inode);
return (0);
}
@
1.1
log
@Initial revision
@
text
@d43 4
d50 1
a50 1
int res;
d55 1
a55 1
res = inode->i_mode & 0777;
d57 1
a57 6
if (!(current->euid && current->uid))
if (res & 0111)
res = 0777;
else
res = 0666;
if (current->euid == inode->i_uid)
d59 1
a59 1
else if (current->egid == inode->i_gid)
d63 9
d111 4
a114 6
if (current->uid && current->euid)
if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) {
iput(inode);
return -EACCES;
} else
mode = (mode & 0777) | (inode->i_mode & 07000);
d127 1
a127 1
if (current->uid && current->euid) {
@
/*
* linux/fs/bitmap.c
*
* (C) 1991 Linus Torvalds
*/
/* bitmap.c contains the code that handles the inode and block bitmaps */ /* bitmap.c contains the code that handles the inode and block bitmaps */
#include <string.h> #include <string.h>
......
/*
* linux/fs/block_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <linux/fs.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/segment.h> #include <asm/segment.h>
#include <asm/system.h>
#define NR_BLK_DEV ((sizeof (rd_blk))/(sizeof (rd_blk[0])))
int block_write(int dev, long * pos, char * buf, int count) int block_write(int dev, long * pos, char * buf, int count)
{ {
int block = *pos / BLOCK_SIZE; int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos % BLOCK_SIZE; int offset = *pos & (BLOCK_SIZE-1);
int chars; int chars;
int written = 0; int written = 0;
struct buffer_head * bh; struct buffer_head * bh;
register char * p; register char * p;
while (count>0) { while (count>0) {
bh = bread(dev,block); chars = BLOCK_SIZE - offset;
if (chars > count)
chars=count;
if (chars == BLOCK_SIZE)
bh = getblk(dev,block);
else
bh = breada(dev,block,block+1,block+2,-1);
block++;
if (!bh) if (!bh)
return written?written:-EIO; return written?written:-EIO;
chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE;
p = offset + bh->b_data; p = offset + bh->b_data;
offset = 0; offset = 0;
block++;
*pos += chars; *pos += chars;
written += chars; written += chars;
count -= chars; count -= chars;
...@@ -36,51 +46,28 @@ int block_write(int dev, long * pos, char * buf, int count) ...@@ -36,51 +46,28 @@ int block_write(int dev, long * pos, char * buf, int count)
int block_read(int dev, unsigned long * pos, char * buf, int count) int block_read(int dev, unsigned long * pos, char * buf, int count)
{ {
int block = *pos / BLOCK_SIZE; int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos % BLOCK_SIZE; int offset = *pos & (BLOCK_SIZE-1);
int chars; int chars;
int read = 0; int read = 0;
struct buffer_head * bh; struct buffer_head * bh;
register char * p; register char * p;
while (count>0) { while (count>0) {
bh = bread(dev,block); chars = BLOCK_SIZE-offset;
if (!bh) if (chars > count)
chars = count;
if (!(bh = breada(dev,block,block+1,block+2,-1)))
return read?read:-EIO; return read?read:-EIO;
chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE; block++;
p = offset + bh->b_data; p = offset + bh->b_data;
offset = 0; offset = 0;
block++;
*pos += chars; *pos += chars;
read += chars; read += chars;
count -= chars; count -= chars;
while (chars-->0) while (chars-->0)
put_fs_byte(*(p++),buf++); put_fs_byte(*(p++),buf++);
bh->b_dirt = 1;
brelse(bh); brelse(bh);
} }
return read; return read;
} }
extern void rw_hd(int rw, struct buffer_head * bh);
typedef void (*blk_fn)(int rw, struct buffer_head * bh);
static blk_fn rd_blk[]={
NULL, /* nodev */
NULL, /* dev mem */
NULL, /* dev fd */
rw_hd, /* dev hd */
NULL, /* dev ttyx */
NULL, /* dev tty */
NULL}; /* dev lp */
void ll_rw_block(int rw, struct buffer_head * bh)
{
blk_fn blk_addr;
unsigned int major;
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_addr=rd_blk[major]))
panic("Trying to read nonexistent block-device");
blk_addr(rw, bh);
}
/*
* linux/fs/buffer.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* 'buffer.c' implements the buffer-cache functions. Race-conditions have * 'buffer.c' implements the buffer-cache functions. Race-conditions have
* been avoided by NEVER letting a interrupt change a buffer (except for the * been avoided by NEVER letting a interrupt change a buffer (except for the
...@@ -6,18 +12,19 @@ ...@@ -6,18 +12,19 @@
* sleep-on-calls. These should be extremely quick, though (I hope). * sleep-on-calls. These should be extremely quick, though (I hope).
*/ */
/*
* NOTE! There is one discordant note here: checking floppies for
* disk change. This is where it fits best, I think, as it should
* invalidate changed floppy-disk-caches.
*/
#include <stdarg.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h>
#if (BUFFER_END & 0xfff)
#error "Bad BUFFER_END value"
#endif
#if (BUFFER_END > 0xA0000 && BUFFER_END <= 0x100000)
#error "Bad BUFFER_END value"
#endif
extern int end; extern int end;
struct buffer_head * start_buffer = (struct buffer_head *) &end; struct buffer_head * start_buffer = (struct buffer_head *) &end;
...@@ -49,7 +56,7 @@ int sys_sync(void) ...@@ -49,7 +56,7 @@ int sys_sync(void)
return 0; return 0;
} }
static int sync_dev(int dev) int sync_dev(int dev)
{ {
int i; int i;
struct buffer_head * bh; struct buffer_head * bh;
...@@ -59,12 +66,59 @@ static int sync_dev(int dev) ...@@ -59,12 +66,59 @@ static int sync_dev(int dev)
if (bh->b_dev != dev) if (bh->b_dev != dev)
continue; continue;
wait_on_buffer(bh); wait_on_buffer(bh);
if (bh->b_dirt) if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh);
}
sync_inodes();
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if (bh->b_dev != dev)
continue;
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_dirt)
ll_rw_block(WRITE,bh); ll_rw_block(WRITE,bh);
} }
return 0; return 0;
} }
/*
* This routine checks whether a floppy has been changed, and
* invalidates all buffer-cache-entries in that case. This
* is a relatively slow routine, so we have to try to minimize using
* it. Thus it is called only upon a 'mount' or 'open'. This
* is the best way of combining speed and utility, I think.
* People changing diskettes in the middle of an operation deserve
* to loose :-)
*
* NOTE! Although currently this is only for floppies, the idea is
* that any additional removable block-device will use this routine,
* and that mount/open needn't know that floppies/whatever are
* special.
*/
void check_disk_change(int dev)
{
int i;
struct buffer_head * bh;
if (MAJOR(dev) != 2)
return;
dev=MINOR(dev) & 0x03; /* which floppy is it? */
if (!floppy_change(dev))
return;
dev |= 0x200;
for (i=0 ; i<NR_SUPER ; i++)
if ((super_block[i].s_dev & 0xff03)==dev)
put_super(super_block[i].s_dev);
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
if ((bh->b_dev & 0xff03) != dev)
continue;
wait_on_buffer(bh);
if ((bh->b_dev & 0xff03) == dev)
bh->b_uptodate = bh->b_dirt = 0;
}
}
#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH) #define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
#define hash(dev,block) hash_table[_hashfn(dev,block)] #define hash(dev,block) hash_table[_hashfn(dev,block)]
...@@ -124,73 +178,70 @@ struct buffer_head * get_hash_table(int dev, int block) ...@@ -124,73 +178,70 @@ struct buffer_head * get_hash_table(int dev, int block)
{ {
struct buffer_head * bh; struct buffer_head * bh;
repeat: for (;;) {
if (!(bh=find_buffer(dev,block))) if (!(bh=find_buffer(dev,block)))
return NULL; return NULL;
bh->b_count++; bh->b_count++;
wait_on_buffer(bh); wait_on_buffer(bh);
if (bh->b_dev != dev || bh->b_blocknr != block) { if (bh->b_dev == dev && bh->b_blocknr == block)
brelse(bh); return bh;
goto repeat; bh->b_count--;
} }
return bh;
} }
/* /*
* Ok, this is getblk, and it isn't very clear, again to hinder * Ok, this is getblk, and it isn't very clear, again to hinder
* race-conditions. Most of the code is seldom used, (ie repeating), * race-conditions. Most of the code is seldom used, (ie repeating),
* so it should be much more efficient than it looks. * so it should be much more efficient than it looks.
*
* The algoritm is changed: better, and an elusive bug removed.
* LBT 11.11.91
*/ */
#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
struct buffer_head * getblk(int dev,int block) struct buffer_head * getblk(int dev,int block)
{ {
struct buffer_head * tmp; struct buffer_head * tmp, * bh;
repeat: repeat:
if (tmp=get_hash_table(dev,block)) if (bh = get_hash_table(dev,block))
return tmp; return bh;
tmp = free_list; tmp = free_list;
do { do {
if (!tmp->b_count) { if (tmp->b_count)
wait_on_buffer(tmp); /* we still have to wait */ continue;
if (!tmp->b_count) /* on it, it might be dirty */ if (!bh || BADNESS(tmp)<BADNESS(bh)) {
bh = tmp;
if (!BADNESS(tmp))
break; break;
} }
tmp = tmp->b_next_free; } while ((tmp = tmp->b_next_free) != free_list);
} while (tmp != free_list || (tmp=NULL)); if (!bh) {
/* Kids, don't try THIS at home ^^^^^. Magic */
if (!tmp) {
printk("Sleeping on free buffer ..");
sleep_on(&buffer_wait); sleep_on(&buffer_wait);
printk("ok\n");
goto repeat; goto repeat;
} }
tmp->b_count++; wait_on_buffer(bh);
remove_from_queues(tmp); if (bh->b_count)
/*
* Now, when we know nobody can get to this node (as it's removed from the
* free list), we write it out. We can sleep here without fear of race-
* conditions.
*/
if (tmp->b_dirt)
sync_dev(tmp->b_dev);
/* update buffer contents */
tmp->b_dev=dev;
tmp->b_blocknr=block;
tmp->b_dirt=0;
tmp->b_uptodate=0;
/* NOTE!! While we possibly slept in sync_dev(), somebody else might have
* added "this" block already, so check for that. Thank God for goto's.
*/
if (find_buffer(dev,block)) {
tmp->b_dev=0; /* ok, someone else has beaten us */
tmp->b_blocknr=0; /* to it - free this block and */
tmp->b_count=0; /* try again */
insert_into_queues(tmp);
goto repeat; goto repeat;
while (bh->b_dirt) {
sync_dev(bh->b_dev);
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
} }
/* and then insert into correct position */ /* NOTE!! While we slept waiting for this block, somebody else might */
insert_into_queues(tmp); /* already have added "this" block to the cache. check it */
return tmp; if (find_buffer(dev,block))
goto repeat;
/* OK, FINALLY we know that this buffer is the only one of it's kind, */
/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
bh->b_count=1;
bh->b_dirt=0;
bh->b_uptodate=0;
remove_from_queues(bh);
bh->b_dev=dev;
bh->b_blocknr=block;
insert_into_queues(bh);
return bh;
} }
void brelse(struct buffer_head * buf) void brelse(struct buffer_head * buf)
...@@ -216,18 +267,54 @@ struct buffer_head * bread(int dev,int block) ...@@ -216,18 +267,54 @@ struct buffer_head * bread(int dev,int block)
if (bh->b_uptodate) if (bh->b_uptodate)
return bh; return bh;
ll_rw_block(READ,bh); ll_rw_block(READ,bh);
wait_on_buffer(bh);
if (bh->b_uptodate)
return bh;
brelse(bh);
return NULL;
}
/*
* Ok, breada can be used as bread, but additionally to mark other
* blocks for reading as well. End the argument list with a negative
* number.
*/
struct buffer_head * breada(int dev,int first, ...)
{
va_list args;
struct buffer_head * bh, *tmp;
va_start(args,first);
if (!(bh=getblk(dev,first)))
panic("bread: getblk returned NULL\n");
if (!bh->b_uptodate)
ll_rw_block(READ,bh);
while ((first=va_arg(args,int))>=0) {
tmp=getblk(dev,first);
if (tmp) {
if (!tmp->b_uptodate)
ll_rw_block(READA,bh);
tmp->b_count--;
}
}
va_end(args);
wait_on_buffer(bh);
if (bh->b_uptodate) if (bh->b_uptodate)
return bh; return bh;
brelse(bh); brelse(bh);
return (NULL); return (NULL);
} }
void buffer_init(void) void buffer_init(long buffer_end)
{ {
struct buffer_head * h = start_buffer; struct buffer_head * h = start_buffer;
void * b = (void *) BUFFER_END; void * b;
int i; int i;
if (buffer_end == 1<<20)
b = (void *) (640*1024);
else
b = (void *) buffer_end;
while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
h->b_dev = 0; h->b_dev = 0;
h->b_dirt = 0; h->b_dirt = 0;
......
/*
* linux/fs/char_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <sys/types.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/io.h>
extern int tty_read(unsigned minor,char * buf,int count); extern int tty_read(unsigned minor,char * buf,int count);
extern int tty_write(unsigned minor,char * buf,int count); extern int tty_write(unsigned minor,char * buf,int count);
static int rw_ttyx(int rw,unsigned minor,char * buf,int count); typedef (*crw_ptr)(int rw,unsigned minor,char * buf,int count,off_t * pos);
static int rw_tty(int rw,unsigned minor,char * buf,int count);
static int rw_ttyx(int rw,unsigned minor,char * buf,int count,off_t * pos)
{
return ((rw==READ)?tty_read(minor,buf,count):
tty_write(minor,buf,count));
}
static int rw_tty(int rw,unsigned minor,char * buf,int count, off_t * pos)
{
if (current->tty<0)
return -EPERM;
return rw_ttyx(rw,current->tty,buf,count,pos);
}
static int rw_ram(int rw,char * buf, int count, off_t *pos)
{
return -EIO;
}
static int rw_mem(int rw,char * buf, int count, off_t * pos)
{
return -EIO;
}
typedef (*crw_ptr)(int rw,unsigned minor,char * buf,int count); static int rw_kmem(int rw,char * buf, int count, off_t * pos)
{
return -EIO;
}
static int rw_port(int rw,char * buf, int count, off_t * pos)
{
int i=*pos;
while (count-->0 && i<65536) {
if (rw==READ)
put_fs_byte(inb(i),buf++);
else
outb(get_fs_byte(buf++),i);
i++;
}
i -= *pos;
*pos += i;
return i;
}
static int rw_memory(int rw, unsigned minor, char * buf, int count, off_t * pos)
{
switch(minor) {
case 0:
return rw_ram(rw,buf,count,pos);
case 1:
return rw_mem(rw,buf,count,pos);
case 2:
return rw_kmem(rw,buf,count,pos);
case 3:
return (rw==READ)?0:count; /* rw_null */
case 4:
return rw_port(rw,buf,count,pos);
default:
return -EIO;
}
}
#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr))) #define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr)))
static crw_ptr crw_table[]={ static crw_ptr crw_table[]={
NULL, /* nodev */ NULL, /* nodev */
NULL, /* /dev/mem */ rw_memory, /* /dev/mem etc */
NULL, /* /dev/fd */ NULL, /* /dev/fd */
NULL, /* /dev/hd */ NULL, /* /dev/hd */
rw_ttyx, /* /dev/ttyx */ rw_ttyx, /* /dev/ttyx */
...@@ -23,20 +92,7 @@ static crw_ptr crw_table[]={ ...@@ -23,20 +92,7 @@ static crw_ptr crw_table[]={
NULL, /* /dev/lp */ NULL, /* /dev/lp */
NULL}; /* unnamed pipes */ NULL}; /* unnamed pipes */
static int rw_ttyx(int rw,unsigned minor,char * buf,int count) int rw_char(int rw,int dev, char * buf, int count, off_t * pos)
{
return ((rw==READ)?tty_read(minor,buf,count):
tty_write(minor,buf,count));
}
static int rw_tty(int rw,unsigned minor,char * buf,int count)
{
if (current->tty<0)
return -EPERM;
return rw_ttyx(rw,current->tty,buf,count);
}
int rw_char(int rw,int dev, char * buf, int count)
{ {
crw_ptr call_addr; crw_ptr call_addr;
...@@ -46,5 +102,5 @@ int rw_char(int rw,int dev, char * buf, int count) ...@@ -46,5 +102,5 @@ int rw_char(int rw,int dev, char * buf, int count)
printk("dev: %04x\n",dev); printk("dev: %04x\n",dev);
panic("Trying to r/w from/to nonexistent character device"); panic("Trying to r/w from/to nonexistent character device");
} }
return call_addr(rw,MINOR(dev),buf,count); return call_addr(rw,MINOR(dev),buf,count,pos);
} }
/*
* linux/fs/exec.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <a.out.h> #include <a.out.h>
...@@ -158,36 +165,66 @@ static int count(char ** argv) ...@@ -158,36 +165,66 @@ static int count(char ** argv)
* 'copy_string()' copies argument/envelope strings from user * 'copy_string()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready * memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory. * to be put directly into the top of new user memory.
*
* Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
* whether the string and the string array are from user or kernel segments:
*
* from_kmem argv * argv **
* 0 user space user space
* 1 kernel space user space
* 2 kernel space kernel space
*
* We do this by playing games with the fs segment register. Since it
* it is expensive to load a segment register, we try to avoid calling
* set_fs() unless we absolutely have to.
*/ */
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
unsigned long p) unsigned long p, int from_kmem)
{ {
int len,i; char *tmp, *pag;
char *tmp; int len, offset = 0;
unsigned long old_fs, new_fs;
if (!p)
return 0; /* bullet-proofing */
new_fs = get_ds();
old_fs = get_fs();
if (from_kmem==2)
set_fs(new_fs);
while (argc-- > 0) { while (argc-- > 0) {
if (!(tmp = (char *)get_fs_long(((unsigned long *) argv)+argc))) if (from_kmem == 1)
set_fs(new_fs);
if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
panic("argc is wrong"); panic("argc is wrong");
if (from_kmem == 1)
set_fs(old_fs);
len=0; /* remember zero-padding */ len=0; /* remember zero-padding */
do { do {
len++; len++;
} while (get_fs_byte(tmp++)); } while (get_fs_byte(tmp++));
if (p-len < 0) /* this shouldn't happen - 128kB */ if (p-len < 0) { /* this shouldn't happen - 128kB */
set_fs(old_fs);
return 0; return 0;
i = ((unsigned) (p-len)) >> 12;
while (i<MAX_ARG_PAGES && !page[i]) {
if (!(page[i]=get_free_page()))
return 0;
i++;
} }
do { while (len) {
--p; --p; --tmp; --len;
if (!page[p/PAGE_SIZE]) if (--offset < 0) {
panic("nonexistent page in exec.c"); offset = p % PAGE_SIZE;
((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] = if (from_kmem==2)
get_fs_byte(--tmp); set_fs(old_fs);
} while (--len); if (!(pag = (char *) page[p/PAGE_SIZE]) &&
!(pag = (char *) page[p/PAGE_SIZE] =
(unsigned long *) get_free_page()))
return 0;
if (from_kmem==2)
set_fs(new_fs);
}
*(pag + offset) = get_fs_byte(tmp);
}
} }
if (from_kmem==2)
set_fs(old_fs);
return p; return p;
} }
...@@ -227,7 +264,11 @@ int do_execve(unsigned long * eip,long tmp,char * filename, ...@@ -227,7 +264,11 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
struct exec ex; struct exec ex;
unsigned long page[MAX_ARG_PAGES]; unsigned long page[MAX_ARG_PAGES];
int i,argc,envc; int i,argc,envc;
unsigned long p; int e_uid, e_gid;
int retval;
int sh_bang = 0;
char *buf = 0;
unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
if ((0xffff & eip[1]) != 0x000f) if ((0xffff & eip[1]) != 0x000f)
panic("execve called from supervisor mode"); panic("execve called from supervisor mode");
...@@ -235,49 +276,130 @@ int do_execve(unsigned long * eip,long tmp,char * filename, ...@@ -235,49 +276,130 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
page[i]=0; page[i]=0;
if (!(inode=namei(filename))) /* get executables inode */ if (!(inode=namei(filename))) /* get executables inode */
return -ENOENT; return -ENOENT;
argc = count(argv);
envc = count(envp);
restart_interp:
if (!S_ISREG(inode->i_mode)) { /* must be regular file */ if (!S_ISREG(inode->i_mode)) { /* must be regular file */
iput(inode); retval = -EACCES;
return -EACCES; goto exec_error2;
} }
i = inode->i_mode; i = inode->i_mode;
if (current->uid && current->euid) { e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
if (current->euid == inode->i_uid) e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
i >>= 6; if (current->euid == inode->i_uid)
else if (current->egid == inode->i_gid) i >>= 6;
i >>= 3; else if (current->egid == inode->i_gid)
} else if (i & 0111) i >>= 3;
i=1; if (!(i & 1) &&
if (!(i & 1)) { !((inode->i_mode & 0111) && suser())) {
iput(inode); retval = -ENOEXEC;
return -ENOEXEC; goto exec_error2;
} }
if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
iput(inode); retval = -EACCES;
return -EACCES; goto exec_error2;
} }
ex = *((struct exec *) bh->b_data); /* read exec-header */ ex = *((struct exec *) bh->b_data); /* read exec-header */
if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {
/*
* This section does the #! interpretation.
* Sorta complicated, but hopefully it will work. -TYT
*/
char *cp, *interp, *i_name, *i_arg;
unsigned long old_fs;
if (!buf)
buf = malloc(1024);
strncpy(buf, bh->b_data+2, 1022);
brelse(bh);
iput(inode);
buf[1022] = '\0';
if (cp = strchr(buf, '\n')) {
*cp = '\0';
for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
}
if (!cp || *cp == '\0') {
retval = -ENOEXEC; /* No interpreter name found */
goto exec_error1;
}
interp = i_name = cp;
i_arg = 0;
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
if (*cp == '/')
i_name = cp+1;
}
if (*cp) {
*cp++ = '\0';
i_arg = cp;
}
/*
* OK, we've parsed out the interpreter name and
* (optional) argument.
*/
if (sh_bang++ == 0) {
p = copy_strings(envc, envp, page, p, 0);
p = copy_strings(--argc, argv+1, page, p, 0);
}
/*
* Splice in (1) the interpreter's name for argv[0]
* (2) (optional) argument to interpreter
* (3) filename of shell script
*
* This is done in reverse order, because of how the
* user environment and arguments are stored.
*/
p = copy_strings(1, &filename, page, p, 1);
argc++;
if (i_arg) {
p = copy_strings(1, &i_arg, page, p, 2);
argc++;
}
p = copy_strings(1, &i_name, page, p, 2);
argc++;
if (!p) {
retval = -ENOMEM;
goto exec_error1;
}
/*
* OK, now restart the process with the interpreter's inode.
*/
old_fs = get_fs();
set_fs(get_ds());
if (!(inode=namei(interp))) { /* get executables inode */
set_fs(old_fs);
retval = -ENOENT;
goto exec_error1;
}
set_fs(old_fs);
goto restart_interp;
}
brelse(bh); brelse(bh);
if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
ex.a_text+ex.a_data+ex.a_bss>0x3000000 || ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
iput(inode); retval = -ENOEXEC;
return -ENOEXEC; goto exec_error2;
} }
if (N_TXTOFF(ex) != BLOCK_SIZE) if (N_TXTOFF(ex) != BLOCK_SIZE) {
panic("N_TXTOFF != BLOCK_SIZE. See a.out.h."); printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
argc = count(argv); retval = -ENOEXEC;
envc = count(envp); goto exec_error2;
p = copy_strings(envc,envp,page,PAGE_SIZE*MAX_ARG_PAGES-4); }
p = copy_strings(argc,argv,page,p); if (!sh_bang) {
if (!p) { p = copy_strings(envc,envp,page,p,0);
for (i=0 ; i<MAX_ARG_PAGES ; i++) p = copy_strings(argc,argv,page,p,0);
free_page(page[i]); if (!p) {
iput(inode); retval = -ENOMEM;
return -1; goto exec_error2;
}
} }
/* OK, This is the point of no return */ /* OK, This is the point of no return */
if (buf)
free_s(buf, 1024);
for (i=0 ; i<32 ; i++) for (i=0 ; i<32 ; i++)
current->sig_fn[i] = NULL; current->sigaction[i].sa_handler = NULL;
for (i=0 ; i<NR_OPEN ; i++) for (i=0 ; i<NR_OPEN ; i++)
if ((current->close_on_exec>>i)&1) if ((current->close_on_exec>>i)&1)
sys_close(i); sys_close(i);
...@@ -293,6 +415,8 @@ int do_execve(unsigned long * eip,long tmp,char * filename, ...@@ -293,6 +415,8 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
(current->end_data = ex.a_data + (current->end_data = ex.a_data +
(current->end_code = ex.a_text)); (current->end_code = ex.a_text));
current->start_stack = p & 0xfffff000; current->start_stack = p & 0xfffff000;
current->euid = e_uid;
current->egid = e_gid;
i = read_area(inode,ex.a_text+ex.a_data); i = read_area(inode,ex.a_text+ex.a_data);
iput(inode); iput(inode);
if (i<0) if (i<0)
...@@ -303,4 +427,12 @@ int do_execve(unsigned long * eip,long tmp,char * filename, ...@@ -303,4 +427,12 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
eip[0] = ex.a_entry; /* eip, magic happens :-) */ eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */ eip[3] = p; /* stack pointer */
return 0; return 0;
exec_error2:
iput(inode);
exec_error1:
if (buf)
free(buf);
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(page[i]);
return(retval);
} }
/*
* linux/fs/exec.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <a.out.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/segment.h>
extern int sys_exit(int exit_code);
extern int sys_close(int fd);
/*
* MAX_ARG_PAGES defines the number of pages allocated for arguments
* and envelope for the new program. 32 should suffice, this gives
* a maximum env+arg of 128kB !
*/
#define MAX_ARG_PAGES 32
#define cp_block(from,to) \
__asm__("pushl $0x10\n\t" \
"pushl $0x17\n\t" \
"pop %%es\n\t" \
"cld\n\t" \
"rep\n\t" \
"movsl\n\t" \
"pop %%es" \
::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
:"cx","di","si")
/*
* read_head() reads blocks 1-6 (not 0). Block 0 has already been
* read for header information.
*/
int read_head(struct m_inode * inode,int blocks)
{
struct buffer_head * bh;
int count;
if (blocks>6)
blocks=6;
for(count = 0 ; count<blocks ; count++) {
if (!inode->i_zone[count+1])
continue;
if (!(bh=bread(inode->i_dev,inode->i_zone[count+1])))
return -1;
cp_block(bh->b_data,count*BLOCK_SIZE);
brelse(bh);
}
return 0;
}
int read_ind(int dev,int ind,long size,unsigned long offset)
{
struct buffer_head * ih, * bh;
unsigned short * table,block;
if (size<=0)
panic("size<=0 in read_ind");
if (size>512*BLOCK_SIZE)
size=512*BLOCK_SIZE;
if (!ind)
return 0;
if (!(ih=bread(dev,ind)))
return -1;
table = (unsigned short *) ih->b_data;
while (size>0) {
if (block=*(table++))
if (!(bh=bread(dev,block))) {
brelse(ih);
return -1;
} else {
cp_block(bh->b_data,offset);
brelse(bh);
}
size -= BLOCK_SIZE;
offset += BLOCK_SIZE;
}
brelse(ih);
return 0;
}
/*
* read_area() reads an area into %fs:mem.
*/
int read_area(struct m_inode * inode,long size)
{
struct buffer_head * dind;
unsigned short * table;
int i,count;
if ((i=read_head(inode,(size+BLOCK_SIZE-1)/BLOCK_SIZE)) ||
(size -= BLOCK_SIZE*6)<=0)
return i;
if ((i=read_ind(inode->i_dev,inode->i_zone[7],size,BLOCK_SIZE*6)) ||
(size -= BLOCK_SIZE*512)<=0)
return i;
if (!(i=inode->i_zone[8]))
return 0;
if (!(dind = bread(inode->i_dev,i)))
return -1;
table = (unsigned short *) dind->b_data;
for(count=0 ; count<512 ; count++)
if ((i=read_ind(inode->i_dev,*(table++),size,
BLOCK_SIZE*(518+count))) || (size -= BLOCK_SIZE*512)<=0)
return i;
panic("Impossibly long executable");
}
/*
* create_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
* addresses on the "stack", returning the new stack pointer value.
*/
static unsigned long * create_tables(char * p,int argc,int envc)
{
unsigned long *argv,*envp;
unsigned long * sp;
sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
sp -= envc+1;
envp = sp;
sp -= argc+1;
argv = sp;
put_fs_long((unsigned long)envp,--sp);
put_fs_long((unsigned long)argv,--sp);
put_fs_long((unsigned long)argc,--sp);
while (argc-->0) {
put_fs_long((unsigned long) p,argv++);
while (get_fs_byte(p++)) /* nothing */ ;
}
put_fs_long(0,argv);
while (envc-->0) {
put_fs_long((unsigned long) p,envp++);
while (get_fs_byte(p++)) /* nothing */ ;
}
put_fs_long(0,envp);
return sp;
}
/*
* count() counts the number of arguments/envelopes
*/
static int count(char ** argv)
{
int i=0;
char ** tmp;
if (tmp = argv)
while (get_fs_long((unsigned long *) (tmp++)))
i++;
return i;
}
/*
* 'copy_string()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory.
*
* Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
* whether the string and the string array are from user or kernel segments:
*
* from_kmem argv * argv **
* 0 user space user space
* 1 kernel space user space
* 2 kernel space kernel space
*
* We do this by playing games with the fs segment register. Since it
* it is expensive to load a segment register, we try to avoid calling
* set_fs() unless we absolutely have to.
*/
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
unsigned long p, int from_kmem)
{
char *tmp, *pag;
int len, offset = 0;
unsigned long old_fs, new_fs;
if (!p)
return 0; /* bullet-proofing */
new_fs = get_ds();
old_fs = get_fs();
if (from_kmem==2)
set_fs(new_fs);
while (argc-- > 0) {
if (from_kmem == 1)
set_fs(new_fs);
if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
panic("argc is wrong");
if (from_kmem == 1)
set_fs(old_fs);
len=0; /* remember zero-padding */
do {
len++;
} while (get_fs_byte(tmp++));
if (p-len < 0) { /* this shouldn't happen - 128kB */
set_fs(old_fs);
return 0;
}
while (len) {
--p; --tmp; --len;
if (--offset < 0) {
offset = p % PAGE_SIZE;
if (from_kmem==2)
set_fs(old_fs);
if (!(pag = (char *) page[p/PAGE_SIZE]) &&
!(pag = (char *) page[p/PAGE_SIZE] =
(unsigned long *) get_free_page()))
return 0;
if (from_kmem==2)
set_fs(new_fs);
}
*(pag + offset) = get_fs_byte(tmp);
}
}
if (from_kmem==2)
set_fs(old_fs);
return p;
}
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_base = get_base(current->ldt[1]);
data_base = code_base;
set_base(current->ldt[1],code_base);
set_limit(current->ldt[1],code_limit);
set_base(current->ldt[2],data_base);
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;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
if (page[i])
put_page(page[i],data_base);
}
return data_limit;
}
/*
* 'do_execve()' executes a new program.
*/
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
{
struct m_inode * inode;
struct buffer_head * bh;
struct exec ex;
unsigned long page[MAX_ARG_PAGES];
int i,argc,envc;
int e_uid, e_gid;
int retval;
int sh_bang = 0;
unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
if ((0xffff & eip[1]) != 0x000f)
panic("execve called from supervisor mode");
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
page[i]=0;
if (!(inode=namei(filename))) /* get executables inode */
return -ENOENT;
argc = count(argv);
envc = count(envp);
restart_interp:
if (!S_ISREG(inode->i_mode)) { /* must be regular file */
retval = -EACCES;
goto exec_error2;
}
i = inode->i_mode;
e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
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)
i >>= 3;
if (!(i & 1) &&
!((inode->i_mode & 0111) && suser())) {
retval = -ENOEXEC;
goto exec_error2;
}
if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
retval = -EACCES;
goto exec_error2;
}
ex = *((struct exec *) bh->b_data); /* read exec-header */
if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {
/*
* This section does the #! interpretation.
* Sorta complicated, but hopefully it will work. -TYT
*/
char buf[1023], *cp, *interp, *i_name, *i_arg;
unsigned long old_fs;
strncpy(buf, bh->b_data+2, 1022);
brelse(bh);
iput(inode);
buf[1022] = '\0';
if (cp = strchr(buf, '\n')) {
*cp = '\0';
for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
}
if (!cp || *cp == '\0') {
retval = -ENOEXEC; /* No interpreter name found */
goto exec_error1;
}
interp = i_name = cp;
i_arg = 0;
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
if (*cp == '/')
i_name = cp+1;
}
if (*cp) {
*cp++ = '\0';
i_arg = cp;
}
/*
* OK, we've parsed out the interpreter name and
* (optional) argument.
*/
if (sh_bang++ == 0) {
p = copy_strings(envc, envp, page, p, 0);
p = copy_strings(--argc, argv+1, page, p, 0);
}
/*
* Splice in (1) the interpreter's name for argv[0]
* (2) (optional) argument to interpreter
* (3) filename of shell script
*
* This is done in reverse order, because of how the
* user environment and arguments are stored.
*/
p = copy_strings(1, &filename, page, p, 1);
argc++;
if (i_arg) {
p = copy_strings(1, &i_arg, page, p, 2);
argc++;
}
p = copy_strings(1, &i_name, page, p, 2);
argc++;
if (!p) {
retval = -ENOMEM;
goto exec_error1;
}
/*
* OK, now restart the process with the interpreter's inode.
*/
old_fs = get_fs();
set_fs(get_ds());
if (!(inode=namei(interp))) { /* get executables inode */
set_fs(old_fs);
retval = -ENOENT;
goto exec_error1;
}
set_fs(old_fs);
goto restart_interp;
}
brelse(bh);
if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
retval = -ENOEXEC;
goto exec_error2;
}
if (N_TXTOFF(ex) != BLOCK_SIZE) {
printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
retval = -ENOEXEC;
goto exec_error2;
}
if (!sh_bang) {
p = copy_strings(envc,envp,page,p,0);
p = copy_strings(argc,argv,page,p,0);
if (!p) {
retval = -ENOMEM;
goto exec_error2;
}
}
/* OK, This is the point of no return */
for (i=0 ; i<32 ; i++)
current->sigaction[i].sa_handler = NULL;
for (i=0 ; i<NR_OPEN ; i++)
if ((current->close_on_exec>>i)&1)
sys_close(i);
current->close_on_exec = 0;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
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 = (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 = read_area(inode,ex.a_text+ex.a_data);
iput(inode);
if (i<0)
sys_exit(-1);
i = ex.a_text+ex.a_data;
while (i&0xfff)
put_fs_byte(0,(char *) (i++));
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
return 0;
exec_error2:
iput(inode);
exec_error1:
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(page[i]);
return(retval);
}
/*
* linux/fs/exec.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <sys/stat.h>
#include <a.out.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/segment.h>
extern int sys_exit(int exit_code);
extern int sys_close(int fd);
/*
* MAX_ARG_PAGES defines the number of pages allocated for arguments
* and envelope for the new program. 32 should suffice, this gives
* a maximum env+arg of 128kB !
*/
#define MAX_ARG_PAGES 32
#define cp_block(from,to) \
__asm__("pushl $0x10\n\t" \
"pushl $0x17\n\t" \
"pop %%es\n\t" \
"cld\n\t" \
"rep\n\t" \
"movsl\n\t" \
"pop %%es" \
::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
:"cx","di","si")
/*
* read_head() reads blocks 1-6 (not 0). Block 0 has already been
* read for header information.
*/
int read_head(struct m_inode * inode,int blocks)
{
struct buffer_head * bh;
int count;
if (blocks>6)
blocks=6;
for(count = 0 ; count<blocks ; count++) {
if (!inode->i_zone[count+1])
continue;
if (!(bh=bread(inode->i_dev,inode->i_zone[count+1])))
return -1;
cp_block(bh->b_data,count*BLOCK_SIZE);
brelse(bh);
}
return 0;
}
int read_ind(int dev,int ind,long size,unsigned long offset)
{
struct buffer_head * ih, * bh;
unsigned short * table,block;
if (size<=0)
panic("size<=0 in read_ind");
if (size>512*BLOCK_SIZE)
size=512*BLOCK_SIZE;
if (!ind)
return 0;
if (!(ih=bread(dev,ind)))
return -1;
table = (unsigned short *) ih->b_data;
while (size>0) {
if (block=*(table++))
if (!(bh=bread(dev,block))) {
brelse(ih);
return -1;
} else {
cp_block(bh->b_data,offset);
brelse(bh);
}
size -= BLOCK_SIZE;
offset += BLOCK_SIZE;
}
brelse(ih);
return 0;
}
/*
* read_area() reads an area into %fs:mem.
*/
int read_area(struct m_inode * inode,long size)
{
struct buffer_head * dind;
unsigned short * table;
int i,count;
if ((i=read_head(inode,(size+BLOCK_SIZE-1)/BLOCK_SIZE)) ||
(size -= BLOCK_SIZE*6)<=0)
return i;
if ((i=read_ind(inode->i_dev,inode->i_zone[7],size,BLOCK_SIZE*6)) ||
(size -= BLOCK_SIZE*512)<=0)
return i;
if (!(i=inode->i_zone[8]))
return 0;
if (!(dind = bread(inode->i_dev,i)))
return -1;
table = (unsigned short *) dind->b_data;
for(count=0 ; count<512 ; count++)
if ((i=read_ind(inode->i_dev,*(table++),size,
BLOCK_SIZE*(518+count))) || (size -= BLOCK_SIZE*512)<=0)
return i;
panic("Impossibly long executable");
}
/*
* create_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
* addresses on the "stack", returning the new stack pointer value.
*/
static unsigned long * create_tables(char * p,int argc,int envc)
{
unsigned long *argv,*envp;
unsigned long * sp;
sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
sp -= envc+1;
envp = sp;
sp -= argc+1;
argv = sp;
put_fs_long((unsigned long)envp,--sp);
put_fs_long((unsigned long)argv,--sp);
put_fs_long((unsigned long)argc,--sp);
while (argc-->0) {
put_fs_long((unsigned long) p,argv++);
while (get_fs_byte(p++)) /* nothing */ ;
}
put_fs_long(0,argv);
while (envc-->0) {
put_fs_long((unsigned long) p,envp++);
while (get_fs_byte(p++)) /* nothing */ ;
}
put_fs_long(0,envp);
return sp;
}
/*
* count() counts the number of arguments/envelopes
*/
static int count(char ** argv)
{
int i=0;
char ** tmp;
if (tmp = argv)
while (get_fs_long((unsigned long *) (tmp++)))
i++;
return i;
}
/*
* 'copy_string()' copies argument/envelope strings from user
* memory to free pages in kernel mem. These are in a format ready
* to be put directly into the top of new user memory.
*/
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
unsigned long p)
{
int len,i;
char *tmp;
while (argc-- > 0) {
if (!(tmp = (char *)get_fs_long(((unsigned long *) argv)+argc)))
panic("argc is wrong");
len=0; /* remember zero-padding */
do {
len++;
} while (get_fs_byte(tmp++));
if (p-len < 0) /* this shouldn't happen - 128kB */
return 0;
i = ((unsigned) (p-len)) >> 12;
while (i<MAX_ARG_PAGES && !page[i]) {
if (!(page[i]=get_free_page()))
return 0;
i++;
}
do {
--p;
if (!page[p/PAGE_SIZE])
panic("nonexistent page in exec.c");
((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] =
get_fs_byte(--tmp);
} while (--len);
}
return p;
}
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_base = get_base(current->ldt[1]);
data_base = code_base;
set_base(current->ldt[1],code_base);
set_limit(current->ldt[1],code_limit);
set_base(current->ldt[2],data_base);
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;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
if (page[i])
put_page(page[i],data_base);
}
return data_limit;
}
/*
* 'do_execve()' executes a new program.
*/
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
{
struct m_inode * inode;
struct buffer_head * bh;
struct exec ex;
unsigned long page[MAX_ARG_PAGES];
int i,argc,envc;
int e_uid, e_gid;
unsigned long p;
int retval;
int interp = 0;
if ((0xffff & eip[1]) != 0x000f)
panic("execve called from supervisor mode");
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
page[i]=0;
if (!(inode=namei(filename))) { /* get executables inode */
retval = -ENOENT;
goto exec_error1;
}
argc = count(argv);
envc = count(envp);
restart_interp:
e_uid = current->euid; /* Note this means no setuid */
e_gid = current->egid; /* shell scripts! */
if (!S_ISREG(inode->i_mode)) { /* must be regular file */
retval = -EACCES;
goto exec_error2;
}
i = inode->i_mode;
if (i & S_ISUID)
e_uid = inode->i_uid;
if (i & S_ISGID)
e_gid = inode->i_gid;
if (current->euid == inode->i_uid)
i >>= 6;
else if (current->egid == inode->i_gid)
i >>= 3;
if (!(i & 1) &&
!((inode->i_mode & 111) && suser())) {
retval = -ENOEXEC;
goto exec_error2;
}
if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
retval = -EACCES;
goto exec_error2;
}
ex = *((struct exec *) bh->b_data); /* read exec-header */
if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (interp > 3)) {
/*
* This section does the #! interpretation.
* Gee, I hope the C compiler is intelligent about this,
* and only activates this sub-frame when necessary.
*/
char buf[1023], *cp, *interp, *i_name, *i_arg;
int len;
strncpy(buf, bh->b_data+2, 1022);
brelse(bh);
iput(inode);
buf[1022] = '\0';
if (!(cp = strchr(buf, '\n'))) {
retval = -ENOEXEC;
goto exec_error1;
}
*cp = '\0';
for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++) ;
if (*cp == '\0') {
retval = -ENOEXEC; /* No interpreter name found */
goto exec_error1;
}
interp = i_name = cp;
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
if (*cp == '/')
i_name = cp;
}
if (*cp) {
*cp++ = '\0';
i_arg = cp;
} else
i_arg = 0;
/*
* OK, we've parsed out the interpreter name and
* (optional) argument.
*/
if (interp++ == 0) {
p = copy_strings(envc,envp,page,
PAGE_SIZE*MAX_ARG_PAGES-4);
p = copy_strings(--argc,argv+1,page,p);
}
/*
* Copy in interpreter name
*/
len = strlen(i_name) + 1;
i = ((unsigned) (p-len)) >> 12;
while (i<MAX_ARG_PAGES && !page[i]) {
if (!(page[i]=get_free_page())) {
retval = -ENOMEM;
goto exec_error1;
}
i++;
}
do {
--p;
if (!page[p/PAGE_SIZE])
panic("nonexistent page in exec.c");
((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] =
i_name[--len];
} while (len);
/*
* OK, now restart the process with the interpreter's inode.
*/
if (!(inode=namei(interpreter))) { /* get executables inode */
retval = -ENOENT;
goto exec_error1;
}
goto restart_interp;
} else
brelse(bh);
if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
retval = -ENOEXEC;
goto exec_error2;
}
if (N_TXTOFF(ex) != BLOCK_SIZE) {
printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
retval = -ENOEXEC;
goto exec_error2;
}
if (!interp) {
p = copy_strings(envc,envp,page,PAGE_SIZE*MAX_ARG_PAGES-4);
p = copy_strings(argc,argv,page,p);
}
if (!p) {
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(page[i]);
retval = -ENOMEM;
goto exec_error2;
}
/* OK, This is the point of no return */
for (i=0 ; i<32 ; i++)
current->sigaction[i].sa_handler = NULL;
for (i=0 ; i<NR_OPEN ; i++)
if ((current->close_on_exec>>i)&1)
sys_close(i);
current->close_on_exec = 0;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
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 = (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 = read_area(inode,ex.a_text+ex.a_data);
iput(inode);
if (i<0)
sys_exit(-1);
i = ex.a_text+ex.a_data;
while (i&0xfff)
put_fs_byte(0,(char *) (i++));
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
return 0;
exec_error2:
iput(inode);
exec_error1:
for (i=0 ; i<MAX_ARG_PAGES ; i++)
free_page(page[i]);
return(retval);
}
/*
* linux/fs/fcntl.c
*
* (C) 1991 Linus Torvalds
*/
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <linux/sched.h> #include <linux/sched.h>
......
/*
* linux/fs/file_dev.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
......
/*
* linux/fs/file_table.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/fs.h> #include <linux/fs.h>
struct file file_table[NR_FILE]; struct file file_table[NR_FILE];
/*
* linux/fs/inode.c
*
* (C) 1991 Linus Torvalds
*/
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -146,6 +153,10 @@ void iput(struct m_inode * inode) ...@@ -146,6 +153,10 @@ void iput(struct m_inode * inode)
return; return;
} }
repeat: repeat:
if (S_ISBLK(inode->i_mode)) {
sync_dev(inode->i_zone[0]);
wait_on_inode(inode);
}
if (!inode->i_nlinks) { if (!inode->i_nlinks) {
truncate(inode); truncate(inode);
free_inode(inode); free_inode(inode);
...@@ -234,6 +245,24 @@ struct m_inode * iget(int dev,int nr) ...@@ -234,6 +245,24 @@ struct m_inode * iget(int dev,int nr)
continue; continue;
} }
inode->i_count++; inode->i_count++;
if (inode->i_mount) {
int i;
for (i = 0 ; i<NR_SUPER ; i++)
if (super_block[i].s_imount==inode)
break;
if (i >= NR_SUPER) {
printk("Mounted inode hasn't got sb\n");
if (empty)
iput(empty);
return inode;
}
iput(inode);
dev = super_block[i].s_dev;
nr = ROOT_INO;
inode = inode_table;
continue;
}
if (empty) if (empty)
iput(empty); iput(empty);
return inode; return inode;
......
/*
* linux/fs/ioctl.c
*
* (C) 1991 Linus Torvalds
*/
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
......
/*
* linux/fs/namei.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/segment.h> #include <asm/segment.h>
...@@ -34,13 +40,13 @@ static int permission(struct m_inode * inode,int mask) ...@@ -34,13 +40,13 @@ static int permission(struct m_inode * inode,int mask)
/* special case: not even root can read/write a deleted file */ /* special case: not even root can read/write a deleted file */
if (inode->i_dev && !inode->i_nlinks) if (inode->i_dev && !inode->i_nlinks)
return 0; return 0;
if (!(current->uid && current->euid)) else if (current->euid==inode->i_uid)
mode=0777;
else if (current->uid==inode->i_uid || current->euid==inode->i_uid)
mode >>= 6; mode >>= 6;
else if (current->gid==inode->i_gid || current->egid==inode->i_gid) else if (current->egid==inode->i_gid)
mode >>= 3; mode >>= 3;
return mode & mask & 0007; if (((mode & mask & 0007) == mask) || suser())
return 1;
return 0;
} }
/* /*
...@@ -74,14 +80,18 @@ static int match(int len,const char * name,struct dir_entry * de) ...@@ -74,14 +80,18 @@ static int match(int len,const char * name,struct dir_entry * de)
* returns the cache buffer in which the entry was found, and the entry * returns the cache buffer in which the entry was found, and the entry
* itself (as a parameter - res_dir). It does NOT read the inode of the * itself (as a parameter - res_dir). It does NOT read the inode of the
* entry - you'll have to do that yourself if you want to. * entry - you'll have to do that yourself if you want to.
*
* This also takes care of the few special cases due to '..'-traversal
* over a pseudo-root and a mount point.
*/ */
static struct buffer_head * find_entry(struct m_inode * dir, static struct buffer_head * find_entry(struct m_inode ** dir,
const char * name, int namelen, struct dir_entry ** res_dir) const char * name, int namelen, struct dir_entry ** res_dir)
{ {
int entries; int entries;
int block,i; int block,i;
struct buffer_head * bh; struct buffer_head * bh;
struct dir_entry * de; struct dir_entry * de;
struct super_block * sb;
#ifdef NO_TRUNCATE #ifdef NO_TRUNCATE
if (namelen > NAME_LEN) if (namelen > NAME_LEN)
...@@ -90,13 +100,29 @@ static struct buffer_head * find_entry(struct m_inode * dir, ...@@ -90,13 +100,29 @@ static struct buffer_head * find_entry(struct m_inode * dir,
if (namelen > NAME_LEN) if (namelen > NAME_LEN)
namelen = NAME_LEN; namelen = NAME_LEN;
#endif #endif
entries = dir->i_size / (sizeof (struct dir_entry)); entries = (*dir)->i_size / (sizeof (struct dir_entry));
*res_dir = NULL; *res_dir = NULL;
if (!namelen) if (!namelen)
return NULL; return NULL;
if (!(block = dir->i_zone[0])) /* 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) */
if ((*dir) == current->root)
namelen=1;
else if ((*dir)->i_num == ROOT_INO) {
/* '..' over a mount-point results in 'dir' being exchanged for the mounted
directory-inode. NOTE! We set mounted, so that we can iput the new dir */
sb=get_super((*dir)->i_dev);
if (sb->s_imount) {
iput(*dir);
(*dir)=sb->s_imount;
(*dir)->i_count++;
}
}
}
if (!(block = (*dir)->i_zone[0]))
return NULL; return NULL;
if (!(bh = bread(dir->i_dev,block))) if (!(bh = bread((*dir)->i_dev,block)))
return NULL; return NULL;
i = 0; i = 0;
de = (struct dir_entry *) bh->b_data; de = (struct dir_entry *) bh->b_data;
...@@ -104,8 +130,8 @@ static struct buffer_head * find_entry(struct m_inode * dir, ...@@ -104,8 +130,8 @@ static struct buffer_head * find_entry(struct m_inode * dir,
if ((char *)de >= BLOCK_SIZE+bh->b_data) { if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh); brelse(bh);
bh = NULL; bh = NULL;
if (!(block = bmap(dir,i/DIR_ENTRIES_PER_BLOCK)) || if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
!(bh = bread(dir->i_dev,block))) { !(bh = bread((*dir)->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK; i += DIR_ENTRIES_PER_BLOCK;
continue; continue;
} }
...@@ -226,7 +252,7 @@ static struct m_inode * get_dir(const char * pathname) ...@@ -226,7 +252,7 @@ static struct m_inode * get_dir(const char * pathname)
/* nothing */ ; /* nothing */ ;
if (!c) if (!c)
return inode; return inode;
if (!(bh = find_entry(inode,thisname,namelen,&de))) { if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
iput(inode); iput(inode);
return NULL; return NULL;
} }
...@@ -282,7 +308,7 @@ struct m_inode * namei(const char * pathname) ...@@ -282,7 +308,7 @@ struct m_inode * namei(const char * pathname)
return NULL; return NULL;
if (!namelen) /* special case: '/usr/' etc */ if (!namelen) /* special case: '/usr/' etc */
return dir; return dir;
bh = find_entry(dir,basename,namelen,&de); bh = find_entry(&dir,basename,namelen,&de);
if (!bh) { if (!bh) {
iput(dir); iput(dir);
return NULL; return NULL;
...@@ -327,7 +353,7 @@ int open_namei(const char * pathname, int flag, int mode, ...@@ -327,7 +353,7 @@ int open_namei(const char * pathname, int flag, int mode,
iput(dir); iput(dir);
return -EISDIR; return -EISDIR;
} }
bh = find_entry(dir,basename,namelen,&de); bh = find_entry(&dir,basename,namelen,&de);
if (!bh) { if (!bh) {
if (!(flag & O_CREAT)) { if (!(flag & O_CREAT)) {
iput(dir); iput(dir);
...@@ -342,6 +368,7 @@ int open_namei(const char * pathname, int flag, int mode, ...@@ -342,6 +368,7 @@ int open_namei(const char * pathname, int flag, int mode,
iput(dir); iput(dir);
return -ENOSPC; return -ENOSPC;
} }
inode->i_uid = current->euid;
inode->i_mode = mode; inode->i_mode = mode;
inode->i_dirt = 1; inode->i_dirt = 1;
bh = add_entry(dir,basename,namelen,&de); bh = add_entry(dir,basename,namelen,&de);
...@@ -367,7 +394,7 @@ int open_namei(const char * pathname, int flag, int mode, ...@@ -367,7 +394,7 @@ int open_namei(const char * pathname, int flag, int mode,
if (!(inode=iget(dev,inr))) if (!(inode=iget(dev,inr)))
return -EACCES; return -EACCES;
if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) || if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) { !permission(inode,ACC_MODE(flag))) {
iput(inode); iput(inode);
return -EPERM; return -EPERM;
} }
...@@ -378,6 +405,57 @@ int open_namei(const char * pathname, int flag, int mode, ...@@ -378,6 +405,57 @@ int open_namei(const char * pathname, int flag, int mode,
return 0; return 0;
} }
int sys_mknod(const char * filename, int mode, int dev)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(filename,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
return -EEXIST;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_mode = mode;
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_zone[0] = dev;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
inode->i_nlinks=0;
iput(inode);
return -ENOSPC;
}
de->inode = inode->i_num;
bh->b_dirt = 1;
iput(dir);
iput(inode);
brelse(bh);
return 0;
}
int sys_mkdir(const char * pathname, int mode) int sys_mkdir(const char * pathname, int mode)
{ {
const char * basename; const char * basename;
...@@ -386,7 +464,7 @@ int sys_mkdir(const char * pathname, int mode) ...@@ -386,7 +464,7 @@ int sys_mkdir(const char * pathname, int mode)
struct buffer_head * bh, *dir_block; struct buffer_head * bh, *dir_block;
struct dir_entry * de; struct dir_entry * de;
if (current->euid && current->uid) if (!suser())
return -EPERM; return -EPERM;
if (!(dir = dir_namei(pathname,&namelen,&basename))) if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT; return -ENOENT;
...@@ -398,7 +476,7 @@ int sys_mkdir(const char * pathname, int mode) ...@@ -398,7 +476,7 @@ int sys_mkdir(const char * pathname, int mode)
iput(dir); iput(dir);
return -EPERM; return -EPERM;
} }
bh = find_entry(dir,basename,namelen,&de); bh = find_entry(&dir,basename,namelen,&de);
if (bh) { if (bh) {
brelse(bh); brelse(bh);
iput(dir); iput(dir);
...@@ -510,7 +588,7 @@ int sys_rmdir(const char * name) ...@@ -510,7 +588,7 @@ int sys_rmdir(const char * name)
struct buffer_head * bh; struct buffer_head * bh;
struct dir_entry * de; struct dir_entry * de;
if (current->euid && current->uid) if (!suser())
return -EPERM; return -EPERM;
if (!(dir = dir_namei(name,&namelen,&basename))) if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT; return -ENOENT;
...@@ -518,7 +596,7 @@ int sys_rmdir(const char * name) ...@@ -518,7 +596,7 @@ int sys_rmdir(const char * name)
iput(dir); iput(dir);
return -ENOENT; return -ENOENT;
} }
bh = find_entry(dir,basename,namelen,&de); bh = find_entry(&dir,basename,namelen,&de);
if (!bh) { if (!bh) {
iput(dir); iput(dir);
return -ENOENT; return -ENOENT;
...@@ -584,7 +662,7 @@ int sys_unlink(const char * name) ...@@ -584,7 +662,7 @@ int sys_unlink(const char * name)
iput(dir); iput(dir);
return -EPERM; return -EPERM;
} }
bh = find_entry(dir,basename,namelen,&de); bh = find_entry(&dir,basename,namelen,&de);
if (!bh) { if (!bh) {
iput(dir); iput(dir);
return -ENOENT; return -ENOENT;
...@@ -596,12 +674,25 @@ int sys_unlink(const char * name) ...@@ -596,12 +674,25 @@ int sys_unlink(const char * name)
brelse(bh); brelse(bh);
return -ENOENT; return -ENOENT;
} }
if (!S_ISREG(inode->i_mode)) { if (S_ISDIR(inode->i_mode)) {
iput(inode); iput(inode);
iput(dir); iput(dir);
brelse(bh); brelse(bh);
return -EPERM; return -EPERM;
} }
/*
* If the directory has the sticky bit, the user must either
* own the file or own the directory or be the superuser to
* delete a file in that directory. This is typically used
* for /tmp and /usr/tmp.
*/
if ((dir->i_mode & S_ISVTX) && (current->euid != inode->i_uid) &&
(current->euid != dir->i_uid) && !suser()) {
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
if (!inode->i_nlinks) { if (!inode->i_nlinks) {
printk("Deleting nonexistent file (%04x:%d), %d\n", printk("Deleting nonexistent file (%04x:%d), %d\n",
inode->i_dev,inode->i_num,inode->i_nlinks); inode->i_dev,inode->i_num,inode->i_nlinks);
...@@ -629,7 +720,7 @@ int sys_link(const char * oldname, const char * newname) ...@@ -629,7 +720,7 @@ int sys_link(const char * oldname, const char * newname)
oldinode=namei(oldname); oldinode=namei(oldname);
if (!oldinode) if (!oldinode)
return -ENOENT; return -ENOENT;
if (!S_ISREG(oldinode->i_mode)) { if (S_ISDIR(oldinode->i_mode)) {
iput(oldinode); iput(oldinode);
return -EPERM; return -EPERM;
} }
...@@ -653,7 +744,7 @@ int sys_link(const char * oldname, const char * newname) ...@@ -653,7 +744,7 @@ int sys_link(const char * oldname, const char * newname)
iput(oldinode); iput(oldinode);
return -EACCES; return -EACCES;
} }
bh = find_entry(dir,basename,namelen,&de); bh = find_entry(&dir,basename,namelen,&de);
if (bh) { if (bh) {
brelse(bh); brelse(bh);
iput(dir); iput(dir);
......
/*
* linux/fs/namei.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.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>
#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
/*
* comment out this line if you want names > NAME_LEN chars to be
* truncated. Else they will be disallowed.
*/
/* #define NO_TRUNCATE */
#define MAY_EXEC 1
#define MAY_WRITE 2
#define MAY_READ 4
/*
* permission()
*
* is used to check for read/write/execute permissions on a file.
* I don't know if we should look at just the euid or both euid and
* uid, but that should be easily changed.
*/
static int permission(struct m_inode * inode,int mask)
{
int mode = inode->i_mode;
/* special case: not even root can read/write a deleted file */
if (inode->i_dev && !inode->i_nlinks)
return 0;
else if (current->euid==inode->i_uid)
mode >>= 6;
else if (current->egid==inode->i_gid)
mode >>= 3;
return mode & mask & 0007;
}
/*
* ok, we cannot use strncmp, as the name is not in our data space.
* Thus we'll have to use match. No big problem. Match also makes
* some sanity tests.
*
* NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
*/
static int match(int len,const char * name,struct dir_entry * de)
{
register int same __asm__("ax");
if (!de || !de->inode || len > NAME_LEN)
return 0;
if (len < NAME_LEN && de->name[len])
return 0;
__asm__("cld\n\t"
"fs ; repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
:"cx","di","si");
return same;
}
/*
* find_entry()
*
* finds and entry in the specified directory with the wanted name. It
* returns the cache buffer in which the entry was found, and the entry
* itself (as a parameter - res_dir). It does NOT read the inode of the
* entry - you'll have to do that yourself if you want to.
*
* This also takes care of the few special cases due to '..'-traversal
* over a pseudo-root and a mount point.
*/
static struct buffer_head * find_entry(struct m_inode ** dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int entries;
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
struct super_block * sb;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#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) */
if ((*dir) == current->root)
namelen=1;
else if ((*dir)->i_num == ROOT_INO) {
/* '..' over a mount-point results in 'dir' being exchanged for the mounted
directory-inode. NOTE! We set mounted, so that we can iput the new dir */
sb=get_super((*dir)->i_dev);
if (sb->s_imount) {
iput(*dir);
(*dir)=sb->s_imount;
(*dir)->i_count++;
}
}
}
if (!(block = (*dir)->i_zone[0]))
return NULL;
if (!(bh = bread((*dir)->i_dev,block)))
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;
while (i < entries) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
!(bh = bread((*dir)->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if (match(namelen,name,de)) {
*res_dir = de;
return bh;
}
de++;
i++;
}
brelse(bh);
return NULL;
}
/*
* add_entry()
*
* adds a file entry to the specified directory, using the same
* semantics as find_entry(). It returns NULL if it failed.
*
* NOTE!! The inode part of 'de' is left at 0 - which means you
* may not sleep between calling this and putting something into
* the entry, as someone else might have used it while you slept.
*/
static struct buffer_head * add_entry(struct m_inode * dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
*res_dir = NULL;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)
return NULL;
#else
if (namelen > NAME_LEN)
namelen = NAME_LEN;
#endif
if (!namelen)
return NULL;
if (!(block = dir->i_zone[0]))
return NULL;
if (!(bh = bread(dir->i_dev,block)))
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;
while (1) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) {
brelse(bh);
bh = NULL;
block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
if (!block)
return NULL;
if (!(bh = bread(dir->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if (i*sizeof(struct dir_entry) >= dir->i_size) {
de->inode=0;
dir->i_size = (i+1)*sizeof(struct dir_entry);
dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME;
}
if (!de->inode) {
dir->i_mtime = CURRENT_TIME;
for (i=0; i < NAME_LEN ; i++)
de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
bh->b_dirt = 1;
*res_dir = de;
return bh;
}
de++;
i++;
}
brelse(bh);
return NULL;
}
/*
* 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)
{
char c;
const char * thisname;
struct m_inode * inode;
struct buffer_head * bh;
int namelen,inr,idev;
struct dir_entry * de;
if (!current->root || !current->root->i_count)
panic("No root inode");
if (!current->pwd || !current->pwd->i_count)
panic("No cwd inode");
if ((c=get_fs_byte(pathname))=='/') {
inode = current->root;
pathname++;
} else if (c)
inode = current->pwd;
else
return NULL; /* empty name is bad */
inode->i_count++;
while (1) {
thisname = pathname;
if (!S_ISDIR(inode->i_mode) ||
!(permission(inode,MAY_EXEC) || suser())) {
iput(inode);
return NULL;
}
for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
/* nothing */ ;
if (!c)
return inode;
if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
iput(inode);
return NULL;
}
inr = de->inode;
idev = inode->i_dev;
brelse(bh);
iput(inode);
if (!(inode = iget(idev,inr)))
return NULL;
}
}
/*
* dir_namei()
*
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name)
{
char c;
const char * basename;
struct m_inode * dir;
if (!(dir = get_dir(pathname)))
return NULL;
basename = pathname;
while (c=get_fs_byte(pathname++))
if (c=='/')
basename=pathname;
*namelen = pathname-basename-1;
*name = basename;
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)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return NULL;
if (!namelen) /* special case: '/usr/' etc */
return dir;
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
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;
}
return dir;
}
/*
* open_namei()
*
* namei for open - this is in fact almost the whole open-routine.
*/
int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir, *inode;
struct buffer_head * bh;
struct dir_entry * de;
if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
flag |= O_WRONLY;
mode &= 0777 & ~current->umask;
mode |= I_REGULAR;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen) { /* special case: '/usr/' etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
*res_inode=dir;
return 0;
}
iput(dir);
return -EISDIR;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
if (!(flag & O_CREAT)) {
iput(dir);
return -ENOENT;
}
if (!(permission(dir,MAY_WRITE) || suser())) {
iput(dir);
return -EACCES;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_mode = mode;
inode->i_dirt = 1;
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);
*res_inode = inode;
return 0;
}
inr = de->inode;
dev = dir->i_dev;
brelse(bh);
iput(dir);
if (flag & O_EXCL)
return -EEXIST;
if (!(inode=iget(dev,inr)))
return -EACCES;
if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
(!(permission(inode,ACC_MODE(flag))==ACC_MODE(flag)) ||
suser())) {
iput(inode);
return -EPERM;
}
inode->i_atime = CURRENT_TIME;
if (flag & O_TRUNC)
truncate(inode);
*res_inode = inode;
return 0;
}
int sys_mknod(const char * filename, int mode, int dev)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(filename,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!(permission(dir,MAY_WRITE) || suser())) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
return -EEXIST;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_mode = mode;
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_zone[0] = dev;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
inode->i_nlinks=0;
iput(inode);
return -ENOSPC;
}
de->inode = inode->i_num;
bh->b_dirt = 1;
iput(dir);
iput(inode);
brelse(bh);
return 0;
}
int sys_mkdir(const char * pathname, int mode)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh, *dir_block;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!(permission(dir,MAY_WRITE) || suser())) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
return -EEXIST;
}
inode = new_inode(dir->i_dev);
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_size = 32;
inode->i_dirt = 1;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
iput(dir);
inode->i_nlinks--;
iput(inode);
return -ENOSPC;
}
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;
}
de = (struct dir_entry *) dir_block->b_data;
de->inode=inode->i_num;
strcpy(de->name,".");
de++;
de->inode = dir->i_num;
strcpy(de->name,"..");
inode->i_nlinks = 2;
dir_block->b_dirt = 1;
brelse(dir_block);
inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
inode->i_dirt = 1;
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;
}
de->inode = inode->i_num;
bh->b_dirt = 1;
dir->i_nlinks++;
dir->i_dirt = 1;
iput(dir);
iput(inode);
brelse(bh);
return 0;
}
/*
* routine to check that the specified directory is empty (for rmdir)
*/
static int empty_dir(struct m_inode * inode)
{
int nr,block;
int len;
struct buffer_head * bh;
struct dir_entry * de;
len = inode->i_size / sizeof (struct dir_entry);
if (len<2 || !inode->i_zone[0] ||
!(bh=bread(inode->i_dev,inode->i_zone[0]))) {
printk("warning - bad directory on dev %04x\n",inode->i_dev);
return 0;
}
de = (struct dir_entry *) bh->b_data;
if (de[0].inode != inode->i_num || !de[1].inode ||
strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
printk("warning - bad directory on dev %04x\n",inode->i_dev);
return 0;
}
nr = 2;
de += 2;
while (nr<len) {
if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
brelse(bh);
block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
if (!block) {
nr += DIR_ENTRIES_PER_BLOCK;
continue;
}
if (!(bh=bread(inode->i_dev,block)))
return 0;
de = (struct dir_entry *) bh->b_data;
}
if (de->inode) {
brelse(bh);
return 0;
}
de++;
nr++;
}
brelse(bh);
return 1;
}
int sys_rmdir(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
return -ENOENT;
}
if (!(permission(dir,MAY_WRITE) || suser())) {
iput(dir);
brelse(bh);
return -EPERM;
}
if (!(inode = iget(dir->i_dev, de->inode))) {
iput(dir);
brelse(bh);
return -EPERM;
}
if (inode == dir) { /* we may not delete ".", but "../dir" is ok */
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
iput(dir);
brelse(bh);
return -ENOTDIR;
}
if (!empty_dir(inode)) {
iput(inode);
iput(dir);
brelse(bh);
return -ENOTEMPTY;
}
if (inode->i_nlinks != 2)
printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
de->inode = 0;
bh->b_dirt = 1;
brelse(bh);
inode->i_nlinks=0;
inode->i_dirt=1;
dir->i_nlinks--;
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt=1;
iput(dir);
iput(inode);
return 0;
}
int sys_unlink(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(name,&namelen,&basename)))
return -ENOENT;
if (!namelen) {
iput(dir);
return -ENOENT;
}
if (!(permission(dir,MAY_WRITE) || suser())) {
iput(dir);
return -EPERM;
}
bh = find_entry(&dir,basename,namelen,&de);
if (!bh) {
iput(dir);
return -ENOENT;
}
inode = iget(dir->i_dev, de->inode);
if (!inode) {
printk("iget failed in delete (%04x:%d)",dir->i_dev,de->inode);
iput(dir);
brelse(bh);
return -ENOENT;
}
if (S_ISDIR(inode->i_mode)) {
iput(inode);
iput(dir);
brelse(bh);
return -EPERM;
}
if (!inode->i_nlinks) {
printk("Deleting nonexistent file (%04x:%d), %d\n",
inode->i_dev,inode->i_num,inode->i_nlinks);
inode->i_nlinks=1;
}
de->inode = 0;
bh->b_dirt = 1;
brelse(bh);
inode->i_nlinks--;
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
iput(inode);
iput(dir);
return 0;
}
int sys_link(const char * oldname, const char * newname)
{
struct dir_entry * de;
struct m_inode * oldinode, * dir;
struct buffer_head * bh;
const char * basename;
int namelen;
oldinode=namei(oldname);
if (!oldinode)
return -ENOENT;
if (S_ISDIR(oldinode->i_mode)) {
iput(oldinode);
return -EPERM;
}
dir = dir_namei(newname,&namelen,&basename);
if (!dir) {
iput(oldinode);
return -EACCES;
}
if (!namelen) {
iput(oldinode);
iput(dir);
return -EPERM;
}
if (dir->i_dev != oldinode->i_dev) {
iput(dir);
iput(oldinode);
return -EXDEV;
}
if (!(permission(dir,MAY_WRITE) || suser())) {
iput(dir);
iput(oldinode);
return -EACCES;
}
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
brelse(bh);
iput(dir);
iput(oldinode);
return -EEXIST;
}
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
iput(oldinode);
return -ENOSPC;
}
de->inode = oldinode->i_num;
bh->b_dirt = 1;
brelse(bh);
iput(dir);
oldinode->i_nlinks++;
oldinode->i_ctime = CURRENT_TIME;
oldinode->i_dirt = 1;
iput(oldinode);
return 0;
}
/*
* linux/fs/open.c
*
* (C) 1991 Linus Torvalds
*/
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -10,6 +16,11 @@ ...@@ -10,6 +16,11 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/segment.h> #include <asm/segment.h>
int sys_ustat(int dev, struct ustat * ubuf)
{
return -ENOSYS;
}
int sys_utime(char * filename, struct utimbuf * times) int sys_utime(char * filename, struct utimbuf * times)
{ {
struct m_inode * inode; struct m_inode * inode;
...@@ -29,27 +40,35 @@ int sys_utime(char * filename, struct utimbuf * times) ...@@ -29,27 +40,35 @@ int sys_utime(char * filename, struct utimbuf * times)
return 0; return 0;
} }
/*
* XXX should we use the real or effective uid? BSD uses the real uid,
* so as to make this call useful to setuid programs.
*/
int sys_access(const char * filename,int mode) int sys_access(const char * filename,int mode)
{ {
struct m_inode * inode; struct m_inode * inode;
int res; int res, i_mode;
mode &= 0007; mode &= 0007;
if (!(inode=namei(filename))) if (!(inode=namei(filename)))
return -EACCES; return -EACCES;
res = inode->i_mode & 0777; i_mode = res = inode->i_mode & 0777;
iput(inode); iput(inode);
if (!(current->euid && current->uid)) if (current->uid == inode->i_uid)
if (res & 0111)
res = 0777;
else
res = 0666;
if (current->euid == inode->i_uid)
res >>= 6; res >>= 6;
else if (current->egid == inode->i_gid) else if (current->gid == inode->i_gid)
res >>= 6; res >>= 6;
if ((res & 0007 & mode) == mode) if ((res & 0007 & mode) == mode)
return 0; return 0;
/*
* XXX we are doing this test last because we really should be
* swapping the effective with the real user id (temporarily),
* and then calling suser() routine. If we do call the
* suser() routine, it needs to be called last.
*/
if ((!current->uid) &&
(!(mode & 1) || (i_mode & 0111)))
return 0;
return -EACCES; return -EACCES;
} }
...@@ -89,12 +108,10 @@ int sys_chmod(const char * filename,int mode) ...@@ -89,12 +108,10 @@ int sys_chmod(const char * filename,int mode)
if (!(inode=namei(filename))) if (!(inode=namei(filename)))
return -ENOENT; return -ENOENT;
if (current->uid && current->euid) if ((current->euid != inode->i_uid) && !suser()) {
if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) { iput(inode);
iput(inode); return -EACCES;
return -EACCES; }
} else
mode = (mode & 0777) | (inode->i_mode & 07000);
inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777); inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
inode->i_dirt = 1; inode->i_dirt = 1;
iput(inode); iput(inode);
...@@ -107,7 +124,7 @@ int sys_chown(const char * filename,int uid,int gid) ...@@ -107,7 +124,7 @@ int sys_chown(const char * filename,int uid,int gid)
if (!(inode=namei(filename))) if (!(inode=namei(filename)))
return -ENOENT; return -ENOENT;
if (current->uid && current->euid) { if (!suser()) {
iput(inode); iput(inode);
return -EACCES; return -EACCES;
} }
...@@ -156,6 +173,9 @@ int sys_open(const char * filename,int flag,int mode) ...@@ -156,6 +173,9 @@ int sys_open(const char * filename,int flag,int mode)
f->f_count=0; f->f_count=0;
return -EPERM; return -EPERM;
} }
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode; f->f_mode = inode->i_mode;
f->f_flags = flag; f->f_flags = flag;
f->f_count = 1; f->f_count = 1;
......
/*
* linux/fs/open.c
*
* (C) 1991 Linus Torvalds
*/
#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/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
int sys_ustat(int dev, struct ustat * ubuf)
{
return -ENOSYS;
}
int sys_utime(char * filename, struct utimbuf * times)
{
struct m_inode * inode;
long actime,modtime;
if (!(inode=namei(filename)))
return -ENOENT;
if (times) {
actime = get_fs_long((unsigned long *) &times->actime);
modtime = get_fs_long((unsigned long *) &times->modtime);
} else
actime = modtime = CURRENT_TIME;
inode->i_atime = actime;
inode->i_mtime = modtime;
inode->i_dirt = 1;
iput(inode);
return 0;
}
int sys_access(const char * filename,int mode)
{
struct m_inode * inode;
int res;
mode &= 0007;
if (!(inode=namei(filename)))
return -EACCES;
res = inode->i_mode & 0777;
iput(inode);
if (!(current->euid && current->uid))
if (res & 0111)
res = 0777;
else
res = 0666;
if (current->euid == inode->i_uid)
res >>= 6;
else if (current->egid == inode->i_gid)
res >>= 6;
if ((res & 0007 & mode) == mode)
return 0;
return -EACCES;
}
int sys_chdir(const char * filename)
{
struct m_inode * inode;
if (!(inode = namei(filename)))
return -ENOENT;
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
return -ENOTDIR;
}
iput(current->pwd);
current->pwd = inode;
return (0);
}
int sys_chroot(const char * filename)
{
struct m_inode * inode;
if (!(inode=namei(filename)))
return -ENOENT;
if (!S_ISDIR(inode->i_mode)) {
iput(inode);
return -ENOTDIR;
}
iput(current->root);
current->root = inode;
return (0);
}
int sys_chmod(const char * filename,int mode)
{
struct m_inode * inode;
if (!(inode=namei(filename)))
return -ENOENT;
if (current->uid && current->euid)
if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) {
iput(inode);
return -EACCES;
} else
mode = (mode & 0777) | (inode->i_mode & 07000);
inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
inode->i_dirt = 1;
iput(inode);
return 0;
}
int sys_chown(const char * filename,int uid,int gid)
{
struct m_inode * inode;
if (!(inode=namei(filename)))
return -ENOENT;
if (current->uid && current->euid) {
iput(inode);
return -EACCES;
}
inode->i_uid=uid;
inode->i_gid=gid;
inode->i_dirt=1;
iput(inode);
return 0;
}
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
struct file * f;
int i,fd;
mode &= 0777 & ~current->umask;
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])
break;
if (fd>=NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
f=0+file_table;
for (i=0 ; i<NR_FILE ; i++,f++)
if (!f->f_count) break;
if (i>=NR_FILE)
return -EINVAL;
(current->filp[fd]=f)->f_count++;
if ((i=open_namei(filename,flag,mode,&inode))<0) {
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
/* 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;
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode;
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
return (fd);
}
int sys_creat(const char * pathname, int mode)
{
return sys_open(pathname, O_CREAT | O_TRUNC, mode);
}
int sys_close(unsigned int fd)
{
struct file * filp;
if (fd >= NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
if (!(filp = current->filp[fd]))
return -EINVAL;
current->filp[fd] = NULL;
if (filp->f_count == 0)
panic("Close: file count is 0");
if (--filp->f_count)
return (0);
iput(filp->f_inode);
return (0);
}
/*
* linux/fs/pipe.c
*
* (C) 1991 Linus Torvalds
*/
#include <signal.h> #include <signal.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -6,47 +12,60 @@ ...@@ -6,47 +12,60 @@
int read_pipe(struct m_inode * inode, char * buf, int count) int read_pipe(struct m_inode * inode, char * buf, int count)
{ {
char * b=buf; int chars, size, read = 0;
while (PIPE_EMPTY(*inode)) { while (count>0) {
wake_up(&inode->i_wait); while (!(size=PIPE_SIZE(*inode))) {
if (inode->i_count != 2) /* are there any writers left? */ wake_up(&inode->i_wait);
return 0; if (inode->i_count != 2) /* are there any writers? */
sleep_on(&inode->i_wait); return read;
} sleep_on(&inode->i_wait);
while (count>0 && !(PIPE_EMPTY(*inode))) { }
count --; chars = PAGE_SIZE-PIPE_TAIL(*inode);
put_fs_byte(((char *)inode->i_size)[PIPE_TAIL(*inode)],b++); if (chars > count)
INC_PIPE( PIPE_TAIL(*inode) ); chars = count;
if (chars > size)
chars = size;
count -= chars;
read += chars;
size = PIPE_TAIL(*inode);
PIPE_TAIL(*inode) += chars;
PIPE_TAIL(*inode) &= (PAGE_SIZE-1);
while (chars-->0)
put_fs_byte(((char *)inode->i_size)[size++],buf++);
} }
wake_up(&inode->i_wait); wake_up(&inode->i_wait);
return b-buf; return read;
} }
int write_pipe(struct m_inode * inode, char * buf, int count) int write_pipe(struct m_inode * inode, char * buf, int count)
{ {
char * b=buf; int chars, size, written = 0;
wake_up(&inode->i_wait); while (count>0) {
if (inode->i_count != 2) { /* no readers */ while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) {
current->signal |= (1<<(SIGPIPE-1));
return -1;
}
while (count-->0) {
while (PIPE_FULL(*inode)) {
wake_up(&inode->i_wait); wake_up(&inode->i_wait);
if (inode->i_count != 2) { if (inode->i_count != 2) { /* no readers */
current->signal |= (1<<(SIGPIPE-1)); current->signal |= (1<<(SIGPIPE-1));
return b-buf; return written?written:-1;
} }
sleep_on(&inode->i_wait); sleep_on(&inode->i_wait);
} }
((char *)inode->i_size)[PIPE_HEAD(*inode)] = get_fs_byte(b++); chars = PAGE_SIZE-PIPE_HEAD(*inode);
INC_PIPE( PIPE_HEAD(*inode) ); if (chars > count)
wake_up(&inode->i_wait); chars = count;
if (chars > size)
chars = size;
count -= chars;
written += chars;
size = PIPE_HEAD(*inode);
PIPE_HEAD(*inode) += chars;
PIPE_HEAD(*inode) &= (PAGE_SIZE-1);
while (chars-->0)
((char *)inode->i_size)[size++]=get_fs_byte(buf++);
} }
wake_up(&inode->i_wait); wake_up(&inode->i_wait);
return b-buf; return written;
} }
int sys_pipe(unsigned long * fildes) int sys_pipe(unsigned long * fildes)
......
/*
* linux/fs/read_write.c
*
* (C) 1991 Linus Torvalds
*/
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
...@@ -6,7 +12,7 @@ ...@@ -6,7 +12,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/segment.h> #include <asm/segment.h>
extern int rw_char(int rw,int dev, char * buf, int count); extern int rw_char(int rw,int dev, char * buf, int count, off_t * pos);
extern int read_pipe(struct m_inode * inode, char * buf, int count); extern int read_pipe(struct m_inode * inode, char * buf, int count);
extern int write_pipe(struct m_inode * inode, char * buf, int count); extern int write_pipe(struct m_inode * inode, char * buf, int count);
extern int block_read(int dev, off_t * pos, char * buf, int count); extern int block_read(int dev, off_t * pos, char * buf, int count);
...@@ -22,7 +28,7 @@ int sys_lseek(unsigned int fd,off_t offset, int origin) ...@@ -22,7 +28,7 @@ int sys_lseek(unsigned int fd,off_t offset, int origin)
int tmp; int tmp;
if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode) if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode)
|| !IS_BLOCKDEV(MAJOR(file->f_inode->i_dev))) || !IS_SEEKABLE(MAJOR(file->f_inode->i_dev)))
return -EBADF; return -EBADF;
if (file->f_inode->i_pipe) if (file->f_inode->i_pipe)
return -ESPIPE; return -ESPIPE;
...@@ -60,7 +66,7 @@ int sys_read(unsigned int fd,char * buf,int count) ...@@ -60,7 +66,7 @@ int sys_read(unsigned int fd,char * buf,int count)
if (inode->i_pipe) if (inode->i_pipe)
return (file->f_mode&1)?read_pipe(inode,buf,count):-1; return (file->f_mode&1)?read_pipe(inode,buf,count):-1;
if (S_ISCHR(inode->i_mode)) if (S_ISCHR(inode->i_mode))
return rw_char(READ,inode->i_zone[0],buf,count); return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode)) if (S_ISBLK(inode->i_mode))
return block_read(inode->i_zone[0],&file->f_pos,buf,count); return block_read(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) { if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
...@@ -87,7 +93,7 @@ int sys_write(unsigned int fd,char * buf,int count) ...@@ -87,7 +93,7 @@ int sys_write(unsigned int fd,char * buf,int count)
if (inode->i_pipe) if (inode->i_pipe)
return (file->f_mode&2)?write_pipe(inode,buf,count):-1; return (file->f_mode&2)?write_pipe(inode,buf,count):-1;
if (S_ISCHR(inode->i_mode)) if (S_ISCHR(inode->i_mode))
return rw_char(WRITE,inode->i_zone[0],buf,count); return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode)) if (S_ISBLK(inode->i_mode))
return block_write(inode->i_zone[0],&file->f_pos,buf,count); return block_write(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISREG(inode->i_mode)) if (S_ISREG(inode->i_mode))
......
/*
* linux/fs/stat.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
...@@ -6,7 +12,7 @@ ...@@ -6,7 +12,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/segment.h> #include <asm/segment.h>
static int cp_stat(struct m_inode * inode, struct stat * statbuf) static void cp_stat(struct m_inode * inode, struct stat * statbuf)
{ {
struct stat tmp; struct stat tmp;
int i; int i;
...@@ -25,19 +31,17 @@ static int cp_stat(struct m_inode * inode, struct stat * statbuf) ...@@ -25,19 +31,17 @@ static int cp_stat(struct m_inode * inode, struct stat * statbuf)
tmp.st_ctime = inode->i_ctime; tmp.st_ctime = inode->i_ctime;
for (i=0 ; i<sizeof (tmp) ; i++) for (i=0 ; i<sizeof (tmp) ; i++)
put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]); put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]);
return (0);
} }
int sys_stat(char * filename, struct stat * statbuf) int sys_stat(char * filename, struct stat * statbuf)
{ {
int i;
struct m_inode * inode; struct m_inode * inode;
if (!(inode=namei(filename))) if (!(inode=namei(filename)))
return -ENOENT; return -ENOENT;
i=cp_stat(inode,statbuf); cp_stat(inode,statbuf);
iput(inode); iput(inode);
return i; return 0;
} }
int sys_fstat(unsigned int fd, struct stat * statbuf) int sys_fstat(unsigned int fd, struct stat * statbuf)
...@@ -46,6 +50,7 @@ int sys_fstat(unsigned int fd, struct stat * statbuf) ...@@ -46,6 +50,7 @@ int sys_fstat(unsigned int fd, struct stat * statbuf)
struct m_inode * inode; struct m_inode * inode;
if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode)) if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode))
return -ENOENT; return -EBADF;
return cp_stat(inode,statbuf); cp_stat(inode,statbuf);
return 0;
} }
/*
* linux/fs/super.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* super.c contains code to handle the super-block tables. * super.c contains code to handle the super-block tables.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/system.h>
#include <errno.h>
#include <sys/stat.h>
int sync_dev(int dev);
void wait_for_keypress(void);
/* set_bit uses setb, as gas doesn't recognize setc */ /* set_bit uses setb, as gas doesn't recognize setc */
#define set_bit(bitnr,addr) ({ \ #define set_bit(bitnr,addr) ({ \
...@@ -12,59 +25,217 @@ __asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \ ...@@ -12,59 +25,217 @@ __asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
__res; }) __res; })
struct super_block super_block[NR_SUPER]; struct super_block super_block[NR_SUPER];
/* this is initialized in init/main.c */
int ROOT_DEV = 0;
struct super_block * do_mount(int dev) static void lock_super(struct super_block * sb)
{ {
struct super_block * p; cli();
while (sb->s_lock)
sleep_on(&(sb->s_wait));
sb->s_lock = 1;
sti();
}
static void free_super(struct super_block * sb)
{
cli();
sb->s_lock = 0;
wake_up(&(sb->s_wait));
sti();
}
static void wait_on_super(struct super_block * sb)
{
cli();
while (sb->s_lock)
sleep_on(&(sb->s_wait));
sti();
}
struct super_block * get_super(int dev)
{
struct super_block * s;
if (!dev)
return NULL;
s = 0+super_block;
while (s < NR_SUPER+super_block)
if (s->s_dev == dev) {
wait_on_super(s);
if (s->s_dev == dev)
return s;
s = 0+super_block;
} else
s++;
return NULL;
}
void put_super(int dev)
{
struct super_block * sb;
int i;
if (dev == ROOT_DEV) {
printk("root diskette changed: prepare for armageddon\n\r");
return;
}
if (!(sb = get_super(dev)))
return;
if (sb->s_imount) {
printk("Mounted disk changed - tssk, tssk\n\r");
return;
}
lock_super(sb);
sb->s_dev = 0;
for(i=0;i<I_MAP_SLOTS;i++)
brelse(sb->s_imap[i]);
for(i=0;i<Z_MAP_SLOTS;i++)
brelse(sb->s_zmap[i]);
free_super(sb);
return;
}
static struct super_block * read_super(int dev)
{
struct super_block * s;
struct buffer_head * bh; struct buffer_head * bh;
int i,block; int i,block;
for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++ ) if (!dev)
if (!(p->s_dev))
break;
p->s_dev = -1; /* mark it in use */
if (p >= &super_block[NR_SUPER])
return NULL; return NULL;
if (!(bh = bread(dev,1))) check_disk_change(dev);
if (s = get_super(dev))
return s;
for (s = 0+super_block ;; s++) {
if (s >= NR_SUPER+super_block)
return NULL;
if (!s->s_dev)
break;
}
s->s_dev = dev;
s->s_isup = NULL;
s->s_imount = NULL;
s->s_time = 0;
s->s_rd_only = 0;
s->s_dirt = 0;
lock_super(s);
if (!(bh = bread(dev,1))) {
s->s_dev=0;
free_super(s);
return NULL; return NULL;
*p = *((struct super_block *) bh->b_data); }
*((struct d_super_block *) s) =
*((struct d_super_block *) bh->b_data);
brelse(bh); brelse(bh);
if (p->s_magic != SUPER_MAGIC) { if (s->s_magic != SUPER_MAGIC) {
p->s_dev = 0; s->s_dev = 0;
free_super(s);
return NULL; return NULL;
} }
for (i=0;i<I_MAP_SLOTS;i++) for (i=0;i<I_MAP_SLOTS;i++)
p->s_imap[i] = NULL; s->s_imap[i] = NULL;
for (i=0;i<Z_MAP_SLOTS;i++) for (i=0;i<Z_MAP_SLOTS;i++)
p->s_zmap[i] = NULL; s->s_zmap[i] = NULL;
block=2; block=2;
for (i=0 ; i < p->s_imap_blocks ; i++) for (i=0 ; i < s->s_imap_blocks ; i++)
if (p->s_imap[i]=bread(dev,block)) if (s->s_imap[i]=bread(dev,block))
block++; block++;
else else
break; break;
for (i=0 ; i < p->s_zmap_blocks ; i++) for (i=0 ; i < s->s_zmap_blocks ; i++)
if (p->s_zmap[i]=bread(dev,block)) if (s->s_zmap[i]=bread(dev,block))
block++; block++;
else else
break; break;
if (block != 2+p->s_imap_blocks+p->s_zmap_blocks) { if (block != 2+s->s_imap_blocks+s->s_zmap_blocks) {
for(i=0;i<I_MAP_SLOTS;i++) for(i=0;i<I_MAP_SLOTS;i++)
brelse(p->s_imap[i]); brelse(s->s_imap[i]);
for(i=0;i<Z_MAP_SLOTS;i++) for(i=0;i<Z_MAP_SLOTS;i++)
brelse(p->s_zmap[i]); brelse(s->s_zmap[i]);
p->s_dev=0; s->s_dev=0;
free_super(s);
return NULL; return NULL;
} }
p->s_imap[0]->b_data[0] |= 1; s->s_imap[0]->b_data[0] |= 1;
p->s_zmap[0]->b_data[0] |= 1; s->s_zmap[0]->b_data[0] |= 1;
p->s_dev = dev; free_super(s);
p->s_isup = NULL; return s;
p->s_imount = NULL; }
p->s_time = 0;
p->s_rd_only = 0; int sys_umount(char * dev_name)
p->s_dirt = 0; {
return p; struct m_inode * inode;
struct super_block * sb;
int dev;
if (!(inode=namei(dev_name)))
return -ENOENT;
dev = inode->i_zone[0];
if (!S_ISBLK(inode->i_mode)) {
iput(inode);
return -ENOTBLK;
}
iput(inode);
if (dev==ROOT_DEV)
return -EBUSY;
if (!(sb=get_super(dev)) || !(sb->s_imount))
return -ENOENT;
if (!sb->s_imount->i_mount)
printk("Mounted inode has i_mount=0\n");
for(inode=inode_table+0 ; inode<inode_table+NR_INODE ; inode++)
if (inode->i_dev==dev && inode->i_count)
return -EBUSY;
sb->s_imount->i_mount=0;
iput(sb->s_imount);
sb->s_imount = NULL;
iput(sb->s_isup);
sb->s_isup = NULL;
put_super(dev);
sync_dev(dev);
return 0;
}
int sys_mount(char * dev_name, char * dir_name, int rw_flag)
{
struct m_inode * dev_i, * dir_i;
struct super_block * sb;
int dev;
if (!(dev_i=namei(dev_name)))
return -ENOENT;
dev = dev_i->i_zone[0];
if (!S_ISBLK(dev_i->i_mode)) {
iput(dev_i);
return -EPERM;
}
iput(dev_i);
if (!(dir_i=namei(dir_name)))
return -ENOENT;
if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) {
iput(dir_i);
return -EBUSY;
}
if (!S_ISDIR(dir_i->i_mode)) {
iput(dir_i);
return -EPERM;
}
if (!(sb=read_super(dev))) {
iput(dir_i);
return -EBUSY;
}
if (sb->s_imount) {
iput(dir_i);
return -EBUSY;
}
if (dir_i->i_mount) {
iput(dir_i);
return -EPERM;
}
sb->s_imount=dir_i;
dir_i->i_mount=1;
dir_i->i_dirt=1; /* NOTE! we don't iput(dir_i) */
return 0; /* we do that in umount */
} }
void mount_root(void) void mount_root(void)
...@@ -77,11 +248,18 @@ void mount_root(void) ...@@ -77,11 +248,18 @@ void mount_root(void)
panic("bad i-node size"); panic("bad i-node size");
for(i=0;i<NR_FILE;i++) for(i=0;i<NR_FILE;i++)
file_table[i].f_count=0; file_table[i].f_count=0;
for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) if (MAJOR(ROOT_DEV) == 2) {
printk("Insert root floppy and press ENTER");
wait_for_keypress();
}
for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++) {
p->s_dev = 0; p->s_dev = 0;
if (!(p=do_mount(ROOT_DEV))) p->s_lock = 0;
p->s_wait = NULL;
}
if (!(p=read_super(ROOT_DEV)))
panic("Unable to mount root"); panic("Unable to mount root");
if (!(mi=iget(ROOT_DEV,1))) if (!(mi=iget(ROOT_DEV,ROOT_INO)))
panic("Unable to read root i-node"); panic("Unable to read root i-node");
mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */
p->s_isup = p->s_imount = mi; p->s_isup = p->s_imount = mi;
......
/*
* linux/fs/truncate.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.h> #include <linux/sched.h>
#include <sys/stat.h> #include <sys/stat.h>
......
/*
* linux/fs/tty_ioctl.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <termios.h> #include <termios.h>
...@@ -62,6 +68,9 @@ static int get_termio(struct tty_struct * tty, struct termio * termio) ...@@ -62,6 +68,9 @@ static int get_termio(struct tty_struct * tty, struct termio * termio)
return 0; return 0;
} }
/*
* 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 i; int i;
......
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.11.15.19.30.40; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.15.16.16.12; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Fixed bug in tolower(). Plus sign should have been a minus sign.
@
text
@#ifndef _CTYPE_H
#define _CTYPE_H
#define _U 0x01 /* upper */
#define _L 0x02 /* lower */
#define _D 0x04 /* digit */
#define _C 0x08 /* cntrl */
#define _P 0x10 /* punct */
#define _S 0x20 /* white space (space/lf/tab) */
#define _X 0x40 /* hex digit */
#define _SP 0x80 /* hard space (0x20) */
extern unsigned char _ctype[];
extern char _ctmp;
#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D))
#define isalpha(c) ((_ctype+1)[c]&(_U|_L))
#define iscntrl(c) ((_ctype+1)[c]&(_C))
#define isdigit(c) ((_ctype+1)[c]&(_D))
#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D))
#define islower(c) ((_ctype+1)[c]&(_L))
#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP))
#define ispunct(c) ((_ctype+1)[c]&(_P))
#define isspace(c) ((_ctype+1)[c]&(_S))
#define isupper(c) ((_ctype+1)[c]&(_U))
#define isxdigit(c) ((_ctype+1)[c]&(_D|_X))
#define isascii(c) (((unsigned) c)<=0x7f)
#define toascii(c) (((unsigned) c)&0x7f)
#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'-'A'):_ctmp)
#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp)
#endif
@
1.1
log
@Initial revision
@
text
@d31 1
a31 1
#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'+'A'):_ctmp)
@
head 1.1;
branch ;
access ;
symbols ;
locks tytso:1.1; strict;
comment @ * @;
1.1
date 91.11.25.10.43.03; author tytso; state Exp;
branches ;
next ;
desc
@@
1.1
log
@Initial revision
@
text
@extern inline unsigned char get_fs_byte(const char * addr)
{
unsigned register char _v;
__asm__ ("movb %%fs:%1,%0":"=r" (_v):"m" (*addr));
return _v;
}
extern inline unsigned short get_fs_word(const unsigned short *addr)
{
unsigned short _v;
__asm__ ("movw %%fs:%1,%0":"=r" (_v):"m" (*addr));
return _v;
}
extern inline unsigned long get_fs_long(const unsigned long *addr)
{
unsigned long _v;
__asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); \
return _v;
}
extern inline void put_fs_byte(char val,char *addr)
{
__asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr));
}
extern inline void put_fs_word(short val,short * addr)
{
__asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr));
}
extern inline void put_fs_long(unsigned long val,unsigned long * addr)
{
__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr));
}
@
...@@ -36,3 +36,30 @@ extern inline void put_fs_long(unsigned long val,unsigned long * addr) ...@@ -36,3 +36,30 @@ extern inline void put_fs_long(unsigned long val,unsigned long * addr)
{ {
__asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr)); __asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr));
} }
/*
* Someone who knows GNU asm better than I should double check the followig.
* It seems to work, but I don't know if I'm doing something subtly wrong.
* --- TYT, 11/24/91
*/
extern inline unsigned long get_fs()
{
unsigned short _v;
__asm__("mov %%fs,%%ax":"=a" (_v):);
return _v;
}
extern inline unsigned long get_ds()
{
unsigned short _v;
__asm__("mov %%ds,%%ax":"=a" (_v):);
return _v;
}
extern inline void set_fs(unsigned long val)
{
__asm__("mov %0,%%fs"::"a" ((unsigned short) val));
}
...@@ -28,7 +28,7 @@ extern char _ctmp; ...@@ -28,7 +28,7 @@ extern char _ctmp;
#define isascii(c) (((unsigned) c)<=0x7f) #define isascii(c) (((unsigned) c)<=0x7f)
#define toascii(c) (((unsigned) c)&0x7f) #define toascii(c) (((unsigned) c)&0x7f)
#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'+'A'):_ctmp) #define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'-'A'):_ctmp)
#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp) #define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp)
#endif #endif
#ifndef _CTYPE_H
#define _CTYPE_H
#define _U 0x01 /* upper */
#define _L 0x02 /* lower */
#define _D 0x04 /* digit */
#define _C 0x08 /* cntrl */
#define _P 0x10 /* punct */
#define _S 0x20 /* white space (space/lf/tab) */
#define _X 0x40 /* hex digit */
#define _SP 0x80 /* hard space (0x20) */
extern unsigned char _ctype[];
extern char _ctmp;
#define isalnum(c) ((_ctype+1)[c]&(_U|_L|_D))
#define isalpha(c) ((_ctype+1)[c]&(_U|_L))
#define iscntrl(c) ((_ctype+1)[c]&(_C))
#define isdigit(c) ((_ctype+1)[c]&(_D))
#define isgraph(c) ((_ctype+1)[c]&(_P|_U|_L|_D))
#define islower(c) ((_ctype+1)[c]&(_L))
#define isprint(c) ((_ctype+1)[c]&(_P|_U|_L|_D|_SP))
#define ispunct(c) ((_ctype+1)[c]&(_P))
#define isspace(c) ((_ctype+1)[c]&(_S))
#define isupper(c) ((_ctype+1)[c]&(_U))
#define isxdigit(c) ((_ctype+1)[c]&(_D|_X))
#define isascii(c) (((unsigned) c)<=0x7f)
#define toascii(c) (((unsigned) c)&0x7f)
#define tolower(c) (_ctmp=c,isupper(_ctmp)?_ctmp+('a'+'A'):_ctmp)
#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp+('A'-'a'):_ctmp)
#endif
head 1.2;
branch ;
access ;
symbols ;
locks tytso:1.2; strict;
comment @ * @;
1.2
date 91.12.01.09.22.22; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.21.08.56.53; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Patches sent to Linus
@
text
@/*
* 'kernel.h' contains some often-used function prototypes etc
*/
void verify_area(void * addr,int count);
volatile void panic(const char * str);
int printf(const char * fmt, ...);
int printk(const char * fmt, ...);
int tty_write(unsigned ch,char * buf,int count);
/*
* This is defined as a macro, but at some point this might become a
* real subroutine that sets a flag if it returns true (to do
* BSD-style accounting where the process is flagged if it uses root
* privs). The implication of this is that you should do normal
* permissions checks first, and check suser() last.
*/
#define suser() (current->euid == 0)
@
1.1
log
@Initial revision
@
text
@d9 9
@
#ifndef _CONFIG_H #ifndef _CONFIG_H
#define _CONFIG_H #define _CONFIG_H
/* #define LASU_HD */
#define LINUS_HD
/* /*
* Amount of ram memory (in bytes, 640k-1M not discounted). Currently 8Mb. * The root-device is no longer hard-coded. You can change the default
* Don't make this bigger without making sure that there are enough page * root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s
* directory entries (boot/head.s)
*/ */
#if defined(LINUS_HD)
#define HIGH_MEMORY (0x800000)
#elif defined(LASU_HD)
#define HIGH_MEMORY (0x400000)
#else
#error "must define hd"
#endif
/* End of buffer memory. Must be 0xA0000, or > 0x100000, 4096-byte aligned */ /* define your keyboard here - US (KBD_US) or Finnish (KBD_FINNISH) */
#if (HIGH_MEMORY>=0x600000) #define KBD_US
#define BUFFER_END 0x200000 /* #define KBD_FINNISH */
#else
#define BUFFER_END 0xA0000
#endif
/* Root device at bootup. */
#if defined(LINUS_HD)
#define ROOT_DEV 0x306
#elif defined(LASU_HD)
#define ROOT_DEV 0x302
#else
#error "must define HD"
#endif
/* /*
* HD type. If 2, put 2 structures with a comma. If just 1, put * Normally, Linux can get the drive parameters from the BIOS at
* only 1 struct. The structs are { HEAD, SECTOR, TRACKS, WPCOM, LZONE, CTL } * startup, but if this for some unfathomable reason fails, you'd
* be left stranded. For this case, you can define HD_TYPE, which
* contains all necessary info on your harddisk.
*
* The HD_TYPE macro should look like this:
*
* #define HD_TYPE { head, sect, cyl, wpcom, lzone, ctl}
* *
* NOTE. CTL is supposed to be 0 for drives with less than 8 heads, and * In case of two harddisks, the info should be sepatated by
* 8 if heads >= 8. Don't know why, and I haven't tested it on a drive with * commas:
* more than 8 heads, but that is what the bios-listings seem to imply. I *
* just love not having a manual. * #define HD_TYPE { h,s,c,wpcom,lz,ctl },{ h,s,c,wpcom,lz,ctl }
*/ */
#if defined(LASU_HD) /*
#define HD_TYPE { 7,35,915,65536,920,0 } This is an example, two drives, first is type 2, second is type 3:
#elif defined(LINUS_HD)
#define HD_TYPE { 5,17,980,300,980,0 },{ 5,17,980,300,980,0 } #define HD_TYPE { 4,17,615,300,615,8 }, { 6,17,615,300,615,0 }
#else
#error "must define a hard-disk type" NOTE: ctl is 0 for all drives with heads<=8, and ctl=8 for drives
#endif with more than 8 heads.
If you want the BIOS to tell what kind of drive you have, just
leave HD_TYPE undefined.
*/
#endif #endif
/*
* This file contains some defines for the floppy disk controller.
* Various sources. Mostly "IBM Microcomputers: A Programmers
* Handbook", Sanches and Canton.
*/
#ifndef _FDREG_H
#define _FDREG_H
extern int ticks_to_floppy_on(unsigned int nr);
extern void floppy_on(unsigned int nr);
extern void floppy_off(unsigned int nr);
extern void floppy_select(unsigned int nr);
extern void floppy_deselect(unsigned int nr);
/* Fd controller regs. S&C, about page 340 */
#define FD_STATUS 0x3f4
#define FD_DATA 0x3f5
#define FD_DOR 0x3f2 /* Digital Output Register */
#define FD_DIR 0x3f7 /* Digital Input Register (read) */
#define FD_DCR 0x3f7 /* Diskette Control Register (write)*/
/* Bits of main status register */
#define STATUS_BUSYMASK 0x0F /* drive busy mask */
#define STATUS_BUSY 0x10 /* FDC busy */
#define STATUS_DMA 0x20 /* 0- DMA mode */
#define STATUS_DIR 0x40 /* 0- cpu->fdc */
#define STATUS_READY 0x80 /* Data reg ready */
/* Bits of FD_ST0 */
#define ST0_DS 0x03 /* drive select mask */
#define ST0_HA 0x04 /* Head (Address) */
#define ST0_NR 0x08 /* Not Ready */
#define ST0_ECE 0x10 /* Equipment chech error */
#define ST0_SE 0x20 /* Seek end */
#define ST0_INTR 0xC0 /* Interrupt code mask */
/* Bits of FD_ST1 */
#define ST1_MAM 0x01 /* Missing Address Mark */
#define ST1_WP 0x02 /* Write Protect */
#define ST1_ND 0x04 /* No Data - unreadable */
#define ST1_OR 0x10 /* OverRun */
#define ST1_CRC 0x20 /* CRC error in data or addr */
#define ST1_EOC 0x80 /* End Of Cylinder */
/* Bits of FD_ST2 */
#define ST2_MAM 0x01 /* Missing Addess Mark (again) */
#define ST2_BC 0x02 /* Bad Cylinder */
#define ST2_SNS 0x04 /* Scan Not Satisfied */
#define ST2_SEH 0x08 /* Scan Equal Hit */
#define ST2_WC 0x10 /* Wrong Cylinder */
#define ST2_CRC 0x20 /* CRC error in data field */
#define ST2_CM 0x40 /* Control Mark = deleted */
/* Bits of FD_ST3 */
#define ST3_HA 0x04 /* Head (Address) */
#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */
#define ST3_WP 0x40 /* Write Protect */
/* Values for FD_COMMAND */
#define FD_RECALIBRATE 0x07 /* move to track 0 */
#define FD_SEEK 0x0F /* seek track */
#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */
#define FD_WRITE 0xC5 /* write with MT, MFM */
#define FD_SENSEI 0x08 /* Sense Interrupt Status */
#define FD_SPECIFY 0x03 /* specify HUT etc */
/* DMA commands */
#define DMA_READ 0x46
#define DMA_WRITE 0x4A
#endif
...@@ -21,17 +21,19 @@ ...@@ -21,17 +21,19 @@
* 7 - unnamed pipes * 7 - unnamed pipes
*/ */
#define IS_BLOCKDEV(x) ((x)==2 || (x)==3) #define IS_SEEKABLE(x) ((x)>=1 && (x)<=3)
#define READ 0 #define READ 0
#define WRITE 1 #define WRITE 1
#define READA 2 /* read-ahead - don't pause */
void buffer_init(void); void buffer_init(long buffer_end);
#define MAJOR(a) (((unsigned)(a))>>8) #define MAJOR(a) (((unsigned)(a))>>8)
#define MINOR(a) ((a)&0xff) #define MINOR(a) ((a)&0xff)
#define NAME_LEN 14 #define NAME_LEN 14
#define ROOT_INO 1
#define I_MAP_SLOTS 8 #define I_MAP_SLOTS 8
#define Z_MAP_SLOTS 8 #define Z_MAP_SLOTS 8
...@@ -44,6 +46,7 @@ void buffer_init(void); ...@@ -44,6 +46,7 @@ void buffer_init(void);
#define NR_HASH 307 #define NR_HASH 307
#define NR_BUFFERS nr_buffers #define NR_BUFFERS nr_buffers
#define BLOCK_SIZE 1024 #define BLOCK_SIZE 1024
#define BLOCK_SIZE_BITS 10
#ifndef NULL #ifndef NULL
#define NULL ((void *) 0) #define NULL ((void *) 0)
#endif #endif
...@@ -51,6 +54,14 @@ void buffer_init(void); ...@@ -51,6 +54,14 @@ void buffer_init(void);
#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode))) #define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry))) #define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))
#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))
typedef char buffer_block[BLOCK_SIZE]; typedef char buffer_block[BLOCK_SIZE];
struct buffer_head { struct buffer_head {
...@@ -101,14 +112,6 @@ struct m_inode { ...@@ -101,14 +112,6 @@ struct m_inode {
unsigned char i_update; unsigned char i_update;
}; };
#define PIPE_HEAD(inode) (((long *)((inode).i_zone))[0])
#define PIPE_TAIL(inode) (((long *)((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))
struct file { struct file {
unsigned short f_mode; unsigned short f_mode;
unsigned short f_flags; unsigned short f_flags;
...@@ -133,10 +136,23 @@ struct super_block { ...@@ -133,10 +136,23 @@ struct super_block {
struct m_inode * s_isup; struct m_inode * s_isup;
struct m_inode * s_imount; struct m_inode * s_imount;
unsigned long s_time; unsigned long s_time;
struct task_struct * s_wait;
unsigned char s_lock;
unsigned char s_rd_only; unsigned char s_rd_only;
unsigned char s_dirt; unsigned char s_dirt;
}; };
struct d_super_block {
unsigned short s_ninodes;
unsigned short s_nzones;
unsigned short s_imap_blocks;
unsigned short s_zmap_blocks;
unsigned short s_firstdatazone;
unsigned short s_log_zone_size;
unsigned long s_max_size;
unsigned short s_magic;
};
struct dir_entry { struct dir_entry {
unsigned short inode; unsigned short inode;
char name[NAME_LEN]; char name[NAME_LEN];
...@@ -148,6 +164,11 @@ extern struct super_block super_block[NR_SUPER]; ...@@ -148,6 +164,11 @@ extern struct super_block super_block[NR_SUPER];
extern struct buffer_head * start_buffer; extern struct buffer_head * start_buffer;
extern int nr_buffers; extern int nr_buffers;
extern void check_disk_change(int dev);
extern int floppy_change(unsigned int nr);
extern int ticks_to_floppy_on(unsigned int dev);
extern void floppy_on(unsigned int dev);
extern void floppy_off(unsigned int dev);
extern void truncate(struct m_inode * inode); extern void truncate(struct m_inode * inode);
extern void sync_inodes(void); extern void sync_inodes(void);
extern void wait_on(struct m_inode * inode); extern void wait_on(struct m_inode * inode);
...@@ -165,21 +186,15 @@ extern struct buffer_head * getblk(int dev, int block); ...@@ -165,21 +186,15 @@ extern struct buffer_head * getblk(int dev, int block);
extern void ll_rw_block(int rw, struct buffer_head * bh); extern void ll_rw_block(int rw, struct buffer_head * bh);
extern void brelse(struct buffer_head * buf); extern void brelse(struct buffer_head * buf);
extern struct buffer_head * bread(int dev,int block); extern struct buffer_head * bread(int dev,int block);
extern struct buffer_head * breada(int dev,int block,...);
extern int new_block(int dev); extern int new_block(int dev);
extern void free_block(int dev, int block); extern void free_block(int dev, int block);
extern struct m_inode * new_inode(int dev); extern struct m_inode * new_inode(int dev);
extern void free_inode(struct m_inode * inode); extern void free_inode(struct m_inode * inode);
extern int sync_dev(int dev);
extern struct super_block * get_super(int dev);
extern int ROOT_DEV;
extern void mount_root(void); extern void mount_root(void);
extern inline struct super_block * get_super(int dev)
{
struct super_block * s;
for(s = 0+super_block;s < NR_SUPER+super_block; s++)
if (s->s_dev == dev)
return s;
return NULL;
}
#endif #endif
...@@ -6,40 +6,6 @@ ...@@ -6,40 +6,6 @@
#ifndef _HDREG_H #ifndef _HDREG_H
#define _HDREG_H #define _HDREG_H
/* currently supports only 1 hd, put type here */
#define HARD_DISK_TYPE 17
/*
* Ok, hard-disk-type is currently hardcoded. Not beatiful,
* but easier. We don't use BIOS for anything else, why should
* we get HD-type from it? Get these values from Reference Guide.
*/
#if HARD_DISK_TYPE == 17
#define _CYL 977
#define _HEAD 5
#define __WPCOM 300
#define _LZONE 977
#define _SECT 17
#define _CTL 0
#elif HARD_DISK_TYPE == 18
#define _CYL 977
#define _HEAD 7
#define __WPCOM (-1)
#define _LZONE 977
#define _SECT 17
#define _CTL 0
#else
#error Define HARD_DISK_TYPE and parameters, add your own entries as well
#endif
/* Controller wants just wp-com/4 */
#if __WPCOM >= 0
#define _WPCOM ((__WPCOM)>>2)
#else
#define _WPCOM __WPCOM
#endif
/* Hd controller regs. Ref: IBM AT Bios-listing */ /* Hd controller regs. Ref: IBM AT Bios-listing */
#define HD_DATA 0x1f0 /* _CTL when writing */ #define HD_DATA 0x1f0 /* _CTL when writing */
#define HD_ERROR 0x1f1 /* see err-bits */ #define HD_ERROR 0x1f1 /* see err-bits */
......
...@@ -6,3 +6,16 @@ volatile void panic(const char * str); ...@@ -6,3 +6,16 @@ volatile void panic(const char * str);
int printf(const char * fmt, ...); int printf(const char * fmt, ...);
int printk(const char * fmt, ...); int printk(const char * fmt, ...);
int tty_write(unsigned ch,char * buf,int count); int tty_write(unsigned ch,char * buf,int count);
void *malloc(unsigned int len);
void free_s(void *obj, int size);
#define free(x) free_s((x), 0)
/*
* This is defined as a macro, but at some point this might become a
* real subroutine that sets a flag if it returns true (to do
* BSD-style accounting where the process is flagged if it uses root
* privs). The implication of this is that you should do normal
* permissions checks first, and check suser() last.
*/
#define suser() (current->euid == 0)
/*
* 'kernel.h' contains some often-used function prototypes etc
*/
void verify_area(void * addr,int count);
volatile void panic(const char * str);
int printf(const char * fmt, ...);
int printk(const char * fmt, ...);
int tty_write(unsigned ch,char * buf,int count);
/*
* This is defined as a macro, but at some point this might become a
* real subroutine that sets a flag if it returns true (to do
* BSD-style accounting where the process is flagged if it uses root
* privs). The implication of this is that you should do normal
* permissions checks first, and check suser() last.
*/
#define suser() (current->euid == 0)
/*
* 'kernel.h' contains some often-used function prototypes etc
*/
void verify_area(void * addr,int count);
volatile void panic(const char * str);
int printf(const char * fmt, ...);
int printk(const char * fmt, ...);
int tty_write(unsigned ch,char * buf,int count);
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/head.h> #include <linux/head.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <signal.h>
#if (NR_OPEN > 32) #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 are in one word, max 32 files/proc"
...@@ -80,8 +81,8 @@ struct task_struct { ...@@ -80,8 +81,8 @@ struct task_struct {
long counter; long counter;
long priority; long priority;
long signal; long signal;
fn_ptr sig_restorer; struct sigaction sigaction[32];
fn_ptr sig_fn[32]; long blocked; /* bitmap of masked signals */
/* various fields */ /* various fields */
int exit_code; int exit_code;
unsigned long end_code,end_data,brk,start_stack; unsigned long end_code,end_data,brk,start_stack;
...@@ -110,13 +111,13 @@ struct task_struct { ...@@ -110,13 +111,13 @@ struct task_struct {
*/ */
#define INIT_TASK \ #define INIT_TASK \
/* state etc */ { 0,15,15, \ /* state etc */ { 0,15,15, \
/* signals */ 0,NULL,{(fn_ptr) 0,}, \ /* signals */ 0,{{},},0, \
/* ec,brk... */ 0,0,0,0,0, \ /* ec,brk... */ 0,0,0,0,0, \
/* pid etc.. */ 0,-1,0,0,0, \ /* pid etc.. */ 0,-1,0,0,0, \
/* uid etc */ 0,0,0,0,0,0, \ /* uid etc */ 0,0,0,0,0,0, \
/* alarm */ 0,0,0,0,0,0, \ /* alarm */ 0,0,0,0,0,0, \
/* math */ 0, \ /* math */ 0, \
/* fs info */ -1,0133,NULL,NULL,0, \ /* fs info */ -1,0022,NULL,NULL,0, \
/* filp */ {NULL,}, \ /* filp */ {NULL,}, \
{ \ { \
{0,0}, \ {0,0}, \
...@@ -139,6 +140,7 @@ extern long startup_time; ...@@ -139,6 +140,7 @@ extern long startup_time;
#define CURRENT_TIME (startup_time+jiffies/HZ) #define CURRENT_TIME (startup_time+jiffies/HZ)
extern void add_timer(long jiffies, void (*fn)(void));
extern void sleep_on(struct task_struct ** p); extern void sleep_on(struct task_struct ** p);
extern void interruptible_sleep_on(struct task_struct ** p); extern void interruptible_sleep_on(struct task_struct ** p);
extern void wake_up(struct task_struct ** p); extern void wake_up(struct task_struct ** p);
...@@ -169,15 +171,15 @@ __asm__("str %%ax\n\t" \ ...@@ -169,15 +171,15 @@ __asm__("str %%ax\n\t" \
struct {long a,b;} __tmp; \ struct {long a,b;} __tmp; \
__asm__("cmpl %%ecx,_current\n\t" \ __asm__("cmpl %%ecx,_current\n\t" \
"je 1f\n\t" \ "je 1f\n\t" \
"xchgl %%ecx,_current\n\t" \
"movw %%dx,%1\n\t" \ "movw %%dx,%1\n\t" \
"xchgl %%ecx,_current\n\t" \
"ljmp %0\n\t" \ "ljmp %0\n\t" \
"cmpl %%ecx,%2\n\t" \ "cmpl %%ecx,_last_task_used_math\n\t" \
"jne 1f\n\t" \ "jne 1f\n\t" \
"clts\n" \ "clts\n" \
"1:" \ "1:" \
::"m" (*&__tmp.a),"m" (*&__tmp.b), \ ::"m" (*&__tmp.a),"m" (*&__tmp.b), \
"m" (last_task_used_math),"d" _TSS(n),"c" ((long) task[n])); \ "d" (_TSS(n)),"c" ((long) task[n])); \
} }
#define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000) #define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)
......
...@@ -65,6 +65,9 @@ extern int sys_dup2(); ...@@ -65,6 +65,9 @@ extern int sys_dup2();
extern int sys_getppid(); extern int sys_getppid();
extern int sys_getpgrp(); extern int sys_getpgrp();
extern int sys_setsid(); extern int sys_setsid();
extern int sys_sigaction();
extern int sys_sgetmask();
extern int sys_ssetmask();
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, 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, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
...@@ -77,4 +80,4 @@ sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid, ...@@ -77,4 +80,4 @@ sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys, 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_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid, sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp,sys_setsid}; sys_getpgrp,sys_setsid,sys_sigaction,sys_sgetmask,sys_ssetmask };
...@@ -33,11 +33,14 @@ struct tty_queue { ...@@ -33,11 +33,14 @@ struct tty_queue {
#define PUTCH(c,queue) \ #define PUTCH(c,queue) \
(void)({(queue).buf[(queue).head]=(c);INC((queue).head);}) (void)({(queue).buf[(queue).head]=(c);INC((queue).head);})
#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF])
#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR]) #define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR])
#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP]) #define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT])
#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART])
#define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE]) #define ERASE_CHAR(tty) ((tty)->termios.c_cc[VERASE])
#define KILL_CHAR(tty) ((tty)->termios.c_cc[VKILL])
#define EOF_CHAR(tty) ((tty)->termios.c_cc[VEOF])
#define START_CHAR(tty) ((tty)->termios.c_cc[VSTART])
#define STOP_CHAR(tty) ((tty)->termios.c_cc[VSTOP])
#define SUSPEND_CHAR(tty) ((tty)->termios.c_cc[VSUSP])
struct tty_struct { struct tty_struct {
struct termios termios; struct termios termios;
...@@ -53,11 +56,11 @@ extern struct tty_struct tty_table[]; ...@@ -53,11 +56,11 @@ extern struct tty_struct tty_table[];
/* intr=^C quit=^| erase=del kill=^U /* intr=^C quit=^| erase=del kill=^U
eof=^D vtime=\0 vmin=\1 sxtc=\0 eof=^D vtime=\0 vmin=\1 sxtc=\0
start=^Q stop=^S susp=^Y eol=\0 start=^Q stop=^S susp=^Z eol=\0
reprint=^R discard=^U werase=^W lnext=^V reprint=^R discard=^U werase=^W lnext=^V
eol2=\0 eol2=\0
*/ */
#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\031\0\022\017\027\026\0" #define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
void rs_init(void); void rs_init(void);
void con_init(void); void con_init(void);
......
...@@ -35,6 +35,8 @@ typedef unsigned int sigset_t; /* 32 bits */ ...@@ -35,6 +35,8 @@ typedef unsigned int sigset_t; /* 32 bits */
/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */ /* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
#define SA_NOCLDSTOP 1 #define SA_NOCLDSTOP 1
#define SA_NOMASK 0x40000000
#define SA_ONESHOT 0x80000000
#define SIG_BLOCK 0 /* for blocking signals */ #define SIG_BLOCK 0 /* for blocking signals */
#define SIG_UNBLOCK 1 /* for unblocking signals */ #define SIG_UNBLOCK 1 /* for unblocking signals */
...@@ -47,6 +49,7 @@ struct sigaction { ...@@ -47,6 +49,7 @@ struct sigaction {
void (*sa_handler)(int); void (*sa_handler)(int);
sigset_t sa_mask; sigset_t sa_mask;
int sa_flags; int sa_flags;
void (*sa_restorer)(void);
}; };
void (*signal(int _sig, void (*_func)(int)))(int); void (*signal(int _sig, void (*_func)(int)))(int);
......
...@@ -124,6 +124,7 @@ ...@@ -124,6 +124,7 @@
#define __NR_getppid 64 #define __NR_getppid 64
#define __NR_getpgrp 65 #define __NR_getpgrp 65
#define __NR_setsid 66 #define __NR_setsid 66
#define __NR_sigaction 67
#define _syscall0(type,name) \ #define _syscall0(type,name) \
type name(void) \ type name(void) \
......
/*
* linux/init/main.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
...@@ -16,7 +22,7 @@ ...@@ -16,7 +22,7 @@
*/ */
static inline _syscall0(int,fork) static inline _syscall0(int,fork)
static inline _syscall0(int,pause) static inline _syscall0(int,pause)
static inline _syscall0(int,setup) static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync) static inline _syscall0(int,sync)
#include <linux/tty.h> #include <linux/tty.h>
...@@ -37,10 +43,21 @@ static char printbuf[1024]; ...@@ -37,10 +43,21 @@ static char printbuf[1024];
extern int vsprintf(); extern int vsprintf();
extern void init(void); extern void init(void);
extern void blk_dev_init(void);
extern void chr_dev_init(void);
extern void hd_init(void); extern void hd_init(void);
extern void floppy_init(void);
extern void mem_init(long start, long end);
extern long kernel_mktime(struct tm * tm); extern long kernel_mktime(struct tm * tm);
extern long startup_time; extern long startup_time;
/*
* This is set up by the setup-routine at boot-time
*/
#define EXT_MEM_K (*(unsigned short *)0x90002)
#define DRIVE_INFO (*(struct drive_info *)0x90080)
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
/* /*
* Yeah, yeah, it's ugly, but I cannot find how to do this correctly * Yeah, yeah, it's ugly, but I cannot find how to do this correctly
* and this seems to work. I anybody has more info on the real-time * and this seems to work. I anybody has more info on the real-time
...@@ -64,7 +81,7 @@ static void time_init(void) ...@@ -64,7 +81,7 @@ static void time_init(void)
time.tm_min = CMOS_READ(2); time.tm_min = CMOS_READ(2);
time.tm_hour = CMOS_READ(4); time.tm_hour = CMOS_READ(4);
time.tm_mday = CMOS_READ(7); time.tm_mday = CMOS_READ(7);
time.tm_mon = CMOS_READ(8)-1; time.tm_mon = CMOS_READ(8);
time.tm_year = CMOS_READ(9); time.tm_year = CMOS_READ(9);
} while (time.tm_sec != CMOS_READ(0)); } while (time.tm_sec != CMOS_READ(0));
BCD_TO_BIN(time.tm_sec); BCD_TO_BIN(time.tm_sec);
...@@ -73,21 +90,41 @@ static void time_init(void) ...@@ -73,21 +90,41 @@ static void time_init(void)
BCD_TO_BIN(time.tm_mday); BCD_TO_BIN(time.tm_mday);
BCD_TO_BIN(time.tm_mon); BCD_TO_BIN(time.tm_mon);
BCD_TO_BIN(time.tm_year); BCD_TO_BIN(time.tm_year);
time.tm_mon--;
startup_time = kernel_mktime(&time); startup_time = kernel_mktime(&time);
} }
static long memory_end = 0;
static long buffer_memory_end = 0;
struct drive_info { char dummy[32]; } drive_info;
void main(void) /* This really IS void, no error here. */ void main(void) /* This really IS void, no error here. */
{ /* The startup routine assumes (well, ...) this */ { /* The startup routine assumes (well, ...) this */
/* /*
* Interrupts are still disabled. Do necessary setups, then * Interrupts are still disabled. Do necessary setups, then
* enable them * enable them
*/ */
time_init(); ROOT_DEV = ORIG_ROOT_DEV;
tty_init(); drive_info = DRIVE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
if (memory_end > 16*1024*1024)
memory_end = 16*1024*1024;
if (memory_end > 6*1024*1024)
buffer_memory_end = 2*1024*1024;
else
buffer_memory_end = 1*1024*1024;
mem_init(buffer_memory_end,memory_end);
trap_init(); trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init(); sched_init();
buffer_init(); buffer_init(buffer_memory_end);
hd_init(); hd_init();
floppy_init();
sti(); sti();
move_to_user_mode(); move_to_user_mode();
if (!fork()) { /* we count on this going ok */ if (!fork()) { /* we count on this going ok */
...@@ -114,14 +151,14 @@ static int printf(const char *fmt, ...) ...@@ -114,14 +151,14 @@ static int printf(const char *fmt, ...)
return i; return i;
} }
static char * argv[] = { "-",NULL }; static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL }; static char * envp[] = { "HOME=/usr/root", NULL };
void init(void) void init(void)
{ {
int i,j; int i,j;
setup(); setup((void *) &drive_info);
if (!fork()) if (!fork())
_exit(execve("/bin/update",NULL,NULL)); _exit(execve("/bin/update",NULL,NULL));
(void) open("/dev/tty0",O_RDWR,0); (void) open("/dev/tty0",O_RDWR,0);
...@@ -129,6 +166,7 @@ void init(void) ...@@ -129,6 +166,7 @@ void init(void)
(void) dup(0); (void) dup(0);
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS, printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE); NR_BUFFERS*BLOCK_SIZE);
printf("Free mem: %d bytes\n\r",memory_end-buffer_memory_end);
printf(" Ok.\n\r"); printf(" Ok.\n\r");
if ((i=fork())<0) if ((i=fork())<0)
printf("Fork failed in init\r\n"); printf("Fork failed in init\r\n");
......
...@@ -25,29 +25,28 @@ CPP =gcc -E -nostdinc -I../include ...@@ -25,29 +25,28 @@ CPP =gcc -E -nostdinc -I../include
-c -o $*.o $< -c -o $*.o $<
OBJS = sched.o system_call.o traps.o asm.o fork.o \ OBJS = sched.o system_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o tty_io.o console.o \ panic.o printk.o vsprintf.o sys.o exit.o \
keyboard.o rs_io.o hd.o sys.o exit.o serial.o \ signal.o mktime.o
mktime.o
kernel.o: $(OBJS) kernel.o: $(OBJS)
$(LD) -r -o kernel.o $(OBJS) $(LD) -r -o kernel.o $(OBJS)
sync sync
clean: clean:
rm -f core *.o *.a tmp_make rm -f core *.o *.a tmp_make keyboard.s
for i in *.c;do rm -f `basename $$i .c`.s;done for i in *.c;do rm -f `basename $$i .c`.s;done
(cd chr_drv; make clean)
(cd blk_drv; make clean)
dep: dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \ (for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make $(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile cp tmp_make Makefile
(cd chr_drv; make dep)
(cd blk_drv; make dep)
### Dependencies: ### 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/linux/tty.h ../include/termios.h ../include/asm/io.h \
../include/asm/system.h
exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \ 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/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/head.h ../include/linux/fs.h ../include/linux/mm.h \
...@@ -55,36 +54,29 @@ exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \ ...@@ -55,36 +54,29 @@ exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \
../include/asm/segment.h ../include/asm/segment.h
fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.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/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/asm/segment.h \ ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/system.h ../include/asm/segment.h ../include/asm/system.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/linux/kernel.h ../include/linux/hdreg.h \
../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h
mktime.s mktime.o : mktime.c ../include/time.h mktime.s mktime.o : mktime.c ../include/time.h
panic.s panic.o : panic.c ../include/linux/kernel.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
printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \ printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \
../include/linux/kernel.h ../include/linux/kernel.h
sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.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/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/signal.h ../include/linux/sys.h \ ../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \
../include/asm/system.h ../include/asm/io.h ../include/asm/segment.h ../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \
serial.s serial.o : serial.c ../include/linux/tty.h ../include/termios.h \ ../include/asm/segment.h
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \ signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h \
../include/sys/types.h ../include/linux/mm.h ../include/asm/system.h \ ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/asm/io.h ../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h
sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.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/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/tty.h ../include/termios.h \ ../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \
../include/linux/kernel.h ../include/asm/segment.h ../include/sys/times.h \ ../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \
../include/sys/utsname.h ../include/sys/times.h ../include/sys/utsname.h
traps.s traps.o : traps.c ../include/string.h ../include/linux/head.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/sched.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h \ ../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h ../include/asm/system.h ../include/asm/segment.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
vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h
head 1.3;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.3
date 91.12.01.09.21.46; author tytso; state Exp;
branches ;
next 1.2;
1.2
date 91.11.20.00.10.00; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.16.21.02.17; author tytso; state Exp;
branches ;
next ;
desc
@@
1.3
log
@Patches sent to Linus
@
text
@/*
* linux/kernel/exit.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>
int sys_pause(void);
int sys_close(int fd);
void release(struct task_struct * p)
{
int i;
if (!p)
return;
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
free_page((long)p);
schedule();
return;
}
panic("trying to release non-existent task");
}
static inline int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32)
return -EINVAL;
if (priv || (current->euid==p->euid) || suser())
p->signal |= (1<<(sig-1));
else
return -EPERM;
return 0;
}
static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;
while (--p > &FIRST_TASK) {
if (*p && (*p)->session == current->session)
(*p)->signal |= 1<<(SIGHUP-1);
}
}
/*
* XXX need to check permissions needed to send signals to process
* groups, etc. etc. kill() permissions semantics are tricky!
*/
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 (err = send_sig(sig,*p,0))
retval = err;
return retval;
}
static void tell_father(int pid)
{
int i;
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 */
release(current);
}
int do_exit(long code)
{
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 = 0;
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
sys_close(i);
iput(current->pwd);
current->pwd=NULL;
iput(current->root);
current->root=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->state = TASK_ZOMBIE;
current->exit_code = code;
tell_father(current->father);
schedule();
return (-1); /* just to suppress warnings */
}
int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
int flag;
struct task_struct ** p;
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;
if (pid>0) {
if ((*p)->pid != pid)
continue;
} else if (!pid) {
if ((*p)->pgrp != current->pgrp)
continue;
} else if (pid != -1) {
if ((*p)->pgrp != -pid)
continue;
}
switch ((*p)->state) {
case TASK_STOPPED:
if (!(options & WUNTRACED))
continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
case TASK_ZOMBIE:
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
put_fs_long((*p)->exit_code,stat_addr);
release(*p);
return flag;
default:
flag=1;
continue;
}
}
if (flag) {
if (options & WNOHANG)
return 0;
current->state=TASK_INTERRUPTIBLE;
schedule();
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
else
return -EINTR;
}
return -ECHILD;
}
@
1.2
log
@Fixed bug in waitpid() so that the proper exit code would be returned.
@
text
@d35 1
a35 1
static inline void send_sig(long sig,struct task_struct * p,int priv)
d38 2
a39 6
return;
if (priv ||
current->uid==p->uid ||
current->euid==p->uid ||
current->uid==p->euid ||
current->euid==p->euid)
d41 3
d49 1
a49 1
d56 5
a60 1
void do_kill(long pid,long sig,int priv)
d63 1
d66 3
a68 2
if (*p && (*p)->pgrp == current->pid)
send_sig(sig,*p,priv);
d70 3
a72 2
if (*p && (*p)->pid == pid)
send_sig(sig,*p,priv);
d74 2
a75 1
send_sig(sig,*p,priv);
d78 3
a80 7
send_sig(sig,*p,priv);
}
int sys_kill(int pid,int sig)
{
do_kill(pid,sig,!(current->uid || current->euid));
return 0;
@
1.1
log
@Initial revision
@
text
@d164 1
a165 1
put_fs_long((*p)->exit_code,stat_addr);
@
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.12.01.09.21.22; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.21.08.47.30; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Patches sent to linus
@
text
@/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#include <linux/sched.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 <signal.h>
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
void show_task(int nr,struct task_struct * p)
{
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
printk("eip=%04x:%08x\n\r",p->tss.cs&0xffff,p->tss.eip);
}
void show_stat(void)
{
int i;
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
}
#define LATCH (1193180/HZ)
extern void mem_use(void);
extern int timer_interrupt(void);
extern int system_call(void);
union task_union {
struct task_struct task;
char stack[PAGE_SIZE];
};
static union task_union init_task = {INIT_TASK,};
long volatile jiffies=0;
long startup_time=0;
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&(init_task.task), };
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
void math_state_restore()
{
if (last_task_used_math == current)
return;
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
}
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387));
} else {
__asm__("fninit"::);
current->used_math=1;
}
last_task_used_math=current;
}
/*
* 'schedule()' is the scheduler function. This is GOOD CODE! There
* probably won't be any reason to change this, as it should work well
* in all circumstances (ie gives IO-bound processes good response etc).
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*/
void schedule(void)
{
int i,next,c;
struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
if ((*p)->alarm && (*p)->alarm < jiffies) {
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
/* this is the scheduler proper: */
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)
continue;
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i;
}
if (c) break;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
}
switch_to(next);
}
int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}
void sleep_on(struct task_struct **p)
{
struct task_struct *tmp;
if (!p)
return;
if (current == &(init_task.task))
panic("task[0] trying to sleep");
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
if (tmp)
tmp->state=0;
}
void interruptible_sleep_on(struct task_struct **p)
{
struct task_struct *tmp;
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 wake_up(struct task_struct **p)
{
if (p && *p) {
(**p).state=0;
*p=NULL;
}
}
/*
* OK, here are some floppy things that shouldn't be in the kernel
* proper. They are here because the floppy needs a timer, and this
* was the easiest way of doing it.
*/
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};
static int mon_timer[4]={0,0,0,0};
static int moff_timer[4]={0,0,0,0};
unsigned char current_DOR = 0x0C;
unsigned char selected = 0;
struct task_struct * wait_on_floppy_select = NULL;
void floppy_select(unsigned int nr)
{
if (nr>3)
printk("floppy_select: nr>3\n\r");
cli();
while (selected)
sleep_on(&wait_on_floppy_select);
current_DOR &= 0xFC;
current_DOR |= nr;
outb(current_DOR,FD_DOR);
sti();
}
void floppy_deselect(unsigned int nr)
{
if (nr != (current_DOR & 3))
printk("floppy_deselect: drive not selected\n\r");
selected = 0;
wake_up(&wait_on_floppy_select);
}
int ticks_to_floppy_on(unsigned int nr)
{
unsigned char mask = 1<<(nr+4);
if (nr>3)
panic("floppy_on: nr>3");
moff_timer[nr]=10000; /* 100 s = very big :-) */
cli(); /* use floppy_off to turn it off */
if (!(mask & current_DOR)) {
current_DOR |= mask;
if (!selected) {
current_DOR &= 0xFC;
current_DOR |= nr;
}
outb(current_DOR,FD_DOR);
mon_timer[nr] = HZ;
}
sti();
return mon_timer[nr];
}
void floppy_on(unsigned int nr)
{
cli();
while (ticks_to_floppy_on(nr))
sleep_on(nr+wait_motor);
sti();
}
void floppy_off(unsigned int nr)
{
moff_timer[nr]=3*HZ;
}
void do_floppy_timer(void)
{
int i;
unsigned char mask = 0x10;
for (i=0 ; i<4 ; i++,mask <<= 1) {
if (!(mask & current_DOR))
continue;
if (mon_timer[i]) {
if (!--mon_timer[i])
wake_up(i+wait_motor);
} else if (!moff_timer[i]) {
current_DOR &= ~mask;
outb(current_DOR,FD_DOR);
} else
moff_timer[i]--;
}
}
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
void do_timer(long cpl)
{
if (cpl)
current->utime++;
else
current->stime++;
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
if (current_DOR & 0xf0)
do_floppy_timer();
if ((--current->counter)>0) return;
current->counter=0;
if (!cpl) return;
schedule();
}
int sys_alarm(long seconds)
{
int old = current->alarm;
if (old)
old = (old - jiffies) / HZ;
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return (old);
}
int sys_getpid(void)
{
return current->pid;
}
int sys_getppid(void)
{
return current->father;
}
int sys_getuid(void)
{
return current->uid;
}
int sys_geteuid(void)
{
return current->euid;
}
int sys_getgid(void)
{
return current->gid;
}
int sys_getegid(void)
{
return current->egid;
}
int sys_nice(long increment)
{
if (current->priority-increment>0)
current->priority -= increment;
return 0;
}
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1;i<NR_TASKS;i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
ltr(0);
lldt(0);
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
set_intr_gate(0x20,&timer_interrupt);
outb(inb_p(0x21)&~0x01,0x21);
set_system_gate(0x80,&system_call);
}
@
1.1
log
@Initial revision
@
text
@d344 4
d349 1
a349 1
return seconds;
@
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.12.01.09.21.30; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.21.09.18.36; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Patches sent to linus
@
text
@/*
* linux/kernel/sys.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <sys/times.h>
#include <sys/utsname.h>
int sys_ftime()
{
return -ENOSYS;
}
int sys_break()
{
return -ENOSYS;
}
int sys_ptrace()
{
return -ENOSYS;
}
int sys_stty()
{
return -ENOSYS;
}
int sys_gtty()
{
return -ENOSYS;
}
int sys_rename()
{
return -ENOSYS;
}
int sys_prof()
{
return -ENOSYS;
}
int sys_setregid(int rgid, int egid)
{
if (rgid>0) {
if ((current->gid == rgid) ||
suser())
current->gid = rgid;
else
return(-EPERM);
}
if (egid>0) {
if ((current->gid == egid) ||
(current->egid == egid) ||
(current->sgid == egid) ||
suser())
current->egid = egid;
else
return(-EPERM);
}
return 0;
}
int sys_setgid(int gid)
{
return(sys_setregid(gid, gid));
}
int sys_acct()
{
return -ENOSYS;
}
int sys_phys()
{
return -ENOSYS;
}
int sys_lock()
{
return -ENOSYS;
}
int sys_mpx()
{
return -ENOSYS;
}
int sys_ulimit()
{
return -ENOSYS;
}
int sys_time(long * tloc)
{
int i;
i = CURRENT_TIME;
if (tloc) {
verify_area(tloc,4);
put_fs_long(i,(unsigned long *)tloc);
}
return i;
}
/*
* Unprivileged users may change the real user id to the effective uid
* or vice versa.
*/
int sys_setreuid(int ruid, int euid)
{
int old_ruid = current->uid;
if (ruid>0) {
if ((current->euid==ruid) ||
(old_ruid == ruid) ||
suser())
current->uid = ruid;
else
return(-EPERM);
}
if (euid>0) {
if ((old_ruid == euid) ||
(current->euid == euid) ||
suser())
current->euid = euid;
else {
current->uid = old_ruid;
return(-EPERM);
}
}
return 0;
}
int sys_setuid(int uid)
{
return(sys_setreuid(uid, uid));
}
int sys_stime(long * tptr)
{
if (!suser())
return -EPERM;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
return 0;
}
int sys_times(struct tms * tbuf)
{
if (tbuf) {
verify_area(tbuf,sizeof *tbuf);
put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
}
return jiffies;
}
int sys_brk(unsigned long end_data_seg)
{
if (end_data_seg >= current->end_code &&
end_data_seg < current->start_stack - 16384)
current->brk = end_data_seg;
return current->brk;
}
/*
* 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.
*/
int sys_setpgid(int pid, int pgid)
{
int i;
if (!pid)
pid = current->pid;
if (!pgid)
pgid = current->pid;
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->pid==pid) {
if (task[i]->leader)
return -EPERM;
if (task[i]->session != current->session)
return -EPERM;
task[i]->pgrp = pgid;
return 0;
}
return -ESRCH;
}
int sys_getpgrp(void)
{
return current->pgrp;
}
int sys_setsid(void)
{
if (current->leader && !suser())
return -EPERM;
current->leader = 1;
current->session = current->pgrp = current->pid;
current->tty = -1;
return current->pgrp;
}
int sys_uname(struct utsname * name)
{
static struct utsname thisname = {
"linux .0","nodename","release ","version ","machine "
};
int i;
if (!name) return -ERROR;
verify_area(name,sizeof *name);
for(i=0;i<sizeof *name;i++)
put_fs_byte(((char *) &thisname)[i],i+(char *) name);
return 0;
}
int sys_umask(int mask)
{
int old = current->umask;
current->umask = mask & 0777;
return (old);
}
@
1.1
log
@Initial revision
@
text
@d51 1
a51 1
int sys_setgid(int gid)
d53 4
a56 3
if (current->euid && current->uid)
if (current->gid==gid || current->sgid==gid)
current->egid = gid;
d58 11
a68 3
return -EPERM;
else
current->gid = current->egid = gid;
d72 5
d114 5
a118 1
int sys_setuid(int uid)
d120 7
a126 3
if (current->euid && current->uid)
if (uid==current->uid || current->suid==current->uid)
current->euid=uid;
d128 12
a139 3
return -EPERM;
else
current->euid=current->uid=uid;
d143 5
d150 1
a150 1
if (current->euid && current->uid)
d208 1
a208 3
if (current->uid && current->euid)
return -EPERM;
if (current->leader)
@
/*
* linux/kernel/asm.s
*
* (C) 1991 Linus Torvalds
*/
/* /*
* asm.s contains the low-level code for most hardware faults. * asm.s contains the low-level code for most hardware faults.
* page_exception is handled by the mm, so that isn't here. This * page_exception is handled by the mm, so that isn't here. This
...@@ -8,7 +14,7 @@ ...@@ -8,7 +14,7 @@
.globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
.globl _device_not_available,_double_fault,_coprocessor_segment_overrun .globl _device_not_available,_double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment .globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_coprocessor_error,_reserved .globl _general_protection,_coprocessor_error,_irq13,_reserved
_divide_error: _divide_error:
pushl $_do_divide_error pushl $_do_divide_error
...@@ -75,12 +81,9 @@ math_emulate: ...@@ -75,12 +81,9 @@ math_emulate:
_device_not_available: _device_not_available:
pushl %eax pushl %eax
movl %cr0,%eax movl %cr0,%eax
bt $2,%eax # EM (math emulation bit) testl $0x4,%eax # EM (math emulation bit)
jc math_emulate jne math_emulate
clts # clear TS so that we can use math clts # clear TS so that we can use math
movl _current,%eax
cmpl _last_task_used_math,%eax
je 1f # shouldn't happen really ...
pushl %ecx pushl %ecx
pushl %edx pushl %edx
push %ds push %ds
...@@ -90,7 +93,7 @@ _device_not_available: ...@@ -90,7 +93,7 @@ _device_not_available:
pop %ds pop %ds
popl %edx popl %edx
popl %ecx popl %ecx
1: popl %eax popl %eax
iret iret
_coprocessor_segment_overrun: _coprocessor_segment_overrun:
...@@ -101,7 +104,18 @@ _reserved: ...@@ -101,7 +104,18 @@ _reserved:
pushl $_do_reserved pushl $_do_reserved
jmp no_error_code jmp no_error_code
_irq13:
pushl %eax
xorb %al,%al
outb %al,$0xF0
movb $0x20,%al
outb %al,$0x20
jmp 1f
1: jmp 1f
1: outb %al,$0xA0
popl %eax
_coprocessor_error: _coprocessor_error:
fnclex
pushl $_do_coprocessor_error pushl $_do_coprocessor_error
jmp no_error_code jmp no_error_code
......
#
# Makefile for the FREAX-kernel block device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
-finline-functions -mstring-insns -nostdinc -I../../include
CPP =gcc -E -nostdinc -I../../include
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = ll_rw_blk.o floppy.o hd.o
blk_drv.a: $(OBJS)
$(AR) rcs blk_drv.a $(OBJS)
sync
clean:
rm -f core *.o *.a tmp_make
for i in *.c;do rm -f `basename $$i .c`.s;done
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
### 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
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/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
#ifndef _BLK_H
#define _BLK_H
#define NR_BLK_DEV 7
#define NR_REQUEST 64
/*
* Ok, this is an expanded form so that we can use the same
* request for paging requests when that is implemented. In
* paging, 'bh' is NULL, and 'waiting' is used to wait for
* read/write completion.
*/
struct request {
int dev; /* -1 if no request */
int cmd; /* READ or WRITE */
int errors;
unsigned long sector;
unsigned long nr_sectors;
char * buffer;
struct task_struct * waiting;
struct buffer_head * bh;
struct request * next;
};
#define IN_ORDER(s1,s2) \
((s1)->dev < (s2)->dev || ((s1)->dev == (s2)->dev && \
(s1)->sector < (s2)->sector))
struct blk_dev_struct {
void (*request_fn)(void);
struct request * current_request;
};
extern struct blk_dev_struct blk_dev[NR_BLK_DEV];
extern struct request request[NR_REQUEST];
extern struct task_struct * wait_for_request;
#ifdef MAJOR_NR
/*
* Add entries as needed. Currently the only block devices
* supported are hard-disks and floppies.
*/
#if (MAJOR_NR == 2)
/* floppy */
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
#define DEVICE_NR(device) ((device) & 3)
#define DEVICE_ON(device) floppy_on(DEVICE_NR(device))
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
#elif (MAJOR_NR == 3)
/* harddisk */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)/5)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#elif
/* unknown blk device */
#error "unknown blk device"
#endif
#define CURRENT (blk_dev[MAJOR_NR].current_request)
#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
void (*DEVICE_INTR)(void) = NULL;
static void (DEVICE_REQUEST)(void);
extern inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock)
printk(DEVICE_NAME ": free buffer being unlocked\n");
bh->b_lock=0;
wake_up(&bh->b_wait);
}
extern inline void end_request(int uptodate)
{
DEVICE_OFF(CURRENT->dev);
if (CURRENT->bh) {
CURRENT->bh->b_uptodate = uptodate;
unlock_buffer(CURRENT->bh);
}
if (!uptodate) {
printk(DEVICE_NAME " I/O error\n\r");
printk("dev %04x, block %d\n\r",CURRENT->dev,
CURRENT->bh->b_blocknr);
}
wake_up(&CURRENT->waiting);
wake_up(&wait_for_request);
CURRENT->dev = -1;
CURRENT = CURRENT->next;
}
#define INIT_REQUEST \
repeat: \
if (!CURRENT) \
return; \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) \
if (!CURRENT->bh->b_lock) \
panic(DEVICE_NAME ": block not locked"); \
else { \
CURRENT->bh->b_dirt = 0; \
CURRENT->bh->b_uptodate = 0; \
}
#endif
#endif
/*
* linux/kernel/floppy.c
*
* (C) 1991 Linus Torvalds
*/
/*
* As with hd.c, all routines within this file can (and will) be called
* by interrupts, so extreme caution is needed. A hardware interrupt
* handler may not sleep, or a kernel panic will happen. Thus I cannot
* call "floppy-on" directly, but have to set a special timer interrupt
* etc.
*
* Also, I'm not certain this works on more than 1 floppy. Bugs may
* abund.
*/
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/fdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR 2
#include "blk.h"
static void reset_floppy(void);
static void seek_interrupt(void);
static void rw_interrupt(void);
extern unsigned char current_DOR;
extern unsigned char selected;
#define immoutb_p(val,port) \
__asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))
#define TYPE(x) ((x)>>2)
#define DRIVE(x) ((x)&0x03)
/*
* Note that MAX_ERRORS=10 doesn't imply that we retry every bad read
* max 10 times - some types of errors increase the errorcount by 2,
* so we might actually retry only 6-7 times before giving up.
*/
#define MAX_ERRORS 10
/*
* globals used by 'result()'
*/
#define MAX_REPLIES 7
static unsigned char reply_buffer[MAX_REPLIES];
#define ST0 (reply_buffer[0])
#define ST1 (reply_buffer[1])
#define ST2 (reply_buffer[2])
#define ST3 (reply_buffer[3])
/*
* This struct defines the different floppy types. Unlike minix
* linux doesn't have a "search for right type"-type, as the code
* for that is convoluted and weird.
*
* The 'stretch' tells if the tracks need to be boubled for some
* types (ie 360kB diskette in 1.2MB drive etc). Others should
* be self-explanatory.
*/
static struct floppy_struct {
int size, sect, head, track, stretch;
unsigned char gap,rate,spec1;
} floppy_type[] = {
{ 0, 0,0, 0,0,0x00,0x00,0x00 }, /* no testing */
{ 720, 9,2,40,0,0x2A,0x02,0xDF }, /* 360kB PC diskettes */
{ 2400,15,2,80,0,0x1B,0x00,0xDF }, /* 1.2 MB AT-diskettes */
{ 720, 9,2,40,1,0x2A,0x02,0xDF }, /* 360kB in 720kB drive */
{ 1440, 9,2,80,0,0x2A,0x02,0xDF }, /* 3.5" 720kB diskette */
{ 720, 9,2,40,1,0x23,0x01,0xDF }, /* 360kB in 1.2MB drive */
{ 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */
{ 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */
};
/*
* 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),
* H is head unload time (1=16ms, 2=32ms, etc)
*
* Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc)
* and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
*/
extern void floppy_interrupt(void);
extern char tmp_floppy_area[1024];
/*
* These are global variables, as that's the easiest way to give
* information to interrupts. They are the data used for the current
* request.
*/
static int cur_spec1 = -1;
static int cur_rate = -1;
static struct floppy_struct * floppy = floppy_type;
static unsigned char current_drive = 0;
static unsigned char sector = 0;
static unsigned char head = 0;
static unsigned char track = 0;
static unsigned char seek_track = 0;
static unsigned char command = 0;
/*
* floppy-change is never called from an interrupt, so we can relax a bit
* here.
*/
int floppy_change(unsigned int nr)
{
floppy_on(nr);
floppy_select(nr);
if (inb(FD_DIR) & 0x80) {
floppy_off(nr);
return 1;
}
floppy_off(nr);
return 0;
}
#define copy_buffer(from,to) \
__asm__("cld ; rep ; movsl" \
::"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \
:"cx","di","si")
static void setup_DMA(void)
{
long addr = (long) CURRENT->buffer;
if (addr >= 0x100000) {
addr = (long) tmp_floppy_area;
if (command == FD_WRITE)
copy_buffer(CURRENT->buffer,tmp_floppy_area);
}
/* mask DMA 2 */
immoutb_p(4|2,10);
/* output command byte. I don't know why, but everyone (minix, */
/* sanches & canton) output this twice, first to 12 then to 11 */
__asm__("outb %%al,$12\n\tjmp 1f\n1:\tjmp 1f\n1:\t"
"outb %%al,$11\n\tjmp 1f\n1:\tjmp 1f\n1:"::
"a" ((char) ((command == FD_READ)?DMA_READ:DMA_WRITE)));
/* 8 low bits of addr */
immoutb_p(addr,4);
addr >>= 8;
/* bits 8-15 of addr */
immoutb_p(addr,4);
addr >>= 8;
/* bits 16-19 of addr */
immoutb_p(addr,0x81);
/* low 8 bits of count-1 (1024-1=0x3ff) */
immoutb_p(0xff,5);
/* high 8 bits of count-1 */
immoutb_p(3,5);
/* activate DMA 2 */
immoutb_p(0|2,10);
}
static void output_byte(char byte)
{
int counter;
unsigned char status;
for(counter = 0 ; counter < 10000 ; counter++) {
status = inb(FD_STATUS) & (STATUS_READY | STATUS_DIR);
if (status == STATUS_READY) {
outb(byte,FD_DATA);
return;
}
}
printk("Unable to send byte to FDC\n\r");
}
static int result(void)
{
int i = 0, counter, status;
for (counter = 0 ; counter < 10000 ; counter++) {
status = inb(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY);
if (status == STATUS_READY)
return i;
if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
if (i >= MAX_REPLIES)
break;
reply_buffer[i++] = inb(FD_DATA);
}
}
printk("Getstatus times out\n\r");
return -1;
}
/*
* This is the routine called after every seek (or recalibrate) interrupt
* from the floppy controller. Note that the "unexpected interrupt" routine
* also does a recalibrate, but doesn't come here.
*/
static void seek_interrupt(void)
{
/* sense drive status */
output_byte(FD_SENSEI);
if (result() != 2 || (ST0 & 0xF8) != 0x20) {
CURRENT->errors++;
if (CURRENT->errors > MAX_ERRORS) {
floppy_deselect(current_drive);
end_request(0);
reset_floppy();
return;
}
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
return;
}
/* are we on the right track? */
if (ST1 != seek_track) {
CURRENT->errors++;
if (CURRENT->errors > MAX_ERRORS) {
floppy_deselect(current_drive);
end_request(0);
reset_floppy();
return;
}
output_byte(FD_SEEK);
output_byte(head<<2 | current_drive);
output_byte(seek_track);
return;
}
/* yes - set up DMA and read/write command */
setup_DMA();
do_floppy = rw_interrupt;
output_byte(command);
output_byte(head<<2 | current_drive);
output_byte(track);
output_byte(head);
output_byte(sector);
output_byte(2); /* sector size = 512 */
output_byte(floppy->sect);
output_byte(floppy->gap);
output_byte(0xFF); /* sector size (0xff when n!=0 ?) */
}
/*
* Ok, this interrupt is called after a DMA read/write has succeeded,
* so we check the results, and copy any buffers.
*/
static void rw_interrupt(void)
{
if (result() != 7 || (ST0 & 0xf8) || (ST1 & 0xbf) ||
(ST2 & 0x73)) {
CURRENT->errors++;
if (CURRENT->errors > MAX_ERRORS || (ST1 & 0x02)) {
if (ST1 & 0x02)
printk("Drive %d is write protected\n\r",
current_drive);
floppy_deselect(current_drive);
end_request(0);
do_fd_request();
return;
}
do_floppy = seek_interrupt;
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
return;
}
if (command == FD_READ && (long)(CURRENT->buffer) >= 0x100000)
copy_buffer(tmp_floppy_area,CURRENT->buffer);
floppy_deselect(current_drive);
end_request(1);
do_fd_request();
}
/*
* This routine is called when everything should be correctly set up
* for the transfer (ie floppy motor is on and the correct floppy is
* selected).
*/
static void transfer(void)
{
if (cur_spec1 != floppy->spec1) {
cur_spec1 = floppy->spec1;
output_byte(FD_SPECIFY);
output_byte(cur_spec1); /* hut etc */
output_byte(6); /* Head load time =6ms, DMA */
}
if (cur_rate != floppy->rate)
outb_p(cur_rate = floppy->rate,FD_DCR);
do_floppy = seek_interrupt;
if (seek_track) {
output_byte(FD_SEEK);
output_byte(head<<2 | current_drive);
output_byte(seek_track);
} else {
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
}
}
/*
* Special case - used after a unexpected interrupt (or reset)
*/
static void recal_interrupt(void)
{
do_floppy = NULL;
output_byte(FD_SENSEI);
if (result()!=2 || (ST0 & 0xE0) == 0x60) {
reset_floppy();
return;
}
do_fd_request();
}
void unexpected_floppy_interrupt(void)
{
output_byte(FD_SENSEI);
if (result()!=2 || (ST0 & 0xE0) == 0x60) {
reset_floppy();
return;
}
do_floppy = recal_interrupt;
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
}
static void reset_interrupt(void)
{
output_byte(FD_SENSEI);
(void) result();
do_floppy = recal_interrupt;
output_byte(FD_RECALIBRATE);
output_byte(head<<2 | current_drive);
}
static void reset_floppy(void)
{
printk("Reset-floppy called\n\r");
do_floppy = reset_interrupt;
outb_p(0,FD_DOR);
outb(current_DOR,FD_DOR);
}
static void floppy_on_interrupt(void)
{
/* We cannot do a floppy-select, as that might sleep. We just force it */
selected = 1;
current_DOR &= 0xFC;
current_DOR |= current_drive;
transfer();
}
void do_fd_request(void)
{
unsigned int block;
INIT_REQUEST;
floppy = (MINOR(CURRENT->dev)>>2) + floppy_type;
current_drive = CURRENT_DEV;
block = CURRENT->sector;
if (block+2 > floppy->size) {
end_request(0);
goto repeat;
}
sector = block % floppy->sect;
block /= floppy->sect;
head = block % floppy->head;
track = block / floppy->head;
seek_track = track << floppy->stretch;
sector++;
if (CURRENT->cmd == READ)
command = FD_READ;
else if (CURRENT->cmd == WRITE)
command = FD_WRITE;
else
panic("do_fd_request: unknown command");
add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
}
void floppy_init(void)
{
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
set_intr_gate(0x26,&floppy_interrupt);
outb(inb_p(0x21)&~0x40,0x21);
}
/*
* linux/kernel/hd.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This is the low-level hd interrupt support. It traverses the
* request-list, using interrupts to jump between functions. As
* all the functions are called within interrupts, we may not
* sleep. Special care is recommended.
*/
#include <linux/config.h> #include <linux/config.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -7,64 +20,32 @@ ...@@ -7,64 +20,32 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/segment.h> #include <asm/segment.h>
/* #define MAJOR_NR 3
* This code handles all hd-interrupts, and read/write requests to #include "blk.h"
* the hard-disk. It is relatively straigthforward (not obvious maybe,
* but interrupts never are), while still being efficient, and never
* disabling interrupts (except to overcome possible race-condition).
* The elevator block-seek algorithm doesn't need to disable interrupts
* due to clever programming.
*/
/* Max read/write errors/sector */ /* Max read/write errors/sector */
#define MAX_ERRORS 5 #define MAX_ERRORS 5
#define MAX_HD 2 #define MAX_HD 2
#define NR_REQUEST 32
/* /*
* This struct defines the HD's and their types. * This struct defines the HD's and their types.
* Currently defined for CP3044's, ie a modified
* type 17.
*/ */
static struct hd_i_struct{ struct hd_i_struct {
int head,sect,cyl,wpcom,lzone,ctl; int head,sect,cyl,wpcom,lzone,ctl;
} hd_info[]= { HD_TYPE }; };
#ifdef HD_TYPE
struct hd_i_struct hd_info[] = { HD_TYPE };
#define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
#else
struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
static int NR_HD = 0;
#endif
static struct hd_struct { static struct hd_struct {
long start_sect; long start_sect;
long nr_sects; long nr_sects;
} hd[5*MAX_HD]={{0,0},}; } hd[5*MAX_HD]={{0,0},};
static struct hd_request {
int hd; /* -1 if no request */
int nsector;
int sector;
int head;
int cyl;
int cmd;
int errors;
struct buffer_head * bh;
struct hd_request * next;
} request[NR_REQUEST];
#define IN_ORDER(s1,s2) \
((s1)->hd<(s2)->hd || (s1)->hd==(s2)->hd && \
((s1)->cyl<(s2)->cyl || (s1)->cyl==(s2)->cyl && \
((s1)->head<(s2)->head || (s1)->head==(s2)->head && \
((s1)->sector<(s2)->sector))))
static struct hd_request * this_request = NULL;
static int sorting=0;
static void do_request(void);
static void reset_controller(void);
static void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
unsigned int cyl,struct buffer_head * bh);
void hd_init(void);
#define port_read(port,buf,nr) \ #define port_read(port,buf,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di") __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
...@@ -73,88 +54,60 @@ __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si") ...@@ -73,88 +54,60 @@ __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
extern void hd_interrupt(void); extern void hd_interrupt(void);
static struct task_struct * wait_for_request=NULL;
static inline void lock_buffer(struct buffer_head * bh)
{
if (bh->b_lock)
printk("hd.c: buffer multiply locked\n");
bh->b_lock=1;
}
static inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock)
printk("hd.c: free buffer being unlocked\n");
bh->b_lock=0;
wake_up(&bh->b_wait);
}
static inline void wait_on_buffer(struct buffer_head * bh)
{
cli();
while (bh->b_lock)
sleep_on(&bh->b_wait);
sti();
}
void rw_hd(int rw, struct buffer_head * bh)
{
unsigned int block,dev;
unsigned int sec,head,cyl;
block = bh->b_blocknr << 1;
dev = MINOR(bh->b_dev);
if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects)
return;
block += hd[dev].start_sect;
dev /= 5;
__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
"r" (hd_info[dev].sect));
__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
"r" (hd_info[dev].head));
rw_abs_hd(rw,dev,sec+1,head,cyl,bh);
}
/* This may be used only once, enforced by 'static int callable' */ /* This may be used only once, enforced by 'static int callable' */
int sys_setup(void) int sys_setup(void * BIOS)
{ {
static int callable = 1; static int callable = 1;
int i,drive; int i,drive;
struct partition *p; struct partition *p;
struct buffer_head * bh;
if (!callable) if (!callable)
return -1; return -1;
callable = 0; callable = 0;
#ifndef HD_TYPE
for (drive=0 ; drive<2 ; drive++) {
hd_info[drive].cyl = *(unsigned short *) BIOS;
hd_info[drive].head = *(unsigned char *) (2+BIOS);
hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
hd_info[drive].sect = *(unsigned char *) (14+BIOS);
BIOS += 16;
}
if (hd_info[1].cyl)
NR_HD=2;
else
NR_HD=1;
#endif
for (i=0 ; i<NR_HD ; i++) {
hd[i*5].start_sect = 0;
hd[i*5].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
}
for (drive=0 ; drive<NR_HD ; drive++) { for (drive=0 ; drive<NR_HD ; drive++) {
rw_abs_hd(READ,drive,1,0,0,(struct buffer_head *) start_buffer); if (!(bh = bread(0x300 + drive*5,0))) {
if (!start_buffer->b_uptodate) {
printk("Unable to read partition table of drive %d\n\r", printk("Unable to read partition table of drive %d\n\r",
drive); drive);
panic(""); panic("");
} }
if (start_buffer->b_data[510] != 0x55 || (unsigned char) if (bh->b_data[510] != 0x55 || (unsigned char)
start_buffer->b_data[511] != 0xAA) { bh->b_data[511] != 0xAA) {
printk("Bad partition table on drive %d\n\r",drive); printk("Bad partition table on drive %d\n\r",drive);
panic(""); panic("");
} }
p = 0x1BE + (void *)start_buffer->b_data; p = 0x1BE + (void *)bh->b_data;
for (i=1;i<5;i++,p++) { for (i=1;i<5;i++,p++) {
hd[i+5*drive].start_sect = p->start_sect; hd[i+5*drive].start_sect = p->start_sect;
hd[i+5*drive].nr_sects = p->nr_sects; hd[i+5*drive].nr_sects = p->nr_sects;
} }
brelse(bh);
} }
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":""); printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
mount_root(); mount_root();
return (0); return (0);
} }
/*
* This is the pointer to a routine to be executed at every hd-interrupt.
* Interesting way of doing things, but should be rather practical.
*/
void (*do_hd)(void) = NULL;
static int controller_ready(void) static int controller_ready(void)
{ {
int retries=1000; int retries=1000;
...@@ -185,9 +138,9 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, ...@@ -185,9 +138,9 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
if (!controller_ready()) if (!controller_ready())
panic("HD controller not ready"); panic("HD controller not ready");
do_hd = intr_addr; do_hd = intr_addr;
outb(_CTL,HD_CMD); outb(hd_info[drive].ctl,HD_CMD);
port=HD_DATA; port=HD_DATA;
outb_p(_WPCOM,++port); outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port); outb_p(nsect,++port);
outb_p(sect,++port); outb_p(sect,++port);
outb_p(cyl,++port); outb_p(cyl,++port);
...@@ -225,28 +178,30 @@ static void reset_controller(void) ...@@ -225,28 +178,30 @@ static void reset_controller(void)
printk("HD-controller reset failed: %02x\n\r",i); printk("HD-controller reset failed: %02x\n\r",i);
} }
static void redo_hd_request(void)
{
do_hd = NULL;
do_hd_request();
}
static void reset_hd(int nr) static void reset_hd(int nr)
{ {
reset_controller(); reset_controller();
hd_out(nr,_SECT,_SECT,_HEAD-1,_CYL,WIN_SPECIFY,&do_request); hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1,
hd_info[nr].cyl,WIN_SPECIFY,&redo_hd_request);
} }
void unexpected_hd_interrupt(void) void unexpected_hd_interrupt(void)
{ {
panic("Unexpected HD interrupt\n\r"); printk("Unexpected HD interrupt\n\r");
} }
static void bad_rw_intr(void) static void bad_rw_intr(void)
{ {
int i = this_request->hd; int i = CURRENT_DEV;
if (this_request->errors++ >= MAX_ERRORS) { if (CURRENT->errors++ >= MAX_ERRORS)
this_request->bh->b_uptodate = 0; end_request(0);
unlock_buffer(this_request->bh);
wake_up(&wait_for_request);
this_request->hd = -1;
this_request=this_request->next;
}
reset_hd(i); reset_hd(i);
} }
...@@ -256,18 +211,14 @@ static void read_intr(void) ...@@ -256,18 +211,14 @@ static void read_intr(void)
bad_rw_intr(); bad_rw_intr();
return; return;
} }
port_read(HD_DATA,this_request->bh->b_data+ port_read(HD_DATA,CURRENT->buffer,256);
512*(this_request->nsector&1),256); CURRENT->errors = 0;
this_request->errors = 0; CURRENT->buffer += 512;
if (--this_request->nsector) CURRENT->sector++;
if (--CURRENT->nr_sectors)
return; return;
this_request->bh->b_uptodate = 1; end_request(1);
this_request->bh->b_dirt = 0; do_hd_request();
wake_up(&wait_for_request);
unlock_buffer(this_request->bh);
this_request->hd = -1;
this_request=this_request->next;
do_request();
} }
static void write_intr(void) static void write_intr(void)
...@@ -276,137 +227,56 @@ static void write_intr(void) ...@@ -276,137 +227,56 @@ static void write_intr(void)
bad_rw_intr(); bad_rw_intr();
return; return;
} }
if (--this_request->nsector) { if (--CURRENT->nr_sectors) {
port_write(HD_DATA,this_request->bh->b_data+512,256); CURRENT->sector++;
CURRENT->buffer += 512;
port_write(HD_DATA,CURRENT->buffer,256);
return; return;
} }
this_request->bh->b_uptodate = 1; end_request(1);
this_request->bh->b_dirt = 0; do_hd_request();
wake_up(&wait_for_request);
unlock_buffer(this_request->bh);
this_request->hd = -1;
this_request=this_request->next;
do_request();
} }
static void do_request(void) void do_hd_request(void)
{ {
int i,r; int i,r;
unsigned int block,dev;
unsigned int sec,head,cyl;
unsigned int nsect;
if (sorting) INIT_REQUEST;
return; dev = MINOR(CURRENT->dev);
if (!this_request) { block = CURRENT->sector;
do_hd=NULL; if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
return; end_request(0);
goto repeat;
} }
if (this_request->cmd == WIN_WRITE) { block += hd[dev].start_sect;
hd_out(this_request->hd,this_request->nsector,this_request-> dev /= 5;
sector,this_request->head,this_request->cyl, __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
this_request->cmd,&write_intr); "r" (hd_info[dev].sect));
__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
"r" (hd_info[dev].head));
sec++;
nsect = CURRENT->nr_sectors;
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<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
/* nothing */ ; /* nothing */ ;
if (!r) { if (!r) {
reset_hd(this_request->hd); reset_hd(CURRENT_DEV);
return; return;
} }
port_write(HD_DATA,this_request->bh->b_data+ port_write(HD_DATA,CURRENT->buffer,256);
512*(this_request->nsector&1),256); } else if (CURRENT->cmd == READ) {
} else if (this_request->cmd == WIN_READ) { hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
hd_out(this_request->hd,this_request->nsector,this_request->
sector,this_request->head,this_request->cyl,
this_request->cmd,&read_intr);
} else } else
panic("unknown hd-command"); panic("unknown hd-command");
} }
/*
* add-request adds a request to the linked list.
* It sets the 'sorting'-variable when doing something
* that interrupts shouldn't touch.
*/
static void add_request(struct hd_request * req)
{
struct hd_request * tmp;
if (req->nsector != 2)
panic("nsector!=2 not implemented");
/*
* Not to mess up the linked lists, we never touch the two first
* entries (not this_request, as it is used by current interrups,
* and not this_request->next, as it can be assigned to this_request).
* This is not too high a price to pay for the ability of not
* disabling interrupts.
*/
sorting=1;
if (!(tmp=this_request))
this_request=req;
else {
if (!(tmp->next))
tmp->next=req;
else {
tmp=tmp->next;
for ( ; tmp->next ; tmp=tmp->next)
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
req->next=tmp->next;
tmp->next=req;
}
}
sorting=0;
/*
* NOTE! As a result of sorting, the interrupts may have died down,
* as they aren't redone due to locking with sorting=1. They might
* also never have started, if this is the first request in the queue,
* so we restart them if necessary.
*/
if (!do_hd)
do_request();
}
void rw_abs_hd(int rw,unsigned int nr,unsigned int sec,unsigned int head,
unsigned int cyl,struct buffer_head * bh)
{
struct hd_request * req;
if (rw!=READ && rw!=WRITE)
panic("Bad hd command, must be R/W");
lock_buffer(bh);
repeat:
for (req=0+request ; req<NR_REQUEST+request ; req++)
if (req->hd<0)
break;
if (req==NR_REQUEST+request) {
sleep_on(&wait_for_request);
goto repeat;
}
req->hd=nr;
req->nsector=2;
req->sector=sec;
req->head=head;
req->cyl=cyl;
req->cmd = ((rw==READ)?WIN_READ:WIN_WRITE);
req->bh=bh;
req->errors=0;
req->next=NULL;
add_request(req);
wait_on_buffer(bh);
}
void hd_init(void) void hd_init(void)
{ {
int i; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
for (i=0 ; i<NR_REQUEST ; i++) {
request[i].hd = -1;
request[i].next = NULL;
}
for (i=0 ; i<NR_HD ; i++) {
hd[i*5].start_sect = 0;
hd[i*5].nr_sects = hd_info[i].head*
hd_info[i].sect*hd_info[i].cyl;
}
set_trap_gate(0x2E,&hd_interrupt); set_trap_gate(0x2E,&hd_interrupt);
outb_p(inb_p(0x21)&0xfb,0x21); outb_p(inb_p(0x21)&0xfb,0x21);
outb(inb_p(0xA1)&0xbf,0xA1); outb(inb_p(0xA1)&0xbf,0xA1);
......
/*
* linux/kernel/blk_dev/ll_rw.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This handles all read/write requests to block devices
*/
#include <errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include "blk.h"
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
*/
struct request request[NR_REQUEST];
/*
* used to wait on when there are no free requests
*/
struct task_struct * wait_for_request = NULL;
/* blk_dev_struct is:
* do_request-address
* next-request
*/
struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
{ NULL, NULL }, /* no_dev */
{ NULL, NULL }, /* dev mem */
{ NULL, NULL }, /* dev fd */
{ NULL, NULL }, /* dev hd */
{ NULL, NULL }, /* dev ttyx */
{ NULL, NULL }, /* dev tty */
{ NULL, NULL } /* dev lp */
};
static inline void lock_buffer(struct buffer_head * bh)
{
cli();
while (bh->b_lock)
sleep_on(&bh->b_wait);
bh->b_lock=1;
sti();
}
static inline void unlock_buffer(struct buffer_head * bh)
{
if (!bh->b_lock)
printk("ll_rw_block.c: buffer not locked\n\r");
bh->b_lock = 0;
wake_up(&bh->b_wait);
}
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*/
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
req->next = NULL;
cli();
if (!(tmp = dev->current_request)) {
dev->current_request = req;
sti();
(dev->request_fn)();
} else {
for ( ; tmp->next ; tmp=tmp->next)
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
req->next=tmp->next;
tmp->next=req;
}
sti();
}
static void make_request(int major,int rw, struct buffer_head * bh)
{
struct request * req;
/* READA is special case - the read is not really needed, so if the */
/* buffer is locked, we just forget about it, else it's a normal read */
if (rw == READA) {
if (bh->b_lock)
return;
else
rw=READ;
}
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W/RA");
lock_buffer(bh);
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
unlock_buffer(bh);
return;
}
repeat:
for (req=0+request ; req<NR_REQUEST+request ; req++)
if (req->dev<0)
break;
if (req==NR_REQUEST+request) {
sleep_on(&wait_for_request);
goto repeat;
}
req->dev = bh->b_dev;
req->cmd = rw;
req->errors=0;
req->sector = bh->b_blocknr<<1;
req->nr_sectors = 2;
req->buffer = bh->b_data;
req->waiting = NULL;
req->bh = bh;
req->next = NULL;
add_request(major+blk_dev,req);
}
void ll_rw_block(int rw, struct buffer_head * bh)
{
unsigned int major;
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
!(blk_dev[major].request_fn))
panic("Trying to read nonexistent block-device");
make_request(major,rw,bh);
}
void blk_dev_init(void)
{
int i;
for (i=0 ; i<NR_REQUEST ; i++) {
request[i].dev = -1;
request[i].next = NULL;
}
}
/*
* linux/kernel/blk_drv/ramdisk.c
*
* Written by Theodore Ts'o
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/memory.h>
#define MAJOR_NR 1
#include "blk.h"
char *ram_disk; /* Start of ram disk */
int ram_disk_size; /* Size of ram disk */
void do_ram_request(void)
{
int i,r;
unsigned int block,dev;
unsigned int sec,head,cyl;
unsigned int nsect;
INIT_REQUEST;
if (MINOR(CURRENT->dev) != 0) {
end_request(0);
goto repeat;
}
block = CURRENT->sector;
end_request(1);
}
void ram_init(void)
{
}
/*
* linux/kernel/blk_drv/ramdisk.c
*
* Written by Theodore Ts'o
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR 2
#include "blk.h"
void do_ram_request(void)
{
int i,r;
unsigned int block,dev;
unsigned int sec,head,cyl;
unsigned int nsect;
INIT_REQUEST;
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
end_request(0);
goto repeat;
}
block += hd[dev].start_sect;
dev /= 5;
__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
"r" (hd_info[dev].sect));
__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
"r" (hd_info[dev].head));
sec++;
nsect = CURRENT->nr_sectors;
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++)
/* nothing */ ;
if (!r) {
reset_hd(CURRENT_DEV);
return;
}
port_write(HD_DATA,CURRENT->buffer,256);
} else if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
} else
panic("unknown hd-command");
}
void ram_init(void)
{
}
#
# Makefile for the FREAX-kernel character device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
AR =gar
AS =gas
LD =gld
LDFLAGS =-s -x
CC =gcc
CFLAGS =-Wall -O -fstrength-reduce -fomit-frame-pointer -fcombine-regs \
-finline-functions -mstring-insns -nostdinc -I../../include
CPP =gcc -E -nostdinc -I../../include
.c.s:
$(CC) $(CFLAGS) \
-S -o $*.s $<
.s.o:
$(AS) -c -o $*.o $<
.c.o:
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o
chr_drv.a: $(OBJS)
$(AR) rcs chr_drv.a $(OBJS)
sync
keyboard.s: keyboard.S ../../include/linux/config.h
$(CPP) -traditional keyboard.S -o keyboard.s
clean:
rm -f core *.o *.a tmp_make keyboard.s
for i in *.c;do rm -f `basename $$i .c`.s;done
dep:
sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
(for i in *.c;do echo -n `echo $$i | sed 's,\.c,\.s,'`" "; \
$(CPP) -M $$i;done) >> tmp_make
cp tmp_make Makefile
### Dependencies:
console.s console.o : console.c ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \
../../include/linux/tty.h ../../include/termios.h ../../include/asm/io.h \
../../include/asm/system.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
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
head 1.1;
branch ;
access ;
symbols ;
locks tytso:1.1; strict;
comment @@;
1.1
date 91.11.21.14.50.43; author tytso; state Exp;
branches ;
next ;
desc
@@
1.1
log
@Initial revision
@
text
@/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/
/*
* Thanks to Alfred Leung for US keyboard patches
*/
#include <linux/config.h>
.text
.globl _keyboard_interrupt
/*
* these are for the keyboard read functions
*/
size = 1024 /* must be a power of two ! And MUST be the same
as in tty_io.c !!!! */
head = 4
tail = 8
proc_list = 12
buf = 16
mode: .byte 0 /* caps, alt, ctrl and shift mode */
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate
* ascii character(s).
*/
_keyboard_interrupt:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
xorl %al,%al /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
cmpb $0xe1,%al
je set_e1
call key_table(,%eax,4)
movb $0,e0
e0_e1: inb $0x61,%al
jmp 1f
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
movb $0x20,%al
outb %al,$0x20
pushl $0
call _do_tty_interrupt
addl $4,%esp
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
put_queue:
pushl %ecx
pushl %edx
movl _table_list,%edx # read-queue for console
movl head(%edx),%ecx
1: movb %al,buf(%edx,%ecx)
incl %ecx
andl $size-1,%ecx
cmpl tail(%edx),%ecx # buffer full - discard everything
je 3f
shrdl $8,%ebx,%eax
je 2f
shrl $8,%ebx
jmp 1b
2: movl %ecx,head(%edx)
movl proc_list(%edx),%ecx
testl %ecx,%ecx
je 3f
movl $0,(%ecx)
3: popl %edx
popl %ecx
ret
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret
caps: testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: andb $0x7f,mode
ret
scroll:
xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
cursor:
subb $0x47,%al
jb 1f
cmpb $12,%al
ja 1f
jne cur2 /* check for ctrl-alt-del */
testb $0x0c,mode
je cur2
testb $0x30,mode
jne reboot
cur2: cmpb $0x01,e0 /* e0 forces cursor movement */
je cur
testb $0x02,leds /* not num-lock forces cursor */
je cur
testb $0x03,mode /* shift forces cursor */
jne cur
xorl %ebx,%ebx
movb num_table(%eax),%al
jmp put_queue
1: ret
cur: movb cur_table(%eax),%al
cmpb $'9,%al
ja ok_cur
movb $'~,%ah
ok_cur: shll $16,%eax
movw $0x5b1b,%ax
xorl %ebx,%ebx
jmp put_queue
num_table:
.ascii "789 456 1230,"
cur_table:
.ascii "HA5 DGC YB623"
/*
* 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
jbe ok_func
subb $18,%al
cmpb $10,%al
jb end_func
cmpb $11,%al
ja end_func
ok_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
end_func:
ret
/*
* function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
*/
func_table:
.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
#if defined(KBD_FINNISH)
key_map:
.byte 0,27
.ascii "1234567890+'"
.byte 127,9
.ascii "qwertyuiop}"
.byte 0,13,0
.ascii "asdfghjkl|{"
.byte 0,0
.ascii "'zxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!\"#$%&/()=?`"
.byte 127,9
.ascii "QWERTYUIOP]^"
.byte 13,0
.ascii "ASDFGHJKL\\["
.byte 0,0
.ascii "*ZXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US)
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "\\zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:\""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/*
* do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns.
*/
do_self:
lea alt_map,%ebx
testb $0x20,mode /* alt-gr */
jne 1f
lea shift_map,%ebx
testb $0x03,mode
jne 1f
lea key_map,%ebx
1: movb (%ebx,%eax),%al
orb %al,%al
je none
testb $0x4c,mode /* ctrl or caps */
je 2f
cmpb $'a,%al
jb 2f
cmpb $'},%al
ja 2f
subb $32,%al
2: testb $0x0c,mode /* ctrl */
je 3f
cmpb $64,%al
jb 3f
cmpb $64+32,%al
jae 3f
subb $64,%al
3: testb $0x10,mode /* left alt */
je 4f
orb $0x80,%al
4: andl $0xff,%eax
xorl %ebx,%ebx
call put_queue
none: ret
/*
* minus has a routine of it's own, as a 'E0h' before
* the scan code for minus means that the numeric keypad
* slash was pushed.
*/
minus: cmpb $1,e0
jne do_self
movl $'/,%eax
xorl %ebx,%ebx
jmp put_queue
/*
* This table decides which routine to call when a scan-code has been
* gotten. Most routines just call do_self, or none, depending if
* they are make or break.
*/
key_table:
.long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */
.long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */
.long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */
.long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */
.long do_self,do_self,do_self,do_self /* 10-13 q w e r */
.long do_self,do_self,do_self,do_self /* 14-17 t y u i */
.long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */
.long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */
.long do_self,do_self,do_self,do_self /* 20-23 d f g h */
.long do_self,do_self,do_self,do_self /* 24-27 j k l | */
.long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */
.long do_self,do_self,do_self,do_self /* 2C-2F z x c v */
.long do_self,do_self,do_self,do_self /* 30-33 b n m , */
.long do_self,minus,rshift,do_self /* 34-37 . - rshift * */
.long alt,do_self,caps,func /* 38-3B alt sp caps f1 */
.long func,func,func,func /* 3C-3F f2 f3 f4 f5 */
.long func,func,func,func /* 40-43 f6 f7 f8 f9 */
.long func,num,scroll,cursor /* 44-47 f10 num scr home */
.long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */
.long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */
.long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */
.long none,none,do_self,func /* 54-57 sysreq ? < f11 */
.long func,none,none,none /* 58-5B f12 ? ? ? */
.long none,none,none,none /* 5C-5F ? ? ? ? */
.long none,none,none,none /* 60-63 ? ? ? ? */
.long none,none,none,none /* 64-67 ? ? ? ? */
.long none,none,none,none /* 68-6B ? ? ? ? */
.long none,none,none,none /* 6C-6F ? ? ? ? */
.long none,none,none,none /* 70-73 ? ? ? ? */
.long none,none,none,none /* 74-77 ? ? ? ? */
.long none,none,none,none /* 78-7B ? ? ? ? */
.long none,none,none,none /* 7C-7F ? ? ? ? */
.long none,none,none,none /* 80-83 ? br br br */
.long none,none,none,none /* 84-87 br br br br */
.long none,none,none,none /* 88-8B br br br br */
.long none,none,none,none /* 8C-8F br br br br */
.long none,none,none,none /* 90-93 br br br br */
.long none,none,none,none /* 94-97 br br br br */
.long none,none,none,none /* 98-9B br br br br */
.long none,unctrl,none,none /* 9C-9F br unctrl br br */
.long none,none,none,none /* A0-A3 br br br br */
.long none,none,none,none /* A4-A7 br br br br */
.long none,none,unlshift,none /* A8-AB br br unlshift br */
.long none,none,none,none /* AC-AF br br br br */
.long none,none,none,none /* B0-B3 br br br br */
.long none,none,unrshift,none /* B4-B7 br br unrshift br */
.long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */
.long none,none,none,none /* BC-BF br br br br */
.long none,none,none,none /* C0-C3 br br br br */
.long none,none,none,none /* C4-C7 br br br br */
.long none,none,none,none /* C8-CB br br br br */
.long none,none,none,none /* CC-CF br br br br */
.long none,none,none,none /* D0-D3 br br br br */
.long none,none,none,none /* D4-D7 br br br br */
.long none,none,none,none /* D8-DB br ? ? ? */
.long none,none,none,none /* DC-DF ? ? ? ? */
.long none,none,none,none /* E0-E3 e0 e1 ? ? */
.long none,none,none,none /* E4-E7 ? ? ? ? */
.long none,none,none,none /* E8-EB ? ? ? ? */
.long none,none,none,none /* EC-EF ? ? ? ? */
.long none,none,none,none /* F0-F3 ? ? ? ? */
.long none,none,none,none /* F4-F7 ? ? ? ? */
.long none,none,none,none /* F8-FB ? ? ? ? */
.long none,none,none,none /* FC-FF ? ? ? ? */
/*
* kb_wait waits for the keyboard controller buffer to empty.
* there is no timeout - if the buffer doesn't empty, we hang.
*/
kb_wait:
pushl %eax
1: inb $0x64,%al
testb $0x02,%al
jne 1b
popl %eax
ret
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low.
*/
reboot:
call kb_wait
movw $0x1234,0x472 /* don't do memory check */
movb $0xfc,%al /* pulse reset and A20 low */
outb %al,$0x64
die: jmp die
@
/*
* linux/kernel/console.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* console.c * console.c
* *
...@@ -20,6 +26,12 @@ ...@@ -20,6 +26,12 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/system.h> #include <asm/system.h>
/*
* These are set up by the setup-routine at boot-time:
*/
#define ORIG_X (*(unsigned char *)0x90000)
#define ORIG_Y (*(unsigned char *)0x90001)
#define SCREEN_START 0xb8000 #define SCREEN_START 0xb8000
#define SCREEN_END 0xc0000 #define SCREEN_END 0xc0000
#define LINES 25 #define LINES 25
...@@ -45,9 +57,10 @@ static unsigned char attr=0x07; ...@@ -45,9 +57,10 @@ static unsigned char attr=0x07;
*/ */
#define RESPONSE "\033[?1;2c" #define RESPONSE "\033[?1;2c"
/* NOTE! gotoxy thinks x==columns is ok */
static inline void gotoxy(unsigned int new_x,unsigned int new_y) static inline void gotoxy(unsigned int new_x,unsigned int new_y)
{ {
if (new_x>=columns || new_y>=lines) if (new_x > columns || new_y >= lines)
return; return;
x=new_x; x=new_x;
y=new_y; y=new_y;
...@@ -313,7 +326,7 @@ static void delete_line(void) ...@@ -313,7 +326,7 @@ static void delete_line(void)
bottom=oldbottom; bottom=oldbottom;
} }
static void csi_at(int nr) static void csi_at(unsigned int nr)
{ {
if (nr>columns) if (nr>columns)
nr=columns; nr=columns;
...@@ -323,7 +336,7 @@ static void csi_at(int nr) ...@@ -323,7 +336,7 @@ static void csi_at(int nr)
insert_char(); insert_char();
} }
static void csi_L(int nr) static void csi_L(unsigned int nr)
{ {
if (nr>lines) if (nr>lines)
nr=lines; nr=lines;
...@@ -333,7 +346,7 @@ static void csi_L(int nr) ...@@ -333,7 +346,7 @@ static void csi_L(int nr)
insert_line(); insert_line();
} }
static void csi_P(int nr) static void csi_P(unsigned int nr)
{ {
if (nr>columns) if (nr>columns)
nr=columns; nr=columns;
...@@ -343,7 +356,7 @@ static void csi_P(int nr) ...@@ -343,7 +356,7 @@ static void csi_P(int nr)
delete_char(); delete_char();
} }
static void csi_M(int nr) static void csi_M(unsigned int nr)
{ {
if (nr>lines) if (nr>lines)
nr=lines; nr=lines;
...@@ -364,9 +377,7 @@ static void save_cur(void) ...@@ -364,9 +377,7 @@ static void save_cur(void)
static void restore_cur(void) static void restore_cur(void)
{ {
x=saved_x; gotoxy(saved_x, saved_y);
y=saved_y;
pos=origin+((y*columns+x)<<1);
} }
void con_write(struct tty_struct * tty) void con_write(struct tty_struct * tty)
...@@ -541,7 +552,7 @@ void con_init(void) ...@@ -541,7 +552,7 @@ void con_init(void)
{ {
register unsigned char a; register unsigned char a;
gotoxy(*(unsigned char *)(0x90000+510),*(unsigned char *)(0x90000+511)); gotoxy(ORIG_X,ORIG_Y);
set_trap_gate(0x21,&keyboard_interrupt); set_trap_gate(0x21,&keyboard_interrupt);
outb_p(inb_p(0x21)&0xfd,0x21); outb_p(inb_p(0x21)&0xfd,0x21);
a=inb_p(0x61); a=inb_p(0x61);
......
/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/
/*
* Thanks to Alfred Leung for US keyboard patches
*/
#include <linux/config.h>
.text
.globl _keyboard_interrupt
/*
* these are for the keyboard read functions
*/
size = 1024 /* must be a power of two ! And MUST be the same
as in tty_io.c !!!! */
head = 4
tail = 8
proc_list = 12
buf = 16
mode: .byte 0 /* caps, alt, ctrl and shift mode */
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0
flags: .byte 0 /* 0x1 - map caps lock to ctrl */
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate
* ascii character(s).
*/
_keyboard_interrupt:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
xorl %al,%al /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
cmpb $0xe1,%al
je set_e1
call key_table(,%eax,4)
movb $0,e0
e0_e1: inb $0x61,%al
jmp 1f
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
movb $0x20,%al
outb %al,$0x20
pushl $0
call _do_tty_interrupt
addl $4,%esp
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
put_queue:
pushl %ecx
pushl %edx
movl _table_list,%edx # read-queue for console
movl head(%edx),%ecx
1: movb %al,buf(%edx,%ecx)
incl %ecx
andl $size-1,%ecx
cmpl tail(%edx),%ecx # buffer full - discard everything
je 3f
shrdl $8,%ebx,%eax
je 2f
shrl $8,%ebx
jmp 1b
2: movl %ecx,head(%edx)
movl proc_list(%edx),%ecx
testl %ecx,%ecx
je 3f
movl $0,(%ecx)
3: popl %edx
popl %ecx
ret
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret
caps: testb $0x1,flags
je ctrl
testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: testb $0x1,flags
je unctrl
andb $0x7f,mode
ret
scroll:
xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
cursor:
subb $0x47,%al
jb 1f
cmpb $12,%al
ja 1f
jne cur2 /* check for ctrl-alt-del */
testb $0x0c,mode
je cur2
testb $0x30,mode
jne reboot
cur2: cmpb $0x01,e0 /* e0 forces cursor movement */
je cur
testb $0x02,leds /* not num-lock forces cursor */
je cur
testb $0x03,mode /* shift forces cursor */
jne cur
xorl %ebx,%ebx
movb num_table(%eax),%al
jmp put_queue
1: ret
cur: movb cur_table(%eax),%al
cmpb $'9,%al
ja ok_cur
movb $'~,%ah
ok_cur: shll $16,%eax
movw $0x5b1b,%ax
xorl %ebx,%ebx
jmp put_queue
num_table:
.ascii "789 456 1230,"
cur_table:
.ascii "HA5 DGC YB623"
/*
* 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
jbe ok_func
subb $18,%al
cmpb $10,%al
jb end_func
cmpb $11,%al
ja end_func
ok_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
end_func:
ret
/*
* function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
*/
func_table:
.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
#if defined(KBD_FINNISH)
key_map:
.byte 0,27
.ascii "1234567890+'"
.byte 127,9
.ascii "qwertyuiop}"
.byte 0,13,0
.ascii "asdfghjkl|{"
.byte 0,0
.ascii "'zxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!\"#$%&/()=?`"
.byte 127,9
.ascii "QWERTYUIOP]^"
.byte 13,0
.ascii "ASDFGHJKL\\["
.byte 0,0
.ascii "*ZXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US)
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "\\zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:\""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/*
* do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns.
*/
do_self:
lea alt_map,%ebx
testb $0x20,mode /* alt-gr */
jne 1f
lea shift_map,%ebx
testb $0x03,mode
jne 1f
lea key_map,%ebx
1: movb (%ebx,%eax),%al
orb %al,%al
je none
testb $0x4c,mode /* ctrl or caps */
je 2f
cmpb $'a,%al
jb 2f
cmpb $'},%al
ja 2f
subb $32,%al
2: testb $0x0c,mode /* ctrl */
je 3f
cmpb $64,%al
jb 3f
cmpb $64+32,%al
jae 3f
subb $64,%al
3: testb $0x10,mode /* left alt */
je 4f
orb $0x80,%al
4: andl $0xff,%eax
xorl %ebx,%ebx
call put_queue
none: ret
/*
* minus has a routine of it's own, as a 'E0h' before
* the scan code for minus means that the numeric keypad
* slash was pushed.
*/
minus: cmpb $1,e0
jne do_self
movl $'/,%eax
xorl %ebx,%ebx
jmp put_queue
/*
* do_space handles ctrl-space as an ASCII NUL. Old habits die hard.
*/
do_space:
testb $0x04,mode /* ctrl */
je do_self
testb $0x03,mode /* shift */
jne 1f
xorb $0x01, flags /* toggle caps lock flag */
ret
1f: movl $0,%al /* ASCII NUL */
xorl %ebx,%ebx
jmp put_queue
/*
* This table decides which routine to call when a scan-code has been
* gotten. Most routines just call do_self, or none, depending if
* they are make or break.
*/
key_table:
.long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */
.long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */
.long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */
.long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */
.long do_self,do_self,do_self,do_self /* 10-13 q w e r */
.long do_self,do_self,do_self,do_self /* 14-17 t y u i */
.long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */
.long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */
.long do_self,do_self,do_self,do_self /* 20-23 d f g h */
.long do_self,do_self,do_self,do_self /* 24-27 j k l | */
.long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */
.long do_self,do_self,do_self,do_self /* 2C-2F z x c v */
.long do_self,do_self,do_self,do_self /* 30-33 b n m , */
.long do_self,minus,rshift,do_self /* 34-37 . - rshift * */
.long alt,do_space,caps,func /* 38-3B alt sp caps f1 */
.long func,func,func,func /* 3C-3F f2 f3 f4 f5 */
.long func,func,func,func /* 40-43 f6 f7 f8 f9 */
.long func,num,scroll,cursor /* 44-47 f10 num scr home */
.long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */
.long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */
.long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */
.long none,none,do_self,func /* 54-57 sysreq ? < f11 */
.long func,none,none,none /* 58-5B f12 ? ? ? */
.long none,none,none,none /* 5C-5F ? ? ? ? */
.long none,none,none,none /* 60-63 ? ? ? ? */
.long none,none,none,none /* 64-67 ? ? ? ? */
.long none,none,none,none /* 68-6B ? ? ? ? */
.long none,none,none,none /* 6C-6F ? ? ? ? */
.long none,none,none,none /* 70-73 ? ? ? ? */
.long none,none,none,none /* 74-77 ? ? ? ? */
.long none,none,none,none /* 78-7B ? ? ? ? */
.long none,none,none,none /* 7C-7F ? ? ? ? */
.long none,none,none,none /* 80-83 ? br br br */
.long none,none,none,none /* 84-87 br br br br */
.long none,none,none,none /* 88-8B br br br br */
.long none,none,none,none /* 8C-8F br br br br */
.long none,none,none,none /* 90-93 br br br br */
.long none,none,none,none /* 94-97 br br br br */
.long none,none,none,none /* 98-9B br br br br */
.long none,unctrl,none,none /* 9C-9F br unctrl br br */
.long none,none,none,none /* A0-A3 br br br br */
.long none,none,none,none /* A4-A7 br br br br */
.long none,none,unlshift,none /* A8-AB br br unlshift br */
.long none,none,none,none /* AC-AF br br br br */
.long none,none,none,none /* B0-B3 br br br br */
.long none,none,unrshift,none /* B4-B7 br br unrshift br */
.long unalt,none,uncaps,none /* B8-BB unalt br uncaps br */
.long none,none,none,none /* BC-BF br br br br */
.long none,none,none,none /* C0-C3 br br br br */
.long none,none,none,none /* C4-C7 br br br br */
.long none,none,none,none /* C8-CB br br br br */
.long none,none,none,none /* CC-CF br br br br */
.long none,none,none,none /* D0-D3 br br br br */
.long none,none,none,none /* D4-D7 br br br br */
.long none,none,none,none /* D8-DB br ? ? ? */
.long none,none,none,none /* DC-DF ? ? ? ? */
.long none,none,none,none /* E0-E3 e0 e1 ? ? */
.long none,none,none,none /* E4-E7 ? ? ? ? */
.long none,none,none,none /* E8-EB ? ? ? ? */
.long none,none,none,none /* EC-EF ? ? ? ? */
.long none,none,none,none /* F0-F3 ? ? ? ? */
.long none,none,none,none /* F4-F7 ? ? ? ? */
.long none,none,none,none /* F8-FB ? ? ? ? */
.long none,none,none,none /* FC-FF ? ? ? ? */
/*
* kb_wait waits for the keyboard controller buffer to empty.
* there is no timeout - if the buffer doesn't empty, we hang.
*/
kb_wait:
pushl %eax
1: inb $0x64,%al
testb $0x02,%al
jne 1b
popl %eax
ret
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low.
*/
reboot:
call kb_wait
movw $0x1234,0x472 /* don't do memory check */
movb $0xfc,%al /* pulse reset and A20 low */
outb %al,$0x64
die: jmp die
/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/
/*
* Thanks to Alfred Leung for US keyboard patches
*/
#include <linux/config.h>
.text
.globl _keyboard_interrupt
/*
* these are for the keyboard read functions
*/
size = 1024 /* must be a power of two ! And MUST be the same
as in tty_io.c !!!! */
head = 4
tail = 8
proc_list = 12
buf = 16
mode: .byte 0 /* caps, alt, ctrl and shift mode */
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate
* ascii character(s).
*/
_keyboard_interrupt:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
xorl %al,%al /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
cmpb $0xe1,%al
je set_e1
call key_table(,%eax,4)
movb $0,e0
e0_e1: inb $0x61,%al
jmp 1f
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
movb $0x20,%al
outb %al,$0x20
pushl $0
call _do_tty_interrupt
addl $4,%esp
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
put_queue:
pushl %ecx
pushl %edx
movl _table_list,%edx # read-queue for console
movl head(%edx),%ecx
1: movb %al,buf(%edx,%ecx)
incl %ecx
andl $size-1,%ecx
cmpl tail(%edx),%ecx # buffer full - discard everything
je 3f
shrdl $8,%ebx,%eax
je 2f
shrl $8,%ebx
jmp 1b
2: movl %ecx,head(%edx)
movl proc_list(%edx),%ecx
testl %ecx,%ecx
je 3f
movl $0,(%ecx)
3: popl %edx
popl %ecx
ret
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret
caps: testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: andb $0x7f,mode
ret
scroll:
xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
cursor:
subb $0x47,%al
jb 1f
cmpb $12,%al
ja 1f
jne cur2 /* check for ctrl-alt-del */
testb $0x0c,mode
je cur2
testb $0x30,mode
jne reboot
cur2: cmpb $0x01,e0 /* e0 forces cursor movement */
je cur
testb $0x02,leds /* not num-lock forces cursor */
je cur
testb $0x03,mode /* shift forces cursor */
jne cur
xorl %ebx,%ebx
movb num_table(%eax),%al
jmp put_queue
1: ret
cur: movb cur_table(%eax),%al
cmpb $'9,%al
ja ok_cur
movb $'~,%ah
ok_cur: shll $16,%eax
movw $0x5b1b,%ax
xorl %ebx,%ebx
jmp put_queue
num_table:
.ascii "789 456 1230,"
cur_table:
.ascii "HA5 DGC YB623"
/*
* 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
jbe ok_func
subb $18,%al
cmpb $10,%al
jb end_func
cmpb $11,%al
ja end_func
ok_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
end_func:
ret
/*
* function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
*/
func_table:
.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
#if defined(KBD_FINNISH)
key_map:
.byte 0,27
.ascii "1234567890+'"
.byte 127,9
.ascii "qwertyuiop}"
.byte 0,13,0
.ascii "asdfghjkl|{"
.byte 0,0
.ascii "'zxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!\"#$%&/()=?`"
.byte 127,9
.ascii "QWERTYUIOP]^"
.byte 13,0
.ascii "ASDFGHJKL\\["
.byte 0,0
.ascii "*ZXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US)
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "\\zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:\""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/*
* do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns.
*/
do_self:
lea alt_map,%ebx
testb $0x20,mode /* alt-gr */
jne 1f
lea shift_map,%ebx
testb $0x03,mode
jne 1f
lea key_map,%ebx
1: movb (%ebx,%eax),%al
orb %al,%al
je none
testb $0x4c,mode /* ctrl or caps */
je 2f
cmpb $'a,%al
jb 2f
cmpb $'},%al
ja 2f
subb $32,%al
2: testb $0x0c,mode /* ctrl */
je 3f
cmpb $64,%al
jb 3f
cmpb $64+32,%al
jae 3f
subb $64,%al
3: testb $0x10,mode /* left alt */
je 4f
orb $0x80,%al
4: andl $0xff,%eax
xorl %ebx,%ebx
call put_queue
none: ret
/*
* minus has a routine of it's own, as a 'E0h' before
* the scan code for minus means that the numeric keypad
* slash was pushed.
*/
minus: cmpb $1,e0
jne do_self
movl $'/,%eax
xorl %ebx,%ebx
jmp put_queue
/*
* do_space handles ctrl-space as an ASCII NUL. Old habits die hard.
*/
do_space:
testb $0x04,mode /* ctrl */
je do_self
movl $0,%al /* ASCII NUL */
xorl %ebx,%ebx
jmp put_queue
/*
* This table decides which routine to call when a scan-code has been
* gotten. Most routines just call do_self, or none, depending if
* they are make or break.
*/
key_table:
.long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */
.long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */
.long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */
.long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */
.long do_self,do_self,do_self,do_self /* 10-13 q w e r */
.long do_self,do_self,do_self,do_self /* 14-17 t y u i */
.long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */
.long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */
.long do_self,do_self,do_self,do_self /* 20-23 d f g h */
.long do_self,do_self,do_self,do_self /* 24-27 j k l | */
.long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */
.long do_self,do_self,do_self,do_self /* 2C-2F z x c v */
.long do_self,do_self,do_self,do_self /* 30-33 b n m , */
.long do_self,minus,rshift,do_self /* 34-37 . - rshift * */
.long alt,do_space,ctrl,func /* 38-3B alt sp caps f1 */
.long func,func,func,func /* 3C-3F f2 f3 f4 f5 */
.long func,func,func,func /* 40-43 f6 f7 f8 f9 */
.long func,num,scroll,cursor /* 44-47 f10 num scr home */
.long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */
.long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */
.long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */
.long none,none,do_self,func /* 54-57 sysreq ? < f11 */
.long func,none,none,none /* 58-5B f12 ? ? ? */
.long none,none,none,none /* 5C-5F ? ? ? ? */
.long none,none,none,none /* 60-63 ? ? ? ? */
.long none,none,none,none /* 64-67 ? ? ? ? */
.long none,none,none,none /* 68-6B ? ? ? ? */
.long none,none,none,none /* 6C-6F ? ? ? ? */
.long none,none,none,none /* 70-73 ? ? ? ? */
.long none,none,none,none /* 74-77 ? ? ? ? */
.long none,none,none,none /* 78-7B ? ? ? ? */
.long none,none,none,none /* 7C-7F ? ? ? ? */
.long none,none,none,none /* 80-83 ? br br br */
.long none,none,none,none /* 84-87 br br br br */
.long none,none,none,none /* 88-8B br br br br */
.long none,none,none,none /* 8C-8F br br br br */
.long none,none,none,none /* 90-93 br br br br */
.long none,none,none,none /* 94-97 br br br br */
.long none,none,none,none /* 98-9B br br br br */
.long none,unctrl,none,none /* 9C-9F br unctrl br br */
.long none,none,none,none /* A0-A3 br br br br */
.long none,none,none,none /* A4-A7 br br br br */
.long none,none,unlshift,none /* A8-AB br br unlshift br */
.long none,none,none,none /* AC-AF br br br br */
.long none,none,none,none /* B0-B3 br br br br */
.long none,none,unrshift,none /* B4-B7 br br unrshift br */
.long unalt,none,unctrl,none /* B8-BB unalt br uncaps br */
.long none,none,none,none /* BC-BF br br br br */
.long none,none,none,none /* C0-C3 br br br br */
.long none,none,none,none /* C4-C7 br br br br */
.long none,none,none,none /* C8-CB br br br br */
.long none,none,none,none /* CC-CF br br br br */
.long none,none,none,none /* D0-D3 br br br br */
.long none,none,none,none /* D4-D7 br br br br */
.long none,none,none,none /* D8-DB br ? ? ? */
.long none,none,none,none /* DC-DF ? ? ? ? */
.long none,none,none,none /* E0-E3 e0 e1 ? ? */
.long none,none,none,none /* E4-E7 ? ? ? ? */
.long none,none,none,none /* E8-EB ? ? ? ? */
.long none,none,none,none /* EC-EF ? ? ? ? */
.long none,none,none,none /* F0-F3 ? ? ? ? */
.long none,none,none,none /* F4-F7 ? ? ? ? */
.long none,none,none,none /* F8-FB ? ? ? ? */
.long none,none,none,none /* FC-FF ? ? ? ? */
/*
* kb_wait waits for the keyboard controller buffer to empty.
* there is no timeout - if the buffer doesn't empty, we hang.
*/
kb_wait:
pushl %eax
1: inb $0x64,%al
testb $0x02,%al
jne 1b
popl %eax
ret
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low.
*/
reboot:
call kb_wait
movw $0x1234,0x472 /* don't do memory check */
movb $0xfc,%al /* pulse reset and A20 low */
outb %al,$0x64
die: jmp die
/* /*
* keyboard.s * linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/ */
/*
* Thanks to Alfred Leung for US keyboard patches
*/
#include <linux/config.h>
.text .text
.globl _keyboard_interrupt .globl _keyboard_interrupt
...@@ -193,6 +201,13 @@ cur_table: ...@@ -193,6 +201,13 @@ cur_table:
* this routine handles function keys * this routine handles function keys
*/ */
func: func:
pushl %eax
pushl %ecx
pushl %edx
call _show_stat
popl %edx
popl %ecx
popl %eax
subb $0x3B,%al subb $0x3B,%al
jb end_func jb end_func
cmpb $9,%al cmpb $9,%al
...@@ -219,12 +234,13 @@ func_table: ...@@ -219,12 +234,13 @@ func_table:
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
#if defined(KBD_FINNISH)
key_map: key_map:
.byte 0,27 .byte 0,27
.ascii "1234567890+'" .ascii "1234567890+'"
.byte 127,9 .byte 127,9
.ascii "qwertyuiop}" .ascii "qwertyuiop}"
.byte 0,10,0 .byte 0,13,0
.ascii "asdfghjkl|{" .ascii "asdfghjkl|{"
.byte 0,0 .byte 0,0
.ascii "'zxcvbnm,.-" .ascii "'zxcvbnm,.-"
...@@ -240,7 +256,7 @@ shift_map: ...@@ -240,7 +256,7 @@ shift_map:
.ascii "!\"#$%&/()=?`" .ascii "!\"#$%&/()=?`"
.byte 127,9 .byte 127,9
.ascii "QWERTYUIOP]^" .ascii "QWERTYUIOP]^"
.byte 10,0 .byte 13,0
.ascii "ASDFGHJKL\\[" .ascii "ASDFGHJKL\\["
.byte 0,0 .byte 0,0
.ascii "*ZXCVBNM;:_" .ascii "*ZXCVBNM;:_"
...@@ -256,7 +272,58 @@ alt_map: ...@@ -256,7 +272,58 @@ alt_map:
.ascii "\0@\0$\0\0{[]}\\\0" .ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0 .byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,10,0 .byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US)
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "\\zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:\""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0 .byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0,0,0,0
...@@ -267,6 +334,9 @@ alt_map: ...@@ -267,6 +334,9 @@ alt_map:
.byte '| .byte '|
.fill 10,1,0 .fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/* /*
* do_self handles "normal" keys, ie keys that don't change meaning * do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns. * and which have just one character returns.
...@@ -286,7 +356,7 @@ do_self: ...@@ -286,7 +356,7 @@ do_self:
je 2f je 2f
cmpb $'a,%al cmpb $'a,%al
jb 2f jb 2f
cmpb $'z,%al cmpb $'},%al
ja 2f ja 2f
subb $32,%al subb $32,%al
2: testb $0x0c,mode /* ctrl */ 2: testb $0x0c,mode /* ctrl */
......
/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/
/*
* Thanks to Alfred Leung for US keyboard patches
*/
#include <linux/config.h>
.text
.globl _keyboard_interrupt
/*
* these are for the keyboard read functions
*/
size = 1024 /* must be a power of two ! And MUST be the same
as in tty_io.c !!!! */
head = 4
tail = 8
proc_list = 12
buf = 16
mode: .byte 0 /* caps, alt, ctrl and shift mode */
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate
* ascii character(s).
*/
_keyboard_interrupt:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
xorl %al,%al /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
cmpb $0xe1,%al
je set_e1
call key_table(,%eax,4)
movb $0,e0
e0_e1: inb $0x61,%al
jmp 1f
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
movb $0x20,%al
outb %al,$0x20
pushl $0
call _do_tty_interrupt
addl $4,%esp
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
put_queue:
pushl %ecx
pushl %edx
movl _table_list,%edx # read-queue for console
movl head(%edx),%ecx
1: movb %al,buf(%edx,%ecx)
incl %ecx
andl $size-1,%ecx
cmpl tail(%edx),%ecx # buffer full - discard everything
je 3f
shrdl $8,%ebx,%eax
je 2f
shrl $8,%ebx
jmp 1b
2: movl %ecx,head(%edx)
movl proc_list(%edx),%ecx
testl %ecx,%ecx
je 3f
movl $0,(%ecx)
3: popl %edx
popl %ecx
ret
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret
caps: testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: andb $0x7f,mode
ret
scroll:
xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
cursor:
subb $0x47,%al
jb 1f
cmpb $12,%al
ja 1f
jne cur2 /* check for ctrl-alt-del */
testb $0x0c,mode
je cur2
testb $0x30,mode
jne reboot
cur2: cmpb $0x01,e0 /* e0 forces cursor movement */
je cur
testb $0x02,leds /* not num-lock forces cursor */
je cur
testb $0x03,mode /* shift forces cursor */
jne cur
xorl %ebx,%ebx
movb num_table(%eax),%al
jmp put_queue
1: ret
cur: movb cur_table(%eax),%al
cmpb $'9,%al
ja ok_cur
movb $'~,%ah
ok_cur: shll $16,%eax
movw $0x5b1b,%ax
xorl %ebx,%ebx
jmp put_queue
num_table:
.ascii "789 456 1230,"
cur_table:
.ascii "HA5 DGC YB623"
/*
* 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
jbe ok_func
subb $18,%al
cmpb $10,%al
jb end_func
cmpb $11,%al
ja end_func
ok_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
end_func:
ret
/*
* function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
*/
func_table:
.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
#if defined(KBD_FINNISH)
key_map:
.byte 0,27
.ascii "1234567890+'"
.byte 127,9
.ascii "qwertyuiop}"
.byte 0,13,0
.ascii "asdfghjkl|{"
.byte 0,0
.ascii "'zxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!\"#$%&/()=?`"
.byte 127,9
.ascii "QWERTYUIOP]^"
.byte 13,0
.ascii "ASDFGHJKL\\["
.byte 0,0
.ascii "*ZXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US)
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "\\zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:\""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "\0@\0$\0\0{[]}\\\0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/*
* do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns.
*/
do_self:
lea alt_map,%ebx
testb $0x20,mode /* alt-gr */
jne 1f
lea shift_map,%ebx
testb $0x03,mode
jne 1f
lea key_map,%ebx
1: movb (%ebx,%eax),%al
orb %al,%al
je none
testb $0x4c,mode /* ctrl or caps */
je 2f
cmpb $'a,%al
jb 2f
cmpb $'},%al
ja 2f
subb $32,%al
2: testb $0x0c,mode /* ctrl */
je 3f
cmpb $64,%al
jb 3f
cmpb $64+32,%al
jae 3f
subb $64,%al
3: testb $0x10,mode /* left alt */
je 4f
orb $0x80,%al
4: andl $0xff,%eax
xorl %ebx,%ebx
call put_queue
none: ret
/*
* minus has a routine of it's own, as a 'E0h' before
* the scan code for minus means that the numeric keypad
* slash was pushed.
*/
minus: cmpb $1,e0
jne do_self
movl $'/,%eax
xorl %ebx,%ebx
jmp put_queue
/*
* do_space handles ctrl-space as an ASCII NUL. Old habits die hard.
*/
do_space:
testb $0x04,mode /* ctrl */
je do_self
movl $0,%al /* ASCII NUL */
xorl %ebx,%ebx
jmp put_queue
/*
* This table decides which routine to call when a scan-code has been
* gotten. Most routines just call do_self, or none, depending if
* they are make or break.
*/
key_table:
.long none,do_self,do_self,do_self /* 00-03 s0 esc 1 2 */
.long do_self,do_self,do_self,do_self /* 04-07 3 4 5 6 */
.long do_self,do_self,do_self,do_self /* 08-0B 7 8 9 0 */
.long do_self,do_self,do_self,do_self /* 0C-0F + ' bs tab */
.long do_self,do_self,do_self,do_self /* 10-13 q w e r */
.long do_self,do_self,do_self,do_self /* 14-17 t y u i */
.long do_self,do_self,do_self,do_self /* 18-1B o p } ^ */
.long do_self,ctrl,do_self,do_self /* 1C-1F enter ctrl a s */
.long do_self,do_self,do_self,do_self /* 20-23 d f g h */
.long do_self,do_self,do_self,do_self /* 24-27 j k l | */
.long do_self,do_self,lshift,do_self /* 28-2B { para lshift , */
.long do_self,do_self,do_self,do_self /* 2C-2F z x c v */
.long do_self,do_self,do_self,do_self /* 30-33 b n m , */
.long do_self,minus,rshift,do_self /* 34-37 . - rshift * */
.long alt,do_space,ctrl,func /* 38-3B alt sp caps f1 */
.long func,func,func,func /* 3C-3F f2 f3 f4 f5 */
.long func,func,func,func /* 40-43 f6 f7 f8 f9 */
.long func,num,scroll,cursor /* 44-47 f10 num scr home */
.long cursor,cursor,do_self,cursor /* 48-4B up pgup - left */
.long cursor,cursor,do_self,cursor /* 4C-4F n5 right + end */
.long cursor,cursor,cursor,cursor /* 50-53 dn pgdn ins del */
.long none,none,do_self,func /* 54-57 sysreq ? < f11 */
.long func,none,none,none /* 58-5B f12 ? ? ? */
.long none,none,none,none /* 5C-5F ? ? ? ? */
.long none,none,none,none /* 60-63 ? ? ? ? */
.long none,none,none,none /* 64-67 ? ? ? ? */
.long none,none,none,none /* 68-6B ? ? ? ? */
.long none,none,none,none /* 6C-6F ? ? ? ? */
.long none,none,none,none /* 70-73 ? ? ? ? */
.long none,none,none,none /* 74-77 ? ? ? ? */
.long none,none,none,none /* 78-7B ? ? ? ? */
.long none,none,none,none /* 7C-7F ? ? ? ? */
.long none,none,none,none /* 80-83 ? br br br */
.long none,none,none,none /* 84-87 br br br br */
.long none,none,none,none /* 88-8B br br br br */
.long none,none,none,none /* 8C-8F br br br br */
.long none,none,none,none /* 90-93 br br br br */
.long none,none,none,none /* 94-97 br br br br */
.long none,none,none,none /* 98-9B br br br br */
.long none,unctrl,none,none /* 9C-9F br unctrl br br */
.long none,none,none,none /* A0-A3 br br br br */
.long none,none,none,none /* A4-A7 br br br br */
.long none,none,unlshift,none /* A8-AB br br unlshift br */
.long none,none,none,none /* AC-AF br br br br */
.long none,none,none,none /* B0-B3 br br br br */
.long none,none,unrshift,none /* B4-B7 br br unrshift br */
.long unalt,none,unctrl,none /* B8-BB unalt br uncaps br */
.long none,none,none,none /* BC-BF br br br br */
.long none,none,none,none /* C0-C3 br br br br */
.long none,none,none,none /* C4-C7 br br br br */
.long none,none,none,none /* C8-CB br br br br */
.long none,none,none,none /* CC-CF br br br br */
.long none,none,none,none /* D0-D3 br br br br */
.long none,none,none,none /* D4-D7 br br br br */
.long none,none,none,none /* D8-DB br ? ? ? */
.long none,none,none,none /* DC-DF ? ? ? ? */
.long none,none,none,none /* E0-E3 e0 e1 ? ? */
.long none,none,none,none /* E4-E7 ? ? ? ? */
.long none,none,none,none /* E8-EB ? ? ? ? */
.long none,none,none,none /* EC-EF ? ? ? ? */
.long none,none,none,none /* F0-F3 ? ? ? ? */
.long none,none,none,none /* F4-F7 ? ? ? ? */
.long none,none,none,none /* F8-FB ? ? ? ? */
.long none,none,none,none /* FC-FF ? ? ? ? */
/*
* kb_wait waits for the keyboard controller buffer to empty.
* there is no timeout - if the buffer doesn't empty, we hang.
*/
kb_wait:
pushl %eax
1: inb $0x64,%al
testb $0x02,%al
jne 1b
popl %eax
ret
/*
* This routine reboots the machine by asking the keyboard
* controller to pulse the reset-line low.
*/
reboot:
call kb_wait
movw $0x1234,0x472 /* don't do memory check */
movb $0xfc,%al /* pulse reset and A20 low */
outb %al,$0x64
die: jmp die
/*
* linux/kernel/rs_io.s
*
* (C) 1991 Linus Torvalds
*/
/* /*
* rs_io.s * rs_io.s
* *
...@@ -99,10 +105,10 @@ read_char: ...@@ -99,10 +105,10 @@ read_char:
cmpl tail(%ecx),%ebx cmpl tail(%ecx),%ebx
je 1f je 1f
movl %ebx,head(%ecx) movl %ebx,head(%ecx)
pushl %edx 1: pushl %edx
call _do_tty_interrupt call _do_tty_interrupt
addl $4,%esp addl $4,%esp
1: ret ret
.align 2 .align 2
write_char: write_char:
......
/*
* linux/kernel/serial.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* serial.c * serial.c
* *
......
/*
* linux/kernel/tty_io.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
* or rs-channels. It also implements echoing, cooked mode etc (well, * or rs-channels. It also implements echoing, cooked mode etc (well,
...@@ -8,6 +14,10 @@ ...@@ -8,6 +14,10 @@
#include <signal.h> #include <signal.h>
#define ALRMMASK (1<<(SIGALRM-1)) #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/sched.h>
#include <linux/tty.h> #include <linux/tty.h>
...@@ -39,7 +49,7 @@ ...@@ -39,7 +49,7 @@
struct tty_struct tty_table[] = { struct tty_struct tty_table[] = {
{ {
{0, {ICRNL,
OPOST|ONLCR, /* change outgoing NL to CRNL */ OPOST|ONLCR, /* change outgoing NL to CRNL */
0, 0,
ICANON | ECHO | ECHOCTL | ECHOKE, ICANON | ECHO | ECHOCTL | ECHOKE,
...@@ -97,7 +107,7 @@ void tty_init(void) ...@@ -97,7 +107,7 @@ void tty_init(void)
con_init(); con_init();
} }
void tty_intr(struct tty_struct * tty, int signal) void tty_intr(struct tty_struct * tty, int mask)
{ {
int i; int i;
...@@ -105,7 +115,7 @@ void tty_intr(struct tty_struct * tty, int signal) ...@@ -105,7 +115,7 @@ void tty_intr(struct tty_struct * tty, int signal)
return; return;
for (i=0;i<NR_TASKS;i++) for (i=0;i<NR_TASKS;i++)
if (task[i] && task[i]->pgrp==tty->pgrp) if (task[i] && task[i]->pgrp==tty->pgrp)
task[i]->signal |= 1<<(signal-1); task[i]->signal |= mask;
} }
static void sleep_if_empty(struct tty_queue * queue) static void sleep_if_empty(struct tty_queue * queue)
...@@ -126,6 +136,11 @@ static void sleep_if_full(struct tty_queue * queue) ...@@ -126,6 +136,11 @@ static void sleep_if_full(struct tty_queue * queue)
sti(); sti();
} }
void wait_for_keypress(void)
{
sleep_if_empty(&tty_table[0].secondary);
}
void copy_to_cooked(struct tty_struct * tty) void copy_to_cooked(struct tty_struct * tty)
{ {
signed char c; signed char c;
...@@ -168,7 +183,11 @@ void copy_to_cooked(struct tty_struct * tty) ...@@ -168,7 +183,11 @@ void copy_to_cooked(struct tty_struct * tty)
} }
if (!L_ISIG(tty)) { if (!L_ISIG(tty)) {
if (c==INTR_CHAR(tty)) { if (c==INTR_CHAR(tty)) {
tty_intr(tty,SIGINT); tty_intr(tty,INTMASK);
continue;
}
if (c==KILL_CHAR(tty)) {
tty_intr(tty,KILLMASK);
continue; continue;
} }
} }
...@@ -299,8 +318,16 @@ int tty_write(unsigned channel, char * buf, int nr) ...@@ -299,8 +318,16 @@ int tty_write(unsigned channel, char * buf, int nr)
* hate intel for all time :-). We'll have to * hate intel for all time :-). We'll have to
* be careful and see to reinstating the interrupt * be careful and see to reinstating the interrupt
* chips before calling this, though. * chips before calling this, though.
*
* I don't think we sleep here under normal circumstances
* anyway, which is good, as the task sleeping might be
* totally innocent.
*/ */
void do_tty_interrupt(int tty) void do_tty_interrupt(int tty)
{ {
copy_to_cooked(tty_table+tty); copy_to_cooked(tty_table+tty);
} }
void chr_dev_init(void)
{
}
/*
* linux/kernel/exit.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
#include <sys/wait.h> #include <sys/wait.h>
...@@ -26,39 +32,69 @@ void release(struct task_struct * p) ...@@ -26,39 +32,69 @@ void release(struct task_struct * p)
panic("trying to release non-existent task"); panic("trying to release non-existent task");
} }
static inline void send_sig(long sig,struct task_struct * p,int priv) static inline int send_sig(long sig,struct task_struct * p,int priv)
{ {
if (!p || sig<1 || sig>32) if (!p || sig<1 || sig>32)
return; return -EINVAL;
if (priv || if (priv || (current->euid==p->euid) || suser())
current->uid==p->uid ||
current->euid==p->uid ||
current->uid==p->euid ||
current->euid==p->euid)
p->signal |= (1<<(sig-1)); p->signal |= (1<<(sig-1));
else
return -EPERM;
return 0;
} }
void do_kill(long pid,long sig,int priv) static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;
while (--p > &FIRST_TASK) {
if (*p && (*p)->session == current->session)
(*p)->signal |= 1<<(SIGHUP-1);
}
}
/*
* XXX need to check permissions needed to send signals to process
* groups, etc. etc. kill() permissions semantics are tricky!
*/
int sys_kill(int pid,int sig)
{ {
struct task_struct **p = NR_TASKS + task; struct task_struct **p = NR_TASKS + task;
int err, retval = 0;
if (!pid) while (--p > &FIRST_TASK) { if (!pid) while (--p > &FIRST_TASK) {
if (*p && (*p)->pgrp == current->pid) if (*p && (*p)->pgrp == current->pid)
send_sig(sig,*p,priv); if (err=send_sig(sig,*p,1))
retval = err;
} else if (pid>0) while (--p > &FIRST_TASK) { } else if (pid>0) while (--p > &FIRST_TASK) {
if (*p && (*p)->pid == pid) if (*p && (*p)->pid == pid)
send_sig(sig,*p,priv); if (err=send_sig(sig,*p,0))
retval = err;
} else if (pid == -1) while (--p > &FIRST_TASK) } else if (pid == -1) while (--p > &FIRST_TASK)
send_sig(sig,*p,priv); if (err = send_sig(sig,*p,0))
retval = err;
else while (--p > &FIRST_TASK) else while (--p > &FIRST_TASK)
if (*p && (*p)->pgrp == -pid) if (*p && (*p)->pgrp == -pid)
send_sig(sig,*p,priv); if (err = send_sig(sig,*p,0))
retval = err;
return retval;
} }
int sys_kill(int pid,int sig) static void tell_father(int pid)
{ {
do_kill(pid,sig,!(current->uid || current->euid)); int i;
return 0;
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 */
release(current);
} }
int do_exit(long code) int do_exit(long code)
...@@ -81,12 +117,11 @@ int do_exit(long code) ...@@ -81,12 +117,11 @@ int do_exit(long code)
tty_table[current->tty].pgrp = 0; tty_table[current->tty].pgrp = 0;
if (last_task_used_math == current) if (last_task_used_math == current)
last_task_used_math = NULL; last_task_used_math = NULL;
if (current->father) { if (current->leader)
current->state = TASK_ZOMBIE; kill_session();
do_kill(current->father,SIGCHLD,1); current->state = TASK_ZOMBIE;
current->exit_code = code; current->exit_code = code;
} else tell_father(current->father);
release(current);
schedule(); schedule();
return (-1); /* just to suppress warnings */ return (-1); /* just to suppress warnings */
} }
...@@ -96,34 +131,52 @@ int sys_exit(int error_code) ...@@ -96,34 +131,52 @@ int sys_exit(int error_code)
return do_exit((error_code&0xff)<<8); return do_exit((error_code&0xff)<<8);
} }
int sys_waitpid(pid_t pid,int * stat_addr, int options) int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{ {
int flag=0; int flag;
struct task_struct ** p; struct task_struct ** p;
verify_area(stat_addr,4); verify_area(stat_addr,4);
repeat: repeat:
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) flag=0;
if (*p && *p != current && for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
(pid==-1 || (*p)->pid==pid || if (!*p || *p == current)
(pid==0 && (*p)->pgrp==current->pgrp) || continue;
(pid<0 && (*p)->pgrp==-pid))) if ((*p)->father != current->pid)
if ((*p)->father == current->pid) { continue;
if (pid>0) {
if ((*p)->pid != pid)
continue;
} else if (!pid) {
if ((*p)->pgrp != current->pgrp)
continue;
} else if (pid != -1) {
if ((*p)->pgrp != -pid)
continue;
}
switch ((*p)->state) {
case TASK_STOPPED:
if (!(options & WUNTRACED))
continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
case TASK_ZOMBIE:
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
put_fs_long((*p)->exit_code,stat_addr);
release(*p);
return flag;
default:
flag=1; flag=1;
if ((*p)->state==TASK_ZOMBIE) { continue;
put_fs_long((*p)->exit_code, }
(unsigned long *) stat_addr); }
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
release(*p);
return flag;
}
}
if (flag) { if (flag) {
if (options & WNOHANG) if (options & WNOHANG)
return 0; return 0;
sys_pause(); current->state=TASK_INTERRUPTIBLE;
schedule();
if (!(current->signal &= ~(1<<(SIGCHLD-1)))) if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat; goto repeat;
else else
......
/*
* linux/kernel/exit.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <asm/segment.h>
int sys_pause(void);
int sys_close(int fd);
void release(struct task_struct * p)
{
int i;
if (!p)
return;
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
free_page((long)p);
schedule();
return;
}
panic("trying to release non-existent task");
}
static inline void send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32)
return;
if (priv ||
current->uid==p->uid ||
current->euid==p->uid ||
current->uid==p->euid ||
current->euid==p->euid)
p->signal |= (1<<(sig-1));
}
static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;
while (--p > &FIRST_TASK) {
if (*p && (*p)->session == current->session)
(*p)->signal |= 1<<(SIGHUP-1);
}
}
void do_kill(long pid,long sig,int priv)
{
struct task_struct **p = NR_TASKS + task;
if (!pid) while (--p > &FIRST_TASK) {
if (*p && (*p)->pgrp == current->pid)
send_sig(sig,*p,priv);
} else if (pid>0) while (--p > &FIRST_TASK) {
if (*p && (*p)->pid == pid)
send_sig(sig,*p,priv);
} else if (pid == -1) while (--p > &FIRST_TASK)
send_sig(sig,*p,priv);
else while (--p > &FIRST_TASK)
if (*p && (*p)->pgrp == -pid)
send_sig(sig,*p,priv);
}
int sys_kill(int pid,int sig)
{
do_kill(pid,sig,!(current->uid || current->euid));
return 0;
}
static void tell_father(int pid)
{
int i;
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 */
release(current);
}
int do_exit(long code)
{
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 = 0;
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
sys_close(i);
iput(current->pwd);
current->pwd=NULL;
iput(current->root);
current->root=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->state = TASK_ZOMBIE;
current->exit_code = code;
tell_father(current->father);
schedule();
return (-1); /* just to suppress warnings */
}
int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
int flag;
struct task_struct ** p;
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;
if (pid>0) {
if ((*p)->pid != pid)
continue;
} else if (!pid) {
if ((*p)->pgrp != current->pgrp)
continue;
} else if (pid != -1) {
if ((*p)->pgrp != -pid)
continue;
}
switch ((*p)->state) {
case TASK_STOPPED:
if (!(options & WUNTRACED))
continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
case TASK_ZOMBIE:
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
put_fs_long((*p)->exit_code,stat_addr);
release(*p);
return flag;
default:
flag=1;
continue;
}
}
if (flag) {
if (options & WNOHANG)
return 0;
current->state=TASK_INTERRUPTIBLE;
schedule();
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
else
return -EINTR;
}
return -ECHILD;
}
/*
* linux/kernel/fork.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* 'fork.c' contains the help-routines for the 'fork' system call * 'fork.c' contains the help-routines for the 'fork' system call
* (see also system_call.s), and some misc functions ('verify_area'). * (see also system_call.s), and some misc functions ('verify_area').
......
/*
* malloc.c --- a general purpose kernel memory allocator for Linux.
*
* Written by Theodore Ts'o (tytso@mit.edu), 11/29/91
*
* This routine is written to be as fast as possible, so that it
* can be called from the interrupt level.
*
* Limitations: maximum size of memory we can allocate using this routine
* is 4k, the size of a page in Linux.
*
* The general game plan is that each page (called a bucket) will only hold
* objects of a given size. When all of the object on a page are released,
* the page can be returned to the general free pool. When malloc() is
* called, it looks for the smallest bucket size which will fulfill its
* request, and allocate a piece of memory from that bucket pool.
*
* Each bucket has as its control block a bucket descriptor which keeps
* track of how many objects are in use on that page, and the free list
* for that page. Like the buckets themselves, bucket descriptors are
* stored on pages requested from get_free_page(). However, unlike buckets,
* pages devoted to bucket descriptor pages are never released back to the
* system. Fortunately, a system should probably only need 1 or 2 bucket
* descriptor pages, since a page can hold 256 bucket descriptors (which
* corresponds to 1 megabyte worth of bucket pages.) If the kernel is using
* that much allocated memory, it's probably doing something wrong. :-)
*
* Note: malloc() and free() both call get_free_page() and free_page()
* in sections of code where interrupts are turned off, to allow
* malloc() and free() to be safely called from an interrupt routine.
* (We will probably need this functionality when networking code,
* particularily things like NFS, is added to Linux.) However, this
* presumes that get_free_page() and free_page() are interrupt-level
* safe, which they may not be once paging is added. If this is the
* case, we will need to modify malloc() to keep a few unused pages
* "pre-allocated" so that it can safely draw upon those pages if
* it is called from an interrupt routine.
*
* Another concern is that get_free_page() should not sleep; if it
* does, the code is carefully ordered so as to avoid any race
* conditions. The catch is that if malloc() is called re-entrantly,
* there is a chance that unecessary pages will be grabbed from the
* system. Except for the pages for the bucket descriptor page, the
* extra pages will eventually get released back to the system, though,
* so it isn't all that bad.
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/system.h>
struct bucket_desc { /* 16 bytes */
void *page;
struct bucket_desc *next;
void *freeptr;
unsigned short refcnt;
unsigned short bucket_size;
};
struct _bucket_dir { /* 8 bytes */
int size;
struct bucket_desc *chain;
};
/*
* The following is the where we store a pointer to the first bucket
* descriptor for a given size.
*
* If it turns out that the Linux kernel allocates a lot of objects of a
* specific size, then we may want to add that specific size to this list,
* since that will allow the memory to be allocated more efficiently.
* However, since an entire page must be dedicated to each specific size
* on this list, some amount of temperance must be exercised here.
*
* Note that this list *must* be kept in order.
*/
struct _bucket_dir bucket_dir[] = {
{ 16, (struct bucket_desc *) 0},
{ 32, (struct bucket_desc *) 0},
{ 64, (struct bucket_desc *) 0},
{ 128, (struct bucket_desc *) 0},
{ 256, (struct bucket_desc *) 0},
{ 512, (struct bucket_desc *) 0},
{ 1024, (struct bucket_desc *) 0},
{ 2048, (struct bucket_desc *) 0},
{ 4096, (struct bucket_desc *) 0},
{ 0, (struct bucket_desc *) 0}}; /* End of list marker */
/*
* This contains a linked list of free bucket descriptor blocks
*/
struct bucket_desc *free_bucket_desc = (struct bucket_desc *) 0;
/*
* This routine initializes a bucket description page.
*/
static inline void init_bucket_desc()
{
struct bucket_desc *bdesc, *first;
int i;
first = bdesc = (struct bucket_desc *) get_free_page();
if (!bdesc)
panic("Out of memory in init_bucket_desc()");
for (i = PAGE_SIZE/sizeof(struct bucket_desc); i > 1; i--) {
bdesc->next = bdesc+1;
bdesc++;
}
/*
* This is done last, to avoid race conditions in case
* get_free_page() sleeps and this routine gets called again....
*/
bdesc->next = free_bucket_desc;
free_bucket_desc = first;
}
void *malloc(unsigned int len)
{
struct _bucket_dir *bdir;
struct bucket_desc *bdesc;
void *retval;
/*
* First we search the bucket_dir to find the right bucket change
* for this request.
*/
for (bdir = bucket_dir; bdir->size; bdir++)
if (bdir->size >= len)
break;
if (!bdir->size) {
printk("malloc called with impossibly large argument (%d)\n",
len);
panic("malloc: bad arg");
}
/*
* Now we search for a bucket descriptor which has free space
*/
cli(); /* Avoid race conditions */
for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next)
if (bdesc->freeptr)
break;
/*
* If we didn't find a bucket with free space, then we'll
* allocate a new one.
*/
if (!bdesc) {
char *cp;
int i;
if (!free_bucket_desc)
init_bucket_desc();
bdesc = free_bucket_desc;
free_bucket_desc = bdesc->next;
bdesc->refcnt = 0;
bdesc->bucket_size = bdir->size;
bdesc->page = bdesc->freeptr = (void *) cp = get_free_page();
if (!cp)
panic("Out of memory in kernel malloc()");
/* Set up the chain of free objects */
for (i=PAGE_SIZE/bdir->size; i > 1; i--) {
*((char **) cp) = cp + bdir->size;
cp += bdir->size;
}
*((char **) cp) = 0;
bdesc->next = bdir->chain; /* OK, link it in! */
bdir->chain = bdesc;
}
retval = (void *) bdesc->freeptr;
bdesc->freeptr = *((void **) retval);
bdesc->refcnt++;
sti(); /* OK, we're safe again */
return(retval);
}
/*
* Here is the free routine. If you know the size of the object that you
* are freeing, then free_s() will use that information to speed up the
* search for the bucket descriptor.
*
* We will #define a macro so that "free(x)" is becomes "free_s(x, 0)"
*/
void free_s(void *obj, int size)
{
void *page;
struct _bucket_dir *bdir;
struct bucket_desc *bdesc, *prev;
/* Calculate what page this object lives in */
page = (void *) ((unsigned long) obj & 0xfffff000);
/* Now search the buckets looking for that page */
for (bdir = bucket_dir; bdir->size; bdir++) {
prev = 0;
/* If size is zero then this conditional is always false */
if (bdir->size < size)
continue;
for (bdesc = bdir->chain; bdesc; bdesc = bdesc->next) {
if (bdesc->page == page)
goto found;
prev = bdesc;
}
}
panic("Bad address passed to kernel free_s()");
found:
cli(); /* To avoid race conditions */
*((void **)obj) = bdesc->freeptr;
bdesc->freeptr = obj;
bdesc->refcnt--;
if (bdesc->refcnt == 0) {
/*
* We need to make sure that prev is still accurate. It
* may not be, if someone rudely interrupted us....
*/
if ((prev && (prev->next != bdesc)) ||
(!prev && (bdir->chain != bdesc)))
for (prev = bdir->chain; prev; prev = prev->next)
if (prev->next == bdesc)
break;
if (prev)
prev->next = bdesc->next;
else {
if (bdir->chain != bdesc)
panic("malloc bucket chains corrupted");
bdir->chain = bdesc->next;
}
free_page((unsigned long) bdesc->page);
bdesc->next = free_bucket_desc;
free_bucket_desc = bdesc;
}
sti();
return;
}
/*
* linux/kernel/mktime.c
*
* (C) 1991 Linus Torvalds
*/
#include <time.h> #include <time.h>
/* /*
......
/*
* linux/kernel/panic.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* This function is used through-out the kernel (includeinh mm and fs) * This function is used through-out the kernel (includeinh mm and fs)
* to indicate a major problem. * to indicate a major problem.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h>
void sys_sync(void); /* it's really int */
volatile void panic(const char * s) volatile void panic(const char * s)
{ {
printk("Kernel panic: %s\n\r",s); printk("Kernel panic: %s\n\r",s);
if (current == task[0])
printk("In swapper task - not syncing\n\r");
else
sys_sync();
for(;;); for(;;);
} }
/*
* linux/kernel/printk.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* When in kernel-mode, we cannot use printf, as fs is liable to * When in kernel-mode, we cannot use printf, as fs is liable to
* point to 'interesting' things. Make a printf with fs-saving, and * point to 'interesting' things. Make a printf with fs-saving, and
...@@ -10,6 +16,8 @@ ...@@ -10,6 +16,8 @@
static char buf[1024]; static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
int printk(const char *fmt, ...) int printk(const char *fmt, ...)
{ {
va_list args; va_list args;
......
/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* 'sched.c' is the main kernel file. It contains scheduling primitives * 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system * (sleep_on, wakeup, schedule etc) as well as a number of simple system
...@@ -6,12 +12,32 @@ ...@@ -6,12 +12,32 @@
*/ */
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <signal.h>
#include <linux/sys.h> #include <linux/sys.h>
#include <linux/fdreg.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/segment.h> #include <asm/segment.h>
#include <signal.h>
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
void show_task(int nr,struct task_struct * p)
{
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
printk("eip=%04x:%08x\n\r",p->tss.cs&0xffff,p->tss.eip);
}
void show_stat(void)
{
int i;
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
}
#define LATCH (1193180/HZ) #define LATCH (1193180/HZ)
extern void mem_use(void); extern void mem_use(void);
...@@ -28,7 +54,8 @@ static union task_union init_task = {INIT_TASK,}; ...@@ -28,7 +54,8 @@ static union task_union init_task = {INIT_TASK,};
long volatile jiffies=0; long volatile jiffies=0;
long startup_time=0; long startup_time=0;
struct task_struct *current = &(init_task.task), *last_task_used_math = NULL; struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&(init_task.task), }; struct task_struct * task[NR_TASKS] = {&(init_task.task), };
...@@ -44,11 +71,14 @@ struct { ...@@ -44,11 +71,14 @@ struct {
*/ */
void math_state_restore() void math_state_restore()
{ {
if (last_task_used_math) if (last_task_used_math == current)
return;
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
if (current->used_math) }
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387)); __asm__("frstor %0"::"m" (current->tss.i387));
else { } else {
__asm__("fninit"::); __asm__("fninit"::);
current->used_math=1; current->used_math=1;
} }
...@@ -78,7 +108,8 @@ void schedule(void) ...@@ -78,7 +108,8 @@ void schedule(void)
(*p)->signal |= (1<<(SIGALRM-1)); (*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0; (*p)->alarm = 0;
} }
if ((*p)->signal && (*p)->state==TASK_INTERRUPTIBLE) if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING; (*p)->state=TASK_RUNNING;
} }
...@@ -156,12 +187,152 @@ void wake_up(struct task_struct **p) ...@@ -156,12 +187,152 @@ void wake_up(struct task_struct **p)
} }
} }
/*
* OK, here are some floppy things that shouldn't be in the kernel
* proper. They are here because the floppy needs a timer, and this
* was the easiest way of doing it.
*/
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};
static int mon_timer[4]={0,0,0,0};
static int moff_timer[4]={0,0,0,0};
unsigned char current_DOR = 0x0C;
unsigned char selected = 0;
struct task_struct * wait_on_floppy_select = NULL;
void floppy_select(unsigned int nr)
{
if (nr>3)
printk("floppy_select: nr>3\n\r");
cli();
while (selected)
sleep_on(&wait_on_floppy_select);
current_DOR &= 0xFC;
current_DOR |= nr;
outb(current_DOR,FD_DOR);
sti();
}
void floppy_deselect(unsigned int nr)
{
if (nr != (current_DOR & 3))
printk("floppy_deselect: drive not selected\n\r");
selected = 0;
wake_up(&wait_on_floppy_select);
}
int ticks_to_floppy_on(unsigned int nr)
{
unsigned char mask = 1<<(nr+4);
if (nr>3)
panic("floppy_on: nr>3");
moff_timer[nr]=10000; /* 100 s = very big :-) */
cli(); /* use floppy_off to turn it off */
if (!(mask & current_DOR)) {
current_DOR |= mask;
if (!selected) {
current_DOR &= 0xFC;
current_DOR |= nr;
}
outb(current_DOR,FD_DOR);
mon_timer[nr] = HZ;
}
sti();
return mon_timer[nr];
}
void floppy_on(unsigned int nr)
{
cli();
while (ticks_to_floppy_on(nr))
sleep_on(nr+wait_motor);
sti();
}
void floppy_off(unsigned int nr)
{
moff_timer[nr]=3*HZ;
}
void do_floppy_timer(void)
{
int i;
unsigned char mask = 0x10;
for (i=0 ; i<4 ; i++,mask <<= 1) {
if (!(mask & current_DOR))
continue;
if (mon_timer[i]) {
if (!--mon_timer[i])
wake_up(i+wait_motor);
} else if (!moff_timer[i]) {
current_DOR &= ~mask;
outb(current_DOR,FD_DOR);
} else
moff_timer[i]--;
}
}
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
void do_timer(long cpl) void do_timer(long cpl)
{ {
if (cpl) if (cpl)
current->utime++; current->utime++;
else else
current->stime++; current->stime++;
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
if (current_DOR & 0xf0)
do_floppy_timer();
if ((--current->counter)>0) return; if ((--current->counter)>0) return;
current->counter=0; current->counter=0;
if (!cpl) return; if (!cpl) return;
...@@ -170,8 +341,12 @@ void do_timer(long cpl) ...@@ -170,8 +341,12 @@ void do_timer(long cpl)
int sys_alarm(long seconds) int sys_alarm(long seconds)
{ {
int old = current->alarm;
if (old)
old = (old - jiffies) / HZ;
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0; current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return seconds; return (old);
} }
int sys_getpid(void) int sys_getpid(void)
...@@ -211,28 +386,13 @@ int sys_nice(long increment) ...@@ -211,28 +386,13 @@ int sys_nice(long increment)
return 0; return 0;
} }
int sys_signal(long signal,long addr,long restorer)
{
long i;
switch (signal) {
case SIGHUP: case SIGINT: case SIGQUIT: case SIGILL:
case SIGTRAP: case SIGABRT: case SIGFPE: case SIGUSR1:
case SIGSEGV: case SIGUSR2: case SIGPIPE: case SIGALRM:
case SIGCHLD:
i=(long) current->sig_fn[signal-1];
current->sig_fn[signal-1] = (fn_ptr) addr;
current->sig_restorer = (fn_ptr) restorer;
return i;
default: return -1;
}
}
void sched_init(void) void sched_init(void)
{ {
int i; int i;
struct desc_struct * p; struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)); set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt)); set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY; p = gdt+2+FIRST_TSS_ENTRY;
......
/*
* linux/kernel/sched.c
*
* (C) 1991 Linus Torvalds
*/
/*
* 'sched.c' is the main kernel file. It contains scheduling primitives
* (sleep_on, wakeup, schedule etc) as well as a number of simple system
* call functions (type getpid(), which just extracts a field from
* current-task
*/
#include <linux/sched.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 <signal.h>
#define _S(nr) (1<<((nr)-1))
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
void show_task(int nr,struct task_struct * p)
{
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
printk("eip=%04x:%08x\n\r",p->tss.cs&0xffff,p->tss.eip);
}
void show_stat(void)
{
int i;
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
}
#define LATCH (1193180/HZ)
extern void mem_use(void);
extern int timer_interrupt(void);
extern int system_call(void);
union task_union {
struct task_struct task;
char stack[PAGE_SIZE];
};
static union task_union init_task = {INIT_TASK,};
long volatile jiffies=0;
long startup_time=0;
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
struct task_struct * task[NR_TASKS] = {&(init_task.task), };
long user_stack [ PAGE_SIZE>>2 ] ;
struct {
long * a;
short b;
} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
/*
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
void math_state_restore()
{
if (last_task_used_math == current)
return;
if (last_task_used_math) {
__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
}
if (current->used_math) {
__asm__("frstor %0"::"m" (current->tss.i387));
} else {
__asm__("fninit"::);
current->used_math=1;
}
last_task_used_math=current;
}
/*
* 'schedule()' is the scheduler function. This is GOOD CODE! There
* probably won't be any reason to change this, as it should work well
* in all circumstances (ie gives IO-bound processes good response etc).
* The one thing you might take a look at is the signal-handler code here.
*
* NOTE!! Task 0 is the 'idle' task, which gets called when no other
* tasks can run. It can not be killed, and it cannot sleep. The 'state'
* information in task[0] is never used.
*/
void schedule(void)
{
int i,next,c;
struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that have got a signal */
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
if ((*p)->alarm && (*p)->alarm < jiffies) {
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
/* this is the scheduler proper: */
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)
continue;
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i;
}
if (c) break;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
}
switch_to(next);
}
int sys_pause(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}
void sleep_on(struct task_struct **p)
{
struct task_struct *tmp;
if (!p)
return;
if (current == &(init_task.task))
panic("task[0] trying to sleep");
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
if (tmp)
tmp->state=0;
}
void interruptible_sleep_on(struct task_struct **p)
{
struct task_struct *tmp;
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 wake_up(struct task_struct **p)
{
if (p && *p) {
(**p).state=0;
*p=NULL;
}
}
/*
* OK, here are some floppy things that shouldn't be in the kernel
* proper. They are here because the floppy needs a timer, and this
* was the easiest way of doing it.
*/
static struct task_struct * wait_motor[4] = {NULL,NULL,NULL,NULL};
static int mon_timer[4]={0,0,0,0};
static int moff_timer[4]={0,0,0,0};
unsigned char current_DOR = 0x0C;
unsigned char selected = 0;
struct task_struct * wait_on_floppy_select = NULL;
void floppy_select(unsigned int nr)
{
if (nr>3)
printk("floppy_select: nr>3\n\r");
cli();
while (selected)
sleep_on(&wait_on_floppy_select);
current_DOR &= 0xFC;
current_DOR |= nr;
outb(current_DOR,FD_DOR);
sti();
}
void floppy_deselect(unsigned int nr)
{
if (nr != (current_DOR & 3))
printk("floppy_deselect: drive not selected\n\r");
selected = 0;
wake_up(&wait_on_floppy_select);
}
int ticks_to_floppy_on(unsigned int nr)
{
unsigned char mask = 1<<(nr+4);
if (nr>3)
panic("floppy_on: nr>3");
moff_timer[nr]=10000; /* 100 s = very big :-) */
cli(); /* use floppy_off to turn it off */
if (!(mask & current_DOR)) {
current_DOR |= mask;
if (!selected) {
current_DOR &= 0xFC;
current_DOR |= nr;
}
outb(current_DOR,FD_DOR);
mon_timer[nr] = HZ;
}
sti();
return mon_timer[nr];
}
void floppy_on(unsigned int nr)
{
cli();
while (ticks_to_floppy_on(nr))
sleep_on(nr+wait_motor);
sti();
}
void floppy_off(unsigned int nr)
{
moff_timer[nr]=3*HZ;
}
void do_floppy_timer(void)
{
int i;
unsigned char mask = 0x10;
for (i=0 ; i<4 ; i++,mask <<= 1) {
if (!(mask & current_DOR))
continue;
if (mon_timer[i]) {
if (!--mon_timer[i])
wake_up(i+wait_motor);
} else if (!moff_timer[i]) {
current_DOR &= ~mask;
outb(current_DOR,FD_DOR);
} else
moff_timer[i]--;
}
}
#define TIME_REQUESTS 64
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
void add_timer(long jiffies, void (*fn)(void))
{
struct timer_list * p;
if (!fn)
return;
cli();
if (jiffies <= 0)
(fn)();
else {
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}
}
sti();
}
void do_timer(long cpl)
{
if (cpl)
current->utime++;
else
current->stime++;
if (next_timer) {
next_timer->jiffies--;
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);
fn = next_timer->fn;
next_timer->fn = NULL;
next_timer = next_timer->next;
(fn)();
}
}
if (current_DOR & 0xf0)
do_floppy_timer();
if ((--current->counter)>0) return;
current->counter=0;
if (!cpl) return;
schedule();
}
int sys_alarm(long seconds)
{
current->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
return seconds;
}
int sys_getpid(void)
{
return current->pid;
}
int sys_getppid(void)
{
return current->father;
}
int sys_getuid(void)
{
return current->uid;
}
int sys_geteuid(void)
{
return current->euid;
}
int sys_getgid(void)
{
return current->gid;
}
int sys_getegid(void)
{
return current->egid;
}
int sys_nice(long increment)
{
if (current->priority-increment>0)
current->priority -= increment;
return 0;
}
void sched_init(void)
{
int i;
struct desc_struct * p;
if (sizeof(struct sigaction) != 16)
panic("Struct sigaction MUST be 16 bytes");
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));
set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));
p = gdt+2+FIRST_TSS_ENTRY;
for(i=1;i<NR_TASKS;i++) {
task[i] = NULL;
p->a=p->b=0;
p++;
p->a=p->b=0;
p++;
}
ltr(0);
lldt(0);
outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */
set_intr_gate(0x20,&timer_interrupt);
outb(inb_p(0x21)&~0x01,0x21);
set_system_gate(0x80,&system_call);
}
/*
* linux/kernel/signal.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <signal.h>
volatile void do_exit(int error_code);
int sys_sgetmask()
{
return current->blocked;
}
int sys_ssetmask(int newmask)
{
int old=current->blocked;
current->blocked = newmask & ~(1<<(SIGKILL-1));
return old;
}
static inline void save_old(char * from,char * to)
{
int i;
verify_area(to, sizeof(struct sigaction));
for (i=0 ; i< sizeof(struct sigaction) ; i++) {
put_fs_byte(*from,to);
from++;
to++;
}
}
static inline void get_new(char * from,char * to)
{
int i;
for (i=0 ; i< sizeof(struct sigaction) ; i++)
*(to++) = get_fs_byte(from++);
}
int sys_signal(int signum, long handler, long restorer)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
tmp.sa_handler = (void (*)(int)) handler;
tmp.sa_mask = 0;
tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
tmp.sa_restorer = (void (*)(void)) restorer;
handler = (long) current->sigaction[signum-1].sa_handler;
current->sigaction[signum-1] = tmp;
return handler;
}
int sys_sigaction(int signum, const struct sigaction * action,
struct sigaction * oldaction)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
tmp = current->sigaction[signum-1];
get_new((char *) action,
(char *) (signum-1+current->sigaction));
if (oldaction)
save_old((char *) &tmp,(char *) oldaction);
if (current->sigaction[signum-1].sa_flags & SA_NOMASK)
current->sigaction[signum-1].sa_mask = 0;
else
current->sigaction[signum-1].sa_mask |= (1<<(signum-1));
return 0;
}
void do_signal(long signr,long eax, long ebx, long ecx, long edx,
long fs, long es, long ds,
long eip, long cs, long eflags,
unsigned long * esp, long ss)
{
long sa_handler;
long old_eip=eip;
struct sigaction * sa = current->sigaction + signr - 1;
int longs;
unsigned long * tmp_esp;
sa_handler = (long) sa->sa_handler;
if (sa_handler==1)
return;
if (!sa_handler) {
if (signr==SIGCHLD)
return;
else
do_exit(1<<(signr-1));
}
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
*(&eip) = sa_handler;
longs = (sa->sa_flags & SA_NOMASK)?7:8;
*(&esp) -= longs;
verify_area(esp,longs*4);
tmp_esp=esp;
put_fs_long((long) sa->sa_restorer,tmp_esp++);
put_fs_long(signr,tmp_esp++);
if (!(sa->sa_flags & SA_NOMASK))
put_fs_long(current->blocked,tmp_esp++);
put_fs_long(eax,tmp_esp++);
put_fs_long(ecx,tmp_esp++);
put_fs_long(edx,tmp_esp++);
put_fs_long(eflags,tmp_esp++);
put_fs_long(old_eip,tmp_esp++);
current->blocked |= sa->sa_mask;
}
/*
* linux/kernel/sys.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h> #include <errno.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -12,31 +18,11 @@ int sys_ftime() ...@@ -12,31 +18,11 @@ int sys_ftime()
return -ENOSYS; return -ENOSYS;
} }
int sys_mknod()
{
return -ENOSYS;
}
int sys_break() int sys_break()
{ {
return -ENOSYS; return -ENOSYS;
} }
int sys_mount()
{
return -ENOSYS;
}
int sys_umount()
{
return -ENOSYS;
}
int sys_ustat(int dev,struct ustat * ubuf)
{
return -1;
}
int sys_ptrace() int sys_ptrace()
{ {
return -ENOSYS; return -ENOSYS;
...@@ -62,18 +48,32 @@ int sys_prof() ...@@ -62,18 +48,32 @@ int sys_prof()
return -ENOSYS; return -ENOSYS;
} }
int sys_setgid(int gid) int sys_setregid(int rgid, int egid)
{ {
if (current->euid && current->uid) if (rgid>0) {
if (current->gid==gid || current->sgid==gid) if ((current->gid == rgid) ||
current->egid=gid; suser())
current->gid = rgid;
else
return(-EPERM);
}
if (egid>0) {
if ((current->gid == egid) ||
(current->egid == egid) ||
(current->sgid == egid) ||
suser())
current->egid = egid;
else else
return -EPERM; return(-EPERM);
else }
current->gid=current->egid=gid;
return 0; return 0;
} }
int sys_setgid(int gid)
{
return(sys_setregid(gid, gid));
}
int sys_acct() int sys_acct()
{ {
return -ENOSYS; return -ENOSYS;
...@@ -111,35 +111,57 @@ int sys_time(long * tloc) ...@@ -111,35 +111,57 @@ int sys_time(long * tloc)
return i; return i;
} }
int sys_setuid(int uid) /*
{ * Unprivileged users may change the real user id to the effective uid
if (current->euid && current->uid) * or vice versa.
if (uid==current->uid || current->suid==current->uid) */
current->euid=uid; int sys_setreuid(int ruid, int euid)
{
int old_ruid = current->uid;
if (ruid>0) {
if ((current->euid==ruid) ||
(old_ruid == ruid) ||
suser())
current->uid = ruid;
else else
return -EPERM; return(-EPERM);
else }
current->euid=current->uid=uid; if (euid>0) {
if ((old_ruid == euid) ||
(current->euid == euid) ||
suser())
current->euid = euid;
else {
current->uid = old_ruid;
return(-EPERM);
}
}
return 0; return 0;
} }
int sys_setuid(int uid)
{
return(sys_setreuid(uid, uid));
}
int sys_stime(long * tptr) int sys_stime(long * tptr)
{ {
if (current->euid && current->uid) if (!suser())
return -1; return -EPERM;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ; startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
return 0; return 0;
} }
int sys_times(struct tms * tbuf) int sys_times(struct tms * tbuf)
{ {
if (!tbuf) if (tbuf) {
return jiffies; verify_area(tbuf,sizeof *tbuf);
verify_area(tbuf,sizeof *tbuf); put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime); put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime); put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime); put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime); }
return jiffies; return jiffies;
} }
...@@ -163,7 +185,7 @@ int sys_setpgid(int pid, int pgid) ...@@ -163,7 +185,7 @@ int sys_setpgid(int pid, int pgid)
if (!pid) if (!pid)
pid = current->pid; pid = current->pid;
if (!pgid) if (!pgid)
pgid = pid; pgid = current->pid;
for (i=0 ; i<NR_TASKS ; i++) for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->pid==pid) { if (task[i] && task[i]->pid==pid) {
if (task[i]->leader) if (task[i]->leader)
...@@ -183,9 +205,7 @@ int sys_getpgrp(void) ...@@ -183,9 +205,7 @@ int sys_getpgrp(void)
int sys_setsid(void) int sys_setsid(void)
{ {
if (current->uid && current->euid) if (current->leader && !suser())
return -EPERM;
if (current->leader)
return -EPERM; return -EPERM;
current->leader = 1; current->leader = 1;
current->session = current->pgrp = current->pid; current->session = current->pgrp = current->pid;
...@@ -200,11 +220,11 @@ int sys_uname(struct utsname * name) ...@@ -200,11 +220,11 @@ int sys_uname(struct utsname * name)
}; };
int i; int i;
if (!name) return -1; if (!name) return -ERROR;
verify_area(name,sizeof *name); verify_area(name,sizeof *name);
for(i=0;i<sizeof *name;i++) for(i=0;i<sizeof *name;i++)
put_fs_byte(((char *) &thisname)[i],i+(char *) name); put_fs_byte(((char *) &thisname)[i],i+(char *) name);
return (0); return 0;
} }
int sys_umask(int mask) int sys_umask(int mask)
......
/*
* linux/kernel/sys.c
*
* (C) 1991 Linus Torvalds
*/
#include <errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <sys/times.h>
#include <sys/utsname.h>
int sys_ftime()
{
return -ENOSYS;
}
int sys_break()
{
return -ENOSYS;
}
int sys_ptrace()
{
return -ENOSYS;
}
int sys_stty()
{
return -ENOSYS;
}
int sys_gtty()
{
return -ENOSYS;
}
int sys_rename()
{
return -ENOSYS;
}
int sys_prof()
{
return -ENOSYS;
}
int sys_setgid(int gid)
{
if (current->euid && current->uid)
if (current->gid==gid || current->sgid==gid)
current->egid = gid;
else
return -EPERM;
else
current->gid = current->egid = gid;
return 0;
}
int sys_acct()
{
return -ENOSYS;
}
int sys_phys()
{
return -ENOSYS;
}
int sys_lock()
{
return -ENOSYS;
}
int sys_mpx()
{
return -ENOSYS;
}
int sys_ulimit()
{
return -ENOSYS;
}
int sys_time(long * tloc)
{
int i;
i = CURRENT_TIME;
if (tloc) {
verify_area(tloc,4);
put_fs_long(i,(unsigned long *)tloc);
}
return i;
}
int sys_setuid(int uid)
{
if (current->euid && current->uid)
if (uid==current->uid || current->suid==current->uid)
current->euid=uid;
else
return -EPERM;
else
current->euid=current->uid=uid;
return 0;
}
int sys_stime(long * tptr)
{
if (current->euid && current->uid)
return -EPERM;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
return 0;
}
int sys_times(struct tms * tbuf)
{
if (tbuf) {
verify_area(tbuf,sizeof *tbuf);
put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
put_fs_long(current->cstime,(unsigned long *)&tbuf->tms_cstime);
}
return jiffies;
}
int sys_brk(unsigned long end_data_seg)
{
if (end_data_seg >= current->end_code &&
end_data_seg < current->start_stack - 16384)
current->brk = end_data_seg;
return current->brk;
}
/*
* 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.
*/
int sys_setpgid(int pid, int pgid)
{
int i;
if (!pid)
pid = current->pid;
if (!pgid)
pgid = current->pid;
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->pid==pid) {
if (task[i]->leader)
return -EPERM;
if (task[i]->session != current->session)
return -EPERM;
task[i]->pgrp = pgid;
return 0;
}
return -ESRCH;
}
int sys_getpgrp(void)
{
return current->pgrp;
}
int sys_setsid(void)
{
if (current->uid && current->euid)
return -EPERM;
if (current->leader)
return -EPERM;
current->leader = 1;
current->session = current->pgrp = current->pid;
current->tty = -1;
return current->pgrp;
}
int sys_uname(struct utsname * name)
{
static struct utsname thisname = {
"linux .0","nodename","release ","version ","machine "
};
int i;
if (!name) return -ERROR;
verify_area(name,sizeof *name);
for(i=0;i<sizeof *name;i++)
put_fs_byte(((char *) &thisname)[i],i+(char *) name);
return 0;
}
int sys_umask(int mask)
{
int old = current->umask;
current->umask = mask & 0777;
return (old);
}
/*
* linux/kernel/system_call.s
*
* (C) 1991 Linus Torvalds
*/
/* /*
* system_call.s contains the system-call low-level handling routines. * system_call.s contains the system-call low-level handling routines.
* This also contains the timer-interrupt handler, as some of the code is * This also contains the timer-interrupt handler, as some of the code is
* the same. The hd-interrupt is also here. * the same. The hd- and flopppy-interrupts are also here.
* *
* NOTE: This code handles signal-recognition, which happens every time * NOTE: This code handles signal-recognition, which happens every time
* after a timer-interrupt and after each system call. Ordinary interrupts * after a timer-interrupt and after each system call. Ordinary interrupts
...@@ -25,6 +31,7 @@ ...@@ -25,6 +31,7 @@
*/ */
SIG_CHLD = 17 SIG_CHLD = 17
EAX = 0x00 EAX = 0x00
EBX = 0x04 EBX = 0x04
ECX = 0x08 ECX = 0x08
...@@ -42,12 +49,23 @@ state = 0 # these are offsets into the task-struct. ...@@ -42,12 +49,23 @@ state = 0 # these are offsets into the task-struct.
counter = 4 counter = 4
priority = 8 priority = 8
signal = 12 signal = 12
restorer = 16 # address of info-restorer sigaction = 16 # MUST be 16 (=len of sigaction)
sig_fn = 20 # table of 32 signal addresses blocked = (33*16)
# offsets within sigaction
sa_handler = 0
sa_mask = 4
sa_flags = 8
sa_restorer = 12
nr_system_calls = 67 nr_system_calls = 70
.globl _system_call,_sys_fork,_timer_interrupt,_hd_interrupt,_sys_execve /*
* Ok, I get parallel printer interrupts while using the floppy for some
* strange reason. Urgel. Now I just ignore them.
*/
.globl _system_call,_sys_fork,_timer_interrupt,_sys_execve
.globl _hd_interrupt,_floppy_interrupt,_parallel_interrupt
.align 2 .align 2
bad_sys_call: bad_sys_call:
...@@ -83,46 +101,22 @@ ret_from_sys_call: ...@@ -83,46 +101,22 @@ ret_from_sys_call:
movl _current,%eax # task[0] cannot have signals movl _current,%eax # task[0] cannot have signals
cmpl _task,%eax cmpl _task,%eax
je 3f je 3f
movl CS(%esp),%ebx # was old code segment supervisor cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
testl $3,%ebx # mode? If so - don't check signals jne 3f
je 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f jne 3f
2: movl signal(%eax),%ebx # signals (bitmap, 32 signals) movl signal(%eax),%ebx
bsfl %ebx,%ecx # %ecx is signal nr, return if none movl blocked(%eax),%ecx
notl %ecx
andl %ebx,%ecx
bsfl %ecx,%ecx
je 3f je 3f
btrl %ecx,%ebx # clear it btrl %ecx,%ebx
movl %ebx,signal(%eax) movl %ebx,signal(%eax)
movl sig_fn(%eax,%ecx,4),%ebx # %ebx is signal handler address
cmpl $1,%ebx
jb default_signal # 0 is default signal handler - exit
je 2b # 1 is ignore - find next signal
movl $0,sig_fn(%eax,%ecx,4) # reset signal handler address
incl %ecx incl %ecx
xchgl %ebx,EIP(%esp) # put new return address on stack
subl $28,OLDESP(%esp)
movl OLDESP(%esp),%edx # push old return address on stack
pushl %eax # but first check that it's ok.
pushl %ecx pushl %ecx
pushl $28 call _do_signal
pushl %edx
call _verify_area
popl %edx
addl $4,%esp
popl %ecx
popl %eax popl %eax
movl restorer(%eax),%eax
movl %eax,%fs:(%edx) # flag/reg restorer
movl %ecx,%fs:4(%edx) # signal nr
movl EAX(%esp),%eax
movl %eax,%fs:8(%edx) # old eax
movl ECX(%esp),%eax
movl %eax,%fs:12(%edx) # old ecx
movl EDX(%esp),%eax
movl %eax,%fs:16(%edx) # old edx
movl EFLAGS(%esp),%eax
movl %eax,%fs:20(%edx) # old eflags
movl %ebx,%fs:24(%edx) # old return addr
3: popl %eax 3: popl %eax
popl %ebx popl %ebx
popl %ecx popl %ecx
...@@ -132,15 +126,6 @@ ret_from_sys_call: ...@@ -132,15 +126,6 @@ ret_from_sys_call:
pop %ds pop %ds
iret iret
default_signal:
incl %ecx
cmpl $SIG_CHLD,%ecx
je 2b
pushl %ecx
call _do_exit # remember to set bit 7 when dumping core
addl $4,%esp
jmp 3b
.align 2 .align 2
_timer_interrupt: _timer_interrupt:
push %ds # save ds,es and put kernel data space push %ds # save ds,es and put kernel data space
...@@ -197,7 +182,6 @@ _hd_interrupt: ...@@ -197,7 +182,6 @@ _hd_interrupt:
movl $0x10,%eax movl $0x10,%eax
mov %ax,%ds mov %ax,%ds
mov %ax,%es mov %ax,%es
movl $0x17,%eax
mov %ax,%fs mov %ax,%fs
movb $0x20,%al movb $0x20,%al
outb %al,$0x20 # EOI to interrupt controller #1 outb %al,$0x20 # EOI to interrupt controller #1
...@@ -217,3 +201,35 @@ _hd_interrupt: ...@@ -217,3 +201,35 @@ _hd_interrupt:
popl %eax popl %eax
iret iret
_floppy_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
movb $0x20,%al
outb %al,$0x20 # EOI to interrupt controller #1
movl _do_floppy,%eax
testl %eax,%eax
jne 1f
movl $_unexpected_floppy_interrupt,%eax
1: call *%eax # "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
_parallel_interrupt:
pushl %eax
movb $0x20,%al
outb %al,$0x20
popl %eax
iret
/*
* linux/kernel/traps.c
*
* (C) 1991 Linus Torvalds
*/
/* /*
* 'Traps.c' handles hardware traps and faults after we have saved some * 'Traps.c' handles hardware traps and faults after we have saved some
* state in 'asm.s'. Currently mostly a debugging-aid, will be extended * state in 'asm.s'. Currently mostly a debugging-aid, will be extended
...@@ -11,6 +17,7 @@ ...@@ -11,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/segment.h> #include <asm/segment.h>
#include <asm/io.h>
#define get_seg_byte(seg,addr) ({ \ #define get_seg_byte(seg,addr) ({ \
register char __res; \ register char __res; \
...@@ -50,6 +57,8 @@ void general_protection(void); ...@@ -50,6 +57,8 @@ void general_protection(void);
void page_fault(void); void page_fault(void);
void coprocessor_error(void); void coprocessor_error(void);
void reserved(void); void reserved(void);
void parallel_interrupt(void);
void irq13(void);
static void die(char * str,long esp_ptr,long nr) static void die(char * str,long esp_ptr,long nr)
{ {
...@@ -159,12 +168,14 @@ void do_stack_segment(long esp,long error_code) ...@@ -159,12 +168,14 @@ void do_stack_segment(long esp,long error_code)
void do_coprocessor_error(long esp, long error_code) void do_coprocessor_error(long esp, long error_code)
{ {
if (last_task_used_math != current)
return;
die("coprocessor error",esp,error_code); die("coprocessor error",esp,error_code);
} }
void do_reserved(long esp, long error_code) void do_reserved(long esp, long error_code)
{ {
die("reserved (15,17-31) error",esp,error_code); die("reserved (15,17-47) error",esp,error_code);
} }
void trap_init(void) void trap_init(void)
...@@ -188,12 +199,10 @@ void trap_init(void) ...@@ -188,12 +199,10 @@ void trap_init(void)
set_trap_gate(14,&page_fault); set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved); set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error); set_trap_gate(16,&coprocessor_error);
for (i=17;i<32;i++) for (i=17;i<48;i++)
set_trap_gate(i,&reserved); set_trap_gate(i,&reserved);
/* __asm__("movl $0x3ff000,%%eax\n\t" set_trap_gate(45,&irq13);
"movl %%eax,%%db0\n\t" outb_p(inb_p(0x21)&0xfb,0x21);
"movl $0x000d0303,%%eax\n\t" outb(inb_p(0xA1)&0xdf,0xA1);
"movl %%eax,%%db7" set_trap_gate(39,&parallel_interrupt);
:::"ax");*/
} }
/*
* linux/kernel/vsprintf.c
*
* (C) 1991 Linus Torvalds
*/
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/* /*
* Wirzenius wrote this portably, Torvalds fucked it up :-) * Wirzenius wrote this portably, Torvalds fucked it up :-)
......
...@@ -42,3 +42,30 @@ dep: ...@@ -42,3 +42,30 @@ dep:
cp tmp_make Makefile cp tmp_make Makefile
### Dependencies: ### Dependencies:
_exit.s _exit.o : _exit.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h
close.s close.o : close.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h
ctype.s ctype.o : ctype.c ../include/ctype.h
dup.s dup.o : dup.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h
errno.s errno.o : errno.c
execve.s execve.o : execve.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h
open.s open.o : open.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h ../include/stdarg.h
setsid.s setsid.o : setsid.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h
string.s string.o : string.c ../include/string.h
wait.s wait.o : wait.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h ../include/sys/wait.h
write.s write.o : write.c ../include/unistd.h ../include/sys/stat.h \
../include/sys/types.h ../include/sys/times.h ../include/sys/utsname.h \
../include/utime.h
/*
* linux/lib/_exit.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
......
/*
* linux/lib/close.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
......
/*
* linux/lib/ctype.c
*
* (C) 1991 Linus Torvalds
*/
#include <ctype.h> #include <ctype.h>
char _ctmp; char _ctmp;
......
/*
* linux/lib/dup.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
......
/*
* linux/lib/errno.c
*
* (C) 1991 Linus Torvalds
*/
int errno; int errno;
/*
* linux/lib/execve.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
......
/*
* linux/lib/open.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
#include <stdarg.h> #include <stdarg.h>
......
/*
* linux/lib/setsid.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
......
/*
* linux/lib/string.c
*
* (C) 1991 Linus Torvalds
*/
#ifndef __GNUC__ #ifndef __GNUC__
#error I want gcc! #error I want gcc!
#endif #endif
......
/*
* linux/lib/wait.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
#include <sys/wait.h> #include <sys/wait.h>
......
/*
* linux/lib/write.c
*
* (C) 1991 Linus Torvalds
*/
#define __LIBRARY__ #define __LIBRARY__
#include <unistd.h> #include <unistd.h>
......
...@@ -33,5 +33,4 @@ dep: ...@@ -33,5 +33,4 @@ dep:
### Dependencies: ### Dependencies:
memory.o : memory.c ../include/signal.h ../include/sys/types.h \ memory.o : memory.c ../include/signal.h ../include/sys/types.h \
../include/linux/config.h ../include/linux/head.h ../include/linux/kernel.h \ ../include/linux/head.h ../include/linux/kernel.h ../include/asm/system.h
../include/asm/system.h
/*
* linux/mm/memory.c
*
* (C) 1991 Linus Torvalds
*/
#include <signal.h> #include <signal.h>
#include <linux/config.h>
#include <linux/head.h> #include <linux/head.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -10,25 +15,19 @@ int do_exit(long code); ...@@ -10,25 +15,19 @@ int do_exit(long code);
#define invalidate() \ #define invalidate() \
__asm__("movl %%eax,%%cr3"::"a" (0)) __asm__("movl %%eax,%%cr3"::"a" (0))
#if (BUFFER_END < 0x100000) /* these are not to be changed without changing head.s etc */
#define LOW_MEM 0x100000 #define LOW_MEM 0x100000
#else #define PAGING_MEMORY (15*1024*1024)
#define LOW_MEM BUFFER_END #define PAGING_PAGES (PAGING_MEMORY>>12)
#endif
/* these are not to be changed - thay are calculated from the above */
#define PAGING_MEMORY (HIGH_MEMORY - LOW_MEM)
#define PAGING_PAGES (PAGING_MEMORY/4096)
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) #define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100
#if (PAGING_PAGES < 10) static long HIGH_MEMORY = 0;
#error "Won't work"
#endif
#define copy_page(from,to) \ #define copy_page(from,to) \
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si") __asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
static unsigned short mem_map [ PAGING_PAGES ] = {0,}; static unsigned char mem_map [ PAGING_PAGES ] = {0,};
/* /*
* Get physical address of first (actually last :-) free page, and mark it * Get physical address of first (actually last :-) free page, and mark it
...@@ -38,12 +37,12 @@ unsigned long get_free_page(void) ...@@ -38,12 +37,12 @@ unsigned long get_free_page(void)
{ {
register unsigned long __res asm("ax"); register unsigned long __res asm("ax");
__asm__("std ; repne ; scasw\n\t" __asm__("std ; repne ; scasb\n\t"
"jne 1f\n\t" "jne 1f\n\t"
"movw $1,2(%%edi)\n\t" "movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t" "sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t" "movl %%ecx,%%edx\n\t"
"addl %2,%%edx\n\t"
"movl $1024,%%ecx\n\t" "movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t" "leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t" "rep ; stosl\n\t"
...@@ -62,8 +61,8 @@ return __res; ...@@ -62,8 +61,8 @@ return __res;
*/ */
void free_page(unsigned long addr) void free_page(unsigned long addr)
{ {
if (addr<LOW_MEM) return; if (addr < LOW_MEM) return;
if (addr>HIGH_MEMORY) if (addr > HIGH_MEMORY)
panic("trying to free nonexistent page"); panic("trying to free nonexistent page");
addr -= LOW_MEM; addr -= LOW_MEM;
addr >>= 12; addr >>= 12;
...@@ -244,6 +243,20 @@ void do_no_page(unsigned long error_code,unsigned long address) ...@@ -244,6 +243,20 @@ void do_no_page(unsigned long error_code,unsigned long address)
do_exit(SIGSEGV); do_exit(SIGSEGV);
} }
void mem_init(long start_mem, long end_mem)
{
int i;
HIGH_MEMORY = end_mem;
for (i=0 ; i<PAGING_PAGES ; i++)
mem_map[i] = USED;
i = MAP_NR(start_mem);
end_mem -= start_mem;
end_mem >>= 12;
while (end_mem-->0)
mem_map[i++]=0;
}
void calc_mem(void) void calc_mem(void)
{ {
int i,j,k,free=0; int i,j,k,free=0;
......
/*
* linux/mm/page.s
*
* (C) 1991 Linus Torvalds
*/
/* /*
* page.s contains the low-level page-exception code. * page.s contains the low-level page-exception code.
* the real work is done in mm.c * the real work is done in mm.c
......
head 1.2;
branch ;
access ;
symbols ;
locks ; strict;
comment @ * @;
1.2
date 91.11.20.00.07.29; author tytso; state Exp;
branches ;
next 1.1;
1.1
date 91.11.11.14.45.51; author tytso; state Exp;
branches ;
next ;
desc
@@
1.2
log
@Add code to support changing the root device at boot time.
@
text
@/*
* linux/tools/build.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file builds a disk-image from three different files:
*
* - bootsect: max 510 bytes of 8086 machine code, loads the rest
* - setup: max 4 sectors of 8086 machine code, sets up system parm
* - system: 80386 code for actual system
*
* It does some checking that all files are of the correct type, and
* just writes the result to stdout, removing headers and padding to
* the right amount. It also writes some system data to stderr.
*/
#include <stdio.h> /* fprintf */
#include <string.h>
#include <stdlib.h> /* contains exit */
#include <sys/types.h> /* unistd.h needs this */
#include <sys/stat.h>
#include <linux/fs.h>
#include <unistd.h> /* contains read/write */
#include <fcntl.h>
#define MINIX_HEADER 32
#define GCC_HEADER 1024
#define DEFAULT_MAJOR_ROOT 3
#define DEFAULT_MINOR_ROOT 6
/* max nr of sectors of setup: don't change unless you also change
* bootsect etc */
#define SETUP_SECTS 4
#define STRINGIFY(x) #x
void die(char * str)
{
fprintf(stderr,"%s\n",str);
exit(1);
}
void usage(void)
{
die("Usage: build bootsect setup system [> image]");
}
int main(int argc, char ** argv)
{
int i,c,id;
char buf[1024];
char major_root, minor_root;
struct stat sb;
if ((argc != 4) && (argc != 5))
usage();
if (argc == 5) {
if (strcmp(argv[4], "FLOPPY")) {
if (stat(argv[4], &sb)) {
perror(argv[4]);
die("Couldn't stat root device.");
}
major_root = MAJOR(sb.st_rdev);
minor_root = MINOR(sb.st_rdev);
} else {
major_root = 0;
minor_root = 0;
}
} else {
major_root = DEFAULT_MAJOR_ROOT;
minor_root = DEFAULT_MINOR_ROOT;
}
fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
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 #");
}
for (i=0;i<sizeof buf; i++) buf[i]=0;
if ((id=open(argv[1],O_RDONLY,0))<0)
die("Unable to open 'boot'");
if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
die("Unable to read header of 'boot'");
if (((long *) buf)[0]!=0x04100301)
die("Non-Minix header of 'boot'");
if (((long *) buf)[1]!=MINIX_HEADER)
die("Non-Minix header of 'boot'");
if (((long *) buf)[3]!=0)
die("Illegal data segment in 'boot'");
if (((long *) buf)[4]!=0)
die("Illegal bss in 'boot'");
if (((long *) buf)[5] != 0)
die("Non-Minix header of 'boot'");
if (((long *) buf)[7] != 0)
die("Illegal symbol table in 'boot'");
i=read(id,buf,sizeof buf);
fprintf(stderr,"Boot sector %d bytes.\n",i);
if (i != 512)
die("Boot block must be exactly 512 bytes");
if ((*(unsigned short *)(buf+510)) != 0xAA55)
die("Boot block hasn't got boot flag (0xAA55)");
buf[508] = (char) minor_root;
buf[509] = (char) major_root;
i=write(1,buf,512);
if (i!=512)
die("Write call failed");
close (id);
if ((id=open(argv[2],O_RDONLY,0))<0)
die("Unable to open 'setup'");
if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
die("Unable to read header of 'setup'");
if (((long *) buf)[0]!=0x04100301)
die("Non-Minix header of 'setup'");
if (((long *) buf)[1]!=MINIX_HEADER)
die("Non-Minix header of 'setup'");
if (((long *) buf)[3]!=0)
die("Illegal data segment in 'setup'");
if (((long *) buf)[4]!=0)
die("Illegal bss in 'setup'");
if (((long *) buf)[5] != 0)
die("Non-Minix header of 'setup'");
if (((long *) buf)[7] != 0)
die("Illegal symbol table in 'setup'");
for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
if (write(1,buf,c)!=c)
die("Write call failed");
close (id);
if (i > SETUP_SECTS*512)
die("Setup exceeds " STRINGIFY(SETUP_SECTS)
" sectors - rewrite build/boot/setup");
fprintf(stderr,"Setup is %d bytes.\n",i);
for (c=0 ; c<sizeof(buf) ; c++)
buf[c] = '\0';
while (i<SETUP_SECTS*512) {
c = SETUP_SECTS*512-i;
if (c > sizeof(buf))
c = sizeof(buf);
if (write(1,buf,c) != c)
die("Write call failed");
i += c;
}
if ((id=open(argv[3],O_RDONLY,0))<0)
die("Unable to open 'system'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system'");
if (((long *) buf)[5] != 0)
die("Non-GCC header of 'system'");
for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
if (write(1,buf,c)!=c)
die("Write call failed");
close(id);
fprintf(stderr,"System is %d bytes.\n",i);
return(0);
}
@
1.1
log
@Initial revision
@
text
@d20 1
d23 2
d31 3
d55 2
d58 1
a58 1
if (argc != 4)
d60 23
d106 2
@
/*
* linux/tools/build.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file builds a disk-image from three different files:
*
* - bootsect: max 510 bytes of 8086 machine code, loads the rest
* - setup: max 4 sectors of 8086 machine code, sets up system parm
* - system: 80386 code for actual system
*
* It does some checking that all files are of the correct type, and
* just writes the result to stdout, removing headers and padding to
* the right amount. It also writes some system data to stderr.
*/
#include <stdio.h> /* fprintf */ #include <stdio.h> /* fprintf */
#include <string.h>
#include <stdlib.h> /* contains exit */ #include <stdlib.h> /* contains exit */
#include <sys/types.h> /* unistd.h needs this */ #include <sys/types.h> /* unistd.h needs this */
#include <sys/stat.h>
#include <linux/fs.h>
#include <unistd.h> /* contains read/write */ #include <unistd.h> /* contains read/write */
#include <fcntl.h> #include <fcntl.h>
#define MINIX_HEADER 32 #define MINIX_HEADER 32
#define GCC_HEADER 1024 #define GCC_HEADER 1024
#define DEFAULT_MAJOR_ROOT 3
#define DEFAULT_MINOR_ROOT 6
/* max nr of sectors of setup: don't change unless you also change
* bootsect etc */
#define SETUP_SECTS 4
#define STRINGIFY(x) #x
void die(char * str) void die(char * str)
{ {
fprintf(stderr,"%s\n",str); fprintf(stderr,"%s\n",str);
...@@ -15,16 +45,41 @@ void die(char * str) ...@@ -15,16 +45,41 @@ void die(char * str)
void usage(void) void usage(void)
{ {
die("Usage: build boot system [> image]"); die("Usage: build bootsect setup system [> image]");
} }
int main(int argc, char ** argv) int main(int argc, char ** argv)
{ {
int i,c,id; int i,c,id;
char buf[1024]; char buf[1024];
char major_root, minor_root;
struct stat sb;
if (argc != 3) if ((argc != 4) && (argc != 5))
usage(); usage();
if (argc == 5) {
if (strcmp(argv[4], "FLOPPY")) {
if (stat(argv[4], &sb)) {
perror(argv[4]);
die("Couldn't stat root device.");
}
major_root = MAJOR(sb.st_rdev);
minor_root = MINOR(sb.st_rdev);
} else {
major_root = 0;
minor_root = 0;
}
} else {
major_root = DEFAULT_MAJOR_ROOT;
minor_root = DEFAULT_MINOR_ROOT;
}
fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
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 #");
}
for (i=0;i<sizeof buf; i++) buf[i]=0; for (i=0;i<sizeof buf; i++) buf[i]=0;
if ((id=open(argv[1],O_RDONLY,0))<0) if ((id=open(argv[1],O_RDONLY,0))<0)
die("Unable to open 'boot'"); die("Unable to open 'boot'");
...@@ -44,16 +99,53 @@ int main(int argc, char ** argv) ...@@ -44,16 +99,53 @@ int main(int argc, char ** argv)
die("Illegal symbol table in 'boot'"); die("Illegal symbol table in 'boot'");
i=read(id,buf,sizeof buf); i=read(id,buf,sizeof buf);
fprintf(stderr,"Boot sector %d bytes.\n",i); fprintf(stderr,"Boot sector %d bytes.\n",i);
if (i>510) if (i != 512)
die("Boot block may not exceed 510 bytes"); die("Boot block must be exactly 512 bytes");
buf[510]=0x55; if ((*(unsigned short *)(buf+510)) != 0xAA55)
buf[511]=0xAA; die("Boot block hasn't got boot flag (0xAA55)");
buf[508] = (char) minor_root;
buf[509] = (char) major_root;
i=write(1,buf,512); i=write(1,buf,512);
if (i!=512) if (i!=512)
die("Write call failed"); die("Write call failed");
close (id); close (id);
if ((id=open(argv[2],O_RDONLY,0))<0) if ((id=open(argv[2],O_RDONLY,0))<0)
die("Unable to open 'setup'");
if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
die("Unable to read header of 'setup'");
if (((long *) buf)[0]!=0x04100301)
die("Non-Minix header of 'setup'");
if (((long *) buf)[1]!=MINIX_HEADER)
die("Non-Minix header of 'setup'");
if (((long *) buf)[3]!=0)
die("Illegal data segment in 'setup'");
if (((long *) buf)[4]!=0)
die("Illegal bss in 'setup'");
if (((long *) buf)[5] != 0)
die("Non-Minix header of 'setup'");
if (((long *) buf)[7] != 0)
die("Illegal symbol table in 'setup'");
for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
if (write(1,buf,c)!=c)
die("Write call failed");
close (id);
if (i > SETUP_SECTS*512)
die("Setup exceeds " STRINGIFY(SETUP_SECTS)
" sectors - rewrite build/boot/setup");
fprintf(stderr,"Setup is %d bytes.\n",i);
for (c=0 ; c<sizeof(buf) ; c++)
buf[c] = '\0';
while (i<SETUP_SECTS*512) {
c = SETUP_SECTS*512-i;
if (c > sizeof(buf))
c = sizeof(buf);
if (write(1,buf,c) != c)
die("Write call failed");
i += c;
}
if ((id=open(argv[3],O_RDONLY,0))<0)
die("Unable to open 'system'"); die("Unable to open 'system'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER) if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system'"); die("Unable to read header of 'system'");
...@@ -63,6 +155,6 @@ int main(int argc, char ** argv) ...@@ -63,6 +155,6 @@ int main(int argc, char ** argv)
if (write(1,buf,c)!=c) if (write(1,buf,c)!=c)
die("Write call failed"); die("Write call failed");
close(id); close(id);
fprintf(stderr,"System %d bytes.\n",i); fprintf(stderr,"System is %d bytes.\n",i);
return(0); return(0);
} }
/*
* linux/tools/build.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file builds a disk-image from three different files:
*
* - bootsect: max 510 bytes of 8086 machine code, loads the rest
* - setup: max 4 sectors of 8086 machine code, sets up system parm
* - system: 80386 code for actual system
*
* It does some checking that all files are of the correct type, and
* just writes the result to stdout, removing headers and padding to
* the right amount. It also writes some system data to stderr.
*/
#include <stdio.h> /* fprintf */
#include <stdlib.h> /* contains exit */
#include <sys/types.h> /* unistd.h needs this */
#include <unistd.h> /* contains read/write */
#include <fcntl.h>
#define MINIX_HEADER 32
#define GCC_HEADER 1024
/* max nr of sectors of setup: don't change unless you also change
* bootsect etc */
#define SETUP_SECTS 4
#define STRINGIFY(x) #x
void die(char * str)
{
fprintf(stderr,"%s\n",str);
exit(1);
}
void usage(void)
{
die("Usage: build bootsect setup system [> image]");
}
int main(int argc, char ** argv)
{
int i,c,id;
char buf[1024];
if (argc != 4)
usage();
for (i=0;i<sizeof buf; i++) buf[i]=0;
if ((id=open(argv[1],O_RDONLY,0))<0)
die("Unable to open 'boot'");
if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
die("Unable to read header of 'boot'");
if (((long *) buf)[0]!=0x04100301)
die("Non-Minix header of 'boot'");
if (((long *) buf)[1]!=MINIX_HEADER)
die("Non-Minix header of 'boot'");
if (((long *) buf)[3]!=0)
die("Illegal data segment in 'boot'");
if (((long *) buf)[4]!=0)
die("Illegal bss in 'boot'");
if (((long *) buf)[5] != 0)
die("Non-Minix header of 'boot'");
if (((long *) buf)[7] != 0)
die("Illegal symbol table in 'boot'");
i=read(id,buf,sizeof buf);
fprintf(stderr,"Boot sector %d bytes.\n",i);
if (i != 512)
die("Boot block must be exactly 512 bytes");
if ((*(unsigned short *)(buf+510)) != 0xAA55)
die("Boot block hasn't got boot flag (0xAA55)");
i=write(1,buf,512);
if (i!=512)
die("Write call failed");
close (id);
if ((id=open(argv[2],O_RDONLY,0))<0)
die("Unable to open 'setup'");
if (read(id,buf,MINIX_HEADER) != MINIX_HEADER)
die("Unable to read header of 'setup'");
if (((long *) buf)[0]!=0x04100301)
die("Non-Minix header of 'setup'");
if (((long *) buf)[1]!=MINIX_HEADER)
die("Non-Minix header of 'setup'");
if (((long *) buf)[3]!=0)
die("Illegal data segment in 'setup'");
if (((long *) buf)[4]!=0)
die("Illegal bss in 'setup'");
if (((long *) buf)[5] != 0)
die("Non-Minix header of 'setup'");
if (((long *) buf)[7] != 0)
die("Illegal symbol table in 'setup'");
for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
if (write(1,buf,c)!=c)
die("Write call failed");
close (id);
if (i > SETUP_SECTS*512)
die("Setup exceeds " STRINGIFY(SETUP_SECTS)
" sectors - rewrite build/boot/setup");
fprintf(stderr,"Setup is %d bytes.\n",i);
for (c=0 ; c<sizeof(buf) ; c++)
buf[c] = '\0';
while (i<SETUP_SECTS*512) {
c = SETUP_SECTS*512-i;
if (c > sizeof(buf))
c = sizeof(buf);
if (write(1,buf,c) != c)
die("Write call failed");
i += c;
}
if ((id=open(argv[3],O_RDONLY,0))<0)
die("Unable to open 'system'");
if (read(id,buf,GCC_HEADER) != GCC_HEADER)
die("Unable to read header of 'system'");
if (((long *) buf)[5] != 0)
die("Non-GCC header of 'system'");
for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )
if (write(1,buf,c)!=c)
die("Write call failed");
close(id);
fprintf(stderr,"System is %d bytes.\n",i);
return(0);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment