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<address@hidden>
---
pc-bios/s390-ccw/cio.c | 114 +++++++++++++++++++++++++++++++++++++++
pc-bios/s390-ccw/cio.h | 127 ++++++++++++++++++++++++++++++++++++++++++--
pc-bios/s390-ccw/s390-ccw.h | 1 +
pc-bios/s390-ccw/start.S | 33 +++++++++++-
4 files changed, 270 insertions(+), 5 deletions(-)
diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
index 095f79b..63581c6 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,116 @@ void enable_subchannel(SubChannelId schid)
schib.pmcw.ena = 1;
msch(schid, &schib);
}
+
+uint16_t cu_type(SubChannelId schid)
+{
+ Ccw1 sense_id_ccw;
+ SenseId sense_data;
+
+ sense_id_ccw.cmd_code = CCW_CMD_SENSE_ID;
+ sense_id_ccw.cda = ptr2u32(&sense_data);
+ sense_id_ccw.count = sizeof(sense_data);
+ sense_id_ccw.flags |= CCW_FLAG_SLI;
+
+ if (do_cio(schid, ptr2u32(&sense_id_ccw), CCW_FMT1)) {
+ panic("Failed to run SenseID CCw\n");
+ }
+
+ return sense_data.cu_type;
+}
+
+void basic_sense(SubChannelId schid, void *sense_data, uint16_t data_size)
+{
+ Ccw1 senseCcw;
+
+ senseCcw.cmd_code = CCW_CMD_BASIC_SENSE;
+ senseCcw.cda = ptr2u32(sense_data);
+ senseCcw.count = data_size;
+
+ if (do_cio(schid, ptr2u32(&senseCcw), CCW_FMT1)) {
+ panic("Failed to run Basic Sense CCW\n");
+ }
+}
+
+static bool irb_error(Irb *irb)
+{
+ if (irb->scsw.cstat) {
+ return true;
+ }
+ return irb->scsw.dstat != (SCSW_DSTAT_DEVEND | SCSW_DSTAT_CHEND);
+}
+
+/*
+ * 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
+ * signaling completion of the I/O operation(s) performed 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)
+{
+ CmdOrb orb = {};
+ Irb irb = {};
+ sense_data_eckd_dasd sd;
+ int rc, retries = 0;
+
+ 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;
+
+ while (true) {
+ rc = ssch(schid, &orb);
+ if (rc == 1) {
+ /* Status pending, not sure why. Let's eat the status and retry. */
+ tsch(schid, &irb);
+ retries++;
+ continue;
+ }
+ if (rc) {
+ print_int("ssch failed with rc=", rc);
+ break;
+ }
+
+ consume_io_int();
+
+ /* collect status */
+ rc = tsch(schid, &irb);
+ if (rc) {
+ print_int("tsch failed with rc=", rc);
+ break;
+ }
+
+ if (!irb_error(&irb)) {
+ break;
+ }
+
+ /*
+ * Unexpected unit check, or interface-control-check. Use sense to
+ * clear unit check then retry.
+ */
+ if ((unit_check(&irb) || iface_ctrl_check(&irb)) && retries <= 2) {
+ basic_sense(schid, &sd, sizeof(sd));