Commit 2ab763b2 authored by Linus Torvalds's avatar Linus Torvalds

Linux-0.12 (January 15, 1992)

This was created from a re-packaged 0.12 tree

Major milestone! Over the christmas break, I implemented paging to disk,
meaning that you could actually use gcc on a 2MB system.  Some poor sod
(Robert Blum) wanted to use Linux on such a system, and couldn't get the
kernel to compile with anything less "bloated" than gcc.

[ Irony alert: this was back when gcc worked fine on a system with just
  4MB.  Gone are those days. _Loong_ gone. ]

The task size was still limited to 63 tasks of at most 64MB each, but
other than that we were actually getting usable.

Together with other improvements and fixes, 0.12 was actually a very
nice kernel.  It was by now clearly more usable than Minix, which caused
us to think that a 1.0 release was imminent.  The next kernel version
was to be named 0.95, which turned out to be less than a stellar idea.

This was also the point where we changed the copyright license.  See the
attached original release notes.

Other changes:

 - Ted Ts'o continued on his rampage, and implemented BSD process
   control (ie ^Z) etc.  This also introduced the process tree code,
   with pointers between parents and children, rather than iterating
   over the whole list of processes.

 - Ted also did SVR4-style "saved uid/gid" handling.

 - use the C preprocessor for assembly files, cleaning up a lot of
   duplicate definitions etc.

 - better boot loader diagnostics

 - boot sequence now can change the size of the text display.  Who the
   hell is d88-man?

 - fix nasty race condition between "truncate" and file IO.

 - add support for shared libraries with the "uselib()" system call.
   This (together with the fact that we could share clean executable
   pages) cut down on memory usage a lot.

 - supplemental group support.  Hey, what can I say? Unix users expected
   them.

 - symbolic link handling.  This was the first real extension to the
   standard Minix disk layout, and was made possible by the fact that I
   had written my own "mkfs" and "fsck".  Before that, we were still on
   crutches, in that a Linux system depended on a Minix installation for
   these fundamental system tools.

 - mkdir()/rmdir() isn't just for root, you know..  (Yes, seriously.
   Old-style UNIX used to limit them to root-only, since they were just
   special sequences of mknod's)

 - Virtual terminals by Peter MacDonald (who was to do the SLS
   distribution).

   Before having X, this was a _big_ deal.  The fact that Linux had
   virtual terminals with a good vt100 emulation actually made Linux
   stand out even among some of the big commercial unixes.  The Linux
   console was just _so_ much more pleasant to use that it isn't even
   funny.

 - first implementation of "select()", virtual terminals, and pty's.

   These too were originally done by Peter MacDonald, based on some
   patches that had been floating around for Minix for a long time (but
   were never accepted into Minix).

   They didn't get accepted into Linux either, but the patches _did_ end
   up inspiring me to re-do the select/pty parts in a way that was more
   palatable to me.

 - restartable system calls

   This was needed for Ted's code to do ^Z

 - Math emulation! The code was a total crock, and didn't bother with
   such unnecessary niceties as getting rounding right (or, to be
   honest, even getting more than about 60 bits right), but let's face
   it: it was enough to get work done.

   My math emulation was eventually to be entirely replaced by a much
   more complete, and much more precise implementation by Bill
   Metzenthen.  But my original stupid implementation actually ended
   living on at least for a while in BSD - I ended up making it
   available to the BSD people who couldn't use Bill's much better
   implementation due to licensing reasons.  I don't know whatever
   eventually happened to it.

 - support alignment check on i486+. Nobody seems to have ever used it,
   though.

Original release notes:

         RELEASE NOTES FOR LINUX v0.12

This is file mostly contains info on changed features of Linux, and
using old versions as a help-reference might be a good idea.

         COPYRIGHT

The Linux copyright will change: I've had a couple of requests to make
it compatible with the GNU copyleft, removing the "you may not
distribute it for money" condition.  I agree.  I propose that the
copyright be changed so that it confirms to GNU - pending approval of
the persons who have helped write code.  I assume this is going to be no
problem for anybody: If you have grievances ("I wrote that code assuming
the copyright would stay the same") mail me.  Otherwise The GNU copyleft
takes effect as of the first of February.  If you do not know the gist
of the GNU copyright - read it.

         INSTALLATION

This is a SHORT install-note. The installation is very similar to 0.11,
so read that (INSTALL-0.11) too. There are a couple of programs you will
need to install linux: something that writes disk images (rawrite.exe or
NU or...) and something that can create harddisk partitions (fdisk under
xenix or older versions of dos, edpart.exe or something like that).

NOTE! Repartitioning your harddisk will destroy all data on it (well,
not exactly, but if you know enough to get back the data you probably
didn't need this warning).  So be careful.

READ THIS THROUGH, THEN READ INSTALL-0.11, AND IF YOU ARE SURE YOU KNOW
WHAT YOU ARE DOING, CONTINUE.  OTHERWISE, PANIC.  OR WRITE ME FOR
EXPLANATIONS.  OR DO ANYTHING BUT INSTALL LINUX - IT'S VERY SIMPLE, BUT
IF YOU DON'T KNOW WHAT YOU ARE DOING YOU'LL PROBABLY BE SORRY.  I'D
RATHER ANSWER A FEW UNNECESSARY MAILS THAN GET MAIL SAYING "YOU KILLED
MY HARDDISK, BASTARD.  I'M GOING TO FIND YOU, AND YOU'LL BE SORRY WHEN I
DO".

1) back up everything you have on your harddisk - linux-0.12 is still in
   beta and might do weird things.  The only thing I guarantee is that
   it has worked fine on /my/ machine - for all I know it might eat your
   harddisk and spit it out in small pieces on any other hardware.

2) Test out the linux boot-disk with the root file system.  If it
   doesn't work, check the hardware requirements, and mail me if you
   still think it should work.  I might not be able to help you, but
   your bug-report would still be appreciated.

   Test that linux can read your harddisk at least partly: run the fdisk
   program on the root-disk, and see if it barfs.  If it tells you about
   any partitions at all, linux can successfully read at least part of
   your harddisk.

3) Make sure that you have a free /primary/ partition.  There can be 4
   primary partitions per drive: newer DOS fdisks seem to be able to
   create only 2 (one primary and one extended).  In that case use some
   other partitioning software: edpart.exe etc.  Linux fdisk currently
   only tells you the partition info - it doesn't write to the disk.

   Remember to check how big your partition was, as that can be used to
   tell which device Linux thinks it is.

4) Boot up linux again, fdisk to make sure you now have the new
   partition, and use mkfs to make a filesystem on one of the partitions
   fdisk reports.  Write "mkfs -c /dev/hdX nnn" where X is the device
   number reported by linux fdisk, and nnn is the size - also reported
   by fdisk.  nnn is the size in /blocks/, ie kilobytes.  You should be
   able to use the size info to determine which partition is represented
   by which device name.

5) Mount the new disk partition: "mount /dev/hdX /user".  Copy over the
   root filesystem to the harddisk, eg like this:

        # for i in bin dev etc usr tmp
        # do
        # cp +recursive /$i /user
        # done

   You caanot use just "cp +recursive / /user", as that will result in a
   loop.

6) Sync the filesystem after you have played around enough, and reboot.

        # sync
        <wait for it to sync>
        ctrl-alt-del

   The folklore says you should do this three times before rebooting:
   once should be enough, but I admit I do it three times anyway :) THIS
   IS IMPORTANT! NEVER EVER FORGET TO SYNC BEFORE KILLING THE MACHINE.

7) Change the bootdisk to understand which partition it should use as a
   root filesystem.  See INSTALL-0.11: it's still the word at offset
   508 into the image. You should be up and running.

That's it. Go back and read the INSTALL-0.11

         New features of 0.12, in order of appearance
         (ie in the order you see them)

        Linux now prints cute dots when loading

WoW. Run, don't walk, to see this :). Seriously, it should hopefully now
load even on machines that never got off the ground before, but
otherwise the loading hasn't changed. Implemented by drew.

        Super-VGA detection for extended alphamun modes

I cannot guarantee it, I didn't write it, but it works great on a ET400
SVGA card.  I'm addicted to the new look with 100x40 character editing,
instead of a cramped 80x25.  This only works on VGA-cards that support
higher text-resolutions, and which are correctly identified. Implemented
by d88-man.

        Job Control.

Ok, everybody used to typing ^Z after they started a long command, and
forgot to put it in the background - now it works on linux too.  Bash
knows the usualy job-control commands: bg, fg, jobs & kill.  I hope
there will be no nasty surprises.  Job control was implemented by
tytso@athena.mit.edu.

        Virtual consoles on EGA/VGA screens.

You can select one of several consoles by pressing the left alt-key and
a function key at the same time. Linux should report the number of
virtual consoles available upon bootup. /dev/tty0 is now "the current"
screen, /dev/tty1 is the main console, and /dev/tty2-8 can exist
depending on your text-mode or card.

NOTE! Scrolling is noticeably much slower with virtual consoles on a
EGA/VGA. The reason is that no longer does linux use all the screen
memory as a long buffer, but crams in several consoles in it. I think
it's worth it.

