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

View File

@ -1181,6 +1181,7 @@ S: Supported
F: hw/s390x/ipl.*
F: pc-bios/s390-ccw/
F: pc-bios/s390-ccw.img
F: docs/devel/s390-dasd-ipl.txt
T: git https://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org
@ -1445,6 +1446,7 @@ S: Supported
F: hw/vfio/ccw.c
F: hw/s390x/s390-ccw.c
F: include/hw/s390x/s390-ccw.h
F: include/hw/s390x/vfio-ccw.h
T: git https://github.com/cohuck/qemu.git s390-next
L: qemu-s390x@nongnu.org

View File

@ -0,0 +1,133 @@
*****************************
***** s390 hardware IPL *****
*****************************
The s390 hardware IPL process consists of the following steps.
1. A READ IPL ccw is constructed in memory location 0x0.
This ccw, by definition, reads the IPL1 record which is located on the disk
at cylinder 0 track 0 record 1. Note that the chain flag is on in this ccw
so when it is complete another ccw will be fetched and executed from memory
location 0x08.
2. Execute the Read IPL ccw at 0x00, thereby reading IPL1 data into 0x00.
IPL1 data is 24 bytes in length and consists of the following pieces of
information: [psw][read ccw][tic ccw]. When the machine executes the Read
IPL ccw it read the 24-bytes of IPL1 to be read into memory starting at
location 0x0. Then the ccw program at 0x08 which consists of a read
ccw and a tic ccw is automatically executed because of the chain flag from
the original READ IPL ccw. The read ccw will read the IPL2 data into memory
and the TIC (Transfer In Channel) will transfer control to the channel
program contained in the IPL2 data. The TIC channel command is the
equivalent of a branch/jump/goto instruction for channel programs.
NOTE: The ccws in IPL1 are defined by the architecture to be format 0.
3. Execute IPL2.
The TIC ccw instruction at the end of the IPL1 channel program will begin
the execution of the IPL2 channel program. IPL2 is stage-2 of the boot
process and will contain a larger channel program than IPL1. The point of
IPL2 is to find and load either the operating system or a small program that
loads the operating system from disk. At the end of this step all or some of
the real operating system is loaded into memory and we are ready to hand
control over to the guest operating system. At this point the guest
operating system is entirely responsible for loading any more data it might
need to function. NOTE: The IPL2 channel program might read data into memory
location 0 thereby overwriting the IPL1 psw and channel program. This is ok
as long as the data placed in location 0 contains a psw whose instruction
address points to the guest operating system code to execute at the end of
the IPL/boot process.
NOTE: The ccws in IPL2 are defined by the architecture to be format 0.
4. Start executing the guest operating system.
The psw that was loaded into memory location 0 as part of the ipl process
should contain the needed flags for the operating system we have loaded. The
psw's instruction address will point to the location in memory where we want
to start executing the operating system. This psw is loaded (via LPSW
instruction) causing control to be passed to the operating system code.
In a non-virtualized environment this process, handled entirely by the hardware,
is kicked off by the user initiating a "Load" procedure from the hardware
management console. This "Load" procedure crafts a special "Read IPL" ccw in
memory location 0x0 that reads IPL1. It then executes this ccw thereby kicking
off the reading of IPL1 data. Since the channel program from IPL1 will be
written immediately after the special "Read IPL" ccw, the IPL1 channel program
will be executed immediately (the special read ccw has the chaining bit turned
on). The TIC at the end of the IPL1 channel program will cause the IPL2 channel
program to be executed automatically. After this sequence completes the "Load"
procedure then loads the psw from 0x0.
**********************************************************
***** How this all pertains to QEMU (and the kernel) *****
**********************************************************
In theory we should merely have to do the following to IPL/boot a guest
operating system from a DASD device:
1. Place a "Read IPL" ccw into memory location 0x0 with chaining bit on.
2. Execute channel program at 0x0.
3. LPSW 0x0.
However, our emulation of the machine's channel program logic within the kernel
is missing one key feature that is required for this process to work:
non-prefetch of ccw data.
When we start a channel program we pass the channel subsystem parameters via an
ORB (Operation Request Block). One of those parameters is a prefetch bit. If the
bit is on then the vfio-ccw kernel driver is allowed to read the entire channel
program from guest memory before it starts executing it. This means that any
channel commands that read additional channel commands will not work as expected
because the newly read commands will only exist in guest memory and NOT within
the kernel's channel subsystem memory. The kernel vfio-ccw driver currently
requires this bit to be on for all channel programs. This is a problem because
the IPL process consists of transferring control from the "Read IPL" ccw
immediately to the IPL1 channel program that was read by "Read IPL".
Not being able to turn off prefetch will also prevent the TIC at the end of the
IPL1 channel program from transferring control to the IPL2 channel program.
Lastly, in some cases (the zipl bootloader for example) the IPL2 program also
transfers control to another channel program segment immediately after reading
it from the disk. So we need to be able to handle this case.
**************************
***** What QEMU does *****
**************************
Since we are forced to live with prefetch we cannot use the very simple IPL
procedure we defined in the preceding section. So we compensate by doing the
following.
1. Place "Read IPL" ccw into memory location 0x0, but turn off chaining bit.
2. Execute "Read IPL" at 0x0.
So now IPL1's psw is at 0x0 and IPL1's channel program is at 0x08.
4. Write a custom channel program that will seek to the IPL2 record and then
execute the READ and TIC ccws from IPL1. Normally the seek is not required
because after reading the IPL1 record the disk is automatically positioned
to read the very next record which will be IPL2. But since we are not reading
both IPL1 and IPL2 as part of the same channel program we must manually set
the position.
5. Grab the target address of the TIC instruction from the IPL1 channel program.
This address is where the IPL2 channel program starts.
Now IPL2 is loaded into memory somewhere, and we know the address.
6. Execute the IPL2 channel program at the address obtained in step #5.
Because this channel program can be dynamic, we must use a special algorithm
that detects a READ immediately followed by a TIC and breaks the ccw chain
by turning off the chain bit in the READ ccw. When control is returned from
the kernel/hardware to the QEMU bios code we immediately issue another start
subchannel to execute the remaining TIC instruction. This causes the entire
channel program (starting from the TIC) and all needed data to be refetched
thereby stepping around the limitation that would otherwise prevent this
channel program from executing properly.
Now the operating system code is loaded somewhere in guest memory and the psw
in memory location 0x0 will point to entry code for the guest operating
system.
7. LPSW 0x0.
LPSW transfers control to the guest operating system and we're done.

