aboutsummaryrefslogtreecommitdiff
path: root/pc-bios
diff options
context:
space:
mode:
authorCornelia Huck <cohuck@redhat.com>2019-04-25 14:08:17 +0200
committerCornelia Huck <cohuck@redhat.com>2019-04-25 14:09:20 +0200
commit41c3d4269b1c18d12d33c5bf089dace25d08e82e (patch)
tree5b9447cf21e53ce4fec524e8f9e836fcfd4780da /pc-bios
parent905b7ee4d647111d29f29c55618591366321c47a (diff)
parent4a33fac54665558b7f7a68135c4a4c602eceb72f (diff)
Merge tag 's390-ccw-bios-2019-04-12' into s390-next-staging
Support for booting from a vfio-ccw passthrough dasd device # gpg: Signature made Fri 12 Apr 2019 01:17:03 PM CEST # gpg: using RSA key 2ED9D774FE702DB5 # gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full] # gpg: aka "Thomas Huth <thuth@redhat.com>" [undefined] # gpg: aka "Thomas Huth <huth@tuxfamily.org>" [undefined] # gpg: aka "Thomas Huth <th.huth@posteo.de>" [unknown] * tag 's390-ccw-bios-2019-04-12': pc-bios/s390: Update firmware images s390-bios: Use control unit type to find bootable devices s390-bios: Support booting from real dasd device s390-bios: Add channel command codes/structs needed for dasd-ipl s390-bios: Use control unit type to determine boot method s390-bios: Refactor virtio to run channel programs via cio s390-bios: Factor finding boot device out of virtio code path s390-bios: Extend find_dev() for non-virtio devices s390-bios: cio error handling s390-bios: Support for running format-0/1 channel programs s390-bios: ptr2u32 and u32toptr s390-bios: Map low core memory s390-bios: Decouple channel i/o logic from virtio s390-bios: Clean up cio.h s390-bios: decouple common boot logic from virtio s390-bios: decouple cio setup from virtio s390 vfio-ccw: Add bootindex property and IPLB data Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Diffstat (limited to 'pc-bios')
-rw-r--r--pc-bios/s390-ccw.imgbin34568 -> 42608 bytes
-rw-r--r--pc-bios/s390-ccw/Makefile2
-rw-r--r--pc-bios/s390-ccw/cio.c423
-rw-r--r--pc-bios/s390-ccw/cio.h270
-rw-r--r--pc-bios/s390-ccw/dasd-ipl.c235
-rw-r--r--pc-bios/s390-ccw/dasd-ipl.h16
-rw-r--r--pc-bios/s390-ccw/helper.h31
-rw-r--r--pc-bios/s390-ccw/libc.h11
-rw-r--r--pc-bios/s390-ccw/main.c190
-rw-r--r--pc-bios/s390-ccw/netboot.mak2
-rw-r--r--pc-bios/s390-ccw/netmain.c2
-rw-r--r--pc-bios/s390-ccw/s390-arch.h103
-rw-r--r--pc-bios/s390-ccw/s390-ccw.h10
-rw-r--r--pc-bios/s390-ccw/start.S29
-rw-r--r--pc-bios/s390-ccw/virtio.c81
-rw-r--r--pc-bios/s390-netboot.imgbin54944 -> 67232 bytes
16 files changed, 1223 insertions, 182 deletions
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index 450a076dc0..ba054828d3 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile
index 1eb316b02f..a048b6b077 100644
--- a/pc-bios/s390-ccw/Makefile
+++ b/pc-bios/s390-ccw/Makefile
@@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw)
.PHONY : all clean build-all
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \
- virtio.o virtio-scsi.o virtio-blkdev.o libc.o
+ virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o
QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS))
QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float
diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c
new file mode 100644
index 0000000000..339ec5fbe7
--- /dev/null
+++ b/pc-bios/s390-ccw/cio.c
@@ -0,0 +1,423 @@
+/*
+ * S390 Channel I/O
+ *
+ * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
+ * Copyright (c) 2019 IBM Corp.
+ *
+ * Author(s): Jason J. Herne <jjherne@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#include "libc.h"
+#include "s390-ccw.h"
+#include "s390-arch.h"
+#include "helper.h"
+#include "cio.h"
+
+static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+
+static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb);
+
+int enable_mss_facility(void)
+{
+ int ret;
+ ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page;
+
+ memset(sda_area, 0, PAGE_SIZE);
+ sda_area->request.length = 0x0400;
+ sda_area->request.code = 0x0031;
+ sda_area->operation_code = 0x2;
+
+ ret = chsc(sda_area);
+ if ((ret == 0) && (sda_area->response.code == 0x0001)) {
+ return 0;
+ }
+ return -EIO;
+}
+
+void enable_subchannel(SubChannelId schid)
+{
+ Schib schib;
+
+ stsch_err(schid, &schib);
+ 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, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) {
+ panic("Failed to run SenseID CCw\n");
+ }
+
+ return sense_data.cu_type;
+}
+
+int basic_sense(SubChannelId schid, uint16_t cutype, void *sense_data,
+ uint16_t data_size)
+{
+ Ccw1 senseCcw;
+ Irb irb;
+
+ senseCcw.cmd_code = CCW_CMD_BASIC_SENSE;
+ senseCcw.cda = ptr2u32(sense_data);
+ senseCcw.count = data_size;
+
+ return __do_cio(schid, ptr2u32(&senseCcw), CCW_FMT1, &irb);
+}
+
+static bool irb_error(Irb *irb)
+{
+ if (irb->scsw.cstat) {
+ return true;
+ }
+ return irb->scsw.dstat != (SCSW_DSTAT_DEVEND | SCSW_DSTAT_CHEND);
+}
+
+static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd)
+{
+ char msgline[512];
+
+ if (sd->config_info & 0x8000) {
+ sclp_print("Eckd Dasd Sense Data (fmt 24-bytes):\n");
+ } else {
+ sclp_print("Eckd Dasd Sense Data (fmt 32-bytes):\n");
+ }
+
+ strcat(msgline, " Sense Condition Flags :");
+ if (sd->common_status & SNS_STAT0_CMD_REJECT) {
+ strcat(msgline, " [Cmd-Reject]");
+ }
+ if (sd->common_status & SNS_STAT0_INTERVENTION_REQ) {
+ strcat(msgline, " [Intervention-Required]");
+ }
+ if (sd->common_status & SNS_STAT0_BUS_OUT_CHECK) {
+ strcat(msgline, " [Bus-Out-Parity-Check]");
+ }
+ if (sd->common_status & SNS_STAT0_EQUIPMENT_CHECK) {
+ strcat(msgline, " [Equipment-Check]");
+ }
+ if (sd->common_status & SNS_STAT0_DATA_CHECK) {
+ strcat(msgline, " [Data-Check]");
+ }
+ if (sd->common_status & SNS_STAT0_OVERRUN) {
+ strcat(msgline, " [Overrun]");
+ }
+ if (sd->common_status & SNS_STAT0_INCOMPL_DOMAIN) {
+ strcat(msgline, " [Incomplete-Domain]");
+ }
+
+ if (sd->status[0] & SNS_STAT1_PERM_ERR) {
+ strcat(msgline, " [Permanent-Error]");
+ }
+ if (sd->status[0] & SNS_STAT1_INV_TRACK_FORMAT) {
+ strcat(msgline, " [Invalid-Track-Fmt]");
+ }
+ if (sd->status[0] & SNS_STAT1_EOC) {
+ strcat(msgline, " [End-of-Cyl]");
+ }
+ if (sd->status[0] & SNS_STAT1_MESSAGE_TO_OPER) {
+ strcat(msgline, " [Operator-Msg]");
+ }
+ if (sd->status[0] & SNS_STAT1_NO_REC_FOUND) {
+ strcat(msgline, " [No-Record-Found]");
+ }
+ if (sd->status[0] & SNS_STAT1_FILE_PROTECTED) {
+ strcat(msgline, " [File-Protected]");
+ }
+ if (sd->status[0] & SNS_STAT1_WRITE_INHIBITED) {
+ strcat(msgline, " [Write-Inhibited]");
+ }
+ if (sd->status[0] & SNS_STAT1_IMPRECISE_END) {
+ strcat(msgline, " [Imprecise-Ending]");
+ }
+
+ if (sd->status[1] & SNS_STAT2_REQ_INH_WRITE) {
+ strcat(msgline, " [Req-Inhibit-Write]");
+ }
+ if (sd->status[1] & SNS_STAT2_CORRECTABLE) {
+ strcat(msgline, " [Correctable-Data-Check]");
+ }
+ if (sd->status[1] & SNS_STAT2_FIRST_LOG_ERR) {
+ strcat(msgline, " [First-Error-Log]");
+ }
+ if (sd->status[1] & SNS_STAT2_ENV_DATA_PRESENT) {
+ strcat(msgline, " [Env-Data-Present]");
+ }
+ if (sd->status[1] & SNS_STAT2_IMPRECISE_END) {
+ strcat(msgline, " [Imprecise-End]");
+ }
+ strcat(msgline, "\n");
+ sclp_print(msgline);
+
+ print_int(" Residual Count =", sd->res_count);
+ print_int(" Phys Drive ID =", sd->phys_drive_id);
+ print_int(" low cyl address =", sd->low_cyl_addr);
+ print_int(" head addr & hi cyl =", sd->head_high_cyl_addr);
+ print_int(" format/message =", sd->fmt_msg);
+ print_int(" fmt-dependent[0-7] =", sd->fmt_dependent_info[0]);
+ print_int(" fmt-dependent[8-15]=", sd->fmt_dependent_info[1]);
+ print_int(" prog action code =", sd->program_action_code);
+ print_int(" Configuration info =", sd->config_info);
+ print_int(" mcode / hi-cyl =", sd->mcode_hicyl);
+ print_int(" cyl & head addr [0]=", sd->cyl_head_addr[0]);
+ print_int(" cyl & head addr [1]=", sd->cyl_head_addr[1]);
+ print_int(" cyl & head addr [2]=", sd->cyl_head_addr[2]);
+}
+
+static void print_irb_err(Irb *irb)
+{
+ uint64_t this_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa);
+ uint64_t prev_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa - 8);
+ char msgline[256];
+
+ sclp_print("Interrupt Response Block Data:\n");
+
+ strcat(msgline, " Function Ctrl :");
+ if (irb->scsw.ctrl & SCSW_FCTL_START_FUNC) {
+ strcat(msgline, " [Start]");
+ }
+ if (irb->scsw.ctrl & SCSW_FCTL_HALT_FUNC) {
+ strcat(msgline, " [Halt]");
+ }
+ if (irb->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) {
+ strcat(msgline, " [Clear]");
+ }
+ strcat(msgline, "\n");
+ sclp_print(msgline);
+
+ msgline[0] = '\0';
+ strcat(msgline, " Activity Ctrl :");
+ if (irb->scsw.ctrl & SCSW_ACTL_RESUME_PEND) {
+ strcat(msgline, " [Resume-Pending]");
+ }
+ if (irb->scsw.ctrl & SCSW_ACTL_START_PEND) {
+ strcat(msgline, " [Start-Pending]");
+ }
+ if (irb->scsw.ctrl & SCSW_ACTL_HALT_PEND) {
+ strcat(msgline, " [Halt-Pending]");
+ }
+ if (irb->scsw.ctrl & SCSW_ACTL_CLEAR_PEND) {
+ strcat(msgline, " [Clear-Pending]");
+ }
+ if (irb->scsw.ctrl & SCSW_ACTL_CH_ACTIVE) {
+ strcat(msgline, " [Channel-Active]");
+ }
+ if (irb->scsw.ctrl & SCSW_ACTL_DEV_ACTIVE) {
+ strcat(msgline, " [Device-Active]");
+ }
+ if (irb->scsw.ctrl & SCSW_ACTL_SUSPENDED) {
+ strcat(msgline, " [Suspended]");
+ }
+ strcat(msgline, "\n");
+ sclp_print(msgline);
+
+ msgline[0] = '\0';
+ strcat(msgline, " Status Ctrl :");
+ if (irb->scsw.ctrl & SCSW_SCTL_ALERT) {
+ strcat(msgline, " [Alert]");
+ }
+ if (irb->scsw.ctrl & SCSW_SCTL_INTERMED) {
+ strcat(msgline, " [Intermediate]");
+ }
+ if (irb->scsw.ctrl & SCSW_SCTL_PRIMARY) {
+ strcat(msgline, " [Primary]");
+ }
+ if (irb->scsw.ctrl & SCSW_SCTL_SECONDARY) {
+ strcat(msgline, " [Secondary]");
+ }
+ if (irb->scsw.ctrl & SCSW_SCTL_STATUS_PEND) {
+ strcat(msgline, " [Status-Pending]");
+ }
+
+ strcat(msgline, "\n");
+ sclp_print(msgline);
+
+ msgline[0] = '\0';
+ strcat(msgline, " Device Status :");
+ if (irb->scsw.dstat & SCSW_DSTAT_ATTN) {
+ strcat(msgline, " [Attention]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_STATMOD) {
+ strcat(msgline, " [Status-Modifier]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_CUEND) {
+ strcat(msgline, " [Ctrl-Unit-End]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_BUSY) {
+ strcat(msgline, " [Busy]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_CHEND) {
+ strcat(msgline, " [Channel-End]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_DEVEND) {
+ strcat(msgline, " [Device-End]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_UCHK) {
+ strcat(msgline, " [Unit-Check]");
+ }
+ if (irb->scsw.dstat & SCSW_DSTAT_UEXCP) {
+ strcat(msgline, " [Unit-Exception]");
+ }
+ strcat(msgline, "\n");
+ sclp_print(msgline);
+
+ msgline[0] = '\0';
+ strcat(msgline, " Channel Status :");
+ if (irb->scsw.cstat & SCSW_CSTAT_PCINT) {
+ strcat(msgline, " [Program-Ctrl-Interruption]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_BADLEN) {
+ strcat(msgline, " [Incorrect-Length]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_PROGCHK) {
+ strcat(msgline, " [Program-Check]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_PROTCHK) {
+ strcat(msgline, " [Protection-Check]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_CHDCHK) {
+ strcat(msgline, " [Channel-Data-Check]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_CHCCHK) {
+ strcat(msgline, " [Channel-Ctrl-Check]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_ICCHK) {
+ strcat(msgline, " [Interface-Ctrl-Check]");
+ }
+ if (irb->scsw.cstat & SCSW_CSTAT_CHAINCHK) {
+ strcat(msgline, " [Chaining-Check]");
+ }
+ strcat(msgline, "\n");
+ sclp_print(msgline);
+
+ print_int(" cpa=", irb->scsw.cpa);
+ print_int(" prev_ccw=", prev_ccw);
+ print_int(" this_ccw=", this_ccw);
+}
+
+/*
+ * Handles executing ssch, tsch and returns the irb obtained from tsch.
+ * Returns 0 on success, -1 if unexpected status pending and we need to retry,
+ * otherwise returns condition code from ssch/tsch for error cases.
+ */
+static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb)
+{
+ CmdOrb orb = {};
+ 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 == 1 || rc == 2) {
+ /* Subchannel status pending or busy. Eat status and ask for retry. */
+ tsch(schid, irb);
+ return -1;
+ }
+ if (rc) {
+ print_int("ssch failed with cc=", rc);
+ return rc;
+ }
+
+ consume_io_int();
+
+ /* collect status */
+ rc = tsch(schid, irb);
+ if (rc) {
+ print_int("tsch failed with cc=", rc);
+ }
+
+ return rc;
+}
+
+/*
+ * 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. We also assume that only one device is
+ * active (generating i/o interrupts).
+ *
+ * Returns non-zero on error.
+ */
+int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt)
+{
+ Irb irb = {};
+ SenseDataEckdDasd sd;
+ int rc, retries = 0;
+
+ while (true) {
+ rc = __do_cio(schid, ccw_addr, fmt, &irb);
+
+ if (rc == -1) {
+ retries++;
+ continue;
+ }
+ if (rc) {
+ /* ssch/tsch error. Message already reported by __do_cio */
+ break;
+ }
+
+ if (!irb_error(&irb)) {
+ break;
+ }
+
+ /*
+ * Unexpected unit check, or interface-control-check. Use sense to
+ * clear (unit check only) then retry.
+ */
+ if ((unit_check(&irb) || iface_ctrl_check(&irb)) && retries <= 2) {
+ if (unit_check(&irb)) {
+ basic_sense(schid, cutype, &sd, sizeof(sd));
+ }
+ retries++;
+ continue;
+ }
+
+ sclp_print("cio device error\n");
+ print_int(" ssid ", schid.ssid);
+ print_int(" cssid ", schid.cssid);
+ print_int(" sch_no", schid.sch_no);
+ print_int(" ctrl-unit type", cutype);
+ sclp_print("\n");
+ print_irb_err(&irb);
+ if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 ||
+ cutype == CU_TYPE_UNKNOWN) {
+ if (!basic_sense(schid, cutype, &sd, sizeof(sd))) {
+ print_eckd_dasd_sense_data(&sd);
+ }
+ }
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
index 1a0795f645..aaa432dedd 100644
--- a/pc-bios/s390-ccw/cio.h
+++ b/pc-bios/s390-ccw/cio.h
@@ -17,35 +17,35 @@
* path management control word
*/
struct pmcw {
- __u32 intparm; /* interruption parameter */
- __u32 qf : 1; /* qdio facility */
- __u32 w : 1;
- __u32 isc : 3; /* interruption sublass */
- __u32 res5 : 3; /* reserved zeros */
- __u32 ena : 1; /* enabled */
- __u32 lm : 2; /* limit mode */
- __u32 mme : 2; /* measurement-mode enable */
- __u32 mp : 1; /* multipath mode */
- __u32 tf : 1; /* timing facility */
- __u32 dnv : 1; /* device number valid */
- __u32 dev : 16; /* device number */
- __u8 lpm; /* logical path mask */
- __u8 pnom; /* path not operational mask */
- __u8 lpum; /* last path used mask */
- __u8 pim; /* path installed mask */
- __u16 mbi; /* measurement-block index */
- __u8 pom; /* path operational mask */
- __u8 pam; /* path available mask */
- __u8 chpid[8]; /* CHPID 0-7 (if available) */
- __u32 unused1 : 8; /* reserved zeros */
- __u32 st : 3; /* subchannel type */
- __u32 unused2 : 18; /* reserved zeros */
- __u32 mbfc : 1; /* measurement block format control */
- __u32 xmwme : 1; /* extended measurement word mode enable */
- __u32 csense : 1; /* concurrent sense; can be enabled ...*/
- /* ... per MSCH, however, if facility */
- /* ... is not installed, this results */
- /* ... in an operand exception. */
+ __u32 intparm; /* interruption parameter */
+ __u32 qf:1; /* qdio facility */
+ __u32 w:1;
+ __u32 isc:3; /* interruption sublass */
+ __u32 res5:3; /* reserved zeros */
+ __u32 ena:1; /* enabled */
+ __u32 lm:2; /* limit mode */
+ __u32 mme:2; /* measurement-mode enable */
+ __u32 mp:1; /* multipath mode */
+ __u32 tf:1; /* timing facility */
+ __u32 dnv:1; /* device number valid */
+ __u32 dev:16; /* device number */
+ __u8 lpm; /* logical path mask */
+ __u8 pnom; /* path not operational mask */
+ __u8 lpum; /* last path used mask */
+ __u8 pim; /* path installed mask */
+ __u16 mbi; /* measurement-block index */
+ __u8 pom; /* path operational mask */
+ __u8 pam; /* path available mask */
+ __u8 chpid[8]; /* CHPID 0-7 (if available) */
+ __u32 unused1:8; /* reserved zeros */
+ __u32 st:3; /* subchannel type */
+ __u32 unused2:18; /* reserved zeros */
+ __u32 mbfc:1; /* measurement block format control */
+ __u32 xmwme:1; /* extended measurement word mode enable */
+ __u32 csense:1; /* concurrent sense; can be enabled ...*/
+ /* ... per MSCH, however, if facility */
+ /* ... is not installed, this results */
+ /* ... in an operand exception. */
} __attribute__ ((packed));
/* Target SCHIB configuration. */
@@ -70,35 +70,72 @@ struct scsw {
__u16 count;
} __attribute__ ((packed));
-#define SCSW_FCTL_CLEAR_FUNC 0x1000
-#define SCSW_FCTL_HALT_FUNC 0x2000
+/* Function Control */
#define SCSW_FCTL_START_FUNC 0x4000
+#define SCSW_FCTL_HALT_FUNC 0x2000
+#define SCSW_FCTL_CLEAR_FUNC 0x1000
+
+/* Activity Control */
+#define SCSW_ACTL_RESUME_PEND 0x0800
+#define SCSW_ACTL_START_PEND 0x0400
+#define SCSW_ACTL_HALT_PEND 0x0200
+#define SCSW_ACTL_CLEAR_PEND 0x0100
+#define SCSW_ACTL_CH_ACTIVE 0x0080
+#define SCSW_ACTL_DEV_ACTIVE 0x0040
+#define SCSW_ACTL_SUSPENDED 0x0020
+
+/* Status Control */
+#define SCSW_SCTL_ALERT 0x0010
+#define SCSW_SCTL_INTERMED 0x0008
+#define SCSW_SCTL_PRIMARY 0x0004
+#define SCSW_SCTL_SECONDARY 0x0002
+#define SCSW_SCTL_STATUS_PEND 0x0001
+
+/* SCSW Device Status Flags */
+#define SCSW_DSTAT_ATTN 0x80
+#define SCSW_DSTAT_STATMOD 0x40
+#define SCSW_DSTAT_CUEND 0x20
+#define SCSW_DSTAT_BUSY 0x10
+#define SCSW_DSTAT_CHEND 0x08
+#define SCSW_DSTAT_DEVEND 0x04
+#define SCSW_DSTAT_UCHK 0x02
+#define SCSW_DSTAT_UEXCP 0x01
+
+/* SCSW Subchannel Status Flags */
+#define SCSW_CSTAT_PCINT 0x80
+#define SCSW_CSTAT_BADLEN 0x40
+#define SCSW_CSTAT_PROGCHK 0x20
+#define SCSW_CSTAT_PROTCHK 0x10
+#define SCSW_CSTAT_CHDCHK 0x08
+#define SCSW_CSTAT_CHCCHK 0x04
+#define SCSW_CSTAT_ICCHK 0x02
+#define SCSW_CSTAT_CHAINCHK 0x01
/*
* subchannel information block
*/
-struct schib {
+typedef struct schib {
struct pmcw pmcw; /* path management control word */
struct scsw scsw; /* subchannel status word */
__u64 mba; /* measurement block address */
__u8 mda[4]; /* model dependent area */
-} __attribute__ ((packed,aligned(4)));
-
-struct subchannel_id {
- __u32 cssid : 8;
- __u32 : 4;
- __u32 m : 1;
- __u32 ssid : 2;
- __u32 one : 1;
- __u32 sch_no : 16;
-} __attribute__ ((packed, aligned(4)));
+} __attribute__ ((packed, aligned(4))) Schib;
+
+typedef struct subchannel_id {
+ __u32 cssid:8;
+ __u32:4;
+ __u32 m:1;
+ __u32 ssid:2;
+ __u32 one:1;
+ __u32 sch_no:16;
+} __attribute__ ((packed, aligned(4))) SubChannelId;
struct chsc_header {
__u16 length;
__u16 code;
} __attribute__((packed));
-struct chsc_area_sda {
+typedef struct chsc_area_sda {
struct chsc_header request;
__u8 reserved1:4;
__u8 format:4;
@@ -111,29 +148,49 @@ struct chsc_area_sda {
__u32 reserved5:4;
__u32 format2:4;
__u32 reserved6:24;
-} __attribute__((packed));
+} __attribute__((packed)) ChscAreaSda;
/*
* TPI info structure
*/
struct tpi_info {
struct subchannel_id schid;
- __u32 intparm; /* interruption parameter */
- __u32 adapter_IO : 1;
- __u32 reserved2 : 1;
- __u32 isc : 3;
- __u32 reserved3 : 12;
- __u32 int_type : 3;
- __u32 reserved4 : 12;
+ __u32 intparm; /* interruption parameter */
+ __u32 adapter_IO:1;
+ __u32 reserved2:1;
+ __u32 isc:3;
+ __u32 reserved3:12;
+ __u32 int_type:3;
+ __u32 reserved4:12;
} __attribute__ ((packed, aligned(4)));
-/* channel command word (type 1) */
-struct ccw1 {
+/* 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;
__u16 count;
__u32 cda;
-} __attribute__ ((packed, aligned(8)));
+} __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
@@ -143,11 +200,14 @@ struct ccw1 {
#define CCW_FLAG_IDA 0x04
#define CCW_FLAG_SUSPEND 0x02
+/* Common CCW commands */
+#define CCW_CMD_READ_IPL 0x02
#define CCW_CMD_NOOP 0x03
#define CCW_CMD_BASIC_SENSE 0x04
#define CCW_CMD_TIC 0x08
#define CCW_CMD_SENSE_ID 0xe4
+/* Virtio CCW commands */
#define CCW_CMD_SET_VQ 0x13
#define CCW_CMD_VDEV_RESET 0x33
#define CCW_CMD_READ_FEAT 0x12
@@ -159,10 +219,16 @@ struct ccw1 {
#define CCW_CMD_SET_CONF_IND 0x53
#define CCW_CMD_READ_VQ_CONF 0x32
+/* DASD CCW commands */
+#define CCW_CMD_DASD_READ 0x06
+#define CCW_CMD_DASD_SEEK 0x07
+#define CCW_CMD_DASD_SEARCH_ID_EQ 0x31
+#define CCW_CMD_DASD_READ_MT 0x86
+
/*
* Command-mode operation request block
*/
-struct cmd_orb {
+typedef struct cmd_orb {
__u32 intparm; /* interruption parameter */
__u32 key:4; /* flags, like key, suspend control, etc. */
__u32 spnd:1; /* suspend control */
@@ -182,7 +248,7 @@ struct cmd_orb {
__u32 zero:6; /* reserved zeros */
__u32 orbx:1; /* ORB extension control */
__u32 cpa; /* channel program address */
-} __attribute__ ((packed, aligned(4)));
+} __attribute__ ((packed, aligned(4))) CmdOrb;
struct ciw {
__u8 type;
@@ -190,10 +256,15 @@ struct ciw {
__u16 count;
};
+#define CU_TYPE_UNKNOWN 0x0000
+#define CU_TYPE_DASD_2107 0x2107
+#define CU_TYPE_VIRTIO 0x3832
+#define CU_TYPE_DASD_3990 0x3990
+
/*
* sense-id response buffer layout
*/
-struct senseid {
+typedef struct senseid {
/* common part */
__u8 reserved; /* always 0x'FF' */
__u16 cu_type; /* control unit type */
@@ -203,15 +274,94 @@ struct senseid {
__u8 unused; /* padding byte */
/* extended part */
struct ciw ciw[62];
-} __attribute__ ((packed, aligned(4)));
+} __attribute__ ((packed, aligned(4))) SenseId;
+
+/*
+ * architected values for first sense byte - common_status. Bits 0-5 of this
+ * field are common to all device types.
+ */
+#define SNS_STAT0_CMD_REJECT 0x80
+#define SNS_STAT0_INTERVENTION_REQ 0x40
+#define SNS_STAT0_BUS_OUT_CHECK 0x20
+#define SNS_STAT0_EQUIPMENT_CHECK 0x10
+#define SNS_STAT0_DATA_CHECK 0x08
+#define SNS_STAT0_OVERRUN 0x04
+#define SNS_STAT0_INCOMPL_DOMAIN 0x01
+
+/* ECKD DASD status[0] byte */
+#define SNS_STAT1_PERM_ERR 0x80
+#define SNS_STAT1_INV_TRACK_FORMAT 0x40
+#define SNS_STAT1_EOC 0x20
+#define SNS_STAT1_MESSAGE_TO_OPER 0x10
+#define SNS_STAT1_NO_REC_FOUND 0x08
+#define SNS_STAT1_FILE_PROTECTED 0x04
+#define SNS_STAT1_WRITE_INHIBITED 0x02
+#define SNS_STAT1_IMPRECISE_END 0x01
+
+/* ECKD DASD status[1] byte */
+#define SNS_STAT2_REQ_INH_WRITE 0x80
+#define SNS_STAT2_CORRECTABLE 0x40
+#define SNS_STAT2_FIRST_LOG_ERR 0x20
+#define SNS_STAT2_ENV_DATA_PRESENT 0x10
+#define SNS_STAT2_IMPRECISE_END 0x04
+
+/* ECKD DASD 24-byte Sense fmt_msg codes */
+#define SENSE24_FMT_PROG_SYS 0x0
+#define SENSE24_FMT_EQUIPMENT 0x2
+#define SENSE24_FMT_CONTROLLER 0x3
+#define SENSE24_FMT_MISC 0xF
+
+/* basic sense response buffer layout */
+typedef struct SenseDataEckdDasd {
+ uint8_t common_status;
+ uint8_t status[2];
+ uint8_t res_count;
+ uint8_t phys_drive_id;
+ uint8_t low_cyl_addr;
+ uint8_t head_high_cyl_addr;
+ uint8_t fmt_msg;
+ uint64_t fmt_dependent_info[2];
+ uint8_t reserved;
+ uint8_t program_action_code;
+ uint16_t config_info;
+ uint8_t mcode_hicyl;
+ uint8_t cyl_head_addr[3];
+} __attribute__ ((packed, aligned(4))) SenseDataEckdDasd;
+
+#define ECKD_SENSE24_GET_FMT(sd) (sd->fmt_msg & 0xF0 >> 4)
+#define ECKD_SENSE24_GET_MSG(sd) (sd->fmt_msg & 0x0F)
+
+#define unit_check(irb) ((irb)->scsw.dstat & SCSW_DSTAT_UCHK)
+#define iface_ctrl_check(irb) ((irb)->scsw.cstat & SCSW_CSTAT_ICCHK)
/* interruption response block */
-struct irb {
+typedef struct irb {
struct scsw scsw;
__u32 esw[5];
__u32 ecw[8];
__u32 emw[8];
-} __attribute__ ((packed, aligned(4)));
+} __attribute__ ((packed, aligned(4))) Irb;
+
+/* Used for SEEK ccw commands */
+typedef struct CcwSeekData {
+ uint16_t reserved;
+ uint16_t cyl;
+ uint16_t head;
+} __attribute__((packed)) CcwSeekData;
+
+/* Used for SEARCH ID ccw commands */
+typedef struct CcwSearchIdData {
+ uint16_t cyl;
+ uint16_t head;
+ uint8_t record;
+} __attribute__((packed)) CcwSearchIdData;
+
+int enable_mss_facility(void);
+void enable_subchannel(SubChannelId schid);
+uint16_t cu_type(SubChannelId schid);
+int basic_sense(SubChannelId schid, uint16_t cutype, void *sense_data,
+ uint16_t data_size);
+int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt);
/*
* Some S390 specific IO instructions as inline
diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c
new file mode 100644
index 0000000000..0fc879bb8e
--- /dev/null
+++ b/pc-bios/s390-ccw/dasd-ipl.c
@@ -0,0 +1,235 @@
+/*
+ * S390 IPL (boot) from a real DASD device via vfio framework.
+ *
+ * Copyright (c) 2019 Jason J. Herne <jjherne@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#include "libc.h"
+#include "s390-ccw.h"
+#include "s390-arch.h"
+#include "dasd-ipl.h"
+#include "helper.h"
+
+static char prefix_page[PAGE_SIZE * 2]
+ __attribute__((__aligned__(PAGE_SIZE * 2)));
+
+static void enable_prefixing(void)
+{
+ memcpy(&prefix_page, lowcore, 4096);
+ set_prefix(ptr2u32(&prefix_page));
+}
+
+static void disable_prefixing(void)
+{
+ set_prefix(0);
+ /* Copy io interrupt info back to low core */
+ memcpy((void *)&lowcore->subchannel_id, prefix_page + 0xB8, 12);
+}
+
+static bool is_read_tic_ccw_chain(Ccw0 *ccw)
+{
+ Ccw0 *next_ccw = ccw + 1;
+
+ return ((ccw->cmd_code == CCW_CMD_DASD_READ ||
+ ccw->cmd_code == CCW_CMD_DASD_READ_MT) &&
+ ccw->chain && next_ccw->cmd_code == CCW_CMD_TIC);
+}
+
+static bool dynamic_cp_fixup(uint32_t ccw_addr, uint32_t *next_cpa)
+{
+ Ccw0 *cur_ccw = (Ccw0 *)(uint64_t)ccw_addr;
+ Ccw0 *tic_ccw;
+
+ while (true) {
+ /* Skip over inline TIC (it might not have the chain bit on) */
+ if (cur_ccw->cmd_code == CCW_CMD_TIC &&
+ cur_ccw->cda == ptr2u32(cur_ccw) - 8) {
+ cur_ccw += 1;
+ continue;
+ }
+
+ if (!cur_ccw->chain) {
+ break;
+ }
+ if (is_read_tic_ccw_chain(cur_ccw)) {
+ /*
+ * Breaking a chain of CCWs may alter the semantics or even the
+ * validity of a channel program. The heuristic implemented below
+ * seems to work well in practice for the channel programs
+ * generated by zipl.
+ */
+ tic_ccw = cur_ccw + 1;
+ *next_cpa = tic_ccw->cda;
+ cur_ccw->chain = 0;
+ return true;
+ }
+ cur_ccw += 1;
+ }
+ return false;
+}
+
+static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype,
+ uint32_t cpa)
+{
+ bool has_next;
+ uint32_t next_cpa = 0;
+ int rc;
+
+ do {
+ has_next = dynamic_cp_fixup(cpa, &next_cpa);
+
+ print_int("executing ccw chain at ", cpa);
+ enable_prefixing();
+ rc = do_cio(schid, cutype, cpa, CCW_FMT0);
+ disable_prefixing();
+
+ if (rc) {
+ break;
+ }
+ cpa = next_cpa;
+ } while (has_next);
+
+ return rc;
+}
+
+static void make_readipl(void)
+{
+ Ccw0 *ccwIplRead = (Ccw0 *)0x00;
+
+ /* Create Read IPL ccw at address 0 */
+ ccwIplRead->cmd_code = CCW_CMD_READ_IPL;
+ ccwIplRead->cda = 0x00; /* Read into address 0x00 in main memory */
+ ccwIplRead->chain = 0; /* Chain flag */
+ ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
+}
+
+static void run_readipl(SubChannelId schid, uint16_t cutype)
+{
+ if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
+ panic("dasd-ipl: Failed to run Read IPL channel program\n");
+ }
+}
+
+/*
+ * The architecture states that IPL1 data should consist of a psw followed by
+ * format-0 READ and TIC CCWs. Let's sanity check.
+ */
+static void check_ipl1(void)
+{
+ Ccw0 *ccwread = (Ccw0 *)0x08;
+ Ccw0 *ccwtic = (Ccw0 *)0x10;
+
+ if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
+ ccwtic->cmd_code != CCW_CMD_TIC) {
+ panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
+ }
+}
+
+static void check_ipl2(uint32_t ipl2_addr)
+{
+ Ccw0 *ccw = u32toptr(ipl2_addr);
+
+ if (ipl2_addr == 0x00) {
+ panic("IPL2 address invalid. Is this disk really bootable?\n");
+ }
+ if (ccw->cmd_code == 0x00) {
+ panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
+ }
+}
+
+static uint32_t read_ipl2_addr(void)
+{
+ Ccw0 *ccwtic = (Ccw0 *)0x10;
+
+ return ccwtic->cda;
+}
+
+static void ipl1_fixup(void)
+{
+ Ccw0 *ccwSeek = (Ccw0 *) 0x08;
+ Ccw0 *ccwSearchID = (Ccw0 *) 0x10;
+ Ccw0 *ccwSearchTic = (Ccw0 *) 0x18;
+ Ccw0 *ccwRead = (Ccw0 *) 0x20;
+ CcwSeekData *seekData = (CcwSeekData *) 0x30;
+ CcwSearchIdData *searchData = (CcwSearchIdData *) 0x38;
+
+ /* move IPL1 CCWs to make room for CCWs needed to locate record 2 */
+ memcpy(ccwRead, (void *)0x08, 16);
+
+ /* Disable chaining so we don't TIC to IPL2 channel program */
+ ccwRead->chain = 0x00;
+
+ ccwSeek->cmd_code = CCW_CMD_DASD_SEEK;
+ ccwSeek->cda = ptr2u32(seekData);
+ ccwSeek->chain = 1;
+ ccwSeek->count = sizeof(*seekData);
+ seekData->reserved = 0x00;
+ seekData->cyl = 0x00;
+ seekData->head = 0x00;
+
+ ccwSearchID->cmd_code = CCW_CMD_DASD_SEARCH_ID_EQ;
+ ccwSearchID->cda = ptr2u32(searchData);
+ ccwSearchID->chain = 1;
+ ccwSearchID->count = sizeof(*searchData);
+ searchData->cyl = 0;
+ searchData->head = 0;
+ searchData->record = 2;
+
+ /* Go back to Search CCW if correct record not yet found */
+ ccwSearchTic->cmd_code = CCW_CMD_TIC;
+ ccwSearchTic->cda = ptr2u32(ccwSearchID);
+}
+
+static void run_ipl1(SubChannelId schid, uint16_t cutype)
+ {
+ uint32_t startAddr = 0x08;
+
+ if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
+ panic("dasd-ipl: Failed to run IPL1 channel program\n");
+ }
+}
+
+static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
+{
+ if (run_dynamic_ccw_program(schid, cutype, addr)) {
+ panic("dasd-ipl: Failed to run IPL2 channel program\n");
+ }
+}
+
+/*
+ * Limitations in vfio-ccw support complicate the IPL process. Details can
+ * be found in docs/devel/s390-dasd-ipl.txt
+ */
+void dasd_ipl(SubChannelId schid, uint16_t cutype)
+{
+ PSWLegacy *pswl = (PSWLegacy *) 0x00;
+ uint32_t ipl2_addr;
+
+ /* Construct Read IPL CCW and run it to read IPL1 from boot disk */
+ make_readipl();
+ run_readipl(schid, cutype);
+ ipl2_addr = read_ipl2_addr();
+ check_ipl1();
+
+ /*
+ * Fixup IPL1 channel program to account for vfio-ccw limitations, then run
+ * it to read IPL2 channel program from boot disk.
+ */
+ ipl1_fixup();
+ run_ipl1(schid, cutype);
+ check_ipl2(ipl2_addr);
+
+ /*
+ * Run IPL2 channel program to read operating system code from boot disk
+ */
+ run_ipl2(schid, cutype, ipl2_addr);
+
+ /* Transfer control to the guest operating system */
+ pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */
+ pswl->addr |= PSW_MASK_BAMODE; /* ... */
+ jump_to_low_kernel();
+}
diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h
new file mode 100644
index 0000000000..c394828906
--- /dev/null
+++ b/pc-bios/s390-ccw/dasd-ipl.h
@@ -0,0 +1,16 @@
+/*
+ * S390 IPL (boot) from a real DASD device via vfio framework.
+ *
+ * Copyright (c) 2019 Jason J. Herne <jjherne@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef DASD_IPL_H
+#define DASD_IPL_H
+
+void dasd_ipl(SubChannelId schid, uint16_t cutype);
+
+#endif /* DASD_IPL_H */
diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h
new file mode 100644
index 0000000000..78d5bc7442
--- /dev/null
+++ b/pc-bios/s390-ccw/helper.h
@@ -0,0 +1,31 @@
+/*
+ * Helper Functions
+ *
+ * Copyright (c) 2019 IBM Corp.
+ *
+ * Author(s): Jason J. Herne <jjherne@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef S390_CCW_HELPER_H
+#define S390_CCW_HELPER_H
+
+#include "s390-ccw.h"
+
+/* Avoids compiler warnings when casting a pointer to a u32 */
+static inline uint32_t ptr2u32(void *ptr)
+{
+ IPL_assert((uint64_t)ptr <= 0xffffffff, "ptr2u32: ptr too large");
+ return (uint32_t)(uint64_t)ptr;
+}
+
+/* Avoids compiler warnings when casting a u32 to a pointer */
+static inline void *u32toptr(uint32_t n)
+{
+ return (void *)(uint64_t)n;
+}
+
+#endif
diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h
index 818517ff5d..bcdc45732d 100644
--- a/pc-bios/s390-ccw/libc.h
+++ b/pc-bios/s390-ccw/libc.h
@@ -67,6 +67,17 @@ static inline size_t strlen(const char *str)
return i;
}
+static inline char *strcat(char *dest, const char *src)
+{
+ int i;
+ char *dest_end = dest + strlen(dest);
+
+ for (i = 0; i <= strlen(src); i++) {
+ dest_end[i] = src[i];
+ }
+ return dest;
+}
+
static inline int isdigit(int c)
{
return (c >= '0') && (c <= '9');
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 544851d672..a69c73349e 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -9,21 +9,27 @@
*/
#include "libc.h"
+#include "s390-arch.h"
#include "s390-ccw.h"
+#include "cio.h"
#include "virtio.h"
+#include "dasd-ipl.h"
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
static SubChannelId blk_schid = { .one = 1 };
-IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
static char loadparm_str[LOADPARM_LEN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
QemuIplParameters qipl;
+IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
+static bool have_iplb;
+static uint16_t cutype;
+LowCore const *lowcore; /* Yes, this *is* a pointer to address 0 */
#define LOADPARM_PROMPT "PROMPT "
#define LOADPARM_EMPTY " "
#define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL)
/*
- * Priniciples of Operations (SA22-7832-09) chapter 17 requires that
+ * Principles of Operations (SA22-7832-09) chapter 17 requires that
* a subsystem-identification is at 184-187 and bytes 188-191 are zero
* after list-directed-IPL and ccw-IPL.
*/
@@ -48,29 +54,64 @@ unsigned int get_loadparm_index(void)
return atoui(loadparm_str);
}
-static bool find_dev(Schib *schib, int dev_no)
+/*
+ * Find the subchannel connected to the given device (dev_no) and fill in the
+ * subchannel information block (schib) with the connected subchannel's info.
+ * NOTE: The global variable blk_schid is updated to contain the subchannel
+ * information.
+ *
+ * If the caller gives dev_no=-1 then the user did not specify a boot device.
+ * In this case we'll just use the first potentially bootable device we find.
+ */
+static bool find_subch(int dev_no)
{
+ Schib schib;
int i, r;
+ bool is_virtio;
for (i = 0; i < 0x10000; i++) {
blk_schid.sch_no = i;
- r = stsch_err(blk_schid, schib);
+ r = stsch_err(blk_schid, &schib);
if ((r == 3) || (r == -EIO)) {
break;
}
- if (!schib->pmcw.dnv) {
- continue;
- }
- if (!virtio_is_supported(blk_schid)) {
+ if (!schib.pmcw.dnv) {
continue;
}
- /* Skip net devices since no IPLB is created and therefore no
- * no network bootloader has been loaded
+
+ enable_subchannel(blk_schid);
+ cutype = cu_type(blk_schid);
+
+ /*
+ * Note: we always have to run virtio_is_supported() here to make
+ * sure that the vdev.senseid data gets pre-initialized correctly
*/
- if (virtio_get_device_type() == VIRTIO_ID_NET && dev_no < 0) {
- continue;
+ is_virtio = virtio_is_supported(blk_schid);
+
+ /* No specific devno given, just return 1st possibly bootable device */
+ if (dev_no < 0) {
+ switch (cutype) {
+ case CU_TYPE_VIRTIO:
+ if (is_virtio) {
+ /*
+ * Skip net devices since no IPLB is created and therefore
+ * no network bootloader has been loaded
+ */
+ if (virtio_get_device_type() != VIRTIO_ID_NET) {
+ return true;
+ }
+ }
+ continue;
+ case CU_TYPE_DASD_3990:
+ case CU_TYPE_DASD_2107:
+ return true;
+ default:
+ continue;
+ }
}
- if ((dev_no < 0) || (schib->pmcw.dev == dev_no)) {
+
+ /* Caller asked for a specific devno */
+ if (schib.pmcw.dev == dev_no) {
return true;
}
}
@@ -99,68 +140,88 @@ static void menu_setup(void)
}
}
-static void virtio_setup(void)
+/*
+ * Initialize the channel I/O subsystem so we can talk to our ipl/boot device.
+ */
+static void css_setup(void)
{
- Schib schib;
- int ssid;
- bool found = false;
- uint16_t dev_no;
- char ldp[] = "LOADPARM=[________]\n";
- VDev *vdev = virtio_get_device();
- QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
-
/*
- * We unconditionally enable mss support. In every sane configuration,
- * this will succeed; and even if it doesn't, stsch_err() can deal
- * with the consequences.
+ * Unconditionally enable mss support. In every sane configuration this
+ * will succeed; and even if it doesn't, stsch_err() can handle it.
*/
enable_mss_facility();
+}
+
+/*
+ * Collect various pieces of information from the hypervisor/hardware that
+ * we'll use to determine exactly how we'll boot.
+ */
+static void boot_setup(void)
+{
+ char lpmsg[] = "LOADPARM=[________]\n";
sclp_get_loadparm_ascii(loadparm_str);
- memcpy(ldp + 10, loadparm_str, LOADPARM_LEN);
- sclp_print(ldp);
+ memcpy(lpmsg + 10, loadparm_str, 8);
+ sclp_print(lpmsg);
- memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
+ have_iplb = store_iplb(&iplb);
+}
- if (store_iplb(&iplb)) {
- switch (iplb.pbt) {
- case S390_IPL_TYPE_CCW:
- dev_no = iplb.ccw.devno;
- debug_print_int("device no. ", dev_no);
- blk_schid.ssid = iplb.ccw.ssid & 0x3;
- debug_print_int("ssid ", blk_schid.ssid);
- found = find_dev(&schib, dev_no);
- break;
- case S390_IPL_TYPE_QEMU_SCSI:
- vdev->scsi_device_selected = true;
- vdev->selected_scsi_device.channel = iplb.scsi.channel;
- vdev->selected_scsi_device.target = iplb.scsi.target;
- vdev->selected_scsi_device.lun = iplb.scsi.lun;
- blk_schid.ssid = iplb.scsi.ssid & 0x3;
- found = find_dev(&schib, iplb.scsi.devno);
- break;
- default:
- panic("List-directed IPL not supported yet!\n");
- }
- menu_setup();
- } else {
+static void find_boot_device(void)
+{
+ VDev *vdev = virtio_get_device();
+ int ssid;
+ bool found;
+
+ if (!have_iplb) {
for (ssid = 0; ssid < 0x3; ssid++) {
blk_schid.ssid = ssid;
- found = find_dev(&schib, -1);
+ found = find_subch(-1);
if (found) {
- break;
+ return;
}
}
+ panic("Could not find a suitable boot device (none specified)\n");
+ }
+
+ switch (iplb.pbt) {
+ case S390_IPL_TYPE_CCW:
+ debug_print_int("device no. ", iplb.ccw.devno);
+ blk_schid.ssid = iplb.ccw.ssid & 0x3;
+ debug_print_int("ssid ", blk_schid.ssid);
+ found = find_subch(iplb.ccw.devno);
+ break;
+ case S390_IPL_TYPE_QEMU_SCSI:
+ vdev->scsi_device_selected = true;
+ vdev->selected_scsi_device.channel = iplb.scsi.channel;
+ vdev->selected_scsi_device.target = iplb.scsi.target;
+ vdev->selected_scsi_device.lun = iplb.scsi.lun;
+ blk_schid.ssid = iplb.scsi.ssid & 0x3;
+ found = find_subch(iplb.scsi.devno);
+ break;
+ default:
+ panic("List-directed IPL not supported yet!\n");
}
- IPL_assert(found, "No virtio device found");
+ IPL_assert(found, "Boot device not found\n");
+}
+
+static void virtio_setup(void)
+{
+ VDev *vdev = virtio_get_device();
+ QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
+
+ memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
+
+ if (have_iplb) {
+ menu_setup();
+ }
if (virtio_get_device_type() == VIRTIO_ID_NET) {
sclp_print("Network boot device detected\n");
vdev->netboot_start_addr = qipl.netboot_start_addr;
} else {
virtio_blk_setup_device(blk_schid);
-
IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected");
}
}
@@ -168,9 +229,24 @@ static void virtio_setup(void)
int main(void)
{
sclp_setup();
- virtio_setup();
-
- zipl_load(); /* no return */
+ css_setup();
+ boot_setup();
+ find_boot_device();
+ enable_subchannel(blk_schid);
+
+ switch (cutype) {
+ case CU_TYPE_DASD_3990:
+ case CU_TYPE_DASD_2107:
+ dasd_ipl(blk_schid, cutype); /* no return */
+ break;
+ case CU_TYPE_VIRTIO:
+ virtio_setup();
+ zipl_load(); /* no return */
+ break;
+ default:
+ print_int("Attempting to boot from unexpected device type", cutype);
+ panic("");
+ }
panic("Failed to load OS from hard disk\n");
return 0; /* make compiler happy */
diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak
index 14e96b2aa6..5eefb7c289 100644
--- a/pc-bios/s390-ccw/netboot.mak
+++ b/pc-bios/s390-ccw/netboot.mak
@@ -1,7 +1,7 @@
SLOF_DIR := $(SRC_PATH)/roms/SLOF
-NETOBJS := start.o sclp.o virtio.o virtio-net.o jump2ipl.o netmain.o \
+NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o \
libnet.a libc.a
LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index 0392131c27..f3542cb2cf 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -33,6 +33,7 @@
#include <pxelinux.h>
#include "s390-ccw.h"
+#include "cio.h"
#include "virtio.h"
#define DEFAULT_BOOT_RETRIES 10
@@ -475,6 +476,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
if (!schib->pmcw.dnv) {
continue;
}
+ enable_subchannel(net_schid);
if (!virtio_is_supported(net_schid)) {
continue;
}
diff --git a/pc-bios/s390-ccw/s390-arch.h b/pc-bios/s390-ccw/s390-arch.h
new file mode 100644
index 0000000000..504fc7c2f0
--- /dev/null
+++ b/pc-bios/s390-ccw/s390-arch.h
@@ -0,0 +1,103 @@
+/*
+ * S390 Basic Architecture
+ *
+ * Copyright (c) 2019 Jason J. Herne <jjherne@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef S390_ARCH_H
+#define S390_ARCH_H
+
+typedef struct PSW {
+ uint64_t mask;
+ uint64_t addr;
+} __attribute__ ((aligned(8))) PSW;
+_Static_assert(sizeof(struct PSW) == 16, "PSW size incorrect");
+
+/* Older PSW format used by LPSW instruction */
+typedef struct PSWLegacy {
+ uint32_t mask;
+ uint32_t addr;
+} __attribute__ ((aligned(8))) PSWLegacy;
+_Static_assert(sizeof(struct PSWLegacy) == 8, "PSWLegacy size incorrect");
+
+/* s390 psw bit masks */
+#define PSW_MASK_IOINT 0x0200000000000000ULL
+#define PSW_MASK_WAIT 0x0002000000000000ULL
+#define PSW_MASK_EAMODE 0x0000000100000000ULL
+#define PSW_MASK_BAMODE 0x0000000080000000ULL
+#define PSW_MASK_ZMODE (PSW_MASK_EAMODE | PSW_MASK_BAMODE)
+
+/* Low core mapping */
+typedef struct LowCore {
+ /* prefix area: defined by architecture */
+ PSWLegacy ipl_psw; /* 0x000 */
+ uint32_t ccw1[2]; /* 0x008 */
+ uint32_t ccw2[2]; /* 0x010 */
+ uint8_t pad1[0x80 - 0x18]; /* 0x018 */
+ uint32_t ext_params; /* 0x080 */
+ uint16_t cpu_addr; /* 0x084 */
+ uint16_t ext_int_code; /* 0x086 */
+ uint16_t svc_ilen; /* 0x088 */
+ uint16_t svc_code; /* 0x08a */
+ uint16_t pgm_ilen; /* 0x08c */
+ uint16_t pgm_code; /* 0x08e */
+ uint32_t data_exc_code; /* 0x090 */
+ uint16_t mon_class_num; /* 0x094 */
+ uint16_t per_perc_atmid; /* 0x096 */
+ uint64_t per_address; /* 0x098 */
+ uint8_t exc_access_id; /* 0x0a0 */
+ uint8_t per_access_id; /* 0x0a1 */
+ uint8_t op_access_id; /* 0x0a2 */
+ uint8_t ar_access_id; /* 0x0a3 */
+ uint8_t pad2[0xA8 - 0xA4]; /* 0x0a4 */
+ uint64_t trans_exc_code; /* 0x0a8 */
+ uint64_t monitor_code; /* 0x0b0 */
+ uint16_t subchannel_id; /* 0x0b8 */
+ uint16_t subchannel_nr; /* 0x0ba */
+ uint32_t io_int_parm; /* 0x0bc */
+ uint32_t io_int_word; /* 0x0c0 */
+ uint8_t pad3[0xc8 - 0xc4]; /* 0x0c4 */
+ uint32_t stfl_fac_list; /* 0x0c8 */
+ uint8_t pad4[0xe8 - 0xcc]; /* 0x0cc */
+ uint64_t mcic; /* 0x0e8 */
+ uint8_t pad5[0xf4 - 0xf0]; /* 0x0f0 */
+ uint32_t external_damage_code; /* 0x0f4 */
+ uint64_t failing_storage_address; /* 0x0f8 */
+ uint8_t pad6[0x110 - 0x100]; /* 0x100 */
+ uint64_t per_breaking_event_addr; /* 0x110 */
+ uint8_t pad7[0x120 - 0x118]; /* 0x118 */
+ PSW restart_old_psw; /* 0x120 */
+ PSW external_old_psw; /* 0x130 */
+ PSW svc_old_psw; /* 0x140 */
+ PSW program_old_psw; /* 0x150 */
+ PSW mcck_old_psw; /* 0x160 */
+ PSW io_old_psw; /* 0x170 */
+ uint8_t pad8[0x1a0 - 0x180]; /* 0x180 */
+ PSW restart_new_psw; /* 0x1a0 */
+ PSW external_new_psw; /* 0x1b0 */
+ PSW svc_new_psw; /* 0x1c0 */
+ PSW program_new_psw; /* 0x1d0 */
+ PSW mcck_new_psw; /* 0x1e0 */
+ PSW io_new_psw; /* 0x1f0 */
+} __attribute__((packed, aligned(8192))) LowCore;
+
+extern LowCore const *lowcore;
+
+static inline void set_prefix(uint32_t address)
+{
+ asm volatile("spx %0" : : "m" (address) : "memory");
+}
+
+static inline uint32_t store_prefix(void)
+{
+ uint32_t address;
+
+ asm volatile("stpx %0" : "=m" (address));
+ return address;
+}
+
+#endif
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 9828aa233d..11bce7d73c 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -49,17 +49,10 @@ typedef unsigned long long __u64;
#include "cio.h"
#include "iplb.h"
-typedef struct irb Irb;
-typedef struct ccw1 Ccw1;
-typedef struct cmd_orb CmdOrb;
-typedef struct schib Schib;
-typedef struct chsc_area_sda ChscAreaSda;
-typedef struct senseid SenseId;
-typedef struct subchannel_id SubChannelId;
-
/* start.s */
void disabled_wait(void);
void consume_sclp_int(void);
+void consume_io_int(void);
/* main.c */
void panic(const char *string);
@@ -80,7 +73,6 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
bool virtio_is_supported(SubChannelId schid);
void virtio_blk_setup_device(SubChannelId schid);
int virtio_read(ulong sector, void *load_addr);
-int enable_mss_facility(void);
u64 get_clock(void);
ulong get_second(void);
diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S
index 5c22cb0849..aa8fceb19d 100644
--- a/pc-bios/s390-ccw/start.S
+++ b/pc-bios/s390-ccw/start.S
@@ -71,6 +71,26 @@ consume_sclp_int:
larl %r1, enabled_wait_psw
lpswe 0(%r1)
+/*
+ * void consume_io_int(void)
+ *
+ * eats one I/O interrupt
+ */
+ .globl consume_io_int
+consume_io_int:
+ /* enable I/O interrupts in cr6 */
+ stctg %c6,%c6,0(%r15)
+ oi 4(%r15), 0xff
+ lctlg %c6,%c6,0(%r15)
+ /* prepare i/o call handler */
+ larl %r1, io_new_code
+ stg %r1, 0x1f8
+ larl %r1, io_new_mask
+ mvc 0x1f0(8),0(%r1)
+ /* load enabled wait PSW */
+ larl %r1, enabled_wait_psw
+ lpswe 0(%r1)
+
external_new_code:
/* disable service interrupts in cr0 */
stctg %c0,%c0,0(%r15)
@@ -78,6 +98,13 @@ external_new_code:
lctlg %c0,%c0,0(%r15)
br %r14
+io_new_code:
+ /* disable I/O interrupts in cr6 */
+ stctg %c6,%c6,0(%r15)
+ ni 4(%r15), 0x00
+ lctlg %c6,%c6,0(%r15)
+ br %r14
+
.align 8
disabled_wait_psw:
.quad 0x0002000180000000,0x0000000000000000
@@ -85,3 +112,5 @@ enabled_wait_psw:
.quad 0x0302000180000000,0x0000000000000000
external_new_mask:
.quad 0x0000000180000000
+io_new_mask:
+ .quad 0x0000000180000000
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index cdb66f459e..fb40ca9828 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -10,9 +10,11 @@
#include "libc.h"
#include "s390-ccw.h"
+#include "cio.h"
#include "virtio.h"
#include "virtio-scsi.h"
#include "bswap.h"
+#include "helper.h"
#define VRING_WAIT_REPLY_TIMEOUT 30
@@ -20,8 +22,6 @@ static VRing block[VIRTIO_MAX_VQS];
static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
__attribute__((__aligned__(PAGE_SIZE)));
-static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
-
static VDev vdev = {
.nr_vqs = 1,
.vrings = block,
@@ -90,38 +90,19 @@ int drain_irqs(SubChannelId schid)
}
}
-static int run_ccw(VDev *vdev, int cmd, void *ptr, int len)
+static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
{
Ccw1 ccw = {};
- CmdOrb orb = {};
- Schib schib;
- int r;
-
- /* start command processing */
- stsch_err(vdev->schid, &schib);
- /* enable the subchannel for IPL device */
- schib.pmcw.ena = 1;
- msch(vdev->schid, &schib);
-
- /* start subchannel command */
- orb.fmt = 1;
- orb.cpa = (u32)(long)&ccw;
- orb.lpm = 0x80;
ccw.cmd_code = cmd;
ccw.cda = (long)ptr;
ccw.count = len;
- r = ssch(vdev->schid, &orb);
- /*
- * XXX Wait until device is done processing the CCW. For now we can
- * assume that a simple tsch will have finished the CCW processing,
- * but the architecture allows for asynchronous operation
- */
- if (!r) {
- r = drain_irqs(vdev->schid);
+ if (sli) {
+ ccw.flags |= CCW_FLAG_SLI;
}
- return r;
+
+ return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
}
static void vring_init(VRing *vr, VqInfo *info)
@@ -263,7 +244,7 @@ void virtio_setup_ccw(VDev *vdev)
vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
- run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0);
+ run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_NET:
@@ -284,18 +265,19 @@ void virtio_setup_ccw(VDev *vdev)
default:
panic("Unsupported virtio device\n");
}
- IPL_assert(run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size) == 0,
- "Could not get block device configuration");
+ IPL_assert(
+ run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false) == 0,
+ "Could not get block device configuration");
/* Feature negotiation */
for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
feats.features = 0;
feats.index = i;
- rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats));
+ rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false);
IPL_assert(rc == 0, "Could not get features bits");
vdev->guest_features[i] &= bswap32(feats.features);
feats.features = bswap32(vdev->guest_features[i]);
- rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats));
+ rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false);
IPL_assert(rc == 0, "Could not set features bits");
}
@@ -312,16 +294,17 @@ void virtio_setup_ccw(VDev *vdev)
};
IPL_assert(
- run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config)) == 0,
+ run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false) == 0,
"Could not get block device VQ configuration");
info.num = config.num;
vring_init(&vdev->vrings[i], &info);
vdev->vrings[i].schid = vdev->schid;
- IPL_assert(run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info)) == 0,
- "Cannot set VQ info");
+ IPL_assert(
+ run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0,
+ "Cannot set VQ info");
}
IPL_assert(
- run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status)) == 0,
+ run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false) == 0,
"Could not write status to host");
}
@@ -329,8 +312,15 @@ bool virtio_is_supported(SubChannelId schid)
{
vdev.schid = schid;
memset(&vdev.senseid, 0, sizeof(vdev.senseid));
- /* run sense id command */
- if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid))) {
+
+ /*
+ * Run sense id command.
+ * The size of the senseid data differs between devices (notably,
+ * between virtio devices and dasds), so specify the largest possible
+ * size and suppress the incorrect length indication for smaller sizes.
+ */
+ if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
+ true)) {
return false;
}
if (vdev.senseid.cu_type == 0x3832) {
@@ -343,20 +333,3 @@ bool virtio_is_supported(SubChannelId schid)
}
return false;
}
-
-int enable_mss_facility(void)
-{
- int ret;
- ChscAreaSda *sda_area = (ChscAreaSda *) chsc_page;
-
- memset(sda_area, 0, PAGE_SIZE);
- sda_area->request.length = 0x0400;
- sda_area->request.code = 0x0031;
- sda_area->operation_code = 0x2;
-
- ret = chsc(sda_area);
- if ((ret == 0) && (sda_area->response.code == 0x0001)) {
- return 0;
- }
- return -EIO;
-}
diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img
index 2c6886efb8..aa90fbccb1 100644
--- a/pc-bios/s390-netboot.img
+++ b/pc-bios/s390-netboot.img
Binary files differ