From 3579bc6f7292b92352f3240ab73bce91815898ed Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:09:10 -0500 Subject: [PATCH] [PATCH] Linux-0.99.9 (April 23, 1993) Bill's math emulator now passes paranoia. Last argument to ioctl is "long". sys_clone() appears. [original announcement below] The latest kernel release is 0.99.9, and can be found on nic.funet.fi: pub/OS/Linux/PEOPLE/Linus, both as patches relative to pl8 and as full sources. The only major new feature is that the ST-0x driver has finally been updated to the scatter-gather code: ST-0x users should with luck get about 5 times the performance on disk-operations.. Seagate code written by Drew Eckhardt. 0.99.9 also fixes: - the FPU-emulator should now handle all rounding-modes correctly, and pass all the paranoia package tests. Patches by Bill Metzenthen. - bootup enhancements by Chrisoph Niemann (but the SVGA mode numbers have changed, so you may have to edit your lilo configuration file and/or the main Makefile to get the mode you normally want) - ext2fs updated to the very latest release. Code by Remy Card and Stephen Tweedie. - various minor patches, some of them cosmetic, some of them fixes to smaller bugs.. Thanks to everybody who sent them in (even though not all made it) It might be a good idea to test it all out, Linus --- Makefile | 4 +- boot/setup.S | 349 ++++++++++------ fs/ext2/Makefile | 4 +- fs/ext2/acl.c | 37 ++ fs/ext2/balloc.c | 107 ++--- fs/ext2/bitmap.c | 5 + fs/ext2/dir.c | 12 +- fs/ext2/file.c | 13 +- fs/ext2/ialloc.c | 63 ++- fs/ext2/inode.c | 137 ++++--- fs/ext2/ioctl.c | 18 + fs/ext2/namei.c | 167 ++++---- fs/ext2/symlink.c | 6 +- fs/file_table.c | 6 + fs/isofs/util.c | 28 +- fs/namei.c | 2 - fs/nfs/dir.c | 5 +- fs/open.c | 2 +- fs/pipe.c | 2 +- fs/select.c | 42 +- fs/super.c | 6 +- include/asm/bitops.h | 6 +- include/asm/dma.h | 6 +- include/asm/segment.h | 22 +- include/linux/ext2_fs.h | 51 ++- include/linux/ext2_fs_i.h | 2 +- include/linux/fs.h | 9 +- include/linux/keyboard.h | 1 + include/linux/mm.h | 1 + include/linux/sched.h | 3 +- include/linux/sys.h | 5 +- include/linux/tty.h | 9 +- include/linux/unistd.h | 1 + include/linux/xd.h | 2 +- init/main.c | 11 +- kernel/FPU-emu/Makefile | 16 +- kernel/FPU-emu/README | 22 +- kernel/FPU-emu/control_w.h | 19 +- kernel/FPU-emu/fpu_arith.c | 62 +-- kernel/FPU-emu/fpu_emu.h | 17 +- kernel/FPU-emu/fpu_entry.c | 24 +- kernel/FPU-emu/fpu_proto.h | 6 +- kernel/FPU-emu/fpu_trig.c | 48 ++- kernel/FPU-emu/poly_atan.c | 14 +- kernel/FPU-emu/poly_l2.c | 26 +- kernel/FPU-emu/poly_sin.c | 8 +- kernel/FPU-emu/poly_tan.c | 18 +- kernel/FPU-emu/polynomial.S | 19 +- kernel/FPU-emu/precision.c | 134 ------ kernel/FPU-emu/reg_add_sub.c | 26 +- kernel/FPU-emu/reg_constant.c | 3 +- kernel/FPU-emu/reg_div.S | 84 ++-- kernel/FPU-emu/reg_ld_str.c | 108 ++++- kernel/FPU-emu/reg_mul.c | 14 +- kernel/FPU-emu/reg_round.S | 369 +++++++++++++++++ kernel/FPU-emu/reg_u_add.S | 112 +++-- kernel/FPU-emu/reg_u_div.S | 341 +++++++--------- kernel/FPU-emu/reg_u_mul.S | 105 +++-- kernel/FPU-emu/reg_u_sub.S | 151 +++---- kernel/FPU-emu/version.h | 2 +- kernel/FPU-emu/wm_sqrt.S | 316 ++++++++++---- kernel/blk_drv/floppy.c | 2 +- kernel/blk_drv/hd.c | 6 +- kernel/blk_drv/scsi/aha1542.c | 5 +- kernel/blk_drv/scsi/scsi.c | 11 +- kernel/blk_drv/scsi/scsi.h | 11 +- kernel/blk_drv/scsi/scsi_ioctl.c | 8 +- kernel/blk_drv/scsi/sd.c | 11 +- kernel/blk_drv/scsi/sd_ioctl.c | 2 +- kernel/blk_drv/scsi/seagate.c | 680 +++++++++++++++++++++++++++---- kernel/blk_drv/scsi/seagate.h | 10 +- kernel/blk_drv/scsi/sr.c | 2 +- kernel/blk_drv/scsi/sr_ioctl.c | 173 +++++++- kernel/blk_drv/scsi/st.c | 6 +- kernel/blk_drv/scsi/ultrastor.c | 2 + kernel/blk_drv/scsi/wd7000.c | 4 +- kernel/blk_drv/xd.c | 26 +- kernel/chr_drv/console.c | 4 +- kernel/chr_drv/keyboard.c | 61 ++- kernel/chr_drv/lp.c | 4 +- kernel/chr_drv/serial.c | 8 +- kernel/chr_drv/tty_io.c | 30 +- kernel/chr_drv/tty_ioctl.c | 2 +- kernel/chr_drv/vt.c | 2 +- kernel/exit.c | 12 +- kernel/fork.c | 9 +- kernel/printk.c | 6 +- kernel/sched.c | 4 +- kernel/signal.c | 14 +- kernel/sys.c | 50 ++- kernel/sys_call.S | 22 +- mm/memory.c | 185 ++++++--- mm/swap.c | 29 +- net/socket.c | 45 +- net/tcp/packet.c | 3 +- net/tcp/udp.c | 1 + net/tcp/we.c | 1 + net/unix.c | 4 - zBoot/Makefile | 5 +- zBoot/inflate.c | 183 +-------- zBoot/misc.c | 118 +++--- zBoot/piggyback.c | 77 +++- zBoot/unzip.c | 16 +- zBoot/xtract.c | 21 +- 104 files changed, 3269 insertions(+), 1814 deletions(-) create mode 100644 fs/ext2/acl.c create mode 100644 fs/ext2/ioctl.c delete mode 100644 kernel/FPU-emu/precision.c create mode 100644 kernel/FPU-emu/reg_round.S diff --git a/Makefile b/Makefile index e7fbf00dc794..c672ec827b7a 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,7 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0 # The number is the same as you would ordinarily press at bootup. # -SVGA_MODE= -DSVGA_MODE=1 +SVGA_MODE= -DSVGA_MODE=3 # # standard CFLAGS @@ -139,7 +139,7 @@ tools/./version.h: tools/version.h tools/version.h: $(CONFIGURE) Makefile @./makever.sh - @echo \#define UTS_RELEASE \"0.99.pl8-`cat .version`\" > tools/version.h + @echo \#define UTS_RELEASE \"0.99.pl9-`cat .version`\" > tools/version.h @echo \#define UTS_VERSION \"`date +%D`\" >> tools/version.h @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h diff --git a/boot/setup.S b/boot/setup.S index 83f310162af3..3dbd9cf4dea7 100644 --- a/boot/setup.S +++ b/boot/setup.S @@ -14,6 +14,9 @@ ! Move PS/2 aux init code to psaux.c ! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92 ! +! some changes and additional features by Christoph Niemann, March 1993 +! (niemann@rubdv15.ETDV.Ruhr-Uni-Bochum.De) +! ! NOTE! These had better be the same as in bootsect.s! #include @@ -53,7 +56,7 @@ start: ! set the keyboard repeat rate to the max mov ax,#0x0305 - mov bx,#0x0000 + xor bx,bx ! clear bx int 0x16 ! check for EGA/VGA and some config parameters @@ -76,7 +79,7 @@ start: call chsvga novga: mov [14],ax mov ah,#0x03 ! read cursor pos - xor bh,bh + xor bh,bh ! clear bh int 0x10 ! save it in known place, con_init fetches mov [0],dx ! it from 0x90000. @@ -89,7 +92,7 @@ novga: mov [14],ax ! Get hd0 data - mov ax,#0x0000 + xor ax,ax ! clear ax mov ds,ax lds si,[4*0x41] mov ax,#INITSEG @@ -102,7 +105,7 @@ novga: mov [14],ax ! Get hd1 data - mov ax,#0x0000 + xor ax,ax ! clear ax mov ds,ax lds si,[4*0x46] mov ax,#INITSEG @@ -126,7 +129,7 @@ no_disk1: mov es,ax mov di,#0x0090 mov cx,#0x10 - mov ax,#0x00 + xor ax,ax ! clear ax cld rep stosb @@ -142,7 +145,6 @@ is_disk1: jz no_psmouse mov [0x1ff],#0xaa ! device present no_psmouse: - ! now we want to move to protected mode ... cli ! no interrupts allowed ! @@ -151,7 +153,7 @@ no_psmouse: ! first we move the system to it's rightful place - mov ax,#0x0000 + xor ax,ax ! clear ax cld ! 'direction'=0, movs moves forward do_move: mov es,ax ! destination segment @@ -184,6 +186,14 @@ end_move: out #0x60,al call empty_8042 +! make sure any possible coprocessor is properly reset.. + + xor ax,ax + out #0xf0,al + call delay + out #0xf1,al + call delay + ! well, that went ok, I hope. Now we have to reprogram the interrupts :-( ! we put them right after the intel-reserved hardware interrupts, at ! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really @@ -194,29 +204,29 @@ end_move: mov al,#0x11 ! initialization sequence out #0x20,al ! send it to 8259A-1 - .word 0x00eb,0x00eb ! jmp $+2, jmp $+2 + call delay out #0xA0,al ! and to 8259A-2 - .word 0x00eb,0x00eb + call delay mov al,#0x20 ! start of hardware int's (0x20) out #0x21,al - .word 0x00eb,0x00eb + call delay mov al,#0x28 ! start of hardware int's 2 (0x28) out #0xA1,al - .word 0x00eb,0x00eb + call delay mov al,#0x04 ! 8259-1 is master out #0x21,al - .word 0x00eb,0x00eb + call delay mov al,#0x02 ! 8259-2 is slave out #0xA1,al - .word 0x00eb,0x00eb + call delay mov al,#0x01 ! 8086 mode for both out #0x21,al - .word 0x00eb,0x00eb + call delay out #0xA1,al - .word 0x00eb,0x00eb + call delay mov al,#0xFF ! mask off all interrupts for now out #0xA1,al - .word 0x00eb,0x00eb + call delay mov al,#0xFB ! mask all irq's but irq2 which out #0x21,al ! is cascaded @@ -245,29 +255,78 @@ flush_instr: ! No timeout is used - if this hangs there is something wrong with ! the machine, and we probably couldn't proceed anyway. empty_8042: - .word 0x00eb,0x00eb + call delay in al,#0x64 ! 8042 status port test al,#1 ! output buffer? jz no_output - .word 0x00eb,0x00eb + call delay in al,#0x60 ! read it jmp empty_8042 no_output: test al,#2 ! is input buffer full? jnz empty_8042 ! yes - loop ret - +! +! Read a key and return the (US-)ascii code in al, scan code in ah +! getkey: - in al,#0x60 ! Quick and dirty... - .word 0x00eb,0x00eb ! jmp $+2, jmp $+2 + xor ah,ah + int 0x16 + ret + +! +! Read a key with a timeout of 30 seconds. The cmos clock is used to get +! the time. +! +getkt: + call gettime + add al,#30 ! wait 30 seconds + cmp al,#60 + jl lminute + sub al,#60 +lminute: + mov cl,al +again: mov ah,#0x01 + int 0x16 + jnz getkey ! key pressed, so get it + call gettime + cmp al,cl + jne again + mov al,#0x20 ! timeout, return default char `space' ret ! ! Flush the keyboard buffer ! -flush: call getkey - cmp al,#0x82 - jae flush +flush: mov ah,#0x01 + int 0x16 + jz empty + xor ah,ah + int 0x16 + jmp flush +empty: ret + +! +! Read the cmos clock. Return the seconds in al +! +gettime: + push cx + mov ah,#0x02 + int 0x1a + mov al,dh ! dh contains the seconds + and al,#0x0f + mov ah,dh + mov cl,#0x04 + shr ah,cl + aad + pop cx + ret + +! +! Delay is needed after doing i/o +! +delay: + .word 0x00eb ! jmp $+2 ret ! Routine trying to recognize type of SVGA-board present (if any) @@ -286,24 +345,26 @@ chsvga: cld cmp ax,#NORMAL_VGA je defvga cmp ax,#EXTENDED_VGA - je extvga + je vga50 cmp ax,#ASK_VGA jne svga lea si,msg1 call prtstr call flush -nokey: call getkey - cmp al,#0x9c ! enter ? +nokey: call getkt + cmp al,#0x0d ! enter ? je svga ! yes - svga selection - cmp al,#0xb9 ! space ? - jne nokey ! no - repeat + cmp al,#0x20 ! space ? + je defvga ! no - repeat + call beep + jmp nokey defvga: mov ax,#0x5019 pop ds ret /* extended vga mode: 80x50 */ -extvga: +vga50: mov ax,#0x1112 - mov bl,#0 + xor bl,bl int 0x10 ! use 8x8 font set (50 lines on VGA) mov ax,#0x1200 mov bl,#0x20 @@ -317,6 +378,18 @@ extvga: pop ds mov ax,#0x5032 ! return 80x50 ret +/* extended vga mode: 80x28 */ +vga28: + pop ax ! clean the stack + mov ax,#0x1111 + xor bl,bl + int 0x10 ! use 9x14 fontset (28 lines on VGA) + mov ah, #0x01 + mov cx,#0x0b0c + int 0x10 ! turn on cursor (scan lines 11 to 12) + pop ds + mov ax,#0x501c ! return 80x28 + ret /* svga modes */ svga: cld lea si,idf1280 ! Check for Orchid F1280 (dingbat@diku.dk) @@ -325,10 +398,16 @@ svga: cld repe cmpsb jne nf1280 - lea si,dscf1280 +isVRAM: lea si,dscf1280 lea di,mof1280 br selmod -nf1280: cld +nf1280: lea si,idVRAM + mov di,#0x10a + mov cx,#0x0c + repe + cmpsb + je isVRAM + cld lea si,idati ! Check ATI 'clues' mov di,#0x31 mov cx,#0x09 @@ -474,7 +553,7 @@ nopara: mov dx,#0x3c4 ! Check Trident 'clues' inc dx in al,dx xchg ah,al - mov al,#0x00 + xor al,al out dx,al in al,dx xchg al,ah @@ -537,11 +616,25 @@ even7: mov al,#0x0c jne novid7 lea si,dscvideo7 lea di,movideo7 -selmod: push si + jmp selmod +novid7: lea si,dsunknown + lea di,mounknown +selmod: xor cx,cx + mov cl,(di) + mov ax,modesave + cmp ax,#ASK_VGA + je askmod + cmp ax,#NORMAL_VGA + je askmod + cmp al,cl + jl gotmode + push si + lea si,msg4 + call prtstr + pop si +askmod: push si lea si,msg2 call prtstr - xor cx,cx - mov cl,(di) pop si push si push cx @@ -549,8 +642,7 @@ tbl: pop bx push bx mov al,bl sub al,cl - call dprnt - call spcing + call modepr lodsw xchg al,ah call dprnt @@ -560,34 +652,42 @@ tbl: pop bx call prnt1 pop ax call dprnt - call docr + push si + lea si,crlf ! print CR+LF + call prtstr + pop si loop tbl pop cx - call docr lea si,msg3 call prtstr pop si - add cl,#0x80 - mov ax,modesave - cmp ax,#ASK_VGA - je nonum - cmp ax,#NORMAL_VGA - jne gotmode + add cl,#0x30 + jmp nonum +nonumb: call beep nonum: call getkey - cmp al,#0x82 - jb nonum - cmp al,#0x8b - je zero + cmp al,#0x30 ! ascii `0' + jb nonumb + cmp al,#0x3a ! ascii `9' + jbe number + cmp al,#0x61 ! ascii `a' + jb nonumb + cmp al,#0x7a ! ascii `z' + ja nonumb + sub al,#0x27 cmp al,cl - ja nonum - jmp nozero -zero: sub al,#0x0a -nozero: sub al,#0x80 - dec al + jae nonumb + sub al,#0x30 + jmp gotmode +number: cmp al,cl + jae nonumb + sub al,#0x30 gotmode: xor ah,ah - add di,ax - inc di + or al,al + beq vga50 push ax + dec ax + beq vga28 + add di,ax mov al,(di) int 0x10 pop ax @@ -596,22 +696,6 @@ gotmode: xor ah,ah lodsw pop ds ret -novid7: - br extvga - -! 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 @@ -627,7 +711,7 @@ fin: ret dprnt: push ax push cx - mov ah,#0x00 + xor ah,ah ! Clear ah mov cl,#0x0a idiv cl cmp al,#0x09 @@ -643,11 +727,31 @@ skip10: mov al,ah pop ax ret +! +! Routine to print the mode number key on screen. Mode numbers +! 0-9 print the ascii values `0' to '9', 10-35 are represented by +! the letters `a' to `z'. This routine prints some spaces around the +! mode no. +! + +modepr: push ax + cmp al,#0x0a + jb digit ! Here is no check for number > 35 + add al,#0x27 +digit: add al,#0x30 + mov modenr, al + push si + lea si, modestring + call prtstr + pop si + pop ax + ret + ! Part of above routine, this one just prints ascii al prnt1: push ax push cx - mov bh,#0x00 + xor bh,bh mov cx,#0x01 mov ah,#0x0e int 0x10 @@ -655,20 +759,8 @@ prnt1: push ax pop ax ret -! Prints + - -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 +beep: mov al,#0x07 + jmp prnt1 gdt: .word 0,0,0,0 ! dummy @@ -691,12 +783,19 @@ gdt_48: .word 0x800 ! gdt limit=2048, 256 GDT entries .word 512+gdt,0x9 ! gdt base = 0X9xxxx -msg1: .ascii "Press to see SVGA-modes available or to continue." +msg1: .ascii "Press to see SVGA-modes available, to continue or wait 30 secs." 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 +msg3: db 0x0d, 0x0a + .ascii "Choose mode by pressing the corresponding number or letter." +crlf: db 0x0d, 0x0a, 0x00 +msg4: .ascii "You passed an undefined mode number to setup. Please choose a new mode." + db 0x0d, 0x0a, 0x0a, 0x07, 0x00 +modestring: .ascii " " +modenr: db 0x00 ! mode number + .ascii ": " + db 0x00 idati: .ascii "761295520" idcandt: .byte 0xa5 @@ -704,37 +803,45 @@ idgenoa: .byte 0x77, 0x00, 0x99, 0x66 idparadise: .ascii "VGA=" idoakvga: .ascii "OAK VGA " idf1280: .ascii "Orchid Technology Fahrenheit 1280" - -! 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 -mooakvga: .byte 0x05, 0x00, 0x07, 0x4f, 0x50, 0x51 -mof1280: .byte 0x02, 0x54, 0x55 +idVRAM: .ascii "Stealth VRAM" + +! Manufacturer: Numofmodes+2: Mode: +! Number of modes is the number of chip-specific svga modes plus the extended +! modes available on any vga (currently 2) + +moati: .byte 0x04, 0x23, 0x33 +moahead: .byte 0x07, 0x22, 0x23, 0x24, 0x2f, 0x34 +mocandt: .byte 0x04, 0x60, 0x61 +mocirrus: .byte 0x06, 0x1f, 0x20, 0x22, 0x31 +moeverex: .byte 0x0c, 0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40 +mogenoa: .byte 0x0c, 0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78 +moparadise: .byte 0x04, 0x55, 0x54 +motrident: .byte 0x09, 0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a +motseng: .byte 0x07, 0x26, 0x2a, 0x23, 0x24, 0x22 +movideo7: .byte 0x08, 0x40, 0x43, 0x44, 0x41, 0x42, 0x45 +mooakvga: .byte 0x07, 0x00, 0x07, 0x4f, 0x50, 0x51 +mof1280: .byte 0x04, 0x54, 0x55 +mounknown: .byte 0x02 ! 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 -dscoakvga: .word 0x2819, 0x5019, 0x843c, 0x8419, 0x842C -dscf1280: .word 0x842b, 0x8419 +! The first two modes are standard vga modes available on any vga. +! mode 0 is 80x50 and mode 1 is 80x28 + +dscati: .word 0x5032, 0x501c, 0x8419, 0x842c +dscahead: .word 0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042 +dsccandt: .word 0x5032, 0x501c, 0x8419, 0x8432 +dsccirrus: .word 0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425 +dsceverex: .word 0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e +dscgenoa: .word 0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b +dscparadise: .word 0x5032, 0x501c, 0x8419, 0x842b +dsctrident: .word 0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c +dsctseng: .word 0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c +dscvideo7: .word 0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c +dscoakvga: .word 0x5032, 0x501c, 0x2819, 0x5019, 0x843c, 0x8419, 0x842C +dscf1280: .word 0x5032, 0x501c, 0x842b, 0x8419 +dsunknown: .word 0x5032, 0x501c modesave: .word SVGA_MODE + .text endtext: diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index d4abbd300850..b91eaf8544c4 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -14,8 +14,8 @@ .s.o: $(AS) -o $*.o $< -OBJS= balloc.o bitmap.o dcache.o dir.o file.o ialloc.o inode.o \ - namei.o symlink.o truncate.o +OBJS= acl.o balloc.o bitmap.o dcache.o dir.o file.o ialloc.o inode.o \ + ioctl.o namei.o symlink.o truncate.o ext2.o: $(OBJS) $(LD) -r -o ext2.o $(OBJS) diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c new file mode 100644 index 000000000000..adf834aeaad1 --- /dev/null +++ b/fs/ext2/acl.c @@ -0,0 +1,37 @@ +/* + * linux/fs/ext2/acl.c + * + * Copyright (C) 1993 Remy Card (card@masi.ibp.fr) + */ + +/* + * This file will contain the Access Control Lists management for the + * second extended file system. + */ + +#include +#include +#include + +/* + * ext2_permission () + * + * Check for access rights + */ +int ext2_permission (struct inode * inode, int mask) +{ + int mode = inode->i_mode; + + /* Special case, access is always granted for root */ + if (suser ()) + return 1; + /* If no ACL, checks using the file mode */ + else if (current->euid == inode->i_uid) + mode >>= 6; + else if (in_group_p (inode->i_gid)) + mode >>= 3; + if (((mode & mask & 0007) == mask)) + return 1; + else + return 0; +} diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index a080f443d3b6..0fdd81b6371a 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -38,31 +38,32 @@ :"a" (0), "c" (size/4), "D" ((long) (addr)) \ :"cx", "di") -static inline int find_first_zero_bit(unsigned *addr, unsigned size) +static inline int find_first_zero_bit (unsigned *addr, unsigned size) { int res; if (!size) return 0; __asm__(" - cld - movl $-1,%%eax - repe; scasl - je 1f - subl $4,%%edi - movl (%%edi),%%eax - notl %%eax - bsfl %%eax,%%edx - jmp 2f -1: xorl %%edx,%%edx -2: subl %%ebx,%%edi - shll $3,%%edi - addl %%edi,%%edx" - :"=d" (res):"c" ((size+31)>>5), "D" (addr), "b" (addr) - :"ax", "bx", "cx", "di"); + cld + movl $-1,%%eax + repe; scasl + je 1f + subl $4,%%edi + movl (%%edi),%%eax + notl %%eax + bsfl %%eax,%%edx + jmp 2f +1: xorl %%edx,%%edx +2: subl %%ebx,%%edi + shll $3,%%edi + addl %%edi,%%edx" + :"=d" (res) + :"c" ((size+31)>>5), "D" (addr), "b" (addr) + :"ax", "bx", "cx", "di"); return res; } -static inline int find_next_zero_bit(unsigned *addr, int size, int offset) +static inline int find_next_zero_bit (unsigned * addr, int size, int offset) { unsigned *p = ((unsigned *) addr) + (offset >> 5); int set = 0, bit = offset & 31, res; @@ -70,32 +71,37 @@ static inline int find_next_zero_bit(unsigned *addr, int size, int offset) if (bit) { /* Look for zero in first byte */ __asm__(" - bsfl %1,%0 - jne 1f - movl $32, %0 -1: " : "=r" (set) : "r" (~(*p >> bit))); - if (set < (32-bit)) + bsfl %1,%0 + jne 1f + movl $32, %0 +1: " + : "=r" (set) + : "r" (~(*p >> bit))); + if (set < (32 - bit)) return set + offset; - set = 32-bit; + set = 32 - bit; p++; } /* No zero yet, search remaining full bytes for a zero */ - res = find_first_zero_bit(p, size-32*(p-addr)); + res = find_first_zero_bit (p, size - 32 * (p - addr)); return (offset + set + res); } -static inline char * find_first_zero_byte(char *addr,int size) +static inline char * find_first_zero_byte (char * addr, int size) { char *res; if (!size) return 0; __asm__(" - cld - mov $0,%%eax - repnz; scasb - jnz 1f - dec %%edi -1: " : "=D" (res) : "0" (addr), "c" (size) : "ax"); + cld + mov $0,%%eax + repnz; scasb + jnz 1f + dec %%edi +1: " + : "=D" (res) + : "0" (addr), "c" (size) + : "ax"); return res; } @@ -155,8 +161,8 @@ static int load__block_bitmap (struct super_block * sb, if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) { if (sb->u.ext2_sb.s_block_bitmap[block_group]) { - if (sb->u.ext2_sb.s_block_bitmap_number[block_group] - != block_group) + if (sb->u.ext2_sb.s_block_bitmap_number[block_group] != + block_group) panic ("load_block_bitmap: " "block_group != block_bitmap_number"); else @@ -189,7 +195,7 @@ static int load__block_bitmap (struct super_block * sb, else brelse (sb->u.ext2_sb.s_block_bitmap [EXT2_MAX_GROUP_LOADED - 1]); - for (j = sb->u.ext2_sb.s_loaded_block_bitmaps-1; j>0; j--) { + for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0; j--) { sb->u.ext2_sb.s_block_bitmap_number[j] = sb->u.ext2_sb.s_block_bitmap_number[j - 1]; sb->u.ext2_sb.s_block_bitmap[j] = @@ -337,14 +343,12 @@ repeat: bitmap_nr = load_block_bitmap (sb, i); bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; if (!bh) { - printk ("Cannot load bitmap_nr %d.\n", - bitmap_nr); + printk ("Cannot load bitmap_nr %d.\n", bitmap_nr); unlock_super (sb); return 0; } #ifdef EXT2FS_DEBUG - printk ("goal is at %d[%d,%d]:%d.\n", - i, group_desc, desc, j); + printk ("goal is at %d[%d,%d]:%d.\n", i, group_desc, desc, j); #endif if (!test_bit(j, bh->b_data)) { #ifdef EXT2FS_DEBUG @@ -356,14 +360,18 @@ repeat: if (j) { /* The goal was occupied; search forward for a free block within the next 32 blocks */ - lmap = (((((unsigned long *) bh->b_data)[j >> 5]) - >> ((j&31)+1)) | - ((((unsigned long *) bh->b_data)[(j>>5)+1]) - <<(31-(j&31)))); + lmap = ((((unsigned long *) bh->b_data)[j >> 5]) >> + ((j & 31) + 1)); + if (j < EXT2_BLOCKS_PER_GROUP(sb) - 32) + lmap |= (((unsigned long *) bh->b_data)[(j >> 5) + 1]) << + (31 - (j & 31)); + else + lmap |= 0xffffffff << (31 - (j & 31)); if (lmap != 0xffffffffl) { - __asm__ ("bsfl %1,%0" : - "=r" (k) : - "r" (~lmap)); k++; + __asm__ ("bsfl %1,%0" + : "=r" (k) + : "r" (~lmap)); + k++; if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) { j += k; goto got_block; @@ -381,9 +389,9 @@ repeat: Search first in the remainder of the current group; then, cyclicly search throught the rest of the groups. */ - p = ((char *) bh->b_data) + (j>>3); + p = ((char *) bh->b_data) + (j >> 3); r = find_first_zero_byte (p, - (EXT2_BLOCKS_PER_GROUP(sb)-j+7)>>3); + (EXT2_BLOCKS_PER_GROUP(sb) - j + 7) >> 3); k = (r - ((char *) bh->b_data)) << 3; if (k < EXT2_BLOCKS_PER_GROUP(sb)) { j = k; @@ -440,7 +448,7 @@ repeat: } r = find_first_zero_byte (bh->b_data, EXT2_BLOCKS_PER_GROUP(sb) >> 3); - j = (r-bh->b_data) << 3; + j = (r - bh->b_data) << 3; if (j >= EXT2_BLOCKS_PER_GROUP(sb)) j = find_first_zero_bit ((unsigned long *) bh->b_data, EXT2_BLOCKS_PER_GROUP(sb)); @@ -466,8 +474,7 @@ got_block: #ifdef EXT2FS_DEBUG printk ("ext2_new_block: found bit %d\n", j); #endif - j += i * EXT2_BLOCKS_PER_GROUP(sb) + - sb->u.ext2_sb.s_first_data_block; + j += i * EXT2_BLOCKS_PER_GROUP(sb) + sb->u.ext2_sb.s_first_data_block; if (j >= sb->u.ext2_sb.s_blocks_count) { printk ("block_group = %d,block=%d\n", i, j); printk ("ext2_new_block: block >= blocks count"); diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c index fcaa960ddc47..1c6945143c0b 100644 --- a/fs/ext2/bitmap.c +++ b/fs/ext2/bitmap.c @@ -6,6 +6,9 @@ #include #include +#include + +#ifdef EXT2FS_DEBUG static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; @@ -21,3 +24,5 @@ unsigned long ext2_count_free (struct buffer_head * map, unsigned numchars) nibblemap[(map->b_data[i] >> 4) & 0xf]; return (sum); } + +#endif diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 937d1b26dd88..1a949864b145 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -37,7 +37,7 @@ static struct file_operations ext2_dir_operations = { NULL, /* write - bad */ ext2_readdir, /* readdir */ NULL, /* select - default */ - NULL, /* ioctl - default */ + ext2_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ @@ -61,8 +61,8 @@ struct inode_operations ext2_dir_inode_operations = { NULL, /* readlink */ NULL, /* follow_link */ NULL, /* bmap */ - ext2_truncate, /* truncate */ - NULL /* permission */ + ext2_truncate, /* truncate */ + ext2_permission /* permission */ }; int ext2_check_dir_entry (char * function, struct inode * dir, @@ -93,17 +93,17 @@ int ext2_check_dir_entry (char * function, struct inode * dir, static int ext2_readdir (struct inode * inode, struct file * filp, struct dirent * dirent, int count) { - unsigned int offset, i; + unsigned int offset, i, err; struct buffer_head * bh; struct ext2_dir_entry * de; struct super_block * sb; - + if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; sb = inode->i_sb; while (filp->f_pos < inode->i_size) { offset = filp->f_pos & (sb->s_blocksize - 1); - bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), 0); + bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err); if (!bh) { filp->f_pos += sb->s_blocksize - offset; continue; diff --git a/fs/ext2/file.c b/fs/ext2/file.c index e2a3f1cbeb23..68279b2fc7b5 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -44,7 +44,7 @@ static struct file_operations ext2_file_operations = { ext2_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* select - default */ - NULL, /* ioctl - default */ + ext2_ioctl, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open is needed */ NULL, /* release */ @@ -66,7 +66,7 @@ struct inode_operations ext2_file_inode_operations = { NULL, /* follow_link */ ext2_bmap, /* bmap */ ext2_truncate, /* truncate */ - NULL /* permission */ + ext2_permission /* permission */ }; /* static */ int ext2_file_read (struct inode * inode, struct file * filp, @@ -79,7 +79,7 @@ struct inode_operations ext2_file_inode_operations = { struct buffer_head * bhreq[NBUF]; struct buffer_head * buflist[NBUF]; struct super_block * sb; - unsigned int size; + unsigned int size, err; if (!inode) { printk ("ext2_file_read: inode = NULL\n"); @@ -127,7 +127,7 @@ struct inode_operations ext2_file_inode_operations = { uptodate = 1; while (blocks) { --blocks; - *bhb = ext2_getblk (inode, block++, 0); + *bhb = ext2_getblk (inode, block++, 0, &err); if (*bhb && !(*bhb)->b_uptodate) { uptodate = 0; bhreq[bhrequest++] = *bhb; @@ -203,6 +203,7 @@ static int ext2_file_write (struct inode * inode, struct file * filp, struct buffer_head * bh; char * p; struct super_block * sb; + int err; if (!inode) { printk("ext2_file_write: inode = NULL\n"); @@ -223,13 +224,13 @@ static int ext2_file_write (struct inode * inode, struct file * filp, pos = filp->f_pos; written = 0; while (written < count) { - bh = ext2_getblk (inode, pos / sb->s_blocksize, 1); + bh = ext2_getblk (inode, pos / sb->s_blocksize, 1, &err); if (!bh) { #ifdef EXT2FS_DEBUG printk ("ext2_file_write: ext2_getblk returned NULL\n"); #endif if (!written) - written = -ENOSPC; + written = err; break; } c = sb->s_blocksize - (pos % sb->s_blocksize); diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index e0df46f6ca4e..39fcb6cc0078 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -32,27 +32,28 @@ #include -static inline int find_first_zero_bit(unsigned *addr, unsigned size) +static inline int find_first_zero_bit(unsigned * addr, unsigned size) { int res; if (!size) return 0; __asm__(" - cld - movl $-1,%%eax - repe; scasl - je 1f - subl $4,%%edi - movl (%%edi),%%eax - notl %%eax - bsfl %%eax,%%edx - jmp 2f -1: xorl %%edx,%%edx -2: subl %%ebx,%%edi - shll $3,%%edi - addl %%edi,%%edx" - :"=d" (res):"c" ((size+31)>>5), "D" (addr), "b" (addr) - :"ax", "bx", "cx", "di"); + cld + movl $-1,%%eax + repe; scasl + je 1f + subl $4,%%edi + movl (%%edi),%%eax + notl %%eax + bsfl %%eax,%%edx + jmp 2f +1: xorl %%edx,%%edx +2: subl %%ebx,%%edi + shll $3,%%edi + addl %%edi,%%edx" + : "=d" (res) + :"c" ((size + 31) >> 5), "D" (addr), "b" (addr) + : "ax", "bx", "cx", "di"); return res; } @@ -289,17 +290,16 @@ static void inc_inode_version (struct inode * inode, EXT2_INODES_PER_GROUP(inode->i_sb)) % EXT2_INODES_PER_BLOCK(inode->i_sb)); raw_inode->i_version++; - if (!S_ISFIFO(mode)) - inode->u.ext2_i.i_version = raw_inode->i_version; + inode->u.ext2_i.i_version = raw_inode->i_version; bh->b_dirt = 1; brelse (bh); } -static struct ext2_group_desc * -get_group_desc(struct super_block *sb, int group) +static struct ext2_group_desc * get_group_desc(struct super_block * sb, + int group) { struct ext2_group_desc * gdp; - if (group >= sb->u.ext2_sb.s_groups_count || group < 0 ) + if (group >= sb->u.ext2_sb.s_groups_count || group < 0 ) panic ("ext2: get_group_desc: Invalid group\n"); if (!sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)]) panic ("ext2: get_group_desc: Descriptor not loaded"); @@ -340,7 +340,7 @@ repeat: gdp = NULL; i=0; if (S_ISDIR(mode)) { - avefreei = es->s_free_inodes_count / + avefreei = es->s_free_inodes_count / sb->u.ext2_sb.s_groups_count; /* I am not yet convinced that this next bit is necessary. i = dir->u.ext2_i.i_block_group; @@ -377,11 +377,11 @@ repeat: gdp = tmp; else { /* Use a quadratic hash to find a group with a free inode */ - for (j=1; ju.ext2_sb.s_groups_count; j<<=1) { - i+=j; - if (i>=sb->u.ext2_sb.s_groups_count) - i-=sb->u.ext2_sb.s_groups_count; - tmp = get_group_desc(sb,i); + for (j = 1; j < sb->u.ext2_sb.s_groups_count; j <<= 1) { + i += j; + if (i >= sb->u.ext2_sb.s_groups_count) + i -= sb->u.ext2_sb.s_groups_count; + tmp = get_group_desc(sb, i); if (tmp->bg_free_inodes_count) { gdp = tmp; break; @@ -391,9 +391,9 @@ repeat: if (!gdp) { /* That failed: try linear search for a free inode */ i = dir->u.ext2_i.i_block_group + 2; - for (j=2; ju.ext2_sb.s_groups_count; j++) { - if (++i > sb->u.ext2_sb.s_groups_count) - i=0; + for (j = 2; j < sb->u.ext2_sb.s_groups_count; j++) { + if (++i >= sb->u.ext2_sb.s_groups_count) + i = 0; tmp = get_group_desc(sb,i); if (tmp->bg_free_inodes_count) { gdp = tmp; @@ -454,8 +454,7 @@ repeat: inode->u.ext2_i.i_file_acl = 0; inode->u.ext2_i.i_dir_acl = 0; inode->u.ext2_i.i_dtime = 0; - if (!S_ISFIFO(mode)) - inode->u.ext2_i.i_block_group = i; + inode->u.ext2_i.i_block_group = i; inode->i_op = NULL; inc_inode_version (inode, gdp, mode); #ifdef EXT2FS_DEBUG diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 0451fb1d5410..a82ae1d9862b 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -19,13 +19,14 @@ #include #include #include +#include #include #include void ext2_put_inode (struct inode * inode) { - if (inode->i_nlink) + if (inode->i_nlink || inode->i_ino == EXT2_ACL_INO) return; inode->i_size = 0; if (inode->i_blocks) @@ -69,6 +70,8 @@ static struct super_operations ext2_sops = { ext2_statfs }; +#ifdef EXT2FS_PRE_02B_COMPAT + static int convert_pre_02b_fs (struct super_block * sb, struct buffer_head * bh) { @@ -107,6 +110,8 @@ static int convert_pre_02b_fs (struct super_block * sb, return 1; } +#endif + struct super_block * ext2_read_super (struct super_block * s, void * data, int silent) { @@ -115,7 +120,9 @@ struct super_block * ext2_read_super (struct super_block * s, void * data, int dev = s->s_dev; int bh_count; int i, j; +#ifdef EXT2FS_PRE_02B_COMPAT int fs_converted = 0; +#endif lock_super (s); if (!(bh = bread (dev, 1, BLOCK_SIZE))) { @@ -151,6 +158,7 @@ struct super_block * ext2_read_super (struct super_block * s, void * data, s->u.ext2_sb.s_was_mounted_valid = es->s_valid; s->u.ext2_sb.s_rename_lock = 0; s->u.ext2_sb.s_rename_wait = NULL; +#ifdef EXT2FS_PRE_02B_COMPAT if (s->s_magic == EXT2_OLD_SUPER_MAGIC) { if (es->s_blocks_count > 262144) { /* fs > 256 MB can't be converted */ @@ -180,6 +188,7 @@ struct super_block * ext2_read_super (struct super_block * s, void * data, printk ("EXT2-fs: conversion succeeded !!!\n"); fs_converted = 1; } +#endif if (s->s_magic != EXT2_SUPER_MAGIC) { s->s_dev = 0; unlock_super (s); @@ -201,8 +210,8 @@ struct super_block * ext2_read_super (struct super_block * s, void * data, "running e2fsck is recommended\n"); s->u.ext2_sb.s_groups_count = (s->u.ext2_sb.s_blocks_count - s->u.ext2_sb.s_first_data_block + - (EXT2_BLOCK_SIZE(s) * 8) - 1) / - (EXT2_BLOCK_SIZE(s) * 8); + EXT2_BLOCKS_PER_GROUP(s) - 1) / + EXT2_BLOCKS_PER_GROUP(s); for (i = 0; i < EXT2_MAX_GROUP_DESC; i++) s->u.ext2_sb.s_group_desc[i] = NULL; bh_count = (s->u.ext2_sb.s_groups_count + @@ -247,11 +256,13 @@ struct super_block * ext2_read_super (struct super_block * s, void * data, bh->b_dirt = 1; s->s_dirt = 1; } +#ifdef EXT2FS_PRE_02B_COMPAT if (fs_converted) { for (i = 0; i < bh_count; i++) s->u.ext2_sb.s_group_desc[i]->b_dirt = 1; s->s_dirt = 1; } +#endif printk ("[EXT II FS %s, bs=%d, fs=%d, gc=%d, bpg=%d, ipg=%d]\n", EXT2FS_VERSION, s->s_blocksize, s->u.ext2_sb.s_frag_size, s->u.ext2_sb.s_groups_count, @@ -412,7 +423,7 @@ int ext2_bmap (struct inode * inode, int block) } static struct buffer_head * inode_getblk (struct inode * inode, int nr, - int create, int new_block) + int create, int new_block, int *err) { int tmp, goal = 0; unsigned long * p; @@ -431,8 +442,10 @@ repeat: } if (!create || new_block >= (current->rlim[RLIMIT_FSIZE].rlim_cur >> - EXT2_BLOCK_SIZE_BITS(inode->i_sb))) + EXT2_BLOCK_SIZE_BITS(inode->i_sb))) { + *err = -EFBIG; return NULL; + } if (inode->u.ext2_i.i_next_alloc_block == new_block) goal = inode->u.ext2_i.i_next_alloc_goal; #ifdef EXT2FS_DEBUG @@ -474,9 +487,10 @@ repeat: static struct buffer_head * block_getblk (struct inode * inode, struct buffer_head * bh, int nr, - int create, int blocksize, int new_block) + int create, int blocksize, + int new_block, int *err) { - int tmp, goal; + int tmp, goal = 0; unsigned long * p; struct buffer_head * result; int blocks = inode->i_sb->s_blocksize / 512; @@ -507,14 +521,15 @@ repeat: (current->rlim[RLIMIT_FSIZE].rlim_cur >> EXT2_BLOCK_SIZE_BITS(inode->i_sb))) { brelse (bh); + *err = -EFBIG; return NULL; } if (inode->u.ext2_i.i_next_alloc_block == new_block) goal = inode->u.ext2_i.i_next_alloc_goal; if (!goal) { for (tmp = nr-1; tmp>=0; tmp--) { - if (bh->b_data[tmp]) { - goal = bh->b_data[tmp]; + if (((unsigned long *) bh->b_data)[tmp]) { + goal = ((unsigned long *)bh->b_data)[tmp]; break; } } @@ -547,12 +562,13 @@ repeat: } struct buffer_head * ext2_getblk (struct inode * inode, int block, - int create) + int create, int *err) { struct buffer_head * bh; int b; unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); + *err = -EIO; if (block < 0) { printk ("ext2_getblk: block < 0\n"); return NULL; @@ -564,8 +580,8 @@ struct buffer_head * ext2_getblk (struct inode * inode, int block, return NULL; } /* If this is a sequential block allocation, set the next_alloc_block - to this block now so that all the indblock and data block allocations - use the same goal zone */ + to this block now so that all the indblock and data block + allocations use the same goal zone */ #ifdef EXT2FS_DEBUG printk ("ext2_getblk: block %d, next %d, goal %d.\n", block, inode->u.ext2_i.i_next_alloc_block, @@ -576,38 +592,40 @@ struct buffer_head * ext2_getblk (struct inode * inode, int block, inode->u.ext2_i.i_next_alloc_goal++; } + *err = -ENOSPC; b = block; if (block < EXT2_NDIR_BLOCKS) - return inode_getblk (inode, block, create, b); + return inode_getblk (inode, block, create, b, err); block -= EXT2_NDIR_BLOCKS; if (block < addr_per_block) { - bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b); + bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err); return block_getblk (inode, bh, block, create, - inode->i_sb->s_blocksize, b); + inode->i_sb->s_blocksize, b, err); } block -= addr_per_block; if (block < addr_per_block * addr_per_block) { - bh = inode_getblk (inode, EXT2_DIND_BLOCK, create,b ); + bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err); bh = block_getblk (inode, bh, block / addr_per_block, create, - inode->i_sb->s_blocksize, b); + inode->i_sb->s_blocksize, b, err); return block_getblk (inode, bh, block & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b); + create, inode->i_sb->s_blocksize, b, err); } block -= addr_per_block * addr_per_block; - bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b); - bh = block_getblk (inode, bh, block / (addr_per_block * addr_per_block), - create, inode->i_sb->s_blocksize, b); - bh = block_getblk (inode, bh, (block / addr_per_block) & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b); + bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err); + bh = block_getblk (inode, bh, block/(addr_per_block * addr_per_block), + create, inode->i_sb->s_blocksize, b, err); + bh = block_getblk (inode, bh, (block/addr_per_block) & (addr_per_block - 1), + create, inode->i_sb->s_blocksize, b, err); return block_getblk (inode, bh, block & (addr_per_block - 1), create, - inode->i_sb->s_blocksize, b); + inode->i_sb->s_blocksize, b, err); } -struct buffer_head * ext2_bread (struct inode * inode, int block, int create) +struct buffer_head * ext2_bread (struct inode * inode, int block, + int create, int *err) { struct buffer_head * bh; - bh = ext2_getblk (inode, block, create); + bh = ext2_getblk (inode, block, create, err); if (!bh || bh->b_uptodate) return bh; ll_rw_block (READ, 1, &bh); @@ -615,6 +633,7 @@ struct buffer_head * ext2_bread (struct inode * inode, int block, int create) if (bh->b_uptodate) return bh; brelse (bh); + *err = -EIO; return NULL; } @@ -628,7 +647,8 @@ void ext2_read_inode (struct inode * inode) unsigned long block; struct ext2_group_desc * gdp; - if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) || + if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_INO && + inode->i_ino < EXT2_FIRST_INO) || inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) { printk ("ext2_read_inode: bad inode number of dev %0x04: %d\n", inode->i_dev, inode->i_ino); @@ -658,29 +678,28 @@ void ext2_read_inode (struct inode * inode) inode->i_atime = raw_inode->i_atime; inode->i_ctime = raw_inode->i_ctime; inode->i_mtime = raw_inode->i_mtime; - if (!S_ISFIFO(inode->i_mode)) - inode->u.ext2_i.i_dtime = raw_inode->i_dtime; + inode->u.ext2_i.i_dtime = raw_inode->i_dtime; inode->i_blksize = inode->i_sb->s_blocksize; inode->i_blocks = raw_inode->i_blocks; - if (!S_ISFIFO(inode->i_mode)) { - inode->u.ext2_i.i_flags = raw_inode->i_flags; - inode->u.ext2_i.i_faddr = raw_inode->i_faddr; - inode->u.ext2_i.i_frag = raw_inode->i_frag; - inode->u.ext2_i.i_fsize = raw_inode->i_fsize; - inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl; - inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl; - inode->u.ext2_i.i_version = raw_inode->i_version; - inode->u.ext2_i.i_block_group = block_group; - inode->u.ext2_i.i_next_alloc_block = 0; - inode->u.ext2_i.i_next_alloc_goal = 0; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = raw_inode->i_block[0]; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; - } + inode->u.ext2_i.i_flags = raw_inode->i_flags; + inode->u.ext2_i.i_faddr = raw_inode->i_faddr; + inode->u.ext2_i.i_frag = raw_inode->i_frag; + inode->u.ext2_i.i_fsize = raw_inode->i_fsize; + inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl; + inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl; + inode->u.ext2_i.i_version = raw_inode->i_version; + inode->u.ext2_i.i_block_group = block_group; + inode->u.ext2_i.i_next_alloc_block = 0; + inode->u.ext2_i.i_next_alloc_goal = 0; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + inode->i_rdev = raw_inode->i_block[0]; + else for (block = 0; block < EXT2_N_BLOCKS; block++) + inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; brelse (bh); inode->i_op = NULL; - if (S_ISREG(inode->i_mode)) + if (inode->i_ino == EXT2_ACL_INO) + /* Nothing to do */ ; + else if (S_ISREG(inode->i_mode)) inode->i_op = &ext2_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &ext2_dir_inode_operations; @@ -735,20 +754,18 @@ void ext2_write_inode (struct inode * inode) raw_inode->i_ctime = inode->i_ctime; raw_inode->i_mtime = inode->i_mtime; raw_inode->i_blocks = inode->i_blocks; - if (!S_ISFIFO(inode->i_mode)) { - raw_inode->i_dtime = inode->u.ext2_i.i_dtime; - raw_inode->i_flags = inode->u.ext2_i.i_flags; - raw_inode->i_faddr = inode->u.ext2_i.i_faddr; - raw_inode->i_frag = inode->u.ext2_i.i_frag; - raw_inode->i_fsize = inode->u.ext2_i.i_fsize; - raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl; - raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl; - raw_inode->i_version = inode->u.ext2_i.i_version; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - raw_inode->i_block[0] = inode->i_rdev; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; - } + raw_inode->i_dtime = inode->u.ext2_i.i_dtime; + raw_inode->i_flags = inode->u.ext2_i.i_flags; + raw_inode->i_faddr = inode->u.ext2_i.i_faddr; + raw_inode->i_frag = inode->u.ext2_i.i_frag; + raw_inode->i_fsize = inode->u.ext2_i.i_fsize; + raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl; + raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl; + raw_inode->i_version = inode->u.ext2_i.i_version; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_block[0] = inode->i_rdev; + else for (block = 0; block < EXT2_N_BLOCKS; block++) + raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; bh->b_dirt = 1; inode->i_dirt = 0; brelse (bh); diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c new file mode 100644 index 000000000000..42621729e3b2 --- /dev/null +++ b/fs/ext2/ioctl.c @@ -0,0 +1,18 @@ +/* + * linux/fs/ext2/ioctl.c + * + * Copyright (C) 1993 Remy Card (card@masi.ibp.fr) + */ + +#include +#include +#include + +int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, + unsigned long arg) +{ +#ifdef EXT2FS_DEBUG + printk ("ext2_ioctl: cmd = %d, arg = %d\n", cmd, arg); +#endif + return -EINVAL; +} diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index c133e6a098c1..d000efc99167 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -25,7 +25,7 @@ * truncated. Else they will be disallowed. */ /* #define NO_TRUNCATE */ - + /* * ok, we cannot use strncmp, as the name is not in our data space. * Thus we'll have to use ext2_match. No big problem. ext2_match also makes @@ -33,25 +33,26 @@ * * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. */ -static int ext2_match (int len, const char * name, +static int ext2_match (int len, const char * const name, struct ext2_dir_entry * de) { - register int same __asm__("ax"); + unsigned char same; if (!de || !de->inode || len > EXT2_NAME_LEN) return 0; /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && (de->name[0] == '.') && (de->name[1] == '\0')) + if (!len && de->name_len == 1 && (de->name[0] == '.') && + (de->name[1] == '\0')) return 1; - if (len < EXT2_NAME_LEN && len != de->name_len) + if (len != de->name_len) return 0; __asm__("cld\n\t" "repe ; cmpsb\n\t" - "setz %%al" - :"=a" (same) - :"0" (0), "S" ((long) name), "D" ((long) de->name), "c" (len) + "setz %0" + :"=q" (same) + :"S" ((long) name), "D" ((long) de->name), "c" (len) :"cx", "di", "si"); - return same; + return (int) same; } /* @@ -67,7 +68,7 @@ static int ext2_match (int len, const char * name, * entries need it */ static struct buffer_head * ext2_find_entry (struct inode * dir, - const char * name, int namelen, + const char * const name, int namelen, struct ext2_dir_entry ** res_dir, struct ext2_dir_entry ** prev_dir) { @@ -75,6 +76,7 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, struct buffer_head * bh; struct ext2_dir_entry * de; struct super_block * sb; + int err; *res_dir = NULL; if (!dir) @@ -87,7 +89,7 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, if (namelen > EXT2_NAME_LEN) namelen = EXT2_NAME_LEN; #endif - bh = ext2_bread (dir, 0, 0); + bh = ext2_bread (dir, 0, 0, &err); if (!bh) return NULL; if (prev_dir) @@ -95,12 +97,13 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, offset = 0; de = (struct ext2_dir_entry *) bh->b_data; while (offset < dir->i_size) { - if ((char *)de >= sb->s_blocksize + bh->b_data) { + if (!bh || (char *)de >= sb->s_blocksize + bh->b_data) { brelse (bh); - bh = NULL; - bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0); - if (!bh) + bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0, &err); + if (!bh) { + offset += sb->s_blocksize; continue; + } de = (struct ext2_dir_entry *) bh->b_data; if (prev_dir) *prev_dir = NULL; @@ -173,7 +176,8 @@ int ext2_lookup (struct inode * dir, const char * name, int len, */ static struct buffer_head * ext2_add_entry (struct inode * dir, const char * name, int namelen, - struct ext2_dir_entry ** res_dir) + struct ext2_dir_entry ** res_dir, + int *err) { int i; long offset; @@ -182,6 +186,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, struct ext2_dir_entry * de, * de1; struct super_block * sb; + *err = -EINVAL; *res_dir = NULL; if (!dir) return NULL; @@ -195,29 +200,30 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, #endif if (!namelen) return NULL; - bh = ext2_bread (dir, 0, 0); + bh = ext2_bread (dir, 0, 0, err); if (!bh) return NULL; rec_len = EXT2_DIR_REC_LEN(namelen); offset = 0; de = (struct ext2_dir_entry *) bh->b_data; + *err = -ENOSPC; while (1) { if ((char *)de >= sb->s_blocksize + bh->b_data) { brelse (bh); bh = NULL; - bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0); - if (!bh) { + bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, err); + if (!bh) + return NULL; + if (dir->i_size <= offset) { #ifdef EXT2FS_DEBUG printk ("ext2_add_entry: creating next block\n"); #endif - bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1); - if (!bh) - return NULL; de = (struct ext2_dir_entry *) bh->b_data; de->inode = 0; de->rec_len = sb->s_blocksize; - dir->i_size += sb->s_blocksize; + dir->i_size = offset + sb->s_blocksize; dir->i_dirt = 1; + dir->i_ctime = CURRENT_TIME; } else { #ifdef EXT2FS_DEBUG printk ("ext2_add_entry: skipping to next block\n"); @@ -227,9 +233,17 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, } if (! ext2_check_dir_entry ("ext2_add_entry", dir, de, bh, offset)) { + *err = -ENOENT; brelse (bh); return NULL; } + if (de->inode) { + if (ext2_match (namelen, name, de)) { + *err = -EEXIST; + brelse (bh); + return NULL; + } + } if ((!de->inode && de->rec_len >= rec_len) || (de->rec_len >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) { offset += de->rec_len; @@ -245,7 +259,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, de->name_len = namelen; for (i = 0; i < namelen ; i++) de->name[i] = name [i]; - dir->i_mtime = CURRENT_TIME; + dir->i_mtime = dir->i_ctime = CURRENT_TIME; bh->b_dirt = 1; *res_dir = de; return bh; @@ -276,6 +290,7 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode, struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; + int err; *result = NULL; if (!dir) @@ -288,13 +303,13 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode, inode->i_op = &ext2_file_inode_operations; inode->i_mode = mode; inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de); + bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { inode->i_nlink --; inode->i_dirt = 1; iput (inode); iput (dir); - return -ENOSPC; + return err; } de->inode = inode->i_ino; #ifndef DONT_USE_DCACHE @@ -314,6 +329,7 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; + int err; if (!dir) return -ENOENT; @@ -350,13 +366,13 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, inode->i_rdev = rdev; inode->i_mtime = inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de); + bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { inode->i_nlink --; inode->i_dirt = 1; iput (inode); iput (dir); - return -ENOSPC; + return err; } de->inode = inode->i_ino; #ifndef DONT_USE_DCACHE @@ -375,13 +391,20 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) struct inode * inode; struct buffer_head * bh, * dir_block; struct ext2_dir_entry * de; - + int err; + + if (!dir) + return -ENOENT; bh = ext2_find_entry (dir, name, len, &de, NULL); if (bh) { brelse (bh); iput (dir); return -EEXIST; } + if (dir->i_nlink >= EXT2_LINK_MAX) { + iput (dir); + return -EMLINK; + } inode = ext2_new_inode (dir, S_IFDIR); if (!inode) { iput (dir); @@ -390,13 +413,13 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) inode->i_op = &ext2_dir_inode_operations; inode->i_size = inode->i_sb->s_blocksize; inode->i_mtime = inode->i_atime = CURRENT_TIME; - dir_block = ext2_bread (inode, 0, 1); + dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { iput (dir); inode->i_nlink --; inode->i_dirt = 1; iput (inode); - return -ENOSPC; + return err; } inode->i_blocks = inode->i_sb->s_blocksize / 512; de = (struct ext2_dir_entry *) dir_block->b_data; @@ -416,12 +439,12 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de); + bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { iput (dir); inode->i_nlink = 0; iput (inode); - return -ENOSPC; + return err; } de->inode = inode->i_ino; #ifndef DONT_USE_DCACHE @@ -446,10 +469,11 @@ static int empty_dir (struct inode * inode) struct buffer_head * bh; struct ext2_dir_entry * de, * de1; struct super_block * sb; + int err; sb = inode->i_sb; if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) || - !(bh = ext2_bread (inode, 0, 0))) { + !(bh = ext2_bread (inode, 0, 0, &err))) { printk ("warning - bad directory (dev %04x, dir %d)\n", inode->i_dev, inode->i_ino); return 1; @@ -467,7 +491,7 @@ static int empty_dir (struct inode * inode) while (offset < inode->i_size ) { if ((void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { brelse (bh); - bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1); + bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &err); if (!bh) { offset += sb->s_blocksize; continue; @@ -497,6 +521,8 @@ int ext2_rmdir (struct inode * dir, const char * name, int len) struct buffer_head * bh; struct ext2_dir_entry * de, * pde; + if (!dir) + return -ENOENT; inode = NULL; bh = ext2_find_entry (dir, name, len, &de, &pde); retval = -ENOENT; @@ -530,12 +556,6 @@ int ext2_rmdir (struct inode * dir, const char * name, int len) ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len); #endif ext2_delete_entry (de, pde); -#if 0 - if (pde) - pde->rec_len += de->rec_len; - else - de->inode = 0; -#endif bh->b_dirt = 1; inode->i_nlink = 0; inode->i_dirt = 1; @@ -557,6 +577,8 @@ int ext2_unlink (struct inode * dir, const char * name, int len) struct buffer_head * bh; struct ext2_dir_entry * de, * pde; + if (!dir) + return -ENOENT; retval = -ENOENT; inode = NULL; bh = ext2_find_entry (dir, name, len, &de, &pde); @@ -580,12 +602,6 @@ int ext2_unlink (struct inode * dir, const char * name, int len) ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len); #endif ext2_delete_entry (de, pde); -#if 0 - if (pde) - pde->rec_len += de->rec_len; - else - de->inode = 0; -#endif bh->b_dirt = 1; dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; @@ -607,7 +623,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len, struct inode * inode = NULL; struct buffer_head * bh = NULL, * name_block = NULL; char * link; - int i; + int i, err; int l; char c; @@ -624,13 +640,13 @@ int ext2_symlink (struct inode * dir, const char * name, int len, #ifdef EXT2FS_DEBUG printk ("ext2_symlink: l=%d, normal symlink\n", l); #endif - name_block = ext2_bread (inode, 0, 1); + name_block = ext2_bread (inode, 0, 1, &err); if (!name_block) { iput (dir); inode->i_nlink --; inode->i_dirt = 1; iput (inode); - return -ENOSPC; + return err; } link = name_block->b_data; } else { @@ -658,13 +674,13 @@ int ext2_symlink (struct inode * dir, const char * name, int len, iput (dir); return -EEXIST; } - bh = ext2_add_entry (dir, name, len, &de); + bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { inode->i_nlink --; inode->i_dirt = 1; iput (inode); iput (dir); - return -ENOSPC; + return err; } de->inode = inode->i_ino; #ifndef DONT_USE_DCACHE @@ -683,13 +699,14 @@ int ext2_link (struct inode * oldinode, struct inode * dir, { struct ext2_dir_entry * de; struct buffer_head * bh; + int err; if (S_ISDIR(oldinode->i_mode)) { iput (oldinode); iput (dir); return -EPERM; } - if (oldinode->i_nlink > 32000) { + if (oldinode->i_nlink > EXT2_LINK_MAX) { iput (oldinode); iput (dir); return -EMLINK; @@ -701,11 +718,11 @@ int ext2_link (struct inode * oldinode, struct inode * dir, iput (oldinode); return -EEXIST; } - bh = ext2_add_entry (dir, name, len, &de); + bh = ext2_add_entry (dir, name, len, &de, &err); if (!bh) { iput (dir); iput (oldinode); - return -ENOSPC; + return err; } de->inode = oldinode->i_ino; #ifndef DONT_USE_DCACHE @@ -793,6 +810,7 @@ start_up: old_inode = iget (old_dir->i_sb, old_de->inode); if (!old_inode) goto end_rename; + retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->euid != old_inode->i_uid && current->euid != old_dir->i_uid && !suser()) @@ -810,8 +828,18 @@ start_up: goto end_rename; } if (new_inode && S_ISDIR(new_inode->i_mode)) { - retval = -EEXIST; - goto end_rename; + retval = -EISDIR; + if (!S_ISDIR(old_inode->i_mode)) + goto end_rename; + retval = -EINVAL; + if (subdir (new_dir, old_inode)) + goto end_rename; + retval = -ENOTEMPTY; + if (!empty_dir (new_inode)) + goto end_rename; + retval = -EBUSY; + if (new_inode->i_count > 1) + goto end_rename; } retval = -EPERM; if (new_inode && (new_dir->i_mode & S_ISVTX) && @@ -819,25 +847,24 @@ start_up: current->euid != new_dir->i_uid && !suser()) goto end_rename; if (S_ISDIR(old_inode->i_mode)) { - retval = -EEXIST; - if (new_bh) - goto end_rename; - retval = -EACCES; - if (!permission (old_inode, MAY_WRITE)) + retval = -ENOTDIR; + if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; if (subdir (new_dir, old_inode)) goto end_rename; - retval = -EIO; - dir_bh = ext2_bread (old_inode, 0, 0); + dir_bh = ext2_bread (old_inode, 0, 0, &retval); if (!dir_bh) goto end_rename; if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; + retval = -EMLINK; + if (!new_inode && new_dir->i_nlink >= EXT2_LINK_MAX) + goto end_rename; } if (!new_bh) - new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de); - retval = -ENOSPC; + new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de, + &retval); if (!new_bh) goto end_rename; /* sanity checking before doing the rename - avoid races */ @@ -860,14 +887,6 @@ start_up: new_de->rec_len += old_de->rec_len; else ext2_delete_entry (old_de, pde); -#if 0 - if (((char *) new_de) + new_de->rec_len == (char *) old_de) - new_de->rec_len += old_de->rec_len; - else if (pde) - pde->rec_len += old_de->rec_len; - else - old_de->inode = 0; -#endif if (new_inode) { new_inode->i_nlink --; new_inode->i_dirt = 1; diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 64904d977e41..2c255eb3205d 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -72,7 +72,7 @@ static int ext2_follow_link(struct inode * dir, struct inode * inode, return -ELOOP; } if (inode->i_blocks) { - if (!(bh = ext2_bread (inode, 0, 0))) { + if (!(bh = ext2_bread (inode, 0, 0, &error))) { iput (dir); iput (inode); return -EIO; @@ -93,7 +93,7 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh = NULL; char * link; - int i; + int i, err; char c; if (!S_ISLNK(inode->i_mode)) { @@ -103,7 +103,7 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen) if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; if (inode->i_blocks) { - bh = ext2_bread (inode, 0, 0); + bh = ext2_bread (inode, 0, 0, &err); if (!bh) { iput (inode); return 0; diff --git a/fs/file_table.c b/fs/file_table.c index fe0f44a685a9..fb55105f2dca 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -9,6 +9,12 @@ struct file file_table[NR_FILE]; +unsigned long file_table_init(unsigned long start, unsigned long end) +{ + memset(file_table,0,sizeof(file_table)); + return start; +} + struct file * get_empty_filp(void) { int i; diff --git a/fs/isofs/util.c b/fs/isofs/util.c index a91c109bc56c..97783936c83d 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -11,15 +11,13 @@ int -isonum_711 (p) -char *p; +isonum_711 (char * p) { return (*p & 0xff); } int -isonum_712 (p) -char *p; +isonum_712 (char * p) { int val; @@ -30,22 +28,19 @@ char *p; } int -isonum_721 (p) -char *p; +isonum_721 (char * p) { return ((p[0] & 0xff) | ((p[1] & 0xff) << 8)); } int -isonum_722 (p) -char *p; +isonum_722 (char * p) { return (((p[0] & 0xff) << 8) | (p[1] & 0xff)); } int -isonum_723 (p) -char *p; +isonum_723 (char * p) { #if 0 if (p[0] != p[3] || p[1] != p[2]) { @@ -57,8 +52,7 @@ char *p; } int -isonum_731 (p) -char *p; +isonum_731 (char * p) { return ((p[0] & 0xff) | ((p[1] & 0xff) << 8) @@ -67,8 +61,7 @@ char *p; } int -isonum_732 (p) -char *p; +isonum_732 (char * p) { return (((p[0] & 0xff) << 24) | ((p[1] & 0xff) << 16) @@ -77,8 +70,7 @@ char *p; } int -isonum_733 (p) -char *p; +isonum_733 (char * p) { #if 0 int i; @@ -93,9 +85,7 @@ char *p; return (isonum_731 (p)); } -int iso_date(p, flag) -char * p; -int flag; +int iso_date(char * p, int flag) { int year, month, day, hour ,minute, second, tz; int crtime, days, i; diff --git a/fs/namei.c b/fs/namei.c index 3a3e62a7bf7a..6d922d13ee9a 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -183,9 +183,7 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name, if (!c) break; base->i_count++; - ((char *) thisname)[len] = '\0'; /* fake string.. */ error = lookup(base,thisname,len,&inode); - ((char *) thisname)[len] = c; if (error) { iput(base); return error; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index c5526f93a778..f225ce2be2c3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -283,11 +283,12 @@ static void nfs_lookup_cache_refresh(struct inode *file, } } -static int nfs_lookup(struct inode *dir, const char *name, int len, +static int nfs_lookup(struct inode *dir, const char *__name, int len, struct inode **result) { struct nfs_fh fhandle; struct nfs_fattr fattr; + char name[len > NFS_MAXNAMLEN? 1 : len+1]; int error; *result = NULL; @@ -300,6 +301,8 @@ static int nfs_lookup(struct inode *dir, const char *name, int len, iput(dir); return -ENAMETOOLONG; } + memcpy(name,__name,len); + name[len] = '\0'; if (len == 1 && name[0] == '.') { /* cheat for "." */ *result = dir; return 0; diff --git a/fs/open.c b/fs/open.c index c4c332231e2c..421d22fa915c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -167,11 +167,11 @@ int sys_access(const char * filename,int mode) return res; i_mode = inode->i_mode; res = i_mode & 0777; - iput(inode); if (current->uid == inode->i_uid) res >>= 6; else if (in_group_p(inode->i_gid)) res >>= 3; + iput(inode); if ((res & mode) == mode) return 0; /* diff --git a/fs/pipe.c b/fs/pipe.c index 86809e00148b..0bd2d3f3a797 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -108,7 +108,7 @@ static int bad_pipe_rw(struct inode * inode, struct file * filp, char * buf, int } static int pipe_ioctl(struct inode *pino, struct file * filp, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { int error; diff --git a/fs/select.c b/fs/select.c index 19b3553a5e85..ebdff9547ec5 100644 --- a/fs/select.c +++ b/fs/select.c @@ -81,23 +81,30 @@ int do_select(int n, fd_set *in, fd_set *out, fd_set *ex, int count; select_table wait_table, *wait; struct select_table_entry *entry; - int i; - int max; - - max = -1; - for (i = 0 ; i < n ; i++) { - if (!FD_ISSET(i, in) && - !FD_ISSET(i, out) && - !FD_ISSET(i, ex)) - continue; - if (!current->filp[i]) - return -EBADF; - if (!current->filp[i]->f_inode) - return -EBADF; - max = i; + unsigned long set; + int i,j; + int max = -1; + + for (j = 0 ; j < __FDSET_LONGS ; j++) { + i = j << 5; + if (i >= n) + break; + set = in->fds_bits[j] | out->fds_bits[j] | ex->fds_bits[j]; + for ( ; set ; i++,set >>= 1) { + if (i >= n) + goto end_check; + if (!(set & 1)) + continue; + if (!current->filp[i]) + return -EBADF; + if (!current->filp[i]->f_inode) + return -EBADF; + max = i; + } } +end_check: n = max + 1; - entry = (struct select_table_entry *) get_free_page(GFP_KERNEL); + entry = (struct select_table_entry *) __get_free_page(GFP_KERNEL); if (!entry) return -ENOMEM; FD_ZERO(res_in); @@ -182,6 +189,9 @@ __set_fd_set(nr, (unsigned long *) (fsp), (unsigned long *) (fdp)) * We can actually return ERESTARTSYS insetad of EINTR, but I'd * like to be certain this leads to no problems. So I return * EINTR just for safety. + * + * Update: ERESTARTSYS breaks at least the xview clock binary, so + * I'm trying ERESTARTNOHAND which restart only when you want to. */ int sys_select( unsigned long *buffer ) { @@ -236,7 +246,7 @@ int sys_select( unsigned long *buffer ) if (i < 0) return i; if (!i && (current->signal & ~current->blocked)) - return -EINTR; + return -ERESTARTNOHAND; set_fd_set(n, inp, &res_in); set_fd_set(n, outp, &res_out); set_fd_set(n, exp, &res_ex); diff --git a/fs/super.c b/fs/super.c index 886889e8186b..ac7509dbdcb1 100644 --- a/fs/super.c +++ b/fs/super.c @@ -379,9 +379,12 @@ int sys_mount(char * dev_name, char * dir_name, char * type, return do_remount(dir_name,new_flags & ~MS_MGC_MSK & ~MS_REMOUNT); } if (type) { - for (i = 0 ; i < 100 ; i++) + for (i = 0 ; i < 100 ; i++) { + if (TASK_SIZE <= (unsigned long) type) + return -EFAULT; if (!(tmp[i] = get_fs_byte(type++))) break; + } t = tmp; } else t = NULL; @@ -446,7 +449,6 @@ void mount_root(void) struct super_block * sb; struct inode * inode; - memset(file_table, 0, sizeof(file_table)); memset(super_block, 0, sizeof(super_block)); fcntl_init_locks(); if (MAJOR(ROOT_DEV) == 2) { diff --git a/include/asm/bitops.h b/include/asm/bitops.h index b7a88331ed41..eb075d951aad 100644 --- a/include/asm/bitops.h +++ b/include/asm/bitops.h @@ -20,7 +20,7 @@ struct __dummy { unsigned long a[100]; }; extern inline int set_bit(int nr, void * addr) { - char ok; + unsigned char ok; __asm__ __volatile__("btsl %2,%1\n\tsetb %0" :"=q" (ok),"=m" (ADDR) @@ -30,7 +30,7 @@ extern inline int set_bit(int nr, void * addr) extern inline int clear_bit(int nr, void * addr) { - char ok; + unsigned char ok; __asm__ __volatile__("btrl %2,%1\n\tsetnb %0" :"=q" (ok),"=m" (ADDR) @@ -44,7 +44,7 @@ extern inline int clear_bit(int nr, void * addr) */ extern inline int test_bit(int nr, void * addr) { - char ok; + unsigned char ok; __asm__ __volatile__("btl %2,%1\n\tsetb %0" :"=q" (ok) diff --git a/include/asm/dma.h b/include/asm/dma.h index bfff47f88cdc..741c2dadaef5 100644 --- a/include/asm/dma.h +++ b/include/asm/dma.h @@ -247,9 +247,11 @@ static __inline__ int get_dma_residue(unsigned int dmanr) : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE; /* using short to get 16-bit wrap around */ - short count = 1 + inb(io_port) + - ( inb(io_port) << 8 ); + unsigned short count; + count = 1 + inb(io_port); + count += inb(io_port) << 8; + return (dmanr<=3)? count : (count<<1); } diff --git a/include/asm/segment.h b/include/asm/segment.h index 884fdaab2e3b..b5fc088ec2c7 100644 --- a/include/asm/segment.h +++ b/include/asm/segment.h @@ -1,4 +1,4 @@ -extern inline unsigned char get_fs_byte(const char * addr) +static inline unsigned char get_fs_byte(const char * addr) { register unsigned char _v; @@ -6,7 +6,7 @@ extern inline unsigned char get_fs_byte(const char * addr) return _v; } -extern inline unsigned short get_fs_word(const unsigned short *addr) +static inline unsigned short get_fs_word(const unsigned short *addr) { unsigned short _v; @@ -14,7 +14,7 @@ extern inline unsigned short get_fs_word(const unsigned short *addr) return _v; } -extern inline unsigned long get_fs_long(const unsigned long *addr) +static inline unsigned long get_fs_long(const unsigned long *addr) { unsigned long _v; @@ -22,22 +22,22 @@ extern inline unsigned long get_fs_long(const unsigned long *addr) return _v; } -extern inline void put_fs_byte(char val,char *addr) +static inline void put_fs_byte(char val,char *addr) { __asm__ ("movb %0,%%fs:%1"::"q" (val),"m" (*addr)); } -extern inline void put_fs_word(short val,short * addr) +static inline void put_fs_word(short val,short * addr) { __asm__ ("movw %0,%%fs:%1"::"r" (val),"m" (*addr)); } -extern inline void put_fs_long(unsigned long val,unsigned long * addr) +static inline void put_fs_long(unsigned long val,unsigned long * addr) { __asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr)); } -extern inline void memcpy_tofs(void * to, const void * from, unsigned long n) +static inline void memcpy_tofs(void * to, const void * from, unsigned long n) { __asm__("cld\n\t" "push %%es\n\t" @@ -56,7 +56,7 @@ __asm__("cld\n\t" :"cx","di","si"); } -extern inline void memcpy_fromfs(void * to, const void * from, unsigned long n) +static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) { __asm__("cld\n\t" "testb $1,%%cl\n\t" @@ -78,21 +78,21 @@ __asm__("cld\n\t" * [ nothing wrong here, Linus: I just changed the ax to be any reg ] */ -extern inline unsigned long get_fs(void) +static inline unsigned long get_fs(void) { unsigned short _v; __asm__("mov %%fs,%0":"=r" (_v):); return _v; } -extern inline unsigned long get_ds(void) +static inline unsigned long get_ds(void) { unsigned short _v; __asm__("mov %%ds,%0":"=r" (_v):); return _v; } -extern inline void set_fs(unsigned long val) +static inline void set_fs(unsigned long val) { __asm__ __volatile__("mov %0,%%fs"::"r" ((unsigned short) val)); } diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index f77a6789f150..13a4d4757c20 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -10,6 +10,11 @@ */ #undef EXT2FS_DEBUG +/* + * Define EXT2FS_PRE_02B_COMPAT to convert ext 2 fs prior to 0.2b + */ +#undef EXT2FS_PRE_02B_COMPAT + /* * Define DONT_USE_DCACHE to inhibit the directory cache */ @@ -23,7 +28,7 @@ /* * The second extended file system version */ -#define EXT2FS_VERSION "0.2d, 93/03/30" +#define EXT2FS_VERSION "0.3, 93/04/22" /* * Special inodes numbers @@ -39,6 +44,11 @@ #define EXT2_OLD_SUPER_MAGIC 0xEF51 #define EXT2_SUPER_MAGIC 0xEF53 +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 32000 + /* * Macro-instructions used to manage several block sizes */ @@ -50,6 +60,7 @@ #else # define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #endif +#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (unsigned long)) #ifdef KERNEL # define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_log_block_size + 10) @@ -72,6 +83,29 @@ # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) #endif +/* + * ACL structures + */ + +struct ext2_acl_header /* Header of Access Control Lists */ +{ + unsigned long aclh_file_count; + unsigned long aclh_acle_count; + unsigned long aclh_first_acle; + unsigned long aclh_reserved; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + unsigned short acle_perms; /* Access permissions */ + unsigned short acle_type; /* Type of entry */ + unsigned short acle_tag; /* User or group identity */ + unsigned short acle_pad1; + unsigned long acle_reserved; + unsigned long acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + /* * Structure of a blocks group descriptor */ @@ -196,6 +230,9 @@ struct ext2_dir_entry { * Function prototypes */ +/* acl.c */ +extern int ext2_permission (struct inode *, int); + /* balloc.c */ extern int ext2_new_block (struct super_block *, unsigned long); extern void ext2_free_block (struct super_block *, unsigned long); @@ -230,10 +267,9 @@ extern unsigned long ext2_count_free_inodes (struct super_block *); /* inode.c */ extern int ext2_bmap (struct inode *, int); -extern struct buffer_head * ext2_getblk (struct inode *, int, int); -extern struct buffer_head * ext2_bread (struct inode *, int, int); +extern struct buffer_head * ext2_getblk (struct inode *, int, int, int *); +extern struct buffer_head * ext2_bread (struct inode *, int, int, int *); -extern void ext2_truncate (struct inode *); extern void ext2_put_super (struct super_block *); extern void ext2_write_super (struct super_block *); extern struct super_block * ext2_read_super (struct super_block *,void *,int); @@ -242,6 +278,10 @@ extern void ext2_write_inode (struct inode *); extern void ext2_put_inode (struct inode *); extern void ext2_statfs (struct super_block *, struct statfs *); +/* ioctl.c */ +extern int ext2_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); + /* namei.c */ extern int ext2_open (struct inode *, struct file *); extern void ext2_release (struct inode *, struct file *); @@ -257,6 +297,9 @@ extern int ext2_mknod (struct inode *, const char *, int, int, int); extern int ext2_rename (struct inode *, const char *, int, struct inode *, const char *, int); +/* truncate.c */ +extern void ext2_truncate (struct inode *); + /* * Inodes and files operations */ diff --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h index ce26502200b0..c3bb5c1e3dc2 100644 --- a/include/linux/ext2_fs_i.h +++ b/include/linux/ext2_fs_i.h @@ -5,6 +5,7 @@ * second extended file system inode data in memory */ struct ext2_inode_info { + unsigned long i_data[15]; unsigned long i_flags; unsigned long i_faddr; unsigned char i_frag; @@ -17,7 +18,6 @@ struct ext2_inode_info { unsigned long i_block_group; unsigned long i_next_alloc_block; unsigned long i_next_alloc_goal; - unsigned long i_data[15]; }; #endif diff --git a/include/linux/fs.h b/include/linux/fs.h index d149e6a5835c..add94da10fe0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -21,10 +21,10 @@ * recompiled to take full advantage of the new limits.. */ #undef NR_OPEN -#define NR_OPEN 256 +#define NR_OPEN 256 /* don't change - fd_set etc depend on this */ -#define NR_INODE 256 -#define NR_FILE 128 +#define NR_INODE 256 /* this should be bigger than NR_FILE */ +#define NR_FILE 128 /* this can well be larger on a larger system */ #define NR_SUPER 16 #define NR_HASH 997 #define NR_FILE_LOCKS 32 @@ -67,6 +67,7 @@ extern void buffer_init(void); extern unsigned long inode_init(unsigned long start, unsigned long end); +extern unsigned long file_table_init(unsigned long start, unsigned long end); #define MAJOR(a) (((unsigned)(a))>>8) #define MINOR(a) ((a)&0xff) @@ -264,7 +265,7 @@ struct file_operations { int (*write) (struct inode *, struct file *, char *, int); int (*readdir) (struct inode *, struct file *, struct dirent *, int); int (*select) (struct inode *, struct file *, int, select_table *); - int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned int); + int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct inode *, struct file *, unsigned long, size_t, int, unsigned long); int (*open) (struct inode *, struct file *); void (*release) (struct inode *, struct file *); diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h index 149c58cd6850..da2a78584083 100644 --- a/include/linux/keyboard.h +++ b/include/linux/keyboard.h @@ -65,6 +65,7 @@ extern struct kbd_struct kbd_table[]; #define VC_RAW 7 /* raw (scancode) mode */ #define VC_CRLF 8 /* 0 - enter sends CR, 1 - enter sends CRLF */ #define VC_META 9 /* 0 - meta, 1 - meta=prefix with ESC */ +#define VC_PAUSE 10 /* pause key pressed */ #define LED_MASK 7 diff --git a/include/linux/mm.h b/include/linux/mm.h index 16ebe8d8510c..f207a6c9c81e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -92,6 +92,7 @@ extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page, extern void free_page_tables(struct task_struct * tsk); extern void clear_page_tables(struct task_struct * tsk); extern int copy_page_tables(struct task_struct * new); +extern int clone_page_tables(struct task_struct * new); extern int unmap_page_range(unsigned long from, unsigned long size); extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask); extern int zeromap_page_range(unsigned long from, unsigned long size, int mask); diff --git a/include/linux/sched.h b/include/linux/sched.h index d853c4f7b22c..575e32ac2e14 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -149,6 +149,7 @@ struct task_struct { unsigned long signal; unsigned long blocked; /* bitmap of masked signals */ unsigned long flags; /* per process flags, defined below */ + int errno; /* various fields */ struct sigaction sigaction[32]; unsigned long saved_kernel_stack; @@ -220,7 +221,7 @@ struct task_struct { * your own risk!. Base=0, limit=0x1fffff (=2MB) */ #define INIT_TASK \ -/* state etc */ { 0,15,15,0,0,0, \ +/* state etc */ { 0,15,15,0,0,0,0, \ /* signals */ {{ 0, },}, \ /* stack */ 0,0, \ /* ec,brk... */ 0,0,0,0,0,0,0,0, \ diff --git a/include/linux/sys.h b/include/linux/sys.h index da5b1a930497..d98e6802d7cd 100644 --- a/include/linux/sys.h +++ b/include/linux/sys.h @@ -2,6 +2,8 @@ * Why isn't this a .c file? Enquiring minds.... */ +#define sys_clone sys_fork + extern int sys_setup(); extern int sys_exit(); extern int sys_fork(); @@ -144,7 +146,8 @@ sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority, sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, sys_socketcall, sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat, sys_newfstat, sys_newuname, sys_iopl, sys_vhangup, sys_idle, sys_vm86, -sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn }; +sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn, +sys_clone }; /* So we don't have to do any more manual updating.... */ int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); diff --git a/include/linux/tty.h b/include/linux/tty.h index eb18e648d24f..95c8ee82d12c 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -138,6 +138,7 @@ extern int get_tty_queue(struct tty_queue * queue); #define QUIT_CHAR(tty) ((tty)->termios->c_cc[VQUIT]) #define ERASE_CHAR(tty) ((tty)->termios->c_cc[VERASE]) #define KILL_CHAR(tty) ((tty)->termios->c_cc[VKILL]) +#define WERASE_CHAR(tty) ((tty)->termios->c_cc[VWERASE]) #define EOF_CHAR(tty) ((tty)->termios->c_cc[VEOF]) #define START_CHAR(tty) ((tty)->termios->c_cc[VSTART]) #define STOP_CHAR(tty) ((tty)->termios->c_cc[VSTOP]) @@ -213,7 +214,7 @@ struct tty_struct { void (*close)(struct tty_struct * tty, struct file * filp); void (*write)(struct tty_struct * tty); int (*ioctl)(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned int arg); + unsigned int cmd, unsigned long arg); void (*throttle)(struct tty_struct * tty, int status); void (*set_termios)(struct tty_struct *tty, struct termios * old); struct tty_struct *link; @@ -239,7 +240,7 @@ struct tty_ldisc { int (*write)(struct tty_struct * tty, struct file * file, char * buf, int nr); int (*ioctl)(struct tty_struct * tty, struct file * file, - unsigned int cmd, unsigned int arg); + unsigned int cmd, unsigned long arg); /* * The following routines are called from below. */ @@ -340,7 +341,7 @@ extern void wait_until_sent(struct tty_struct * tty); extern void copy_to_cooked(struct tty_struct * tty); extern int tty_register_ldisc(int disc, struct tty_ldisc *new); -extern int tty_ioctl(struct inode *, struct file *, unsigned int, unsigned int); +extern int tty_ioctl(struct inode *, struct file *, unsigned int, unsigned long); extern int is_orphaned_pgrp(int pgrp); extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); @@ -375,6 +376,6 @@ extern void unblank_screen(void); /* vt.c */ extern int vt_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned int arg); + unsigned int cmd, unsigned long arg); #endif diff --git a/include/linux/unistd.h b/include/linux/unistd.h index d7581dd46d51..c6aab9acc71d 100644 --- a/include/linux/unistd.h +++ b/include/linux/unistd.h @@ -126,6 +126,7 @@ #define __NR_ipc 117 /* not implemented yet */ #define __NR_fsync 118 /* not implemented yet */ #define __NR_sigreturn 119 +#define __NR_clone 120 extern int errno; diff --git a/include/linux/xd.h b/include/linux/xd.h index 3ff0844c3cf5..9c37a95884df 100644 --- a/include/linux/xd.h +++ b/include/linux/xd.h @@ -114,7 +114,7 @@ static void xd_geninit (void); static int xd_open (struct inode *inode,struct file *file); static void do_xd_request (void); -static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned int arg); +static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg); static void xd_release (struct inode *inode,struct file *file); static int xd_reread_partitions (int dev); static int xd_readwrite (u_char operation,u_char drive,u_char *buffer,u_int block,u_int count); diff --git a/init/main.c b/init/main.c index 272cae2b32bb..a393cc50aa39 100644 --- a/init/main.c +++ b/init/main.c @@ -198,7 +198,15 @@ static void parse_options(char *line) if (!strcmp(line,"rw")) { root_mountflags &= ~MS_RDONLY; continue; - } + } + if (!strcmp(line,"no387")) { + hard_math = 0; + __asm__("movl %%cr0,%%eax\n\t" + "andl $0xFFFFFFF9,%%eax\n\t" + "orl $0x4,%%eax\n\t" + "movl %%eax,%%cr0\n\t" ::: "ax"); + continue; + } /* * Then check if it's an environment variable or * an option. @@ -275,6 +283,7 @@ void start_kernel(void) memory_start = scsi_dev_init(memory_start,memory_end); #endif memory_start = inode_init(memory_start,memory_end); + memory_start = file_table_init(memory_start,memory_end); mem_init(low_memory_start,memory_start,memory_end); buffer_init(); time_init(); diff --git a/kernel/FPU-emu/Makefile b/kernel/FPU-emu/Makefile index 96ac5e139e4d..6a051f7faddc 100644 --- a/kernel/FPU-emu/Makefile +++ b/kernel/FPU-emu/Makefile @@ -19,15 +19,15 @@ OBJS = fpu_entry.o ifdef CONFIG_MATH_EMULATION -OBJS := $(OBJS) div_small.o errors.o\ - fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o\ - load_store.o get_address.o\ - poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o\ - poly_div.o poly_mul64.o polynomial.o\ - precision.o\ - reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o\ +OBJS := $(OBJS) div_small.o errors.o \ + fpu_arith.o fpu_aux.o fpu_etc.o fpu_trig.o \ + load_store.o get_address.o \ + poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o \ + poly_div.o poly_mul64.o polynomial.o \ + reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o \ reg_div.o reg_mul.o reg_norm.o \ - reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o\ + reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o \ + reg_round.o \ wm_shrx.o wm_sqrt.o endif diff --git a/kernel/FPU-emu/README b/kernel/FPU-emu/README index 1de54ff74735..5b7c05d9d43f 100644 --- a/kernel/FPU-emu/README +++ b/kernel/FPU-emu/README @@ -83,7 +83,7 @@ is confined to five files: ----------------------- Limitations of wm-FPU-emu ----------------------- There are a number of differences between the current wm-FPU-emu -(version beta 1.2) and the 80486 FPU (apart from bugs). Some of the +(version beta 1.3) and the 80486 FPU (apart from bugs). Some of the more important differences are listed below: Internal computations do not use de-normal numbers (but External @@ -91,12 +91,11 @@ de-normals ARE recognised and generated). The design of wm-FPU-emu allows a larger exponent range than the 80486 FPU for internal computations. -All computations are performed at full 64 bit precision with `round to -nearest or even' performed for the basic functions. The results of the -basic arithmetic functions and sqrt are then rounded to lower -precision if required by the PC bits of the FPU control word. Under -the crt0 version for Linux current at March 1993, the FPU PC bits -specify 53 bits precision. +All internal computations are performed at 64 bit or higher precision. +The results of the basic arithmetic functions and sqrt are then +rounded to the precision required by the PC bits of the FPU control +word. Under the crt0 version for Linux current at March 1993, the FPU +PC bits specify 53 bits precision. The precision flag (PE of the FPU status word) is not implemented. Does anyone write code which uses this feature? @@ -193,3 +192,12 @@ tan(x) 1e-10 .. pi/2-(1e-10) 62.4 (x <= pi/4) 62.1 exp(x) 0 .. 1 63.1 62.9 log(x) 1+1e-6 .. 2 62.4 62.1 + +As of version 1.3 of the emulator, the accuracy of the basic +arithmetic has been improved (by a small fraction of a bit). Care has +been taken to ensure full accuracy of the rounding of the basic +arithmetic functions (+,-,*,/,and fsqrt), and they all now produce +results which are exact to the 64th bit (unless there are any bugs +left). To ensure this, it was necessary to effectively get information +of up to about 128 bits precision. The emulator now passes the +"paranoia" tests. diff --git a/kernel/FPU-emu/control_w.h b/kernel/FPU-emu/control_w.h index fedcfd408b25..f084b7c5c0e0 100644 --- a/kernel/FPU-emu/control_w.h +++ b/kernel/FPU-emu/control_w.h @@ -33,20 +33,9 @@ /* p 15-5: Precision control bits affect only the following: ADD, SUB(R), MUL, DIV(R), and SQRT */ -#define PRECISION_ADJUST_CONTROL (control_word & 0x300) -#define PR_24_BITS 0x000 -#define PR_53_BITS 0x200 -/* By doing this as a macro, we allow easy modification */ -#define PRECISION_ADJUST(x) \ - switch (PRECISION_ADJUST_CONTROL) \ - { \ - case PR_24_BITS: \ - round_to_24_bits(x); \ - break; \ - case PR_53_BITS: \ - round_to_53_bits(x); \ - break; \ - } - +#define FULL_PRECISION (CW_PC | RC_RND) +#define PR_24_BITS _Const_(0x000) +#define PR_53_BITS _Const_(0x200) +#define PR_64_BITS _Const_(0x300) #endif _CONTROLW_H_ diff --git a/kernel/FPU-emu/fpu_arith.c b/kernel/FPU-emu/fpu_arith.c index b27a747073bd..96ba79a766b7 100644 --- a/kernel/FPU-emu/fpu_arith.c +++ b/kernel/FPU-emu/fpu_arith.c @@ -18,16 +18,14 @@ void fadd__() { /* fadd st,st(i) */ - reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); - PRECISION_ADJUST(FPU_st0_ptr); + reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } void fmul__() { /* fmul st,st(i) */ - reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); - PRECISION_ADJUST(FPU_st0_ptr); + reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } @@ -35,32 +33,28 @@ void fmul__() void fsub__() { /* fsub st,st(i) */ - reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); - PRECISION_ADJUST(FPU_st0_ptr); + reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } void fsubr_() { /* fsubr st,st(i) */ - reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr); - PRECISION_ADJUST(FPU_st0_ptr); + reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); } void fdiv__() { /* fdiv st,st(i) */ - reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); - PRECISION_ADJUST(FPU_st0_ptr); + reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr, control_word); } void fdivr_() { /* fdivr st,st(i) */ - reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr); - PRECISION_ADJUST(FPU_st0_ptr); + reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr, control_word); } @@ -68,16 +62,14 @@ void fdivr_() void fadd_i() { /* fadd st(i),st */ - reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } void fmul_i() { /* fmul st(i),st */ - reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); } @@ -85,9 +77,8 @@ void fsubri() { /* fsubr st(i),st */ /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); */ - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */ + reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } @@ -95,25 +86,22 @@ void fsub_i() { /* fsub st(i),st */ /* This is the sense of the 80486 manual - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); */ - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */ + reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); } void fdivri() { /* fdivr st(i),st */ - reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); } void fdiv_i() { /* fdiv st(i),st */ - reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); } @@ -121,8 +109,7 @@ void fdiv_i() void faddp_() { /* faddp st(i),st */ - reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); pop(); } @@ -130,8 +117,7 @@ void faddp_() void fmulp_() { /* fmulp st(i),st */ - reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); pop(); } @@ -141,9 +127,8 @@ void fsubrp() { /* fsubrp st(i),st */ /* This is the sense of the 80486 manual - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); */ - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); */ + reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); pop(); } @@ -152,9 +137,8 @@ void fsubp_() { /* fsubp st(i),st */ /* This is the sense of the 80486 manual - reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); */ - reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); */ + reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); pop(); } @@ -162,8 +146,7 @@ void fsubp_() void fdivrp() { /* fdivrp st(i),st */ - reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm), control_word); pop(); } @@ -171,8 +154,7 @@ void fdivrp() void fdivp_() { /* fdivp st(i),st */ - reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); - PRECISION_ADJUST(&st(FPU_rm)); + reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm), control_word); pop(); } diff --git a/kernel/FPU-emu/fpu_emu.h b/kernel/FPU-emu/fpu_emu.h index 770437bcf678..926db6d9a5c8 100644 --- a/kernel/FPU-emu/fpu_emu.h +++ b/kernel/FPU-emu/fpu_emu.h @@ -92,12 +92,17 @@ extern void poly_div16(long long *x); extern void polynomial(unsigned accum[], unsigned x[], unsigned short terms[][4], int n); extern void normalize(FPU_REG *x); -extern void reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ); -extern void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ); -extern void reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ); -extern void reg_u_div(long long *arg1, long long *arg2, FPU_REG *answ); -extern void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ); -extern void wm_sqrt(FPU_REG *n); +extern void reg_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + unsigned int control_w); +extern void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + unsigned int control_w); +extern void reg_u_mul(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + unsigned int control_w); +extern void reg_u_div(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + unsigned int control_w); +extern void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, + unsigned int control_w); +extern void wm_sqrt(FPU_REG *n, unsigned int control_w); extern unsigned shrx(void *l, unsigned x); extern unsigned shrxs(void *v, unsigned x); extern unsigned long div_small(unsigned long long *x, unsigned long y); diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c index 870a51506327..4112fb6498b8 100644 --- a/kernel/FPU-emu/fpu_entry.c +++ b/kernel/FPU-emu/fpu_entry.c @@ -225,36 +225,37 @@ do_another: switch ( (FPU_modrm >> 3) & 7 ) { case 0: /* fadd */ - reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); + reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + control_word); break; case 1: /* fmul */ - reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); + reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + control_word); break; case 2: /* fcom */ compare_st_data(); - goto no_precision_adjust; break; case 3: /* fcomp */ compare_st_data(); pop(); - goto no_precision_adjust; break; case 4: /* fsub */ - reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); + reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + control_word); break; case 5: /* fsubr */ - reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr); + reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, + control_word); break; case 6: /* fdiv */ - reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); + reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr, + control_word); break; case 7: /* fdivr */ - reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr); + reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr, + control_word); break; } - PRECISION_ADJUST(FPU_st0_ptr); -no_precision_adjust: - ; } else stack_underflow(); @@ -338,7 +339,6 @@ test_for_fp: void __math_abort(struct info * info, unsigned int signal) { - RE_ENTRANT_CHECK_OFF FPU_EIP = FPU_ORIG_EIP; send_sig(signal,current,1); RE_ENTRANT_CHECK_OFF diff --git a/kernel/FPU-emu/fpu_proto.h b/kernel/FPU-emu/fpu_proto.h index 525b119e8bb2..f7d71ba7947c 100644 --- a/kernel/FPU-emu/fpu_proto.h +++ b/kernel/FPU-emu/fpu_proto.h @@ -68,8 +68,8 @@ extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg); extern int round_to_53_bits(FPU_REG *reg); extern int round_to_24_bits(FPU_REG *reg); /* reg_add_sub.c */ -extern void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest); -extern void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest); +extern void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w); +extern void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w); /* reg_compare.c */ extern int compare(FPU_REG *b); extern void compare_st_data(void); @@ -102,4 +102,4 @@ extern void frstor(void); extern char *fstenv(void); extern void fsave(void); /* reg_mul.c */ -extern void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest); +extern void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w); diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c index 10d57dfdd94b..e6531430442d 100644 --- a/kernel/FPU-emu/fpu_trig.c +++ b/kernel/FPU-emu/fpu_trig.c @@ -30,7 +30,7 @@ static int trig_arg(FPU_REG *X) control_word |= RC_CHOP; reg_move(X, "); - reg_div(", &CONST_PI2, "); + reg_div(", &CONST_PI2, ", FULL_PRECISION); reg_move(", &tmp); round_to_int(&tmp); @@ -40,7 +40,7 @@ static int trig_arg(FPU_REG *X) q = *(long long *)&(tmp.sigl); normalize(&tmp); - reg_sub(", &tmp, X); + reg_sub(", &tmp, X, FULL_PRECISION); rv = q & 7; control_word = old_cw; @@ -107,17 +107,17 @@ static void f2xm1(void) /* poly_2xm1(x) requires 0 < x < 1. */ if ( poly_2xm1(FPU_st0_ptr, &rv) ) return; /* error */ - reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr); + reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); } else { /* **** Should change poly_2xm1() to at least handle numbers near 0 */ /* poly_2xm1(x) doesn't handle negative numbers. */ /* So we compute (poly_2xm1(x+1)-1)/2, for -1 < x < 0 */ - reg_add(FPU_st0_ptr, &CONST_1, &tmp); + reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION); poly_2xm1(&tmp, &rv); - reg_mul(&rv, &tmp, &tmp); - reg_sub(&tmp, &CONST_1, FPU_st0_ptr); + reg_mul(&rv, &tmp, &tmp, FULL_PRECISION); + reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION); FPU_st0_ptr->exp--; if ( FPU_st0_ptr->exp <= EXP_UNDER ) arith_underflow(FPU_st0_ptr); @@ -155,7 +155,7 @@ static void fptan(void) if ( (q = trig_arg(FPU_st0_ptr)) != -1 ) { if (q & 1) - reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr); + reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); poly_tan(FPU_st0_ptr, FPU_st0_ptr); @@ -286,10 +286,9 @@ static void fsqrt_(void) expon = FPU_st0_ptr->exp - EXP_BIAS; FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 .. 4.0) */ - wm_sqrt(FPU_st0_ptr); /* Do the computation */ + wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */ FPU_st0_ptr->exp += expon >> 1; - FPU_st0_ptr->tag = TW_Valid; FPU_st0_ptr->sign = SIGN_POS; } else if ( FPU_st0_tag == TW_Zero ) @@ -303,7 +302,6 @@ static void fsqrt_(void) else { single_arg_error(); return; } - PRECISION_ADJUST(FPU_st0_ptr); } @@ -338,7 +336,7 @@ static void fsin(void) FPU_REG rv; if (q & 1) - reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr); + reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); poly_sine(FPU_st0_ptr, &rv); @@ -390,7 +388,7 @@ static int f_cos(FPU_REG *arg) FPU_REG rv; if ( !(q & 1) ) - reg_sub(&CONST_1, arg, arg); + reg_sub(&CONST_1, arg, arg, FULL_PRECISION); poly_sine(arg, &rv); @@ -479,15 +477,15 @@ static void fprem_kernel(int round) /* This should be the most common case */ long long q; int c = 0; - reg_div(FPU_st0_ptr, st1_ptr, &tmp); + reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); round_to_int(&tmp); /* Fortunately, this can't overflow to 2^64 */ tmp.exp = EXP_BIAS + 63; q = *(long long *)&(tmp.sigl); normalize(&tmp); - reg_mul(st1_ptr, &tmp, &tmp); - reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr); + reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); + reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); if (q&4) c |= SW_C3; if (q&2) c |= SW_C1; @@ -500,7 +498,7 @@ static void fprem_kernel(int round) /* There is a large exponent difference ( >= 64 ) */ int N_exp; - reg_div(FPU_st0_ptr, st1_ptr, &tmp); + reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); /* N is 'a number between 32 and 63' (p26-113) */ N_exp = (tmp.exp & 31) + 32; tmp.exp = EXP_BIAS + N_exp; @@ -511,8 +509,8 @@ static void fprem_kernel(int round) tmp.exp = EXP_BIAS + expdif - N_exp; - reg_mul(st1_ptr, &tmp, &tmp); - reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr); + reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); + reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); setcc(SW_C2); } @@ -556,7 +554,7 @@ static void fyl2x(void) { poly_l2(FPU_st0_ptr, FPU_st0_ptr); - reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr); + reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); pop(); FPU_st0_ptr = &st(0); if ( FPU_st0_ptr->exp <= EXP_UNDER ) { arith_underflow(FPU_st0_ptr); return; } @@ -667,20 +665,20 @@ static void fpatan(void) if (compare(st1_ptr) == COMP_A_LT_B) { quadrant |= 4; - reg_div(FPU_st0_ptr, st1_ptr, &sum); + reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION); } else - reg_div(st1_ptr, FPU_st0_ptr, &sum); + reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION); poly_atan(&sum); if (quadrant & 4) { - reg_sub(&CONST_PI2, &sum, &sum); + reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION); } if (quadrant & 2) { - reg_sub(&CONST_PI, &sum, &sum); + reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION); } if (quadrant & 1) sum.sign ^= SIGN_POS^SIGN_NEG; @@ -708,7 +706,7 @@ static void fpatan(void) if ( FPU_st0_ptr->sign == SIGN_POS ) { reg_move(&CONST_PI4, st1_ptr); } else - reg_add(&CONST_PI4, &CONST_PI2, st1_ptr); + reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION); } else { @@ -775,7 +773,7 @@ static void fyl2xp1(void) arith_invalid(st1_ptr); pop(); return; } - reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr); + reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); pop(); } else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) diff --git a/kernel/FPU-emu/poly_atan.c b/kernel/FPU-emu/poly_atan.c index b62b0e7105cd..a8609e41fcec 100644 --- a/kernel/FPU-emu/poly_atan.c +++ b/kernel/FPU-emu/poly_atan.c @@ -3,7 +3,8 @@ | | | Compute the tan of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -12,6 +13,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "control_w.h" #define HIPOWERon 6 /* odd poly, negative terms */ @@ -111,8 +113,7 @@ void poly_atan(FPU_REG *arg) denom.sigh |= 0x80000000; /* 1 + arg */ arg->exp = numerator.exp; - reg_u_div((long long *)&(numerator.sigl), - (long long *)&(denom.sigl), arg); + reg_u_div(&numerator, &denom, arg, FULL_PRECISION); exponent = arg->exp - EXP_BIAS; } @@ -161,7 +162,8 @@ void poly_atan(FPU_REG *arg) reg_move(&pos_poly, &odd_poly); poly_add_1(&odd_poly); - reg_u_mul(&odd_poly, arg, &odd_poly); /* The complete odd polynomial */ + /* The complete odd polynomial */ + reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); odd_poly.exp -= EXP_BIAS - 1; /* will be a valid positive nr with expon = 0 */ @@ -172,10 +174,10 @@ void poly_atan(FPU_REG *arg) poly_add_1(&even_poly); - reg_div(&odd_poly, &even_poly, arg); + reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION); if ( recursions ) - reg_sub(&CONST_PI4, arg, arg); + reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION); } diff --git a/kernel/FPU-emu/poly_l2.c b/kernel/FPU-emu/poly_l2.c index 193c3e40ceb9..6365fce4b5b2 100644 --- a/kernel/FPU-emu/poly_l2.c +++ b/kernel/FPU-emu/poly_l2.c @@ -3,7 +3,8 @@ | | | Compute the base 2 log of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -13,6 +14,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "control_w.h" @@ -60,7 +62,7 @@ void poly_l2(FPU_REG *arg, FPU_REG *result) exponent++; accum.sign = 1; /* sign to negative */ num.exp = EXP_BIAS; /* needed to prevent errors in div routine */ - reg_u_div((long long *)&(CONST_1.sigl), (long long *)&(arg->sigl), &num); + reg_u_div(&CONST_1, arg, &num, FULL_PRECISION); } else { @@ -79,7 +81,7 @@ void poly_l2(FPU_REG *arg, FPU_REG *result) poly_div4((long long *)&(denom.sigl)); denom.sigh += 0x80000000; /* set the msb */ Xx.exp = EXP_BIAS; /* needed to prevent errors in div routine */ - reg_u_div((long long *)&num.sigl, (long long *)&(denom.sigl), &Xx); + reg_u_div(&num, &denom, &Xx, FULL_PRECISION); zero = !(Xx.sigh | Xx.sigl); @@ -122,7 +124,7 @@ void poly_l2(FPU_REG *arg, FPU_REG *result) /* Use 1-1/(1-x) = x/(1-x) */ *((long long *)&num.sigl) = - *((long long *)&(arg->sigl)); normalize(&num); - reg_div(&num, arg, &num); + reg_div(&num, arg, &num, FULL_PRECISION); } else { @@ -133,12 +135,12 @@ void poly_l2(FPU_REG *arg, FPU_REG *result) denom.sign = SIGN_POS; /* set the sign to positive */ denom.exp = EXP_BIAS; - reg_div(&num, &denom, &lXx); + reg_div(&num, &denom, &lXx, FULL_PRECISION); - reg_u_mul(&lXx, &accum, &accum); + reg_u_mul(&lXx, &accum, &accum, FULL_PRECISION); accum.exp += - EXP_BIAS + 1; - reg_u_add(&lXx, &accum, result); + reg_u_add(&lXx, &accum, result, FULL_PRECISION); normalize(result); } @@ -230,15 +232,15 @@ int poly_l2p1(FPU_REG *arg, FPU_REG *result) sign = arg->sign; - reg_add(arg, &CONST_1, &arg_pl1); + reg_add(arg, &CONST_1, &arg_pl1, FULL_PRECISION); if ( (arg_pl1.sign) | (arg_pl1.tag) ) { /* We need a valid positive number! */ return 1; } - reg_add(&CONST_1, &arg_pl1, &denom); - reg_div(arg, &denom, &local_arg); + reg_add(&CONST_1, &arg_pl1, &denom, FULL_PRECISION); + reg_div(arg, &denom, &local_arg, FULL_PRECISION); local_arg.sign = 0; /* Make the sign positive */ /* Now we need to check that |local_arg| is less than @@ -273,10 +275,10 @@ int poly_l2p1(FPU_REG *arg, FPU_REG *result) accum.exp = EXP_BIAS - 1; normalize(&accum); - reg_u_mul(&local_arg, &accum, &accum); + reg_u_mul(&local_arg, &accum, &accum, FULL_PRECISION); accum.exp -= EXP_BIAS - 1; - reg_u_add(&local_arg, &accum, result); + reg_u_add(&local_arg, &accum, result, FULL_PRECISION); /* Multiply the result by 2 */ result->exp++; diff --git a/kernel/FPU-emu/poly_sin.c b/kernel/FPU-emu/poly_sin.c index 91a9cf320a9d..98bed99c2f42 100644 --- a/kernel/FPU-emu/poly_sin.c +++ b/kernel/FPU-emu/poly_sin.c @@ -3,7 +3,8 @@ | | | Computation of an approximation of the sin function by a polynomial | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -13,6 +14,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "control_w.h" #define HIPOWER 5 @@ -117,8 +119,8 @@ void poly_sine(FPU_REG *arg, FPU_REG *result) normalize(result); - reg_mul(result, arg, result); - reg_u_add(result, arg, result); + reg_mul(result, arg, result, FULL_PRECISION); + reg_u_add(result, arg, result, FULL_PRECISION); /* A small overflow may be possible... but an illegal result. */ if ( result->exp >= EXP_BIAS ) diff --git a/kernel/FPU-emu/poly_tan.c b/kernel/FPU-emu/poly_tan.c index 0129f5b3e8d5..149d8403e12f 100644 --- a/kernel/FPU-emu/poly_tan.c +++ b/kernel/FPU-emu/poly_tan.c @@ -3,7 +3,8 @@ | | | Compute the tan of a FPU_REG, using a polynomial approximation. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -12,6 +13,7 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "control_w.h" #define HIPOWERop 3 /* odd poly, positive terms */ @@ -131,8 +133,8 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg) reg_move(&pos_poly, &odd_poly); normalize(&odd_poly); - reg_mul(&odd_poly, arg, &odd_poly); - reg_u_add(&odd_poly, arg, &odd_poly); /* This is just the odd polynomial */ + reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); + reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd polynomial */ /* will be a valid positive nr with expon = 0 */ @@ -166,14 +168,14 @@ void poly_tan(FPU_REG *arg, FPU_REG *y_reg) reg_move(&neg_poly, &even_poly); normalize(&even_poly); - reg_mul(&even_poly, &argSq, &even_poly); - reg_add(&even_poly, &argSq, &even_poly); - reg_sub(&CONST_1, &even_poly, &even_poly); /* This is just the even polynomial */ + reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION); + reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION); + reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even polynomial */ /* Now ready to copy the results */ if ( invert ) - { reg_div(&even_poly, &odd_poly, y_reg); } + { reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION); } else - { reg_div(&odd_poly, &even_poly, y_reg); } + { reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION); } } diff --git a/kernel/FPU-emu/polynomial.S b/kernel/FPU-emu/polynomial.S index 16118d76ab22..3f12fbb80e16 100644 --- a/kernel/FPU-emu/polynomial.S +++ b/kernel/FPU-emu/polynomial.S @@ -3,7 +3,8 @@ | | | Fixed point arithmetic polynomial evaluation. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | Call from C as: | @@ -54,9 +55,9 @@ _polynomial: movl %eax,-28(%ebp) subl TERM_SIZE,%ecx - js xL_accum_done + js L_accum_done -xL_accum_loop: +L_accum_loop: xor %eax,%eax movl %eax,-4(%ebp) movl %eax,-8(%ebp) @@ -107,27 +108,27 @@ xL_accum_loop: movl %eax,-28(%ebp) #else testb $128,-25(%ebp) - je xL_no_poly_round + je L_no_poly_round addl $1,-24(%ebp) adcl $0,-20(%ebp) -xL_no_poly_round: +L_no_poly_round: #endif EXTRA_PRECISE subl TERM_SIZE,%ecx - jns xL_accum_loop + jns L_accum_loop -xL_accum_done: +L_accum_done: #ifdef EXTRA_PRECISE /* And round the result */ testb $128,-25(%ebp) - je xL_poly_done + je L_poly_done addl $1,-24(%ebp) adcl $0,-20(%ebp) #endif EXTRA_PRECISE -xL_poly_done: +L_poly_done: movl -24(%ebp),%eax movl %eax,(%esi) movl -20(%ebp),%eax diff --git a/kernel/FPU-emu/precision.c b/kernel/FPU-emu/precision.c deleted file mode 100644 index 7e3078250e95..000000000000 --- a/kernel/FPU-emu/precision.c +++ /dev/null @@ -1,134 +0,0 @@ -/*---------------------------------------------------------------------------+ - | precision.c | - | | - | The functions which adjust the precision of a result. | - | | - | Copyright (C) 1993 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - - -#include - -#include "fpu_system.h" -#include "exception.h" -#include "reg_constant.h" -#include "fpu_emu.h" -#include "control_w.h" - - -/* Round the result to 53 bits */ -int round_to_53_bits(FPU_REG *reg) -{ - - if (reg->tag == TW_Valid) - { - unsigned long increment = 0; /* avoid gcc warnings */ - - switch (control_word & CW_RC) - { - case RC_RND: - /* Rounding can get a little messy.. */ - increment = ((reg->sigl & 0x7ff) > 0x400) | /* nearest */ - ((reg->sigl & 0xc00) == 0xc00); /* odd -> even */ - break; - case RC_DOWN: /* towards -infinity */ - increment = (reg->sign == SIGN_POS) ? 0 : reg->sigl & 0x7ff; - break; - case RC_UP: /* towards +infinity */ - increment = (reg->sign == SIGN_POS) ? reg->sigl & 0x7ff : 0; - break; - case RC_CHOP: - increment = 0; - break; - } - - /* Truncate the mantissa */ - reg->sigl &= 0xfffff800; - - if ( increment ) - { - if ( reg->sigl >= 0xfffff800 ) - { - /* the sigl part overflows */ - if ( reg->sigh == 0xffffffff ) - { - /* The sigh part overflows */ - reg->sigh = 0x80000000; - reg->exp++; - if (reg->exp >= EXP_OVER) - { arith_overflow(reg); return 1; } - } - else - { - reg->sigh ++; - } - reg->sigl = 0x00000000; - } - else - { - /* We only need to increment sigl */ - reg->sigl += 0x00000800; - } - } - } - - return 0; - -} - - -/* Round the result to 24 bits */ -int round_to_24_bits(FPU_REG *reg) -{ - - if (reg->tag == TW_Valid) - { - unsigned long increment = 0; /* avoid gcc warnings */ - unsigned long sigh = reg->sigh; - unsigned long sigl = reg->sigl; - - switch (control_word & CW_RC) - { - case RC_RND: - increment = ((sigh & 0xff) > 0x80) /* more than half */ - || (((sigh & 0xff) == 0x80) && sigl) /* more than half */ - || ((sigh & 0x180) == 0x180); /* round to even */ - break; - case RC_DOWN: /* towards -infinity */ - increment = (reg->sign == SIGN_POS) ? 0 : (sigl | (sigh & 0xff)); - break; - case RC_UP: /* towards +infinity */ - increment = (reg->sign == SIGN_POS) ? (sigl | (sigh & 0xff)) : 0; - break; - case RC_CHOP: - increment = 0; - break; - } - - /* Truncate the mantissa */ - reg->sigl = 0; - - if (increment) - { - if ( sigh >= 0xffffff00 ) - { - /* The sigh part overflows */ - reg->sigh = 0x80000000; - reg->exp++; - if (reg->exp >= EXP_OVER) - { arith_overflow(reg); return 1; } - } - else - { - reg->sigh &= 0xffffff00; - reg->sigh += 0x100; - } - } - } - - return 0; - -} diff --git a/kernel/FPU-emu/reg_add_sub.c b/kernel/FPU-emu/reg_add_sub.c index 07e8e01f769f..3366a6d467da 100644 --- a/kernel/FPU-emu/reg_add_sub.c +++ b/kernel/FPU-emu/reg_add_sub.c @@ -22,7 +22,7 @@ #include "fpu_system.h" -void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest) +void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { int diff; @@ -32,7 +32,7 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest) if (!(a->sign ^ b->sign)) { /* signs are the same */ - reg_u_add(a, b, dest); + reg_u_add(a, b, dest, control_w); dest->sign = a->sign; return; } @@ -52,19 +52,19 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest) if (diff > 0) { - reg_u_sub(a, b, dest); + reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if ( diff == 0 ) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ - dest->sign = ((control_word & CW_RC) != RC_DOWN) + dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { - reg_u_sub(b, a, dest); + reg_u_sub(b, a, dest, control_w); dest->sign = b->sign; } return; @@ -84,7 +84,7 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest) { /* Signs are different. */ /* Sign of answer depends upon rounding mode. */ - dest->sign = ((control_word & CW_RC) != RC_DOWN) + dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } @@ -114,7 +114,7 @@ void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest) /* Subtract b from a. (a-b) -> dest */ -void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest) +void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { int diff; @@ -139,28 +139,28 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest) case 3: /* N - N */ if (diff > 0) { - reg_u_sub(a, b, dest); + reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if ( diff == 0 ) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ - dest->sign = ((control_word & CW_RC) != RC_DOWN) + dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { - reg_u_sub(b, a, dest); + reg_u_sub(b, a, dest, control_w); dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; } return; case 1: /* P - N */ - reg_u_add(a, b, dest); + reg_u_add(a, b, dest, control_w); dest->sign = SIGN_POS; return; case 2: /* N - P */ - reg_u_add(a, b, dest); + reg_u_add(a, b, dest, control_w); dest->sign = SIGN_NEG; return; } @@ -179,7 +179,7 @@ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest) if (same_signs) { /* Sign depends upon rounding mode */ - dest->sign = ((control_word & CW_RC) != RC_DOWN) + dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } diff --git a/kernel/FPU-emu/reg_constant.c b/kernel/FPU-emu/reg_constant.c index b61e39d9d9b1..88c03b6d428f 100644 --- a/kernel/FPU-emu/reg_constant.c +++ b/kernel/FPU-emu/reg_constant.c @@ -3,7 +3,8 @@ | | | All of the constant FPU_REGs | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/reg_div.S b/kernel/FPU-emu/reg_div.S index 5c24cb7db538..eb25dc3a178a 100644 --- a/kernel/FPU-emu/reg_div.S +++ b/kernel/FPU-emu/reg_div.S @@ -4,11 +4,13 @@ | | | Divide one FPU_REG by another and put the result in a destination FPU_REG.| | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | Call from C as: | - | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest) | + | void reg_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, | + | unsigned int control_word) | | | +---------------------------------------------------------------------------*/ @@ -34,7 +36,7 @@ _reg_div: movb TAG(%esi),%al orb TAG(%ebx),%al - jne xL_div_special // Not (both numbers TW_Valid) + jne L_div_special // Not (both numbers TW_Valid) // Both arguments are TW_Valid @@ -50,72 +52,72 @@ _reg_div: cmpb %cl,SIGN(%ebx) setne (%edi) // Set the sign, requires SIGN_NEG=1, SIGN_POS=0 - add $SIGL_OFFSET,%ebx - add $SIGL_OFFSET,%esi +// add $SIGL_OFFSET,%ebx +// add $SIGL_OFFSET,%esi jmp _divide_kernel /*-----------------------------------------------------------------------*/ -xL_div_special: +L_div_special: cmpb TW_NaN,TAG(%esi) // A NaN with anything to give NaN - je xL_arg1_NaN + je L_arg1_NaN cmpb TW_NaN,TAG(%ebx) // A NaN with anything to give NaN - jne xL_no_NaN_arg + jne L_no_NaN_arg // Operations on NaNs -xL_arg1_NaN: -xL_arg2_NaN: +L_arg1_NaN: +L_arg2_NaN: pushl %edi pushl %ebx pushl %esi call _real_2op_NaN - jmp xL78 + jmp LDiv_exit // Invalid operations -xL_zero_zero: -xL_inf_inf: +L_zero_zero: +L_inf_inf: pushl %esi call _arith_invalid - jmp xL78 + jmp LDiv_exit -xL_no_NaN_arg: +L_no_NaN_arg: cmpb TW_Infinity,TAG(%esi) - jne xL_arg1_not_inf + jne L_arg1_not_inf cmpb TW_Infinity,TAG(%ebx) - je xL_inf_inf // invalid operation + je L_inf_inf // invalid operation // Note that p16-9 says that infinity/0 returns infinity - jmp xL_copy_arg1 // Answer is Inf + jmp L_copy_arg1 // Answer is Inf -xL_arg1_not_inf: - cmpb TW_Zero,TAG(%ebx) // Priority to div-by-zero error - jne xL_arg2_not_zero +L_arg1_not_inf: + cmpb TW_Zero,TAG(%ebx) // Priority to div-by-zero error + jne L_arg2_not_zero cmpb TW_Zero,TAG(%esi) - je xL_zero_zero // invalid operation + je L_zero_zero // invalid operation // Division by zero error pushl %esi movb SIGN(%esi),%al xorb SIGN(%ebx),%al - pushl %eax // lower 8 bits have the sign + pushl %eax // lower 8 bits have the sign call _divide_by_zero - jmp xL78 + jmp LDiv_exit -xL_arg2_not_zero: +L_arg2_not_zero: cmpb TW_Infinity,TAG(%ebx) - jne xL_arg2_not_inf + jne L_arg2_not_inf - jmp xL_return_zero // Answer is zero + jmp L_return_zero // Answer is zero -xL_arg2_not_inf: +L_arg2_not_inf: cmpb TW_Zero,TAG(%esi) - jne xL_unknown_tags + jne L_unknown_tags -xL_copy_arg1: +L_copy_arg1: movb TAG(%esi),%ax movb %ax,TAG(%edi) movl EXP(%esi),%eax @@ -127,24 +129,24 @@ xL_copy_arg1: movb SIGN(%esi),%cl cmpb %cl,SIGN(%ebx) - jne xL76 + jne LDiv_negative_result movb SIGN_POS,SIGN(%edi) - jmp xL78 + jmp LDiv_exit -xL71: +LDiv_set_result_sign: movb SIGN(%esi),%cl cmpb %cl,SIGN(%edi) - jne xL76 + jne LDiv_negative_result movb SIGN_POS,SIGN(%ebx) - jmp xL78 + jmp LDiv_exit .align 2,0x90 -xL76: +LDiv_negative_result: movb SIGN_NEG,SIGN(%edi) -xL78: +LDiv_exit: leal -12(%ebp),%esp popl %ebx @@ -154,11 +156,11 @@ xL78: ret -xL_return_zero: +L_return_zero: movb TW_Zero,TAG(%edi) - jmp xL71 + jmp LDiv_set_result_sign -xL_unknown_tags: +L_unknown_tags: push EX_INTERNAL | 0x208 call EXCEPTION @@ -169,4 +171,4 @@ xL_unknown_tags: movl %eax,SIGL(%edi) movl _CONST_QNaN+8,%eax movl %eax,SIGH(%edi) - jmp xL78 + jmp LDiv_exit diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c index 23b79684a8f9..710b7a422547 100644 --- a/kernel/FPU-emu/reg_ld_str.c +++ b/kernel/FPU-emu/reg_ld_str.c @@ -503,22 +503,73 @@ int reg_store_double(void) double *dfloat = (double *)FPU_data_address; unsigned long l[2]; - if (FPU_st0_tag == TW_Valid) { int exp; FPU_REG tmp; reg_move(FPU_st0_ptr, &tmp); - if (round_to_53_bits(&tmp)) goto overflow; + + if ( tmp.sigl & 0x000007ff ) + { + unsigned long increment = 0; /* avoid gcc warnings */ + + switch (control_word & CW_RC) + { + case RC_RND: + /* Rounding can get a little messy.. */ + increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */ + ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff; + break; + case RC_UP: /* towards +infinity */ + increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate the mantissa */ + tmp.sigl &= 0xfffff800; + + if ( increment ) + { + if ( tmp.sigl >= 0xfffff800 ) + { + /* the sigl part overflows */ + if ( tmp.sigh == 0xffffffff ) + { + /* The sigh part overflows */ + tmp.sigh = 0x80000000; + tmp.exp++; + if (tmp.exp >= EXP_OVER) + goto overflow; + } + else + { + tmp.sigh ++; + } + tmp.sigl = 0x00000000; + } + else + { + /* We only need to increment sigl */ + tmp.sigl += 0x00000800; + } + } + } + l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21); l[1] = ((tmp.sigh >> 11) & 0xfffff); exp = tmp.exp - EXP_BIAS; if ( exp > DOUBLE_Emax ) { - EXCEPTION(EX_Overflow); overflow: + EXCEPTION(EX_Overflow); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ if ( control_word & EX_Overflow ) { @@ -635,14 +686,61 @@ int reg_store_single(void) FPU_REG tmp; reg_move(FPU_st0_ptr, &tmp); - if (round_to_24_bits(&tmp)) goto overflow; + + if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) + { + unsigned long increment = 0; /* avoid gcc warnings */ + unsigned long sigh = tmp.sigh; + unsigned long sigl = tmp.sigl; + + switch (control_word & CW_RC) + { + case RC_RND: + increment = ((sigh & 0xff) > 0x80) /* more than half */ + || (((sigh & 0xff) == 0x80) && sigl) /* more than half */ + || ((sigh & 0x180) == 0x180); /* round to even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (tmp.sign == SIGN_POS) ? 0 : (sigl | (sigh & 0xff)); + break; + case RC_UP: /* towards +infinity */ + increment = (tmp.sign == SIGN_POS) ? (sigl | (sigh & 0xff)) : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate part of the mantissa */ + tmp.sigl = 0; + + if (increment) + { + if ( sigh >= 0xffffff00 ) + { + /* The sigh part overflows */ + tmp.sigh = 0x80000000; + tmp.exp++; + if (tmp.exp >= EXP_OVER) + goto overflow; + } + else + { + tmp.sigh &= 0xffffff00; + tmp.sigh += 0x100; + } + } + else + tmp.sigh &= 0xffffff00; /* Finish the truncation */ + } + templ = (tmp.sigh >> 8) & 0x007fffff; exp = tmp.exp - EXP_BIAS; if ( exp > SINGLE_Emax ) { - EXCEPTION(EX_Overflow); overflow: + EXCEPTION(EX_Overflow); /* This is a special case: see sec 16.2.5.1 of the 80486 book */ if ( control_word & EX_Overflow ) { diff --git a/kernel/FPU-emu/reg_mul.c b/kernel/FPU-emu/reg_mul.c index d5992c3fb181..88272cd7fbd3 100644 --- a/kernel/FPU-emu/reg_mul.c +++ b/kernel/FPU-emu/reg_mul.c @@ -3,7 +3,8 @@ | | | Multiply one FPU_REG by another, put the result in a destination FPU_REG. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -16,18 +17,19 @@ #include "exception.h" #include "reg_constant.h" #include "fpu_emu.h" +#include "fpu_system.h" -/* This routine must be called with non-empty registers */ -void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest) +/* This routine must be called with non-empty source registers */ +void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest, unsigned int control_w) { if (!(a->tag | b->tag)) { /* This should be the most common case */ - reg_u_mul(a, b, dest); - dest->exp += - EXP_BIAS + 1; dest->sign = (a->sign ^ b->sign); - dest->tag = TW_Valid; + reg_u_mul(a, b, dest, control_w); + dest->exp += - EXP_BIAS + 1; +/* dest->tag = TW_Valid; ****** */ if ( dest->exp <= EXP_UNDER ) { arith_underflow(FPU_st0_ptr); } else if ( dest->exp >= EXP_OVER ) diff --git a/kernel/FPU-emu/reg_round.S b/kernel/FPU-emu/reg_round.S new file mode 100644 index 000000000000..78e136af45ca --- /dev/null +++ b/kernel/FPU-emu/reg_round.S @@ -0,0 +1,369 @@ + .file "reg_round.S" +/*---------------------------------------------------------------------------+ + | reg_round.S | + | | + | Rounding/truncation/etc for FPU basic arithmetic functions. | + | | + | Copyright (C) 1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | + | | + | Not callable from C. | + | Must be entered by a jmp intruction. | + | | + +---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------+ + | Two entry points. | + | | + | Needed by both entry points: | + | %eax:%ebx 64 bit significand | + | %edx 32 bit extension of the significand | + | %edi pointer to an FPU_REG for the result to be stored | + | stack calling function must have set up a C stack frame and | + | pushed %esi, %edi, and %ebx | + | | + | Needed just for the FPU_round_sqrt entry point: | + | %cx A control word in the same format as the FPU control word. | + | Otherwise, PARAM4 must give such a value. | + | | + | | + | The significand and its extension are assumed to be exact in the | + | following sense: | + | If the significand by itself is the exact result then the significand | + | extension (%edx) must contain 0, otherwise the significand extension | + | must be non-zero. | + | If the significand extension is non-zero then the significand is | + | smaller than the magnitude of the correct exact result by an amount | + | greater than zero and less than one ls bit of the significand. | + | The significand extension is only required to have three possible | + | non-zero values: | + | less than 0x80000000 <=> the significand is less than 1/2 an ls | + | bit smaller than the magnitude of the | + | true exact result. | + | exactly 0x80000000 <=> the significand is exactly 1/2 an ls bit | + | smaller than the magnitude of the true | + | exact result. | + | greater than 0x80000000 <=> the significand is more than 1/2 an ls | + | bit smaller than the magnitude of the | + | true exact result. | + | | + +---------------------------------------------------------------------------*/ + +#include "fpu_asm.h" +#include "fpu_emu.h" +#include "exception.h" +#include "control_w.h" + + +.text + .align 2,144 +.globl FPU_round +.globl FPU_round_sqrt + +FPU_round: + /* Round the result */ + movl PARAM4,%ecx + +FPU_round_sqrt: // entry point from wm_sqrt.S + movl %ecx,%esi + andl CW_PC,%ecx + cmpl PR_64_BITS,%ecx + je LRound_To_64 + + cmpl PR_53_BITS,%ecx + je LRound_To_53 + + cmpl PR_24_BITS,%ecx + je LRound_To_24 + +#ifdef PARANOID + jmp L_bugged // There is no bug, just a bad control word +#endif PARANOID + + +LRound_To_24: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_24 + + cmpl RC_CHOP,%ecx + je LTruncate_24 + + cmpl RC_UP,%ecx // Towards +infinity + je LUp_24 + + cmpl RC_DOWN,%ecx // Towards -infinity + je LDown_24 + +#ifdef PARANOID + jmp L_bugged +#endif PARANOID + +LUp_24: + cmpb SIGN_POS,SIGN(%edi) + je LUp_24_pos + + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jnz LTruncate_24 + jmp L_store + +LUp_24_pos: + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jnz LDo_24_round_up + jmp L_store + +LDown_24: + cmpb SIGN_POS,SIGN(%edi) + je LDown_24_pos + + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jnz LDo_24_round_up + jmp L_store + +LDown_24_pos: + movl %eax,%ecx + andl $0x000000ff,%ecx + orl %ebx,%ecx + orl %edx,%ecx + jnz LTruncate_24 + jmp L_store + +LRound_nearest_24: + // Do rounding of the 24th bit if needed (nearest or even) + movl %eax,%ecx + andl $0x000000ff,%ecx + cmpl $0x00000080,%ecx + jc LTruncate_24 // less than half, no increment needed + + jne LGreater_Half_24 // greater than half, increment needed + + // Possibly half, we need to check the ls bits + orl %ebx,%ebx + jne LGreater_Half_24 // greater than half, increment needed + + orl %edx,%edx + jne LGreater_Half_24 // greater than half, increment needed + + // Exactly half, increment only if 24th bit is 1 (round to even) + testl $0x00000100,%eax + jz LTruncate_24 + +LGreater_Half_24: // Rounding: increment at the 24th bit +LDo_24_round_up: + andl $0xffffff00,%eax // Truncate to 24 bits + xorl %ebx,%ebx + addl $0x00000100,%eax + jmp LCheck_Round_Overflow + +LTruncate_24: + andl $0xffffff00,%eax // Truncate to 24 bits + xorl %ebx,%ebx + jmp L_store + + +LRound_To_53: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_53 + + cmpl RC_CHOP,%ecx + je LTruncate_53 + + cmpl RC_UP,%ecx // Towards +infinity + je LUp_53 + + cmpl RC_DOWN,%ecx // Towards -infinity + je LDown_53 + +#ifdef PARANOID + jmp L_bugged +#endif PARANOID + +LUp_53: + cmpb SIGN_POS,SIGN(%edi) + je LUp_53_pos + + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jnz LTruncate_53 + jmp L_store + +LUp_53_pos: + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jnz LDo_53_round_up + jmp L_store + +LDown_53: + cmpb SIGN_POS,SIGN(%edi) + je LDown_53_pos + + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jnz LDo_53_round_up + jmp L_store + +LDown_53_pos: + movl %ebx,%ecx + andl $0x000007ff,%ecx + orl %edx,%ecx + jnz LTruncate_53 + jmp L_store + +LRound_nearest_53: + // Do rounding of the 53rd bit if needed (nearest or even) + movl %ebx,%ecx + andl $0x000007ff,%ecx + cmpl $0x00000400,%ecx + jc LTruncate_53 // less than half, no increment needed + + jne LGreater_Half_53 // greater than half, increment needed + + // Possibly half, we need to check the ls bits + orl %edx,%edx + jne LGreater_Half_53 // greater than half, increment needed + + // Exactly half, increment only if 53rd bit is 1 (round to even) + testl $0x00000800,%ebx + jz LTruncate_53 + +LGreater_Half_53: // Rounding: increment at the 53rd bit +LDo_53_round_up: + andl $0xfffff800,%ebx // Truncate to 53 bits + addl $0x00000800,%ebx + adcl $0,%eax + jmp LCheck_Round_Overflow + +LTruncate_53: + andl $0xfffff800,%ebx // Truncate to 53 bits + jmp L_store + + +LRound_To_64: + movl %esi,%ecx + andl CW_RC,%ecx + cmpl RC_RND,%ecx + je LRound_nearest_64 + + cmpl RC_CHOP,%ecx + je LTruncate_64 + + cmpl RC_UP,%ecx // Towards +infinity + je LUp_64 + + cmpl RC_DOWN,%ecx // Towards -infinity + je LDown_64 + +#ifdef PARANOID + jmp L_bugged +#endif PARANOID + +LUp_64: + cmpb SIGN_POS,SIGN(%edi) + je LUp_64_pos + + orl %edx,%edx + jnz LTruncate_64 + jmp L_store + +LUp_64_pos: + orl %edx,%edx + jnz LDo_64_round_up + jmp L_store + +LDown_64: + cmpb SIGN_POS,SIGN(%edi) + je LDown_64_pos + + orl %edx,%edx + jnz LDo_64_round_up + jmp L_store + +LDown_64_pos: + orl %edx,%edx + jnz LTruncate_64 + jmp L_store + +LRound_nearest_64: + cmpl $0x80000000,%edx + jc LTruncate_64 + + jne LDo_64_round_up + + /* Now test for round-to-even */ + testb $1,%ebx + jz LTruncate_64 + +LDo_64_round_up: + addl $1,%ebx + adcl $0,%eax + +LCheck_Round_Overflow: + jnc L_store /* Rounding done, no overflow */ + + /* Overflow, adjust the result (to 1.0) */ + rcrl $1,%eax + rcrl $1,%ebx + incl EXP(%edi) + +LTruncate_64: +L_store: + /* store the result */ + movl %eax,SIGH(%edi) + movl %ebx,SIGL(%edi) + + movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */ + + // The number may have overflowed + cmpl EXP_OVER,EXP(%edi) + jge L_overflow + + cmpl EXP_UNDER,EXP(%edi) + jle L_underflow + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret + + +/* The operations resulted in a number too large to represent */ +L_overflow: + push %edi + call _arith_overflow + pop %edi + jmp L_exit + +/* The operations resulted in a number too small to represent */ +L_underflow: + pushl %edi + call _arith_underflow + popl %edi + jmp L_exit + + +#ifdef PARANOID +/* If we ever get here then we have problems! */ +L_bugged: + pushl EX_INTERNAL|0x201 + call EXCEPTION + pop %ebx + jmp L_exit +#endif PARANOID diff --git a/kernel/FPU-emu/reg_u_add.S b/kernel/FPU-emu/reg_u_add.S index 1595e3e4b377..f2032615a2b1 100644 --- a/kernel/FPU-emu/reg_u_add.S +++ b/kernel/FPU-emu/reg_u_add.S @@ -5,11 +5,13 @@ | Add two valid (TW_Valid) FPU_REG numbers, of the same sign, and put the | | result in a destination FPU_REG. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | Call from C as: | - | void reg_u_add(reg *arg1, reg *arg2, reg *answ) | + | void reg_u_add(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int control_w) | | | +---------------------------------------------------------------------------*/ @@ -24,6 +26,7 @@ #include "exception.h" #include "fpu_asm.h" +#include "control_w.h" .text .align 2,144 @@ -59,7 +62,10 @@ L_arg1_larger: movl SIGH(%edi),%eax L_accum_loaded: - movl 16(%ebp),%edi /* destination */ + movl PARAM3,%edi /* destination */ + movb SIGN(%esi),%dl + movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ + movl EXP(%esi),%edx movl %edx,EXP(%edi) /* Copy exponent to destination */ @@ -74,6 +80,7 @@ L_accum_loaded: je L_bugged #endif PARANOID +// The number to be shifted is in %eax:%ebx:%edx cmpw $32,%cx /* shrd only works for 0..31 bits */ jnc L_more_than_31 @@ -88,32 +95,43 @@ L_more_than_31: jnc L_more_than_63 subb $32,%cl + jz L_exactly_32 + shrd %cl,%eax,%edx shr %cl,%eax + orl %ebx,%ebx + jz L_more_31_no_low // none of the lowest bits is set + + orl $1,%edx // record the fact in the extension + +L_more_31_no_low: + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_exactly_32: + movl %ebx,%edx movl %eax,%ebx xorl %eax,%eax jmp L_shift_done L_more_than_63: - cmpw $66,%cx - jnc L_more_than_65 + cmpw $65,%cx + jnc L_more_than_64 - subb $64,%cl movl %eax,%edx - shr %cl,%edx + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_64: + movl $1,%edx // The shifted nr always at least one '1' + +L_more_63_no_low: xorl %ebx,%ebx xorl %eax,%eax - jmp L_shift_done - -L_more_than_65: - /* just copy the larger reg to dest */ - movw SIGN(%esi),%ax - movw %ax,SIGN(%edi) - movl SIGL(%esi),%eax - movl %eax,SIGL(%edi) - movl SIGH(%esi),%eax - movl %eax,SIGH(%edi) - jmp L_exit // Does not overflow L_shift_done: /* Now do the addition */ @@ -125,56 +143,16 @@ L_shift_done: rcrl $1,%eax rcrl $1,%ebx rcrl $1,%edx + jnc L_no_bit_lost - incl EXP(%edi) - -L_round_the_result: - /* Round the result */ - cmpl $0x80000000,%edx - jc L_no_round_up - - jne L_do_round_up - - /* Now test for round-to-even */ - testb $1,%ebx - jz L_no_round_up - -L_do_round_up: - addl $1,%ebx - adcl $0,%eax - jnc L_no_round_up /* Rounding done, no overflow */ + orl $1,%edx - /* Overflow, adjust the result */ - rcrl $1,%eax - rcrl $1,%ebx +L_no_bit_lost: incl EXP(%edi) -L_no_round_up: - /* store the result */ - movl %eax,SIGH(%edi) - movl %ebx,SIGL(%edi) +L_round_the_result: + jmp FPU_round // Round the result - movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */ - movb SIGN(%esi),%al - movb %al,SIGN(%edi) /* Copy the sign from the first arg */ - - // The number may have overflowed - cmpl EXP_OVER,EXP(%edi) - jge L_overflow - -L_exit: - popl %ebx - popl %edi - popl %esi - leave - ret - -/* The addition resulted in a number too large to represent */ -L_overflow: - push %edi - call _arith_overflow - pop %ebx - jmp L_exit #ifdef PARANOID @@ -185,3 +163,11 @@ L_bugged: pop %ebx jmp L_exit #endif PARANOID + + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret diff --git a/kernel/FPU-emu/reg_u_div.S b/kernel/FPU-emu/reg_u_div.S index a24059c14106..0a6a0dda2234 100644 --- a/kernel/FPU-emu/reg_u_div.S +++ b/kernel/FPU-emu/reg_u_div.S @@ -4,7 +4,8 @@ | | | Core division routines | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -13,18 +14,19 @@ /*---------------------------------------------------------------------------+ | Kernel for the division routines. | | | - | void reg_u_div(unsigned long long *a, unsigned long long *a, | - | FPU_REG *dest) | + | void reg_u_div(FPU_REG *a, FPU_REG *a, | + | FPU_REG *dest, unsigned int control_word) | | | | Does not compute the destination exponent, but does adjust it. | +---------------------------------------------------------------------------*/ #include "exception.h" #include "fpu_asm.h" +#include "control_w.h" -#define dSIGL(x) (x) -#define dSIGH(x) 4(x) +// #define dSIGL(x) (x) +// #define dSIGH(x) 4(x) .data @@ -71,43 +73,43 @@ _reg_u_div: _divide_kernel: #ifdef PARANOID -// testl $0x80000000, dSIGH(%esi) -// je xL_bugged - testl $0x80000000, dSIGH(%ebx) - je xL_bugged +// testl $0x80000000, SIGH(%esi) // Dividend +// je L_bugged + testl $0x80000000, SIGH(%ebx) // Divisor + je L_bugged #endif PARANOID -/* Check if the denominator can be treated as having just 32 bits */ - cmpl $0,dSIGL(%ebx) +/* Check if the divisor can be treated as having just 32 bits */ + cmpl $0,SIGL(%ebx) jnz L_Full_Division /* Can't do a quick divide */ /* We should be able to zip through the division here */ - movl dSIGH(%ebx),%ecx /* The denominator */ - movl dSIGH(%esi),%edx /* Get the current num */ - movl dSIGL(%esi),%eax /* Get the current num */ + movl SIGH(%ebx),%ecx /* The divisor */ + movl SIGH(%esi),%edx /* Dividend */ + movl SIGL(%esi),%eax /* Dividend */ cmpl %ecx,%edx setaeb ovfl_flag /* Keep a record */ - jb xL_no_adjust + jb L_no_adjust subl %ecx,%edx /* Prevent the overflow */ -xL_no_adjust: +L_no_adjust: /* Divide the 64 bit number by the 32 bit denominator */ divl %ecx - movl %eax,SIGH(%edi) /* Put the result in the answer */ + movl %eax,result_2 /* Work on the remainder of the first division */ xorl %eax,%eax divl %ecx - movl %eax,SIGL(%edi) /* Put the result in the answer */ + movl %eax,result_1 /* Work on the remainder of the 64 bit division */ xorl %eax,%eax divl %ecx testb $255,ovfl_flag /* was the num > denom ? */ - je xL_no_overflow + je L_no_overflow /* Do the shifting here */ /* increase the exponent */ @@ -115,44 +117,12 @@ xL_no_adjust: /* shift the mantissa right one bit */ stc /* To set the ms bit */ - rcrl SIGH(%edi) - rcrl SIGL(%edi) + rcrl result_2 + rcrl result_1 rcrl %eax -xL_no_overflow: - cmpl $0x80000000,%eax - jc xL_no_round - jnz xL_round_up - - /* "round to even" used */ - testb $1,SIGL(%edi) - jz xL_no_round - -xL_round_up: - addl $1,SIGL(%edi) - adcl $0,SIGH(%edi) - -#ifdef PARANOID - jc xL_bugged2 -#endif PARANOID - -xL_no_round: - jmp xL_done - - -#ifdef PARANOID -xL_bugged: - pushl EX_INTERNAL|0x203 - call EXCEPTION - pop %ebx - jmp xL_exit - -xL_bugged2: - pushl EX_INTERNAL|0x204 - call EXCEPTION - pop %ebx - jmp xL_exit -#endif PARANOID +L_no_overflow: + jmp LRound_precision // Do the rounding as required /*---------------------------------------------------------------------------+ @@ -174,234 +144,248 @@ xL_bugged2: L_Full_Division: - movl dSIGL(%esi),%eax /* Save extended num in local register */ + // Save extended dividend in local register + movl SIGL(%esi),%eax movl %eax,accum_2 - movl dSIGH(%esi),%eax + movl SIGH(%esi),%eax movl %eax,accum_3 xorl %eax,%eax movl %eax,accum_1 /* zero the extension */ movl %eax,accum_0 /* zero the extension */ - movl dSIGL(%esi),%eax /* Get the current num */ - movl dSIGH(%esi),%edx + movl SIGL(%esi),%eax /* Get the current num */ + movl SIGH(%esi),%edx /*----------------------------------------------------------------------*/ /* Initialization done */ /* Do the first 32 bits */ movb $0,ovfl_flag - cmpl dSIGH(%ebx),%edx /* Test for imminent overflow */ - jb L02 - ja L01 + cmpl SIGH(%ebx),%edx /* Test for imminent overflow */ + jb LLess_than_1 + ja LGreater_than_1 - cmpl dSIGL(%ebx),%eax - jb L02 + cmpl SIGL(%ebx),%eax + jb LLess_than_1 -L01: -/* The numerator is greater or equal, would cause overflow */ +LGreater_than_1: +/* The dividend is greater or equal, would cause overflow */ setaeb ovfl_flag /* Keep a record */ - subl dSIGL(%ebx),%eax - sbbl dSIGH(%ebx),%edx /* Prevent the overflow */ + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx /* Prevent the overflow */ movl %eax,accum_2 movl %edx,accum_3 -L02: -/* At this point, we have a num < denom, with a record of +LLess_than_1: +/* At this point, we have a dividend < divisor, with a record of adjustment in ovfl_flag */ /* We will divide by a number which is too large */ - movl dSIGH(%ebx),%ecx + movl SIGH(%ebx),%ecx addl $1,%ecx - jnc L04 + jnc LFirst_div_not_1 /* here we need to divide by 100000000h, i.e., no division at all.. */ - mov %edx,%eax - jmp L05 + jmp LFirst_div_done -L04: +LFirst_div_not_1: divl %ecx /* Divide the numerator by the augmented denom ms dw */ -L05: +LFirst_div_done: movl %eax,result_2 /* Put the result in the answer */ - mull dSIGH(%ebx) /* mul by the ms dw of the denom */ + mull SIGH(%ebx) /* mul by the ms dw of the denom */ subl %eax,accum_2 /* Subtract from the num local reg */ sbbl %edx,accum_3 movl result_2,%eax /* Get the result back */ - mull dSIGL(%ebx) /* now mul the ls dw of the denom */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ subl %eax,accum_1 /* Subtract from the num local reg */ sbbl %edx,accum_2 sbbl $0,accum_3 - je L10 /* Must check for non-zero result here */ + je LDo_2nd_32_bits /* Must check for non-zero result here */ #ifdef PARANOID - jb L_bugged + jb L_bugged_1 #endif PARANOID /* need to subtract another once of the denom */ - incl result_2 /* Correct the answer */ + incl result_2 /* Correct the answer */ - movl dSIGL(%ebx),%eax - movl dSIGH(%ebx),%edx + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx subl %eax,accum_1 /* Subtract from the num local reg */ sbbl %edx,accum_2 #ifdef PARANOID sbbl $0,accum_3 - jne L_bugged /* Must check for non-zero result here */ + jne L_bugged_1 /* Must check for non-zero result here */ #endif PARANOID /*----------------------------------------------------------------------*/ /* Half of the main problem is done, there is just a reduced numerator to handle now */ /* Work with the second 32 bits, accum_0 not used from now on */ -L10: +LDo_2nd_32_bits: movl accum_2,%edx /* get the reduced num */ movl accum_1,%eax /* need to check for possible subsequent overflow */ - cmpl dSIGH(%ebx),%edx - jb L22 - ja L21 + cmpl SIGH(%ebx),%edx + jb LDo_2nd_div + ja LPrevent_2nd_overflow - cmpl dSIGL(%ebx),%eax - jb L22 + cmpl SIGL(%ebx),%eax + jb LDo_2nd_div -L21: +LPrevent_2nd_overflow: /* The numerator is greater or equal, would cause overflow */ /* prevent overflow */ - subl dSIGL(%ebx),%eax - sbbl dSIGH(%ebx),%edx + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx movl %edx,accum_2 movl %eax,accum_1 - incl result_2 /* Reflect the subtraction in the answer */ + incl result_2 /* Reflect the subtraction in the answer */ #ifdef PARANOID - je L_bugged + je L_bugged_2 /* Can't bump the result to 1.0 */ #endif PARANOID -L22: - cmpl $0,%ecx - jnz L24 +LDo_2nd_div: + cmpl $0,%ecx // augmented denom msw + jnz LSecond_div_not_1 /* %ecx == 0, we are dividing by 1.0 */ mov %edx,%eax - jmp L25 + jmp LSecond_div_done -L24: +LSecond_div_not_1: divl %ecx /* Divide the numerator by the denom ms dw */ -L25: +LSecond_div_done: movl %eax,result_1 /* Put the result in the answer */ - mull dSIGH(%ebx) /* mul by the ms dw of the denom */ + mull SIGH(%ebx) /* mul by the ms dw of the denom */ subl %eax,accum_1 /* Subtract from the num local reg */ sbbl %edx,accum_2 #ifdef PARANOID - jc L_bugged + jc L_bugged_2 #endif PARANOID movl result_1,%eax /* Get the result back */ - mull dSIGL(%ebx) /* now mul the ls dw of the denom */ + mull SIGL(%ebx) /* now mul the ls dw of the denom */ - /* Here we are throwing away some ls bits */ subl %eax,accum_0 /* Subtract from the num local reg */ sbbl %edx,accum_1 /* Subtract from the num local reg */ sbbl $0,accum_2 #ifdef PARANOID - jc L_bugged + jc L_bugged_2 #endif PARANOID - jz L35 /* Just deal with rounding now */ + jz LDo_3rd_32_bits #ifdef PARANOID cmpl $1,accum_2 - jne L_bugged + jne L_bugged_2 #endif PARANOID -L32: /* need to subtract another once of the denom */ - movl dSIGL(%ebx),%eax - movl dSIGH(%ebx),%edx + movl SIGL(%ebx),%eax + movl SIGH(%ebx),%edx subl %eax,accum_0 /* Subtract from the num local reg */ sbbl %edx,accum_1 sbbl $0,accum_2 #ifdef PARANOID - jc L_bugged - jne L_bugged + jc L_bugged_2 + jne L_bugged_2 #endif PARANOID addl $1,result_1 /* Correct the answer */ adcl $0,result_2 #ifdef PARANOID - /* Do we ever really need this check? ***** */ - jc L_bugged /* Must check for non-zero result here */ + jc L_bugged_2 /* Must check for non-zero result here */ #endif PARANOID /*----------------------------------------------------------------------*/ /* The division is essentially finished here, we just need to perform tidying operations. */ /* deal with the 3rd 32 bits */ -L35: - movl accum_1,%edx /* get the reduced num */ +LDo_3rd_32_bits: + movl accum_1,%edx /* get the reduced num */ movl accum_0,%eax /* need to check for possible subsequent overflow */ - cmpl dSIGH(%ebx),%edx - jb L42 - ja L41 + cmpl SIGH(%ebx),%edx // denom + jb LRound_prep + ja LPrevent_3rd_overflow - cmpl dSIGL(%ebx),%eax - jb L42 + cmpl SIGL(%ebx),%eax // denom + jb LRound_prep -L41: +LPrevent_3rd_overflow: /* prevent overflow */ - subl dSIGL(%ebx),%eax - sbbl dSIGH(%ebx),%edx + subl SIGL(%ebx),%eax + sbbl SIGH(%ebx),%edx movl %edx,accum_1 - movl %eax,accum_0 /* ***** not needed unless extended rounding */ + movl %eax,accum_0 addl $1,result_1 /* Reflect the subtraction in the answer */ adcl $0,result_2 - jne L42 - jnc L42 + jne LRound_prep + jnc LRound_prep /* This is a tricky spot, there is an overflow of the answer */ movb $255,ovfl_flag /* Overflow -> 1.000 */ - -L42: - /* Now test for rounding */ - movl accum_1,%edx /* ms byte of accum extension */ -L44: - cmpl $0,%ecx - jnz L241 +LRound_prep: +// Prepare for rounding. +// To test for rounding, we just need to compare 2*accum with the +// denom. + movl accum_0,%ecx + movl accum_1,%edx + movl %ecx,%eax + orl %edx,%eax + jz LRound_ovfl // The accumulator contains zero. - /* %ecx == 0, we are dividing by 1.0 */ - mov %edx,%eax - jmp L251 + // Multiply by 2 + clc + rcll $1,%ecx + rcll $1,%edx + jc LRound_large // No need to compare, denom smaller -L241: - divl %ecx /* Divide the numerator by the denom ms dw */ + subl SIGL(%ebx),%ecx + sbbl SIGH(%ebx),%edx + jnc LRound_not_small + + movl $0x70000000,%eax // Denom was larger + jmp LRound_ovfl + +LRound_not_small: + jnz LRound_large -L251: + movl $0x80000000,%eax // Remainder was exactly 1/2 denom + jmp LRound_ovfl + +LRound_large: + movl $0xff000000,%eax // Denom was smaller + +LRound_ovfl: /* We are now ready to deal with rounding, but first we must get the bits properly aligned */ testb $255,ovfl_flag /* was the num > denom ? */ - je L45 + je LRound_precision incl EXP(%edi) @@ -411,66 +395,41 @@ L251: rcrl result_1 rcrl %eax -L45: - cmpl $0x80000000,%eax - jc xL_no_round_2 // No round up - jnz xL_round_up_2 +// Round the result as required +LRound_precision: + decl EXP(%edi) /* binary point between 1st & 2nd bits */ - /* "round to even" used here for now... */ - testb $1,result_1 - jz xL_no_round_2 // No round up + movl %eax,%edx + movl result_1,%ebx + movl result_2,%eax + jmp FPU_round -xL_round_up_2: - addl $1,result_1 - adcl $0,result_2 #ifdef PARANOID - jc L_bugged -#endif PARANOID - -xL_no_round_2: - movl result_1,%eax - movl %eax,SIGL(%edi) - movl result_2,%eax - movl %eax,SIGH(%edi) - -xL_done: - decl EXP(%edi) /* binary point between 1st & 2nd bits */ +/* The logic is wrong if we got here */ +L_bugged: + pushl EX_INTERNAL|0x202 + call EXCEPTION + pop %ebx + jmp L_exit -xL_check_exponent: - cmpl EXP_OVER,EXP(%edi) - jge xL_overflow +L_bugged_1: + pushl EX_INTERNAL|0x203 + call EXCEPTION + pop %ebx + jmp L_exit - cmpl EXP_UNDER,EXP(%edi) - jle xL_underflow +L_bugged_2: + pushl EX_INTERNAL|0x204 + call EXCEPTION + pop %ebx + jmp L_exit -xL_exit: +L_exit: popl %ebx popl %edi popl %esi leave ret - - -xL_overflow: - pushl %edi - call _arith_overflow - popl %ebx - jmp xL_exit - -xL_underflow: - pushl %edi - call _arith_underflow - popl %ebx - jmp xL_exit - - -#ifdef PARANOID -/* The logic is wrong if we got here */ -L_bugged: - pushl EX_INTERNAL|0x202 - call EXCEPTION - pop %ebx - jmp xL_exit #endif PARANOID diff --git a/kernel/FPU-emu/reg_u_mul.S b/kernel/FPU-emu/reg_u_mul.S index 71f0289443f7..102c2c73c39e 100644 --- a/kernel/FPU-emu/reg_u_mul.S +++ b/kernel/FPU-emu/reg_u_mul.S @@ -4,7 +4,8 @@ | | | Core multiplication routine | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -14,17 +15,22 @@ | Basic multiplication routine. | | Does not check the resulting exponent for overflow/underflow | | | - | Internal working is at approx 96 bits. | - | Result is rounded to nearest 64 bits, using "nearest or even". | + | reg_u_mul(FPU_REG *a, FPU_REG *b, FPU_REG *c, unsigned int cw); | + | | + | Internal working is at approx 128 bits. | + | Result is rounded to nearest 53 or 64 bits, using "nearest or even". | +---------------------------------------------------------------------------*/ #include "exception.h" #include "fpu_asm.h" +#include "control_w.h" .data .align 2,0 +accum_0: + .long 0 accum_1: .long 0 @@ -41,97 +47,82 @@ _reg_u_mul: pushl %ebx movl PARAM1,%esi - movl PARAM2,%ecx + movl PARAM2,%edi #ifdef PARANOID testl $0x80000000,SIGH(%esi) - jz xL_bugged - testl $0x80000000,SIGH(%ecx) - jz xL_bugged + jz L_bugged + testl $0x80000000,SIGH(%edi) + jz L_bugged #endif PARANOID - xorl %edi,%edi + xorl %ecx,%ecx xorl %ebx,%ebx movl SIGL(%esi),%eax - mull SIGL(%ecx) -// movl %eax,accum_0 + mull SIGL(%edi) + movl %eax,accum_0 movl %edx,accum_1 movl SIGL(%esi),%eax - mull SIGH(%ecx) + mull SIGH(%edi) addl %eax,accum_1 adcl %edx,%ebx -// adcl $0,%edi // overflow here is not possible +// adcl $0,%ecx // overflow here is not possible movl SIGH(%esi),%eax - mull SIGL(%ecx) + mull SIGL(%edi) addl %eax,accum_1 adcl %edx,%ebx - adcl $0,%edi + adcl $0,%ecx movl SIGH(%esi),%eax - mull SIGH(%ecx) + mull SIGH(%edi) addl %eax,%ebx - adcl %edx,%edi + adcl %edx,%ecx movl EXP(%esi),%eax /* Compute the exponent */ - addl EXP(%ecx),%eax + addl EXP(%edi),%eax // Have now finished with the sources - movl PARAM3,%esi // Point to the destination - movl %eax,EXP(%esi) + movl PARAM3,%edi // Point to the destination + movl %eax,EXP(%edi) // Now make sure that the result is normalized - testl $0x80000000,%edi - jnz L20 + testl $0x80000000,%ecx + jnz LResult_Normalised /* Normalize by shifting left one bit */ -// shll $1,accum_0 // If using this, change next to rcll - shll $1,accum_1 + shll $1,accum_0 + rcll $1,accum_1 rcll $1,%ebx - rcll $1,%edi - decl EXP(%esi) + rcll $1,%ecx + decl EXP(%edi) -L20: - /* Do the rounding */ - cmpl $0x80000000,accum_1 - jc L40 +LResult_Normalised: + movl accum_0,%eax + movl accum_1,%edx + orl %eax,%eax + jz L_extent_zero - jne L30 + orl $1,%edx - /* 0x80000000, round up only if previous bit is 1 */ - testl $1,%ebx - jz L40 +L_extent_zero: + movl %ecx,%eax + jmp FPU_round -L30: - addl $1,%ebx - adcl $0,%edi - /* An overflow can occur here (rare!) */ - jc xL_overflow_adjust - -L40: - /* Copy the result to the destination register */ - movl %ebx,SIGL(%esi) - movl %edi,SIGH(%esi) +#ifdef PARANOID +L_bugged: + pushl EX_INTERNAL|0x205 + call EXCEPTION + pop %ebx + jmp L_exit -xL_exit: +L_exit: popl %ebx popl %edi popl %esi leave ret - - -xL_overflow_adjust: - rcrl %edi - incl EXP(%esi) - jmp L40 - -#ifdef PARANOID -xL_bugged: - pushl EX_INTERNAL|0x205 - call EXCEPTION - pop %ebx - jmp xL_exit #endif PARANOID + diff --git a/kernel/FPU-emu/reg_u_sub.S b/kernel/FPU-emu/reg_u_sub.S index f3615a3de7c8..c14fc8144516 100644 --- a/kernel/FPU-emu/reg_u_sub.S +++ b/kernel/FPU-emu/reg_u_sub.S @@ -4,11 +4,13 @@ | | | Core floating point subtraction routine. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | Call from C as: | - | void reg_u_sub(reg *arg1, reg *arg2, reg *answ) | + | void reg_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, | + | int control_w) | | | +---------------------------------------------------------------------------*/ @@ -25,6 +27,7 @@ #include "exception.h" #include "fpu_asm.h" +#include "control_w.h" .text @@ -66,6 +69,8 @@ _reg_u_sub: movl PARAM3,%edi /* destination */ movl EXP(%esi),%edx movl %edx,EXP(%edi) /* Copy exponent to destination */ + movb SIGN(%esi),%dl + movb %dl,SIGN(%edi) /* Copy the sign from the first arg */ xorl %edx,%edx // register extension @@ -89,34 +94,63 @@ L_more_than_31: jnc L_more_than_63 subb $32,%cl + jz L_exactly_32 + shrd %cl,%eax,%edx + shr %cl,%eax + orl %ebx,%ebx + jz L_more_31_no_low // none of the lowest bits is set + + orl $1,%edx // record the fact in the extension + +L_more_31_no_low: + movl %eax,%ebx + xorl %eax,%eax + jmp L_shift_done + +L_exactly_32: + movl %ebx,%edx movl %eax,%ebx - shr %cl,%ebx xorl %eax,%eax jmp L_shift_done L_more_than_63: - cmpl $66,%ecx - jnc L_more_than_65 + cmpw $65,%cx + jnc L_more_than_64 - subb $64,%cl + // Shift right by 64 bits movl %eax,%edx - shr %cl,%edx - xorl %ebx,%ebx - xorl %eax,%eax - jmp L_shift_done + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low + +L_more_than_64: + jne L_more_than_65 + + // Shift right by 65 bits + // Carry is clear if we get here + movl %eax,%edx + rcrl %edx + jnc L_shift_65_nc + + orl $1,%edx + jmp L_more_63_no_low + +L_shift_65_nc: + orl %ebx,%ebx + jz L_more_63_no_low + + orl $1,%edx + jmp L_more_63_no_low L_more_than_65: - /* just copy the larger reg to dest */ - movw SIGN(%esi),%ax - movw %ax,SIGN(%edi) - movl EXP(%esi),%eax - movl %eax,EXP(%edi) - movl SIGL(%esi),%eax - movl %eax,SIGL(%edi) - movl SIGH(%esi),%eax - movl %eax,SIGH(%edi) - jmp L_exit // Does not underflow + movl $1,%edx // The shifted nr always at least one '1' + +L_more_63_no_low: + xorl %ebx,%ebx + xorl %eax,%eax L_shift_done: L_subtr: @@ -194,54 +228,7 @@ L_shift_1: subl %ecx,EXP(%edi) /* Can get underflow here */ L_round: -/*------------------------------+ - | Round the result | - +------------------------------*/ - cmpl $0x80000000,%edx - jc L_store - - jne L_round_up - - testb $1,%dl - jz L_store - -L_round_up: - addl $1,%ebx - adcl $0,%eax - jnc L_store - - /* We just rounded up to (1) 00 00 */ - /* This *is* possible, if the subtraction is of the - form (1. + x) - (x + y) where x is small and y is - very small. */ - incl EXP(%edi) - movl $0x80000000,%eax - -L_store: -/*------------------------------+ - | Store the result | - +------------------------------*/ - movl %eax,SIGH(%edi) - movl %ebx,SIGL(%edi) - - movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */ - - cmpl EXP_UNDER,EXP(%edi) - jle L_underflow - -L_exit: - popl %ebx - popl %edi - popl %esi - leave - ret - - -L_underflow: - push %edi - call _arith_underflow - pop %ebx - jmp L_exit + jmp FPU_round // Round the result #ifdef PARANOID @@ -275,3 +262,31 @@ L_bugged: pop %ebx jmp L_exit #endif PARANOID + + +L_store: +/*------------------------------+ + | Store the result | + +------------------------------*/ + movl %eax,SIGH(%edi) + movl %ebx,SIGL(%edi) + + movb TW_Valid,TAG(%edi) /* Set the tags to TW_Valid */ + + cmpl EXP_UNDER,EXP(%edi) + jle L_underflow + +L_exit: + popl %ebx + popl %edi + popl %esi + leave + ret + + +L_underflow: + push %edi + call _arith_underflow + pop %ebx + jmp L_exit + diff --git a/kernel/FPU-emu/version.h b/kernel/FPU-emu/version.h index 75edb8c76032..c4a37cbee1a7 100644 --- a/kernel/FPU-emu/version.h +++ b/kernel/FPU-emu/version.h @@ -9,5 +9,5 @@ | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version BETA 1.2" +#define FPU_VERSION "wm-FPU-emu version BETA 1.3" diff --git a/kernel/FPU-emu/wm_sqrt.S b/kernel/FPU-emu/wm_sqrt.S index b3fb0c23bf8f..2f2d53086fa2 100644 --- a/kernel/FPU-emu/wm_sqrt.S +++ b/kernel/FPU-emu/wm_sqrt.S @@ -4,16 +4,17 @@ | | | Fixed point arithmetic square root evaluation. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | Call from C as: | - | void wm_sqrt(FPU_REG *n) | + | void wm_sqrt(FPU_REG *n, unsigned int control_word) | | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ - | wm_sqrt(FPU_REG *n) | + | wm_sqrt(FPU_REG *n, unsigned int control_word) | | returns the square root of n in n. | | | | Use Newton's method to compute the square root of a number, which must | @@ -33,19 +34,25 @@ Local storage: */ .align 4,0 -accum_2: +accum_3: .long 0 // ms word +accum_2: + .long 0 accum_1: .long 0 accum_0: .long 0 -sq_2: +// The de-normalised argument: +// sq_2 sq_1 sq_0 +// b b b b b b b ... b b b b b b .... b b b b 0 0 0 ... 0 +// ^ binary point here +fsqrt_arg_2: .long 0 // ms word -sq_1: - .long 0 -sq_0: +fsqrt_arg_1: .long 0 +fsqrt_arg_0: + .long 0 // ls word, at most the ms bit is set .text .align 2,144 @@ -57,7 +64,7 @@ _wm_sqrt: movl %esp,%ebp pushl %esi pushl %edi -// pushl %ebx + pushl %ebx movl PARAM1,%esi @@ -68,37 +75,38 @@ _wm_sqrt: // We use a rough linear estimate for the first guess.. cmpl EXP_BIAS,EXP(%esi) - jnz L10 + jnz sqrt_arg_ge_2 shrl $1,%eax /* arg is in the range [1.0 .. 2.0) */ rcrl $1,%ecx rcrl $1,%edx -L10: +sqrt_arg_ge_2: // From here on, n is never accessed directly again until it is // replaced by the answer. - movl %eax,sq_2 // ms word of n - movl %ecx,sq_1 - movl %edx,sq_0 + movl %eax,fsqrt_arg_2 // ms word of n + movl %ecx,fsqrt_arg_1 + movl %edx,fsqrt_arg_0 +// Make a linear first estimate shrl $1,%eax addl $0x40000000,%eax movl $0xaaaaaaaa,%ecx mull %ecx shll %edx /* max result was 7fff... */ testl $0x80000000,%edx /* but min was 3fff... */ - jnz no_adjust + jnz sqrt_prelim_no_adjust movl $0x80000000,%edx /* round up */ -no_adjust: +sqrt_prelim_no_adjust: movl %edx,%esi // Our first guess /* We have now computed (approx) (2 + x) / 3, which forms the basis for a few iterations of Newton's method */ - movl sq_2,%ecx // ms word + movl fsqrt_arg_2,%ecx // ms word // From our initial estimate, three iterations are enough to get us // to 30 bits or so. This will then allow two iterations at better @@ -108,10 +116,10 @@ no_adjust: shrl %ecx // Doing this first will prevent a divide // overflow later. - movl %ecx,%edx - divl %esi - shrl %esi - addl %eax,%esi + movl %ecx,%edx // msw of the arg / 2 + divl %esi // current estimate + shrl %esi // divide by 2 + addl %eax,%esi // the new estimate movl %ecx,%edx divl %esi @@ -123,7 +131,7 @@ no_adjust: shrl %esi addl %eax,%esi -// Now that an estimate accurate to about 30 bits has been obtained, +// Now that an estimate accurate to about 30 bits has been obtained (in %esi), // we improve it to 60 bits or so. // The strategy from now on is to compute new estimates from @@ -134,14 +142,14 @@ no_adjust: mull %esi // guess^2 now in %edx:%eax - movl sq_1,%ecx + movl fsqrt_arg_1,%ecx subl %ecx,%eax - movl sq_2,%ecx // ms word of normalized n + movl fsqrt_arg_2,%ecx // ms word of normalized n sbbl %ecx,%edx - jnc l40 + jnc sqrt_stage_2_positive // subtraction gives a negative result -// negate the reult before division +// negate the result before division notl %edx notl %eax addl $1,%eax @@ -152,9 +160,9 @@ no_adjust: movl %edx,%eax divl %esi - jmp l45 + jmp sqrt_stage_2_finish -l40: +sqrt_stage_2_positive: divl %esi movl %eax,%ecx @@ -166,65 +174,93 @@ l40: addl $1,%eax adcl $0,%ecx -l45: +sqrt_stage_2_finish: sarl $1,%ecx // divide by 2 rcrl $1,%eax + // Form the new estimate in %esi:%edi movl %eax,%edi addl %ecx,%esi + jnz sqrt_stage_2_done // result should be [1..2) + +#ifdef PARANOID +// It should be possible to get here only if the arg is ffff....ffff + cmp $0xffffffff,fsqrt_arg_1 + jnz sqrt_stage_2_error +#endif PARANOID + +// The best rounded result. + xorl %eax,%eax + decl %eax + movl %eax,%edi + movl %eax,%esi + movl $0x7fffffff,%eax + jmp sqrt_round_result + +#ifdef PARANOID +sqrt_stage_2_error: + pushl EX_INTERNAL|0x213 + call EXCEPTION +#endif PARANOID + +sqrt_stage_2_done: + // Now the square root has been computed to better than 60 bits // Find the square of the guess movl %edi,%eax // ls word of guess mull %edi - movl %edx,accum_0 + movl %edx,accum_1 movl %esi,%eax mull %esi - movl %edx,accum_2 - movl %eax,accum_1 + movl %edx,accum_3 + movl %eax,accum_2 movl %edi,%eax mull %esi - addl %eax,accum_0 - adcl %edx,accum_1 - adcl $0,accum_2 + addl %eax,accum_1 + adcl %edx,accum_2 + adcl $0,accum_3 - movl %esi,%eax - mull %edi - addl %eax,accum_0 - adcl %edx,accum_1 - adcl $0,accum_2 +// movl %esi,%eax +// mull %edi + addl %eax,accum_1 + adcl %edx,accum_2 + adcl $0,accum_3 -// guess^2 now in accum_2:accum_1:accum_0 +// guess^2 now in accum_3:accum_2:accum_1 - movl sq_1,%eax // get normalized n + movl fsqrt_arg_0,%eax // get normalized n subl %eax,accum_1 - movl sq_2,%eax // ms word of normalized n + movl fsqrt_arg_1,%eax sbbl %eax,accum_2 - jnc l60 + movl fsqrt_arg_2,%eax // ms word of normalized n + sbbl %eax,accum_3 + jnc sqrt_stage_3_positive // subtraction gives a negative result -// negate the reult before division - notl accum_0 +// negate the result before division notl accum_1 notl accum_2 - addl $1,accum_0 - adcl $0,accum_1 + notl accum_3 + addl $1,accum_1 + adcl $0,accum_2 #ifdef PARANOID - adcl $0,accum_2 // This must be zero - jz l51 + adcl $0,accum_3 // This must be zero + jz sqrt_stage_3_no_error +sqrt_stage_3_error: pushl EX_INTERNAL|0x207 call EXCEPTION -l51: +sqrt_stage_3_no_error: #endif PARANOID - movl accum_1,%edx - movl accum_0,%eax + movl accum_2,%edx + movl accum_1,%eax divl %esi movl %eax,%ecx @@ -234,18 +270,16 @@ l51: sarl $1,%ecx // divide by 2 rcrl $1,%eax - // round the result - addl $0x80000000,%eax - adcl $0,%ecx + // prepare to round the result addl %ecx,%edi adcl $0,%esi - jmp l65 + jmp sqrt_stage_3_finished -l60: - movl accum_1,%edx - movl accum_0,%eax +sqrt_stage_3_positive: + movl accum_2,%edx + movl accum_1,%eax divl %esi movl %eax,%ecx @@ -255,23 +289,157 @@ l60: sarl $1,%ecx // divide by 2 rcrl $1,%eax - // round the result - addl $0x80000000,%eax - adcl $0,%ecx + // prepare to round the result + + notl %eax // Negate the correction term + notl %ecx + addl $1,%eax + adcl $0,%ecx // carry here ==> correction == 0 + adcl $0xffffffff,%esi + + addl %ecx,%edi + adcl $0,%esi + +sqrt_stage_3_finished: + +// The result in %esi:%edi:%esi should be good to about 90 bits here, +// and the rounding information here does not have sufficient accuracy +// in a few rare cases. + cmpl $0xffffffe0,%eax + ja sqrt_near_exact_x + + cmpl $0x00000020,%eax + jb sqrt_near_exact + + cmpl $0x7fffffe0,%eax + jb sqrt_round_result + + cmpl $0x80000020,%eax + jb sqrt_get_more_precision + +sqrt_round_result: +// Set up for rounding operations + movl %eax,%edx + movl %esi,%eax + movl %edi,%ebx + movl PARAM1,%edi + movl EXP_BIAS,EXP(%edi) // Result is in [1.0 .. 2.0) + movl PARAM2,%ecx + jmp FPU_round_sqrt + + +sqrt_near_exact_x: +// First, the estimate must be rounded up. + addl $1,%edi + adcl $0,%esi + +sqrt_near_exact: +// This is an easy case because x^1/2 is monotonic. +// We need just find the square of our estimate, compare it +// with the argument, and deduce whether our estimate is +// above, below, or exact. We use the fact that the estimate +// is known to be accurate to about 90 bits. + movl %edi,%eax // ls word of guess + mull %edi + movl %edx,%ebx // 2nd ls word of square + movl %eax,%ecx // ls word of square + + movl %edi,%eax + mull %esi + addl %eax,%ebx + addl %eax,%ebx + +#ifdef PARANOID + cmp $0xffffffb0,%ebx + jb sqrt_near_exact_ok + + cmp $0x00000050,%ebx + ja sqrt_near_exact_ok + + pushl EX_INTERNAL|0x214 + call EXCEPTION + +sqrt_near_exact_ok: +#endif PARANOID + + or %ebx,%ebx + js sqrt_near_exact_small + + jnz sqrt_near_exact_large - subl %ecx,%edi + or %ebx,%edx + jnz sqrt_near_exact_large + +// Our estimate is exactly the right answer + xorl %eax,%eax + jmp sqrt_round_result + +sqrt_near_exact_small: +// Our estimate is too small + movl $0x000000ff,%eax + jmp sqrt_round_result + +sqrt_near_exact_large: +// Our estimate is too large, we need to decrement it + subl $1,%edi sbbl $0,%esi + movl $0xffffff00,%eax + jmp sqrt_round_result + + +sqrt_get_more_precision: +// This case is almost the same as the above, except we start +// with an extra bit of precision in the estimate. + stc // The extra bit. + rcll $1,%edi // Shift the estimate left one bit + rcll $1,%esi + + movl %edi,%eax // ls word of guess + mull %edi + movl %edx,%ebx // 2nd ls word of square + movl %eax,%ecx // ls word of square + + movl %edi,%eax + mull %esi + addl %eax,%ebx + addl %eax,%ebx + +// Put our estimate back to its original value + stc // The ms bit. + rcrl $1,%esi // Shift the estimate left one bit + rcrl $1,%edi + +#ifdef PARANOID + cmp $0xffffff60,%ebx + jb sqrt_more_prec_ok + + cmp $0x000000a0,%ebx + ja sqrt_more_prec_ok + + pushl EX_INTERNAL|0x215 + call EXCEPTION + +sqrt_more_prec_ok: +#endif PARANOID + + or %ebx,%ebx + js sqrt_more_prec_small -l65: - movl PARAM1,%ecx + jnz sqrt_more_prec_large - movl %edi,SIGL(%ecx) - movl %esi,SIGH(%ecx) + or %ebx,%ecx + jnz sqrt_more_prec_large - movl EXP_BIAS,EXP(%ecx) /* Result is in [1.0 .. 2.0) */ +// Our estimate is exactly the right answer + movl $0x80000000,%eax + jmp sqrt_round_result -// popl %ebx - popl %edi - popl %esi - leave - ret +sqrt_more_prec_small: +// Our estimate is too small + movl $0x800000ff,%eax + jmp sqrt_round_result + +sqrt_more_prec_large: +// Our estimate is too large + movl $0x7fffff00,%eax + jmp sqrt_round_result diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c index 4b67fb04fef1..64057a21a3f0 100644 --- a/kernel/blk_drv/floppy.c +++ b/kernel/blk_drv/floppy.c @@ -1031,7 +1031,7 @@ void do_fd_request(void) } static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned int param) + unsigned long param) { int i,drive,cnt,okay; struct floppy_struct *this; diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c index 187a5ee12b43..455a1c9df66a 100644 --- a/kernel/blk_drv/hd.c +++ b/kernel/blk_drv/hd.c @@ -305,6 +305,8 @@ static void read_intr(void) do { i = (unsigned) inb_p(HD_STATUS); + if (i & BUSY_STAT) + continue; if ((i & STAT_MASK) != STAT_OK) break; if (i & DRQ_STAT) @@ -354,6 +356,8 @@ static void write_intr(void) do { i = (unsigned) inb_p(HD_STATUS); + if (i & BUSY_STAT) + continue; if ((i & STAT_MASK) != STAT_OK) break; if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT)) @@ -503,7 +507,7 @@ repeat: } static int hd_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { struct hd_geometry *loc = (void *) arg; int dev, err; diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c index c9aba1a8f805..719c67e97e9f 100644 --- a/kernel/blk_drv/scsi/aha1542.c +++ b/kernel/blk_drv/scsi/aha1542.c @@ -66,8 +66,7 @@ static int aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1); static long WAITnexttimeout = 3000000; static int aha1542_host = 0; -static void setup_mailboxes(); -extern void aha1542_interrupt(); +static void setup_mailboxes(void); #define aha1542_intr_reset() outb(IRST, CONTROL) @@ -530,7 +529,7 @@ int aha1542_command(Scsi_Cmnd * SCpnt) } /* Initialize mailboxes */ -static void setup_mailboxes() +static void setup_mailboxes(void) { int i; static unchar cmd[5] = {CMD_MBINIT, AHA1542_MAILBOXES}; diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c index 78a1c10b39e0..15cb27866436 100644 --- a/kernel/blk_drv/scsi/scsi.c +++ b/kernel/blk_drv/scsi/scsi.c @@ -193,6 +193,8 @@ static void scan_scsis (void) SCmd.request.dev = 0xffff; /* Mark not busy */ SCmd.use_sg = 0; + SCmd.transfersize = 0; + SCmd.underflow = 0; scsi_do_cmd (&SCmd, (void *) scsi_cmd, (void *) @@ -445,6 +447,8 @@ Scsi_Cmnd * request_queueable (struct request * req, int index) }; SCpnt->use_sg = 0; /* Reset the scatter-gather flag */ + SCpnt->transfersize = 0; + SCpnt->underflow = 0; return SCpnt; } @@ -516,6 +520,8 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, int index, int wait) }; SCpnt->use_sg = 0; /* Reset the scatter-gather flag */ + SCpnt->transfersize = 0; /* No default transfer size */ + SCpnt->underflow = 0; /* Do not flag underflow conditions */ return SCpnt; } @@ -929,7 +935,8 @@ static void scsi_done (Scsi_Cmnd * SCpnt) } break; default: - panic ("unsupported message byte recieved."); + printk("scsi: unsupported message byte %d recieved\n", msg_byte(result)); + panic (""); } break; case DID_TIME_OUT: @@ -1387,6 +1394,8 @@ unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end SCpnt->index = i; SCpnt->request.dev = -1; /* Mark not busy */ SCpnt->use_sg = 0; + SCpnt->underflow = 0; + SCpnt->transfersize = 0; host = scsi_devices[i].host_no; if(host_queue[host]) host_queue[host]->prev = SCpnt; diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h index 7bc76635aa2e..cc0a59fed858 100644 --- a/kernel/blk_drv/scsi/scsi.h +++ b/kernel/blk_drv/scsi/scsi.h @@ -351,11 +351,20 @@ typedef struct scsi_cmnd { void * request_buffer; /* Actual requested buffer */ /* These elements define the operation we ultimately want to perform */ - unsigned char data_cmnd[10]; + unsigned char data_cmnd[12]; unsigned short use_sg; /* Number of pieces of scatter-gather */ unsigned short sglist_len; /* size of malloc'd scatter-gather list */ unsigned bufflen; /* Size of data buffer */ void *buffer; /* Data buffer */ + + unsigned underflow; /* Return error if less than this amount is + transfered */ + + unsigned transfersize; /* How much we are guranteed to transfer with + each SCSI transfer (ie, between disconnect / + reconnects. Probably == sector size */ + + struct request request; /* A copy of the command we are working on*/ diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c index 45142d120712..9a7f4237f6dc 100644 --- a/kernel/blk_drv/scsi/scsi_ioctl.c +++ b/kernel/blk_drv/scsi/scsi_ioctl.c @@ -28,13 +28,15 @@ static int ioctl_probe(int dev, void *buffer) { int temp; - int len; - char * string; + unsigned int len,slen; + const char * string; if ((temp = scsi_hosts[dev].present) && buffer) { len = get_fs_long ((int *) buffer); string = scsi_hosts[dev].info(); - if (len > strlen(string)) len = strlen(string)+1; + slen = strlen(string); + if (len > slen) + len = slen + 1; verify_area(VERIFY_WRITE, buffer, len); memcpy_tofs (buffer, string, len); } diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c index 889a5f94f456..2aafe290d0fc 100644 --- a/kernel/blk_drv/scsi/sd.c +++ b/kernel/blk_drv/scsi/sd.c @@ -50,7 +50,7 @@ static int * sd_sizes; /* used to re-read partitions. */ extern void resetup_one_dev(struct gendisk *, unsigned int); -extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned int); +extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); static sd_init_onedisk(int); @@ -605,6 +605,15 @@ repeat: cmd[5] = 0; } +/* + * We shouldn't disconnect in the middle of a sector, so with a dumb + * host adapter, it's safe to assume that we can at least transfer + * this many bytes between each connect / disconnect. + */ + + SCpnt->transfersize = rscsi_disks[dev].sector_size; + SCpnt->underflow = this_count << 9; + scsi_do_cmd (SCpnt, (void *) cmd, buff, this_count * rscsi_disks[dev].sector_size, rw_intr, SD_TIMEOUT, MAX_RETRIES); diff --git a/kernel/blk_drv/scsi/sd_ioctl.c b/kernel/blk_drv/scsi/sd_ioctl.c index a658f4edde3a..4bb25e2debdf 100644 --- a/kernel/blk_drv/scsi/sd_ioctl.c +++ b/kernel/blk_drv/scsi/sd_ioctl.c @@ -14,7 +14,7 @@ extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg); extern int revalidate_scsidisk(int, int); -int sd_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg) +int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { int dev = inode->i_rdev; int host, error; diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c index 400d06c76750..c5f19e4ab5bf 100644 --- a/kernel/blk_drv/scsi/seagate.c +++ b/kernel/blk_drv/scsi/seagate.c @@ -6,6 +6,15 @@ * */ +/* + * Configuration : + * To use without BIOS -DOVERRIDE=base_address -DCONTROLLER=FD or SEAGATE + * -DIRQ will overide the default of 5. + * + * -DFAST or -DFAST32 will use blind transfers where possible + * + */ + #include #if defined(CONFIG_SCSI_SEAGATE) || defined(CONFIG_SCSI_FD_88x) @@ -18,6 +27,17 @@ #include "hosts.h" #include "seagate.h" +#ifndef IRQ +#define IRQ 5 +#endif + +#if (defined(FAST32) && !defined(FAST)) +#define FAST +#endif + +#if defined(LINKED) +#undef LINKED /* Linked commands are currently broken ! */ +#endif static int internal_command(unsigned char target, unsigned char lun, const void *cmnd, @@ -102,11 +122,13 @@ static const Signature signatures[] = { */ {"FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88",5,48, FD}, +{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD}, {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD}, {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD}, +{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD}, {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD}, {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD}, -#endif +#endif /* CONFIG_SCSI_SEAGATE */ } ; /* @@ -119,7 +141,7 @@ SEAGATE SCSI BIOS REVISION 3.2 */ #define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature)) -#endif +#endif /* n OVERRIDE */ /* * hostno stores the hostnumber, as told to us by the init routine. @@ -128,11 +150,21 @@ SEAGATE SCSI BIOS REVISION 3.2 static int hostno = -1; static void seagate_reconnect_intr(int); +/* + * We try to autodetect the 0ws jumper. If we get an over / under run, + * then we assume that the the handshaking done by 0ws to synchronize the + * SCSI and ISA busses failed, and disable fast transfers. + */ + +#ifdef FAST +static int fast = 1; +#endif + int seagate_st0x_detect (int hostnum) { #ifndef OVERRIDE int i,j; -#endif +#endif static struct sigaction seagate_sigaction = { &seagate_reconnect_intr, 0, @@ -162,12 +194,12 @@ static struct sigaction seagate_sigaction = { controller_type = CONTROLLER; #else #error Please use -DCONTROLLER=SEAGATE or -DCONTROLLER=FD to override controller type -#endif +#endif /* CONTROLLER */ #ifdef DEBUG printk("Base address overridden to %x, controller type is %s\n", base_address,controller_type == SEAGATE ? "SEAGATE" : "FD"); -#endif -#else +#endif +#else /* OVERIDE */ /* * To detect this card, we simply look for the signature * from the BIOS version notice in all the possible locations @@ -183,8 +215,10 @@ static struct sigaction seagate_sigaction = { base_address = (void *) seagate_bases[i]; controller_type = signatures[j].type; } - #endif +#endif /* OVERIDE */ + scsi_hosts[hostnum].this_id = (controller_type == SEAGATE) ? 7 : 6; + if (base_address) { st0x_cr_sr =(void *) (((unsigned char *) base_address) + (controller_type == SEAGATE ? 0x1a00 : 0x1c00)); @@ -193,11 +227,13 @@ static struct sigaction seagate_sigaction = { printk("ST0x detected. Base address = %x, cr = %x, dr = %x\n", base_address, st0x_cr_sr, st0x_dr); #endif /* - * At all times, we will use IRQ 5. + * At all times, we will use IRQ 5. Should also check for IRQ3 if we + * loose our first interrupt. */ hostno = hostnum; - if (irqaction(5, &seagate_sigaction)) { - printk("Unable to allocate IRQ5 for ST0x driver\n"); + if (irqaction(IRQ, &seagate_sigaction)) { + printk("scsi%d : unable to allocate IRQ%d\n", + hostno, IRQ); return 0; } return -1; @@ -211,11 +247,23 @@ static struct sigaction seagate_sigaction = { } } -const char *seagate_st0x_info(void) -{ - static char buffer[] = "Seagate ST-0X SCSI driver by Drew Eckhardt \n" -"$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/seagate.c,v 1.1 1992/07/24 06:27:38 root Exp root $\n"; - return buffer; +const char *seagate_st0x_info(void) { + static char buffer[256]; + sprintf(buffer, "scsi%d : %s at irq %d address %p options :" +#ifdef FAST +#ifdef FAST32 +" FAST32" +#else +" FAST" +#endif +#endif + +#ifdef LINKED +" LINKED" +#endif + "\n", hostno, (controller_type == SEAGATE) ? "seagate" : + "FD TMC-950", IRQ, base_address); + return buffer; } /* @@ -225,7 +273,23 @@ const char *seagate_st0x_info(void) static unsigned char current_target, current_lun; static unsigned char *current_cmnd, *current_data; +static int current_nobuffs; +static struct scatterlist *current_buffer; static int current_bufflen; + +#ifdef LINKED + +/* + * linked_connected indicates weather or not we are currently connected to + * linked_target, linked_lun and in an INFORMATION TRANSFER phase, + * using linked commands. + */ + +static int linked_connected = 0; +static unsigned char linked_target, linked_lun; +#endif + + static void (*done_fn)(Scsi_Cmnd *) = NULL; static Scsi_Cmnd * SCint = NULL; @@ -238,6 +302,18 @@ static Scsi_Cmnd * SCint = NULL; #define RECONNECT_NOW 1 #define CAN_RECONNECT 2 +#ifdef LINKED + +/* + * LINKED_RIGHT indicates that we are currently connected to the correct target + * for this command, LINKED_WRONG indicates that we are connected to the wrong + * target. Note that these imply CAN_RECONNECT. + */ + +#define LINKED_RIGHT 3 +#define LINKED_WRONG 4 +#endif + /* * This determines if we are expecting to reconnect or not. */ @@ -263,9 +339,8 @@ static void seagate_reconnect_intr (int unused) if (!should_reconnect) printk("scsi%d: unexpected interrupt.\n", hostno); - else - { - should_reconnect = 0; + else { + should_reconnect = 0; #if (DEBUG & PHASE_RESELECT) printk("scsi%d : internal_command(" @@ -277,10 +352,8 @@ static void seagate_reconnect_intr (int unused) current_cmnd, current_data, current_bufflen, RECONNECT_NOW); - if (msg_byte(temp) != DISCONNECT) - { - if (done_fn) - { + if (msg_byte(temp) != DISCONNECT) { + if (done_fn) { #if (DEBUG & PHASE_RESELECT) printk("scsi%d : done_fn(%d,%08x)", hostno, hostno, temp); @@ -290,8 +363,7 @@ static void seagate_reconnect_intr (int unused) SCint = NULL; SCtmp->result = temp; done_fn (SCtmp); - } - else + } else printk("done_fn() not defined.\n"); } } @@ -312,7 +384,7 @@ static int recursion_depth = 0; int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) { - int result; + int result, reconnect; Scsi_Cmnd * SCtmp; done_fn = done; @@ -327,10 +399,40 @@ int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) }; recursion_depth++; do{ - +#ifdef LINKED +/* + * Set linked command bit in control field of SCSI command. + */ + + current_cmnd[COMMAND_SIZE(current_cmnd[0])] |= 0x01; + if (linked_connected) { +#if (DEBUG & DEBUG_LINKED) + printk("scsi%d : using linked commands, current I_T_L nexus is ", + hostno); +#endif + if ((linked_target == current_target) && + (linked_lun == current_lun)) { +#if (DEBUG & DEBUG_LINKED) + printk("correct\n"); +#endif + reconnect = LINKED_RIGHT; + } else { +#if (DEBUG & DEBUG_LINKED) + printk("incorrect\n"); +#endif + reconnect = LINKED_WRONG; + } + } else +#endif /* LINKED */ + reconnect = CAN_RECONNECT; + + + + + result = internal_command (SCint->target, SCint->lun, SCint->cmnd, SCint->request_buffer, SCint->request_bufflen, - CAN_RECONNECT); + reconnect); if (msg_byte(result) == DISCONNECT) break; SCtmp = SCint; SCint = NULL; @@ -341,18 +443,18 @@ int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) return 0; } -int seagate_st0x_command (Scsi_Cmnd * SCpnt) - { +int seagate_st0x_command (Scsi_Cmnd * SCpnt) { return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd, SCpnt->request_buffer, SCpnt->request_bufflen, (int) NO_RECONNECT); - } +} static int internal_command(unsigned char target, unsigned char lun, const void *cmnd, - void *buff, int bufflen, int reselect) - { - int len; + void *buff, int bufflen, int reselect) { + int len; unsigned char *data; + struct scatterlist *buffer; + int nobuffs; int clock; int temp; @@ -370,8 +472,7 @@ static int internal_command(unsigned char target, unsigned char lun, const void unsigned char message = 0; register unsigned char status_read; - len=bufflen; - data=(unsigned char *) buff; + unsigned transfersize = 0, underflow = 0; incommand = 0; st0x_aborted = 0; @@ -384,11 +485,19 @@ static int internal_command(unsigned char target, unsigned char lun, const void #endif #if (DEBUG & PHASE_RESELECT) - switch (reselect) - { + switch (reselect) { case RECONNECT_NOW : printk("scsi%d : reconnecting\n", hostno); break; +#ifdef LINKED + case LINKED_RIGHT : + printk("scsi%d : connected, can reconnect\n", hostno); + break; + case LINKED_WRONG : + printk("scsi%d : connected to wrong target, can reconnect\n", + hostno); + break; +#endif case CAN_RECONNECT : printk("scsi%d : allowed to reconnect\n", hostno); break; @@ -407,8 +516,8 @@ static int internal_command(unsigned char target, unsigned char lun, const void * be asserted, and we must skip selection / arbitration phases. */ - if (reselect == RECONNECT_NOW) - { + switch (reselect) { + case RECONNECT_NOW: #if (DEBUG & PHASE_RESELECT) printk("scsi%d : phase RESELECT \n", hostno); #endif @@ -454,9 +563,12 @@ static int internal_command(unsigned char target, unsigned char lun, const void hostno, temp); return (DID_BAD_INTR << 16); } - data=current_data; /* WDE add */ + + buffer=current_buffer; cmnd=current_cmnd; /* WDE add */ + data=current_data; /* WDE add */ len=current_bufflen; /* WDE add */ + nobuffs=current_nobuffs; /* * We have determined that we have been selected. At this point, @@ -488,9 +600,21 @@ static int internal_command(unsigned char target, unsigned char lun, const void * At this point, we have connected with the target and can get * on with our lives. */ - } - else - { + break; + case CAN_RECONNECT: + +#ifdef LINKED +/* + * This is a bletcherous hack, just as bad as the Unix #! interpreter stuff. + * If it turns out we are using the wrong I_T_L nexus, the easiest way to deal + * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT + * message on MESSAGE OUT phase, and then loop back to here. + */ + +connect_loop : + +#endif + #if (DEBUG & PHASE_BUS_FREE) printk ("scsi%d : phase = BUS FREE \n", hostno); #endif @@ -595,20 +719,66 @@ static int internal_command(unsigned char target, unsigned char lun, const void * notify the midlevel driver to expect sense. */ - if (st0x_aborted) - { + if (st0x_aborted) { CONTROL = BASE_CMD; - if (STATUS & STAT_BSY) - { + if (STATUS & STAT_BSY) { seagate_st0x_reset(); return retcode(DID_RESET); - } + } return retcode(st0x_aborted); - } - } + } + +/* Establish current pointers. Take into account scatter / gather */ + + if ((nobuffs = SCint->use_sg)) { +#if (DEBUG & DEBUG_SG) + { + int i; + printk("scsi%d : scatter gather requested, using %d buffers.\n", + hostno, nobuffs); + for (i = 0; i < nobuffs; ++i) + printk("scsi%d : buffer %d address = %08x length = %d\n", + hostno, i, buffer[i].address, buffer[i].length); + } +#endif + + buffer = (struct scatterlist *) SCint->buffer; + len = buffer->length; + data = (unsigned char *) buffer->address; + } else { +#if (DEBUG & DEBUG_SG) + printk("scsi%d : scatter gather not requested.\n", hostno); +#endif + buffer = NULL; + len = SCint->request_bufflen; + data = (unsigned char *) SCint->request_buffer; + } + + break; +#ifdef LINKED + case LINKED_RIGHT: + break; + case LINKED_WRONG: + break; +#endif + } + +/* + * There are several conditions under which we wish to send a message : + * 1. When we are allowing disconnect / reconnect, and need to establish + * the I_T_L nexus via an IDENTIFY with the DiscPriv bit set. + * + * 2. When we are doing linked commands, are have the wrong I_T_L nexus + * established and want to send an ABORT message. + */ + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | - ((reselect == CAN_RECONNECT) ? CMD_ATTN : 0) ; + (((reselect == CAN_RECONNECT) +#ifdef LINKED + || (reselect == LINKED_WRONG) +#endif + ) ? CMD_ATTN : 0) ; /* * INFORMATION TRANSFER PHASE @@ -624,6 +794,8 @@ static int internal_command(unsigned char target, unsigned char lun, const void #endif incommand = 1; + transfersize = SCint->transfersize; + underflow = SCint->underflow; /* @@ -684,11 +856,69 @@ static int internal_command(unsigned char target, unsigned char lun, const void } } #endif - switch (status_read & REQ_MASK) { case REQ_DATAOUT : +/* + * If we are in fast mode, then we simply splat the data out + * in word-sized chunks as fast as we can. + */ + +#ifdef FAST +if (!len) { +#if 0 + printk("scsi%d: underflow to target %d lun %d \n", + hostno, target, lun); + st0x_aborted = DID_ERROR; + fast = 0; +#endif + break; +} +if (fast && transfersize && !(len % transfersize) && (len >= transfersize) +#ifdef FAST32 + && !(transfersize % 4) +#endif + ) { +#if (DEBUG & DEBUG_FAST) + printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n" + " len = %d, data = %08x\n", hostno, SCint->underflow, + SCint->transfersize, len, data); +#endif + + __asm__(" + cld; + movl %0, %%edi; +" +#ifdef FAST32 +" shr $2, %%ecx; +1: lodsl; + movl %%eax, (%%edi); +" +#else +"1: lodsb; + movb %%al, (%%edi); +" +#endif +" loop 1b;" :: + /* input */ + "r" (st0x_dr), "S" (data), "c" (SCint->transfersize) : + /* clobbered */ + "eax", "esi", "ecx", "edi"); + + len -= transfersize; + data += transfersize; + +#if (DEBUG & DEBUG_FAST) + printk("scsi%d : FAST transfer complete len = %d data = %08x\n", + hostno, len, data); +#endif + + +} else +#endif + +{ /* * We loop as long as we are in a data out phase, there is data to send, * and BSY is still active. @@ -746,10 +976,74 @@ static int internal_command(unsigned char target, unsigned char lun, const void "0" (data), "1" (len) : /* clobbered */ "ebx", "ecx", "edi", "esi"); +} + if (!len && nobuffs) { + --nobuffs; + ++buffer; + len = buffer->length; + data = (unsigned char *) buffer->address; +#if (DEBUG & DEBUG_SG) + printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n", + hostno, len, data); +#endif + } break; case REQ_DATAIN : +#ifdef FAST +if (!len) { +#if 0 + printk("scsi%d: overflow from target %d lun %d \n", + hostno, target, lun); + st0x_aborted = DID_ERROR; + fast = 0; +#endif + break; +} + +if (fast && transfersize && !(len % transfersize) && (len >= transfersize) +#ifdef FAST32 + && !(transfersize % 4) +#endif + ) { +#if (DEBUG & DEBUG_FAST) + printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n" + " len = %d, data = %08x\n", hostno, SCint->underflow, + SCint->transfersize, len, data); +#endif + __asm__(" + cld; + movl %0, %%esi; +" +#ifdef FAST32 +" shr $2, %%ecx; +1: movl (%%esi), %%eax; + stosl; +" +#else +"1: movb (%%esi), %%al; + stosb; +" +#endif + +" loop 1b;" :: + /* input */ + "r" (st0x_dr), "D" (data), "c" (SCint->transfersize) : + /* clobbered */ + "eax", "ecx", "edi", "esi"); + + len -= transfersize; + data += transfersize; + +#if (DEBUG & DEBUG_FAST) + printk("scsi%d : FAST transfer complete len = %d data = %08x\n", + hostno, len, data); +#endif + +} else +#endif +{ /* * We loop as long as we are in a data in phase, there is room to read, * and BSY is still active @@ -810,6 +1104,18 @@ static int internal_command(unsigned char target, unsigned char lun, const void "0" (data), "1" (len) : /* clobbered */ "ebx", "ecx", "edi", "esi"); +} + if (!len && nobuffs) { + --nobuffs; + ++buffer; + len = buffer->length; + data = (unsigned char *) buffer->address; +#if (DEBUG & DEBUG_SG) + printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n", + hostno, len, data); +#endif + } + break; case REQ_CMDOUT : @@ -834,41 +1140,64 @@ static int internal_command(unsigned char target, unsigned char lun, const void * If we are reconecting, then we must send an IDENTIFY message in * response to MSGOUT. */ - if (reselect) - { + switch (reselect) { + case CAN_RECONNECT: DATA = IDENTIFY(1, lun); #if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT)) printk("scsi%d : sent IDENTIFY message.\n", hostno); #endif - } - else - { - DATA = MESSAGE_REJECT; - + break; +#ifdef LINKED + case LINKED_WRONG: + DATA = ABORT; + linked_connected = 0; + reselect = CAN_RECONNECT; + goto connect_loop; +#if (DEBUG & (PHASE_MSGOUT | DEBUG_LINKED)) + printk("scsi%d : sent ABORT message to cancle incorrect I_T_L nexus.\n", hostno); +#endif +#endif /* LINKED */ +#if (DEBUG & DEBUG_LINKED) + printk("correct\n"); +#endif + default: + DATA = NOP; #if (DEBUG & PHASE_MSGOUT) - printk("scsi%d : sent MESSAGE REJECT message.\n", hostno); + printk("scsi%d : sent NOP message.\n", hostno); #endif - } + } break; case REQ_MSGIN : - switch (message = DATA) - { + switch (message = DATA) { case DISCONNECT : should_reconnect = 1; current_data = data; /* WDE add */ + current_buffer = buffer; current_bufflen = len; /* WDE add */ + current_nobuffs = nobuffs; +#ifdef LINKED + linked_connected = 0; +#endif + done=1; #if (DEBUG & (PHASE_RESELECT | PHASE_MSGIN)) printk("scsi%d : disconnected.\n", hostno); - done=1; +#endif break; + +#ifdef LINKED + case LINKED_CMD_COMPLETE: + case LINKED_FLG_CMD_COMPLETE: #endif case COMMAND_COMPLETE : +/* + * Note : we should check for underflow here. + */ #if (DEBUG & PHASE_MSGIN) printk("scsi%d : command complete.\n", hostno); - done=1; - break; #endif + done = 1; + break; case ABORT : #if (DEBUG & PHASE_MSGIN) printk("scsi%d : abort message.\n", hostno); @@ -876,15 +1205,20 @@ static int internal_command(unsigned char target, unsigned char lun, const void done=1; break; case SAVE_POINTERS : - current_data = data; /* WDE mod */ + current_buffer = buffer; current_bufflen = len; /* WDE add */ + current_data = data; /* WDE mod */ + current_nobuffs = nobuffs; #if (DEBUG & PHASE_MSGIN) printk("scsi%d : pointers saved.\n", hostno); #endif break; case RESTORE_POINTERS: - data=current_data; /* WDE mod */ + buffer=current_buffer; cmnd=current_cmnd; + data=current_data; /* WDE mod */ + len=current_bufflen; + nobuffs=current_nobuffs; #if (DEBUG & PHASE_MSGIN) printk("scsi%d : pointers restored.\n", hostno); #endif @@ -894,23 +1228,30 @@ static int internal_command(unsigned char target, unsigned char lun, const void /* * IDENTIFY distinguishes itself from the other messages by setting the * high byte. + * + * Note : we need to handle at least one outstanding command per LUN, + * and need to hash the SCSI command for that I_T_L nexus based on the + * known ID (at this point) and LUN. */ - if (message & 0x80) - { + if (message & 0x80) { #if (DEBUG & PHASE_MSGIN) printk("scsi%d : IDENTIFY message received from id %d, lun %d.\n", hostno, target, message & 7); #endif - } - else - { - + } else { + +/* + * We should go into a MESSAGE OUT phase, and send a MESSAGE_REJECT + * if we run into a message that we don't like. The seagate driver + * needs some serious restructuring first though. + */ + #if (DEBUG & PHASE_MSGIN) printk("scsi%d : unknown message %d from target %d.\n", hostno, message, target); #endif - } + } } break; @@ -933,26 +1274,69 @@ static int internal_command(unsigned char target, unsigned char lun, const void printk("Status = %02x, message = %02x\n", status, message); #endif - - if (st0x_aborted) - { - if (STATUS & STAT_BSY) - { + if (st0x_aborted) { + if (STATUS & STAT_BSY) { seagate_st0x_reset(); st0x_aborted = DID_RESET; - } + } abort_confirm = 1; - } + } +#ifdef LINKED +else { +/* + * Fix the message byte so that unsuspecting high level drivers don't + * puke when they see a LINKED COMMAND message in place of the COMMAND + * COMPLETE they may be expecting. Shouldn't be necessary, but it's + * better to be on the safe side. + * + * A non LINKED* message byte will indicate that the command completed, + * and we are now disconnected. + */ - if (should_reconnect) - { + switch (message) { + case LINKED_CMD_COMPLETE : + case LINKED_FLG_CMD_COMPLETE : + message = COMMAND_COMPLETE; + linked_target = current_target; + linked_lun = current_lun; + linked_connected = 1; +#if (DEBUG & DEBUG_LINKED) + printk("scsi%d : keeping I_T_L nexus established for linked command.\n", + hostno); +#endif +/* + * We also will need to adjust status to accomodate intermediate conditions. + */ + if ((status == INTERMEDIATE_GOOD) || + (status == INTERMEDIATE_C_GOOD)) + status = GOOD; + + break; +/* + * We should also handle what are "normal" termination messages + * here (ABORT, BUS_DEVICE_RESET?, and COMMAND_COMPLETE individually, + * and flake if things aren't right. + */ + + default : +#if (DEBUG & DEBUG_LINKED) + printk("scsi%d : closing I_T_L nexus.\n", hostno); +#endif + linked_connected = 0; + } + } +#endif /* LINKED */ + + + + + if (should_reconnect) { #if (DEBUG & PHASE_RESELECT) printk("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n", hostno); #endif CONTROL = BASE_CMD | CMD_INTR ; - } - else + } else CONTROL = BASE_CMD; return retcode (st0x_aborted); @@ -1005,5 +1389,127 @@ int seagate_st0x_reset (void) return 0; } -#endif +#ifdef CONFIG_BLK_DEV_SD + +#include +#include "sd.h" +#include "scsi_ioctl.h" + +extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg); + +int seagate_st0x_biosparam(int size, int dev, int* info) { + unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page; + int *sizes, result, formatted_sectors, total_sectors; + int cylinders, heads, sectors; + + unsigned long oldfs; + Scsi_Device *disk; + + disk = rscsi_disks[MINOR(dev) >> 4].device; + +/* + * Only SCSI-I CCS drives and later implement the necessary mode sense + * pages. + */ + + if (disk->scsi_level < 2) + return -1; + + sizes = (int *) buf; + data = (unsigned char *) (sizes + 2); + +/* + * Point fs, which normally points to user space, at kernel space so that + * we can do a syscall with the data coming from kernel space. + */ + + oldfs = get_fs(); + set_fs(get_ds()); + + cmd[0] = MODE_SENSE; + cmd[1] = (disk->lun << 5) & 0xe5; + cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */ + cmd[3] = 0; + cmd[4] = 255; + cmd[5] = 0; + +/* + * We are transfering 0 bytes in the out direction, and expect to get back + * 24 bytes for each mode page. + */ + + sizes[0] = 0; + sizes[1] = 256; + + memcpy (data, cmd, 6); + + if (!(result = scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) { +/* + * The mode page lies beyond the MODE SENSE header, with length 4, and + * the BLOCK DESCRIPTOR, with length header[3]. + */ + + page = data + 4 + data[3]; + heads = (int) page[5]; + cylinders = (page[2] << 16) | (page[3] << 8) | page[4]; + + cmd[2] = 0x03; /* Read page 3, format page current values */ + memcpy (data, cmd, 6); + + if (!(result = scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) { + page = data + 4 + data[3]; + sectors = (page[10] << 8) | page[11]; + + +/* + * Get the total number of formatted sectors from the block descriptor, + * so we can tell how many are being used for alternates. + */ + + formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) | + data[4 + 3] ; + + total_sectors = (heads * cylinders * sectors); + +/* + * Adjust the real geometry by subtracting + * (spare sectors / (heads * tracks)) cylinders from the number of cylinders. + * + * It appears that the CE cylinder CAN be a partial cylinder. + */ + + +printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n", + hostno, heads, cylinders, sectors, total_sectors, formatted_sectors); + + cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors)); + +/* + * Now, we need to do a sanity check on the geometry to see if it is + * BIOS compatable. The maximum BIOS geometry is 1024 cylinders * + * 256 heads * 64 sectors. + */ + + if ((cylinders > 1024) || (sectors > 64)) + result = -1; + else { + info[0] = heads; + info[1] = sectors; + info[2] = cylinders; + } + +/* + * There should be an alternate mapping for things the seagate doesn't + * understand, but I couldn't say what it is with reasonable certainty. + */ + + } + } + + set_fs(oldfs); + return result; +} +#endif /* CONFIG_BLK_DEV_SD */ + +#endif /* defined(CONFIG_SCSI_SEGATE) */ diff --git a/kernel/blk_drv/scsi/seagate.h b/kernel/blk_drv/scsi/seagate.h index efbbe05703ea..b27e769ba997 100644 --- a/kernel/blk_drv/scsi/seagate.h +++ b/kernel/blk_drv/scsi/seagate.h @@ -12,6 +12,7 @@ $Header */ #ifndef ASM +int seagate_st0x_biosparam(int, int, int*); int seagate_st0x_detect(int); int seagate_st0x_command(Scsi_Cmnd *); int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); @@ -27,8 +28,8 @@ int seagate_st0x_reset(void); #define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \ seagate_st0x_info, seagate_st0x_command, \ seagate_st0x_queue_command, seagate_st0x_abort, \ - seagate_st0x_reset, NULL, NULL, \ - 1, 7, SG_NONE, 1, 0, 0} + seagate_st0x_reset, NULL, seagate_st0x_biosparam, \ + 1, 7, SG_ALL, 1, 0, 0} #endif @@ -115,13 +116,16 @@ extern volatile int seagate_st0x_timeout; #define PRINT_COMMAND 0x200 #define PHASE_EXIT 0x400 #define PHASE_RESELECT 0x800 +#define DEBUG_FAST 0x1000 +#define DEBUG_SG 0x2000 +#define DEBUG_LINKED 0x4000 /* * Control options - these are timeouts specified in .01 seconds. */ #define ST0X_BUS_FREE_DELAY 25 -#define ST0X_SELECTION_DELAY 3 +#define ST0X_SELECTION_DELAY 15 #define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::) diff --git a/kernel/blk_drv/scsi/sr.c b/kernel/blk_drv/scsi/sr.c index 22df4b75b4e5..ed2980253009 100644 --- a/kernel/blk_drv/scsi/sr.c +++ b/kernel/blk_drv/scsi/sr.c @@ -38,7 +38,7 @@ static int * sr_sizes; static int sr_open(struct inode *, struct file *); -extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned int); +extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long); void requeue_sr_request (Scsi_Cmnd * SCpnt); diff --git a/kernel/blk_drv/scsi/sr_ioctl.c b/kernel/blk_drv/scsi/sr_ioctl.c index af9b4dc0cd18..2f198b45c2b4 100644 --- a/kernel/blk_drv/scsi/sr_ioctl.c +++ b/kernel/blk_drv/scsi/sr_ioctl.c @@ -37,14 +37,14 @@ static void sr_ioctl_done(Scsi_Cmnd * SCpnt) error code is. Normally the UNIT_ATTENTION code will automatically clear after one error */ -static int do_ioctl(int target, unsigned char * sr_cmd) +static int do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength) { Scsi_Cmnd * SCpnt; int result; SCpnt = allocate_device(NULL, scsi_CDs[target].device->index, 1); scsi_do_cmd(SCpnt, - (void *) sr_cmd, NULL, 255, sr_ioctl_done, + (void *) sr_cmd, buffer, buflength, sr_ioctl_done, IOCTL_TIMEOUT, IOCTL_RETRIES); @@ -89,7 +89,7 @@ static int do_ioctl(int target, unsigned char * sr_cmd) return result; } -int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg) +int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) { u_char sr_cmd[10]; @@ -108,10 +108,10 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign sr_cmd[1] = scsi_CDs[target].device->lun << 5; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0; sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0; - sr_cmd[8] = 1; + sr_cmd[8] = 0; sr_cmd[9] = 0; - result = do_ioctl(target, sr_cmd); + result = do_ioctl(target, sr_cmd, NULL, 255); return result; case CDROMRESUME: @@ -120,10 +120,10 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign sr_cmd[1] = scsi_CDs[target].device->lun << 5; sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0; sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0; - sr_cmd[8] = 0; + sr_cmd[8] = 1; sr_cmd[9] = 0; - result = do_ioctl(target, sr_cmd); + result = do_ioctl(target, sr_cmd, NULL, 255); return result; @@ -143,7 +143,7 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign sr_cmd[8] = msf.cdmsf_frame1; sr_cmd[9] = 0; - result = do_ioctl(target, sr_cmd); + result = do_ioctl(target, sr_cmd, NULL, 255); return result; } @@ -163,15 +163,67 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign sr_cmd[8] = ti.cdti_ind1; sr_cmd[9] = 0; - result = do_ioctl(target, sr_cmd); + result = do_ioctl(target, sr_cmd, NULL, 255); return result; } case CDROMREADTOCHDR: - return -EINVAL; + { + struct cdrom_tochdr tochdr; + char buffer[12]; + + sr_cmd[0] = SCMD_READ_TOC; + sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */ + sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; + sr_cmd[6] = 0; + sr_cmd[7] = 0; /* MSB of length (12) */ + sr_cmd[8] = 12; /* LSB of length */ + sr_cmd[9] = 0; + + result = do_ioctl(target, sr_cmd, buffer, sizeof (buffer)); + + tochdr.cdth_trk0 = buffer[2]; + tochdr.cdth_trk1 = buffer[3]; + + verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tochdr)); + memcpy_tofs ((void *) arg, &tochdr, sizeof (struct cdrom_tochdr)); + + return result; + } + case CDROMREADTOCENTRY: - return -EINVAL; + { + struct cdrom_tocentry tocentry; + char buffer[12]; + + verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_tocentry)); + memcpy_fromfs (&tocentry, (void *) arg, sizeof (struct cdrom_tocentry)); + + sr_cmd[0] = SCMD_READ_TOC; + sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */ + sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0; + sr_cmd[6] = tocentry.cdte_track; + sr_cmd[7] = 0; /* MSB of length (12) */ + sr_cmd[8] = 12; /* LSB of length */ + sr_cmd[9] = 0; + + result = do_ioctl (target, sr_cmd, buffer, sizeof (buffer)); + + if (tocentry.cdte_format == CDROM_MSF) { + tocentry.cdte_addr.msf.minute = buffer[9]; + tocentry.cdte_addr.msf.second = buffer[10]; + tocentry.cdte_addr.msf.frame = buffer[11]; + tocentry.cdte_ctrl = buffer[5] & 0xf; + } + else + tocentry.cdte_addr.lba = (int) buffer[0]; + + verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tocentry)); + memcpy_tofs ((void *) arg, &tocentry, sizeof (struct cdrom_tocentry)); + + return result; + } case CDROMSTOP: sr_cmd[0] = START_STOP; @@ -179,7 +231,7 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = 0; - result = do_ioctl(target, sr_cmd); + result = do_ioctl(target, sr_cmd, NULL, 255); return result; case CDROMSTART: @@ -188,22 +240,109 @@ int sr_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = 1; - result = do_ioctl(target, sr_cmd); + result = do_ioctl(target, sr_cmd, NULL, 255); return result; case CDROMEJECT: + if (scsi_CDs[target].device -> access_count == 1) + sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0); + sr_cmd[0] = START_STOP; - sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1; + sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 1; sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0; sr_cmd[4] = 0x02; - result = do_ioctl(target, sr_cmd); + if (!(result = do_ioctl(target, sr_cmd, NULL, 255))) + scsi_CDs[target].device -> changed = 1; + return result; case CDROMVOLCTRL: - return -EINVAL; + { + char buffer[28], mask[28]; + struct cdrom_volctrl volctrl; + + verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_volctrl)); + memcpy_fromfs (&volctrl, (void *) arg, sizeof (struct cdrom_volctrl)); + + /* First we get the current params so we can just twiddle the volume */ + + sr_cmd[0] = MODE_SENSE; + sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5; + sr_cmd[2] = 0xe; /* Want mode page 0xe, CDROM audio params */ + sr_cmd[3] = 0; + sr_cmd[4] = 28; + sr_cmd[5] = 0; + + if ((result = do_ioctl (target, sr_cmd, buffer, sizeof (buffer)))) { + printk ("Hosed while obtaining audio mode page\n"); + return result; + } + + sr_cmd[0] = MODE_SENSE; + sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5; + sr_cmd[2] = 0x4e; /* Want the mask for mode page 0xe */ + sr_cmd[3] = 0; + sr_cmd[4] = 28; + sr_cmd[5] = 0; + + if ((result = do_ioctl (target, sr_cmd, mask, sizeof (mask)))) { + printk ("Hosed while obtaining mask for audio mode page\n"); + return result; + } + + /* Now mask and substitute our own volume and reuse the rest */ + + buffer[21] = volctrl.channel0 & mask[21]; + buffer[23] = volctrl.channel1 & mask[23]; + buffer[25] = volctrl.channel2 & mask[25]; + buffer[27] = volctrl.channel3 & mask[27]; + + sr_cmd[0] = MODE_SELECT; + sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 0x10; /* Params are SCSI-2 */ + sr_cmd[2] = sr_cmd[3] = 0; + sr_cmd[4] = 28; + sr_cmd[5] = 0; + + result = do_ioctl (target, sr_cmd, buffer, sizeof (buffer)); + return result; + } + case CDROMSUBCHNL: - return -EINVAL; + { + struct cdrom_subchnl subchnl; + char buffer[16]; + + sr_cmd[0] = SCMD_READ_SUBCHANNEL; + sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */ + sr_cmd[2] = 0x40; /* I do want the subchannel info */ + sr_cmd[3] = 0x01; /* Give me current position info */ + sr_cmd[4] = sr_cmd[5] = 0; + sr_cmd[6] = 0; + sr_cmd[7] = 0; + sr_cmd[8] = 16; + sr_cmd[9] = 0; + + result = do_ioctl(target, sr_cmd, buffer, sizeof (buffer)); + + subchnl.cdsc_audiostatus = buffer[1]; + subchnl.cdsc_format = CDROM_MSF; + subchnl.cdsc_ctrl = buffer[5] & 0xf; + subchnl.cdsc_trk = buffer[6]; + subchnl.cdsc_ind = buffer[7]; + + subchnl.cdsc_reladdr.msf.minute = buffer[13]; + subchnl.cdsc_reladdr.msf.second = buffer[14]; + subchnl.cdsc_reladdr.msf.frame = buffer[15]; + subchnl.cdsc_absaddr.msf.minute = buffer[9]; + subchnl.cdsc_absaddr.msf.second = buffer[10]; + subchnl.cdsc_absaddr.msf.frame = buffer[11]; + + verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_subchnl)); + memcpy_tofs ((void *) arg, &subchnl, sizeof (struct cdrom_subchnl)); + return result; + } + case CDROMREADMODE2: return -EINVAL; case CDROMREADMODE1: diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c index bfe053943b28..35934702602f 100644 --- a/kernel/blk_drv/scsi/st.c +++ b/kernel/blk_drv/scsi/st.c @@ -83,7 +83,7 @@ int NR_ST=0; int MAX_ST=0; static int st_int_ioctl(struct inode * inode,struct file * file, - unsigned int cmd_in, unsigned int arg); + unsigned int cmd_in, unsigned long arg); @@ -845,7 +845,7 @@ int st_read(struct inode * inode, struct file * filp, char * buf, int count) /* Internal ioctl function */ static int st_int_ioctl(struct inode * inode,struct file * file, - unsigned int cmd_in, unsigned int arg) + unsigned int cmd_in, unsigned long arg) { int dev = inode->i_rdev; int timeout = ST_LONG_TIMEOUT; @@ -1086,7 +1086,7 @@ static int st_int_ioctl(struct inode * inode,struct file * file, /* The ioctl command */ static int st_ioctl(struct inode * inode,struct file * file, - unsigned int cmd_in, unsigned int arg) + unsigned int cmd_in, unsigned long arg) { int dev = inode->i_rdev; int i, cmd, result; diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c index 6bc1a007c367..6fb5aedc98f4 100644 --- a/kernel/blk_drv/scsi/ultrastor.c +++ b/kernel/blk_drv/scsi/ultrastor.c @@ -464,7 +464,9 @@ int ultrastor_abort(Scsi_Cmnd *SCpnt, int code) int ultrastor_reset(void) { +#if 0 unsigned char in_byte; +#endif #if (ULTRASTOR_DEBUG & UD_RESET) printk("US14F: reset: called\n"); diff --git a/kernel/blk_drv/scsi/wd7000.c b/kernel/blk_drv/scsi/wd7000.c index 234ae7f511dd..774796b6c1b2 100644 --- a/kernel/blk_drv/scsi/wd7000.c +++ b/kernel/blk_drv/scsi/wd7000.c @@ -80,14 +80,14 @@ static unchar rev_1 = 0, rev_2 = 0; /* filled in by wd7000_revision */ #define WAITnexttimeout 3000000 -static inline void wd7000_enable_intr() +static inline void wd7000_enable_intr(void) { controlstat |= INT_EN; outb(controlstat,CONTROL); } -static inline void wd7000_enable_dma() +static inline void wd7000_enable_dma(void) { controlstat |= DMA_EN; outb(controlstat,CONTROL); diff --git a/kernel/blk_drv/xd.c b/kernel/blk_drv/xd.c index daf8a7a58c2a..26150729430a 100644 --- a/kernel/blk_drv/xd.c +++ b/kernel/blk_drv/xd.c @@ -81,11 +81,11 @@ u_long xd_init (u_long mem_start,u_long mem_end) { u_char i,controller,*address; - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) { printk("xd_init: unable to get major number %d\n",MAJOR_NR); return (mem_start); } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ xd_gendisk.next = gendisk_head; gendisk_head = &xd_gendisk; @@ -149,14 +149,18 @@ static void xd_geninit (void) /* xd_open: open a device */ static int xd_open (struct inode *inode,struct file *file) { - int target = DEVICE_NR(MINOR(inode->i_rdev)); + int dev = DEVICE_NR(MINOR(inode->i_rdev)); - while (!xd_valid[target]) - sleep_on(&xd_wait_open); + if (dev < xd_drives) { + while (!xd_valid[dev]) + sleep_on(&xd_wait_open); - xd_access[target]++; + xd_access[dev]++; - return (0); + return (0); + } + else + return (-ENODEV); } /* do_xd_request: handle an incoming request */ @@ -186,7 +190,7 @@ static void do_xd_request (void) } /* xd_ioctl: handle device ioctl's */ -static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_int arg) +static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg) { XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg; int dev = DEVICE_NR(MINOR(inode->i_rdev)),err; @@ -221,8 +225,12 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_int arg) /* xd_release: release the device */ static void xd_release (struct inode *inode, struct file *file) { - sync_dev(inode->i_rdev); - xd_access[DEVICE_NR(MINOR(inode->i_rdev))]--; + int dev = DEVICE_NR(MINOR(inode->i_rdev)); + + if (dev < xd_drives) { + sync_dev(dev); + xd_access[dev]--; + } } /* xd_reread_partitions: rereads the partition table from a drive */ diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c index 32bec47d4004..d56cdd0605c1 100644 --- a/kernel/chr_drv/console.c +++ b/kernel/chr_drv/console.c @@ -1350,8 +1350,8 @@ void kbdsave(int new_console) static void get_scrmem(int currcons) { - memcpy((void *)vc_scrbuf[fg_console],(void *)origin, screen_size); - video_mem_start = (unsigned long)vc_scrbuf[fg_console]; + memcpy((void *)vc_scrbuf[currcons],(void *)origin, screen_size); + video_mem_start = (unsigned long)vc_scrbuf[currcons]; origin = video_mem_start; scr_end = video_mem_end = video_mem_start+screen_size; pos = origin + y*video_size_row + (x<<1); diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c index 033831a91b58..d0e70d20db26 100644 --- a/kernel/chr_drv/keyboard.c +++ b/kernel/chr_drv/keyboard.c @@ -5,6 +5,8 @@ * * Written for linux by Johan Myreen as a translation from * the assembly version by Linus (with diacriticals added) + * + * Some additional features added by Christoph Niemann (ChN), March 1993 */ #define KEYBOARD_IRQ 1 @@ -17,6 +19,7 @@ #include #include #include +#include #ifndef KBD_DEFFLAGS #ifdef CONFIG_KBD_META @@ -49,6 +52,8 @@ unsigned long kbd_dead_keys = 0; unsigned long kbd_prev_dead_keys = 0; static int want_console = -1; +static int last_console = 0; /* last used VC */ +static unsigned char rep = 0xff; /* last pressed key */ struct kbd_struct kbd_table[NR_CONSOLES]; static struct kbd_struct * kbd = kbd_table; static struct tty_struct * tty = NULL; @@ -80,7 +85,6 @@ static inline void kb_wait(void) static void keyboard_interrupt(int int_pt_regs) { - static unsigned char rep = 0xff; unsigned char scancode; pt_regs = (struct pt_regs *) int_pt_regs; @@ -261,21 +265,36 @@ static void show_ptregs(void) static void scroll(int sc) { - if (kbd_flag(KG_LSHIFT) || kbd_flag(KG_RSHIFT)) - show_mem(); - else if (kbd_flag(KG_ALT) || kbd_flag(KG_ALTGR)) - show_ptregs(); - else if (kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL)) - show_state(); - else - chg_vc_kbd_flag(kbd,VC_SCROLLOCK); + if (kbd_dead(KGD_E0)) + put_queue(INTR_CHAR(tty)); + else if ( sc != rep ) /* no autorepeat for scroll lock, ChN */ + if (kbd_flag(KG_LSHIFT) || kbd_flag(KG_RSHIFT)) + show_mem(); + else if (kbd_flag(KG_ALT) || kbd_flag(KG_ALTGR)) + show_ptregs(); + else if (kbd_flag(KG_LCTRL) || kbd_flag(KG_RCTRL)) + show_state(); + else { + if (vc_kbd_flag(kbd, VC_SCROLLOCK)) + /* pressing srcoll lock 2nd time sends ^Q, ChN */ + put_queue(START_CHAR(tty)); + else + + /* pressing srcoll lock 1st time sends ^S, ChN */ + put_queue(STOP_CHAR(tty)); + chg_vc_kbd_flag(kbd,VC_SCROLLOCK); + } + } static void num(int sc) { - if (vc_kbd_flag(kbd,VC_APPLIC)) + if (kbd_flag(KG_LCTRL)) + /* pause key pressed, sends E1 1D 45, ChN */ + chg_vc_kbd_flag(kbd,VC_PAUSE); + else if (vc_kbd_flag(kbd,VC_APPLIC)) applkey(0x50); - else + else if ( rep != sc ) /* no autorepeat for numlock, ChN */ chg_vc_kbd_flag(kbd,VC_NUMLOCK); } @@ -287,6 +306,13 @@ static void applkey(int key) puts_queue(buf); } +static void sysreq(int sc) +{ + /* pressing alt-printscreen switches to the last used console, ChN */ + if ( sc != rep) + want_console = last_console; +} + #if defined KBD_FINNISH static unsigned char key_map[] = { @@ -1262,7 +1288,11 @@ static void slash(int sc) static void star(int sc) { - if (vc_kbd_flag(kbd,VC_APPLIC)) + if (kbd_dead(KGD_E0)) + /* Print screen key sends E0 2A E0 37 and puts + the VT100-ESC sequence ESC [ i into the queue, ChN */ + puts_queue("\033\133\151"); + else if (vc_kbd_flag(kbd,VC_APPLIC)) applkey('R'); else do_self(sc); @@ -1351,7 +1381,10 @@ static void kbd_bh(void * unused) send_data(0xf4); /* re-enable kbd if any errors */ } if (want_console >= 0) { - change_console(want_console); + if (want_console != fg_console) { + last_console = fg_console; + change_console(want_console); + } want_console = -1; } do_keyboard_interrupt(); @@ -1410,7 +1443,7 @@ static fptr key_table[] = { cursor,cursor,minus,cursor, /* 48-4B up pgup - left */ cursor,cursor,plus,cursor, /* 4C-4F n5 right + end */ cursor,cursor,cursor,cursor, /* 50-53 dn pgdn ins del */ - none,none,do_self,func, /* 54-57 sysreq ? < f11 */ + sysreq,none,do_self,func, /* 54-57 sysreq ? < f11 */ func,none,none,none, /* 58-5B f12 ? ? ? */ none,none,none,none, /* 5C-5F ? ? ? ? */ none,none,none,none, /* 60-63 ? ? ? ? */ diff --git a/kernel/chr_drv/lp.c b/kernel/chr_drv/lp.c index 679634f4bede..55adcfce8ff0 100644 --- a/kernel/chr_drv/lp.c +++ b/kernel/chr_drv/lp.c @@ -301,7 +301,7 @@ static int lp_open(struct inode * inode, struct file * file) if (ret) { kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE); lp_table[minor].lp_buffer = NULL; - printk("lp%d unable to use interrupt %d, error %d\n", irq, ret); + printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret); return ret; } } @@ -327,7 +327,7 @@ static void lp_release(struct inode * inode, struct file * file) static int lp_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { unsigned int minor = MINOR(inode->i_rdev); diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c index 0637af9651d9..cff68c5dba52 100644 --- a/kernel/chr_drv/serial.c +++ b/kernel/chr_drv/serial.c @@ -573,15 +573,15 @@ static void startup(struct async_struct * info) info->flags |= ASYNC_INITIALIZED; if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); - restore_flags(flags); /* * Set up parity check flag */ - if (I_INPCK(info->tty)) + if (info->tty && info->tty->termios && I_INPCK(info->tty)) info->read_status_mask = UART_LSR_BI | UART_LSR_FE | UART_LSR_PE; else info->read_status_mask = UART_LSR_BI | UART_LSR_FE; + restore_flags(flags); } /* @@ -970,7 +970,7 @@ static void send_break( struct async_struct * info, int duration) } static int rs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { int error, line; struct async_struct * info; @@ -988,7 +988,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ wait_until_sent(tty); - send_break(info, arg ? HZ/4 : arg*(HZ/10)); + send_break(info, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *)); diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c index 2017cdae3615..473957348044 100644 --- a/kernel/chr_drv/tty_io.c +++ b/kernel/chr_drv/tty_io.c @@ -182,7 +182,7 @@ static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_ } static int hung_up_tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { return -EIO; } @@ -545,12 +545,24 @@ void copy_to_cooked(struct tty_struct * tty) if (c == __DISABLED_CHAR) tty->lnext = 1; if (L_CANON(tty) && !tty->lnext) { - if (c == KILL_CHAR(tty)) { + if (c == KILL_CHAR(tty) || c == WERASE_CHAR(tty)) { + int seen_alnums = + (c == WERASE_CHAR(tty)) ? 0 : -1; + /* deal with killing the input line */ while(!(EMPTY(&tty->secondary) || (c=LAST(&tty->secondary))==10 || ((EOF_CHAR(tty) != __DISABLED_CHAR) && (c==EOF_CHAR(tty))))) { + /* if killing just a word, kill all + non-alnum chars, then all alnum + chars. */ + if (seen_alnums >= 0) { + if (isalnum(c)) + seen_alnums++; + else if (seen_alnums) + break; + } if (L_ECHO(tty)) { if (c<32) { put_tty_queue(8, &tty->write_q); @@ -598,6 +610,10 @@ void copy_to_cooked(struct tty_struct * tty) tty->status_changed = 1; tty->ctrl_status |= TIOCPKT_STOP; tty->stopped=1; + if (IS_A_CONSOLE(tty->line)) { + set_vc_kbd_flag(kbd_table + fg_console, VC_SCROLLOCK); + set_leds(); + } continue; } if (((I_IXANY(tty)) && tty->stopped) || @@ -605,6 +621,10 @@ void copy_to_cooked(struct tty_struct * tty) tty->status_changed = 1; tty->ctrl_status |= TIOCPKT_START; tty->stopped=0; + if (IS_A_CONSOLE(tty->line)) { + clr_vc_kbd_flag(kbd_table + fg_console, VC_SCROLLOCK); + set_leds(); + } continue; } } @@ -620,8 +640,10 @@ void copy_to_cooked(struct tty_struct * tty) continue; } if (c == SUSPEND_CHAR(tty)) { - if (!is_orphaned_pgrp(tty->pgrp)) + if (!is_orphaned_pgrp(tty->pgrp)) { kill_pg(tty->pgrp, SIGTSTP, 1); + flush_input(tty); + } continue; } } @@ -1045,7 +1067,7 @@ end_init: if (tty) free_page((unsigned long) tty); if (o_tty) - free_page((unsigned long) tty); + free_page((unsigned long) o_tty); if (tp) kfree_s(tp, sizeof(struct termios)); if (o_tp) diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c index d9cd91eb4563..e7f071b5e772 100644 --- a/kernel/chr_drv/tty_ioctl.c +++ b/kernel/chr_drv/tty_ioctl.c @@ -301,7 +301,7 @@ static int tty_set_ldisc(struct tty_struct *tty, int ldisc) int tty_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { struct tty_struct * tty; struct tty_struct * other_tty; diff --git a/kernel/chr_drv/vt.c b/kernel/chr_drv/vt.c index ec9cbf58af93..bb749116d1c0 100644 --- a/kernel/chr_drv/vt.c +++ b/kernel/chr_drv/vt.c @@ -102,7 +102,7 @@ kd_mksound(unsigned int count, unsigned int ticks) * capability to modify any console, not just the fg_console. */ int vt_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned int arg) + unsigned int cmd, unsigned long arg) { int console, i; unsigned char ucval; diff --git a/kernel/exit.c b/kernel/exit.c index 52553866e3c9..40bd1f0496f5 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -123,13 +123,17 @@ void audit_ptree(void) 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"); + printk("Warning, pid %d parent link points to self\n", + task[i]->pid); if (task[i]->p_cptr == task[i]) - printk("Warning, pid %d child link points to self\n"); + printk("Warning, pid %d child link points to self\n", + task[i]->pid); if (task[i]->p_ysptr == task[i]) - printk("Warning, pid %d ys link points to self\n"); + printk("Warning, pid %d ys link points to self\n", + task[i]->pid); if (task[i]->p_osptr == task[i]) - printk("Warning, pid %d os link points to self\n"); + printk("Warning, pid %d os link points to self\n", + task[i]->pid); if (task[i]->p_osptr) { if (task[i]->p_pptr != task[i]->p_osptr->p_pptr) printk( diff --git a/kernel/fork.c b/kernel/fork.c index 8bf00084af9d..b8ba3a20bad6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,9 @@ repeat: return -EAGAIN; } +#define IS_CLONE (orig_eax == __NR_clone) +#define copy_vm(p) (IS_CLONE?clone_page_tables:copy_page_tables)(p) + /* * Ok, this is the main fork-routine. It copies the system process * information (task[nr]) and sets up the necessary registers. It @@ -107,6 +111,8 @@ int sys_fork(long ebx,long ecx,long edx, p->tss.edx = edx; p->tss.ebx = ebx; p->tss.esp = esp; + if (IS_CLONE) /* clone() gets the new stack value */ + p->tss.esp = ebx; p->tss.ebp = ebp; p->tss.esi = esi; p->tss.edi = edi; @@ -123,7 +129,7 @@ int sys_fork(long ebx,long ecx,long edx, if (last_task_used_math == current) __asm__("clts ; fnsave %0 ; frstor %0"::"m" (p->tss.i387)); p->kernel_stack_page = get_free_page(GFP_KERNEL); - if (!p->kernel_stack_page || copy_page_tables(p)) { + if (!p->kernel_stack_page || copy_vm(p)) { task[nr] = NULL; REMOVE_LINKS(p); free_page(p->kernel_stack_page); @@ -145,6 +151,7 @@ int sys_fork(long ebx,long ecx,long edx, current->libraries[i].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->counter = current->counter >> 1; p->state = TASK_RUNNING; /* do this last, just in case */ return p->pid; } diff --git a/kernel/printk.c b/kernel/printk.c index 8503ce60c54b..b3be7693e735 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -37,7 +37,7 @@ struct wait_queue * log_wait = NULL; * Commands to sys_syslog: * * 0 -- Close the log. Currently a NOP. - * 1 -- Open and reset log. + * 1 -- Open the log. Currently a NOP. * 2 -- Read from the log. * 3 -- Read up to the last 4k of messages in the ring buffer. * 4 -- Read and clear last 4k of messages in the ring buffer @@ -56,9 +56,7 @@ int sys_syslog(int type, char * buf, int len) switch (type) { case 0: /* Close log */ return 0; - case 1: /* Open and reset log */ - log_start += log_size; - log_size = 0; + case 1: /* Open log */ return 0; case 2: /* Read from log */ if (!buf || len < 0) diff --git a/kernel/sched.c b/kernel/sched.c index 6fba0a8867c3..f84c2ed9176f 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -37,7 +37,7 @@ unsigned long prof_len = 0; #define _S(nr) (1<<((nr)-1)) -#define LATCH (1193180/HZ) +#define LATCH ((1193180 + HZ/2)/HZ) extern void mem_use(void); @@ -567,7 +567,7 @@ void sched_init(void) __asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl"); load_TR(0); load_ldt(0); - outb_p(0x36,0x43); /* binary, mode 3, LSB/MSB, ch 0 */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ outb_p(LATCH & 0xff , 0x40); /* LSB */ outb(LATCH >> 8 , 0x40); /* MSB */ request_irq(TIMER_IRQ,(void (*)(int)) do_timer); diff --git a/kernel/signal.c b/kernel/signal.c index cea610c1bed6..9892b26299f1 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -143,9 +143,17 @@ int sys_sigaction(int signum, const struct sigaction * action, extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); -void sys_sigreturn(int signr, unsigned long oldmask, unsigned long unused) +/* + * This sets regs->esp even though we don't actually use sigstacks yet.. + */ +int sys_sigreturn(int signr, unsigned long oldmask, unsigned long esp) { + struct pt_regs * regs; + + regs = (struct pt_regs *) &signr; current->blocked = oldmask & _BLOCKABLE; + regs->esp = esp; + return 0; } /* @@ -165,7 +173,7 @@ static unsigned long * setup_first(struct pt_regs * regs, /* set up the "normal" stack seen by the signal handler */ put_fs_long(regs->esp+15*4,tmp_esp); /* points to the stack.. */ put_fs_long(signr,tmp_esp+1); /* parameter to handler and sigreturn */ - put_fs_long(0,tmp_esp+2); /* third parameter to sigreturn */ + put_fs_long((unsigned long) (tmp_esp+5),tmp_esp+2); put_fs_long(oldmask,tmp_esp+3); /* second .. */ put_fs_long(__NR_sigreturn,tmp_esp+4); /* sigreturn number.. */ /* save this frame so that we later can fill in the saved registers */ @@ -188,7 +196,7 @@ static void setup_other(unsigned long eip, struct pt_regs * regs, int signr, /* set up the "normal" stack seen by the signal handler */ put_fs_long(regs->esp+6*4,tmp_esp); /* points to the stack.. */ put_fs_long(signr,tmp_esp+1); /* parameter to handler and sigreturn */ - put_fs_long(0,tmp_esp+2); /* third parameter to sigreturn */ + put_fs_long((unsigned long) (tmp_esp+5),tmp_esp+2); put_fs_long(oldmask,tmp_esp+3); /* second .. */ put_fs_long(__NR_sigreturn,tmp_esp+4); /* sigreturn number.. */ put_fs_long(eip,tmp_esp+5); /* return address */ diff --git a/kernel/sys.c b/kernel/sys.c index 0777f8e6fcd5..5ce28e5ad625 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -18,6 +18,7 @@ #include #include +#include /* * this indicates wether you can reboot with ctrl-alt-del: the default is yes @@ -695,19 +696,58 @@ int sys_getrusage(int who, struct rusage *ru) return getrusage(current, who, ru); } +#define LATCH ((1193180 + HZ/2)/HZ) + +/* + * This version of gettimeofday has near nanosecond resolution. + * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs + */ +static inline void do_gettimeofday(struct timeval *tv) +{ + unsigned long nowtime; + long count; + +#ifdef __i386__ + cli(); + /* timer count may underflow right here */ + outb_p(0x00, 0x43); /* latch the count ASAP */ + nowtime = jiffies; /* must be saved inside cli/sti */ + count = inb_p(0x40); /* read the latched count */ + count |= inb_p(0x40) << 8; + /* we know probability of underflow is always MUCH less than 1% */ + if (count < (LATCH - LATCH/100)) + sti(); + else { + /* check for pending timer interrupt */ + outb_p(0x0a, 0x20); + if (inb(0x20) & 1) + nowtime++; + sti(); + } + nowtime += jiffies_offset; + tv->tv_sec = startup_time + CT_TO_SECS(nowtime); + /* the correction term is always in the range [0, 1 clocktick) */ + tv->tv_usec = CT_TO_USECS(nowtime) + + ((LATCH - 1) - count)*(1000000/HZ)/LATCH; +#else /* not __i386__ */ + nowtime = jiffies + jiffes_offset; + tv->tv_sec = startup_time + CT_TO_SECS(nowtime); + tv->tv_usec = CT_TO_USECS(nowtime); +#endif /* not __i386__ */ +} + int sys_gettimeofday(struct timeval *tv, struct timezone *tz) { int error; if (tv) { - unsigned long nowtime = jiffies+jiffies_offset; + struct timeval ktv; error = verify_area(VERIFY_WRITE, tv, sizeof *tv); if (error) return error; - put_fs_long(startup_time + CT_TO_SECS(nowtime), - (unsigned long *) tv); - put_fs_long(CT_TO_USECS(nowtime), - ((unsigned long *) tv)+1); + do_gettimeofday(&ktv); + put_fs_long(ktv.tv_sec, &tv->tv_sec); + put_fs_long(ktv.tv_usec, &tv->tv_usec); } if (tz) { error = verify_area(VERIFY_WRITE, tz, sizeof *tz); diff --git a/kernel/sys_call.S b/kernel/sys_call.S index d16ec17ae445..9c4fd61bb868 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -58,6 +58,7 @@ EFLAGS = 0x38 OLDESP = 0x3C OLDSS = 0x40 +CF_MASK = 0x00000001 IF_MASK = 0x00000200 NT_MASK = 0x00004000 VM_MASK = 0x00020000 @@ -71,6 +72,7 @@ priority = 8 signal = 12 blocked = 16 flags = 20 +errno = 24 ENOSYS = 38 @@ -129,6 +131,8 @@ _system_call: cmpl _NR_syscalls,%eax jae ret_from_sys_call movl _current,%ebx + movl $0,errno(%ebx) + andl $~CF_MASK,EFLAGS(%esp) # clear carry - assume no errors testl $0x20,flags(%ebx) # PF_TRACESYS je 1f pushl $0 @@ -141,7 +145,12 @@ _system_call: 1: call _sys_call_table(,%eax,4) movl %eax,EAX(%esp) # save the return value movl _current,%eax - testl $0x20,flags(%eax) # PF_TRACESYS + movl errno(%eax),%edx + negl %edx + je 2f + movl %edx,EAX(%esp) + orl $CF_MASK,EFLAGS(%esp) # set carry to indicate error +2: testl $0x20,flags(%eax) # PF_TRACESYS je ret_from_sys_call cmpl $0,signal(%eax) jne ret_from_sys_call # ptrace would clear signal @@ -174,17 +183,18 @@ ret_from_sys_call: cmpl $0,counter(%eax) # counter je reschedule movl blocked(%eax),%ecx + movl %ecx,%ebx # save blocked in %ebx for signal handling notl %ecx andl signal(%eax),%ecx jne signal_return 2: RESTORE_ALL .align 4 signal_return: - movl %esp,%ebx - pushl %ebx - testl $VM_MASK,EFLAGS(%ebx) + movl %esp,%ecx + pushl %ecx + testl $VM_MASK,EFLAGS(%ecx) jne v86_signal_return - pushl blocked(%eax) + pushl %ebx call _do_signal popl %ebx popl %ebx @@ -194,7 +204,7 @@ v86_signal_return: call _save_v86_state movl %eax,%esp pushl %eax - pushl blocked(%eax) + pushl %ebx call _do_signal popl %ebx popl %ebx diff --git a/mm/memory.c b/mm/memory.c index 64655cc62acc..40389e099694 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -152,6 +152,10 @@ void free_page_tables(struct task_struct * tsk) tsk->tss.cr3 = (unsigned long) swapper_pg_dir; if (tsk == current) __asm__ __volatile__("movl %0,%%cr3"::"a" (tsk->tss.cr3)); + if (mem_map[MAP_NR(pg_dir)] > 1) { + free_page(pg_dir); + return; + } page_dir = (unsigned long *) pg_dir; for (i = 0 ; i < 1024 ; i++,page_dir++) free_one_table(page_dir); @@ -159,6 +163,22 @@ void free_page_tables(struct task_struct * tsk) invalidate(); } +/* + * clone_page_tables() clones the page table for a process - both + * processes will have the exact same pages in memory. There are + * probably races in the memory management with cloning, but we'll + * see.. + */ +int clone_page_tables(struct task_struct * tsk) +{ + unsigned long pg_dir; + + pg_dir = current->tss.cr3; + mem_map[MAP_NR(pg_dir)]++; + tsk->tss.cr3 = pg_dir; + return 0; +} + /* * copy_page_tables() just copies the whole process memory range: * note the special handling of RESERVED (ie kernel) pages, which @@ -170,10 +190,10 @@ int copy_page_tables(struct task_struct * tsk) unsigned long old_pg_dir, *old_page_dir; unsigned long new_pg_dir, *new_page_dir; - old_pg_dir = current->tss.cr3; new_pg_dir = get_free_page(GFP_KERNEL); if (!new_pg_dir) return -ENOMEM; + old_pg_dir = current->tss.cr3; tsk->tss.cr3 = new_pg_dir; old_page_dir = (unsigned long *) old_pg_dir; new_page_dir = (unsigned long *) new_pg_dir; @@ -200,7 +220,6 @@ int copy_page_tables(struct task_struct * tsk) free_page_tables(tsk); return -ENOMEM; } - *new_page_dir = new_pg_table | PAGE_TABLE; old_page_table = (unsigned long *) (0xfffff000 & old_pg_table); new_page_table = (unsigned long *) (0xfffff000 & new_pg_table); for (j = 0 ; j < 1024 ; j++,old_page_table++,new_page_table++) { @@ -220,6 +239,7 @@ int copy_page_tables(struct task_struct * tsk) *old_page_table = pg; mem_map[MAP_NR(pg)]++; } + *new_page_dir = new_pg_table | PAGE_TABLE; } invalidate(); return 0; @@ -416,15 +436,12 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size, i * It returns the physical address of the page gotten, 0 if * out of memory (either when trying to access page-table or * page.) - * if wp = 1 the page will be write protected */ static unsigned long put_page(struct task_struct * tsk,unsigned long page, unsigned long address,int prot) { unsigned long tmp, *page_table; -/* NOTE !!! This uses the fact that _pg_dir=0 */ - if ((prot & 0xfffff001) != PAGE_PRESENT) printk("put_page: prot = %08x\n",prot); if (page >= high_memory) { @@ -445,7 +462,7 @@ static unsigned long put_page(struct task_struct * tsk,unsigned long page, *page_table = BAD_PAGETABLE | PAGE_TABLE; return 0; } - page_table += (address >> PAGE_SHIFT) & 0x3ff; + page_table += ((address & 0x003ff000) >> PAGE_SHIFT); if (*page_table) { printk("put_page: page already exists\n"); *page_table = 0; @@ -471,13 +488,18 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig if (mem_map[MAP_NR(page)] != 1) printk("mem_map disagrees with %p at %p\n",page,address); page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc)); - if ((*page_table)&PAGE_PRESENT) + if (PAGE_PRESENT & *page_table) page_table = (unsigned long *) (0xfffff000 & *page_table); else { if (!(tmp=get_free_page(GFP_KERNEL))) return 0; - *page_table = tmp | PAGE_TABLE; - page_table = (unsigned long *) tmp; + if (PAGE_PRESENT & *page_table) { + free_page(tmp); + page_table = (unsigned long *) (0xfffff000 & *page_table); + } else { + *page_table = tmp | PAGE_TABLE; + page_table = (unsigned long *) tmp; + } } page_table += (address >> PAGE_SHIFT) & 0x3ff; if (*page_table) { @@ -494,8 +516,14 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig * This routine handles present pages, when users try to write * to a shared page. It is done by copying the page to a new address * and decrementing the shared-page counter for the old page. + * + * Note that we do many checks twice (look at do_wp_page()), as + * we have to be careful about race-conditions. + * + * Goto-purists beware: the only reason for goto's here is that it results + * in better assembly code.. The "default" path will see no jumps at all. */ -void do_wp_page(unsigned long error_code, unsigned long address, +static void __do_wp_page(unsigned long error_code, unsigned long address, struct task_struct * tsk, unsigned long user_esp) { unsigned long pde, pte, old_page, prot; @@ -504,63 +532,90 @@ void do_wp_page(unsigned long error_code, unsigned long address, new_page = __get_free_page(GFP_KERNEL); pde = tsk->tss.cr3 + ((address>>20) & 0xffc); pte = *(unsigned long *) pde; - if (!(pte & PAGE_PRESENT)) { - if (new_page) - free_page(new_page); - return; - } - if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) { - printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte); - *(unsigned long *) pde = BAD_PAGETABLE | PAGE_TABLE; - send_sig(SIGKILL, tsk, 1); - if (new_page) - free_page(new_page); - return; - } + if (!(pte & PAGE_PRESENT)) + goto end_wp_page; + if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) + goto bad_wp_pagetable; pte &= 0xfffff000; pte += (address>>10) & 0xffc; old_page = *(unsigned long *) pte; - if (!(old_page & PAGE_PRESENT)) { - if (new_page) - free_page(new_page); - return; - } - if (old_page >= high_memory) { - printk("do_wp_page: bogus page at address %08x (%08x)\n",address,old_page); - *(unsigned long *) pte = BAD_PAGE | PAGE_SHARED; - send_sig(SIGKILL, tsk, 1); - if (new_page) - free_page(new_page); - return; - } - if (old_page & PAGE_RW) { - if (new_page) - free_page(new_page); - return; - } - if (!(old_page & PAGE_COW)) { - if (user_esp && tsk == current) - send_sig(SIGSEGV, tsk, 1); - } + if (!(old_page & PAGE_PRESENT)) + goto end_wp_page; + if (old_page >= high_memory) + goto bad_wp_page; + if (old_page & PAGE_RW) + goto end_wp_page; tsk->min_flt++; prot = (old_page & 0x00000fff) | PAGE_RW; old_page &= 0xfffff000; - if (mem_map[MAP_NR(old_page)]==1) { - *(unsigned long *) pte |= 2; + if (mem_map[MAP_NR(old_page)] != 1) { + if (new_page) { + copy_page(old_page,new_page); + *(unsigned long *) pte = new_page | prot; + free_page(old_page); + invalidate(); + return; + } + free_page(old_page); + oom(tsk); + *(unsigned long *) pte = BAD_PAGE | prot; invalidate(); - if (new_page) - free_page(new_page); return; } + *(unsigned long *) pte |= PAGE_RW; + invalidate(); if (new_page) - copy_page(old_page,new_page); - else { - new_page = BAD_PAGE; - oom(tsk); + free_page(new_page); + return; +bad_wp_page: + printk("do_wp_page: bogus page at address %08x (%08x)\n",address,old_page); + *(unsigned long *) pte = BAD_PAGE | PAGE_SHARED; + send_sig(SIGKILL, tsk, 1); + goto end_wp_page; +bad_wp_pagetable: + printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte); + *(unsigned long *) pde = BAD_PAGETABLE | PAGE_TABLE; + send_sig(SIGKILL, tsk, 1); +end_wp_page: + if (new_page) + free_page(new_page); + return; +} + +/* + * check that a page table change is actually needed, and call + * the low-level function only in that case.. + */ +void do_wp_page(unsigned long error_code, unsigned long address, + struct task_struct * tsk, unsigned long user_esp) +{ + unsigned long page; + unsigned long * pg_table; + + pg_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc)); + page = *pg_table; + if (!page) + return; + if ((page & PAGE_PRESENT) && page < high_memory) { + pg_table = (unsigned long *) ((page & 0xfffff000) + ((address>>10) & 0xffc)); + page = *pg_table; + if (!(page & PAGE_PRESENT)) + return; + if (page & PAGE_RW) + return; + if (!(page & PAGE_COW)) + if (user_esp && tsk == current) + send_sig(SIGSEGV, tsk, 1); + if (mem_map[MAP_NR(page)] == 1) { + *pg_table |= PAGE_RW; + invalidate(); + return; + } + __do_wp_page(error_code, address, tsk, user_esp); + return; } - *(unsigned long *) pte = new_page | prot; - free_page(old_page); - invalidate(); + printk("bad page directory entry %08x\n",page); + *pg_table = 0; } int verify_area(int type, void * addr, unsigned long size) @@ -691,13 +746,21 @@ static int share_page(struct task_struct * tsk, struct inode * inode, } /* - * fill in an empty page-table if none exists + * fill in an empty page-table if none exists. */ -static unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long address) +static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long address) { - unsigned long page = 0; + unsigned long page; unsigned long *p; -repeat: + + p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc)); + if (PAGE_PRESENT & *p) + return *p; + if (*p) { + printk("get_empty_pgtable: bad page-directory entry \n"); + *p = 0; + } + page = get_free_page(GFP_KERNEL); p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc)); if (PAGE_PRESENT & *p) { free_page(page); @@ -711,8 +774,6 @@ repeat: *p = page | PAGE_TABLE; return *p; } - if ((page = get_free_page(GFP_KERNEL)) != 0) - goto repeat; oom(current); *p = BAD_PAGETABLE | PAGE_TABLE; return 0; diff --git a/mm/swap.c b/mm/swap.c index 71ff3fe878cf..60dd31121708 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -16,7 +16,9 @@ #include #include #include + #include /* for cli()/sti() */ +#include #define MAX_SWAPFILES 8 @@ -51,20 +53,6 @@ static unsigned long last_free_pages[NR_LAST_FREE_PAGES] = {0,}; #define SWAP_BITS 4096 -#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") - void rw_swap_page(int rw, unsigned long entry, char * buf) { unsigned long type, offset; @@ -85,7 +73,7 @@ void rw_swap_page(int rw, unsigned long entry, char * buf) printk("Trying to swap to unused swap-device\n"); return; } - while (setbit(p->swap_lockmap,offset)) + while (set_bit(offset,p->swap_lockmap)) sleep_on(&lock_queue); if (p->swap_device) { ll_rw_page(rw,p->swap_device,offset,buf); @@ -102,7 +90,7 @@ void rw_swap_page(int rw, unsigned long entry, char * buf) ll_rw_swap_file(rw,p->swap_file->i_dev, zones,4,buf); } else printk("re_swap_page: no swap file or device\n"); - if (!clrbit(p->swap_lockmap,offset)) + if (clear_bit(offset,p->swap_lockmap)) printk("rw_swap_page: lock already cleared\n"); wake_up(&lock_queue); } @@ -177,7 +165,7 @@ void swap_free(unsigned long entry) printk("Trying to free swap from unused swap-device\n"); return; } - while (setbit(p->swap_lockmap,offset)) + while (set_bit(offset,p->swap_lockmap)) sleep_on(&lock_queue); if (offset < p->lowest_bit) p->lowest_bit = offset; @@ -187,7 +175,7 @@ void swap_free(unsigned long entry) printk("swap_free: swap-space map bad (entry %08x)\n",entry); else p->swap_map[offset]--; - if (!clrbit(p->swap_lockmap,offset)) + if (clear_bit(offset,p->swap_lockmap)) printk("swap_free: lock already cleared\n"); wake_up(&lock_queue); } @@ -399,6 +387,7 @@ void free_page(unsigned long addr) return; } printk("Trying to free free memory (%08x): memory probabably corrupted\n",addr); + printk("PC = %08x\n",*(((unsigned long *)&addr)-1)); return; } printk("Trying to free nonexistent page %08x\n",addr); @@ -680,7 +669,7 @@ int sys_swapon(const char * specialfile) p->lowest_bit = 0; p->highest_bit = 0; for (i = 1 ; i < SWAP_BITS ; i++) - if (bit(tmp,i)) { + if (test_bit(i,tmp)) { if (!p->lowest_bit) p->lowest_bit = i; p->highest_bit = i; @@ -700,7 +689,7 @@ int sys_swapon(const char * specialfile) } i = SWAP_BITS; while (i--) - if (bit(tmp,i)) + if (test_bit(i,tmp)) tmp[i] = 0; else tmp[i] = 128; diff --git a/net/socket.c b/net/socket.c index 8b192bb3ded2..2adfcd737a2e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -56,7 +56,7 @@ static int sock_readdir(struct inode *inode, struct file *file, static void sock_close(struct inode *inode, struct file *file); static int sock_select(struct inode *inode, struct file *file, int which, select_table *seltable); static int sock_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned int arg); + unsigned int cmd, unsigned long arg); static struct file_operations socket_file_ops = { sock_lseek, @@ -281,7 +281,7 @@ sock_readdir(struct inode *inode, struct file *file, struct dirent *dirent, int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned int arg) + unsigned long arg) { struct socket *sock; @@ -335,7 +335,7 @@ sock_close(struct inode *inode, struct file *file) int sock_awaitconn(struct socket *mysock, struct socket *servsock) { - struct socket *last; + struct socket **last; PRINTK(("sock_awaitconn: trying to connect socket 0x%x to 0x%x\n", mysock, servsock)); @@ -349,13 +349,10 @@ sock_awaitconn(struct socket *mysock, struct socket *servsock) */ mysock->next = NULL; cli(); - if (!(last = servsock->iconn)) - servsock->iconn = mysock; - else { - while (last->next) - last = last->next; - last->next = mysock; - } + last = &servsock->iconn; + while (*last) + last = &(*last)->next; + *last = mysock; mysock->state = SS_CONNECTING; mysock->conn = servsock; sti(); @@ -367,7 +364,20 @@ sock_awaitconn(struct socket *mysock, struct socket *servsock) wake_up(servsock->wait); if (mysock->state != SS_CONNECTED) { interruptible_sleep_on(mysock->wait); - if (mysock->state != SS_CONNECTED) { + if (mysock->state != SS_CONNECTED && + mysock->state != SS_DISCONNECTING) + /* SS_DISCONNECTING means the server accepted + * the connection, maybe wrote some data, and then + * closed it again, all before we got to run. + * The only way a socket can get to SS_DISCONNECTING + * is through sock_release or sock_release_peer. + * The former is only called in case of various setup + * failures or sock_close (which obviously isn't the + * case as we still have it open); the latter is + * called when the server (to which we obviously + * must have been connected) closes its end. + */ + { /* * if we're not connected we could have been * 1) interrupted, so we need to remove ourselves @@ -377,12 +387,13 @@ sock_awaitconn(struct socket *mysock, struct socket *servsock) */ if (mysock->conn == servsock) { cli(); - if ((last = servsock->iconn) == mysock) - servsock->iconn = mysock->next; - else { - while (last->next != mysock) - last = last->next; - last->next = mysock->next; + last = &servsock->iconn; + while (*last) { + if (*last == mysock) { + *last = mysock->next; + break; + } + last = &(*last)->next; } sti(); } diff --git a/net/tcp/packet.c b/net/tcp/packet.c index 339abf0e9802..b749aab09960 100644 --- a/net/tcp/packet.c +++ b/net/tcp/packet.c @@ -158,7 +158,7 @@ packet_sendto (volatile struct sock *sk, unsigned char *from, int len, /* this shouldn't happen, but it could. */ if (skb == NULL) { - PRINTK ("packet_sendto: write buffer full?\n"); + PRINTK (("packet_sendto: write buffer full?\n")); return (-EAGAIN); } skb->lock = 0; @@ -259,6 +259,7 @@ packet_recvfrom (volatile struct sock *sk, unsigned char *to, int len, interruptible_sleep_on (sk->sleep); if (current->signal & ~current->blocked) { + sti(); return (-ERESTARTSYS); } } diff --git a/net/tcp/udp.c b/net/tcp/udp.c index d07425af3571..4c5d0d16932d 100644 --- a/net/tcp/udp.c +++ b/net/tcp/udp.c @@ -559,6 +559,7 @@ udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len, interruptible_sleep_on (sk->sleep); if (current->signal & ~current->blocked) { + sti(); return (-ERESTARTSYS); } } diff --git a/net/tcp/we.c b/net/tcp/we.c index 5cd607953de9..a074838f025b 100644 --- a/net/tcp/we.c +++ b/net/tcp/we.c @@ -222,6 +222,7 @@ wd8003_start_xmit(struct sk_buff *skb, struct device *dev) /* put in a time out. */ if (jiffies - dev->trans_start < 30) { + sti(); return (1); } diff --git a/net/unix.c b/net/unix.c index 2e077f427ba3..05cbd4204ae7 100644 --- a/net/unix.c +++ b/net/unix.c @@ -560,10 +560,6 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) PRINTK(("unix_proto_read: interrupted\n")); return -ERESTARTSYS; } - if (sock->state == SS_DISCONNECTING) { - PRINTK(("unix_proto_read: disconnected\n")); - return 0; - } } /* diff --git a/zBoot/Makefile b/zBoot/Makefile index 35081f8d7e99..5768caa9f44e 100644 --- a/zBoot/Makefile +++ b/zBoot/Makefile @@ -15,7 +15,6 @@ CFLAGS = -O6 -DSTDC_HEADERS $(TEST) .c.o: $(CC) $(CFLAGS) -c -o $*.o $< - all: zSystem zSystem: piggy.o $(zOBJECTS) @@ -26,8 +25,8 @@ head.o: head.s head.s: head.S ../include/linux/tasks.h $(CPP) -traditional head.S -o head.s -piggy.s: $(SYSTEM) xtract piggyback - ./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.s +piggy.o: $(SYSTEM) xtract piggyback + ./xtract $(SYSTEM) | gzip -9 | ./piggyback > piggy.o $(SYSTEM): cd ..;make tools/zSystem diff --git a/zBoot/inflate.c b/zBoot/inflate.c index b261914eaa3a..7c230cb0b067 100644 --- a/zBoot/inflate.c +++ b/zBoot/inflate.c @@ -3,106 +3,15 @@ /* inflate.c -- Not copyrighted 1992 by Mark Adler version c10p1, 10 January 1993 */ -/* You can do whatever you like with this source file, though I would - prefer that if you modify it and redistribute it that you include - comments to that effect with your name and the date. Thank you. - [The history has been moved to the file ChangeLog.] - */ - -/* - Inflate deflated (PKZIP's method 8 compressed) data. The compression - method searches for as much of the current string of bytes (up to a - length of 258) in the previous 32K bytes. If it doesn't find any - matches (of at least length 3), it codes the next byte. Otherwise, it - codes the length of the matched string and its distance backwards from - the current position. There is a single Huffman code that codes both - single bytes (called "literals") and match lengths. A second Huffman - code codes the distance information, which follows a length code. Each - length or distance code actually represents a base value and a number - of "extra" (sometimes zero) bits to get to add to the base value. At - the end of each deflated block is a special end-of-block (EOB) literal/ - length code. The decoding process is basically: get a literal/length - code; if EOB then done; if a literal, emit the decoded byte; if a - length then get the distance and emit the referred-to bytes from the - sliding window of previously emitted data. - - There are (currently) three kinds of inflate blocks: stored, fixed, and - dynamic. The compressor deals with some chunk of data at a time, and - decides which method to use on a chunk-by-chunk basis. A chunk might - typically be 32K or 64K. If the chunk is uncompressible, then the - "stored" method is used. In this case, the bytes are simply stored as - is, eight bits per byte, with none of the above coding. The bytes are - preceded by a count, since there is no longer an EOB code. - - If the data is compressible, then either the fixed or dynamic methods - are used. In the dynamic method, the compressed data is preceded by - an encoding of the literal/length and distance Huffman codes that are - to be used to decode this block. The representation is itself Huffman - coded, and so is preceded by a description of that code. These code - descriptions take up a little space, and so for small blocks, there is - a predefined set of codes, called the fixed codes. The fixed method is - used if the block codes up smaller that way (usually for quite small - chunks), otherwise the dynamic method is used. In the latter case, the - codes are customized to the probabilities in the current block, and so - can code it much better than the pre-determined fixed codes. - - The Huffman codes themselves are decoded using a mutli-level table - lookup, in order to maximize the speed of decoding plus the speed of - building the decoding tables. See the comments below that precede the - lbits and dbits tuning parameters. - */ - - -/* - Notes beyond the 1.93a appnote.txt: - - 1. Distance pointers never point before the beginning of the output - stream. - 2. Distance pointers can point back across blocks, up to 32k away. - 3. There is an implied maximum of 7 bits for the bit length table and - 15 bits for the actual data. - 4. If only one code exists, then it is encoded using one bit. (Zero - would be more efficient, but perhaps a little confusing.) If two - codes exist, they are coded using one bit each (0 and 1). - 5. There is no way of sending zero distance codes--a dummy must be - sent if there are none. (History: a pre 2.0 version of PKZIP would - store blocks with no distance codes, but this was discovered to be - too harsh a criterion.) Valid only for 1.93a. 2.04c does allow - zero distance codes, which is sent as one code of zero bits in - length. - 6. There are up to 286 literal/length codes. Code 256 represents the - end-of-block. Note however that the static length tree defines - 288 codes just to fill out the Huffman codes. Codes 286 and 287 - cannot be used though, since there is no length base or extra bits - defined for them. Similarily, there are up to 30 distance codes. - However, static trees define 32 codes (all 5 bits) to fill out the - Huffman codes, but the last two had better not show up in the data. - 7. Unzip can check dynamic Huffman blocks for complete code sets. - The exception is that a single code would not be complete (see #4). - 8. The five bits following the block type is really the number of - literal codes sent minus 257. - 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits - (1+6+6). Therefore, to output three times the length, you output - three codes (1+1+1), whereas to output four times the same length, - you only need two codes (1+3). Hmm. - 10. In the tree reconstruction algorithm, Code = Code + Increment - only if BitLength(i) is not zero. (Pretty obvious.) - 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) - 12. Note: length code 284 can represent 227-258, but length code 285 - really is 258. The last length deserves its own, short code - since it gets used a lot in very redundant files. The length - 258 is special since 258 - 3 (the min match length) is 255. - 13. The literal/length and distance code bit lengths are read as a - single stream of lengths. It is possible (and advantageous) for - a repeat code (16, 17, or 18) to go across the boundary between - the two sets of lengths. +/* + * Adapted for booting Linux by Hannu Savolainen 1993 + * based on gzip-1.0.3 */ #ifndef lint static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $"; #endif -/* #include "tailor.h" */ #include "gzip.h" #define slide window @@ -111,13 +20,6 @@ static char rcsid[] = "$Id: inflate.c,v 0.10 1993/02/04 13:21:06 jloup Exp $"; # include #endif -/* Huffman code lookup table entry--this entry is four bytes for machines - that have 16-bit pointers (e.g. PC's in the small or medium model). - Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 - means that v is a literal, 16 < e < 32 means that v is a pointer to - the next table, which codes e - 16 bits, and lastly e == 99 indicates - an unused code. If a code with e == 99 is looked up, this implies an - error in the data. */ struct huft { uch e; /* number of extra bits or operation */ uch b; /* number of bits in this code or subcode */ @@ -140,15 +42,6 @@ int inflate_block OF((int *)); int inflate OF((void)); -/* The inflate algorithm uses a sliding 32K byte window on the uncompressed - stream to find repeated byte strings. This is implemented here as a - circular buffer. The index is updated simply by incrementing and then - and'ing with 0x7fff (32K-1). */ -/* It is left to other modules to supply the 32K area. It is assumed - to be usable as if it were declared "uch slide[32768];" or as just - "uch *slide;" and then malloc'ed in the latter case. The definition - must be in unzip.h, included above. */ -/* unsigned wp; current position in slide */ #define wp outcnt #define flush_output(w) (wp=(w),flush_window()) @@ -172,37 +65,6 @@ static ush cpdext[] = { /* Extra bits for distance codes */ 12, 12, 13, 13}; - -/* Macros for inflate() bit peeking and grabbing. - The usage is: - - NEEDBITS(j) - x = b & mask_bits[j]; - DUMPBITS(j) - - where NEEDBITS makes sure that b has at least j bits in it, and - DUMPBITS removes the bits from b. The macros use the variable k - for the number of bits in b. Normally, b and k are register - variables for speed, and are initialized at the begining of a - routine that uses these macros from a global bit buffer and count. - - If we assume that EOB will be the longest code, then we will never - ask for bits with NEEDBITS that are beyond the end of the stream. - So, NEEDBITS should not read any more bytes than are needed to - meet the request. Then no bytes need to be "returned" to the buffer - at the end of the last block. - - However, this assumption is not true for fixed blocks--the EOB code - is 7 bits, but the other literal/length codes can be 8 or 9 bits. - (The EOB code is shorter than other codes becuase fixed blocks are - generally short. So, while a block always has an EOB, many other - literal/length codes have a significantly lower probability of - showing up at all.) However, by making the first table have a - lookup of seven bits, the EOB code will be found in that first - lookup, and so will not require that too many bits be pulled from - the stream. - */ - ulg bb; /* bit buffer */ unsigned bk; /* bits in bit buffer */ @@ -222,40 +84,6 @@ ush mask_bits[] = { #define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<>=(n);k-=(n);} - -/* - Huffman code decoding is performed using a multi-level table lookup. - The fastest way to decode is to simply build a lookup table whose - size is determined by the longest code. However, the time it takes - to build this table can also be a factor if the data being decoded - is not very long. The most common codes are necessarily the - shortest codes, so those codes dominate the decoding time, and hence - the speed. The idea is you can have a shorter table that decodes the - shorter, more probable codes, and then point to subsidiary tables for - the longer codes. The time it costs to decode the longer codes is - then traded against the time it takes to make longer tables. - - This results of this trade are in the variables lbits and dbits - below. lbits is the number of bits the first level table for literal/ - length codes can decode in one step, and dbits is the same thing for - the distance codes. Subsequent tables are also less than or equal to - those sizes. These values may be adjusted either when all of the - codes are shorter than that, in which case the longest code length in - bits is used, or when the shortest code is *longer* than the requested - table size, in which case the length of the shortest code in bits is - used. - - There are two different values for the two tables, since they code a - different number of possibilities each. The literal/length table - codes 286 possible values, or in a flat code, a little over eight - bits. The distance table codes 30 possible values, or a little less - than five bits, flat. The optimum values for speed end up being - about one bit more than those, so lbits is 8+1 and dbits is 5+1. - The optimum values may differ though from machine to machine, and - possibly even between compilers. Your mileage may vary. - */ - - int lbits = 9; /* bits in base literal/length lookup table */ int dbits = 6; /* bits in base distance lookup table */ @@ -308,8 +136,6 @@ DEBG("huft1 "); memzero(c, sizeof(c)); p = b; i = n; do { - Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), - n-i, *p)); c[*p++]++; /* assume all entries <= BMAX */ } while (--i); if (c[0] == n) /* null input--all zero length codes */ @@ -545,7 +371,6 @@ int bl, bd; /* number of bits decoded by tl[] and td[] */ if (e == 16) /* then it's a literal */ { slide[w++] = (uch)t->v.n; - Tracevv((stderr, "%c", slide[w-1])); if (w == WSIZE) { flush_output(w); @@ -577,7 +402,6 @@ int bl, bd; /* number of bits decoded by tl[] and td[] */ NEEDBITS(e) d = w - t->v.n - ((unsigned)b & mask_bits[e]); DUMPBITS(e) - Tracevv((stderr,"\\[%d,%d]", w-d, n)); /* do the copy */ do { @@ -593,7 +417,6 @@ int bl, bd; /* number of bits decoded by tl[] and td[] */ #endif /* !NOMEMCPY */ do { slide[w++] = slide[d++]; - Tracevv((stderr, "%c", slide[w-1])); } while (--e); if (w == WSIZE) { diff --git a/zBoot/misc.c b/zBoot/misc.c index 179e6c81114d..ae883b9828e5 100644 --- a/zBoot/misc.c +++ b/zBoot/misc.c @@ -1,3 +1,12 @@ +/* + * misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc and printk by Hannu Savolainen 1993 + */ + #include "gzip.h" #include "lzw.h" @@ -59,6 +68,7 @@ int to_stdout = 0; int hard_math = 0; void (*work)(int inf, int outf); +void makecrc(void); char *vidmem = (char *)0xb8000; int vidp = 0; @@ -203,68 +213,51 @@ void flush_window() outcnt = 0; } -/* ======================================================================== - * Table of CRC-32's of all single-byte values (made by makecrc.c) +/* + * Code to compute the CRC-32 table. Borrowed from + * gzip-1.0.3/makecrc.c. */ -ulg crc_32_tab[] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; + +ulg crc_32_tab[256]; + +void +makecrc(void) +{ +/* Not copyrighted 1990 Mark Adler */ + + unsigned long c; /* crc shift register */ + unsigned long e; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial */ + e = 0; + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + crc_32_tab[0] = 0; + + for (i = 1; i < 256; i++) + { + c = 0; + for (k = i | 256; k != 1; k >>= 1) + { + c = c & 1 ? (c >> 1) ^ e : c >> 1; + if (k & 1) + c ^= e; + } + crc_32_tab[i] = c; + } +} void error(char *x) { + printk("\n\n"); printk(x); - printk(" -- System halted"); + printk("\n\n -- System halted"); while(1); /* Halt */ } @@ -290,7 +283,7 @@ void decompress_kernel() lines = SCREEN_INFO.orig_video_lines; cols = SCREEN_INFO.orig_video_cols; - if (EXT_MEM_K < 2048) error("At least 2M of memory required by Turbo Ignition\n"); + if (EXT_MEM_K < 1024) error("<2M of mem\n"); output_data = (char *)1048576; /* Points to 1M */ output_ptr = 0; @@ -301,13 +294,12 @@ void decompress_kernel() part_nb = 0; clear_bufs(); + makecrc(); - printk("Uncompressing Linux: check - "); + printk("Uncompressing Linux..."); method = get_method(0); - printk("work - "); - work(0, 0); printk("done.\n\n"); @@ -346,12 +338,12 @@ local int get_method(in) method = (int)get_byte(); flags = (uch)get_byte(); if ((flags & ENCRYPTED) != 0) { - error("Input is encrypted -- too secret\n"); + error("Input is encrypted\n"); exit_code = ERROR; return -1; } if ((flags & CONTINUATION) != 0) { - error("Input is a a multi-part gzip file\n"); + error("Multi part input\n"); exit_code = ERROR; if (force <= 1) return -1; } diff --git a/zBoot/piggyback.c b/zBoot/piggyback.c index 907cb9beda43..40284118bc45 100644 --- a/zBoot/piggyback.c +++ b/zBoot/piggyback.c @@ -1,31 +1,80 @@ +/* + * linux/zBoot/piggyback.c + * + * (C) 1993 Hannu Savolainen + */ + +/* + * This program reads the compressed system image from stdin and + * encapsulates it into an object file written to the stdout. + */ + #include +#include +#include int main(int argc, char *argv[]) { int c, n=0, len=0; + char tmp_buf[512*1024]; + + struct exec obj = {0x00640107}; /* object header */ + char string_names[] = {"_input_data\0_input_len\0"}; + + struct nlist var_names[2] = /* Symbol table */ + { + { /* _input_data */ + (char *)4, 7, 0, 0, 0 + }, + { /* _input_len */ + (char *)16, 7, 0, 0, 0 + } + }; + - printf( - ".globl _input_data\n" - ".data\n" - "_input_data:\n"); + len = 0; + while ((n = read(0, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0) + len += n; - while ((c=getchar()) != EOF) + if (n==-1) { - len++; - if (!n) printf("\n.byte "); else printf(","); - printf("%d", c); - n = (n+1) & 0x1f; + perror("stdin"); + exit(-1); } - printf("\n\n"); + if (len >= sizeof(tmp_buf)) + { + fprintf(stderr, "%s: Input too large\n", argv[0]); + exit(-1); + } fprintf(stderr, "Compressed size %d.\n", len); +/* + * Output object header + */ + obj.a_data = len + sizeof(long); + obj.a_syms = sizeof(var_names); + write(1, (char *)&obj, sizeof(obj)); + +/* + * Output data segment (compressed system & len) + */ + write(1, tmp_buf, len); + write(1, (char *)&len, sizeof(len)); + +/* + * Output symbol table + */ + var_names[1].n_value = len; + write(1, (char *)&var_names, sizeof(var_names)); - printf( ".globl _input_len\n" - ".align 2\n" - "_input_len:\n" - "\t.long %d\n", len); +/* + * Output string table + */ + len = sizeof(string_names) + sizeof(len); + write(1, (char *)&len, sizeof(len)); + write(1, string_names, sizeof(string_names)); exit(0); diff --git a/zBoot/unzip.c b/zBoot/unzip.c index d752b5ec4727..d4a6617cd88b 100644 --- a/zBoot/unzip.c +++ b/zBoot/unzip.c @@ -1,5 +1,8 @@ /* unzip.c -- decompress files in gzip or pkzip format. * Copyright (C) 1992-1993 Jean-loup Gailly + * + * Adapted for Linux booting by Hannu Savolainen 1993 + * * This is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License, see the file COPYING. * @@ -17,7 +20,6 @@ static char rcsid[] = "$Id: unzip.c,v 0.9 1993/02/10 16:07:22 jloup Exp $"; #endif -/* #include "tailor.h" */ #include "gzip.h" #include "crypt.h" @@ -61,7 +63,7 @@ int check_zipfile(in) inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT); if (inptr > insize || LG(h) != LOCSIG) { - error("input not a zip file or empty"); + error("input not a zip"); } method = h[LOCHOW]; if (method != STORED && method != DEFLATED) { @@ -70,7 +72,7 @@ int check_zipfile(in) /* If entry encrypted, decrypt and validate encryption header */ if ((decrypt = h[LOCFLG] & CRPFLG) != 0) { - error("encrypted file, not yet supported.\n"); + error("encrypted file\n"); exit_code = ERROR; return -1; } @@ -116,7 +118,7 @@ void unzip(in, out) if (res == 3) { error("out of memory"); } else if (res != 0) { - error("invalid compressed data--format violated"); + error("invalid compressed format"); } } else if (pkzip && method == STORED) { @@ -125,7 +127,7 @@ void unzip(in, out) if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) { - error("invalid compressed data--length mismatch"); + error("length mismatch"); } while (n--) { uch c = (uch)get_byte(); @@ -164,10 +166,10 @@ void unzip(in, out) /* Validate decompression */ if (orig_crc != updcrc(outbuf, 0)) { - error("invalid compressed data--crc error"); + error("crc error"); } if (orig_len != bytes_out) { - error("invalid compressed data--length error"); + error("length error"); } /* Check if there are more entries in a pkzip file */ diff --git a/zBoot/xtract.c b/zBoot/xtract.c index 40f3af1058dc..c698a5c6e8af 100644 --- a/zBoot/xtract.c +++ b/zBoot/xtract.c @@ -1,23 +1,10 @@ /* - * linux/tools/build.c + * linux/zBoot/xtract.c * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * This file builds a disk-image from three different files: - * - * - bootsect: max 510 bytes of 8086 machine code, loads the rest - * - setup: max 4 sectors of 8086 machine code, sets up system parm - * - system: 80386 code for actual system + * Copyright (C) 1993 Hannu Savolainen * - * It does some checking that all files are of the correct type, and - * just writes the result to stdout, removing headers and padding to - * the right amount. It also writes some system data to stderr. - */ - -/* - * Changes by tytso to allow root device specification + * Extracts the system image and writes it to the stdout. + * based on tools/build.c by Linus Torvalds */ #include /* fprintf */ -- 2.39.5