diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-05-12 23:48:34 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-05-12 23:48:34 +0000 |
commit | 7fd36ebeeec9244a7431bb010e6e3c5e4848a0d5 (patch) | |
tree | 5fb03a9aafdd1cec5f4f6ff7f1873174cb89b66c /arch/s390 | |
parent | ba2dacab305c598cd4c34a604f8e276bf5bab5ff (diff) |
Merge with Linux 2.3.99-pre8. Linus must hate me, too man patches ;-)
Diffstat (limited to 'arch/s390')
57 files changed, 17753 insertions, 0 deletions
diff --git a/arch/s390/Makefile b/arch/s390/Makefile new file mode 100644 index 000000000..7af7bb937 --- /dev/null +++ b/arch/s390/Makefile @@ -0,0 +1,68 @@ + +# s390/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# + +LD=$(CROSS_COMPILE)ld -m elf_s390 +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +LDFLAGS=-e start +LINKFLAGS =-T $(TOPDIR)/arch/s390/vmlinux.lds $(LDFLAGS) + +CFLAGS_PIPE := -pipe +CFLAGS_NSR := -fno-strength-reduce +CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR) + +HEAD := arch/s390/kernel/head.o arch/s390/kernel/init_task.o + +SUBDIRS := $(SUBDIRS) arch/s390/mm arch/s390/kernel arch/s390/lib \ + drivers/s390 +CORE_FILES := arch/s390/mm/mm.o arch/s390/kernel/kernel.o $(CORE_FILES) \ + drivers/s390/io.o +LIBS := $(TOPDIR)/arch/s390/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390/lib/lib.a + +all: image listing + +listing: vmlinux + @$(MAKEBOOT) listing + +arch/s390/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/s390/kernel + +arch/s390/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/s390/mm + +drivers/s390: dummy + $(MAKE) linuxsubdirs SUBDIRS=drivers/s390 + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +MAKESILO = $(MAKE) -C arch/$(ARCH)/tools/silo + +MAKEDASDFMT = $(MAKE) -C arch/$(ARCH)/tools/dasdfmt + +silo: + @$(MAKESILO) silo + +dasdfmt: + @$(MAKEDASDFMT) dasdfmt + +image: vmlinux + @$(MAKEBOOT) image + +archclean: + @$(MAKEBOOT) clean + +archmrproper: + +archdep: + @$(MAKEBOOT) dep diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile new file mode 100644 index 000000000..817152810 --- /dev/null +++ b/arch/s390/boot/Makefile @@ -0,0 +1,38 @@ +# +# Makefile for the linux s390-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +OBJCOPY = $(CROSS_COMPILE)objcopy + +O_TARGET := +O_OBJS := + +include $(TOPDIR)/Rules.make + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +%.lnk: %.o + $(LD) -Ttext 0x0 -o $@ $< + +%.boot: %.lnk + $(OBJCOPY) -O binary $< $@ + +image: $(CONFIGURE) $(TOPDIR)/vmlinux \ + iplfba.boot ipleckd.boot ipldump.boot + $(OBJCOPY) -O binary $(TOPDIR)/vmlinux image + $(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aU] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map + +listing: ../../../vmlinux + $(OBJDUMP) --disassemble --disassemble-all --disassemble-zeroes --reloc $(TOPDIR)/vmlinux > listing + +dep: + +clean: + rm -f image listing iplfba.boot ipleckd.boot ipldump.boot + diff --git a/arch/s390/boot/ipldump.S b/arch/s390/boot/ipldump.S new file mode 100644 index 000000000..7868268af --- /dev/null +++ b/arch/s390/boot/ipldump.S @@ -0,0 +1,178 @@ +/* + * arch/s390/boot/ipldump.S + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Tape dump ipl record. Put it on a tape and ipl from it and it will + * write a dump of the real storage after the ipl record on that tape. + */ + +#include <asm/setup.h> +#include <asm/lowcore.h> + +#define IPL_BS 1024 + .org 0 + .long 0x00080000,0x80000000+_start # The first 24 bytes are loaded + .long 0x07000000,0x60000001 # by ipl to addresses 0-23. + .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 # svc old psw + .long 0x00000000,0x00000000 # program check old psw + .long 0x00000000,0x00000000 # machine check old psw + .long 0x00000000,0x00000000 # io old psw + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x000a0000,0x00000058 # external new psw + .long 0x000a0000,0x00000060 # svc new psw + .long 0x000a0000,0x00000068 # program check new psw + .long 0x000a0000,0x00000070 # machine check new psw + .long 0x00080000,0x80000000+.Lioint # io new psw + + .org 0x100 + .globl _start +_start: + l %r1,0xb8 # load ipl subchannel number +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r3,%r3 + lhi %r2,1 + sll %r2,20 +.Lloop0: + l %r0,0(%r3) # test page + ar %r3,%r2 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r3,.L4malign0 # align to multiples of 4M + st %r3,.Lmemsize # store memory size +.Lmemok: + +# +# first write a tape mark +# + bras %r14,.Ltapemark +# +# write real storage to tape +# + slr %r2,%r2 # start at address 0 + bras %r14,.Lwriter # load ramdisk +# +# write another tape mark +# + bras %r14,.Ltapemark +# +# everything written, stop processor +# + lpsw .Lstopped +# +# subroutine for writing to tape +# Paramters: +# R1 = device number +# R2 = start address +# R3 = length +.Lwriter: + st %r14,.Lldret + la %r12,.Lorbread # r12 = address of orb + la %r5,.Lirb # r5 = address of irb + st %r2,.Lccwwrite+4 # initialize CCW data addresses + lctl %c6,%c6,.Lcr6 + slr %r2,%r2 +.Lldlp: + lhi %r6,3 # 3 retries +.Lssch: + ssch 0(%r12) # write chunk of IPL_BS bytes + jnz .Llderr +.Lw4end: + bras %r14,.Lwait4io + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lrecov + l %r0,.Lccwwrite+4 # update CCW data addresses + ahi %r0,IPL_BS + st %r0,.Lccwwrite+4 + clr %r0,%r3 # enough ? + jl .Lldlp +.Ldone: + l %r14,.Lldret + br %r14 # r2 contains the total size +.Lrecov: + bras %r14,.Lsense # do the sensing + brct %r6,.Lssch # dec. retry count & branch + j .Llderr +.Ltapemark: + st %r14,.Lldret + la %r12,.Lorbmark # r12 = address of orb + la %r5,.Lirb # r5 = address of irb + lctl %c6,%c6,.Lcr6 + ssch 0(%r12) # write a tape mark + jnz .Llderr + bras %r14,.Lwait4io + l %r14,.Lldret + br %r14 +# +# Sense subroutine +# +.Lsense: + st %r14,.Lsnsret + la %r7,.Lorbsense + ssch 0(%r7) # start sense command + jnz .Llderr + bras %r14,.Lwait4io + l %r14,.Lsnsret + tm 8(%r5),0x82 # do we have a problem ? + jnz .Llderr + br %r14 +# +# Wait for interrupt subroutine +# +.Lwait4io: + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + jne .Lwait4io + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lwtexit + tm 8(%r5),0x04 # got device end ? + jz .Lwait4io +.Lwtexit: + br %r14 +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorbread: + .long 0x00000000,0x0080ff00,.Lccwwrite + .align 8 +.Lorbsense: + .long 0x00000000,0x0080ff00,.Lccwsense + .align 8 +.Lorbmark: + .long 0x00000000,0x0080ff00,.Lccwmark + .align 8 +.Lccwwrite: + .long 0x01200000+IPL_BS,0x00000000 +.Lccwsense: + .long 0x04200001,0x00000000 +.Lccwmark: + .long 0x1f200001,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 + .align 8 +.Lcrash:.long 0x000a0000,0x00000000 +.Lstopped: .long 0x000a0000,0x00001234 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 +.L4malign0:.long 0xffc00000 +.Lmemsize:.long 0 +.Lldret:.long 0 +.Lsnsret: .long 0 + + .org IPL_BS + diff --git a/arch/s390/boot/ipleckd.S b/arch/s390/boot/ipleckd.S new file mode 100644 index 000000000..63b0330b2 --- /dev/null +++ b/arch/s390/boot/ipleckd.S @@ -0,0 +1,299 @@ +# +# arch/s390/boot/ipleckd.S +# IPL record for 3380/3390 DASD +# +# S390 version +# Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation +# Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com> +# +# +# FIXME: should use the countarea to determine the blocksize +# FIXME: should insert zeroes into memory when filling holes +# FIXME: calculate blkpertrack from rdc data and blksize + +# Usage of registers +# r1: ipl subchannel ( general use, dont overload without save/restore !) +# r10: +# r13: base register index to 0x0000 +# r14: callers address +# r15: temporary save register (we have no stack!) + +# storage layout: + +#include <asm/lowcore.h> + + .org 0 +.psw: .long 0x00080000,0x80000000+_start +.ccw1: .long 0x06000000,0x00001000 # Re-Read enough of bootsector to start +.ccw2: .long 0x00000000,0x00000000 # read countarea of record 1 to s/w. + + .org 0x58 +.Lextn: .long 0x000a0000,0x00000000+.Lextn +.Lsvcn: .long 0x000a0000,0x00000000+.Lsvcn +.Lprgn: .long 0x00080000,0x00000000+.Lecs +.Lmcn: .long 0x000a0000,0x00000000+.Lmcn +.Lion: .long 0x00080000,0x80000000+.Lionewaddr + + .org 0xe0 +.Llstad:.long 0x00000000,0x00000000 # sectorno + ct of bootlist + + .org 0xf0 # Lets start now... +_start: .globl _start + l %r1,__LC_SUBCHANNEL_ID # get IPL-subchannel from lowcore + st %r1,__LC_IPLDEV # keep it for reipl + stsch .Lrdcdata + oi .Lrdcdata+5,0x84 # enable ssch and multipath mode +.Lecs: xi .Lrdcdata+27,0x01 # enable concurrent sense + msch .Lrdcdata + xi .Lprgn,6 # restore Wait and d/a bit in PCnew PSW + l %r2,.Lparm + mvc 0x0(8,%r2),.Lnull # set parmarea to null + lctl %c6,%c6,.Lc6 # enable all interrupts +.Lrdc: # read device characteristics + la %r6,.Lrdcccw + st %r6,.Lorb+8 # store cp-address to orb + bras %r15,.Lssch # start I/O + oi .Llodata+1,0x80 + lh %r5,.Lcountarea+6 # init r5 from countarea + stcm %r5,3,.Lrdccw+2 # and store into rd template *FIXME* + stcm %r5,3,.Llodata+14 # and store into lodata *FIXME* +.Lbootlist: + l %r2,.Llstad + l %r3,.Lblklst + lhi %r4,1 + bras %r14,.Lreadblks +.Lloader: + l %r10,.Lblklst # r10 is index to bootlist + lhi %r5,4 # r5: skip 4 blocks = firstpage.... +.Lkloop: + clc .Lnull(8),0(%r10) # test blocklist + jz .Lchkparm # end of list? + l %r2,0(%r10) # get startblock to r2 + slr %r4,%r4 # erase r4 + icm %r4,1,7(%r10) # get blockcount + slr %r3,%r3 # get address to r3 + icm %r3,0xe,4(%r10) + chi %r5,0 # still blocks to skip? + jz .Ldoread # no: start reading + cr %r5,%r4 # #skipblocks >= blockct? + jm .L007 # no: skip the blocks one by one +.L006: + sr %r5,%r4 # decrease number of blocks to skip + j .Lkcont # advance to next entry +.L007: + ahi %r2,1 # skip 1 block... + bctr %r4,0 # update blockct + ah %r3,.Lcountarea+6 # increment address + bct %r5,.L007 # 4 blocks skipped? +.Ldoread: + ltr %r2,%r2 # test startblock + jz .Lzeroes # startblocks is zero (hole) +.Ldiskread: + bras %r14,.Lreadblks + j .Lkcont +.Lzeroes: + lr %r2,%r3 +.L001: slr %r3,%r3 + icm %r3,3,.Lcountarea+6 # get blocksize + slr %r5,%r5 # no bytes to move +.L008: mvcle %r2,%r4,0 # fill zeroes to storage + jo .L008 # until block is filled + brct %r4,.L001 # skip to next block +.Lkcont: + ahi %r10,8 + j .Lkloop +.Lchkparm: + lm %r3,%r4,.Lstart # load .Lstart and .Lparm + clc 0x0(4,%r4),.Lnull + je .Lrunkern + mvc 0x480(128,%r3),0(%r4) # move 1k-0x80 to parmarea + mvc 0x500(256,%r3),0x80(%r4) + mvc 0x600(256,%r3),0x180(%r4) + mvc 0x700(256,%r3),0x280(%r4) +.Lrunkern: + lhi %r2,17 + sll %r2,12 + st %r1,0xc6c(%r2) # store iplsubchannel to lowcore + st %r1,0xc6c # store iplsubchannel to lowcore + br %r3 +# This function does the start IO +# r2: number of first block to read ( input by caller ) +# r3: address to read data to ( input by caller ) +# r4: number of blocks to read ( input by caller ) +# r5: destroyed +# r6: blocks per track ( input by caller ) +# r7: number of heads +# r8: +# r9: +# r10: +# r11: temporary register +# r12: local use for base address +# r13: base address for module +# r14: address of caller for subroutine +# r15: temporary save register (since we have no stack) +.Lreadblks: + la %r12,.Ldeccw + st %r12,8+.Lorb # store cpaddr to orb + ahi %r12,0x10 # increment r12 to point to rdccw + oi 1(%r12),0x40 # set CC in rd template + # first setup the read CCWs + lr %r15,%r4 # save number or blocks + slr %r7,%r7 + icm %r7,3,.Lrdcdata+14 # load heads to r7 + clc .Lrdcdata+3(2),.L3390 + jne .L010 # 3380 or 3390 ? + lhi %r6,12 # setup r6 correct! + j .L011 +.L010: + clc .Lrdcdata+3(2),.L9343 + jne .L013 + lhi %r6,9 + j .L011 +.L013: + lhi %r6,10 +.L011: + # loop for nbl times +.Lrdloop: + mvc 0(8,%r12),.Lrdccw # copy template to this ccw + st %r3,4(%r12) # store target address to this ccw + bct %r4,.L005 # decrement no of blks still to do + ni 1(%r12),0x3f # delete CC from last ccw + lr %r4,%r15 # restore number of blocks + # read CCWs are setup now + stcm %r4,3,.Llodata+2 # store blockno to lodata clears r4 + ar %r4,%r2 # r4 (clear): ebl = blk + nbl + bctr %r4,0 # decrement r4 ( last blk touched + srda %r2,32 # trk = blk / bpt, bot = blk % bpt + dr %r2,%r6 # r3: trk, r2: bot + ahi %r2,1 # bot++ ( we start counting at 1 ) + stcm %r2,1,.Llodata+12 # store bot to lodata + xr %r2,%r2 # cy = trk / heads, hd = trk % heads + dr %r2,%r7 # r3: cy, r2: hd + sll %r3,16 # combine to CCHH in r3 + or %r3,%r2 + st %r3,.Ldedata+8 # store cchh to dedata + st %r3,.Llodata+4 # store cchh to lodata + st %r3,.Llodata+8 # store cchh to lodata + lr %r15,%r5 # save r5 + srda %r4,32 # tr2 = ebl / bpt + dr %r4,%r6 # r5: tr2, r4: bot2 + xr %r4,%r4 # cy2 = tr2 / heads, hd2 = hd2 % heads + dr %r4,%r7 # r5: cy2, r4: hd2 + stcm %r5,3,.Ldedata+12 # store cy2,hd2 to dedata + stcm %r4,3,.Ldedata+14 # store cy2,hd2 to dedata + lr %r5,%r15 # restore r5 + # CCWs are setup now, arent they? + bras %r15,.Lssch # start I/O + br %r14 # return to caller +.L005: + ah %r3,.Lcountarea+6 # add blocksize to target address + ahi %r12,8 # add sizeof(ccw) to base address + j .Lrdloop +# end of function +# This function does the start IO +# r1: Subchannel number +# r8: ORB address +# r9: IRB address +.Lssch: + lhi %r13,10 # initialize retries +.L012: + ssch .Lorb # start I/O + jz .Ltpi # ok? + bras %r14,.Ldisab # error +.Ltpi: + lpsw .Lwaitpsw # load wait-PSW +.Lionewaddr: + c %r1,0xb8 # compare to ipl subhchannel + jnz .Ltpi # not equal: loop + clc 0xbc(4),.Lorb # cross check the intparm + jnz .Ltpi # not equal: loop + tsch .Lirb # get status + tm .Lirb+9,0xff # channel status ? + jz .L003 # CS == 0x00 + bras %r14,.Ldisab # error +.L003: + tm .Lirb+8,0xf3 # DS different from CE/DE + jz .L004 # ok ? + bct %r13,.L012 # retries <= 5 ? + bras %r14,.Ldisab # error +.L004: + tm .Lirb+8,0x04 # DE set? + jz .Ltpi # DE not set, loop +.Lsschend: + br %r15 # return to caller +# end of function +# In case of error goto disabled wait with %r14 containing the caller +.Ldisab: + st %r14,.Ldisabpsw+4 + lpsw .Ldisabpsw + +# FIXME pre-initialized data should be listed first +# NULLed storage can be taken from anywhere ;) +.Lblklst: + .long 0x00002000 + .align 8 +.Ldisabpsw: + .long 0x000a0000,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x00000000+.Ltpi +.Lorb: + .long 0x0049504c,0x0080ff00 # intparm is " IPL" +.Lc6: .long 0xff000000 +.Lstart: + .long 0x00010000 # do not separate .Lstart and .Lparm +.Lparm: + .long 0x00008000 # they are loaded with a LM +.L3390: + .word 0x3390 +.L9343: + .word 0x9343 +.Lnull: + .long 0x00000000,0x00000000 +.Lrdcdata: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lirb: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lcountarea: + .word 0x0000 # cyl; + .word 0x0000 # head; + .byte 0x00 # record; + .byte 0x00 # key length; + .word 0x0000 # data length == blocksize; +.Ldedata: + .long 0x40c00000,0x00000000 + .long 0x00000000,0x00000000 +.Llodata: + .long 0x06000001,0x00000000 + .long 0x00000000,0x01000000 + .long 0x12345678 + .org 0x7c8 +.Lrdcccw: # CCW read device characteristics + .long 0x64400040,0x00000000+.Lrdcdata + .long 0x63400010,0x00000000+.Ldedata + .long 0x47400010,0x00000000+.Llodata + .long 0x12000008,0x00000000+.Lcountarea +.Ldeccw: + .long 0x63400010,0x00000000+.Ldedata +.Lloccw: + .long 0x47400010,0x00000000+.Llodata +.Lrdccw: + .long 0x86400000,0x00000000 + .org 0x800 +# end of pre initialized data is here CCWarea follows +# from here we load 1k blocklist +# end of function + diff --git a/arch/s390/boot/iplfba.S b/arch/s390/boot/iplfba.S new file mode 100644 index 000000000..732a9848c --- /dev/null +++ b/arch/s390/boot/iplfba.S @@ -0,0 +1,131 @@ +# +# Ipl block for fba devices +# Copyright (C) 1998 IBM Corporation +# Author(s): Martin Schwidefsky +# +# startup for ipl at address 0 +# start with restart + +# The first 24 byes are loaded by ipl to addresses 0-23 (a PSW and two CCWs). +# The CCWs on 8-23 are used as a continuation of the implicit ipl channel +# program. The fba ipl loader only uses the CCW on 8-15 to load the first 512 +# byte block to location 0-511 (the reading starts again at block 0, byte 0). +# The second CCW is used to store the location of the load list. + .org 0 + .long 0x00080000,0x80000000+_start # The first 24 byte are loaded + .long 0x02000000,0x20000200 # by ipl to addresses 0-23. + .long 0x00000001,0x00000001 # (PSW, one CCW & loadlist info). + + .globl _start +_start: + basr %r13,0 +.LPG0: + l %r1,0xb8 # load ipl subchannel number + lhi %r2,0x200 # location for the loadlist + lm %r3,%r4,0x10 # blocknr and length of loadlist + bras %r14,.Lloader # load loadlist + + lhi %r11,0x400 + lhi %r12,0x200 # load address of loadlist + l %r3,0(%r12) # get first block number + l %r4,4(%r12) # get first block count + la %r12,8(%r12) + j .Llistloop + .org 0x50 +.Llistloop: + lr %r2,%r11 # load address + lr %r5,%r4 # block count + mhi %r5,512 + la %r11,0(%r5,%r11) # update load address + bras %r14,.Lloader # load chunk of the image + l %r3,0(%r12) # get next block number + icm %r4,15,4(%r12) # get next block count + la %r12,8(%r12) + jnz .Llistloop + +# +# everything loaded, go for it +# + l %r1,.Lstart-.LPG0(%r13) + br %r1 + +# +# subroutine for loading a sequence of block from fba +# %r2: load address (24 bit address) +# %r3: number of first block (unsigned long) +# %r4: number of blocks to load (unsigned short) +# + .org 0xC0 +.Lloader: + la %r5,.Llo-.LPG0(%r13) + sth %r4,2(%r5) # initialize block count + st %r3,4(%r5) # initialize block number + la %r5,.Lccws-.LPG0(%r13) + mhi %r4,512 + sth %r4,22(%r5) # initialize byte count + icm %r2,8,16(%r5) + st %r2,16(%r5) # initialize CCW data address + + slr %r2,%r2 + la %r3,.Lorb-.LPG0(%r13) # r2 = address of orb into r2 + la %r4,.Ltinfo-.LPG0(%r13) # r3 = address of tpi info block + la %r5,.Lirb-.LPG0(%r13) # r4 = address of irb + + lctl %c6,%c6,.Lc6-.LPG0(%r13) +.Lldlp: + ssch 0(%r3) # read blocks +.Ltpi: + tpi 0(%r4) # test pending interrupt + jz .Ltpi + c %r1,0(%r4) # compare subchannel number + jne .Ltpi + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Ldwpsw + tm 8(%r5),0x04 # got device end ? + jz .Ltpi +.Lexit: + br %r14 + + .align 8 +.Ldwpsw:.long 0x000a0000,0x00000000 +.Lorb: .long 0x00000000,0x0000ff00,.Lccws +.Ltinfo:.long 0 +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lc6: .long 0xff000000 +.Lloadp:.long 0,0 +.Lparm: .long 0x10400 +.Lstart:.long 0x10000 + .align 8 +.Lccws: .long 0x63000000+.Lde,0x60000010 # define extent + .long 0x43000000+.Llo,0x60000008 # locate +# offset 1 in read CCW: data address (24 bit) +# offset 6 in read CCW: number of bytes (16 bit) + .long 0x42000000,0x20000000 # read +.Lde: .long 0x40000200,0x00000000 + .long 0x00000000,0x00001000 +# offset 2 in .Llo: block count (unsigned short) +# offset 4 in .Llo: block number (unsigned long) +.Llo: .long 0x06000000,0x00000000 + + .org 0x200 + .long 0x00000002,0x0000007f + .long 0x00000081,0x0000007f + .long 0x00000100,0x0000007f + .long 0x0000017f,0x0000007f + .long 0x000001fe,0x0000007f + .long 0x0000027d,0x0000007f + .long 0x000002fc,0x0000007f + .long 0x0000037b,0x0000007f + .long 0x000003fa,0x0000007f + .long 0x00000479,0x0000007f + .long 0x000004f8,0x0000007f + .long 0x00000577,0x0000007f + .long 0x000005f6,0x0000007f + .long 0x00000675,0x0000007f + .long 0x000006f4,0x0000007f + .long 0x00000773,0x0000003f + .long 0x00000000,0x00000000 + .org 0x400 + diff --git a/arch/s390/config.in b/arch/s390/config.in new file mode 100644 index 000000000..5ffd230a0 --- /dev/null +++ b/arch/s390/config.in @@ -0,0 +1,75 @@ + +# For a description of the syntax of this configuration file, +# see the Configure script. +# + +define_bool CONFIG_UID16 y + +mainmenu_name "Linux Kernel Configuration" +define_bool CONFIG_ARCH_S390 y + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Processor type and features' +bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'IEEE FPU emulation' CONFIG_IEEEFPU_EMULATION +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'General setup' +bool 'Fast IRQ handling' CONFIG_FAST_IRQ +bool 'Builtin IPL record support' CONFIG_IPL +if [ "$CONFIG_IPL" = "y" ]; then + choice 'IPL method generated into head.S' \ + "tape CONFIG_IPL_TAPE \ + vm_reader CONFIG_IPL_VM" tape +fi +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF + +endmenu + +source drivers/s390/Config.in +bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS +if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then + int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 +fi + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +source fs/Config.in + +# source drivers/char/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Kernel profiling support' CONFIG_PROFILE +if [ "$CONFIG_PROFILE" = "y" ]; then + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 +fi +if [ "$CONFIG_CTC" = "y" ]; then + bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG +fi +# this does not work. bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +endmenu + diff --git a/arch/s390/defconfig b/arch/s390/defconfig new file mode 100644 index 000000000..aa0440919 --- /dev/null +++ b/arch/s390/defconfig @@ -0,0 +1,178 @@ +# +# Automatically generated by make menuconfig: don't edit +# +CONFIG_UID16=y +CONFIG_ARCH_S390=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Processor type and features +# +CONFIG_SMP=y +CONFIG_IEEEFPU_EMULATION=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# General setup +# +CONFIG_FAST_IRQ=y +CONFIG_IPL=y +# CONFIG_IPL_TAPE is not set +CONFIG_IPL_VM=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_BINFMT_ELF=y + +# +# S/390 block device drivers +# +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_MDISK is not set +CONFIG_DASD=y +CONFIG_DASD_ECKD=y +# CONFIG_DASD_MDSK is not set + +# +# S/390 Network device support +# +# CONFIG_CHANDEV is not set +CONFIG_NETDEVICES=y +CONFIG_CTC=y +CONFIG_IUCV=y +# CONFIG_DUMMY is not set +CONFIG_NET_ETHERNET=y +CONFIG_TR=y +# CONFIG_FDDI is not set + +# +# S/390 Terminal and Console options +# +CONFIG_3215=y +CONFIG_3215_CONSOLE=y +CONFIG_HWC=y +CONFIG_HWC_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_ALIAS is not set +# CONFIG_SYN_COOKIES is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_IBM_PARTITION=y +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_NLS is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set +# CONFIG_REMOTE_DEBUG is not set diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile new file mode 100644 index 000000000..f04b1cd74 --- /dev/null +++ b/arch/s390/kernel/Makefile @@ -0,0 +1,54 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +all: kernel.o head.o init_task.o + +O_TARGET := kernel.o +O_OBJS := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ + setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ + semaphore.o s390fpu.o s390io.o s390mach.o s390dyn.o reipl.o +OX_OBJS := s390_ksyms.o +MX_OBJS := + +ifdef CONFIG_SMP +O_OBJS += smp.o +endif + +ifdef CONFIG_PCI +O_OBJS += bios32.o +endif + +ifdef CONFIG_MCA +O_OBJS += mca.o +endif + +ifeq ($(CONFIG_MTRR),y) +OX_OBJS += mtrr.o +else + ifeq ($(CONFIG_MTRR),m) + MX_OBJS += mtrr.o + endif +endif + +ifeq ($(CONFIG_IEEEFPU_EMULATION),y) + O_OBJS += mathemu.o floatlib.o +endif + +# +# Kernel debugging +# +ifdef CONFIG_REMOTE_DEBUG +O_OBJS += gdb-stub.o #gdb-low.o +endif + +include $(TOPDIR)/Rules.make + diff --git a/arch/s390/kernel/bitmap.S b/arch/s390/kernel/bitmap.S new file mode 100644 index 000000000..a212ba450 --- /dev/null +++ b/arch/s390/kernel/bitmap.S @@ -0,0 +1,37 @@ +/* + * arch/s390/kernel/bitmap.S + * Bitmaps for set_bit, clear_bit, test_and_set_bit, ... + * See include/asm-s390/{bitops.h|posix_types.h} for details + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + + .globl _oi_bitmap +_oi_bitmap: + .byte 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 + + .globl _ni_bitmap +_ni_bitmap: + .byte 0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F + + .globl _zb_findmap +_zb_findmap: + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 + diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c new file mode 100644 index 000000000..818958b4f --- /dev/null +++ b/arch/s390/kernel/cpcmd.c @@ -0,0 +1,45 @@ +/* + * arch/s390/kernel/cpcmd.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <asm/string.h> +#include <asm/ebcdic.h> + +void cpcmd(char *cmd, char *response, int rlen) +{ + const int mask = 0x40000000L; + char obuffer[128]; + int olen; + + olen = strlen(cmd); + strcpy(obuffer, cmd); + ASCEBC(obuffer,olen); + + if (response != NULL && rlen > 0) { + asm volatile ("LRA 2,0(0,%0)\n\t" + "LR 4,%1\n\t" + "O 4,%4\n\t" + "LRA 3,0(0,%2)\n\t" + "LR 5,%3\n\t" + ".long 0x83240008 # Diagnose 83\n\t" + : /* no output */ + : "a" (obuffer), "d" (olen), + "a" (response), "d" (rlen), "m" (mask) + : "2", "3", "4", "5" ); + EBCASC(response, rlen); + } else { + asm volatile ("LRA 2,0(0,%0)\n\t" + "LR 3,%1\n\t" + ".long 0x83230008 # Diagnose 83\n\t" + : /* no output */ + : "a" (obuffer), "d" (olen) + : "2", "3" ); + } +} + diff --git a/arch/s390/kernel/cpcmd.h b/arch/s390/kernel/cpcmd.h new file mode 100644 index 000000000..38b88d9ea --- /dev/null +++ b/arch/s390/kernel/cpcmd.h @@ -0,0 +1,14 @@ +/* + * arch/s390/kernel/cpcmd.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#ifndef __CPCMD__ +#define __CPCMD__ + +extern void cpcmd(char *cmd, char *response, int rlen); + +#endif diff --git a/arch/s390/kernel/ebcdic.c b/arch/s390/kernel/ebcdic.c new file mode 100644 index 000000000..3c70c4aa5 --- /dev/null +++ b/arch/s390/kernel/ebcdic.c @@ -0,0 +1,242 @@ +/* + * arch/s390/kernel/ebcdic.c + * ECBDIC -> ASCII, ASCII -> ECBDIC, + * upper to lower case (EBCDIC) conversion tables. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> + * Martin Peschke <peschke@fh-brandenburg.de> + */ + +#include <asm/types.h> + +/* + * ASCII (IBM PC 437) -> EBCDIC 037 + */ +__u8 _ascebc[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +/* + * EBCDIC 037 -> ASCII (IBM PC 437) + */ +__u8 _ebcasc[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + + +/* + * EBCDIC 037 conversion table: + * from upper to lower case + */ +__u8 _ebc_tolower[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9C, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, 0x8C, 0x8D, 0x8E, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xEA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xDB, 0xDC, 0xDD, 0xDE, 0xFF +}; + + +/* + * EBCDIC 037 conversion table: + * from lower to upper case + */ +__u8 _ebc_toupper[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0x8A, 0x8B, 0xAC, 0xAD, 0xAE, 0x8F, + 0x90, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0x9A, 0x9B, 0x9E, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S new file mode 100644 index 000000000..9acccfd97 --- /dev/null +++ b/arch/s390/kernel/entry.S @@ -0,0 +1,917 @@ +/* + * arch/s390/kernel/entry.S + * S390 low-level entry points. + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Hartmut Penner (hp@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <linux/config.h> +#include <asm/lowcore.h> +#include <asm/errno.h> +#define ASSEMBLY +#include <asm/smp.h> +#include <asm/s390-regs-common.h> + + +/* + * stack layout for the system_call stack entry + * Martin please don't modify these back to hard coded values + * You know how bad I'm at mental arithmetic DJB & it gives + * me grief when I modify the pt_regs + */ +SP_PTREGS = STACK_FRAME_OVERHEAD +SP_PSW = SP_PTREGS +SP_R0 = (SP_PSW+PSW_MASK_SIZE+PSW_ADDR_SIZE) +SP_R1 = (SP_R0+GPR_SIZE) +SP_R2 = (SP_R1+GPR_SIZE) +SP_R3 = (SP_R2+GPR_SIZE) +SP_R4 = (SP_R3+GPR_SIZE) +SP_R5 = (SP_R4+GPR_SIZE) +SP_R6 = (SP_R5+GPR_SIZE) +SP_R7 = (SP_R6+GPR_SIZE) +SP_R8 = (SP_R7+GPR_SIZE) +SP_R9 = (SP_R8+GPR_SIZE) +SP_RA = (SP_R9+GPR_SIZE) +SP_RB = (SP_RA+GPR_SIZE) +SP_RC = (SP_RB+GPR_SIZE) +SP_RD = (SP_RC+GPR_SIZE) +SP_RE = (SP_RD+GPR_SIZE) +SP_RF = (SP_RE+GPR_SIZE) +SP_AREGS = (SP_RF+GPR_SIZE) +SP_ORIG_R2 = (SP_AREGS+(NUM_ACRS*ACR_SIZE)) +SP_TRAP = (SP_ORIG_R2+GPR_SIZE) +#if CONFIG_REMOTE_DEBUG +SP_CRREGS = (SP_TRAP+4) +/* fpu registers are saved & restored by the gdb stub itself */ +SP_FPC = (SP_CRREGS+(NUM_CRS*CR_SIZE)) +SP_FPRS = (SP_FPC+FPC_SIZE+FPC_PAD_SIZE) +/* SP_PGM_OLD_ILC etc are not part of pt_regs & they are not + defined in ptrace.h but space is needed for this too */ +SP_PGM_OLD_ILC= (SP_FPRS+(NUM_FPRS*FPR_SIZE)) +#else +SP_PGM_OLD_ILC= (SP_TRAP+4) +#endif +SP_SVC_STEP = (SP_PGM_OLD_ILC+4) +SP_SIZE = (SP_SVC_STEP+4) +/* + * these defines are offsets into the thread_struct + */ +_TSS_PTREGS = 0 +_TSS_FPRS = (_TSS_PTREGS+8) +_TSS_AR2 = (_TSS_FPRS+136) +_TSS_AR4 = (_TSS_AR2+4) +_TSS_KSP = (_TSS_AR4+4) +_TSS_USERSEG = (_TSS_KSP+4) +_TSS_ERROR = (_TSS_USERSEG+4) +_TSS_PROT = (_TSS_ERROR+4) +_TSS_TRAP = (_TSS_PROT+4) +_TSS_MM = (_TSS_TRAP+4) +_TSS_PER = (_TSS_MM+8) + +/* + * these are offsets into the task-struct. + */ +state = 0 +flags = 4 +sigpending = 8 +need_resched = 24 +processor = 60 + +/* PSW related defines */ +disable = 0xFC +enable = 0x03 +daton = 0x04 + + +#if 0 +/* some code left lying around in case we need a + * printk for debugging purposes + */ + sysc_printk: .long printk + sysc_msg: .string "<2>r15 %X\n" + .align 4 + +# basr %r13,0 + l %r0,SP_PSW+4(%r15) + sll %r0,1 + chi %r0,0 + jnz sysc_dn + l %r9,sysc_printk-sysc_lit(%r13) + la %r2,sysc_msg-sysc_lit(%r13) + lr %r3,%r15 + basr %r14,%r9 +sysc_dn: +#endif + +/* + * Register usage in interrupt handlers: + * R9 - pointer to current task structure + * R13 - pointer to literal pool + * R14 - return register for function calls + * R15 - kernel stack pointer + */ + +#define SAVE_ALL(psworg) \ + st %r15,__LC_SAVE_AREA ; \ + tm psworg+1,0x01 ; /* test problem state bit */ \ + jz 0f ; /* skip stack setup save */ \ + l %r15,__LC_KERNEL_STACK ; /* problem state -> load ksp */ \ +0: ahi %r15,-SP_SIZE ; /* make room for registers & psw */ \ + srl %r15,3 ; \ + sll %r15,3 ; /* align stack pointer to 8 */ \ + stm %r0,%r14,SP_R0(%r15) ; /* store gprs 0-14 to kernel stack */ \ + st %r2,SP_ORIG_R2(%r15) ; /* store original content of gpr 2 */ \ + mvc SP_RF(4,%r15),__LC_SAVE_AREA ; /* move R15 to stack */ \ + stam %a0,%a15,SP_AREGS(%r15) ; /* store access registers to kst. */ \ + mvc SP_PSW(8,%r15),psworg ; /* move user PSW to stack */ \ + lhi %r0,psworg ; /* store trap indication */ \ + st %r0,SP_TRAP(%r15) ; \ + xc 0(4,%r15),0(%r15) ; /* clear back chain */ \ + tm psworg+1,0x01 ; /* kmod.c .wishes the set_fs & gs */ \ + jz 1f ; /* to work across syscalls */ \ + slr %r0,%r0 ; \ + sar %a2,%r0 ; /* set ac.reg. 2 to primary space */ \ + lhi %r0,1 ; \ + sar %a4,%r0 ; /* set access reg. 4 to home space */ \ +1: + +#define RESTORE_ALL \ + mvc __LC_RETURN_PSW(8,0),SP_PSW(%r15) ; /* move user PSW to lowcore */ \ + lam %a0,%a15,SP_AREGS(%r15) ; /* load the access registers */ \ + lm %r0,%r15,SP_R0(%r15) ; /* load gprs 0-15 of user */ \ + ni __LC_RETURN_PSW+1(0),0xfd ; /* clear wait state bit */ \ + lpsw __LC_RETURN_PSW /* back to caller */ + +#define GET_CURRENT /* load pointer to task_struct to R9 */ \ + lhi %r9,-8192 ; \ + nr %r9,15 + + +/* + * Scheduler resume function, called by switch_to + * grp2 = (thread_struct *) prev->tss + * grp3 = (thread_struct *) next->tss + * Returns: + * gpr2 = prev + */ + .globl resume +resume: + l %r4,_TSS_PTREGS(%r3) + tm SP_PSW-SP_PTREGS(%r4),0x40 # is the new process using per ? + jz RES_DN1 # if not we're fine + stctl %r9,%r11,24(%r15) # We are using per stuff + clc _TSS_PER(12,%r3),24(%r15) + je RES_DN1 # we got away without bashing TLB's + lctl %c9,%c11,_TSS_PER(%r3) # Nope we didn't +RES_DN1: + stm %r6,%r15,24(%r15) # store resume registers of prev task + st %r15,_TSS_KSP(%r2) # store kernel stack ptr to prev->tss.ksp + lhi %r0,-8192 + nr %r0,%r15 + l %r15,_TSS_KSP(%r3) # load kernel stack ptr from next->tss.ksp + lhi %r1,8191 + or %r1,%r15 + ahi %r1,1 + st %r1,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack + stam %a2,%a2,_TSS_AR2(%r2) # store kernel access reg. 2 + stam %a4,%a4,_TSS_AR4(%r2) # store kernel access reg. 4 + lam %a2,%a2,_TSS_AR2(%r3) # load kernel access reg. 2 + lam %a4,%a4,_TSS_AR4(%r3) # load kernel access reg. 4 + lr %r2,%r0 # return task_struct of last task + lm %r6,%r15,24(%r15) # load resume registers of next task + br %r14 + +/* + * SVC interrupt handler routine. System calls are synchronous events and + * are executed with interrupts enabled. + */ + +sysc_lit: + sysc_softirq_state: .long softirq_state + sysc_do_signal: .long do_signal + sysc_do_softirq: .long do_softirq + sysc_schedule: .long schedule + sysc_trace: .long syscall_trace +#ifdef CONFIG_SMP + sysc_schedtail: .long schedule_tail +#endif + sysc_clone: .long sys_clone + sysc_fork: .long sys_fork + sysc_vfork: .long sys_vfork + sysc_sigreturn: .long sys_sigreturn + sysc_rt_sigreturn: .long sys_rt_sigreturn + sysc_execve: .long sys_execve + sysc_sigsuspend: .long sys_sigsuspend + sysc_rt_sigsuspend: .long sys_rt_sigsuspend + + .globl system_call +system_call: + SAVE_ALL(0x20) + XC SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) +pgm_system_call: + basr %r13,0 + ahi %r13,sysc_lit-. # setup base pointer R13 to sysc_lit + slr %r8,%r8 # gpr 8 is call save (-> tracesys) + ic %r8,0x8B # get svc number from lowcore + stosm 24(%r15),0x03 # reenable interrupts + GET_CURRENT # load pointer to task_struct to R9 + sll %r8,2 + l %r8,sys_call_table-sysc_lit(8,%r13) # get address of system call + tm flags+3(%r9),0x20 # PF_TRACESYS + jnz sysc_tracesys + basr %r14,%r8 # call sys_xxxx + st %r2,SP_R2(%r15) # store return value (change R2 on stack) + # ATTENTION: check sys_execve_glue before + # changing anything here !! + +sysc_return: + GET_CURRENT # load pointer to task_struct to R9 + tm SP_PSW+1(%r15),0x01 # returning to user ? + jno sysc_leave # no-> skip bottom half, resched & signal +# +# check, if bottom-half has to be done +# +#ifdef CONFIG_SMP + l %r1,processor(%r9) # get processor index + sll %r1,5 + al %r1,sysc_softirq_state-sysc_lit(%r13) + l %r0,0(%r1) # get softirq_state[cpu].active + n %r0,4(%r1) # and it with softirq_state[cpu].mask +#else + l %r1,sysc_softirq_state-sysc_lit(%r13) + l %r0,0(%r1) # get softirq_state.active + n %r0,4(%r1) # and it with softirq_state.mask +#endif + jnz sysc_handle_bottom_half +# +# check, if reschedule is needed +# +sysc_return_bh: + icm %r0,15,need_resched(%r9) # get need_resched from task_struct + jnz sysc_reschedule + icm %r0,15,sigpending(%r9) # get sigpending from task_struct + jnz sysc_signal_return +sysc_leave: + icm %r0,15,SP_SVC_STEP(%r15) # get sigpending from task_struct + jnz pgm_svcret + stnsm 24(%r15),disable # disable I/O and ext. interrupts + RESTORE_ALL + +# +# call do_signal before return +# +sysc_signal_return: + la %r2,SP_PTREGS(%r15) # load pt_regs + sr %r3,%r3 # clear *oldset + l %r1,sysc_do_signal-sysc_lit(%r13) + la %r14,sysc_leave-sysc_lit(%r13) + br %r1 # return point is sysc_leave + +# +# call trace before and after sys_call +# +sysc_tracesys: + l %r1,sysc_trace-sysc_lit(%r13) + lhi %r2,-ENOSYS + st %r2,SP_R2(%r15) # give sysc_trace an -ENOSYS retval + basr %r14,%r1 + lm %r3,%r6,SP_R3(%r15) + l %r2,SP_ORIG_R2(%r15) + basr %r14,%r8 # call sys_xxx + st %r2,SP_R2(%r15) # store return value + l %r1,sysc_trace-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) + br %r1 # return point is sysc_return + + +# +# call do_softirq and return from syscall, if interrupt-level +# is zero +# +sysc_handle_bottom_half: + l %r1,sysc_do_softirq-sysc_lit(%r13) + la %r14,sysc_return_bh-sysc_lit(%r13) + br %r1 # call do_softirq + +# +# call schedule with sysc_return as return-address +# +sysc_reschedule: + l %r1,sysc_schedule-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) + br %r1 # call scheduler, return to sysc_return + +# +# a new process exits the kernel with ret_from_fork +# + .globl ret_from_fork +ret_from_fork: + basr %r13,0 + ahi %r13,sysc_lit-. # setup base pointer R13 to $SYSCDAT + GET_CURRENT # load pointer to task_struct to R9 + stosm 24(%r15),0x03 # reenable interrupts + sr %r0,%r0 # child returns 0 + st %r0,SP_R2(%r15) # store return value (change R2 on stack) +#ifdef CONFIG_SMP + l %r1,sysc_schedtail-sysc_lit(%r13) + la %r14,sysc_return-sysc_lit(%r13) + br %r1 # call schedule_tail, return to sysc_return +#else + j sysc_return +#endif + +# +# clone, fork, vfork, exec and sigreturn need glue, +# because they all expect pt_regs as parameter, +# but are called with different parameter. +# return-address is set up above +# +sys_clone_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_clone-sysc_lit(%r13) + br %r1 # branch to sys_clone + +sys_fork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_fork-sysc_lit(%r13) + br %r1 # branch to sys_fork + +sys_vfork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_vfork-sysc_lit(%r13) + br %r1 # branch to sys_vfork + +sys_execve_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,sysc_execve-sysc_lit(%r13) + lr %r12,%r14 # save return address + basr %r14,%r1 # call sys_execve + ltr %r2,%r2 # check if execve failed + bnz 0(%r12) # it did fail -> store result in gpr2 + b 4(%r12) # SKIP ST 2,SP_R2(15) after BASR 14,8 + # in system_call/sysc_tracesys + +sys_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + l %r1,sysc_sigreturn-sysc_lit(%r13) + br %r1 # branch to sys_sigreturn + +sys_rt_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + l %r1,sysc_rt_sigreturn-sysc_lit(%r13) + br %r1 # branch to sys_sigreturn + +# +# sigsuspend and rt_sigsuspend need pt_regs as an additional +# parameter and they have to skip the store of %r2 into the +# user register %r2 because the return value was set in +# sigsuspend and rt_sigsuspend already and must not be overwritten! +# + +sys_sigsuspend_glue: + lr %r5,%r4 # move mask back + lr %r4,%r3 # move history1 parameter + lr %r3,%r2 # move history0 parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + l %r1,sysc_sigsuspend-sysc_lit(%r13) + la %r14,4(%r14) # skip store of return value + br %r1 # branch to sys_sigsuspend + +sys_rt_sigsuspend_glue: + lr %r4,%r3 # move sigsetsize parameter + lr %r3,%r2 # move unewset parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + l %r1,sysc_rt_sigsuspend-sysc_lit(%r13) + la %r14,4(%r14) # skip store of return value + br %r1 # branch to sys_rt_sigsuspend + + .globl sys_call_table +sys_call_table: + .long sys_ni_syscall /* 0 */ + .long sys_exit + .long sys_fork_glue + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_ni_syscall /* old waitpid syscall holder */ + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve_glue + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_ni_syscall /* old stat syscall holder */ + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_ni_syscall /* old fstat syscall holder */ + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old uname syscall holder */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_ni_syscall /* old sgetmask syscall holder */ + .long sys_ni_syscall /* old ssetmask syscall holder */ + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend_glue + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long sys_ni_syscall /* old select syscall holder */ + .long sys_symlink + .long sys_ni_syscall /* old lstat syscall holder */ + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long sys_ni_syscall /* old readdir syscall holder */ + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ioperm + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* old uname syscall holder */ + .long sys_ni_syscall /* 110 */ /* iopl for i386 */ + .long sys_vhangup + .long sys_ni_syscall /* old "idle" system call */ + .long sys_ni_syscall /* vm86old for i386 */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn_glue + .long sys_clone_glue /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* modify_ldt for i386 */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_create_module + .long sys_init_module + .long sys_delete_module + .long sys_get_kernel_syms /* 130 */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* for vm86 */ + .long sys_query_module + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn_glue + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend_glue + .long sys_pread /* 180 */ + .long sys_pwrite + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork_glue /* 190 */ + .long sys_getrlimit + .long sys_ni_syscall /* FIXME: problem with sys_mmap2: 6 parms */ + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .rept 255-219 + .long sys_ni_syscall + .endr + +/* + * Program check handler routine + */ + +pgm_lit: + pgm_handle_per: .long handle_per_exception + pgm_jump_table: .long pgm_check_table + pgm_sysc_ret: .long sysc_return + pgm_sysc_lit: .long sysc_lit + pgm_do_signal: .long do_signal + + .globl pgm_check_handler +pgm_check_handler: +/* + * First we need to check for a special case: + * Single stepping an instruction that disables the PER event mask will + * cause a PER event AFTER the mask has been set. Example: SVC or LPSW. + * For a single stepped SVC the program check handler gets control after + * the SVC new PSW has been loaded. But we want to execute the SVC first and + * then handle the PER event. Therefore we update the SVC old PSW to point + * to the pgm_check_handler and branch to the SVC handler after we checked + * if we have to load the kernel stack register. + * For every other possible cause for PER event without the PER mask set + * we just ignore the PER event (FIXME: is there anything we have to do + * for LPSW?). + */ + tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception + jz pgm_sv # skip if not + tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on + jnz pgm_sv # skip if it is +# ok its one of the special cases, now we need to find out which one + clc __LC_PGM_OLD_PSW(8),__LC_SVC_NEW_PSW + je pgm_svcper +# no interesting special case, ignore PER event + lpsw 0x28 +# it was a single stepped SVC that is causing all the trouble +pgm_svcper: + SAVE_ALL(0x20) + mvi SP_SVC_STEP(%r15),1 # make SP_SVC_STEP nonzero + mvc SP_PGM_OLD_ILC(4,%r15),__LC_PGM_ILC # save program check information + j pgm_system_call # now do the svc +pgm_svcret: + lh %r7,SP_PGM_OLD_ILC(%r15) # get ilc from stack + lhi %r0,0x28 + st %r0,SP_TRAP(%r15) # set new trap indicator + xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) + basr %r13,0 + ahi %r13,pgm_lit-. # setup base pointer + j pgm_no_sv +pgm_sv: + SAVE_ALL(0x28) + XC SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) + basr %r13,0 + ahi %r13,pgm_lit-. # setup base pointer R13 to $PGMDAT + lh %r7,__LC_PGM_ILC # load instruction length +pgm_no_sv: + lh %r8,__LC_PGM_INT_CODE # N.B. saved int code used later KEEP it + stosm 24(%r15),0x03 # reenable interrupts + lr %r3,%r8 + lhi %r0,0x7f + nr %r3,%r0 # clear per-event-bit + je pgm_dn # none of Martins exceptions occured bypass + l %r9,pgm_jump_table-pgm_lit(%r13) + sll %r3,2 + l %r9,0(%r3,%r9) # load address of handler routine + la %r2,SP_PTREGS(%r15) # address of register-save area + srl %r3,2 + chi %r3,0x4 # protection-exception ? + jne pgm_go # if not, + l %r5,SP_PSW+4(15) # load psw addr + sr %r5,%r7 # substract ilc from psw + st %r5,SP_PSW+4(15) # store corrected psw addr +pgm_go: basr %r14,%r9 # branch to interrupt-handler +pgm_dn: lhi %r0,0x80 + nr %r8,%r0 # check for per exception + je pgm_return + la %r2,SP_PTREGS(15) # address of register-save area + l %r9,pgm_handle_per-pgm_lit(%r13) # load adr. of per handler + l %r14,pgm_sysc_ret-pgm_lit(%r13) # load adr. of system return + l %r13,pgm_sysc_lit-pgm_lit(%r13) + br %r9 # branch to handle_per_exception +# +# the backend code is the same as for sys-call +# +pgm_return: + l %r14,pgm_sysc_ret-pgm_lit(%r13) + l %r13,pgm_sysc_lit-pgm_lit(%r13) + br %r14 + +/* + * IO interrupt handler routine + */ + +io_lit: + io_do_IRQ: .long do_IRQ + io_schedule: .long schedule + io_do_signal: .long do_signal + io_softirq_state: .long softirq_state + io_do_softirq: .long do_softirq + + .globl io_int_handler +io_int_handler: + SAVE_ALL(0x38) + basr %r13,0 + ahi %r13,io_lit-. # setup base pointer R13 to $IODAT + la %r2,SP_PTREGS(%r15) # address of register-save area + sr %r3,%r3 + icm %r3,%r3,__LC_SUBCHANNEL_NR # load subchannel nr & extend to int + l %r4,__LC_IO_INT_PARM # load interuption parm + l %r9,io_do_IRQ-io_lit(%r13) # load address of do_IRQ + basr %r14,%r9 # branch to standard irq handler + +io_return: + GET_CURRENT # load pointer to task_struct to R9 + tm SP_PSW+1(%r15),0x01 # returning to user ? + jz io_leave # no-> skip resched & signal + stosm 24(%r15),0x03 # reenable interrupts +# +# check, if bottom-half has to be done +# +#ifdef CONFIG_SMP + l %r1,processor(%r9) # get processor index + sll %r1,5 + al %r1,io_softirq_state-io_lit(%r13) + l %r0,0(%r1) # get softirq_state[cpu].active + n %r0,4(%r1) # and it with softirq_state[cpu].mask +#else + l %r1,io_softirq_state-io_lit(%r13) + l %r0,0(%r1) # get softirq_state.active + n %r0,4(%r1) # and it with softirq_state.mask +#endif + jnz io_handle_bottom_half +io_return_bh: +# +# check, if reschedule is needed +# + icm %r0,15,need_resched(%r9) # get need_resched from task_struct + jnz io_reschedule + icm %r0,15,sigpending(%r9) # get sigpending from task_struct + jnz io_signal_return +io_leave: + stnsm 24(%r15),disable # disable I/O and ext. interrupts + RESTORE_ALL + +# +# call do_softirq and return from syscall, if interrupt-level +# is zero +# +io_handle_bottom_half: + l %r1,io_do_softirq-io_lit(%r13) + la %r14,io_return_bh-io_lit(%r13) + br %r1 # call do_softirq + +# +# call schedule with io_return as return-address +# +io_reschedule: + l %r1,io_schedule-io_lit(%r13) + la %r14,io_return-io_lit(%r13) + br %r1 # call scheduler, return to io_return + +# +# call do_signal before return +# +io_signal_return: + la %r2,SP_PTREGS(%r15) # load pt_regs + sr %r3,%r3 # clear *oldset + l %r1,io_do_signal-io_lit(%r13) + la %r14,io_leave-io_lit(%r13) + br %r1 # return point is io_leave + +/* + * External interrupt handler routine + */ + +ext_lit: + ext_timer_int: .long do_timer_interrupt +#ifdef CONFIG_SMP + ext_call_int: .long do_ext_call_interrupt +#endif +#ifdef CONFIG_HWC + ext_hwc_int: .long do_hwc_interrupt +#endif +#ifdef CONFIG_MDISK + ext_mdisk_int: .long do_mdisk_interrupt +#endif +#ifdef CONFIG_IUCV + ext_iucv_int: .long do_iucv_interrupt +#endif + ext_io_lit: .long io_lit + ext_io_return: .long io_return + + .globl ext_int_handler +ext_int_handler: + SAVE_ALL(0x18) + basr %r13,0 + ahi %r13,ext_lit-. # setup base pointer R13 to $EXTDAT + la %r2,SP_PTREGS(%r15) # address of register-save area + lh %r3,__LC_EXT_INT_CODE # error code +#ifdef CONFIG_SMP + chi %r3,0x1202 # EXTERNAL_CALL + jne ext_no_extcall + l %r9,ext_call_int-ext_lit(%r13) # load ext_call_interrupt + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_extcall: +#endif + chi %r3,0x1004 # CPU_TIMER + jne ext_no_timer + l %r9,ext_timer_int-ext_lit(%r13) # load timer_interrupt + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_timer: +#ifdef CONFIG_HWC + chi %r3,0x2401 # HWC interrupt + jne ext_no_hwc + l %r9,ext_hwc_int-ext_lit(%r13) # load addr. of hwc routine + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_hwc: +#endif +#ifdef CONFIG_MDISK + chi %r3,0x2603 # diag 250 (VM) interrupt + jne ext_no_mdisk + l %r9,ext_mdisk_int-ext_lit(%r13) + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_mdisk: +#endif +#ifdef CONFIG_IUCV + chi %r3,0x4000 # diag 250 (VM) interrupt + jne ext_no_iucv + l %r9,ext_iucv_int-ext_lit(%r13) + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r9 # branch to ext call handler +ext_no_iucv: +#endif + + l %r14,ext_io_return-ext_lit(%r13) + l %r13,ext_io_lit-ext_lit(%r13) + br %r14 # use backend code of io_int_handler + +/* + * Machine check handler routines + */ +mcck_lit: + mcck_crw_pending: .long do_crw_pending + + + .globl mcck_int_handler +mcck_int_handler: + SAVE_ALL(0x30) + basr %r13,0 + ahi %r13,mcck_lit-. # setup base pointer R13 to $MCCKDAT + tm __LC_MCCK_CODE+1,0x40 + jno mcck_no_crw + l %r1,mcck_crw_pending-mcck_lit(%r13) + basr %r14,%r1 # call do_crw_pending +mcck_no_crw: +mcck_return: + RESTORE_ALL + +#ifdef CONFIG_SMP +/* + * Restart interruption handler, kick starter for additional CPUs + */ + .globl restart_int_handler +restart_int_handler: + l %r15,__LC_KERNEL_STACK # load ksp + lctl %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs + lam %a0,%a15,__LC_AREGS_SAVE_AREA + stosm 0(%r15),daton # now we can turn dat on + lm %r6,%r15,24(%r15) # load registers from clone + bras %r14,restart_go + .long start_secondary +restart_go: + l %r14,0(%r14) + br %r14 # branch to start_secondary +#else +/* + * If we do not run with SMP enabled, let the new CPU crash ... + */ + .globl restart_int_handler +restart_int_handler: + basr %r1,0 +restart_base: + lpsw restart_crash-restart_base(%r1) + .align 8 +restart_crash: + .long 0x000a0000,0x00000000 +restart_go: +#endif + diff --git a/arch/s390/kernel/floatlib.c b/arch/s390/kernel/floatlib.c new file mode 100644 index 000000000..3d1d05a53 --- /dev/null +++ b/arch/s390/kernel/floatlib.c @@ -0,0 +1,1021 @@ +/* +** libgcc support for software floating point. +** Copyright (C) 1991 by Pipeline Associates, Inc. All rights reserved. +** Permission is granted to do *anything* you want with this file, +** commercial or otherwise, provided this message remains intact. So there! +** I would appreciate receiving any updates/patches/changes that anyone +** makes, and am willing to be the repository for said changes (am I +** making a big mistake?). + +Warning! Only single-precision is actually implemented. This file +won't really be much use until double-precision is supported. + +However, once that is done, this file might eventually become a +replacement for libgcc1.c. It might also make possible +cross-compilation for an IEEE target machine from a non-IEEE +host such as a VAX. + +If you'd like to work on completing this, please talk to rms@gnu.ai.mit.edu. + +--> Double precision floating support added by James Carlson on 20 April 1998. + +** +** Pat Wood +** Pipeline Associates, Inc. +** pipeline!phw@motown.com or +** sun!pipeline!phw or +** uunet!motown!pipeline!phw +** +** 05/01/91 -- V1.0 -- first release to gcc mailing lists +** 05/04/91 -- V1.1 -- added float and double prototypes and return values +** -- fixed problems with adding and subtracting zero +** -- fixed rounding in truncdfsf2 +** -- fixed SWAP define and tested on 386 +*/ + +/* +** The following are routines that replace the libgcc soft floating point +** routines that are called automatically when -msoft-float is selected. +** The support single and double precision IEEE format, with provisions +** for byte-swapped machines (tested on 386). Some of the double-precision +** routines work at full precision, but most of the hard ones simply punt +** and call the single precision routines, producing a loss of accuracy. +** long long support is not assumed or included. +** Overall accuracy is close to IEEE (actually 68882) for single-precision +** arithmetic. I think there may still be a 1 in 1000 chance of a bit +** being rounded the wrong way during a multiply. I'm not fussy enough to +** bother with it, but if anyone is, knock yourself out. +** +** Efficiency has only been addressed where it was obvious that something +** would make a big difference. Anyone who wants to do this right for +** best speed should go in and rewrite in assembler. +** +** I have tested this only on a 68030 workstation and 386/ix integrated +** in with -msoft-float. +*/ + +#define float long +#define double long long + +/* the following deal with IEEE single-precision numbers */ +#define EXCESS 126 +#define SIGNBIT 0x80000000 +#define HIDDEN (1 << 23) +#define SIGN(fp) ((fp) & SIGNBIT) +#define EXP(fp) (((fp) >> 23) & 0xFF) +#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) +#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) + +/* the following deal with IEEE double-precision numbers */ +#define EXCESSD 1022 +#define HIDDEND (1 << 20) +#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) +#define SIGND(fp) ((fp.l.upper) & SIGNBIT) +#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) |(fp.l.lower >> 22)) +#define HIDDEND_LL ((long long)1 << 52) +#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) +#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) + +/* define SWAP for 386/960 reverse-byte-order brain-damaged CPUs */ +union double_long { + double d; +#ifdef SWAP + struct { + unsigned long lower; + long upper; + } l; +#else + struct { + long upper; + unsigned long lower; + } l; +#endif + long long ll; +}; + +union float_long + { + float f; + long l; + }; + +long long +__negdi2 (long long u) +{ + + union lll { + long long ll; + long s[2]; + }; + + union lll w,uu; + + uu.ll = u; + + w.s[1] = -uu.s[1]; + w.s[0] = -uu.s[0] - ((int) w.s[1] != 0); + + return w.ll; +} + + + +/* add two floats */ +float +__addsf3 (float a1, float a2) +{ + register long mant1, mant2; + register union float_long fl1, fl2; + register int exp1, exp2; + int sign = 0; + + fl1.f = a1; + fl2.f = a2; + + /* check for zero args */ + if (!fl1.l) { + fl1.f = fl2.f; + goto test_done; + } + if (!fl2.l) + goto test_done; + + exp1 = EXP (fl1.l); + exp2 = EXP (fl2.l); + + if (exp1 > exp2 + 25) + goto test_done; + if (exp2 > exp1 + 25) { + fl1.f = fl2.f; + goto test_done; + } + + /* do everything in excess precision so's we can round later */ + mant1 = MANT (fl1.l) << 6; + mant2 = MANT (fl2.l) << 6; + + if (SIGN (fl1.l)) + mant1 = -mant1; + if (SIGN (fl2.l)) + mant2 = -mant2; + + if (exp1 > exp2) + { + mant2 >>= exp1 - exp2; + } + else + { + mant1 >>= exp2 - exp1; + exp1 = exp2; + } + mant1 += mant2; + + if (mant1 < 0) + { + mant1 = -mant1; + sign = SIGNBIT; + } + else if (!mant1) { + fl1.f = 0; + goto test_done; + } + + /* normalize up */ + while (!(mant1 & 0xE0000000)) + { + mant1 <<= 1; + exp1--; + } + + /* normalize down? */ + if (mant1 & (1 << 30)) + { + mant1 >>= 1; + exp1++; + } + + /* round to even */ + mant1 += (mant1 & 0x40) ? 0x20 : 0x1F; + + /* normalize down? */ + if (mant1 & (1 << 30)) + { + mant1 >>= 1; + exp1++; + } + + /* lose extra precision */ + mant1 >>= 6; + + /* turn off hidden bit */ + mant1 &= ~HIDDEN; + + /* pack up and go home */ + fl1.l = PACK (sign, exp1, mant1); +test_done: + return (fl1.f); +} + +/* subtract two floats */ +float +__subsf3 (float a1, float a2) +{ + register union float_long fl1, fl2; + + fl1.f = a1; + fl2.f = a2; + + /* check for second arg zero */ + if (!fl2.l) + return (fl1.f); + /* twiddle sign bit */ + fl2.l ^= SIGNBIT; + /* check for first arg zero */ + if (!fl1.l) + return (fl2.f); + /* add values */ + return __addsf3 (a1, fl2.f); +} + +/* compare two floats */ +long +__cmpsf2 (float a1, float a2) +{ + register union float_long fl1, fl2; + + fl1.f = a1; + fl2.f = a2; + + if (SIGN (fl1.l) && SIGN (fl2.l)) + { + fl1.l ^= SIGNBIT; + fl2.l ^= SIGNBIT; + if (fl1.l < fl2.l) + return (-1); + if (fl1.l > fl2.l) + return (1); + return 0; + } else { + if (fl1.l < fl2.l) + return (-1); + if (fl1.l > fl2.l) + return (1); + return (0); + } +} + +/* multiply two floats */ +float +__mulsf3 (float a1, float a2) +{ + register union float_long fl1, fl2; + register unsigned long result; + register int exp; + int sign; + + fl1.f = a1; + fl2.f = a2; + + if (!fl1.l || !fl2.l) { + fl1.f = 0; + goto test_done; + } + + /* compute sign and exponent */ + sign = SIGN (fl1.l) ^ SIGN (fl2.l); + exp = EXP (fl1.l) - EXCESS; + exp += EXP (fl2.l); + + fl1.l = MANT (fl1.l); + fl2.l = MANT (fl2.l); + + /* the multiply is done as one 16x16 multiply and two 16x8 multiples */ + result = (fl1.l >> 8) * (fl2.l >> 8); + result += ((fl1.l & 0xFF) * (fl2.l >> 8)) >> 8; + result += ((fl2.l & 0xFF) * (fl1.l >> 8)) >> 8; + + result >>= 2; + if (result & 0x20000000) + { + /* round */ + result += 0x20; + result >>= 6; + } + else + { + /* round */ + result += 0x10; + result >>= 5; + exp--; + } + if (result & (HIDDEN<<1)) { + result >>= 1; + exp++; + } + + result &= ~HIDDEN; + + /* pack up and go home */ + fl1.l = PACK (sign, exp, result); +test_done: + return (fl1.f); +} + +/* divide two floats */ +float +__divsf3 (float a1, float a2) +{ + register union float_long fl1, fl2; + register int result; + register int mask; + register int exp, sign; + + fl1.f = a1; + fl2.f = a2; + + /* subtract exponents */ + exp = EXP (fl1.l) - EXP (fl2.l) + EXCESS; + + /* compute sign */ + sign = SIGN (fl1.l) ^ SIGN (fl2.l); + + /* divide by zero??? */ + if (!fl2.l) + /* return NaN or -NaN */ + return (sign ? 0xFFFFFFFF : 0x7FFFFFFF); + + /* numerator zero??? */ + if (!fl1.l) + return (0); + + /* now get mantissas */ + fl1.l = MANT (fl1.l); + fl2.l = MANT (fl2.l); + + /* this assures we have 25 bits of precision in the end */ + if (fl1.l < fl2.l) + { + fl1.l <<= 1; + exp--; + } + + /* now we perform repeated subtraction of fl2.l from fl1.l */ + mask = 0x1000000; + result = 0; + while (mask) + { + if (fl1.l >= fl2.l) + { + result |= mask; + fl1.l -= fl2.l; + } + fl1.l <<= 1; + mask >>= 1; + } + + /* round */ + result += 1; + + /* normalize down */ + exp++; + result >>= 1; + + result &= ~HIDDEN; + + /* pack up and go home */ + fl1.l = PACK (sign, exp, result); + return (fl1.f); +} + +/* convert double to float */ +float +__truncdfsf2 (double a1) +{ + register int exp; + register long mant; + register union float_long fl; + register union double_long dl1; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (float)(0); + + exp = EXPD (dl1) - EXCESSD + EXCESS; + + /* shift double mantissa 6 bits so we can round */ + mant = MANTD (dl1) >> 6; + + /* now round and shift down */ + mant += 1; + mant >>= 1; + + /* did the round overflow? */ + if (mant & 0xFF000000) + { + mant >>= 1; + exp++; + } + + mant &= ~HIDDEN; + + /* pack up and go home */ + fl.l = PACK (SIGND (dl1), exp, mant); + return (fl.f); +} + +/* convert int to double */ +double +__floatsidf (register long a1) +{ + register int sign = 0, exp = 31 + EXCESSD; + union double_long dl; + + if (a1 == 0x80000000) + { + /* + * -a1 would be 0 ! + */ + dl.l.upper = 0xc1e00000; + dl.l.lower = 0x0; + return (dl.d); + } + + if (!a1) + { + dl.l.upper = dl.l.lower = 0; + return (dl.d); + } + + if (a1 < 0) + { + sign = SIGNBIT; + a1 = -a1; + } + + while (a1 < 0x1000000) + { + a1 <<= 4; + exp -= 4; + } + + while (a1 < 0x40000000) + { + a1 <<= 1; + exp--; + } + + /* pack up and go home */ + dl.l.upper = sign; + dl.l.upper |= exp << 20; + dl.l.upper |= (a1 >> 10) & ~HIDDEND; + dl.l.lower = a1 << 22; + + return (dl.d); +} + +double +__floatdidf (register long long a1) +{ + register int exp = 63 + EXCESSD; + union double_long dl; + + dl.l.upper = dl.l.lower = 0; + if (a1 == 0) + return (dl.d); + + if (a1 < 0) { + dl.l.upper = SIGNBIT; + a1 = -a1; + } + + while (a1 < (long long)1<<54) { + a1 <<= 8; + exp -= 8; + } + while (a1 < (long long)1<<62) { + a1 <<= 1; + exp -= 1; + } + /* pack up and go home */ + dl.ll |= (a1 >> 10) & ~HIDDEND_LL; + dl.l.upper |= exp << 20; + + return (dl.d); +} + +float +__floatsisf (register long a1) +{ + return __truncdfsf2(__floatsidf(a1)); +} + +float +__floatdisf (register long long a1) +{ + return (float)__floatdidf(a1); +} +/* negate a float */ +float +__negsf2 (float a1) +{ + register union float_long fl1; + + fl1.f = a1; + if (!fl1.l) + return (0); + + fl1.l ^= SIGNBIT; + return (fl1.f); +} + +/* negate a double */ +double +__negdf2 (double a1) +{ + register union double_long dl1; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (dl1.d); + + dl1.l.upper ^= SIGNBIT; + return (dl1.d); +} + +/* convert float to double */ +double +__extendsfdf2 (float a1) +{ + register union float_long fl1; + register union double_long dl; + register int exp; + + fl1.f = a1; + + if (!fl1.l) + { + dl.l.upper = dl.l.lower = 0; + return (dl.d); + } + + dl.l.upper = SIGN (fl1.l); + exp = EXP (fl1.l) - EXCESS + EXCESSD; + dl.l.upper |= exp << 20; + dl.l.upper |= (MANT (fl1.l) & ~HIDDEN) >> 3; + dl.l.lower = MANT (fl1.l) << 29; + + return (dl.d); +} + + +/* compare two doubles */ +long +__cmpdf2 (double a1, double a2) +{ + register union double_long dl1, dl2; + + dl1.d = a1; + dl2.d = a2; + + if (SIGND (dl1) && SIGND (dl2)) + { + dl1.l.upper ^= SIGNBIT; + dl2.l.upper ^= SIGNBIT; + if (dl1.l.upper < dl2.l.upper) + return (1); + if (dl1.l.upper > dl2.l.upper) + return (-1); + if (dl1.l.lower < dl2.l.lower) + return (1); + if (dl1.l.lower > dl2.l.lower) + return (-1); + return (0); + } else { + if (dl1.l.upper < dl2.l.upper) + return (-1); + if (dl1.l.upper > dl2.l.upper) + return (1); + if (dl1.l.lower < dl2.l.lower) + return (-1); + if (dl1.l.lower > dl2.l.lower) + return (1); + return (0); + } +} + +/* convert double to int */ +long +__fixdfsi (double a1) +{ + register union double_long dl1; + register int exp; + register long l; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (0); + + exp = EXPD (dl1) - EXCESSD - 31; + l = MANTD (dl1); + + if (exp > 0) + return SIGND(dl1) ? (1<<31) : ((1ul<<31)-1); + + /* shift down until exp = 0 or l = 0 */ + if (exp <= 0 && exp > -32 && l) + l >>= -exp; + else + return (0); + + return (SIGND (dl1) ? -l : l); +} + +/* convert float to int */ +long +__fixsfsi (float a1) +{ + return __fixdfsi(__extendsfdf2(a1)); +} + +/* convert double to int */ +long long +__fixdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register long long l; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (0); + + exp = EXPD (dl1) - EXCESSD - 64; + l = MANTD_LL(dl1); + + if (exp > 0) { + l = (long long)1<<63; + if (!SIGND(dl1)) + l--; + return l; + } + + /* shift down until exp = 0 or l = 0 */ + if (exp <= 0 && exp > -64 && l) + l >>= -exp; + else + return (0); + + return (SIGND (dl1) ? -l : l); +} + +/* convert double to unsigned int */ +unsigned long +__fixunsdfsi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long l; + + dl1.d = a1; + + if (!dl1.l.upper && !dl1.l.lower) + return (0); + + exp = EXPD (dl1) - EXCESSD - 32; + l = (((((dl1.l.upper) & 0xFFFFF) | HIDDEND) << 11) | (dl1.l.lower >> 21)); + + if (exp > 0) + return (0xFFFFFFFFul); /* largest integer */ + + /* shift down until exp = 0 or l = 0 */ + if (exp < 0 && exp > -32 && l) + l >>= -exp; + else + return (0); + + return (l); +} + +/* convert double to unsigned int */ +unsigned long long +__fixunsdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long long l; + + dl1.d = a1; + + if (dl1.ll == 0) + return (0); + + exp = EXPD (dl1) - EXCESSD - 64; + + l = dl1.ll; + + if (exp > 0) + return (unsigned long long)-1; + + /* shift down until exp = 0 or l = 0 */ + if (exp < 0 && exp > -64 && l) + l >>= -exp; + else + return (0); + + return (l); +} + +/* addtwo doubles */ +double +__adddf3 (double a1, double a2) +{ + register long long mant1, mant2; + register union double_long fl1, fl2; + register int exp1, exp2; + int sign = 0; + + fl1.d = a1; + fl2.d = a2; + + /* check for zero args */ + if (!fl2.ll) + goto test_done; + if (!fl1.ll) { + fl1.d = fl2.d; + goto test_done; + } + + exp1 = EXPD(fl1); + exp2 = EXPD(fl2); + + if (exp1 > exp2 + 54) + goto test_done; + if (exp2 > exp1 + 54) { + fl1.d = fl2.d; + goto test_done; + } + + /* do everything in excess precision so's we can round later */ + mant1 = MANTD_LL(fl1) << 9; + mant2 = MANTD_LL(fl2) << 9; + + if (SIGND(fl1)) + mant1 = -mant1; + if (SIGND(fl2)) + mant2 = -mant2; + + if (exp1 > exp2) + mant2 >>= exp1 - exp2; + else { + mant1 >>= exp2 - exp1; + exp1 = exp2; + } + mant1 += mant2; + + if (mant1 < 0) { + mant1 = -mant1; + sign = SIGNBIT; + } else if (!mant1) { + fl1.d = 0; + goto test_done; + } + + /* normalize up */ + while (!(mant1 & ((long long)7<<61))) { + mant1 <<= 1; + exp1--; + } + + /* normalize down? */ + if (mant1 & ((long long)3<<62)) { + mant1 >>= 1; + exp1++; + } + + /* round to even */ + mant1 += (mant1 & (1<<9)) ? (1<<8) : ((1<<8)-1); + + /* normalize down? */ + if (mant1 & ((long long)3<<62)) { + mant1 >>= 1; + exp1++; + } + + /* lose extra precision */ + mant1 >>= 9; + + /* turn off hidden bit */ + mant1 &= ~HIDDEND_LL; + + /* pack up and go home */ + fl1.ll = PACKD_LL(sign,exp1,mant1); + +test_done: + return (fl1.d); +} + +/* subtract two doubles */ +double +__subdf3 (double a1, double a2) +{ + register union double_long fl1, fl2; + + fl1.d = a1; + fl2.d = a2; + + /* check for zero args */ + if (!fl2.ll) + return (fl1.d); + /* twiddle sign bit and add */ + fl2.l.upper ^= SIGNBIT; + if (!fl1.ll) + return (fl2.d); + return __adddf3 (a1, fl2.d); +} + +/* multiply two doubles */ +double +__muldf3 (double a1, double a2) +{ + register union double_long fl1, fl2; + register unsigned long long result=0ULL; + register int exp; + int sign; + + fl1.d = a1; + fl2.d = a2; + + if (!fl1.ll || !fl2.ll) { + fl1.d = 0; + goto test_done; + } + + /* compute sign and exponent */ + sign = SIGND(fl1) ^ SIGND(fl2); + exp = EXPD(fl1) - EXCESSD; + exp += EXPD(fl2); + + fl1.ll = MANTD_LL(fl1); + fl2.ll = MANTD_LL(fl2); + + /* the multiply is done as one 31x31 multiply and two 31x21 multiples */ + result = (fl1.ll >> 21) * (fl2.ll >> 21); + result += ((fl1.ll & 0x1FFFFF) * (fl2.ll >> 21)) >> 21; + result += ((fl2.ll & 0x1FFFFF) * (fl1.ll >> 21)) >> 21; + + result >>= 2; + if (result & ((long long)1<<61)) { + /* round */ + result += 1<<8; + result >>= 9; + } else { + /* round */ + result += 1<<7; + result >>= 8; + exp--; + } + if (result & (HIDDEND_LL<<1)) { + result >>= 1; + exp++; + } + + result &= ~HIDDEND_LL; + + /* pack up and go home */ + fl1.ll = PACKD_LL(sign,exp,result); +test_done: + return (fl1.d); +} + +/* divide two doubles */ +double +__divdf3 (double a1, double a2) +{ + register union double_long fl1, fl2; + register long long mask,result; + register int exp, sign; + + fl1.d = a1; + fl2.d = a2; + + /* subtract exponents */ + exp = EXPD(fl1) - EXPD(fl2) + EXCESSD; + + /* compute sign */ + sign = SIGND(fl1) ^ SIGND(fl2); + + /* numerator zero??? */ + if (fl1.ll == 0) { + /* divide by zero??? */ + if (fl2.ll == 0) + fl1.ll = ((unsigned long long)1<<63)-1; /* NaN */ + else + fl1.ll = 0; + goto test_done; + } + + /* return +Inf or -Inf */ + if (fl2.ll == 0) { + fl1.ll = PACKD_LL(SIGND(fl1),2047,0); + goto test_done; + } + + + /* now get mantissas */ + fl1.ll = MANTD_LL(fl1); + fl2.ll = MANTD_LL(fl2); + + /* this assures we have 54 bits of precision in the end */ + if (fl1.ll < fl2.ll) { + fl1.ll <<= 1; + exp--; + } + + /* now we perform repeated subtraction of fl2.ll from fl1.ll */ + mask = (long long)1<<53; + result = 0; + while (mask) { + if (fl1.ll >= fl2.ll) + { + result |= mask; + fl1.ll -= fl2.ll; + } + fl1.ll <<= 1; + mask >>= 1; + } + + /* round */ + result += 1; + + /* normalize down */ + exp++; + result >>= 1; + + result &= ~HIDDEND_LL; + + /* pack up and go home */ + fl1.ll = PACKD_LL(sign, exp, result); + +test_done: + return (fl1.d); +} + +int +__gtdf2 (double a1, double a2) +{ + return __cmpdf2 ((float) a1, (float) a2) > 0; +} + +int +__gedf2 (double a1, double a2) +{ + return (__cmpdf2 ((float) a1, (float) a2) >= 0) - 1; +} + +int +__ltdf2 (double a1, double a2) +{ + return - (__cmpdf2 ((float) a1, (float) a2) < 0); +} + +int +__ledf2 (double a1, double a2) +{ + return __cmpdf2 ((float) a1, (float) a2) > 0; +} + +int +__eqdf2 (double a1, double a2) +{ + return *(long long *) &a1 == *(long long *) &a2; +} + +int +__nedf2 (double a1, double a2) +{ + return *(long long *) &a1 != *(long long *) &a2; +} + +/* absolute value of double */ +double +__absdf2(double a1) +{ + if (__cmpdf2(a1,0.0) < 0) + return __negdf2(a1); + else + return a1; +} + +/* absolute value of float */ +float +__abssf2(float a1) +{ + if (__cmpsf2(a1,0.0) < 0) + return __negsf2(a1); + else + return a1; +} diff --git a/arch/s390/kernel/gdb-stub.c b/arch/s390/kernel/gdb-stub.c new file mode 100644 index 000000000..06e3adbb0 --- /dev/null +++ b/arch/s390/kernel/gdb-stub.c @@ -0,0 +1,575 @@ +/* + * arch/s390/kernel/gdb-stub.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + +#include <asm/gdb-stub.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/pgtable.h> +#include <asm/system.h> + + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void fltr_set_mem_err(void); +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); + + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +int gdb_stub_initialised = FALSE; +static const char hexchars[]="0123456789abcdef"; + + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + while (count-- > 0) { + ch = *(mem++); + if (mem_err) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + +/* set_mem_fault_trap(0); */ + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +static char *hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + for (i=0; i<count; i++) + { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *(mem++) = ch; + if (mem_err) + return 0; + } + +/* set_mem_fault_trap(0); */ + + return mem; +} + + + +/* + * Set up exception handlers for tracing and breakpoints + */ +void set_debug_traps(void) +{ +// unsigned long flags; + unsigned char c; + +// save_and_cli(flags); + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ + + gdb_stub_initialised = TRUE; +// restore_flags(flags); +} + + +/* + * Trap handler for memory errors. This just sets mem_err to be non-zero. It + * assumes that %l1 is non-zero. This should be safe, as it is doubtful that + * 0 would ever contain code that could mem fault. This routine will skip + * past the faulting instruction after setting mem_err. + */ +extern void fltr_set_mem_err(void) +{ + /* FIXME: Needs to be written... */ +} + + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +void gdb_stub_get_non_pt_regs(gdb_pt_regs *regs) +{ + s390_fp_regs *fpregs=®s->fp_regs; + int has_ieee=save_fp_regs1(fpregs); + + if(!has_ieee) + { + fpregs->fpc=0; + fpregs->fprs[1].d= + fpregs->fprs[3].d= + fpregs->fprs[5].d= + fpregs->fprs[7].d=0; + memset(&fpregs->fprs[8].d,0,sizeof(freg_t)*8); + } +} + +void gdb_stub_set_non_pt_regs(gdb_pt_regs *regs) +{ + restore_fp_regs1(®s->fp_regs); +} + +void gdb_stub_send_signal(int sigval) +{ + char *ptr; + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ +} + +/* + * This function does all command processing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +void gdb_stub_handle_exception(gdb_pt_regs *regs,int sigval) +{ + int trap; /* Trap type */ + int addr; + int length; + char *ptr; + unsigned long *stack; + + + /* + * reply to host that an exception has occurred + */ + send_signal(sigval); + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + send_signal(sigval); + continue; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + gdb_stub_get_non_pt_regs(regs); + ptr = output_buffer; + ptr= mem2hex((char *)regs,ptr,sizeof(s390_regs_common),FALSE); + ptr= mem2hex((char *)®s->crs[0],ptr,NUM_CRS*CR_SIZE,FALSE); + ptr = mem2hex((char *)®s->fp_regs, ptr,sizeof(s390_fp_regs)); + break; + + /* + * set the value of the CPU registers - return OK + * FIXME: Needs to be written + */ + case 'G': + ptr=input_buffer; + hex2mem (ptr, (char *)regs,sizeof(s390_regs_common), FALSE); + ptr+=sizeof(s390_regs_common)*2; + hex2mem (ptr, (char *)regs->crs[0],NUM_CRS*CR_SIZE, FALSE); + ptr+=NUM_CRS*CR_SIZE*2; + hex2mem (ptr, (char *)regs->fp_regs,sizeof(s390_fp_regs), FALSE); + gdb_stub_set_non_pt_regs(regs); + strcpy(output_buffer,"OK"); + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->cp0_epc = addr; + + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + + flush_cache_all(); + return; + /* NOTREACHED */ + break; + + + /* + * kill the program + */ + case 'k' : + break; /* do nothing */ + + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + + /* + * Step to next instruction + */ + case 's': + /* + * There is no single step insn in the MIPS ISA, so we + * use breakpoints and continue, instead. + */ + single_step(regs); + flush_cache_all(); + return; + /* NOTREACHED */ + + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!gdb_stub_initialised) + return; + __asm__ __volatile__( + ".globl breakinst\n" + "breakinst:\t.word %0\n\t" + : + : "i" (S390_BREAKPOINT_U16) + : + ); +} + + + + + + + diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S new file mode 100644 index 000000000..a3b5df25c --- /dev/null +++ b/arch/s390/kernel/head.S @@ -0,0 +1,668 @@ +/* + * arch/s390/kernel/head.S + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * There are 4 different IPL methods + * 1) load the image directly into ram at address 0 and do an PSW restart + * 2) linload will load the image from address 0x10000 to memory 0x10000 + * and start the code thru LPSW 0x0008000080010000 (VM only, deprecated) + * 3) generate the tape ipl header, store the generated image on a tape + * and ipl from it + * 4) generate the vm reader ipl header, move the generated image to the + * VM reader (use option NOH!) and do a ipl from reader (VM only) + * We use the cpuid to distinguish between VM and native ipl + * params for kernel are pushed to 0x10400 (see setup.h) + */ + +#include <linux/config.h> +#include <asm/setup.h> +#include <asm/lowcore.h> + +#ifndef CONFIG_IPL + .org 0 + .long 0x00080000,0x80000000+iplstart # Just a restart PSW + +iplstart: + l %r12,.Lparm # pointer to parameter area +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r2,%r2 + lhi %r3,1 + sll %r3,20 +.Lloop0: + l %r0,0(%r2) # test page + ar %r2,%r3 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r2,.L4malign0 # align to multiples of 4M + st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size + slr %r2,%r2 + st %r2,INITRD_SIZE-PARMAREA(%r12) # null ramdisk + st %r2,INITRD_START-PARMAREA(%r12) + j start + +.Lparm: .long PARMAREA +.L4malign0:.long 0xffc00000 + .align 8 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 + +#else +#ifdef CONFIG_IPL_TAPE +#define IPL_BS 1024 + .org 0 + .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded + .long 0x07000000,0x60000001 # by ipl to addresses 0-23. + .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). + .long 0x00000000,0x00000000 # external old psw + .long 0x00000000,0x00000000 # svc old psw + .long 0x00000000,0x00000000 # program check old psw + .long 0x00000000,0x00000000 # machine check old psw + .long 0x00000000,0x00000000 # io old psw + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x000a0000,0x00000058 # external new psw + .long 0x000a0000,0x00000060 # svc new psw + .long 0x000a0000,0x00000068 # program check new psw + .long 0x000a0000,0x00000070 # machine check new psw + .long 0x00080000,0x80000000+.Lioint # io new psw + + .org 0x100 +iplstart: + l %r1,0xb8 # load ipl subchannel number + lhi %r2,IPL_BS # load start address + bras %r14,.Lloader # load rest of ipl image + st %r1,__LC_IPLDEV # store ipl device number + l %r12,.Lparm # pointer to parameter area + +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r2,%r2 + lhi %r3,1 + sll %r3,20 +.Lloop0: + l %r0,0(%r2) # test page + ar %r2,%r3 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r2,.L4malign0 # align to multiples of 4M + st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size + c %r2,.Lbigmem # more than 64 MB of memory ? + jl .Lmemok # if yes load ramdisk to 32 MB + mvc INITRD_START-PARMAREA(4,%r12),.Lrdstart +.Lmemok: + +# +# load parameter file from tape +# + l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp + bras %r14,.Lloader # load parameter file + ltr %r2,%r2 # got anything ? + jz .Lnopf + chi %r2,895 + jnh .Lnotrunc + lhi %r2,895 +.Lnotrunc: + l %r4,INITRD_START-PARMAREA(%r12) + la %r5,0(%r4,%r2) + lr %r3,%r2 +.Lidebc: + tm 0(%r5),0x80 # high order bit set ? + jo .Ldocv # yes -> convert from EBCDIC + ahi %r5,-1 + brct %r3,.Lidebc + j .Lnocv +.Ldocv: + l %r3,.Lcvtab + tr 0(256,%r4),0(%r3) # convert parameters to ascii + tr 256(256,%r4),0(%r3) + tr 512(256,%r4),0(%r3) + tr 768(122,%r4),0(%r3) +.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + mvc 0(256,%r3),0(%r4) + mvc 256(256,%r3),256(%r4) + mvc 512(256,%r3),512(%r4) + mvc 768(122,%r3),768(%r4) + slr %r0,%r0 + j .Lcntlp +.Ldelspc: + ic %r0,0(%r2,%r3) + chi %r0,0x20 # is it a space ? + je .Lcntlp + ahi %r2,1 + j .Leolp +.Lcntlp: + brct %r2,.Ldelspc +.Leolp: + slr %r0,%r0 + stc %r0,0(%r2,%r3) # terminate buffer +.Lnopf: + +# +# load ramdisk from tape +# + l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk + bras %r14,.Lloader # load ramdisk + st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk + ltr %r2,%r2 + jnz .Lrdcont + st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it +.Lrdcont: +# +# everything loaded, go for it +# + j start +# +# subroutine for loading from tape +# Paramters: +# R1 = device number +# R2 = load address +.Lloader: + st %r14,.Lldret + la %r3,.Lorbread # r3 = address of orb + la %r5,.Lirb # r5 = address of irb + st %r2,.Lccwread+4 # initialize CCW data addresses + lctl %c6,%c6,.Lcr6 + slr %r2,%r2 +.Lldlp: + lhi %r6,3 # 3 retries +.Lssch: + ssch 0(%r3) # load chunk of IPL_BS bytes + jnz .Llderr +.Lw4end: + bras %r14,.Lwait4io + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lrecov + slr %r7,%r7 + icm %r7,3,10(%r5) # get residual count + lcr %r7,%r7 + ahi %r7,IPL_BS # IPL_BS-residual=#bytes read + ar %r2,%r7 # add to total size + tm 8(%r5),0x01 # found a tape mark ? + jnz .Ldone + l %r0,.Lccwread+4 # update CCW data addresses + ar %r0,%r7 + st %r0,.Lccwread+4 + j .Lldlp +.Ldone: + l %r14,.Lldret + br %r14 # r2 contains the total size +.Lrecov: + bras %r14,.Lsense # do the sensing + brct %r6,.Lssch # dec. retry count & branch + j .Llderr +# +# Sense subroutine +# +.Lsense: + st %r14,.Lsnsret + la %r7,.Lorbsense + ssch 0(%r7) # start sense command + jnz .Llderr + bras %r14,.Lwait4io + l %r14,.Lsnsret + tm 8(%r5),0x82 # do we have a problem ? + jnz .Llderr + br %r14 +# +# Wait for interrupt subroutine +# +.Lwait4io: + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + jne .Lwait4io + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lwtexit + tm 8(%r5),0x04 # got device end ? + jz .Lwait4io +.Lwtexit: + br %r14 +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorbread: + .long 0x00000000,0x0080ff00,.Lccwread + .align 8 +.Lorbsense: + .long 0x00000000,0x0080ff00,.Lccwsense + .align 8 +.Lccwread: + .long 0x02200000+IPL_BS,0x00000000 +.Lccwsense: + .long 0x04200001,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 + .align 8 +.Lcrash:.long 0x000a0000,0x00000000 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 +.Lparm: .long PARMAREA +.L4malign0:.long 0xffc00000 +.Lbigmem:.long 0x04000000 +.Lrdstart:.long 0x02000000 +.Lldret:.long 0 +.Lsnsret: .long 0 +.Lcvtab:.long _ebcasc # ebcdic to ascii table + +#endif /* CONFIG_IPL_TAPE */ + +#ifdef CONFIG_IPL_VM + .org 0 + .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded + .long 0x02000018,0x60000050 # by ipl to addresses 0-23. + .long 0x02000068,0x60000050 # (a PSW and two CCWs). + .fill 80-24,1,0x40 # bytes 24-79 are discarded !! + .long 0x020000f0,0x60000050 # The next 160 byte are loaded + .long 0x02000140,0x60000050 # to addresses 0x18-0xb7 + .long 0x02000190,0x60000050 # They form the continuation + .long 0x020001e0,0x60000050 # of the CCW program started + .long 0x02000230,0x60000050 # by ipl and load the range + .long 0x02000280,0x60000050 # 0x0f0-0x730 from the image + .long 0x020002d0,0x60000050 # to the range 0x0f0-0x730 + .long 0x02000320,0x60000050 # in memory. At the end of + .long 0x02000370,0x60000050 # the channel program the PSW + .long 0x020003c0,0x60000050 # at location 0 is loaded. + .long 0x02000410,0x60000050 # Initial processing starts + .long 0x02000460,0x60000050 # at 0xf0 = iplstart. + .long 0x020004b0,0x60000050 + .long 0x02000500,0x60000050 + .long 0x02000550,0x60000050 + .long 0x020005a0,0x60000050 + .long 0x020005f0,0x60000050 + .long 0x02000640,0x60000050 + .long 0x02000690,0x60000050 + .long 0x020006e0,0x20000050 + + + .org 0xf0 +iplstart: + l %r1,0xb8 # load ipl subchannel number + lhi %r2,0x730 # load start address + bras %r14,.Lloader # load rest of ipl image + st %r1,__LC_IPLDEV # store ipl device number + l %r12,.Lparm # pointer to parameter area + +# +# find out memory size +# + mvc 104(8,0),.Lpcmem0 # setup program check handler + slr %r2,%r2 + lhi %r3,1 + sll %r3,20 +.Lloop0: + l %r0,0(%r2) # test page + ar %r2,%r3 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r2,.L4malign0 # align to multiples of 4M + st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size + c %r2,.Lbigmem # more than 64 MB of memory ? + jl .Lmemok # if yes load ramdisk to 32 MB + mvc INITRD_START-PARMAREA(4,%r12),.Lrdstart +.Lmemok: + +# +# load parameter file from reader +# + l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp + bras %r14,.Lloader # load parameter file + ltr %r2,%r2 # got anything ? + jz .Lnopf + chi %r2,895 + jnh .Lnotrunc + lhi %r2,895 +.Lnotrunc: + l %r4,INITRD_START-PARMAREA(%r12) + la %r5,0(%r4,%r2) + lr %r3,%r2 +.Lidebc: + tm 0(%r5),0x80 # high order bit set ? + jo .Ldocv # yes -> convert from EBCDIC + ahi %r5,-1 + brct %r3,.Lidebc + j .Lnocv +.Ldocv: + l %r3,.Lcvtab + tr 0(256,%r4),0(%r3) # convert parameters to ascii + tr 256(256,%r4),0(%r3) + tr 512(256,%r4),0(%r3) + tr 768(122,%r4),0(%r3) +.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + mvc 0(256,%r3),0(%r4) + mvc 256(256,%r3),256(%r4) + mvc 512(256,%r3),512(%r4) + mvc 768(122,%r3),768(%r4) + slr %r0,%r0 + j .Lcntlp +.Ldelspc: + ic %r0,0(%r2,%r3) + chi %r0,0x20 # is it a space ? + je .Lcntlp + ahi %r2,1 + j .Leolp +.Lcntlp: + brct %r2,.Ldelspc +.Leolp: + slr %r0,%r0 + stc %r0,0(%r2,%r3) # terminate buffer +.Lnopf: + +# +# load ramdisk from reader +# + l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk + bras %r14,.Lloader # load ramdisk + st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk + ltr %r2,%r2 + jnz .Lrdcont + st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it +.Lrdcont: + +# +# everything loaded, reset files in reader, then go for it +# + stidp __LC_CPUID # store cpuid + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on P/390 ? + je start # no -> skip reset + la %r2,.Lreset + lhi %r3,26 + .long 0x83230008 + j start + +# +# subroutine for loading cards from the reader +# +.Lloader: + la %r3,.Lorb # r2 = address of orb into r2 + la %r5,.Lirb # r4 = address of irb + la %r6,.Lccws + la %r7,20 +.Linit: + st %r2,4(%r6) # initialize CCW data addresses + ahi %r2,0x50 + ahi %r6,8 + brct 7,.Linit + + lctl %c6,%c6,.Lcr6 # set IO subclass mask + slr %r2,%r2 +.Lldlp: + ssch 0(%r3) # load chunk of 1600 bytes + jnz .Llderr +.Lwait4irq: + mvc __LC_IO_NEW_PSW(8),.Lnewpsw # set up IO interrupt psw + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + jne .Lwait4irq + tsch 0(%r5) + + slr %r0,%r0 + ic %r0,8(%r5) # get device status + chi %r0,8 # channel end ? + je .Lcont + chi %r0,12 # channel end + device end ? + je .Lcont + + l %r0,4(%r5) + s %r0,8(%r3) # r0/8 = number of ccws executed + mhi %r0,10 # *10 = number of bytes in ccws + lh %r3,10(%r5) # get residual count + sr %r0,%r3 # #ccws*80-residual=#bytes read + ar %r2,%r0 + + br %r14 # r2 contains the total size + +.Lcont: + ahi %r2,0x640 # add 0x640 to total size + la %r6,.Lccws + la %r7,20 +.Lincr: + l %r0,4(%r6) # update CCW data addresses + ahi %r0,0x640 + st %r0,4(%r6) + ahi %r6,8 + brct 7,.Lincr + + j .Lldlp +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorb: .long 0x00000000,0x0080ff00,.Lccws +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 +.Lloadp:.long 0,0 +.Lparm: .long PARMAREA +.L4malign0:.long 0xffc00000 +.Lbigmem:.long 0x04000000 +.Lrdstart:.long 0x02000000 +.Lcvtab:.long _ebcasc # ebcdic to ascii table +.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40 + .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6 + .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold" + .align 8 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 +.Lcrash:.long 0x000a0000,0x00000000 +.Lnewpsw: + .long 0x00080000,0x80000000+.Lioint +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + + .align 8 +.Lccws: .rept 19 + .long 0x02600050,0x00000000 + .endr + .long 0x02200050,0x00000000 + + .org 0x730 # end of the area loaded by the ipl channel program +#endif /* CONFIG_IPL_VM */ + +#endif /* CONFIG_IPL */ + +# +# startup-code at 0x10000, running in real mode +# this is called either by the ipl loader or directly by PSW restart or linload +# + .org 0x10000 + .globl start +start: basr %r13,0 # get base +.LPG1: lctl %c1,%c1,.Lpstd-.LPG1(%r13) # load pstd + lctl %c7,%c7,.Lpstd-.LPG1(%r13) # load sstd + lctl %c13,%c13,.Lpstd-.LPG1(%r13) # load hstd + lctl %c0,%c0,.Lcr0-.LPG1(%r13) # set CR0 + l %r12,.Lparm1-.LPG1(%r13) # pointer to parameter area + +# +# find out memory size. That is done in the ipl loader too but for +# ipl from dasd the size of the memory has to be detected too... +# + icm %r0,15,MEMORY_SIZE-PARMAREA(%r12) + jnz .Lsizeok + mvc 104(8,0),.Lpcmem-.LPG1(%r13) # setup program check handler + slr %r1,%r1 + lhi %r2,1 + sll %r2,20 +.Lloop: + l %r0,0(%r1) # test page + ar %r1,%r2 # add 1M + jnm .Lloop # r1 < 0x80000000 -> loop +.Lchkmem: + n %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M + st %r1,MEMORY_SIZE-PARMAREA(%r12) # store memory size +.Lsizeok: + +# +# find out if we are running under VM +# + stidp __LC_CPUID # store cpuid + tm __LC_CPUID,0xff # running under VM ? + jno .Lnovm + oi MACHINE_FLAGS+3-PARMAREA(%r12),1 # set VM flag +.Lnovm: + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on a P/390 ? + jne .Lnop390 + oi MACHINE_FLAGS+3-PARMAREA(%r12),4 # set P/390 flag +.Lnop390: + +# +# find out if we have an IEEE fpu +# + mvc 104(8,0),.Lpcfpu-.LPG1(%r13) # setup program check handler + ld %f0,.Lflt0-.LPG1(%r13) # load (float) 0.0 + ldr %f2,%f0 + adbr %f0,%f2 # test IEEE add instruction + oi MACHINE_FLAGS+3-PARMAREA(%r12),2 # set IEEE fpu flag +.Lchkfpu: + + lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space, + # virtual and never return ... + .align 8 +.Lentry:.long 0x04080000,0x80000000 + _stext +.Lpstd: .long .Lpgd+0x7F # segment-table +.Lcr0: .long 0x04b50002 +.Lpcmem:.long 0x00080000,0x80000000 + .Lchkmem +.Lpcfpu:.long 0x00080000,0x80000000 + .Lchkfpu +.Lflt0: .double 0 +.Lparm1:.long PARMAREA +.L4malign:.long 0xffc00000 + +# +# params at 10400 (setup.h) +# + .org PARMAREA + .long 0x0100 # ORIG_ROOT_DEV: ramdisk major/minor + .word 0 # MOUNT_ROOT_RDONLY: no + .long 0 # MEMORY_SIZE + .long 0 # MACHINE_FLAGS (bit 0:VM, bit 1:IEEE) + .long RAMDISK_ORIGIN # INITRD_START + .long 0x800000 # INITRD_SIZE + .word 0 # RAMDISK_FLAGS + + .org COMMAND_LINE +# .byte "root=/dev/nfs rw nfsroot=9.164.160.7:/home/mschwide/nfsboot " +# .byte "ip=9.164.147.12:9.164.160.7:9.164.147.1:255.255.255.0:vmlinux:tr0:off" +# .byte "root=/dev/nfs nfsroot=9.164.160.7:/home/mschwide/nfsboot " +# .byte "ip=9.164.181.228:9.164.160.7:9.164.181.1:255.255.224.0:vmlinux:tr0:off" +# .byte "root=/dev/nfs nfsroot=9.164.160.7:/home/pasch/nfsboot " +# .byte "ip=9.164.185.120:9.164.160.7:9.164.181.1:255.255.224.0:vmlinux:tr0:off" +# .byte "mdisk=402:65536:1229,403:131072:2780 root=/dev/mnda ro" +# .byte "root=/dev/nfs rw nfsroot=9.164.160.209:/usr/local/nfsboot " +# .byte "ip=9.164.181.228:9.164.160.209:9.164.181.1:255.255.224.0:vmlinux:tr0:off" + .byte "root=/dev/ram0 ro" +# .byte 0 + +# +# startup-code, running in virtual mode +# + .org 0x10800 + .globl _stext +_stext: basr %r13,0 # get base +.LPG2: +# +# Setup lowcore +# + l %r1,__LC_IPLDEV # load ipl device number + spx .Lprefix-.LPG2(%r13) # set prefix to linux lowcore + st %r1,__LC_IPLDEV # store ipl device number + l %r15,.Linittu-.LPG2(%r13) + ahi %r15,8192 # init_task_union + 8191 + st %r15,__LC_KERNEL_STACK # set end of kernel stack + ahi %r15,-96 + xc 0(4,%r15),0(%r15) # set backchain to zero + lhi %r0,-1 + st %r0,__LC_KERNEL_LEVEL # set interrupt count to -1 +# +# clear bss memory +# + l %r2,.Lbss_bgn-.LPG2(%r13) # start of bss + l %r3,.Lbss_end-.LPG2(%r13) # end of bss + sr %r3,%r2 # length of bss + sr %r4,%r4 # + sr %r5,%r5 # set src,length and pad to zero + sr %r0,%r0 # + mvcle %r2,%r4,0 # clear mem + jo .-4 # branch back, if not finish +# check control registers + stctl %c0,%c15,0(%r15) + oc 2(1,%r15),.Locbits+5-.LPG2(%r13) # enable sigp external ints. + oc 0(1,%r15),.Locbits+4-.LPG2(%r13) # low addresss proctection + lctl %c0,%c15,0(%r15) + +# + lam 0,15,.Laregs-.LPG2(%r13) # load access regs needed by uaccess + l %r14,.Lstart-.LPG2(%r13) + basr %r14,%r14 # call start_kernel +# +# We returned from start_kernel ?!? PANIK +# + basr %r13,0 + lpsw .Ldw-.(%r13) # load disabled wait psw +# +.Lstart: .long start_kernel + .align 8 +.Lprefix: .long init_S390_lowcore +.Linittu: .long init_task_union +.Lbss_bgn: .long __bss_start +.Lbss_end: .long _end +.Locbits: .long 0x01020408,0x10204080 + .align 4 +.Laregs: .long 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 + .align 8 +.Ldw: .long 0x000a0000,0x00000000 + +# +# tempory segment-table at 0x11000 +# + .org 0x11000 +.Lpgd: .long .Lpt0+0x1f # 00000000-000fffff + .long .Lpt1+0x1f # 00100000-001fffff + .long .Lpt2+0x1f # 00200000-002fffff + .long .Lpt3+0x1f # 00300000-003fffff + .fill 2044,4,0x20 # 00400000-7fffffff + +# +# tempory page-tables at 0x12000-0x15fff +# + .macro mktable from,to + .long \from*0x10000 + .long \from*0x10000+0x1000 + .long \from*0x10000+0x2000 + .long \from*0x10000+0x3000 + .long \from*0x10000+0x4000 + .long \from*0x10000+0x5000 + .long \from*0x10000+0x6000 + .long \from*0x10000+0x7000 + .long \from*0x10000+0x8000 + .long \from*0x10000+0x9000 + .long \from*0x10000+0xa000 + .long \from*0x10000+0xb000 + .long \from*0x10000+0xc000 + .long \from*0x10000+0xd000 + .long \from*0x10000+0xe000 + .long \from*0x10000+0xf000 + .if \to-\from + mktable "(\from+1)",\to + .endif + .endm + +.Lpt0: mktable 0,15 +.Lpt1: mktable 16,31 +.Lpt2: mktable 32,47 +.Lpt3: mktable 48,63 + diff --git a/arch/s390/kernel/ieee.h b/arch/s390/kernel/ieee.h new file mode 100644 index 000000000..ef7cc29de --- /dev/null +++ b/arch/s390/kernel/ieee.h @@ -0,0 +1,90 @@ +/* + * arch/s390/kernel/ieee.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include <linux/sched.h> + +static inline void _adddf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd + + current->tss.fprs[R2].fd; +} + +static inline void _subdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd - + current->tss.fprs[R2].fd; +} + +static inline void _muldf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd * + current->tss.fprs[R2].fd; +} + +static inline void _divdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd / + current->tss.fprs[R2].fd; +} + +static inline void _negdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = -current->tss.fprs[R1].fd; +} + +static inline void _fixdfsi(int R1,int R2) +{ + current->tss.regs->gprs[R1] = (__u32) current->tss.fprs[R2].fd; +} + +static inline void _extendsidf(int R1,int R2) +{ + current->tss.fprs[R1].fd = (double) current->tss.regs->gprs[R2]; +} + + +static inline void _addsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff + + current->tss.fprs[R2].ff; +} + +static inline void _subsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff - + current->tss.fprs[R2].ff; +} + +static inline void _mulsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff * + current->tss.fprs[R2].ff; +} + +static inline void _divsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff / + current->tss.fprs[R2].ff; +} + +static inline void _negsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = -current->tss.fprs[R1].ff; +} + +static inline void _fixsfsi(int R1,int R2) +{ + current->tss.regs->gprs[R1] = (__u32) current->tss.fprs[R2].ff; +} + +static inline void _extendsisf(int R1,int R2) +{ + current->tss.fprs[R1].ff = (double) current->tss.regs->gprs[R2]; +} + + diff --git a/arch/s390/kernel/init_task.c b/arch/s390/kernel/init_task.c new file mode 100644 index 000000000..3e2600776 --- /dev/null +++ b/arch/s390/kernel/init_task.c @@ -0,0 +1,32 @@ +/* + * arch/s390/kernel/init_task.c + * + * S390 version + * + * Derived from "arch/i386/kernel/init_task.c" + */ + +#include <linux/mm.h> +#include <linux/sched.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + * + * The things we do for performance.. + */ +union task_union init_task_union __attribute__((aligned(8192))) = + { INIT_TASK(init_task_union.task) }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c new file mode 100644 index 000000000..23165e3ba --- /dev/null +++ b/arch/s390/kernel/irq.c @@ -0,0 +1,427 @@ +/* + * arch/s390/kernel/irq.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + * + * Derived from "arch/i386/kernel/irq.c" + * Copyright (C) 1992, 1999 Linus Torvalds, Ingo Molnar + * + * S/390 I/O interrupt processing and I/O request processing is + * implemented in arch/s390/kernel/s390io.c + */ +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/random.h> +#include <linux/smp.h> +#include <linux/tasks.h> +#include <linux/smp_lock.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/smp.h> +#include <asm/pgtable.h> +#include <asm/delay.h> +#include <asm/lowcore.h> + +void s390_init_IRQ ( void ); +void s390_free_irq ( unsigned int irq, void *dev_id); +int s390_request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id); + +atomic_t nmi_counter; + +#if 0 +/* + * The following vectors are part of the Linux architecture, there + * is no hardware IRQ pin equivalent for them, they are triggered + * through the ICC by us (IPIs), via smp_message_pass(): + */ +BUILD_SMP_INTERRUPT(reschedule_interrupt) +BUILD_SMP_INTERRUPT(invalidate_interrupt) +BUILD_SMP_INTERRUPT(stop_cpu_interrupt) +BUILD_SMP_INTERRUPT(mtrr_interrupt) +BUILD_SMP_INTERRUPT(spurious_interrupt) +#endif + +#if 0 +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + + for (j=0; j<smp_num_cpus; j++) + p += sprintf(p, "CPU%d ",j); + + *p++ = '\n'; + + for (i = 0 ; i < NR_IRQS ; i++) + { + if (ioinfo[i] == INVALID_STORAGE_AREA) + continue; + + action = ioinfo[i]->irq_desc.action; + + if (!action) + continue; + + p += sprintf(p, "%3d: ",i); +#ifndef CONFIG_SMP + p += sprintf(p, "%10u ", kstat_irqs(i)); +#else + for (j=0; j<smp_num_cpus; j++) + p += sprintf( p, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#endif + p += sprintf(p, " %14s", ioinfo[i]->irq_desc.handler->typename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + { + p += sprintf(p, ", %s", action->name); + + } /* endfor */ + + *p++ = '\n'; + + } /* endfor */ + + p += sprintf(p, "NMI: %10u\n", atomic_read(&nmi_counter)); +#ifdef CONFIG_SMP + p += sprintf(p, "IPI: %10u\n", atomic_read(&ipi_count)); +#endif + + return p - buf; +} +#endif + +/* + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + */ +#ifdef CONFIG_SMP +atomic_t global_irq_holder = ATOMIC_INIT(NO_PROC_ID); +atomic_t global_irq_lock; +atomic_t global_irq_count = ATOMIC_INIT(0); +atomic_t global_bh_count; + +/* + * "global_cli()" is a special case, in that it can hold the + * interrupts disabled for a longish time, and also because + * we may be doing TLB invalidates when holding the global + * IRQ lock for historical reasons. Thus we may need to check + * SMP invalidate events specially by hand here (but not in + * any normal spinlocks) + * + * Thankfully we don't need this as we can deliver flush tlbs with + * interrupts disabled DJB :-) + */ +#define check_smp_invalidate(cpu) + +static void show(char * str) +{ + int i; + unsigned long *stack; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [%d]\n", + atomic_read(&global_irq_count),atomic_read(&S390_lowcore.local_irq_count)); + printk("bh: %d [%d]\n", + atomic_read(&global_bh_count),atomic_read(&S390_lowcore.local_bh_count)); + stack = (unsigned long *) &str; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!atomic_read(&global_irq_count)) { + if (atomic_read(&safe_get_cpu_lowcore(cpu).local_bh_count)|| + !atomic_read(&global_bh_count)) + break; + } + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + clear_bit(0,&global_irq_lock); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + SYNC_OTHER_CORES(cpu); + __cli(); + check_smp_invalidate(cpu); + if (atomic_read(&global_irq_count)) + continue; + if (atomic_read(&global_irq_lock)) + continue; + if (!(atomic_read(&safe_get_cpu_lowcore(cpu).local_bh_count)) + && atomic_read(&global_bh_count)) + continue; + if (!test_and_set_bit(0,&global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (atomic_read(&global_irq_count)) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + if (test_and_set_bit(0,&global_irq_lock)) { + /* do we already hold the lock? */ + if ( cpu == atomic_read(&global_irq_holder)) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + do { + check_smp_invalidate(cpu); + } while (test_bit(0,&global_irq_lock)); + } while (test_and_set_bit(0,&global_irq_lock)); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + atomic_set(&global_irq_holder,cpu); +} + +#define EFLAGS_I_SHIFT 25 + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned int flags; + + __save_flags(flags); + if (flags & (1 << EFLAGS_I_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!atomic_read(&S390_lowcore.local_irq_count)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + + if (!atomic_read(&S390_lowcore.local_irq_count)) + release_irqlock(smp_processor_id()); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_I_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!atomic_read(&S390_lowcore.local_irq_count)) + { + if (local_enabled) + retval = 1; + if (atomic_read(&global_irq_holder)== smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} + +#endif + +/* + * Note : This fuction should be eliminated as it doesn't comply with the + * S/390 irq scheme we have implemented ... + */ +int handle_IRQ_event( unsigned int irq, int cpu, struct pt_regs * regs) +{ + struct irqaction * action; + int status; + + status = 0; + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + + action = ioinfo[irq]->irq_desc.action; + + if (action) + { + status |= 1; + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do + { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + } /* endif */ + + return status; +} + +void enable_nop(int irq) +{ +} + +void __init init_IRQ(void) +{ + s390_init_IRQ(); +} + + +void free_irq(unsigned int irq, void *dev_id) +{ + s390_free_irq( irq, dev_id); +} + + +int request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) ); + +} + +void init_irq_proc(void) +{ + /* For now, nothing... */ +} + diff --git a/arch/s390/kernel/irqextras390.c b/arch/s390/kernel/irqextras390.c new file mode 100644 index 000000000..e1e455813 --- /dev/null +++ b/arch/s390/kernel/irqextras390.c @@ -0,0 +1,35 @@ +/* + * arch/s390/kernel/irqextras390.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Some channel code by D.J. Barrow + */ + +/* + +*/ +#include<asm/irqextras390.h> +#include<asm/lowcore.h> + +#if 0 +// fixchannelprogram is now obselete +void fixchannelprogram(orb_bits_t *orbptr) +{ + __u32 newAddress=orbptr->ccw_program_address; + fixccws(orbptr->ccw_program_address); + orbptr->ccw_program_address=newAddress; + orbptr->ccw_program_address=(ccw1_t *)(((__u32)orbptr->ccw_program_address)); +} +#endif + +void fixccws(ccw1_bits_t *ccwptr) +{ + for(;;ccwptr++) + { // Just hope nobody starts doing prefixing + if(!ccwptr->cc) + break; + } +} diff --git a/arch/s390/kernel/lowcore.S b/arch/s390/kernel/lowcore.S new file mode 100644 index 000000000..b5c1c6bce --- /dev/null +++ b/arch/s390/kernel/lowcore.S @@ -0,0 +1,60 @@ +/* + * arch/s390/kernel/lowcore.S + * S390 lowcore definition. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include <asm/lowcore.h> + .align 4096 + .globl init_S390_lowcore +init_S390_lowcore: + .long _RESTART_PSW_MASK + .long restart_int_handler + _ADDR_31 + .long 0,0 + .long 0,0 +EXT_OLD: .long 0,0 +SVC_OLD: .long 0,0 +PGM_OLD: .long 0,0 +MCCK_OLD:.long 0,0 +IO_OLD: .long 0,0 + .long 0,0,0,0,0,0 +# +# new psws need all to be physical +# because we start with dat off +# +EXT_PSW: .long _EXT_PSW_MASK + .long ext_int_handler + _ADDR_31 +# +SVC_PSW: .long _SVC_PSW_MASK + .long system_call + _ADDR_31 +# +PGM_PSW: .long _PGM_PSW_MASK + .long pgm_check_handler + _ADDR_31 +# +MCCK_PSW:.long _MCCK_PSW_MASK + .long mcck_int_handler + _ADDR_31 +# +IO_PSW: .long _IO_PSW_MASK + .long io_int_handler + _ADDR_31 +# +# +# +EXTERNAL_PARAMETER: .long 0 +CPU_ADDRESS: .word 0 +EXT_INTERRUPT_CODE: .word 0 +SVC_ILC: .word 0 +SVC_CODE: .word 0 +PGM_ILC: .word 0 +PGM_CODE: .word 0 +TRANS_EXC_ADDR: .long 0 # 090 + .fill 0xC00-0x094,1,0 +SAVE_AREA: .fill 0x40,1,0 # C00 +KERNEL_STACK: .long 0 # C40 +KERNEL_LEVEL: .long 0 # C44 +CPUID: .long 0,0 # C48 + .fill 0x1000-0xC50,1,0 + diff --git a/arch/s390/kernel/mathemu.c b/arch/s390/kernel/mathemu.c new file mode 100644 index 000000000..78b6e5ec6 --- /dev/null +++ b/arch/s390/kernel/mathemu.c @@ -0,0 +1,894 @@ +/* + * arch/s390/kernel/mathemu.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * 'mathemu.c' handles IEEE instructions on a S390 processor + * that does not have the IEEE fpu + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/ptrace.h> + +#include <asm/uaccess.h> +#include <asm/mathemu.h> + +static void set_CC_df(__u64 val1,__u64 val2) { + int rc; + rc = __cmpdf2(val1,val2); + current->thread.regs->psw.mask &= 0xFFFFCFFF; + switch (rc) { + case -1: + current->thread.regs->psw.mask |= 0x00001000; + break; + case 1: + current->thread.regs->psw.mask |= 0x00002000; + break; + } +} + +static void set_CC_sf(__u32 val1,__u32 val2) { + int rc; + rc = __cmpsf2(val1,val2); + current->thread.regs->psw.mask &= 0xFFFFCFFF; + switch (rc) { + case -1: + current->thread.regs->psw.mask |= 0x00001000; + break; + case 1: + current->thread.regs->psw.mask |= 0x00002000; + break; + } +} + + +static void emu_adb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d,val); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_adbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_aeb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f,val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_aebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_axbr (int rx, int ry) { + printk("axbr emulation not implemented!\n"); +} + +static void emu_cdb (int rx, __u64 val) { + set_CC_df(current->thread.fp_regs.fprs[rx].d,val); +} + +static void emu_cdbr (int rx, int ry) { + set_CC_df(current->thread.fp_regs.fprs[rx].d,current->thread.fp_regs.fprs[ry].d); +} + +static void emu_cdfbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = + __floatsidf(current->thread.regs->gprs[ry]); +} + +static void emu_ceb (int rx, __u32 val) { + set_CC_sf(current->thread.fp_regs.fprs[rx].f,val); +} + +static void emu_cebr (int rx, int ry) { + set_CC_sf(current->thread.fp_regs.fprs[rx].f,current->thread.fp_regs.fprs[ry].f); +} + +static void emu_cefbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = + __floatsisf(current->thread.regs->gprs[ry]); +} + +static void emu_cfdbr (int rx, int ry, int mask) { + current->thread.regs->gprs[rx] = + __fixdfsi(current->thread.fp_regs.fprs[ry].d); +} + +static void emu_cfebr (int rx, int ry, int mask) { + current->thread.regs->gprs[rx] = + __fixsfsi(current->thread.fp_regs.fprs[ry].f); +} + +static void emu_cfxbr (int rx, int ry, int mask) { + printk("cfxbr emulation not implemented!\n"); +} + +static void emu_cxbr (int rx, int ry) { + printk("cxbr emulation not implemented!\n"); +} + +static void emu_cxfbr (int rx, int ry) { + printk("cxfbr emulation not implemented!\n"); +} + +static void emu_ddb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d,val); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_ddbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_deb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f,val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_debr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_didbr (int rx, int ry, int mask) { + printk("didbr emulation not implemented!\n"); +} + +static void emu_diebr (int rx, int ry, int mask) { + printk("diebr emulation not implemented!\n"); +} + +static void emu_dxbr (int rx, int ry) { + printk("dxbr emulation not implemented!\n"); +} + +static void emu_efpc (int rx, int ry) { + printk("efpc emulation not implemented!\n"); +} + +static void emu_fidbr (int rx, int ry, int mask) { + printk("fidbr emulation not implemented!\n"); +} + +static void emu_fiebr (int rx, int ry, int mask) { + printk("fiebr emulation not implemented!\n"); +} + +static void emu_fixbr (int rx, int ry, int mask) { + printk("fixbr emulation not implemented!\n"); +} + +static void emu_kdb (int rx, __u64 val) { + printk("kdb emulation not implemented!\n"); +} + +static void emu_kdbr (int rx, int ry) { + printk("kdbr emulation not implemented!\n"); +} + +static void emu_keb (int rx, __u32 val) { + printk("keb emulation not implemented!\n"); +} + +static void emu_kebr (int rx, int ry) { + printk("kebr emulation not implemented!\n"); +} + +static void emu_kxbr (int rx, int ry) { + printk("kxbr emulation not implemented!\n"); +} + +static void emu_lcdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = + __negdf2(current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_lcebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = + __negsf2(current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_lcxbr (int rx, int ry) { + printk("lcxbr emulation not implemented!\n"); +} + +static void emu_ldeb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].d = __extendsfdf2(val); +} + +static void emu_ldebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = + __extendsfdf2(current->thread.fp_regs.fprs[ry].f); +} + +static void emu_ldxbr (int rx, int ry) { + printk("ldxbr emulation not implemented!\n"); +} + +static void emu_ledbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __truncdfsf2(current->thread.fp_regs.fprs[ry].d); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_lexbr (int rx, int ry) { + printk("lexbr emulation not implemented!\n"); +} + +static void emu_lndbr (int rx, int ry) { + printk("lndbr emulation not implemented!\n"); +} + +static void emu_lnebr (int rx, int ry) { + printk("lnebr emulation not implemented!\n"); +} + +static void emu_lnxbr (int rx, int ry) { + printk("lnxbr emulation not implemented!\n"); +} + +static void emu_lpdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __absdf2(current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0); +} + +static void emu_lpebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __abssf2(current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_lpxbr (int rx, int ry) { + printk("lpxbr emulation not implemented!\n"); +} + +static void emu_ltdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = current->thread.fp_regs.fprs[ry].d; + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_ltebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = current->thread.fp_regs.fprs[ry].f; + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_ltxbr (int rx, int ry) { + printk("ltxbr emulation not implemented!\n"); +} + +static void emu_lxdb (int rx, __u64 val) { + printk("lxdb emulation not implemented!\n"); +} + +static void emu_lxdbr (int rx, int ry) { + printk("lxdbr emulation not implemented!\n"); +} + +static void emu_lxeb (int rx, __u32 val) { + printk("lxeb emulation not implemented!\n"); +} + +static void emu_lxebr (int rx, int ry) { + printk("lxebr emulation not implemented!\n"); +} + +static void emu_madb (int rx, __u64 val, int mask) { + printk("madb emulation not implemented!\n"); +} + +static void emu_madbr (int rx, int ry, int mask) { + printk(" emulation not implemented!\n"); +} + +static void emu_maeb (int rx, __u32 val, int mask) { + printk("maeb emulation not implemented!\n"); +} + +static void emu_maebr (int rx, int ry, int mask) { + printk("maebr emulation not implemented!\n"); +} + +static void emu_mdb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d,val); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_mdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_mdeb (int rx, __u32 val) { + printk("mdeb emulation not implemented!\n"); +} + +static void emu_mdebr (int rx, int ry) { + printk("mdebr emulation not implemented!\n"); +} + +static void emu_meeb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, + val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_meebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_msdb (int rx, __u64 val, int mask) { + printk("msdb emulation not implemented!\n"); +} + +static void emu_msdbr (int rx, int ry, int mask) { + printk("msdbr emulation not implemented!\n"); +} + +static void emu_mseb (int rx, __u32 val, int mask) { + printk("mseb emulation not implemented!\n"); +} + +static void emu_msebr (int rx, int ry, int mask) { + printk("msebr emulation not implemented!\n"); +} + +static void emu_mxbr (int rx, int ry) { + printk("mxbr emulation not implemented!\n"); +} + +static void emu_mxdb (int rx, __u64 val) { + printk("mxdb emulation not implemented!\n"); +} + +static void emu_mxdbr (int rx, int ry) { + printk("mxdbr emulation not implemented!\n"); +} + +static void emu_sdb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, + val); + set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_sdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_seb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, + val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_sebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_sfpc (int rx, int ry) { + printk("sfpc emulation not implemented!\n"); +} + +static void emu_sqdb (int rx, __u64 val) { + printk("sqdb emulation not implemented!\n"); +} + +static void emu_sqdbr (int rx, int ry) { + printk("sqdbr emulation not implemented!\n"); +} + +static void emu_sqeb (int rx, __u32 val) { + printk("sqeb emulation not implemented!\n"); +} + +static void emu_sqebr (int rx, int ry) { + printk("sqebr emulation not implemented!\n"); +} + +static void emu_sqxbr (int rx, int ry) { + printk("sqxbr emulation not implemented!\n"); +} + +static void emu_sxbr (int rx, int ry) { + printk("sxbr emulation not implemented!\n"); +} + +static void emu_tcdb (int rx, __u64 val) { + printk("tcdb emulation not implemented!\n"); +} + +static void emu_tceb (int rx, __u32 val) { + printk("tceb emulation not implemented!\n"); +} + +static void emu_tcxb (int rx, __u64 val) { + printk("tcxb emulation not implemented!\n"); +} + + +static inline void emu_load_regd(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ + " bras 1,0f\n" + " ld 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d) + : "1" ); + } +} + +static inline void emu_load_rege(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ + " bras 1,0f\n" + " le 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f) + : "1" ); + } +} + +static inline void emu_store_regd(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ + " bras 1,0f\n" + " std 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d) + : "1" ); + } +} + + +static inline void emu_store_rege(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ + " bras 1,0f\n" + " ste 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f) + : "1" ); + } +} + +int math_emu_b3(__u8 *opcode, struct pt_regs * regs) { + static const __u8 format_table[] = { + 2, 2, 2, 2, 9, 1, 2, 1, 2, 2, 2, 2, 9, 2, 4, 4, + 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1,10, 1, 1, 3, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 5, 6, 6, 0, 7, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const void *jump_table[]= { + emu_lpebr, emu_lnebr, emu_ltebr, emu_lcebr, + emu_ldebr, emu_lxdbr, emu_lxebr, emu_mxdbr, + emu_kebr, emu_cebr, emu_aebr, emu_sebr, + emu_mdebr, emu_debr, emu_maebr, emu_msebr, + emu_lpdbr, emu_lndbr, emu_ltdbr, emu_lcdbr, + emu_sqebr, emu_sqdbr, emu_sqxbr, emu_meebr, + emu_kdbr, emu_cdbr, emu_adbr, emu_sdbr, + emu_mdbr, emu_ddbr, emu_madbr, emu_msdbr, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_lpxbr, emu_lnxbr, emu_ltxbr, emu_lcxbr, + emu_ledbr, emu_ldxbr, emu_lexbr, emu_fixbr, + emu_kxbr, emu_cxbr, emu_axbr, emu_sxbr, + emu_mxbr, emu_dxbr, NULL, NULL, + NULL, NULL, NULL, emu_diebr, + NULL, NULL, NULL, emu_fiebr, + NULL, NULL, NULL, emu_didbr, + NULL, NULL, NULL, emu_fidbr, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_sfpc, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_efpc, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_cefbr, emu_cdfbr, emu_cxfbr, NULL, + emu_cfebr, emu_cfdbr, emu_cfxbr + }; + + switch (format_table[opcode[1]]) { + case 1: /* RRE format, double operation */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return 0; + case 2: /* RRE format, float operation */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return 0; + case 3: /* RRF format, double operation */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return 0; + case 4: /* RRF format, float operation */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return 0; + case 5: /* RRE format, cefbr instruction */ + emu_store_rege((opcode[3]>>4)&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + return 0; + case 6: /* RRE format, cdfbr & cxfbr instruction */ + emu_store_regd((opcode[3]>>4)&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + return 0; + /* FIXME !! */ + return 0; + case 7: /* RRF format, cfebr instruction */ + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + return 0; + case 8: /* RRF format, cfdbr & cfxbr instruction */ + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + return 0; + case 9: /* RRE format, ldebr & mdebr instruction */ + /* float store but double load */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + return 0; + case 10: /* RRE format, ledbr instruction */ + /* double store but float load */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + return 0; + default: + return 1; + } +} + +static void* calc_addr(struct pt_regs *regs,int rx,int rb,int disp) +{ + rx &= 0xf; + rb &= 0xf; + disp &= 0xfff; + return (void*) ((rx != 0 ? regs->gprs[rx] : 0) + /* index */ + (rb != 0 ? regs->gprs[rb] : 0) + /* base */ + disp); +} + +int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { + static const __u8 format_table[] = { + 0, 0, 0, 0, 5, 1, 2, 1, 2, 2, 2, 2, 5, 2, 4, 4, + 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const void *jump_table[]= { + NULL, NULL, NULL, NULL, + emu_ldeb, emu_lxdb, emu_lxeb, emu_mxdb, + emu_keb, emu_ceb, emu_aeb, emu_seb, + emu_mdeb, emu_deb, emu_maeb, emu_mseb, + emu_tceb, emu_tcdb, emu_tcxb, NULL, + emu_sqeb, emu_sqdb, NULL, emu_meeb, + emu_kdb, emu_cdb, emu_adb, emu_sdb, + emu_mdb, emu_ddb, emu_madb, emu_msdb + }; + + switch (format_table[opcode[5]]) { + case 1: /* RXE format, __u64 constant */ { + __u64 *dxb, temp; + __u32 opc; + + emu_store_regd((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(&temp, dxb, 8); + /* call the emulation function */ + ((void (*)(int, __u64))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + case 2: /* RXE format, __u32 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_rege((opcode[1]>>4)&15); + return 0; + } + case 3: /* RXF format, __u64 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_regd((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(&temp, dxb, 8); + /* call the emulation function */ + ((void (*)(int, __u32, int))jump_table[opcode[5]]) + (opcode[1]>>4,temp,opcode[4]>>4); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + case 4: /* RXF format, __u32 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32, int))jump_table[opcode[5]]) + (opcode[1]>>4,temp,opcode[4]>>4); + emu_load_rege((opcode[1]>>4)&15); + return 0; + } + case 5: /* RXE format, __u32 constant */ + /* store_rege and load_regd */ + { + __u32 *dxb, temp; + __u32 opc; + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + default: + return 1; + } +} + +/* + * Emulate LDR Rx,Ry with Rx or Ry not in {0, 2, 4, 6} + */ +void math_emu_ldr(__u8 *opcode) { + __u16 opc = *((__u16 *) opcode); + + if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ + /* we got an exception therfore ry can't be in {0,2,4,6} */ + __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ + " bras 1,0f\n" + " ld 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (opc&0x00f0), + "a" (¤t->thread.fp_regs.fprs[opc&0x000f].d) + : "1" ); + } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ + __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ + " bras 1,0f\n" + " std 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" ((opc&0x000f)<<4), + "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].d) + : "1" ); + } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ + current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = + current->thread.fp_regs.fprs[opc&0x000f]; + } +} + +/* + * Emulate LER Rx,Ry with Rx or Ry not in {0, 2, 4, 6} + */ +void math_emu_ler(__u8 *opcode) { + __u16 opc = *((__u16 *) opcode); + + if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ + /* we got an exception therfore ry can't be in {0,2,4,6} */ + __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ + " bras 1,0f\n" + " le 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (opc&0x00f0), + "a" (¤t->thread.fp_regs.fprs[opc&0x000f].f) + : "1" ); + } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ + __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ + " bras 1,0f\n" + " ste 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" ((opc&0x000f)<<4), + "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].f) + : "1" ); + } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ + current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = + current->thread.fp_regs.fprs[opc&0x000f]; + } +} + +/* + * Emulate LD R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_ld(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u64 *dxb; + + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(¤t->thread.fp_regs.fprs[(opc>>20)&15].d, dxb, 8); +} + +/* + * Emulate LE R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_le(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u32 *mem, *dxb; + + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); + get_user(mem[0], dxb); +} + +/* + * Emulate STD R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_std(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u64 *dxb; + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_to_user fails ? */ + copy_to_user(dxb, ¤t->thread.fp_regs.fprs[(opc>>20)&15].d, 8); +} + +/* + * Emulate STE R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_ste(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u32 *mem, *dxb; + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if put_user fails ? */ + mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); + put_user(mem[0], dxb); +} + +/* + * Emulate LFPC D(B) + */ +int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + +/* + * Emulate STFPC D(B) + */ +int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + +/* + * Emulate SRNM D(B) + */ +int math_emu_srnm(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + + + + + + + + + + + + + + + + diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c new file mode 100644 index 000000000..33e8092cf --- /dev/null +++ b/arch/s390/kernel/process.c @@ -0,0 +1,431 @@ +/* + * arch/s390/kernel/process.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Hartmut Penner (hp@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995, Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include <stdarg.h> + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/init.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/misc390.h> +#include <asm/irq.h> + +spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); + +/* + * The idle loop on a S390... + */ + +static psw_t wait_psw; + +int cpu_idle(void *unused) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->priority = 0; + current->counter = -100; + wait_psw.mask = _WAIT_PSW_MASK; + wait_psw.addr = (unsigned long) &&idle_wakeup | 0x80000000L; + while(1) { + if (softirq_state[smp_processor_id()].active & + softirq_state[smp_processor_id()].mask) { + do_softirq(); + continue; + } + if (current->need_resched) { + schedule(); + check_pgt_cache(); + continue; + } + + /* load wait psw */ + asm volatile ( + "lpsw %0" + : : "m" (wait_psw) ); +idle_wakeup: + } +} + +/* + As all the register will only be made displayable to the root + user ( via printk ) or checking if the uid of the user is 0 from + the /proc filesystem please god this will be secure enough DJB. + The lines are given one at a time so as not to chew stack space in + printk on a crash & also for the proc filesystem when you get + 0 returned you know you've got all the lines + */ + +int sprintf_regs(int line, char *buff, struct task_struct * task, + struct thread_struct *thread, struct pt_regs * regs) +{ + int linelen=0; + int regno,chaincnt; + u32 backchain,prev_backchain,endchain; + + enum + { + sp_linefeed, + sp_psw, + sp_ksp, + sp_gprs, + sp_gprs1, + sp_gprs2, + sp_gprs3, + sp_gprs4, + sp_acrs, + sp_acrs1, + sp_acrs2, + sp_acrs3, + sp_acrs4, + sp_kern_backchain, + sp_kern_backchain1 + }; + + if(task) + thread = &task->thread; + if(thread) + regs = thread->regs; + switch (line) { + case sp_linefeed: + linelen=sprintf(buff,"\n"); + break; + case sp_psw: + if(regs) + linelen = sprintf(buff,"User PSW: %08lx %08lx\n", + (unsigned long) regs->psw.mask, + (unsigned long) regs->psw.addr); + else + linelen = sprintf(buff,"pt_regs=NULL some info unavailable\n"); + break; + case sp_ksp: + if (task) + linelen += sprintf(&buff[linelen], + "task: %08x ", (addr_t)task); + if (thread) + linelen += sprintf(&buff[linelen], + "thread: %08x ksp: %08x ", + (addr_t)thread,(addr_t)thread->ksp); + if (regs) + linelen += sprintf(&buff[linelen], + "pt_regs: %08x\n", (addr_t)regs); + break; + case sp_gprs: + if (regs) + linelen = sprintf(buff,"User GPRS:\n"); + break; + case sp_gprs1 ... sp_gprs4: + if (regs) { + regno = (line-sp_gprs1)*4; + linelen = sprintf(buff,"%08x %08x %08x %08x\n", + regs->gprs[regno], + regs->gprs[regno+1], + regs->gprs[regno+2], + regs->gprs[regno+3]); + } + break; + case sp_acrs: + if (regs) + linelen = sprintf(buff,"User ACRS:\n"); + break; + case sp_acrs1 ... sp_acrs4: + if (regs) { + regno = (line-sp_acrs1)*4; + linelen = sprintf(buff,"%08x %08x %08x %08x\n", + regs->acrs[regno], + regs->acrs[regno+1], + regs->acrs[regno+2], + regs->acrs[regno+3]); + } + break; + case sp_kern_backchain: + if (thread && thread->ksp && regs) + linelen = sprintf(buff,"Kernel BackChain CallChain BackChain CallChain\n"); + break; + default: + if(thread && thread->ksp && regs) { + backchain = (thread->ksp & PSW_ADDR_MASK); + endchain = ((backchain & (-8192)) + 8192); + prev_backchain = backchain - 1; + line -= sp_kern_backchain1; + for (chaincnt = 0; ; chaincnt++) { + if ((backchain == 0) || + (backchain >= endchain) || + (chaincnt >= 8) || + (prev_backchain >= backchain)) + break; + if ((chaincnt >> 1) == line) { + linelen += sprintf(&buff[linelen],"%s%08x %08x ", + (chaincnt&1) ? "":" ", + backchain,*(u32 *)(backchain+56)); + } + if ((chaincnt >> 1) > line) + break; + prev_backchain = backchain; + backchain = (*((u32 *)backchain)) & PSW_ADDR_MASK; + } + if (linelen) + linelen += sprintf(&buff[linelen],"\n"); + } + } + return linelen; +} + + +void show_regs(struct task_struct *task, struct thread_struct *thread, + struct pt_regs *regs) +{ + char buff[80]; + int line; + + for (line = 0; sprintf_regs(line,buff,task,thread,regs); line++) + printk(buff); +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + int clone_arg = flags | CLONE_VM; + int retval; + + __asm__ __volatile__( + " sr 2,2\n" + " lr 3,%1\n" + " l 4,%6\n" /* load kernel stack ptr of parent */ + " svc %b2\n" /* Linux system call*/ + " cl 4,%6\n" /* compare ksp's: child or parent ? */ + " je 0f\n" /* parent - jump*/ + " l 15,%6\n" /* fix kernel stack pointer*/ + " ahi 15,%7\n" + " xc 0(96,15),0(15)\n" /* clear save area */ + " lr 2,%4\n" /* load argument*/ + " lr 14,%5\n" /* get fn-pointer*/ + " basr 14,14\n" /* call fn*/ + " svc %b3\n" /* Linux system call*/ + "0: lr %0,2" + : "=a" (retval) + : "d" (clone_arg), "i" (__NR_clone), "i" (__NR_exit), + "d" (arg), "d" (fn), "i" (__LC_KERNEL_STACK) , "i" (-STACK_FRAME_OVERHEAD) + : "2", "3", "4" ); + return retval; +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ + + current->used_math = 0; + current->flags &= ~PF_USEDFPU; +} + +void release_thread(struct task_struct *dead_task) +{ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp, + struct task_struct * p, struct pt_regs * regs) +{ + struct stack_frame + { + unsigned long back_chain; + unsigned long eos; + unsigned long glue1; + unsigned long glue2; + unsigned long scratch[2]; + unsigned long gprs[10]; /* gprs 6 -15 */ + unsigned long fprs[4]; /* fpr 4 and 6 */ + unsigned long empty[4]; +#if CONFIG_REMOTE_DEBUG + gdb_pt_regs childregs; +#else + pt_regs childregs; +#endif + __u32 pgm_old_ilc; /* single step magic from entry.S */ + __u32 pgm_svc_step; + } *frame; + + frame = (struct stack_frame *) (2*PAGE_SIZE + (unsigned long) p) -1; + frame = (struct stack_frame *) (((unsigned long) frame)&-8L); + p->thread.regs = &frame->childregs; + p->thread.ksp = (unsigned long) frame; + frame->childregs = *regs; + frame->childregs.gprs[15] = new_stackp; + frame->eos = 0; + + /* new return point is ret_from_sys_call */ + frame->gprs[8] = ((unsigned long) &ret_from_fork) | 0x80000000; + + /* fake return stack for resume(), don't go back to schedule */ + frame->gprs[9] = (unsigned long) frame; + frame->pgm_svc_step = 0; /* Nope we aren't single stepping an svc */ + /* save fprs, if used in last task */ + save_fp_regs(&p->thread.fp_regs); + p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _SEGMENT_TABLE; + p->thread.fs = USER_DS; + /* Don't copy debug registers */ + memset(&p->thread.per_info,0,sizeof(p->thread.per_info)); + return 0; +} + +asmlinkage int sys_fork(struct pt_regs regs) +{ + int ret; + + lock_kernel(); + ret = do_fork(SIGCHLD, regs.gprs[15], ®s); + unlock_kernel(); + return ret; +} + +asmlinkage int sys_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int ret; + + lock_kernel(); + clone_flags = regs.gprs[3]; + newsp = regs.gprs[2]; + if (!newsp) + newsp = regs.gprs[15]; + ret = do_fork(clone_flags, newsp, ®s); + unlock_kernel(); + return ret; +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, + regs.gprs[15], ®s); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname((char *) regs.orig_gpr2); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], ®s); + if (error == 0) + current->flags &= ~PF_DTRACE; + putname(filename); +out: + return error; +} + + +/* + * fill in the FPU structure for a core dump. + */ +int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs) +{ + save_fp_regs(fpregs); + return 1; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->gprs[15] & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs)); + dump_fpu (regs, &dump->regs.fp_regs); + memcpy(&dump->regs.per_info,¤t->thread.per_info,sizeof(per_struct)); +} + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long r14, r15; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + stack_page = (unsigned long) p; + r15 = p->thread.ksp; + do { + r14 = *(unsigned long *) (r15+56); + if (r14 < first_sched || r14 >= last_sched) + return r14; + r15 = *(unsigned long *) (r15+60); + } while (count++ < 16); + return 0; +} +#undef last_sched +#undef first_sched + diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c new file mode 100644 index 000000000..2643f100a --- /dev/null +++ b/arch/s390/kernel/ptrace.c @@ -0,0 +1,395 @@ +/* + * arch/s390/kernel/ptrace.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Based on PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include <stddef.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> + +#include <asm/segment.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/system.h> +#include <asm/uaccess.h> + + +void FixPerRegisters(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + per_struct *per_info= + (per_struct *)&task->thread.per_info; + + per_info->control_regs.bits.em_instruction_fetch= + per_info->single_step|per_info->instruction_fetch; + + if(per_info->single_step) + { + per_info->control_regs.bits.starting_addr=0; + per_info->control_regs.bits.ending_addr=0x7fffffffUL; + } + else + { + per_info->control_regs.bits.starting_addr= + per_info->starting_addr; + per_info->control_regs.bits.ending_addr= + per_info->ending_addr; + } + /* if any of the control reg tracing bits are on + we switch on per in the psw */ + if(per_info->control_regs.words.cr[0]&PER_EM_MASK) + regs->psw.mask |=PSW_PER_MASK; + else + regs->psw.mask &= ~PSW_PER_MASK; + if(per_info->control_regs.bits.storage_alt_space_ctl) + task->thread.user_seg|=USER_STD_MASK; + else + task->thread.user_seg&=~USER_STD_MASK; +} + +void set_single_step(struct task_struct *task) +{ + per_struct *per_info= + (per_struct *)&task->thread.per_info; + + per_info->single_step=1; /* Single step */ + FixPerRegisters(task); +} + +void clear_single_step(struct task_struct *task) +{ + per_struct *per_info= + (per_struct *)&task->thread.per_info; + + per_info->single_step=0; + FixPerRegisters(task); +} + +int ptrace_usercopy(addr_t realuseraddr,addr_t copyaddr,int len,int tofromuser,int writeuser,u32 mask) +{ + u32 tempuser; + int retval=0; + + if(writeuser&&realuseraddr==(addr_t)NULL) + return(0); + if(mask!=0xffffffff) + { + tempuser=*((u32 *)realuseraddr); + if(!writeuser) + { + tempuser&=mask; + realuseraddr=(addr_t)&tempuser; + } + } + if(tofromuser) + { + if(writeuser) + { + retval=copy_from_user((void *)realuseraddr,(void *)copyaddr,len); + } + else + { + if(realuseraddr==(addr_t)NULL) + retval=(clear_user((void *)copyaddr,len)==-EFAULT ? -EIO:0); + else + retval=(copy_to_user((void *)copyaddr,(void *)realuseraddr,len)==-EFAULT ? -EIO:0); + } + } + else + { + if(writeuser) + memcpy((void *)realuseraddr,(void *)copyaddr,len); + else + memcpy((void *)copyaddr,(void *)realuseraddr,len); + } + if(mask!=0xffffffff&&writeuser) + (*((u32 *)realuseraddr))=(((*((u32 *)realuseraddr))&mask)|(tempuser&~mask)); + return(retval); +} + +int copy_user(struct task_struct *task,saddr_t useraddr,addr_t copyaddr,int len,int tofromuser,int writingtouser) +{ + int copylen=0,copymax; + addr_t realuseraddr; + saddr_t enduseraddr=useraddr+len; + + u32 mask; + + if (useraddr < 0 || enduseraddr > sizeof(struct user)|| + (useraddr < PT_ENDREGS && (useraddr&3))|| + (enduseraddr < PT_ENDREGS && (enduseraddr&3))) + return (-EIO); + while(len>0) + { + mask=0xffffffff; + if(useraddr<PT_FPC) + { + realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]); + if(useraddr<PT_PSWMASK) + { + copymax=PT_PSWMASK; + } + else if(useraddr<(PT_PSWMASK+4)) + { + copymax=(PT_PSWMASK+4); + if(writingtouser) + mask=PSW_MASK_DEBUGCHANGE; + } + else if(useraddr<(PT_PSWADDR+4)) + { + copymax=PT_PSWADDR+4; + mask=PSW_ADDR_DEBUGCHANGE; + } + else + copymax=PT_FPC; + + } + else if(useraddr<(PT_FPR15_LO+4)) + { + copymax=(PT_FPR15_LO+4); + realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]); + } + else if(useraddr<sizeof(user_regs_struct)) + { + copymax=sizeof(user_regs_struct); + realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]); + } + else + { + copymax=sizeof(struct user); + realuseraddr=(addr_t)NULL; + } + copylen=copymax-useraddr; + copylen=(copylen>len ? len:copylen); + if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask)) + return (-EIO); + copyaddr+=copylen; + len-=copylen; + useraddr+=copylen; + } + FixPerRegisters(task); + return(0); +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + unsigned long flags; + unsigned long tmp; + int copied; + ptrace_area parea; + + lock_kernel(); + if (request == PTRACE_TRACEME) + { + /* are we already being traced? */ + if (current->flags & PF_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->flags |= PF_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + read_unlock(&tasklist_lock); + if (!child) + goto out; + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; + if (request == PTRACE_ATTACH) + { + if (child == current) + goto out; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid)) && !capable(CAP_SYS_PTRACE)) + goto out; + /* the same process cannot be attached many times */ + if (child->flags & PF_PTRACED) + goto out; + child->flags |= PF_PTRACED; + + write_lock_irqsave(&tasklist_lock, flags); + if (child->p_pptr != current) + { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irqrestore(&tasklist_lock, flags); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out; + } + ret = -ESRCH; + // printk("child=%lX child->flags=%lX",child,child->flags); + if (!(child->flags & PF_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) + { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + + switch (request) + { + /* If I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + copied = access_process_vm(child,ADDR_BITS_REMOVE(addr), &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + goto out; + ret = put_user(tmp,(unsigned long *) data); + goto out; + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: + ret=copy_user(child,addr,data,sizeof(unsigned long),1,0); + break; + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child,ADDR_BITS_REMOVE(addr), &data, sizeof(data), 1) == sizeof(data)) + goto out; + ret = -EIO; + goto out; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1); + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->flags |= PF_TRACESYS; + else + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + clear_single_step(child); + wake_up_process(child); + /* make sure the single step bit is not set. */ + break; + + case PTRACE_SINGLESTEP: /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->flags &= ~PF_TRACESYS; + child->exit_code = data; + set_single_step(child); + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->flags &= ~(PF_PTRACED|PF_TRACESYS); + child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0) + ret=copy_user(child,parea.kernel_addr,parea.process_addr, + parea.len,1,(request==PTRACE_POKEUSR_AREA)); + break; + default: + ret = -EIO; + break; + } + out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(void) +{ + lock_kernel(); + if ((current->flags & (PF_PTRACED|PF_TRACESYS)) + != (PF_PTRACED|PF_TRACESYS)) + goto out; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } + out: + unlock_kernel(); +} diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S new file mode 100644 index 000000000..a77940905 --- /dev/null +++ b/arch/s390/kernel/reipl.S @@ -0,0 +1,67 @@ +#include <asm/lowcore.h> + .globl do_reipl +do_reipl: basr %r13,0 +.Lpg0: lpsw .Lnewpsw-.Lpg0(%r13) +.Lpg1: lctl %c6,%c6,.Lall-.Lpg0(%r13) + stctl %c0,%c0,.Lctlsave-.Lpg0(%r13) + ni .Lctlsave-.Lpg0(%r13),0xef + lctl %c0,%c0,.Lctlsave-.Lpg0(%r13) + lr %r1,%r2 + mvc __LC_PGM_NEW_PSW(8,0),.Lpcnew-.Lpg0(%r13) + stsch .Lschib-.Lpg0(%r13) + oi .Lschib+5-.Lpg0(%r13),0x84 +.Lecs: xi .Lschib+27-.Lpg0(%r13),0x01 + msch .Lschib-.Lpg0(%r13) + ssch .Liplorb-.Lpg0(%r13) + jz .L001 + bas %r14,.Ldisab-.Lpg0(%r13) +.L001: mvc __LC_IO_NEW_PSW(8,0),.Lionew-.Lpg0(%r13) +.Ltpi: lpsw .Lwaitpsw-.Lpg0(%r13) +.Lcont: c %r1,__LC_SUBCHANNEL_ID(%r0) + jnz .Ltpi + clc __LC_IO_INT_PARM(4),.Liplorb-.Lpg0(%r13) + jnz .Ltpi + tsch .Liplirb-.Lpg0(%r13) + tm .Liplirb+9-.Lpg0(%r13),0xbf + jz .L002 + bas %r14,.Ldisab-.Lpg0(%r13) +.L002: tm .Liplirb+8-.Lpg0(%r13),0xf3 + jz .L003 + bas %r14,.Ldisab-.Lpg0(%r13) +.L003: spx .Lnull-.Lpg0(%r13) + st %r1,__LC_SUBCHANNEL_ID(%r0) + lpsw 0 + sigp 0,0,0(6) +.Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) + lpsw .Ldispsw-.Lpg0(%r13) + .align 8 +.Lall: .long 0xff000000 +.Lnull: .long 0x00000000 +.Lctlsave: .long 0x00000000 + .align 8 +.Lnewpsw: .long 0x00080000,0x80000000+.Lpg1 +.Lpcnew: .long 0x00080000,0x80000000+.Lecs +.Lionew: .long 0x00080000,0x80000000+.Lcont +.Lwaitpsw: .long 0x020a0000,0x00000000+.Ltpi +.Ldispsw: .long 0x000a0000,0x00000000 +.Liplccws: .long 0x02000000,0x60000018 + .long 0x08000008,0x20000001 +.Liplorb: .long 0x0049504c,0x0000ff80 + .long 0x00000000+.Liplccws +.Lschib: .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Liplirb: .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + + + diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c new file mode 100644 index 000000000..85ecb5ce0 --- /dev/null +++ b/arch/s390/kernel/s390_ksyms.c @@ -0,0 +1,64 @@ +/* + * arch/s390/kernel/s390_ksyms.c + * + * S390 version + */ +#include <linux/config.h> +#include <linux/module.h> +#include <asm/irq.h> +#include <asm/string.h> +#include <asm/checksum.h> + +/* + * I/O subsystem + */ +EXPORT_SYMBOL(halt_IO); +EXPORT_SYMBOL(do_IO); +EXPORT_SYMBOL(resume_IO); +EXPORT_SYMBOL(ioinfo); +EXPORT_SYMBOL(get_dev_info_by_irq); +EXPORT_SYMBOL(get_dev_info_by_devno); +EXPORT_SYMBOL(get_irq_by_devno); +EXPORT_SYMBOL(get_devno_by_irq); +EXPORT_SYMBOL(get_irq_first); +EXPORT_SYMBOL(get_irq_next); + +/* + * memory management + */ +EXPORT_SYMBOL(_oi_bitmap); +EXPORT_SYMBOL(_ni_bitmap); +EXPORT_SYMBOL(_zb_findmap); + +/* + * string functions + */ +EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(strchr); +EXPORT_SYMBOL_NOVERS(strcmp); +EXPORT_SYMBOL_NOVERS(strncat); +EXPORT_SYMBOL_NOVERS(strncmp); +EXPORT_SYMBOL_NOVERS(strncpy); +EXPORT_SYMBOL_NOVERS(strnlen); +EXPORT_SYMBOL_NOVERS(strrchr); +EXPORT_SYMBOL_NOVERS(strtok); +EXPORT_SYMBOL_NOVERS(strpbrk); + +/* + * misc. + */ +#ifdef CONFIG_SMP +#include <asm/smplock.h> +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(global_bh_lock); +EXPORT_SYMBOL(kernel_flag); +#endif +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(csum_fold); + + diff --git a/arch/s390/kernel/s390dyn.c b/arch/s390/kernel/s390dyn.c new file mode 100644 index 000000000..0a5625830 --- /dev/null +++ b/arch/s390/kernel/s390dyn.c @@ -0,0 +1,36 @@ +/* + * arch/s390/kernel/s390dyn.c + * S/390 dynamic device attachment + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + */ + +#include <linux/init.h> + +#include <asm/irq.h> +#include <asm/s390io.h> +#include <asm/s390dyn.h> + +int s390_device_register( devreg_t *drinfo ) +{ + return -EOPNOTSUPP; +} + + +int s390_device_deregister ( devreg_t *dreg ) +{ + return -EOPNOTSUPP; +} + +int s390_request_irq_special( int irq, + io_handler_func_t io_handler, + not_oper_handler_func_t not_oper_handler, + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + return -EOPNOTSUPP; +} + diff --git a/arch/s390/kernel/s390fpu.c b/arch/s390/kernel/s390fpu.c new file mode 100644 index 000000000..42048abbc --- /dev/null +++ b/arch/s390/kernel/s390fpu.c @@ -0,0 +1,147 @@ +/* + * arch/s390/kernel/s390fpu.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * s390fpu.h functions for saving & restoring the fpu state. + * + * I couldn't inline these as linux/sched.h included half the world + * & was required to at the task structure. + * & the functions were too complex to make macros from. + * ( & as usual I didn't feel like debugging inline code ). + */ + +#include <linux/config.h> +#include <linux/sched.h> + +int save_fp_regs1(s390_fp_regs *fpregs) +{ + int has_ieee=MACHINE_HAS_IEEE; +/* + I don't think we can use STE here as this would load + fp registers 0 & 2 into memory locations 0 & 1 etc. + */ + asm volatile ("STD 0,8(%0)\n\t" + "STD 2,24(%0)\n\t" + "STD 4,40(%0)\n\t" + "STD 6,56(%0)" + : + : "a" (fpregs) + : "memory" + ); + if(has_ieee) + { + asm volatile ("STFPC 0(%0)\n\t" + "STD 1,16(%0)\n\t" + "STD 3,32(%0)\n\t" + "STD 5,48(%0)\n\t" + "STD 7,64(%0)\n\t" + "STD 8,72(%0)\n\t" + "STD 9,80(%0)\n\t" + "STD 10,88(%0)\n\t" + "STD 11,96(%0)\n\t" + "STD 12,104(%0)\n\t" + "STD 13,112(%0)\n\t" + "STD 14,120(%0)\n\t" + "STD 15,128(%0)\n\t" + : + : "a" (fpregs) + : "memory" + ); + } + return(has_ieee); +} + + +void save_fp_regs(s390_fp_regs *fpregs) +{ +#if CONFIG_IEEEFPU_EMULATION + s390_fp_regs *currentfprs; +#endif +#if CONFIG_IEEEFPU_EMULATION + if(!save_fp_regs1(fpregs)) + { + currentfprs=¤t->thread.fp_regs; + fpregs->fpc=currentfprs->fpc; + fpregs->fprs[1].d=currentfprs->fprs[1].d; + fpregs->fprs[3].d=currentfprs->fprs[3].d; + fpregs->fprs[5].d=currentfprs->fprs[5].d; + fpregs->fprs[7].d=currentfprs->fprs[7].d; + memcpy(&fpregs->fprs[8].d,¤tfprs->fprs[8].d,sizeof(freg_t)*8); + } +#else + save_fp_regs1(fpregs); +#endif +} + + +int restore_fp_regs1(s390_fp_regs *fpregs) +{ + int has_ieee=MACHINE_HAS_IEEE; + + asm volatile ("LD 0,8(%0)\n\t" + "LD 2,24(%0)\n\t" + "LD 4,40(%0)\n\t" + "LD 6,56(%0)" + : + : "a" (fpregs) + : "memory" + ); + if(has_ieee) + { + asm volatile ("LFPC 0(%0)\n\t" + "LD 1,16(%0)\n\t" + "LD 3,32(%0)\n\t" + "LD 5,48(%0)\n\t" + "LD 7,64(%0)\n\t" + "LD 8,72(%0)\n\t" + "LD 9,80(%0)\n\t" + "LD 10,88(%0)\n\t" + "LD 11,96(%0)\n\t" + "LD 12,104(%0)\n\t" + "LD 13,112(%0)\n\t" + "LD 14,120(%0)\n\t" + "LD 15,128(%0)\n\t" + : + : "a" (fpregs) + : "memory" + ); + } + return(has_ieee); +} + +void restore_fp_regs(s390_fp_regs *fpregs) +{ +#if CONFIG_IEEEFPU_EMULATION + s390_fp_regs *currentfprs; +#endif + +#if CONFIG_IEEEFPU_EMULATION + if(!restore_fp_regs1(fpregs)) + { + currentfprs=¤t->thread.fp_regs; + currentfprs->fpc=fpregs->fpc; + currentfprs->fprs[1].d=fpregs->fprs[1].d; + currentfprs->fprs[3].d=fpregs->fprs[3].d; + currentfprs->fprs[5].d=fpregs->fprs[5].d; + currentfprs->fprs[7].d=fpregs->fprs[7].d; + memcpy(¤tfprs->fprs[8].d,&fpregs->fprs[8].d,sizeof(freg_t)*8); + } +#else + restore_fp_regs1(fpregs); +#endif +} + + + + + + + + + + + + diff --git a/arch/s390/kernel/s390io.c b/arch/s390/kernel/s390io.c new file mode 100644 index 000000000..7a4e531d5 --- /dev/null +++ b/arch/s390/kernel/s390io.c @@ -0,0 +1,4602 @@ +/* + * arch/s390/kernel/s390io.c + * S/390 common I/O routines + * + * S390 version + * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, + * IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/smp.h> +#include <linux/tasks.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/bootmem.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/smp.h> +#include <asm/pgtable.h> +#include <asm/delay.h> +#include <asm/processor.h> +#include <asm/lowcore.h> + +#include <asm/s390io.h> +#include <asm/s390dyn.h> +#include <asm/s390mach.h> + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#undef CONFIG_DEBUG_IO + +#define REIPL_DEVID_MAGIC 0x87654321 + +struct irqaction init_IRQ_action; +unsigned int highest_subchannel; +ioinfo_t *ioinfo_head = NULL; +ioinfo_t *ioinfo_tail = NULL; +ioinfo_t *ioinfo[__MAX_SUBCHANNELS] = { + [0 ... (__MAX_SUBCHANNELS-1)] = INVALID_STORAGE_AREA +}; + +static spinlock_t sync_isc; // synchronous irq processing lock +static psw_t io_sync_wait; // wait PSW for sync IO, prot. by sync_isc +static psw_t io_new_psw; // save I/O new PSW, prot. by sync_isc +static int cons_dev = -1; // identify console device +static int init_IRQ_complete = 0; +static schib_t init_schib; +static __u64 irq_IPL_TOD; + +/* + * Dummy controller type for unused interrupts + */ +int do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;} +int enable_none(unsigned int irq) { return(-ENODEV); } +int disable_none(unsigned int irq) { return(-ENODEV); } + +struct hw_interrupt_type no_irq_type = { + "none", + do_none, + enable_none, + disable_none +}; + +static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs); +static int s390_setup_irq(unsigned int irq, struct irqaction * new); +static void s390_process_subchannels( void); +static void s390_device_recognition( void); +static int s390_validate_subchannel( int irq); +static int s390_SenseID( int irq, senseid_t *sid); +static int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid); +static int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid); +static int s390_process_IRQ( unsigned int irq ); +static int s390_DevicePathVerification( int irq ); + +extern int do_none(unsigned int irq, int cpu, struct pt_regs * regs); +extern int enable_none(unsigned int irq); +extern int disable_none(unsigned int irq); +extern void tod_wait(unsigned long usecs); + +asmlinkage void do_IRQ( struct pt_regs regs, + unsigned int irq, + __u32 s390_intparm ); + +void s390_displayhex(char *str,void *ptr,s32 cnt); + +void s390_displayhex(char *str,void *ptr,s32 cnt) +{ + s32 cnt1,cnt2,maxcnt2; + u32 *currptr=(__u32 *)ptr; + + printk("\n%s\n",str); + + for(cnt1=0;cnt1<cnt;cnt1+=16) + { + printk("%08lX ",(unsigned long)currptr); + maxcnt2=cnt-cnt1; + if(maxcnt2>16) + maxcnt2=16; + for(cnt2=0;cnt2<maxcnt2;cnt2+=4) + printk("%08X ",*currptr++); + printk("\n"); + } +} + +int s390_request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + int retval; + struct irqaction *action; + + if (irq >= __MAX_SUBCHANNELS) + return -EINVAL; + + if ( !handler || !dev_id ) + return -EINVAL; + + /* + * during init_IRQ() processing we don't have memory + * management yet, thus need to use a statically + * allocated irqaction control block + */ + if ( init_IRQ_complete ) + { + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_KERNEL); + } + else + { + action = &init_IRQ_action; + + } /* endif */ + + if (!action) + { + return -ENOMEM; + + } /* endif */ + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = s390_setup_irq( irq, action); + + if ( !retval ) + { + retval = s390_DevicePathVerification( irq ); + } + else if ( retval && init_IRQ_complete ) + { + kfree(action); + + } /* endif */ + + return retval; +} + +void s390_free_irq(unsigned int irq, void *dev_id) +{ + unsigned int flags; + int ret; + + unsigned int count = 0; + + if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return; + + } /* endif */ + + s390irq_spin_lock_irqsave( irq, flags); + +#ifdef CONFIG_KERNEL_DEBUG + if ( irq != cons_dev ) + { + printk("Trying to free IRQ%d\n",irq); + + } /* endif */ +#endif + + /* + * disable the device and reset all IRQ info if + * the IRQ is actually owned by the handler ... + */ + if ( ioinfo[irq]->irq_desc.action ) + { + if ( (dev_id == ioinfo[irq]->irq_desc.action->dev_id ) + || (dev_id == (devstat_t *)REIPL_DEVID_MAGIC) ) + { + /* start deregister */ + ioinfo[irq]->ui.flags.unready = 1; + + do + { + ret = ioinfo[irq]->irq_desc.handler->disable(irq); + + count++; + + if ( ret == -EBUSY ) + { + int iret; + + /* + * kill it ! + * ... we first try sync and eventually + * try terminating the current I/O by + * an async request, twice halt, then + * clear. + */ + if ( count < 3 ) + { + iret = halt_IO( irq, + 0xC8C1D3E3, + DOIO_WAIT_FOR_INTERRUPT ); + + if ( iret == -EBUSY ) + { + halt_IO( irq, 0xC8C1D3E3, 0); + s390irq_spin_unlock_irqrestore( irq, flags); + tod_wait( 200000 ); /* 200 ms */ + s390irq_spin_lock_irqsave( irq, flags); + + } /* endif */ + } + else + { + iret = clear_IO( irq, + 0x40C3D3D9, + DOIO_WAIT_FOR_INTERRUPT ); + + if ( iret == -EBUSY ) + { + clear_IO( irq, 0xC8C1D3E3, 0); + s390irq_spin_unlock_irqrestore( irq, flags); + tod_wait( 1000000 ); /* 1000 ms */ + s390irq_spin_lock_irqsave( irq, flags); + + } /* endif */ + + } /* endif */ + + if ( count == 3 ) + { + /* give it a very last try ... */ + ioinfo[irq]->irq_desc.handler->disable(irq); + + if ( ioinfo[irq]->ui.flags.busy ) + { + printk( KERN_CRIT"free_irq(%04X) " + "- device %04X busy, retry " + "count exceeded\n", + irq, + ioinfo[irq]->devstat.devno); + + } /* endif */ + + break; /* sigh, let's give up ... */ + + } /* endif */ + + } /* endif */ + + } while ( ret == -EBUSY ); + + if ( init_IRQ_complete ) + kfree( ioinfo[irq]->irq_desc.action ); + + ioinfo[irq]->irq_desc.action = NULL; + ioinfo[irq]->ui.flags.ready = 0; + + ioinfo[irq]->irq_desc.handler->enable = &enable_none; + ioinfo[irq]->irq_desc.handler->disable = &disable_none; + + ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */ + + s390irq_spin_unlock_irqrestore( irq, flags); + } + else + { + s390irq_spin_unlock_irqrestore( irq, flags); + + printk("free_irq() : error, dev_id does not match !"); + + } /* endif */ + + } + else + { + s390irq_spin_unlock_irqrestore( irq, flags); + + printk("free_irq() : error, no action block ... !"); + + } /* endif */ + +} + +/* + * Generic enable/disable code + */ +int disable_irq(unsigned int irq) +{ + unsigned long flags; + int ret; + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + + s390irq_spin_lock_irqsave(irq, flags); + + /* + * At this point we may actually have a pending interrupt being active + * on another CPU. So don't touch the IRQ_INPROGRESS bit.. + */ + ioinfo[irq]->irq_desc.status |= IRQ_DISABLED; + ret = ioinfo[irq]->irq_desc.handler->disable(irq); + s390irq_spin_unlock_irqrestore(irq, flags); + + synchronize_irq(); + + return( ret); +} + +int enable_irq(unsigned int irq) +{ + unsigned long flags; + int ret; + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + + s390irq_spin_lock_irqsave(irq, flags); + + ioinfo[irq]->irq_desc.status = 0; + ret = ioinfo[irq]->irq_desc.handler->enable(irq); + + s390irq_spin_unlock_irqrestore(irq, flags); + + return(ret); +} + +/* + * Enable IRQ by modifying the subchannel + */ +static int enable_subchannel( unsigned int irq) +{ + int ret; + int ccode; + int retry = 5; + + if ( irq > highest_subchannel || irq < 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + + /* + * If a previous disable request is pending we reset it. However, this + * status implies that the device may (still) be not-operational. + */ + if ( ioinfo[irq]->ui.flags.d_disable ) + { + ioinfo[irq]->ui.flags.d_disable = 0; + ret = 0; + } + else + { + + ccode = stsch(irq, &(ioinfo[irq]->schib) ); + + if ( ccode ) + { + ret = -ENODEV; + } + else + { + ioinfo[irq]->schib.pmcw.ena = 1; + + do + { + ccode = msch( irq, &(ioinfo[irq]->schib) ); + + switch (ccode) { + case 0: + ret = 0; + break; + + case 1: + /* + * very bad, requires interrupt alike + * processing, where "rbh" is a dummy + * parameter for interface compatibility + * only. Bottom-half handling cannot be + * required as this must be an + * unsolicited interrupt (!busy). + */ + + ioinfo[irq]->ui.flags.s_pend = 1; + + s390_process_IRQ( irq ); + + ioinfo[irq]->ui.flags.s_pend = 0; + + ret = -EIO; /* might be overwritten */ + /* ... on re-driving */ + /* ... the msch() */ + retry--; + break; + + case 3: + ioinfo[irq]->ui.flags.oper = 0; + ret = -ENODEV; + break; + + default: + printk( KERN_CRIT"enable_subchannel(%04X) " + " : ccode 2 on msch() for device " + "%04X received !\n", + irq, + ioinfo[irq]->devstat.devno); + ret = -ENODEV; // never reached + } + + } while ( (ccode == 1) && retry ); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + + +/* + * Disable IRQ by modifying the subchannel + */ +static int disable_subchannel( unsigned int irq) +{ + int cc; /* condition code */ + int ret; /* function return value */ + int retry = 5; + + if ( irq > highest_subchannel ) + { + ret = -ENODEV; + } + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + else if ( ioinfo[irq]->ui.flags.busy ) + { + /* + * the disable function must not be called while there are + * requests pending for completion ! + */ + ret = -EBUSY; + } + else + { + /* + * If device isn't operational we have to perform delayed + * disabling when the next interrupt occurs - unless the + * irq is re-requested prior to the interrupt to occur. + */ + cc = stsch(irq, &(ioinfo[irq]->schib) ); + + if ( cc == 3 ) + { + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->ui.flags.d_disable = 1; + + ret = 0; + } + else // cc == 0 + { + ioinfo[irq]->schib.pmcw.ena = 0; + + do + { + cc = msch( irq, &(ioinfo[irq]->schib) ); + + switch (cc) { + case 0 : + ret = 0; /* done */ + break; + + case 1 : + /* + * very bad, requires interrupt alike + * processing, where "rbh" is a dummy + * parm for interface compatibility + * only. Bottom-half handling cannot + * be required as this must be an + * unsolicited interrupt (!busy). + */ + ioinfo[irq]->ui.flags.s_pend = 1; + s390_process_IRQ( irq ); + ioinfo[irq]->ui.flags.s_pend = 0; + + ret = -EBUSY; /* might be overwritten */ + /* ... on re-driving the */ + /* ... msch() call */ + retry--; + break; + + case 2 : + /* + * *** must not occur ! *** + * *** *** + * *** indicates our internal *** + * *** interrupt accounting is out *** + * *** of sync ===> panic() *** + */ + printk( KERN_CRIT"disable_subchannel(%04X) " + "- unexpected busy condition for " + "device %04X received !\n", + irq, + ioinfo[irq]->devstat.devno); + + ret = -ENODEV; // never reached + break; + + case 3 : + /* + * should hardly occur ?! + */ + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->ui.flags.d_disable = 1; + + ret = 0; /* if the device has gone we */ + /* ... don't need to disable */ + /* ... it anymore ! */ + break; + + default : + ret = -ENODEV; // never reached ... + break; + + } /* endswitch */ + + } while ( (cc == 1) && retry ); + + } /* endif */ + + } /* endif */ + + return( ret); +} + + + +int s390_setup_irq( unsigned int irq, struct irqaction * new) +{ + unsigned long flags; + int rc = 0; + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + + /* + * The following block of code has to be executed atomically + */ + s390irq_spin_lock_irqsave( irq, flags); + + if ( ioinfo[irq]->irq_desc.action == NULL ) + { + ioinfo[irq]->irq_desc.action = new; + ioinfo[irq]->irq_desc.status = 0; + ioinfo[irq]->irq_desc.handler->enable = &enable_subchannel; + ioinfo[irq]->irq_desc.handler->disable = &disable_subchannel; + ioinfo[irq]->irq_desc.handler->handle = &handle_IRQ_event; + + ioinfo[irq]->ui.flags.ready = 1; + + ioinfo[irq]->irq_desc.handler->enable(irq); + } + else + { + /* + * interrupt already owned, and shared interrupts + * aren't supported on S/390. + */ + rc = -EBUSY; + + } /* endif */ + + s390irq_spin_unlock_irqrestore(irq,flags); + + return( rc); +} + +void s390_init_IRQ( void ) +{ + unsigned long flags; /* PSW flags */ + long cr6 __attribute__ ((aligned (8))); + + // Hopefully bh_count's will get set when we copy the prefix lowcore + // structure to other CPI's ( DJB ) + atomic_set(&S390_lowcore.local_bh_count,0); + atomic_set(&S390_lowcore.local_irq_count,0); + + asm volatile ("STCK %0" : "=m" (irq_IPL_TOD)); + + /* + * As we don't know about the calling environment + * we assure running disabled. Before leaving the + * function we resestablish the old environment. + * + * Note : as we don't need a system wide lock, therefore + * we shouldn't use cli(), but __cli() as this + * affects the current CPU only. + */ + __save_flags(flags); + __cli(); + + /* + * disable all interrupts + */ + cr6 = 0; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + s390_process_subchannels(); + + /* + * enable default I/O-interrupt sublass 3 + */ + cr6 = 0x10000000; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + s390_device_recognition(); + + init_IRQ_complete = 1; + + s390_init_machine_check(); + + __restore_flags(flags); + + return; +} + + +/* + * dummy handler, used during init_IRQ() processing for compatibility only + */ +void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs) +{ + /* this is a dummy handler only ... */ +} + + +int s390_start_IO( int irq, /* IRQ */ + ccw1_t *cpa, /* logical channel prog addr */ + unsigned long user_intparm, /* interruption parameter */ + __u8 lpm, /* logical path mask */ + unsigned long flag) /* flags */ +{ + int ccode; + unsigned long psw_flags; + + int sync_isc_locked = 0; + int ret = 0; + + /* + * The flag usage is mutal exclusive ... + */ + if ( (flag & DOIO_RETURN_CHAN_END) + && (flag & DOIO_REPORT_ALL ) ) + { + return( -EINVAL ); + + } /* endif */ + + memset( &(ioinfo[irq]->orb), '\0', sizeof( orb_t) ); + + /* + * setup ORB + */ + ioinfo[irq]->orb.intparm = (__u32)&ioinfo[irq]->u_intparm; + ioinfo[irq]->orb.fmt = 1; + + ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH); + ioinfo[irq]->orb.spnd = (flag & DOIO_ALLOW_SUSPEND); + ioinfo[irq]->orb.ssic = ( (flag & DOIO_ALLOW_SUSPEND ) + && (flag & DOIO_SUPPRESS_INTER) ); + + if ( flag & DOIO_VALID_LPM ) + { + ioinfo[irq]->orb.lpm = lpm; + } + else + { + ioinfo[irq]->orb.lpm = ioinfo[irq]->opm; + + } /* endif */ + + ioinfo[irq]->orb.cpa = (__u32)virt_to_phys( cpa); + + /* + * If sync processing was requested we lock the sync ISC, modify the + * device to present interrupts for this ISC only and switch the + * CPU to handle this ISC + the console ISC exclusively. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + // + // check whether we run recursively (sense processing) + // + if ( !ioinfo[irq]->ui.flags.syncio ) + { + spin_lock_irqsave( &sync_isc, psw_flags); + + ret = enable_cpu_sync_isc( irq); + + if ( ret ) + { + spin_unlock_irqrestore( &sync_isc, psw_flags); + return( ret); + } + else + { + sync_isc_locked = 1; // local + ioinfo[irq]->ui.flags.syncio = 1; // global + + } /* endif */ + + } /* endif */ + + } /* endif */ + + /* + * Issue "Start subchannel" and process condition code + */ + ccode = ssch( irq, &(ioinfo[irq]->orb) ); + + switch ( ccode ) { + case 0: + + if ( !ioinfo[irq]->ui.flags.w4sense ) + { + /* + * init the device driver specific devstat irb area + * + * Note : don´t clear saved irb info in case of sense ! + */ + memset( &((devstat_t *)ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, + '\0', sizeof( irb_t) ); + } /* endif */ + + /* + * initialize device status information + */ + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; + + ioinfo[irq]->u_intparm = user_intparm; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.lpum = 0; + ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION; + ioinfo[irq]->devstat.scnt = 0; + + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + + /* + * Check for either early (FAST) notification requests + * or if we are to return all interrupt info. + * Default is to call IRQ handler at secondary status only + */ + if ( flag & DOIO_RETURN_CHAN_END ) + { + ioinfo[irq]->ui.flags.fast = 1; + } + else if ( flag & DOIO_REPORT_ALL ) + { + ioinfo[irq]->ui.flags.repall = 1; + + } /* endif */ + + ioinfo[irq]->ulpm = ioinfo[irq]->orb.lpm; + + /* + * If synchronous I/O processing is requested, we have + * to wait for the corresponding interrupt to occur by + * polling the interrupt condition. However, as multiple + * interrupts may be outstanding, we must not just wait + * for the first interrupt, but must poll until ours + * pops up. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + // __u32 io_parm; + psw_t io_new_psw; + int ccode; + + int ready = 0; + int io_sub = -1; + struct _lowcore *lc = NULL; + int count = 30000; + + /* + * We shouldn't perform a TPI loop, waiting for an + * interrupt to occur, but should load a WAIT PSW + * instead. Otherwise we may keep the channel subsystem + * busy, not able to present the interrupt. When our + * sync. interrupt arrived we reset the I/O old PSW to + * its original value. + */ + memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); + + ccode = iac(); + + switch (ccode) { + case 0: // primary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_PRIM_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 1: // secondary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_SEC_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 2: // access-register + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_ACC_REG_MODE + | _PSW_IO_WAIT; + break; + case 3: // home-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_HOME_SPACE_MODE + | _PSW_IO_WAIT; + break; + default: + panic( "start_IO() : unexpected " + "address-space-control %d\n", + ccode); + break; + } /* endswitch */ + + io_sync_wait.addr = FIX_PSW(&&io_wakeup); + + /* + * Martin didn't like modifying the new PSW, now we take + * a fast exit in do_IRQ() instead + */ + *(__u32 *)__LC_SYNC_IO_WORD = 1; + + do + { + if ( flag & DOIO_TIMEOUT ) + { + tpi_info_t tpi_info; + + do + { + if ( tpi(&tpi_info) == 1 ) + { + io_sub = tpi_info.irq; + break; + } + else + { + count--; + tod_wait(100); /* usecs */ + + } /* endif */ + + } while ( count ); + } + else + { + asm volatile ("lpsw %0" : : "m" (io_sync_wait)); + +io_wakeup: + io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; + + } /* endif */ + + if ( count ) + ready = s390_process_IRQ( io_sub ); + + /* + * surrender when retry count's exceeded ... + */ + + } while ( !( ( io_sub == irq ) + && ( ready == 1 )) + && count ); + + *(__u32 *)__LC_SYNC_IO_WORD = 0; + + if ( !count ) + ret = -ETIMEDOUT; + + } /* endif */ + + break; + + case 1 : /* status pending */ + + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; + + /* + * initialize the device driver specific devstat irb area + */ + memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, + '\0', sizeof( irb_t) ); + + /* + * Let the common interrupt handler process the pending status. + * However, we must avoid calling the user action handler, as + * it won't be prepared to handle a pending status during + * do_IO() processing inline. This also implies that process_IRQ + * must terminate synchronously - especially if device sensing + * is required. + */ + ioinfo[irq]->ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; + + s390_process_IRQ( irq ); + + ioinfo[irq]->ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * In multipath mode a condition code 3 implies the last path + * has gone, except we have previously restricted the I/O to + * a particular path. A condition code 1 (0 won't occur) + * results in return code EIO as well as 3 with another path + * than the one used (i.e. path available mask is non-zero). + */ + if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) + { + ret = -ENODEV; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 0; + +#if CONFIG_DEBUG_IO + { + char buffer[80]; + + stsch(irq, &(ioinfo[irq]->schib) ); + + sprintf( buffer, "s390_start_IO(%04X) - irb for " + "device %04X, after status pending\n", + irq, + ioinfo[irq]->devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq]->devstat.ii.irb) , + sizeof(irb_t)); + + sprintf( buffer, "s390_start_IO(%04X) - schib for " + "device %04X, after status pending\n", + irq, + ioinfo[irq]->devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq]->schib) , + sizeof(schib_t)); + + + if (ioinfo[irq]->devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) + { + sprintf( buffer, "s390_start_IO(%04X) - sense " + "data for " + "device %04X, after status pending\n", + irq, + ioinfo[irq]->devstat.devno ); + + s390_displayhex( buffer, + ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->ii.sense.data, + ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->rescnt); + + } + } +#endif + } + else + { + ret = -EIO; + ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 1; + + } /* endif */ + + break; + + case 2 : /* busy */ + + ret = -EBUSY; + break; + + default: /* device not operational */ + + ret = -ENODEV; + ioinfo[irq]->ui.flags.oper = 0; + + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + + memcpy( ioinfo[irq]->irq_desc.action->dev_id, + &(ioinfo[irq]->devstat), + sizeof( devstat_t) ); + +#if CONFIG_DEBUG_IO + { + char buffer[80]; + + stsch(irq, &(ioinfo[irq]->schib) ); + + sprintf( buffer, "s390_start_IO(%04X) - schib for " + "device %04X, after 'not oper' status\n", + irq, + ioinfo[irq]->devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq]->schib), + sizeof(schib_t)); + } +#endif + break; + + } /* endswitch */ + + if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) + && ( sync_isc_locked ) ) + { + disable_cpu_sync_isc( irq ); + + spin_unlock_irqrestore( &sync_isc, psw_flags); + + sync_isc_locked = 0; // local setting + ioinfo[irq]->ui.flags.syncio = 0; // global setting + + } /* endif */ + + return( ret); +} + +int do_IO( int irq, /* IRQ */ + ccw1_t *cpa, /* channel program address */ + unsigned long user_intparm, /* interruption parameter */ + __u8 lpm, /* logical path mask */ + unsigned long flag) /* flags : see above */ +{ + int ret = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + + /* handler registered ? or free_irq() in process already ? */ + if ( !ioinfo[irq]->ui.flags.ready || ioinfo[irq]->ui.flags.unready ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * Note: We ignore the device operational status - if not operational, + * the SSCH will lead to an -ENODEV condition ... + */ + if ( !ioinfo[irq]->ui.flags.busy ) /* last I/O completed ? */ + { + ret = s390_start_IO( irq, cpa, user_intparm, lpm, flag); + } + else if ( ioinfo[irq]->ui.flags.fast ) + { + /* + * If primary status was received and ending status is missing, + * the device driver won't be notified on the ending status + * if early (fast) interrupt notification was requested. + * Therefore we have to queue the next incoming request. If + * halt_IO() is issued while there is a request queued, a HSCH + * needs to be issued and the queued request must be deleted + * but its intparm must be returned (see halt_IO() processing) + */ + if ( ioinfo[irq]->ui.flags.w4final + && !ioinfo[irq]->ui.flags.doio_q ) + { + ioinfo[irq]->qflag = flag; + ioinfo[irq]->qcpa = cpa; + ioinfo[irq]->qintparm = user_intparm; + ioinfo[irq]->qlpm = lpm; + } + else + { + ret = -EBUSY; + + } /* endif */ + } + else + { + ret = -EBUSY; + + } /* endif */ + + return( ret ); + +} + +/* + * resume suspended I/O operation + */ +int resume_IO( int irq) +{ + int ret = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + + /* + * We allow for 'resume' requests only for active I/O operations + */ + if ( ioinfo[irq]->ui.flags.busy ) + { + int ccode; + + ccode = rsch( irq); + + switch (ccode) { + case 0 : + break; + + case 1 : + s390_process_IRQ( irq ); + ret = -EBUSY; + break; + + case 2 : + ret = -EINVAL; + break; + + case 3 : + /* + * useless to wait for request completion + * as device is no longer operational ! + */ + ioinfo[irq]->ui.flags.oper = 0; + ioinfo[irq]->ui.flags.busy = 0; + ret = -ENODEV; + break; + + } /* endswitch */ + + } + else + { + ret = -ENOTCONN; + + } /* endif */ + + return( ret); +} + +/* + * Note: The "intparm" parameter is not used by the halt_IO() function + * itself, as no ORB is built for the HSCH instruction. However, + * it allows the device interrupt handler to associate the upcoming + * interrupt with the halt_IO() request. + */ +int halt_IO( int irq, + unsigned long user_intparm, + unsigned long flag) /* possible DOIO_WAIT_FOR_INTERRUPT */ +{ + int ret; + int ccode; + unsigned long psw_flags; + + int sync_isc_locked = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + ret = -ENODEV; + } + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + + /* + * we only allow for halt_IO if the device has an I/O handler associated + */ + else if ( !ioinfo[irq]->ui.flags.ready ) + { + ret = -ENODEV; + } + /* + * we ignore the halt_io() request if ending_status was received but + * a SENSE operation is waiting for completion. + */ + else if ( ioinfo[irq]->ui.flags.w4sense ) + { + ret = 0; + } + /* + * We don't allow for halt_io with a sync do_IO() requests pending. + */ + else if ( ioinfo[irq]->ui.flags.syncio ) + { + ret = -EBUSY; + } + else + { + /* + * If sync processing was requested we lock the sync ISC, + * modify the device to present interrupts for this ISC only + * and switch the CPU to handle this ISC + the console ISC + * exclusively. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + // + // check whether we run recursively (sense processing) + // + if ( !ioinfo[irq]->ui.flags.syncio ) + { + spin_lock_irqsave( &sync_isc, psw_flags); + + ret = enable_cpu_sync_isc( irq); + + if ( ret ) + { + spin_unlock_irqrestore( &sync_isc, + psw_flags); + return( ret); + } + else + { + sync_isc_locked = 1; // local + ioinfo[irq]->ui.flags.syncio = 1; // global + + } /* endif */ + + } /* endif */ + + } /* endif */ + + /* + * Issue "Halt subchannel" and process condition code + */ + ccode = hsch( irq ); + + switch ( ccode ) { + case 0: + + ioinfo[irq]->ui.flags.haltio = 1; + + if ( !ioinfo[irq]->ui.flags.doio ) + { + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->u_intparm = user_intparm; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.lpum = 0; + ioinfo[irq]->devstat.flag = DEVSTAT_HALT_FUNCTION; + ioinfo[irq]->devstat.scnt = 0; + + } + else + { + ioinfo[irq]->devstat.flag |= DEVSTAT_HALT_FUNCTION; + + } /* endif */ + + /* + * If synchronous I/O processing is requested, we have + * to wait for the corresponding interrupt to occur by + * polling the interrupt condition. However, as multiple + * interrupts may be outstanding, we must not just wait + * for the first interrupt, but must poll until ours + * pops up. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + int io_sub; + __u32 io_parm; + psw_t io_new_psw; + int ccode; + + int ready = 0; + struct _lowcore *lc = NULL; + + /* + * We shouldn't perform a TPI loop, waiting for + * an interrupt to occur, but should load a + * WAIT PSW instead. Otherwise we may keep the + * channel subsystem busy, not able to present + * the interrupt. When our sync. interrupt + * arrived we reset the I/O old PSW to its + * original value. + */ + memcpy( &io_new_psw, + &lc->io_new_psw, + sizeof(psw_t)); + + ccode = iac(); + + switch (ccode) { + case 0: // primary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_PRIM_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 1: // secondary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_SEC_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 2: // access-register + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_ACC_REG_MODE + | _PSW_IO_WAIT; + break; + case 3: // home-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_HOME_SPACE_MODE + | _PSW_IO_WAIT; + break; + default: + panic( "halt_IO() : unexpected " + "address-space-control %d\n", + ccode); + break; + } /* endswitch */ + + io_sync_wait.addr = FIX_PSW(&&hio_wakeup); + + /* + * Martin didn't like modifying the new PSW, now we take + * a fast exit in do_IRQ() instead + */ + *(__u32 *)__LC_SYNC_IO_WORD = 1; + + do + { + + asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); +hio_wakeup: + io_parm = *(__u32 *)__LC_IO_INT_PARM; + io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; + + ready = s390_process_IRQ( io_sub ); + + } while ( !((io_sub == irq) && (ready == 1)) ); + + *(__u32 *)__LC_SYNC_IO_WORD = 0; + + } /* endif */ + + ret = 0; + break; + + case 1 : /* status pending */ + + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; + + /* + * initialize the device driver specific devstat irb area + */ + memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, + '\0', sizeof( irb_t) ); + + /* + * Let the common interrupt handler process the pending + * status. However, we must avoid calling the user + * action handler, as it won't be prepared to handle + * a pending status during do_IO() processing inline. + * This also implies that s390_process_IRQ must + * terminate synchronously - especially if device + * sensing is required. + */ + ioinfo[irq]->ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; + + s390_process_IRQ( irq ); + + ioinfo[irq]->ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * In multipath mode a condition code 3 implies the last + * path has gone, except we have previously restricted + * the I/O to a particular path. A condition code 1 + * (0 won't occur) results in return code EIO as well + * as 3 with another path than the one used (i.e. path available mask is non-zero). + */ + if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) + { + ret = -ENODEV; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 0; + } + else + { + ret = -EIO; + ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 1; + + } /* endif */ + + break; + + case 2 : /* busy */ + + ret = -EBUSY; + break; + + default: /* device not operational */ + + ret = -ENODEV; + break; + + } /* endswitch */ + + if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) + && ( sync_isc_locked ) ) + { + sync_isc_locked = 0; // local setting + ioinfo[irq]->ui.flags.syncio = 0; // global setting + + disable_cpu_sync_isc( irq ); + + spin_unlock_irqrestore( &sync_isc, psw_flags); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + +/* + * Note: The "intparm" parameter is not used by the clear_IO() function + * itself, as no ORB is built for the CSCH instruction. However, + * it allows the device interrupt handler to associate the upcoming + * interrupt with the clear_IO() request. + */ +int clear_IO( int irq, + unsigned long user_intparm, + unsigned long flag) /* possible DOIO_WAIT_FOR_INTERRUPT */ +{ + int ret; + int ccode; + unsigned long psw_flags; + + int sync_isc_locked = 0; + + if ( irq > highest_subchannel || irq < 0 ) + { + ret = -ENODEV; + } + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + + /* + * we only allow for halt_IO if the device has an I/O handler associated + */ + else if ( !ioinfo[irq]->ui.flags.ready ) + { + ret = -ENODEV; + } + /* + * we ignore the halt_io() request if ending_status was received but + * a SENSE operation is waiting for completion. + */ + else if ( ioinfo[irq]->ui.flags.w4sense ) + { + ret = 0; + } + /* + * We don't allow for halt_io with a sync do_IO() requests pending. + * Concurrent I/O is possible in SMP environments only, but the + * sync. I/O request can be gated to one CPU at a time only. + */ + else if ( ioinfo[irq]->ui.flags.syncio ) + { + ret = -EBUSY; + } + else + { + /* + * If sync processing was requested we lock the sync ISC, + * modify the device to present interrupts for this ISC only + * and switch the CPU to handle this ISC + the console ISC + * exclusively. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + // + // check whether we run recursively (sense processing) + // + if ( !ioinfo[irq]->ui.flags.syncio ) + { + spin_lock_irqsave( &sync_isc, psw_flags); + + ret = enable_cpu_sync_isc( irq); + + if ( ret ) + { + spin_unlock_irqrestore( &sync_isc, + psw_flags); + return( ret); + } + else + { + sync_isc_locked = 1; // local + ioinfo[irq]->ui.flags.syncio = 1; // global + + } /* endif */ + + } /* endif */ + + } /* endif */ + + /* + * Issue "Halt subchannel" and process condition code + */ + ccode = csch( irq ); + + switch ( ccode ) { + case 0: + + ioinfo[irq]->ui.flags.haltio = 1; + + if ( !ioinfo[irq]->ui.flags.doio ) + { + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->u_intparm = user_intparm; + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.lpum = 0; + ioinfo[irq]->devstat.flag = DEVSTAT_CLEAR_FUNCTION; + ioinfo[irq]->devstat.scnt = 0; + + } + else + { + ioinfo[irq]->devstat.flag |= DEVSTAT_CLEAR_FUNCTION; + + } /* endif */ + + /* + * If synchronous I/O processing is requested, we have + * to wait for the corresponding interrupt to occur by + * polling the interrupt condition. However, as multiple + * interrupts may be outstanding, we must not just wait + * for the first interrupt, but must poll until ours + * pops up. + */ + if ( flag & DOIO_WAIT_FOR_INTERRUPT ) + { + int io_sub; + __u32 io_parm; + psw_t io_new_psw; + int ccode; + + int ready = 0; + struct _lowcore *lc = NULL; + + /* + * We shouldn't perform a TPI loop, waiting for + * an interrupt to occur, but should load a + * WAIT PSW instead. Otherwise we may keep the + * channel subsystem busy, not able to present + * the interrupt. When our sync. interrupt + * arrived we reset the I/O old PSW to its + * original value. + */ + memcpy( &io_new_psw, + &lc->io_new_psw, + sizeof(psw_t)); + + ccode = iac(); + + switch (ccode) { + case 0: // primary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_PRIM_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 1: // secondary-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_SEC_SPACE_MODE + | _PSW_IO_WAIT; + break; + case 2: // access-register + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_ACC_REG_MODE + | _PSW_IO_WAIT; + break; + case 3: // home-space + io_sync_wait.mask = _IO_PSW_MASK + | _PSW_HOME_SPACE_MODE + | _PSW_IO_WAIT; + break; + default: + panic( "halt_IO() : unexpected " + "address-space-control %d\n", + ccode); + break; + } /* endswitch */ + + io_sync_wait.addr = FIX_PSW(&&cio_wakeup); + + /* + * Martin didn't like modifying the new PSW, now we take + * a fast exit in do_IRQ() instead + */ + *(__u32 *)__LC_SYNC_IO_WORD = 1; + + do + { + + asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); +cio_wakeup: + io_parm = *(__u32 *)__LC_IO_INT_PARM; + io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; + + ready = s390_process_IRQ( io_sub ); + + } while ( !((io_sub == irq) && (ready == 1)) ); + + *(__u32 *)__LC_SYNC_IO_WORD = 0; + + } /* endif */ + + ret = 0; + break; + + case 1 : /* status pending */ + + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; + + /* + * initialize the device driver specific devstat irb area + */ + memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, + '\0', sizeof( irb_t) ); + + /* + * Let the common interrupt handler process the pending + * status. However, we must avoid calling the user + * action handler, as it won't be prepared to handle + * a pending status during do_IO() processing inline. + * This also implies that s390_process_IRQ must + * terminate synchronously - especially if device + * sensing is required. + */ + ioinfo[irq]->ui.flags.s_pend = 1; + ioinfo[irq]->ui.flags.busy = 1; + ioinfo[irq]->ui.flags.doio = 1; + + s390_process_IRQ( irq ); + + ioinfo[irq]->ui.flags.s_pend = 0; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * In multipath mode a condition code 3 implies the last + * path has gone, except we have previously restricted + * the I/O to a particular path. A condition code 1 + * (0 won't occur) results in return code EIO as well + * as 3 with another path than the one used (i.e. path available mask is non-zero). + */ + if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) + { + ret = -ENODEV; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 0; + } + else + { + ret = -EIO; + ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; + ioinfo[irq]->ui.flags.oper = 1; + + } /* endif */ + + break; + + case 2 : /* busy */ + + ret = -EBUSY; + break; + + default: /* device not operational */ + + ret = -ENODEV; + break; + + } /* endswitch */ + + if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) + && ( sync_isc_locked ) ) + { + sync_isc_locked = 0; // local setting + ioinfo[irq]->ui.flags.syncio = 0; // global setting + + disable_cpu_sync_isc( irq ); + + spin_unlock_irqrestore( &sync_isc, psw_flags); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + + +/* + * do_IRQ() handles all normal I/O device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + * + * Returns: 0 - no ending status received, no further action taken + * 1 - interrupt handler was called with ending status + */ +asmlinkage void do_IRQ( struct pt_regs regs, + unsigned int irq, + __u32 s390_intparm ) +{ +#ifdef CONFIG_FAST_IRQ + int ccode; + tpi_info_t tpi_info; + int new_irq; +#endif + int use_irq = irq; +// __u32 use_intparm = s390_intparm; + + // + // fix me !!! + // + // We need to schedule device recognition, the interrupt stays + // pending. We need to dynamically allocate an ioinfo structure. + // + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return; /* this keeps the device boxed ... */ + } + + /* + * take fast exit if CPU is in sync. I/O state + * + * Note: we have to turn off the WAIT bit and re-disable + * interrupts prior to return as this was the initial + * entry condition to synchronous I/O. + */ + if ( *(__u32 *)__LC_SYNC_IO_WORD ) + { + regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT); + + return; + + } /* endif */ + + s390irq_spin_lock(use_irq); + +#ifdef CONFIG_FAST_IRQ + do { +#endif /* CONFIG_FAST_IRQ */ + + s390_process_IRQ( use_irq ); + +#ifdef CONFIG_FAST_IRQ + + /* + * more interrupts pending ? + */ + ccode = tpi( &tpi_info ); + + if ( ! ccode ) + break; // no, leave ... + + new_irq = tpi_info.irq; +// use_intparm = tpi_info.intparm; + + /* + * if the interrupt is for a different irq we + * release the current irq lock and obtain + * a new one ... + */ + if ( new_irq != use_irq ) + { + s390irq_spin_unlock(use_irq); + use_irq = new_irq; + s390irq_spin_lock(use_irq); + + } /* endif */ + + } while ( 1 ); + +#endif /* CONFIG_FAST_IRQ */ + + s390irq_spin_unlock(use_irq); + + return; +} + +/* + * s390_process_IRQ() handles status pending situations and interrupts + * + * Called by : do_IRQ() - for "real" interrupts + * s390_start_IO, halt_IO() + * - status pending cond. after SSCH, or HSCH + * disable_subchannel() - status pending conditions (after MSCH) + * + * Returns: 0 - no ending status received, no further action taken + * 1 - interrupt handler was called with ending status + */ +int s390_process_IRQ( unsigned int irq ) +{ + int ccode; /* condition code from tsch() operation */ + int irb_cc; /* condition code from irb */ + int sdevstat; /* effective struct devstat size to copy */ + unsigned int fctl; /* function control */ + unsigned int stctl; /* status control */ + unsigned int actl; /* activity control */ + struct irqaction *action; + struct pt_regs regs; /* for interface compatibility only */ + + int issense = 0; + int ending_status = 0; + int allow4handler = 1; + int chnchk = 0; +#if 0 + int cpu = smp_processor_id(); + + kstat.irqs[cpu][irq]++; +#endif + action = ioinfo[irq]->irq_desc.action; + + /* + * It might be possible that a device was not-oper. at the time + * of free_irq() processing. This means the handler is no longer + * available when the device possibly becomes ready again. In + * this case we perform delayed disable_subchannel() processing. + */ + if ( action == NULL ) + { + if ( !ioinfo[irq]->ui.flags.d_disable ) + { + printk( KERN_CRIT"s390_process_IRQ(%04X) " + "- no interrupt handler registered" + "for device %04X !\n", + irq, + ioinfo[irq]->devstat.devno); + + } /* endif */ + + } /* endif */ + + /* + * retrieve the i/o interrupt information (irb), + * update the device specific status information + * and possibly call the interrupt handler. + * + * Note 1: At this time we don't process the resulting + * condition code (ccode) from tsch(), although + * we probably should. + * + * Note 2: Here we will have to check for channel + * check conditions and call a channel check + * handler. + * + * Note 3: If a start function was issued, the interruption + * parameter relates to it. If a halt function was + * issued for an idle device, the intparm must not + * be taken from lowcore, but from the devstat area. + */ + ccode = tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); + + // + // We must only accumulate the status if initiated by do_IO() or halt_IO() + // + if ( ioinfo[irq]->ui.flags.busy ) + { + ioinfo[irq]->devstat.dstat |= ioinfo[irq]->devstat.ii.irb.scsw.dstat; + ioinfo[irq]->devstat.cstat |= ioinfo[irq]->devstat.ii.irb.scsw.cstat; + } + else + { + ioinfo[irq]->devstat.dstat = ioinfo[irq]->devstat.ii.irb.scsw.dstat; + ioinfo[irq]->devstat.cstat = ioinfo[irq]->devstat.ii.irb.scsw.cstat; + + ioinfo[irq]->devstat.flag = 0; // reset status flags + + } /* endif */ + + ioinfo[irq]->devstat.lpum = ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum; + + if ( ioinfo[irq]->ui.flags.busy) + { + ioinfo[irq]->devstat.intparm = ioinfo[irq]->u_intparm; + + } /* endif */ + + /* + * reset device-busy bit if no longer set in irb + */ + if ( (ioinfo[irq]->devstat.dstat & DEV_STAT_BUSY ) + && ((ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) + { + ioinfo[irq]->devstat.dstat &= ~DEV_STAT_BUSY; + + } /* endif */ + + /* + * Save residual count and CCW information in case primary and + * secondary status are presented with different interrupts. + */ + if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS ) + { + ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count; + +#if CONFIG_DEBUG_IO + if ( irq != cons_dev ) + printk( "s390_process_IRQ( %04X ) : " + "residual count from irb after tsch() %d\n", + irq, ioinfo[irq]->devstat.rescnt ); +#endif + } /* endif */ + + if ( ioinfo[irq]->devstat.ii.irb.scsw.cpa != 0 ) + { + ioinfo[irq]->devstat.cpa = ioinfo[irq]->devstat.ii.irb.scsw.cpa; + + } /* endif */ + + irb_cc = ioinfo[irq]->devstat.ii.irb.scsw.cc; + + // + // check for any kind of channel or interface control check but don't + // issue the message for the console device + // + if ( (ioinfo[irq]->devstat.ii.irb.scsw.cstat + & ( SCHN_STAT_CHN_DATA_CHK + | SCHN_STAT_CHN_CTRL_CHK + | SCHN_STAT_INTF_CTRL_CHK ) ) + && (irq != cons_dev ) ) + { + printk( "Channel-Check or Interface-Control-Check " + "received\n" + " ... device %04X on subchannel %04X, dev_stat " + ": %02X sch_stat : %02X\n", + ioinfo[irq]->devstat.devno, + irq, + ioinfo[irq]->devstat.dstat, + ioinfo[irq]->devstat.cstat); + + chnchk = 1; + + } /* endif */ + + issense = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.cons; + + if ( issense ) + { + ioinfo[irq]->devstat.scnt = + ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.scnt; + ioinfo[irq]->devstat.flag |= + DEVSTAT_FLAG_SENSE_AVAIL; + + sdevstat = sizeof( devstat_t); + +#if CONFIG_DEBUG_IO + if ( irq != cons_dev ) + printk( "s390_process_IRQ( %04X ) : " + "concurrent sense bytes avail %d\n", + irq, ioinfo[irq]->devstat.scnt ); +#endif + } + else + { + /* don't copy the sense data area ! */ + sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT; + + } /* endif */ + + switch ( irb_cc ) { + case 1: /* status pending */ + + ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; + + case 0: /* normal i/o interruption */ + + fctl = ioinfo[irq]->devstat.ii.irb.scsw.fctl; + stctl = ioinfo[irq]->devstat.ii.irb.scsw.stctl; + actl = ioinfo[irq]->devstat.ii.irb.scsw.actl; + + if ( chnchk && (ioinfo[irq]->senseid.cu_type == 0x3088)) + { + char buffer[80]; + + sprintf( buffer, "s390_process_IRQ(%04X) - irb for " + "device %04X after channel check\n", + irq, + ioinfo[irq]->devstat.devno ); + + s390_displayhex( buffer, + &(ioinfo[irq]->devstat.ii.irb) , + sizeof(irb_t)); + } /* endif */ + + ioinfo[irq]->stctl |= stctl; + + ending_status = ( stctl & SCSW_STCTL_SEC_STATUS ) + || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) ) + || ( (fctl == SCSW_FCTL_HALT_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) ); + + /* + * Check for unsolicited interrupts - for debug purposes only + * + * We only consider an interrupt as unsolicited, if the device was not + * actively in use (busy) and an interrupt other than an ALERT status + * was received. + * + * Note: We must not issue a message to the console, if the + * unsolicited interrupt applies to the console device + * itself ! + */ +#if CONFIG_DEBUG_IO + if ( ( irq != cons_dev ) + && !( stctl & SCSW_STCTL_ALERT_STATUS ) + && ( ioinfo[irq]->ui.flags.busy == 0 ) ) + { + char buffer[80]; + + printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n" + " ... device status : %02X subchannel status : %02X\n", + ioinfo[irq]->devstat.devno, + irq, + ioinfo[irq]->devstat.dstat, + ioinfo[irq]->devstat.cstat); + + sprintf( buffer, "s390_process_IRQ(%04X) - irb for " + "device %04X, ending_status %d\n", + irq, + ioinfo[irq]->devstat.devno, + ending_status); + + s390_displayhex( buffer, + &(ioinfo[irq]->devstat.ii.irb) , + sizeof(irb_t)); + + } /* endif */ + + /* + * take fast exit if no handler is available + */ + if ( !action ) + return( ending_status ); + +#endif + /* + * Check whether we must issue a SENSE CCW ourselves if there is no + * concurrent sense facility installed for the subchannel. + * + * Note: We should check for ioinfo[irq]->ui.flags.consns but VM + * violates the ESA/390 architecture and doesn't present an + * operand exception for virtual devices without concurrent + * sense facility available/supported when enabling the + * concurrent sense facility. + */ + if ( ( ( ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK ) + && ( !issense ) ) + || ( ioinfo[irq]->ui.flags.delsense && ending_status ) ) + { + int ret_io; + ccw1_t *s_ccw = &ioinfo[irq]->senseccw; + unsigned long s_flag = 0; + + if ( ending_status ) + { + /* + * We copy the current status information into the device driver + * status area. Then we can use the local devstat area for device + * sensing. When finally calling the IRQ handler we must not overlay + * the original device status but copy the sense data only. + */ + memcpy( action->dev_id, + &(ioinfo[irq]->devstat), + sizeof( devstat_t) ); + + s_ccw->cmd_code = CCW_CMD_BASIC_SENSE; + s_ccw->cda = (__u32)virt_to_phys( ioinfo[irq]->devstat.ii.sense.data); + s_ccw->count = SENSE_MAX_COUNT; + s_ccw->flags = CCW_FLAG_SLI; + + /* + * If free_irq() or a sync do_IO/s390_start_IO() is in + * process we have to sense synchronously + */ + if ( ioinfo[irq]->ui.flags.unready || ioinfo[irq]->ui.flags.syncio ) + { + s_flag = DOIO_WAIT_FOR_INTERRUPT; + + } /* endif */ + + /* + * Reset status info + * + * It does not matter whether this is a sync. or async. + * SENSE request, but we have to assure we don't call + * the irq handler now, but keep the irq in busy state. + * In sync. mode s390_process_IRQ() is called recursively, + * while in async. mode we re-enter do_IRQ() with the + * next interrupt. + * + * Note : this may be a delayed sense request ! + */ + allow4handler = 0; + + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + ioinfo[irq]->ui.flags.delsense = 0; + + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.rescnt = SENSE_MAX_COUNT; + + ioinfo[irq]->ui.flags.w4sense = 1; + + ret_io = s390_start_IO( irq, + s_ccw, + 0xE2C5D5E2, // = SENSe + 0, // n/a + s_flag); + } + else + { + /* + * we received an Unit Check but we have no final + * status yet, therefore we must delay the SENSE + * processing. However, we must not report this + * intermediate status to the device interrupt + * handler. + */ + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + + ioinfo[irq]->ui.flags.delsense = 1; + allow4handler = 0; + + } /* endif */ + + } /* endif */ + + /* + * we allow for the device action handler if . + * - we received ending status + * - the action handler requested to see all interrupts + * - we received a PCI + * - fast notification was requested (primary status) + * - unsollicited interrupts + * + */ + if ( allow4handler ) + { + allow4handler = ending_status + || ( ioinfo[irq]->ui.flags.repall ) + || ( ioinfo[irq]->devstat.ii.irb.scsw.cstat & SCHN_STAT_PCI ) + || ( (ioinfo[irq]->ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) ) + || ( ioinfo[irq]->ui.flags.oper == 0 ); + + } /* endif */ + + /* + * We used to copy the device status information right before + * calling the device action handler. However, in status + * pending situations during do_IO() or halt_IO(), as well as + * enable_subchannel/disable_subchannel processing we must + * synchronously return the status information and must not + * call the device action handler. + * + */ + if ( allow4handler ) + { + /* + * if we were waiting for sense data we copy the sense + * bytes only as the original status information was + * saved prior to sense already. + */ + if ( ioinfo[irq]->ui.flags.w4sense ) + { + int sense_count = SENSE_MAX_COUNT-ioinfo[irq]->devstat.rescnt; + +#if CONFIG_DEBUG_IO + if ( irq != cons_dev ) + printk( "s390_process_IRQ( %04X ) : " + "BASIC SENSE bytes avail %d\n", + irq, sense_count ); +#endif + ioinfo[irq]->ui.flags.w4sense = 0; + ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL; + ((devstat_t *)(action->dev_id))->scnt = sense_count; + + if ( sense_count >= 0 ) + { + memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data, + &(ioinfo[irq]->devstat.ii.sense.data), + sense_count); + } + else + { +#if 1 + panic( "s390_process_IRQ(%04x) encountered " + "negative sense count\n", + irq); +#else + printk( KERN_CRIT"s390_process_IRQ(%04x) encountered " + "negative sense count\n", + irq); +#endif + } /* endif */ + } + else + { + memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat ); + + } /* endif */ + + } /* endif */ + + /* + * for status pending situations other than deferred interrupt + * conditions detected by s390_process_IRQ() itself we must not + * call the handler. This will synchronously be reported back + * to the caller instead, e.g. when detected during do_IO(). + */ + if ( ioinfo[irq]->ui.flags.s_pend || ioinfo[irq]->ui.flags.unready ) + allow4handler = 0; + + /* + * Call device action handler if applicable + */ + if ( allow4handler ) + { + + /* + * We only reset the busy condition when we are sure that no further + * interrupt is pending for the current I/O request (ending_status). + */ + if ( ending_status || !ioinfo[irq]->ui.flags.oper ) + { + ioinfo[irq]->ui.flags.oper = 1; /* dev IS oper */ + + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.haltio = 0; + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; + ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FINAL_STATUS; + + action->handler( irq, action->dev_id, ®s); + + // + // reset intparm after final status or we will badly present unsolicited + // interrupts with a intparm value possibly no longer valid. + // + ioinfo[irq]->devstat.intparm = 0; + + // + // Was there anything queued ? Start the pending channel program + // if there is one. + // + if ( ioinfo[irq]->ui.flags.doio_q ) + { + int ret; + + ret = s390_start_IO( irq, + ioinfo[irq]->qcpa, + ioinfo[irq]->qintparm, + ioinfo[irq]->qlpm, + ioinfo[irq]->qflag); + + ioinfo[irq]->ui.flags.doio_q = 0; + + /* + * If s390_start_IO() failed call the device's interrupt + * handler, the IRQ related devstat area was setup by + * s390_start_IO() accordingly already (status pending + * condition). + */ + if ( ret ) + { + action->handler( irq, action->dev_id, ®s); + + } /* endif */ + + } /* endif */ + + } + else + { + ioinfo[irq]->ui.flags.w4final = 1; + action->handler( irq, action->dev_id, ®s); + + } /* endif */ + + } /* endif */ + + break; + + case 3: /* device not operational */ + + ioinfo[irq]->ui.flags.oper = 0; + + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.doio = 0; + ioinfo[irq]->ui.flags.haltio = 0; + + ioinfo[irq]->devstat.cstat = 0; + ioinfo[irq]->devstat.dstat = 0; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; + + /* + * When we find a device "not oper" we save the status + * information into the device status area and call the + * device specific interrupt handler. + * + * Note: currently we don't have any way to reenable + * the device unless an unsolicited interrupt + * is presented. We don't check for spurious + * interrupts on "not oper" conditions. + */ + + if ( ( ioinfo[irq]->ui.flags.fast ) + && ( ioinfo[irq]->ui.flags.w4final ) ) + { + /* + * If a new request was queued already, we have + * to simulate the "not oper" status for the + * queued request by switching the "intparm" value + * and notify the interrupt handler. + */ + if ( ioinfo[irq]->ui.flags.doio_q ) + { + ioinfo[irq]->devstat.intparm = ioinfo[irq]->qintparm; + + } /* endif */ + + } /* endif */ + + ioinfo[irq]->ui.flags.fast = 0; + ioinfo[irq]->ui.flags.repall = 0; + ioinfo[irq]->ui.flags.w4final = 0; + + memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat ); + + ioinfo[irq]->devstat.intparm = 0; + + if ( !ioinfo[irq]->ui.flags.s_pend ) + action->handler( irq, action->dev_id, ®s); + + ending_status = 1; + + break; + + } /* endswitch */ + + return( ending_status ); +} + +/* + * Set the special i/o-interruption sublass 7 for the + * device specified by parameter irq. There can only + * be a single device been operated on this special + * isc. This function is aimed being able to check + * on special device interrupts in disabled state, + * without having to delay I/O processing (by queueing) + * for non-console devices. + * + * Setting of this isc is done by set_cons_dev(), while + * reset_cons_dev() resets this isc and re-enables the + * default isc3 for this device. wait_cons_dev() allows + * to actively wait on an interrupt for this device in + * disabed state. When the interrupt condition is + * encountered, wait_cons_dev(9 calls do_IRQ() to have + * the console device driver processing the interrupt. + */ +int set_cons_dev( int irq ) +{ + int ccode; + unsigned long cr6 __attribute__ ((aligned (8))); + int rc = 0; + + if ( cons_dev != -1 ) + { + rc = -EBUSY; + } + else if ( (irq > highest_subchannel) || (irq < 0) ) + { + rc = -ENODEV; + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + else + { + /* + * modify the indicated console device to operate + * on special console interrupt sublass 7 + */ + ccode = stsch( irq, &(ioinfo[irq]->schib) ); + + if (ccode) + { + rc = -ENODEV; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + } + else + { + ioinfo[irq]->schib.pmcw.isc = 7; + + ccode = msch( irq, &(ioinfo[irq]->schib) ); + + if (ccode) + { + rc = -EIO; + } + else + { + cons_dev = irq; + + /* + * enable console I/O-interrupt sublass 7 + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 |= 0x01000000; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } /* endif */ + + } /* endif */ + + return( rc); +} + +int reset_cons_dev( int irq) +{ + int rc = 0; + int ccode; + long cr6 __attribute__ ((aligned (8))); + + if ( cons_dev != -1 ) + { + rc = -EBUSY; + } + else if ( (irq > highest_subchannel) || (irq < 0) ) + { + rc = -ENODEV; + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + else + { + /* + * reset the indicated console device to operate + * on default console interrupt sublass 3 + */ + ccode = stsch( irq, &(ioinfo[irq]->schib) ); + + if (ccode) + { + rc = -ENODEV; + ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; + } + else + { + + ioinfo[irq]->schib.pmcw.isc = 3; + + ccode = msch( irq, &(ioinfo[irq]->schib) ); + + if (ccode) + { + rc = -EIO; + } + else + { + cons_dev = -1; + + /* + * disable special console I/O-interrupt sublass 7 + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 &= 0xFEFFFFFF; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } /* endif */ + + } /* endif */ + + return( rc); +} + +int wait_cons_dev( int irq ) +{ + int rc = 0; + long save_cr6; + + if ( irq == cons_dev ) + { + + /* + * before entering the spinlock we may already have + * processed the interrupt on a different CPU ... + */ + if ( ioinfo[irq]->ui.flags.busy == 1 ) + { + long cr6 __attribute__ ((aligned (8))); + + /* + * disable all, but isc 7 (console device) + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + save_cr6 = cr6; + cr6 &= 0x01FFFFFF; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + do { + tpi_info_t tpi_info; + if (tpi(&tpi_info) == 1) { + s390_process_IRQ( tpi_info.irq ); + } else { + s390irq_spin_unlock(irq); + tod_wait(100); + s390irq_spin_lock(irq); + } + eieio(); + } while (ioinfo[irq]->ui.flags.busy == 1); + + /* + * restore previous isc value + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 = save_cr6; + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } + else + { + rc = EINVAL; + + } /* endif */ + + + return(rc); +} + + +int enable_cpu_sync_isc( int irq ) +{ + int ccode; + long cr6 __attribute__ ((aligned (8))); + + int count = 0; + int rc = 0; + + if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA ) + { + ccode = stsch( irq, &(ioinfo[irq]->schib) ); + + if ( !ccode ) + { + ioinfo[irq]->schib.pmcw.isc = 5; + + do + { + ccode = msch( irq, &(ioinfo[irq]->schib) ); + + if (ccode == 0 ) + { + /* + * enable interrupt subclass in CPU + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 |= 0x04000000; // enable sync isc 5 + cr6 &= 0xEFFFFFFF; // disable standard isc 3 + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + } + else if (ccode == 3) + { + rc = -ENODEV; // device not-oper - very unlikely + + } + else if (ccode == 2) + { + rc = -EBUSY; // device busy - should not happen + + } + else if (ccode == 1) + { + // + // process pending status + // + ioinfo[irq]->ui.flags.s_pend = 1; + + s390_process_IRQ( irq ); + + ioinfo[irq]->ui.flags.s_pend = 0; + + count++; + + } /* endif */ + + } while ( ccode == 1 && count < 3 ); + + if ( count == 3) + { + rc = -EIO; + + } /* endif */ + } + else + { + rc = -ENODEV; // device is not-operational + + } /* endif */ + } + else + { + rc = -EINVAL; + + } /* endif */ + + return( rc); +} + +int disable_cpu_sync_isc( int irq) +{ + int rc = 0; + int ccode; + long cr6 __attribute__ ((aligned (8))); + + if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA ) + { + ccode = stsch( irq, &(ioinfo[irq]->schib) ); + + ioinfo[irq]->schib.pmcw.isc = 3; + + ccode = msch( irq, &(ioinfo[irq]->schib) ); + + if (ccode) + { + rc = -EIO; + } + else + { + + /* + * enable interrupt subclass in CPU + */ + asm volatile ("STCTL 6,6,%0": "=m" (cr6)); + cr6 &= 0xFBFFFFFF; // disable sync isc 5 + cr6 |= 0x10000000; // enable standard isc 3 + asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); + + } /* endif */ + + } + else + { + rc = -EINVAL; + + } /* endif */ + + return( rc); +} + +// +// Input : +// devno - device number +// ps - pointer to sense ID data area +// +// Output : none +// +void VM_virtual_device_info( __u16 devno, + senseid_t *ps ) +{ + diag210_t diag_data; + int ccode; + + int error = 0; + + diag_data.vrdcdvno = devno; + diag_data.vrdclen = sizeof( diag210_t); + ccode = diag210( (diag210_t *)virt_to_phys( &diag_data ) ); + ps->reserved = 0xff; + + switch (diag_data.vrdcvcla) { + case 0x80: + + switch (diag_data.vrdcvtyp) { + case 00: + + ps->cu_type = 0x3215; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x40: + + switch (diag_data.vrdcvtyp) { + case 0xC0: + + ps->cu_type = 0x5080; + + break; + + case 0x80: + + ps->cu_type = 0x2250; + + break; + + case 0x04: + + ps->cu_type = 0x3277; + + break; + + case 0x01: + + ps->cu_type = 0x3278; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x20: + + switch (diag_data.vrdcvtyp) { + case 0x84: + + ps->cu_type = 0x3505; + + break; + + case 0x82: + + ps->cu_type = 0x2540; + + break; + + case 0x81: + + ps->cu_type = 0x2501; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x10: + + switch (diag_data.vrdcvtyp) { + case 0x84: + + ps->cu_type = 0x3525; + + break; + + case 0x82: + + ps->cu_type = 0x2540; + + break; + + case 0x4F: + case 0x4E: + case 0x48: + + ps->cu_type = 0x3820; + + break; + + case 0x4D: + case 0x49: + case 0x45: + + ps->cu_type = 0x3800; + + break; + + case 0x4B: + + ps->cu_type = 0x4248; + + break; + + case 0x4A: + + ps->cu_type = 0x4245; + + break; + + case 0x47: + + ps->cu_type = 0x3262; + + break; + + case 0x43: + + ps->cu_type = 0x3203; + + break; + + case 0x42: + + ps->cu_type = 0x3211; + + break; + + case 0x41: + + ps->cu_type = 0x1403; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + case 0x08: + + switch (diag_data.vrdcvtyp) { + case 0x82: + + ps->cu_type = 0x3422; + + break; + + case 0x81: + + ps->cu_type = 0x3490; + + break; + + case 0x10: + + ps->cu_type = 0x3420; + + break; + + case 0x02: + + ps->cu_type = 0x3430; + + break; + + case 0x01: + + ps->cu_type = 0x3480; + + break; + + case 0x42: + + ps->cu_type = 0x3424; + + break; + + case 0x44: + + ps->cu_type = 0x9348; + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + break; + + default: + + error = 1; + + break; + + } /* endswitch */ + + if ( error ) + {printk( "DIAG X'210' for device %04X returned (cc = %d): vdev class : %02X, " + "vdev type : %04X \n ... rdev class : %02X, rdev type : %04X, rdev model: %02X\n", + devno, + ccode, + diag_data.vrdcvcla, + diag_data.vrdcvtyp, + diag_data.vrdcrccl, + diag_data.vrdccrty, + diag_data.vrdccrmd ); + + } /* endif */ + +} + +/* + * This routine returns the characteristics for the device + * specified. Some old devices might not provide the necessary + * command code information during SenseID processing. In this + * case the function returns -EINVAL. Otherwise the function + * allocates a decice specific data buffer and provides the + * device characteristics together with the buffer size. Its + * the callers responability to release the kernel memory if + * not longer needed. In case of persistent I/O problems -EBUSY + * is returned. + * + * The function may be called enabled or disabled. However, the + * caller must have locked the irq it is requesting data for. + * + * Note : It would have been nice to collect this information + * during init_IRQ() processing but this is not possible + * + * a) without statically pre-allocation fixed size buffers + * as virtual memory management isn't available yet. + * + * b) without unnecessarily increase system startup by + * evaluating devices eventually not used at all. + */ +int read_dev_chars( int irq, void **buffer, int length ) +{ + unsigned int flags; + ccw1_t *rdc_ccw; + devstat_t devstat; + char *rdc_buf; + int devflag; + + int ret = 0; + int emulated = 0; + int retry = 5; + + if ( !buffer || !length ) + { + return( -EINVAL ); + + } /* endif */ + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + + if ( ioinfo[irq]->ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * Before playing around with irq locks we should assure + * running disabled on (just) our CPU. Sync. I/O requests + * also require to run disabled. + * + * Note : as no global lock is required, we must not use + * cli(), but __cli() instead. + */ + __save_flags(flags); + __cli(); + + rdc_ccw = &ioinfo[irq]->senseccw; + + if ( !ioinfo[irq]->ui.flags.ready ) + { + ret = request_irq( irq, + init_IRQ_handler, + 0, "RDC", &devstat ); + + if ( !ret ) + { + emulated = 1; + + } /* endif */ + + } /* endif */ + + if ( !ret ) + { + if ( ! *buffer ) + { + rdc_buf = kmalloc( length, GFP_KERNEL); + } + else + { + rdc_buf = *buffer; + + } /* endif */ + + if ( !rdc_buf ) + { + ret = -ENOMEM; + } + else + { + do + { + rdc_ccw->cmd_code = CCW_CMD_RDC; + rdc_ccw->cda = (__u32)virt_to_phys( rdc_buf ); + rdc_ccw->count = length; + rdc_ccw->flags = CCW_FLAG_SLI; + + ret = s390_start_IO( irq, + rdc_ccw, + 0x00524443, // RDC + 0, // n/a + DOIO_WAIT_FOR_INTERRUPT ); + retry--; + devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag; + + } while ( ( retry ) + && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); + + } /* endif */ + + if ( !retry ) + { + ret = -EBUSY; + + } /* endif */ + + __restore_flags(flags); + + /* + * on success we update the user input parms + */ + if ( !ret ) + { + *buffer = rdc_buf; + + } /* endif */ + + if ( emulated ) + { + free_irq( irq, &devstat); + + } /* endif */ + + } /* endif */ + + return( ret ); +} + +/* + * Read Configuration data + */ +int read_conf_data( int irq, void **buffer, int *length ) +{ + int found = 0; + int ciw_cnt = 0; + unsigned int flags; + + int ret = 0; + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + + } /* endif */ + + if ( ioinfo[irq]->ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + /* + * scan for RCD command in extended SenseID data + */ + for ( ; (found == 0) && (ciw_cnt < 62); ciw_cnt++ ) + { + if ( ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD ) + { + found = 1; + break; + } /* endif */ + + } /* endfor */ + + if ( found ) + { + ccw1_t *rcd_ccw = &ioinfo[irq]->senseccw; + devstat_t devstat; + char *rcd_buf; + int devflag; + + int emulated = 0; + int retry = 5; + + __save_flags(flags); + __cli(); + + if ( !ioinfo[irq]->ui.flags.ready ) + { + ret = request_irq( irq, + init_IRQ_handler, + 0, "RCD", &devstat ); + + if ( !ret ) + { + emulated = 1; + + } /* endif */ + + } /* endif */ + + if ( !ret ) + { + rcd_buf = kmalloc( ioinfo[irq]->senseid.ciw[ciw_cnt].count, + GFP_KERNEL); + + do + { + rcd_ccw->cmd_code = ioinfo[irq]->senseid.ciw[ciw_cnt].cmd; + rcd_ccw->cda = (__u32)virt_to_phys( rcd_buf ); + rcd_ccw->count = ioinfo[irq]->senseid.ciw[ciw_cnt].count; + rcd_ccw->flags = CCW_FLAG_SLI; + + ret = s390_start_IO( irq, + rcd_ccw, + 0x00524344, // == RCD + 0, // n/a + DOIO_WAIT_FOR_INTERRUPT ); + + retry--; + + devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag; + + } while ( ( retry ) + && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); + + if ( !retry ) + ret = -EBUSY; + + __restore_flags(flags); + + } /* endif */ + + /* + * on success we update the user input parms + */ + if ( !ret ) + { + *length = ioinfo[irq]->senseid.ciw[ciw_cnt].count; + *buffer = rcd_buf; + + } /* endif */ + + if ( emulated ) + free_irq( irq, &devstat); + } + else + { + ret = -EINVAL; + + } /* endif */ + + return( ret ); + +} + +int get_dev_info( int irq, dev_info_t * pdi) +{ + return( get_dev_info_by_irq( irq, pdi)); +} + +static int __inline__ get_next_available_irq( ioinfo_t *pi) +{ + int ret_val; + + while ( TRUE ) + { + if ( pi->ui.flags.oper ) + { + ret_val = pi->irq; + break; + } + else + { + pi = pi->next; + + // + // leave at end of list unconditionally + // + if ( pi == NULL ) + { + ret_val = -ENODEV; + break; + } + + } /* endif */ + + } /* endwhile */ + + return ret_val; +} + + +int get_irq_first( void ) +{ + int ret_irq; + + if ( ioinfo_head ) + { + if ( ioinfo_head->ui.flags.oper ) + { + ret_irq = ioinfo_head->irq; + } + else if ( ioinfo_head->next ) + { + ret_irq = get_next_available_irq( ioinfo_head->next ); + + } + else + { + ret_irq = -ENODEV; + + } /* endif */ + } + else + { + ret_irq = -ENODEV; + + } /* endif */ + + return ret_irq; +} + +int get_irq_next( int irq ) +{ + int ret_irq; + + if ( ioinfo[irq] != INVALID_STORAGE_AREA ) + { + if ( ioinfo[irq]->next ) + { + if ( ioinfo[irq]->next->ui.flags.oper ) + { + ret_irq = ioinfo[irq]->next->irq; + } + else + { + ret_irq = get_next_available_irq( ioinfo[irq]->next ); + + } /* endif */ + } + else + { + ret_irq = -ENODEV; + + } /* endif */ + } + else + { + ret_irq = -EINVAL; + + } /* endif */ + + return ret_irq; +} + +int get_dev_info_by_irq( int irq, dev_info_t *pdi) +{ + + if ( irq > highest_subchannel || irq < 0 ) + { + return -ENODEV; + } + else if ( pdi == NULL ) + { + return -EINVAL; + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } + else + { + pdi->devno = ioinfo[irq]->schib.pmcw.dev; + pdi->irq = irq; + + if ( ioinfo[irq]->ui.flags.oper ) + { + pdi->status = 0; + memcpy( &(pdi->sid_data), + &ioinfo[irq]->senseid, + sizeof( senseid_t)); + } + else + { + pdi->status = DEVSTAT_NOT_OPER; + memcpy( &(pdi->sid_data), + '\0', + sizeof( senseid_t)); + pdi->sid_data.cu_type = 0xFFFF; + + } /* endif */ + + if ( ioinfo[irq]->ui.flags.ready ) + pdi->status |= DEVSTAT_DEVICE_OWNED; + + return 0; + + } /* endif */ + +} + + +int get_dev_info_by_devno( __u16 devno, dev_info_t *pdi) +{ + int i; + int rc = -ENODEV; + + if ( devno > 0x0000ffff ) + { + return -ENODEV; + } + else if ( pdi == NULL ) + { + return -EINVAL; + } + else + { + + for ( i=0; i <= highest_subchannel; i++ ) + { + + if ( ioinfo[i] != INVALID_STORAGE_AREA + && ioinfo[i]->schib.pmcw.dev == devno ) + { + if ( ioinfo[i]->ui.flags.oper ) + { + pdi->status = 0; + pdi->irq = i; + pdi->devno = devno; + + memcpy( &(pdi->sid_data), + &ioinfo[i]->senseid, + sizeof( senseid_t)); + } + else + { + pdi->status = DEVSTAT_NOT_OPER; + pdi->irq = i; + pdi->devno = devno; + + memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t)); + pdi->sid_data.cu_type = 0xFFFF; + + } /* endif */ + + if ( ioinfo[i]->ui.flags.ready ) + pdi->status |= DEVSTAT_DEVICE_OWNED; + + rc = 0; /* found */ + break; + + } /* endif */ + + } /* endfor */ + + return( rc); + + } /* endif */ + +} + +int get_irq_by_devno( __u16 devno ) +{ + int i; + int rc = -1; + + if ( devno <= 0x0000ffff ) + { + for ( i=0; i <= highest_subchannel; i++ ) + { + if ( (ioinfo[i] != INVALID_STORAGE_AREA ) + && (ioinfo[i]->schib.pmcw.dev == devno) + && (ioinfo[i]->schib.pmcw.dnv == 1 ) ) + { + rc = i; + break; + + } /* endif */ + + } /* endfor */ + + } /* endif */ + + return( rc); +} + +unsigned int get_devno_by_irq( int irq ) +{ + + if ( ( irq > highest_subchannel ) + || ( irq < 0 ) + || ( ioinfo[irq] == INVALID_STORAGE_AREA ) ) + { + return -1; + + } /* endif */ + + /* + * we don't need to check for the device be operational + * as the initial STSCH will always present the device + * number defined by the IOCDS regardless of the device + * existing or not. However, there could be subchannels + * defined who's device number isn't valid ... + */ + if ( ioinfo[irq]->schib.pmcw.dnv ) + return( ioinfo[irq]->schib.pmcw.dev ); + else + return -1; +} + +/* + * s390_device_recognition + * + * Used for system wide device recognition. Issues the device + * independant SenseID command to obtain info the device type. + * + */ +void s390_device_recognition( void) +{ + + int irq = 0; /* let's start with subchannel 0 ... */ + + do + { + /* + * We issue the SenseID command on I/O subchannels we think are + * operational only. + */ + if ( ( ioinfo[irq] != INVALID_STORAGE_AREA ) + && ( ioinfo[irq]->schib.pmcw.st == 0 ) + && ( ioinfo[irq]->ui.flags.oper == 1 ) ) + { + s390_SenseID( irq, &ioinfo[irq]->senseid ); + + } /* endif */ + + irq ++; + + } while ( irq <= highest_subchannel ); + +} + + +/* + * s390_search_devices + * + * Determines all subchannels available to the system. + * + */ +void s390_process_subchannels( void) +{ + int isValid; + int irq = 0; /* Evaluate all subchannels starting with 0 ... */ + + do + { + isValid = s390_validate_subchannel( irq); + + irq++; + + } while ( isValid && irq < __MAX_SUBCHANNELS ); + + highest_subchannel = --irq; + + printk( "\nHighest subchannel number detected: %u\n", + highest_subchannel); +} + +/* + * s390_validate_subchannel() + * + * Process the subchannel for the requested irq. Returns 1 for valid + * subchannels, otherwise 0. + */ +int s390_validate_subchannel( int irq ) +{ + + int retry; /* retry count for status pending conditions */ + int ccode; /* condition code for stsch() only */ + int ccode2; /* condition code for other I/O routines */ + schib_t *p_schib; + + /* + * The first subchannel that is not-operational (ccode==3) + * indicates that there aren't any more devices available. + */ + if ( ( init_IRQ_complete ) + && ( ioinfo[irq] != INVALID_STORAGE_AREA ) ) + { + p_schib = &ioinfo[irq]->schib; + } + else + { + p_schib = &init_schib; + + } /* endif */ + + ccode = stsch( irq, p_schib); + + if ( ccode == 0) + { + /* + * ... just being curious we check for non I/O subchannels + */ + if ( p_schib->pmcw.st ) + { + printk( "Subchannel %04X reports " + "non-I/O subchannel type %04X\n", + irq, + p_schib->pmcw.st); + + if ( ioinfo[irq] != INVALID_STORAGE_AREA ) + ioinfo[irq]->ui.flags.oper = 0; + + } /* endif */ + + if ( p_schib->pmcw.dnv ) + { + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + + if ( !init_IRQ_complete ) + { + ioinfo[irq] = + (ioinfo_t *)alloc_bootmem( sizeof(ioinfo_t)); + } + else + { + ioinfo[irq] = + (ioinfo_t *)kmalloc( sizeof(ioinfo_t), + GFP_KERNEL ); + + } /* endif */ + + memset( ioinfo[irq], '\0', sizeof( ioinfo_t)); + memcpy( &ioinfo[irq]->schib, + &init_schib, + sizeof( schib_t)); + ioinfo[irq]->irq_desc.status = IRQ_DISABLED; + ioinfo[irq]->irq_desc.handler = &no_irq_type; + + /* + * We have to insert the new ioinfo element + * into the linked list, either at its head, + * its tail or insert it. + */ + if ( ioinfo_head == NULL ) /* first element */ + { + ioinfo_head = ioinfo[irq]; + ioinfo_tail = ioinfo[irq]; + } + else if ( irq < ioinfo_head->irq ) /* new head */ + { + ioinfo[irq]->next = ioinfo_head; + ioinfo_head->prev = ioinfo[irq]; + ioinfo_head = ioinfo[irq]; + } + else if ( irq > ioinfo_tail->irq ) /* new tail */ + { + ioinfo_tail->next = ioinfo[irq]; + ioinfo[irq]->prev = ioinfo_tail; + ioinfo_tail = ioinfo[irq]; + } + else /* insert element */ + { + ioinfo_t *pi = ioinfo_head; + + do + { + if ( irq < pi->next->irq ) + { + ioinfo[irq]->next = pi->next; + ioinfo[irq]->prev = pi; + pi->next->prev = ioinfo[irq]; + pi->next = ioinfo[irq]; + break; + + } /* endif */ + + pi = pi->next; + + } while ( 1 ); + + } /* endif */ + + } /* endif */ + + // initialize some values ... + ioinfo[irq]->ui.flags.pgid_supp = 1; + + ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pam + & ioinfo[irq]->schib.pmcw.pom; + + printk( "Detected device %04X on subchannel %04X" + " - PIM = %02X, PAM = %02X, POM = %02X\n", + ioinfo[irq]->schib.pmcw.dev, + irq, + ioinfo[irq]->schib.pmcw.pim, + ioinfo[irq]->schib.pmcw.pam, + ioinfo[irq]->schib.pmcw.pom); + + /* + * We should have at least one CHPID ... + */ + if ( ioinfo[irq]->schib.pmcw.pim + & ioinfo[irq]->schib.pmcw.pam + & ioinfo[irq]->schib.pmcw.pom ) + { + ioinfo[irq]->ui.flags.oper = 0; + + /* + * We now have to initially ... + * ... set "interruption sublass" + * ... enable "concurrent sense" + * ... enable "multipath mode" if more than one + * CHPID is available. This is done regardless + * whether multiple paths are available for us. + * + * Note : we don't enable the device here, this is temporarily + * done during device sensing below. + */ + ioinfo[irq]->schib.pmcw.isc = 3; /* could be smth. else */ + ioinfo[irq]->schib.pmcw.csense = 1; /* concurrent sense */ + ioinfo[irq]->schib.pmcw.ena = 0; /* force disable it */ + ioinfo[irq]->schib.pmcw.intparm = + ioinfo[irq]->schib.pmcw.dev; + + if ( ( ioinfo[irq]->schib.pmcw.pim != 0 ) + && ( ioinfo[irq]->schib.pmcw.pim != 0x80 ) ) + { + ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */ + + } /* endif */ + + /* + * initialize ioinfo structure + */ + ioinfo[irq]->irq = irq; + ioinfo[irq]->ui.flags.busy = 0; + ioinfo[irq]->ui.flags.ready = 0; + ioinfo[irq]->ui.flags.oper = 1; + ioinfo[irq]->devstat.intparm = 0; + ioinfo[irq]->devstat.devno = ioinfo[irq]->schib.pmcw.dev; + + retry = 5; + + do + { + ccode2 = msch_err( irq, &ioinfo[irq]->schib); + + switch (ccode2) { + case 0: // successful completion + // + // concurrent sense facility available ... + // + ioinfo[irq]->ui.flags.consns = 1; + break; + + case 1: // status pending + // + // How can we have a pending status as device is + // disabled for interrupts ? Anyway, clear it ... + // + tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); + retry--; + break; + + case 2: // busy + retry--; + break; + + case 3: // not operational + ioinfo[irq]->ui.flags.oper = 0; + retry = 0; + break; + + default: +#define PGMCHK_OPERAND_EXC 0x15 + + if ( (ccode2 & PGMCHK_OPERAND_EXC) == PGMCHK_OPERAND_EXC ) + { + /* + * re-issue the modify subchannel without trying to + * enable the concurrent sense facility + */ + ioinfo[irq]->schib.pmcw.csense = 0; + + ccode2 = msch_err( irq, &ioinfo[irq]->schib); + + if ( ccode2 != 0 ) + { + printk( " ... modify subchannel (2) failed with CC = %X\n", + ccode2 ); + ioinfo[irq]->ui.flags.oper = 0; + } + else + { + ioinfo[irq]->ui.flags.consns = 0; + + } /* endif */ + } + else + { + printk( " ... modify subchannel (1) failed with CC = %X\n", + ccode2); + ioinfo[irq]->ui.flags.oper = 0; + + } /* endif */ + + retry = 0; + break; + + } /* endswitch */ + + } while ( ccode2 && retry ); + + if ( (ccode2 < 3) && (!retry) ) + { + printk( " ... msch() retry count for " + "subchannel %04X exceeded, CC = %d\n", + irq, + ccode2); + + } /* endif */ + + } + else + { + ioinfo[irq]->ui.flags.oper = 0; + + } /* endif */ + + } /* endif */ + + } /* endif */ + + /* + * indicate whether the subchannel is valid + */ + if ( ccode == 3) + return(0); + else + return(1); +} + +/* + * s390_SenseID + * + * Try to obtain the 'control unit'/'device type' information + * associated with the subchannel. + * + * The function is primarily meant to be called without irq + * action handler in place. However, it also allows for + * use with an action handler in place. If there is already + * an action handler registered assure it can handle the + * s390_SenseID() related device interrupts - interruption + * parameter used is 0x00E2C9C4 ( SID ). + */ +int s390_SenseID( int irq, senseid_t *sid ) +{ + ccw1_t sense_ccw; /* ccw area for SenseID command */ + devstat_t devstat; /* required by request_irq() */ + + int irq_ret = 0; /* return code */ + int retry = 5; /* retry count */ + int inlreq = 0; /* inline request_irq() */ + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + } /* endif */ + + if ( ioinfo[irq]->ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( !ioinfo[irq]->ui.flags.ready ) + { + /* + * Perform SENSE ID command processing. We have to request device + * ownership and provide a dummy I/O handler. We issue sync. I/O + * requests and evaluate the devstat area on return therefore + * we don't need a real I/O handler in place. + */ + irq_ret = request_irq( irq, init_IRQ_handler, 0, "SID", &devstat); + + if ( irq_ret == 0 ) + inlreq = 1; + + } /* endif */ + + if ( irq_ret == 0 ) + { + s390irq_spin_lock( irq); + + sense_ccw.cmd_code = CCW_CMD_SENSE_ID; + sense_ccw.cda = (__u32)virt_to_phys( sid ); + sense_ccw.count = sizeof( senseid_t); + sense_ccw.flags = CCW_FLAG_SLI; + + ioinfo[irq]->senseid.cu_type = 0xFFFF; /* initialize fields ... */ + ioinfo[irq]->senseid.cu_model = 0; + ioinfo[irq]->senseid.dev_type = 0; + ioinfo[irq]->senseid.dev_model = 0; + + /* + * We now issue a SenseID request. In case of BUSY + * or STATUS PENDING conditions we retry 5 times. + */ + do + { + memset( &devstat, '\0', sizeof( devstat_t) ); + + irq_ret = s390_start_IO( irq, + &sense_ccw, + 0x00E2C9C4, // == SID + 0, // n/a + DOIO_WAIT_FOR_INTERRUPT + | DOIO_TIMEOUT ); + + if ( irq_ret == -ETIMEDOUT ) + { + halt_IO( irq, + 0x80E2C9C4, + DOIO_WAIT_FOR_INTERRUPT); + devstat.flag |= DEVSTAT_NOT_OPER; + + } /* endif */ + + if ( sid->cu_type == 0xFFFF && devstat.flag != DEVSTAT_NOT_OPER ) + { + if ( devstat.flag & DEVSTAT_STATUS_PENDING ) + { +#if CONFIG_DEBUG_IO + printk( "Device %04X on Subchannel %04X " + "reports pending status, retry : %d\n", + ioinfo[irq]->schib.pmcw.dev, + irq, + retry); +#endif + } /* endif */ + + if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) + { + /* + * if the device doesn't support the SenseID + * command further retries wouldn't help ... + */ + if ( devstat.ii.sense.data[0] & SNS0_CMD_REJECT ) + { + retry = 0; + } +#if CONFIG_DEBUG_IO + else + { + printk( "Device %04X," + " UC/SenseID," + " retry %d, cnt %02d," + " sns :" + " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", + ioinfo[irq]->schib.pmcw.dev, + retry, + devstat.scnt, + devstat.ii.sense.data[0], + devstat.ii.sense.data[1], + devstat.ii.sense.data[2], + devstat.ii.sense.data[3], + devstat.ii.sense.data[4], + devstat.ii.sense.data[5], + devstat.ii.sense.data[6], + devstat.ii.sense.data[7]); + + } /* endif */ +#endif + } + else if ( devstat.flag & DEVSTAT_NOT_OPER ) + { + printk( "Device %04X on Subchannel %04X " + "became 'not operational'\n", + ioinfo[irq]->schib.pmcw.dev, + irq); + + retry = 0; + + } /* endif */ + } + else // we got it or the device is not-operational ... + { + retry = 0; + + } /* endif */ + + retry--; + + } while ( retry > 0 ); + + s390irq_spin_unlock( irq); + + /* + * If we installed the irq action handler we have to + * release it too. + */ + if ( inlreq ) + free_irq( irq, &devstat); + + /* + * if running under VM check there ... perhaps we should do + * only if we suffered a command reject, but it doesn't harm + */ + if ( ( sid->cu_type == 0xFFFF ) + && ( MACHINE_IS_VM ) ) + { + VM_virtual_device_info( ioinfo[irq]->schib.pmcw.dev, + sid ); + } /* endif */ + + if ( sid->cu_type == 0xFFFF ) + { + /* + * SenseID CU-type of 0xffff indicates that no device + * information could be retrieved (pre-init value). + * + * If we can't couldn't identify the device type we + * consider the device "not operational". + */ + printk( "Unknown device %04X on subchannel %04X\n", + ioinfo[irq]->schib.pmcw.dev, + irq); + ioinfo[irq]->ui.flags.oper = 0; + + } /* endif */ + + /* + * Issue device info message if unit was operational . + */ + if ( ioinfo[irq]->ui.flags.oper ) + { + if ( sid->dev_type != 0 ) + { + printk( "Device %04X reports: CU Type/Mod = %04X/%02X," + " Dev Type/Mod = %04X/%02X\n", + ioinfo[irq]->schib.pmcw.dev, + sid->cu_type, + sid->cu_model, + sid->dev_type, + sid->dev_model); + } + else + { + printk( "Device %04X reports:" + " Dev Type/Mod = %04X/%02X\n", + ioinfo[irq]->schib.pmcw.dev, + sid->cu_type, + sid->cu_model); + + } /* endif */ + + } /* endif */ + + if ( ioinfo[irq]->ui.flags.oper ) + irq_ret = 0; + else + irq_ret = -ENODEV; + + } /* endif */ + + return( irq_ret ); +} + +static int __inline__ s390_SetMultiPath( int irq ) +{ + int cc; + + cc = stsch( irq, &ioinfo[irq]->schib ); + + if ( !cc ) + { + ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */ + + cc = msch( irq, &ioinfo[irq]->schib ); + + } /* endif */ + + return( cc); +} + +/* + * Device Path Verification + * + * Path verification is accomplished by checking which paths (CHPIDs) are + * available. Further, a path group ID is set, if possible in multipath + * mode, otherwise in single path mode. + * + */ +int s390_DevicePathVerification( int irq ) +{ +#if 0 + int ccode; + __u8 pathmask; + + int ret = 0; + + if ( ioinfo[irq]->ui.flags.pgid_supp == 0 ) + { + ret = -EOPNOTSUPP; + + } /* endif */ + + ccode = stsch( irq, &(ioinfo[irq]->schib) ); + + if ( ccode ) + { + ret = -ENODEV; + } + else + { + int i; + pgid_t pgid; + + int first = 1; + __u8 dev_path = ioinfo[irq]->schib.pmcw.pam + & ioinfo[irq]->schib.pmcw.pom; + + /* + * let's build a path group ID if we don't have one yet + */ + if ( ioinfo[irq]->ui.flags.pgid == 0) + { + struct _lowcore *lowcore = &get_cpu_lowcore(cpu); + + ioinfo->pgid.cpu_addr = lowcore->cpu_data.cpu_addr; + ioinfo->pgid.cpu_id = lowcore->cpu_data.cpu_id.ident; + ioinfo->pgid.cpu_model = lowcore->cpu_data.cpu_id.machine; + ioinfo->pgid.tod_high = *(__u32 *)&irq_IPL_TOD; + + ioinfo[irq]->ui.flags.pgid = 1; + + } /* endif */ + + memcpy( &pgid, ioinfo[irq]->pgid, sizeof(pgid_t)); + + for ( i = 0; i < 8 && !ret ; i++) + { + pathmask = 0x80 >> i; + + if ( dev_path & pathmask ) + { + ret = s390_SetPGID( irq, pathmask, &pgid ); + + /* + * For the *first* path we are prepared + * for recovery + * + * - If we fail setting the PGID we assume its + * using a different PGID already (VM) we + * try to sense. + */ + if ( ret == -EOPNOTSUPP && first ) + { + *(int *)&pgid = 0; + + ret = s390_SensePGID( irq, pathmask, &pgid); + first = 0; + + if ( !ret ) + { + /* + * Check whether we retrieved + * a reasonable PGID ... + */ + if ( !ret && (*(int *)&pgid == 0) ) + { + ret = -EOPNOTSUPP; + } + else + { + ret = s390_SetPGID( irq, pathmask, &pgid ); + + } /* endif */ + + } /* endif */ + + if ( ret ) + { + ioinfo[irq]->ui.flags.pgid_supp = 0; + + printk( "PathVerification(%04X) " + "- Device %04X doesn't " + " support path grouping", + irq, + ioinfo[irq]->schib.pmcw.dev); + + } /* endif */ + } + else if ( ret ) + { + ioinfo[irq]->ui.flags.pgid_supp = 0; + + } /* endif */ + + } /* endif */ + + } /* endfor */ + + } /* endif */ + + return ret; +#else + return 0; +#endif +} + +/* + * s390_SetPGID + * + * Set Path Group ID + * + */ +int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) +{ + ccw1_t spid_ccw; /* ccw area for SPID command */ + devstat_t devstat; /* required by request_irq() */ + + int irq_ret = 0; /* return code */ + int retry = 5; /* retry count */ + int inlreq = 0; /* inline request_irq() */ + int mpath = 1; /* try multi-path first */ + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + + } /* endif */ + + if ( ioinfo[irq]->ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( !ioinfo[irq]->ui.flags.ready ) + { + /* + * Perform SENSE ID command processing. We have to request device + * ownership and provide a dummy I/O handler. We issue sync. I/O + * requests and evaluate the devstat area on return therefore + * we don't need a real I/O handler in place. + */ + irq_ret = request_irq( irq, init_IRQ_handler, 0, "SPID", &devstat); + + if ( irq_ret == 0 ) + inlreq = 1; + + } /* endif */ + + if ( irq_ret == 0 ) + { + s390irq_spin_lock( irq); + + spid_ccw.cmd_code = CCW_CMD_SET_PGID; + spid_ccw.cda = (__u32)virt_to_phys( pgid ); + spid_ccw.count = sizeof( pgid_t); + spid_ccw.flags = CCW_FLAG_SLI; + + + /* + * We now issue a SenseID request. In case of BUSY + * or STATUS PENDING conditions we retry 5 times. + */ + do + { + memset( &devstat, '\0', sizeof( devstat_t) ); + + irq_ret = s390_start_IO( irq, + &spid_ccw, + 0xE2D7C9C4, // == SPID + lpm, // n/a + DOIO_WAIT_FOR_INTERRUPT + | DOIO_VALID_LPM ); + + if ( !irq_ret ) + { + if ( devstat.flag & DEVSTAT_STATUS_PENDING ) + { +#if CONFIG_DEBUG_IO + printk( "SPID - Device %04X " + "on Subchannel %04X " + "reports pending status, " + "retry : %d\n", + ioinfo[irq]->schib.pmcw.dev, + irq, + retry); +#endif + } /* endif */ + + if ( devstat.flag == ( DEVSTAT_START_FUNCTION + | DEVSTAT_FINAL_STATUS ) ) + { + retry = 0; // successfully set ... + } + else if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) + { + /* + * If the device doesn't support the + * Sense Path Group ID command + * further retries wouldn't help ... + */ + if ( devstat.ii.sense.data[0] & SNS0_CMD_REJECT ) + { + if ( mpath ) + { + pgid->inf.fc = SPID_FUNC_ESTABLISH; + mpath = 0; + retry--; + } + else + { + irq_ret = -EOPNOTSUPP; + retry = 0; + + } /* endif */ + } +#if CONFIG_DEBUG_IO + else + { + printk( "SPID - device %04X," + " unit check," + " retry %d, cnt %02d," + " sns :" + " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", + ioinfo[irq]->schib.pmcw.dev, + retry, + devstat.scnt, + devstat.ii.sense.data[0], + devstat.ii.sense.data[1], + devstat.ii.sense.data[2], + devstat.ii.sense.data[3], + devstat.ii.sense.data[4], + devstat.ii.sense.data[5], + devstat.ii.sense.data[6], + devstat.ii.sense.data[7]); + + } /* endif */ +#endif + } + else if ( devstat.flag & DEVSTAT_NOT_OPER ) + { + printk( "SPID - Device %04X " + "on Subchannel %04X " + "became 'not operational'\n", + ioinfo[irq]->schib.pmcw.dev, + irq); + + retry = 0; + + } /* endif */ + } + else if ( irq_ret != -ENODEV ) + { + retry--; + } + else + { + retry = 0; + + } /* endif */ + + } while ( retry > 0 ); + + s390irq_spin_unlock( irq); + + /* + * If we installed the irq action handler we have to + * release it too. + */ + if ( inlreq ) + free_irq( irq, &devstat); + + } /* endif */ + + return( irq_ret ); +} + + +/* + * s390_SensePGID + * + * Sense Path Group ID + * + */ +int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid ) +{ + ccw1_t snid_ccw; /* ccw area for SNID command */ + devstat_t devstat; /* required by request_irq() */ + + int irq_ret = 0; /* return code */ + int retry = 5; /* retry count */ + int inlreq = 0; /* inline request_irq() */ + + if ( (irq > highest_subchannel) || (irq < 0 ) ) + { + return( -ENODEV ); + + } + else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + { + return( -ENODEV); + + } /* endif */ + + if ( ioinfo[irq]->ui.flags.oper == 0 ) + { + return( -ENODEV ); + + } /* endif */ + + if ( !ioinfo[irq]->ui.flags.ready ) + { + /* + * Perform SENSE ID command processing. We have to request device + * ownership and provide a dummy I/O handler. We issue sync. I/O + * requests and evaluate the devstat area on return therefore + * we don't need a real I/O handler in place. + */ + irq_ret = request_irq( irq, init_IRQ_handler, 0, "SNID", &devstat); + + if ( irq_ret == 0 ) + inlreq = 1; + + } /* endif */ + + if ( irq_ret == 0 ) + { + s390irq_spin_lock( irq); + + snid_ccw.cmd_code = CCW_CMD_SENSE_PGID; + snid_ccw.cda = (__u32)virt_to_phys( pgid ); + snid_ccw.count = sizeof( pgid_t); + snid_ccw.flags = CCW_FLAG_SLI; + + /* + * We now issue a SenseID request. In case of BUSY + * or STATUS PENDING conditions we retry 5 times. + */ + do + { + memset( &devstat, '\0', sizeof( devstat_t) ); + + irq_ret = s390_start_IO( irq, + &snid_ccw, + 0xE2D5C9C4, // == SNID + lpm, // n/a + DOIO_WAIT_FOR_INTERRUPT + | DOIO_VALID_LPM ); + + if ( !irq_ret ) + { + if ( devstat.flag & DEVSTAT_STATUS_PENDING ) + { +#if CONFIG_DEBUG_IO + printk( "SNID - Device %04X " + "on Subchannel %04X " + "reports pending status, " + "retry : %d\n", + ioinfo[irq]->schib.pmcw.dev, + irq, + retry); +#endif + } /* endif */ + + if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) + { + /* + * If the device doesn't support the + * Sense Path Group ID command + * further retries wouldn't help ... + */ + if ( devstat.ii.sense.data[0] & SNS0_CMD_REJECT ) + { + retry = 0; + irq_ret = -EOPNOTSUPP; + } +#if CONFIG_DEBUG_IO + else + { + printk( "SNID - device %04X," + " unit check," + " retry %d, cnt %02d," + " sns :" + " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", + ioinfo[irq]->schib.pmcw.dev, + retry, + devstat.scnt, + devstat.ii.sense.data[0], + devstat.ii.sense.data[1], + devstat.ii.sense.data[2], + devstat.ii.sense.data[3], + devstat.ii.sense.data[4], + devstat.ii.sense.data[5], + devstat.ii.sense.data[6], + devstat.ii.sense.data[7]); + + } /* endif */ +#endif + } + else if ( devstat.flag & DEVSTAT_NOT_OPER ) + { + printk( "SNID - Device %04X " + "on Subchannel %04X " + "became 'not operational'\n", + ioinfo[irq]->schib.pmcw.dev, + irq); + + retry = 0; + + } /* endif */ + } + else if ( irq_ret != -ENODEV ) + { + retry--; + } + else + { + retry = 0; + + } /* endif */ + + } while ( retry > 0 ); + + s390irq_spin_unlock( irq); + + /* + * If we installed the irq action handler we have to + * release it too. + */ + if ( inlreq ) + free_irq( irq, &devstat); + + } /* endif */ + + return( irq_ret ); +} + + +void do_crw_pending( void ) +{ + return; +} + + +/* added by Holger Smolinski for reipl support in reipl.S */ +void +reipl ( int sch ) +{ + int i; + + for ( i = 0; i < highest_subchannel; i ++ ) { + free_irq ( i, (void*)REIPL_DEVID_MAGIC ); + } + do_reipl( 0x10000 | sch ); +} + diff --git a/arch/s390/kernel/s390mach.c b/arch/s390/kernel/s390mach.c new file mode 100644 index 000000000..750e50c30 --- /dev/null +++ b/arch/s390/kernel/s390mach.c @@ -0,0 +1,157 @@ +/* + * arch/s390/kernel/s390mach.c + * S/390 machine check handler, + * currently only channel-reports are supported + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + */ + +#include <linux/init.h> + +#include <asm/irq.h> +#include <asm/lowcore.h> +#include <asm/semaphore.h> +#include <asm/s390io.h> +#include <asm/s390dyn.h> +#include <asm/s390mach.h> + +#define S390_MACHCHK_DEBUG + +static mchchk_queue_element_t *mchchk_queue_head = NULL; +static mchchk_queue_element_t *mchchk_queue_tail = NULL; +static mchchk_queue_element_t *mchchk_queue_free = NULL; +static spinlock_t mchchk_queue_lock; +static struct semaphore s_sem[2]; + +// +// initialize machine check handling +// +void s390_init_machine_check( void ) +{ + init_MUTEX_LOCKED( &s_sem[0] ); + init_MUTEX_LOCKED( &s_sem[1] ); + +#if 0 + // + // fix me ! initialize a machine check queue with 100 elements + // +#ifdef S390_MACHCHK_DEBUG + printk( "init_mach : starting kernel thread\n"); +#endif + + kernel_thread( s390_machine_check_handler, s_sem, 0); + + // + // wait for the machine check handler to be ready + // +#ifdef S390_MACHCHK_DEBUG + printk( "init_mach : waiting for kernel thread\n"); +#endif + + down( &sem[0]); + +#ifdef S390_MACHCHK_DEBUG + printk( "init_mach : kernel thread ready\n"); +#endif + + // + // fix me ! we have to initialize CR14 to allow for CRW pending + // conditions + + // + // fix me ! enable machine checks in the PSW + // +#endif + return; +} + +// +// machine check pre-processor +// +void __init s390_do_machine_check( void ) +{ + // fix me ! we have to check for machine check and + // post the handler eventually + + return; +} + +// +// machine check handler +// +static void __init s390_machine_check_handler( struct semaphore *sem ) +{ +#ifdef S390_MACHCHK_DEBUG + printk( "mach_handler : kernel thread up\n"); +#endif + + up( &sem[0] ); + +#ifdef S390_MACHCHK_DEBUG + printk( "mach_handler : kernel thread ready\n"); +#endif + + do { + +#ifdef S390_MACHCHK_DEBUG + printk( "mach_handler : waiting for wakeup\n"); +#endif + + down_interruptible( &sem[1] ); +#ifdef S390_MACHCHK_DEBUG + printk( "mach_handler : wakeup\n"); +#endif + + break; // fix me ! unconditional surrender ... + + // fix me ! check for machine checks and + // call do_crw_pending() eventually + + } while (1); + + return; +} + +mchchk_queue_element_t *s390_get_mchchk( void ) +{ + unsigned long flags; + mchchk_queue_element_t *qe; + + spin_lock_irqsave( &mchchk_queue_lock, flags ); + + // fix me ! dequeue first element if available + qe = NULL; + + spin_unlock_irqrestore( &mchchk_queue_lock, flags ); + + return qe; +} + +void s390_free_mchchk( mchchk_queue_element_t *mchchk ) +{ + unsigned long flags; + + if ( mchchk != NULL) + { + spin_lock_irqsave( &mchchk_queue_lock, flags ); + + mchchk->next = mchchk_queue_free; + + if ( mchchk_queue_free != NULL ) + { + mchchk_queue_free->prev = mchchk; + + } /* endif */ + + mchchk->prev = NULL; + mchchk_queue_free = mchchk; + + spin_unlock_irqrestore( &mchchk_queue_lock, flags ); + + } /* endif */ + + return; +} + diff --git a/arch/s390/kernel/semaphore.c b/arch/s390/kernel/semaphore.c new file mode 100644 index 000000000..368692222 --- /dev/null +++ b/arch/s390/kernel/semaphore.c @@ -0,0 +1,302 @@ +/* + * linux/arch/S390/kernel/semaphore.c + * + * S390 version + * Copyright (C) 1998 IBM Corporation + * Author(s): Martin Schwidefsky + * + * Derived from "linux/arch/i386/kernel/semaphore.c + * Copyright (C) 1999, Linus Torvalds + * + */ +#include <linux/sched.h> + +#include <asm/semaphore.h> + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to aquire the semaphore, while the "sleeping" + * variable is a count of such aquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is + * protected by the semaphore spinlock. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in <asm/semaphore.h> + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED; + +void __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers ++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. The + * "-1" is because we're still hoping to get + * the lock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_INTERRUPTIBLE|TASK_EXCLUSIVE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + */ +int __down_trylock(struct semaphore * sem) +{ + unsigned long flags; + int sleepers; + + spin_lock_irqsave(&semaphore_lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers, &sem->count)) + wake_up(&sem->wait); + + spin_unlock_irqrestore(&semaphore_lock, flags); + return 1; +} + +void down_read_failed_biased(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&sem->wait, &wait); /* put ourselves at the head of the list */ + + for (;;) { + if (sem->read_bias_granted && xchg(&sem->read_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!sem->read_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +void down_write_failed_biased(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); /* put ourselves at the end of the list */ + + for (;;) { + if (sem->write_bias_granted && xchg(&sem->write_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (!sem->write_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* if the lock is currently unbiased, awaken the sleepers + * FIXME: this wakes up the readers early in a bit of a + * stampede -> bad! + */ + if (atomic_read(&sem->count) >= 0) + wake_up(&sem->wait); +} + +/* Wait for the lock to become unbiased. Readers + * are non-exclusive. =) + */ +void down_read_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + up_read(sem); /* this takes care of granting the lock */ + + add_wait_queue(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +/* Wait for the lock to become unbiased. Since we're + * a writer, we'll make ourselves exclusive. + */ +void down_write_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + up_write(sem); /* this takes care of granting the lock */ + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + if (atomic_read(&sem->count) >= 0) + break; /* we must attempt to aquire or bias the lock */ + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +/* Called when someone has done an up that transitioned from + * negative to non-negative, meaning that the lock has been + * granted to whomever owned the bias. + */ +void rwsem_wake_readers(struct rw_semaphore *sem) +{ + if (xchg(&sem->read_bias_granted, 1)) + BUG(); + wake_up(&sem->wait); +} + +void rwsem_wake_writers(struct rw_semaphore *sem) +{ + if (xchg(&sem->write_bias_granted, 1)) + BUG(); + wake_up(&sem->write_bias_wait); +} + +void __down_read_failed(int count, struct rw_semaphore *sem) +{ + do { + if (count == -1) { + down_read_failed_biased(sem); + break; + } + down_read_failed(sem); + count = atomic_dec_return(&sem->count); + } while (count != 0); +} + +void __down_write_failed(int count, struct rw_semaphore *sem) +{ + do { + if (count < 0 && count > -RW_LOCK_BIAS) { + down_write_failed_biased(sem); + break; + } + down_write_failed(sem); + count = atomic_add_return(-RW_LOCK_BIAS, &sem->count); + } while (count != 0); +} + +void __rwsem_wake(int count, struct rw_semaphore *sem) +{ + if (count == 0) + rwsem_wake_readers(sem); + else + rwsem_wake_writers(sem); +} + diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c new file mode 100644 index 000000000..109af4e76 --- /dev/null +++ b/arch/s390/kernel/setup.c @@ -0,0 +1,385 @@ +/* + * arch/s390/kernel/setup.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * Derived from "arch/i386/kernel/setup.c" + * Copyright (C) 1995, Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/config.h> +#include <linux/init.h> +#ifdef CONFIG_BLK_DEV_RAM +#include <linux/blk.h> +#endif +#include <linux/bootmem.h> +#include <linux/console.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/smp.h> +#include <asm/mmu_context.h> + +/* + * Machine setup.. + */ +__u16 boot_cpu_addr; +int cpus_initialized = 0; +unsigned long cpu_initialized = 0; + +/* + * Setup options + */ + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt*/ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern int root_mountflags; +extern int _text,_etext, _edata, _end; + + +/* + * This is set up by the setup-routine at boot-time + * for S390 need to find out, what we have to setup + * using address 0x10400 ... + */ + +#include <asm/setup.h> + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; + +static struct resource code_resource = { "Kernel code", 0x100000, 0 }; +static struct resource data_resource = { "Kernel data", 0, 0 }; + +/* + * cpu_init() initializes state that is per-CPU. + */ +void __init cpu_init (void) +{ + int nr = smp_processor_id(); + + if (test_and_set_bit(nr,&cpu_initialized)) { + printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr); + for (;;) __sti(); + } + cpus_initialized++; + + /* + * Store processor id in lowcore (used e.g. in timer_interrupt) + */ + asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id)); + S390_lowcore.cpu_data.cpu_addr = hard_smp_processor_id(); + S390_lowcore.cpu_data.cpu_nr = nr; + + /* + * Force FPU initialization: + */ + current->flags &= ~PF_USEDFPU; + current->used_math = 0; + + /* Setup active_mm for idle_task */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if (current->mm) + BUG(); + enter_lazy_tlb(&init_mm, current, nr); +} + +/* + * VM halt and poweroff setup routines + */ +char vmhalt_cmd[128] = ""; +char vmpoff_cmd[128] = ""; + +static inline void strncpy_skip_quote(char *dst, char *src, int n) +{ + int sx, dx; + + dx = 0; + for (sx = 0; src[sx] != 0; sx++) { + if (src[sx] == '"') continue; + dst[dx++] = src[sx]; + if (dx >= n) break; + } +} + +static int __init vmhalt_setup(char *str) +{ + strncpy_skip_quote(vmhalt_cmd, str, 127); + vmhalt_cmd[127] = 0; + return 1; +} + +__setup("vmhalt=", vmhalt_setup); + +static int __init vmpoff_setup(char *str) +{ + strncpy_skip_quote(vmpoff_cmd, str, 127); + vmpoff_cmd[127] = 0; + return 1; +} + +__setup("vmpoff=", vmpoff_setup); + +/* + * Reboot, halt and power_off routines for non SMP. + */ + +#ifndef CONFIG_SMP +void machine_restart(char * __unused) +{ + reipl(S390_lowcore.ipl_device); +} + +void machine_halt(void) +{ + if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) + cpcmd(vmhalt_cmd, NULL, 0); + disabled_wait(0); +} + +void machine_power_off(void) +{ + if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) + cpcmd(vmpoff_cmd, NULL, 0); + disabled_wait(0); +} +#endif + +/* + * Waits for 'delay' microseconds using the tod clock + */ +void tod_wait(unsigned long delay) +{ + uint64_t start_cc, end_cc; + + if (delay == 0) + return; + asm volatile ("STCK %0" : "=m" (start_cc)); + do { + asm volatile ("STCK %0" : "=m" (end_cc)); + } while (((end_cc - start_cc)/4096) < delay); +} + +/* + * Setup function called from init/main.c just after the banner + * was printed. + */ +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long memory_start, memory_end; + char c = ' ', *to = command_line, *from = COMMAND_LINE; + struct resource *res; + unsigned long start_pfn, end_pfn; + static unsigned int smptrap=0; + unsigned long delay = 0; + int len = 0; + + if (smptrap) + return; + smptrap=1; + + printk("Command line is: %s\n", COMMAND_LINE); + + /* + * Setup lowcore information for boot cpu + */ + cpu_init(); + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + + /* + * print what head.S has found out about the machine + */ + printk((MACHINE_IS_VM) ? + "We are running under VM\n" : + "We are running native\n"); + printk((MACHINE_HAS_IEEE) ? + "This machine has an IEEE fpu\n" : + "This machine has no IEEE fpu\n"); + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + /* nasty stuff with PARMAREAs. we use head.S or parameterline + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + */ + memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ + memory_end = MEMORY_SIZE; /* detected in head.s */ + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = (unsigned long) &_text; + code_resource.end = (unsigned long) &_etext - 1; + data_resource.start = (unsigned long) &_etext; + data_resource.end = (unsigned long) &_edata - 1; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + for (;;) { + /* + * "mem=XXX[kKmM]" sets memsize + */ + if (c == ' ' && strncmp(from, "mem=", 4) == 0) { + if (to != command_line) to--; + memory_end = simple_strtoul(from+4, &from, 0); + if ( *from == 'K' || *from == 'k' ) { + memory_end = memory_end << 10; + from++; + } else if ( *from == 'M' || *from == 'm' ) { + memory_end = memory_end << 20; + from++; + } + } + /* + * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes + */ + if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) { + if (to != command_line) to--; + delay = simple_strtoul(from+9, &from, 0); + if (*from == 's' || *from == 'S') { + delay = delay*1000000; + from++; + } else if (*from == 'm' || *from == 'M') { + delay = delay*60*1000000; + from++; + } + /* now wait for the requestion amount of time */ + tod_wait(delay); + } + c = *(from++); + if (!c) + break; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT; + end_pfn = memory_end >> PAGE_SHIFT; + + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem(start_pfn, end_pfn); + + /* + * Register RAM pages with the bootmem allocator. + */ + free_bootmem(start_pfn << PAGE_SHIFT, + (end_pfn - start_pfn) << PAGE_SHIFT); + + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + */ + reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size); + + paging_init(); +#ifdef CONFIG_BLK_DEV_INITRD + if (INITRD_START) { + if (INITRD_START + INITRD_SIZE < memory_end) { + reserve_bootmem(INITRD_START, INITRD_SIZE); + initrd_start = INITRD_START; + initrd_end = initrd_start + INITRD_SIZE; + } else { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_start + INITRD_SIZE, memory_end); + initrd_start = initrd_end = 0; + } + } +#endif + res = alloc_bootmem_low(sizeof(struct resource)); + res->start = 0; + res->end = memory_end; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + request_resource(res, &code_resource); + request_resource(res, &data_resource); +} + +void print_cpu_info(struct cpuinfo_S390 *cpuinfo) +{ + printk("cpu %d " +#ifdef CONFIG_SMP + "phys_idx=%d " +#endif + "vers=%02X ident=%06X machine=%04X unused=%04X\n", + cpuinfo->cpu_nr, +#ifdef CONFIG_SMP + cpuinfo->cpu_addr, +#endif + cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine, + cpuinfo->cpu_id.unused); +} + +/* + * Get CPU information for use by the procfs. + */ + +int get_cpuinfo(char * buffer) +{ + struct cpuinfo_S390 *cpuinfo; + char *p = buffer; + int i; + + p += sprintf(p,"vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: %lu.%02lu\n", + smp_num_cpus, loops_per_sec/500000, + (loops_per_sec/5000)%100); + for (i = 0; i < smp_num_cpus; i++) { + cpuinfo = &safe_get_cpu_lowcore(i).cpu_data; + p += sprintf(p,"processor %i: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + i, cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine); + } + return p - buffer; +} + diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c new file mode 100644 index 000000000..fd1a5e58a --- /dev/null +++ b/arch/s390/kernel/signal.c @@ -0,0 +1,555 @@ +/* + * arch/s390/kernel/signal.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * Based on Intel version + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* pretcode & sig are used to store the return addr on Intel + & the signal no as the first parameter we do this differently + using gpr14 & gpr2. */ + +#define SIGFRAME_COMMON \ +__u8 callee_used_stack[__SIGNAL_FRAMESIZE]; \ +struct sigcontext sc; \ +sigregs sregs; \ +__u8 retcode[S390_SYSCALL_SIZE]; + +typedef struct +{ + SIGFRAME_COMMON +} sigframe; + +typedef struct +{ + SIGFRAME_COMMON + struct siginfo info; + struct ucontext uc; +} rt_sigframe; + +asmlinkage int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); +asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int +sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + struct pt_regs *regs = (struct pt_regs *) &uss; + return do_sigaltstack(uss, uoss, regs->gprs[15]); +} + + + + +static int save_sigregs(struct pt_regs *regs,sigregs *sregs) +{ + int err; + s390_fp_regs fpregs; + + err = __copy_to_user(&sregs->regs,regs,sizeof(s390_regs_common)); + if(!err) + { + save_fp_regs(&fpregs); + err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs)); + } + return(err); + +} + +static int restore_sigregs(struct pt_regs *regs,sigregs *sregs) +{ + int err; + s390_fp_regs fpregs; + psw_t saved_psw=regs->psw; + err=__copy_from_user(regs,&sregs->regs,sizeof(s390_regs_common)); + if(!err) + { + regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| + (regs->psw.mask&PSW_MASK_DEBUGCHANGE); + regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| + (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); + err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs)); + if(!err) + restore_fp_regs(&fpregs); + } + return(err); +} + +static int +restore_sigcontext(struct sigcontext *sc, pt_regs *regs, + sigregs *sregs,sigset_t *set) +{ + unsigned int err; + + err=restore_sigregs(regs,sregs); + if(!err) + err=__copy_from_user(&set->sig,&sc->oldmask,SIGMASK_COPY_SIZE); + return(err); +} + +int sigreturn_common(struct pt_regs *regs,int framesize) +{ + sigframe *frame = (sigframe *)regs->gprs[15]; + sigset_t set; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + return -1; + if (restore_sigcontext(&frame->sc,regs,&frame->sregs,&set)) + return -1; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} + +asmlinkage int sys_sigreturn(struct pt_regs *regs) +{ + + if (sigreturn_common(regs,sizeof(sigframe))) + goto badframe; + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) +{ + rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; + stack_t st; + + if (sigreturn_common(regs,sizeof(rt_sigframe))) + goto badframe; + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs->gprs[15]); + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long sp; + + /* Default to using normal stack */ + sp = regs->gprs[15]; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + /* This is the legacy signal stack switching. */ + else if (!user_mode(regs) && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + sp = (unsigned long) ka->sa.sa_restorer; + } + + return (void *)((sp - frame_size) & -8ul); +} + +static void *setup_frame_common(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs, + int frame_size,u16 retcode) +{ + sigframe *frame; + int err; + + frame = get_sigframe(ka, regs,frame_size); + if (!access_ok(VERIFY_WRITE, frame,frame_size)) + return 0; + err = save_sigregs(regs,&frame->sregs); + if(!err) + err=__put_user(&frame->sregs,&frame->sc.sregs); + if(!err) + + err=__copy_to_user(&frame->sc.oldmask,&set->sig,SIGMASK_COPY_SIZE); + if(!err) + { + regs->gprs[2]=(current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig); + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + } + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(retcode, (u16 *)(frame->retcode)); + } + return(err ? 0:frame); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + + if(!setup_frame_common(sig,ka,set,regs,sizeof(sigframe), + (S390_SYSCALL_OPCODE|__NR_sigreturn))) + goto give_sigsegv; +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + rt_sigframe *frame; + addr_t orig_sp=regs->gprs[15]; + int err; + + if((frame=setup_frame_common(sig,ka,set,regs,sizeof(rt_sigframe), + (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + goto give_sigsegv; + + err = __copy_to_user(&frame->info, info, sizeof(*info)); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(orig_sp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + regs->gprs[3] = (u32)&frame->info; + regs->gprs[4] = (u32)&frame->uc; + + if (err) + goto give_sigsegv; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ + /* Are we from a system call? */ + if (regs->orig_gpr2 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->gprs[2]) { + case -ERESTARTNOHAND: + regs->gprs[2] = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->gprs[2] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + + if (!oldset) + oldset = ¤t->blocked; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if ( regs->trap == 0x20 /* System Call! */ ) { + /* Restart the system call - no handlers present */ + if (regs->gprs[2] == -ERESTARTNOHAND || + regs->gprs[2] == -ERESTARTSYS || + regs->gprs[2] == -ERESTARTNOINTR) { + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + } + } + return 0; +} diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c new file mode 100644 index 000000000..e8b975070 --- /dev/null +++ b/arch/s390/kernel/smp.c @@ -0,0 +1,729 @@ +/* + * arch/s390/kernel/smp.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * based on other smp stuff by + * (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net> + * (c) 1998 Ingo Molnar + * + * We work with logical cpu numbering everywhere we can. The only + * functions using the real cpu address (got from STAP) are the sigp + * functions. For all other functions we use the identity mapping. + * That means that cpu_number_map[i] == i for every cpu. cpu_number_map is + * used e.g. to find the idle task belonging to a logical cpu. Every array + * in the kernel is sorted by the logical cpu number and not by the physical + * one which is causing all the confusion with __cpu_logical_map and + * cpu_number_map in other architectures. + */ + +#include <linux/init.h> + +#include <linux/mm.h> +#include <linux/spinlock.h> +#include <linux/kernel_stat.h> +#include <linux/smp_lock.h> + +#include <linux/delay.h> + +#include <asm/sigp.h> +#include <asm/pgalloc.h> +#include <asm/irq.h> + +#include "cpcmd.h" + +/* prototypes */ +extern void update_one_process( struct task_struct *p, + unsigned long ticks, unsigned long user, + unsigned long system, int cpu); +extern int cpu_idle(void * unused); + +extern __u16 boot_cpu_addr; + +/* + * An array with a pointer the lowcore of every CPU. + */ +static int max_cpus = NR_CPUS; /* Setup configured maximum number of CPUs to activate */ +int smp_num_cpus; +struct _lowcore *lowcore_ptr[NR_CPUS]; +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_old_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; +volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ +cycles_t cacheflush_time=0; +int smp_threads_ready=0; /* Set when the idlers are all forked. */ +unsigned long ipi_count=0; /* Number of IPIs delivered. */ +static atomic_t smp_commenced = ATOMIC_INIT(0); + +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +/* + * Setup routine for controlling SMP activation + * + * Command-line option of "nosmp" or "maxcpus=0" will disable SMP + * activation entirely (the MPS table probe still happens, though). + * + * Command-line option of "maxcpus=<NUM>", where <NUM> is an integer + * greater than 0, limits the maximum number of CPUs activated in + * SMP mode to <NUM>. + */ + +static int __init nosmp(char *str) +{ + max_cpus = 0; + return 1; +} + +__setup("nosmp", nosmp); + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); + +/* + * Reboot, halt and power_off routines for SMP. + */ +extern char vmhalt_cmd[]; +extern char vmpoff_cmd[]; + +extern void reipl(unsigned long devno); + +void do_machine_restart(void) +{ + smp_send_stop(); + reipl(S390_lowcore.ipl_device); +} + +void machine_restart(char * __unused) +{ + if (smp_processor_id() != 0) { + smp_ext_call_async(0, ec_restart); + for (;;); + } else + do_machine_restart(); +} + +void do_machine_halt(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) + cpcmd(vmhalt_cmd, NULL, 0); + disabled_wait(0); +} + +void machine_halt(void) +{ + if (smp_processor_id() != 0) { + smp_ext_call_async(0, ec_halt); + for (;;); + } else + do_machine_halt(); +} + +void do_machine_power_off(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) + cpcmd(vmpoff_cmd, NULL, 0); + disabled_wait(0); +} + +void machine_power_off(void) +{ + if (smp_processor_id() != 0) { + smp_ext_call_async(0, ec_power_off); + for (;;); + } else + do_machine_power_off(); +} + +/* + * This is the main routine where commands issued by other + * cpus are handled. + */ + +void do_ext_call_interrupt(__u16 source_cpu_addr) +{ + ec_ext_call *ec, *next; + int bits; + + /* + * handle bit signal external calls + * + * For the ec_schedule signal we have to do nothing. All the work + * is done automatically when we return from the interrupt. + * For the ec_restart, ec_halt and ec_power_off we call the + * appropriate routine. + */ + do { + bits = atomic_read(&S390_lowcore.ext_call_fast); + } while (atomic_compare_and_swap(bits,0,&S390_lowcore.ext_call_fast)); + + if (test_bit(ec_restart, &bits)) + do_machine_restart(); + if (test_bit(ec_halt, &bits)) + do_machine_halt(); + if (test_bit(ec_power_off, &bits)) + do_machine_power_off(); + + /* + * Handle external call commands with a parameter area + */ + do { + ec = (ec_ext_call *) atomic_read(&S390_lowcore.ext_call_queue); + } while (atomic_compare_and_swap((int) ec, 0, + &S390_lowcore.ext_call_queue)); + if (ec == NULL) + return; /* no command signals */ + + /* Make a fifo out of the lifo */ + next = ec; + ec->next = NULL; + while (next != NULL) { + ec_ext_call *tmp = next->next; + next->next = ec; + ec = next; + next = tmp; + } + + /* Execute every sigp command on the queue */ + while (ec != NULL) { + switch (ec->cmd) { + case ec_get_ctl: { + ec_creg_parms *pp; + pp = (ec_creg_parms *) ec->parms; + atomic_set(&ec->status,ec_executing); + asm volatile ( + " bras 1,0f\n" + " stctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (pp->cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + atomic_set(&ec->status,ec_done); + return; + } + case ec_set_ctl: { + ec_creg_parms *pp; + pp = (ec_creg_parms *) ec->parms; + atomic_set(&ec->status,ec_executing); + asm volatile ( + " bras 1,0f\n" + " lctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (pp->cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + atomic_set(&ec->status,ec_done); + return; + } + case ec_set_ctl_masked: { + ec_creg_mask_parms *pp; + u32 cregs[16]; + int i; + + pp = (ec_creg_mask_parms *) ec->parms; + atomic_set(&ec->status,ec_executing); + asm volatile ( + " bras 1,0f\n" + " stctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + for (i = pp->start_ctl; i <= pp->end_ctl; i++) + cregs[i] = (cregs[i] & pp->andvals[i]) + | pp->orvals[i]; + asm volatile ( + " bras 1,0f\n" + " lctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + atomic_set(&ec->status,ec_done); + return; + } + default: + } + ec = ec->next; + } +} + +/* + * Send an external call sigp to another cpu and wait for its completion. + */ +sigp_ccode smp_ext_call_sync(int cpu, ec_cmd_sig cmd, void *parms) +{ + struct _lowcore *lowcore = &get_cpu_lowcore(cpu); + sigp_ccode ccode; + ec_ext_call ec; + + ec.cmd = cmd; + atomic_set(&ec.status, ec_pending); + ec.parms = parms; + do { + ec.next = (ec_ext_call*) atomic_read(&lowcore->ext_call_queue); + } while (atomic_compare_and_swap((int) ec.next, (int)(&ec), + &lowcore->ext_call_queue)); + /* + * We try once to deliver the signal. There are four possible + * return codes: + * 0) Order code accepted - can't show up on an external call + * 1) Status stored - fine, wait for completion. + * 2) Busy - there is another signal pending. Thats fine too, because + * do_ext_call from the pending signal will execute all signals on + * the queue. We wait for completion. + * 3) Not operational - something very bad has happened to the cpu. + * do not wait for completion. + */ + ccode = signal_processor(cpu, sigp_external_call); + + if (ccode != sigp_not_operational) + /* wait for completion, FIXME: possible seed of a deadlock */ + while (atomic_read(&ec.status) != ec_done); + + return ccode; +} + +/* + * Send an external call sigp to another cpu and return without waiting + * for its completion. Currently we do not support parameters with + * asynchronous sigps. + */ +sigp_ccode smp_ext_call_async(int cpu, ec_bit_sig sig) +{ + struct _lowcore *lowcore = &get_cpu_lowcore(cpu); + sigp_ccode ccode; + + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + atomic_set_mask(1<<sig, &lowcore->ext_call_fast); + ccode = signal_processor(cpu, sigp_external_call); + return ccode; +} + +/* + * Send an external call sigp to every other cpu in the system and + * wait for the completion of the sigps. + */ +void smp_ext_call_sync_others(ec_cmd_sig cmd, void *parms) +{ + struct _lowcore *lowcore; + ec_ext_call ec[NR_CPUS]; + sigp_ccode ccode; + int i; + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + lowcore = &get_cpu_lowcore(i); + ec[i].cmd = cmd; + atomic_set(&ec[i].status, ec_pending); + ec[i].parms = parms; + do { + ec[i].next = (ec_ext_call *) + atomic_read(&lowcore->ext_call_queue); + } while (atomic_compare_and_swap((int) ec[i].next, (int)(ec+i), + &lowcore->ext_call_queue)); + ccode = signal_processor(i, sigp_external_call); + } + + /* wait for completion, FIXME: possible seed of a deadlock */ + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + while (atomic_read(&ec[i].status) != ec_done); + } +} + +/* + * Send an external call sigp to every other cpu in the system and + * return without waiting for the completion of the sigps. Currently + * we do not support parameters with asynchronous sigps. + */ +void smp_ext_call_async_others(ec_bit_sig sig) +{ + struct _lowcore *lowcore; + sigp_ccode ccode; + int i; + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + lowcore = &get_cpu_lowcore(i); + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + atomic_set_mask(1<<sig, &lowcore->ext_call_fast); + ccode = signal_processor(i, sigp_external_call); + } +} + +/* + * cycles through all the cpus, + * returns early if info is not NULL & the processor has something + * of intrest to report in the info structure. + * it returns the next cpu to check if it returns early. + * i.e. it should be used as follows if you wish to receive info. + * next_cpu=0; + * do + * { + * info->cpu=next_cpu; + * next_cpu=smp_signal_others(order_code,parameter,1,info); + * ... check info here + * } while(next_cpu<=smp_num_cpus) + * + * if you are lazy just use it like + * smp_signal_others(order_code,parameter,0,1,NULL); + */ +int smp_signal_others(sigp_order_code order_code, u32 parameter, + int spin, sigp_info *info) +{ + sigp_ccode ccode; + u32 dummy; + u16 i; + + if (info) + info->intresting = 0; + for (i = (info ? info->cpu : 0); i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + do { + ccode = signal_processor_ps( + (info ? &info->status : &dummy), + parameter, i, order_code); + } while(spin && ccode == sigp_busy); + if (info && ccode != sigp_order_code_accepted) { + info->intresting = 1; + info->cpu = i; + info->ccode = ccode; + i++; + break; + } + } + } + return i; +} + +/* + * this function sends a 'stop' sigp to all other CPUs in the system. + * it goes straight through. + */ + +void smp_send_stop(void) +{ + smp_signal_others(sigp_stop, 0, 1, NULL); +} + +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ + +void smp_send_reschedule(int cpu) +{ + smp_ext_call_async(cpu, ec_schedule); +} + +/* + * Set a bit in a control register of all cpus + */ +void smp_ctl_set_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + if (atomic_read(&smp_commenced) != 0) { + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 1 << bit; + parms.andvals[cr] = 0xFFFFFFFF; + smp_ext_call_sync_others(ec_set_ctl_masked,&parms); + } + __ctl_set_bit(cr, bit); +} + +/* + * Clear a bit in a control register of all cpus + */ +void smp_ctl_clear_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + if (atomic_read(&smp_commenced) != 0) { + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 0x00000000; + parms.andvals[cr] = ~(1 << bit); + smp_ext_call_sync_others(ec_set_ctl_masked,&parms); + } + __ctl_clear_bit(cr, bit); +} + + +/* + * Lets check how many CPUs we have. + */ + +void smp_count_cpus(void) +{ + int curr_cpu; + + __cpu_logical_map[0] = boot_cpu_addr; + current->processor = 0; + smp_num_cpus = 1; + for (curr_cpu = 0; + curr_cpu <= 65535 && smp_num_cpus < max_cpus; curr_cpu++) { + if ((__u16) curr_cpu == boot_cpu_addr) + continue; + __cpu_logical_map[smp_num_cpus] = (__u16) curr_cpu; + if (signal_processor(smp_num_cpus, sigp_sense) == + sigp_not_operational) + continue; + smp_num_cpus++; + } + printk("Detected %d CPU's\n",(int) smp_num_cpus); + printk("Boot cpu address %2X\n", boot_cpu_addr); +} + + +/* + * Activate a secondary processor. + */ +extern void init_100hz_timer(void); + +int __init start_secondary(void *cpuvoid) +{ + /* Setup the cpu */ + cpu_init(); + /* Print info about this processor */ + print_cpu_info(&safe_get_cpu_lowcore(smp_processor_id()).cpu_data); + /* Wait for completion of smp startup */ + while (!atomic_read(&smp_commenced)) + /* nothing */ ; + /* init per CPU 100 hz timer */ + init_100hz_timer(); + /* cpu_idle will call schedule for us */ + return cpu_idle(NULL); +} + +/* + * The restart interrupt handler jumps to start_secondary directly + * without the detour over initialize_secondary. We defined it here + * so that the linker doesn't complain. + */ +void __init initialize_secondary(void) +{ +} + +static int __init fork_by_hand(void) +{ + struct pt_regs regs; + /* don't care about the psw and regs settings since we'll never + reschedule the forked task. */ + memset(®s,sizeof(pt_regs),0); + return do_fork(CLONE_VM|CLONE_PID, 0, ®s); +} + +static void __init do_boot_cpu(int cpu) +{ + struct task_struct *idle; + struct _lowcore *cpu_lowcore; + + /* We can't use kernel_thread since we must _avoid_ to reschedule + the child. */ + if (fork_by_hand() < 0) + panic("failed fork for CPU %d", cpu); + + /* + * We remove it from the pidhash and the runqueue + * once we got the process: + */ + idle = init_task.prev_task; + if (!idle) + panic("No idle process for CPU %d",cpu); + idle->processor = cpu; + idle->has_cpu = 1; /* we schedule the first task manually */ + + del_from_runqueue(idle); + unhash_process(idle); + init_tasks[cpu] = idle; + + cpu_lowcore=&get_cpu_lowcore(cpu); + cpu_lowcore->kernel_stack=idle->thread.ksp; + __asm__ __volatile__("stctl 0,15,%0\n\t" + "stam 0,15,%1" + : "=m" (cpu_lowcore->cregs_save_area[0]), + "=m" (cpu_lowcore->access_regs_save_area[0]) + : : "memory"); + + eieio(); + signal_processor(cpu,sigp_restart); +} + +/* + * Architecture specific routine called by the kernel just before init is + * fired off. This allows the BP to have everything in order [we hope]. + * At the end of this all the APs will hit the system scheduling and off + * we go. Each AP will load the system gdt's and jump through the kernel + * init into idle(). At this point the scheduler will one day take over + * and give them jobs to do. smp_callin is a standard routine + * we use to track CPUs as they power up. + */ + +void __init smp_commence(void) +{ + /* + * Lets the callins below out of their loop. + */ + atomic_set(&smp_commenced,1); +} + +/* + * Cycle through the processors sending APIC IPIs to boot each. + */ + +void __init smp_boot_cpus(void) +{ + struct _lowcore *curr_lowcore; + sigp_ccode ccode; + int i; + + smp_count_cpus(); + memset(lowcore_ptr,0,sizeof(lowcore_ptr)); + + /* + * Initialize the logical to physical CPU number mapping + * and the per-CPU profiling counter/multiplier + */ + + for (i = 0; i < NR_CPUS; i++) { + prof_counter[i] = 1; + prof_old_multiplier[i] = 1; + prof_multiplier[i] = 1; + } + + print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data); + + for(i = 0; i < smp_num_cpus; i++) + { + curr_lowcore = (struct _lowcore *) + __get_free_page(GFP_KERNEL|GFP_DMA); + if (curr_lowcore == NULL) { + printk("smp_boot_cpus failed to allocate prefix memory\n"); + break; + } + lowcore_ptr[i] = curr_lowcore; + memcpy(curr_lowcore, &S390_lowcore, sizeof(struct _lowcore)); + /* + * Most of the parameters are set up when the cpu is + * started up. + */ + if (smp_processor_id() == i) + set_prefix((u32) curr_lowcore); + else { + ccode = signal_processor_p((u32)(curr_lowcore), + i, sigp_set_prefix); + if(ccode) { + /* if this gets troublesome I'll have to do + * something about it. */ + printk("ccode %d for cpu %d returned when " + "setting prefix in smp_boot_cpus not good.\n", + (int) ccode, (int) i); + } + else + do_boot_cpu(i); + } + } +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + * + * usually you want to run this on all CPUs ;) + */ +int setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* + * Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new multiplier + * value into /proc/profile. + */ + +void smp_local_timer_interrupt(struct pt_regs * regs) +{ + int user = (user_mode(regs) != 0); + int cpu = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + if (!user_mode(regs)) + s390_do_profile(regs->psw.addr); + + if (!--prof_counter[cpu]) { + int system = 1-user; + struct task_struct * p = current; + + /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + prof_counter[cpu] = prof_multiplier[cpu]; + if (prof_counter[cpu] != prof_old_multiplier[cpu]) { + /* FIXME setup_APIC_timer(calibration_result/prof_counter[cpu] + ); */ + prof_old_multiplier[cpu] = prof_counter[cpu]; + } + + /* + * After doing the above, we need to make like + * a normal interrupt - otherwise timer interrupts + * ignore the global interrupt lock, which is the + * WrongThing (tm) to do. + */ + + irq_enter(cpu, 0); + update_one_process(p, 1, user, system, cpu); + if (p->pid) { + p->counter -= 1; + if (p->counter <= 0) { + p->counter = 0; + p->need_resched = 1; + } + if (p->priority < DEF_PRIORITY) { + kstat.cpu_nice += user; + kstat.per_cpu_nice[cpu] += user; + } else { + kstat.cpu_user += user; + kstat.per_cpu_user[cpu] += user; + } + kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; + + } + irq_exit(cpu, 0); + } +} + diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c new file mode 100644 index 000000000..268ea007a --- /dev/null +++ b/arch/s390/kernel/sys_s390.c @@ -0,0 +1,257 @@ +/* + * arch/s390/kernel/sys_s390.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Derived from "arch/i386/kernel/sys_i386.c" + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/s390 + * platform. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/utsname.h> + +#include <asm/uaccess.h> +#include <asm/ipc.h> + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + lock_kernel(); + error = do_pipe(fd); + unlock_kernel(); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down(¤t->mm->mmap_sem); + lock_kernel(); + + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + + unlock_kernel(); + up(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +/* FIXME: 6 parameters is one too much ... */ +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +asmlinkage int old_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + error = -EINVAL; + if (a.offset & ~PAGE_MASK) + goto out; + + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); +out: + return error; +} + +extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr) +{ + struct ipc_kludge tmp; + int ret; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + break; + case MSGRCV: + if (!ptr) + return -EINVAL; + if (copy_from_user (&tmp, (struct ipc_kludge *) ptr, + sizeof (struct ipc_kludge))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, + second, tmp.msgtyp, third); + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + break; + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + + } + + return -EINVAL; +} + +/* + * Old cruft + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + + error = __copy_to_user(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN); + error |= __put_user(0,name->sysname+__OLD_UTS_LEN); + error |= __copy_to_user(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN); + error |= __put_user(0,name->nodename+__OLD_UTS_LEN); + error |= __copy_to_user(&name->release,&system_utsname.release,__OLD_UTS_LEN); + error |= __put_user(0,name->release+__OLD_UTS_LEN); + error |= __copy_to_user(&name->version,&system_utsname.version,__OLD_UTS_LEN); + error |= __put_user(0,name->version+__OLD_UTS_LEN); + error |= __copy_to_user(&name->machine,&system_utsname.machine,__OLD_UTS_LEN); + error |= __put_user(0,name->machine+__OLD_UTS_LEN); + + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + return error; +} + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -ENOSYS; +} + diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c new file mode 100644 index 000000000..8cd84ee54 --- /dev/null +++ b/arch/s390/kernel/time.c @@ -0,0 +1,250 @@ +/* + * arch/s390/kernel/time.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * Derived from "arch/i386/kernel/time.c" + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/types.h> + +#include <asm/uaccess.h> +#include <asm/delay.h> + +#include <linux/mc146818rtc.h> +#include <linux/timex.h> + +#include <asm/irq.h> + + +extern volatile unsigned long lost_ticks; + +/* change this if you have some constant time drift */ +#define USECS_PER_JIFFY ((signed long)1000000/HZ) +#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12) + +#define TICK_SIZE tick + +static uint64_t init_timer_cc, last_timer_cc; + +extern rwlock_t xtime_lock; + +void tod_to_timeval(uint64_t todval, struct timeval *xtime) +{ + const int high_bit = 0x80000000L; + const int c_f4240 = 0xf4240L; + const int c_7a120 = 0x7a120; + /* We have to divide the 64 bit value todval by 4096 + * (because the 2^12 bit is the one that changes every + * microsecond) and then split it into seconds and + * microseconds. A value of max (2^52-1) divided by + * the value 0xF4240 can yield a max result of approx + * (2^32.068). Thats to big to fit into a signed int + * ... hacking time! + */ + asm volatile ("L 2,%1\n\t" + "LR 3,2\n\t" + "SRL 2,12\n\t" + "SLL 3,20\n\t" + "L 4,%O1+4(%R1)\n\t" + "SRL 4,12\n\t" + "OR 3,4\n\t" /* now R2/R3 contain (todval >> 12) */ + "SR 4,4\n\t" + "CL 2,%2\n\t" + "JL .+12\n\t" + "S 2,%2\n\t" + "L 4,%3\n\t" + "D 2,%4\n\t" + "OR 3,4\n\t" + "ST 2,%O0+4(%R0)\n\t" + "ST 3,%0" + : "=m" (*xtime) : "m" (todval), + "m" (c_7a120), "m" (high_bit), "m" (c_f4240) + : "cc", "memory", "2", "3", "4" ); +} + +unsigned long do_gettimeoffset(void) +{ + __u64 timer_cc; + + asm volatile ("STCK %0" : "=m" (timer_cc)); + /* We require the offset from the previous interrupt */ + return ((unsigned long)((timer_cc - last_timer_cc)>>12)); +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + extern volatile unsigned long lost_ticks; + unsigned long flags; + unsigned long usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + if (lost_ticks) + usec +=(USECS_PER_JIFFY*lost_ticks); + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + + write_lock_irq(&xtime_lock); + /* This is revolting. We need to set the xtime.tv_usec + * correctly. However, the value in this location is + * is value at the last tick. + * Discover what correction gettimeofday + * would have done, and then undo it! + */ + tv->tv_usec -= do_gettimeoffset(); + + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +#ifdef CONFIG_SMP +extern __u16 boot_cpu_addr; +#endif + +void do_timer_interrupt(struct pt_regs *regs,int error_code) +{ + unsigned long flags; + + /* + * reset timer to 10ms minus time already elapsed + * since timer-interrupt pending + */ + + save_flags(flags); + cli(); +#ifdef CONFIG_SMP + if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) { + write_lock(&xtime_lock); + last_timer_cc = S390_lowcore.jiffy_timer_cc; + } +#else + last_timer_cc = S390_lowcore.jiffy_timer_cc; +#endif + /* set clock comparator */ + S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc)); + +/* + * In the SMP case we use the local timer interrupt to do the + * profiling, except when we simulate SMP mode on a uniprocessor + * system, in that case we have to call the local interrupt handler. + */ +#ifdef CONFIG_SMP + /* when SMP, do smp_local_timer_interrupt for *all* CPUs, + but only do the rest for the boot CPU */ + smp_local_timer_interrupt(regs); +#else + if (!user_mode(regs)) + s390_do_profile(regs->psw.addr); +#endif + +#ifdef CONFIG_SMP + if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) +#endif + { + do_timer(regs); +#ifdef CONFIG_SMP + write_unlock(&xtime_lock); +#endif + } + restore_flags(flags); + +} + +/* + * Start the clock comparator on the current CPU + */ +static long cr0 __attribute__ ((aligned (8))); + +void init_100hz_timer(void) +{ + /* allow clock comparator timer interrupt */ + asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory"); + cr0 |= 0x800; + asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory"); + /* set clock comparator */ + /* read the TOD clock */ + asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc)); + S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc)); +} + +/* + * Initialize the TOD clock and the CPU timer of + * the boot cpu. + */ +void __init time_init(void) +{ + int cc; + + /* kick the TOD clock */ + asm volatile ("STCK %1\n\t" + "IPM %0\n\t" + "SRL %0,28" : "=r" (cc), "=m" (init_timer_cc)); + switch (cc) { + case 0: /* clock in set state: all is fine */ + break; + case 1: /* clock in non-set state: FIXME */ + printk("time_init: TOD clock in non-set state\n"); + break; + case 2: /* clock in error state: FIXME */ + printk("time_init: TOD clock in error state\n"); + break; + case 3: /* clock in stopped or not-operational state: FIXME */ + printk("time_init: TOD clock stopped/non-operational\n"); + break; + } + init_100hz_timer(); + init_timer_cc = S390_lowcore.jiffy_timer_cc; + init_timer_cc -= 0x8126d60e46000000LL - + (0x3c26700LL*1000000*4096); + tod_to_timeval(init_timer_cc, &xtime); +} diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c new file mode 100644 index 000000000..b207d78aa --- /dev/null +++ b/arch/s390/kernel/traps.c @@ -0,0 +1,480 @@ +/* + * arch/s390/kernel/traps.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Derived from "arch/i386/kernel/traps.c" + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. + */ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/mathemu.h> +#if CONFIG_REMOTE_DEBUG +#include <asm/gdb-stub.h> +#endif + +/* Called from entry.S only */ +extern void handle_per_exception(struct pt_regs *regs); + +typedef void pgm_check_handler_t(struct pt_regs *, long); +pgm_check_handler_t *pgm_check_table[128]; + +extern pgm_check_handler_t default_trap_handler; +extern pgm_check_handler_t do_page_fault; + +asmlinkage int system_call(void); + +static inline void console_verbose(void) +{ + extern int console_loglevel; + console_loglevel = 15; +} + +#define DO_ERROR(trapnr, signr, str, name, tsk) \ +asmlinkage void name(struct pt_regs * regs, long error_code) \ +{ \ + tsk->thread.error_code = error_code; \ + tsk->thread.trap_no = trapnr; \ + die_if_no_fixup(str,regs,error_code); \ + force_sig(signr, tsk); \ +} + + +void page_exception(void); + +/* TODO: define these as 'pgm_check_handler_t xxx;' +asmlinkage void divide_error(void); +asmlinkage void debug(void); +asmlinkage void nmi(void); +asmlinkage void int3(void); +asmlinkage void overflow(void); +asmlinkage void bounds(void); +asmlinkage void invalid_op(void); +asmlinkage void device_not_available(void); +asmlinkage void double_fault(void); +asmlinkage void coprocessor_segment_overrun(void); +asmlinkage void invalid_TSS(void); +asmlinkage void segment_not_present(void); +asmlinkage void stack_segment(void); +asmlinkage void general_protection(void); +asmlinkage void coprocessor_error(void); +asmlinkage void reserved(void); +asmlinkage void alignment_check(void); +asmlinkage void spurious_interrupt_bug(void); +*/ + +int kstack_depth_to_print = 24; + +/* + * These constants are for searching for possible module text + * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is + * a guess of how much space is likely to be vmalloced. + */ +#define VMALLOC_OFFSET (8*1024*1024) +#define MODULE_RANGE (8*1024*1024) + +void show_crashed_task_info(void) +{ + printk("CPU: %d\n",smp_processor_id()); + printk("Process %s (pid: %d, stackpage=%08X)\n", + current->comm, current->pid, 4096+(addr_t)current); + show_regs(current,NULL,NULL); +} +#if 0 +static void show_registers(struct pt_regs *regs) +{ + printk("CPU: %d\nPSW: %08lx %08lx\n", + smp_processor_id(), (unsigned long) regs->psw.mask, + (unsigned long) regs->psw.addr); + printk("GPRS:\n"); + + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[0], regs->gprs[1], + regs->gprs[2], regs->gprs[3]); + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[4], regs->gprs[5], + regs->gprs[6], regs->gprs[7]); + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[8], regs->gprs[9], + regs->gprs[10], regs->gprs[11]); + printk("%08lx %08lx %08lx %08lx\n", + regs->gprs[12], regs->gprs[13], + regs->gprs[14], regs->gprs[15]); + printk("Process %s (pid: %d, stackpage=%08lx)\nStack: ", + current->comm, current->pid, 4096+(unsigned long)current); +/* + stack = (unsigned long *) esp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & 4095) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", get_seg_long(ss,stack++)); + } + printk("\nCall Trace: "); + stack = (unsigned long *) esp; + i = 1; + module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT); + module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); + module_end = module_start + MODULE_RANGE; + while (((long) stack & 4095) != 0) { + addr = get_seg_long(ss, stack++); */ + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ +/* if (((addr >= (unsigned long) &_stext) && + (addr <= (unsigned long) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } + printk("\nCode: "); + for(i=0;i<20;i++) + printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip))); + printk("\n"); +*/ +} +#endif + + +spinlock_t die_lock; + +void die(const char * str, struct pt_regs * regs, long err) +{ + console_verbose(); + spin_lock_irq(&die_lock); + printk("%s: %04lx\n", str, err & 0xffff); + show_crashed_task_info(); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +int check_for_fixup(struct pt_regs * regs) +{ + if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + unsigned long fixup; + fixup = search_exception_table(regs->psw.addr); + if (fixup) { + regs->psw.addr = fixup; + return 1; + } + } + return 0; +} + +int do_debugger_trap(struct pt_regs *regs,int signal) +{ + if(regs->psw.mask&PSW_PROBLEM_STATE) + { + if(current->flags & PF_PTRACED) + force_sig(signal,current); + else + return 1; + } + else + { +#if CONFIG_REMOTE_DEBUG + if(gdb_stub_initialised) + { + gdb_stub_handle_exception((gdb_pt_regs *)regs,signal); + return 0; + } +#endif + return 1; + } + return 0; +} + +static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) +{ + if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { + unsigned long fixup; + fixup = search_exception_table(regs->psw.addr); + if (fixup) { + regs->psw.addr = fixup; + return; + } + die(str, regs, err); + } +} + +asmlinkage void default_trap_handler(struct pt_regs * regs, long error_code) +{ + current->thread.error_code = error_code; + current->thread.trap_no = error_code; + die_if_no_fixup("Unknown program exception",regs,error_code); + force_sig(SIGSEGV, current); +} + +DO_ERROR(2, SIGILL, "privileged operation", privileged_op, current) +DO_ERROR(3, SIGILL, "execute exception", execute_exception, current) +DO_ERROR(5, SIGSEGV, "addressing exception", addressing_exception, current) +DO_ERROR(9, SIGFPE, "fixpoint divide exception", divide_exception, current) +DO_ERROR(0x12, SIGILL, "translation exception", translation_exception, current) +DO_ERROR(0x13, SIGILL, "special operand exception", special_op_exception, current) +DO_ERROR(0x15, SIGILL, "operand exception", operand_exception, current) + +/* need to define +DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current) +DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current) +DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math) +DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) +DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) +DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) +DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) +DO_ERROR(18, SIGSEGV, "reserved", reserved, current) +DO_ERROR(19, SIGSEGV, "cache flush denied", cache_flush_denied, current) +*/ + +#ifdef CONFIG_IEEEFPU_EMULATION + +asmlinkage void illegal_op(struct pt_regs * regs, long error_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE); + + lock_kernel(); + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + if(problem_state) + get_user(*((__u16 *) opcode), location); + else + *((__u16 *)opcode)=*((__u16 *)location); + if(*((__u16 *)opcode)==S390_BREAKPOINT_U16) + { + if(do_debugger_trap(regs,SIGTRAP)) + do_sig=1; + } + else if (problem_state ) + { + if (opcode[0] == 0xb3) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_b3(opcode, regs); + } else if (opcode[0] == 0xed) { + get_user(*((__u32 *) (opcode+2)), + (__u32 *)(location+1)); + do_sig = math_emu_ed(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb299) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_srnm(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb29c) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_stfpc(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb29d) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_lfpc(opcode, regs); + } else + do_sig = 1; + } else + do_sig = 1; + if (do_sig) { + current->thread.error_code = error_code; + current->thread.trap_no = 1; + force_sig(SIGILL, current); + die_if_no_fixup("illegal operation", regs, error_code); + } + unlock_kernel(); +} + +asmlinkage void specification_exception(struct pt_regs * regs, long error_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + + lock_kernel(); + if (regs->psw.mask & 0x00010000L) { + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + get_user(*((__u16 *) opcode), location); + switch (opcode[0]) { + case 0x28: /* LDR Rx,Ry */ + math_emu_ldr(opcode); + break; + case 0x38: /* LER Rx,Ry */ + math_emu_ler(opcode); + break; + case 0x60: /* STD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_std(opcode, regs); + break; + case 0x68: /* LD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ld(opcode, regs); + break; + case 0x70: /* STE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ste(opcode, regs); + break; + case 0x78: /* LE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_le(opcode, regs); + break; + default: + do_sig = 1; + break; + } + } else + do_sig = 1; + if (do_sig) { + current->thread.error_code = error_code; + current->thread.trap_no = 1; + force_sig(SIGILL, current); + die_if_no_fixup("illegal operation", regs, error_code); + } + unlock_kernel(); +} + +asmlinkage void data_exception(struct pt_regs * regs, long error_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + + lock_kernel(); + if (regs->psw.mask & 0x00010000L) { + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + get_user(*((__u16 *) opcode), location); + switch (opcode[0]) { + case 0x28: /* LDR Rx,Ry */ + math_emu_ldr(opcode); + break; + case 0x38: /* LER Rx,Ry */ + math_emu_ler(opcode); + break; + case 0x60: /* STD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_std(opcode, regs); + break; + case 0x68: /* LD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ld(opcode, regs); + break; + case 0x70: /* STE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_ste(opcode, regs); + break; + case 0x78: /* LE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + math_emu_le(opcode, regs); + break; + case 0xb3: + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_b3(opcode, regs); + break; + case 0xed: + get_user(*((__u32 *) (opcode+2)), + (__u32 *)(location+1)); + do_sig = math_emu_ed(opcode, regs); + break; + case 0xb2: + if (opcode[1] == 0x99) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_srnm(opcode, regs); + } else if (opcode[1] == 0x9c) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_stfpc(opcode, regs); + } else if (opcode[1] == 0x9d) { + get_user(*((__u16 *) (opcode+2)), location+1); + do_sig = math_emu_lfpc(opcode, regs); + } else + do_sig = 1; + break; + default: + do_sig = 1; + break; + } + } else + do_sig = 1; + if (do_sig) { + current->thread.error_code = error_code; + current->thread.trap_no = 1; + force_sig(SIGILL, current); + die_if_no_fixup("illegal operation", regs, error_code); + } + unlock_kernel(); +} + +#else +DO_ERROR(1, SIGILL, "illegal operation", illegal_op, current) +DO_ERROR(6, SIGILL, "specification exception", specification_exception, current) +DO_ERROR(7, SIGILL, "data exception", data_exception, current) +#endif /* CONFIG_IEEEFPU_EMULATION */ + + +/* init is done in lowcore.S and head.S */ + +void __init trap_init(void) +{ + int i; + + for (i = 0; i < 128; i++) + pgm_check_table[i] = &default_trap_handler; + pgm_check_table[1] = &illegal_op; + pgm_check_table[2] = &privileged_op; + pgm_check_table[3] = &execute_exception; + pgm_check_table[5] = &addressing_exception; + pgm_check_table[6] = &specification_exception; + pgm_check_table[7] = &data_exception; + pgm_check_table[9] = ÷_exception; + pgm_check_table[0x12] = &translation_exception; + pgm_check_table[0x13] = &special_op_exception; + pgm_check_table[0x15] = &operand_exception; + pgm_check_table[4] = &do_page_fault; + pgm_check_table[0x10] = &do_page_fault; + pgm_check_table[0x11] = &do_page_fault; + pgm_check_table[0x1C] = &privileged_op; +} + + +void handle_per_exception(struct pt_regs *regs) +{ + if(regs->psw.mask&PSW_PROBLEM_STATE) + { + per_struct *per_info=¤t->thread.per_info; + per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid; + per_info->lowcore.words.address=S390_lowcore.per_address; + per_info->lowcore.words.access_id=S390_lowcore.per_access_id; + } + if(do_debugger_trap(regs,SIGTRAP)) + { + /* I've seen this possibly a task structure being reused ? */ + printk("Spurious per exception detected\n"); + printk("switching off per tracing for this task.\n"); + show_crashed_task_info(); + /* Hopefully switching off per tracing will help us survive */ + regs->psw.mask &= ~PSW_PER_MASK; + } +} + diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile new file mode 100644 index 000000000..aa8b0e5da --- /dev/null +++ b/arch/s390/lib/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for s390-specific library files.. +# + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +L_TARGET = lib.a +L_OBJS = checksum.o delay.o memset.o strcmp.o strncpy.o + +include $(TOPDIR)/Rules.make + diff --git a/arch/s390/lib/checksum.c b/arch/s390/lib/checksum.c new file mode 100644 index 000000000..9411e1c5e --- /dev/null +++ b/arch/s390/lib/checksum.c @@ -0,0 +1,56 @@ +/* + * arch/s390/lib/checksum.c + * S390 fast network checksum routines + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ulrich Hild (first version), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * This file contains network checksum routines + */ + +#include <linux/string.h> +#include <linux/types.h> +#include <asm/uaccess.h> +#include <asm/byteorder.h> +#include <asm/checksum.h> + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ +unsigned int +csum_partial (const unsigned char *buff, int len, unsigned int sum) +{ + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. + */ + __asm__ __volatile__ ( + " lr 2,%1\n" /* address in gpr 2 */ + " lr 3,%2\n" /* length in gpr 3 */ + "0: cksm %0,2\n" /* do checksum on longs */ + " jo 0b\n" + : "+&d" (sum) + : "d" (buff), "d" (len) + : "cc", "2", "3" ); + return sum; +} + +/* + * Fold a partial checksum without adding pseudo headers + */ +unsigned short csum_fold(unsigned int sum) +{ + __asm__ __volatile__ ( + " sr 3,3\n" /* %0 = H*65536 + L */ + " lr 2,%0\n" /* %0 = H L, R2/R3 = H L / 0 0 */ + " srdl 2,16\n" /* %0 = H L, R2/R3 = 0 H / L 0 */ + " alr 2,3\n" /* %0 = H L, R2/R3 = L H / L 0 */ + " alr %0,2\n" /* %0 = H+L+C L+H */ + " srl %0,16\n" /* %0 = H+L+C */ + : "+d" (sum) : : "cc", "2", "3"); + return ((unsigned short) ~sum); +} + diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c new file mode 100644 index 000000000..ec3274487 --- /dev/null +++ b/arch/s390/lib/delay.c @@ -0,0 +1,45 @@ +/* + * arch/s390/kernel/delay.c + * Precise Delay Loops for S390 + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Derived from "arch/i386/lib/delay.c" + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/delay.h> + +#ifdef CONFIG_SMP +#include <asm/smp.h> +#endif + +void __delay(unsigned long loops) +{ + __asm__ __volatile__( + "0: ahi %0,-1\n" + " jnm 0b" + : /* no outputs */ : "r" (loops) ); +} + +inline void __const_udelay(unsigned long xloops) +{ + + __asm__("LR 3,%1\n\t" + "MR 2,%2\n\t" + "LR %0,2\n\t" + : "=r" (xloops) + : "r" (xloops) , "r" (loops_per_sec) + : "2" , "3"); + __delay(xloops); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */ +} diff --git a/arch/s390/lib/memset.S b/arch/s390/lib/memset.S new file mode 100644 index 000000000..447af53f8 --- /dev/null +++ b/arch/s390/lib/memset.S @@ -0,0 +1,30 @@ +/* + * arch/s390/lib/memset.S + * S390 fast memset routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address to memory area + * R3 = byte to fill memory with + * R4 = number of bytes to fill + */ + .globl memset +memset: + LTR 4,4 + JZ memset_end + LR 0,2 # save pointer to memory area + LR 1,3 # move pad byte to R1 + LR 3,4 + SR 4,4 # no source for MVCLE, only a pad byte + SR 5,5 + MVCLE 2,4,0(1) # thats it, MVCLE is your friend + JO .-4 + LR 2,0 # return pointer to mem. +memset_end: + BR 14 + + diff --git a/arch/s390/lib/strcmp.S b/arch/s390/lib/strcmp.S new file mode 100644 index 000000000..d3f63942f --- /dev/null +++ b/arch/s390/lib/strcmp.S @@ -0,0 +1,27 @@ +/* + * arch/s390/lib/strcmp.S + * S390 strcmp routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of compare string + * R3 = address of test string + */ + .globl strcmp +strcmp: + SR 0,0 + SR 1,1 + CLST 2,3 + JO .-4 + JE strcmp_equal + IC 0,0(0,3) + IC 1,0(0,2) + SR 1,0 +strcmp_equal: + LR 2,1 + BR 14 + diff --git a/arch/s390/lib/strncpy.S b/arch/s390/lib/strncpy.S new file mode 100644 index 000000000..83578909c --- /dev/null +++ b/arch/s390/lib/strncpy.S @@ -0,0 +1,30 @@ +/* + * arch/s390/kernel/strncpy.S + * S390 strncpy routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of destination + * R3 = address of source string + * R4 = max number of bytes to copy + */ + .globl strncpy +strncpy: + LR 1,2 # don't touch address in R2 + LTR 4,4 + JZ strncpy_exit # 0 bytes -> nothing to do + SR 0,0 +strncpy_loop: + ICM 0,1,0(3) # ICM sets the cc, IC does not + LA 3,1(0,3) + STC 0,0(0,1) + LA 1,1(0,1) + JZ strncpy_exit # ICM inserted a 0x00 + BRCT 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 +strncpy_exit: + BR 14 + diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile new file mode 100644 index 000000000..cee7d4e6d --- /dev/null +++ b/arch/s390/mm/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +O_OBJS := init.o fault.o ioremap.o extable.o + +include $(TOPDIR)/Rules.make diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c new file mode 100644 index 000000000..2aac12c97 --- /dev/null +++ b/arch/s390/mm/extable.c @@ -0,0 +1,63 @@ +/* + * arch/s390/mm/extable.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/extable.c" + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#ifndef CONFIG_MODULES + addr &= 0x7fffffff; /* remove amode bit from address */ + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return FIX_PSW(ret); +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + addr &= 0x7fffffff; /* remove amode bit from address */ + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return FIX_PSW(ret); + } +#endif + + return 0; +} diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c new file mode 100644 index 000000000..cfc744a63 --- /dev/null +++ b/arch/s390/mm/fault.c @@ -0,0 +1,203 @@ +/* + * arch/s390/mm/fault.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/hardirq.h> + +extern void die(const char *,struct pt_regs *,long); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * error_code: + * ****0004 Protection -> Write-Protection (suprression) + * ****0010 Segment translation -> Not present (nullification) + * ****0011 Page translation -> Not present (nullification) + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long address; + unsigned long fixup; + int write; + unsigned long psw_mask; + unsigned long psw_addr; + + /* + * get psw mask of Program old psw to find out, + * if user or kernel mode + */ + + psw_mask = S390_lowcore.program_old_psw.mask; + psw_addr = S390_lowcore.program_old_psw.addr; + + /* + * get the failing address + * more specific the segment and page table portion of + * the address + */ + + address = S390_lowcore.trans_exc_code&0x7ffff000; + + if (atomic_read(&S390_lowcore.local_irq_count)) + die("page fault from irq handler",regs,error_code); + + tsk = current; + mm = tsk->mm; + + down(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) { + printk("no vma for address %lX\n",address); + goto bad_area; + } + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) { + printk("VM_GROWSDOWN not set, but address %lX \n",address); + printk("not in vma %p (start %lX end %lX)\n",vma, + vma->vm_start,vma->vm_end); + goto bad_area; + } + if (expand_stack(vma, address)) { + printk("expand of vma failed address %lX\n",address); + printk("vma %p (start %lX end %lX)\n",vma, + vma->vm_start,vma->vm_end); + goto bad_area; + } +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + write = 0; + switch (error_code & 0xFF) { + case 0x04: /* write, present*/ + write = 1; + break; + case 0x10: /* not present*/ + case 0x11: /* not present*/ + if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) { + printk("flags %X of vma for address %lX wrong \n", + vma->vm_flags,address); + printk("vma %p (start %lX end %lX)\n",vma, + vma->vm_start,vma->vm_end); + goto bad_area; + } + break; + default: + printk("code should be 4, 10 or 11 (%lX) \n",error_code&0xFF); + goto bad_area; + } + handle_mm_fault(tsk, vma, address, write); + + up(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up(&mm->mmap_sem); + + /* User mode accesses just cause a SIGSEGV */ + if (psw_mask & PSW_PROBLEM_STATE) { + tsk->thread.prot_addr = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + + printk("User process fault: interruption code 0x%lX\n",error_code); + printk("failing address: %lX\n",address); + show_crashed_task_info(); + force_sig(SIGSEGV, tsk); + return; + } + + /* Are we prepared to handle this kernel fault? */ + + if ((fixup = search_exception_table(regs->psw.addr)) != 0) { + regs->psw.addr = fixup; + return; + } + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + * + * First we check if it was the bootup rw-test, though.. + */ + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n",address); +/* + * need to define, which information is useful here + */ + + lock_kernel(); + die("Oops", regs, error_code); + do_exit(SIGKILL); + unlock_kernel(); +} + +/* + { + char c; + int i,j; + char *addr; + addr = ((char*) psw_addr)-0x20; + for (i=0;i<16;i++) { + if (i == 2) + printk("\n"); + printk ("%08X: ",(unsigned long) addr); + for (j=0;j<4;j++) { + printk("%08X ",*(unsigned long*)addr); + addr += 4; + } + addr -=0x10; + printk(" | "); + for (j=0;j<16;j++) { + printk("%c",(c=*addr++) < 0x20 ? '.' : c ); + } + + printk("\n"); + } + printk("\n"); + } + +*/ + + + + + + diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c new file mode 100644 index 000000000..4e30c015a --- /dev/null +++ b/arch/s390/mm/init.c @@ -0,0 +1,385 @@ +/* + * arch/s390/mm/init.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/init.h> +#ifdef CONFIG_BLK_DEV_INITRD +#include <linux/blk.h> +#endif +#include <linux/pagemap.h> +#include <linux/bootmem.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/dma.h> +#include <asm/lowcore.h> + +static unsigned long totalram_pages = 0; + +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving an inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ + +pgd_t swapper_pg_dir[512] __attribute__ ((__aligned__ (4096))); +unsigned long empty_bad_page[1024] __attribute__ ((__aligned__ (4096))); +unsigned long empty_zero_page[1024] __attribute__ ((__aligned__ (4096))); +pte_t empty_bad_pte_table[1024] __attribute__ ((__aligned__ (4096))); + +static int test_access(unsigned long loc) +{ + static const int ssm_mask = 0x07000000L; + int rc, i; + + rc = 0; + for (i=0; i<4; i++) { + __asm__ __volatile__( + " slr %0,%0\n" + " ssm %1\n" + " tprot 0(%2),0\n" + "0: jne 1f\n" + " lhi %0,1\n" + "1: ssm %3\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 0b,1b\n" + ".previous" + : "+&d" (rc) : "i" (0), "a" (loc), "m" (ssm_mask) + : "cc"); + if (rc == 0) + break; + loc += 0x100000; + } + return rc; +} + +static pte_t * get_bad_pte_table(void) +{ + pte_t v; + int i; + + v = pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED)); + + for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++) + empty_bad_pte_table[i] = v; + + return empty_bad_pte_table; +} + +static inline void invalidate_page(pte_t *pte) +{ + int i; + for (i=0;i<PTRS_PER_PTE;i++) + pte_clear(pte++); +} + +void __handle_bad_pmd(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table()); +} + +void __handle_bad_pmd_kernel(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table()); +} + +pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + invalidate_page(pte); + pmd_val(pmd[0]) = _KERNPG_TABLE + __pa(pte); + pmd_val(pmd[1]) = _KERNPG_TABLE + __pa(pte)+1024; + pmd_val(pmd[2]) = _KERNPG_TABLE + __pa(pte)+2048; + pmd_val(pmd[3]) = _KERNPG_TABLE + __pa(pte)+3072; + return pte + offset; + } + pte = get_bad_pte_table(); + pmd_val(pmd[0]) = _KERNPG_TABLE + __pa(pte); + pmd_val(pmd[1]) = _KERNPG_TABLE + __pa(pte)+1024; + pmd_val(pmd[2]) = _KERNPG_TABLE + __pa(pte)+2048; + pmd_val(pmd[3]) = _KERNPG_TABLE + __pa(pte)+3072; + return NULL; + } + free_page((unsigned long)pte); + if (pmd_bad(*pmd)) { + __handle_bad_pmd_kernel(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + unsigned long pte; + + pte = (unsigned long) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + invalidate_page((pte_t*) pte); + pmd_val(pmd[0]) = _PAGE_TABLE + __pa(pte); + pmd_val(pmd[1]) = _PAGE_TABLE + __pa(pte)+1024; + pmd_val(pmd[2]) = _PAGE_TABLE + __pa(pte)+2048; + pmd_val(pmd[3]) = _PAGE_TABLE + __pa(pte)+3072; + return (pte_t *) pte + offset; + } + pte = (unsigned long) get_bad_pte_table(); + pmd_val(pmd[0]) = _PAGE_TABLE + __pa(pte); + pmd_val(pmd[1]) = _PAGE_TABLE + __pa(pte)+1024; + pmd_val(pmd[2]) = _PAGE_TABLE + __pa(pte)+2048; + pmd_val(pmd[3]) = _PAGE_TABLE + __pa(pte)+3072; + return NULL; + } + free_page(pte); + if (pmd_bad(*pmd)) { + __handle_bad_pmd(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(pgtable_cache_size > high) { + do { + if(pgd_quicklist) + free_pgd_slow(get_pgd_fast()), freed++; + if(pmd_quicklist) + free_pmd_slow(get_pmd_fast()), freed++; + if(pte_quicklist) + free_pte_slow(get_pte_fast()), freed++; + } while(pgtable_cache_size > low); + } + return freed; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!atomic_read(&mem_map[i].count)) + free++; + else + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); +} + +/* References to section boundaries */ + +extern unsigned long _text; +extern unsigned long _etext; +extern unsigned long _edata; +extern unsigned long __bss_start; +extern unsigned long _end; + +extern unsigned long __init_begin; +extern unsigned long __init_end; + +/* + * paging_init() sets up the page tables - note that the first 4MB are + * already mapped by head.S. + * paging_init will erase this initial mapping + */ + +unsigned long last_valid_pfn; + +void __init paging_init(void) +{ + pgd_t * pg_dir; + pte_t * pg_table; + pte_t pte; + int i; + unsigned long tmp; + unsigned long address=0; + unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE; + unsigned long end_mem = (unsigned long) __va(max_low_pfn*PAGE_SIZE); + + /* unmap whole virtual address space */ + + pg_dir = swapper_pg_dir; + + for (i=0;i<KERNEL_PGD_PTRS;i++) + pmd_clear((pmd_t*)pg_dir++); + + /* + * map whole physical memory to virtual memory (identity mapping) + */ + + pg_dir = swapper_pg_dir; + + while (address < end_mem) { + /* + * pg_table is physical at this point + */ + pg_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + + pg_dir->pgd0 = (_PAGE_TABLE | __pa(pg_table)); + pg_dir->pgd1 = (_PAGE_TABLE | (__pa(pg_table)+1024)); + pg_dir->pgd2 = (_PAGE_TABLE | (__pa(pg_table)+2048)); + pg_dir->pgd3 = (_PAGE_TABLE | (__pa(pg_table)+3072)); + pg_dir++; + + for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { + pte = mk_pte_phys(address, PAGE_KERNEL); + if (address >= end_mem) + pte_clear(&pte); + set_pte(pg_table, pte); + address += PAGE_SIZE; + } + } + + /* enable virtual mapping in kernel mode */ + __asm__ __volatile__(" LCTL 1,1,%0\n" + " LCTL 7,7,%0\n" + " LCTL 13,13,%0" + : :"m" (pgdir_k)); + + local_flush_tlb(); + + { + unsigned long zones_size[MAX_NR_ZONES] = { 0, 0, 0}; + + zones_size[ZONE_DMA] = max_low_pfn; + free_area_init(zones_size); + } + + return; +} + +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int tmp; + + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + /* mark usable pages in the mem_map[] and count reserved pages */ + reservedpages = 0; + tmp = 0; + do { + if (tmp && (tmp & 0x3ff) == 0 && + test_access(tmp * PAGE_SIZE) == 0) { + printk("4M Segment %lX not available\n",tmp*PAGE_SIZE); + do { + set_bit(PG_reserved, &mem_map[tmp].flags); + reservedpages++; + tmp++; + } while (tmp < max_low_pfn && (tmp & 0x3ff)); + } else { + if (PageReserved(mem_map+tmp)) + reservedpages++; + tmp++; + } + } while (tmp < max_low_pfn); + + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >>10, + initsize >> 10); +} + +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(addr)); + set_page_count(mem_map+MAP_NR(addr), 1); + free_page(addr); + totalram_pages++; + } + printk ("Freeing unused kernel memory: %dk freed\n", + (&__init_end - &__init_begin) >> 10); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < end) + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(start)); + set_page_count(mem_map+MAP_NR(start), 1); + free_page(start); + totalram_pages++; + } +} +#endif + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->mem_unit = PAGE_SIZE; + return; +} diff --git a/arch/s390/mm/ioremap.c b/arch/s390/mm/ioremap.c new file mode 100644 index 000000000..f9f0024c4 --- /dev/null +++ b/arch/s390/mm/ioremap.c @@ -0,0 +1,129 @@ +/* + * arch/s390/mm/ioremap.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/extable.c" + * (C) Copyright 1995 1996 Linus Torvalds + * + * Re-map IO memory to kernel address space so that we can access it. + * This is needed for high PCI addresses that aren't mapped in the + * 640k-1MB IO memory area on PC's + */ + +#include <linux/vmalloc.h> +#include <asm/io.h> +#include <asm/pgalloc.h> + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + do { + if (!pte_none(*pte)) { + printk("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | + _PAGE_DIRTY | _PAGE_ACCESSED | flags))); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address && (address < end)); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t * pte = pte_alloc_kernel(pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + do { + pmd_t *pmd = pmd_alloc_kernel(dir, address); + if (!pmd) + return -ENOMEM; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + return -ENOMEM; + set_pgdir(address, *dir); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + flush_tlb_all(); + return 0; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + */ +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + + if (phys_addr < virt_to_phys(high_memory)) + return phys_to_virt(phys_addr); + if (phys_addr & ~PAGE_MASK) + return NULL; + size = PAGE_ALIGN(size); + if (!size || size > phys_addr + size) + return NULL; + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + vfree(addr); + return NULL; + } + return addr; +} + +void iounmap(void *addr) +{ + if (addr > high_memory) + return vfree(addr); +} diff --git a/arch/s390/tools/dasdfmt/Makefile b/arch/s390/tools/dasdfmt/Makefile new file mode 100644 index 000000000..f63ff468b --- /dev/null +++ b/arch/s390/tools/dasdfmt/Makefile @@ -0,0 +1,9 @@ +all: dasdfmt + +dasdfmt: dasdfmt.c + $(CROSS_COMPILE)gcc -o $@ $^ + $(STRIP) $@ + +clean: + rm -f dasdfmt + diff --git a/arch/s390/tools/dasdfmt/dasdfmt.8 b/arch/s390/tools/dasdfmt/dasdfmt.8 new file mode 100644 index 000000000..b08244322 --- /dev/null +++ b/arch/s390/tools/dasdfmt/dasdfmt.8 @@ -0,0 +1,74 @@ +.TH DASDFMT 8 "Tue Jan 25 2000" +.UC 4 +.SH NAME +dasdfmt \- formatting of DSAD (ECKD) disk drives. +.SH SYNOPSIS +\fBdasdfmt\fR [-tvyV] [-b \fIblockSize\fR] [\fIblockRange\fI] + \fIdiskSpec\fR +.SH DESCRIPTION +\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it +for usage with Linux for S/390. \fBWARNING\fR: Incautious usage of +\fBdasdfmt\fR can result in \fBLOSS OF DATA\fR. + +.SH OPTIONS +.TP +\fB-t\fR +Disables any modification of the disk drive. \fBdasdfmt\fR just prints +out, what it \fBwould\fR do. + +.TP +\fB-v\fR +Increases verbosity. + +.TP +\fB-y\fR +Start formatting without further user-confirmation. + +.TP +\fB-V\fR +Print version number and exit. + +.TP +\fB-b\fR \fIblockSize\fR +Specify blocksize to be used. \fIblocksize\fR must be a positive integer +and always be a power of two. Due due some limitations in the driver, +it is \fBstrongly\fR recommended to use a \fIblockSize\fR of \fI4096\fR. + +.TP +\fIblockRange\fR +This parameter specifies the number of the first and last block to be +formatted. If this parameter is \fBomitted\fR, formatting the \fBwhole\fR disk +is assumed. The \fIblockRange\fR can be specified in two different formats: +.sp + \fB-s\fR \fIstartBlock\fR \fB-e\fR \fIendBlock\fR +.br +or +.br + \fB-r\fR \fIstartBlock\fR-\fIendBlock\fR +.sp +If \fIstartBlock\fR is omitted, block \fB0\fR is assumed. If +\fIendBlock\fR is omitted, the last block of the disk is assumed. + +.TP +\fIdiskSpec\fR +This parameter specified the device to be formatted. It also can be +given in two variants: +.sp + \fB-f\fR \fB/dev/dd\fR\fIX\fR +.br +or +.br + \fB-n\fR \fIdevnum\fR +.sp +The first form uses the commonly used +.SM UNIX +device notation where \fIX\fR is a single lowercase letter. +The second form uses simply the VM vdev number. + +.SH BUGS +None so far ;-) + +.SH AUTHOR +.nf +This man-page was written by Fritz Elfert <felfert@to.com> +.fi diff --git a/arch/s390/tools/dasdfmt/dasdfmt.c b/arch/s390/tools/dasdfmt/dasdfmt.c new file mode 100644 index 000000000..1726e7061 --- /dev/null +++ b/arch/s390/tools/dasdfmt/dasdfmt.c @@ -0,0 +1,638 @@ +/* + * + * dasdfmt.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Corporation + * Author(s): Utz Bacher, <utz.bacher@de.ibm.com> + * + * Device-in-use-checks by Fritz Elfert, <felfert@to.com> + * + * Still to do: + * detect non-switch parameters ("dasdfmt -n 170 XY") and complain about them + */ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <mntent.h> +#include "../../../drivers/s390/block/dasd.h" /* uses DASD_PARTN_BITS */ +#define __KERNEL__ /* we want to use kdev_t and not have to define it */ +#include <linux/kdev_t.h> +#undef __KERNEL__ + +#define EXIT_MISUSE 1 +#define EXIT_BUSY 2 +#define TEMPFILENAME "/tmp/ddfXXXXXX" +#define TEMPFILENAMECHARS 8 /* 8 characters are fixed in all temp filenames */ +#define IOCTL_COMMAND 'D' << 8 +#define SLASHDEV "/dev/" +#define PROC_DASD_DEVICES "/proc/dasd/devices" +#define DASD_DRIVER_NAME "dasd" +#define PROC_LINE_LENGTH 80 +#define ERR_LENGTH 80 + +#define MAX_FILELEN NAME_MAX+PATH_MAX + +#define GIVEN_DEVNO 1 +#define GIVEN_MAJOR 2 +#define GIVEN_MINOR 4 + +#define CHECK_START 1 +#define CHECK_END 2 +#define CHECK_BLKSIZE 4 +#define CHECK_ALL ~0 + +#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} +#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} + +#define CHECK_SPEC_MAX_ONCE(i,str) \ + {if (i>1) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "can only be specified once\n",prog_name);} + +#define PARSE_PARAM_INTO(x,param,base,str) \ + {x=(int)strtol(param,&endptr,base); \ + if (*endptr) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "is in invalid format\n",prog_name);} + +typedef struct { + int start_unit; + int stop_unit; + int blksize; +} format_data_t; + +char prog_name[]="dasd_format"; +char tempfilename[]=TEMPFILENAME; + +void +exit_usage(int exitcode) +{ + printf("Usage: %s [-htvyV] [-b blocksize] <range> <diskspec>\n\n", + prog_name); + printf(" where <range> is either\n"); + printf(" -s start_track -e end_track\n"); + printf(" or\n"); + printf(" -r start_track-end_track\n"); + printf(" and <diskspec> is either\n"); + printf(" -f /dev/dasdX\n"); + printf(" or\n"); + printf(" -n <s390-devnr>\n"); + exit(exitcode); +} + +void +get_xno_from_xno(int *devno,kdev_t *major_no,kdev_t *minor_no,int mode) +{ + FILE *file; + int d,rc; + kdev_t mi,ma; + int mi_i,ma_i; /* for scanf :-( */ + char line[PROC_LINE_LENGTH]; + + file=fopen(PROC_DASD_DEVICES,"r"); + if (file==NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to open " \ + PROC_DASD_DEVICES ": %s (do you have the /proc " \ + "filesystem enabled?)\n",prog_name,strerror(errno)); + + fgets(line,sizeof(line),file); /* omit first line */ + while (fgets(line,sizeof(line),file)!=NULL) { + rc=sscanf(line,"%X%d%d",&d,&ma_i,&mi_i); + ma=ma_i; + mi=mi_i; + if ( (rc==3) && + !((d!=*devno)&&(mode&GIVEN_DEVNO)) && + !((ma!=*major_no)&&(mode&GIVEN_MAJOR)) && + !((mi!=*minor_no)&&(mode&GIVEN_MINOR)) ) { + *devno=d; + *major_no=ma; + *minor_no=mi; + /* yes, this is a quick exit, but the easiest way */ + fclose(file); + return; + } + } + fclose(file); + + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to find device in the /proc " \ + "filesystem (are you sure to have the right param line?)\n", + prog_name); +} + +char * +get_devname_from_devno(int devno,int verbosity) +{ + kdev_t major_no,minor_no; + kdev_t file_major,file_minor; + struct stat stat_buf; + int rc; + int found; + char *devname; + char tmpname[MAX_FILELEN]; + + DIR *dp; + struct dirent *direntp; + + /**** get minor number ****/ + get_xno_from_xno(&devno,&major_no,&minor_no,GIVEN_DEVNO); + + /**** get device file ****/ + if ((dp=opendir(SLASHDEV)) == NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: unable to read " SLASHDEV \ + "\n",prog_name); + found=0; + while ((direntp=readdir(dp)) != NULL) { + strcpy(tmpname,SLASHDEV); + strcat(tmpname,direntp->d_name); + rc=stat(tmpname,&stat_buf); + if (!rc) { + file_major=MAJOR(stat_buf.st_rdev); + file_minor=MINOR(stat_buf.st_rdev); + if ((file_major==major_no) && (file_minor==minor_no)) { + found=1; + break; + } + } + } + if (found) { + devname=malloc(strlen(direntp->d_name)); + strcpy(devname,tmpname); + } + rc=closedir(dp); + if (rc<0) ERRMSG("%s: unable to close directory " SLASHDEV \ + "; continuing\n",prog_name); + if (found) + return devname; + + if (verbosity>=1) + printf("I didn't find device node in " SLASHDEV \ + "; trying to create a temporary node\n"); + + /**** get temp file and create device node *****/ + rc=mkstemp(tempfilename); + if (rc==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to get temporary " \ + "filename: %s\n",prog_name,strerror(errno)); + close(rc); + rc=unlink(tempfilename); + + rc=mknod(tempfilename,S_IFBLK|0600,MKDEV(major_no,minor_no)); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to create temporary " \ + "device node %s: %s\n",prog_name,tempfilename, + strerror(errno)); + return tempfilename; +} + +char * +check_param(int mode,format_data_t data) +{ + char *s; + + if (NULL==(s=malloc(ERR_LENGTH))) + ERRMSG_EXIT(EXIT_FAILURE,"%s: not enough memory.\n",prog_name); + + if ((mode&CHECK_START)&&(data.start_unit<0)) { + strcpy(s,"start track must be greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.stop_unit<-1)) { + strcpy(s,"end track must be -1 or greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.start_unit>data.stop_unit)&& + (data.stop_unit!=-1)) { + strcpy(s,"end track must be higher than start track"); + goto exit; + } + + if ((mode&CHECK_BLKSIZE)&&(data.blksize<1)) { + strcpy(s,"blocksize must be a positive integer"); + goto exit; + } + if (mode&CHECK_BLKSIZE) while (data.blksize>0) { + if ((data.blksize%2)&&(data.blksize!=1)) { + strcpy(s,"blocksize must be a power of 2"); + goto exit; + } + data.blksize/=2; + } + + free(s); + return NULL; +exit: + return s; +} + +#define ASK_PRINTOUT printf("Please enter %s",output) +#define ASK_GETBUFFER fgets(buffer,sizeof(buffer),stdin) +#define ASK_SCANFORNUMBER(var) rc=sscanf(buffer,"%d%c",&var,&c) +#define ASK_COMPLAIN_FORMAT if ((rc==2)&&(c=='\n')) rc=1; \ + if (rc==-1) rc=1; /* this happens, if enter is pressed */ \ + if (rc!=1) printf(" -- wrong input, try again.\n") +#define ASK_CHECK_PARAM(mode) str=check_param(mode,params); \ + if (str!=NULL) { printf(" -- %s\n",str); rc=0; free(str); } + +format_data_t +ask_user_for_data(format_data_t params) +{ + char buffer[20]; /* should be enough for inputing track numbers */ + char c; + int i,rc; + char *str; + char output[60],o2[12]; + + i=params.start_unit; + do { + params.start_unit=i; + sprintf(output,"the start track of the range to format " \ + "[%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.start_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_START); + } while (rc!=1); + + i=params.stop_unit; + do { + params.stop_unit=i; + sprintf(output,"the end track of the range to format ["); + if (i==-1) sprintf(o2,"END]: "); else + sprintf(o2,"%d]: ",i); + strcat(output,o2); + ASK_PRINTOUT; + ASK_GETBUFFER; + if ( (!strcasecmp(buffer,"end")) || + (!strcasecmp(buffer,"end\n")) ) { + rc=1; + params.stop_unit=-1; + } else { + ASK_SCANFORNUMBER(params.stop_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_END); + } + } while (rc!=1); + + i=params.blksize; + do { + params.blksize=i; + sprintf(output,"the blocksize of the formatting [%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.blksize); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_BLKSIZE); + } while (rc!=1); + + return params; +} + +/* Check if the device we are going to format is mounted. + * If true, complain and exit. + */ +void +check_mounted(int major, int minor) +{ + FILE *f; + int ishift = 0; + struct mntent *ment; + struct stat stbuf; + char line[128]; + + /* If whole disk to be formatted ... */ + if ((minor % (1U << DASD_PARTN_BITS)) == 0) { + /* ... ignore partition-selector */ + minor >>= DASD_PARTN_BITS; + ishift = DASD_PARTN_BITS; + } + /* + * first, check filesystems + */ + if (!(f = fopen(_PATH_MOUNTED, "r"))) + ERRMSG_EXIT(EXIT_FAILURE, "%s: %s\n", _PATH_MOUNTED, + strerror(errno)); + while ((ment = getmntent(f))) { + if (stat(ment->mnt_fsname, &stbuf) == 0) + if ((major == MAJOR(stbuf.st_rdev)) && + (minor == (MINOR(stbuf.st_rdev)>>ishift))) { + ERRMSG("%s: device is mounted on %s!!\n", + prog_name,ment->mnt_dir); + ERRMSG_EXIT(EXIT_BUSY, "If you really want to " + "format it, please unmount it.\n"); + } + } + fclose(f); + /* + * second, check active swap spaces + */ + if (!(f = fopen("/proc/swaps", "r"))) + ERRMSG_EXIT(EXIT_FAILURE, "/proc/swaps: %s", strerror(errno)); + /* + * skip header line + */ + fgets(line, sizeof(line), f); + while (fgets(line, sizeof(line), f)) { + char *p; + for (p = line; *p && (!isspace(*p)); p++) ; + *p = '\0'; + if (stat(line, &stbuf) == 0) + if ((major == MAJOR(stbuf.st_rdev)) && + (minor == (MINOR(stbuf.st_rdev)>>ishift))) { + ERRMSG("%s: the device is in use for " + "swapping!!\n",prog_name); + ERRMSG_EXIT(EXIT_BUSY, "If you really want to " + "format it, please use swapoff %s.\n", + line); + } + } + fclose(f); +} + +void +do_format_dasd(char *dev_name,format_data_t format_params,int testmode, + int verbosity,int withoutprompt) +{ + int fd,rc; + struct stat stat_buf; + kdev_t minor_no,major_no; + int devno; + char inp_buffer[5]; /* to contain yes */ + + fd=open(dev_name,O_RDWR); + if (fd==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: error opening device %s: " \ + "%s\n",prog_name,dev_name,strerror(errno)); + + if (verbosity>=1) { + } + + rc=stat(dev_name,&stat_buf); + if (rc) { + ERRMSG_EXIT(EXIT_FAILURE,"%s: error occured during stat: " \ + "%s\n",prog_name,strerror(errno)); + } else { + if (!S_ISBLK(stat_buf.st_mode)) + ERRMSG_EXIT(EXIT_FAILURE,"%s: file is not a " \ + "blockdevice.\n",prog_name); + major_no=MAJOR(stat_buf.st_rdev); + minor_no=MINOR(stat_buf.st_rdev); + } + check_mounted(major_no, minor_no); + + if ( ((withoutprompt)&&(verbosity>=1)) || + (!withoutprompt) ) { + get_xno_from_xno(&devno,&major_no,&minor_no, + GIVEN_MAJOR|GIVEN_MINOR); + printf("\nI am going to format the device %s in the " \ + "following way:\n",dev_name); + printf(" Device number of device : 0x%x\n",devno); + printf(" Major number of device : %u\n",major_no); + printf(" Minor number of device : %u\n",minor_no); + printf(" Start track : %d\n" \ + ,format_params.start_unit); + printf(" End track : "); + if (format_params.stop_unit==-1) + printf("last track of disk\n"); + else + printf("%d\n",format_params.stop_unit); + printf(" Blocksize : %d\n" \ + ,format_params.blksize); + if (testmode) printf("Test mode active, omitting ioctl.\n"); + } + + while (!testmode) { + if (!withoutprompt) { + printf("\n--->> ATTENTION! <<---\n"); + printf("All data in the specified range of that " \ + "device will be lost.\nType yes to continue" \ + ", no will leave the disk untouched: "); + fgets(inp_buffer,sizeof(inp_buffer),stdin); + if (strcasecmp(inp_buffer,"yes") && + strcasecmp(inp_buffer,"yes\n")) { + printf("Omitting ioctl call (disk will " \ + "NOT be formatted).\n"); + break; + } + } + + if ( !( (withoutprompt)&&(verbosity<1) )) + printf("Formatting the device. This may take a " \ + "while (get yourself a coffee).\n"); + rc=ioctl(fd,IOCTL_COMMAND,format_params); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: the dasd driver " \ + "returned with the following error " \ + "message:\n%s\n",prog_name,strerror(errno)); + printf("Finished formatting the device.\n"); + + break; + } + + rc=close(fd); + if (rc) + ERRMSG("%s: error during close: " \ + "%s; continuing.\n",prog_name,strerror(errno)); +} + + + +int main(int argc,char *argv[]) { + int verbosity; + int testmode; + int withoutprompt; + + char *dev_name; + int devno; + char *dev_filename,*devno_param_str,*range_param_str; + char *start_param_str,*end_param_str,*blksize_param_str; + + format_data_t format_params; + + int rc; + int oc; + char *endptr; + + char c1,c2,cbuffer[6]; /* should be able to contain -end plus 1 char */ + int i1,i2; + char *str; + + int start_specified,end_specified,blksize_specified; + int devfile_specified,devno_specified,range_specified; + + /******************* initialization ********************/ + + endptr=NULL; + + /* set default values */ + format_params.start_unit=0; + format_params.stop_unit=-1; + format_params.blksize=4096; + testmode=0; + verbosity=0; + withoutprompt=0; + start_specified=end_specified=blksize_specified=0; + devfile_specified=devno_specified=range_specified=0; + + /*************** parse parameters **********************/ + + /* avoid error message generated by getopt */ + opterr=0; + + while ( (oc=getopt(argc,argv,"r:s:e:b:n:f:hty?vV")) !=EOF) { + switch (oc) { + case 'y': + withoutprompt=1; + break; + + case 't': + testmode=1; + break; + + case 'v': + verbosity++; + break; + + case '?': /* fall-through */ + case ':': + exit_usage(EXIT_MISUSE); + + case 'h': + exit_usage(0); + + case 'V': + printf("%s version 0.99\n",prog_name); + exit(0); + + case 's' : + start_param_str=optarg; + start_specified++; + break; + + case 'e' : + end_param_str=optarg; + end_specified++; + break; + + case 'b' : + blksize_param_str=optarg; + blksize_specified++; + break; + + case 'n' : + devno_param_str=optarg; + devno_specified++; + break; + + case 'f' : + dev_filename=optarg; + devfile_specified++; + break; + case 'r' : + range_param_str=optarg; + range_specified++; + break; + } + } + + /******************** checking of parameters **************/ + + /* convert range into -s and -e */ + CHECK_SPEC_MAX_ONCE(range_specified,"formatting range"); + + while (range_specified) { + start_specified++; + end_specified++; + + /* scan for 1 or 2 integers, separated by a dash */ + rc=sscanf(range_param_str,"%d%c%d%c",&i1,&c1,&i2,&c2); + if ((rc==3)&&(c1=='-')) { + format_params.start_unit=i1; + format_params.stop_unit=i2; + break; + } + if (rc==1) { + format_params.start_unit=i1; + break; + } + + /* scan for integer and -END */ + rc=sscanf(range_param_str,"%d%s",&i1,cbuffer); + if ((rc==2)&&(!strcasecmp(cbuffer,"-END"))) { + format_params.start_unit=i1; + format_params.stop_unit=-1; + break; + } + ERRMSG_EXIT(EXIT_MISUSE,"%s: specified range " \ + "is in invalid format\n",prog_name); + } + + if ((!devfile_specified)&&(!devno_specified)) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "not specified\n",prog_name); + + if ((devfile_specified+devno_specified)>1) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "can only be specified once\n",prog_name); + + if ((!start_specified)&&(!end_specified)&&(!range_specified)&& + (!blksize_specified)) { + format_params=ask_user_for_data(format_params); + } + + CHECK_SPEC_MAX_ONCE(start_specified,"start track"); + CHECK_SPEC_MAX_ONCE(end_specified,"end track"); + CHECK_SPEC_MAX_ONCE(blksize_specified,"blocksize"); + + if (devno_specified) + PARSE_PARAM_INTO(devno,devno_param_str,16,"device number"); + if (start_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.start_unit,start_param_str,10, + "start track"); + if (end_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.stop_unit,end_param_str,10, + "end track"); + if (blksize_specified) + PARSE_PARAM_INTO(format_params.blksize,blksize_param_str,10, + "blocksize"); + + /***********get dev_name *********************/ + dev_name=(devno_specified)? + get_devname_from_devno(devno,verbosity): + dev_filename; + + /*** range checking *********/ + str=check_param(CHECK_ALL,format_params); + if (str!=NULL) ERRMSG_EXIT(EXIT_MISUSE,"%s: %s\n",prog_name,str); + + /*************** issue the real command *****************/ + do_format_dasd(dev_name,format_params,testmode,verbosity, + withoutprompt); + + /*************** cleanup ********************************/ + if (strncmp(dev_name,TEMPFILENAME,TEMPFILENAMECHARS)==0) { + rc=unlink(dev_name); + if ((rc)&&(verbosity>=1)) + ERRMSG("%s: temporary device node %s could not be " \ + "removed: %s\n",prog_name,dev_name, + strerror(errno)); + } else { + if (devno_specified) { + /* so we have allocated space for the filename */ + free(dev_name); + } + } + + return 0; +} diff --git a/arch/s390/tools/silo/Makefile b/arch/s390/tools/silo/Makefile new file mode 100644 index 000000000..fb100e1b9 --- /dev/null +++ b/arch/s390/tools/silo/Makefile @@ -0,0 +1,15 @@ +all: silo + +silo.o: silo.c + $(CROSS_COMPILE)gcc -c -o silo.o -O2 silo.c + +cfg.o: cfg.c + $(CROSS_COMPILE)gcc -c -o cfg.o -O2 cfg.c + +silo: silo.o cfg.o + $(CROSS_COMPILE)gcc -o $@ $^ + $(STRIP) $@ + +clean: + rm -f *.o silo + diff --git a/arch/s390/tools/silo/cfg.c b/arch/s390/tools/silo/cfg.c new file mode 100644 index 000000000..2b11d93b8 --- /dev/null +++ b/arch/s390/tools/silo/cfg.c @@ -0,0 +1,373 @@ +/* cfg.c - Configuration file parser */ + +/* Copyright 1992-1997 Werner Almesberger. See file COPYING for details. */ + + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> + +#include "cfg.h" + +#define MAX_TOKEN 200 +#define MAX_VAR_NAME MAX_TOKEN + +static FILE *file; +static char flag_set; +static char *last_token = NULL,*last_item = NULL,*last_value = NULL; +static int line_num; +static char *file_name = NULL; +static int back = 0; /* can go back by one char */ + + +void pdie(char *msg) +{ + fflush(stdout); + perror(msg); + exit(1); +} + + +void die(char *fmt,...) +{ + va_list ap; + + fflush(stdout); + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + fputc('\n',stderr); + exit(1); +} + +char *pstrdup(const char *str) +{ + char *this; + + if ((this = strdup(str)) == NULL) pdie("Out of memory"); + return this; +} + +int cfg_open(char *name) +{ + if (!strcmp(name,"-")) file = stdin; + else if (!(file = fopen(file_name = name,"r"))) pdie(name); + line_num = 1; + return fileno(file); +} + +void cfg_error(char *msg,...) +{ + va_list ap; + + fflush(stdout); + va_start(ap,msg); + vfprintf(stderr,msg,ap); + va_end(ap); + if (!file_name) fputc('\n',stderr); + else fprintf(stderr," near line %d in file %s\n",line_num,file_name); + exit(1); +} + + +static int next_raw(void) +{ + int ch; + + if (!back) return getc(file); + ch = back; + back = 0; + return ch; +} + + +static int next(void) +{ + static char *var; + char buffer[MAX_VAR_NAME+1]; + int ch,braced; + char *put; + + if (back) { + ch = back; + back = 0; + return ch; + } + if (var && *var) return *var++; + ch = getc(file); + if (ch == '\\') { + ch = getc(file); + if (ch == '$') return ch; + ungetc(ch,file); + return '\\'; + } + if (ch != '$') return ch; + ch = getc(file); + braced = ch == '{'; + put = buffer; + if (!braced) *put++ = ch; + while (1) { + ch = getc(file); +#if 0 + if (!braced && ch < ' ') { + ungetc(ch,file); + break; + } +#endif + if (ch == EOF) cfg_error("EOF in variable name"); + if (ch < ' ') cfg_error("control character in variable name"); + if (braced && ch == '}') break; + if (!braced && !isalpha(ch) && !isdigit(ch) && ch != '_') { + ungetc(ch,file); + break; + } + if (put-buffer == MAX_VAR_NAME) cfg_error("variable name too long"); + *put++ = ch; + } + *put = 0; + if (!(var = getenv(buffer))) cfg_error("unknown variable \"%s\"",buffer); + return next(); +} + + +static void again(int ch) +{ + if (back) die("internal error: again invoked twice"); + back = ch; +} + + +static char *cfg_get_token(void) +{ + char buf[MAX_TOKEN+1]; + char *here; + int ch,escaped; + + if (last_token) { + here = last_token; + last_token = NULL; + return here; + } + while (1) { + while ((ch = next()), ch == ' ' || ch == '\t' || ch == '\n') + if (ch == '\n') line_num++; + if (ch == EOF) return NULL; + if (ch != '#') break; + while ((ch = next_raw()), ch != '\n') + if (ch == EOF) return NULL; + line_num++; + } + if (ch == '=') return pstrdup("="); + if (ch == '"') { + here = buf; + while (here-buf < MAX_TOKEN) { + if ((ch = next()) == EOF) cfg_error("EOF in quoted string"); + if (ch == '"') { + *here = 0; + return pstrdup(buf); + } + if (ch == '\\') { + ch = next(); + if (ch != '"' && ch != '\\' && ch != '\n') + cfg_error("Bad use of \\ in quoted string"); + if (ch == '\n') { + while ((ch = next()), ch == ' ' || ch == '\t'); + if (!ch) continue; + again(ch); + ch = ' '; + } + } + if (ch == '\n' || ch == '\t') + cfg_error("\\n and \\t are not allowed in quoted strings"); + *here++ = ch; + } + cfg_error("Quoted string is too long"); + return 0; /* not reached */ + } + here = buf; + escaped = 0; + while (here-buf < MAX_TOKEN) { + if (escaped) { + if (ch == EOF) cfg_error("\\ precedes EOF"); + if (ch == '\n') line_num++; + else *here++ = ch == '\t' ? ' ' : ch; + escaped = 0; + } + else { + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' || + ch == '=' || ch == EOF) { + again(ch); + *here = 0; + return pstrdup(buf); + } + if (!(escaped = (ch == '\\'))) *here++ = ch; + } + ch = next(); + } + cfg_error("Token is too long"); + return 0; /* not reached */ +} + + +static void cfg_return_token(char *token) +{ + last_token = token; +} + + +static int cfg_next(char **item,char **value) +{ + char *this; + + if (last_item) { + *item = last_item; + *value = last_value; + last_item = NULL; + return 1; + } + *value = NULL; + if (!(*item = cfg_get_token())) return 0; + if (!strcmp(*item,"=")) cfg_error("Syntax error"); + if (!(this = cfg_get_token())) return 1; + if (strcmp(this,"=")) { + cfg_return_token(this); + return 1; + } + if (!(*value = cfg_get_token())) cfg_error("Value expected at EOF"); + if (!strcmp(*value,"=")) cfg_error("Syntax error after %s",*item); + return 1; +} + + +static void cfg_return(char *item,char *value) +{ + last_item = item; + last_value = value; +} + + +void cfg_init(CONFIG *table) +{ + while (table->type != cft_end) { + switch (table->type) { + case cft_strg: + if (table->data) free(table->data); + case cft_flag: + table->data = NULL; + break; + case cft_link: + table = ((CONFIG *) table->action)-1; + break; + default: + die("Unknown syntax code %d",table->type); + } + table++; + } +} + + +static int cfg_do_set(CONFIG *table,char *item,char *value,int copy, + void *context) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (value && walk->type != cft_strg) + cfg_error("'%s' doesn't have a value",walk->name); + if (!value && walk->type == cft_strg) + cfg_error("Value expected for '%s'",walk->name); + if (walk->data) { + if (walk->context == context) + cfg_error("Duplicate entry '%s'",walk->name); + else { + fprintf(stderr,"Ignoring entry '%s'\n",walk->name); + if (!copy) free(value); + return 1; + } + } + if (walk->type == cft_flag) walk->data = &flag_set; + else if (walk->type == cft_strg) { + if (copy) walk->data = pstrdup(value); + else walk->data = value; + } + walk->context = context; + if (walk->action) ((void (*)(void)) walk->action)(); + break; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + if (walk->type != cft_end) return 1; + cfg_return(item,value); + return 0; +} + + +void cfg_set(CONFIG *table,char *item,char *value,void *context) +{ + if (!cfg_do_set(table,item,value,1,context)) + cfg_error("cfg_set: Can't set %s",item); +} + + +void cfg_unset(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) + if (walk->name && !strcasecmp(walk->name,item)) { + if (!walk->data) die("internal error (cfg_unset %s, unset)",item); + if (walk->type == cft_strg) free(walk->data); + walk->data = NULL; + return; + } + die("internal error (cfg_unset %s, unknown",item); +} + + +int cfg_parse(CONFIG *table) +{ + char *item,*value; + + while (1) { + if (!cfg_next(&item,&value)) return 0; + if (!cfg_do_set(table,item,value,0,table)) return 1; + free(item); + } +} + + +int cfg_get_flag(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (walk->type != cft_flag) + die("cfg_get_flag: operating on non-flag %s",item); + return !!walk->data; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + die("cfg_get_flag: unknown item %s",item); + return 0; /* not reached */ +} + + +char *cfg_get_strg(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (walk->type != cft_strg) + die("cfg_get_strg: operating on non-string %s",item); + return walk->data; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + die("cfg_get_strg: unknown item %s",item); + return 0; /* not reached */ +} diff --git a/arch/s390/tools/silo/cfg.h b/arch/s390/tools/silo/cfg.h new file mode 100644 index 000000000..97d10bf73 --- /dev/null +++ b/arch/s390/tools/silo/cfg.h @@ -0,0 +1,58 @@ +/* cfg.h - Configuration file parser */ + +/* Copyright 1992-1996 Werner Almesberger. See file COPYING for details. */ + + +#ifndef CFG_H +#define CFG_H + +typedef enum { cft_strg, cft_flag, cft_link, cft_end } CFG_TYPE; + +typedef struct { + CFG_TYPE type; + char *name; + void *action; + void *data; + void *context; +} CONFIG; + +extern int cfg_open(char *name); + +/* Opens the configuration file. Returns the file descriptor of the open + file. */ + +extern void cfg_error(char *msg,...); + +/* Signals an error while parsing the configuration file and terminates the + program. */ + +extern void cfg_init(CONFIG *table); + +/* Initializes the specified table. */ + +extern void cfg_set(CONFIG *table,char *item,char *value,void *context); + +/* Sets the specified variable in table. If the variable has already been set + since the last call to cfg_init, a warning message is issued if the context + keys don't match or a fatal error is reported if they do. */ + +extern void cfg_unset(CONFIG *table,char *item); + +/* Unsets the specified variable in table. It is a fatal error if the variable + was not set. */ + +extern int cfg_parse(CONFIG *table); + +/* Parses the configuration file for variables contained in table. A non-zero + value is returned if a variable not found in table has been met. Zero is + returned if EOF has been reached. */ + +extern int cfg_get_flag(CONFIG *table,char *item); + +/* Returns one if the specified variable is set, zero if it isn't. */ + +extern char *cfg_get_strg(CONFIG *table,char *item); + +/* Returns the value of the specified variable if it is set, NULL otherwise. */ + +#endif diff --git a/arch/s390/tools/silo/silo.c b/arch/s390/tools/silo/silo.c new file mode 100644 index 000000000..827082f5c --- /dev/null +++ b/arch/s390/tools/silo/silo.c @@ -0,0 +1,573 @@ +/* + * arch/s390/boot/silo.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * + * Report bugs to: <linux390@de.ibm.com> + * + * Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com> + * Fritz Elfert <felfert@to.com> contributed support for + * /etc/silo.conf based on Intel's lilo + */ + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <dirent.h> +#include <linux/fs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <asm/ioctl.h> + +#include "cfg.h" + +CONFIG cf_options[] = { + { cft_strg, "append", NULL, NULL,NULL }, + { cft_strg, "image", NULL, NULL,NULL }, + { cft_strg, "ipldevice", NULL, NULL,NULL }, + { cft_strg, "bootsect", NULL, NULL,NULL }, + { cft_strg, "map", NULL, NULL,NULL }, + { cft_strg, "parmfile", NULL, NULL,NULL }, + { cft_strg, "ramdisk", NULL, NULL,NULL }, + { cft_strg, "root", NULL, NULL,NULL }, + { cft_flag, "readonly", NULL, NULL,NULL }, + { cft_strg, "verbose", NULL, NULL,NULL }, + { cft_strg, "testlevel", NULL, NULL,NULL }, + { cft_end, NULL, NULL, NULL,NULL } +}; + +/* from dasd.h */ +#define DASD_PARTN_BITS 2 +#define BIODASDRWTB _IOWR('D',5,int) +/* end */ + +#define SILO_CFG "/etc/silo.conf" +#define SILO_IMAGE "./image" +#define SILO_BOOTMAP "./boot.map" +#define SILO_PARMFILE "./parmfile" +#define SILO_BOOTSECT "/boot/ipleckd.boot" + +#define PRINT_LEVEL(x,y...) if ( silo_options.verbosity >= x ) printf(y) +#define ERROR_LEVEL(x,y...) if ( silo_options.verbosity >= x ) fprintf(stderr,y) +#define TOGGLE(x) ((x)=((x)?(0):(1))) +#define GETARG(x) {int len=strlen(optarg);x=malloc(len);strncpy(x,optarg,len);PRINT_LEVEL(1,"%s set to %s\n",#x,optarg);} + +#define ITRY(x) if ( (x) == -1 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } +#define NTRY(x) if ( (x) == 0 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } + +#define MAX_CLUSTERS 256 +#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) + +#define SILO_VERSION "1.1" + +struct silo_options + { + short int verbosity; + short int testlevel; + char *image; + char *ipldevice; + char *parmfile; + char *ramdisk; + char *bootsect; + char *conffile; + char *bootmap; + } +silo_options = +{ + 1, /* verbosity */ + 2, /* testlevel */ + SILO_IMAGE, /* image */ + NULL, /* ipldevice */ + SILO_PARMFILE, /* parmfile */ + NULL, /* initrd */ + SILO_BOOTSECT, /* bootsector */ + SILO_CFG, /* silo.conf file */ + SILO_BOOTMAP, /* boot.map */ +}; + +struct blockdesc + { + unsigned long off; + unsigned short ct; + unsigned long addr; + }; + +struct blocklist + { + struct blockdesc blk[MAX_CLUSTERS]; + unsigned short ix; + }; + +void +usage (void) +{ + printf ("Usage:\n"); + printf ("silo -d ipldevice [additional options]\n"); + printf ("-d /dev/node : set ipldevice to /dev/node\n"); + printf ("-f image : set image to image\n"); + printf ("-F conffile : specify configuration file (/etc/silo.conf)\n"); + printf ("-p parmfile : set parameter file to parmfile\n"); + printf ("-b bootsect : set bootsector to bootsect\n"); + printf ("Additional options\n"); + printf ("-B bootmap:\n"); + printf ("-v: increase verbosity level\n"); + printf ("-v#: set verbosity level to #\n"); + printf ("-t: decrease testing level\n"); + printf ("-h: print this message\n"); + printf ("-?: print this message\n"); + printf ("-V: print version\n"); +} + +int +read_cfg(struct silo_options *o) +{ + char *tmp; + if (access(o->conffile, R_OK) && (errno == ENOENT)) + return 0; + /* If errno != ENOENT, let cfg_open report an error */ + cfg_open(o->conffile); + cfg_parse(cf_options); + tmp = cfg_get_strg(cf_options, "ipldevice"); + if ( ! o->ipldevice && tmp ) + o->ipldevice = tmp; + tmp = cfg_get_strg(cf_options, "image"); + if ( ! strncmp(o-> image,SILO_IMAGE,strlen(SILO_IMAGE)) && tmp ) + o->image = tmp; + tmp = cfg_get_strg(cf_options, "parmfile"); + if ( !strncmp(o->parmfile,SILO_PARMFILE,strlen(SILO_PARMFILE)) && tmp) + o->parmfile = tmp; + if ( ! o -> ramdisk ) + o->ramdisk = cfg_get_strg(cf_options, "ramdisk"); + tmp = cfg_get_strg(cf_options, "bootsect"); + if ( !strncmp(o -> bootsect,SILO_BOOTSECT,strlen(SILO_BOOTSECT))&&tmp) + o->bootsect = tmp; + tmp = cfg_get_strg(cf_options, "map") ; + if ( !strncmp(o -> bootmap,SILO_BOOTMAP,strlen(SILO_BOOTMAP)) && tmp) + o->bootmap = tmp; + tmp = cfg_get_strg(cf_options, "verbose"); + if ( tmp ) { + unsigned short v; + sscanf (tmp, "%hu", &v); + o->verbosity = v; + } + tmp = cfg_get_strg(cf_options, "testlevel"); + if ( tmp ) { + unsigned short t; + sscanf (tmp, "%hu", &t); + o->testlevel += t; + } + return 1; +} + +char * +gen_tmpparm( char *pfile ) +{ + char *append = cfg_get_strg(cf_options, "append"); + char *root = cfg_get_strg(cf_options, "root"); + int ro = cfg_get_flag(cf_options, "readonly"); + FILE *f,*of; + char *fn; + char c; + char *tmpdir=NULL,*save=NULL; + + if (!append && !root && !ro) + return pfile; + of = fopen(pfile, "r"); + if ( of ) { + NTRY( fn = tempnam(NULL,"parm.")); + } else { + fn = pfile; + } + NTRY( f = fopen(fn, "a+")); + if ( of ) { + while ( ! feof (of) ) { + c=fgetc(of); + fputc(c,f); + } + } + if (root) + fprintf(f, " root=%s", root); + if (ro) + fprintf(f, " ro"); + if (append) + fprintf(f, " %s", append); + fprintf(f, "\n"); + fclose(f); + fclose(of); + printf ("tempfile is %s\n",fn); + return strdup(fn); +} + +int +parse_options (struct silo_options *o, int argc, char *argv[]) +{ + int rc = 0; + int oc; + + while ((oc = getopt (argc, argv, "Vf:F:d:p:r:b:B:h?v::t::")) != -1) + { + switch (oc) + { + case 'V': + printf("silo version: %s\n",SILO_VERSION); + exit(0); + case 'v': + { + unsigned short v; + if (optarg && sscanf (optarg, "%hu", &v)) + o->verbosity = v; + else + o->verbosity++; + PRINT_LEVEL (1, "Verbosity value is now %hu\n", o->verbosity); + break; + } + case 't': + { + unsigned short t; + if (optarg && sscanf (optarg, "%hu", &t)) + o->testlevel -= t; + else + o->testlevel--; + PRINT_LEVEL (1, "Testonly flag is now %d\n", o->testlevel); + break; + } + case 'h': + case '?': + usage (); + exit(0); + case 'd': + GETARG (o->ipldevice); + break; + case 'f': + GETARG (o->image); + break; + case 'F': + GETARG (o->conffile); + break; + case 'p': + GETARG (o->parmfile); + break; + case 'r': + GETARG (o->ramdisk); + break; + case 'b': + GETARG (o->bootsect); + break; + case 'B': + GETARG (o->bootmap); + default: + rc = EINVAL; + break; + } + } + read_cfg(o); + return rc; +} + +int +verify_device (char *name) +{ + int rc = 0; + struct stat dst; + struct stat st; + ITRY (stat (name, &dst)); + if (S_ISBLK (dst.st_mode)) + { + if (!(MINOR (dst.st_rdev) & PARTN_MASK)) + { + rc = dst.st_rdev; + } + else + /* invalid MINOR & PARTN_MASK */ + { + ERROR_LEVEL (1, "Cannot boot from partition %d %d %d", + (int) PARTN_MASK, (int) MINOR (dst.st_rdev), (int) (PARTN_MASK & MINOR (dst.st_rdev))); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISBLK */ + { + ERROR_LEVEL (1, "%s is no block device\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_file (char *name, int dev) +{ + int rc = 0; + struct stat dst; + struct stat st; + int bs = 1024; + int l; + + ITRY(stat ( name, &dst )); + if (S_ISREG (dst.st_mode)) + { + if ((unsigned) MAJOR (dev) == (unsigned) MAJOR (dst.st_dev) && (unsigned) MINOR (dev) == (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)) + { + /* whatever to do if all is ok... */ + } + else + /* devicenumber doesn't match */ + { + ERROR_LEVEL (1, "%s is not on device (%d/%d) but on (%d/%d)\n", name, (unsigned) MAJOR (dev), (unsigned) MINOR (dev), (unsigned) MAJOR (dst.st_dev), (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISREG */ + { + ERROR_LEVEL (1, "%s is neither regular file nor linkto one\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_options (struct silo_options *o) +{ + int rc = 0; + int dev = 0; + int crc = 0; + if (!o->ipldevice || !o->image || !o->bootsect) + { + if (!o->ipldevice) + fprintf(stderr,"ipldevice\n"); + if (!o->image) + fprintf(stderr,"image\n"); + if (!o->bootsect) + fprintf(stderr,"bootsect\n"); + + usage (); + exit (1); + } + PRINT_LEVEL (1, "Testlevel is set to %d\n",o->testlevel); + + PRINT_LEVEL (1, "IPL device is: '%s'", o->ipldevice); + ITRY (dev = verify_device (o->ipldevice)); + PRINT_LEVEL (2, "...ok...(%d/%d)", (unsigned short) MAJOR (dev), (unsigned short) MINOR (dev)); + PRINT_LEVEL (1, "\n"); + + PRINT_LEVEL (0, "bootsector is: '%s'", o->bootsect); + ITRY (verify_file (o->bootsect, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + if ( o -> testlevel > 0 && + ! strncmp( o->bootmap, SILO_BOOTMAP,strlen(SILO_BOOTMAP) )) { + NTRY( o -> bootmap = tempnam(NULL,"boot.")); + } + PRINT_LEVEL (0, "bootmap is set to: '%s'", o->bootmap); + if ( access ( o->bootmap, O_RDWR ) == -1 ) { + if ( errno == ENOENT ) { + ITRY (creat ( o-> bootmap, O_RDWR )); + } else { + PRINT_LEVEL(1,"Cannot acces bootmap file '%s': %s\n",o->bootmap, + strerror(errno)); + } + } + ITRY (verify_file (o->bootmap, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + PRINT_LEVEL (0, "Kernel image is: '%s'", o->image); + ITRY (verify_file (o->image, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + PRINT_LEVEL (0, "original parameterfile is: '%s'", o->parmfile); + ITRY (verify_file (o->parmfile, dev)); + PRINT_LEVEL (1, "...ok..."); + o->parmfile = gen_tmpparm(o->parmfile); + PRINT_LEVEL (0, "final parameterfile is: '%s'", o->parmfile); + ITRY (verify_file (o->parmfile, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + if (o->ramdisk) + { + PRINT_LEVEL (0, "initialramdisk is: '%s'", o->ramdisk); + ITRY (verify_file (o->ramdisk, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + } + + return crc; +} + + +int +add_file_to_blocklist (char *name, struct blocklist *lst, long addr) +{ + int fd; + int devfd; + struct stat fst; + int i; + int blk; + int bs; + int blocks; + + int rc = 0; + + ITRY (fd = open (name, O_RDONLY)); + ITRY (fstat (fd, &fst)); + ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, fst.st_dev)); + ITRY (devfd = open ("/tmp/silodev", O_RDONLY)); + ITRY (ioctl (fd, FIGETBSZ, &bs)); + blocks = (fst.st_size + bs - 1) / bs; + for (i = 0; i < blocks; i++) + { + blk = i; + ITRY (ioctl (fd, FIBMAP, &blk)); + if (blk) + { + int oldblk = blk; + ITRY (ioctl (devfd, BIODASDRWTB, &blk)); + if (blk <= 0) + { + ERROR_LEVEL (0, "BIODASDRWTB on blk %d returned %d\n", oldblk, blk); + break; + } + } + else + { + PRINT_LEVEL (1, "Filled hole on blk %d\n", i); + } + if (lst->ix == 0 || i == 0 || + lst->blk[lst->ix - 1].ct >= 128 || + (lst->blk[lst->ix - 1].off + lst->blk[lst->ix - 1].ct != blk && + !(lst->blk[lst->ix - 1].off == 0 && blk == 0))) + { + if (lst->ix >= MAX_CLUSTERS) + { + rc = 1; + errno = ENOMEM; + break; + } + lst->blk[lst->ix].off = blk; + lst->blk[lst->ix].ct = 1; + lst->blk[lst->ix].addr = addr + i * bs; + lst->ix++; + } + else + { + lst->blk[lst->ix - 1].ct++; + } + } + ITRY(unlink("/tmp/silodev")); + return rc; +} + +int +write_bootsect (struct silo_options *o, struct blocklist *blklst) +{ + int i; + int s_fd, d_fd, b_fd, bd_fd; + struct stat s_st, d_st, b_st; + int rc=0; + int bs, boots; + char *tmpdev; + char buffer[4096]={0,}; + ITRY (d_fd = open (o->ipldevice, O_RDWR | O_SYNC)); + ITRY (fstat (d_fd, &d_st)); + ITRY (s_fd = open (o->bootmap, O_RDWR | O_TRUNC | O_CREAT | O_SYNC)); + ITRY (verify_file (o->bootsect, d_st.st_rdev)); + for (i = 0; i < blklst->ix; i++) + { + int offset = blklst->blk[i].off; + int addrct = blklst->blk[i].addr | (blklst->blk[i].ct & 0xff); + PRINT_LEVEL (1, "ix %i: offset: %06x count: %02x address: 0x%08x\n", i, offset, blklst->blk[i].ct & 0xff, blklst->blk[i].addr); + if ( o->testlevel <= 1 ) { + NTRY (write (s_fd, &offset, sizeof (int))); + NTRY (write (s_fd, &addrct, sizeof (int))); + } + } + ITRY (ioctl (s_fd,FIGETBSZ, &bs)); + ITRY (stat (o->bootmap, &s_st)); + if (s_st.st_size > bs ) + { + ERROR_LEVEL (0,"%s is larger than one block\n", o->bootmap); + rc = -1; + errno = EINVAL; + } + boots=0; + NTRY ( tmpdev = tmpnam(NULL) ); + ITRY (mknod (tmpdev, S_IFBLK | S_IRUSR | S_IWUSR, s_st.st_dev)); + ITRY (bd_fd = open (tmpdev, O_RDONLY)); + ITRY (ioctl(s_fd,FIBMAP,&boots)); + ITRY (ioctl (bd_fd, BIODASDRWTB, &boots)); + PRINT_LEVEL (1, "Bootmap is in block no: 0x%08x\n", boots); + close (bd_fd); + close(s_fd); + ITRY (unlink(tmpdev)); + /* Now patch the bootsector */ + ITRY (b_fd = open (o->bootsect, O_RDONLY)); + NTRY (read (b_fd, buffer, 4096)); + memset (buffer + 0xe0, 0, 8); + *(int *) (buffer + 0xe0) = boots; + if ( o -> testlevel <= 0 ) { + NTRY (write (d_fd, buffer, 4096)); + NTRY (write (d_fd, buffer, 4096)); + } + close (b_fd); + close (d_fd); + return rc; +} + +int +do_silo (struct silo_options *o) +{ + int rc = 0; + + int device_fd; + int image_fd; + struct blocklist blklist; + memset (&blklist, 0, sizeof (struct blocklist)); + ITRY (add_file_to_blocklist (o->image, &blklist, 0x00000000)); + if (o->parmfile) + { + ITRY (add_file_to_blocklist (o->parmfile, &blklist, 0x00008000)); + } + if (o->ramdisk) + { + ITRY (add_file_to_blocklist (o->ramdisk, &blklist, 0x00800000)); + } + ITRY (write_bootsect (o, &blklist)); + return rc; +} + +int +main (int argct, char *argv[]) +{ + int rc = 0; + char *save; + char *tmpdir=getenv("TMPDIR"); + if (tmpdir) { + NTRY( save=(char*)malloc(strlen(tmpdir))); + NTRY( strncpy(save,tmpdir,strlen(tmpdir))); + } + ITRY( setenv("TMPDIR",".",1)); + ITRY (parse_options (&silo_options, argct, argv)); + ITRY (verify_options (&silo_options)); + if ( silo_options.testlevel > 0 ) { + printf ("WARNING: silo does not modify your volume. Use -t2 to change IPL records\n"); + } + ITRY (do_silo (&silo_options)); + if ( save ) + ITRY( setenv("TMPDIR",save,1)); + return rc; +} diff --git a/arch/s390/tools/silo/silo.conf b/arch/s390/tools/silo/silo.conf new file mode 100644 index 000000000..47d8a4551 --- /dev/null +++ b/arch/s390/tools/silo/silo.conf @@ -0,0 +1,7 @@ +ipldevice = /dev/dasda +image = /boot/image +bootsect = /boot/ipleckd.boot +map = /boot/boot.map +root = /dev/dasd01 +readonly +append = "dasd=200-20f noinitrd" diff --git a/arch/s390/vmlinux.lds b/arch/s390/vmlinux.lds new file mode 100644 index 000000000..b1b556d14 --- /dev/null +++ b/arch/s390/vmlinux.lds @@ -0,0 +1,78 @@ +/* ld script to make s390 Linux kernel + * Written by Martin Schwidefsky (schwidefsky@de.ibm.com) + */ +OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390") +OUTPUT_ARCH(s390) +ENTRY(_start) +SECTIONS +{ + . = 0x00000000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x0700 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + .rodata : { *(.rodata) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + _etext = .; /* End of text section */ + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + _end = . ; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} |