- properly detect page size of initial memory
- support for IPL (boot) from ECKD DASD passed through via vfio-ccw -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEEw9DWbcNiT/aowBjO3s9rk8bwL68FAlzBsfYSHGNvaHVja0By ZWRoYXQuY29tAAoJEN7Pa5PG8C+vkEsP/j1raVsK0Sq3UYg0GuVRxEO+H2k3lcD4 5SouI7cBC0OKglvRcn1fpZlLjw7Q/djC4PRu0hPZUBjGRdEB5FEnOgDShosMPU// KG15gLRDcyQKj6lIAr4laKAfRB9ujDFHrs23ZIHcXlBHEX4elhit7zndewThefuP xiMbiTDEOa4+WEKqf1oPzfWnPT0ss+xDdvVU6sKsYSpCDqjIhLIcYEVfc0cwZlP/ gvqgVolrwNkILkkyp5tENn2nCddmW4Dro+OINWuaMjAM20/0U9ewXwN4c1aSi9K3 WxeJ/tHPDeza65Jqbupcqb8K4LjWTV0qpOz8yBDOFdEL6cxK/epbhHYSkNYpwAbK Kf4yzpW5h/+i0XR/38L6VhJXneAj7IapLV4TAXE4NHxTnmMyrLON53BRTqlXpPk1 8t+LiJWEC6a/FXlF6KgKq56klhDO8/hVPOINmr7gW3oKmbUn+Lh4eqwFY80L0CyV pA3Xzi3XLfvLqLgTtULNHumc5kpizGcXf30qALsDB8Bf002+GUndl/KPjoyUdsA1 uJ87EX7nziqu95HSe57d4U5Ukh9XcwYPGhmVx+A9alTtn1/y6Gw09viSirzSgVZ4 mYq5/mqNg30ouaMK9VvEZ9PaHR+Y+6rIe+fDf5q5+LmU2hHBWMpUL/PX+Nm5qTDI 5R+Qvxv9bA8M =8saZ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20190425' into staging - properly detect page size of initial memory - support for IPL (boot) from ECKD DASD passed through via vfio-ccw # gpg: Signature made Thu 25 Apr 2019 14:11:18 BST # gpg: using RSA key C3D0D66DC3624FF6A8C018CEDECF6B93C6F02FAF # gpg: issuer "cohuck@redhat.com" # gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>" [unknown] # gpg: aka "Cornelia Huck <huckc@linux.vnet.ibm.com>" [full] # gpg: aka "Cornelia Huck <cornelia.huck@de.ibm.com>" [full] # gpg: aka "Cornelia Huck <cohuck@kernel.org>" [unknown] # gpg: aka "Cornelia Huck <cohuck@redhat.com>" [unknown] # Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0 18CE DECF 6B93 C6F0 2FAF * remotes/cohuck/tags/s390x-20190425: exec: Introduce qemu_maxrampagesize() and rename qemu_getrampagesize() s390x/kvm: Configure page size after memory has actually been initialized 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: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
f7526eece2
@ -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
|
||||
|
||||
|
133
docs/devel/s390-dasd-ipl.txt
Normal file
133
docs/devel/s390-dasd-ipl.txt
Normal 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.
|
44
exec.c
44
exec.c
@ -1688,7 +1688,7 @@ void ram_block_dump(Monitor *mon)
|
||||
* when we actually open and map them. Iterate over the file
|
||||
* descriptors instead, and use qemu_fd_getpagesize().
|
||||
*/
|
||||
static int find_max_supported_pagesize(Object *obj, void *opaque)
|
||||
static int find_min_backend_pagesize(Object *obj, void *opaque)
|
||||
{
|
||||
long *hpsize_min = opaque;
|
||||
|
||||
@ -1704,7 +1704,27 @@ static int find_max_supported_pagesize(Object *obj, void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
long qemu_getrampagesize(void)
|
||||
static int find_max_backend_pagesize(Object *obj, void *opaque)
|
||||
{
|
||||
long *hpsize_max = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
long hpsize = host_memory_backend_pagesize(backend);
|
||||
|
||||
if (host_memory_backend_is_mapped(backend) && (hpsize > *hpsize_max)) {
|
||||
*hpsize_max = hpsize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: We assume right now that all mapped host memory backends are
|
||||
* used as RAM, however some might be used for different purposes.
|
||||
*/
|
||||
long qemu_minrampagesize(void)
|
||||
{
|
||||
long hpsize = LONG_MAX;
|
||||
long mainrampagesize;
|
||||
@ -1724,7 +1744,7 @@ long qemu_getrampagesize(void)
|
||||
*/
|
||||
memdev_root = object_resolve_path("/objects", NULL);
|
||||
if (memdev_root) {
|
||||
object_child_foreach(memdev_root, find_max_supported_pagesize, &hpsize);
|
||||
object_child_foreach(memdev_root, find_min_backend_pagesize, &hpsize);
|
||||
}
|
||||
if (hpsize == LONG_MAX) {
|
||||
/* No additional memory regions found ==> Report main RAM page size */
|
||||
@ -1747,8 +1767,24 @@ long qemu_getrampagesize(void)
|
||||
|
||||
return hpsize;
|
||||
}
|
||||
|
||||
long qemu_maxrampagesize(void)
|
||||
{
|
||||
long pagesize = qemu_mempath_getpagesize(mem_path);
|
||||
Object *memdev_root = object_resolve_path("/objects", NULL);
|
||||
|
||||
if (memdev_root) {
|
||||
object_child_foreach(memdev_root, find_max_backend_pagesize,
|
||||
&pagesize);
|
||||
}
|
||||
return pagesize;
|
||||
}
|
||||
#else
|
||||
long qemu_getrampagesize(void)
|
||||
long qemu_minrampagesize(void)
|
||||
{
|
||||
return getpagesize();
|
||||
}
|
||||
long qemu_maxrampagesize(void)
|
||||
{
|
||||
return getpagesize();
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ static void cap_hpt_maxpagesize_apply(SpaprMachineState *spapr,
|
||||
warn_report("Many guests require at least 64kiB hpt-max-page-size");
|
||||
}
|
||||
|
||||
spapr_check_pagesize(spapr, qemu_getrampagesize(), errp);
|
||||
spapr_check_pagesize(spapr, qemu_minrampagesize(), errp);
|
||||
}
|
||||
|
||||
static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift,
|
||||
@ -609,7 +609,7 @@ static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr,
|
||||
uint8_t mps;
|
||||
|
||||
if (kvmppc_hpt_needs_host_contiguous_pages()) {
|
||||
mps = ctz64(qemu_getrampagesize());
|
||||
mps = ctz64(qemu_minrampagesize());
|
||||
} else {
|
||||
mps = 34; /* allow everything up to 16GiB, i.e. everything */
|
||||
}
|
||||
|
@ -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 &&
|
||||
|
@ -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,
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "cpu.h"
|
||||
#include "hw/boards.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "exec/ram_addr.h"
|
||||
#include "hw/s390x/s390-virtio-hcall.h"
|
||||
#include "hw/s390x/sclp.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
@ -163,6 +164,7 @@ static void s390_memory_init(ram_addr_t mem_size)
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
ram_addr_t chunk, offset = 0;
|
||||
unsigned int number = 0;
|
||||
Error *local_err = NULL;
|
||||
gchar *name;
|
||||
|
||||
/* allocate RAM for core */
|
||||
@ -182,6 +184,15 @@ static void s390_memory_init(ram_addr_t mem_size)
|
||||
}
|
||||
g_free(name);
|
||||
|
||||
/*
|
||||
* Configure the maximum page size. As no memory devices were created
|
||||
* yet, this is the page size of initial memory only.
|
||||
*/
|
||||
s390_set_max_pagesize(qemu_maxrampagesize(), &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* Initialize storage key device */
|
||||
s390_skeys_init();
|
||||
/* Initialize storage attributes device */
|
||||
@ -253,6 +264,7 @@ static void ccw_init(MachineState *machine)
|
||||
DeviceState *dev;
|
||||
|
||||
s390_sclp_init();
|
||||
/* init memory + setup max page size. Required for the CPU model */
|
||||
s390_memory_init(machine->ram_size);
|
||||
|
||||
/* init CPUs (incl. CPU model) early so s390_has_feature() works */
|
||||
|
@ -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)
|
||||
|
@ -148,7 +148,7 @@ int vfio_spapr_create_window(VFIOContainer *container,
|
||||
uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr);
|
||||
unsigned entries, bits_total, bits_per_level, max_levels;
|
||||
struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) };
|
||||
long rampagesize = qemu_getrampagesize();
|
||||
long rampagesize = qemu_minrampagesize();
|
||||
|
||||
/*
|
||||
* The host might not support the guest supported IOMMU page size,
|
||||
|
@ -73,7 +73,8 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr,
|
||||
|
||||
bool ramblock_is_pmem(RAMBlock *rb);
|
||||
|
||||
long qemu_getrampagesize(void);
|
||||
long qemu_minrampagesize(void);
|
||||
long qemu_maxrampagesize(void);
|
||||
|
||||
/**
|
||||
* qemu_ram_alloc_from_file,
|
||||
|
@ -27,6 +27,7 @@ typedef struct S390CCWDevice {
|
||||
CcwDevice parent_obj;
|
||||
CssDevId hostid;
|
||||
char *mdevid;
|
||||
int32_t bootindex;
|
||||
} S390CCWDevice;
|
||||
|
||||
typedef struct S390CCWDeviceClass {
|
||||
|
28
include/hw/s390x/vfio-ccw.h
Normal file
28
include/hw/s390x/vfio-ccw.h
Normal 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.
@ -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
423
pc-bios/s390-ccw/cio.c
Normal 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;
|
||||
}
|
@ -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
235
pc-bios/s390-ccw/dasd-ipl.c
Normal 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();
|
||||
}
|
16
pc-bios/s390-ccw/dasd-ipl.h
Normal file
16
pc-bios/s390-ccw/dasd-ipl.h
Normal 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
31
pc-bios/s390-ccw/helper.h
Normal 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
|
@ -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');
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
103
pc-bios/s390-ccw/s390-arch.h
Normal file
103
pc-bios/s390-ccw/s390-arch.h
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
@ -2136,7 +2136,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift)
|
||||
/* Find the largest hardware supported page size that's less than
|
||||
* or equal to the (logical) backing page size of guest RAM */
|
||||
kvm_get_smmu_info(&info, &error_fatal);
|
||||
rampagesize = qemu_getrampagesize();
|
||||
rampagesize = qemu_minrampagesize();
|
||||
best_page_shift = 0;
|
||||
|
||||
for (i = 0; i < KVM_PPC_PAGE_SIZES_MAX_SZ; i++) {
|
||||
|
@ -399,6 +399,13 @@ int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s390_set_max_pagesize(uint64_t pagesize, Error **errp)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
kvm_s390_set_max_pagesize(pagesize, errp);
|
||||
}
|
||||
}
|
||||
|
||||
void s390_cmma_reset(void)
|
||||
{
|
||||
if (kvm_enabled()) {
|
||||
|
@ -734,6 +734,7 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg)
|
||||
/* cpu.c */
|
||||
void s390_crypto_reset(void);
|
||||
int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit);
|
||||
void s390_set_max_pagesize(uint64_t pagesize, Error **errp);
|
||||
void s390_cmma_reset(void);
|
||||
void s390_enable_css_support(S390CPU *cpu);
|
||||
int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id,
|
||||
|
@ -93,6 +93,10 @@ int kvm_s390_set_mem_limit(uint64_t new_limit, uint64_t *hw_limit)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_s390_crypto_reset(void)
|
||||
{
|
||||
}
|
||||
|
@ -283,44 +283,37 @@ void kvm_s390_crypto_reset(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int kvm_s390_configure_mempath_backing(KVMState *s)
|
||||
void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp)
|
||||
{
|
||||
size_t path_psize = qemu_getrampagesize();
|
||||
|
||||
if (path_psize == 4 * KiB) {
|
||||
return 0;
|
||||
if (pagesize == 4 * KiB) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hpage_1m_allowed()) {
|
||||
error_report("This QEMU machine does not support huge page "
|
||||
"mappings");
|
||||
return -EINVAL;
|
||||
error_setg(errp, "This QEMU machine does not support huge page "
|
||||
"mappings");
|
||||
return;
|
||||
}
|
||||
|
||||
if (path_psize != 1 * MiB) {
|
||||
error_report("Memory backing with 2G pages was specified, "
|
||||
"but KVM does not support this memory backing");
|
||||
return -EINVAL;
|
||||
if (pagesize != 1 * MiB) {
|
||||
error_setg(errp, "Memory backing with 2G pages was specified, "
|
||||
"but KVM does not support this memory backing");
|
||||
return;
|
||||
}
|
||||
|
||||
if (kvm_vm_enable_cap(s, KVM_CAP_S390_HPAGE_1M, 0)) {
|
||||
error_report("Memory backing with 1M pages was specified, "
|
||||
"but KVM does not support this memory backing");
|
||||
return -EINVAL;
|
||||
if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_HPAGE_1M, 0)) {
|
||||
error_setg(errp, "Memory backing with 1M pages was specified, "
|
||||
"but KVM does not support this memory backing");
|
||||
return;
|
||||
}
|
||||
|
||||
cap_hpage_1m = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_init(MachineState *ms, KVMState *s)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
|
||||
if (kvm_s390_configure_mempath_backing(s)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mc->default_cpu_type = S390_CPU_TYPE_NAME("host");
|
||||
cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS);
|
||||
cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF);
|
||||
|
@ -36,6 +36,7 @@ int kvm_s390_cmma_active(void);
|
||||
void kvm_s390_cmma_reset(void);
|
||||
void kvm_s390_reset_vcpu(S390CPU *cpu);
|
||||
int kvm_s390_set_mem_limit(uint64_t new_limit, uint64_t *hw_limit);
|
||||
void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp);
|
||||
void kvm_s390_crypto_reset(void);
|
||||
void kvm_s390_restart_interrupt(S390CPU *cpu);
|
||||
void kvm_s390_stop_interrupt(S390CPU *cpu);
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user