summaryrefslogtreecommitdiffstats
path: root/arch/s390x/boot
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390x/boot')
-rw-r--r--arch/s390x/boot/Makefile37
-rw-r--r--arch/s390x/boot/ipldump.S178
-rw-r--r--arch/s390x/boot/ipleckd.S303
-rw-r--r--arch/s390x/boot/iplfba.S131
4 files changed, 649 insertions, 0 deletions
diff --git a/arch/s390x/boot/Makefile b/arch/s390x/boot/Makefile
new file mode 100644
index 000000000..fb112b964
--- /dev/null
+++ b/arch/s390x/boot/Makefile
@@ -0,0 +1,37 @@
+#
+# 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 :=
+
+include $(TOPDIR)/Rules.make
+
+.S.o:
+ $(CC) -D__ASSEMBLY__ $(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/s390x/boot/ipldump.S b/arch/s390x/boot/ipldump.S
new file mode 100644
index 000000000..84150b5af
--- /dev/null
+++ b/arch/s390x/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),.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/s390x/boot/ipleckd.S b/arch/s390x/boot/ipleckd.S
new file mode 100644
index 000000000..d66a8d684
--- /dev/null
+++ b/arch/s390x/boot/ipleckd.S
@@ -0,0 +1,303 @@
+#
+# arch/s390/boot/ipleckd.S
+# IPL record for 3380/3390 DASD
+#
+# S390 version
+# Copyright (C) 1999,2000 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
+
+# change 09/20/00 removed obsolete store of ipldevice to textesegment
+
+# 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
+ lhi %r6,9
+ clc .Lrdcdata+3(2),.L9345
+ je .L011
+ lhi %r6,10
+ clc .Lrdcdata+3(2),.L3380
+ je .L011
+ lhi %r6,12
+ clc .Lrdcdata+3(2),.L3390
+ je .L011
+ bras %r14,.Ldisab
+.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
+.L9345:
+ .word 0x9345
+.L3380:
+ .word 0x3380
+.Lnull:
+ .long 0x00000000,0x00000000
+ .align 4
+.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/s390x/boot/iplfba.S b/arch/s390x/boot/iplfba.S
new file mode 100644
index 000000000..732a9848c
--- /dev/null
+++ b/arch/s390x/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
+