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