summaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/Makefile68
-rw-r--r--arch/s390/boot/Makefile38
-rw-r--r--arch/s390/boot/ipldump.S178
-rw-r--r--arch/s390/boot/ipleckd.S299
-rw-r--r--arch/s390/boot/iplfba.S131
-rw-r--r--arch/s390/config.in75
-rw-r--r--arch/s390/defconfig178
-rw-r--r--arch/s390/kernel/Makefile54
-rw-r--r--arch/s390/kernel/bitmap.S37
-rw-r--r--arch/s390/kernel/cpcmd.c45
-rw-r--r--arch/s390/kernel/cpcmd.h14
-rw-r--r--arch/s390/kernel/ebcdic.c242
-rw-r--r--arch/s390/kernel/entry.S917
-rw-r--r--arch/s390/kernel/floatlib.c1021
-rw-r--r--arch/s390/kernel/gdb-stub.c575
-rw-r--r--arch/s390/kernel/head.S668
-rw-r--r--arch/s390/kernel/ieee.h90
-rw-r--r--arch/s390/kernel/init_task.c32
-rw-r--r--arch/s390/kernel/irq.c427
-rw-r--r--arch/s390/kernel/irqextras390.c35
-rw-r--r--arch/s390/kernel/lowcore.S60
-rw-r--r--arch/s390/kernel/mathemu.c894
-rw-r--r--arch/s390/kernel/process.c431
-rw-r--r--arch/s390/kernel/ptrace.c395
-rw-r--r--arch/s390/kernel/reipl.S67
-rw-r--r--arch/s390/kernel/s390_ksyms.c64
-rw-r--r--arch/s390/kernel/s390dyn.c36
-rw-r--r--arch/s390/kernel/s390fpu.c147
-rw-r--r--arch/s390/kernel/s390io.c4602
-rw-r--r--arch/s390/kernel/s390mach.c157
-rw-r--r--arch/s390/kernel/semaphore.c302
-rw-r--r--arch/s390/kernel/setup.c385
-rw-r--r--arch/s390/kernel/signal.c555
-rw-r--r--arch/s390/kernel/smp.c729
-rw-r--r--arch/s390/kernel/sys_s390.c257
-rw-r--r--arch/s390/kernel/time.c250
-rw-r--r--arch/s390/kernel/traps.c480
-rw-r--r--arch/s390/lib/Makefile12
-rw-r--r--arch/s390/lib/checksum.c56
-rw-r--r--arch/s390/lib/delay.c45
-rw-r--r--arch/s390/lib/memset.S30
-rw-r--r--arch/s390/lib/strcmp.S27
-rw-r--r--arch/s390/lib/strncpy.S30
-rw-r--r--arch/s390/mm/Makefile13
-rw-r--r--arch/s390/mm/extable.c63
-rw-r--r--arch/s390/mm/fault.c203
-rw-r--r--arch/s390/mm/init.c385
-rw-r--r--arch/s390/mm/ioremap.c129
-rw-r--r--arch/s390/tools/dasdfmt/Makefile9
-rw-r--r--arch/s390/tools/dasdfmt/dasdfmt.874
-rw-r--r--arch/s390/tools/dasdfmt/dasdfmt.c638
-rw-r--r--arch/s390/tools/silo/Makefile15
-rw-r--r--arch/s390/tools/silo/cfg.c373
-rw-r--r--arch/s390/tools/silo/cfg.h58
-rw-r--r--arch/s390/tools/silo/silo.c573
-rw-r--r--arch/s390/tools/silo/silo.conf7
-rw-r--r--arch/s390/vmlinux.lds78
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=&regs->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(&regs->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 *)&regs->crs[0],ptr,NUM_CRS*CR_SIZE,FALSE);
+ ptr = mem2hex((char *)&regs->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" (&current->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" (&current->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" (&current->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" (&current->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" (&current->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" (&current->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" (&current->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" (&current->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(&current->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 *) (&current->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, &current->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 *) (&current->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], &regs);
+ 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, &regs);
+ 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], &regs);
+}
+
+/*
+ * 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], &regs);
+ 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,&current->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=&current->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,&currentfprs->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=&current->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(&currentfprs->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, &regs);
+
+ //
+ // 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, &regs);
+
+ } /* endif */
+
+ } /* endif */
+
+ }
+ else
+ {
+ ioinfo[irq]->ui.flags.w4final = 1;
+ action->handler( irq, action->dev_id, &regs);
+
+ } /* 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, &regs);
+
+ 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(&current->sigmask_lock);
+ saveset = current->blocked;
+ siginitset(&current->blocked, mask);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->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(&current->sigmask_lock);
+ saveset = current->blocked;
+ current->blocked = newset;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->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(&current->sigmask_lock);
+ current->blocked = set;
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->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(&current->sigmask_lock);
+ sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+ sigaddset(&current->blocked,sig);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->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 = &current->blocked;
+
+ for (;;) {
+ unsigned long signr;
+
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->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(&current->blocked, signr)) {
+ send_sig_info(signr, &info, current);
+ continue;
+ }
+ }
+
+ ka = &current->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(&current->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(&regs,sizeof(pt_regs),0);
+ return do_fork(CLONE_VM|CLONE_PID, 0, &regs);
+}
+
+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(&current->mm->mmap_sem);
+ lock_kernel();
+
+ error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+
+ unlock_kernel();
+ up(&current->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] = &divide_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=&current->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) }
+}