View File

@ -19,6 +19,7 @@
#include "hw/loader.h"
#include "hw/boards.h"
#include "hw/s390x/virtio-ccw.h"
#include "hw/s390x/vfio-ccw.h"
#include "hw/s390x/css.h"
#include "hw/s390x/ebcdic.h"
#include "ipl.h"
@ -303,16 +304,36 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
ipl->qipl.boot_menu_timeout = cpu_to_be32(splash_time);
}
static CcwDevice *s390_get_ccw_device(DeviceState *dev_st)
#define CCW_DEVTYPE_NONE 0x00
#define CCW_DEVTYPE_VIRTIO 0x01
#define CCW_DEVTYPE_VIRTIO_NET 0x02
#define CCW_DEVTYPE_SCSI 0x03
#define CCW_DEVTYPE_VFIO 0x04
static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
{
CcwDevice *ccw_dev = NULL;
int tmp_dt = CCW_DEVTYPE_NONE;
if (dev_st) {
VirtIONet *virtio_net_dev = (VirtIONet *)
object_dynamic_cast(OBJECT(dev_st), TYPE_VIRTIO_NET);
VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *)
object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent),
TYPE_VIRTIO_CCW_DEVICE);
VFIOCCWDevice *vfio_ccw_dev = (VFIOCCWDevice *)
object_dynamic_cast(OBJECT(dev_st), TYPE_VFIO_CCW);
if (virtio_ccw_dev) {
ccw_dev = CCW_DEVICE(virtio_ccw_dev);
if (virtio_net_dev) {
tmp_dt = CCW_DEVTYPE_VIRTIO_NET;
} else {
tmp_dt = CCW_DEVTYPE_VIRTIO;
}
} else if (vfio_ccw_dev) {
ccw_dev = CCW_DEVICE(vfio_ccw_dev);
tmp_dt = CCW_DEVTYPE_VFIO;
} else {
SCSIDevice *sd = (SCSIDevice *)
object_dynamic_cast(OBJECT(dev_st),
@ -325,9 +346,13 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st)
ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw),
TYPE_CCW_DEVICE);
tmp_dt = CCW_DEVTYPE_SCSI;
}
}
}
if (devtype) {
*devtype = tmp_dt;
}
return ccw_dev;
}
@ -335,20 +360,22 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl)
{
DeviceState *dev_st;
CcwDevice *ccw_dev = NULL;
SCSIDevice *sd;
int devtype;
dev_st = get_boot_device(0);
if (dev_st) {
ccw_dev = s390_get_ccw_device(dev_st);
ccw_dev = s390_get_ccw_device(dev_st, &devtype);
}
/*
* Currently allow IPL only from CCW devices.
*/
if (ccw_dev) {
SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st),
TYPE_SCSI_DEVICE);
if (sd) {
switch (devtype) {
case CCW_DEVTYPE_SCSI:
sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st),
TYPE_SCSI_DEVICE);
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
ipl->iplb.blk0_len =
cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
@ -358,20 +385,24 @@ static bool s390_gen_initial_iplb(S390IPLState *ipl)
ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
} else {
VirtIONet *vn = (VirtIONet *) object_dynamic_cast(OBJECT(dev_st),
TYPE_VIRTIO_NET);
break;
case CCW_DEVTYPE_VFIO:
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
ipl->iplb.pbt = S390_IPL_TYPE_CCW;
ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
break;
case CCW_DEVTYPE_VIRTIO_NET:
ipl->netboot = true;
/* Fall through to CCW_DEVTYPE_VIRTIO case */
case CCW_DEVTYPE_VIRTIO:
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
ipl->iplb.blk0_len =
cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
ipl->iplb.pbt = S390_IPL_TYPE_CCW;
ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
if (vn) {
ipl->netboot = true;
}
break;
}
if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) {
@ -530,7 +561,7 @@ void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type)
!ipl->netboot &&
ipl->iplb.pbt == S390_IPL_TYPE_CCW &&
is_virtio_scsi_device(&ipl->iplb)) {
CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0));
CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0), NULL);
if (ccw_dev &&
cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno &&

View File

@ -124,6 +124,14 @@ static void s390_ccw_unrealize(S390CCWDevice *cdev, Error **errp)
g_free(cdev->mdevid);
}
static void s390_ccw_instance_init(Object *obj)
{
S390CCWDevice *dev = S390_CCW_DEVICE(obj);
device_add_bootindex_property(obj, &dev->bootindex, "bootindex",
"/disk@0,0", DEVICE(obj), NULL);
}
static void s390_ccw_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@ -137,6 +145,7 @@ static void s390_ccw_class_init(ObjectClass *klass, void *data)
static const TypeInfo s390_ccw_info = {
.name = TYPE_S390_CCW,
.parent = TYPE_CCW_DEVICE,
.instance_init = s390_ccw_instance_init,
.instance_size = sizeof(S390CCWDevice),
.class_size = sizeof(S390CCWDeviceClass),
.class_init = s390_ccw_class_init,

View File

@ -21,12 +21,12 @@
#include "hw/vfio/vfio.h"
#include "hw/vfio/vfio-common.h"
#include "hw/s390x/s390-ccw.h"
#include "hw/s390x/vfio-ccw.h"
#include "hw/s390x/ccw-device.h"
#include "exec/address-spaces.h"
#include "qemu/error-report.h"
#define TYPE_VFIO_CCW "vfio-ccw"
typedef struct VFIOCCWDevice {
struct VFIOCCWDevice {
S390CCWDevice cdev;
VFIODevice vdev;
uint64_t io_region_size;
@ -35,7 +35,7 @@ typedef struct VFIOCCWDevice {
EventNotifier io_notifier;
bool force_orb_pfch;
bool warned_orb_pfch;
} VFIOCCWDevice;
};
static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch,
const char *msg)

View File

@ -27,6 +27,7 @@ typedef struct S390CCWDevice {
CcwDevice parent_obj;
CssDevId hostid;
char *mdevid;
int32_t bootindex;
} S390CCWDevice;
typedef struct S390CCWDeviceClass {

View File

@ -0,0 +1,28 @@
/*
* vfio based subchannel assignment support
*
* Copyright 2017, 2019 IBM Corp.
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
* Pierre Morel <pmorel@linux.vnet.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 HW_VFIO_CCW_H
#define HW_VFIO_CCW_H
#include "hw/vfio/vfio-common.h"
#include "hw/s390x/s390-ccw.h"
#include "hw/s390x/ccw-device.h"
#define TYPE_VFIO_CCW "vfio-ccw"
#define VFIO_CCW(obj) \
OBJECT_CHECK(VFIOCCWDevice, (obj), TYPE_VFIO_CCW)
#define TYPE_VFIO_CCW "vfio-ccw"
typedef struct VFIOCCWDevice VFIOCCWDevice;
#endif

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.

View File

@ -114,7 +114,7 @@ static testdef_t tests[] = {
{ "sparc", "SS-4", "", "MB86904" },
{ "sparc", "SS-600MP", "", "TMS390Z55" },
{ "sparc64", "sun4u", "", "UltraSPARC" },
{ "s390x", "s390-ccw-virtio", "", "virtio device" },
{ "s390x", "s390-ccw-virtio", "", "device" },
{ "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 },
{ "microblaze", "petalogix-s3adsp1800", "", "TT",
sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 },