ESA S/390(X) Channel SubSystem

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.

CCW Commands

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));

Types of commands

SEEK (0x07)

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)

SEARCH (0x31)

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)

Transfer In Channel (0x08)

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.

Read key and Data (0x0E)

Address is used to point to the destination buffer and length is used to denote the length of data to read.

Enable (0x27)

Used mostly by telnet terminals to request them to be enabled (start listening/activate send mode).

Sample command chain

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 %r14
Source code