* 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:
Peter Maydell 2024-10-24 15:21:53 +01:00
commit e17e57e862
39 changed files with 1179 additions and 1090 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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++) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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