The channel subsystem in the ESA S/390(x) is the system responsible for communication between the devices and the operating system.
One of the most confusing aspects of the CSS is that it uses chains of commands instead of a sequential execution; in short: it uses multiple commands at once instead of only one command at a time (for example, in x86, I/O bound instructions can only output 1 element at a time).
The CSS can be viewed as a DMA device with command chains.
The execution of CSS commands is sequential. The commands are executed in the order they appear.
CSS addresses are real, this means that all addresses are physical and are not bound to the current virtual space.
There are 2 types of CCWs, however CCW0 is only used by SIO/TIO instruction (pre-S390) and CCW1 is used by S390 and above:
// Format 0 Control Command Word struct css_ccw0 { uint8_t cmd; uint16_t lo_addr; uint8_t hi_addr; uint8_t flags; uint8_t reserved; uint16_t count; } __attribute__((packed)); // Format 1 Control Command Word struct css_ccw1 { uint8_t cmd; uint8_t flags; uint16_t count; uint32_t addr; } __attribute__((packed));
Address points to a BBCCHH field (that is, 1 unused halfword, 1 halfword denoting cylinders and another halfword denoting heads). The count should be the size of the BBCCHH (6)
This will perform a search on the disk (something akin to a seek, but done on the track); Address points to a CCHHR field (that is, 1 halfword denoting cylinders, 1 halfword denoting heads and a character denoting record number). The coount should be the size of the CCHHR (5)
Jumps to the address field if an error occurs during a SEARCH on the DASD - when the operation is sucessful this command is skipped.
TIC will loop infinitely when SEARCH fails, when the DASD detects that it has read the entire track it will abnormally exit the channel program.
Address is used to point to the destination buffer and length is used to denote the length of data to read.
Used mostly by telnet terminals to request them to be enabled (start listening/activate send mode).
Please note that HH1 == HH2 and CC1 == CC2. The reason why they are duplicate is to separate the SEEK and SEARCH CCW parameters.
Let's decompose what this chain does:
You should be familiar with s390 assembler, if not you can see the introduction page.
Below there is a bunch of code for doing a DASD read operation:
Now what do is basically saving the arguments in the BBCCHHCCHHR data area so the commands can properly use them.
# Read a block from the DASD # r1 = Device number (subchannel id) # r2 = Head # r3 = Cylinder # r4 = Record # r5 = Buffer # r6 = Size of buffer read_block: lr %r12, %r15 lr %r10, %r1 # Save device number in r10 stcm %r2, 0b0011, rb_cc1 # Cylinder stcm %r2, 0b0011, rb_cc2 stcm %r3, 0b0011, rb_hh1 # Head stcm %r3, 0b0011, rb_hh2 stc %r4, rb_record # Record lr %r2, %r5 # Buffer lr %r7, %r6 # Bytes to read st %r2, rb_ldccw + 4 # Overwrite LD.addr to point to our buffer sth %r7, rb_ldccw + 2 # And set the size too
We are going to save a PSW which will point to rb_count, this means that once an interrupt signaling the success of the read operation occurs, we will jump to rb_count
mvc FLCINPSW(8), rb_newio stosm FLCINPSW, 0x00
Now we are going to put the first command of the chain into FLCCAW
la %r3, rb_seek st %r3, FLCCAW
Remember when we stored the device number in r10? Well, all CSS operations in S390 require r1 to be filled with the device number!
lr %r1, %r10
IRB must be an area of 24 words, we don't need to initialize it, however we need to consume any pending interrupt before requesting execution
la %r9, rb_irb tsch 0(%r9) ipm %r11 # Error checking, if the CC is not zero then an error occoured (see Principles of Operation for error codes) srl %r11, 28 c %r11, %r11 bnz fatal_error
Now, we will request execution of our command chain, ORB must be initialized and point to the first command in the chain
la %r10, rb_orb ssch 0(%r10) ipm %r11 srl %r11, 28 c %r11, %r11 bnz fatal_error
We are now going to wait for a response from the device
lpsw rb_wtnoer
When the device answers it will jump to this label, notice that labels in PSWs must be aligned to a word-boundary
.align 4 rb_count:
Finally, we are going to see if the operation succeded at all
tsch 0(%r9) ipm %r11 srl %r11, 28 c %r11, %r11 bnz fatal_error sh %r7, 10(%r9) lr %r15, %r7 clc 4(4, %r9), rb_end_chain be rb_end rb_end: br %r14Source code