From: "Jason J. Herne" <jjhe...@linux.vnet.ibm.com>

Add struct for format-0 ccws. Support executing format-0 channel
programs and waiting for their completion before continuing execution.
This will be used for real dasd ipl.

Add cu_type() to channel io library. This will be used to query control
unit type which is used to determine if we are booting a virtio device or a
real dasd device.

Signed-off-by: Jason J. Herne <jjhe...@linux.vnet.ibm.com>
Signed-off-by: Jason J. Herne <jjhe...@linux.ibm.com>
---
 pc-bios/s390-ccw/cio.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/cio.h |  25 +++++++++-
 2 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
index 095f79b..f440380 100644
--- a/pc-bios/s390-ccw/cio.c
+++ b/pc-bios/s390-ccw/cio.c
@@ -10,6 +10,7 @@
 
 #include "libc.h"
 #include "s390-ccw.h"
+#include "s390-arch.h"
 #include "cio.h"
 
 static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
@@ -39,3 +40,129 @@ void enable_subchannel(SubChannelId schid)
     schib.pmcw.ena = 1;
     msch(schid, &schib);
 }
+
+__u16 cu_type(SubChannelId schid)
+{
+    Ccw1 senseIdCcw;
+    SenseId senseData;
+
+    senseIdCcw.cmd_code = CCW_CMD_SENSE_ID;
+    senseIdCcw.cda = ptr2u32(&senseData);
+    senseIdCcw.count = sizeof(senseData);
+
+    if (do_cio(schid, ptr2u32(&senseIdCcw), CCW_FMT1)) {
+        panic("Failed to run SenseID CCw\n");
+    }
+
+    return senseData.cu_type;
+}
+
+static bool irb_error(Irb *irb)
+{
+    /* We have to ignore Incorrect Length (cstat == 0x40) indicators because
+     * real devices expect a 24 byte SenseID  buffer, and virtio devices expect
+     * a much larger buffer. Neither device type can tolerate a buffer size
+     * different from what they expect so they set this indicator.
+     */
+    if (irb->scsw.cstat != 0x00 && irb->scsw.cstat != 0x40) {
+        return true;
+    }
+    return irb->scsw.dstat != 0xc;
+}
+
+/* Executes a channel program at a given subchannel. The request to run the
+ * channel program is sent to the subchannel, we then wait for the interrupt
+ * singaling completion of the I/O operation(s) perfomed by the channel
+ * program. Lastly we verify that the i/o operation completed without error and
+ * that the interrupt we received was for the subchannel used to run the
+ * channel program.
+ *
+ * Note: This function assumes it is running in an environment where no other
+ * cpus are generating or receiving I/O interrupts. So either run it in a
+ * single-cpu environment or make sure all other cpus are not doing I/O and
+ * have I/O interrupts masked off.
+ */
+int do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt)
+{
+    Ccw0 *this_ccw, *prev_ccw;
+    CmdOrb orb = {};
+    Irb irb = {};
+    int rc;
+
+    IPL_assert(fmt == 0 || fmt == 1, "Invalid ccw format");
+
+    /* ccw_addr must be <= 24 bits and point to at least one whole ccw. */
+    if (fmt == 0) {
+        IPL_assert(ccw_addr <= 0xFFFFFF - 8, "Invalid ccw address");
+    }
+
+    orb.fmt = fmt ;
+    orb.pfch = 1;  /* QEMU's cio implementation requires prefetch */
+    orb.c64 = 1;   /* QEMU's cio implementation requires 64-bit idaws */
+    orb.lpm = 0xFF; /* All paths allowed */
+    orb.cpa = ccw_addr;
+
+    rc = ssch(schid, &orb);
+    if (rc) {
+        print_int("ssch failed with rc=", rc);
+        return rc;
+    }
+
+    await_io_int(schid.sch_no);
+
+    /* Clear read */
+    rc = tsch(schid, &irb);
+    if (rc) {
+        print_int("tsch failed with rc=", rc);
+        return rc;
+    }
+
+    if (irb_error(&irb)) {
+        this_ccw = u32toptr(irb.scsw.cpa);
+        prev_ccw = u32toptr(irb.scsw.cpa - 8);
+
+        print_int("irb_error: cstat=", irb.scsw.cstat);
+        print_int("           dstat=", irb.scsw.dstat);
+        print_int("           cpa=", irb.scsw.cpa);
+        print_int("           prev_ccw=", *((uint64_t *)prev_ccw));
+        print_int("           this_ccw=", *((uint64_t *)this_ccw));
+    }
+
+    return 0;
+}
+
+void await_io_int(uint16_t sch_no)
+{
+    /*
+     * wait_psw and ctl6 must be static to avoid stack allocation as gcc cannot
+     * align stack variables. The stctg, lctlg and lpswe instructions require
+     * that their operands be aligned on an 8-byte boundary.
+    */
+    static uint64_t ctl6 __attribute__((__aligned__(8)));
+    static PSW wait_psw;
+
+    /* PSW to load when I/O interrupt happens */
+    lowcore->io_new_psw.mask = PSW_MASK_ZMODE;
+    lowcore->io_new_psw.addr = (uint64_t)&&IOIntWakeup; /* Wake-up address */
+
+    /* Enable io interrupts subclass mask */
+    asm volatile("stctg 6,6,%0" : "=S" (ctl6) : : "memory");
+    ctl6 |= 0x00000000FF000000;
+    asm volatile("lctlg 6,6,%0" : : "S" (ctl6));
+
+    /* Set wait psw enabled for io interrupt */
+    wait_psw.mask = (PSW_MASK_ZMODE | PSW_MASK_IOINT | PSW_MASK_WAIT);
+    asm volatile("lpswe %0" : : "Q" (wait_psw) : "cc");
+
+    panic("await_io_int: lpswe failed!!\n");
+
+IOIntWakeup:
+    /* Should never happen - all other subchannels are disabled by default */
+    IPL_assert(lowcore->subchannel_nr == sch_no,
+               "Interrupt from unexpected device");
+
+    /* Disable all subclasses of I/O interrupts for this cpu */
+    asm volatile("stctg 6,6,%0" : "=S" (ctl6) : : "memory");
+    ctl6 &= ~(0x00000000FF000000);
+    asm volatile("lctlg 6,6,%0" : : "S" (ctl6));
+}
diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
index 7b07d75..d8e2955 100644
--- a/pc-bios/s390-ccw/cio.h
+++ b/pc-bios/s390-ccw/cio.h
@@ -127,7 +127,23 @@ struct tpi_info {
     __u32 reserved4  : 12;
 } __attribute__ ((packed, aligned(4)));
 
