ZIPL adds a "BOOT_IMAGE=x" to the kernel parameters to indicate which kernel entry has been chosen during the boot process. Apparently some Linux tools like "dracut" use this setting, so we should provide this kernel parameter with the s390-ccw bios, too. However, it's a little bit tricky to get additional parameters from the s390-ccw bios into the kernel command line: Since we are running the ZIPL stage 3 boot loader first (which then finally jumps into the Linux kernel), we have to adapt to the parameter conventions of ZIPL and put the argument into ZIPLs "COMMAND_LINE_EXTRA" area. Unfortunately, the location of this area changed in the course of time (it has been moved between ZIPL v2.9 and v2.10), so we need to detect the right version of ZIPL here, too. The only reasonable way that I could figure out was the start address of the ZIPL stage 3 bootloader which has been changed in almost the same timeframe - just a little bit earlier, between v2.8 and v2.9, so if a user is using exactly ZIPL v2.9, they won't see the new BOOT_IMAGE parameter (but at least the new code in s390-ccw should also not hurt in this case - the area where we write the parameter to is just the lowest part of the stack area of ZIPL, which should be unused). Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1782026 Signed-off-by: Thomas Huth <thuth@xxxxxxxxxx> --- Note: I've marked the patch as RFC since I'm not quite sure whether this is really the right way to address this issue: It's unfortunate that we have to mess with different location in ZIPL which might also change again in the future. As suggested by Christian on IRC last week, maybe it would make more sense to change ZIPL to add this parameter already when zipl is installed (i.e. by the Linux userspace "zipl" pro- gram), instead of adding it during boot time? Also, the BOOT_IMAGE para- meter on s390x is quite different from the BOOT_IMAGE paramter that is used on x86 - while s390x only uses one single number here, the x86 variant (added by grub2, I guess) uses the boot device + full filename of the kernel on the boot partition. Should we maybe make the s390x variant more conform to x86? If so, I think this really has to be fixed in zipl userspace tool, and not in the s390-ccw bios (and zipl stage3 bootloader). pc-bios/s390-ccw/bootmap.c | 56 +++++++++++++++++++++++++++++++++++-- pc-bios/s390-ccw/jump2ipl.c | 2 +- pc-bios/s390-ccw/s390-ccw.h | 2 ++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index d13b7cbd15..bc7fa597b4 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -49,6 +49,56 @@ static inline bool is_iso_vd_valid(IsoVolDesc *vd) vd->type <= VOL_DESC_TYPE_PARTITION; } +/** + * The ZIPL boot loader adds a BOOT_IMAGE=x to the kernel parameters + * (where x is the number of the selected boot entry). Since some + * programs might rely on this parameter, we mimic this behavior here. + */ +static void add_boot_image_param(uint64_t start_addr, int index) +{ + /* "BOOT_IMAGE=00" in EBCDIC */ + char bootimg_str[15] = { + 0xc2, 0xd6, 0xd6, 0xe3, 0x6d, 0xc9, 0xd4, 0xc1, 0xc7, 0xc5, 0x7e, + 0xf0, 0xf0, 0 + }; + + /* Only do it for Linux images */ + if (memcmp((char *)LINUX_MAGIC_ADDR, "S390EP", 6) != 0) { + return; + } + + if (index < 10) { + bootimg_str[11] = 0xf0 + index; /* 0xf0 is '0' in EBCDIC */ + bootimg_str[12] = 0; + } else if (index < 100) { + bootimg_str[11] = 0xf0 + index / 10; + bootimg_str[12] = 0xf0 + index % 10; + } else { + /* This should never happen since index should be < MAX_BOOT_ENTRIES */ + panic("BOOT_IMAGE index too big"); + } + + /* + * Now write the parameter to the COMMAND_LINE_EXTRA area of the zipl + * stage3 boot loader that we are going to run. Unfortunately, the + * location of this area changed in the course of time, but we can + * use the stage3 start address to determine which area we have to + * use (unless it is zipl v2.9 - the start address already has changed + * there but the area has not been moved yet ... so for this version + * we are writing the parameters into the unused stack area instead + * and thus the BOOT_PARAM won't show up there) + */ + if ((start_addr & 0x7fffffff) == 0xa050) { + *(uint64_t *)0xa020 = true; + memcpy((char *)0xa000 - 0x400, bootimg_str, sizeof(bootimg_str)); + } else if ((start_addr & 0x7fffffff) == 0xa000) { + *(uint64_t *)0x9020 = true; + memcpy((char *)0xe000, bootimg_str, sizeof(bootimg_str)); + } else { + sclp_print("\nWarning: Unsupported ZIPL stage 3 start address.\n"); + } +} + /*********************************************************************** * IPL an ECKD DASD (CDL or LDL/CMS format) */ @@ -480,7 +530,7 @@ static void zipl_load_segment(ComponentEntry *entry) } /* Run a zipl program */ -static void zipl_run(ScsiBlockPtr *pte) +static void zipl_run(ScsiBlockPtr *pte, int loadparm) { ComponentHeader *header; ComponentEntry *entry; @@ -515,6 +565,8 @@ static void zipl_run(ScsiBlockPtr *pte) IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry"); + add_boot_image_param(entry->load_address, loadparm); + /* should not return */ jump_to_IPL_code(entry->load_address); } @@ -565,7 +617,7 @@ static void ipl_scsi(void) IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); - zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ + zipl_run(&prog_table->entry[loadparm].scsi, loadparm); /* no return */ } /*********************************************************************** diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index 266f1502b9..36090631f9 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -77,7 +77,7 @@ void jump_to_low_kernel(void) * kernel start address (when jumping to the PSW-at-zero address instead, * the kernel startup code fails when we booted from a network device). */ - if (!memcmp((char *)0x10008, "S390EP", 6)) { + if (!memcmp((char *)LINUX_MAGIC_ADDR, "S390EP", 6)) { jump_to_IPL_code(KERN_IMAGE_START); } diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 11bce7d73c..3e23c3c400 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -46,6 +46,8 @@ typedef unsigned long long __u64; #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define LINUX_MAGIC_ADDR 0x010008UL + #include "cio.h" #include "iplb.h" -- 2.18.1