* Allow multiple boot devices (via bootindex properties) on s390x
* Avoid TEXTREL relocations in the s390-ccw.img firmware -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAmcY9vsRHHRodXRoQHJl ZGhhdC5jb20ACgkQLtnXdP5wLbWnBQ//eM0RPHgp3OCbCy42HYp569RdHvXE69BS 7iO8uu7MGRKIAHJqqmDwFDfhfMtoJkfjq9bQpb+pmIUe50r2NAC9na9+bjIc2bK+ sxHvS5PTZiQcPOiGwAmp3lEheFPWi6sugYVruO+6syGLJdoa39Vnj71pF86lJ6dk HFltOqa0lG3YWzueKXInLQnpRaIvPzjJVEUuNRk5H6ai0woItWvrPK82HMcKXSu5 mwJiGP2dmwvZw3nC8GsNKbxxZNc2gEM6tFFozbsaE/Yfzmg/S4kHexzjSGd7svT7 kex65F9aqiK3x1WrWwuCCU6D8qEkNqzXezNlmedvgcMokkdd8Xwlqvw2Ng3sbF7i 466jEdq0CuAiEhO6AvSYrEDpI8trWxj4EtxcjfqltUVR/SYFcW9hRdx99VwrRCo2 woazKmHz7Hu6uS5+JPtfD+KxalIDXNXevY6uGyaWJp8TahDkUCim/gJ+Zb7Mx1iq Vrx/rCm1oV8v1xrn8NWTGewA8emVjbLI3PuDtPh9FWRpER7ekn+dDUinzeUl2wWK +USqIE2ougo1etRm7FAkyWEv9F/GjGTd8OegIGD6etWBNBq/YVMNwX1gjw4Q0qhM +7KmXLBKkuEBYSo4scSGt6DcasIUL93sc6rnWgiH3S/qMAYyKempKPkoROYQI8yA 0EI+x1fxW1w= =mWW5 -----END PGP SIGNATURE----- Merge tag 'pull-request-2024-10-23' of https://gitlab.com/thuth/qemu into staging * Allow multiple boot devices (via bootindex properties) on s390x * Avoid TEXTREL relocations in the s390-ccw.img firmware # -----BEGIN PGP SIGNATURE----- # # iQJFBAABCAAvFiEEJ7iIR+7gJQEY8+q5LtnXdP5wLbUFAmcY9vsRHHRodXRoQHJl # ZGhhdC5jb20ACgkQLtnXdP5wLbWnBQ//eM0RPHgp3OCbCy42HYp569RdHvXE69BS # 7iO8uu7MGRKIAHJqqmDwFDfhfMtoJkfjq9bQpb+pmIUe50r2NAC9na9+bjIc2bK+ # sxHvS5PTZiQcPOiGwAmp3lEheFPWi6sugYVruO+6syGLJdoa39Vnj71pF86lJ6dk # HFltOqa0lG3YWzueKXInLQnpRaIvPzjJVEUuNRk5H6ai0woItWvrPK82HMcKXSu5 # mwJiGP2dmwvZw3nC8GsNKbxxZNc2gEM6tFFozbsaE/Yfzmg/S4kHexzjSGd7svT7 # kex65F9aqiK3x1WrWwuCCU6D8qEkNqzXezNlmedvgcMokkdd8Xwlqvw2Ng3sbF7i # 466jEdq0CuAiEhO6AvSYrEDpI8trWxj4EtxcjfqltUVR/SYFcW9hRdx99VwrRCo2 # woazKmHz7Hu6uS5+JPtfD+KxalIDXNXevY6uGyaWJp8TahDkUCim/gJ+Zb7Mx1iq # Vrx/rCm1oV8v1xrn8NWTGewA8emVjbLI3PuDtPh9FWRpER7ekn+dDUinzeUl2wWK # +USqIE2ougo1etRm7FAkyWEv9F/GjGTd8OegIGD6etWBNBq/YVMNwX1gjw4Q0qhM # +7KmXLBKkuEBYSo4scSGt6DcasIUL93sc6rnWgiH3S/qMAYyKempKPkoROYQI8yA # 0EI+x1fxW1w= # =mWW5 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 23 Oct 2024 14:15:39 BST # gpg: using RSA key 27B88847EEE0250118F3EAB92ED9D774FE702DB5 # gpg: issuer "thuth@redhat.com" # gpg: Good signature from "Thomas Huth <th.huth@gmx.de>" [full] # gpg: aka "Thomas Huth <thuth@redhat.com>" [full] # gpg: aka "Thomas Huth <huth@tuxfamily.org>" [full] # gpg: aka "Thomas Huth <th.huth@posteo.de>" [unknown] # Primary key fingerprint: 27B8 8847 EEE0 2501 18F3 EAB9 2ED9 D774 FE70 2DB5 * tag 'pull-request-2024-10-23' of https://gitlab.com/thuth/qemu: (23 commits) pc-bios/s390-ccw: Update s390-ccw.img with the full boot order support feature pc-bios/s390-ccw: Introduce `EXTRA_LDFLAGS` pc-bios/s390-ccw: Don't generate TEXTRELs pc-bios/s390-ccw: Clarify alignment is in bytes tests/qtest: Add s390x boot order tests to cdrom-test.c docs/system: Update documentation for s390x IPL pc-bios/s390x: Enable multi-device boot loop s390x: Rebuild IPLB for SCSI device directly from DIAG308 hw/s390x: Build an IPLB for each boot device s390x: Add individual loadparm assignment to CCW device include/hw/s390x: Add include files for common IPL structs pc-bios/s390-ccw: Enable failed IPL to return after error pc-bios/s390-ccw: Remove panics from Netboot IPL path pc-bios/s390-ccw: Remove panics from DASD IPL path pc-bios/s390-ccw: Remove panics from SCSI IPL path pc-bios/s390-ccw: Remove panics from ECKD IPL path pc-bios/s390-ccw: Remove panics from ISO IPL path docs/system/s390x/bootdevices: Update the documentation about network booting pc-bios/s390-ccw: Merge netboot.mak into the main Makefile hw/s390x: Remove the possibility to load the s390-netboot.img binary ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
e17e57e862
@ -49,10 +49,11 @@ Limitations
|
||||
-----------
|
||||
|
||||
Some firmware has limitations on which devices can be considered for
|
||||
booting. For instance, the PC BIOS boot specification allows only one
|
||||
disk to be bootable. If boot from disk fails for some reason, the BIOS
|
||||
booting. For instance, the x86 PC BIOS boot specification allows only one
|
||||
disk to be bootable. If boot from disk fails for some reason, the x86 BIOS
|
||||
won't retry booting from other disk. It can still try to boot from
|
||||
floppy or net, though.
|
||||
floppy or net, though. In the case of s390x BIOS, the BIOS will try up to
|
||||
8 total devices, any number of which may be disks.
|
||||
|
||||
Sometimes, firmware cannot map the device path QEMU wants firmware to
|
||||
boot from to a boot method. It doesn't happen for devices the firmware
|
||||
|
@ -6,9 +6,7 @@ Booting with bootindex parameter
|
||||
|
||||
For classical mainframe guests (i.e. LPAR or z/VM installations), you always
|
||||
have to explicitly specify the disk where you want to boot from (or "IPL" from,
|
||||
in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
|
||||
also be only one boot device according to the architecture specification, thus
|
||||
specifying multiple boot devices is not possible (yet).
|
||||
in s390x-speak -- IPL means "Initial Program Load").
|
||||
|
||||
So for booting an s390x guest in QEMU, you should always mark the
|
||||
device where you want to boot from with the ``bootindex`` property, for
|
||||
@ -17,6 +15,11 @@ example::
|
||||
qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
|
||||
-device virtio-blk,drive=dr1,bootindex=1
|
||||
|
||||
Multiple devices may have a bootindex. The lowest bootindex is assigned to the
|
||||
device to IPL first. If the IPL fails for the first, the device with the second
|
||||
lowest bootindex will be tried and so on until IPL is successful or there are no
|
||||
remaining boot devices to try.
|
||||
|
||||
For booting from a CD-ROM ISO image (which needs to include El-Torito boot
|
||||
information in order to be bootable), it is recommended to specify a ``scsi-cd``
|
||||
device, for example like this::
|
||||
@ -82,23 +85,17 @@ Note that ``0`` can be used to boot the default entry.
|
||||
Booting from a network device
|
||||
-----------------------------
|
||||
|
||||
Beside the normal guest firmware (which is loaded from the file ``s390-ccw.img``
|
||||
in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with
|
||||
a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This
|
||||
firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data
|
||||
directory. In case you want to load it from a different filename instead,
|
||||
you can specify it via the ``-global s390-ipl.netboot_fw=filename``
|
||||
command line option.
|
||||
|
||||
The ``bootindex`` property is especially important for booting via the network.
|
||||
If you don't specify the ``bootindex`` property here, the network bootloader
|
||||
firmware code won't get loaded into the guest memory so that the network boot
|
||||
will fail. For a successful network boot, try something like this::
|
||||
The firmware that ships with QEMU includes a small TFTP network bootloader
|
||||
for virtio-net-ccw devices. The ``bootindex`` property is especially
|
||||
important for booting via the network. If you don't specify the ``bootindex``
|
||||
property here, the network bootloader won't be taken into consideration and
|
||||
the network boot will fail. For a successful network boot, try something
|
||||
like this::
|
||||
|
||||
qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \
|
||||
-device virtio-net-ccw,netdev=n1,bootindex=1
|
||||
|
||||
The network bootloader firmware also has basic support for pxelinux.cfg-style
|
||||
The network bootloader also has basic support for pxelinux.cfg-style
|
||||
configuration files. See the `PXELINUX Configuration page
|
||||
<https://wiki.syslinux.org/wiki/index.php?title=PXELINUX#Configuration>`__
|
||||
for details how to set up the configuration file on your TFTP server.
|
||||
|
@ -13,6 +13,10 @@
|
||||
#include "ccw-device.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/module.h"
|
||||
#include "ipl.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/ctype.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static void ccw_device_refill_ids(CcwDevice *dev)
|
||||
{
|
||||
@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ccw_device_get_loadparm(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
CcwDevice *dev = CCW_DEVICE(obj);
|
||||
char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
|
||||
|
||||
visit_type_str(v, name, &str, errp);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void ccw_device_set_loadparm(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
CcwDevice *dev = CCW_DEVICE(obj);
|
||||
char *val;
|
||||
int index;
|
||||
|
||||
index = object_property_get_int(obj, "bootindex", NULL);
|
||||
|
||||
if (index < 0) {
|
||||
error_setg(errp, "LOADPARM is only valid for boot devices!");
|
||||
}
|
||||
|
||||
if (!visit_type_str(v, name, &val, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
|
||||
}
|
||||
|
||||
static const PropertyInfo ccw_loadparm = {
|
||||
.name = "ccw_loadparm",
|
||||
.description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass"
|
||||
" to the guest loader/kernel",
|
||||
.get = ccw_device_get_loadparm,
|
||||
.set = ccw_device_set_loadparm,
|
||||
};
|
||||
|
||||
static Property ccw_device_properties[] = {
|
||||
DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
|
||||
DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
|
||||
DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
|
||||
DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm,
|
||||
typeof(uint8_t[8])),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
@ -26,6 +26,8 @@ struct CcwDevice {
|
||||
CssDevId dev_id;
|
||||
/* The actual busid of the virtual subchannel. */
|
||||
CssDevId subch_id;
|
||||
/* If set, use this loadparm value when device is boot target */
|
||||
uint8_t loadparm[8];
|
||||
};
|
||||
typedef struct CcwDevice CcwDevice;
|
||||
|
||||
|
296
hw/s390x/ipl.c
296
hw/s390x/ipl.c
@ -34,6 +34,7 @@
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/ctype.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
|
||||
#define KERN_IMAGE_START 0x010000UL
|
||||
@ -45,6 +46,7 @@
|
||||
#define INITRD_PARM_START 0x010408UL
|
||||
#define PARMFILE_START 0x001000UL
|
||||
#define ZIPL_IMAGE_START 0x009000UL
|
||||
#define BIOS_MAX_SIZE 0x300000UL
|
||||
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
|
||||
|
||||
static bool iplb_extended_needed(void *opaque)
|
||||
@ -54,6 +56,13 @@ static bool iplb_extended_needed(void *opaque)
|
||||
return ipl->iplbext_migration;
|
||||
}
|
||||
|
||||
/* Place the IPLB chain immediately before the BIOS in memory */
|
||||
static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
|
||||
{
|
||||
return (bios_addr & TARGET_PAGE_MASK)
|
||||
- (count * sizeof(IplParameterBlock));
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_iplb_extended = {
|
||||
.name = "ipl/iplb_extended",
|
||||
.version_id = 0,
|
||||
@ -144,7 +153,14 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
|
||||
* even if an external kernel has been defined.
|
||||
*/
|
||||
if (!ipl->kernel || ipl->enforce_bios) {
|
||||
uint64_t fwbase = (MIN(ms->ram_size, 0x80000000U) - 0x200000) & ~0xffffUL;
|
||||
uint64_t fwbase;
|
||||
|
||||
if (ms->ram_size < BIOS_MAX_SIZE) {
|
||||
error_setg(errp, "not enough RAM to load the BIOS file");
|
||||
return;
|
||||
}
|
||||
|
||||
fwbase = (MIN(ms->ram_size, 0x80000000U) - BIOS_MAX_SIZE) & ~0xffffUL;
|
||||
|
||||
bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware);
|
||||
if (bios_filename == NULL) {
|
||||
@ -280,7 +296,6 @@ static Property s390_ipl_properties[] = {
|
||||
DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
|
||||
DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
|
||||
DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
|
||||
DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
|
||||
DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
|
||||
DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
|
||||
true),
|
||||
@ -390,174 +405,169 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
|
||||
return ccw_dev;
|
||||
}
|
||||
|
||||
static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
||||
static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
|
||||
{
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
uint16_t count = be16_to_cpu(ipl->qipl.chain_len);
|
||||
uint64_t len = sizeof(IplParameterBlock) * count;
|
||||
uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
|
||||
|
||||
cpu_physical_memory_write(chain_addr, iplb_chain, len);
|
||||
return chain_addr;
|
||||
}
|
||||
|
||||
void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize the loadparm with spaces */
|
||||
memset(loadparm, ' ', LOADPARM_LEN);
|
||||
for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
|
||||
uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
|
||||
|
||||
if (qemu_isalnum(c) || c == '.' || c == ' ') {
|
||||
loadparm[i] = c;
|
||||
} else {
|
||||
error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
|
||||
c, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize the loadparm with EBCDIC spaces (0x40) */
|
||||
memset(ebcdic_lp, '@', LOADPARM_LEN);
|
||||
for (i = 0; i < LOADPARM_LEN && ascii_lp[i]; i++) {
|
||||
ebcdic_lp[i] = ascii2ebcdic[(uint8_t) ascii_lp[i]];
|
||||
}
|
||||
}
|
||||
|
||||
static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
|
||||
{
|
||||
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, &devtype);
|
||||
}
|
||||
uint8_t *lp;
|
||||
|
||||
/*
|
||||
* Currently allow IPL only from CCW devices.
|
||||
*/
|
||||
ccw_dev = s390_get_ccw_device(dev_st, &devtype);
|
||||
if (ccw_dev) {
|
||||
lp = ccw_dev->loadparm;
|
||||
|
||||
switch (devtype) {
|
||||
case CCW_DEVTYPE_SCSI:
|
||||
sd = SCSI_DEVICE(dev_st);
|
||||
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
|
||||
ipl->iplb.blk0_len =
|
||||
iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
|
||||
iplb->blk0_len =
|
||||
cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
|
||||
ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
|
||||
ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
|
||||
ipl->iplb.scsi.target = cpu_to_be16(sd->id);
|
||||
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;
|
||||
iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
|
||||
iplb->scsi.lun = cpu_to_be32(sd->lun);
|
||||
iplb->scsi.target = cpu_to_be16(sd->id);
|
||||
iplb->scsi.channel = cpu_to_be16(sd->channel);
|
||||
iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
|
||||
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;
|
||||
iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||||
iplb->pbt = S390_IPL_TYPE_CCW;
|
||||
iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
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 =
|
||||
iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||||
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;
|
||||
iplb->pbt = S390_IPL_TYPE_CCW;
|
||||
iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) {
|
||||
ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;
|
||||
/* If the device loadparm is empty use the global machine loadparm */
|
||||
if (memcmp(lp, NO_LOADPARM, 8) == 0) {
|
||||
lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm;
|
||||
}
|
||||
|
||||
s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
|
||||
iplb->flags |= DIAG308_FLAGS_LP_VALID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int s390_ipl_set_loadparm(uint8_t *loadparm)
|
||||
void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb)
|
||||
{
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL);
|
||||
|
||||
if (lp) {
|
||||
int i;
|
||||
|
||||
/* lp is an uppercase string without leading/embedded spaces */
|
||||
for (i = 0; i < 8 && lp[i]; i++) {
|
||||
loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]];
|
||||
}
|
||||
|
||||
if (i < 8) {
|
||||
memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */
|
||||
}
|
||||
|
||||
g_free(lp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int load_netboot_image(Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
char *netboot_filename;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *mr = NULL;
|
||||
void *ram_ptr = NULL;
|
||||
int img_size = -1;
|
||||
uint16_t index;
|
||||
index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index;
|
||||
|
||||
mr = memory_region_find(sysmem, 0, 1).mr;
|
||||
if (!mr) {
|
||||
error_setg(errp, "Failed to find memory region at address 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ram_ptr = memory_region_get_ram_ptr(mr);
|
||||
if (!ram_ptr) {
|
||||
error_setg(errp, "No RAM found");
|
||||
goto unref_mr;
|
||||
}
|
||||
|
||||
netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
|
||||
if (netboot_filename == NULL) {
|
||||
error_setg(errp, "Could not find network bootloader '%s'",
|
||||
ipl->netboot_fw);
|
||||
goto unref_mr;
|
||||
}
|
||||
|
||||
img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL,
|
||||
&ipl->start_addr,
|
||||
NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL,
|
||||
false);
|
||||
|
||||
if (img_size < 0) {
|
||||
img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size);
|
||||
ipl->start_addr = KERN_IMAGE_START;
|
||||
}
|
||||
|
||||
if (img_size < 0) {
|
||||
error_setg(errp, "Failed to load network bootloader");
|
||||
}
|
||||
|
||||
g_free(netboot_filename);
|
||||
|
||||
unref_mr:
|
||||
memory_region_unref(mr);
|
||||
return img_size;
|
||||
ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb);
|
||||
ipl->iplb_index = index;
|
||||
}
|
||||
|
||||
static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
|
||||
int virtio_id)
|
||||
static bool s390_init_all_iplbs(S390IPLState *ipl)
|
||||
{
|
||||
uint8_t cssid;
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint16_t schid;
|
||||
SubchDev *sch = NULL;
|
||||
int iplb_num = 0;
|
||||
IplParameterBlock iplb_chain[7];
|
||||
DeviceState *dev_st = get_boot_device(0);
|
||||
Object *machine = qdev_get_machine();
|
||||
|
||||
if (iplb->pbt != S390_IPL_TYPE_CCW) {
|
||||
/*
|
||||
* Parse the boot devices. Generate an IPLB for only the first boot device
|
||||
* which will later be set with DIAG308.
|
||||
*/
|
||||
if (!dev_st) {
|
||||
ipl->qipl.chain_len = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
devno = be16_to_cpu(iplb->ccw.devno);
|
||||
ssid = iplb->ccw.ssid & 3;
|
||||
|
||||
for (schid = 0; schid < MAX_SCHID; schid++) {
|
||||
for (cssid = 0; cssid < MAX_CSSID; cssid++) {
|
||||
sch = css_find_subch(1, cssid, ssid, schid);
|
||||
|
||||
if (sch && sch->devno == devno) {
|
||||
return sch->id.cu_model == virtio_id;
|
||||
}
|
||||
}
|
||||
/* If no machine loadparm was defined fill it with spaces */
|
||||
if (memcmp(S390_CCW_MACHINE(machine)->loadparm, NO_LOADPARM, 8) == 0) {
|
||||
object_property_set_str(machine, "loadparm", " ", NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_virtio_net_device(IplParameterBlock *iplb)
|
||||
{
|
||||
return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET);
|
||||
}
|
||||
iplb_num = 1;
|
||||
s390_build_iplb(dev_st, &ipl->iplb);
|
||||
|
||||
static bool is_virtio_scsi_device(IplParameterBlock *iplb)
|
||||
{
|
||||
return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI);
|
||||
/* Index any fallback boot devices */
|
||||
while (get_boot_device(iplb_num)) {
|
||||
iplb_num++;
|
||||
}
|
||||
|
||||
if (iplb_num > MAX_BOOT_DEVS) {
|
||||
warn_report("Excess boot devices defined! %d boot devices found, "
|
||||
"but only the first %d will be considered.",
|
||||
iplb_num, MAX_BOOT_DEVS);
|
||||
|
||||
iplb_num = MAX_BOOT_DEVS;
|
||||
}
|
||||
|
||||
ipl->qipl.chain_len = cpu_to_be16(iplb_num - 1);
|
||||
|
||||
/*
|
||||
* Build fallback IPLBs for any boot devices above index 0, up to a
|
||||
* maximum amount as defined in ipl.h
|
||||
*/
|
||||
if (iplb_num > 1) {
|
||||
/* Start at 1 because the IPLB for boot index 0 is not chained */
|
||||
for (int i = 1; i < iplb_num; i++) {
|
||||
dev_st = get_boot_device(i);
|
||||
s390_build_iplb(dev_st, &iplb_chain[i - 1]);
|
||||
}
|
||||
|
||||
ipl->qipl.next_iplb = cpu_to_be64(s390_ipl_map_iplb_chain(iplb_chain));
|
||||
}
|
||||
|
||||
return iplb_num;
|
||||
}
|
||||
|
||||
static void update_machine_ipl_properties(IplParameterBlock *iplb)
|
||||
@ -577,7 +587,7 @@ static void update_machine_ipl_properties(IplParameterBlock *iplb)
|
||||
ascii_loadparm[i] = 0;
|
||||
object_property_set_str(machine, "loadparm", ascii_loadparm, &err);
|
||||
} else {
|
||||
object_property_set_str(machine, "loadparm", "", &err);
|
||||
object_property_set_str(machine, "loadparm", " ", &err);
|
||||
}
|
||||
if (err) {
|
||||
warn_report_err(err);
|
||||
@ -599,7 +609,7 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb)
|
||||
ipl->iplb = *iplb;
|
||||
ipl->iplb_valid = true;
|
||||
}
|
||||
ipl->netboot = is_virtio_net_device(iplb);
|
||||
|
||||
update_machine_ipl_properties(iplb);
|
||||
}
|
||||
|
||||
@ -626,32 +636,14 @@ IplParameterBlock *s390_ipl_get_iplb(void)
|
||||
void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type)
|
||||
{
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
|
||||
if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) {
|
||||
/* use CPU 0 for full resets */
|
||||
ipl->reset_cpu_index = 0;
|
||||
} else {
|
||||
ipl->reset_cpu_index = cs->cpu_index;
|
||||
}
|
||||
|
||||
ipl->reset_type = reset_type;
|
||||
|
||||
if (reset_type == S390_RESET_REIPL &&
|
||||
ipl->iplb_valid &&
|
||||
!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), NULL);
|
||||
|
||||
if (ccw_dev &&
|
||||
cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno &&
|
||||
(ccw_dev->sch->ssid & 3) == ipl->iplb.ccw.ssid) {
|
||||
/*
|
||||
* this is the original boot device's SCSI
|
||||
* so restore IPL parameter info from it
|
||||
*/
|
||||
ipl->iplb_valid = s390_gen_initial_iplb(ipl);
|
||||
}
|
||||
}
|
||||
if (reset_type == S390_RESET_MODIFIED_CLEAR ||
|
||||
reset_type == S390_RESET_LOAD_NORMAL ||
|
||||
reset_type == S390_RESET_PV) {
|
||||
@ -743,13 +735,11 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||||
if (!ipl->kernel || ipl->iplb_valid) {
|
||||
cpu->env.psw.addr = ipl->bios_start_addr;
|
||||
if (!ipl->iplb_valid) {
|
||||
ipl->iplb_valid = s390_gen_initial_iplb(ipl);
|
||||
ipl->iplb_valid = s390_init_all_iplbs(ipl);
|
||||
} else {
|
||||
ipl->qipl.chain_len = 0;
|
||||
}
|
||||
}
|
||||
if (ipl->netboot) {
|
||||
load_netboot_image(&error_fatal);
|
||||
ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
||||
}
|
||||
s390_ipl_set_boot_menu(ipl);
|
||||
s390_ipl_prepare_qipl(cpu);
|
||||
}
|
||||
|
123
hw/s390x/ipl.h
123
hw/s390x/ipl.h
@ -16,96 +16,15 @@
|
||||
#include "cpu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/s390x/ipl/qipl.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
struct IPLBlockPVComp {
|
||||
uint64_t tweak_pref;
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPVComp IPLBlockPVComp;
|
||||
|
||||
struct IPLBlockPV {
|
||||
uint8_t reserved18[87]; /* 0x18 */
|
||||
uint8_t version; /* 0x6f */
|
||||
uint32_t reserved70; /* 0x70 */
|
||||
uint32_t num_comp; /* 0x74 */
|
||||
uint64_t pv_header_addr; /* 0x78 */
|
||||
uint64_t pv_header_len; /* 0x80 */
|
||||
struct IPLBlockPVComp components[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPV IPLBlockPV;
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved3[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved4[8];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockCcw IplBlockCcw;
|
||||
|
||||
struct IplBlockFcp {
|
||||
uint8_t reserved1[305 - 1];
|
||||
uint8_t opt;
|
||||
uint8_t reserved2[3];
|
||||
uint16_t reserved3;
|
||||
uint16_t devno;
|
||||
uint8_t reserved4[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved5[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
struct IplBlockQemuScsi {
|
||||
uint32_t lun;
|
||||
uint16_t target;
|
||||
uint16_t channel;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
|
||||
|
||||
#define DIAG308_FLAGS_LP_VALID 0x80
|
||||
#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */
|
||||
|
||||
union IplParameterBlock {
|
||||
struct {
|
||||
uint32_t len;
|
||||
uint8_t reserved0[3];
|
||||
uint8_t version;
|
||||
uint32_t blk0_len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint16_t reserved01;
|
||||
uint8_t loadparm[8];
|
||||
union {
|
||||
IplBlockCcw ccw;
|
||||
IplBlockFcp fcp;
|
||||
IPLBlockPV pv;
|
||||
IplBlockQemuScsi scsi;
|
||||
};
|
||||
} QEMU_PACKED;
|
||||
struct {
|
||||
uint8_t reserved1[110];
|
||||
uint16_t devno;
|
||||
uint8_t reserved2[88];
|
||||
uint8_t reserved_ext[4096 - 200];
|
||||
} QEMU_PACKED;
|
||||
} QEMU_PACKED;
|
||||
typedef union IplParameterBlock IplParameterBlock;
|
||||
|
||||
int s390_ipl_set_loadparm(uint8_t *loadparm);
|
||||
void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
|
||||
void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
|
||||
void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb);
|
||||
void s390_ipl_update_diag308(IplParameterBlock *iplb);
|
||||
int s390_ipl_prepare_pv_header(Error **errp);
|
||||
int s390_ipl_pv_unpack(void);
|
||||
@ -131,27 +50,6 @@ void s390_ipl_clear_reset_request(void);
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
/*
|
||||
* The QEMU IPL Parameters will be stored at absolute address
|
||||
* 204 (0xcc) which means it is 32-bit word aligned but not
|
||||
* double-word aligned.
|
||||
* Placement of data fields in this area must account for
|
||||
* their alignment needs. E.g., netboot_start_address must
|
||||
* have an offset of 4 + n * 8 bytes within the struct in order
|
||||
* to keep it double-word aligned.
|
||||
* The total size of the struct must never exceed 28 bytes.
|
||||
* This definition must be kept in sync with the definition
|
||||
* in pc-bios/s390-ccw/iplb.h.
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t reserved1[3];
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved2[12];
|
||||
} QEMU_PACKED;
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
|
||||
#define TYPE_S390_IPL "s390-ipl"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
|
||||
|
||||
@ -168,7 +66,8 @@ struct S390IPLState {
|
||||
bool enforce_bios;
|
||||
bool iplb_valid;
|
||||
bool iplb_valid_pv;
|
||||
bool netboot;
|
||||
bool rebuilt_iplb;
|
||||
uint16_t iplb_index;
|
||||
/* reset related properties don't have to be migrated or reset */
|
||||
enum s390_reset reset_type;
|
||||
int reset_cpu_index;
|
||||
@ -178,7 +77,6 @@ struct S390IPLState {
|
||||
char *initrd;
|
||||
char *cmdline;
|
||||
char *firmware;
|
||||
char *netboot_fw;
|
||||
uint8_t cssid;
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
@ -276,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb)
|
||||
|
||||
static inline bool iplb_valid(IplParameterBlock *iplb)
|
||||
{
|
||||
uint32_t len = be32_to_cpu(iplb->len);
|
||||
|
||||
switch (iplb->pbt) {
|
||||
case S390_IPL_TYPE_FCP:
|
||||
return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN;
|
||||
return len >= S390_IPLB_MIN_FCP_LEN;
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN;
|
||||
return len >= S390_IPLB_MIN_CCW_LEN;
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram)
|
||||
static void s390_init_ipl_dev(const char *kernel_filename,
|
||||
const char *kernel_cmdline,
|
||||
const char *initrd_filename, const char *firmware,
|
||||
const char *netboot_fw, bool enforce_bios)
|
||||
bool enforce_bios)
|
||||
{
|
||||
Object *new = object_new(TYPE_S390_IPL);
|
||||
DeviceState *dev = DEVICE(new);
|
||||
char *netboot_fw_prop;
|
||||
|
||||
if (kernel_filename) {
|
||||
qdev_prop_set_string(dev, "kernel", kernel_filename);
|
||||
@ -212,11 +211,6 @@ static void s390_init_ipl_dev(const char *kernel_filename,
|
||||
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
|
||||
qdev_prop_set_string(dev, "firmware", firmware);
|
||||
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
|
||||
netboot_fw_prop = object_property_get_str(new, "netboot_fw", &error_abort);
|
||||
if (!strlen(netboot_fw_prop)) {
|
||||
qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
|
||||
}
|
||||
g_free(netboot_fw_prop);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
|
||||
new);
|
||||
object_unref(new);
|
||||
@ -284,7 +278,7 @@ static void ccw_init(MachineState *machine)
|
||||
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
|
||||
machine->initrd_filename,
|
||||
machine->firmware ?: "s390-ccw.img",
|
||||
"s390-netboot.img", true);
|
||||
true);
|
||||
|
||||
dev = qdev_new(TYPE_S390_PCI_HOST_BRIDGE);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE,
|
||||
@ -728,28 +722,12 @@ static void machine_set_loadparm(Object *obj, Visitor *v,
|
||||
{
|
||||
S390CcwMachineState *ms = S390_CCW_MACHINE(obj);
|
||||
char *val;
|
||||
int i;
|
||||
|
||||
if (!visit_type_str(v, name, &val, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(ms->loadparm) && val[i]; i++) {
|
||||
uint8_t c = qemu_toupper(val[i]); /* mimic HMC */
|
||||
|
||||
if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') ||
|
||||
(c == ' ')) {
|
||||
ms->loadparm[i] = c;
|
||||
} else {
|
||||
error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
|
||||
c, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < sizeof(ms->loadparm); i++) {
|
||||
ms->loadparm[i] = ' '; /* pad right with spaces */
|
||||
}
|
||||
s390_ipl_fmt_loadparm(ms->loadparm, val, errp);
|
||||
}
|
||||
|
||||
static void ccw_machine_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -110,7 +110,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
int cpu_count;
|
||||
int rnsize, rnmax;
|
||||
IplParameterBlock *ipib = s390_ipl_get_iplb();
|
||||
int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len);
|
||||
int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ?
|
||||
offsetof(ReadInfo, entries) :
|
||||
@ -171,12 +170,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
||||
read_info->rnmax2 = cpu_to_be64(rnmax);
|
||||
}
|
||||
|
||||
if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) {
|
||||
memcpy(&read_info->loadparm, &ipib->loadparm,
|
||||
sizeof(read_info->loadparm));
|
||||
} else {
|
||||
s390_ipl_set_loadparm(read_info->loadparm);
|
||||
}
|
||||
s390_ipl_convert_loadparm((char *)S390_CCW_MACHINE(machine)->loadparm,
|
||||
read_info->loadparm);
|
||||
|
||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
|
||||
}
|
||||
|
127
include/hw/s390x/ipl/qipl.h
Normal file
127
include/hw/s390x/ipl/qipl.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* S/390 boot structures
|
||||
*
|
||||
* Copyright 2024 IBM Corp.
|
||||
* Author(s): Jared Rossi <jrossi@linux.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 S390X_QIPL_H
|
||||
#define S390X_QIPL_H
|
||||
|
||||
/* Boot Menu flags */
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
#define QIPL_ADDRESS 0xcc
|
||||
#define LOADPARM_LEN 8
|
||||
#define NO_LOADPARM "\0\0\0\0\0\0\0\0"
|
||||
|
||||
/*
|
||||
* The QEMU IPL Parameters will be stored at absolute address
|
||||
* 204 (0xcc) which means it is 32-bit word aligned but not
|
||||
* double-word aligned. Placement of 64-bit data fields in this
|
||||
* area must account for their alignment needs.
|
||||
* The total size of the struct must never exceed 28 bytes.
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t index;
|
||||
uint8_t reserved1[2];
|
||||
uint64_t reserved2;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved3[2];
|
||||
uint16_t chain_len;
|
||||
uint64_t next_iplb;
|
||||
} QEMU_PACKED;
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
|
||||
struct IPLBlockPVComp {
|
||||
uint64_t tweak_pref;
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPVComp IPLBlockPVComp;
|
||||
|
||||
struct IPLBlockPV {
|
||||
uint8_t reserved18[87]; /* 0x18 */
|
||||
uint8_t version; /* 0x6f */
|
||||
uint32_t reserved70; /* 0x70 */
|
||||
uint32_t num_comp; /* 0x74 */
|
||||
uint64_t pv_header_addr; /* 0x78 */
|
||||
uint64_t pv_header_len; /* 0x80 */
|
||||
struct IPLBlockPVComp components[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPV IPLBlockPV;
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved3[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved4[8];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockCcw IplBlockCcw;
|
||||
|
||||
struct IplBlockFcp {
|
||||
uint8_t reserved1[305 - 1];
|
||||
uint8_t opt;
|
||||
uint8_t reserved2[3];
|
||||
uint16_t reserved3;
|
||||
uint16_t devno;
|
||||
uint8_t reserved4[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved5[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
struct IplBlockQemuScsi {
|
||||
uint32_t lun;
|
||||
uint16_t target;
|
||||
uint16_t channel;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
|
||||
|
||||
union IplParameterBlock {
|
||||
struct {
|
||||
uint32_t len;
|
||||
uint8_t reserved0[3];
|
||||
uint8_t version;
|
||||
uint32_t blk0_len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint16_t reserved01;
|
||||
uint8_t loadparm[LOADPARM_LEN];
|
||||
union {
|
||||
IplBlockCcw ccw;
|
||||
IplBlockFcp fcp;
|
||||
IPLBlockPV pv;
|
||||
IplBlockQemuScsi scsi;
|
||||
};
|
||||
} QEMU_PACKED;
|
||||
struct {
|
||||
uint8_t reserved1[110];
|
||||
uint16_t devno;
|
||||
uint8_t reserved2[88];
|
||||
uint8_t reserved_ext[4096 - 200];
|
||||
} QEMU_PACKED;
|
||||
} QEMU_PACKED;
|
||||
typedef union IplParameterBlock IplParameterBlock;
|
||||
|
||||
#endif
|
@ -68,7 +68,6 @@ blobs = [
|
||||
'kvmvapic.bin',
|
||||
'pvh.bin',
|
||||
's390-ccw.img',
|
||||
's390-netboot.img',
|
||||
'slof.bin',
|
||||
'skiboot.lid',
|
||||
'palcode-clipper',
|
||||
|
Binary file not shown.
@ -3,7 +3,8 @@ all: build-all
|
||||
@true
|
||||
|
||||
include config-host.mak
|
||||
CFLAGS = -O2 -g
|
||||
CFLAGS = -O2 -g -I $(SRC_PATH)/../../include/hw/s390x/ipl
|
||||
LDFLAGS ?=
|
||||
MAKEFLAGS += -rR
|
||||
|
||||
GIT_SUBMODULES = roms/SLOF
|
||||
@ -32,15 +33,21 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
|
||||
|
||||
.PHONY : all clean build-all distclean
|
||||
|
||||
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \
|
||||
virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o
|
||||
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
|
||||
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o
|
||||
|
||||
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
|
||||
|
||||
LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
|
||||
LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
|
||||
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE
|
||||
EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables
|
||||
EXTRA_CFLAGS += -msoft-float
|
||||
EXTRA_CFLAGS += -std=gnu99
|
||||
LDFLAGS += -Wl,-pie -nostdlib -z noexecstack
|
||||
EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
|
||||
EXTRA_LDFLAGS += -Wl,-pie -nostdlib -z noexecstack -z text
|
||||
|
||||
cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null
|
||||
cc-option = if $(call cc-test, $1); then \
|
||||
@ -55,19 +62,64 @@ config-cc.mak: Makefile
|
||||
$(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak
|
||||
-include config-cc.mak
|
||||
|
||||
build-all: s390-ccw.img s390-netboot.img
|
||||
# libc files:
|
||||
|
||||
s390-ccw.elf: $(OBJECTS)
|
||||
$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),Linking)
|
||||
LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
|
||||
-MMD -MP -MT $@ -MF $(@:%.o=%.d)
|
||||
|
||||
CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
|
||||
strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
|
||||
memset.o memcpy.o memmove.o memcmp.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/string/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
|
||||
printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
sbrk.o: $(SLOF_DIR)/slof/sbrk.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
|
||||
|
||||
libc.a: $(LIBCOBJS)
|
||||
$(call quiet-command,$(AR) -rc $@ $^,Creating static library)
|
||||
|
||||
# libnet files:
|
||||
|
||||
LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
|
||||
dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
|
||||
LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
|
||||
-DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
|
||||
|
||||
%.o : $(SLOF_DIR)/lib/libnet/%.c
|
||||
$(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
libnet.a: $(LIBNETOBJS)
|
||||
$(call quiet-command,$(AR) -rc $@ $^,Creating static library)
|
||||
|
||||
# Main targets:
|
||||
|
||||
build-all: s390-ccw.img
|
||||
|
||||
s390-ccw.elf: $(OBJECTS) libnet.a libc.a
|
||||
$(call quiet-command,$(CC) $(EXTRA_LDFLAGS) $(LDFLAGS) -o $@ $^,Linking)
|
||||
|
||||
s390-ccw.img: s390-ccw.elf
|
||||
$(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into)
|
||||
|
||||
$(OBJECTS): Makefile
|
||||
|
||||
include $(SRC_PATH)/netboot.mak
|
||||
|
||||
ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS))
|
||||
ALL_OBJS = $(sort $(OBJECTS) $(LIBCOBJS) $(LIBNETOBJS))
|
||||
-include $(ALL_OBJS:%.o=%.d)
|
||||
|
||||
clean:
|
||||
|
@ -8,7 +8,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
#include "bootmap.h"
|
||||
@ -21,7 +22,7 @@
|
||||
|
||||
#ifdef DEBUG_FALLBACK
|
||||
#define dputs(txt) \
|
||||
do { sclp_print("zipl: " txt); } while (0)
|
||||
do { printf("zipl: " txt); } while (0)
|
||||
#else
|
||||
#define dputs(fmt, ...) \
|
||||
do { } while (0)
|
||||
@ -61,15 +62,34 @@ static void *s2_prev_blk = _s2;
|
||||
static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
|
||||
static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
|
||||
|
||||
static inline void verify_boot_info(BootInfo *bip)
|
||||
static inline int verify_boot_info(BootInfo *bip)
|
||||
{
|
||||
IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo");
|
||||
IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
|
||||
IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
|
||||
IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
|
||||
IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch");
|
||||
IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size),
|
||||
"Bad block size in zIPL section of the 1st record.");
|
||||
if (!magic_match(bip->magic, ZIPL_MAGIC)) {
|
||||
puts("No zIPL sig in BootInfo");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bip->version != BOOT_INFO_VERSION) {
|
||||
puts("Wrong zIPL version");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) {
|
||||
puts("DASD is not for IPL");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) {
|
||||
puts("DASD is not ECKD");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (bip->flags != BOOT_INFO_FLAGS_ARCH) {
|
||||
puts("Not for this arch");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) {
|
||||
puts("Bad block size in zIPL section of 1st record");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl,
|
||||
@ -144,14 +164,17 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
|
||||
bool more_data;
|
||||
|
||||
memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
|
||||
read_block(blk, bprs, "BPRS read failed");
|
||||
if (virtio_read(blk, bprs)) {
|
||||
puts("BPRS read failed");
|
||||
return ERROR_BLOCK_NR;
|
||||
}
|
||||
|
||||
do {
|
||||
more_data = false;
|
||||
for (j = 0;; j++) {
|
||||
block_nr = gen_eckd_block_num(&bprs[j].xeckd, ldipl);
|
||||
if (is_null_block_number(block_nr)) { /* end of chunk */
|
||||
break;
|
||||
return NULL_BLOCK_NR;
|
||||
}
|
||||
|
||||
/* we need the updated blockno for the next indirect entry
|
||||
@ -162,15 +185,20 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
|
||||
}
|
||||
|
||||
/* List directed pointer does not store block size */
|
||||
IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size),
|
||||
"bad chunk block size");
|
||||
if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) {
|
||||
puts("Bad chunk block size");
|
||||
return ERROR_BLOCK_NR;
|
||||
}
|
||||
|
||||
if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) {
|
||||
/*
|
||||
* If an invalid address is found during LD-IPL then break and
|
||||
* retry as CCW
|
||||
* retry as CCW-IPL, otherwise abort on error
|
||||
*/
|
||||
IPL_assert(ldipl, "bad chunk ECKD addr");
|
||||
if (!ldipl) {
|
||||
puts("Bad chunk ECKD address");
|
||||
return ERROR_BLOCK_NR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -188,7 +216,10 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
|
||||
* I.e. the next ptr must point to the unused memory area
|
||||
*/
|
||||
memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
|
||||
read_block(block_nr, bprs, "BPRS continuation read failed");
|
||||
if (virtio_read(block_nr, bprs)) {
|
||||
puts("BPRS continuation read failed");
|
||||
return ERROR_BLOCK_NR;
|
||||
}
|
||||
more_data = true;
|
||||
break;
|
||||
}
|
||||
@ -197,7 +228,10 @@ static block_number_t load_eckd_segments(block_number_t blk, bool ldipl,
|
||||
* to memory (address).
|
||||
*/
|
||||
rc = virtio_read_many(block_nr, (void *)(*address), count + 1);
|
||||
IPL_assert(rc == 0, "code chunk read failed");
|
||||
if (rc != 0) {
|
||||
puts("Code chunk read failed");
|
||||
return ERROR_BLOCK_NR;
|
||||
}
|
||||
|
||||
*address += (count + 1) * virtio_get_block_size();
|
||||
}
|
||||
@ -231,7 +265,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||
|
||||
/* Get Stage1b data */
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader");
|
||||
if (virtio_read(s1b_block_nr, s1b)) {
|
||||
puts("Cannot read stage1b boot loader");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memset(_s2, FREE_SPACE_FILLER, sizeof(_s2));
|
||||
|
||||
@ -243,7 +280,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||
break;
|
||||
}
|
||||
|
||||
read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader");
|
||||
if (virtio_read(cur_block_nr, s2_cur_blk)) {
|
||||
puts("Cannot read stage2 boot loader");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (find_zipl_boot_menu_banner(&banner_offset)) {
|
||||
/*
|
||||
@ -251,8 +291,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||
* possibility of menu data spanning multiple blocks.
|
||||
*/
|
||||
if (prev_block_nr) {
|
||||
read_block(prev_block_nr, s2_prev_blk,
|
||||
"Cannot read stage2 boot loader");
|
||||
if (virtio_read(prev_block_nr, s2_prev_blk)) {
|
||||
puts("Cannot read stage2 boot loader");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 1 < STAGE2_BLK_CNT_MAX) {
|
||||
@ -260,8 +302,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||
}
|
||||
|
||||
if (next_block_nr && !is_null_block_number(next_block_nr)) {
|
||||
read_block(next_block_nr, s2_next_blk,
|
||||
"Cannot read stage2 boot loader");
|
||||
if (virtio_read(next_block_nr, s2_next_blk)) {
|
||||
puts("Cannot read stage2 boot loader");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return menu_get_zipl_boot_index(s2_cur_blk + banner_offset);
|
||||
@ -270,11 +314,11 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
|
||||
prev_block_nr = cur_block_nr;
|
||||
}
|
||||
|
||||
sclp_print("No zipl boot menu data found. Booting default entry.");
|
||||
printf("No zipl boot menu data found. Booting default entry.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_eckd_boot_script(block_number_t bmt_block_nr,
|
||||
static int run_eckd_boot_script(block_number_t bmt_block_nr,
|
||||
block_number_t s1b_block_nr)
|
||||
{
|
||||
int i;
|
||||
@ -291,17 +335,28 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
|
||||
}
|
||||
|
||||
debug_print_int("loadparm", loadparm);
|
||||
IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than"
|
||||
" maximum number of boot entries allowed");
|
||||
if (loadparm >= MAX_BOOT_ENTRIES) {
|
||||
puts("loadparm value greater than max number of boot entries allowed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(bmt_block_nr, sec, "Cannot read Boot Map Table");
|
||||
if (virtio_read(bmt_block_nr, sec)) {
|
||||
puts("Cannot read Boot Map Table");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl);
|
||||
IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry");
|
||||
if (block_nr == NULL_BLOCK_NR) {
|
||||
puts("Cannot find Boot Map Table Entry");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(block_nr, sec, "Cannot read Boot Map Script");
|
||||
if (virtio_read(block_nr, sec)) {
|
||||
puts("Cannot read Boot Map Script");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD ||
|
||||
bms->entry[i].type == BOOT_SCRIPT_SIGNATURE; i++) {
|
||||
@ -316,21 +371,27 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr,
|
||||
|
||||
do {
|
||||
block_nr = load_eckd_segments(block_nr, ldipl, &address);
|
||||
} while (block_nr != -1);
|
||||
if (block_nr == ERROR_BLOCK_NR) {
|
||||
return ldipl ? 0 : -EIO;
|
||||
}
|
||||
} while (block_nr != NULL_BLOCK_NR);
|
||||
}
|
||||
|
||||
if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) {
|
||||
/* Abort LD-IPL and retry as CCW-IPL */
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC,
|
||||
"Unknown script entry type");
|
||||
write_reset_psw(bms->entry[i].address.load_address); /* no return */
|
||||
jump_to_IPL_code(0); /* no return */
|
||||
if (bms->entry[i].type != BOOT_SCRIPT_EXEC) {
|
||||
puts("Unknown script entry type");
|
||||
return -EINVAL;
|
||||
}
|
||||
write_reset_psw(bms->entry[i].address.load_address);
|
||||
jump_to_IPL_code(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ipl_eckd_cdl(void)
|
||||
static int ipl_eckd_cdl(void)
|
||||
{
|
||||
XEckdMbr *mbr;
|
||||
EckdCdlIpl2 *ipl2 = (void *)sec;
|
||||
@ -338,23 +399,26 @@ static void ipl_eckd_cdl(void)
|
||||
block_number_t bmt_block_nr, s1b_block_nr;
|
||||
|
||||
/* we have just read the block #0 and recognized it as "IPL1" */
|
||||
sclp_print("CDL\n");
|
||||
puts("CDL");
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(1, ipl2, "Cannot read IPL2 record at block 1");
|
||||
if (virtio_read(1, ipl2)) {
|
||||
puts("Cannot read IPL2 record at block 1");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mbr = &ipl2->mbr;
|
||||
if (!magic_match(mbr, ZIPL_MAGIC)) {
|
||||
sclp_print("No zIPL section in IPL2 record.\n");
|
||||
return;
|
||||
puts("No zIPL section in IPL2 record.");
|
||||
return 0;
|
||||
}
|
||||
if (!block_size_ok(mbr->blockptr.xeckd.bptr.size)) {
|
||||
sclp_print("Bad block size in zIPL section of IPL2 record.\n");
|
||||
return;
|
||||
puts("Bad block size in zIPL section of IPL2 record.");
|
||||
return 0;
|
||||
}
|
||||
if (mbr->dev_type != DEV_TYPE_ECKD) {
|
||||
sclp_print("Non-ECKD device type in zIPL section of IPL2 record.\n");
|
||||
return;
|
||||
puts("Non-ECKD device type in zIPL section of IPL2 record.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* save pointer to Boot Map Table */
|
||||
@ -364,19 +428,21 @@ static void ipl_eckd_cdl(void)
|
||||
s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs);
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(2, vlbl, "Cannot read Volume Label at block 2");
|
||||
if (virtio_read(2, vlbl)) {
|
||||
puts("Cannot read Volume Label at block 2");
|
||||
return -EIO;
|
||||
}
|
||||
if (!magic_match(vlbl->key, VOL1_MAGIC)) {
|
||||
sclp_print("Invalid magic of volume label block.\n");
|
||||
return;
|
||||
puts("Invalid magic of volume label block.");
|
||||
return 0;
|
||||
}
|
||||
if (!magic_match(vlbl->f.key, VOL1_MAGIC)) {
|
||||
sclp_print("Invalid magic of volser block.\n");
|
||||
return;
|
||||
puts("Invalid magic of volser block.");
|
||||
return 0;
|
||||
}
|
||||
print_volser(vlbl->f.volser);
|
||||
|
||||
run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||
/* no return */
|
||||
return run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||
}
|
||||
|
||||
static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
|
||||
@ -384,8 +450,8 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
|
||||
LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */
|
||||
char msg[4] = { '?', '.', '\n', '\0' };
|
||||
|
||||
sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL");
|
||||
sclp_print(" version ");
|
||||
printf((mode == ECKD_CMS) ? "CMS" : "LDL");
|
||||
printf(" version ");
|
||||
switch (vlbl->LDL_version) {
|
||||
case LDL1_VERSION:
|
||||
msg[0] = '1';
|
||||
@ -398,11 +464,11 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode)
|
||||
msg[1] = '?';
|
||||
break;
|
||||
}
|
||||
sclp_print(msg);
|
||||
printf("%s", msg);
|
||||
print_volser(vlbl->volser);
|
||||
}
|
||||
|
||||
static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
static int ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
{
|
||||
block_number_t bmt_block_nr, s1b_block_nr;
|
||||
EckdLdlIpl1 *ipl1 = (void *)sec;
|
||||
@ -414,12 +480,15 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
/* DO NOT read BootMap pointer (only one, xECKD) at block #2 */
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(0, sec, "Cannot read block 0 to grab boot info.");
|
||||
if (virtio_read(0, sec)) {
|
||||
puts("Cannot read block 0 to grab boot info.");
|
||||
return -EIO;
|
||||
}
|
||||
if (mode == ECKD_LDL_UNLABELED) {
|
||||
if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) {
|
||||
return; /* not applicable layout */
|
||||
return 0; /* not applicable layout */
|
||||
}
|
||||
sclp_print("unlabeled LDL.\n");
|
||||
puts("unlabeled LDL.");
|
||||
}
|
||||
verify_boot_info(&ipl1->bip);
|
||||
|
||||
@ -429,8 +498,7 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
/* save pointer to Stage1b Data */
|
||||
s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs);
|
||||
|
||||
run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||
/* no return */
|
||||
return run_eckd_boot_script(bmt_block_nr, s1b_block_nr);
|
||||
}
|
||||
|
||||
static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr)
|
||||
@ -440,7 +508,10 @@ static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr)
|
||||
BootRecord *br;
|
||||
|
||||
blockno = gen_eckd_block_num(ptr, 0);
|
||||
read_block(blockno, tmp_sec, "Cannot read boot record");
|
||||
if (virtio_read(blockno, tmp_sec)) {
|
||||
puts("Cannot read boot record");
|
||||
return ERROR_BLOCK_NR;
|
||||
}
|
||||
br = (BootRecord *)tmp_sec;
|
||||
if (!magic_match(br->magic, ZIPL_MAGIC)) {
|
||||
/* If the boot record is invalid, return and try CCW-IPL instead */
|
||||
@ -466,10 +537,10 @@ static void print_eckd_msg(void)
|
||||
*p-- = ' ';
|
||||
}
|
||||
}
|
||||
sclp_print(msg);
|
||||
printf("%s", msg);
|
||||
}
|
||||
|
||||
static void ipl_eckd(void)
|
||||
static int ipl_eckd(void)
|
||||
{
|
||||
IplVolumeLabel *vlbl = (void *)sec;
|
||||
LDL_VTOC *vtoc = (void *)sec;
|
||||
@ -479,7 +550,10 @@ static void ipl_eckd(void)
|
||||
|
||||
/* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(2, vlbl, "Cannot read block 2");
|
||||
if (virtio_read(2, vlbl)) {
|
||||
puts("Cannot read block 2");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* First check for a list-directed-format pointer which would
|
||||
@ -487,43 +561,60 @@ static void ipl_eckd(void)
|
||||
*/
|
||||
if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) {
|
||||
ldipl_bmt = eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br);
|
||||
if (ldipl_bmt) {
|
||||
sclp_print("List-Directed\n");
|
||||
/* LD-IPL does not use the S1B bock, just make it NULL */
|
||||
run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR);
|
||||
/* Only return in error, retry as CCW-IPL */
|
||||
sclp_print("Retrying IPL ");
|
||||
switch (ldipl_bmt) {
|
||||
case ERROR_BLOCK_NR:
|
||||
return -EIO;
|
||||
case NULL_BLOCK_NR:
|
||||
break; /* Invalid BMT but the device may still boot with CCW-IPL */
|
||||
default:
|
||||
puts("List-Directed");
|
||||
/*
|
||||
* LD-IPL does not use the S1B bock, just make it NULL_BLOCK_NR.
|
||||
* In some failure cases retry IPL before aborting.
|
||||
*/
|
||||
if (run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR)) {
|
||||
return -EIO;
|
||||
}
|
||||
/* Non-fatal error, retry as CCW-IPL */
|
||||
printf("Retrying IPL ");
|
||||
print_eckd_msg();
|
||||
}
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(2, vtoc, "Cannot read block 2");
|
||||
if (virtio_read(2, vtoc)) {
|
||||
puts("Cannot read block 2");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not list-directed */
|
||||
if (magic_match(vtoc->magic, VOL1_MAGIC)) {
|
||||
ipl_eckd_cdl(); /* may return in error */
|
||||
if (ipl_eckd_cdl()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (magic_match(vtoc->magic, CMS1_MAGIC)) {
|
||||
ipl_eckd_ldl(ECKD_CMS); /* no return */
|
||||
return ipl_eckd_ldl(ECKD_CMS);
|
||||
}
|
||||
if (magic_match(vtoc->magic, LNX1_MAGIC)) {
|
||||
ipl_eckd_ldl(ECKD_LDL); /* no return */
|
||||
return ipl_eckd_ldl(ECKD_LDL);
|
||||
}
|
||||
|
||||
ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */
|
||||
if (ipl_eckd_ldl(ECKD_LDL_UNLABELED)) {
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Ok, it is not a LDL by any means.
|
||||
* It still might be a CDL with zero record keys for IPL1 and IPL2
|
||||
*/
|
||||
ipl_eckd_cdl();
|
||||
return ipl_eckd_cdl();
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* IPL a SCSI disk
|
||||
*/
|
||||
|
||||
static void zipl_load_segment(ComponentEntry *entry)
|
||||
static int zipl_load_segment(ComponentEntry *entry)
|
||||
{
|
||||
const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
|
||||
ScsiBlockPtr *bprs = (void *)sec;
|
||||
@ -543,7 +634,10 @@ static void zipl_load_segment(ComponentEntry *entry)
|
||||
do {
|
||||
memset(bprs, FREE_SPACE_FILLER, bprs_size);
|
||||
fill_hex_val(blk_no, &blockno, sizeof(blockno));
|
||||
read_block(blockno, bprs, err_msg);
|
||||
if (virtio_read(blockno, bprs)) {
|
||||
puts(err_msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0;; i++) {
|
||||
uint64_t *cur_desc = (void *)&bprs[i];
|
||||
@ -571,23 +665,37 @@ static void zipl_load_segment(ComponentEntry *entry)
|
||||
}
|
||||
address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
|
||||
(void *)address);
|
||||
IPL_assert(address != -1, "zIPL load segment failed");
|
||||
if (!address) {
|
||||
puts("zIPL load segment failed");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
} while (blockno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run a zipl program */
|
||||
static void zipl_run(ScsiBlockPtr *pte)
|
||||
static int zipl_run(ScsiBlockPtr *pte)
|
||||
{
|
||||
ComponentHeader *header;
|
||||
ComponentEntry *entry;
|
||||
uint8_t tmp_sec[MAX_SECTOR_SIZE];
|
||||
|
||||
read_block(pte->blockno, tmp_sec, "Cannot read header");
|
||||
if (virtio_read(pte->blockno, tmp_sec)) {
|
||||
puts("Cannot read header");
|
||||
return -EIO;
|
||||
}
|
||||
header = (ComponentHeader *)tmp_sec;
|
||||
|
||||
IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header");
|
||||
IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
|
||||
if (!magic_match(tmp_sec, ZIPL_MAGIC)) {
|
||||
puts("No zIPL magic in header");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (header->type != ZIPL_COMP_HEADER_IPL) {
|
||||
puts("Bad header type");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dputs("start loading images\n");
|
||||
|
||||
@ -602,22 +710,30 @@ static void zipl_run(ScsiBlockPtr *pte)
|
||||
continue;
|
||||
}
|
||||
|
||||
zipl_load_segment(entry);
|
||||
if (zipl_load_segment(entry)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry++;
|
||||
|
||||
IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE),
|
||||
"Wrong entry value");
|
||||
if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
|
||||
puts("Wrong entry value");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry");
|
||||
if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
|
||||
puts("No EXEC entry");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* should not return */
|
||||
write_reset_psw(entry->compdat.load_psw);
|
||||
jump_to_IPL_code(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ipl_scsi(void)
|
||||
static int ipl_scsi(void)
|
||||
{
|
||||
ScsiMbr *mbr = (void *)sec;
|
||||
int program_table_entries = 0;
|
||||
@ -628,22 +744,34 @@ static void ipl_scsi(void)
|
||||
|
||||
/* Grab the MBR */
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(0, mbr, "Cannot read block 0");
|
||||
|
||||
if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
|
||||
return;
|
||||
if (virtio_read(0, mbr)) {
|
||||
puts("Cannot read block 0");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sclp_print("Using SCSI scheme.\n");
|
||||
if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
puts("Using SCSI scheme.");
|
||||
debug_print_int("MBR Version", mbr->version_id);
|
||||
IPL_check(mbr->version_id == 1,
|
||||
"Unknown MBR layout version, assuming version 1");
|
||||
debug_print_int("program table", mbr->pt.blockno);
|
||||
IPL_assert(mbr->pt.blockno, "No Program Table");
|
||||
if (!mbr->pt.blockno) {
|
||||
puts("No Program Table");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse the program table */
|
||||
read_block(mbr->pt.blockno, sec, "Error reading Program Table");
|
||||
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT");
|
||||
if (virtio_read(mbr->pt.blockno, sec)) {
|
||||
puts("Error reading Program Table");
|
||||
return -EIO;
|
||||
}
|
||||
if (!magic_match(sec, ZIPL_MAGIC)) {
|
||||
puts("No zIPL magic in Program Table");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
|
||||
if (prog_table->entry[i].scsi.blockno) {
|
||||
@ -653,17 +781,22 @@ static void ipl_scsi(void)
|
||||
}
|
||||
|
||||
debug_print_int("program table entries", program_table_entries);
|
||||
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
||||
if (program_table_entries == 0) {
|
||||
puts("Empty Program Table");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (menu_is_enabled_enum()) {
|
||||
loadparm = menu_get_enum_boot_index(valid_entries);
|
||||
}
|
||||
|
||||
debug_print_int("loadparm", loadparm);
|
||||
IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than"
|
||||
" maximum number of boot entries allowed");
|
||||
if (loadparm >= MAX_BOOT_ENTRIES) {
|
||||
puts("loadparm value greater than max number of boot entries allowed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
zipl_run(&prog_table->entry[loadparm].scsi); /* no return */
|
||||
return zipl_run(&prog_table->entry[loadparm].scsi);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@ -677,8 +810,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s)
|
||||
if (s->unused || !s->sector_count) {
|
||||
return false;
|
||||
}
|
||||
read_iso_sector(bswap32(s->load_rba), magic_sec,
|
||||
"Failed to read image sector 0");
|
||||
if (virtio_read(bswap32(s->load_rba), magic_sec)) {
|
||||
puts("Failed to read image sector 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Checking bytes 8 - 32 for S390 Linux magic */
|
||||
return !memcmp(magic_sec + 8, linux_s390_magic, 24);
|
||||
@ -691,28 +826,35 @@ static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH];
|
||||
/* Remained directory space in bytes */
|
||||
static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH];
|
||||
|
||||
static inline uint32_t iso_get_file_size(uint32_t load_rba)
|
||||
static inline long iso_get_file_size(uint32_t load_rba)
|
||||
{
|
||||
IsoVolDesc *vd = (IsoVolDesc *)sec;
|
||||
IsoDirHdr *cur_record = &vd->vd.primary.rootdir;
|
||||
uint8_t *temp = sec + ISO_SECTOR_SIZE;
|
||||
int level = 0;
|
||||
|
||||
read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec,
|
||||
"Failed to read ISO primary descriptor");
|
||||
if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) {
|
||||
puts("Failed to read ISO primary descriptor");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sec_loc[0] = iso_733_to_u32(cur_record->ext_loc);
|
||||
dir_rem[0] = 0;
|
||||
sec_offset[0] = 0;
|
||||
|
||||
while (level >= 0) {
|
||||
IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE,
|
||||
"Directory tree structure violation");
|
||||
if (sec_offset[level] > ISO_SECTOR_SIZE) {
|
||||
puts("Directory tree structure violation");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
cur_record = (IsoDirHdr *)(temp + sec_offset[level]);
|
||||
|
||||
if (sec_offset[level] == 0) {
|
||||
read_iso_sector(sec_loc[level], temp,
|
||||
"Failed to read ISO directory");
|
||||
if (virtio_read(sec_loc[level], temp)) {
|
||||
puts("Failed to read ISO directory");
|
||||
return -EIO;
|
||||
}
|
||||
if (dir_rem[level] == 0) {
|
||||
/* Skip self and parent records */
|
||||
dir_rem[level] = iso_733_to_u32(cur_record->data_len) -
|
||||
@ -743,7 +885,7 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba)
|
||||
if (cur_record->file_flags & 0x2) {
|
||||
/* Subdirectory */
|
||||
if (level == ISO9660_MAX_DIR_DEPTH - 1) {
|
||||
sclp_print("ISO-9660 directory depth limit exceeded\n");
|
||||
puts("ISO-9660 directory depth limit exceeded");
|
||||
} else {
|
||||
level++;
|
||||
sec_loc[level] = iso_733_to_u32(cur_record->ext_loc);
|
||||
@ -757,8 +899,10 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba)
|
||||
if (dir_rem[level] == 0) {
|
||||
/* Nothing remaining */
|
||||
level--;
|
||||
read_iso_sector(sec_loc[level], temp,
|
||||
"Failed to read ISO directory");
|
||||
if (virtio_read(sec_loc[level], temp)) {
|
||||
puts("Failed to read ISO directory");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,19 +917,24 @@ static void load_iso_bc_entry(IsoBcSection *load)
|
||||
* is padded and ISO_SECTOR_SIZE bytes aligned
|
||||
*/
|
||||
uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT;
|
||||
uint32_t real_size = iso_get_file_size(bswap32(s.load_rba));
|
||||
long real_size = iso_get_file_size(bswap32(s.load_rba));
|
||||
|
||||
if (real_size) {
|
||||
if (real_size > 0) {
|
||||
/* Round up blocks to load */
|
||||
blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE;
|
||||
sclp_print("ISO boot image size verified\n");
|
||||
puts("ISO boot image size verified");
|
||||
} else {
|
||||
sclp_print("ISO boot image size could not be verified\n");
|
||||
puts("ISO boot image size could not be verified");
|
||||
if (real_size < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
read_iso_boot_image(bswap32(s.load_rba),
|
||||
if (read_iso_boot_image(bswap32(s.load_rba),
|
||||
(void *)((uint64_t)bswap16(s.load_segment)),
|
||||
blks_to_load);
|
||||
blks_to_load)) {
|
||||
return;
|
||||
}
|
||||
|
||||
jump_to_low_kernel();
|
||||
}
|
||||
@ -808,17 +957,18 @@ static uint32_t find_iso_bc(void)
|
||||
return bswap32(et->bc_offset);
|
||||
}
|
||||
}
|
||||
read_iso_sector(block_num++, sec,
|
||||
"Failed to read ISO volume descriptor");
|
||||
if (virtio_read(block_num++, sec)) {
|
||||
puts("Failed to read ISO volume descriptor");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static IsoBcSection *find_iso_bc_entry(void)
|
||||
static IsoBcSection *find_iso_bc_entry(uint32_t offset)
|
||||
{
|
||||
IsoBcEntry *e = (IsoBcEntry *)sec;
|
||||
uint32_t offset = find_iso_bc();
|
||||
int i;
|
||||
unsigned int loadparm = get_loadparm_index();
|
||||
|
||||
@ -826,11 +976,13 @@ static IsoBcSection *find_iso_bc_entry(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
read_iso_sector(offset, sec, "Failed to read El Torito boot catalog");
|
||||
if (virtio_read(offset, sec)) {
|
||||
puts("Failed to read El Torito boot catalog");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!is_iso_bc_valid(e)) {
|
||||
/* The validation entry is mandatory */
|
||||
panic("No valid boot catalog found!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -850,19 +1002,25 @@ static IsoBcSection *find_iso_bc_entry(void)
|
||||
}
|
||||
}
|
||||
|
||||
panic("No suitable boot entry found on ISO-9660 media!\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ipl_iso_el_torito(void)
|
||||
static int ipl_iso_el_torito(void)
|
||||
{
|
||||
IsoBcSection *s = find_iso_bc_entry();
|
||||
uint32_t offset = find_iso_bc();
|
||||
if (!offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IsoBcSection *s = find_iso_bc_entry(offset);
|
||||
|
||||
if (s) {
|
||||
load_iso_bc_entry(s);
|
||||
/* no return */
|
||||
load_iso_bc_entry(s); /* only return in error */
|
||||
return -1;
|
||||
}
|
||||
|
||||
puts("No suitable boot entry found on ISO-9660 media!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -884,7 +1042,7 @@ static bool has_iso_signature(void)
|
||||
* Bus specific IPL sequences
|
||||
*/
|
||||
|
||||
static void zipl_load_vblk(void)
|
||||
static int zipl_load_vblk(void)
|
||||
{
|
||||
int blksize = virtio_get_block_size();
|
||||
|
||||
@ -892,26 +1050,30 @@ static void zipl_load_vblk(void)
|
||||
if (blksize != VIRTIO_ISO_BLOCK_SIZE) {
|
||||
virtio_assume_iso9660();
|
||||
}
|
||||
ipl_iso_el_torito();
|
||||
if (ipl_iso_el_torito()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (blksize != VIRTIO_DASD_DEFAULT_BLOCK_SIZE) {
|
||||
sclp_print("Using guessed DASD geometry.\n");
|
||||
puts("Using guessed DASD geometry.");
|
||||
virtio_assume_eckd();
|
||||
}
|
||||
ipl_eckd();
|
||||
return ipl_eckd();
|
||||
}
|
||||
|
||||
static void zipl_load_vscsi(void)
|
||||
static int zipl_load_vscsi(void)
|
||||
{
|
||||
if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) {
|
||||
/* Is it an ISO image in non-CD drive? */
|
||||
ipl_iso_el_torito();
|
||||
if (ipl_iso_el_torito()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sclp_print("Using guessed DASD geometry.\n");
|
||||
puts("Using guessed DASD geometry.");
|
||||
virtio_assume_eckd();
|
||||
ipl_eckd();
|
||||
return ipl_eckd();
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@ -924,14 +1086,20 @@ void zipl_load(void)
|
||||
|
||||
if (vdev->is_cdrom) {
|
||||
ipl_iso_el_torito();
|
||||
panic("\n! Cannot IPL this ISO image !\n");
|
||||
puts("Failed to IPL this ISO image!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (virtio_get_device_type() == VIRTIO_ID_NET) {
|
||||
jump_to_IPL_code(vdev->netboot_start_addr);
|
||||
netmain();
|
||||
puts("Failed to IPL from this network!");
|
||||
return;
|
||||
}
|
||||
|
||||
ipl_scsi();
|
||||
if (ipl_scsi()) {
|
||||
puts("Failed to IPL from this SCSI device!");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (virtio_get_device_type()) {
|
||||
case VIRTIO_ID_BLOCK:
|
||||
@ -941,8 +1109,9 @@ void zipl_load(void)
|
||||
zipl_load_vscsi();
|
||||
break;
|
||||
default:
|
||||
panic("\n! Unknown IPL device type !\n");
|
||||
puts("Unknown IPL device type!");
|
||||
return;
|
||||
}
|
||||
|
||||
sclp_print("zIPL load failed.\n");
|
||||
puts("zIPL load failed!");
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
typedef uint64_t block_number_t;
|
||||
#define NULL_BLOCK_NR 0xffffffffffffffffULL
|
||||
#define ERROR_BLOCK_NR 0xfffffffffffffffeULL
|
||||
|
||||
#define FREE_SPACE_FILLER '\xAA'
|
||||
|
||||
@ -336,9 +337,7 @@ static inline void print_volser(const void *volser)
|
||||
|
||||
ebcdic_to_ascii((char *)volser, ascii, 6);
|
||||
ascii[6] = '\0';
|
||||
sclp_print("VOLSER=[");
|
||||
sclp_print(ascii);
|
||||
sclp_print("]\n");
|
||||
printf("VOLSER=[%s]\n", ascii);
|
||||
}
|
||||
|
||||
static inline bool unused_space(const void *p, size_t size)
|
||||
@ -387,17 +386,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
|
||||
|
||||
#define ISO_PRIMARY_VD_SECTOR 16
|
||||
|
||||
static inline void read_iso_sector(uint32_t block_offset, void *buf,
|
||||
const char *errmsg)
|
||||
{
|
||||
IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
|
||||
}
|
||||
|
||||
static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
|
||||
static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr,
|
||||
uint32_t blks_to_load)
|
||||
{
|
||||
IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
|
||||
"Failed to read boot image!");
|
||||
if (virtio_read_many(block_offset, load_addr, blks_to_load)) {
|
||||
puts("Failed to read boot image!");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ISO9660_MAX_DIR_DEPTH 8
|
||||
|
@ -11,7 +11,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
#include "helper.h"
|
||||
@ -58,7 +59,8 @@ uint16_t cu_type(SubChannelId schid)
|
||||
};
|
||||
|
||||
if (do_cio(schid, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) {
|
||||
panic("Failed to run SenseID CCw\n");
|
||||
puts("Failed to run SenseID CCW");
|
||||
return CU_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return sense_data.cu_type;
|
||||
@ -90,9 +92,9 @@ 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");
|
||||
puts("Eckd Dasd Sense Data (fmt 24-bytes):");
|
||||
} else {
|
||||
sclp_print("Eckd Dasd Sense Data (fmt 32-bytes):\n");
|
||||
puts("Eckd Dasd Sense Data (fmt 32-bytes):");
|
||||
}
|
||||
|
||||
strcat(msgline, " Sense Condition Flags :");
|
||||
@ -158,22 +160,21 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd)
|
||||
if (sd->status[1] & SNS_STAT2_IMPRECISE_END) {
|
||||
strcat(msgline, " [Imprecise-End]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(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]);
|
||||
printf(" Residual Count = 0x%X\n", sd->res_count);
|
||||
printf(" Phys Drive ID = 0x%X\n", sd->phys_drive_id);
|
||||
printf(" low cyl address = 0x%X\n", sd->low_cyl_addr);
|
||||
printf(" head addr & hi cyl = 0x%X\n", sd->head_high_cyl_addr);
|
||||
printf(" format/message = 0x%X\n", sd->fmt_msg);
|
||||
printf(" fmt-dependent[0-7] = 0x%llX\n", sd->fmt_dependent_info[0]);
|
||||
printf(" fmt-dependent[8-15]= 0x%llX\n", sd->fmt_dependent_info[1]);
|
||||
printf(" prog action code = 0x%X\n", sd->program_action_code);
|
||||
printf(" Configuration info = 0x%X\n", sd->config_info);
|
||||
printf(" mcode / hi-cyl = 0x%X\n", sd->mcode_hicyl);
|
||||
printf(" cyl & head addr [0]= 0x%X\n", sd->cyl_head_addr[0]);
|
||||
printf(" cyl & head addr [1]= 0x%X\n", sd->cyl_head_addr[1]);
|
||||
printf(" cyl & head addr [2]= 0x%X\n", sd->cyl_head_addr[2]);
|
||||
}
|
||||
|
||||
static void print_irb_err(Irb *irb)
|
||||
@ -182,7 +183,7 @@ static void print_irb_err(Irb *irb)
|
||||
uint64_t prev_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa - 8);
|
||||
char msgline[256];
|
||||
|
||||
sclp_print("Interrupt Response Block Data:\n");
|
||||
puts("Interrupt Response Block Data:");
|
||||
|
||||
strcat(msgline, " Function Ctrl :");
|
||||
if (irb->scsw.ctrl & SCSW_FCTL_START_FUNC) {
|
||||
@ -194,8 +195,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) {
|
||||
strcat(msgline, " [Clear]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Activity Ctrl :");
|
||||
@ -220,8 +220,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.ctrl & SCSW_ACTL_SUSPENDED) {
|
||||
strcat(msgline, " [Suspended]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Status Ctrl :");
|
||||
@ -240,9 +239,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.ctrl & SCSW_SCTL_STATUS_PEND) {
|
||||
strcat(msgline, " [Status-Pending]");
|
||||
}
|
||||
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Device Status :");
|
||||
@ -270,8 +267,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.dstat & SCSW_DSTAT_UEXCP) {
|
||||
strcat(msgline, " [Unit-Exception]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Channel Status :");
|
||||
@ -299,12 +295,11 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.cstat & SCSW_CSTAT_CHAINCHK) {
|
||||
strcat(msgline, " [Chaining-Check]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
print_int(" cpa=", irb->scsw.cpa);
|
||||
print_int(" prev_ccw=", prev_ccw);
|
||||
print_int(" this_ccw=", this_ccw);
|
||||
printf(" cpa= 0x%X\n", irb->scsw.cpa);
|
||||
printf(" prev_ccw= 0x%llX\n", prev_ccw);
|
||||
printf(" this_ccw= 0x%llX\n", this_ccw);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -341,7 +336,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb)
|
||||
return -1;
|
||||
}
|
||||
if (rc) {
|
||||
print_int("ssch failed with cc=", rc);
|
||||
printf("ssch failed with cc= 0x%x\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -350,7 +345,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb)
|
||||
/* collect status */
|
||||
rc = tsch(schid, irb);
|
||||
if (rc) {
|
||||
print_int("tsch failed with cc=", rc);
|
||||
printf("tsch failed with cc= 0x%X\n", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -406,12 +401,12 @@ int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt)
|
||||
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");
|
||||
printf("cio device error\n");
|
||||
printf(" ssid 0x%X\n", schid.ssid);
|
||||
printf(" cssid 0x%X\n", schid.cssid);
|
||||
printf(" sch_no 0x%X\n", schid.sch_no);
|
||||
printf(" ctrl-unit type 0x%X\n", cutype);
|
||||
printf("\n");
|
||||
print_irb_err(&irb);
|
||||
if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 ||
|
||||
cutype == CU_TYPE_UNKNOWN) {
|
||||
|
@ -361,6 +361,8 @@ typedef struct CcwSearchIdData {
|
||||
uint8_t record;
|
||||
} __attribute__((packed)) CcwSearchIdData;
|
||||
|
||||
extern SubChannelId net_schid;
|
||||
|
||||
int enable_mss_facility(void);
|
||||
void enable_subchannel(SubChannelId schid);
|
||||
uint16_t cu_type(SubChannelId schid);
|
||||
|
@ -8,7 +8,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
#include "dasd-ipl.h"
|
||||
@ -82,7 +83,7 @@ static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype,
|
||||
do {
|
||||
has_next = dynamic_cp_fixup(cpa, &next_cpa);
|
||||
|
||||
print_int("executing ccw chain at ", cpa);
|
||||
printf("executing ccw chain at 0x%X\n", cpa);
|
||||
enable_prefixing();
|
||||
rc = do_cio(schid, cutype, cpa, CCW_FMT0);
|
||||
disable_prefixing();
|
||||
@ -110,38 +111,29 @@ static void make_readipl(void)
|
||||
ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
|
||||
}
|
||||
|
||||
static void run_readipl(SubChannelId schid, uint16_t cutype)
|
||||
static int 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");
|
||||
}
|
||||
return do_cio(schid, cutype, 0x00, CCW_FMT0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static bool 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");
|
||||
}
|
||||
return (ccwread->cmd_code == CCW_CMD_DASD_READ &&
|
||||
ccwtic->cmd_code == CCW_CMD_TIC);
|
||||
}
|
||||
|
||||
static void check_ipl2(uint32_t ipl2_addr)
|
||||
static bool 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");
|
||||
}
|
||||
return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00);
|
||||
}
|
||||
|
||||
static uint32_t read_ipl2_addr(void)
|
||||
@ -187,52 +179,67 @@ static void ipl1_fixup(void)
|
||||
ccwSearchTic->cda = ptr2u32(ccwSearchID);
|
||||
}
|
||||
|
||||
static void run_ipl1(SubChannelId schid, uint16_t cutype)
|
||||
static int 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");
|
||||
}
|
||||
return do_cio(schid, cutype, startAddr, CCW_FMT0);
|
||||
}
|
||||
|
||||
static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
|
||||
static int 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");
|
||||
}
|
||||
return run_dynamic_ccw_program(schid, cutype, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Limitations in vfio-ccw support complicate the IPL process. Details can
|
||||
* be found in docs/devel/s390-dasd-ipl.rst
|
||||
*/
|
||||
void dasd_ipl(SubChannelId schid, uint16_t cutype)
|
||||
int 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);
|
||||
if (run_readipl(schid, cutype)) {
|
||||
puts("Failed to run Read IPL channel program");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ipl2_addr = read_ipl2_addr();
|
||||
check_ipl1();
|
||||
|
||||
if (!check_ipl1()) {
|
||||
puts("IPL1 invalid for DASD-IPL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (run_ipl1(schid, cutype)) {
|
||||
puts("Failed to run IPL1 channel program");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!check_ipl2(ipl2_addr)) {
|
||||
puts("IPL2 invalid for DASD-IPL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run IPL2 channel program to read operating system code from boot disk
|
||||
*/
|
||||
run_ipl2(schid, cutype, ipl2_addr);
|
||||
if (run_ipl2(schid, cutype, ipl2_addr)) {
|
||||
puts("Failed to run IPL2 channel program");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Transfer control to the guest operating system */
|
||||
pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */
|
||||
pswl->addr |= PSW_MASK_BAMODE; /* ... */
|
||||
jump_to_low_kernel();
|
||||
return -1;
|
||||
}
|
||||
|
@ -11,6 +11,6 @@
|
||||
#ifndef DASD_IPL_H
|
||||
#define DASD_IPL_H
|
||||
|
||||
void dasd_ipl(SubChannelId schid, uint16_t cutype);
|
||||
int dasd_ipl(SubChannelId schid, uint16_t cutype);
|
||||
|
||||
#endif /* DASD_IPL_H */
|
||||
|
@ -12,88 +12,16 @@
|
||||
#ifndef IPLB_H
|
||||
#define IPLB_H
|
||||
|
||||
#define LOADPARM_LEN 8
|
||||
#ifndef QEMU_PACKED
|
||||
#define QEMU_PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved3[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved4[8];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplBlockCcw IplBlockCcw;
|
||||
|
||||
struct IplBlockFcp {
|
||||
uint8_t reserved1[305 - 1];
|
||||
uint8_t opt;
|
||||
uint8_t reserved2[3];
|
||||
uint16_t reserved3;
|
||||
uint16_t devno;
|
||||
uint8_t reserved4[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved5[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
struct IplBlockQemuScsi {
|
||||
uint32_t lun;
|
||||
uint16_t target;
|
||||
uint16_t channel;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
|
||||
|
||||
struct IplParameterBlock {
|
||||
uint32_t len;
|
||||
uint8_t reserved0[3];
|
||||
uint8_t version;
|
||||
uint32_t blk0_len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint16_t reserved01;
|
||||
uint8_t loadparm[LOADPARM_LEN];
|
||||
union {
|
||||
IplBlockCcw ccw;
|
||||
IplBlockFcp fcp;
|
||||
IplBlockQemuScsi scsi;
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplParameterBlock IplParameterBlock;
|
||||
|
||||
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
|
||||
#define QIPL_ADDRESS 0xcc
|
||||
|
||||
/* Boot Menu flags */
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
/*
|
||||
* This definition must be kept in sync with the definition
|
||||
* in hw/s390x/ipl.h
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t reserved1[3];
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved2[12];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
#include <qipl.h>
|
||||
#include <string.h>
|
||||
|
||||
extern QemuIplParameters qipl;
|
||||
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
extern bool have_iplb;
|
||||
|
||||
#define S390_IPL_TYPE_FCP 0x00
|
||||
#define S390_IPL_TYPE_CCW 0x02
|
||||
@ -123,4 +51,26 @@ static inline bool set_iplb(IplParameterBlock *iplb)
|
||||
return manage_iplb(iplb, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* The IPL started on the device, but failed in some way. If the IPLB chain
|
||||
* still has more devices left to try, use the next device in order.
|
||||
*/
|
||||
static inline bool load_next_iplb(void)
|
||||
{
|
||||
IplParameterBlock *next_iplb;
|
||||
|
||||
if (qipl.chain_len < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qipl.index++;
|
||||
next_iplb = (IplParameterBlock *) qipl.next_iplb;
|
||||
memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
|
||||
|
||||
qipl.chain_len--;
|
||||
qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* IPLB_H */
|
||||
|
@ -6,7 +6,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
|
||||
@ -32,16 +33,22 @@ static void jump_to_IPL_addr(void)
|
||||
/* should not return */
|
||||
}
|
||||
|
||||
void jump_to_IPL_code(uint64_t address)
|
||||
int jump_to_IPL_code(uint64_t address)
|
||||
{
|
||||
/* store the subsystem information _after_ the bootmap was loaded */
|
||||
write_subsystem_identification();
|
||||
write_iplb_location();
|
||||
|
||||
/* prevent unknown IPL types in the guest */
|
||||
/*
|
||||
* The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The
|
||||
* iplb.devno is set to the boot position of the target SCSI device.
|
||||
*/
|
||||
if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
|
||||
iplb.pbt = S390_IPL_TYPE_CCW;
|
||||
set_iplb(&iplb);
|
||||
iplb.devno = qipl.index;
|
||||
}
|
||||
|
||||
if (have_iplb && !set_iplb(&iplb)) {
|
||||
panic("Failed to set IPLB");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -57,7 +64,7 @@ void jump_to_IPL_code(uint64_t address)
|
||||
debug_print_int("set IPL addr to", address ?: *reset_psw & PSW_MASK_SHORT_ADDR);
|
||||
|
||||
/* Ensure the guest output starts fresh */
|
||||
sclp_print("\n");
|
||||
printf("\n");
|
||||
|
||||
/*
|
||||
* HACK ALERT.
|
||||
@ -67,7 +74,8 @@ void jump_to_IPL_code(uint64_t address)
|
||||
asm volatile("lghi %%r1,1\n\t"
|
||||
"diag %%r1,%%r1,0x308\n\t"
|
||||
: : : "1", "memory");
|
||||
panic("\n! IPL returns !\n");
|
||||
puts("IPL code jump failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void jump_to_low_kernel(void)
|
||||
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* libc-style definitions and functions
|
||||
*
|
||||
* Copyright 2018 IBM Corp.
|
||||
* Author(s): Collin L. Walling <walling@linux.vnet.ibm.com>
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include "s390-ccw.h"
|
||||
|
||||
/**
|
||||
* atoui:
|
||||
* @str: the string to be converted.
|
||||
*
|
||||
* Given a string @str, convert it to an integer. Leading spaces are
|
||||
* ignored. Any other non-numerical value will terminate the conversion
|
||||
* and return 0. This function only handles numbers between 0 and
|
||||
* UINT64_MAX inclusive.
|
||||
*
|
||||
* Returns: an integer converted from the string @str, or the number 0
|
||||
* if an error occurred.
|
||||
*/
|
||||
uint64_t atoui(const char *str)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (!str || !str[0]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*str == ' ') {
|
||||
str++;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
if (!isdigit(*(unsigned char *)str)) {
|
||||
break;
|
||||
}
|
||||
val = val * 10 + *str - '0';
|
||||
str++;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* uitoa:
|
||||
* @num: an integer (base 10) to be converted.
|
||||
* @str: a pointer to a string to store the conversion.
|
||||
* @len: the length of the passed string.
|
||||
*
|
||||
* Given an integer @num, convert it to a string. The string @str must be
|
||||
* allocated beforehand. The resulting string will be null terminated and
|
||||
* returned. This function only handles numbers between 0 and UINT64_MAX
|
||||
* inclusive.
|
||||
*
|
||||
* Returns: the string @str of the converted integer @num
|
||||
*/
|
||||
char *uitoa(uint64_t num, char *str, size_t len)
|
||||
{
|
||||
long num_idx = 1; /* account for NUL */
|
||||
uint64_t tmp = num;
|
||||
|
||||
IPL_assert(str != NULL, "uitoa: no space allocated to store string");
|
||||
|
||||
/* Count indices of num */
|
||||
while ((tmp /= 10) != 0) {
|
||||
num_idx++;
|
||||
}
|
||||
|
||||
/* Check if we have enough space for num and NUL */
|
||||
IPL_assert(len > num_idx, "uitoa: array too small for conversion");
|
||||
|
||||
str[num_idx--] = '\0';
|
||||
|
||||
/* Convert int to string */
|
||||
while (num_idx >= 0) {
|
||||
str[num_idx--] = num % 10 + '0';
|
||||
num /= 10;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* libc-style definitions and functions
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef S390_CCW_LIBC_H
|
||||
#define S390_CCW_LIBC_H
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef int bool;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
static inline void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
unsigned char *p = s;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
p[i] = c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline void *memcpy(void *s1, const void *s2, size_t n)
|
||||
{
|
||||
uint8_t *dest = s1;
|
||||
const uint8_t *src = s2;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
dest[i] = src[i];
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
static inline int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
const uint8_t *p1 = s1, *p2 = s2;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
return p1[i] > p2[i] ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline size_t strlen(const char *str)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; *str; i++) {
|
||||
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');
|
||||
}
|
||||
|
||||
uint64_t atoui(const char *str);
|
||||
char *uitoa(uint64_t num, char *str, size_t len);
|
||||
|
||||
#endif
|
@ -8,7 +8,9 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "helper.h"
|
||||
#include "s390-arch.h"
|
||||
#include "s390-ccw.h"
|
||||
@ -21,7 +23,7 @@ static SubChannelId blk_schid = { .one = 1 };
|
||||
static char loadparm_str[LOADPARM_LEN + 1];
|
||||
QemuIplParameters qipl;
|
||||
IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
static bool have_iplb;
|
||||
bool have_iplb;
|
||||
static uint16_t cutype;
|
||||
LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
|
||||
|
||||
@ -36,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
|
||||
*/
|
||||
void write_subsystem_identification(void)
|
||||
{
|
||||
lowcore->subchannel_id = blk_schid.sch_id;
|
||||
lowcore->subchannel_nr = blk_schid.sch_no;
|
||||
if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) {
|
||||
lowcore->subchannel_id = net_schid.sch_id;
|
||||
lowcore->subchannel_nr = net_schid.sch_no;
|
||||
} else {
|
||||
lowcore->subchannel_id = blk_schid.sch_id;
|
||||
lowcore->subchannel_nr = blk_schid.sch_no;
|
||||
}
|
||||
lowcore->io_int_parm = 0;
|
||||
}
|
||||
|
||||
@ -48,9 +55,15 @@ void write_iplb_location(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_qipl(void)
|
||||
{
|
||||
QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
|
||||
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
|
||||
}
|
||||
|
||||
unsigned int get_loadparm_index(void)
|
||||
{
|
||||
return atoui(loadparm_str);
|
||||
return atoi(loadparm_str);
|
||||
}
|
||||
|
||||
static int is_dev_possibly_bootable(int dev_no, int sch_no)
|
||||
@ -70,6 +83,9 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
|
||||
|
||||
enable_subchannel(blk_schid);
|
||||
cutype = cu_type(blk_schid);
|
||||
if (cutype == CU_TYPE_UNKNOWN) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: we always have to run virtio_is_supported() here to make
|
||||
@ -142,6 +158,7 @@ static void menu_setup(void)
|
||||
|
||||
/* If loadparm was set to any other value, then do not enable menu */
|
||||
if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) {
|
||||
menu_set_parms(qipl.qipl_flags & ~BOOT_MENU_FLAG_MASK, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -174,26 +191,34 @@ static void boot_setup(void)
|
||||
{
|
||||
char lpmsg[] = "LOADPARM=[________]\n";
|
||||
|
||||
sclp_get_loadparm_ascii(loadparm_str);
|
||||
if (memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
|
||||
ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
|
||||
} else {
|
||||
sclp_get_loadparm_ascii(loadparm_str);
|
||||
}
|
||||
|
||||
if (have_iplb) {
|
||||
menu_setup();
|
||||
}
|
||||
|
||||
memcpy(lpmsg + 10, loadparm_str, 8);
|
||||
sclp_print(lpmsg);
|
||||
puts(lpmsg);
|
||||
|
||||
/*
|
||||
* Clear out any potential S390EP magic (see jump_to_low_kernel()),
|
||||
* so we don't taint our decision-making process during a reboot.
|
||||
*/
|
||||
memset((char *)S390EP, 0, 6);
|
||||
|
||||
have_iplb = store_iplb(&iplb);
|
||||
}
|
||||
|
||||
static void find_boot_device(void)
|
||||
static bool find_boot_device(void)
|
||||
{
|
||||
VDev *vdev = virtio_get_device();
|
||||
bool found;
|
||||
bool found = false;
|
||||
|
||||
switch (iplb.pbt) {
|
||||
case S390_IPL_TYPE_CCW:
|
||||
vdev->scsi_device_selected = false;
|
||||
debug_print_int("device no. ", iplb.ccw.devno);
|
||||
blk_schid.ssid = iplb.ccw.ssid & 0x3;
|
||||
debug_print_int("ssid ", blk_schid.ssid);
|
||||
@ -208,28 +233,20 @@ static void find_boot_device(void)
|
||||
found = find_subch(iplb.scsi.devno);
|
||||
break;
|
||||
default:
|
||||
panic("List-directed IPL not supported yet!\n");
|
||||
puts("Unsupported IPLB");
|
||||
}
|
||||
|
||||
IPL_assert(found, "Boot device not found\n");
|
||||
return found;
|
||||
}
|
||||
|
||||
static int virtio_setup(void)
|
||||
{
|
||||
VDev *vdev = virtio_get_device();
|
||||
QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS;
|
||||
int ret;
|
||||
|
||||
memcpy(&qipl, early_qipl, sizeof(QemuIplParameters));
|
||||
|
||||
if (have_iplb) {
|
||||
menu_setup();
|
||||
}
|
||||
|
||||
switch (vdev->senseid.cu_model) {
|
||||
case VIRTIO_ID_NET:
|
||||
sclp_print("Network boot device detected\n");
|
||||
vdev->netboot_start_addr = qipl.netboot_start_addr;
|
||||
puts("Network boot device detected");
|
||||
return 0;
|
||||
case VIRTIO_ID_BLOCK:
|
||||
ret = virtio_blk_setup_device(blk_schid);
|
||||
@ -238,11 +255,13 @@ static int virtio_setup(void)
|
||||
ret = virtio_scsi_setup_device(blk_schid);
|
||||
break;
|
||||
default:
|
||||
panic("\n! No IPL device available !\n");
|
||||
puts("\n! No IPL device available !\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected");
|
||||
if (!ret && !virtio_ipl_disk_is_valid()) {
|
||||
puts("No valid IPL device detected");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -253,16 +272,15 @@ static void ipl_boot_device(void)
|
||||
switch (cutype) {
|
||||
case CU_TYPE_DASD_3990:
|
||||
case CU_TYPE_DASD_2107:
|
||||
dasd_ipl(blk_schid, cutype); /* no return */
|
||||
dasd_ipl(blk_schid, cutype);
|
||||
break;
|
||||
case CU_TYPE_VIRTIO:
|
||||
if (virtio_setup() == 0) {
|
||||
zipl_load(); /* Only returns in case of errors */
|
||||
zipl_load();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print_int("Attempting to boot from unexpected device type", cutype);
|
||||
panic("\nBoot failed.\n");
|
||||
printf("Attempting to boot from unexpected device type 0x%X\n", cutype);
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,20 +305,27 @@ static void probe_boot_device(void)
|
||||
}
|
||||
}
|
||||
|
||||
sclp_print("Could not find a suitable boot device (none specified)\n");
|
||||
puts("Could not find a suitable boot device (none specified)");
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
copy_qipl();
|
||||
sclp_setup();
|
||||
css_setup();
|
||||
boot_setup();
|
||||
if (have_iplb) {
|
||||
find_boot_device();
|
||||
ipl_boot_device();
|
||||
} else {
|
||||
have_iplb = store_iplb(&iplb);
|
||||
if (!have_iplb) {
|
||||
probe_boot_device();
|
||||
}
|
||||
|
||||
panic("Failed to load OS from hard disk\n");
|
||||
while (have_iplb) {
|
||||
boot_setup();
|
||||
if (have_iplb && find_boot_device()) {
|
||||
ipl_boot_device();
|
||||
}
|
||||
have_iplb = load_next_iplb();
|
||||
}
|
||||
|
||||
panic("No suitable device for IPL. Halting...");
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,10 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "sclp.h"
|
||||
#include "s390-time.h"
|
||||
@ -93,7 +96,7 @@ static int read_prompt(char *buf, size_t len)
|
||||
case KEYCODE_BACKSP:
|
||||
if (idx > 0) {
|
||||
buf[--idx] = 0;
|
||||
sclp_print("\b \b");
|
||||
printf("\b \b");
|
||||
}
|
||||
continue;
|
||||
case KEYCODE_ENTER:
|
||||
@ -103,7 +106,7 @@ static int read_prompt(char *buf, size_t len)
|
||||
/* Echo input and add to buffer */
|
||||
if (idx < len) {
|
||||
buf[idx++] = inp[0];
|
||||
sclp_print(inp);
|
||||
printf("%s", inp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,22 +143,19 @@ static int get_index(void)
|
||||
}
|
||||
}
|
||||
|
||||
return atoui(buf);
|
||||
return atoi(buf);
|
||||
}
|
||||
|
||||
static void boot_menu_prompt(bool retry)
|
||||
{
|
||||
char tmp[11];
|
||||
|
||||
if (retry) {
|
||||
sclp_print("\nError: undefined configuration"
|
||||
printf("\nError: undefined configuration"
|
||||
"\nPlease choose:\n");
|
||||
} else if (timeout > 0) {
|
||||
sclp_print("Please choose (default will boot in ");
|
||||
sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
|
||||
sclp_print(" seconds):\n");
|
||||
printf("Please choose (default will boot in %d seconds):\n",
|
||||
(int)(timeout / 1000));
|
||||
} else {
|
||||
sclp_print("Please choose:\n");
|
||||
puts("Please choose:");
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +163,6 @@ static int get_boot_index(bool *valid_entries)
|
||||
{
|
||||
int boot_index;
|
||||
bool retry = false;
|
||||
char tmp[5];
|
||||
|
||||
do {
|
||||
boot_menu_prompt(retry);
|
||||
@ -172,8 +171,7 @@ static int get_boot_index(bool *valid_entries)
|
||||
} while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES ||
|
||||
!valid_entries[boot_index]);
|
||||
|
||||
sclp_print("\nBooting entry #");
|
||||
sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
|
||||
printf("\nBooting entry #%d", boot_index);
|
||||
|
||||
return boot_index;
|
||||
}
|
||||
@ -187,9 +185,9 @@ static int zipl_print_entry(const char *data, size_t len)
|
||||
buf[len] = '\n';
|
||||
buf[len + 1] = '\0';
|
||||
|
||||
sclp_print(buf);
|
||||
printf("%s", buf);
|
||||
|
||||
return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf);
|
||||
return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf);
|
||||
}
|
||||
|
||||
int menu_get_zipl_boot_index(const char *menu_data)
|
||||
@ -209,7 +207,7 @@ int menu_get_zipl_boot_index(const char *menu_data)
|
||||
}
|
||||
|
||||
/* Print banner */
|
||||
sclp_print("s390-ccw zIPL Boot Menu\n\n");
|
||||
puts("s390-ccw zIPL Boot Menu\n");
|
||||
menu_data += strlen(menu_data) + 1;
|
||||
|
||||
/* Print entries */
|
||||
@ -221,37 +219,34 @@ int menu_get_zipl_boot_index(const char *menu_data)
|
||||
valid_entries[entry] = true;
|
||||
|
||||
if (entry == 0) {
|
||||
sclp_print("\n");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sclp_print("\n");
|
||||
printf("\n");
|
||||
return get_boot_index(valid_entries);
|
||||
}
|
||||
|
||||
int menu_get_enum_boot_index(bool *valid_entries)
|
||||
{
|
||||
char tmp[3];
|
||||
int i;
|
||||
|
||||
sclp_print("s390-ccw Enumerated Boot Menu.\n\n");
|
||||
puts("s390-ccw Enumerated Boot Menu.\n");
|
||||
|
||||
for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
|
||||
if (valid_entries[i]) {
|
||||
if (i < 10) {
|
||||
sclp_print(" ");
|
||||
printf(" ");
|
||||
}
|
||||
sclp_print("[");
|
||||
sclp_print(uitoa(i, tmp, sizeof(tmp)));
|
||||
sclp_print("]");
|
||||
printf("[%d]", i);
|
||||
if (i == 0) {
|
||||
sclp_print(" default\n");
|
||||
printf(" default\n");
|
||||
}
|
||||
sclp_print("\n");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sclp_print("\n");
|
||||
printf("\n");
|
||||
return get_boot_index(valid_entries);
|
||||
}
|
||||
|
||||
|
@ -1,62 +0,0 @@
|
||||
|
||||
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
|
||||
|
||||
NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o
|
||||
|
||||
LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
|
||||
LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
|
||||
|
||||
NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x7800000
|
||||
|
||||
$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
|
||||
|
||||
s390-netboot.elf: $(NETOBJS) libnet.a libc.a
|
||||
$(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking)
|
||||
|
||||
s390-netboot.img: s390-netboot.elf
|
||||
$(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into)
|
||||
|
||||
# libc files:
|
||||
|
||||
LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
|
||||
-MMD -MP -MT $@ -MF $(@:%.o=%.d)
|
||||
|
||||
CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
|
||||
strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
|
||||
memset.o memcpy.o memmove.o memcmp.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/string/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
|
||||
printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
sbrk.o: $(SLOF_DIR)/slof/sbrk.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
|
||||
|
||||
libc.a: $(LIBCOBJS)
|
||||
$(call quiet-command,$(AR) -rc $@ $^,Creating static library)
|
||||
|
||||
# libnet files:
|
||||
|
||||
LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
|
||||
dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
|
||||
LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
|
||||
-DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
|
||||
|
||||
%.o : $(SLOF_DIR)/lib/libnet/%.c
|
||||
$(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
libnet.a: $(LIBNETOBJS)
|
||||
$(call quiet-command,$(AR) -rc $@ $^,Creating static library)
|
@ -41,7 +41,6 @@
|
||||
#define DEFAULT_TFTP_RETRIES 20
|
||||
|
||||
extern char _start[];
|
||||
void write_iplb_location(void) {}
|
||||
|
||||
#define KERNEL_ADDR ((void *)0L)
|
||||
#define KERNEL_MAX_SIZE ((long)_start)
|
||||
@ -50,10 +49,9 @@ void write_iplb_location(void) {}
|
||||
/* STSI 3.2.2 offset of first vmdb + offset of uuid inside vmdb */
|
||||
#define STSI322_VMDB_UUID_OFFSET ((8 + 12) * 4)
|
||||
|
||||
IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE)));
|
||||
static char cfgbuf[2048];
|
||||
|
||||
static SubChannelId net_schid = { .one = 1 };
|
||||
SubChannelId net_schid = { .one = 1 };
|
||||
static uint8_t mac[6];
|
||||
static uint64_t dest_timer;
|
||||
|
||||
@ -293,7 +291,7 @@ static int load_kernel_with_initrd(filename_ip_t *fn_ip,
|
||||
printf("Loading pxelinux.cfg entry '%s'\n", entry->label);
|
||||
|
||||
if (!entry->kernel) {
|
||||
printf("Kernel entry is missing!\n");
|
||||
puts("Kernel entry is missing!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -438,15 +436,6 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
void write_subsystem_identification(void)
|
||||
{
|
||||
SubChannelId *schid = (SubChannelId *) 184;
|
||||
uint32_t *zeroes = (uint32_t *) 188;
|
||||
|
||||
*schid = net_schid;
|
||||
*zeroes = 0;
|
||||
}
|
||||
|
||||
static bool find_net_dev(Schib *schib, int dev_no)
|
||||
{
|
||||
int i, r;
|
||||
@ -475,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void virtio_setup(void)
|
||||
static bool virtio_setup(void)
|
||||
{
|
||||
Schib schib;
|
||||
int ssid;
|
||||
@ -489,7 +478,7 @@ static void virtio_setup(void)
|
||||
*/
|
||||
enable_mss_facility();
|
||||
|
||||
if (store_iplb(&iplb)) {
|
||||
if (have_iplb || store_iplb(&iplb)) {
|
||||
IPL_assert(iplb.pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected");
|
||||
dev_no = iplb.ccw.devno;
|
||||
debug_print_int("device no. ", dev_no);
|
||||
@ -506,22 +495,26 @@ static void virtio_setup(void)
|
||||
}
|
||||
}
|
||||
|
||||
IPL_assert(found, "No virtio net device found");
|
||||
return found;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
int netmain(void)
|
||||
{
|
||||
filename_ip_t fn_ip;
|
||||
int rc, fnlen;
|
||||
|
||||
sclp_setup();
|
||||
sclp_print("Network boot starting...\n");
|
||||
puts("Network boot starting...");
|
||||
|
||||
virtio_setup();
|
||||
if (!virtio_setup()) {
|
||||
puts("No virtio net device found.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = net_init(&fn_ip);
|
||||
if (rc) {
|
||||
panic("Network initialization failed. Halting.\n");
|
||||
puts("Network initialization failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fnlen = strlen(fn_ip.filename);
|
||||
@ -535,9 +528,10 @@ void main(void)
|
||||
net_release(&fn_ip);
|
||||
|
||||
if (rc > 0) {
|
||||
sclp_print("Network loading done, starting kernel...\n");
|
||||
puts("Network loading done, starting kernel...");
|
||||
jump_to_low_kernel();
|
||||
}
|
||||
|
||||
panic("Failed to load OS from network\n");
|
||||
puts("Failed to load OS from network.");
|
||||
return -1;
|
||||
}
|
||||
|
@ -13,6 +13,11 @@
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
@ -25,10 +30,8 @@ typedef unsigned long long u64;
|
||||
#define EIO 1
|
||||
#define EBUSY 2
|
||||
#define ENODEV 3
|
||||
#define EINVAL 4
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
@ -53,6 +56,9 @@ void write_iplb_location(void);
|
||||
unsigned int get_loadparm_index(void);
|
||||
void main(void);
|
||||
|
||||
/* netmain.c */
|
||||
int netmain(void);
|
||||
|
||||
/* sclp.c */
|
||||
void sclp_print(const char *string);
|
||||
void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
|
||||
@ -72,7 +78,7 @@ void zipl_load(void);
|
||||
|
||||
/* jump2ipl.c */
|
||||
void write_reset_psw(uint64_t psw);
|
||||
void jump_to_IPL_code(uint64_t address);
|
||||
int jump_to_IPL_code(uint64_t address);
|
||||
void jump_to_low_kernel(void);
|
||||
|
||||
/* menu.c */
|
||||
@ -87,7 +93,7 @@ bool menu_is_enabled_enum(void);
|
||||
__attribute__ ((__noreturn__))
|
||||
static inline void panic(const char *string)
|
||||
{
|
||||
sclp_print(string);
|
||||
printf("ERROR: %s\n ", string);
|
||||
disabled_wait();
|
||||
}
|
||||
|
||||
@ -109,20 +115,10 @@ static inline void fill_hex_val(char *out, void *ptr, unsigned size)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void print_int(const char *desc, u64 addr)
|
||||
{
|
||||
char out[] = ": 0xffffffffffffffff\n";
|
||||
|
||||
fill_hex_val(&out[4], &addr, sizeof(addr));
|
||||
|
||||
sclp_print(desc);
|
||||
sclp_print(out);
|
||||
}
|
||||
|
||||
static inline void debug_print_int(const char *desc, u64 addr)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
print_int(desc, addr);
|
||||
printf("%s 0x%X\n", desc, addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -147,18 +143,14 @@ static inline void debug_print_addr(const char *desc, void *p)
|
||||
static inline void IPL_assert(bool term, const char *message)
|
||||
{
|
||||
if (!term) {
|
||||
sclp_print("\n! ");
|
||||
sclp_print(message);
|
||||
panic(" !\n"); /* no return */
|
||||
panic(message); /* no return */
|
||||
}
|
||||
}
|
||||
|
||||
static inline void IPL_check(bool term, const char *message)
|
||||
{
|
||||
if (!term) {
|
||||
sclp_print("\n! WARNING: ");
|
||||
sclp_print(message);
|
||||
sclp_print(" !\n");
|
||||
printf("WARNING: %s\n", message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "sclp.h"
|
||||
|
||||
@ -101,11 +101,6 @@ long write(int fd, const void *str, size_t len)
|
||||
return len;
|
||||
}
|
||||
|
||||
void sclp_print(const char *str)
|
||||
{
|
||||
write(1, str, strlen(str));
|
||||
}
|
||||
|
||||
void sclp_get_loadparm_ascii(char *loadparm)
|
||||
{
|
||||
|
||||
|
@ -112,9 +112,7 @@ io_new_code:
|
||||
lctlg %c6,%c6,0(%r15)
|
||||
br %r14
|
||||
|
||||
.align 8
|
||||
bss_start_literal:
|
||||
.quad __bss_start
|
||||
.balign 8
|
||||
disabled_wait_psw:
|
||||
.quad 0x0002000180000000,0x0000000000000000
|
||||
enabled_wait_psw:
|
||||
@ -124,8 +122,13 @@ external_new_mask:
|
||||
io_new_mask:
|
||||
.quad 0x0000000180000000
|
||||
|
||||
.data
|
||||
.balign 8
|
||||
bss_start_literal:
|
||||
.quad __bss_start
|
||||
|
||||
.bss
|
||||
.align 8
|
||||
.balign 8
|
||||
stack:
|
||||
.space STACK_SIZE
|
||||
.size stack,STACK_SIZE
|
||||
|
@ -8,7 +8,7 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
#include "virtio-scsi.h"
|
||||
@ -59,7 +59,7 @@ int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
|
||||
case VIRTIO_ID_SCSI:
|
||||
return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
|
||||
}
|
||||
panic("\n! No readable IPL device !\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -73,13 +73,13 @@ unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list
|
||||
unsigned long addr = (unsigned long)load_addr;
|
||||
|
||||
if (sec_len != virtio_get_block_size()) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sclp_print(".");
|
||||
printf(".");
|
||||
status = virtio_read_many(sec, (void *)addr, sec_num);
|
||||
if (status) {
|
||||
panic("I/O Error");
|
||||
return 0;
|
||||
}
|
||||
addr += sec_num * virtio_get_block_size();
|
||||
|
||||
@ -230,7 +230,7 @@ int virtio_blk_setup_device(SubChannelId schid)
|
||||
vdev->schid = schid;
|
||||
virtio_setup_ccw(vdev);
|
||||
|
||||
sclp_print("Using virtio-blk.\n");
|
||||
puts("Using virtio-blk.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -54,8 +54,11 @@ int virtio_net_init(void *mac_addr)
|
||||
vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
|
||||
virtio_setup_ccw(vdev);
|
||||
|
||||
IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT,
|
||||
"virtio-net device does not support the MAC address feature");
|
||||
if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
|
||||
puts("virtio-net device does not support the MAC address feature");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
|
@ -9,7 +9,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
#include "scsi.h"
|
||||
@ -25,20 +26,22 @@ static uint8_t scsi_inquiry_std_response[256];
|
||||
static ScsiInquiryEvpdPages scsi_inquiry_evpd_pages_response;
|
||||
static ScsiInquiryEvpdBl scsi_inquiry_evpd_bl_response;
|
||||
|
||||
static inline void vs_assert(bool term, const char **msgs)
|
||||
static inline bool vs_assert(bool term, const char **msgs)
|
||||
{
|
||||
if (!term) {
|
||||
int i = 0;
|
||||
|
||||
sclp_print("\n! ");
|
||||
printf("\n! ");
|
||||
while (msgs[i]) {
|
||||
sclp_print(msgs[i++]);
|
||||
printf("%s", msgs[i++]);
|
||||
}
|
||||
panic(" !\n");
|
||||
puts(" !");
|
||||
}
|
||||
|
||||
return term;
|
||||
}
|
||||
|
||||
static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp,
|
||||
static bool virtio_scsi_verify_response(VirtioScsiCmdResp *resp,
|
||||
const char *title)
|
||||
{
|
||||
const char *mr[] = {
|
||||
@ -55,8 +58,8 @@ static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp,
|
||||
0
|
||||
};
|
||||
|
||||
vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr);
|
||||
vs_assert(resp->status == CDB_STATUS_GOOD, ms);
|
||||
return vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr) &&
|
||||
vs_assert(resp->status == CDB_STATUS_GOOD, ms);
|
||||
}
|
||||
|
||||
static void prepare_request(VDev *vdev, const void *cdb, int cdb_size,
|
||||
@ -77,24 +80,31 @@ static void prepare_request(VDev *vdev, const void *cdb, int cdb_size,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void vs_io_assert(bool term, const char *msg)
|
||||
static inline bool vs_io_assert(bool term, const char *msg)
|
||||
{
|
||||
if (!term) {
|
||||
virtio_scsi_verify_response(&resp, msg);
|
||||
if (!term && !virtio_scsi_verify_response(&resp, msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev,
|
||||
static int vs_run(const char *title, VirtioCmd *cmd, VDev *vdev,
|
||||
const void *cdb, int cdb_size,
|
||||
void *data, uint32_t data_size)
|
||||
{
|
||||
prepare_request(vdev, cdb, cdb_size, data, data_size);
|
||||
vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title);
|
||||
if (!vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title)) {
|
||||
puts(title);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* SCSI protocol implementation routines */
|
||||
|
||||
static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page,
|
||||
static int scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page,
|
||||
void *data, uint32_t data_size)
|
||||
{
|
||||
ScsiCdbInquiry cdb = {
|
||||
@ -109,12 +119,13 @@ static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page,
|
||||
{ data, data_size, VRING_DESC_F_WRITE },
|
||||
};
|
||||
|
||||
vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size);
|
||||
int ret = vs_run("inquiry", inquiry,
|
||||
vdev, &cdb, sizeof(cdb), data, data_size);
|
||||
|
||||
return virtio_scsi_response_ok(&resp);
|
||||
return ret ? ret : virtio_scsi_response_ok(&resp);
|
||||
}
|
||||
|
||||
static bool scsi_test_unit_ready(VDev *vdev)
|
||||
static int scsi_test_unit_ready(VDev *vdev)
|
||||
{
|
||||
ScsiCdbTestUnitReady cdb = {
|
||||
.command = 0x00,
|
||||
@ -130,7 +141,7 @@ static bool scsi_test_unit_ready(VDev *vdev)
|
||||
return virtio_scsi_response_ok(&resp);
|
||||
}
|
||||
|
||||
static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size)
|
||||
static int scsi_report_luns(VDev *vdev, void *data, uint32_t data_size)
|
||||
{
|
||||
ScsiCdbReportLuns cdb = {
|
||||
.command = 0xa0,
|
||||
@ -143,13 +154,13 @@ static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size)
|
||||
{ data, data_size, VRING_DESC_F_WRITE },
|
||||
};
|
||||
|
||||
vs_run("report luns", report_luns,
|
||||
int ret = vs_run("report luns", report_luns,
|
||||
vdev, &cdb, sizeof(cdb), data, data_size);
|
||||
|
||||
return virtio_scsi_response_ok(&resp);
|
||||
return ret ? ret : virtio_scsi_response_ok(&resp);
|
||||
}
|
||||
|
||||
static bool scsi_read_10(VDev *vdev,
|
||||
static int scsi_read_10(VDev *vdev,
|
||||
unsigned long sector, int sectors, void *data,
|
||||
unsigned int data_size)
|
||||
{
|
||||
@ -167,12 +178,13 @@ static bool scsi_read_10(VDev *vdev,
|
||||
debug_print_int("read_10 sector", sector);
|
||||
debug_print_int("read_10 sectors", sectors);
|
||||
|
||||
vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size);
|
||||
int ret = vs_run("read(10)", read_10,
|
||||
vdev, &cdb, sizeof(cdb), data, data_size);
|
||||
|
||||
return virtio_scsi_response_ok(&resp);
|
||||
return ret ? ret : virtio_scsi_response_ok(&resp);
|
||||
}
|
||||
|
||||
static bool scsi_read_capacity(VDev *vdev,
|
||||
static int scsi_read_capacity(VDev *vdev,
|
||||
void *data, uint32_t data_size)
|
||||
{
|
||||
ScsiCdbReadCapacity16 cdb = {
|
||||
@ -186,10 +198,10 @@ static bool scsi_read_capacity(VDev *vdev,
|
||||
{ data, data_size, VRING_DESC_F_WRITE },
|
||||
};
|
||||
|
||||
vs_run("read capacity", read_capacity_16,
|
||||
int ret = vs_run("read capacity", read_capacity_16,
|
||||
vdev, &cdb, sizeof(cdb), data, data_size);
|
||||
|
||||
return virtio_scsi_response_ok(&resp);
|
||||
return ret ? ret : virtio_scsi_response_ok(&resp);
|
||||
}
|
||||
|
||||
/* virtio-scsi routines */
|
||||
@ -206,7 +218,7 @@ static int virtio_scsi_locate_device(VDev *vdev)
|
||||
static uint8_t data[16 + 8 * 63];
|
||||
ScsiLunReport *r = (void *) data;
|
||||
ScsiDevice *sdev = vdev->scsi_device;
|
||||
int i, luns;
|
||||
int i, ret, luns;
|
||||
|
||||
/* QEMU has hardcoded channel #0 in many places.
|
||||
* If this hardcoded value is ever changed, we'll need to add code for
|
||||
@ -232,15 +244,23 @@ static int virtio_scsi_locate_device(VDev *vdev)
|
||||
sdev->channel = channel;
|
||||
sdev->target = target;
|
||||
sdev->lun = 0; /* LUN has to be 0 for REPORT LUNS */
|
||||
if (!scsi_report_luns(vdev, data, sizeof(data))) {
|
||||
ret = scsi_report_luns(vdev, data, sizeof(data));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
else if (ret == 0) {
|
||||
if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) {
|
||||
continue;
|
||||
}
|
||||
print_int("target", target);
|
||||
virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs");
|
||||
printf("target 0x%X\n", target);
|
||||
if (!virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs")) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->lun_list_len == 0) {
|
||||
print_int("no LUNs for target", target);
|
||||
printf("no LUNs for target 0x%X\n", target);
|
||||
continue;
|
||||
}
|
||||
luns = r->lun_list_len / 8;
|
||||
@ -264,7 +284,7 @@ static int virtio_scsi_locate_device(VDev *vdev)
|
||||
}
|
||||
}
|
||||
|
||||
sclp_print("Warning: Could not locate a usable virtio-scsi device\n");
|
||||
puts("Warning: Could not locate a usable virtio-scsi device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -282,7 +302,9 @@ int virtio_scsi_read_many(VDev *vdev,
|
||||
data_size = sector_count * virtio_get_block_size() * f;
|
||||
if (!scsi_read_10(vdev, sector * f, sector_count * f, load_addr,
|
||||
data_size)) {
|
||||
virtio_scsi_verify_response(&resp, "virtio-scsi:read_many");
|
||||
if (!virtio_scsi_verify_response(&resp, "virtio-scsi:read_many")) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
load_addr += data_size;
|
||||
sector += sector_count;
|
||||
@ -351,11 +373,16 @@ static int virtio_scsi_setup(VDev *vdev)
|
||||
uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK;
|
||||
uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK;
|
||||
|
||||
IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data");
|
||||
if (resp.sense_len == 0) {
|
||||
puts("virtio-scsi: setup: no SENSE data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
IPL_assert(retry_test_unit_ready && code == 0x70 &&
|
||||
sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION,
|
||||
"virtio-scsi:setup: cannot retry");
|
||||
if (!retry_test_unit_ready || code != 0x70 ||
|
||||
sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) {
|
||||
puts("virtio-scsi:setup: cannot retry");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* retry on CHECK_CONDITION/UNIT_ATTENTION as it
|
||||
* may not designate a real error, but it may be
|
||||
@ -366,30 +393,40 @@ static int virtio_scsi_setup(VDev *vdev)
|
||||
continue;
|
||||
}
|
||||
|
||||
virtio_scsi_verify_response(&resp, "virtio-scsi:setup");
|
||||
if (!virtio_scsi_verify_response(&resp, "virtio-scsi:setup")) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* read and cache SCSI INQUIRY response */
|
||||
if (!scsi_inquiry(vdev,
|
||||
ret = scsi_inquiry(vdev,
|
||||
SCSI_INQUIRY_STANDARD,
|
||||
SCSI_INQUIRY_STANDARD_NONE,
|
||||
scsi_inquiry_std_response,
|
||||
sizeof(scsi_inquiry_std_response))) {
|
||||
virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry");
|
||||
sizeof(scsi_inquiry_std_response));
|
||||
if (ret < 1) {
|
||||
if (ret != 0 || !virtio_scsi_verify_response(&resp,
|
||||
"virtio-scsi:setup:inquiry")) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) {
|
||||
sclp_print("SCSI CD-ROM detected.\n");
|
||||
puts("SCSI CD-ROM detected.");
|
||||
vdev->is_cdrom = true;
|
||||
vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if (!scsi_inquiry(vdev,
|
||||
ret = scsi_inquiry(vdev,
|
||||
SCSI_INQUIRY_EVPD,
|
||||
SCSI_INQUIRY_EVPD_SUPPORTED_PAGES,
|
||||
evpd,
|
||||
sizeof(*evpd))) {
|
||||
virtio_scsi_verify_response(&resp, "virtio-scsi:setup:supported_pages");
|
||||
sizeof(*evpd));
|
||||
if (ret < 1) {
|
||||
if (ret != 0 || !virtio_scsi_verify_response(&resp,
|
||||
"virtio-scsi:setup:supported_pages")) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
debug_print_int("EVPD length", evpd->page_length);
|
||||
@ -401,12 +438,16 @@ static int virtio_scsi_setup(VDev *vdev)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!scsi_inquiry(vdev,
|
||||
ret = scsi_inquiry(vdev,
|
||||
SCSI_INQUIRY_EVPD,
|
||||
SCSI_INQUIRY_EVPD_BLOCK_LIMITS,
|
||||
evpd_bl,
|
||||
sizeof(*evpd_bl))) {
|
||||
virtio_scsi_verify_response(&resp, "virtio-scsi:setup:blocklimits");
|
||||
sizeof(*evpd_bl));
|
||||
if (ret < 1) {
|
||||
if (ret != 0 || !virtio_scsi_verify_response(&resp,
|
||||
"virtio-scsi:setup:blocklimits")) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
debug_print_int("max transfer", evpd_bl->max_transfer);
|
||||
@ -422,8 +463,12 @@ static int virtio_scsi_setup(VDev *vdev)
|
||||
vdev->max_transfer = MIN_NON_ZERO(VIRTIO_SCSI_MAX_SECTORS,
|
||||
vdev->max_transfer);
|
||||
|
||||
if (!scsi_read_capacity(vdev, data, data_size)) {
|
||||
virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity");
|
||||
ret = scsi_read_capacity(vdev, data, data_size);
|
||||
if (ret < 1) {
|
||||
if (ret != 0 || !virtio_scsi_verify_response(&resp,
|
||||
"virtio-scsi:setup:read_capacity")) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
scsi_parse_capacity_report(data, &vdev->scsi_last_block,
|
||||
(uint32_t *) &vdev->scsi_block_size);
|
||||
@ -438,12 +483,17 @@ int virtio_scsi_setup_device(SubChannelId schid)
|
||||
vdev->schid = schid;
|
||||
virtio_setup_ccw(vdev);
|
||||
|
||||
IPL_assert(vdev->config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE,
|
||||
"Config: sense size mismatch");
|
||||
IPL_assert(vdev->config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE,
|
||||
"Config: CDB size mismatch");
|
||||
if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) {
|
||||
puts("Config: sense size mismatch");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sclp_print("Using virtio-scsi.\n");
|
||||
if (vdev->config.scsi.cdb_size != VIRTIO_SCSI_CDB_SIZE) {
|
||||
puts("Config: CDB size mismatch");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
puts("Using virtio-scsi.");
|
||||
|
||||
return virtio_scsi_setup(vdev);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "cio.h"
|
||||
#include "virtio.h"
|
||||
@ -217,16 +217,19 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void virtio_setup_ccw(VDev *vdev)
|
||||
int virtio_setup_ccw(VDev *vdev)
|
||||
{
|
||||
int i, rc, cfg_size = 0;
|
||||
int i, cfg_size = 0;
|
||||
uint8_t status;
|
||||
struct VirtioFeatureDesc {
|
||||
uint32_t features;
|
||||
uint8_t index;
|
||||
} __attribute__((packed)) feats;
|
||||
|
||||
IPL_assert(virtio_is_supported(vdev->schid), "PE");
|
||||
if (!virtio_is_supported(vdev->schid)) {
|
||||
puts("Virtio unsupported for this device ID");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* device ID has been established now */
|
||||
|
||||
vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
|
||||
@ -235,8 +238,10 @@ void virtio_setup_ccw(VDev *vdev)
|
||||
run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
|
||||
|
||||
status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
|
||||
rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false);
|
||||
IPL_assert(rc == 0, "Could not write ACKNOWLEDGE status to host");
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write ACKNOWLEDGE status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (vdev->senseid.cu_model) {
|
||||
case VIRTIO_ID_NET:
|
||||
@ -255,27 +260,37 @@ void virtio_setup_ccw(VDev *vdev)
|
||||
cfg_size = sizeof(vdev->config.scsi);
|
||||
break;
|
||||
default:
|
||||
panic("Unsupported virtio device\n");
|
||||
puts("Unsupported virtio device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
status |= VIRTIO_CONFIG_S_DRIVER;
|
||||
rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false);
|
||||
IPL_assert(rc == 0, "Could not write DRIVER status to host");
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write DRIVER status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* 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), false);
|
||||
IPL_assert(rc == 0, "Could not get features bits");
|
||||
if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
|
||||
puts("Could not get features bits");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
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), false);
|
||||
IPL_assert(rc == 0, "Could not set features bits");
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
|
||||
puts("Could not set features bits");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
rc = run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false);
|
||||
IPL_assert(rc == 0, "Could not get virtio device configuration");
|
||||
if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
|
||||
puts("Could not get virtio device configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < vdev->nr_vqs; i++) {
|
||||
VqInfo info = {
|
||||
@ -289,19 +304,27 @@ void virtio_setup_ccw(VDev *vdev)
|
||||
.num = 0,
|
||||
};
|
||||
|
||||
rc = run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false);
|
||||
IPL_assert(rc == 0, "Could not get virtio device VQ configuration");
|
||||
if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
|
||||
false)) {
|
||||
puts("Could not get virtio device VQ config");
|
||||
return -EIO;
|
||||
}
|
||||
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), false) == 0,
|
||||
"Cannot set VQ info");
|
||||
if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
|
||||
puts("Cannot set VQ info");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
status |= VIRTIO_CONFIG_S_DRIVER_OK;
|
||||
rc = run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false);
|
||||
IPL_assert(rc == 0, "Could not write DRIVER_OK status to host");
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write DRIVER_OK status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool virtio_is_supported(SubChannelId schid)
|
||||
|
@ -253,7 +253,6 @@ struct VDev {
|
||||
uint8_t scsi_dev_heads;
|
||||
bool scsi_device_selected;
|
||||
ScsiDevice selected_scsi_device;
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t max_transfer;
|
||||
uint32_t guest_features[2];
|
||||
};
|
||||
@ -275,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags);
|
||||
int vr_poll(VRing *vr);
|
||||
int vring_wait_reply(void);
|
||||
int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd);
|
||||
void virtio_setup_ccw(VDev *vdev);
|
||||
int virtio_setup_ccw(VDev *vdev);
|
||||
|
||||
int virtio_net_init(void *mac_addr);
|
||||
|
||||
|
Binary file not shown.
@ -133,7 +133,14 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
|
||||
|
||||
valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb);
|
||||
if (!valid) {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
|
||||
if (subcode == DIAG308_SET && iplb->pbt == S390_IPL_TYPE_QEMU_SCSI) {
|
||||
s390_rebuild_iplb(iplb->devno, iplb);
|
||||
s390_ipl_update_diag308(iplb);
|
||||
env->regs[r1 + 1] = DIAG_308_RC_OK;
|
||||
} else {
|
||||
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -213,6 +213,30 @@ static void add_s390x_tests(void)
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
|
||||
"-device virtio-blk,drive=d2,bootindex=1 "
|
||||
"-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
|
||||
qtest_add_data_func("cdrom/boot/as-fallback-device",
|
||||
"-device virtio-serial -device virtio-scsi "
|
||||
"-device virtio-blk,drive=d1,bootindex=1 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
|
||||
"-device virtio-blk,drive=d2,bootindex=2 "
|
||||
"-drive if=none,id=d2,media=cdrom,file=", test_cdboot);
|
||||
qtest_add_data_func("cdrom/boot/as-last-option",
|
||||
"-device virtio-serial -device virtio-scsi "
|
||||
"-device virtio-blk,drive=d1,bootindex=1 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d1 "
|
||||
"-device virtio-blk,drive=d2,bootindex=2 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d2 "
|
||||
"-device virtio-blk,drive=d3,bootindex=3 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d3 "
|
||||
"-device scsi-hd,drive=d4,bootindex=4 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d4 "
|
||||
"-device scsi-hd,drive=d5,bootindex=5 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d5 "
|
||||
"-device virtio-blk,drive=d6,bootindex=6 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d6 "
|
||||
"-device scsi-hd,drive=d7,bootindex=7 "
|
||||
"-drive driver=null-co,read-zeroes=on,if=none,id=d7 "
|
||||
"-device scsi-cd,drive=d8,bootindex=8 "
|
||||
"-drive if=none,id=d8,media=cdrom,file=", test_cdboot);
|
||||
if (qtest_has_device("x-terminal3270")) {
|
||||
qtest_add_data_func("cdrom/boot/without-bootindex",
|
||||
"-device virtio-scsi -device virtio-serial "
|
||||
|
@ -3,7 +3,7 @@ VPATH+=$(S390X_SRC)
|
||||
# EXTFLAGS can be passed by the user, e.g. to override the --accel
|
||||
QEMU_OPTS+=-action panic=exit-failure -nographic -serial chardev:output $(EXTFLAGS) -kernel
|
||||
LINK_SCRIPT=$(S390X_SRC)/softmmu.ld
|
||||
CFLAGS+=-ggdb -O0
|
||||
CFLAGS+=-ggdb -O0 -I$(SRC_PATH)/include/hw/s390x/ipl/
|
||||
LDFLAGS=-nostdlib -static
|
||||
|
||||
%.o: %.S
|
||||
|
@ -4,7 +4,10 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "../../../pc-bios/s390-ccw/sclp.c"
|
||||
#include "../../../roms/SLOF/lib/libc/string/memset.c"
|
||||
#include "../../../roms/SLOF/lib/libc/string/memcpy.c"
|
||||
|
||||
void __sys_outc(char c)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user