mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -49,10 +49,11 @@ Limitations
|
||||
-----------
|
||||
|
||||
Some firmware has limitations on which devices can be considered for
|
||||
booting. For instance, the PC BIOS boot specification allows only one
|
||||
disk to be bootable. If boot from disk fails for some reason, the BIOS
|
||||
booting. For instance, the x86 PC BIOS boot specification allows only one
|
||||
disk to be bootable. If boot from disk fails for some reason, the x86 BIOS
|
||||
won't retry booting from other disk. It can still try to boot from
|
||||
floppy or net, though.
|
||||
floppy or net, though. In the case of s390x BIOS, the BIOS will try up to
|
||||
8 total devices, any number of which may be disks.
|
||||
|
||||
Sometimes, firmware cannot map the device path QEMU wants firmware to
|
||||
boot from to a boot method. It doesn't happen for devices the firmware
|
||||
|
||||
@@ -6,9 +6,7 @@ Booting with bootindex parameter
|
||||
|
||||
For classical mainframe guests (i.e. LPAR or z/VM installations), you always
|
||||
have to explicitly specify the disk where you want to boot from (or "IPL" from,
|
||||
in s390x-speak -- IPL means "Initial Program Load"). In particular, there can
|
||||
also be only one boot device according to the architecture specification, thus
|
||||
specifying multiple boot devices is not possible (yet).
|
||||
in s390x-speak -- IPL means "Initial Program Load").
|
||||
|
||||
So for booting an s390x guest in QEMU, you should always mark the
|
||||
device where you want to boot from with the ``bootindex`` property, for
|
||||
@@ -17,6 +15,11 @@ example::
|
||||
qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \
|
||||
-device virtio-blk,drive=dr1,bootindex=1
|
||||
|
||||
Multiple devices may have a bootindex. The lowest bootindex is assigned to the
|
||||
device to IPL first. If the IPL fails for the first, the device with the second
|
||||
lowest bootindex will be tried and so on until IPL is successful or there are no
|
||||
remaining boot devices to try.
|
||||
|
||||
For booting from a CD-ROM ISO image (which needs to include El-Torito boot
|
||||
information in order to be bootable), it is recommended to specify a ``scsi-cd``
|
||||
device, for example like this::
|
||||
@@ -82,23 +85,17 @@ Note that ``0`` can be used to boot the default entry.
|
||||
Booting from a network device
|
||||
-----------------------------
|
||||
|
||||
Beside the normal guest firmware (which is loaded from the file ``s390-ccw.img``
|
||||
in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with
|
||||
a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This
|
||||
firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data
|
||||
directory. In case you want to load it from a different filename instead,
|
||||
you can specify it via the ``-global s390-ipl.netboot_fw=filename``
|
||||
command line option.
|
||||
|
||||
The ``bootindex`` property is especially important for booting via the network.
|
||||
If you don't specify the ``bootindex`` property here, the network bootloader
|
||||
firmware code won't get loaded into the guest memory so that the network boot
|
||||
will fail. For a successful network boot, try something like this::
|
||||
The firmware that ships with QEMU includes a small TFTP network bootloader
|
||||
for virtio-net-ccw devices. The ``bootindex`` property is especially
|
||||
important for booting via the network. If you don't specify the ``bootindex``
|
||||
property here, the network bootloader won't be taken into consideration and
|
||||
the network boot will fail. For a successful network boot, try something
|
||||
like this::
|
||||
|
||||
qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \
|
||||
-device virtio-net-ccw,netdev=n1,bootindex=1
|
||||
|
||||
The network bootloader firmware also has basic support for pxelinux.cfg-style
|
||||
The network bootloader also has basic support for pxelinux.cfg-style
|
||||
configuration files. See the `PXELINUX Configuration page
|
||||
<https://wiki.syslinux.org/wiki/index.php?title=PXELINUX#Configuration>`__
|
||||
for details how to set up the configuration file on your TFTP server.
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
#include "ccw-device.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qemu/module.h"
|
||||
#include "ipl.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/ctype.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static void ccw_device_refill_ids(CcwDevice *dev)
|
||||
{
|
||||
@@ -37,10 +41,52 @@ static bool ccw_device_realize(CcwDevice *dev, Error **errp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ccw_device_get_loadparm(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
CcwDevice *dev = CCW_DEVICE(obj);
|
||||
char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm));
|
||||
|
||||
visit_type_str(v, name, &str, errp);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void ccw_device_set_loadparm(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
CcwDevice *dev = CCW_DEVICE(obj);
|
||||
char *val;
|
||||
int index;
|
||||
|
||||
index = object_property_get_int(obj, "bootindex", NULL);
|
||||
|
||||
if (index < 0) {
|
||||
error_setg(errp, "LOADPARM is only valid for boot devices!");
|
||||
}
|
||||
|
||||
if (!visit_type_str(v, name, &val, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
s390_ipl_fmt_loadparm(dev->loadparm, val, errp);
|
||||
}
|
||||
|
||||
static const PropertyInfo ccw_loadparm = {
|
||||
.name = "ccw_loadparm",
|
||||
.description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass"
|
||||
" to the guest loader/kernel",
|
||||
.get = ccw_device_get_loadparm,
|
||||
.set = ccw_device_set_loadparm,
|
||||
};
|
||||
|
||||
static Property ccw_device_properties[] = {
|
||||
DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno),
|
||||
DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id),
|
||||
DEFINE_PROP_CSS_DEV_ID_RO("subch_id", CcwDevice, subch_id),
|
||||
DEFINE_PROP("loadparm", CcwDevice, loadparm, ccw_loadparm,
|
||||
typeof(uint8_t[8])),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ struct CcwDevice {
|
||||
CssDevId dev_id;
|
||||
/* The actual busid of the virtual subchannel. */
|
||||
CssDevId subch_id;
|
||||
/* If set, use this loadparm value when device is boot target */
|
||||
uint8_t loadparm[8];
|
||||
};
|
||||
typedef struct CcwDevice CcwDevice;
|
||||
|
||||
|
||||
+143
-153
@@ -34,6 +34,7 @@
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/ctype.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
|
||||
#define KERN_IMAGE_START 0x010000UL
|
||||
@@ -45,6 +46,7 @@
|
||||
#define INITRD_PARM_START 0x010408UL
|
||||
#define PARMFILE_START 0x001000UL
|
||||
#define ZIPL_IMAGE_START 0x009000UL
|
||||
#define BIOS_MAX_SIZE 0x300000UL
|
||||
#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64)
|
||||
|
||||
static bool iplb_extended_needed(void *opaque)
|
||||
@@ -54,6 +56,13 @@ static bool iplb_extended_needed(void *opaque)
|
||||
return ipl->iplbext_migration;
|
||||
}
|
||||
|
||||
/* Place the IPLB chain immediately before the BIOS in memory */
|
||||
static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count)
|
||||
{
|
||||
return (bios_addr & TARGET_PAGE_MASK)
|
||||
- (count * sizeof(IplParameterBlock));
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_iplb_extended = {
|
||||
.name = "ipl/iplb_extended",
|
||||
.version_id = 0,
|
||||
@@ -144,7 +153,14 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
|
||||
* even if an external kernel has been defined.
|
||||
*/
|
||||
if (!ipl->kernel || ipl->enforce_bios) {
|
||||
uint64_t fwbase = (MIN(ms->ram_size, 0x80000000U) - 0x200000) & ~0xffffUL;
|
||||
uint64_t fwbase;
|
||||
|
||||
if (ms->ram_size < BIOS_MAX_SIZE) {
|
||||
error_setg(errp, "not enough RAM to load the BIOS file");
|
||||
return;
|
||||
}
|
||||
|
||||
fwbase = (MIN(ms->ram_size, 0x80000000U) - BIOS_MAX_SIZE) & ~0xffffUL;
|
||||
|
||||
bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware);
|
||||
if (bios_filename == NULL) {
|
||||
@@ -280,7 +296,6 @@ static Property s390_ipl_properties[] = {
|
||||
DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
|
||||
DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
|
||||
DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
|
||||
DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
|
||||
DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
|
||||
DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
|
||||
true),
|
||||
@@ -390,174 +405,169 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype)
|
||||
return ccw_dev;
|
||||
}
|
||||
|
||||
static bool s390_gen_initial_iplb(S390IPLState *ipl)
|
||||
static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain)
|
||||
{
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
uint16_t count = be16_to_cpu(ipl->qipl.chain_len);
|
||||
uint64_t len = sizeof(IplParameterBlock) * count;
|
||||
uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count);
|
||||
|
||||
cpu_physical_memory_write(chain_addr, iplb_chain, len);
|
||||
return chain_addr;
|
||||
}
|
||||
|
||||
void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize the loadparm with spaces */
|
||||
memset(loadparm, ' ', LOADPARM_LEN);
|
||||
for (i = 0; i < LOADPARM_LEN && str[i]; i++) {
|
||||
uint8_t c = qemu_toupper(str[i]); /* mimic HMC */
|
||||
|
||||
if (qemu_isalnum(c) || c == '.' || c == ' ') {
|
||||
loadparm[i] = c;
|
||||
} else {
|
||||
error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
|
||||
c, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Initialize the loadparm with EBCDIC spaces (0x40) */
|
||||
memset(ebcdic_lp, '@', LOADPARM_LEN);
|
||||
for (i = 0; i < LOADPARM_LEN && ascii_lp[i]; i++) {
|
||||
ebcdic_lp[i] = ascii2ebcdic[(uint8_t) ascii_lp[i]];
|
||||
}
|
||||
}
|
||||
|
||||
static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
|
||||
{
|
||||
DeviceState *dev_st;
|
||||
CcwDevice *ccw_dev = NULL;
|
||||
SCSIDevice *sd;
|
||||
int devtype;
|
||||
|
||||
dev_st = get_boot_device(0);
|
||||
if (dev_st) {
|
||||
ccw_dev = s390_get_ccw_device(dev_st, &devtype);
|
||||
}
|
||||
uint8_t *lp;
|
||||
|
||||
/*
|
||||
* Currently allow IPL only from CCW devices.
|
||||
*/
|
||||
ccw_dev = s390_get_ccw_device(dev_st, &devtype);
|
||||
if (ccw_dev) {
|
||||
lp = ccw_dev->loadparm;
|
||||
|
||||
switch (devtype) {
|
||||
case CCW_DEVTYPE_SCSI:
|
||||
sd = SCSI_DEVICE(dev_st);
|
||||
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
|
||||
ipl->iplb.blk0_len =
|
||||
iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
|
||||
iplb->blk0_len =
|
||||
cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
|
||||
ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
|
||||
ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
|
||||
ipl->iplb.scsi.target = cpu_to_be16(sd->id);
|
||||
ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
|
||||
ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
|
||||
iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
|
||||
iplb->scsi.lun = cpu_to_be32(sd->lun);
|
||||
iplb->scsi.target = cpu_to_be16(sd->id);
|
||||
iplb->scsi.channel = cpu_to_be16(sd->channel);
|
||||
iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
|
||||
break;
|
||||
case CCW_DEVTYPE_VFIO:
|
||||
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||||
ipl->iplb.pbt = S390_IPL_TYPE_CCW;
|
||||
ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
|
||||
iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||||
iplb->pbt = S390_IPL_TYPE_CCW;
|
||||
iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
|
||||
break;
|
||||
case CCW_DEVTYPE_VIRTIO_NET:
|
||||
ipl->netboot = true;
|
||||
/* Fall through to CCW_DEVTYPE_VIRTIO case */
|
||||
case CCW_DEVTYPE_VIRTIO:
|
||||
ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||||
ipl->iplb.blk0_len =
|
||||
iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
|
||||
iplb->blk0_len =
|
||||
cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
|
||||
ipl->iplb.pbt = S390_IPL_TYPE_CCW;
|
||||
ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
|
||||
iplb->pbt = S390_IPL_TYPE_CCW;
|
||||
iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
|
||||
iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) {
|
||||
ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;
|
||||
/* If the device loadparm is empty use the global machine loadparm */
|
||||
if (memcmp(lp, NO_LOADPARM, 8) == 0) {
|
||||
lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm;
|
||||
}
|
||||
|
||||
s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
|
||||
iplb->flags |= DIAG308_FLAGS_LP_VALID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int s390_ipl_set_loadparm(uint8_t *loadparm)
|
||||
void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb)
|
||||
{
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL);
|
||||
|
||||
if (lp) {
|
||||
int i;
|
||||
|
||||
/* lp is an uppercase string without leading/embedded spaces */
|
||||
for (i = 0; i < 8 && lp[i]; i++) {
|
||||
loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]];
|
||||
}
|
||||
|
||||
if (i < 8) {
|
||||
memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */
|
||||
}
|
||||
|
||||
g_free(lp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int load_netboot_image(Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
char *netboot_filename;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *mr = NULL;
|
||||
void *ram_ptr = NULL;
|
||||
int img_size = -1;
|
||||
uint16_t index;
|
||||
index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index;
|
||||
|
||||
mr = memory_region_find(sysmem, 0, 1).mr;
|
||||
if (!mr) {
|
||||
error_setg(errp, "Failed to find memory region at address 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ram_ptr = memory_region_get_ram_ptr(mr);
|
||||
if (!ram_ptr) {
|
||||
error_setg(errp, "No RAM found");
|
||||
goto unref_mr;
|
||||
}
|
||||
|
||||
netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
|
||||
if (netboot_filename == NULL) {
|
||||
error_setg(errp, "Could not find network bootloader '%s'",
|
||||
ipl->netboot_fw);
|
||||
goto unref_mr;
|
||||
}
|
||||
|
||||
img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL,
|
||||
&ipl->start_addr,
|
||||
NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL,
|
||||
false);
|
||||
|
||||
if (img_size < 0) {
|
||||
img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size);
|
||||
ipl->start_addr = KERN_IMAGE_START;
|
||||
}
|
||||
|
||||
if (img_size < 0) {
|
||||
error_setg(errp, "Failed to load network bootloader");
|
||||
}
|
||||
|
||||
g_free(netboot_filename);
|
||||
|
||||
unref_mr:
|
||||
memory_region_unref(mr);
|
||||
return img_size;
|
||||
ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb);
|
||||
ipl->iplb_index = index;
|
||||
}
|
||||
|
||||
static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb,
|
||||
int virtio_id)
|
||||
static bool s390_init_all_iplbs(S390IPLState *ipl)
|
||||
{
|
||||
uint8_t cssid;
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint16_t schid;
|
||||
SubchDev *sch = NULL;
|
||||
int iplb_num = 0;
|
||||
IplParameterBlock iplb_chain[7];
|
||||
DeviceState *dev_st = get_boot_device(0);
|
||||
Object *machine = qdev_get_machine();
|
||||
|
||||
if (iplb->pbt != S390_IPL_TYPE_CCW) {
|
||||
/*
|
||||
* Parse the boot devices. Generate an IPLB for only the first boot device
|
||||
* which will later be set with DIAG308.
|
||||
*/
|
||||
if (!dev_st) {
|
||||
ipl->qipl.chain_len = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
devno = be16_to_cpu(iplb->ccw.devno);
|
||||
ssid = iplb->ccw.ssid & 3;
|
||||
|
||||
for (schid = 0; schid < MAX_SCHID; schid++) {
|
||||
for (cssid = 0; cssid < MAX_CSSID; cssid++) {
|
||||
sch = css_find_subch(1, cssid, ssid, schid);
|
||||
|
||||
if (sch && sch->devno == devno) {
|
||||
return sch->id.cu_model == virtio_id;
|
||||
}
|
||||
}
|
||||
/* If no machine loadparm was defined fill it with spaces */
|
||||
if (memcmp(S390_CCW_MACHINE(machine)->loadparm, NO_LOADPARM, 8) == 0) {
|
||||
object_property_set_str(machine, "loadparm", " ", NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_virtio_net_device(IplParameterBlock *iplb)
|
||||
{
|
||||
return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET);
|
||||
}
|
||||
iplb_num = 1;
|
||||
s390_build_iplb(dev_st, &ipl->iplb);
|
||||
|
||||
static bool is_virtio_scsi_device(IplParameterBlock *iplb)
|
||||
{
|
||||
return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI);
|
||||
/* Index any fallback boot devices */
|
||||
while (get_boot_device(iplb_num)) {
|
||||
iplb_num++;
|
||||
}
|
||||
|
||||
if (iplb_num > MAX_BOOT_DEVS) {
|
||||
warn_report("Excess boot devices defined! %d boot devices found, "
|
||||
"but only the first %d will be considered.",
|
||||
iplb_num, MAX_BOOT_DEVS);
|
||||
|
||||
iplb_num = MAX_BOOT_DEVS;
|
||||
}
|
||||
|
||||
ipl->qipl.chain_len = cpu_to_be16(iplb_num - 1);
|
||||
|
||||
/*
|
||||
* Build fallback IPLBs for any boot devices above index 0, up to a
|
||||
* maximum amount as defined in ipl.h
|
||||
*/
|
||||
if (iplb_num > 1) {
|
||||
/* Start at 1 because the IPLB for boot index 0 is not chained */
|
||||
for (int i = 1; i < iplb_num; i++) {
|
||||
dev_st = get_boot_device(i);
|
||||
s390_build_iplb(dev_st, &iplb_chain[i - 1]);
|
||||
}
|
||||
|
||||
ipl->qipl.next_iplb = cpu_to_be64(s390_ipl_map_iplb_chain(iplb_chain));
|
||||
}
|
||||
|
||||
return iplb_num;
|
||||
}
|
||||
|
||||
static void update_machine_ipl_properties(IplParameterBlock *iplb)
|
||||
@@ -577,7 +587,7 @@ static void update_machine_ipl_properties(IplParameterBlock *iplb)
|
||||
ascii_loadparm[i] = 0;
|
||||
object_property_set_str(machine, "loadparm", ascii_loadparm, &err);
|
||||
} else {
|
||||
object_property_set_str(machine, "loadparm", "", &err);
|
||||
object_property_set_str(machine, "loadparm", " ", &err);
|
||||
}
|
||||
if (err) {
|
||||
warn_report_err(err);
|
||||
@@ -599,7 +609,7 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb)
|
||||
ipl->iplb = *iplb;
|
||||
ipl->iplb_valid = true;
|
||||
}
|
||||
ipl->netboot = is_virtio_net_device(iplb);
|
||||
|
||||
update_machine_ipl_properties(iplb);
|
||||
}
|
||||
|
||||
@@ -626,32 +636,14 @@ IplParameterBlock *s390_ipl_get_iplb(void)
|
||||
void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type)
|
||||
{
|
||||
S390IPLState *ipl = get_ipl_device();
|
||||
|
||||
if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) {
|
||||
/* use CPU 0 for full resets */
|
||||
ipl->reset_cpu_index = 0;
|
||||
} else {
|
||||
ipl->reset_cpu_index = cs->cpu_index;
|
||||
}
|
||||
|
||||
ipl->reset_type = reset_type;
|
||||
|
||||
if (reset_type == S390_RESET_REIPL &&
|
||||
ipl->iplb_valid &&
|
||||
!ipl->netboot &&
|
||||
ipl->iplb.pbt == S390_IPL_TYPE_CCW &&
|
||||
is_virtio_scsi_device(&ipl->iplb)) {
|
||||
CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0), NULL);
|
||||
|
||||
if (ccw_dev &&
|
||||
cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno &&
|
||||
(ccw_dev->sch->ssid & 3) == ipl->iplb.ccw.ssid) {
|
||||
/*
|
||||
* this is the original boot device's SCSI
|
||||
* so restore IPL parameter info from it
|
||||
*/
|
||||
ipl->iplb_valid = s390_gen_initial_iplb(ipl);
|
||||
}
|
||||
}
|
||||
if (reset_type == S390_RESET_MODIFIED_CLEAR ||
|
||||
reset_type == S390_RESET_LOAD_NORMAL ||
|
||||
reset_type == S390_RESET_PV) {
|
||||
@@ -743,13 +735,11 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
|
||||
if (!ipl->kernel || ipl->iplb_valid) {
|
||||
cpu->env.psw.addr = ipl->bios_start_addr;
|
||||
if (!ipl->iplb_valid) {
|
||||
ipl->iplb_valid = s390_gen_initial_iplb(ipl);
|
||||
ipl->iplb_valid = s390_init_all_iplbs(ipl);
|
||||
} else {
|
||||
ipl->qipl.chain_len = 0;
|
||||
}
|
||||
}
|
||||
if (ipl->netboot) {
|
||||
load_netboot_image(&error_fatal);
|
||||
ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
|
||||
}
|
||||
s390_ipl_set_boot_menu(ipl);
|
||||
s390_ipl_prepare_qipl(cpu);
|
||||
}
|
||||
|
||||
+12
-111
@@ -16,96 +16,15 @@
|
||||
#include "cpu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/s390x/ipl/qipl.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
struct IPLBlockPVComp {
|
||||
uint64_t tweak_pref;
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPVComp IPLBlockPVComp;
|
||||
|
||||
struct IPLBlockPV {
|
||||
uint8_t reserved18[87]; /* 0x18 */
|
||||
uint8_t version; /* 0x6f */
|
||||
uint32_t reserved70; /* 0x70 */
|
||||
uint32_t num_comp; /* 0x74 */
|
||||
uint64_t pv_header_addr; /* 0x78 */
|
||||
uint64_t pv_header_len; /* 0x80 */
|
||||
struct IPLBlockPVComp components[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPV IPLBlockPV;
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved3[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved4[8];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockCcw IplBlockCcw;
|
||||
|
||||
struct IplBlockFcp {
|
||||
uint8_t reserved1[305 - 1];
|
||||
uint8_t opt;
|
||||
uint8_t reserved2[3];
|
||||
uint16_t reserved3;
|
||||
uint16_t devno;
|
||||
uint8_t reserved4[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved5[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
struct IplBlockQemuScsi {
|
||||
uint32_t lun;
|
||||
uint16_t target;
|
||||
uint16_t channel;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
|
||||
|
||||
#define DIAG308_FLAGS_LP_VALID 0x80
|
||||
#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */
|
||||
|
||||
union IplParameterBlock {
|
||||
struct {
|
||||
uint32_t len;
|
||||
uint8_t reserved0[3];
|
||||
uint8_t version;
|
||||
uint32_t blk0_len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint16_t reserved01;
|
||||
uint8_t loadparm[8];
|
||||
union {
|
||||
IplBlockCcw ccw;
|
||||
IplBlockFcp fcp;
|
||||
IPLBlockPV pv;
|
||||
IplBlockQemuScsi scsi;
|
||||
};
|
||||
} QEMU_PACKED;
|
||||
struct {
|
||||
uint8_t reserved1[110];
|
||||
uint16_t devno;
|
||||
uint8_t reserved2[88];
|
||||
uint8_t reserved_ext[4096 - 200];
|
||||
} QEMU_PACKED;
|
||||
} QEMU_PACKED;
|
||||
typedef union IplParameterBlock IplParameterBlock;
|
||||
|
||||
int s390_ipl_set_loadparm(uint8_t *loadparm);
|
||||
void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp);
|
||||
void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp);
|
||||
void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb);
|
||||
void s390_ipl_update_diag308(IplParameterBlock *iplb);
|
||||
int s390_ipl_prepare_pv_header(Error **errp);
|
||||
int s390_ipl_pv_unpack(void);
|
||||
@@ -131,27 +50,6 @@ void s390_ipl_clear_reset_request(void);
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
/*
|
||||
* The QEMU IPL Parameters will be stored at absolute address
|
||||
* 204 (0xcc) which means it is 32-bit word aligned but not
|
||||
* double-word aligned.
|
||||
* Placement of data fields in this area must account for
|
||||
* their alignment needs. E.g., netboot_start_address must
|
||||
* have an offset of 4 + n * 8 bytes within the struct in order
|
||||
* to keep it double-word aligned.
|
||||
* The total size of the struct must never exceed 28 bytes.
|
||||
* This definition must be kept in sync with the definition
|
||||
* in pc-bios/s390-ccw/iplb.h.
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t reserved1[3];
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved2[12];
|
||||
} QEMU_PACKED;
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
|
||||
#define TYPE_S390_IPL "s390-ipl"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL)
|
||||
|
||||
@@ -168,7 +66,8 @@ struct S390IPLState {
|
||||
bool enforce_bios;
|
||||
bool iplb_valid;
|
||||
bool iplb_valid_pv;
|
||||
bool netboot;
|
||||
bool rebuilt_iplb;
|
||||
uint16_t iplb_index;
|
||||
/* reset related properties don't have to be migrated or reset */
|
||||
enum s390_reset reset_type;
|
||||
int reset_cpu_index;
|
||||
@@ -178,7 +77,6 @@ struct S390IPLState {
|
||||
char *initrd;
|
||||
char *cmdline;
|
||||
char *firmware;
|
||||
char *netboot_fw;
|
||||
uint8_t cssid;
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
@@ -276,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb)
|
||||
|
||||
static inline bool iplb_valid(IplParameterBlock *iplb)
|
||||
{
|
||||
uint32_t len = be32_to_cpu(iplb->len);
|
||||
|
||||
switch (iplb->pbt) {
|
||||
case S390_IPL_TYPE_FCP:
|
||||
return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN;
|
||||
return len >= S390_IPLB_MIN_FCP_LEN;
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN;
|
||||
return len >= S390_IPLB_MIN_CCW_LEN;
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -197,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram)
|
||||
static void s390_init_ipl_dev(const char *kernel_filename,
|
||||
const char *kernel_cmdline,
|
||||
const char *initrd_filename, const char *firmware,
|
||||
const char *netboot_fw, bool enforce_bios)
|
||||
bool enforce_bios)
|
||||
{
|
||||
Object *new = object_new(TYPE_S390_IPL);
|
||||
DeviceState *dev = DEVICE(new);
|
||||
char *netboot_fw_prop;
|
||||
|
||||
if (kernel_filename) {
|
||||
qdev_prop_set_string(dev, "kernel", kernel_filename);
|
||||
@@ -212,11 +211,6 @@ static void s390_init_ipl_dev(const char *kernel_filename,
|
||||
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
|
||||
qdev_prop_set_string(dev, "firmware", firmware);
|
||||
qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
|
||||
netboot_fw_prop = object_property_get_str(new, "netboot_fw", &error_abort);
|
||||
if (!strlen(netboot_fw_prop)) {
|
||||
qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
|
||||
}
|
||||
g_free(netboot_fw_prop);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
|
||||
new);
|
||||
object_unref(new);
|
||||
@@ -284,7 +278,7 @@ static void ccw_init(MachineState *machine)
|
||||
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
|
||||
machine->initrd_filename,
|
||||
machine->firmware ?: "s390-ccw.img",
|
||||
"s390-netboot.img", true);
|
||||
true);
|
||||
|
||||
dev = qdev_new(TYPE_S390_PCI_HOST_BRIDGE);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE,
|
||||
@@ -728,28 +722,12 @@ static void machine_set_loadparm(Object *obj, Visitor *v,
|
||||
{
|
||||
S390CcwMachineState *ms = S390_CCW_MACHINE(obj);
|
||||
char *val;
|
||||
int i;
|
||||
|
||||
if (!visit_type_str(v, name, &val, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(ms->loadparm) && val[i]; i++) {
|
||||
uint8_t c = qemu_toupper(val[i]); /* mimic HMC */
|
||||
|
||||
if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') ||
|
||||
(c == ' ')) {
|
||||
ms->loadparm[i] = c;
|
||||
} else {
|
||||
error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)",
|
||||
c, c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < sizeof(ms->loadparm); i++) {
|
||||
ms->loadparm[i] = ' '; /* pad right with spaces */
|
||||
}
|
||||
s390_ipl_fmt_loadparm(ms->loadparm, val, errp);
|
||||
}
|
||||
|
||||
static void ccw_machine_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
+2
-7
@@ -110,7 +110,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
||||
MachineState *machine = MACHINE(qdev_get_machine());
|
||||
int cpu_count;
|
||||
int rnsize, rnmax;
|
||||
IplParameterBlock *ipib = s390_ipl_get_iplb();
|
||||
int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len);
|
||||
int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ?
|
||||
offsetof(ReadInfo, entries) :
|
||||
@@ -171,12 +170,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
|
||||
read_info->rnmax2 = cpu_to_be64(rnmax);
|
||||
}
|
||||
|
||||
if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) {
|
||||
memcpy(&read_info->loadparm, &ipib->loadparm,
|
||||
sizeof(read_info->loadparm));
|
||||
} else {
|
||||
s390_ipl_set_loadparm(read_info->loadparm);
|
||||
}
|
||||
s390_ipl_convert_loadparm((char *)S390_CCW_MACHINE(machine)->loadparm,
|
||||
read_info->loadparm);
|
||||
|
||||
sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* S/390 boot structures
|
||||
*
|
||||
* Copyright 2024 IBM Corp.
|
||||
* Author(s): Jared Rossi <jrossi@linux.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||
* your option) any later version. See the COPYING file in the top-level
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#ifndef S390X_QIPL_H
|
||||
#define S390X_QIPL_H
|
||||
|
||||
/* Boot Menu flags */
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
#define QIPL_ADDRESS 0xcc
|
||||
#define LOADPARM_LEN 8
|
||||
#define NO_LOADPARM "\0\0\0\0\0\0\0\0"
|
||||
|
||||
/*
|
||||
* The QEMU IPL Parameters will be stored at absolute address
|
||||
* 204 (0xcc) which means it is 32-bit word aligned but not
|
||||
* double-word aligned. Placement of 64-bit data fields in this
|
||||
* area must account for their alignment needs.
|
||||
* The total size of the struct must never exceed 28 bytes.
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t index;
|
||||
uint8_t reserved1[2];
|
||||
uint64_t reserved2;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved3[2];
|
||||
uint16_t chain_len;
|
||||
uint64_t next_iplb;
|
||||
} QEMU_PACKED;
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
|
||||
struct IPLBlockPVComp {
|
||||
uint64_t tweak_pref;
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPVComp IPLBlockPVComp;
|
||||
|
||||
struct IPLBlockPV {
|
||||
uint8_t reserved18[87]; /* 0x18 */
|
||||
uint8_t version; /* 0x6f */
|
||||
uint32_t reserved70; /* 0x70 */
|
||||
uint32_t num_comp; /* 0x74 */
|
||||
uint64_t pv_header_addr; /* 0x78 */
|
||||
uint64_t pv_header_len; /* 0x80 */
|
||||
struct IPLBlockPVComp components[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IPLBlockPV IPLBlockPV;
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved3[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved4[8];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockCcw IplBlockCcw;
|
||||
|
||||
struct IplBlockFcp {
|
||||
uint8_t reserved1[305 - 1];
|
||||
uint8_t opt;
|
||||
uint8_t reserved2[3];
|
||||
uint16_t reserved3;
|
||||
uint16_t devno;
|
||||
uint8_t reserved4[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved5[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[0];
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
struct IplBlockQemuScsi {
|
||||
uint32_t lun;
|
||||
uint16_t target;
|
||||
uint16_t channel;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
} QEMU_PACKED;
|
||||
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
|
||||
|
||||
union IplParameterBlock {
|
||||
struct {
|
||||
uint32_t len;
|
||||
uint8_t reserved0[3];
|
||||
uint8_t version;
|
||||
uint32_t blk0_len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint16_t reserved01;
|
||||
uint8_t loadparm[LOADPARM_LEN];
|
||||
union {
|
||||
IplBlockCcw ccw;
|
||||
IplBlockFcp fcp;
|
||||
IPLBlockPV pv;
|
||||
IplBlockQemuScsi scsi;
|
||||
};
|
||||
} QEMU_PACKED;
|
||||
struct {
|
||||
uint8_t reserved1[110];
|
||||
uint16_t devno;
|
||||
uint8_t reserved2[88];
|
||||
uint8_t reserved_ext[4096 - 200];
|
||||
} QEMU_PACKED;
|
||||
} QEMU_PACKED;
|
||||
typedef union IplParameterBlock IplParameterBlock;
|
||||
|
||||
#endif
|
||||
@@ -68,7 +68,6 @@ blobs = [
|
||||
'kvmvapic.bin',
|
||||
'pvh.bin',
|
||||
's390-ccw.img',
|
||||
's390-netboot.img',
|
||||
'slof.bin',
|
||||
'skiboot.lid',
|
||||
'palcode-clipper',
|
||||
|
||||
Binary file not shown.
+62
-10
@@ -3,7 +3,8 @@ all: build-all
|
||||
@true
|
||||
|
||||
include config-host.mak
|
||||
CFLAGS = -O2 -g
|
||||
CFLAGS = -O2 -g -I $(SRC_PATH)/../../include/hw/s390x/ipl
|
||||
LDFLAGS ?=
|
||||
MAKEFLAGS += -rR
|
||||
|
||||
GIT_SUBMODULES = roms/SLOF
|
||||
@@ -32,15 +33,21 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
|
||||
|
||||
.PHONY : all clean build-all distclean
|
||||
|
||||
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \
|
||||
virtio.o virtio-scsi.o virtio-blkdev.o libc.o cio.o dasd-ipl.o
|
||||
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
|
||||
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o
|
||||
|
||||
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
|
||||
|
||||
LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include
|
||||
LIBNET_INC := -I$(SLOF_DIR)/lib/libnet
|
||||
|
||||
EXTRA_CFLAGS += -Wall
|
||||
EXTRA_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -fno-common -fPIE
|
||||
EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables
|
||||
EXTRA_CFLAGS += -msoft-float
|
||||
EXTRA_CFLAGS += -std=gnu99
|
||||
LDFLAGS += -Wl,-pie -nostdlib -z noexecstack
|
||||
EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC)
|
||||
EXTRA_LDFLAGS += -Wl,-pie -nostdlib -z noexecstack -z text
|
||||
|
||||
cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null
|
||||
cc-option = if $(call cc-test, $1); then \
|
||||
@@ -55,19 +62,64 @@ config-cc.mak: Makefile
|
||||
$(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak
|
||||
-include config-cc.mak
|
||||
|
||||
build-all: s390-ccw.img s390-netboot.img
|
||||
# libc files:
|
||||
|
||||
s390-ccw.elf: $(OBJECTS)
|
||||
$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),Linking)
|
||||
LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
|
||||
-MMD -MP -MT $@ -MF $(@:%.o=%.d)
|
||||
|
||||
CTYPE_OBJS = isdigit.o isxdigit.o toupper.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/ctype/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \
|
||||
strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \
|
||||
memset.o memcpy.o memmove.o memcmp.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/string/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/stdlib/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \
|
||||
printf.o putc.o puts.o putchar.o stdchnls.o fileno.o
|
||||
%.o : $(SLOF_DIR)/lib/libc/stdio/%.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
sbrk.o: $(SLOF_DIR)/slof/sbrk.c
|
||||
$(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
LIBCOBJS := $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) sbrk.o
|
||||
|
||||
libc.a: $(LIBCOBJS)
|
||||
$(call quiet-command,$(AR) -rc $@ $^,Creating static library)
|
||||
|
||||
# libnet files:
|
||||
|
||||
LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \
|
||||
dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o
|
||||
LIBNETCFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \
|
||||
-DDHCPARCH=0x1F -MMD -MP -MT $@ -MF $(@:%.o=%.d)
|
||||
|
||||
%.o : $(SLOF_DIR)/lib/libnet/%.c
|
||||
$(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,Compiling)
|
||||
|
||||
libnet.a: $(LIBNETOBJS)
|
||||
$(call quiet-command,$(AR) -rc $@ $^,Creating static library)
|
||||
|
||||
# Main targets:
|
||||
|
||||
build-all: s390-ccw.img
|
||||
|
||||
s390-ccw.elf: $(OBJECTS) libnet.a libc.a
|
||||
$(call quiet-command,$(CC) $(EXTRA_LDFLAGS) $(LDFLAGS) -o $@ $^,Linking)
|
||||
|
||||
s390-ccw.img: s390-ccw.elf
|
||||
$(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into)
|
||||
|
||||
$(OBJECTS): Makefile
|
||||
|
||||
include $(SRC_PATH)/netboot.mak
|
||||
|
||||
ALL_OBJS = $(sort $(OBJECTS) $(NETOBJS) $(LIBCOBJS) $(LIBNETOBJS))
|
||||
ALL_OBJS = $(sort $(OBJECTS) $(LIBCOBJS) $(LIBNETOBJS))
|
||||
-include $(ALL_OBJS:%.o=%.d)
|
||||
|
||||
clean:
|
||||
|
||||
+314
-145
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
|
||||
typedef uint64_t block_number_t;
|
||||
#define NULL_BLOCK_NR 0xffffffffffffffffULL
|
||||
#define ERROR_BLOCK_NR 0xfffffffffffffffeULL
|
||||
|
||||
#define FREE_SPACE_FILLER '\xAA'
|
||||
|
||||
@@ -336,9 +337,7 @@ static inline void print_volser(const void *volser)
|
||||
|
||||
ebcdic_to_ascii((char *)volser, ascii, 6);
|
||||
ascii[6] = '\0';
|
||||
sclp_print("VOLSER=[");
|
||||
sclp_print(ascii);
|
||||
sclp_print("]\n");
|
||||
printf("VOLSER=[%s]\n", ascii);
|
||||
}
|
||||
|
||||
static inline bool unused_space(const void *p, size_t size)
|
||||
@@ -387,17 +386,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x)
|
||||
|
||||
#define ISO_PRIMARY_VD_SECTOR 16
|
||||
|
||||
static inline void read_iso_sector(uint32_t block_offset, void *buf,
|
||||
const char *errmsg)
|
||||
{
|
||||
IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg);
|
||||
}
|
||||
|
||||
static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
|
||||
static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr,
|
||||
uint32_t blks_to_load)
|
||||
{
|
||||
IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0,
|
||||
"Failed to read boot image!");
|
||||
if (virtio_read_many(block_offset, load_addr, blks_to_load)) {
|
||||
puts("Failed to read boot image!");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ISO9660_MAX_DIR_DEPTH 8
|
||||
|
||||
+37
-42
@@ -11,7 +11,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
#include "helper.h"
|
||||
@@ -58,7 +59,8 @@ uint16_t cu_type(SubChannelId schid)
|
||||
};
|
||||
|
||||
if (do_cio(schid, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) {
|
||||
panic("Failed to run SenseID CCw\n");
|
||||
puts("Failed to run SenseID CCW");
|
||||
return CU_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return sense_data.cu_type;
|
||||
@@ -90,9 +92,9 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd)
|
||||
char msgline[512];
|
||||
|
||||
if (sd->config_info & 0x8000) {
|
||||
sclp_print("Eckd Dasd Sense Data (fmt 24-bytes):\n");
|
||||
puts("Eckd Dasd Sense Data (fmt 24-bytes):");
|
||||
} else {
|
||||
sclp_print("Eckd Dasd Sense Data (fmt 32-bytes):\n");
|
||||
puts("Eckd Dasd Sense Data (fmt 32-bytes):");
|
||||
}
|
||||
|
||||
strcat(msgline, " Sense Condition Flags :");
|
||||
@@ -158,22 +160,21 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd)
|
||||
if (sd->status[1] & SNS_STAT2_IMPRECISE_END) {
|
||||
strcat(msgline, " [Imprecise-End]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
print_int(" Residual Count =", sd->res_count);
|
||||
print_int(" Phys Drive ID =", sd->phys_drive_id);
|
||||
print_int(" low cyl address =", sd->low_cyl_addr);
|
||||
print_int(" head addr & hi cyl =", sd->head_high_cyl_addr);
|
||||
print_int(" format/message =", sd->fmt_msg);
|
||||
print_int(" fmt-dependent[0-7] =", sd->fmt_dependent_info[0]);
|
||||
print_int(" fmt-dependent[8-15]=", sd->fmt_dependent_info[1]);
|
||||
print_int(" prog action code =", sd->program_action_code);
|
||||
print_int(" Configuration info =", sd->config_info);
|
||||
print_int(" mcode / hi-cyl =", sd->mcode_hicyl);
|
||||
print_int(" cyl & head addr [0]=", sd->cyl_head_addr[0]);
|
||||
print_int(" cyl & head addr [1]=", sd->cyl_head_addr[1]);
|
||||
print_int(" cyl & head addr [2]=", sd->cyl_head_addr[2]);
|
||||
printf(" Residual Count = 0x%X\n", sd->res_count);
|
||||
printf(" Phys Drive ID = 0x%X\n", sd->phys_drive_id);
|
||||
printf(" low cyl address = 0x%X\n", sd->low_cyl_addr);
|
||||
printf(" head addr & hi cyl = 0x%X\n", sd->head_high_cyl_addr);
|
||||
printf(" format/message = 0x%X\n", sd->fmt_msg);
|
||||
printf(" fmt-dependent[0-7] = 0x%llX\n", sd->fmt_dependent_info[0]);
|
||||
printf(" fmt-dependent[8-15]= 0x%llX\n", sd->fmt_dependent_info[1]);
|
||||
printf(" prog action code = 0x%X\n", sd->program_action_code);
|
||||
printf(" Configuration info = 0x%X\n", sd->config_info);
|
||||
printf(" mcode / hi-cyl = 0x%X\n", sd->mcode_hicyl);
|
||||
printf(" cyl & head addr [0]= 0x%X\n", sd->cyl_head_addr[0]);
|
||||
printf(" cyl & head addr [1]= 0x%X\n", sd->cyl_head_addr[1]);
|
||||
printf(" cyl & head addr [2]= 0x%X\n", sd->cyl_head_addr[2]);
|
||||
}
|
||||
|
||||
static void print_irb_err(Irb *irb)
|
||||
@@ -182,7 +183,7 @@ static void print_irb_err(Irb *irb)
|
||||
uint64_t prev_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa - 8);
|
||||
char msgline[256];
|
||||
|
||||
sclp_print("Interrupt Response Block Data:\n");
|
||||
puts("Interrupt Response Block Data:");
|
||||
|
||||
strcat(msgline, " Function Ctrl :");
|
||||
if (irb->scsw.ctrl & SCSW_FCTL_START_FUNC) {
|
||||
@@ -194,8 +195,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) {
|
||||
strcat(msgline, " [Clear]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Activity Ctrl :");
|
||||
@@ -220,8 +220,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.ctrl & SCSW_ACTL_SUSPENDED) {
|
||||
strcat(msgline, " [Suspended]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Status Ctrl :");
|
||||
@@ -240,9 +239,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.ctrl & SCSW_SCTL_STATUS_PEND) {
|
||||
strcat(msgline, " [Status-Pending]");
|
||||
}
|
||||
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Device Status :");
|
||||
@@ -270,8 +267,7 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.dstat & SCSW_DSTAT_UEXCP) {
|
||||
strcat(msgline, " [Unit-Exception]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
msgline[0] = '\0';
|
||||
strcat(msgline, " Channel Status :");
|
||||
@@ -299,12 +295,11 @@ static void print_irb_err(Irb *irb)
|
||||
if (irb->scsw.cstat & SCSW_CSTAT_CHAINCHK) {
|
||||
strcat(msgline, " [Chaining-Check]");
|
||||
}
|
||||
strcat(msgline, "\n");
|
||||
sclp_print(msgline);
|
||||
puts(msgline);
|
||||
|
||||
print_int(" cpa=", irb->scsw.cpa);
|
||||
print_int(" prev_ccw=", prev_ccw);
|
||||
print_int(" this_ccw=", this_ccw);
|
||||
printf(" cpa= 0x%X\n", irb->scsw.cpa);
|
||||
printf(" prev_ccw= 0x%llX\n", prev_ccw);
|
||||
printf(" this_ccw= 0x%llX\n", this_ccw);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -341,7 +336,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb)
|
||||
return -1;
|
||||
}
|
||||
if (rc) {
|
||||
print_int("ssch failed with cc=", rc);
|
||||
printf("ssch failed with cc= 0x%x\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -350,7 +345,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb)
|
||||
/* collect status */
|
||||
rc = tsch(schid, irb);
|
||||
if (rc) {
|
||||
print_int("tsch failed with cc=", rc);
|
||||
printf("tsch failed with cc= 0x%X\n", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -406,12 +401,12 @@ int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt)
|
||||
continue;
|
||||
}
|
||||
|
||||
sclp_print("cio device error\n");
|
||||
print_int(" ssid ", schid.ssid);
|
||||
print_int(" cssid ", schid.cssid);
|
||||
print_int(" sch_no", schid.sch_no);
|
||||
print_int(" ctrl-unit type", cutype);
|
||||
sclp_print("\n");
|
||||
printf("cio device error\n");
|
||||
printf(" ssid 0x%X\n", schid.ssid);
|
||||
printf(" cssid 0x%X\n", schid.cssid);
|
||||
printf(" sch_no 0x%X\n", schid.sch_no);
|
||||
printf(" ctrl-unit type 0x%X\n", cutype);
|
||||
printf("\n");
|
||||
print_irb_err(&irb);
|
||||
if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 ||
|
||||
cutype == CU_TYPE_UNKNOWN) {
|
||||
|
||||
@@ -361,6 +361,8 @@ typedef struct CcwSearchIdData {
|
||||
uint8_t record;
|
||||
} __attribute__((packed)) CcwSearchIdData;
|
||||
|
||||
extern SubChannelId net_schid;
|
||||
|
||||
int enable_mss_facility(void);
|
||||
void enable_subchannel(SubChannelId schid);
|
||||
uint16_t cu_type(SubChannelId schid);
|
||||
|
||||
+39
-32
@@ -8,7 +8,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
#include "dasd-ipl.h"
|
||||
@@ -82,7 +83,7 @@ static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype,
|
||||
do {
|
||||
has_next = dynamic_cp_fixup(cpa, &next_cpa);
|
||||
|
||||
print_int("executing ccw chain at ", cpa);
|
||||
printf("executing ccw chain at 0x%X\n", cpa);
|
||||
enable_prefixing();
|
||||
rc = do_cio(schid, cutype, cpa, CCW_FMT0);
|
||||
disable_prefixing();
|
||||
@@ -110,38 +111,29 @@ static void make_readipl(void)
|
||||
ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
|
||||
}
|
||||
|
||||
static void run_readipl(SubChannelId schid, uint16_t cutype)
|
||||
static int run_readipl(SubChannelId schid, uint16_t cutype)
|
||||
{
|
||||
if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
|
||||
panic("dasd-ipl: Failed to run Read IPL channel program\n");
|
||||
}
|
||||
return do_cio(schid, cutype, 0x00, CCW_FMT0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The architecture states that IPL1 data should consist of a psw followed by
|
||||
* format-0 READ and TIC CCWs. Let's sanity check.
|
||||
*/
|
||||
static void check_ipl1(void)
|
||||
static bool check_ipl1(void)
|
||||
{
|
||||
Ccw0 *ccwread = (Ccw0 *)0x08;
|
||||
Ccw0 *ccwtic = (Ccw0 *)0x10;
|
||||
|
||||
if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
|
||||
ccwtic->cmd_code != CCW_CMD_TIC) {
|
||||
panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
|
||||
}
|
||||
return (ccwread->cmd_code == CCW_CMD_DASD_READ &&
|
||||
ccwtic->cmd_code == CCW_CMD_TIC);
|
||||
}
|
||||
|
||||
static void check_ipl2(uint32_t ipl2_addr)
|
||||
static bool check_ipl2(uint32_t ipl2_addr)
|
||||
{
|
||||
Ccw0 *ccw = u32toptr(ipl2_addr);
|
||||
|
||||
if (ipl2_addr == 0x00) {
|
||||
panic("IPL2 address invalid. Is this disk really bootable?\n");
|
||||
}
|
||||
if (ccw->cmd_code == 0x00) {
|
||||
panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
|
||||
}
|
||||
return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00);
|
||||
}
|
||||
|
||||
static uint32_t read_ipl2_addr(void)
|
||||
@@ -187,52 +179,67 @@ static void ipl1_fixup(void)
|
||||
ccwSearchTic->cda = ptr2u32(ccwSearchID);
|
||||
}
|
||||
|
||||
static void run_ipl1(SubChannelId schid, uint16_t cutype)
|
||||
static int run_ipl1(SubChannelId schid, uint16_t cutype)
|
||||
{
|
||||
uint32_t startAddr = 0x08;
|
||||
|
||||
if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
|
||||
panic("dasd-ipl: Failed to run IPL1 channel program\n");
|
||||
}
|
||||
return do_cio(schid, cutype, startAddr, CCW_FMT0);
|
||||
}
|
||||
|
||||
static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
|
||||
static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
|
||||
{
|
||||
if (run_dynamic_ccw_program(schid, cutype, addr)) {
|
||||
panic("dasd-ipl: Failed to run IPL2 channel program\n");
|
||||
}
|
||||
return run_dynamic_ccw_program(schid, cutype, addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Limitations in vfio-ccw support complicate the IPL process. Details can
|
||||
* be found in docs/devel/s390-dasd-ipl.rst
|
||||
*/
|
||||
void dasd_ipl(SubChannelId schid, uint16_t cutype)
|
||||
int dasd_ipl(SubChannelId schid, uint16_t cutype)
|
||||
{
|
||||
PSWLegacy *pswl = (PSWLegacy *) 0x00;
|
||||
uint32_t ipl2_addr;
|
||||
|
||||
/* Construct Read IPL CCW and run it to read IPL1 from boot disk */
|
||||
make_readipl();
|
||||
run_readipl(schid, cutype);
|
||||
if (run_readipl(schid, cutype)) {
|
||||
puts("Failed to run Read IPL channel program");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ipl2_addr = read_ipl2_addr();
|
||||
check_ipl1();
|
||||
|
||||
if (!check_ipl1()) {
|
||||
puts("IPL1 invalid for DASD-IPL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fixup IPL1 channel program to account for vfio-ccw limitations, then run
|
||||
* it to read IPL2 channel program from boot disk.
|
||||
*/
|
||||
ipl1_fixup();
|
||||
run_ipl1(schid, cutype);
|
||||
check_ipl2(ipl2_addr);
|
||||
if (run_ipl1(schid, cutype)) {
|
||||
puts("Failed to run IPL1 channel program");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!check_ipl2(ipl2_addr)) {
|
||||
puts("IPL2 invalid for DASD-IPL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run IPL2 channel program to read operating system code from boot disk
|
||||
*/
|
||||
run_ipl2(schid, cutype, ipl2_addr);
|
||||
if (run_ipl2(schid, cutype, ipl2_addr)) {
|
||||
puts("Failed to run IPL2 channel program");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Transfer control to the guest operating system */
|
||||
pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */
|
||||
pswl->addr |= PSW_MASK_BAMODE; /* ... */
|
||||
jump_to_low_kernel();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
#ifndef DASD_IPL_H
|
||||
#define DASD_IPL_H
|
||||
|
||||
void dasd_ipl(SubChannelId schid, uint16_t cutype);
|
||||
int dasd_ipl(SubChannelId schid, uint16_t cutype);
|
||||
|
||||
#endif /* DASD_IPL_H */
|
||||
|
||||
+29
-79
@@ -12,88 +12,16 @@
|
||||
#ifndef IPLB_H
|
||||
#define IPLB_H
|
||||
|
||||
#define LOADPARM_LEN 8
|
||||
#ifndef QEMU_PACKED
|
||||
#define QEMU_PACKED __attribute__((packed))
|
||||
#endif
|
||||
|
||||
struct IplBlockCcw {
|
||||
uint8_t reserved0[85];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
uint8_t vm_flags;
|
||||
uint8_t reserved3[3];
|
||||
uint32_t vm_parm_len;
|
||||
uint8_t nss_name[8];
|
||||
uint8_t vm_parm[64];
|
||||
uint8_t reserved4[8];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplBlockCcw IplBlockCcw;
|
||||
|
||||
struct IplBlockFcp {
|
||||
uint8_t reserved1[305 - 1];
|
||||
uint8_t opt;
|
||||
uint8_t reserved2[3];
|
||||
uint16_t reserved3;
|
||||
uint16_t devno;
|
||||
uint8_t reserved4[4];
|
||||
uint64_t wwpn;
|
||||
uint64_t lun;
|
||||
uint32_t bootprog;
|
||||
uint8_t reserved5[12];
|
||||
uint64_t br_lba;
|
||||
uint32_t scp_data_len;
|
||||
uint8_t reserved6[260];
|
||||
uint8_t scp_data[];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplBlockFcp IplBlockFcp;
|
||||
|
||||
struct IplBlockQemuScsi {
|
||||
uint32_t lun;
|
||||
uint16_t target;
|
||||
uint16_t channel;
|
||||
uint8_t reserved0[77];
|
||||
uint8_t ssid;
|
||||
uint16_t devno;
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplBlockQemuScsi IplBlockQemuScsi;
|
||||
|
||||
struct IplParameterBlock {
|
||||
uint32_t len;
|
||||
uint8_t reserved0[3];
|
||||
uint8_t version;
|
||||
uint32_t blk0_len;
|
||||
uint8_t pbt;
|
||||
uint8_t flags;
|
||||
uint16_t reserved01;
|
||||
uint8_t loadparm[LOADPARM_LEN];
|
||||
union {
|
||||
IplBlockCcw ccw;
|
||||
IplBlockFcp fcp;
|
||||
IplBlockQemuScsi scsi;
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
typedef struct IplParameterBlock IplParameterBlock;
|
||||
|
||||
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
|
||||
#define QIPL_ADDRESS 0xcc
|
||||
|
||||
/* Boot Menu flags */
|
||||
#define QIPL_FLAG_BM_OPTS_CMD 0x80
|
||||
#define QIPL_FLAG_BM_OPTS_ZIPL 0x40
|
||||
|
||||
/*
|
||||
* This definition must be kept in sync with the definition
|
||||
* in hw/s390x/ipl.h
|
||||
*/
|
||||
struct QemuIplParameters {
|
||||
uint8_t qipl_flags;
|
||||
uint8_t reserved1[3];
|
||||
uint64_t netboot_start_addr;
|
||||
uint32_t boot_menu_timeout;
|
||||
uint8_t reserved2[12];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct QemuIplParameters QemuIplParameters;
|
||||
#include <qipl.h>
|
||||
#include <string.h>
|
||||
|
||||
extern QemuIplParameters qipl;
|
||||
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
|
||||
extern bool have_iplb;
|
||||
|
||||
#define S390_IPL_TYPE_FCP 0x00
|
||||
#define S390_IPL_TYPE_CCW 0x02
|
||||
@@ -123,4 +51,26 @@ static inline bool set_iplb(IplParameterBlock *iplb)
|
||||
return manage_iplb(iplb, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* The IPL started on the device, but failed in some way. If the IPLB chain
|
||||
* still has more devices left to try, use the next device in order.
|
||||
*/
|
||||
static inline bool load_next_iplb(void)
|
||||
{
|
||||
IplParameterBlock *next_iplb;
|
||||
|
||||
if (qipl.chain_len < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qipl.index++;
|
||||
next_iplb = (IplParameterBlock *) qipl.next_iplb;
|
||||
memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
|
||||
|
||||
qipl.chain_len--;
|
||||
qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* IPLB_H */
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "s390-arch.h"
|
||||
|
||||
@@ -32,16 +33,22 @@ static void jump_to_IPL_addr(void)
|
||||
/* should not return */
|
||||
}
|
||||
|
||||
void jump_to_IPL_code(uint64_t address)
|
||||
int jump_to_IPL_code(uint64_t address)
|
||||
{
|
||||
/* store the subsystem information _after_ the bootmap was loaded */
|
||||
write_subsystem_identification();
|
||||
write_iplb_location();
|
||||
|
||||
/* prevent unknown IPL types in the guest */
|
||||
/*
|
||||
* The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The
|
||||
* iplb.devno is set to the boot position of the target SCSI device.
|
||||
*/
|
||||
if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
|
||||
iplb.pbt = S390_IPL_TYPE_CCW;
|
||||
set_iplb(&iplb);
|
||||
iplb.devno = qipl.index;
|
||||
}
|
||||
|
||||
if (have_iplb && !set_iplb(&iplb)) {
|
||||
panic("Failed to set IPLB");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -57,7 +64,7 @@ void jump_to_IPL_code(uint64_t address)
|
||||
debug_print_int("set IPL addr to", address ?: *reset_psw & PSW_MASK_SHORT_ADDR);
|
||||
|
||||
/* Ensure the guest output starts fresh */
|
||||
sclp_print("\n");
|
||||
printf("\n");
|
||||
|
||||
/*
|
||||
* HACK ALERT.
|
||||
@@ -67,7 +74,8 @@ void jump_to_IPL_code(uint64_t address)
|
||||
asm volatile("lghi %%r1,1\n\t"
|
||||
"diag %%r1,%%r1,0x308\n\t"
|
||||
: : : "1", "memory");
|
||||
panic("\n! IPL returns !\n");
|
||||
puts("IPL code jump failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void jump_to_low_kernel(void)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user