Support for booting from a vfio-ccw passthrough dasd device

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJcsHOvAAoJEC7Z13T+cC21IgoP/37yKOLOuagT4an9L573qWPp
 xCS48CJ4rNkpWXP3SHuTe+UHSp20sk+5b6rgM/VkLT2d301WS4gxF/VVu85ZFxGX
 tkqDwnljb87MqwTbH5Yj/U4mmq8tZkNg4CqmWuvJhWv4aFe6T/YtAzUkl7y7YCcT
 QfKqErl363JJKkL7cz+QWopFn5Gl/hy+mvEhbEtexWLIV1UNZ1i2hPWURfkh8FWH
 C1047CAfnC/rEJy0GcwkH4iCem8n4LWkMuf3Zehq+Yx+f2e8FfxMkoOtLJVCoKWj
 qoMidAplGqUxLoamZsbU1wEzwH6YH28X+uNUULsgDIIBuyW35ymbGsGTmKmNm6ED
 zVM1K7badvLeO3PXBxUkviZk7UFjxjXz3xCQMheY47wPoslfX+EN0xUvQ2gW2MvO
 Dhb9oPWsr/PFrMMJ35D2OOFH5kJC8Sj30YiP5lsnRoUBi4ecHCIUSlw6esKuYI+H
 JfDAYzxe6QqoGg5cxSNUXP+vAgU/FQq+nGNGHzHnsIR4Udt/JsAtNwxQ9DCbYR0C
 LA5qxZZTqkDtLPAHynqOzd8m7AoNaBSAgP2qp7Yp8ItXMyemlW2OIYR7yRxuL5bH
 zWO/deGYHp3+j9/Z0quzSUL5G85m0o1xgRJcJe9T2fYjWgsy271WFqaZg1JEvpI6
 zHcXEw71B7WQuAFaFO1+
 =tJvv
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Cornelia Huck 2019-04-25 14:08:17 +02:00
commit 41c3d4269b
24 changed files with 1450 additions and 205 deletions

Binary file not shown.

View file

@ -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

423
pc-bios/s390-ccw/cio.c Normal file
View file

@ -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;
}

View file

@ -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)));
} __attribute__ ((packed, aligned(4))) Schib;
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)));
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

235
pc-bios/s390-ccw/dasd-ipl.c Normal file
View file

@ -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();
}

View file

@ -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 */

31
pc-bios/s390-ccw/helper.h Normal file
View file

@ -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

View file

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

View file

@ -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) {
if (!schib.pmcw.dnv) {
continue;
}
if (!virtio_is_supported(blk_schid)) {
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)
}
}
/*
* Initialize the channel I/O subsystem so we can talk to our ipl/boot device.
*/
static void css_setup(void)
{
/*
* 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(lpmsg + 10, loadparm_str, 8);
sclp_print(lpmsg);
have_iplb = store_iplb(&iplb);
}
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_subch(-1);
if (found) {
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, "Boot device not found\n");
}
static void virtio_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.
*/
enable_mss_facility();
sclp_get_loadparm_ascii(loadparm_str);
memcpy(ldp + 10, loadparm_str, LOADPARM_LEN);
sclp_print(ldp);
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
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");
}
if (have_iplb) {
menu_setup();
} else {
for (ssid = 0; ssid < 0x3; ssid++) {
blk_schid.ssid = ssid;
found = find_dev(&schib, -1);
if (found) {
break;
}
}
}
IPL_assert(found, "No virtio device found");
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();
css_setup();
boot_setup();
find_boot_device();
enable_subchannel(blk_schid);
zipl_load(); /* no return */
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 */

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

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

View file

@ -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

View file

@ -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;
}

Binary file not shown.