diff --git a/ipl_tools/cmd_chreipl.c b/ipl_tools/cmd_chreipl.c index f58a7d605..b7cca2d9e 100644 --- a/ipl_tools/cmd_chreipl.c +++ b/ipl_tools/cmd_chreipl.c @@ -23,12 +23,15 @@ #define BOOTPARMS_CCW_MAX 64 #define BOOTPARMS_FCP_MAX 3452 +#define OPT_BRCHR 0x80 + enum target_type { TT_CCW, TT_FCP, TT_NSS, TT_NODE, TT_NVME, + TT_ECKD, }; enum reipl_type { @@ -36,12 +39,14 @@ enum reipl_type { REIPL_CCW, REIPL_NSS, REIPL_NVME, + REIPL_ECKD, }; static const char *const usage_chreipl = "Usage: %s [TARGET] [ARGS] [OPTIONS]\n" "\n" " chreipl [ccw] [-d] [OPTIONS]\n" +" chreipl [eckd] [-d] [OPTIONS]\n" " chreipl [fcp] [-d] [-w] [-l] [OPTIONS]\n" " chreipl nvme [-i] [-s] [OPTIONS]\n" " chreipl [node] [OPTIONS]\n" @@ -50,6 +55,7 @@ static const char *const usage_chreipl = "\n" "The following re-IPL targets are supported:\n" " ccw IPL from CCW device\n" +" eckd IPL from ECKD device\n" " fcp IPL from FCP device\n" " nvme IPL from NVME device\n" " nss IPL from NSS\n" @@ -66,6 +72,13 @@ static const char *const usage_chreipl = " -L, --loadparm Loadparm specification\n" " -c, --clear 0|1 Control if memory is cleared on re-IPL\n" "\n" +"Options for eckd target:\n" +" -d, --device Device number of the ECKD IPL device\n" +" -b, --bootprog Bootprog specification\n" +" --brchr Boot record location in Cylinder,Head,Record format\n" +" -L, --loadparm Loadparm specification\n" +" -c, --clear 0|1 Control if memory is cleared on re-IPL\n" +"\n" "Options for fcp target:\n" " -d, --device Device number of the adapter of the FCP IPL device\n" " -l --lun Logical unit number of the FCP IPL device\n" @@ -114,6 +127,8 @@ static struct locals { int target_type_auto_mode; enum reipl_type reipl_type; /* CCW, FCP, NVME, NSS */ int reipl_clear; + char *brchr; + int brchr_set; } l; static void __noreturn print_usage_chreipl_exit(void) @@ -329,6 +344,19 @@ static void parse_ccw_args(char *nargv[], int nargc) set_device(nargv[0]); } +static void parse_eckd_args(char *nargv[], int nargc) +{ + /* + * we might be called like this: + * chreipl eckd 4711 + */ + if (l.busid_set) + ERR_EXIT("Use either options or positional parameters"); + if (nargc > 1) + ERR_EXIT("Too many arguments specified for \"eckd\" re-IPL type"); + set_device(nargv[0]); +} + static void parse_nss_args(char *nargv[], int nargc) { /* @@ -369,6 +397,8 @@ static int set_reipl_type(const char *dev_name) l.reipl_type = REIPL_FCP; else if (strncmp(dev_name, "nvme", strlen("nvme")) == 0) l.reipl_type = REIPL_NVME; + else if (strncmp(dev_name, "eckd", strlen("eckd")) == 0) + l.reipl_type = REIPL_ECKD; else return -1; @@ -493,12 +523,15 @@ static void parse_pos_args(char *nargv[], int nargc) case TT_NODE: parse_node_args(nargv, nargc); break; + case TT_ECKD: + parse_eckd_args(nargv, nargc); + break; } } static void check_fcp_opts(void) { - if (l.nss_name_set) + if (l.nss_name_set || l.brchr_set) ERR_EXIT("Invalid option for \"fcp\" target specified"); if (!(l.busid_set && l.wwpn_set && l.lun_set)) ERR_EXIT("The \"fcp\" target requires device, WWPN, " @@ -507,7 +540,8 @@ static void check_fcp_opts(void) static void check_nvme_opts(void) { - if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set) + if (l.nss_name_set || l.wwpn_set || l.lun_set || l.busid_set || + l.brchr_set) ERR_EXIT("Invalid option for \"nvme\" target specified"); if (!(l.fid_set && l.nsid_set)) ERR_EXIT("The \"nvme\" target requires FID, and optional NSID"); @@ -515,16 +549,25 @@ static void check_nvme_opts(void) static void check_ccw_opts(void) { - if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set) + if (l.bootprog_set || l.lun_set || l.wwpn_set || l.nss_name_set || + l.brchr_set) ERR_EXIT("Invalid option for \"ccw\" target specified"); if (!l.busid_set) ERR_EXIT("The \"ccw\" target requires device"); } +static void check_eckd_opts(void) +{ + if (l.nss_name_set || l.wwpn_set || l.lun_set || l.fid_set) + ERR_EXIT("Invalid option for \"eckd\" target specified"); + if (!(l.busid_set)) + ERR_EXIT("The \"eckd\" target requires device"); +} + static void check_nss_opts(void) { if (l.bootprog_set || l.loadparm_set || l.busid_set || l.wwpn_set || - l.lun_set) + l.lun_set || l.brchr_set) ERR_EXIT("Invalid option for \"nss\" target specified"); if (!l.nss_name_set) ERR_EXIT("The \"nss\" target requires NSS name"); @@ -563,6 +606,12 @@ static void set_reipl_clear(const char *arg) ERR_EXIT("re-IPL clear argument must be either 1 or 0"); } +static void set_brchr(const char *arg) +{ + l.brchr = strdup(arg); + l.brchr_set = 1; +} + static void parse_chreipl_options(int argc, char *argv[]) { int opt, idx; @@ -580,6 +629,7 @@ static void parse_chreipl_options(int argc, char *argv[]) { "force", no_argument, NULL, 'f' }, { "version", no_argument, NULL, 'v' }, { "clear", required_argument, NULL, 'c' }, + { "brchr", required_argument, NULL, OPT_BRCHR }, { NULL, 0, NULL, 0 } }; static const char optstr[] = "hd:vw:l:fL:b:n:p:c:i:s:"; @@ -592,6 +642,8 @@ static void parse_chreipl_options(int argc, char *argv[]) set_target_type(TT_FCP, 0); else if (strcmp(argv[1], "ccw") == 0) set_target_type(TT_CCW, 0); + else if (strcmp(argv[1], "eckd") == 0) + set_target_type(TT_ECKD, 0); else if (strcmp(argv[1], "nss") == 0) set_target_type(TT_NSS, 0); else if (strcmp(argv[1], "nvme") == 0) @@ -640,6 +692,9 @@ static void parse_chreipl_options(int argc, char *argv[]) case 'c': set_reipl_clear(optarg); break; + case OPT_BRCHR: + set_brchr(optarg); + break; case 'v': print_version_exit(); default: @@ -737,6 +792,34 @@ static void chreipl_ccw(void) print_ccw(0); } +static void chreipl_eckd(void) +{ + check_eckd_opts(); + + if (!ccw_is_device(l.busid) && !l.force_set) { + if (is_ignored(l.busid)) + ERR_EXIT("Device is on cio_ignore list, try \"cio_ignore -r %s\"?", + l.busid); + ERR_EXIT("Could not find DASD ECKD device \"%s\"", l.busid); + } + + if (l.reipl_clear >= 0) { + check_exists("reipl/eckd/clear", "ECKD re-IPL clear attribute"); + write_str(l.reipl_clear ? "1" : "0", "reipl/eckd/clear"); + } + + if (!l.brchr_set) + l.brchr = "auto"; + write_str(l.brchr, "reipl/eckd/br_chr"); + if (!l.bootprog_set) + sprintf(l.bootprog, "0"); + write_str(l.bootprog, "reipl/eckd/bootprog"); + write_str(l.busid, "reipl/eckd/device"); + write_str_optional(l.loadparm, "reipl/eckd/loadparm", l.loadparm_set, "loadparm"); + write_str("eckd", "reipl/reipl_type"); + print_eckd(0, "eckd"); +} + static void chreipl_fcp(void) { check_fcp_opts(); @@ -850,6 +933,11 @@ static void chreipl_node(void) l.busid_set = 1; chreipl_ccw(); break; + case REIPL_ECKD: + ccw_busid_get(l.dev, l.busid); + l.busid_set = 1; + chreipl_eckd(); + break; case REIPL_FCP: fcp_wwpn_get(l.dev, l.wwpn); l.wwpn_set = 1; @@ -878,6 +966,9 @@ void cmd_chreipl(int argc, char *argv[]) case TT_CCW: chreipl_ccw(); break; + case TT_ECKD: + chreipl_eckd(); + break; case TT_FCP: chreipl_fcp(); break; diff --git a/ipl_tools/cmd_lsreipl.c b/ipl_tools/cmd_lsreipl.c index b006896c3..b0cd8f3cc 100644 --- a/ipl_tools/cmd_lsreipl.c +++ b/ipl_tools/cmd_lsreipl.c @@ -141,6 +141,30 @@ void print_ccw(int show_ipl) print_fw_str("clear: %s\n", dir, "clear"); } +void print_eckd(int show_ipl, const char *name) +{ + char *path_loadparm = show_ipl ? "/sys/firmware/ipl/loadparm" : + "/sys/firmware/reipl/eckd/loadparm"; + char *dir = show_ipl ? "ipl" : "reipl/eckd"; + char loadparm[9], loadparm_path[PATH_MAX]; + + printf("%-12s %s\n", get_ipl_banner(show_ipl), name); + + print_fw_str("Device: %s\n", dir, "device"); + print_fw_str("bootprog: %s\n", dir, "bootprog"); + print_fw_str("br_chr: %s\n", dir, "br_chr"); + print_fw_str("Bootparm: \"%s\"\n", dir, "scp_data"); + if (access(path_loadparm, R_OK) == 0) { + sprintf(loadparm_path, "%s/%s", dir, "loadparm"); + read_fw_str(loadparm, loadparm_path, sizeof(loadparm)); + if (strcmp(loadparm, " ") == 0) + loadparm[0] = 0; + printf("Loadparm: \"%s\"\n", loadparm); + } + if (!show_ipl) + print_fw_str("clear: %s\n", dir, "clear"); +} + static void parse_lsreipl_options(int argc, char *argv[]) { int opt, idx; @@ -193,6 +217,9 @@ void cmd_lsreipl(int argc, char *argv[]) print_nvme(l.ipl_set, 1); else if (strcmp(reipl_type_str, "ccw") == 0) print_ccw(l.ipl_set); + else if (strcmp(reipl_type_str, "eckd") == 0 || + strcmp(reipl_type_str, "eckd_dump") == 0) + print_eckd(l.ipl_set, reipl_type_str); else if (strcmp(reipl_type_str, "nss") == 0) print_nss(l.ipl_set); else diff --git a/ipl_tools/ipl_tools.h b/ipl_tools/ipl_tools.h index cd9167f0f..e9d119769 100644 --- a/ipl_tools/ipl_tools.h +++ b/ipl_tools/ipl_tools.h @@ -45,6 +45,7 @@ extern void print_ccw(int show_ipl); extern void print_fcp(int show_ipl, int dump); extern void print_nvme(int show_ipl, int dump); extern void print_nss(int show_ipl); +extern void print_eckd(int show_ipl, const char *name); /* * Helper diff --git a/ipl_tools/man/chreipl.8 b/ipl_tools/man/chreipl.8 index 89896134e..0a5b845d4 100644 --- a/ipl_tools/man/chreipl.8 +++ b/ipl_tools/man/chreipl.8 @@ -36,6 +36,9 @@ The first argument specifies the re-IPL target: .RB "- " ccw : Specify a DASD CCW device for reboot .TP +.RB "- " eckd : +Specify a DASD ECKD device for reboot +.TP .RB "- " fcp : Specify a FCP device for reboot .TP @@ -175,6 +178,63 @@ in the installed zIPL environment block for evaluation of zIPL environment variables in the kernel command line: \fB# chreipl ccw -d 0.0.7e78 -L 1S2\fP + +.SH eckd +Use the eckd re-IPL target for DASD devices that are accessed by the hardware +using channel command word (CCW) channels. In contrast to the ccw re-IPL target, +the eckd target uses a list-directed bootloader that supports secure boot. +.TP +.BR "\-d" " or " "\-\-device" +Specifies the device bus-ID of the DASD. If the bus-ID starts with "0.0." this +prefix can be omitted and the four digit short notation can be used (e.g. 5000 is +the same as 0.0.5000). + +.TP +.BR "\-b" " or " "\-\-bootprog" +Specifies an entry in the ECKD boot configuration by defining the IPL boot +program selector. If omitted, '0' is used. + +.TP +.BR "\-\-brchr" +Specifies the block on the DASD where the boot record is located. If omitted, +0 is used. The specification comprises comma-separated values for cylinder, head, +and record: "--brchr C,H,R". Alternatively, specifying "0" or "auto" makes the +bootloader determine the boot record location from the volume label. + +.TP +.BR "\-L" " or " "\-\-loadparm" +The loadparm for the eckd re-IPL target is not used to control the ECKD boot +configuration that is defined by the +.BR zipl (8) +boot menu. Instead it can be used to control higher level boot loaders +like GRUB. For more details refer to distribution specific documentation. + +.TP +.BR "\-c" " or " "\-\-clear" +Controls whether memory is cleared on re-IPL. Possible values are 0 to +disable and 1 to enable memory clearing on re-IPL. +Memory clearing is supported if the "clear" attribute is present in +/sys/firmware/reipl/eckd/. + +.PP +\fBExamples:\fP +.br + +1. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and boot +program selector 0: + +\fB# chreipl eckd 0.0.7e78\fP + +2. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and boot +program selector 2: + +\fB# chreipl eckd -d 0.0.7e78 -b 2\fP + +3. Next time reboot from the ECKD device with the bus-ID 0.0.7e78 and tell +the bootloader that the bootrecord is located in Cylinder 3, Head 0, Record 1: + +\fB# chreipl eckd -d 0.0.7e78 --brchr 3,0,1\fP + .SH fcp Use the fcp re-IPL target for SCSI disks that are accessed by the hardware using Fibre Channel Protocol (FCP) channels.