The virtual consoles also have some new screen-handling commands: they
confirm even better to vt200 control codes than 0.11. Special graphic
characters etc: you can well use them as terminals to VMS (although
that's a shameful waste of resources).

        pty's

Ok. I have to admit that I didn't get the hangup-code working correctly,
but that should be easy to add. The general things are there.

        select

I've never used it, so I cannot say how well it works. My minor testing
seems to indicate that it works ok. vc's, pty's and select were
implemented by pmacdona, although I hacked it heavily.

        387-emulation.

It's not complete, but it works well enough to run those gcc2.0 compiled
programs I tested (few).  None of the "heavy" math-functions are
implemented yet.

        Symbolic links.

Try out a few "ln -s xx yy", and ls -l. Note that I think tar should be
recompiled to know anout them, and probably some other programs too. The
0.12 rootimage-disk has most of the recompiled fileutilities.

        Virtual memory.

In addition to the "mkfs" program, there is now a "mkswap" program on
the root disk.  The syntax is identical: "mkswap -c /dev/hdX nnn", and
again: this writes over the partition, so be careful.  Swapping can then
be enabled by changing the word at offset 506 in the bootimage to the
desired device.  Use the same program as for setting the root file
system (but change the 508 offset to 506 of course).

NOTE! This has been tested by Robert Blum, who has a 2M machine, and it
allows you to run gcc without much memory.  HOWEVER, I had to stop using
it, as my diskspace was eaten up by the beta-gcc-2.0, so I'd like to
hear that it still works: I've been totally unable to make a
swap-partition for even rudimentary testing since about christmastime.
Thus the new changes could possibly just have backfired on the VM, but I
doubt it.

        And that's it, I think.

Happy hacking.

         Linus
parent b65b60f4
......@@ -21,6 +21,7 @@ CPP =cpp -nostdinc -Iinclude
# default of /dev/hd6 is used by 'build'.
#
ROOT_DEV=/dev/hd6
SWAP_DEV=/dev/hd2
ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
......@@ -39,7 +40,8 @@ LIBS =lib/lib.a
all: Image
Image: boot/bootsect boot/setup tools/system tools/build
tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) \
$(SWAP_DEV) > Image
sync
disk: Image
......@@ -85,17 +87,19 @@ boot/setup: boot/setup.s
$(AS86) -o boot/setup.o boot/setup.s
$(LD86) -s -o boot/setup boot/setup.o
boot/setup.s: boot/setup.S include/linux/config.h
$(CPP) -traditional boot/setup.S -o boot/setup.s
boot/bootsect.s: boot/bootsect.S include/linux/config.h
$(CPP) -traditional boot/bootsect.S -o boot/bootsect.s
boot/bootsect: boot/bootsect.s
$(AS86) -o boot/bootsect.o boot/bootsect.s
$(LD86) -s -o boot/bootsect boot/bootsect.o
tmp.s: boot/bootsect.s tools/system
(echo -n "SYSSIZE = (";ls -l tools/system | grep system \
| cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s
cat boot/bootsect.s >> tmp.s
clean:
rm -f Image System.map tmp_make core boot/bootsect boot/setup
rm -f Image System.map tmp_make core boot/bootsect boot/setup \
boot/bootsect.s boot/setup.s
rm -f init/*.o tools/system tools/build boot/*.o
(cd mm;make clean)
(cd fs;make clean)
......@@ -116,8 +120,10 @@ dep:
### Dependencies:
init/main.o : init/main.c include/unistd.h include/sys/stat.h \
include/sys/types.h include/sys/times.h include/sys/utsname.h \
include/utime.h include/time.h include/linux/tty.h include/termios.h \
include/linux/sched.h include/linux/head.h include/linux/fs.h \
include/linux/mm.h include/signal.h include/asm/system.h include/asm/io.h \
include/stddef.h include/stdarg.h include/fcntl.h
include/sys/types.h include/sys/time.h include/time.h include/sys/times.h \
include/sys/utsname.h include/sys/param.h include/sys/resource.h \
include/utime.h include/linux/tty.h include/termios.h include/linux/sched.h \
include/linux/head.h include/linux/fs.h include/linux/mm.h \
include/linux/kernel.h include/signal.h include/asm/system.h \
include/asm/io.h include/stddef.h include/stdarg.h include/fcntl.h \
include/string.h
......@@ -3,9 +3,11 @@
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
!
SYSSIZE = 0x3000
#include <linux/config.h>
SYSSIZE = DEF_SYSSIZE
!
! bootsect.s (C) 1991 Linus Torvalds
! modified by Drew Eckhardt
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
......@@ -33,14 +35,14 @@ begbss:
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = 0x9000 ! we move boot here - out of the way
SETUPSEG = 0x9020 ! setup starts here
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SETUPSEG = DEF_SETUPSEG ! setup starts here
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc
ROOT_DEV = 0x306
! ROOT_DEV & SWAP_DEV are now written by "build".
ROOT_DEV = 0
SWAP_DEV = 0
entry start
start:
......@@ -54,25 +56,83 @@ start:
rep
movw
jmpi go,INITSEG
go: mov ax,cs
go: mov ax,cs
mov dx,#0xfef4 ! arbitrary value >>512 - disk parm size
mov ds,ax
mov es,ax
! put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
push ax
mov ss,ax ! put stack at 0x9ff00 - 12.
mov sp,dx
/*
* Many BIOS's default disk parameter tables will not
* recognize multi-sector reads beyond the maximum sector number
* specified in the default diskette parameter tables - this may
* mean 7 sectors in some cases.
*
* Since single sector reads are slow and out of the question,
* we must take care of this by creating new parameter tables
* (for the first disk) in RAM. We will set the maximum sector
* count to 18 - the most we will encounter on an HD 1.44.
*
* High doesn't hurt. Low does.
*
* Segments are as follows: ds=es=ss=cs - INITSEG,
* fs = 0, gs = parameter table segment
*/
push #0
pop fs
mov bx,#0x78 ! fs:bx is parameter table address
seg fs
lgs si,(bx) ! gs:si is source
mov di,dx ! es:di is destination
mov cx,#6 ! copy 12 bytes
cld
rep
seg gs
movw
mov di,dx
movb 4(di),*18 ! patch sector count
seg fs
mov (bx),di
seg fs
mov 2(bx),es
pop ax
mov fs,ax
mov gs,ax
xor ah,ah ! reset FDC
xor dl,dl
int 0x13
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
load_setup:
mov dx,#0x0000 ! drive 0, head 0
xor dx, dx ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
mov dx,#0x0000
mov ax,#0x0000 ! reset the diskette
push ax ! dump error code
call print_nl
mov bp, sp
call print_hex
pop ax
xor dl, dl ! reset FDC
xor ah, ah
int 0x13
j load_setup
......@@ -80,10 +140,10 @@ ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00
mov ax,#0x0800 ! AH=8 is get drive parameters
xor dl,dl
mov ah,#0x08 ! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
xor ch,ch
seg cs
mov sectors,cx
mov ax,#INITSEG
......@@ -95,7 +155,7 @@ ok_load_setup:
xor bh,bh
int 0x10
mov cx,#24
mov cx,#9
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
......@@ -108,6 +168,7 @@ ok_load_setup:
mov es,ax ! segment of 0x010000
call read_it
call kill_motor
call print_nl
! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
......@@ -116,7 +177,7 @@ ok_load_setup:
seg cs
mov ax,root_dev
cmp ax,#0
or ax,ax
jne root_defined
seg cs
mov bx,sectors
......@@ -190,41 +251,122 @@ ok3_read:
add bx,cx
jnc rp_read
mov ax,es
add ax,#0x1000
add ah,#0x10
mov es,ax
xor bx,bx
jmp rp_read
read_track:
push ax
push bx
push cx
push dx
pusha
pusha
mov ax, #0xe2e ! loading... message 2e = .
mov bx, #7
int 0x10
popa
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
mov dl,#0
and dx,#0x0100
mov ah,#2
push dx ! save for error dump
push cx
push bx
push ax
int 0x13
jc bad_rt
pop dx
pop cx
pop bx
pop ax
add sp, #8
popa
ret
bad_rt: mov ax,#0
mov dx,#0
bad_rt: push ax ! save error code
call print_all ! ah = error, al = read
xor ah,ah
xor dl,dl
int 0x13
pop dx
pop cx
pop bx
pop ax
add sp, #10
popa
jmp read_track
/*
* print_all is for debugging purposes.
* It will print out all of the registers. The assumption is that this is
* called from a routine, with a stack frame like
* dx
* cx
* bx
* ax
* error
* ret <- sp
*
*/
print_all:
mov cx, #5 ! error code + 4 registers
mov bp, sp
print_loop:
push cx ! save count left
call print_nl ! nl for readability
jae no_reg ! see if register name is needed
mov ax, #0xe05 + 0x41 - 1
sub al, cl
int 0x10
mov al, #0x58 ! X
int 0x10
mov al, #0x3a ! :
int 0x10
no_reg:
add bp, #2 ! next register
call print_hex ! print it
pop cx
loop print_loop
ret
print_nl:
mov ax, #0xe0d ! CR
int 0x10
mov al, #0xa ! LF
int 0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecmial.
*/
print_hex:
mov cx, #4 ! 4 hex digits
mov dx, (bp) ! load word into dx
print_digit:
rol dx, #4 ! rotate so that lowest 4 bits are used
mov ah, #0xe
mov al, dl ! mask off so we have only next nibble
and al, #0xf
add al, #0x30 ! convert to 0 based digit, '0'
cmp al, #0x39 ! check for overflow
jbe good_digit
add al, #0x41 - 0x30 - 0xa ! 'A' - '0' - 0xa
good_digit:
int 0x10
loop print_digit
ret
/*
* This procedure turns off the floppy drive motor, so
* that we enter the kernel in a known state, and
......@@ -233,7 +375,7 @@ bad_rt: mov ax,#0
kill_motor:
push dx
mov dx,#0x3f2
mov al,#0
xor al, al
outb
pop dx
ret
......@@ -243,10 +385,11 @@ sectors:
msg1:
.byte 13,10
.ascii "Loading system ..."
.byte 13,10,13,10
.ascii "Loading"
.org 508
.org 506
swap_dev:
.word SWAP_DEV
root_dev:
.word ROOT_DEV
boot_flag:
......@@ -258,3 +401,4 @@ endtext:
enddata:
.bss
endbss:
......@@ -13,10 +13,11 @@
!
! NOTE! These had better be the same as in bootsect.s!
#include <linux/config.h>
INITSEG = 0x9000 ! we move boot here - out of the way
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020 ! this is the current segment
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG ! this is the current segment
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
......@@ -35,10 +36,6 @@ start:
mov ax,#INITSEG ! this is done in bootsect already, but...
mov ds,ax
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10 ! save it in known place, con_init fetches
mov [0],dx ! it from 0x90000.
! Get memory size (extended mem, kB)
......@@ -46,13 +43,6 @@ start:
int 0x15
mov [2],ax
! Get video-card data:
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width
! check for EGA/VGA and some config parameters
mov ah,#0x12
......@@ -61,6 +51,22 @@ start:
mov [8],ax
mov [10],bx
mov [12],cx
mov ax,#0x5019
cmp bl,#0x10
je novga
call chsvga
novga: mov [14],ax
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10 ! save it in known place, con_init fetches
mov [0],dx ! it from 0x90000.
! Get video-card data:
mov ah,#0x0f
int 0x10
mov [4],bx ! bh = display page
mov [6],ax ! al = video mode, ah = window width
! Get hd0 data
......@@ -202,6 +208,362 @@ empty_8042:
jnz empty_8042 ! yes - loop
ret
! Routine trying to recognize type of SVGA-board present (if any)
! and if it recognize one gives the choices of resolution it offers.
! If one is found the resolution chosen is given by al,ah (rows,cols).
chsvga: cld
push ds
push cs
pop ds
mov ax,#0xc000
mov es,ax
lea si,msg1
call prtstr
nokey: in al,#0x60
cmp al,#0x82
jb nokey
cmp al,#0xe0
ja nokey
cmp al,#0x9c
je svga
mov ax,#0x5019
pop ds
ret
svga: lea si,idati ! Check ATI 'clues'
mov di,#0x31
mov cx,#0x09
repe
cmpsb
jne noati
lea si,dscati
lea di,moati
lea cx,selmod
jmp cx
noati: mov ax,#0x200f ! Check Ahead 'clues'
mov dx,#0x3ce
out dx,ax
inc dx
in al,dx
cmp al,#0x20
je isahed
cmp al,#0x21
jne noahed
isahed: lea si,dscahead
lea di,moahead
lea cx,selmod
jmp cx
noahed: mov dx,#0x3c3 ! Check Chips & Tech. 'clues'
in al,dx
or al,#0x10
out dx,al
mov dx,#0x104
in al,dx
mov bl,al
mov dx,#0x3c3
in al,dx
and al,#0xef
out dx,al
cmp bl,[idcandt]
jne nocant
lea si,dsccandt
lea di,mocandt
lea cx,selmod
jmp cx
nocant: mov dx,#0x3d4 ! Check Cirrus 'clues'
mov al,#0x0c
out dx,al
inc dx
in al,dx
mov bl,al
xor al,al
out dx,al
dec dx
mov al,#0x1f
out dx,al
inc dx
in al,dx
mov bh,al
xor ah,ah
shl al,#4
mov cx,ax
mov al,bh
shr al,#4
add cx,ax
shl cx,#8
add cx,#6
mov ax,cx
mov dx,#0x3c4
out dx,ax
inc dx
in al,dx
and al,al
jnz nocirr
mov al,bh
out dx,al
in al,dx
cmp al,#0x01
jne nocirr
call rst3d4
lea si,dsccirrus
lea di,mocirrus
lea cx,selmod
jmp cx
rst3d4: mov dx,#0x3d4
mov al,bl
xor ah,ah
shl ax,#8
add ax,#0x0c
out dx,ax
ret
nocirr: call rst3d4 ! Check Everex 'clues'
mov ax,#0x7000
xor bx,bx
int 0x10
cmp al,#0x70
jne noevrx
shr dx,#4
cmp dx,#0x678
je istrid
cmp dx,#0x236
je istrid
lea si,dsceverex
lea di,moeverex
lea cx,selmod
jmp cx
istrid: lea cx,ev2tri
jmp cx
noevrx: lea si,idgenoa ! Check Genoa 'clues'
xor ax,ax
seg es
mov al,[0x37]
mov di,ax
mov cx,#0x04
dec si
dec di
l1: inc si
inc di
mov al,(si)
seg es
and al,(di)
cmp al,(si)
loope l1
cmp cx,#0x00
jne nogen
lea si,dscgenoa
lea di,mogenoa
lea cx,selmod
jmp cx
nogen: lea si,idparadise ! Check Paradise 'clues'
mov di,#0x7d
mov cx,#0x04
repe
cmpsb
jne nopara
lea si,dscparadise
lea di,moparadise
lea cx,selmod
jmp cx
nopara: mov dx,#0x3c4 ! Check Trident 'clues'
mov al,#0x0e
out dx,al
inc dx
in al,dx
xchg ah,al
mov al,#0x00
out dx,al
in al,dx
xchg al,ah
mov bl,al ! Strange thing ... in the book this wasn't
and bl,#0x02 ! necessary but it worked on my card which
jz setb2 ! is a trident. Without it the screen goes
and al,#0xfd ! blurred ...
jmp clrb2 !
setb2: or al,#0x02 !
clrb2: out dx,al
and ah,#0x0f
cmp ah,#0x02
jne notrid
ev2tri: lea si,dsctrident
lea di,motrident
lea cx,selmod
jmp cx
notrid: mov dx,#0x3cd ! Check Tseng 'clues'
in al,dx ! Could things be this simple ! :-)
mov bl,al
mov al,#0x55
out dx,al
in al,dx
mov ah,al
mov al,bl
out dx,al
cmp ah,#0x55
jne notsen
lea si,dsctseng
lea di,motseng
lea cx,selmod
jmp cx
notsen: mov dx,#0x3cc ! Check Video7 'clues'
in al,dx
mov dx,#0x3b4
and al,#0x01
jz even7
mov dx,#0x3d4
even7: mov al,#0x0c
out dx,al
inc dx
in al,dx
mov bl,al
mov al,#0x55
out dx,al
in al,dx
dec dx
mov al,#0x1f
out dx,al
inc dx
in al,dx
mov bh,al
dec dx
mov al,#0x0c
out dx,al
inc dx
mov al,bl
out dx,al
mov al,#0x55
xor al,#0xea
cmp al,bh
jne novid7
lea si,dscvideo7
lea di,movideo7
selmod: push si
lea si,msg2
call prtstr
xor cx,cx
mov cl,(di)
pop si
push si
push cx
tbl: pop bx
push bx
mov al,bl
sub al,cl
call dprnt
call spcing
lodsw
xchg al,ah
call dprnt
xchg ah,al
push ax
mov al,#0x78
call prnt1
pop ax
call dprnt
call docr
loop tbl
pop cx
call docr
lea si,msg3
call prtstr
pop si
add cl,#0x80
nonum: in al,#0x60 ! Quick and dirty...
cmp al,#0x82
jb nonum
cmp al,#0x8b
je zero
cmp al,cl
ja nonum
jmp nozero
zero: sub al,#0x0a
nozero: sub al,#0x80
dec al
xor ah,ah
add di,ax
inc di
push ax
mov al,(di)
int 0x10
pop ax
shl ax,#1
add si,ax
lodsw
pop ds
ret
novid7: pop ds ! Here could be code to support standard 80x50,80x30
mov ax,#0x5019
ret
! Routine that 'tabs' to next col.
spcing: mov al,#0x2e
call prnt1
mov al,#0x20
call prnt1
mov al,#0x20
call prnt1
mov al,#0x20
call prnt1
mov al,#0x20
call prnt1
ret
! Routine to print asciiz-string at DS:SI
prtstr: lodsb
and al,al
jz fin
call prnt1
jmp prtstr
fin: ret
! Routine to print a decimal value on screen, the value to be
! printed is put in al (i.e 0-255).
dprnt: push ax
push cx
mov ah,#0x00
mov cl,#0x0a
idiv cl
cmp al,#0x09
jbe lt100
call dprnt
jmp skip10
lt100: add al,#0x30
call prnt1
skip10: mov al,ah
add al,#0x30
call prnt1
pop cx
pop ax
ret
! Part of above routine, this one just prints ascii al
prnt1: push ax
push cx
mov bh,#0x00
mov cx,#0x01
mov ah,#0x0e
int 0x10
pop cx
pop ax
ret
! Prints <CR> + <LF>
docr: push ax
push cx
mov bh,#0x00
mov ah,#0x0e
mov al,#0x0a
mov cx,#0x01
int 0x10
mov al,#0x0d
int 0x10
pop cx
pop ax
ret
gdt:
.word 0,0,0,0 ! dummy
......@@ -222,6 +584,44 @@ idt_48:
gdt_48:
.word 0x800 ! gdt limit=2048, 256 GDT entries
.word 512+gdt,0x9 ! gdt base = 0X9xxxx
msg1: .ascii "Press <RETURN> to see SVGA-modes available or any other key to continue."
db 0x0d, 0x0a, 0x0a, 0x00
msg2: .ascii "Mode: COLSxROWS:"
db 0x0d, 0x0a, 0x0a, 0x00
msg3: .ascii "Choose mode by pressing the corresponding number."
db 0x0d, 0x0a, 0x00
idati: .ascii "761295520"
idcandt: .byte 0xa5
idgenoa: .byte 0x77, 0x00, 0x66, 0x99
idparadise: .ascii "VGA="
! Manufacturer: Numofmodes: Mode:
moati: .byte 0x02, 0x23, 0x33
moahead: .byte 0x05, 0x22, 0x23, 0x24, 0x2f, 0x34
mocandt: .byte 0x02, 0x60, 0x61
mocirrus: .byte 0x04, 0x1f, 0x20, 0x22, 0x31
moeverex: .byte 0x0a, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
mogenoa: .byte 0x0a, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
moparadise: .byte 0x02, 0x55, 0x54
motrident: .byte 0x07, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
motseng: .byte 0x05, 0x26, 0x2a, 0x23, 0x24, 0x22
movideo7: .byte 0x06, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45
! msb = Cols lsb = Rows:
dscati: .word 0x8419, 0x842c
dscahead: .word 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
dsccandt: .word 0x8419, 0x8432
dsccirrus: .word 0x8419, 0x842c, 0x841e, 0x6425
dsceverex: .word 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
dscgenoa: .word 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
dscparadise: .word 0x8419, 0x842b
dsctrident: .word 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
dsctseng: .word 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
dscvideo7: .word 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
.text
endtext:
......
......@@ -3,7 +3,7 @@ AS =gas
CC =gcc
LD =gld
CFLAGS =-Wall -O -fstrength-reduce -fcombine-regs -fomit-frame-pointer \
-mstring-insns -nostdinc -I../include
-fno-defer-pop -mstring-insns -nostdinc -I../include
CPP =gcc -E -nostdinc -I../include
.c.s:
......@@ -17,7 +17,7 @@ CPP =gcc -E -nostdinc -I../include
OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \
block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \
bitmap.o fcntl.o ioctl.o truncate.o
bitmap.o fcntl.o ioctl.o truncate.o select.o
fs.o: $(OBJS)
$(LD) -r -o fs.o $(OBJS)
......@@ -34,67 +34,96 @@ dep:
### Dependencies:
bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h
block_dev.o : block_dev.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h ../include/asm/system.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/segment.h ../include/asm/system.h
buffer.o : buffer.c ../include/stdarg.h ../include/linux/config.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/sys/types.h ../include/linux/mm.h ../include/signal.h \
../include/linux/kernel.h ../include/asm/system.h ../include/asm/io.h
../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/system.h \
../include/asm/io.h
char_dev.o : char_dev.c ../include/errno.h ../include/sys/types.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h ../include/asm/io.h
exec.o : exec.c ../include/errno.h ../include/string.h \
../include/sys/stat.h ../include/sys/types.h ../include/a.out.h \
../include/linux/fs.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/segment.h ../include/asm/io.h
exec.o : exec.c ../include/signal.h ../include/sys/types.h \
../include/errno.h ../include/string.h ../include/sys/stat.h \
../include/a.out.h ../include/linux/fs.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/segment.h
fcntl.o : fcntl.c ../include/string.h ../include/errno.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/sys/types.h ../include/linux/mm.h ../include/signal.h \
../include/linux/kernel.h ../include/asm/segment.h ../include/fcntl.h \
../include/sys/stat.h
../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h \
../include/fcntl.h ../include/sys/stat.h
file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \
../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \
../include/linux/kernel.h ../include/asm/segment.h
../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h
file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h
inode.o : inode.c ../include/string.h ../include/sys/stat.h \
../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/linux/mm.h ../include/signal.h \
../include/linux/kernel.h ../include/asm/system.h
../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/system.h
ioctl.o : ioctl.c ../include/string.h ../include/errno.h \
../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/signal.h
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h
namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h \
../include/string.h ../include/fcntl.h ../include/errno.h \
../include/const.h ../include/sys/stat.h
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/asm/segment.h ../include/string.h ../include/fcntl.h \
../include/errno.h ../include/const.h ../include/sys/stat.h
open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \
../include/sys/types.h ../include/utime.h ../include/sys/stat.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \
../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/linux/tty.h ../include/termios.h \
../include/asm/segment.h
pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
../include/linux/mm.h ../include/asm/segment.h
../include/errno.h ../include/termios.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h
read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \
../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/signal.h ../include/asm/segment.h
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h
select.o : select.c ../include/linux/fs.h ../include/sys/types.h \
../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h \
../include/asm/system.h ../include/sys/stat.h ../include/string.h \
../include/const.h ../include/errno.h
stat.o : stat.c ../include/errno.h ../include/sys/stat.h \
../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/mm.h ../include/signal.h \
../include/linux/kernel.h ../include/asm/segment.h
../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/signal.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/asm/segment.h
super.o : super.c ../include/linux/config.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/system.h ../include/errno.h ../include/sys/stat.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/system.h ../include/errno.h \
../include/sys/stat.h
truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/signal.h ../include/sys/stat.h
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/sys/stat.h
......@@ -44,7 +44,7 @@ __asm__("cld\n" \
:"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
__res;})
void free_block(int dev, int block)
int free_block(int dev, int block)
{
struct super_block * sb;
struct buffer_head * bh;
......@@ -55,21 +55,22 @@ void free_block(int dev, int block)
panic("trying to free block not in datazone");
bh = get_hash_table(dev,block);
if (bh) {
if (bh->b_count != 1) {
printk("trying to free block (%04x:%d), count=%d\n",
dev,block,bh->b_count);
return;
if (bh->b_count > 1) {
brelse(bh);
return 0;
}
bh->b_dirt=0;
bh->b_uptodate=0;
brelse(bh);
if (bh->b_count)
brelse(bh);
}
block -= sb->s_firstdatazone - 1 ;
if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
panic("free_block: bit already cleared");
printk("free_block: bit already cleared\n");
}
sb->s_zmap[block/8192]->b_dirt = 1;
return 1;
}
int new_block(int dev)
......
......@@ -11,16 +11,25 @@
#include <asm/segment.h>
#include <asm/system.h>
extern int *blk_size[];
int block_write(int dev, long * pos, char * buf, int count)
{
int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos & (BLOCK_SIZE-1);
int chars;
int written = 0;
int size;
struct buffer_head * bh;
register char * p;
if (blk_size[MAJOR(dev)])
size = blk_size[MAJOR(dev)][MINOR(dev)];
else
size = 0x7fffffff;
while (count>0) {
if (block >= size)
return written?written:-EIO;
chars = BLOCK_SIZE - offset;
if (chars > count)
chars=count;
......@@ -49,11 +58,18 @@ int block_read(int dev, unsigned long * pos, char * buf, int count)
int block = *pos >> BLOCK_SIZE_BITS;
int offset = *pos & (BLOCK_SIZE-1);
int chars;
int size;
int read = 0;
struct buffer_head * bh;
register char * p;
if (blk_size[MAJOR(dev)])
size = blk_size[MAJOR(dev)][MINOR(dev)];
else
size = 0x7fffffff;
while (count>0) {
if (block >= size)
return read?read:-EIO;
chars = BLOCK_SIZE-offset;
if (chars > count)
chars = count;
......
......@@ -17,6 +17,7 @@
* was less than 2 hours work to get demand-loading completely implemented.
*/
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
......@@ -38,6 +39,28 @@ extern int sys_close(int fd);
*/
#define MAX_ARG_PAGES 32
int sys_uselib(const char * library)
{
struct m_inode * inode;
unsigned long base;
if (get_limit(0x17) != TASK_SIZE)
return -EINVAL;
if (library) {
if (!(inode=namei(library))) /* get library inode */
return -ENOENT;
} else
inode = NULL;
/* we should check filetypes (headers etc), but we don't */
iput(current->library);
current->library = NULL;
base = get_base(current->ldt[2]);
base += LIBRARY_OFFSET;
free_page_tables(base,LIBRARY_SIZE);
current->library = inode;
return 0;
}
/*
* create_tables() parses the env- and arg-strings in new user
* memory and creates the pointer tables from them, and puts their
......@@ -156,9 +179,8 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
unsigned long code_limit,data_limit,code_base,data_base;
int i;
code_limit = text_size+PAGE_SIZE -1;
code_limit &= 0xFFFFF000;
data_limit = 0x4000000;
code_limit = TASK_SIZE;
data_limit = TASK_SIZE;
code_base = get_base(current->ldt[1]);
data_base = code_base;
set_base(current->ldt[1],code_base);
......@@ -167,17 +189,20 @@ static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
set_limit(current->ldt[2],data_limit);
/* make sure fs points to the NEW data segment */
__asm__("pushl $0x17\n\tpop %%fs"::);
data_base += data_limit;
data_base += data_limit - LIBRARY_SIZE;
for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
data_base -= PAGE_SIZE;
if (page[i])
put_page(page[i],data_base);
put_dirty_page(page[i],data_base);
}
return data_limit;
}
/*
* 'do_execve()' executes a new program.
*
* NOTE! We leave 4MB free at the top of the data-area for a loadable
* library.
*/
int do_execve(unsigned long * eip,long tmp,char * filename,
char ** argv, char ** envp)
......@@ -211,7 +236,7 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
if (current->euid == inode->i_uid)
i >>= 6;
else if (current->egid == inode->i_gid)
else if (in_group_p(inode->i_gid))
i >>= 3;
if (!(i & 1) &&
!((inode->i_mode & 0111) && suser())) {
......@@ -229,13 +254,13 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
* Sorta complicated, but hopefully it will work. -TYT
*/
char buf[1023], *cp, *interp, *i_name, *i_arg;
char buf[128], *cp, *interp, *i_name, *i_arg;
unsigned long old_fs;
strncpy(buf, bh->b_data+2, 1022);
strncpy(buf, bh->b_data+2, 127);
brelse(bh);
iput(inode);
buf[1022] = '\0';
buf[127] = '\0';
if (cp = strchr(buf, '\n')) {
*cp = '\0';
for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
......@@ -316,11 +341,17 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
}
}
/* OK, This is the point of no return */
/* note that current->library stays unchanged by an exec */
if (current->executable)
iput(current->executable);
current->executable = inode;
for (i=0 ; i<32 ; i++)
current->sigaction[i].sa_handler = NULL;
current->signal = 0;
for (i=0 ; i<32 ; i++) {
current->sigaction[i].sa_mask = 0;
current->sigaction[i].sa_flags = 0;
if (current->sigaction[i].sa_handler != SIG_IGN)
current->sigaction[i].sa_handler = NULL;
}
for (i=0 ; i<NR_OPEN ; i++)
if ((current->close_on_exec>>i)&1)
sys_close(i);
......@@ -330,17 +361,15 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
if (last_task_used_math == current)
last_task_used_math = NULL;
current->used_math = 0;
p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
p += change_ldt(ex.a_text,page);
p -= LIBRARY_SIZE + MAX_ARG_PAGES*PAGE_SIZE;
p = (unsigned long) create_tables((char *)p,argc,envc);
current->brk = ex.a_bss +
(current->end_data = ex.a_data +
(current->end_code = ex.a_text));
current->start_stack = p & 0xfffff000;
current->euid = e_uid;
current->egid = e_gid;
i = ex.a_text+ex.a_data;
while (i&0xfff)
put_fs_byte(0,(char *) (i++));
current->suid = current->euid = e_uid;
current->sgid = current->egid = e_gid;
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
return 0;
......
......@@ -12,6 +12,8 @@
#include <linux/mm.h>
#include <asm/system.h>
extern int *blk_size[];
struct m_inode inode_table[NR_INODE]={{0,},};
static void read_inode(struct m_inode * inode);
......@@ -156,6 +158,7 @@ void iput(struct m_inode * inode)
panic("iput: trying to free free inode");
if (inode->i_pipe) {
wake_up(&inode->i_wait);
wake_up(&inode->i_wait2);
if (--inode->i_count)
return;
free_page(inode->i_size);
......@@ -308,6 +311,13 @@ static void read_inode(struct m_inode * inode)
((struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK];
brelse(bh);
if (S_ISBLK(inode->i_mode)) {
int i = inode->i_zone[0];
if (blk_size[MAJOR(i)])
inode->i_size = 1024*blk_size[MAJOR(i)][MINOR(i)];
else
inode->i_size = 0x7fffffff;
}
unlock_inode(inode);
}
......
......@@ -11,6 +11,7 @@
#include <linux/sched.h>
extern int tty_ioctl(int dev, int cmd, int arg);
extern int pipe_ioctl(struct m_inode *pino, int cmd, int arg);
typedef int (*ioctl_ptr)(int dev,int cmd,int arg);
......@@ -34,6 +35,8 @@ int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
if (filp->f_inode->i_pipe)
return (filp->f_mode&1)?pipe_ioctl(filp->f_inode,cmd,arg):-EBADF;
mode=filp->f_inode->i_mode;
if (!S_ISCHR(mode) && !S_ISBLK(mode))
return -EINVAL;
......
......@@ -18,6 +18,9 @@
#include <const.h>
#include <sys/stat.h>
static struct m_inode * _namei(const char * filename, struct m_inode * base,
int follow_links);
#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
/*
......@@ -46,7 +49,7 @@ static int permission(struct m_inode * inode,int mask)
return 0;
else if (current->euid==inode->i_uid)
mode >>= 6;
else if (current->egid==inode->i_gid)
else if (in_group_p(inode->i_gid))
mode >>= 3;
if (((mode & mask & 0007) == mask) || suser())
return 1;
......@@ -66,6 +69,9 @@ static int match(int len,const char * name,struct dir_entry * de)
if (!de || !de->inode || len > NAME_LEN)
return 0;
/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
return 1;
if (len < NAME_LEN && de->name[len])
return 0;
__asm__("cld\n\t"
......@@ -106,8 +112,6 @@ static struct buffer_head * find_entry(struct m_inode ** dir,
#endif
entries = (*dir)->i_size / (sizeof (struct dir_entry));
*res_dir = NULL;
if (!namelen)
return NULL;
/* check for '..', as we might have to do some "magic" for it */
if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
/* '..' in a pseudo-root results in a faked '.' (just change namelen) */
......@@ -219,33 +223,63 @@ static struct buffer_head * add_entry(struct m_inode * dir,
return NULL;
}
static struct m_inode * follow_link(struct m_inode * dir, struct m_inode * inode)
{
unsigned short fs;
struct buffer_head * bh;
if (!dir) {
dir = current->root;
dir->i_count++;
}
if (!inode) {
iput(dir);
return NULL;
}
if (!S_ISLNK(inode->i_mode)) {
iput(dir);
return inode;
}
__asm__("mov %%fs,%0":"=r" (fs));
if (fs != 0x17 || !inode->i_zone[0] ||
!(bh = bread(inode->i_dev, inode->i_zone[0]))) {
iput(dir);
iput(inode);
return NULL;
}
iput(inode);
__asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
inode = _namei(bh->b_data,dir,0);
__asm__("mov %0,%%fs"::"r" (fs));
brelse(bh);
return inode;
}
/*
* get_dir()
*
* Getdir traverses the pathname until it hits the topmost directory.
* It returns NULL on failure.
*/
static struct m_inode * get_dir(const char * pathname)
static struct m_inode * get_dir(const char * pathname, struct m_inode * inode)
{
char c;
const char * thisname;
struct m_inode * inode;
struct buffer_head * bh;
int namelen,inr,idev;
int namelen,inr;
struct dir_entry * de;
struct m_inode * dir;
if (!current->root || !current->root->i_count)
panic("No root inode");
if (!current->pwd || !current->pwd->i_count)
panic("No cwd inode");
if (!inode) {
inode = current->pwd;
inode->i_count++;
}
if ((c=get_fs_byte(pathname))=='/') {
iput(inode);
inode = current->root;
pathname++;
} else if (c)
inode = current->pwd;
else
return NULL; /* empty name is bad */
inode->i_count++;
inode->i_count++;
}
while (1) {
thisname = pathname;
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
......@@ -261,10 +295,13 @@ static struct m_inode * get_dir(const char * pathname)
return NULL;
}
inr = de->inode;
idev = inode->i_dev;
brelse(bh);
iput(inode);
if (!(inode = iget(idev,inr)))
dir = inode;
if (!(inode = iget(dir->i_dev,inr))) {
iput(dir);
return NULL;
}
if (!(inode = follow_link(dir,inode)))
return NULL;
}
}
......@@ -276,13 +313,13 @@ static struct m_inode * get_dir(const char * pathname)
* specified name, and the name within that directory.
*/
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name)
int * namelen, const char ** name, struct m_inode * base)
{
char c;
const char * basename;
struct m_inode * dir;
if (!(dir = get_dir(pathname)))
if (!(dir = get_dir(pathname,base)))
return NULL;
basename = pathname;
while (c=get_fs_byte(pathname++))
......@@ -293,40 +330,54 @@ static struct m_inode * dir_namei(const char * pathname,
return dir;
}
/*
* namei()
*
* is used by most simple commands to get the inode of a specified name.
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
*/
struct m_inode * namei(const char * pathname)
struct m_inode * _namei(const char * pathname, struct m_inode * base,
int follow_links)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir;
int inr,namelen;
struct m_inode * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
if (!(base = dir_namei(pathname,&namelen,&basename,base)))
return NULL;
if (!namelen) /* special case: '/usr/' etc */
return dir;
bh = find_entry(&dir,basename,namelen,&de);
return base;
bh = find_entry(&base,basename,namelen,&de);
if (!bh) {
iput(dir);
iput(base);
return NULL;
}
inr = de->inode;
dev = dir->i_dev;
brelse(bh);
iput(dir);
dir=iget(dev,inr);
if (dir) {
dir->i_atime=CURRENT_TIME;
dir->i_dirt=1;
if (!(inode = iget(base->i_dev,inr))) {
iput(base);
return NULL;
}
return dir;
if (follow_links)
inode = follow_link(base,inode);
else
iput(base);
inode->i_atime=CURRENT_TIME;
inode->i_dirt=1;
return inode;
}
struct m_inode * lnamei(const char * pathname)
{
return _namei(pathname, NULL, 0);
}
/*
* namei()
*
* is used by most simple commands to get the inode of a specified name.
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
*/
struct m_inode * namei(const char * pathname)
{
return _namei(pathname,NULL,1);
}
/*
......@@ -347,7 +398,7 @@ int open_namei(const char * pathname, int flag, int mode,
flag |= O_WRONLY;
mode &= 0777 & ~current->umask;
mode |= I_REGULAR;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
if (!(dir = dir_namei(pathname,&namelen,&basename,NULL)))
return -ENOENT;
if (!namelen) { /* special case: '/usr/' etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
......@@ -392,10 +443,11 @@ int open_namei(const char * pathname, int flag, int mode,
inr = de->inode;
dev = dir->i_dev;
brelse(bh);
iput(dir);
if (flag & O_EXCL)
if (flag & O_EXCL) {
iput(dir);
return -EEXIST;
if (!(inode=iget(dev,inr)))
}
if (!(inode = follow_link(dir,iget(dev,inr))))
return -EACCES;
if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
!permission(inode,ACC_MODE(flag))) {
......@@ -419,7 +471,7 @@ int sys_mknod(const char * filename, int mode, int dev)
if (!suser())
return -EPERM;
if (!(dir = dir_namei(filename,&namelen,&basename)))
if (!(dir = dir_namei(filename,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
......@@ -468,9 +520,7 @@ int sys_mkdir(const char * pathname, int mode)
struct buffer_head * bh, *dir_block;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(pathname,&namelen,&basename)))
if (!(dir = dir_namei(pathname,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
......@@ -503,7 +553,6 @@ int sys_mkdir(const char * pathname, int mode)
inode->i_dirt = 1;
if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
iput(dir);
free_block(inode->i_dev,inode->i_zone[0]);
inode->i_nlinks--;
iput(inode);
return -ERROR;
......@@ -522,7 +571,6 @@ int sys_mkdir(const char * pathname, int mode)
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
iput(dir);
free_block(inode->i_dev,inode->i_zone[0]);
inode->i_nlinks=0;
iput(inode);
return -ENOSPC;
......@@ -592,9 +640,7 @@ int sys_rmdir(const char * name)
struct buffer_head * bh;
struct dir_entry * de;
if (!suser())
return -EPERM;
if (!(dir = dir_namei(name,&namelen,&basename)))
if (!(dir = dir_namei(name,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
......@@ -668,7 +714,7 @@ int sys_unlink(const char * name)
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(name,&namelen,&basename)))
if (!(dir = dir_namei(name,&namelen,&basename, NULL)))
return -ENOENT;
if (!namelen) {
iput(dir);
......@@ -718,6 +764,76 @@ int sys_unlink(const char * name)
return 0;
}
int sys_symlink(const char * oldname, const char * newname)
{
struct dir_entry * de;
struct m_inode * dir, * inode;
struct buffer_head * bh, * name_block;
const char * basename;
int namelen, i;
char c;
dir = dir_namei(newname,&namelen,&basename, NULL);
if (!dir)
return -EACCES;
if (!namelen) {
iput(dir);
return -EPERM;
}
if (!permission(dir,MAY_WRITE)) {
iput(dir);
return -EACCES;
}
if (!(inode = new_inode(dir->i_dev))) {
iput(dir);
return -ENOSPC;
}
inode->i_mode = S_IFLNK | (0777 & ~current->umask);
inode->i_dirt = 1;
if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
iput(dir);
inode->i_nlinks--;
iput(inode);
return -ENOSPC;
}
inode->i_dirt = 1;
if (!(name_block=bread(inode->i_dev,inode->i_zone[0]))) {
iput(dir);
inode->i_nlinks--;
iput(inode);
return -ERROR;
}
i = 0;
while (i < 1023 && (c=get_fs_byte(oldname++)))
name_block->b_data[i++] = c;
name_block->b_data[i] = 0;
name_block->b_dirt = 1;
brelse(name_block);
inode->i_size = i;
inode->i_dirt = 1;
bh = find_entry(&dir,basename,namelen,&de);
if (bh) {
inode->i_nlinks--;
iput(inode);
brelse(bh);
iput(dir);
return -EEXIST;
}
bh = add_entry(dir,basename,namelen,&de);
if (!bh) {
inode->i_nlinks--;
iput(inode);
iput(dir);
return -ENOSPC;
}
de->inode = inode->i_num;
bh->b_dirt = 1;
brelse(bh);
iput(dir);
iput(inode);
return 0;
}
int sys_link(const char * oldname, const char * newname)
{
struct dir_entry * de;
......@@ -733,7 +849,7 @@ int sys_link(const char * oldname, const char * newname)
iput(oldinode);
return -EPERM;
}
dir = dir_namei(newname,&namelen,&basename);
dir = dir_namei(newname,&namelen,&basename, NULL);
if (!dir) {
iput(oldinode);
return -EACCES;
......
......@@ -14,6 +14,7 @@
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <asm/segment.h>
int sys_ustat(int dev, struct ustat * ubuf)
......@@ -135,6 +136,38 @@ int sys_chown(const char * filename,int uid,int gid)
return 0;
}
static int check_char_dev(struct m_inode * inode, int dev, int flag)
{
struct tty_struct *tty;
int min;
if (MAJOR(dev) == 4 || MAJOR(dev) == 5) {
if (MAJOR(dev) == 5)
min = current->tty;
else
min = MINOR(dev);
if (min < 0)
return -1;
if ((IS_A_PTY_MASTER(min)) && (inode->i_count>1))
return -1;
tty = TTY_TABLE(min);
if (!(flag & O_NOCTTY) &&
current->leader &&
current->tty<0 &&
tty->session==0) {
current->tty = min;
tty->session= current->session;
tty->pgrp = current->pgrp;
}
if (flag & O_NONBLOCK) {
TTY_TABLE(min)->termios.c_cc[VMIN] =0;
TTY_TABLE(min)->termios.c_cc[VTIME] =0;
TTY_TABLE(min)->termios.c_lflag &= ~ICANON;
}
}
return 0;
}
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
......@@ -161,18 +194,12 @@ int sys_open(const char * filename,int flag,int mode)
}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
if (S_ISCHR(inode->i_mode))
if (MAJOR(inode->i_zone[0])==4) {
if (current->leader && current->tty<0) {
current->tty = MINOR(inode->i_zone[0]);
tty_table[current->tty].pgrp = current->pgrp;
}
} else if (MAJOR(inode->i_zone[0])==5)
if (current->tty<0) {
iput(inode);
current->filp[fd]=NULL;
f->f_count=0;
return -EPERM;
}
if (check_char_dev(inode,inode->i_zone[0],flag)) {
iput(inode);
current->filp[fd]=NULL;
f->f_count=0;
return -EAGAIN;
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
......
......@@ -5,10 +5,13 @@
*/
#include <signal.h>
#include <errno.h>
#include <termios.h>
#include <linux/sched.h>
#include <linux/mm.h> /* for get_free_page */
#include <asm/segment.h>
#include <linux/kernel.h>
int read_pipe(struct m_inode * inode, char * buf, int count)
{
......@@ -16,10 +19,12 @@ int read_pipe(struct m_inode * inode, char * buf, int count)
while (count>0) {
while (!(size=PIPE_SIZE(*inode))) {
wake_up(&inode->i_wait);
wake_up(& PIPE_WRITE_WAIT(*inode));
if (inode->i_count != 2) /* are there any writers? */
return read;
sleep_on(&inode->i_wait);
if (current->signal & ~current->blocked)
return read?read:-ERESTARTSYS;
interruptible_sleep_on(& PIPE_READ_WAIT(*inode));
}
chars = PAGE_SIZE-PIPE_TAIL(*inode);
if (chars > count)
......@@ -34,7 +39,7 @@ int read_pipe(struct m_inode * inode, char * buf, int count)
while (chars-->0)
put_fs_byte(((char *)inode->i_size)[size++],buf++);
}
wake_up(&inode->i_wait);
wake_up(& PIPE_WRITE_WAIT(*inode));
return read;
}
......@@ -44,12 +49,12 @@ int write_pipe(struct m_inode * inode, char * buf, int count)
while (count>0) {
while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) {
wake_up(&inode->i_wait);
wake_up(& PIPE_READ_WAIT(*inode));
if (inode->i_count != 2) { /* no readers */
current->signal |= (1<<(SIGPIPE-1));
return written?written:-1;
}
sleep_on(&inode->i_wait);
sleep_on(& PIPE_WRITE_WAIT(*inode));
}
chars = PAGE_SIZE-PIPE_HEAD(*inode);
if (chars > count)
......@@ -64,7 +69,7 @@ int write_pipe(struct m_inode * inode, char * buf, int count)
while (chars-->0)
((char *)inode->i_size)[size++]=get_fs_byte(buf++);
}
wake_up(&inode->i_wait);
wake_up(& PIPE_READ_WAIT(*inode));
return written;
}
......@@ -109,3 +114,15 @@ int sys_pipe(unsigned long * fildes)
put_fs_long(fd[1],1+fildes);
return 0;
}
int pipe_ioctl(struct m_inode *pino, int cmd, int arg)
{
switch (cmd) {
case FIONREAD:
verify_area((void *) arg,4);
put_fs_long(PIPE_SIZE(*pino),(unsigned long *) arg);
return 0;
default:
return -EINVAL;
}
}
/*
* This file contains the procedures for the handling of select
*
* Created for Linux based loosely upon Mathius Lattner's minix
* patches by Peter MacDonald. Heavily edited by Linus.
*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <const.h>
#include <errno.h>
#include <sys/time.h>
#include <signal.h>
/*
* Ok, Peter made a complicated, but straightforward multiple_wait() function.
* I have rewritten this, taking some shortcuts: This code may not be easy to
* follow, but it should be free of race-conditions, and it's practical. If you
* understand what I'm doing here, then you understand how the linux sleep/wakeup
* mechanism works.
*
* Two very simple procedures, add_wait() and free_wait() make all the work. We
* have to have interrupts disabled throughout the select, but that's not really
* such a loss: sleeping automatically frees interrupts when we aren't in this
* task.
*/
typedef struct {
struct task_struct * old_task;
struct task_struct ** wait_address;
} wait_entry;
typedef struct {
int nr;
wait_entry entry[NR_OPEN*3];
} select_table;
static void add_wait(struct task_struct ** wait_address, select_table * p)
{
int i;
if (!wait_address)
return;
for (i = 0 ; i < p->nr ; i++)
if (p->entry[i].wait_address == wait_address)
return;
p->entry[p->nr].wait_address = wait_address;
p->entry[p->nr].old_task = * wait_address;
*wait_address = current;
p->nr++;
}
static void free_wait(select_table * p)
{
int i;
struct task_struct ** tpp;
for (i = 0; i < p->nr ; i++) {
tpp = p->entry[i].wait_address;
while (*tpp && *tpp != current) {
(*tpp)->state = 0;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
}
if (!*tpp)
printk("free_wait: NULL");
if (*tpp = p->entry[i].old_task)
(**tpp).state = 0;
}
p->nr = 0;
}
static struct tty_struct * get_tty(struct m_inode * inode)
{
int major, minor;
if (!S_ISCHR(inode->i_mode))
return NULL;
if ((major = MAJOR(inode->i_zone[0])) != 5 && major != 4)
return NULL;
if (major == 5)
minor = current->tty;
else
minor = MINOR(inode->i_zone[0]);
if (minor < 0)
return NULL;
return TTY_TABLE(minor);
}
/*
* The check_XX functions check out a file. We know it's either
* a pipe, a character device or a fifo (fifo's not implemented)
*/
static int check_in(select_table * wait, struct m_inode * inode)
{
struct tty_struct * tty;
if (tty = get_tty(inode))
if (!EMPTY(tty->secondary))
return 1;
else
add_wait(&tty->secondary->proc_list, wait);
else if (inode->i_pipe)
if (!PIPE_EMPTY(*inode))
return 1;
else
add_wait(&inode->i_wait, wait);
return 0;
}
static int check_out(select_table * wait, struct m_inode * inode)
{
struct tty_struct * tty;
if (tty = get_tty(inode))
if (!FULL(tty->write_q))
return 1;
else
add_wait(&tty->write_q->proc_list, wait);
else if (inode->i_pipe)
if (!PIPE_FULL(*inode))
return 1;
else
add_wait(&inode->i_wait, wait);
return 0;
}
static int check_ex(select_table * wait, struct m_inode * inode)
{
struct tty_struct * tty;
if (tty = get_tty(inode))
if (!FULL(tty->write_q))
return 0;
else
return 0;
else if (inode->i_pipe)
if (inode->i_count < 2)
return 1;
else
add_wait(&inode->i_wait,wait);
return 0;
}
int do_select(fd_set in, fd_set out, fd_set ex,
fd_set *inp, fd_set *outp, fd_set *exp)
{
int count;
select_table wait_table;
int i;
fd_set mask;
mask = in | out | ex;
for (i = 0 ; i < NR_OPEN ; i++,mask >>= 1) {
if (!(mask & 1))
continue;
if (!current->filp[i])
return -EBADF;
if (!current->filp[i]->f_inode)
return -EBADF;
if (current->filp[i]->f_inode->i_pipe)
continue;
if (S_ISCHR(current->filp[i]->f_inode->i_mode))
continue;
if (S_ISFIFO(current->filp[i]->f_inode->i_mode))
continue;
return -EBADF;
}
repeat:
wait_table.nr = 0;
*inp = *outp = *exp = 0;
count = 0;
mask = 1;
for (i = 0 ; i < NR_OPEN ; i++, mask += mask) {
if (mask & in)
if (check_in(&wait_table,current->filp[i]->f_inode)) {
*inp |= mask;
count++;
}
if (mask & out)
if (check_out(&wait_table,current->filp[i]->f_inode)) {
*outp |= mask;
count++;
}
if (mask & ex)
if (check_ex(&wait_table,current->filp[i]->f_inode)) {
*exp |= mask;
count++;
}
}
if (!(current->signal & ~current->blocked) &&
(wait_table.nr || current->timeout) && !count) {
current->state = TASK_INTERRUPTIBLE;
schedule();
free_wait(&wait_table);
goto repeat;
}
free_wait(&wait_table);
return count;
}
/*
* Note that we cannot return -ERESTARTSYS, as we change our input
* parameters. Sad, but there you are. We could do some tweaking in
* the library function ...
*/
int sys_select( unsigned long *buffer )
{
/* Perform the select(nd, in, out, ex, tv) system call. */
int i;
fd_set res_in, in = 0, *inp;
fd_set res_out, out = 0, *outp;
fd_set res_ex, ex = 0, *exp;
fd_set mask;
struct timeval *tvp;
unsigned long timeout;
mask = ~((~0) << get_fs_long(buffer++));
inp = (fd_set *) get_fs_long(buffer++);
outp = (fd_set *) get_fs_long(buffer++);
exp = (fd_set *) get_fs_long(buffer++);
tvp = (struct timeval *) get_fs_long(buffer);
if (inp)
in = mask & get_fs_long(inp);
if (outp)
out = mask & get_fs_long(outp);
if (exp)
ex = mask & get_fs_long(exp);
timeout = 0xffffffff;
if (tvp) {
timeout = get_fs_long((unsigned long *)&tvp->tv_usec)/(1000000/HZ);
timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
timeout += jiffies;
}
current->timeout = timeout;
cli();
i = do_select(in, out, ex, &res_in, &res_out, &res_ex);
if (current->timeout > jiffies)
timeout = current->timeout - jiffies;
else
timeout = 0;
sti();
current->timeout = 0;
if (i < 0)
return i;
if (inp) {
verify_area(inp, 4);
put_fs_long(res_in,inp);
}
if (outp) {
verify_area(outp,4);
put_fs_long(res_out,outp);
}
if (exp) {
verify_area(exp,4);
put_fs_long(res_ex,exp);
}
if (tvp) {
verify_area(tvp, sizeof(*tvp));
put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
timeout %= HZ;
timeout *= (1000000/HZ);
put_fs_long(timeout, (unsigned long *) &tvp->tv_usec);
}
if (!i && (current->signal & ~current->blocked))
return -EINTR;
return i;
}
......@@ -17,7 +17,7 @@ static void cp_stat(struct m_inode * inode, struct stat * statbuf)
struct stat tmp;
int i;
verify_area(statbuf,sizeof (* statbuf));
verify_area(statbuf,sizeof (struct stat));
tmp.st_dev = inode->i_dev;
tmp.st_ino = inode->i_num;
tmp.st_mode = inode->i_mode;
......@@ -30,7 +30,7 @@ static void cp_stat(struct m_inode * inode, struct stat * statbuf)
tmp.st_mtime = inode->i_mtime;
tmp.st_ctime = inode->i_ctime;
for (i=0 ; i<sizeof (tmp) ; i++)
put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]);
put_fs_byte(((char *) &tmp)[i],i + (char *) statbuf);
}
int sys_stat(char * filename, struct stat * statbuf)
......@@ -44,6 +44,17 @@ int sys_stat(char * filename, struct stat * statbuf)
return 0;
}
int sys_lstat(char * filename, struct stat * statbuf)
{
struct m_inode * inode;
if (!(inode = lnamei(filename)))
return -ENOENT;
cp_stat(inode,statbuf);
iput(inode);
return 0;
}
int sys_fstat(unsigned int fd, struct stat * statbuf)
{
struct file * f;
......@@ -54,3 +65,33 @@ int sys_fstat(unsigned int fd, struct stat * statbuf)
cp_stat(inode,statbuf);
return 0;
}
int sys_readlink(const char * path, char * buf, int bufsiz)
{
struct m_inode * inode;
struct buffer_head * bh;
int i;
char c;
if (bufsiz <= 0)
return -EBADF;
if (bufsiz > 1023)
bufsiz = 1023;
verify_area(buf,bufsiz);
if (!(inode = lnamei(path)))
return -ENOENT;
if (inode->i_zone[0])
bh = bread(inode->i_dev, inode->i_zone[0]);
else
bh = NULL;
iput(inode);
if (!bh)
return 0;
i = 0;
while (i<bufsiz && (c = bh->b_data[i])) {
i++;
put_fs_byte(c,buf++);
}
brelse(bh);
return i;
}
......@@ -74,7 +74,6 @@ struct super_block * get_super(int dev)
void put_super(int dev)
{
struct super_block * sb;
struct m_inode * inode;
int i;
if (dev == ROOT_DEV) {
......
......@@ -8,58 +8,92 @@
#include <sys/stat.h>
static void free_ind(int dev,int block)
static int free_ind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
int block_busy;
if (!block)
return;
return 1;
block_busy = 0;
if (bh=bread(dev,block)) {
p = (unsigned short *) bh->b_data;
for (i=0;i<512;i++,p++)
if (*p)
free_block(dev,*p);
if (free_block(dev,*p)) {
*p = 0;
bh->b_dirt = 1;
} else
block_busy = 1;
brelse(bh);
}
free_block(dev,block);
if (block_busy)
return 0;
else
return free_block(dev,block);
}
static void free_dind(int dev,int block)
static int free_dind(int dev,int block)
{
struct buffer_head * bh;
unsigned short * p;
int i;
int block_busy;
if (!block)
return;
return 1;
block_busy = 0;
if (bh=bread(dev,block)) {
p = (unsigned short *) bh->b_data;
for (i=0;i<512;i++,p++)
if (*p)
free_ind(dev,*p);
if (free_ind(dev,*p)) {
*p = 0;
bh->b_dirt = 1;
} else
block_busy = 1;
brelse(bh);
}
free_block(dev,block);
if (block_busy)
return 0;
else
return free_block(dev,block);
}
void truncate(struct m_inode * inode)
{
int i;
int block_busy;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return;
repeat:
block_busy = 0;
for (i=0;i<7;i++)
if (inode->i_zone[i]) {
free_block(inode->i_dev,inode->i_zone[i]);
inode->i_zone[i]=0;
if (free_block(inode->i_dev,inode->i_zone[i]))
inode->i_zone[i]=0;
else
block_busy = 1;
}
free_ind(inode->i_dev,inode->i_zone[7]);
free_dind(inode->i_dev,inode->i_zone[8]);
inode->i_zone[7] = inode->i_zone[8] = 0;
inode->i_size = 0;
if (free_ind(inode->i_dev,inode->i_zone[7]))
inode->i_zone[7] = 0;
else
block_busy = 1;
if (free_dind(inode->i_dev,inode->i_zone[8]))
inode->i_zone[8] = 0;
else
block_busy = 1;
inode->i_dirt = 1;
if (block_busy) {
current->counter = 0;
schedule();
goto repeat;
}
inode->i_size = 0;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
......@@ -7,10 +7,10 @@ __asm__ ("movl %%esp,%%eax\n\t" \
"pushl $1f\n\t" \
"iret\n" \
"1:\tmovl $0x17,%%eax\n\t" \
"movw %%ax,%%ds\n\t" \
"movw %%ax,%%es\n\t" \
"movw %%ax,%%fs\n\t" \
"movw %%ax,%%gs" \
"mov %%ax,%%ds\n\t" \
"mov %%ax,%%es\n\t" \
"mov %%ax,%%fs\n\t" \
"mov %%ax,%%gs" \
:::"ax")
#define sti() __asm__ ("sti"::)
......
......@@ -57,4 +57,8 @@ extern int errno;
#define ENOSYS 38
#define ENOTEMPTY 39
/* Should never be seen by user programs */
#define ERESTARTSYS 512
#define ERESTARTNOINTR 513
#endif
#ifndef _CONFIG_H
#define _CONFIG_H
/*
* Defines for what uname() should return
*/
#define UTS_SYSNAME "Linux"
#define UTS_NODENAME "(none)" /* set by sethostname() */
#define UTS_RELEASE "0" /* patchlevel */
#define UTS_VERSION "0.12"
#define UTS_MACHINE "i386" /* hardware type */
/* Don't touch these, unless you really know what your doing. */
#define DEF_INITSEG 0x9000
#define DEF_SYSSEG 0x1000
#define DEF_SETUPSEG 0x9020
#define DEF_SYSSIZE 0x3000
/*
* The root-device is no longer hard-coded. You can change the default
* root-device by changing the line ROOT_DEV = XXX in boot/bootsect.s
*/
/*
* define your keyboard here -
* KBD_FINNISH for Finnish keyboards
* KBD_US for US-type
* KBD_GR for German keyboards
* KBD_FR for Frech keyboard
* The keyboard is now defined in kernel/chr_dev/keyboard.S
*/
/*#define KBD_US */
/*#define KBD_GR */
/*#define KBD_FR */
#define KBD_FINNISH
/*
* Normally, Linux can get the drive parameters from the BIOS at
......
......@@ -41,7 +41,7 @@ void buffer_init(long buffer_end);
#define SUPER_MAGIC 0x137F
#define NR_OPEN 20
#define NR_INODE 32
#define NR_INODE 64
#define NR_FILE 64
#define NR_SUPER 8
#define NR_HASH 307
......@@ -55,13 +55,18 @@ void buffer_init(long buffer_end);
#define INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct d_inode)))
#define DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct dir_entry)))
#define PIPE_READ_WAIT(inode) ((inode).i_wait)
#define PIPE_WRITE_WAIT(inode) ((inode).i_wait2)
#define PIPE_HEAD(inode) ((inode).i_zone[0])
#define PIPE_TAIL(inode) ((inode).i_zone[1])
#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1))
#define PIPE_EMPTY(inode) (PIPE_HEAD(inode)==PIPE_TAIL(inode))
#define PIPE_FULL(inode) (PIPE_SIZE(inode)==(PAGE_SIZE-1))
#define INC_PIPE(head) \
__asm__("incl %0\n\tandl $4095,%0"::"m" (head))
#define NIL_FILP ((struct file *)0)
#define SEL_IN 1
#define SEL_OUT 2
#define SEL_EX 4
typedef char buffer_block[BLOCK_SIZE];
......@@ -100,6 +105,7 @@ struct m_inode {
unsigned short i_zone[9];
/* these are in memory also */
struct task_struct * i_wait;
struct task_struct * i_wait2; /* for pipes */
unsigned long i_atime;
unsigned long i_ctime;
unsigned short i_dev;
......@@ -176,6 +182,7 @@ extern void wait_on(struct m_inode * inode);
extern int bmap(struct m_inode * inode,int block);
extern int create_block(struct m_inode * inode,int block);
extern struct m_inode * namei(const char * pathname);
extern struct m_inode * lnamei(const char * pathname);
extern int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode);
extern void iput(struct m_inode * inode);
......@@ -185,12 +192,13 @@ extern struct m_inode * get_pipe_inode(void);
extern struct buffer_head * get_hash_table(int dev, int block);
extern struct buffer_head * getblk(int dev, int block);
extern void ll_rw_block(int rw, struct buffer_head * bh);
extern void ll_rw_page(int rw, int dev, int nr, char * buffer);
extern void brelse(struct buffer_head * buf);
extern struct buffer_head * bread(int dev,int block);
extern void bread_page(unsigned long addr,int dev,int b[4]);
extern struct buffer_head * breada(int dev,int block,...);
extern int new_block(int dev);
extern void free_block(int dev, int block);
extern int free_block(int dev, int block);
extern struct m_inode * new_inode(int dev);
extern void free_inode(struct m_inode * inode);
extern int sync_dev(int dev);
......
......@@ -3,11 +3,22 @@
*/
void verify_area(void * addr,int count);
volatile void panic(const char * str);
volatile void do_exit(long error_code);
int printf(const char * fmt, ...);
int printk(const char * fmt, ...);
void console_print(const char * str);
int tty_write(unsigned ch,char * buf,int count);
void * malloc(unsigned int size);
void free_s(void * obj, int size);
extern void hd_times_out(void);
extern void sysbeepstop(void);
extern void blank_screen(void);
extern void unblank_screen(void);
extern int beepcount;
extern int hd_timeout;
extern int blankinterval;
extern int blankcount;
#define free(x) free_s((x), 0)
......
/*
* linux/include/linux/math_emu.h
*
* (C) 1991 Linus Torvalds
*/
#ifndef _LINUX_MATH_EMU_H
#define _LINUX_MATH_EMU_H
#include <linux/sched.h>
struct info {
long ___math_ret;
long ___orig_eip;
long ___edi;
long ___esi;
long ___ebp;
long ___sys_call_ret;
long ___eax;
long ___ebx;
long ___ecx;
long ___edx;
long ___orig_eax;
long ___fs;
long ___es;
long ___ds;
long ___eip;
long ___cs;
long ___eflags;
long ___esp;
long ___ss;
};
#define EAX (info->___eax)
#define EBX (info->___ebx)
#define ECX (info->___ecx)
#define EDX (info->___edx)
#define ESI (info->___esi)
#define EDI (info->___edi)
#define EBP (info->___ebp)
#define ESP (info->___esp)
#define EIP (info->___eip)
#define ORIG_EIP (info->___orig_eip)
#define EFLAGS (info->___eflags)
#define DS (*(unsigned short *) &(info->___ds))
#define ES (*(unsigned short *) &(info->___es))
#define FS (*(unsigned short *) &(info->___fs))
#define CS (*(unsigned short *) &(info->___cs))
#define SS (*(unsigned short *) &(info->___ss))
void __math_abort(struct info *, unsigned int);
#define math_abort(x,y) \
(((volatile void (*)(struct info *,unsigned int)) __math_abort)((x),(y)))
/*
* Gcc forces this stupid alignment problem: I want to use only two longs
* for the temporary real 64-bit mantissa, but then gcc aligns out the
* structure to 12 bytes which breaks things in math_emulate.c. Shit. I
* want some kind of "no-alignt" pragma or something.
*/
typedef struct {
long a,b;
short exponent;
} temp_real;
typedef struct {
short m0,m1,m2,m3;
short exponent;
} temp_real_unaligned;
#define real_to_real(a,b) \
((*(long long *) (b) = *(long long *) (a)),((b)->exponent = (a)->exponent))
typedef struct {
long a,b;
} long_real;
typedef long short_real;
typedef struct {
long a,b;
short sign;
} temp_int;
struct swd {
int ie:1;
int de:1;
int ze:1;
int oe:1;
int ue:1;
int pe:1;
int sf:1;
int ir:1;
int c0:1;
int c1:1;
int c2:1;
int top:3;
int c3:1;
int b:1;
};
#define I387 (current->tss.i387)
#define SWD (*(struct swd *) &I387.swd)
#define ROUNDING ((I387.cwd >> 10) & 3)
#define PRECISION ((I387.cwd >> 8) & 3)
#define BITS24 0
#define BITS53 2
#define BITS64 3
#define ROUND_NEAREST 0
#define ROUND_DOWN 1
#define ROUND_UP 2
#define ROUND_0 3
#define CONSTZ (temp_real_unaligned) {0x0000,0x0000,0x0000,0x0000,0x0000}
#define CONST1 (temp_real_unaligned) {0x0000,0x0000,0x0000,0x8000,0x3FFF}
#define CONSTPI (temp_real_unaligned) {0xC235,0x2168,0xDAA2,0xC90F,0x4000}
#define CONSTLN2 (temp_real_unaligned) {0x79AC,0xD1CF,0x17F7,0xB172,0x3FFE}
#define CONSTLG2 (temp_real_unaligned) {0xF799,0xFBCF,0x9A84,0x9A20,0x3FFD}
#define CONSTL2E (temp_real_unaligned) {0xF0BC,0x5C17,0x3B29,0xB8AA,0x3FFF}
#define CONSTL2T (temp_real_unaligned) {0x8AFE,0xCD1B,0x784B,0xD49A,0x4000}
#define set_IE() (I387.swd |= 1)
#define set_DE() (I387.swd |= 2)
#define set_ZE() (I387.swd |= 4)
#define set_OE() (I387.swd |= 8)
#define set_UE() (I387.swd |= 16)
#define set_PE() (I387.swd |= 32)
#define set_C0() (I387.swd |= 0x0100)
#define set_C1() (I387.swd |= 0x0200)
#define set_C2() (I387.swd |= 0x0400)
#define set_C3() (I387.swd |= 0x4000)
/* ea.c */
char * ea(struct info * __info, unsigned short __code);
/* convert.c */
void short_to_temp(const short_real * __a, temp_real * __b);
void long_to_temp(const long_real * __a, temp_real * __b);
void temp_to_short(const temp_real * __a, short_real * __b);
void temp_to_long(const temp_real * __a, long_real * __b);
void real_to_int(const temp_real * __a, temp_int * __b);
void int_to_real(const temp_int * __a, temp_real * __b);
/* get_put.c */
void get_short_real(temp_real *, struct info *, unsigned short);
void get_long_real(temp_real *, struct info *, unsigned short);
void get_temp_real(temp_real *, struct info *, unsigned short);
void get_short_int(temp_real *, struct info *, unsigned short);
void get_long_int(temp_real *, struct info *, unsigned short);
void get_longlong_int(temp_real *, struct info *, unsigned short);
void get_BCD(temp_real *, struct info *, unsigned short);
void put_short_real(const temp_real *, struct info *, unsigned short);
void put_long_real(const temp_real *, struct info *, unsigned short);
void put_temp_real(const temp_real *, struct info *, unsigned short);
void put_short_int(const temp_real *, struct info *, unsigned short);
void put_long_int(const temp_real *, struct info *, unsigned short);
void put_longlong_int(const temp_real *, struct info *, unsigned short);
void put_BCD(const temp_real *, struct info *, unsigned short);
/* add.c */
void fadd(const temp_real *, const temp_real *, temp_real *);
/* mul.c */
void fmul(const temp_real *, const temp_real *, temp_real *);
/* div.c */
void fdiv(const temp_real *, const temp_real *, temp_real *);
/* compare.c */
void fcom(const temp_real *, const temp_real *);
void fucom(const temp_real *, const temp_real *);
void ftst(const temp_real *);
#endif
......@@ -3,8 +3,43 @@
#define PAGE_SIZE 4096
#include <linux/kernel.h>
#include <signal.h>
extern int SWAP_DEV;
#define read_swap_page(nr,buffer) ll_rw_page(READ,SWAP_DEV,(nr),(buffer));
#define write_swap_page(nr,buffer) ll_rw_page(WRITE,SWAP_DEV,(nr),(buffer));
extern unsigned long get_free_page(void);
extern unsigned long put_page(unsigned long page,unsigned long address);
extern unsigned long put_dirty_page(unsigned long page,unsigned long address);
extern void free_page(unsigned long addr);
void swap_free(int page_nr);
void swap_in(unsigned long *table_ptr);
extern inline volatile void oom(void)
{
printk("out of memory\n\r");
do_exit(SIGSEGV);
}
#define invalidate() \
__asm__("movl %%eax,%%cr3"::"a" (0))
/* these are not to be changed without changing head.s etc */
#define LOW_MEM 0x100000
extern unsigned long HIGH_MEMORY;
#define PAGING_MEMORY (15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100
extern unsigned char mem_map [ PAGING_PAGES ];
#define PAGE_DIRTY 0x40
#define PAGE_ACCESSED 0x20
#define PAGE_USER 0x04
#define PAGE_RW 0x02
#define PAGE_PRESENT 0x01
#endif
#ifndef _SCHED_H
#define _SCHED_H
#define NR_TASKS 64
#define HZ 100
#define NR_TASKS 64
#define TASK_SIZE 0x04000000
#define LIBRARY_SIZE 0x00400000
#if (TASK_SIZE & 0x3fffff)
#error "TASK_SIZE must be multiple of 4M"
#endif
#if (LIBRARY_SIZE & 0x3fffff)
#error "LIBRARY_SIZE must be a multiple of 4M"
#endif
#if (LIBRARY_SIZE >= (TASK_SIZE/2))
#error "LIBRARY_SIZE too damn big!"
#endif
#if (((TASK_SIZE>>16)*NR_TASKS) != 0x10000)
#error "TASK_SIZE*NR_TASKS must be 4GB"
#endif
#define LIBRARY_OFFSET (TASK_SIZE - LIBRARY_SIZE)
#define CT_TO_SECS(x) ((x) / HZ)
#define CT_TO_USECS(x) (((x) % HZ) * 1000000/HZ)
#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]
#include <linux/head.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#if (NR_OPEN > 32)
#error "Currently the close-on-exec-flags are in one word, max 32 files/proc"
#error "Currently the close-on-exec-flags and select masks are in one long, max 32 files/proc"
#endif
#define TASK_RUNNING 0
......@@ -86,11 +113,20 @@ struct task_struct {
/* various fields */
int exit_code;
unsigned long start_code,end_code,end_data,brk,start_stack;
long pid,father,pgrp,session,leader;
long pid,pgrp,session,leader;
int groups[NGROUPS];
/*
* pointers to parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->p_pptr->pid)
*/
struct task_struct *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
unsigned short uid,euid,suid;
unsigned short gid,egid,sgid;
long alarm;
unsigned long timeout,alarm;
long utime,stime,cutime,cstime,start_time;
struct rlimit rlim[RLIM_NLIMITS];
unsigned int flags; /* per process flags, defined below */
unsigned short used_math;
/* file system info */
int tty; /* -1 if no tty, so it must be signed */
......@@ -98,6 +134,7 @@ struct task_struct {
struct m_inode * pwd;
struct m_inode * root;
struct m_inode * executable;
struct m_inode * library;
unsigned long close_on_exec;
struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
......@@ -106,6 +143,12 @@ struct task_struct {
struct tss_struct tss;
};
/*
* Per process flags
*/
#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */
/* Not implemented yet, only for 486*/
/*
* INIT_TASK is used to set up the first task table, touch at
* your own risk!. Base=0, limit=0x9ffff (=640kB)
......@@ -114,11 +157,17 @@ struct task_struct {
/* state etc */ { 0,15,15, \
/* signals */ 0,{{},},0, \
/* ec,brk... */ 0,0,0,0,0,0, \
/* pid etc.. */ 0,-1,0,0,0, \
/* pid etc.. */ 0,0,0,0, \
/* suppl grps*/ {NOGROUP,}, \
/* proc links*/ &init_task.task,0,0,0, \
/* uid etc */ 0,0,0,0,0,0, \
/* alarm */ 0,0,0,0,0,0, \
/* timeout */ 0,0,0,0,0,0,0, \
/* rlimits */ { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
{0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \
{0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \
/* flags */ 0, \
/* math */ 0, \
/* fs info */ -1,0022,NULL,NULL,NULL,0, \
/* fs info */ -1,0022,NULL,NULL,NULL,NULL,0, \
/* filp */ {NULL,}, \
{ \
{0,0}, \
......@@ -136,15 +185,17 @@ struct task_struct {
extern struct task_struct *task[NR_TASKS];
extern struct task_struct *last_task_used_math;
extern struct task_struct *current;
extern long volatile jiffies;
extern long startup_time;
extern unsigned long volatile jiffies;
extern unsigned long startup_time;
extern int jiffies_offset;
#define CURRENT_TIME (startup_time+jiffies/HZ)
#define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ)
extern void add_timer(long jiffies, void (*fn)(void));
extern void sleep_on(struct task_struct ** p);
extern void interruptible_sleep_on(struct task_struct ** p);
extern void wake_up(struct task_struct ** p);
extern int in_group_p(gid_t grp);
/*
* Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall
......
/*
* Why isn't this a .c file? Enquiring minds....
*/
extern int sys_setup();
extern int sys_exit();
extern int sys_fork();
......@@ -70,6 +74,21 @@ extern int sys_sgetmask();
extern int sys_ssetmask();
extern int sys_setreuid();
extern int sys_setregid();
extern int sys_sigpending();
extern int sys_sigsuspend();
extern int sys_sethostname();
extern int sys_setrlimit();
extern int sys_getrlimit();
extern int sys_getrusage();
extern int sys_gettimeofday();
extern int sys_settimeofday();
extern int sys_getgroups();
extern int sys_setgroups();
extern int sys_select();
extern int sys_symlink();
extern int sys_lstat();
extern int sys_readlink();
extern int sys_uselib();
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
......@@ -83,4 +102,10 @@ sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid };
sys_setreuid,sys_setregid, sys_sigsuspend, sys_sigpending, sys_sethostname,
sys_setrlimit, sys_getrlimit, sys_getrusage, sys_gettimeofday,
sys_settimeofday, sys_getgroups, sys_setgroups, sys_select, sys_symlink,
sys_lstat, sys_readlink, sys_uselib };
/* So we don't have to do any more manual updating.... */
int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr);
......@@ -9,6 +9,12 @@
#ifndef _TTY_H
#define _TTY_H
#define MAX_CONSOLES 8
#define NR_SERIALS 2
#define NR_PTYS 4
extern int NR_CONSOLES;
#include <termios.h>
#define TTY_BUF_SIZE 1024
......@@ -21,17 +27,24 @@ struct tty_queue {
char buf[TTY_BUF_SIZE];
};
#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00)
#define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40)
#define IS_A_PTY(min) ((min) & 0x80)
#define IS_A_PTY_MASTER(min) (((min) & 0xC0) == 0x80)
#define IS_A_PTY_SLAVE(min) (((min) & 0xC0) == 0xC0)
#define PTY_OTHER(min) ((min) ^ 0x40)
#define INC(a) ((a) = ((a)+1) & (TTY_BUF_SIZE-1))
#define DEC(a) ((a) = ((a)-1) & (TTY_BUF_SIZE-1))
#define EMPTY(a) ((a).head == (a).tail)
#define LEFT(a) (((a).tail-(a).head-1)&(TTY_BUF_SIZE-1))
#define LAST(a) ((a).buf[(TTY_BUF_SIZE-1)&((a).head-1)])
#define EMPTY(a) ((a)->head == (a)->tail)
#define LEFT(a) (((a)->tail-(a)->head-1)&(TTY_BUF_SIZE-1))
#define LAST(a) ((a)->buf[(TTY_BUF_SIZE-1)&((a)->head-1)])
#define FULL(a) (!LEFT(a))
#define CHARS(a) (((a).head-(a).tail)&(TTY_BUF_SIZE-1))
#define CHARS(a) (((a)->head-(a)->tail)&(TTY_BUF_SIZE-1))
#define GETCH(queue,c) \
(void)({c=(queue).buf[(queue).tail];INC((queue).tail);})
(void)({c=(queue)->buf[(queue)->tail];INC((queue)->tail);})
#define PUTCH(c,queue) \
(void)({(queue).buf[(queue).head]=(c);INC((queue).head);})
(void)({(queue)->buf[(queue)->head]=(c);INC((queue)->head);})
#define INTR_CHAR(tty) ((tty)->termios.c_cc[VINTR])
#define QUIT_CHAR(tty) ((tty)->termios.c_cc[VQUIT])
......@@ -45,14 +58,19 @@ struct tty_queue {
struct tty_struct {
struct termios termios;
int pgrp;
int session;
int stopped;
void (*write)(struct tty_struct * tty);
struct tty_queue read_q;
struct tty_queue write_q;
struct tty_queue secondary;
struct tty_queue *read_q;
struct tty_queue *write_q;
struct tty_queue *secondary;
};
extern struct tty_struct tty_table[];
extern int fg_console;
#define TTY_TABLE(nr) \
(tty_table + ((nr) ? (((nr) < 64)? (nr)-1:(nr)) : fg_console))
/* intr=^C quit=^| erase=del kill=^U
eof=^D vtime=\0 vmin=\1 sxtc=\0
......@@ -69,9 +87,13 @@ void tty_init(void);
int tty_read(unsigned c, char * buf, int n);
int tty_write(unsigned c, char * buf, int n);
void rs_write(struct tty_struct * tty);
void con_write(struct tty_struct * tty);
void rs_write(struct tty_struct * tty);
void mpty_write(struct tty_struct * tty);
void spty_write(struct tty_struct * tty);
void copy_to_cooked(struct tty_struct * tty);
void update_screen(void);
#endif
......@@ -35,6 +35,7 @@ typedef unsigned int sigset_t; /* 32 bits */
/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
#define SA_NOCLDSTOP 1
#define SA_INTERRUPT 0x20000000
#define SA_NOMASK 0x40000000
#define SA_ONESHOT 0x80000000
......@@ -44,6 +45,12 @@ typedef unsigned int sigset_t; /* 32 bits */
#define SIG_DFL ((void (*)(int))0) /* default signal handling */
#define SIG_IGN ((void (*)(int))1) /* ignore signal */
#define SIG_ERR ((void (*)(int))-1) /* error return from signal */
#ifdef notdef
#define sigemptyset(mask) ((*(mask) = 0), 1)
#define sigfillset(mask) ((*(mask) = ~0), 1)
#endif
struct sigaction {
void (*sa_handler)(int);
......
......@@ -15,5 +15,4 @@ typedef unsigned long size_t;
#define NULL ((void *)0)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#ifndef _SYS_PARAM_H
#define _SYS_PARAM_H
#define HZ 100
#define EXEC_PAGESIZE 4096
#define NGROUPS 32 /* Max number of groups per user */
#define NOGROUP -1
#define MAXHOSTNAMELEN 8
#endif
/*
* Resource control/accounting header file for linux
*/
#ifndef _SYS_RESOURCE_H
#define _SYS_RESOURCE_H
/*
* Definition of struct rusage taken from BSD 4.3 Reno
*
* We don't support all of these yet, but we might as well have them....
* Otherwise, each time we add new items, programs which depend on this
* structure will lose. This reduces the chances of that happening.
*/
#define RUSAGE_SELF 0
#define RUSAGE_CHILDREN -1
struct rusage {
struct timeval ru_utime; /* user time used */
struct timeval ru_stime; /* system time used */
long ru_maxrss; /* maximum resident set size */
long ru_ixrss; /* integral shared memory size */
long ru_idrss; /* integral unshared data size */
long ru_isrss; /* integral unshared stack size */
long ru_minflt; /* page reclaims */
long ru_majflt; /* page faults */
long ru_nswap; /* swaps */
long ru_inblock; /* block input operations */
long ru_oublock; /* block output operations */
long ru_msgsnd; /* messages sent */
long ru_msgrcv; /* messages received */
long ru_nsignals; /* signals received */
long ru_nvcsw; /* voluntary context switches */
long ru_nivcsw; /* involuntary " */
};
/*
* Resource limits
*/
#define RLIMIT_CPU 0 /* CPU time in ms */
#define RLIMIT_FSIZE 1 /* Maximum filesize */
#define RLIMIT_DATA 2 /* max data size */
#define RLIMIT_STACK 3 /* max stack size */
#define RLIMIT_CORE 4 /* max core file size */
#define RLIMIT_RSS 5 /* max resident set size */
#ifdef notdef
#define RLIMIT_MEMLOCK 6 /* max locked-in-memory address space*/
#define RLIMIT_NPROC 7 /* max number of processes */
#define RLIMIT_OFILE 8 /* max number of open files */
#endif
#define RLIM_NLIMITS 6
#define RLIM_INFINITY 0x7fffffff
struct rlimit {
int rlim_cur;
int rlim_max;
};
#endif /* _SYS_RESOURCE_H */
......@@ -18,6 +18,7 @@ struct stat {
};
#define S_IFMT 00170000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
......@@ -27,6 +28,7 @@ struct stat {
#define S_ISGID 0002000
#define S_ISVTX 0001000
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
......
#ifndef _SYS_TIME_H
#define _SYS_TIME_H
/* gettimofday returns this */
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
#define DST_NONE 0 /* not on dst */
#define DST_USA 1 /* USA style dst */
#define DST_AUST 2 /* Australian style dst */
#define DST_WET 3 /* Western European dst */
#define DST_MET 4 /* Middle European dst */
#define DST_EET 5 /* Eastern European dst */
#define DST_CAN 6 /* Canada */
#define DST_GB 7 /* Great Britain and Eire */
#define DST_RUM 8 /* Rumania */
#define DST_TUR 9 /* Turkey */
#define DST_AUSTALT 10 /* Australian style with shift in 1986 */
#define FD_SET(fd,fdsetp) (*(fdsetp) |= (1 << (fd)))
#define FD_CLR(fd,fdsetp) (*(fdsetp) &= ~(1 << (fd)))
#define FD_ISSET(fd,fdsetp) ((*(fdsetp) >> fd) & 1)
#define FD_ZERO(fdsetp) (*(fdsetp) = 0)
/*
* Operations on timevals.
*
* NB: timercmp does not work for >= or <=.
*/
#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timercmp(tvp, uvp, cmp) \
((tvp)->tv_sec cmp (uvp)->tv_sec || \
(tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec)
#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
/*
* Names of the interval timers, and structure
* defining a timer setting.
*/
#define ITIMER_REAL 0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF 2
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
#include <time.h>
#include <sys/types.h>
int gettimeofday(struct timeval * tp, struct timezone * tz);
int select(int width, fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval * timeout);
#endif /*_SYS_TIME_H*/
......@@ -22,7 +22,7 @@ typedef long ptrdiff_t;
typedef int pid_t;
typedef unsigned short uid_t;
typedef unsigned char gid_t;
typedef unsigned short gid_t;
typedef unsigned short dev_t;
typedef unsigned short ino_t;
typedef unsigned short mode_t;
......@@ -33,6 +33,12 @@ typedef long off_t;
typedef unsigned char u_char;
typedef unsigned short ushort;
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned long tcflag_t;
typedef unsigned long fd_set;
typedef struct { int quot,rem; } div_t;
typedef struct { long quot,rem; } ldiv_t;
......
......@@ -2,10 +2,11 @@
#define _SYS_UTSNAME_H
#include <sys/types.h>
#include <sys/param.h>
struct utsname {
char sysname[9];
char nodename[9];
char nodename[MAXHOSTNAMELEN+1];
char release[9];
char version[9];
char machine[9];
......
......@@ -10,10 +10,11 @@
#define WNOHANG 1
#define WUNTRACED 2
#define WIFEXITED(s) (!((s)&0xFF)
#define WIFEXITED(s) (!((s)&0xFF))
#define WIFSTOPPED(s) (((s)&0xFF)==0x7F)
#define WEXITSTATUS(s) (((s)>>8)&0xFF)
#define WTERMSIG(s) ((s)&0x7F)
#define WCOREDUMP(s) ((s)&0x80)
#define WSTOPSIG(s) (((s)>>8)&0xFF)
#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF)
......
#ifndef _TERMIOS_H
#define _TERMIOS_H
#include <sys/types.h>
#define TTY_BUF_SIZE 1024
/* 0x54 is just a magic number to make these relatively uniqe ('T') */
......@@ -31,7 +33,8 @@
#define TIOCMSET 0x5418
#define TIOCGSOFTCAR 0x5419
#define TIOCSSOFTCAR 0x541A
#define TIOCINQ 0x541B
#define FIONREAD 0x541B
#define TIOCINQ FIONREAD
struct winsize {
unsigned short ws_row;
......@@ -52,12 +55,12 @@ struct termio {
#define NCCS 17
struct termios {
unsigned long c_iflag; /* input mode flags */
unsigned long c_oflag; /* output mode flags */
unsigned long c_cflag; /* control mode flags */
unsigned long c_lflag; /* local mode flags */
unsigned char c_line; /* line discipline */
unsigned char c_cc[NCCS]; /* control characters */
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
};
/* c_cc characters */
......@@ -155,16 +158,13 @@ struct termios {
#define CS8 0000060
#define CSTOPB 0000100
#define CREAD 0000200
#define CPARENB 0000400
#define CPARODD 0001000
#define PARENB 0000400
#define PARODD 0001000
#define HUPCL 0002000
#define CLOCAL 0004000
#define CIBAUD 03600000 /* input baud rate (not used) */
#define CRTSCTS 020000000000 /* flow control */
#define PARENB CPARENB
#define PARODD CPARODD
/* c_lflag bits */
#define ISIG 0000001
#define ICANON 0000002
......@@ -211,8 +211,6 @@ struct termios {
#define TCSADRAIN 1
#define TCSAFLUSH 2
typedef int speed_t;
extern speed_t cfgetispeed(struct termios *termios_p);
extern speed_t cfgetospeed(struct termios *termios_p);
extern int cfsetispeed(struct termios *termios_p, speed_t speed);
......
......@@ -11,6 +11,10 @@ typedef long time_t;
typedef unsigned int size_t;
#endif
#ifndef NULL
#define NULL ((void *) 0)
#endif
#define CLOCKS_PER_SEC 100
typedef long clock_t;
......@@ -27,6 +31,9 @@ struct tm {
int tm_isdst;
};
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 1000 == 0))
clock_t clock(void);
time_t time(time_t * tp);
double difftime(time_t time2, time_t time1);
......
......@@ -7,8 +7,8 @@
#define _POSIX_CHOWN_RESTRICTED /* only root can do a chown (I think..) */
#define _POSIX_NO_TRUNC /* no pathname truncation (but see in kernel) */
#define _POSIX_VDISABLE '\0' /* character to disable things like ^C */
/*#define _POSIX_SAVED_IDS */ /* we'll get to this yet */
/*#define _POSIX_JOB_CONTROL */ /* we aren't there quite yet. Soon hopefully */
#define _POSIX_JOB_CONTROL
#define _POSIX_SAVED_IDS /* Implemented, for whatever good it is */
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
......@@ -51,8 +51,10 @@
#define _PC_CHOWN_RESTRICTED 9
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/utsname.h>
#include <sys/resource.h>
#include <utime.h>
#ifdef __LIBRARY__
......@@ -129,6 +131,21 @@
#define __NR_ssetmask 69
#define __NR_setreuid 70
#define __NR_setregid 71
#define __NR_sigsuspend 72
#define __NR_sigpending 73
#define __NR_sethostname 74
#define __NR_setrlimit 75
#define __NR_getrlimit 76
#define __NR_getrusage 77
#define __NR_gettimeofday 78
#define __NR_settimeofday 79
#define __NR_getgroups 80
#define __NR_setgroups 81
#define __NR_select 82
#define __NR_symlink 83
#define __NR_lstat 84
#define __NR_readlink 85
#define __NR_uselib 86
#define _syscall0(type,name) \
type name(void) \
......@@ -249,5 +266,15 @@ int dup2(int oldfd, int newfd);
int getppid(void);
pid_t getpgrp(void);
pid_t setsid(void);
int sethostname(char *name, int len);
int setrlimit(int resource, struct rlimit *rlp);
int getrlimit(int resource, struct rlimit *rlp);
int getrusage(int who, struct rusage *rusage);
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(struct timeval *tv, struct timezone *tz);
int getgroups(int gidsetlen, gid_t *gidset);
int setgroups(int gidsetlen, gid_t *gidset);
int select(int width, fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval * timeout);
#endif
......@@ -39,8 +39,11 @@ static inline _syscall0(int,sync)
#include <linux/fs.h>
#include <string.h>
static char printbuf[1024];
extern char *strcpy();
extern int vsprintf();
extern void init(void);
extern void blk_dev_init(void);
......@@ -50,14 +53,27 @@ extern void floppy_init(void);
extern void mem_init(long start, long end);
extern long rd_init(long mem_start, int length);
extern long kernel_mktime(struct tm * tm);
extern long startup_time;
static int sprintf(char * str, const char *fmt, ...)
{
va_list args;
int i;
va_start(args, fmt);
i = vsprintf(str, fmt, args);
va_end(args);
return i;
}
/*
* This is set up by the setup-routine at boot-time
*/
#define EXT_MEM_K (*(unsigned short *)0x90002)
#define CON_ROWS ((*(unsigned short *)0x9000e) & 0xff)
#define CON_COLS (((*(unsigned short *)0x9000e) & 0xff00) >> 8)
#define DRIVE_INFO (*(struct drive_info *)0x90080)
#define ORIG_ROOT_DEV (*(unsigned short *)0x901FC)
#define ORIG_SWAP_DEV (*(unsigned short *)0x901FA)
/*
* Yeah, yeah, it's ugly, but I cannot find how to do this correctly
......@@ -98,6 +114,13 @@ static void time_init(void)
static long memory_end = 0;
static long buffer_memory_end = 0;
static long main_memory_start = 0;
static char term[32];
static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL ,NULL };
static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL, NULL };
struct drive_info { char dummy[32]; } drive_info;
......@@ -108,6 +131,10 @@ void main(void) /* This really IS void, no error here. */
* enable them
*/
ROOT_DEV = ORIG_ROOT_DEV;
SWAP_DEV = ORIG_SWAP_DEV;
sprintf(term, "TERM=con%dx%d", CON_COLS, CON_ROWS);
envp[1] = term;
envp_rc[1] = term;
drive_info = DRIVE_INFO;
memory_end = (1<<20) + (EXT_MEM_K<<10);
memory_end &= 0xfffff000;
......@@ -145,7 +172,8 @@ void main(void) /* This really IS void, no error here. */
* can run). For task0 'pause()' just means we go check if some other
* task can run, and if not we return here.
*/
for(;;) pause();
for(;;)
__asm__("int $0x80"::"a" (__NR_pause):"ax");
}
static int printf(const char *fmt, ...)
......@@ -159,18 +187,12 @@ static int printf(const char *fmt, ...)
return i;
}
static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", NULL };
static char * argv[] = { "-/bin/sh",NULL };
static char * envp[] = { "HOME=/usr/root", NULL };
void init(void)
{
int pid,i;
setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);
(void) open("/dev/tty1",O_RDWR,0);
(void) dup(0);
(void) dup(0);
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
......@@ -194,7 +216,7 @@ void init(void)
if (!pid) {
close(0);close(1);close(2);
setsid();
(void) open("/dev/tty0",O_RDWR,0);
(void) open("/dev/tty1",O_RDWR,0);
(void) dup(0);
(void) dup(0);
_exit(execve("/bin/sh",argv,envp));
......
.file "init/main.c"
gcc_compiled.:
.text
LC0:
.ascii "out of memory\12\15\0"
.align 2
_sprintf:
movl 4(%esp),%edx
leal 12(%esp),%eax
pushl %eax
pushl 12(%esp)
pushl %edx
call _vsprintf
addl $12,%esp
ret
.align 2
_time_init:
pushl %ebp
movl %esp,%ebp
subl $44,%esp
L65:
movl $128,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
movl %eax,-36(%ebp)
movl $130,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
movl %eax,-32(%ebp)
movl $132,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
movl %eax,-28(%ebp)
movl $135,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
movl %eax,-24(%ebp)
movl $136,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
movl %eax,-20(%ebp)
movl $137,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
movl %eax,-16(%ebp)
movl $128,%eax
movl $112,%edx
/APP
outb %al,%dx
jmp 1f
1: jmp 1f
1:
/NO_APP
movl $113,%edx
/APP
inb %dx,%al
jmp 1f
1: jmp 1f
1:
/NO_APP
movb %al,-40(%ebp)
movzbl -40(%ebp),%eax
cmpl -36(%ebp),%eax
jne L65
movl -36(%ebp),%eax
andl $15,%eax
movl -36(%ebp),%edx
sarl $4,%edx
leal (%edx,%edx,4),%edx
leal (%eax,%edx,2),%eax
movl %eax,-36(%ebp)
movl -32(%ebp),%eax
andl $15,%eax
movl -32(%ebp),%edx
sarl $4,%edx
leal (%edx,%edx,4),%edx
leal (%eax,%edx,2),%eax
movl %eax,-32(%ebp)
movl -28(%ebp),%eax
andl $15,%eax
movl -28(%ebp),%edx
sarl $4,%edx
leal (%edx,%edx,4),%edx
leal (%eax,%edx,2),%eax
movl %eax,-28(%ebp)
movl -24(%ebp),%eax
andl $15,%eax
movl -24(%ebp),%edx
sarl $4,%edx
leal (%edx,%edx,4),%edx
leal (%eax,%edx,2),%eax
movl %eax,-24(%ebp)
movl -20(%ebp),%eax
andl $15,%eax
movl -20(%ebp),%edx
sarl $4,%edx
leal (%edx,%edx,4),%edx
leal (%eax,%edx,2),%eax
movl %eax,-20(%ebp)
movl -16(%ebp),%eax
andl $15,%eax
movl -16(%ebp),%edx
sarl $4,%edx
leal (%edx,%edx,4),%edx
leal (%eax,%edx,2),%eax
movl %eax,-16(%ebp)
decl -20(%ebp)
leal -36(%ebp),%eax
pushl %eax
call _kernel_mktime
movl %eax,_startup_time
leave
ret
.data
.align 2
_memory_end:
.long 0
.align 2
_buffer_memory_end:
.long 0
.align 2
_main_memory_start:
.long 0
.text
LC1:
.ascii "/bin/sh\0"
.data
.align 2
_argv_rc:
.long LC1
.long 0
.text
LC2:
.ascii "HOME=/\0"
.data
.align 2
_envp_rc:
.long LC2
.long 0
.long 0
.text
LC3:
.ascii "-/bin/sh\0"
.data
.align 2
_argv:
.long LC3
.long 0
.text
LC4:
.ascii "HOME=/usr/root\0"
.data
.align 2
_envp:
.long LC4
.long 0
.long 0
.text
LC5:
.ascii "TERM=con%dx%d\0"
.align 2
.globl _main
_main:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
pushl %edi
pushl %esi
movzwl 590332,%eax
movl %eax,_ROOT_DEV
movzwl 590330,%eax
movl %eax,_SWAP_DEV
movw 589838,%dx
andl $255,%edx
pushl %edx
movw 589838,%ax
andw $65280,%ax
shrw $8,%ax
movw %ax,-4(%ebp)
movzwl -4(%ebp),%eax
pushl %eax
pushl $LC5
pushl $_term
call _sprintf
movl $_term,_envp+4
movl $_term,_envp_rc+4
movl $_drive_info,%edi
movl $589952,%esi
movl $8,%ecx
cld
rep
movsl
movzwl 589826,%eax
sall $10,%eax
addl $1048576,%eax
movl %eax,_memory_end
andl $-4096,_memory_end
addl $16,%esp
cmpl $16777216,_memory_end
jle L69
movl $16777216,_memory_end
L69:
cmpl $12582912,_memory_end
jle L70
movl $4194304,_buffer_memory_end
jmp L71
.align 2
L70:
cmpl $6291456,_memory_end
jle L72
movl $2097152,_buffer_memory_end
jmp L71
.align 2
L72:
movl $1048576,_buffer_memory_end
L71:
movl _buffer_memory_end,%eax
movl %eax,_main_memory_start
pushl _memory_end
pushl _buffer_memory_end
call _mem_init
call _trap_init
call _blk_dev_init
call _chr_dev_init
call _tty_init
call _time_init
call _sched_init
pushl _buffer_memory_end
call _buffer_init
call _hd_init
call _floppy_init
/APP
sti
movl %esp,%eax
pushl $0x17
pushl %eax
pushfl
pushl $0x0f
pushl $1f
iret
1: movl $0x17,%eax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
/NO_APP
addl $12,%esp
movl $2,%eax
/APP
int $0x80
/NO_APP
movl %eax,%edx
testl %edx,%edx
jge L75
negl %edx
movl %edx,_errno
movl $-1,%edx
L75:
testl %edx,%edx
jne L74
call _init
L74:
L77:
movl $29,%eax
/APP
int $0x80
/NO_APP
jmp L77
.align 2
leal -16(%ebp),%esp
popl %esi
popl %edi
leave
ret
.align 2
_printf:
pushl %ebx
leal 12(%esp),%eax
pushl %eax
pushl 12(%esp)
pushl $_printbuf
call _vsprintf
movl %eax,%ebx
pushl %ebx
pushl $_printbuf
pushl $1
call _write
movl %ebx,%eax
addl $24,%esp
popl %ebx
ret
LC6:
.ascii "/dev/tty1\0"
LC7:
.ascii "%d buffers = %d bytes buffer space\12\15\0"
LC8:
.ascii "Free mem: %d bytes\12\15\0"
LC9:
.ascii "/etc/rc\0"
LC10:
.ascii "Fork failed in init\15\12\0"
LC11:
.ascii "\12\15child %d died with code %04x\12\15\0"
.align 2
.globl _init
_init:
pushl %ebp
movl %esp,%ebp
subl $4,%esp
pushl %edi
pushl %esi
pushl %ebx
xorl %eax,%eax
movl $_drive_info,%ebx
/APP
int $0x80
/NO_APP
testl %eax,%eax
jge L82
negl %eax
movl %eax,_errno
L82:
pushl $0
pushl $2
pushl $LC6
call _open
pushl $0
call _dup
pushl $0
call _dup
movl _nr_buffers,%eax
sall $10,%eax
pushl %eax
pushl _nr_buffers
pushl $LC7
call _printf
addl $32,%esp
movl _memory_end,%eax
subl _main_memory_start,%eax
pushl %eax
pushl $LC8
call _printf
addl $8,%esp
movl $2,%eax
/APP
int $0x80
/NO_APP
testl %eax,%eax
jl L86
movl %eax,%edi
jmp L85
.align 2
L86:
negl %eax
movl %eax,_errno
movl $-1,%edi
L85:
testl %edi,%edi
jne L84
pushl $0
call _close
pushl $0
pushl $0
pushl $LC9
call _open
addl $16,%esp
testl %eax,%eax
je L87
pushl $1
call __exit
.align 2
L87:
pushl $_envp_rc
pushl $_argv_rc
pushl $LC1
call _execve
pushl $2
call __exit
.align 2
L84:
testl %edi,%edi
jle L88
leal -4(%ebp),%esi
L89:
pushl %esi
call _wait
addl $4,%esp
cmpl %edi,%eax
jne L89
L88:
leal -4(%ebp),%esi
L91:
movl $2,%eax
/APP
int $0x80
/NO_APP
testl %eax,%eax
jge L94
negl %eax
movl %eax,_errno
movl $-1,%eax
L94:
movl %eax,%edi
testl %edi,%edi
jge L93
pushl $LC10
call _printf
addl $4,%esp
jmp L91
.align 2
L93:
testl %edi,%edi
jne L96
pushl $0
call _close
pushl $1
call _close
pushl $2
call _close
call _setsid
pushl $0
pushl $2
pushl $LC6
call _open
pushl $0
call _dup
pushl $0
call _dup
addl $32,%esp
pushl $_envp
pushl $_argv
pushl $LC1
call _execve
pushl %eax
call __exit
.align 2
L96:
L97:
pushl %esi
call _wait
addl $4,%esp
cmpl %edi,%eax
jne L97
pushl -4(%ebp)
pushl %edi
pushl $LC11
call _printf
addl $12,%esp
movl $36,%eax
/APP
int $0x80
/NO_APP
testl %eax,%eax
jge L91
negl %eax
movl %eax,_errno
jmp L91
.align 2
leal -16(%ebp),%esp
popl %ebx
popl %esi
popl %edi
leave
ret
.comm _drive_info,32
.lcomm _term,32
.lcomm _printbuf,1024
......@@ -24,7 +24,7 @@ CPP =gcc -E -nostdinc -I../include
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = sched.o system_call.o traps.o asm.o fork.o \
OBJS = sched.o sys_call.o traps.o asm.o fork.o \
panic.o printk.o vsprintf.o sys.o exit.o \
signal.o mktime.o
......@@ -51,33 +51,43 @@ dep:
exit.s exit.o : exit.c ../include/errno.h ../include/signal.h \
../include/sys/types.h ../include/sys/wait.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
../include/linux/kernel.h ../include/linux/tty.h ../include/termios.h \
../include/asm/segment.h
../include/linux/kernel.h ../include/sys/param.h ../include/sys/time.h \
../include/time.h ../include/sys/resource.h ../include/linux/tty.h \
../include/termios.h ../include/asm/segment.h
fork.s fork.o : fork.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h ../include/asm/system.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/segment.h ../include/asm/system.h
mktime.s mktime.o : mktime.c ../include/time.h
panic.s panic.o : panic.c ../include/linux/kernel.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h
../include/linux/mm.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h
printk.s printk.o : printk.c ../include/stdarg.h ../include/stddef.h \
../include/linux/kernel.h
sched.s sched.o : sched.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/signal.h ../include/linux/kernel.h ../include/linux/sys.h \
../include/linux/fdreg.h ../include/asm/system.h ../include/asm/io.h \
../include/asm/segment.h
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/linux/sys.h ../include/linux/fdreg.h ../include/asm/system.h \
../include/asm/io.h ../include/asm/segment.h
signal.s signal.o : signal.c ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
../include/signal.h ../include/linux/kernel.h ../include/asm/segment.h
../include/linux/kernel.h ../include/signal.h ../include/sys/param.h \
../include/sys/time.h ../include/time.h ../include/sys/resource.h \
../include/asm/segment.h ../include/errno.h
sys.s sys.o : sys.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/tty.h \
../include/termios.h ../include/linux/kernel.h ../include/asm/segment.h \
../include/sys/times.h ../include/sys/utsname.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/linux/tty.h ../include/termios.h \
../include/linux/config.h ../include/asm/segment.h ../include/sys/times.h \
../include/sys/utsname.h ../include/string.h
traps.s traps.o : traps.c ../include/string.h ../include/linux/head.h \
../include/linux/sched.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/system.h ../include/asm/segment.h ../include/asm/io.h
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h ../include/asm/system.h ../include/asm/segment.h \
../include/asm/io.h
vsprintf.s vsprintf.o : vsprintf.c ../include/stdarg.h ../include/string.h
......@@ -15,6 +15,7 @@
.globl _double_fault,_coprocessor_segment_overrun
.globl _invalid_TSS,_segment_not_present,_stack_segment
.globl _general_protection,_coprocessor_error,_irq13,_reserved
.globl _alignment_check
_divide_error:
pushl $_do_divide_error
......@@ -144,3 +145,7 @@ _general_protection:
pushl $_do_general_protection
jmp error_code
_alignment_check:
pushl $_do_alignment_check
jmp error_code
......@@ -43,16 +43,29 @@ dep:
### Dependencies:
floppy.s floppy.o : floppy.c ../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/signal.h ../../include/linux/kernel.h \
../../include/linux/fdreg.h ../../include/asm/system.h \
../../include/asm/io.h ../../include/asm/segment.h blk.h
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/fdreg.h \
../../include/asm/system.h ../../include/asm/io.h \
../../include/asm/segment.h blk.h
hd.s hd.o : hd.c ../../include/linux/config.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \
../../include/linux/kernel.h ../../include/linux/hdreg.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/hdreg.h \
../../include/asm/system.h ../../include/asm/io.h \
../../include/asm/segment.h blk.h
ll_rw_blk.s ll_rw_blk.o : ll_rw_blk.c ../../include/errno.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \
../../include/linux/kernel.h ../../include/asm/system.h blk.h
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/asm/system.h blk.h
ramdisk.s ramdisk.o : ramdisk.c ../../include/string.h ../../include/linux/config.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/asm/system.h \
../../include/asm/segment.h ../../include/asm/memory.h blk.h
......@@ -51,6 +51,8 @@ extern struct blk_dev_struct blk_dev[NR_BLK_DEV];
extern struct request request[NR_REQUEST];
extern struct task_struct * wait_for_request;
extern int * blk_size[NR_BLK_DEV];
#ifdef MAJOR_NR
/*
......@@ -79,6 +81,7 @@ extern struct task_struct * wait_for_request;
/* harddisk */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_TIMEOUT hd_timeout
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)/5)
#define DEVICE_ON(device)
......@@ -96,6 +99,12 @@ extern struct task_struct * wait_for_request;
#ifdef DEVICE_INTR
void (*DEVICE_INTR)(void) = NULL;
#endif
#ifdef DEVICE_TIMEOUT
int DEVICE_TIMEOUT = 0;
#define SET_INTR(x) (DEVICE_INTR = (x),DEVICE_TIMEOUT = 200)
#else
#define SET_INTR(x) (DEVICE_INTR = (x))
#endif
static void (DEVICE_REQUEST)(void);
extern inline void unlock_buffer(struct buffer_head * bh)
......@@ -124,10 +133,25 @@ extern inline void end_request(int uptodate)
CURRENT = CURRENT->next;
}
#ifdef DEVICE_TIMEOUT
#define CLEAR_DEVICE_TIMEOUT DEVICE_TIMEOUT = 0;
#else
#define CLEAR_DEVICE_TIMEOUT
#endif
#ifdef DEVICE_INTR
#define CLEAR_DEVICE_INTR DEVICE_INTR = 0;
#else
#define CLEAR_DEVICE_INTR
#endif
#define INIT_REQUEST \
repeat: \
if (!CURRENT) \
if (!CURRENT) {\
CLEAR_DEVICE_INTR \
CLEAR_DEVICE_TIMEOUT \
return; \
} \
if (MAJOR(CURRENT->dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
......
......@@ -92,6 +92,7 @@ static struct floppy_struct {
{ 1440, 9,2,80,0,0x23,0x01,0xDF }, /* 720kB in 1.2MB drive */
{ 2880,18,2,80,0,0x1B,0x00,0xCF }, /* 1.44MB diskette */
};
/*
* Rate is 0 for 500kb/s, 2 for 300kbps, 1 for 250kbps
* Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
......@@ -141,7 +142,7 @@ int floppy_change(unsigned int nr)
repeat:
floppy_on(nr);
while ((current_DOR & 3) != nr && selected)
interruptible_sleep_on(&wait_on_floppy_select);
sleep_on(&wait_on_floppy_select);
if ((current_DOR & 3) != nr)
goto repeat;
if (inb(FD_DIR) & 0x80) {
......@@ -454,8 +455,20 @@ void do_fd_request(void)
add_timer(ticks_to_floppy_on(current_drive),&floppy_on_interrupt);
}
static int floppy_sizes[] ={
0, 0, 0, 0,
360, 360 ,360, 360,
1200,1200,1200,1200,
360, 360, 360, 360,
720, 720, 720, 720,
360, 360, 360, 360,
720, 720, 720, 720,
1440,1440,1440,1440
};
void floppy_init(void)
{
blk_size[MAJOR_NR] = floppy_sizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
set_trap_gate(0x26,&floppy_interrupt);
outb(inb_p(0x21)&~0x40,0x21);
......
......@@ -36,8 +36,8 @@ inb_p(0x71); \
static void recal_intr(void);
static int recalibrate = 1;
static int reset = 1;
static int recalibrate = 0;
static int reset = 0;
/*
* This struct defines the HD's and their types.
......@@ -58,6 +58,8 @@ static struct hd_struct {
long nr_sects;
} hd[5*MAX_HD]={{0,0},};
static int hd_sizes[5*MAX_HD] = {0, };
#define port_read(port,buf,nr) \
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
......@@ -151,16 +153,20 @@ int sys_setup(void * BIOS)
}
brelse(bh);
}
for (i=0 ; i<5*MAX_HD ; i++)
hd_sizes[i] = hd[i].nr_sects>>1 ;
blk_size[MAJOR_NR] = hd_sizes;
if (NR_HD)
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
rd_load();
init_swapping();
mount_root();
return (0);
}
static int controller_ready(void)
{
int retries=10000;
int retries = 100000;
while (--retries && (inb_p(HD_STATUS)&0xc0)!=0x40);
return (retries);
......@@ -187,7 +193,7 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
panic("Trying to write bad sector");
if (!controller_ready())
panic("HD controller not ready");
do_hd = intr_addr;
SET_INTR(intr_addr);
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
......@@ -202,14 +208,14 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
static int drive_busy(void)
{
unsigned int i;
unsigned char c;
for (i = 0; i < 10000; i++)
if (READY_STAT == (inb_p(HD_STATUS) & (BUSY_STAT|READY_STAT)))
break;
i = inb(HD_STATUS);
i &= BUSY_STAT | READY_STAT | SEEK_STAT;
if (i == READY_STAT | SEEK_STAT)
return(0);
for (i = 0; i < 50000; i++) {
c = inb_p(HD_STATUS);
c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
if (c == (READY_STAT | SEEK_STAT))
return 0;
}
printk("HD controller times out\n\r");
return(1);
}
......@@ -219,7 +225,7 @@ static void reset_controller(void)
int i;
outb(4,HD_CMD);
for(i = 0; i < 100; i++) nop();
for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
if (drive_busy())
printk("HD-controller still busy\n\r");
......@@ -259,7 +265,7 @@ static void read_intr(void)
CURRENT->buffer += 512;
CURRENT->sector++;
if (--CURRENT->nr_sectors) {
do_hd = &read_intr;
SET_INTR(&read_intr);
return;
}
end_request(1);
......@@ -276,7 +282,7 @@ static void write_intr(void)
if (--CURRENT->nr_sectors) {
CURRENT->sector++;
CURRENT->buffer += 512;
do_hd = &write_intr;
SET_INTR(&write_intr);
port_write(HD_DATA,CURRENT->buffer,256);
return;
}
......@@ -291,6 +297,14 @@ static void recal_intr(void)
do_hd_request();
}
void hd_times_out(void)
{
printk("HD timeout");
SET_INTR(NULL);
reset = 1;
do_hd_request();
}
void do_hd_request(void)
{
int i,r;
......@@ -327,7 +341,7 @@ void do_hd_request(void)
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
/* nothing */ ;
if (!r) {
bad_rw_intr();
......
......@@ -39,6 +39,15 @@ struct blk_dev_struct blk_dev[NR_BLK_DEV] = {
{ NULL, NULL } /* dev lp */
};
/*
* blk_size contains the size of all block-devices:
*
* blk_size[MAJOR][MINOR]
*
* if (!blk_size[MAJOR]) then no minor size checking is done.
*/
int * blk_size[NR_BLK_DEV] = { NULL, NULL, };
static inline void lock_buffer(struct buffer_head * bh)
{
cli();
......@@ -60,6 +69,9 @@ static inline void unlock_buffer(struct buffer_head * bh)
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*
* Note that swapping requests always go before other requests,
* and are done in the order they appear.
*/
static void add_request(struct blk_dev_struct * dev, struct request * req)
{
......@@ -75,11 +87,17 @@ static void add_request(struct blk_dev_struct * dev, struct request * req)
(dev->request_fn)();
return;
}
for ( ; tmp->next ; tmp=tmp->next)
for ( ; tmp->next ; tmp=tmp->next) {
if (!req->bh)
if (tmp->next->bh)
break;
else
continue;
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
}
req->next=tmp->next;
tmp->next=req;
sti();
......@@ -142,6 +160,41 @@ static void make_request(int major,int rw, struct buffer_head * bh)
add_request(major+blk_dev,req);
}
void ll_rw_page(int rw, int dev, int page, char * buffer)
{
struct request * req;
unsigned int major = MAJOR(dev);
if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) {
printk("Trying to read nonexistent block-device\n\r");
return;
}
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W");
repeat:
req = request+NR_REQUEST;
while (--req >= request)
if (req->dev<0)
break;
if (req < request) {
sleep_on(&wait_for_request);
goto repeat;
}
/* fill up the request-info, and add it to the queue */
req->dev = dev;
req->cmd = rw;
req->errors = 0;
req->sector = page<<3;
req->nr_sectors = 8;
req->buffer = buffer;
req->waiting = current;
req->bh = NULL;
req->next = NULL;
current->state = TASK_UNINTERRUPTIBLE;
add_request(major+blk_dev,req);
schedule();
}
void ll_rw_block(int rw, struct buffer_head * bh)
{
unsigned int major;
......
......@@ -25,13 +25,13 @@ CPP =gcc -E -nostdinc -I../../include
-c -o $*.o $<
OBJS = tty_io.o console.o keyboard.o serial.o rs_io.o \
tty_ioctl.o
tty_ioctl.o pty.o
chr_drv.a: $(OBJS)
$(AR) rcs chr_drv.a $(OBJS)
sync
keyboard.s: keyboard.S ../../include/linux/config.h
keyboard.s: keyboard.S
$(CPP) -traditional keyboard.S -o keyboard.s
clean:
......@@ -47,22 +47,42 @@ dep:
### Dependencies:
console.s console.o : console.c ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h ../../include/signal.h \
../../include/linux/tty.h ../../include/termios.h ../../include/asm/io.h \
../../include/asm/system.h
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/sys/param.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/resource.h ../../include/linux/tty.h \
../../include/termios.h ../../include/linux/config.h ../../include/asm/io.h \
../../include/asm/system.h ../../include/asm/segment.h \
../../include/string.h ../../include/errno.h
pty.s pty.o : pty.c ../../include/linux/tty.h ../../include/termios.h \
../../include/sys/types.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/resource.h \
../../include/asm/system.h ../../include/asm/io.h
serial.s serial.o : serial.c ../../include/linux/tty.h ../../include/termios.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/signal.h ../../include/asm/system.h ../../include/asm/io.h
../../include/sys/types.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/resource.h \
../../include/asm/system.h ../../include/asm/io.h
tty_io.s tty_io.o : tty_io.c ../../include/ctype.h ../../include/errno.h \
../../include/signal.h ../../include/sys/types.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/linux/mm.h ../../include/linux/tty.h \
../../include/termios.h ../../include/asm/segment.h \
../../include/asm/system.h
tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/signal.h ../../include/linux/kernel.h \
../../include/linux/tty.h ../../include/asm/io.h \
../../include/signal.h ../../include/sys/types.h ../../include/unistd.h \
../../include/sys/stat.h ../../include/sys/time.h ../../include/time.h \
../../include/sys/times.h ../../include/sys/utsname.h \
../../include/sys/param.h ../../include/sys/resource.h \
../../include/utime.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/linux/tty.h ../../include/termios.h \
../../include/asm/segment.h ../../include/asm/system.h
tty_ioctl.s tty_ioctl.o : tty_ioctl.c ../../include/errno.h ../../include/termios.h \
../../include/sys/types.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/signal.h ../../include/sys/param.h ../../include/sys/time.h \
../../include/time.h ../../include/sys/resource.h ../../include/linux/tty.h \
../../include/asm/io.h ../../include/asm/segment.h \
../../include/asm/system.h
......@@ -13,6 +13,9 @@
* Hopefully this will be a rather complete VT102 implementation.
*
* Beeping thanks to John T Kohl.
*
* Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics
* Chars, and VT100 enhancements by Peter MacDonald.
*/
/*
......@@ -29,8 +32,26 @@
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <string.h>
#include <errno.h>
#define DEF_TERMIOS \
(struct termios) { \
ICRNL, \
OPOST | ONLCR, \
0, \
IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE, \
0, \
INIT_C_CC \
}
/*
* These are set up by the setup-routine at boot-time:
......@@ -41,7 +62,7 @@
#define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004)
#define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff)
#define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8)
#define ORIG_VIDEO_LINES (25)
#define ORIG_VIDEO_LINES ((*(unsigned short *)0x9000e) & 0xff)
#define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008)
#define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a)
#define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c)
......@@ -53,28 +74,69 @@
#define NPAR 16
int NR_CONSOLES = 0;
extern void keyboard_interrupt(void);
static unsigned char video_type; /* Type of display being used */
static unsigned long video_num_columns; /* Number of text columns */
static unsigned long video_mem_base; /* Base of video memory */
static unsigned long video_mem_term; /* End of video memory */
static unsigned long video_size_row; /* Bytes per row */
static unsigned long video_num_lines; /* Number of test lines */
static unsigned char video_page; /* Initial video page */
static unsigned long video_mem_start; /* Start of video RAM */
static unsigned long video_mem_end; /* End of video RAM (sort of) */
static unsigned short video_port_reg; /* Video register select port */
static unsigned short video_port_val; /* Video register value port */
static unsigned short video_erase_char; /* Char+Attrib to erase with */
static unsigned long origin; /* Used for EGA/VGA fast scroll */
static unsigned long scr_end; /* Used for EGA/VGA fast scroll */
static unsigned long pos;
static unsigned long x,y;
static unsigned long top,bottom;
static unsigned long state=0;
static unsigned long npar,par[NPAR];
static unsigned long ques=0;
static unsigned char attr=0x07;
static int can_do_colour = 0;
static struct {
unsigned short vc_video_erase_char;
unsigned char vc_attr;
unsigned char vc_def_attr;
int vc_bold_attr;
unsigned long vc_ques;
unsigned long vc_state;
unsigned long vc_restate;
unsigned long vc_checkin;
unsigned long vc_origin; /* Used for EGA/VGA fast scroll */
unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */
unsigned long vc_pos;
unsigned long vc_x,vc_y;
unsigned long vc_top,vc_bottom;
unsigned long vc_npar,vc_par[NPAR];
unsigned long vc_video_mem_start; /* Start of video RAM */
unsigned long vc_video_mem_end; /* End of video RAM (sort of) */
unsigned int vc_saved_x;
unsigned int vc_saved_y;
unsigned int vc_iscolor;
char * vc_translate;
} vc_cons [MAX_CONSOLES];
#define origin (vc_cons[currcons].vc_origin)
#define scr_end (vc_cons[currcons].vc_scr_end)
#define pos (vc_cons[currcons].vc_pos)
#define top (vc_cons[currcons].vc_top)
#define bottom (vc_cons[currcons].vc_bottom)
#define x (vc_cons[currcons].vc_x)
#define y (vc_cons[currcons].vc_y)
#define state (vc_cons[currcons].vc_state)
#define restate (vc_cons[currcons].vc_restate)
#define checkin (vc_cons[currcons].vc_checkin)
#define npar (vc_cons[currcons].vc_npar)
#define par (vc_cons[currcons].vc_par)
#define ques (vc_cons[currcons].vc_ques)
#define attr (vc_cons[currcons].vc_attr)
#define saved_x (vc_cons[currcons].vc_saved_x)
#define saved_y (vc_cons[currcons].vc_saved_y)
#define translate (vc_cons[currcons].vc_translate)
#define video_mem_start (vc_cons[currcons].vc_video_mem_start)
#define video_mem_end (vc_cons[currcons].vc_video_mem_end)
#define def_attr (vc_cons[currcons].vc_def_attr)
#define video_erase_char (vc_cons[currcons].vc_video_erase_char)
#define iscolor (vc_cons[currcons].vc_iscolor)
int blankinterval = 0;
int blankcount = 0;
static void sysbeep(void);
......@@ -84,28 +146,49 @@ static void sysbeep(void);
*/
#define RESPONSE "\033[?1;2c"
static char * translations[] = {
/* normal 7-bit ascii */
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~ ",
/* vt100 graphics */
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ "
"\004\261\007\007\007\007\370\361\007\007\275\267\326\323\327\304"
"\304\304\304\304\307\266\320\322\272\363\362\343\\007\234\007 "
};
#define NORM_TRANS (translations[0])
#define GRAF_TRANS (translations[1])
/* NOTE! gotoxy thinks x==video_num_columns is ok */
static inline void gotoxy(unsigned int new_x,unsigned int new_y)
static inline void gotoxy(int currcons, int new_x,unsigned int new_y)
{
if (new_x > video_num_columns || new_y >= video_num_lines)
return;
x=new_x;
y=new_y;
pos=origin + y*video_size_row + (x<<1);
x = new_x;
y = new_y;
pos = origin + y*video_size_row + (x<<1);
}
static inline void set_origin(void)
static inline void set_origin(int currcons)
{
if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
return;
if (currcons != fg_console)
return;
cli();
outb_p(12, video_port_reg);
outb_p(0xff&((origin-video_mem_start)>>9), video_port_val);
outb_p(0xff&((origin-video_mem_base)>>9), video_port_val);
outb_p(13, video_port_reg);
outb_p(0xff&((origin-video_mem_start)>>1), video_port_val);
outb_p(0xff&((origin-video_mem_base)>>1), video_port_val);
sti();
}
static void scrup(void)
static void scrup(int currcons)
{
if (bottom<=top)
return;
if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)
{
if (!top && bottom == video_num_lines) {
......@@ -136,7 +219,7 @@ static void scrup(void)
"D" (scr_end-video_size_row)
:"cx","di");
}
set_origin();
set_origin(currcons);
} else {
__asm__("cld\n\t"
"rep\n\t"
......@@ -167,8 +250,10 @@ static void scrup(void)
}
}
static void scrdown(void)
static void scrdown(int currcons)
{
if (bottom <= top)
return;
if (video_type == VIDEO_TYPE_EGAC || video_type == VIDEO_TYPE_EGAM)
{
__asm__("std\n\t"
......@@ -201,33 +286,33 @@ static void scrdown(void)
}
}
static void lf(void)
static void lf(int currcons)
{
if (y+1<bottom) {
y++;
pos += video_size_row;
return;
}
scrup();
scrup(currcons);
}
static void ri(void)
static void ri(int currcons)
{
if (y>top) {
y--;
pos -= video_size_row;
return;
}
scrdown();
scrdown(currcons);
}
static void cr(void)
static void cr(int currcons)
{
pos -= x<<1;
x=0;
}
static void del(void)
static void del(int currcons)
{
if (x) {
pos -= 2;
......@@ -236,12 +321,12 @@ static void del(void)
}
}
static void csi_J(int par)
static void csi_J(int currcons, int vpar)
{
long count __asm__("cx");
long start __asm__("di");
switch (par) {
switch (vpar) {
case 0: /* erase from cursor to end of display */
count = (scr_end-pos)>>1;
start = pos;
......@@ -265,12 +350,12 @@ static void csi_J(int par)
:"cx","di");
}
static void csi_K(int par)
static void csi_K(int currcons, int vpar)
{
long count __asm__("cx");
long start __asm__("di");
switch (par) {
switch (vpar) {
case 0: /* erase from cursor to end of line */
if (x>=video_num_columns)
return;
......@@ -296,31 +381,74 @@ static void csi_K(int par)
:"cx","di");
}
void csi_m(void)
void csi_m(int currcons )
{
int i;
for (i=0;i<=npar;i++)
switch (par[i]) {
case 0:attr=0x07;break;
case 1:attr=0x0f;break;
case 4:attr=0x0f;break;
case 7:attr=0x70;break;
case 27:attr=0x07;break;
case 0: attr=def_attr;break; /* default */
case 1: attr=(iscolor?attr|0x08:attr|0x0f);break; /* bold */
/*case 4: attr=attr|0x01;break;*/ /* underline */
case 4: /* bold */
if (!iscolor)
attr |= 0x01;
else
{ /* check if forground == background */
if (vc_cons[currcons].vc_bold_attr != -1)
attr = (vc_cons[currcons].vc_bold_attr&0x0f)|(0xf0&(attr));
else
{ short newattr = (attr&0xf0)|(0xf&(~attr));
attr = ((newattr&0xf)==((attr>>4)&0xf)?
(attr&0xf0)|(((attr&0xf)+1)%0xf):
newattr);
}
}
break;
case 5: attr=attr|0x80;break; /* blinking */
case 7: attr=(attr<<4)|(attr>>4);break; /* negative */
case 22: attr=attr&0xf7;break; /* not bold */
case 24: attr=attr&0xfe;break; /* not underline */
case 25: attr=attr&0x7f;break; /* not blinking */
case 27: attr=def_attr;break; /* positive image */
case 39: attr=(attr & 0xf0)|(def_attr & 0x0f); break;
case 49: attr=(attr & 0x0f)|(def_attr & 0xf0); break;
default:
if (!can_do_colour)
break;
iscolor = 1;
if ((par[i]>=30) && (par[i]<=38))
attr = (attr & 0xf0) | (par[i]-30);
else /* Background color */
if ((par[i]>=40) && (par[i]<=48))
attr = (attr & 0x0f) | ((par[i]-40)<<4);
else
break;
}
}
static inline void set_cursor(void)
static inline void set_cursor(int currcons)
{
blankcount = blankinterval;
if (currcons != fg_console)
return;
cli();
outb_p(14, video_port_reg);
outb_p(0xff&((pos-video_mem_start)>>9), video_port_val);
outb_p(0xff&((pos-video_mem_base)>>9), video_port_val);
outb_p(15, video_port_reg);
outb_p(0xff&((pos-video_mem_start)>>1), video_port_val);
outb_p(0xff&((pos-video_mem_base)>>1), video_port_val);
sti();
}
static void respond(struct tty_struct * tty)
static inline void hide_cursor(int currcons)
{
outb_p(14, video_port_reg);
outb_p(0xff&((scr_end-video_mem_base)>>9), video_port_val);
outb_p(15, video_port_reg);
outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val);
}
static void respond(int currcons, struct tty_struct * tty)
{
char * p = RESPONSE;
......@@ -333,7 +461,7 @@ static void respond(struct tty_struct * tty)
copy_to_cooked(tty);
}
static void insert_char(void)
static void insert_char(int currcons)
{
int i=x;
unsigned short tmp, old = video_erase_char;
......@@ -347,7 +475,7 @@ static void insert_char(void)
}
}
static void insert_line(void)
static void insert_line(int currcons)
{
int oldtop,oldbottom;
......@@ -355,12 +483,12 @@ static void insert_line(void)
oldbottom=bottom;
top=y;
bottom = video_num_lines;
scrdown();
scrdown(currcons);
top=oldtop;
bottom=oldbottom;
}
static void delete_char(void)
static void delete_char(int currcons)
{
int i;
unsigned short * p = (unsigned short *) pos;
......@@ -375,7 +503,7 @@ static void delete_char(void)
*p = video_erase_char;
}
static void delete_line(void)
static void delete_line(int currcons)
{
int oldtop,oldbottom;
......@@ -383,95 +511,107 @@ static void delete_line(void)
oldbottom=bottom;
top=y;
bottom = video_num_lines;
scrup();
scrup(currcons);
top=oldtop;
bottom=oldbottom;
}
static void csi_at(unsigned int nr)
static void csi_at(int currcons, unsigned int nr)
{
if (nr > video_num_columns)
nr = video_num_columns;
else if (!nr)
nr = 1;
while (nr--)
insert_char();
insert_char(currcons);
}
static void csi_L(unsigned int nr)
static void csi_L(int currcons, unsigned int nr)
{
if (nr > video_num_lines)
nr = video_num_lines;
else if (!nr)
nr = 1;
while (nr--)
insert_line();
insert_line(currcons);
}
static void csi_P(unsigned int nr)
static void csi_P(int currcons, unsigned int nr)
{
if (nr > video_num_columns)
nr = video_num_columns;
else if (!nr)
nr = 1;
while (nr--)
delete_char();
delete_char(currcons);
}
static void csi_M(unsigned int nr)
static void csi_M(int currcons, unsigned int nr)
{
if (nr > video_num_lines)
nr = video_num_lines;
else if (!nr)
nr=1;
while (nr--)
delete_line();
delete_line(currcons);
}
static int saved_x=0;
static int saved_y=0;
static void save_cur(void)
static void save_cur(int currcons)
{
saved_x=x;
saved_y=y;
}
static void restore_cur(void)
static void restore_cur(int currcons)
{
gotoxy(saved_x, saved_y);
gotoxy(currcons,saved_x, saved_y);
}
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
ESsetterm, ESsetgraph };
void con_write(struct tty_struct * tty)
{
int nr;
char c;
int currcons;
currcons = tty - tty_table;
if ((currcons>=MAX_CONSOLES) || (currcons<0))
panic("con_write: illegal tty");
nr = CHARS(tty->write_q);
while (nr--) {
if (tty->stopped)
break;
GETCH(tty->write_q,c);
if (c == 24 || c == 26)
state = ESnormal;
switch(state) {
case 0:
case ESnormal:
if (c>31 && c<127) {
if (x>=video_num_columns) {
x -= video_num_columns;
pos -= video_size_row;
lf();
lf(currcons);
}
__asm__("movb _attr,%%ah\n\t"
__asm__("movb %2,%%ah\n\t"
"movw %%ax,%1\n\t"
::"a" (c),"m" (*(short *)pos)
::"a" (translate[c-32]),
"m" (*(short *)pos),
"m" (attr)
:"ax");
pos += 2;
x++;
} else if (c==27)
state=1;
state=ESesc;
else if (c==10 || c==11 || c==12)
lf();
lf(currcons);
else if (c==13)
cr();
cr(currcons);
else if (c==ERASE_CHAR(tty))
del();
del(currcons);
else if (c==8) {
if (x) {
x--;
......@@ -484,104 +624,144 @@ void con_write(struct tty_struct * tty)
if (x>video_num_columns) {
x -= video_num_columns;
pos -= video_size_row;
lf();
lf(currcons);
}
c=9;
} else if (c==7)
sysbeep();
else if (c == 14)
translate = GRAF_TRANS;
else if (c == 15)
translate = NORM_TRANS;
break;
case 1:
state=0;
if (c=='[')
state=2;
else if (c=='E')
gotoxy(0,y+1);
else if (c=='M')
ri();
else if (c=='D')
lf();
else if (c=='Z')
respond(tty);
else if (x=='7')
save_cur();
else if (x=='8')
restore_cur();
case ESesc:
state = ESnormal;
switch (c)
{
case '[':
state=ESsquare;
break;
case 'E':
gotoxy(currcons,0,y+1);
break;
case 'M':
ri(currcons);
break;
case 'D':
lf(currcons);
break;
case 'Z':
respond(currcons,tty);
break;
case '7':
save_cur(currcons);
break;
case '8':
restore_cur(currcons);
break;
case '(': case ')':
state = ESsetgraph;
break;
case 'P':
state = ESsetterm;
break;
case '#':
state = -1;
break;
case 'c':
tty->termios = DEF_TERMIOS;
state = restate = ESnormal;
checkin = 0;
top = 0;
bottom = video_num_lines;
break;
/* case '>': Numeric keypad */
/* case '=': Appl. keypad */
}
break;
case 2:
case ESsquare:
for(npar=0;npar<NPAR;npar++)
par[npar]=0;
npar=0;
state=3;
state=ESgetpars;
if (c =='[') /* Function key */
{ state=ESfunckey;
break;
}
if (ques=(c=='?'))
break;
case 3:
case ESgetpars:
if (c==';' && npar<NPAR-1) {
npar++;
break;
} else if (c>='0' && c<='9') {
par[npar]=10*par[npar]+c-'0';
break;
} else state=4;
case 4:
state=0;
} else state=ESgotpars;
case ESgotpars:
state = ESnormal;
if (ques)
{ ques =0;
break;
}
switch(c) {
case 'G': case '`':
if (par[0]) par[0]--;
gotoxy(par[0],y);
gotoxy(currcons,par[0],y);
break;
case 'A':
if (!par[0]) par[0]++;
gotoxy(x,y-par[0]);
gotoxy(currcons,x,y-par[0]);
break;
case 'B': case 'e':
if (!par[0]) par[0]++;
gotoxy(x,y+par[0]);
gotoxy(currcons,x,y+par[0]);
break;
case 'C': case 'a':
if (!par[0]) par[0]++;
gotoxy(x+par[0],y);
gotoxy(currcons,x+par[0],y);
break;
case 'D':
if (!par[0]) par[0]++;
gotoxy(x-par[0],y);
gotoxy(currcons,x-par[0],y);
break;
case 'E':
if (!par[0]) par[0]++;
gotoxy(0,y+par[0]);
gotoxy(currcons,0,y+par[0]);
break;
case 'F':
if (!par[0]) par[0]++;
gotoxy(0,y-par[0]);
gotoxy(currcons,0,y-par[0]);
break;
case 'd':
if (par[0]) par[0]--;
gotoxy(x,par[0]);
gotoxy(currcons,x,par[0]);
break;
case 'H': case 'f':
if (par[0]) par[0]--;
if (par[1]) par[1]--;
gotoxy(par[1],par[0]);
gotoxy(currcons,par[1],par[0]);
break;
case 'J':
csi_J(par[0]);
csi_J(currcons,par[0]);
break;
case 'K':
csi_K(par[0]);
csi_K(currcons,par[0]);
break;
case 'L':
csi_L(par[0]);
csi_L(currcons,par[0]);
break;
case 'M':
csi_M(par[0]);
csi_M(currcons,par[0]);
break;
case 'P':
csi_P(par[0]);
csi_P(currcons,par[0]);
break;
case '@':
csi_at(par[0]);
csi_at(currcons,par[0]);
break;
case 'm':
csi_m();
csi_m(currcons);
break;
case 'r':
if (par[0]) par[0]--;
......@@ -593,15 +773,52 @@ void con_write(struct tty_struct * tty)
}
break;
case 's':
save_cur();
save_cur(currcons);
break;
case 'u':
restore_cur();
restore_cur(currcons);
break;
case 'l': /* blank interval */
case 'b': /* bold attribute */
if (!((npar >= 2) &&
((par[1]-13) == par[0]) &&
((par[2]-17) == par[0])))
break;
if ((c=='l')&&(par[0]>=0)&&(par[0]<=60))
{
blankinterval = HZ*60*par[0];
blankcount = blankinterval;
}
if (c=='b')
vc_cons[currcons].vc_bold_attr
= par[0];
}
break;
case ESfunckey:
state = ESnormal;
break;
case ESsetterm: /* Setterm functions. */
state = ESnormal;
if (c == 'S') {
def_attr = attr;
video_erase_char = (video_erase_char&0x0ff) | (def_attr<<8);
} else if (c == 'L')
; /*linewrap on*/
else if (c == 'l')
; /*linewrap off*/
break;
case ESsetgraph:
state = ESnormal;
if (c == '0')
translate = GRAF_TRANS;
else if (c == 'B')
translate = NORM_TRANS;
break;
default:
state = ESnormal;
}
}
set_cursor();
set_cursor(currcons);
}
/*
......@@ -619,53 +836,65 @@ void con_init(void)
register unsigned char a;
char *display_desc = "????";
char *display_ptr;
int currcons = 0;
long base, term;
long video_memory;
video_num_columns = ORIG_VIDEO_COLS;
video_size_row = video_num_columns * 2;
video_num_lines = ORIG_VIDEO_LINES;
video_page = ORIG_VIDEO_PAGE;
video_erase_char = 0x0720;
blankcount = blankinterval;
if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */
{
video_mem_start = 0xb0000;
video_mem_base = 0xb0000;
video_port_reg = 0x3b4;
video_port_val = 0x3b5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAM;
video_mem_end = 0xb8000;
video_mem_term = 0xb8000;
display_desc = "EGAm";
}
else
{
video_type = VIDEO_TYPE_MDA;
video_mem_end = 0xb2000;
video_mem_term = 0xb2000;
display_desc = "*MDA";
}
}
else /* If not, it is color. */
else /* If not, it is color. */
{
video_mem_start = 0xb8000;
can_do_colour = 1;
video_mem_base = 0xb8000;
video_port_reg = 0x3d4;
video_port_val = 0x3d5;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
{
video_type = VIDEO_TYPE_EGAC;
video_mem_end = 0xbc000;
video_mem_term = 0xc0000;
display_desc = "EGAc";
}
else
{
video_type = VIDEO_TYPE_CGA;
video_mem_end = 0xba000;
video_mem_term = 0xba000;
display_desc = "*CGA";
}
}
video_memory = video_mem_term - video_mem_base;
NR_CONSOLES = video_memory / (video_num_lines * video_size_row);
if (NR_CONSOLES > MAX_CONSOLES)
NR_CONSOLES = MAX_CONSOLES;
if (!NR_CONSOLES)
NR_CONSOLES = 1;
video_memory /= NR_CONSOLES;
/* Let the user known what kind of display driver we are using */
display_ptr = ((char *)video_mem_start) + video_size_row - 8;
display_ptr = ((char *)video_mem_base) + video_size_row - 8;
while (*display_desc)
{
*display_ptr++ = *display_desc++;
......@@ -674,18 +903,42 @@ void con_init(void)
/* Initialize the variables used for scrolling (mostly EGA/VGA) */
origin = video_mem_start;
base = origin = video_mem_start = video_mem_base;
term = video_mem_end = base + video_memory;
scr_end = video_mem_start + video_num_lines * video_size_row;
top = 0;
bottom = video_num_lines;
attr = 0x07;
def_attr = 0x07;
restate = state = ESnormal;
checkin = 0;
ques = 0;
iscolor = 0;
translate = NORM_TRANS;
vc_cons[0].vc_bold_attr = -1;
gotoxy(ORIG_X,ORIG_Y);
gotoxy(currcons,ORIG_X,ORIG_Y);
for (currcons = 1; currcons<NR_CONSOLES; currcons++) {
vc_cons[currcons] = vc_cons[0];
origin = video_mem_start = (base += video_memory);
scr_end = origin + video_num_lines * video_size_row;
video_mem_end = (term += video_memory);
gotoxy(currcons,0,0);
}
update_screen();
set_trap_gate(0x21,&keyboard_interrupt);
outb_p(inb_p(0x21)&0xfd,0x21);
a=inb_p(0x61);
outb_p(a|0x80,0x61);
outb(a,0x61);
outb_p(a,0x61);
}
void update_screen(void)
{
set_origin(fg_console);
set_cursor(fg_console);
}
/* from bsd-net-2: */
void sysbeepstop(void)
......@@ -708,3 +961,65 @@ static void sysbeep(void)
/* 1/8 second */
beepcount = HZ/8;
}
int do_screendump(int arg)
{
char *sptr, *buf = (char *)arg;
int currcons, l;
verify_area(buf,video_num_columns*video_num_lines);
currcons = get_fs_byte(buf);
if ((currcons<1) || (currcons>NR_CONSOLES))
return -EIO;
currcons--;
sptr = (char *) origin;
for (l=video_num_lines*video_num_columns; l>0 ; l--)
put_fs_byte(*sptr++,buf++);
return(0);
}
void blank_screen()
{
if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
return;
/* blank here. I can't find out how to do it, though */
}
void unblank_screen()
{
if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM)
return;
/* unblank here */
}
void console_print(const char * b)
{
int currcons = fg_console;
char c;
while (c = *(b++)) {
if (c == 10) {
cr(currcons);
lf(currcons);
continue;
}
if (c == 13) {
cr(currcons);
continue;
}
if (x>=video_num_columns) {
x -= video_num_columns;
pos -= video_size_row;
lf(currcons);
}
__asm__("movb %2,%%ah\n\t"
"movw %%ax,%1\n\t"
::"a" (c),
"m" (*(short *)pos),
"m" (attr)
:"ax");
pos += 2;
x++;
}
set_cursor(currcons);
}
......@@ -10,7 +10,12 @@
* Marc Corsini for the French keyboard
*/
#include <linux/config.h>
/* KBD_FINNISH for Finnish keyboards
* KBD_US for US-type
* KBD_GR for German keyboards
* KBD_FR for Frech keyboard
*/
#define KBD_FINNISH
.text
.globl _keyboard_interrupt
......@@ -44,7 +49,9 @@ _keyboard_interrupt:
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
xorl %al,%al /* %eax is scan code */
movl _blankinterval,%eax
movl %eax,_blankcount
xorl %eax,%eax /* %eax is scan code */
inb $0x60,%al
cmpb $0xe0,%al
je set_e0
......@@ -155,7 +162,12 @@ set_leds:
uncaps: andb $0x7f,mode
ret
scroll:
xorb $1,leds
testb $0x03,mode
je 1f
call _show_mem
jmp 2f
1: call _show_state
2: xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
......@@ -208,13 +220,6 @@ cur_table:
* this routine handles function keys
*/
func:
pushl %eax
pushl %ecx
pushl %edx
call _show_stat
popl %edx
popl %ecx
popl %eax
subb $0x3B,%al
jb end_func
cmpb $9,%al
......@@ -225,11 +230,17 @@ func:
cmpb $11,%al
ja end_func
ok_func:
testb $0x10,mode
jne alt_func
cmpl $4,%ecx /* check that there is enough room */
jl end_func
movl func_table(,%eax,4),%eax
xorl %ebx,%ebx
jmp put_queue
alt_func:
pushl %eax
call _change_console
popl %eax
end_func:
ret
......
/*
* linux/kernel/chr_drv/pty.c
*
* (C) 1991 Linus Torvalds
*/
/*
* pty.c
*
* This module implements the pty functions
* void mpty_write(struct tty_struct * queue);
* void spty_write(struct tty_struct * queue);
*/
#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/io.h>
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
{
char c;
while (!from->stopped && !EMPTY(from->write_q)) {
if (FULL(to->read_q)) {
if (FULL(to->secondary))
break;
copy_to_cooked(to);
continue;
}
GETCH(from->write_q,c);
PUTCH(c,to->read_q);
if (current->signal & ~current->blocked)
break;
}
copy_to_cooked(to);
wake_up(&from->write_q->proc_list);
}
/*
* This routine gets called when tty_write has put something into
* the write_queue. It copies the input to the output-queue of it's
* slave.
*/
void mpty_write(struct tty_struct * tty)
{
int nr = tty - tty_table;
if ((nr >> 6) != 2)
printk("bad mpty\n\r");
else
pty_copy(tty,tty+64);
}
void spty_write(struct tty_struct * tty)
{
int nr = tty - tty_table;
if ((nr >> 6) != 3)
printk("bad spty\n\r");
else
pty_copy(tty,tty-64);
}
......@@ -105,7 +105,8 @@ read_char:
cmpl tail(%ecx),%ebx
je 1f
movl %ebx,head(%ecx)
1: pushl %edx
1: addl $63,%edx
pushl %edx
call _do_tty_interrupt
addl $4,%esp
ret
......
......@@ -38,8 +38,8 @@ void rs_init(void)
{
set_intr_gate(0x24,rs1_interrupt);
set_intr_gate(0x23,rs2_interrupt);
init(tty_table[1].read_q.data);
init(tty_table[2].read_q.data);
init(tty_table[64].read_q->data);
init(tty_table[65].read_q->data);
outb(inb_p(0x21)&0xE7,0x21);
}
......@@ -54,6 +54,6 @@ void rs_write(struct tty_struct * tty)
{
cli();
if (!EMPTY(tty->write_q))
outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1);
outb(inb_p(tty->write_q->data+1)|0x02,tty->write_q->data+1);
sti();
}
......@@ -8,23 +8,24 @@
* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
* or rs-channels. It also implements echoing, cooked mode etc.
*
* Kill-line thanks to John T Kohl.
* Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
*/
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#define ALRMMASK (1<<(SIGALRM-1))
#define KILLMASK (1<<(SIGKILL-1))
#define INTMASK (1<<(SIGINT-1))
#define QUITMASK (1<<(SIGQUIT-1))
#define TSTPMASK (1<<(SIGTSTP-1))
#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/segment.h>
#include <asm/system.h>
int kill_pg(int pgrp, int sig, int priv);
int is_orphaned_pgrp(int pgrp);
#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)
#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)
#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)
......@@ -36,11 +37,13 @@
#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
#define I_NLCR(tty) _I_FLAG((tty),INLCR)
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
#define I_NOCR(tty) _I_FLAG((tty),IGNCR)
#define I_IXON(tty) _I_FLAG((tty),IXON)
#define O_POST(tty) _O_FLAG((tty),OPOST)
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
......@@ -48,136 +51,119 @@
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
struct tty_struct tty_table[] = {
{
{ICRNL, /* change incoming CR to NL */
OPOST|ONLCR, /* change outgoing NL to CRNL */
0,
ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
0, /* console termio */
INIT_C_CC},
0, /* initial pgrp */
0, /* initial stopped */
con_write,
{0,0,0,0,""}, /* console read-queue */
{0,0,0,0,""}, /* console write-queue */
{0,0,0,0,""} /* console secondary queue */
},{
{0, /* no translation */
0, /* no translation */
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
rs_write,
{0x3f8,0,0,0,""}, /* rs 1 */
{0x3f8,0,0,0,""},
{0,0,0,0,""}
},{
{0, /* no translation */
0, /* no translation */
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
rs_write,
{0x2f8,0,0,0,""}, /* rs 2 */
{0x2f8,0,0,0,""},
{0,0,0,0,""}
}
};
#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD)
#define C_HUP(tty) (C_SPEED((tty)) == B0)
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#define QUEUES (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS))
static struct tty_queue tty_queues[QUEUES];
struct tty_struct tty_table[256];
#define con_queues tty_queues
#define rs_queues ((3*MAX_CONSOLES) + tty_queues)
#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues)
#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues)
#define con_table tty_table
#define rs_table (64+tty_table)
#define mpty_table (128+tty_table)
#define spty_table (192+tty_table)
int fg_console = 0;
/*
* these are the tables used by the machine code handlers.
* you can implement pseudo-tty's or something by changing
* them. Currently not done.
* you can implement virtual consoles.
*/
struct tty_queue * table_list[]={
&tty_table[0].read_q, &tty_table[0].write_q,
&tty_table[1].read_q, &tty_table[1].write_q,
&tty_table[2].read_q, &tty_table[2].write_q
con_queues + 0, con_queues + 1,
rs_queues + 0, rs_queues + 1,
rs_queues + 3, rs_queues + 4
};
void tty_init(void)
void change_console(unsigned int new_console)
{
rs_init();
con_init();
}
void tty_intr(struct tty_struct * tty, int mask)
{
int i;
if (tty->pgrp <= 0)
if (new_console == fg_console || new_console >= NR_CONSOLES)
return;
for (i=0;i<NR_TASKS;i++)
if (task[i] && task[i]->pgrp==tty->pgrp)
task[i]->signal |= mask;
fg_console = new_console;
table_list[0] = con_queues + 0 + fg_console*3;
table_list[1] = con_queues + 1 + fg_console*3;
update_screen();
}
static void sleep_if_empty(struct tty_queue * queue)
{
cli();
while (!current->signal && EMPTY(*queue))
while (!(current->signal & ~current->blocked) && EMPTY(queue))
interruptible_sleep_on(&queue->proc_list);
sti();
}
static void sleep_if_full(struct tty_queue * queue)
{
if (!FULL(*queue))
if (!FULL(queue))
return;
cli();
while (!current->signal && LEFT(*queue)<128)
while (!(current->signal & ~current->blocked) && LEFT(queue)<128)
interruptible_sleep_on(&queue->proc_list);
sti();
}
void wait_for_keypress(void)
{
sleep_if_empty(&tty_table[0].secondary);
sleep_if_empty(tty_table[fg_console].secondary);
}
void copy_to_cooked(struct tty_struct * tty)
{
signed char c;
while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {
if (!(tty->read_q || tty->write_q || tty->secondary)) {
printk("copy_to_cooked: missing queues\n\r");
return;
}
while (1) {
if (EMPTY(tty->read_q))
break;
if (FULL(tty->secondary))
break;
GETCH(tty->read_q,c);
if (c==13)
if (c==13) {
if (I_CRNL(tty))
c=10;
else if (I_NOCR(tty))
continue;
else ;
else if (c==10 && I_NLCR(tty))
} else if (c==10 && I_NLCR(tty))
c=13;
if (I_UCLC(tty))
c=tolower(c);
if (L_CANON(tty)) {
if (c==KILL_CHAR(tty)) {
if ((KILL_CHAR(tty) != _POSIX_VDISABLE) &&
(c==KILL_CHAR(tty))) {
/* deal with killing the input line */
while(!(EMPTY(tty->secondary) ||
(c=LAST(tty->secondary))==10 ||
c==EOF_CHAR(tty))) {
((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
(c==EOF_CHAR(tty))))) {
if (L_ECHO(tty)) {
if (c<32)
PUTCH(127,tty->write_q);
PUTCH(127,tty->write_q);
tty->write(tty);
}
DEC(tty->secondary.head);
DEC(tty->secondary->head);
}
continue;
}
if (c==ERASE_CHAR(tty)) {
if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) &&
(c==ERASE_CHAR(tty))) {
if (EMPTY(tty->secondary) ||
(c=LAST(tty->secondary))==10 ||
c==EOF_CHAR(tty))
((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
(c==EOF_CHAR(tty))))
continue;
if (L_ECHO(tty)) {
if (c<32)
......@@ -185,30 +171,45 @@ void copy_to_cooked(struct tty_struct * tty)
PUTCH(127,tty->write_q);
tty->write(tty);
}
DEC(tty->secondary.head);
DEC(tty->secondary->head);
continue;
}
if (c==STOP_CHAR(tty)) {
}
if (I_IXON(tty)) {
if ((STOP_CHAR(tty) != _POSIX_VDISABLE) &&
(c==STOP_CHAR(tty))) {
tty->stopped=1;
tty->write(tty);
continue;
}
if (c==START_CHAR(tty)) {
if ((START_CHAR(tty) != _POSIX_VDISABLE) &&
(c==START_CHAR(tty))) {
tty->stopped=0;
tty->write(tty);
continue;
}
}
if (L_ISIG(tty)) {
if (c==INTR_CHAR(tty)) {
tty_intr(tty,INTMASK);
if ((INTR_CHAR(tty) != _POSIX_VDISABLE) &&
(c==INTR_CHAR(tty))) {
kill_pg(tty->pgrp, SIGINT, 1);
continue;
}
if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) &&
(c==QUIT_CHAR(tty))) {
kill_pg(tty->pgrp, SIGQUIT, 1);
continue;
}
if (c==QUIT_CHAR(tty)) {
tty_intr(tty,QUITMASK);
if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) &&
(c==SUSPEND_CHAR(tty))) {
if (!is_orphaned_pgrp(tty->pgrp))
kill_pg(tty->pgrp, SIGTSTP, 1);
continue;
}
}
if (c==10 || c==EOF_CHAR(tty))
tty->secondary.data++;
if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)))
tty->secondary->data++;
if (L_ECHO(tty)) {
if (c==10) {
PUTCH(10,tty->write_q);
......@@ -224,66 +225,114 @@ void copy_to_cooked(struct tty_struct * tty)
}
PUTCH(c,tty->secondary);
}
wake_up(&tty->secondary.proc_list);
wake_up(&tty->secondary->proc_list);
}
/*
* Called when we need to send a SIGTTIN or SIGTTOU to our process
* group
*
* We only request that a system call be restarted if there was if the
* default signal handler is being used. The reason for this is that if
* a job is catching SIGTTIN or SIGTTOU, the signal handler may not want
* the system call to be restarted blindly. If there is no way to reset the
* terminal pgrp back to the current pgrp (perhaps because the controlling
* tty has been released on logout), we don't want to be in an infinite loop
* while restarting the system call, and have it always generate a SIGTTIN
* or SIGTTOU. The default signal handler will cause the process to stop
* thus avoiding the infinite loop problem. Presumably the job-control
* cognizant parent will fix things up before continuging its child process.
*/
int tty_signal(int sig, struct tty_struct *tty)
{
if (is_orphaned_pgrp(current->pgrp))
return -EIO; /* don't stop an orphaned pgrp */
(void) kill_pg(current->pgrp,sig,1);
if ((current->blocked & (1<<(sig-1))) ||
((int) current->sigaction[sig-1].sa_handler == 1))
return -EIO; /* Our signal will be ignored */
else if (current->sigaction[sig-1].sa_handler)
return -EINTR; /* We _will_ be interrupted :-) */
else
return -ERESTARTSYS; /* We _will_ be interrupted :-) */
/* (but restart after we continue) */
}
int tty_read(unsigned channel, char * buf, int nr)
{
struct tty_struct * tty;
struct tty_struct * other_tty = NULL;
char c, * b=buf;
int minimum,time,flag=0;
long oldalarm;
int minimum,time;
if (channel>2 || nr<0) return -1;
tty = &tty_table[channel];
oldalarm = current->alarm;
if (channel > 255)
return -EIO;
tty = TTY_TABLE(channel);
if (!(tty->write_q || tty->read_q || tty->secondary))
return -EIO;
if ((current->tty == channel) && (tty->pgrp != current->pgrp))
return(tty_signal(SIGTTIN, tty));
if (channel & 0x80)
other_tty = tty_table + (channel ^ 0x40);
time = 10L*tty->termios.c_cc[VTIME];
minimum = tty->termios.c_cc[VMIN];
if (time && !minimum) {
minimum=1;
if (flag=(!oldalarm || time+jiffies<oldalarm))
current->alarm = time+jiffies;
if (L_CANON(tty)) {
minimum = nr;
current->timeout = 0xffffffff;
time = 0;
} else if (minimum)
current->timeout = 0xffffffff;
else {
minimum = nr;
if (time)
current->timeout = time + jiffies;
time = 0;
}
if (minimum>nr)
minimum=nr;
minimum = nr;
while (nr>0) {
if (flag && (current->signal & ALRMMASK)) {
current->signal &= ~ALRMMASK;
break;
}
if (current->signal)
break;
if (other_tty)
other_tty->write(other_tty);
cli();
if (EMPTY(tty->secondary) || (L_CANON(tty) &&
!tty->secondary.data && LEFT(tty->secondary)>20)) {
sleep_if_empty(&tty->secondary);
!FULL(tty->read_q) && !tty->secondary->data)) {
if (!current->timeout ||
(current->signal & ~current->blocked)) {
sti();
break;
}
if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty))
break;
interruptible_sleep_on(&tty->secondary->proc_list);
sti();
continue;
}
sti();
do {
GETCH(tty->secondary,c);
if (c==EOF_CHAR(tty) || c==10)
tty->secondary.data--;
if (c==EOF_CHAR(tty) && L_CANON(tty))
return (b-buf);
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)) || c==10)
tty->secondary->data--;
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
c==EOF_CHAR(tty)) && L_CANON(tty))
break;
else {
put_fs_byte(c,b++);
if (!--nr)
break;
}
} while (nr>0 && !EMPTY(tty->secondary));
if (time && !L_CANON(tty))
if (flag=(!oldalarm || time+jiffies<oldalarm))
current->alarm = time+jiffies;
else
current->alarm = oldalarm;
if (L_CANON(tty)) {
if (b-buf)
if (c==10 && L_CANON(tty))
break;
} else if (b-buf >= minimum)
} while (nr>0 && !EMPTY(tty->secondary));
wake_up(&tty->read_q->proc_list);
if (time)
current->timeout = time+jiffies;
if (L_CANON(tty) || b-buf >= minimum)
break;
}
current->alarm = oldalarm;
if (current->signal && !(b-buf))
return -EINTR;
current->timeout = 0;
if ((current->signal & ~current->blocked) && !(b-buf))
return -ERESTARTSYS;
return (b-buf);
}
......@@ -293,11 +342,17 @@ int tty_write(unsigned channel, char * buf, int nr)
struct tty_struct * tty;
char c, *b=buf;
if (channel>2 || nr<0) return -1;
tty = channel + tty_table;
if (channel > 255)
return -EIO;
tty = TTY_TABLE(channel);
if (!(tty->write_q || tty->read_q || tty->secondary))
return -EIO;
if (L_TOSTOP(tty) &&
(current->tty == channel) && (tty->pgrp != current->pgrp))
return(tty_signal(SIGTTOU, tty));
while (nr>0) {
sleep_if_full(&tty->write_q);
if (current->signal)
sleep_if_full(tty->write_q);
if (current->signal & ~current->blocked)
break;
while (nr>0 && !FULL(tty->write_q)) {
c=get_fs_byte(b);
......@@ -341,9 +396,89 @@ int tty_write(unsigned channel, char * buf, int nr)
*/
void do_tty_interrupt(int tty)
{
copy_to_cooked(tty_table+tty);
copy_to_cooked(TTY_TABLE(tty));
}
void chr_dev_init(void)
{
}
void tty_init(void)
{
int i;
for (i=0 ; i < QUEUES ; i++)
tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""};
rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""};
rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""};
rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""};
for (i=0 ; i<256 ; i++) {
tty_table[i] = (struct tty_struct) {
{0, 0, 0, 0, 0, INIT_C_CC},
0, 0, 0, NULL, NULL, NULL, NULL
};
}
con_init();
for (i = 0 ; i<NR_CONSOLES ; i++) {
con_table[i] = (struct tty_struct) {
{ICRNL, /* change incoming CR to NL */
OPOST|ONLCR, /* change outgoing NL to CRNL */
0,
IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
0, /* console termio */
INIT_C_CC},
0, /* initial pgrp */
0, /* initial session */
0, /* initial stopped */
con_write,
con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
};
}
for (i = 0 ; i<NR_SERIALS ; i++) {
rs_table[i] = (struct tty_struct) {
{0, /* no translation */
0, /* no translation */
B2400 | CS8,
0,
0,
INIT_C_CC},
0,
0,
0,
rs_write,
rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
};
}
for (i = 0 ; i<NR_PTYS ; i++) {
mpty_table[i] = (struct tty_struct) {
{0, /* no translation */
0, /* no translation */
B9600 | CS8,
0,
0,
INIT_C_CC},
0,
0,
0,
mpty_write,
mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
};
spty_table[i] = (struct tty_struct) {
{0, /* no translation */
0, /* no translation */
B9600 | CS8,
IXON | ISIG | ICANON,
0,
INIT_C_CC},
0,
0,
0,
spty_write,
spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
};
}
rs_init();
printk("%d virtual consoles\n\r",NR_CONSOLES);
printk("%d pty's\n\r",NR_PTYS);
}
......@@ -15,6 +15,9 @@
#include <asm/segment.h>
#include <asm/system.h>
extern int session_of_pgrp(int pgrp);
extern int tty_signal(int sig, struct tty_struct *tty);
static unsigned short quotient[] = {
0, 2304, 1536, 1047, 857,
768, 576, 384, 192, 96,
......@@ -25,7 +28,7 @@ static void change_speed(struct tty_struct * tty)
{
unsigned short port,quot;
if (!(port = tty->read_q.data))
if (!(port = tty->read_q->data))
return;
quot = quotient[tty->termios.c_cflag & CBAUD];
cli();
......@@ -63,10 +66,19 @@ static int get_termios(struct tty_struct * tty, struct termios * termios)
return 0;
}
static int set_termios(struct tty_struct * tty, struct termios * termios)
static int set_termios(struct tty_struct * tty, struct termios * termios,
int channel)
{
int i;
int i, retsig;
/* If we try to set the state of terminal and we're not in the
foreground, send a SIGTTOU. If the signal is blocked or
ignored, go ahead and perform the operation. POSIX 7.2) */
if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
retsig = tty_signal(SIGTTOU, tty);
if (retsig == -ERESTARTSYS || retsig == -EINTR)
return retsig;
}
for (i=0 ; i< (sizeof (*termios)) ; i++)
((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
change_speed(tty);
......@@ -94,11 +106,17 @@ static int get_termio(struct tty_struct * tty, struct termio * termio)
/*
* This only works as the 386 is low-byt-first
*/
static int set_termio(struct tty_struct * tty, struct termio * termio)
static int set_termio(struct tty_struct * tty, struct termio * termio,
int channel)
{
int i;
int i, retsig;
struct termio tmp_termio;
if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
retsig = tty_signal(SIGTTOU, tty);
if (retsig == -ERESTARTSYS || retsig == -EINTR)
return retsig;
}
for (i=0 ; i< (sizeof (*termio)) ; i++)
((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
*(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
......@@ -115,30 +133,32 @@ static int set_termio(struct tty_struct * tty, struct termio * termio)
int tty_ioctl(int dev, int cmd, int arg)
{
struct tty_struct * tty;
int pgrp;
if (MAJOR(dev) == 5) {
dev=current->tty;
if (dev<0)
panic("tty_ioctl: dev<0");
} else
dev=MINOR(dev);
tty = dev + tty_table;
tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console);
switch (cmd) {
case TCGETS:
return get_termios(tty,(struct termios *) arg);
case TCSETSF:
flush(&tty->read_q); /* fallthrough */
flush(tty->read_q); /* fallthrough */
case TCSETSW:
wait_until_sent(tty); /* fallthrough */
case TCSETS:
return set_termios(tty,(struct termios *) arg);
return set_termios(tty,(struct termios *) arg, dev);
case TCGETA:
return get_termio(tty,(struct termio *) arg);
case TCSETAF:
flush(&tty->read_q); /* fallthrough */
flush(tty->read_q); /* fallthrough */
case TCSETAW:
wait_until_sent(tty); /* fallthrough */
case TCSETA:
return set_termio(tty,(struct termio *) arg);
return set_termio(tty,(struct termio *) arg, dev);
case TCSBRK:
if (!arg) {
wait_until_sent(tty);
......@@ -146,15 +166,33 @@ int tty_ioctl(int dev, int cmd, int arg)
}
return 0;
case TCXONC:
switch (arg) {
case TCOOFF:
tty->stopped = 1;
tty->write(tty);
return 0;
case TCOON:
tty->stopped = 0;
tty->write(tty);
return 0;
case TCIOFF:
if (STOP_CHAR(tty))
PUTCH(STOP_CHAR(tty),tty->write_q);
return 0;
case TCION:
if (START_CHAR(tty))
PUTCH(START_CHAR(tty),tty->write_q);
return 0;
}
return -EINVAL; /* not implemented */
case TCFLSH:
if (arg==0)
flush(&tty->read_q);
flush(tty->read_q);
else if (arg==1)
flush(&tty->write_q);
flush(tty->write_q);
else if (arg==2) {
flush(&tty->read_q);
flush(&tty->write_q);
flush(tty->read_q);
flush(tty->write_q);
} else
return -EINVAL;
return 0;
......@@ -169,7 +207,16 @@ int tty_ioctl(int dev, int cmd, int arg)
put_fs_long(tty->pgrp,(unsigned long *) arg);
return 0;
case TIOCSPGRP:
tty->pgrp=get_fs_long((unsigned long *) arg);
if ((current->tty < 0) ||
(current->tty != dev) ||
(tty->session != current->session))
return -ENOTTY;
pgrp=get_fs_long((unsigned long *) arg);
if (pgrp < 0)
return -EINVAL;
if (session_of_pgrp(pgrp) != current->session)
return -EPERM;
tty->pgrp = pgrp;
return 0;
case TIOCOUTQ:
verify_area((void *) arg,4);
......
......@@ -4,6 +4,8 @@
* (C) 1991 Linus Torvalds
*/
#define DEBUG_PROC_TREE
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
......@@ -22,9 +24,20 @@ void release(struct task_struct * p)
if (!p)
return;
if (p == current) {
printk("task releasing itself\n\r");
return;
}
for (i=1 ; i<NR_TASKS ; i++)
if (task[i]==p) {
task[i]=NULL;
/* Update links */
if (p->p_osptr)
p->p_osptr->p_ysptr = p->p_ysptr;
if (p->p_ysptr)
p->p_ysptr->p_osptr = p->p_osptr;
else
p->p_pptr->p_cptr = p->p_osptr;
free_page((long)p);
schedule();
return;
......@@ -32,149 +45,366 @@ void release(struct task_struct * p)
panic("trying to release non-existent task");
}
#ifdef DEBUG_PROC_TREE
/*
* Check to see if a task_struct pointer is present in the task[] array
* Return 0 if found, and 1 if not found.
*/
int bad_task_ptr(struct task_struct *p)
{
int i;
if (!p)
return 0;
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] == p)
return 0;
return 1;
}
/*
* This routine scans the pid tree and make sure the rep invarient still
* holds. Used for debugging only, since it's very slow....
*
* It looks a lot scarier than it really is.... we're doing nothing more
* than verifying the doubly-linked list foundin p_ysptr and p_osptr,
* and checking it corresponds with the process tree defined by p_cptr and
* p_pptr;
*/
void audit_ptree()
{
int i;
for (i=1 ; i<NR_TASKS ; i++) {
if (!task[i])
continue;
if (bad_task_ptr(task[i]->p_pptr))
printk("Warning, pid %d's parent link is bad\n",
task[i]->pid);
if (bad_task_ptr(task[i]->p_cptr))
printk("Warning, pid %d's child link is bad\n",
task[i]->pid);
if (bad_task_ptr(task[i]->p_ysptr))
printk("Warning, pid %d's ys link is bad\n",
task[i]->pid);
if (bad_task_ptr(task[i]->p_osptr))
printk("Warning, pid %d's os link is bad\n",
task[i]->pid);
if (task[i]->p_pptr == task[i])
printk("Warning, pid %d parent link points to self\n");
if (task[i]->p_cptr == task[i])
printk("Warning, pid %d child link points to self\n");
if (task[i]->p_ysptr == task[i])
printk("Warning, pid %d ys link points to self\n");
if (task[i]->p_osptr == task[i])
printk("Warning, pid %d os link points to self\n");
if (task[i]->p_osptr) {
if (task[i]->p_pptr != task[i]->p_osptr->p_pptr)
printk(
"Warning, pid %d older sibling %d parent is %d\n",
task[i]->pid, task[i]->p_osptr->pid,
task[i]->p_osptr->p_pptr->pid);
if (task[i]->p_osptr->p_ysptr != task[i])
printk(
"Warning, pid %d older sibling %d has mismatched ys link\n",
task[i]->pid, task[i]->p_osptr->pid);
}
if (task[i]->p_ysptr) {
if (task[i]->p_pptr != task[i]->p_ysptr->p_pptr)
printk(
"Warning, pid %d younger sibling %d parent is %d\n",
task[i]->pid, task[i]->p_osptr->pid,
task[i]->p_osptr->p_pptr->pid);
if (task[i]->p_ysptr->p_osptr != task[i])
printk(
"Warning, pid %d younger sibling %d has mismatched os link\n",
task[i]->pid, task[i]->p_ysptr->pid);
}
if (task[i]->p_cptr) {
if (task[i]->p_cptr->p_pptr != task[i])
printk(
"Warning, pid %d youngest child %d has mismatched parent link\n",
task[i]->pid, task[i]->p_cptr->pid);
if (task[i]->p_cptr->p_ysptr)
printk(
"Warning, pid %d youngest child %d has non-NULL ys link\n",
task[i]->pid, task[i]->p_cptr->pid);
}
}
}
#endif /* DEBUG_PROC_TREE */
static inline int send_sig(long sig,struct task_struct * p,int priv)
{
if (!p || sig<1 || sig>32)
if (!p)
return -EINVAL;
if (priv || (current->euid==p->euid) || suser())
p->signal |= (1<<(sig-1));
else
if (!priv && (current->euid!=p->euid) && !suser())
return -EPERM;
if ((sig == SIGKILL) || (sig == SIGCONT)) {
if (p->state == TASK_STOPPED)
p->state = TASK_RUNNING;
p->exit_code = 0;
p->signal &= ~( (1<<(SIGSTOP-1)) | (1<<(SIGTSTP-1)) |
(1<<(SIGTTIN-1)) | (1<<(SIGTTOU-1)) );
}
/* If the signal will be ignored, don't even post it */
if ((int) p->sigaction[sig-1].sa_handler == 1)
return 0;
/* Depends on order SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */
if ((sig >= SIGSTOP) && (sig <= SIGTTOU))
p->signal &= ~(1<<(SIGCONT-1));
/* Actually deliver the signal */
p->signal |= (1<<(sig-1));
return 0;
}
static void kill_session(void)
int session_of_pgrp(int pgrp)
{
struct task_struct **p = NR_TASKS + task;
while (--p > &FIRST_TASK) {
if (*p && (*p)->session == current->session)
(*p)->signal |= 1<<(SIGHUP-1);
}
struct task_struct **p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->pgrp == pgrp)
return((*p)->session);
return -1;
}
int kill_pg(int pgrp, int sig, int priv)
{
struct task_struct **p;
int err,retval = -ESRCH;
int found = 0;
if (sig<1 || sig>32 || pgrp<=0)
return -EINVAL;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->pgrp == pgrp) {
if (sig && (err = send_sig(sig,*p,priv)))
retval = err;
else
found++;
}
return(found ? 0 : retval);
}
int kill_proc(int pid, int sig, int priv)
{
struct task_struct **p;
if (sig<1 || sig>32)
return -EINVAL;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->pid == pid)
return(sig ? send_sig(sig,*p,priv) : 0);
return(-ESRCH);
}
/*
* XXX need to check permissions needed to send signals to process
* groups, etc. etc. kill() permissions semantics are tricky!
* POSIX specifies that kill(-1,sig) is unspecified, but what we have
* is probably wrong. Should make it like BSD or SYSV.
*/
int sys_kill(int pid,int sig)
{
struct task_struct **p = NR_TASKS + task;
int err, retval = 0;
if (!pid) while (--p > &FIRST_TASK) {
if (*p && (*p)->pgrp == current->pid)
if (err=send_sig(sig,*p,1))
retval = err;
} else if (pid>0) while (--p > &FIRST_TASK) {
if (*p && (*p)->pid == pid)
if (err=send_sig(sig,*p,0))
retval = err;
} else if (pid == -1) while (--p > &FIRST_TASK)
if (err = send_sig(sig,*p,0))
retval = err;
else while (--p > &FIRST_TASK)
if (*p && (*p)->pgrp == -pid)
if (!pid)
return(kill_pg(current->pid,sig,0));
if (pid == -1) {
while (--p > &FIRST_TASK)
if (err = send_sig(sig,*p,0))
retval = err;
return retval;
return(retval);
}
if (pid < 0)
return(kill_pg(-pid,sig,0));
/* Normal kill */
return(kill_proc(pid,sig,0));
}
static void tell_father(int pid)
/*
* Determine if a process group is "orphaned", according to the POSIX
* definition in 2.2.2.52. Orphaned process groups are not to be affected
* by terminal-generated stop signals. Newly orphaned process groups are
* to receive a SIGHUP and a SIGCONT.
*
* "I ask you, have you ever known what it is to be an orphan?"
*/
int is_orphaned_pgrp(int pgrp)
{
int i;
struct task_struct **p;
if (pid)
for (i=0;i<NR_TASKS;i++) {
if (!task[i])
continue;
if (task[i]->pid != pid)
continue;
task[i]->signal |= (1<<(SIGCHLD-1));
return;
}
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
printk("BAD BAD - no father found\n\r");
release(current);
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!(*p) ||
((*p)->pgrp != pgrp) ||
((*p)->state == TASK_ZOMBIE) ||
((*p)->p_pptr->pid == 1))
continue;
if (((*p)->p_pptr->pgrp != pgrp) &&
((*p)->p_pptr->session == (*p)->session))
return 0;
}
return(1); /* (sighing) "Often!" */
}
static int has_stopped_jobs(int pgrp)
{
struct task_struct ** p;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if ((*p)->pgrp != pgrp)
continue;
if ((*p)->state == TASK_STOPPED)
return(1);
}
return(0);
}
int do_exit(long code)
volatile void do_exit(long code)
{
struct task_struct *p;
int i;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->father == current->pid) {
task[i]->father = 1;
if (task[i]->state == TASK_ZOMBIE)
/* assumption task[1] is always init */
(void) send_sig(SIGCHLD, task[1], 1);
}
for (i=0 ; i<NR_OPEN ; i++)
if (current->filp[i])
sys_close(i);
iput(current->pwd);
current->pwd=NULL;
current->pwd = NULL;
iput(current->root);
current->root=NULL;
current->root = NULL;
iput(current->executable);
current->executable=NULL;
if (current->leader && current->tty >= 0)
tty_table[current->tty].pgrp = 0;
if (last_task_used_math == current)
last_task_used_math = NULL;
if (current->leader)
kill_session();
current->executable = NULL;
iput(current->library);
current->library = NULL;
current->state = TASK_ZOMBIE;
current->exit_code = code;
tell_father(current->father);
/*
* Check to see if any process groups have become orphaned
* as a result of our exiting, and if they have any stopped
* jobs, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2)
*
* Case i: Our father is in a different pgrp than we are
* and we were the only connection outside, so our pgrp
* is about to become orphaned.
*/
if ((current->p_pptr->pgrp != current->pgrp) &&
(current->p_pptr->session == current->session) &&
is_orphaned_pgrp(current->pgrp) &&
has_stopped_jobs(current->pgrp)) {
kill_pg(current->pgrp,SIGHUP,1);
kill_pg(current->pgrp,SIGCONT,1);
}
/* Let father know we died */
current->p_pptr->signal |= (1<<(SIGCHLD-1));
/*
* This loop does two things:
*
* A. Make init inherit all the child processes
* B. Check to see if any process groups have become orphaned
* as a result of our exiting, and if they have any stopped
* jons, send them a SIGUP and then a SIGCONT. (POSIX 3.2.2.2)
*/
if (p = current->p_cptr) {
while (1) {
p->p_pptr = task[1];
if (p->state == TASK_ZOMBIE)
task[1]->signal |= (1<<(SIGCHLD-1));
/*
* process group orphan check
* Case ii: Our child is in a different pgrp
* than we are, and it was the only connection
* outside, so the child pgrp is now orphaned.
*/
if ((p->pgrp != current->pgrp) &&
(p->session == current->session) &&
is_orphaned_pgrp(p->pgrp) &&
has_stopped_jobs(p->pgrp)) {
kill_pg(p->pgrp,SIGHUP,1);
kill_pg(p->pgrp,SIGCONT,1);
}
if (p->p_osptr) {
p = p->p_osptr;
continue;
}
/*
* This is it; link everything into init's children
* and leave
*/
p->p_osptr = task[1]->p_cptr;
task[1]->p_cptr->p_ysptr = p;
task[1]->p_cptr = current->p_cptr;
current->p_cptr = 0;
break;
}
}
if (current->leader) {
struct task_struct **p;
struct tty_struct *tty;
if (current->tty >= 0) {
tty = TTY_TABLE(current->tty);
if (tty->pgrp>0)
kill_pg(tty->pgrp, SIGHUP, 1);
tty->pgrp = 0;
tty->session = 0;
}
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if ((*p)->session == current->session)
(*p)->tty = -1;
}
if (last_task_used_math == current)
last_task_used_math = NULL;
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
schedule();
return (-1); /* just to suppress warnings */
}
int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
do_exit((error_code&0xff)<<8);
}
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
int flag, code;
struct task_struct ** p;
int flag;
struct task_struct *p;
unsigned long oldblocked;
verify_area(stat_addr,4);
repeat:
flag=0;
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!*p || *p == current)
continue;
if ((*p)->father != current->pid)
continue;
for (p = current->p_cptr ; p ; p = p->p_osptr) {
if (pid>0) {
if ((*p)->pid != pid)
if (p->pid != pid)
continue;
} else if (!pid) {
if ((*p)->pgrp != current->pgrp)
if (p->pgrp != current->pgrp)
continue;
} else if (pid != -1) {
if ((*p)->pgrp != -pid)
if (p->pgrp != -pid)
continue;
}
switch ((*p)->state) {
switch (p->state) {
case TASK_STOPPED:
if (!(options & WUNTRACED))
if (!(options & WUNTRACED) ||
!p->exit_code)
continue;
put_fs_long(0x7f,stat_addr);
return (*p)->pid;
put_fs_long((p->exit_code << 8) | 0x7f,
stat_addr);
p->exit_code = 0;
return p->pid;
case TASK_ZOMBIE:
current->cutime += (*p)->utime;
current->cstime += (*p)->stime;
flag = (*p)->pid;
code = (*p)->exit_code;
release(*p);
put_fs_long(code,stat_addr);
current->cutime += p->utime;
current->cstime += p->stime;
flag = p->pid;
put_fs_long(p->exit_code, stat_addr);
release(p);
#ifdef DEBUG_PROC_TREE
audit_ptree();
#endif
return flag;
default:
flag=1;
......@@ -185,11 +415,14 @@ int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
if (options & WNOHANG)
return 0;
current->state=TASK_INTERRUPTIBLE;
oldblocked = current->blocked;
current->blocked &= ~(1<<(SIGCHLD-1));
schedule();
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
current->blocked = oldblocked;
if (current->signal & ~(current->blocked | (1<<(SIGCHLD-1))))
return -ERESTARTSYS;
else
return -EINTR;
goto repeat;
}
return -ECHILD;
}
......
......@@ -49,7 +49,7 @@ int copy_mem(int nr,struct task_struct * p)
panic("We don't support separate I&D");
if (data_limit < code_limit)
panic("Bad data_limit");
new_data_base = new_code_base = nr * 0x4000000;
new_data_base = new_code_base = nr * TASK_SIZE;
p->start_code = new_code_base;
set_base(p->ldt[1],new_code_base);
set_base(p->ldt[2],new_data_base);
......@@ -66,7 +66,7 @@ int copy_mem(int nr,struct task_struct * p)
* also copies the data segment in it's entirety.
*/
int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
long ebx,long ecx,long edx,
long ebx,long ecx,long edx, long orig_eax,
long fs,long es,long ds,
long eip,long cs,long eflags,long esp,long ss)
{
......@@ -81,7 +81,6 @@ int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
*p = *current; /* NOTE! this doesn't copy the supervisor stack */
p->state = TASK_UNINTERRUPTIBLE;
p->pid = last_pid;
p->father = current->pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
......@@ -111,7 +110,7 @@ int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
__asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
......@@ -126,8 +125,17 @@ int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
if (current->library)
current->library->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->p_pptr = current;
p->p_cptr = 0;
p->p_ysptr = 0;
p->p_osptr = current->p_cptr;
if (p->p_osptr)
p->p_osptr->p_ysptr = p;
current->p_cptr = p;
p->state = TASK_RUNNING; /* do this last, just in case */
return last_pid;
}
......@@ -139,7 +147,9 @@ int find_empty_process(void)
repeat:
if ((++last_pid)<0) last_pid=1;
for(i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->pid == last_pid) goto repeat;
if (task[i] && ((task[i]->pid == last_pid) ||
(task[i]->pgrp == last_pid)))
goto repeat;
for(i=1 ; i<NR_TASKS ; i++)
if (!task[i])
return i;
......
......@@ -24,7 +24,8 @@ CPP =gcc -E -nostdinc -I../../include
$(CC) $(CFLAGS) \
-c -o $*.o $<
OBJS = math_emulate.o
OBJS = math_emulate.o error.o convert.o ea.o get_put.o \
add.o mul.o div.o compare.o
math.a: $(OBJS)
$(AR) rcs math.a $(OBJS)
......@@ -41,3 +42,42 @@ dep:
cp tmp_make Makefile
### Dependencies:
add.s add.o : add.c ../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
compare.s compare.o : compare.c ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
convert.s convert.o : convert.c ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
div.s div.o : div.c ../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
ea.s ea.o : ea.c ../../include/stddef.h ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h \
../../include/asm/segment.h
error.s error.o : error.c ../../include/signal.h ../../include/sys/types.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/linux/mm.h \
../../include/linux/kernel.h
get_put.s get_put.o : get_put.c ../../include/signal.h ../../include/sys/types.h \
../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/linux/mm.h ../../include/linux/kernel.h \
../../include/asm/segment.h
math_emulate.s math_emulate.o : math_emulate.c ../../include/signal.h \
../../include/sys/types.h ../../include/linux/math_emu.h \
../../include/linux/sched.h ../../include/linux/head.h \
../../include/linux/fs.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/asm/segment.h
mul.s mul.o : mul.c ../../include/linux/math_emu.h ../../include/linux/sched.h \
../../include/linux/head.h ../../include/linux/fs.h \
../../include/sys/types.h ../../include/linux/mm.h \
../../include/linux/kernel.h ../../include/signal.h
/*
* linux/kernel/math/add.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real addition routine.
*
* NOTE! These aren't exact: they are only 62 bits wide, and don't do
* correct rounding. Fast hack. The reason is that we shift right the
* values by two, in order not to have overflow (1 bit), and to be able
* to move the sign into the mantissa (1 bit). Much simpler algorithms,
* and 62 bits (61 really - no rounding) accuracy is usually enough. The
* only time you should notice anything weird is when adding 64-bit
* integers together. When using doubles (52 bits accuracy), the
* 61-bit accuracy never shows at all.
*/
#include <linux/math_emu.h>
#define NEGINT(a) \
__asm__("notl %0 ; notl %1 ; addl $1,%0 ; adcl $0,%1" \
:"=r" (a->a),"=r" (a->b) \
:"0" (a->a),"1" (a->b))
static void signify(temp_real * a)
{
a->exponent += 2;
__asm__("shrdl $2,%1,%0 ; shrl $2,%1"
:"=r" (a->a),"=r" (a->b)
:"0" (a->a),"1" (a->b));
if (a->exponent < 0)
NEGINT(a);
a->exponent &= 0x7fff;
}
static void unsignify(temp_real * a)
{
if (!(a->a || a->b)) {
a->exponent = 0;
return;
}
a->exponent &= 0x7fff;
if (a->b < 0) {
NEGINT(a);
a->exponent |= 0x8000;
}
while (a->b >= 0) {
a->exponent--;
__asm__("addl %0,%0 ; adcl %1,%1"
:"=r" (a->a),"=r" (a->b)
:"0" (a->a),"1" (a->b));
}
}
void fadd(const temp_real * src1, const temp_real * src2, temp_real * result)
{
temp_real a,b;
int x1,x2,shift;
x1 = src1->exponent & 0x7fff;
x2 = src2->exponent & 0x7fff;
if (x1 > x2) {
a = *src1;
b = *src2;
shift = x1-x2;
} else {
a = *src2;
b = *src1;
shift = x2-x1;
}
if (shift >= 64) {
*result = a;
return;
}
if (shift >= 32) {
b.a = b.b;
b.b = 0;
shift -= 32;
}
__asm__("shrdl %4,%1,%0 ; shrl %4,%1"
:"=r" (b.a),"=r" (b.b)
:"0" (b.a),"1" (b.b),"c" ((char) shift));
signify(&a);
signify(&b);
__asm__("addl %4,%0 ; adcl %5,%1"
:"=r" (a.a),"=r" (a.b)
:"0" (a.a),"1" (a.b),"g" (b.a),"g" (b.b));
unsignify(&a);
*result = a;
}
/*
* linux/kernel/math/compare.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real comparison routines
*/
#include <linux/math_emu.h>
#define clear_Cx() (I387.swd &= ~0x4500)
static void normalize(temp_real * a)
{
int i = a->exponent & 0x7fff;
int sign = a->exponent & 0x8000;
if (!(a->a || a->b)) {
a->exponent = 0;
return;
}
while (i && a->b >= 0) {
i--;
__asm__("addl %0,%0 ; adcl %1,%1"
:"=r" (a->a),"=r" (a->b)
:"0" (a->a),"1" (a->b));
}
a->exponent = i | sign;
}
void ftst(const temp_real * a)
{
temp_real b;
clear_Cx();
b = *a;
normalize(&b);
if (b.a || b.b || b.exponent) {
if (b.exponent < 0)
set_C0();
} else
set_C3();
}
void fcom(const temp_real * src1, const temp_real * src2)
{
temp_real a;
a = *src1;
a.exponent ^= 0x8000;
fadd(&a,src2,&a);
ftst(&a);
}
void fucom(const temp_real * src1, const temp_real * src2)
{
fcom(src1,src2);
}
/*
* linux/kernel/math/convert.c
*
* (C) 1991 Linus Torvalds
*/
#include <linux/math_emu.h>
/*
* NOTE!!! There is some "non-obvious" optimisations in the temp_to_long
* and temp_to_short conversion routines: don't touch them if you don't
* know what's going on. They are the adding of one in the rounding: the
* overflow bit is also used for adding one into the exponent. Thus it
* looks like the overflow would be incorrectly handled, but due to the
* way the IEEE numbers work, things are correct.
*
* There is no checking for total overflow in the conversions, though (ie
* if the temp-real number simply won't fit in a short- or long-real.)
*/
void short_to_temp(const short_real * a, temp_real * b)
{
if (!(*a & 0x7fffffff)) {
b->a = b->b = 0;
if (*a)
b->exponent = 0x8000;
else
b->exponent = 0;
return;
}
b->exponent = ((*a>>23) & 0xff)-127+16383;
if (*a<0)
b->exponent |= 0x8000;
b->b = (*a<<8) | 0x80000000;
b->a = 0;
}
void long_to_temp(const long_real * a, temp_real * b)
{
if (!a->a && !(a->b & 0x7fffffff)) {
b->a = b->b = 0;
if (a->b)
b->exponent = 0x8000;
else
b->exponent = 0;
return;
}
b->exponent = ((a->b >> 20) & 0x7ff)-1023+16383;
if (a->b<0)
b->exponent |= 0x8000;
b->b = 0x80000000 | (a->b<<11) | (((unsigned long)a->a)>>21);
b->a = a->a<<11;
}
void temp_to_short(const temp_real * a, short_real * b)
{
if (!(a->exponent & 0x7fff)) {
*b = (a->exponent)?0x80000000:0;
return;
}
*b = ((((long) a->exponent)-16383+127) << 23) & 0x7f800000;
if (a->exponent < 0)
*b |= 0x80000000;
*b |= (a->b >> 8) & 0x007fffff;
switch (ROUNDING) {
case ROUND_NEAREST:
if ((a->b & 0xff) > 0x80)
++*b;
break;
case ROUND_DOWN:
if ((a->exponent & 0x8000) && (a->b & 0xff))
++*b;
break;
case ROUND_UP:
if (!(a->exponent & 0x8000) && (a->b & 0xff))
++*b;
break;
}
}
void temp_to_long(const temp_real * a, long_real * b)
{
if (!(a->exponent & 0x7fff)) {
b->a = 0;
b->b = (a->exponent)?0x80000000:0;
return;
}
b->b = (((0x7fff & (long) a->exponent)-16383+1023) << 20) & 0x7ff00000;
if (a->exponent < 0)
b->b |= 0x80000000;
b->b |= (a->b >> 11) & 0x000fffff;
b->a = a->b << 21;
b->a |= (a->a >> 11) & 0x001fffff;
switch (ROUNDING) {
case ROUND_NEAREST:
if ((a->a & 0x7ff) > 0x400)
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
case ROUND_DOWN:
if ((a->exponent & 0x8000) && (a->b & 0xff))
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
case ROUND_UP:
if (!(a->exponent & 0x8000) && (a->b & 0xff))
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
}
}
void real_to_int(const temp_real * a, temp_int * b)
{
int shift = 16383 + 63 - (a->exponent & 0x7fff);
unsigned long underflow;
b->a = b->b = underflow = 0;
b->sign = (a->exponent < 0);
if (shift < 0) {
set_OE();
return;
}
if (shift < 32) {
b->b = a->b; b->a = a->a;
} else if (shift < 64) {
b->a = a->b; underflow = a->a;
shift -= 32;
} else if (shift < 96) {
underflow = a->b;
shift -= 64;
} else
return;
__asm__("shrdl %2,%1,%0"
:"=r" (underflow),"=r" (b->a)
:"c" ((char) shift),"0" (underflow),"1" (b->a));
__asm__("shrdl %2,%1,%0"
:"=r" (b->a),"=r" (b->b)
:"c" ((char) shift),"0" (b->a),"1" (b->b));
__asm__("shrl %1,%0"
:"=r" (b->b)
:"c" ((char) shift),"0" (b->b));
switch (ROUNDING) {
case ROUND_NEAREST:
__asm__("addl %4,%5 ; adcl $0,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b)
,"r" (0x7fffffff + (b->a & 1))
,"m" (*&underflow));
break;
case ROUND_UP:
if (!b->sign && underflow)
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
case ROUND_DOWN:
if (b->sign && underflow)
__asm__("addl $1,%0 ; adcl $0,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
break;
}
}
void int_to_real(const temp_int * a, temp_real * b)
{
b->a = a->a;
b->b = a->b;
if (b->a || b->b)
b->exponent = 16383 + 63 + (a->sign? 0x8000:0);
else {
b->exponent = 0;
return;
}
while (b->b >= 0) {
b->exponent--;
__asm__("addl %0,%0 ; adcl %1,%1"
:"=r" (b->a),"=r" (b->b)
:"0" (b->a),"1" (b->b));
}
}
/*
* linux/kernel/math/div.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real division routine.
*/
#include <linux/math_emu.h>
static void shift_left(int * c)
{
__asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
"movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
"movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
"movl 12(%0),%%eax ; adcl %%eax,12(%0)"
::"r" ((long) c):"ax");
}
static void shift_right(int * c)
{
__asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
::"r" ((long) c));
}
static int try_sub(int * a, int * b)
{
char ok;
__asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)\n\t"
"movl 4(%1),%%eax ; sbbl %%eax,4(%2)\n\t"
"movl 8(%1),%%eax ; sbbl %%eax,8(%2)\n\t"
"movl 12(%1),%%eax ; sbbl %%eax,12(%2)\n\t"
"setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b));
return ok;
}
static void div64(int * a, int * b, int * c)
{
int tmp[4];
int i;
unsigned int mask = 0;
c += 4;
for (i = 0 ; i<64 ; i++) {
if (!(mask >>= 1)) {
c--;
mask = 0x80000000;
}
tmp[0] = a[0]; tmp[1] = a[1];
tmp[2] = a[2]; tmp[3] = a[3];
if (try_sub(b,tmp)) {
*c |= mask;
a[0] = tmp[0]; a[1] = tmp[1];
a[2] = tmp[2]; a[3] = tmp[3];
}
shift_right(b);
}
}
void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
{
int i,sign;
int a[4],b[4],tmp[4] = {0,0,0,0};
sign = (src1->exponent ^ src2->exponent) & 0x8000;
if (!(src2->a || src2->b)) {
set_ZE();
return;
}
i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383;
if (i<0) {
set_UE();
result->exponent = sign;
result->a = result->b = 0;
return;
}
a[0] = a[1] = 0;
a[2] = src1->a;
a[3] = src1->b;
b[0] = b[1] = 0;
b[2] = src2->a;
b[3] = src2->b;
while (b[3] >= 0) {
i++;
shift_left(b);
}
div64(a,b,tmp);
if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
while (i && tmp[3] >= 0) {
i--;
shift_left(tmp);
}
if (tmp[3] >= 0)
set_DE();
} else
i = 0;
if (i>0x7fff) {
set_OE();
return;
}
if (tmp[0] || tmp[1])
set_PE();
result->exponent = i | sign;
result->a = tmp[2];
result->b = tmp[3];
}
/*
* linux/kernel/math/ea.c
*
* (C) 1991 Linus Torvalds
*/
/*
* Calculate the effective address.
*/
#include <stddef.h>
#include <linux/math_emu.h>
#include <asm/segment.h>
static int __regoffset[] = {
offsetof(struct info,___eax),
offsetof(struct info,___ecx),
offsetof(struct info,___edx),
offsetof(struct info,___ebx),
offsetof(struct info,___esp),
offsetof(struct info,___ebp),
offsetof(struct info,___esi),
offsetof(struct info,___edi)
};
#define REG(x) (*(long *)(__regoffset[(x)]+(char *) info))
static char * sib(struct info * info, int mod)
{
unsigned char ss,index,base;
long offset = 0;
base = get_fs_byte((char *) EIP);
EIP++;
ss = base >> 6;
index = (base >> 3) & 7;
base &= 7;
if (index == 4)
offset = 0;
else
offset = REG(index);
offset <<= ss;
if (mod || base != 5)
offset += REG(base);
if (mod == 1) {
offset += (signed char) get_fs_byte((char *) EIP);
EIP++;
} else if (mod == 2 || base == 5) {
offset += (signed) get_fs_long((unsigned long *) EIP);
EIP += 4;
}
I387.foo = offset;
I387.fos = 0x17;
return (char *) offset;
}
char * ea(struct info * info, unsigned short code)
{
unsigned char mod,rm;
long * tmp = &EAX;
int offset = 0;
mod = (code >> 6) & 3;
rm = code & 7;
if (rm == 4 && mod != 3)
return sib(info,mod);
if (rm == 5 && !mod) {
offset = get_fs_long((unsigned long *) EIP);
EIP += 4;
I387.foo = offset;
I387.fos = 0x17;
return (char *) offset;
}
tmp = & REG(rm);
switch (mod) {
case 0: offset = 0; break;
case 1:
offset = (signed char) get_fs_byte((char *) EIP);
EIP++;
break;
case 2:
offset = (signed) get_fs_long((unsigned long *) EIP);
EIP += 4;
break;
case 3:
math_abort(info,1<<(SIGILL-1));
}
I387.foo = offset;
I387.fos = 0x17;
return offset + (char *) *tmp;
}
/*
* linux/kernel/math/error.c
*
* (C) 1991 Linus Torvalds
*/
#include <signal.h>
#include <linux/sched.h>
void math_error(void)
{
__asm__("fnclex");
if (last_task_used_math)
last_task_used_math->signal |= 1<<(SIGFPE-1);
}
/*
* linux/kernel/math/get_put.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file handles all accesses to user memory: getting and putting
* ints/reals/BCD etc. This is the only part that concerns itself with
* other than temporary real format. All other cals are strictly temp_real.
*/
#include <signal.h>
#include <linux/math_emu.h>
#include <linux/kernel.h>
#include <asm/segment.h>
void get_short_real(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
short_real sr;
addr = ea(info,code);
sr = get_fs_long((unsigned long *) addr);
short_to_temp(&sr,tmp);
}
void get_long_real(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
long_real lr;
addr = ea(info,code);
lr.a = get_fs_long((unsigned long *) addr);
lr.b = get_fs_long(1 + (unsigned long *) addr);
long_to_temp(&lr,tmp);
}
void get_temp_real(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
addr = ea(info,code);
tmp->a = get_fs_long((unsigned long *) addr);
tmp->b = get_fs_long(1 + (unsigned long *) addr);
tmp->exponent = get_fs_word(4 + (unsigned short *) addr);
}
void get_short_int(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
ti.a = (signed short) get_fs_word((unsigned short *) addr);
ti.b = 0;
if (ti.sign = (ti.a < 0))
ti.a = - ti.a;
int_to_real(&ti,tmp);
}
void get_long_int(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
ti.a = get_fs_long((unsigned long *) addr);
ti.b = 0;
if (ti.sign = (ti.a < 0))
ti.a = - ti.a;
int_to_real(&ti,tmp);
}
void get_longlong_int(temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
ti.a = get_fs_long((unsigned long *) addr);
ti.b = get_fs_long(1 + (unsigned long *) addr);
if (ti.sign = (ti.b < 0))
__asm__("notl %0 ; notl %1\n\t"
"addl $1,%0 ; adcl $0,%1"
:"=r" (ti.a),"=r" (ti.b)
:"0" (ti.a),"1" (ti.b));
int_to_real(&ti,tmp);
}
#define MUL10(low,high) \
__asm__("addl %0,%0 ; adcl %1,%1\n\t" \
"movl %0,%%ecx ; movl %1,%%ebx\n\t" \
"addl %0,%0 ; adcl %1,%1\n\t" \
"addl %0,%0 ; adcl %1,%1\n\t" \
"addl %%ecx,%0 ; adcl %%ebx,%1" \
:"=a" (low),"=d" (high) \
:"0" (low),"1" (high):"cx","bx")
#define ADD64(val,low,high) \
__asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \
:"0" (low),"1" (high),"r" ((unsigned long) (val)))
void get_BCD(temp_real * tmp, struct info * info, unsigned short code)
{
int k;
char * addr;
temp_int i;
unsigned char c;
addr = ea(info,code);
addr += 9;
i.sign = 0x80 & get_fs_byte(addr--);
i.a = i.b = 0;
for (k = 0; k < 9; k++) {
c = get_fs_byte(addr--);
MUL10(i.a, i.b);
ADD64((c>>4), i.a, i.b);
MUL10(i.a, i.b);
ADD64((c&0xf), i.a, i.b);
}
int_to_real(&i,tmp);
}
void put_short_real(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
short_real sr;
addr = ea(info,code);
verify_area(addr,4);
temp_to_short(tmp,&sr);
put_fs_long(sr,(unsigned long *) addr);
}
void put_long_real(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
long_real lr;
addr = ea(info,code);
verify_area(addr,8);
temp_to_long(tmp,&lr);
put_fs_long(lr.a, (unsigned long *) addr);
put_fs_long(lr.b, 1 + (unsigned long *) addr);
}
void put_temp_real(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
addr = ea(info,code);
verify_area(addr,10);
put_fs_long(tmp->a, (unsigned long *) addr);
put_fs_long(tmp->b, 1 + (unsigned long *) addr);
put_fs_word(tmp->exponent, 4 + (short *) addr);
}
void put_short_int(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
real_to_int(tmp,&ti);
verify_area(addr,2);
if (ti.sign)
ti.a = -ti.a;
put_fs_word(ti.a,(short *) addr);
}
void put_long_int(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
real_to_int(tmp,&ti);
verify_area(addr,4);
if (ti.sign)
ti.a = -ti.a;
put_fs_long(ti.a,(unsigned long *) addr);
}
void put_longlong_int(const temp_real * tmp,
struct info * info, unsigned short code)
{
char * addr;
temp_int ti;
addr = ea(info,code);
real_to_int(tmp,&ti);
verify_area(addr,8);
if (ti.sign)
__asm__("notl %0 ; notl %1\n\t"
"addl $1,%0 ; adcl $0,%1"
:"=r" (ti.a),"=r" (ti.b)
:"0" (ti.a),"1" (ti.b));
put_fs_long(ti.a,(unsigned long *) addr);
put_fs_long(ti.b,1 + (unsigned long *) addr);
}
#define DIV10(low,high,rem) \
__asm__("divl %6 ; xchgl %1,%2 ; divl %6" \
:"=d" (rem),"=a" (low),"=b" (high) \
:"0" (0),"1" (high),"2" (low),"c" (10))
void put_BCD(const temp_real * tmp,struct info * info, unsigned short code)
{
int k,rem;
char * addr;
temp_int i;
unsigned char c;
addr = ea(info,code);
verify_area(addr,10);
real_to_int(tmp,&i);
if (i.sign)
put_fs_byte(0x80, addr+9);
else
put_fs_byte(0, addr+9);
for (k = 0; k < 9; k++) {
DIV10(i.a,i.b,rem);
c = rem;
DIV10(i.a,i.b,rem);
c += rem<<4;
put_fs_byte(c,addr++);
}
}
......@@ -5,38 +5,525 @@
*/
/*
* This directory should contain the math-emulation code.
* Currently only results in a signal.
* Limited emulation 27.12.91 - mostly loads/stores, which gcc wants
* even for soft-float, unless you use bruce evans' patches. The patches
* are great, but they have to be re-applied for every version, and the
* library is different for soft-float and 80387. So emulation is more
* practical, even though it's slower.
*
* 28.12.91 - loads/stores work, even BCD. I'll have to start thinking
* about add/sub/mul/div. Urgel. I should find some good source, but I'll
* just fake up something.
*
* 30.12.91 - add/sub/mul/div/com seem to work mostly. I should really
* test every possible combination.
*/
/*
* This file is full of ugly macros etc: one problem was that gcc simply
* didn't want to make the structures as they should be: it has to try to
* align them. Sickening code, but at least I've hidden the ugly things
* in this one file: the other files don't need to know about these things.
*
* The other files also don't care about ST(x) etc - they just get addresses
* to 80-bit temporary reals, and do with them as they please. I wanted to
* hide most of the 387-specific things here.
*/
#include <signal.h>
#include <linux/sched.h>
#define __ALIGNED_TEMP_REAL 1
#include <linux/math_emu.h>
#include <linux/kernel.h>
#include <asm/segment.h>
void math_emulate(long edi, long esi, long ebp, long sys_call_ret,
long eax,long ebx,long ecx,long edx,
unsigned short fs,unsigned short es,unsigned short ds,
unsigned long eip,unsigned short cs,unsigned long eflags,
unsigned short ss, unsigned long esp)
#define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
#define ST(x) (*__st((x)))
#define PST(x) ((const temp_real *) __st((x)))
/*
* We don't want these inlined - it gets too messy in the machine-code.
*/
static void fpop(void);
static void fpush(void);
static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b);
static temp_real_unaligned * __st(int i);
static void do_emu(struct info * info)
{
unsigned char first, second;
unsigned short code;
temp_real tmp;
char * address;
if (I387.cwd & I387.swd & 0x3f)
I387.swd |= 0x8000;
else
I387.swd &= 0x7fff;
ORIG_EIP = EIP;
/* 0x0007 means user code space */
if (cs != 0x000F) {
printk("math_emulate: %04x:%08x\n\r",cs,eip);
if (CS != 0x000F) {
printk("math_emulate: %04x:%08x\n\r",CS,EIP);
panic("Math emulation needed in kernel");
}
first = get_fs_byte((char *)((*&eip)++));
second = get_fs_byte((char *)((*&eip)++));
printk("%04x:%08x %02x %02x\n\r",cs,eip-2,first,second);
current->signal |= 1<<(SIGFPE-1);
code = get_fs_word((unsigned short *) EIP);
bswapw(code);
code &= 0x7ff;
I387.fip = EIP;
*(unsigned short *) &I387.fcs = CS;
*(1+(unsigned short *) &I387.fcs) = code;
EIP += 2;
switch (code) {
case 0x1d0: /* fnop */
return;
case 0x1d1: case 0x1d2: case 0x1d3:
case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
math_abort(info,1<<(SIGILL-1));
case 0x1e0:
ST(0).exponent ^= 0x8000;
return;
case 0x1e1:
ST(0).exponent &= 0x7fff;
return;
case 0x1e2: case 0x1e3:
math_abort(info,1<<(SIGILL-1));
case 0x1e4:
ftst(PST(0));
return;
case 0x1e5:
printk("fxam not implemented\n\r");
math_abort(info,1<<(SIGILL-1));
case 0x1e6: case 0x1e7:
math_abort(info,1<<(SIGILL-1));
case 0x1e8:
fpush();
ST(0) = CONST1;
return;
case 0x1e9:
fpush();
ST(0) = CONSTL2T;
return;
case 0x1ea:
fpush();
ST(0) = CONSTL2E;
return;
case 0x1eb:
fpush();
ST(0) = CONSTPI;
return;
case 0x1ec:
fpush();
ST(0) = CONSTLG2;
return;
case 0x1ed:
fpush();
ST(0) = CONSTLN2;
return;
case 0x1ee:
fpush();
ST(0) = CONSTZ;
return;
case 0x1ef:
math_abort(info,1<<(SIGILL-1));
case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3:
case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7:
case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb:
case 0x1fc: case 0x1fd: case 0x1fe: case 0x1ff:
printk("%04x fxxx not implemented\n\r",code + 0xc800);
math_abort(info,1<<(SIGILL-1));
case 0x2e9:
fucom(PST(1),PST(0));
fpop(); fpop();
return;
case 0x3d0: case 0x3d1:
return;
case 0x3e2:
I387.swd &= 0x7f00;
return;
case 0x3e3:
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
return;
case 0x3e4:
return;
case 0x6d9:
fcom(PST(1),PST(0));
fpop(); fpop();
return;
case 0x7e0:
*(short *) &EAX = I387.swd;
return;
}
switch (code >> 3) {
case 0x18:
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x19:
fmul(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1a:
fcom(PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1b:
fcom(PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
fpop();
return;
case 0x1c:
real_to_real(&ST(code & 7),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(0),&tmp,&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1d:
ST(0).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1e:
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x1f:
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 0x38:
fpush();
ST(0) = ST((code & 7)+1);
return;
case 0x39:
fxchg(&ST(0),&ST(code & 7));
return;
case 0x3b:
ST(code & 7) = ST(0);
fpop();
return;
case 0x98:
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x99:
fmul(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9a:
fcom(PST(code & 7),PST(0));
return;
case 0x9b:
fcom(PST(code & 7),PST(0));
fpop();
return;
case 0x9c:
ST(code & 7).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9d:
real_to_real(&ST(0),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(code & 7),&tmp,&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9e:
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0x9f:
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
return;
case 0xb8:
printk("ffree not implemented\n\r");
math_abort(info,1<<(SIGILL-1));
case 0xb9:
fxchg(&ST(0),&ST(code & 7));
return;
case 0xba:
ST(code & 7) = ST(0);
return;
case 0xbb:
ST(code & 7) = ST(0);
fpop();
return;
case 0xbc:
fucom(PST(code & 7),PST(0));
return;
case 0xbd:
fucom(PST(code & 7),PST(0));
fpop();
return;
case 0xd8:
fadd(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xd9:
fmul(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xda:
fcom(PST(code & 7),PST(0));
fpop();
return;
case 0xdc:
ST(code & 7).exponent ^= 0x8000;
fadd(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xdd:
real_to_real(&ST(0),&tmp);
tmp.exponent ^= 0x8000;
fadd(PST(code & 7),&tmp,&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xde:
fdiv(PST(0),PST(code & 7),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xdf:
fdiv(PST(code & 7),PST(0),&tmp);
real_to_real(&tmp,&ST(code & 7));
fpop();
return;
case 0xf8:
printk("ffree not implemented\n\r");
math_abort(info,1<<(SIGILL-1));
fpop();
return;
case 0xf9:
fxchg(&ST(0),&ST(code & 7));
return;
case 0xfa:
case 0xfb:
ST(code & 7) = ST(0);
fpop();
return;
}
switch ((code>>3) & 0xe7) {
case 0x22:
put_short_real(PST(0),info,code);
return;
case 0x23:
put_short_real(PST(0),info,code);
fpop();
return;
case 0x24:
address = ea(info,code);
for (code = 0 ; code < 7 ; code++) {
((long *) & I387)[code] =
get_fs_long((unsigned long *) address);
address += 4;
}
return;
case 0x25:
address = ea(info,code);
*(unsigned short *) &I387.cwd =
get_fs_word((unsigned short *) address);
return;
case 0x26:
address = ea(info,code);
verify_area(address,28);
for (code = 0 ; code < 7 ; code++) {
put_fs_long( ((long *) & I387)[code],
(unsigned long *) address);
address += 4;
}
return;
case 0x27:
address = ea(info,code);
verify_area(address,2);
put_fs_word(I387.cwd,(short *) address);
return;
case 0x62:
put_long_int(PST(0),info,code);
return;
case 0x63:
put_long_int(PST(0),info,code);
fpop();
return;
case 0x65:
fpush();
get_temp_real(&tmp,info,code);
real_to_real(&tmp,&ST(0));
return;
case 0x67:
put_temp_real(PST(0),info,code);
fpop();
return;
case 0xa2:
put_long_real(PST(0),info,code);
return;
case 0xa3:
put_long_real(PST(0),info,code);
fpop();
return;
case 0xa4:
address = ea(info,code);
for (code = 0 ; code < 27 ; code++) {
((long *) & I387)[code] =
get_fs_long((unsigned long *) address);
address += 4;
}
return;
case 0xa6:
address = ea(info,code);
verify_area(address,108);
for (code = 0 ; code < 27 ; code++) {
put_fs_long( ((long *) & I387)[code],
(unsigned long *) address);
address += 4;
}
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
return;
case 0xa7:
address = ea(info,code);
verify_area(address,2);
put_fs_word(I387.swd,(short *) address);
return;
case 0xe2:
put_short_int(PST(0),info,code);
return;
case 0xe3:
put_short_int(PST(0),info,code);
fpop();
return;
case 0xe4:
fpush();
get_BCD(&tmp,info,code);
real_to_real(&tmp,&ST(0));
return;
case 0xe5:
fpush();
get_longlong_int(&tmp,info,code);
real_to_real(&tmp,&ST(0));
return;
case 0xe6:
put_BCD(PST(0),info,code);
fpop();
return;
case 0xe7:
put_longlong_int(PST(0),info,code);
fpop();
return;
}
switch (code >> 9) {
case 0:
get_short_real(&tmp,info,code);
break;
case 1:
get_long_int(&tmp,info,code);
break;
case 2:
get_long_real(&tmp,info,code);
break;
case 4:
get_short_int(&tmp,info,code);
}
switch ((code>>3) & 0x27) {
case 0:
fadd(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 1:
fmul(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 2:
fcom(&tmp,PST(0));
return;
case 3:
fcom(&tmp,PST(0));
fpop();
return;
case 4:
tmp.exponent ^= 0x8000;
fadd(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 5:
ST(0).exponent ^= 0x8000;
fadd(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
case 6:
fdiv(PST(0),&tmp,&tmp);
real_to_real(&tmp,&ST(0));
return;
case 7:
fdiv(&tmp,PST(0),&tmp);
real_to_real(&tmp,&ST(0));
return;
}
if ((code & 0x138) == 0x100) {
fpush();
real_to_real(&tmp,&ST(0));
return;
}
printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code);
math_abort(info,1<<(SIGFPE-1));
}
void math_emulate(long ___false)
{
if (!current->used_math) {
current->used_math = 1;
I387.cwd = 0x037f;
I387.swd = 0x0000;
I387.twd = 0x0000;
}
/* &___false points to info->___orig_eip, so subtract 1 to get info */
do_emu((struct info *) ((&___false) - 1));
}
void __math_abort(struct info * info, unsigned int signal)
{
EIP = ORIG_EIP;
current->signal |= signal;
__asm__("movl %0,%%esp ; ret"::"g" ((long) info));
}
static void fpop(void)
{
unsigned long tmp;
tmp = I387.swd & 0xffffc7ff;
I387.swd += 0x00000800;
I387.swd &= 0x00003800;
I387.swd |= tmp;
}
static void fpush(void)
{
unsigned long tmp;
tmp = I387.swd & 0xffffc7ff;
I387.swd += 0x00003800;
I387.swd &= 0x00003800;
I387.swd |= tmp;
}
static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b)
{
temp_real_unaligned c;
c = *a;
*a = *b;
*b = c;
}
void math_error(void)
static temp_real_unaligned * __st(int i)
{
__asm__("fnclex");
if (last_task_used_math)
last_task_used_math->signal |= 1<<(SIGFPE-1);
i += I387.swd >> 11;
i &= 7;
return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space));
}
/*
* linux/kernel/math/mul.c
*
* (C) 1991 Linus Torvalds
*/
/*
* temporary real multiplication routine.
*/
#include <linux/math_emu.h>
static void shift(int * c)
{
__asm__("movl (%0),%%eax ; addl %%eax,(%0)\n\t"
"movl 4(%0),%%eax ; adcl %%eax,4(%0)\n\t"
"movl 8(%0),%%eax ; adcl %%eax,8(%0)\n\t"
"movl 12(%0),%%eax ; adcl %%eax,12(%0)"
::"r" ((long) c):"ax");
}
static void mul64(const temp_real * a, const temp_real * b, int * c)
{
__asm__("movl (%0),%%eax\n\t"
"mull (%1)\n\t"
"movl %%eax,(%2)\n\t"
"movl %%edx,4(%2)\n\t"
"movl 4(%0),%%eax\n\t"
"mull 4(%1)\n\t"
"movl %%eax,8(%2)\n\t"
"movl %%edx,12(%2)\n\t"
"movl (%0),%%eax\n\t"
"mull 4(%1)\n\t"
"addl %%eax,4(%2)\n\t"
"adcl %%edx,8(%2)\n\t"
"adcl $0,12(%2)\n\t"
"movl 4(%0),%%eax\n\t"
"mull (%1)\n\t"
"addl %%eax,4(%2)\n\t"
"adcl %%edx,8(%2)\n\t"
"adcl $0,12(%2)"
::"b" ((long) a),"c" ((long) b),"D" ((long) c)
:"ax","dx");
}
void fmul(const temp_real * src1, const temp_real * src2, temp_real * result)
{
int i,sign;
int tmp[4] = {0,0,0,0};
sign = (src1->exponent ^ src2->exponent) & 0x8000;
i = (src1->exponent & 0x7fff) + (src2->exponent & 0x7fff) - 16383 + 1;
if (i<0) {
result->exponent = sign;
result->a = result->b = 0;
return;
}
if (i>0x7fff) {
set_OE();
return;
}
mul64(src1,src2,tmp);
if (tmp[0] || tmp[1] || tmp[2] || tmp[3])
while (i && tmp[3] >= 0) {
i--;
shift(tmp);
}
else
i = 0;
result->exponent = i | sign;
result->a = tmp[2];
result->b = tmp[3];
}
......@@ -26,16 +26,6 @@ int printk(const char *fmt, ...)
va_start(args, fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
__asm__("push %%fs\n\t"
"push %%ds\n\t"
"pop %%fs\n\t"
"pushl %0\n\t"
"pushl $_buf\n\t"
"pushl $0\n\t"
"call _tty_write\n\t"
"addl $8,%%esp\n\t"
"popl %0\n\t"
"pop %%fs"
::"r" (i):"ax","cx","dx");
console_print(buf);
return i;
}
......@@ -27,17 +27,26 @@ void show_task(int nr,struct task_struct * p)
{
int i,j = 4096-sizeof(struct task_struct);
printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
printk("%d: pid=%d, state=%d, father=%d, child=%d, ",nr,p->pid,
p->state, p->p_pptr->pid, p->p_cptr ? p->p_cptr->pid : -1);
i=0;
while (i<j && !((char *)(p+1))[i])
i++;
printk("%d (of %d) chars free in kernel stack\n\r",i,j);
printk("%d/%d chars free in kstack\n\r",i,j);
printk(" PC=%08X.", *(1019 + (unsigned long *) p));
if (p->p_ysptr || p->p_osptr)
printk(" Younger sib=%d, older sib=%d\n\r",
p->p_ysptr ? p->p_ysptr->pid : -1,
p->p_osptr ? p->p_osptr->pid : -1);
else
printk("\n\r");
}
void show_stat(void)
void show_state(void)
{
int i;
printk("\rTask-info:\n\r");
for (i=0;i<NR_TASKS;i++)
if (task[i])
show_task(i,task[i]);
......@@ -57,8 +66,14 @@ union task_union {
static union task_union init_task = {INIT_TASK,};
long volatile jiffies=0;
long startup_time=0;
unsigned long volatile jiffies=0;
unsigned long startup_time=0;
int jiffies_offset = 0; /* # clock ticks to add to get "true
time". Should always be less than
1 second's worth. For time fanatics
who like to syncronize their machines
to WWV :-) */
struct task_struct *current = &(init_task.task);
struct task_struct *last_task_used_math = NULL;
......@@ -110,10 +125,15 @@ void schedule(void)
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p) {
if ((*p)->timeout && (*p)->timeout < jiffies) {
(*p)->timeout = 0;
if ((*p)->state == TASK_INTERRUPTIBLE)
(*p)->state = TASK_RUNNING;
}
if ((*p)->alarm && (*p)->alarm < jiffies) {
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
(*p)->signal |= (1<<(SIGALRM-1));
(*p)->alarm = 0;
}
if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
......@@ -148,7 +168,7 @@ int sys_pause(void)
return 0;
}
void sleep_on(struct task_struct **p)
static inline void __sleep_on(struct task_struct **p, int state)
{
struct task_struct *tmp;
......@@ -158,38 +178,37 @@ void sleep_on(struct task_struct **p)
panic("task[0] trying to sleep");
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
if (tmp)
current->state = state;
repeat: schedule();
if (*p && *p != current) {
(**p).state = 0;
current->state = TASK_UNINTERRUPTIBLE;
goto repeat;
}
if (!*p)
printk("Warning: *P = NULL\n\r");
if (*p = tmp)
tmp->state=0;
}
void interruptible_sleep_on(struct task_struct **p)
{
struct task_struct *tmp;
__sleep_on(p,TASK_INTERRUPTIBLE);
}
if (!p)
return;
if (current == &(init_task.task))
panic("task[0] trying to sleep");
tmp=*p;
*p=current;
repeat: current->state = TASK_INTERRUPTIBLE;
schedule();
if (*p && *p != current) {
(**p).state=0;
goto repeat;
}
*p=NULL;
if (tmp)
tmp->state=0;
void sleep_on(struct task_struct **p)
{
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
void wake_up(struct task_struct **p)
{
if (p && *p) {
if ((**p).state == TASK_STOPPED)
printk("wake_up: TASK_STOPPED");
if ((**p).state == TASK_ZOMBIE)
printk("wake_up: TASK_ZOMBIE");
(**p).state=0;
*p=NULL;
}
}
......@@ -304,8 +323,21 @@ void add_timer(long jiffies, void (*fn)(void))
void do_timer(long cpl)
{
extern int beepcount;
extern void sysbeepstop(void);
static int blanked = 0;
if (blankcount || !blankinterval) {
if (blanked)
unblank_screen();
if (blankcount)
blankcount--;
blanked = 0;
} else if (!blanked) {
blank_screen();
blanked = 1;
}
if (hd_timeout)
if (!--hd_timeout)
hd_times_out();
if (beepcount)
if (!--beepcount)
......@@ -352,7 +384,7 @@ int sys_getpid(void)
int sys_getppid(void)
{
return current->father;
return current->p_pptr->pid;
}
int sys_getuid(void)
......
......@@ -9,9 +9,8 @@
#include <asm/segment.h>
#include <signal.h>
volatile void do_exit(int error_code);
#include <errno.h>
int sys_sgetmask()
{
return current->blocked;
......@@ -21,10 +20,48 @@ int sys_ssetmask(int newmask)
{
int old=current->blocked;
current->blocked = newmask & ~(1<<(SIGKILL-1));
current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1));
return old;
}
int sys_sigpending(sigset_t *set)
{
/* fill in "set" with signals pending but blocked. */
verify_area(set,4);
put_fs_long(current->blocked & current->signal, (unsigned long *)set);
return 0;
}
/* atomically swap in the new signal mask, and wait for a signal.
*
* we need to play some games with syscall restarting. We get help
* from the syscall library interface. Note that we need to coordinate
* the calling convention with the libc routine.
*
* "set" is just the sigmask as described in 1003.1-1988, 3.3.7.
* It is assumed that sigset_t can be passed as a 32 bit quantity.
*
* "restart" holds a restart indication. If it's non-zero, then we
* install the old mask, and return normally. If it's zero, we store
* the current mask in old_mask and block until a signal comes in.
*/
int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set)
{
extern int sys_pause(void);
if (restart) {
/* we're restarting */
current->blocked = old_mask;
return -EINTR;
}
/* we're not restarting. do the work */
*(&restart) = 1;
*(&old_mask) = current->blocked;
current->blocked = set;
(void) sys_pause(); /* return after a signal arrives */
return -ERESTARTNOINTR; /* handle the signal, and come back */
}
static inline void save_old(char * from,char * to)
{
int i;
......@@ -49,8 +86,8 @@ int sys_signal(int signum, long handler, long restorer)
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
return -EINVAL;
tmp.sa_handler = (void (*)(int)) handler;
tmp.sa_mask = 0;
tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
......@@ -65,8 +102,8 @@ int sys_sigaction(int signum, const struct sigaction * action,
{
struct sigaction tmp;
if (signum<1 || signum>32 || signum==SIGKILL)
return -1;
if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
return -EINVAL;
tmp = current->sigaction[signum-1];
get_new((char *) action,
(char *) (signum-1+current->sigaction));
......@@ -79,7 +116,16 @@ int sys_sigaction(int signum, const struct sigaction * action,
return 0;
}
void do_signal(long signr,long eax, long ebx, long ecx, long edx,
/*
* Routine writes a core dump image in the current directory.
* Currently not implemented.
*/
int core_dump(long signr)
{
return(0); /* We didn't do a dump */
}
int do_signal(long signr,long eax,long ebx, long ecx, long edx, long orig_eax,
long fs, long es, long ds,
long eip, long cs, long eflags,
unsigned long * esp, long ss)
......@@ -88,17 +134,60 @@ void do_signal(long signr,long eax, long ebx, long ecx, long edx,
long old_eip=eip;
struct sigaction * sa = current->sigaction + signr - 1;
int longs;
unsigned long * tmp_esp;
#ifdef notdef
printk("pid: %d, signr: %x, eax=%d, oeax = %d, int=%d\n",
current->pid, signr, eax, orig_eax,
sa->sa_flags & SA_INTERRUPT);
#endif
if ((orig_eax != -1) &&
((eax == -ERESTARTSYS) || (eax == -ERESTARTNOINTR))) {
if ((eax == -ERESTARTSYS) && ((sa->sa_flags & SA_INTERRUPT) ||
signr < SIGCONT || signr > SIGTTOU))
*(&eax) = -EINTR;
else {
*(&eax) = orig_eax;
*(&eip) = old_eip -= 2;
}
}
sa_handler = (unsigned long) sa->sa_handler;
if (sa_handler==1)
return;
return(1); /* Ignore, see if there are more signals... */
if (!sa_handler) {
if (signr==SIGCHLD)
return;
else
do_exit(1<<(signr-1));
switch (signr) {
case SIGCONT:
case SIGCHLD:
return(1); /* Ignore, ... */
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
current->state = TASK_STOPPED;
current->exit_code = signr;
if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
current->p_pptr->signal |= (1<<(SIGCHLD-1));
return(1); /* Reschedule another event */
case SIGQUIT:
case SIGILL:
case SIGTRAP:
case SIGIOT:
case SIGFPE:
case SIGSEGV:
if (core_dump(signr))
do_exit(signr|0x80);
/* fall through */
default:
do_exit(signr);
}
}
/*
* OK, we're invoking a handler
*/
if (sa->sa_flags & SA_ONESHOT)
sa->sa_handler = NULL;
*(&eip) = sa_handler;
......@@ -116,4 +205,5 @@ void do_signal(long signr,long eax, long ebx, long ecx, long edx,
put_fs_long(eflags,tmp_esp++);
put_fs_long(old_eip,tmp_esp++);
current->blocked |= sa->sa_mask;
return(0); /* Continue, execute handler */
}
......@@ -9,9 +9,21 @@
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kernel.h>
#include <linux/config.h>
#include <asm/segment.h>
#include <sys/times.h>
#include <sys/utsname.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <string.h>
/*
* The timezone where the local system is located. Used as a default by some
* programs who obtain this value by using gettimeofday.
*/
struct timezone sys_tz = { 0, 0};
extern int session_of_pgrp(int pgrp);
int sys_ftime()
{
......@@ -48,6 +60,17 @@ int sys_prof()
return -ENOSYS;
}
/*
* This is done BSD-style, with no consideration of the saved gid, except
* that if you set the effective gid, it sets the saved gid too. This
* makes it possible for a setgid program to completely drop its privileges,
* which is often a useful assertion to make when you are doing a security
* audit over a program.
*
* The general idea is that a program which uses just setregid() will be
* 100% compatible with BSD. A program which uses just setgid() will be
* 100% compatible with POSIX w/ Saved ID's.
*/
int sys_setregid(int rgid, int egid)
{
if (rgid>0) {
......@@ -60,18 +83,27 @@ int sys_setregid(int rgid, int egid)
if (egid>0) {
if ((current->gid == egid) ||
(current->egid == egid) ||
(current->sgid == egid) ||
suser())
suser()) {
current->egid = egid;
else
current->sgid = egid;
} else
return(-EPERM);
}
return 0;
}
/*
* setgid() is implemeneted like SysV w/ SAVED_IDS
*/
int sys_setgid(int gid)
{
return(sys_setregid(gid, gid));
if (suser())
current->gid = current->egid = current->sgid = gid;
else if ((gid == current->gid) || (gid == current->sgid))
current->egid = gid;
else
return -EPERM;
return 0;
}
int sys_acct()
......@@ -113,7 +145,16 @@ int sys_time(long * tloc)
/*
* Unprivileged users may change the real user id to the effective uid
* or vice versa.
* or vice versa. (BSD-style)
*
* When you set the effective uid, it sets the saved uid too. This
* makes it possible for a setuid program to completely drop its privileges,
* which is often a useful assertion to make when you are doing a security
* audit over a program.
*
* The general idea is that a program which uses just setreuid() will be
* 100% compatible with BSD. A program which uses just setuid() will be
* 100% compatible with POSIX w/ Saved ID's.
*/
int sys_setreuid(int ruid, int euid)
{
......@@ -130,9 +171,10 @@ int sys_setreuid(int ruid, int euid)
if (euid>0) {
if ((old_ruid == euid) ||
(current->euid == euid) ||
suser())
suser()) {
current->euid = euid;
else {
current->suid = euid;
} else {
current->uid = old_ruid;
return(-EPERM);
}
......@@ -140,9 +182,26 @@ int sys_setreuid(int ruid, int euid)
return 0;
}
/*
* setuid() is implemeneted like SysV w/ SAVED_IDS
*
* Note that SAVED_ID's is deficient in that a setuid root program
* like sendmail, for example, cannot set its uid to be a normal
* user and then switch back, because if you're root, setuid() sets
* the saved uid too. If you don't like this, blame the bright people
* in the POSIX commmittee and/or USG. Note that the BSD-style setreuid()
* will allow a root program to temporarily drop privileges and be able to
* regain them by swapping the real and effective uid.
*/
int sys_setuid(int uid)
{
return(sys_setreuid(uid, uid));
if (suser())
current->uid = current->euid = current->suid = uid;
else if ((uid == current->uid) || (uid == current->suid))
current->euid = uid;
else
return -EPERM;
return(0);
}
int sys_stime(long * tptr)
......@@ -150,6 +209,7 @@ int sys_stime(long * tptr)
if (!suser())
return -EPERM;
startup_time = get_fs_long((unsigned long *)tptr) - jiffies/HZ;
jiffies_offset = 0;
return 0;
}
......@@ -177,20 +237,30 @@ int sys_brk(unsigned long end_data_seg)
* This needs some heave checking ...
* I just haven't get the stomach for it. I also don't fully
* understand sessions/pgrp etc. Let somebody who does explain it.
*
* OK, I think I have the protection semantics right.... this is really
* only important on a multi-user system anyway, to make sure one user
* can't send a signal to a process owned by another. -TYT, 12/12/91
*/
int sys_setpgid(int pid, int pgid)
{
int i;
int i;
if (!pid)
pid = current->pid;
if (!pgid)
pgid = current->pid;
if (pgid < 0)
return -EINVAL;
for (i=0 ; i<NR_TASKS ; i++)
if (task[i] && task[i]->pid==pid) {
if (task[i] && (task[i]->pid == pid) &&
((task[i]->p_pptr == current) ||
(task[i] == current))) {
if (task[i]->leader)
return -EPERM;
if (task[i]->session != current->session)
if ((task[i]->session != current->session) ||
((pgid != pid) &&
(session_of_pgrp(pgid) != current->session)))
return -EPERM;
task[i]->pgrp = pgid;
return 0;
......@@ -213,11 +283,65 @@ int sys_setsid(void)
return current->pgrp;
}
/*
* Supplementary group ID's
*/
int sys_getgroups(int gidsetsize, gid_t *grouplist)
{
int i;
if (gidsetsize)
verify_area(grouplist, sizeof(gid_t) * gidsetsize);
for (i = 0; (i < NGROUPS) && (current->groups[i] != NOGROUP);
i++, grouplist++) {
if (gidsetsize) {
if (i >= gidsetsize)
return -EINVAL;
put_fs_word(current->groups[i], (short *) grouplist);
}
}
return(i);
}
int sys_setgroups(int gidsetsize, gid_t *grouplist)
{
int i;
if (!suser())
return -EPERM;
if (gidsetsize > NGROUPS)
return -EINVAL;
for (i = 0; i < gidsetsize; i++, grouplist++) {
current->groups[i] = get_fs_word((unsigned short *) grouplist);
}
if (i < NGROUPS)
current->groups[i] = NOGROUP;
return 0;
}
int in_group_p(gid_t grp)
{
int i;
if (grp == current->egid)
return 1;
for (i = 0; i < NGROUPS; i++) {
if (current->groups[i] == NOGROUP)
break;
if (current->groups[i] == grp)
return 1;
}
return 0;
}
static struct utsname thisname = {
UTS_SYSNAME, UTS_NODENAME, UTS_RELEASE, UTS_VERSION, UTS_MACHINE
};
int sys_uname(struct utsname * name)
{
static struct utsname thisname = {
"linux .0","nodename","release ","version ","machine "
};
int i;
if (!name) return -ERROR;
......@@ -227,6 +351,167 @@ int sys_uname(struct utsname * name)
return 0;
}
/*
* Only sethostname; gethostname can be implemented by calling uname()
*/
int sys_sethostname(char *name, int len)
{
int i;
if (!suser())
return -EPERM;
if (len > MAXHOSTNAMELEN)
return -EINVAL;
for (i=0; i < len; i++) {
if ((thisname.nodename[i] = get_fs_byte(name+i)) == 0)
break;
}
if (thisname.nodename[i]) {
thisname.nodename[i>MAXHOSTNAMELEN ? MAXHOSTNAMELEN : i] = 0;
}
return 0;
}
int sys_getrlimit(int resource, struct rlimit *rlim)
{
if (resource >= RLIM_NLIMITS)
return -EINVAL;
verify_area(rlim,sizeof *rlim);
put_fs_long(current->rlim[resource].rlim_cur,
(unsigned long *) rlim);
put_fs_long(current->rlim[resource].rlim_max,
((unsigned long *) rlim)+1);
return 0;
}
int sys_setrlimit(int resource, struct rlimit *rlim)
{
struct rlimit new, *old;
if (resource >= RLIM_NLIMITS)
return -EINVAL;
old = current->rlim + resource;
new.rlim_cur = get_fs_long((unsigned long *) rlim);
new.rlim_max = get_fs_long(((unsigned long *) rlim)+1);
if (((new.rlim_cur > old->rlim_max) ||
(new.rlim_max > old->rlim_max)) &&
!suser())
return -EPERM;
*old = new;
return 0;
}
/*
* It would make sense to put struct rusuage in the task_struct,
* except that would make the task_struct be *really big*. After
* task_struct gets moved into malloc'ed memory, it would
* make sense to do this. It will make moving the rest of the information
* a lot simpler! (Which we're not doing right now because we're not
* measuring them yet).
*/
int sys_getrusage(int who, struct rusage *ru)
{
struct rusage r;
unsigned long *lp, *lpend, *dest;
if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
return -EINVAL;
verify_area(ru, sizeof *ru);
memset((char *) &r, 0, sizeof(r));
if (who == RUSAGE_SELF) {
r.ru_utime.tv_sec = CT_TO_SECS(current->utime);
r.ru_utime.tv_usec = CT_TO_USECS(current->utime);
r.ru_stime.tv_sec = CT_TO_SECS(current->stime);
r.ru_stime.tv_usec = CT_TO_USECS(current->stime);
} else {
r.ru_utime.tv_sec = CT_TO_SECS(current->cutime);
r.ru_utime.tv_usec = CT_TO_USECS(current->cutime);
r.ru_stime.tv_sec = CT_TO_SECS(current->cstime);
r.ru_stime.tv_usec = CT_TO_USECS(current->cstime);
}
lp = (unsigned long *) &r;
lpend = (unsigned long *) (&r+1);
dest = (unsigned long *) ru;
for (; lp < lpend; lp++, dest++)
put_fs_long(*lp, dest);
return(0);
}
int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
{
if (tv) {
verify_area(tv, sizeof *tv);
put_fs_long(startup_time + CT_TO_SECS(jiffies+jiffies_offset),
(unsigned long *) tv);
put_fs_long(CT_TO_USECS(jiffies+jiffies_offset),
((unsigned long *) tv)+1);
}
if (tz) {
verify_area(tz, sizeof *tz);
put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz);
put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1);
}
return 0;
}
/*
* The first time we set the timezone, we will warp the clock so that
* it is ticking GMT time instead of local time. Presumably,
* if someone is setting the timezone then we are running in an
* environment where the programs understand about timezones.
* This should be done at boot time in the /etc/rc script, as
* soon as possible, so that the clock can be set right. Otherwise,
* various programs will get confused when the clock gets warped.
*/
int sys_settimeofday(struct timeval *tv, struct timezone *tz)
{
static int firsttime = 1;
void adjust_clock();
if (!suser())
return -EPERM;
if (tz) {
sys_tz.tz_minuteswest = get_fs_long((unsigned long *) tz);
sys_tz.tz_dsttime = get_fs_long(((unsigned long *) tz)+1);
if (firsttime) {
firsttime = 0;
if (!tv)
adjust_clock();
}
}
if (tv) {
int sec, usec;
sec = get_fs_long((unsigned long *)tv);
usec = get_fs_long(((unsigned long *)tv)+1);
startup_time = sec - jiffies/HZ;
jiffies_offset = usec * HZ / 1000000 - jiffies%HZ;
}
return 0;
}
/*
* Adjust the time obtained from the CMOS to be GMT time instead of
* local time.
*
* This is ugly, but preferable to the alternatives. Otherwise we
* would either need to write a program to do it in /etc/rc (and risk
* confusion if the program gets run more than once; it would also be
* hard to make the program warp the clock precisely n hours) or
* compile in the timezone information into the kernel. Bad, bad....
*
* XXX Currently does not adjust for daylight savings time. May not
* need to do anything, depending on how smart (dumb?) the BIOS
* is. Blast it all.... the best thing to do not depend on the CMOS
* clock at all, but get the time via NTP or timed if you're on a
* network.... - TYT, 1/1/92
*/
void adjust_clock()
{
startup_time += sys_tz.tz_minuteswest*60;
}
int sys_umask(int mask)
{
int old = current->umask;
......@@ -234,3 +519,4 @@ int sys_umask(int mask)
current->umask = mask & 0777;
return (old);
}
......@@ -20,14 +20,15 @@
* 4(%esp) - %ebx
* 8(%esp) - %ecx
* C(%esp) - %edx
* 10(%esp) - %fs
* 14(%esp) - %es
* 18(%esp) - %ds
* 1C(%esp) - %eip
* 20(%esp) - %cs
* 24(%esp) - %eflags
* 28(%esp) - %oldesp
* 2C(%esp) - %oldss
* 10(%esp) - original %eax (-1 if not system call)
* 14(%esp) - %fs
* 18(%esp) - %es
* 1C(%esp) - %ds
* 20(%esp) - %eip
* 24(%esp) - %cs
* 28(%esp) - %eflags
* 2C(%esp) - %oldesp
* 30(%esp) - %oldss
*/
SIG_CHLD = 17
......@@ -36,14 +37,15 @@ EAX = 0x00
EBX = 0x04
ECX = 0x08
EDX = 0x0C
FS = 0x10
ES = 0x14
DS = 0x18
EIP = 0x1C
CS = 0x20
EFLAGS = 0x24
OLDESP = 0x28
OLDSS = 0x2C
ORIG_EAX = 0x10
FS = 0x14
ES = 0x18
DS = 0x1C
EIP = 0x20
CS = 0x24
EFLAGS = 0x28
OLDESP = 0x2C
OLDSS = 0x30
state = 0 # these are offsets into the task-struct.
counter = 4
......@@ -58,7 +60,9 @@ sa_mask = 4
sa_flags = 8
sa_restorer = 12
nr_system_calls = 72
nr_system_calls = 82
ENOSYS = 38
/*
* Ok, I get parallel printer interrupts while using the floppy for some
......@@ -70,20 +74,19 @@ nr_system_calls = 72
.align 2
bad_sys_call:
movl $-1,%eax
iret
pushl $-ENOSYS
jmp ret_from_sys_call
.align 2
reschedule:
pushl $ret_from_sys_call
jmp _schedule
.align 2
_system_call:
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
push %ds
push %es
push %fs
pushl %edx
pushl %eax # save the orig_eax
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx as parameters
pushl %ebx # to the system call
movl $0x10,%edx # set up ds,es to kernel space
......@@ -91,16 +94,19 @@ _system_call:
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
cmpl _NR_syscalls,%eax
jae bad_sys_call
call _sys_call_table(,%eax,4)
pushl %eax
2:
movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
ret_from_sys_call:
movl _current,%eax # task[0] cannot have signals
cmpl _task,%eax
movl _current,%eax
cmpl _task,%eax # task[0] cannot have signals
je 3f
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
......@@ -117,11 +123,14 @@ ret_from_sys_call:
incl %ecx
pushl %ecx
call _do_signal
popl %eax
popl %ecx
testl %eax, %eax
jne 2b # see if we need to switch tasks, or do more signals
3: popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp # skip orig_eax
pop %fs
pop %es
pop %ds
......@@ -132,6 +141,7 @@ _coprocessor_error:
push %ds
push %es
push %fs
pushl $-1 # fill in -1 for orig_eax
pushl %edx
pushl %ecx
pushl %ebx
......@@ -149,6 +159,7 @@ _device_not_available:
push %ds
push %es
push %fs
pushl $-1 # fill in -1 for orig_eax
pushl %edx
pushl %ecx
pushl %ebx
......@@ -166,7 +177,9 @@ _device_not_available:
pushl %ebp
pushl %esi
pushl %edi
pushl $0 # temporary storage for ORIG_EIP
call _math_emulate
addl $4,%esp
popl %edi
popl %esi
popl %ebp
......@@ -177,6 +190,7 @@ _timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
pushl $-1 # fill in -1 for orig_eax
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
......@@ -235,6 +249,7 @@ _hd_interrupt:
jmp 1f # give port chance to breathe
1: jmp 1f
1: xorl %edx,%edx
movl %edx,_hd_timeout
xchgl _do_hd,%edx
testl %edx,%edx
jne 1f
......
......@@ -36,8 +36,6 @@ register unsigned short __res; \
__asm__("mov %%fs,%%ax":"=a" (__res):); \
__res;})
int do_exit(long code);
void page_exception(void);
void divide_error(void);
......@@ -59,6 +57,7 @@ void coprocessor_error(void);
void reserved(void);
void parallel_interrupt(void);
void irq13(void);
void alignment_check(void);
static void die(char * str,long esp_ptr,long nr)
{
......@@ -94,6 +93,11 @@ void do_general_protection(long esp, long error_code)
die("general protection",esp,error_code);
}
void do_alignment_check(long esp, long error_code)
{
die("alignment check",esp,error_code);
}
void do_divide_error(long esp, long error_code)
{
die("divide error",esp,error_code);
......@@ -199,7 +203,8 @@ void trap_init(void)
set_trap_gate(14,&page_fault);
set_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
for (i=17;i<48;i++)
set_trap_gate(17,&alignment_check);
for (i=18;i<48;i++)
set_trap_gate(i,&reserved);
set_trap_gate(45,&irq13);
outb_p(inb_p(0x21)&0xfb,0x21);
......
......@@ -15,7 +15,7 @@ CPP =gcc -E -nostdinc -I../include
$(CC) $(CFLAGS) \
-S -o $*.s $<
OBJS = memory.o page.o
OBJS = memory.o swap.o page.o
all: mm.o
......@@ -34,4 +34,11 @@ dep:
### Dependencies:
memory.o : memory.c ../include/signal.h ../include/sys/types.h \
../include/asm/system.h ../include/linux/sched.h ../include/linux/head.h \
../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h
../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h
swap.o : swap.c ../include/string.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/linux/kernel.h ../include/signal.h \
../include/sys/param.h ../include/sys/time.h ../include/time.h \
../include/sys/resource.h
......@@ -20,6 +20,14 @@
* Also corrected some "invalidate()"s - I wasn't doing enough of them.
*/
/*
* Real VM (paging to/from disk) started 18.12.91. Much more work and
* thought has to go into this. Oh, well..
* 19.12.91 - works, somewhat. Sometimes I get faults, don't know why.
* Found it. Everything seems to work now.
* 20.12.91 - Ok, making the swap-device changeable like the root.
*/
#include <signal.h>
#include <asm/system.h>
......@@ -28,59 +36,15 @@
#include <linux/head.h>
#include <linux/kernel.h>
volatile void do_exit(long code);
static inline volatile void oom(void)
{
printk("out of memory\n\r");
do_exit(SIGSEGV);
}
#define invalidate() \
__asm__("movl %%eax,%%cr3"::"a" (0))
/* these are not to be changed without changing head.s etc */
#define LOW_MEM 0x100000
#define PAGING_MEMORY (15*1024*1024)
#define PAGING_PAGES (PAGING_MEMORY>>12)
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12)
#define USED 100
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
current->start_code + current->end_code)
static long HIGH_MEMORY = 0;
unsigned long HIGH_MEMORY = 0;
#define copy_page(from,to) \
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
static unsigned char mem_map [ PAGING_PAGES ] = {0,};
/*
* Get physical address of first (actually last :-) free page, and mark it
* used. If no free pages left, return 0.
*/
unsigned long get_free_page(void)
{
register unsigned long __res asm("ax");
__asm__("std ; repne ; scasb\n\t"
"jne 1f\n\t"
"movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t"
"movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t"
"movl %%edx,%%eax\n"
"1:"
:"=a" (__res)
:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
"D" (mem_map+PAGING_PAGES-1)
:"di","cx","dx");
return __res;
}
unsigned char mem_map [ PAGING_PAGES ] = {0,};
/*
* Free a page of memory at physical address 'addr'. Used by
......@@ -118,9 +82,13 @@ int free_page_tables(unsigned long from,unsigned long size)
continue;
pg_table = (unsigned long *) (0xfffff000 & *dir);
for (nr=0 ; nr<1024 ; nr++) {
if (1 & *pg_table)
free_page(0xfffff000 & *pg_table);
*pg_table = 0;
if (*pg_table) {
if (1 & *pg_table)
free_page(0xfffff000 & *pg_table);
else
swap_free(*pg_table >> 1);
*pg_table = 0;
}
pg_table++;
}
free_page(0xfffff000 & *dir);
......@@ -153,6 +121,7 @@ int copy_page_tables(unsigned long from,unsigned long to,long size)
unsigned long * to_page_table;
unsigned long this_page;
unsigned long * from_dir, * to_dir;
unsigned long new_page;
unsigned long nr;
if ((from&0x3fffff) || (to&0x3fffff))
......@@ -172,8 +141,16 @@ int copy_page_tables(unsigned long from,unsigned long to,long size)
nr = (from==0)?0xA0:1024;
for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
this_page = *from_page_table;
if (!(1 & this_page))
if (!this_page)
continue;
if (!(1 & this_page)) {
if (!(new_page = get_free_page()))
return -1;
read_swap_page(this_page>>1, (char *) new_page);
*to_page_table = this_page;
*from_page_table = new_page | (PAGE_DIRTY | 7);
continue;
}
this_page &= ~2;
*to_page_table = this_page;
if (this_page > LOW_MEM) {
......@@ -194,7 +171,7 @@ int copy_page_tables(unsigned long from,unsigned long to,long size)
* out of memory (either when trying to access page-table or
* page.)
*/
unsigned long put_page(unsigned long page,unsigned long address)
static unsigned long put_page(unsigned long page,unsigned long address)
{
unsigned long tmp, *page_table;
......@@ -210,7 +187,7 @@ unsigned long put_page(unsigned long page,unsigned long address)
else {
if (!(tmp=get_free_page()))
return 0;
*page_table = tmp|7;
*page_table = tmp | 7;
page_table = (unsigned long *) tmp;
}
page_table[(address>>12) & 0x3ff] = page | 7;
......@@ -218,6 +195,36 @@ unsigned long put_page(unsigned long page,unsigned long address)
return page;
}
/*
* The previous function doesn't work very well if you also want to mark
* the page dirty: exec.c wants this, as it has earlier changed the page,
* and we want the dirty-status to be correct (for VM). Thus the same
* routine, but this time we mark it dirty too.
*/
unsigned long put_dirty_page(unsigned long page, unsigned long address)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
if (page < LOW_MEM || page >= HIGH_MEMORY)
printk("Trying to put page %p at %p\n",page,address);
if (mem_map[(page-LOW_MEM)>>12] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
page_table = (unsigned long *) ((address>>20) & 0xffc);
if ((*page_table)&1)
page_table = (unsigned long *) (0xfffff000 & *page_table);
else {
if (!(tmp=get_free_page()))
return 0;
*page_table = tmp|7;
page_table = (unsigned long *) tmp;
}
page_table[(address>>12) & 0x3ff] = page | (PAGE_DIRTY | 7);
/* no need for invalidate */
return page;
}
void un_wp_page(unsigned long * table_entry)
{
unsigned long old_page,new_page;
......@@ -232,9 +239,9 @@ void un_wp_page(unsigned long * table_entry)
oom();
if (old_page >= LOW_MEM)
mem_map[MAP_NR(old_page)]--;
copy_page(old_page,new_page);
*table_entry = new_page | 7;
invalidate();
copy_page(old_page,new_page);
}
/*
......@@ -246,6 +253,12 @@ void un_wp_page(unsigned long * table_entry)
*/
void do_wp_page(unsigned long error_code,unsigned long address)
{
if (address < TASK_SIZE)
printk("\n\rBAD! KERNEL MEMORY WP-ERR!\n\r");
if (address - current->start_code > TASK_SIZE) {
printk("Bad things happen: page error in do_wp_page\n\r");
do_exit(SIGSEGV);
}
#if 0
/* we cannot do this yet: the estdio library writes to code space */
/* stupid, stupid. I really want the libc.a from GNU */
......@@ -287,7 +300,7 @@ void get_empty_page(unsigned long address)
* task.
*
* NOTE! This assumes we have checked that p != current, and that they
* share the same executable.
* share the same executable or library.
*/
static int try_to_share(unsigned long address, struct task_struct * p)
{
......@@ -341,21 +354,24 @@ static int try_to_share(unsigned long address, struct task_struct * p)
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
static int share_page(unsigned long address)
static int share_page(struct m_inode * inode, unsigned long address)
{
struct task_struct ** p;
if (!current->executable)
return 0;
if (current->executable->i_count < 2)
if (inode->i_count < 2 || !inode)
return 0;
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!*p)
continue;
if (current == *p)
continue;
if ((*p)->executable != current->executable)
continue;
if (address < LIBRARY_OFFSET) {
if (inode != (*p)->executable)
continue;
} else {
if (inode != (*p)->library)
continue;
}
if (try_to_share(address,*p))
return 1;
}
......@@ -368,23 +384,51 @@ void do_no_page(unsigned long error_code,unsigned long address)
unsigned long tmp;
unsigned long page;
int block,i;
struct m_inode * inode;
if (address < TASK_SIZE)
printk("\n\rBAD!! KERNEL PAGE MISSING\n\r");
if (address - current->start_code > TASK_SIZE) {
printk("Bad things happen: nonexistent page error in do_no_page\n\r");
do_exit(SIGSEGV);
}
page = *(unsigned long *) ((address >> 20) & 0xffc);
if (page & 1) {
page &= 0xfffff000;
page += (address >> 10) & 0xffc;
tmp = *(unsigned long *) page;
if (tmp && !(1 & tmp)) {
swap_in((unsigned long *) page);
return;
}
}
address &= 0xfffff000;
tmp = address - current->start_code;
if (!current->executable || tmp >= current->end_data) {
if (tmp >= LIBRARY_OFFSET ) {
inode = current->library;
block = 1 + (tmp-LIBRARY_OFFSET) / BLOCK_SIZE;
} else if (tmp < current->end_data) {
inode = current->executable;
block = 1 + tmp / BLOCK_SIZE;
} else {
inode = NULL;
block = 0;
}
if (!inode) {
get_empty_page(address);
return;
}
if (share_page(tmp))
if (share_page(inode,tmp))
return;
if (!(page = get_free_page()))
oom();
/* remember that 1 block is used for header */
block = 1 + tmp/BLOCK_SIZE;
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(current->executable,block);
bread_page(page,current->executable->i_dev,nr);
nr[i] = bmap(inode,block);
bread_page(page,inode->i_dev,nr);
i = tmp + 4096 - current->end_data;
if (i>4095)
i = 0;
tmp = page + 4096;
while (i-- > 0) {
tmp--;
......@@ -410,21 +454,49 @@ void mem_init(long start_mem, long end_mem)
mem_map[i++]=0;
}
void calc_mem(void)
void show_mem(void)
{
int i,j,k,free=0;
long * pg_tbl;
int i,j,k,free=0,total=0;
int shared=0;
unsigned long * pg_tbl;
for(i=0 ; i<PAGING_PAGES ; i++)
if (!mem_map[i]) free++;
printk("%d pages free (of %d)\n\r",free,PAGING_PAGES);
for(i=2 ; i<1024 ; i++) {
printk("Mem-info:\n\r");
for(i=0 ; i<PAGING_PAGES ; i++) {
if (mem_map[i] == USED)
continue;
total++;
if (!mem_map[i])
free++;
else
shared += mem_map[i]-1;
}
printk("%d free pages of %d\n\r",free,total);
printk("%d pages shared\n\r",shared);
k = 0;
for(i=4 ; i<1024 ;) {
if (1&pg_dir[i]) {
pg_tbl=(long *) (0xfffff000 & pg_dir[i]);
for(j=k=0 ; j<1024 ; j++)
if (pg_tbl[j]&1)
k++;
printk("Pg-dir[%d] uses %d pages\n",i,k);
if (pg_dir[i]>HIGH_MEMORY) {
printk("page directory[%d]: %08X\n\r",
i,pg_dir[i]);
continue;
}
if (pg_dir[i]>LOW_MEM)
free++,k++;
pg_tbl=(unsigned long *) (0xfffff000 & pg_dir[i]);
for(j=0 ; j<1024 ; j++)
if ((pg_tbl[j]&1) && pg_tbl[j]>LOW_MEM)
if (pg_tbl[j]>HIGH_MEMORY)
printk("page_dir[%d][%d]: %08X\n\r",
i,j, pg_tbl[j]);
else
k++,free++;
}
i++;
if (!(i&15) && k) {
k++,free++; /* one page/process for task_struct */
printk("Process %d: %d pages\n\r",(i>>4)-1,k);
k = 0;
}
}
printk("Memory found: %d (%d)\n\r",free-shared,total);
}
/*
* linux/mm/swap.c
*
* (C) 1991 Linus Torvalds
*/
/*
* This file should contain most things doing the swapping from/to disk.
* Started 18.12.91
*/
#include <string.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/head.h>
#include <linux/kernel.h>
#define SWAP_BITS (4096<<3)
#define bitop(name,op) \
static inline int name(char * addr,unsigned int nr) \
{ \
int __res; \
__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
:"=g" (__res) \
:"r" (nr),"m" (*(addr)),"0" (0)); \
return __res; \
}
bitop(bit,"")
bitop(setbit,"s")
bitop(clrbit,"r")
static char * swap_bitmap = NULL;
int SWAP_DEV = 0;
/*
* We never page the pages in task[0] - kernel memory.
* We page all other pages.
*/
#define FIRST_VM_PAGE (TASK_SIZE>>12)
#define LAST_VM_PAGE (1024*1024)
#define VM_PAGES (LAST_VM_PAGE - FIRST_VM_PAGE)
static int get_swap_page(void)
{
int nr;
if (!swap_bitmap)
return 0;
for (nr = 1; nr < 32768 ; nr++)
if (clrbit(swap_bitmap,nr))
return nr;
return 0;
}
void swap_free(int swap_nr)
{
if (!swap_nr)
return;
if (swap_bitmap && swap_nr < SWAP_BITS)
if (!setbit(swap_bitmap,swap_nr))
return;
printk("Swap-space bad (swap_free())\n\r");
return;
}
void swap_in(unsigned long *table_ptr)
{
int swap_nr;
unsigned long page;
if (!swap_bitmap) {
printk("Trying to swap in without swap bit-map");
return;
}
if (1 & *table_ptr) {
printk("trying to swap in present page\n\r");
return;
}
swap_nr = *table_ptr >> 1;
if (!swap_nr) {
printk("No swap page in swap_in\n\r");
return;
}
if (!(page = get_free_page()))
oom();
read_swap_page(swap_nr, (char *) page);
if (setbit(swap_bitmap,swap_nr))
printk("swapping in multiply from same page\n\r");
*table_ptr = page | (PAGE_DIRTY | 7);
}
int try_to_swap_out(unsigned long * table_ptr)
{
unsigned long page;
unsigned long swap_nr;
page = *table_ptr;
if (!(PAGE_PRESENT & page))
return 0;
if (page - LOW_MEM > PAGING_MEMORY)
return 0;
if (PAGE_DIRTY & page) {
page &= 0xfffff000;
if (mem_map[MAP_NR(page)] != 1)
return 0;
if (!(swap_nr = get_swap_page()))
return 0;
*table_ptr = swap_nr<<1;
invalidate();
write_swap_page(swap_nr, (char *) page);
free_page(page);
return 1;
}
*table_ptr = 0;
invalidate();
free_page(page);
return 1;
}
/*
* Ok, this has a rather intricate logic - the idea is to make good
* and fast machine code. If we didn't worry about that, things would
* be easier.
*/
int swap_out(void)
{
static int dir_entry = FIRST_VM_PAGE>>10;
static int page_entry = -1;
int counter = VM_PAGES;
int pg_table;
while (counter>0) {
pg_table = pg_dir[dir_entry];
if (pg_table & 1)
break;
counter -= 1024;
dir_entry++;
if (dir_entry >= 1024)
dir_entry = FIRST_VM_PAGE>>10;
}
pg_table &= 0xfffff000;
while (counter-- > 0) {
page_entry++;
if (page_entry >= 1024) {
page_entry = 0;
repeat:
dir_entry++;
if (dir_entry >= 1024)
dir_entry = FIRST_VM_PAGE>>10;
pg_table = pg_dir[dir_entry];
if (!(pg_table&1))
if ((counter -= 1024) > 0)
goto repeat;
else
break;
pg_table &= 0xfffff000;
}
if (try_to_swap_out(page_entry + (unsigned long *) pg_table))
return 1;
}
printk("Out of swap-memory\n\r");
return 0;
}
/*
* Get physical address of first (actually last :-) free page, and mark it
* used. If no free pages left, return 0.
*/
unsigned long get_free_page(void)
{
register unsigned long __res asm("ax");
repeat:
__asm__("std ; repne ; scasb\n\t"
"jne 1f\n\t"
"movb $1,1(%%edi)\n\t"
"sall $12,%%ecx\n\t"
"addl %2,%%ecx\n\t"
"movl %%ecx,%%edx\n\t"
"movl $1024,%%ecx\n\t"
"leal 4092(%%edx),%%edi\n\t"
"rep ; stosl\n\t"
"movl %%edx,%%eax\n"
"1:"
:"=a" (__res)
:"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES),
"D" (mem_map+PAGING_PAGES-1)
:"di","cx","dx");
if (__res >= HIGH_MEMORY)
goto repeat;
if (!__res && swap_out())
goto repeat;
return __res;
}
void init_swapping(void)
{
extern int *blk_size[];
int swap_size,i,j;
if (!SWAP_DEV)
return;
if (!blk_size[MAJOR(SWAP_DEV)]) {
printk("Unable to get size of swap device\n\r");
return;
}
swap_size = blk_size[MAJOR(SWAP_DEV)][MINOR(SWAP_DEV)];
if (!swap_size)
return;
if (swap_size < 100) {
printk("Swap device too small (%d blocks)\n\r",swap_size);
return;
}
swap_size >>= 2;
if (swap_size > SWAP_BITS)
swap_size = SWAP_BITS;
swap_bitmap = (char *) get_free_page();
if (!swap_bitmap) {
printk("Unable to start swapping: out of memory :-)\n\r");
return;
}
read_swap_page(0,swap_bitmap);
if (strncmp("SWAP-SPACE",swap_bitmap+4086,10)) {
printk("Unable to find swap-space signature\n\r");
free_page((long) swap_bitmap);
swap_bitmap = NULL;
return;
}
memset(swap_bitmap+4086,0,10);
for (i = 0 ; i < SWAP_BITS ; i++) {
if (i == 1)
i = swap_size;
if (bit(swap_bitmap,i)) {
printk("Bad swap-space bit-map\n\r");
free_page((long) swap_bitmap);
swap_bitmap = NULL;
return;
}
}
j = 0;
for (i = 1 ; i < swap_size ; i++)
if (bit(swap_bitmap,i))
j++;
if (!j) {
free_page((long) swap_bitmap);
swap_bitmap = NULL;
return;
}
printk("Swap device ok: %d pages (%d bytes) swap-space\n\r",j,j*4096);
}
......@@ -18,6 +18,8 @@
/*
* Changes by tytso to allow root device specification
*
* Added swap-device specification: Linux 20.12.91
*/
#include <stdio.h> /* fprintf */
......@@ -32,11 +34,14 @@
#define MINIX_HEADER 32
#define GCC_HEADER 1024
#define SYS_SIZE 0x2000
#define SYS_SIZE 0x3000
#define DEFAULT_MAJOR_ROOT 3
#define DEFAULT_MINOR_ROOT 6
#define DEFAULT_MAJOR_SWAP 0
#define DEFAULT_MINOR_SWAP 0
/* max nr of sectors of setup: don't change unless you also change
* bootsect etc */
#define SETUP_SECTS 4
......@@ -59,11 +64,12 @@ int main(int argc, char ** argv)
int i,c,id;
char buf[1024];
char major_root, minor_root;
char major_swap, minor_swap;
struct stat sb;
if ((argc != 4) && (argc != 5))
if ((argc < 4) || (argc > 6))
usage();
if (argc == 5) {
if (argc > 4) {
if (strcmp(argv[4], "FLOPPY")) {
if (stat(argv[4], &sb)) {
perror(argv[4]);
......@@ -79,13 +85,35 @@ int main(int argc, char ** argv)
major_root = DEFAULT_MAJOR_ROOT;
minor_root = DEFAULT_MINOR_ROOT;
}
if (argc == 6) {
if (strcmp(argv[5], "NONE")) {
if (stat(argv[5], &sb)) {
perror(argv[5]);
die("Couldn't stat root device.");
}
major_swap = MAJOR(sb.st_rdev);
minor_swap = MINOR(sb.st_rdev);
} else {
major_swap = 0;
minor_swap = 0;
}
} else {
major_swap = DEFAULT_MAJOR_SWAP;
minor_swap = DEFAULT_MINOR_SWAP;
}
fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root);
fprintf(stderr, "Swap device is (%d, %d)\n", major_swap, minor_swap);
if ((major_root != 2) && (major_root != 3) &&
(major_root != 0)) {
fprintf(stderr, "Illegal root device (major = %d)\n",
major_root);
die("Bad root device --- major #");
}
if (major_swap && major_swap != 3) {
fprintf(stderr, "Illegal swap device (major = %d)\n",
major_swap);
die("Bad root device --- major #");
}
for (i=0;i<sizeof buf; i++) buf[i]=0;
if ((id=open(argv[1],O_RDONLY,0))<0)
die("Unable to open 'boot'");
......@@ -109,6 +137,8 @@ int main(int argc, char ** argv)
die("Boot block must be exactly 512 bytes");
if ((*(unsigned short *)(buf+510)) != 0xAA55)
die("Boot block hasn't got boot flag (0xAA55)");
buf[506] = (char) minor_swap;
buf[507] = (char) major_swap;
buf[508] = (char) minor_root;
buf[509] = (char) major_root;
i=write(1,buf,512);
......
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