-/* channel command word (type 1) */
+/* channel command word (format 0) */
+typedef struct ccw0 {
+    __u8 cmd_code;
+    __u32 cda        : 24;
+    __u32 chainData  : 1;
+    __u32 chain      : 1;
+    __u32 sli        : 1;
+    __u32 skip       : 1;
+    __u32 pci        : 1;
+    __u32 ida        : 1;
+    __u32 suspend    : 1;
+    __u32 mida       : 1;
+    __u8 reserved;
+    __u16 count;
+} __attribute__ ((packed, aligned(8))) Ccw0;
+
+/* channel command word (format 1) */
 typedef struct ccw1 {
     __u8 cmd_code;
     __u8 flags;
@@ -135,6 +151,10 @@ typedef struct ccw1 {
     __u32 cda;
 } __attribute__ ((packed, aligned(8))) Ccw1;
 
+/* do_cio() CCW formats */
+#define CCW_FMT0                 0x00
+#define CCW_FMT1                 0x01
+
 #define CCW_FLAG_DC              0x80
 #define CCW_FLAG_CC              0x40
 #define CCW_FLAG_SLI             0x20
@@ -215,6 +235,9 @@ typedef struct irb {
 
 int enable_mss_facility(void);
 void enable_subchannel(SubChannelId schid);
+__u16 cu_type(SubChannelId schid);
+void await_io_int(uint16_t sch_no);
+int do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt);
 
 /*
  * Some S390 specific IO instructions as inline
-- 
2.7.4


Reply via email to