s390/ipl: add eckd support

This adds support to IPL from ECKD DASDs to linux.
It introduces a few sysfs files in /sys/firmware/reipl/eckd:

bootprog: the boot program selector
clear:    whether to issue a diag308 LOAD_NORMAL or LOAD_CLEAR
device:   the device to ipl from
br_chr:   Cylinder/Head/Record number to read the bootrecord from.
          Might be '0' or 'auto' if it should be read from the
	  volume label.
scpdata:  data to be passed to the ipl'd program.

The new ipl type is called 'eckd'.

Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
This commit is contained in:
Sven Schnelle 2022-10-05 10:17:40 +02:00 committed by Alexander Gordeev
parent 5de2322d7b
commit 87fd22e0ae
6 changed files with 329 additions and 1 deletions

View File

@ -108,6 +108,11 @@ static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
scp_data_len = ipb->nvme.scp_data_len;
scp_data = ipb->nvme.scp_data;
break;
case IPL_PBT_ECKD:
scp_data_len = ipb->eckd.scp_data_len;
scp_data = ipb->eckd.scp_data;
break;
default:
goto out;
}
@ -153,6 +158,7 @@ static void append_ipl_block_parm(void)
break;
case IPL_PBT_FCP:
case IPL_PBT_NVME:
case IPL_PBT_ECKD:
rc = ipl_block_get_ascii_scpdata(
parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
break;

View File

@ -22,6 +22,7 @@ struct ipl_parameter_block {
struct ipl_pb0_common common;
struct ipl_pb0_fcp fcp;
struct ipl_pb0_ccw ccw;
struct ipl_pb0_eckd eckd;
struct ipl_pb0_nvme nvme;
char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
};
@ -41,6 +42,10 @@ struct ipl_parameter_block {
sizeof(struct ipl_pb0_ccw))
#define IPL_BP0_CCW_LEN (sizeof(struct ipl_pb0_ccw))
#define IPL_BP_ECKD_LEN (sizeof(struct ipl_pl_hdr) + \
sizeof(struct ipl_pb0_eckd))
#define IPL_BP0_ECKD_LEN (sizeof(struct ipl_pb0_eckd))
#define IPL_MAX_SUPPORTED_VERSION (0)
#define IPL_RB_CERT_UNKNOWN ((unsigned short)-1)
@ -68,6 +73,7 @@ enum ipl_type {
IPL_TYPE_NSS = 16,
IPL_TYPE_NVME = 32,
IPL_TYPE_NVME_DUMP = 64,
IPL_TYPE_ECKD = 128,
};
struct ipl_info
@ -77,6 +83,9 @@ struct ipl_info
struct {
struct ccw_dev_id dev_id;
} ccw;
struct {
struct ccw_dev_id dev_id;
} eckd;
struct {
struct ccw_dev_id dev_id;
u64 wwpn;

View File

@ -87,6 +87,7 @@ struct sclp_info {
unsigned char has_gisaf : 1;
unsigned char has_diag318 : 1;
unsigned char has_sipl : 1;
unsigned char has_sipl_eckd : 1;
unsigned char has_dirq : 1;
unsigned char has_iplcc : 1;
unsigned char has_zpci_lsi : 1;

View File

@ -27,6 +27,7 @@ enum ipl_pbt {
IPL_PBT_FCP = 0,
IPL_PBT_SCP_DATA = 1,
IPL_PBT_CCW = 2,
IPL_PBT_ECKD = 3,
IPL_PBT_NVME = 4,
};
@ -111,6 +112,33 @@ struct ipl_pb0_ccw {
__u8 reserved5[8];
} __packed;
/* IPL Parameter Block 0 for ECKD */
struct ipl_pb0_eckd {
__u32 len;
__u8 pbt;
__u8 reserved1[3];
__u32 reserved2[78];
__u8 opt;
__u8 reserved4[4];
__u8 reserved5:5;
__u8 ssid:3;
__u16 devno;
__u32 reserved6[5];
__u32 bootprog;
__u8 reserved7[12];
struct {
__u16 cyl;
__u8 head;
__u8 record;
__u32 reserved;
} br_chr __packed;
__u32 scp_data_len;
__u8 reserved8[260];
__u8 scp_data[];
} __packed;
#define IPL_PB0_ECKD_OPT_IPL 0x10
#define IPL_PB0_CCW_VM_FLAG_NSS 0x80
#define IPL_PB0_CCW_VM_FLAG_VP 0x40

View File

@ -40,6 +40,7 @@
#define IPL_UNKNOWN_STR "unknown"
#define IPL_CCW_STR "ccw"
#define IPL_ECKD_STR "eckd"
#define IPL_FCP_STR "fcp"
#define IPL_FCP_DUMP_STR "fcp_dump"
#define IPL_NVME_STR "nvme"
@ -93,6 +94,8 @@ static char *ipl_type_str(enum ipl_type type)
switch (type) {
case IPL_TYPE_CCW:
return IPL_CCW_STR;
case IPL_TYPE_ECKD:
return IPL_ECKD_STR;
case IPL_TYPE_FCP:
return IPL_FCP_STR;
case IPL_TYPE_FCP_DUMP:
@ -148,6 +151,7 @@ static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
static struct ipl_parameter_block *reipl_block_fcp;
static struct ipl_parameter_block *reipl_block_nvme;
static struct ipl_parameter_block *reipl_block_ccw;
static struct ipl_parameter_block *reipl_block_eckd;
static struct ipl_parameter_block *reipl_block_nss;
static struct ipl_parameter_block *reipl_block_actual;
@ -162,6 +166,7 @@ static struct sclp_ipl_info sclp_ipl_info;
static bool reipl_nvme_clear;
static bool reipl_fcp_clear;
static bool reipl_ccw_clear;
static bool reipl_eckd_clear;
static inline int __diag308(unsigned long subcode, void *addr)
{
@ -282,6 +287,8 @@ static __init enum ipl_type get_ipl_type(void)
return IPL_TYPE_NVME_DUMP;
else
return IPL_TYPE_NVME;
case IPL_PBT_ECKD:
return IPL_TYPE_ECKD;
}
return IPL_TYPE_UNKNOWN;
}
@ -335,6 +342,9 @@ static ssize_t sys_ipl_device_show(struct kobject *kobj,
case IPL_TYPE_CCW:
return sprintf(page, "0.%x.%04x\n", ipl_block.ccw.ssid,
ipl_block.ccw.devno);
case IPL_TYPE_ECKD:
return sprintf(page, "0.%x.%04x\n", ipl_block.eckd.ssid,
ipl_block.eckd.devno);
case IPL_TYPE_FCP:
case IPL_TYPE_FCP_DUMP:
return sprintf(page, "0.0.%04x\n", ipl_block.fcp.devno);
@ -380,12 +390,25 @@ static ssize_t ipl_nvme_scp_data_read(struct file *filp, struct kobject *kobj,
return memory_read_from_buffer(buf, count, &off, scp_data, size);
}
static ssize_t ipl_eckd_scp_data_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
unsigned int size = ipl_block.eckd.scp_data_len;
void *scp_data = &ipl_block.eckd.scp_data;
return memory_read_from_buffer(buf, count, &off, scp_data, size);
}
static struct bin_attribute ipl_scp_data_attr =
__BIN_ATTR(scp_data, S_IRUGO, ipl_scp_data_read, NULL, PAGE_SIZE);
static struct bin_attribute ipl_nvme_scp_data_attr =
__BIN_ATTR(scp_data, S_IRUGO, ipl_nvme_scp_data_read, NULL, PAGE_SIZE);
static struct bin_attribute ipl_eckd_scp_data_attr =
__BIN_ATTR(scp_data, S_IRUGO, ipl_eckd_scp_data_read, NULL, PAGE_SIZE);
static struct bin_attribute *ipl_fcp_bin_attrs[] = {
&ipl_parameter_attr,
&ipl_scp_data_attr,
@ -398,6 +421,12 @@ static struct bin_attribute *ipl_nvme_bin_attrs[] = {
NULL,
};
static struct bin_attribute *ipl_eckd_bin_attrs[] = {
&ipl_parameter_attr,
&ipl_eckd_scp_data_attr,
NULL,
};
/* FCP ipl device attributes */
DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n",
@ -419,6 +448,88 @@ DEFINE_IPL_ATTR_RO(ipl_nvme, bootprog, "%lld\n",
DEFINE_IPL_ATTR_RO(ipl_nvme, br_lba, "%lld\n",
(unsigned long long)ipl_block.nvme.br_lba);
/* ECKD ipl device attributes */
DEFINE_IPL_ATTR_RO(ipl_eckd, bootprog, "%lld\n",
(unsigned long long)ipl_block.eckd.bootprog);
#define IPL_ATTR_BR_CHR_SHOW_FN(_name, _ipb) \
static ssize_t eckd_##_name##_br_chr_show(struct kobject *kobj, \
struct kobj_attribute *attr, \
char *buf) \
{ \
struct ipl_pb0_eckd *ipb = &(_ipb); \
\
if (!ipb->br_chr.cyl && \
!ipb->br_chr.head && \
!ipb->br_chr.record) \
return sprintf(buf, "auto\n"); \
\
return sprintf(buf, "0x%x,0x%x,0x%x\n", \
ipb->br_chr.cyl, \
ipb->br_chr.head, \
ipb->br_chr.record); \
}
#define IPL_ATTR_BR_CHR_STORE_FN(_name, _ipb) \
static ssize_t eckd_##_name##_br_chr_store(struct kobject *kobj, \
struct kobj_attribute *attr, \
const char *buf, size_t len) \
{ \
struct ipl_pb0_eckd *ipb = &(_ipb); \
unsigned long args[3] = { 0 }; \
char *p, *p1, *tmp = NULL; \
int i, rc; \
\
if (!strncmp(buf, "auto", 4)) \
goto out; \
\
tmp = kstrdup(buf, GFP_KERNEL); \
p = tmp; \
for (i = 0; i < 3; i++) { \
p1 = strsep(&p, ", "); \
if (!p1) { \
rc = -EINVAL; \
goto err; \
} \
rc = kstrtoul(p1, 0, args + i); \
if (rc) \
goto err; \
} \
\
rc = -EINVAL; \
if (i != 3) \
goto err; \
\
if ((args[0] || args[1]) && !args[2]) \
goto err; \
\
if (args[0] > UINT_MAX || args[1] > 255 || args[2] > 255) \
goto err; \
\
out: \
ipb->br_chr.cyl = args[0]; \
ipb->br_chr.head = args[1]; \
ipb->br_chr.record = args[2]; \
rc = len; \
err: \
kfree(tmp); \
return rc; \
}
IPL_ATTR_BR_CHR_SHOW_FN(ipl, ipl_block.eckd);
static struct kobj_attribute sys_ipl_eckd_br_chr_attr =
__ATTR(br_chr, (S_IRUGO | S_IWUSR),
eckd_ipl_br_chr_show,
NULL);
IPL_ATTR_BR_CHR_SHOW_FN(reipl, reipl_block_eckd->eckd);
IPL_ATTR_BR_CHR_STORE_FN(reipl, reipl_block_eckd->eckd);
static struct kobj_attribute sys_reipl_eckd_br_chr_attr =
__ATTR(br_chr, (S_IRUGO | S_IWUSR),
eckd_reipl_br_chr_show,
eckd_reipl_br_chr_store);
static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
@ -470,6 +581,20 @@ static struct attribute_group ipl_nvme_attr_group = {
.bin_attrs = ipl_nvme_bin_attrs,
};
static struct attribute *ipl_eckd_attrs[] = {
&sys_ipl_type_attr.attr,
&sys_ipl_eckd_bootprog_attr.attr,
&sys_ipl_eckd_br_chr_attr.attr,
&sys_ipl_device_attr.attr,
&sys_ipl_secure_attr.attr,
&sys_ipl_has_secure_attr.attr,
NULL,
};
static struct attribute_group ipl_eckd_attr_group = {
.attrs = ipl_eckd_attrs,
.bin_attrs = ipl_eckd_bin_attrs,
};
/* CCW ipl device attributes */
@ -542,6 +667,9 @@ static int __init ipl_init(void)
rc = sysfs_create_group(&ipl_kset->kobj,
&ipl_ccw_attr_group_lpar);
break;
case IPL_TYPE_ECKD:
rc = sysfs_create_group(&ipl_kset->kobj, &ipl_eckd_attr_group);
break;
case IPL_TYPE_FCP:
case IPL_TYPE_FCP_DUMP:
rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
@ -986,6 +1114,85 @@ static struct attribute_group reipl_ccw_attr_group_lpar = {
.attrs = reipl_ccw_attrs_lpar,
};
/* ECKD reipl device attributes */
static ssize_t reipl_eckd_scpdata_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
size_t size = reipl_block_eckd->eckd.scp_data_len;
void *scp_data = reipl_block_eckd->eckd.scp_data;
return memory_read_from_buffer(buf, count, &off, scp_data, size);
}
static ssize_t reipl_eckd_scpdata_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
size_t scpdata_len = count;
size_t padding;
if (off)
return -EINVAL;
memcpy(reipl_block_eckd->eckd.scp_data, buf, count);
if (scpdata_len % 8) {
padding = 8 - (scpdata_len % 8);
memset(reipl_block_eckd->eckd.scp_data + scpdata_len,
0, padding);
scpdata_len += padding;
}
reipl_block_eckd->hdr.len = IPL_BP_ECKD_LEN + scpdata_len;
reipl_block_eckd->eckd.len = IPL_BP0_ECKD_LEN + scpdata_len;
reipl_block_eckd->eckd.scp_data_len = scpdata_len;
return count;
}
static struct bin_attribute sys_reipl_eckd_scp_data_attr =
__BIN_ATTR(scp_data, (S_IRUGO | S_IWUSR), reipl_eckd_scpdata_read,
reipl_eckd_scpdata_write, DIAG308_SCPDATA_SIZE);
static struct bin_attribute *reipl_eckd_bin_attrs[] = {
&sys_reipl_eckd_scp_data_attr,
NULL,
};
DEFINE_IPL_CCW_ATTR_RW(reipl_eckd, device, reipl_block_eckd->eckd);
DEFINE_IPL_ATTR_RW(reipl_eckd, bootprog, "%lld\n", "%lld\n",
reipl_block_eckd->eckd.bootprog);
static struct attribute *reipl_eckd_attrs[] = {
&sys_reipl_eckd_device_attr.attr,
&sys_reipl_eckd_bootprog_attr.attr,
&sys_reipl_eckd_br_chr_attr.attr,
NULL,
};
static struct attribute_group reipl_eckd_attr_group = {
.attrs = reipl_eckd_attrs,
.bin_attrs = reipl_eckd_bin_attrs
};
static ssize_t reipl_eckd_clear_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
return sprintf(page, "%u\n", reipl_eckd_clear);
}
static ssize_t reipl_eckd_clear_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t len)
{
if (strtobool(buf, &reipl_eckd_clear) < 0)
return -EINVAL;
return len;
}
static struct kobj_attribute sys_reipl_eckd_clear_attr =
__ATTR(clear, 0644, reipl_eckd_clear_show, reipl_eckd_clear_store);
/* NSS reipl device attributes */
static void reipl_get_ascii_nss_name(char *dst,
@ -1069,6 +1276,9 @@ static int reipl_set_type(enum ipl_type type)
case IPL_TYPE_CCW:
reipl_block_actual = reipl_block_ccw;
break;
case IPL_TYPE_ECKD:
reipl_block_actual = reipl_block_eckd;
break;
case IPL_TYPE_FCP:
reipl_block_actual = reipl_block_fcp;
break;
@ -1099,6 +1309,8 @@ static ssize_t reipl_type_store(struct kobject *kobj,
if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
rc = reipl_set_type(IPL_TYPE_CCW);
else if (strncmp(buf, IPL_ECKD_STR, strlen(IPL_ECKD_STR)) == 0)
rc = reipl_set_type(IPL_TYPE_ECKD);
else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
rc = reipl_set_type(IPL_TYPE_FCP);
else if (strncmp(buf, IPL_NVME_STR, strlen(IPL_NVME_STR)) == 0)
@ -1114,6 +1326,7 @@ static struct kobj_attribute reipl_type_attr =
static struct kset *reipl_kset;
static struct kset *reipl_fcp_kset;
static struct kset *reipl_nvme_kset;
static struct kset *reipl_eckd_kset;
static void __reipl_run(void *unused)
{
@ -1125,6 +1338,13 @@ static void __reipl_run(void *unused)
else
diag308(DIAG308_LOAD_NORMAL_DUMP, NULL);
break;
case IPL_TYPE_ECKD:
diag308(DIAG308_SET, reipl_block_eckd);
if (reipl_eckd_clear)
diag308(DIAG308_LOAD_CLEAR, NULL);
else
diag308(DIAG308_LOAD_NORMAL, NULL);
break;
case IPL_TYPE_FCP:
diag308(DIAG308_SET, reipl_block_fcp);
if (reipl_fcp_clear)
@ -1345,6 +1565,58 @@ out1:
return rc;
}
static int __init reipl_eckd_init(void)
{
int rc;
if (!sclp.has_sipl_eckd)
return 0;
reipl_block_eckd = (void *)get_zeroed_page(GFP_KERNEL);
if (!reipl_block_eckd)
return -ENOMEM;
/* sysfs: create kset for mixing attr group and bin attrs */
reipl_eckd_kset = kset_create_and_add(IPL_ECKD_STR, NULL,
&reipl_kset->kobj);
if (!reipl_eckd_kset) {
free_page((unsigned long)reipl_block_eckd);
return -ENOMEM;
}
rc = sysfs_create_group(&reipl_eckd_kset->kobj, &reipl_eckd_attr_group);
if (rc)
goto out1;
if (test_facility(141)) {
rc = sysfs_create_file(&reipl_eckd_kset->kobj,
&sys_reipl_eckd_clear_attr.attr);
if (rc)
goto out2;
} else {
reipl_eckd_clear = true;
}
if (ipl_info.type == IPL_TYPE_ECKD) {
memcpy(reipl_block_eckd, &ipl_block, sizeof(ipl_block));
} else {
reipl_block_eckd->hdr.len = IPL_BP_ECKD_LEN;
reipl_block_eckd->hdr.version = IPL_PARM_BLOCK_VERSION;
reipl_block_eckd->eckd.len = IPL_BP0_ECKD_LEN;
reipl_block_eckd->eckd.pbt = IPL_PBT_ECKD;
reipl_block_eckd->eckd.opt = IPL_PB0_ECKD_OPT_IPL;
}
reipl_capabilities |= IPL_TYPE_ECKD;
return 0;
out2:
sysfs_remove_group(&reipl_eckd_kset->kobj, &reipl_eckd_attr_group);
out1:
kset_unregister(reipl_eckd_kset);
free_page((unsigned long)reipl_block_eckd);
return rc;
}
static int __init reipl_type_init(void)
{
enum ipl_type reipl_type = ipl_info.type;
@ -1366,6 +1638,9 @@ static int __init reipl_type_init(void)
} else if (reipl_block->pb0_hdr.pbt == IPL_PBT_CCW) {
memcpy(reipl_block_ccw, reipl_block, size);
reipl_type = IPL_TYPE_CCW;
} else if (reipl_block->pb0_hdr.pbt == IPL_PBT_ECKD) {
memcpy(reipl_block_eckd, reipl_block, size);
reipl_type = IPL_TYPE_ECKD;
}
out:
return reipl_set_type(reipl_type);
@ -1384,6 +1659,9 @@ static int __init reipl_init(void)
return rc;
}
rc = reipl_ccw_init();
if (rc)
return rc;
rc = reipl_eckd_init();
if (rc)
return rc;
rc = reipl_fcp_init();
@ -2058,6 +2336,10 @@ void __init setup_ipl(void)
ipl_info.data.ccw.dev_id.ssid = ipl_block.ccw.ssid;
ipl_info.data.ccw.dev_id.devno = ipl_block.ccw.devno;
break;
case IPL_TYPE_ECKD:
ipl_info.data.eckd.dev_id.ssid = ipl_block.eckd.ssid;
ipl_info.data.eckd.dev_id.devno = ipl_block.eckd.devno;
break;
case IPL_TYPE_FCP:
case IPL_TYPE_FCP_DUMP:
ipl_info.data.fcp.dev_id.ssid = 0;

View File

@ -57,8 +57,10 @@ static void __init sclp_early_facilities_detect(void)
sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
sclp.has_iplcc = !!(sccb->byte_134 & 0x02);
}
if (sccb->cpuoff > 137)
if (sccb->cpuoff > 137) {
sclp.has_sipl = !!(sccb->cbl & 0x4000);
sclp.has_sipl_eckd = !!(sccb->cbl & 0x2000);
}
sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
sclp.rzm <<= 20;