mirror of
https://github.com/torvalds/linux.git
synced 2024-12-09 12:41:33 +00:00
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:
parent
5de2322d7b
commit
87fd22e0ae
@ -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_len = ipb->nvme.scp_data_len;
|
||||||
scp_data = ipb->nvme.scp_data;
|
scp_data = ipb->nvme.scp_data;
|
||||||
break;
|
break;
|
||||||
|
case IPL_PBT_ECKD:
|
||||||
|
scp_data_len = ipb->eckd.scp_data_len;
|
||||||
|
scp_data = ipb->eckd.scp_data;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -153,6 +158,7 @@ static void append_ipl_block_parm(void)
|
|||||||
break;
|
break;
|
||||||
case IPL_PBT_FCP:
|
case IPL_PBT_FCP:
|
||||||
case IPL_PBT_NVME:
|
case IPL_PBT_NVME:
|
||||||
|
case IPL_PBT_ECKD:
|
||||||
rc = ipl_block_get_ascii_scpdata(
|
rc = ipl_block_get_ascii_scpdata(
|
||||||
parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
|
parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
|
||||||
break;
|
break;
|
||||||
|
@ -22,6 +22,7 @@ struct ipl_parameter_block {
|
|||||||
struct ipl_pb0_common common;
|
struct ipl_pb0_common common;
|
||||||
struct ipl_pb0_fcp fcp;
|
struct ipl_pb0_fcp fcp;
|
||||||
struct ipl_pb0_ccw ccw;
|
struct ipl_pb0_ccw ccw;
|
||||||
|
struct ipl_pb0_eckd eckd;
|
||||||
struct ipl_pb0_nvme nvme;
|
struct ipl_pb0_nvme nvme;
|
||||||
char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
|
char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)];
|
||||||
};
|
};
|
||||||
@ -41,6 +42,10 @@ struct ipl_parameter_block {
|
|||||||
sizeof(struct ipl_pb0_ccw))
|
sizeof(struct ipl_pb0_ccw))
|
||||||
#define IPL_BP0_CCW_LEN (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_MAX_SUPPORTED_VERSION (0)
|
||||||
|
|
||||||
#define IPL_RB_CERT_UNKNOWN ((unsigned short)-1)
|
#define IPL_RB_CERT_UNKNOWN ((unsigned short)-1)
|
||||||
@ -68,6 +73,7 @@ enum ipl_type {
|
|||||||
IPL_TYPE_NSS = 16,
|
IPL_TYPE_NSS = 16,
|
||||||
IPL_TYPE_NVME = 32,
|
IPL_TYPE_NVME = 32,
|
||||||
IPL_TYPE_NVME_DUMP = 64,
|
IPL_TYPE_NVME_DUMP = 64,
|
||||||
|
IPL_TYPE_ECKD = 128,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ipl_info
|
struct ipl_info
|
||||||
@ -77,6 +83,9 @@ struct ipl_info
|
|||||||
struct {
|
struct {
|
||||||
struct ccw_dev_id dev_id;
|
struct ccw_dev_id dev_id;
|
||||||
} ccw;
|
} ccw;
|
||||||
|
struct {
|
||||||
|
struct ccw_dev_id dev_id;
|
||||||
|
} eckd;
|
||||||
struct {
|
struct {
|
||||||
struct ccw_dev_id dev_id;
|
struct ccw_dev_id dev_id;
|
||||||
u64 wwpn;
|
u64 wwpn;
|
||||||
|
@ -87,6 +87,7 @@ struct sclp_info {
|
|||||||
unsigned char has_gisaf : 1;
|
unsigned char has_gisaf : 1;
|
||||||
unsigned char has_diag318 : 1;
|
unsigned char has_diag318 : 1;
|
||||||
unsigned char has_sipl : 1;
|
unsigned char has_sipl : 1;
|
||||||
|
unsigned char has_sipl_eckd : 1;
|
||||||
unsigned char has_dirq : 1;
|
unsigned char has_dirq : 1;
|
||||||
unsigned char has_iplcc : 1;
|
unsigned char has_iplcc : 1;
|
||||||
unsigned char has_zpci_lsi : 1;
|
unsigned char has_zpci_lsi : 1;
|
||||||
|
@ -27,6 +27,7 @@ enum ipl_pbt {
|
|||||||
IPL_PBT_FCP = 0,
|
IPL_PBT_FCP = 0,
|
||||||
IPL_PBT_SCP_DATA = 1,
|
IPL_PBT_SCP_DATA = 1,
|
||||||
IPL_PBT_CCW = 2,
|
IPL_PBT_CCW = 2,
|
||||||
|
IPL_PBT_ECKD = 3,
|
||||||
IPL_PBT_NVME = 4,
|
IPL_PBT_NVME = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,6 +112,33 @@ struct ipl_pb0_ccw {
|
|||||||
__u8 reserved5[8];
|
__u8 reserved5[8];
|
||||||
} __packed;
|
} __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_NSS 0x80
|
||||||
#define IPL_PB0_CCW_VM_FLAG_VP 0x40
|
#define IPL_PB0_CCW_VM_FLAG_VP 0x40
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
|
|
||||||
#define IPL_UNKNOWN_STR "unknown"
|
#define IPL_UNKNOWN_STR "unknown"
|
||||||
#define IPL_CCW_STR "ccw"
|
#define IPL_CCW_STR "ccw"
|
||||||
|
#define IPL_ECKD_STR "eckd"
|
||||||
#define IPL_FCP_STR "fcp"
|
#define IPL_FCP_STR "fcp"
|
||||||
#define IPL_FCP_DUMP_STR "fcp_dump"
|
#define IPL_FCP_DUMP_STR "fcp_dump"
|
||||||
#define IPL_NVME_STR "nvme"
|
#define IPL_NVME_STR "nvme"
|
||||||
@ -93,6 +94,8 @@ static char *ipl_type_str(enum ipl_type type)
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case IPL_TYPE_CCW:
|
case IPL_TYPE_CCW:
|
||||||
return IPL_CCW_STR;
|
return IPL_CCW_STR;
|
||||||
|
case IPL_TYPE_ECKD:
|
||||||
|
return IPL_ECKD_STR;
|
||||||
case IPL_TYPE_FCP:
|
case IPL_TYPE_FCP:
|
||||||
return IPL_FCP_STR;
|
return IPL_FCP_STR;
|
||||||
case IPL_TYPE_FCP_DUMP:
|
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_fcp;
|
||||||
static struct ipl_parameter_block *reipl_block_nvme;
|
static struct ipl_parameter_block *reipl_block_nvme;
|
||||||
static struct ipl_parameter_block *reipl_block_ccw;
|
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_nss;
|
||||||
static struct ipl_parameter_block *reipl_block_actual;
|
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_nvme_clear;
|
||||||
static bool reipl_fcp_clear;
|
static bool reipl_fcp_clear;
|
||||||
static bool reipl_ccw_clear;
|
static bool reipl_ccw_clear;
|
||||||
|
static bool reipl_eckd_clear;
|
||||||
|
|
||||||
static inline int __diag308(unsigned long subcode, void *addr)
|
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;
|
return IPL_TYPE_NVME_DUMP;
|
||||||
else
|
else
|
||||||
return IPL_TYPE_NVME;
|
return IPL_TYPE_NVME;
|
||||||
|
case IPL_PBT_ECKD:
|
||||||
|
return IPL_TYPE_ECKD;
|
||||||
}
|
}
|
||||||
return IPL_TYPE_UNKNOWN;
|
return IPL_TYPE_UNKNOWN;
|
||||||
}
|
}
|
||||||
@ -335,6 +342,9 @@ static ssize_t sys_ipl_device_show(struct kobject *kobj,
|
|||||||
case IPL_TYPE_CCW:
|
case IPL_TYPE_CCW:
|
||||||
return sprintf(page, "0.%x.%04x\n", ipl_block.ccw.ssid,
|
return sprintf(page, "0.%x.%04x\n", ipl_block.ccw.ssid,
|
||||||
ipl_block.ccw.devno);
|
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:
|
||||||
case IPL_TYPE_FCP_DUMP:
|
case IPL_TYPE_FCP_DUMP:
|
||||||
return sprintf(page, "0.0.%04x\n", ipl_block.fcp.devno);
|
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);
|
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 =
|
static struct bin_attribute ipl_scp_data_attr =
|
||||||
__BIN_ATTR(scp_data, S_IRUGO, ipl_scp_data_read, NULL, PAGE_SIZE);
|
__BIN_ATTR(scp_data, S_IRUGO, ipl_scp_data_read, NULL, PAGE_SIZE);
|
||||||
|
|
||||||
static struct bin_attribute ipl_nvme_scp_data_attr =
|
static struct bin_attribute ipl_nvme_scp_data_attr =
|
||||||
__BIN_ATTR(scp_data, S_IRUGO, ipl_nvme_scp_data_read, NULL, PAGE_SIZE);
|
__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[] = {
|
static struct bin_attribute *ipl_fcp_bin_attrs[] = {
|
||||||
&ipl_parameter_attr,
|
&ipl_parameter_attr,
|
||||||
&ipl_scp_data_attr,
|
&ipl_scp_data_attr,
|
||||||
@ -398,6 +421,12 @@ static struct bin_attribute *ipl_nvme_bin_attrs[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct bin_attribute *ipl_eckd_bin_attrs[] = {
|
||||||
|
&ipl_parameter_attr,
|
||||||
|
&ipl_eckd_scp_data_attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
/* FCP ipl device attributes */
|
/* FCP ipl device attributes */
|
||||||
|
|
||||||
DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n",
|
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",
|
DEFINE_IPL_ATTR_RO(ipl_nvme, br_lba, "%lld\n",
|
||||||
(unsigned long long)ipl_block.nvme.br_lba);
|
(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,
|
static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj,
|
||||||
struct kobj_attribute *attr, char *page)
|
struct kobj_attribute *attr, char *page)
|
||||||
{
|
{
|
||||||
@ -470,6 +581,20 @@ static struct attribute_group ipl_nvme_attr_group = {
|
|||||||
.bin_attrs = ipl_nvme_bin_attrs,
|
.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 */
|
/* CCW ipl device attributes */
|
||||||
|
|
||||||
@ -542,6 +667,9 @@ static int __init ipl_init(void)
|
|||||||
rc = sysfs_create_group(&ipl_kset->kobj,
|
rc = sysfs_create_group(&ipl_kset->kobj,
|
||||||
&ipl_ccw_attr_group_lpar);
|
&ipl_ccw_attr_group_lpar);
|
||||||
break;
|
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:
|
||||||
case IPL_TYPE_FCP_DUMP:
|
case IPL_TYPE_FCP_DUMP:
|
||||||
rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
|
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,
|
.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 */
|
/* NSS reipl device attributes */
|
||||||
static void reipl_get_ascii_nss_name(char *dst,
|
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:
|
case IPL_TYPE_CCW:
|
||||||
reipl_block_actual = reipl_block_ccw;
|
reipl_block_actual = reipl_block_ccw;
|
||||||
break;
|
break;
|
||||||
|
case IPL_TYPE_ECKD:
|
||||||
|
reipl_block_actual = reipl_block_eckd;
|
||||||
|
break;
|
||||||
case IPL_TYPE_FCP:
|
case IPL_TYPE_FCP:
|
||||||
reipl_block_actual = reipl_block_fcp;
|
reipl_block_actual = reipl_block_fcp;
|
||||||
break;
|
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)
|
if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
|
||||||
rc = reipl_set_type(IPL_TYPE_CCW);
|
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)
|
else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
|
||||||
rc = reipl_set_type(IPL_TYPE_FCP);
|
rc = reipl_set_type(IPL_TYPE_FCP);
|
||||||
else if (strncmp(buf, IPL_NVME_STR, strlen(IPL_NVME_STR)) == 0)
|
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_kset;
|
||||||
static struct kset *reipl_fcp_kset;
|
static struct kset *reipl_fcp_kset;
|
||||||
static struct kset *reipl_nvme_kset;
|
static struct kset *reipl_nvme_kset;
|
||||||
|
static struct kset *reipl_eckd_kset;
|
||||||
|
|
||||||
static void __reipl_run(void *unused)
|
static void __reipl_run(void *unused)
|
||||||
{
|
{
|
||||||
@ -1125,6 +1338,13 @@ static void __reipl_run(void *unused)
|
|||||||
else
|
else
|
||||||
diag308(DIAG308_LOAD_NORMAL_DUMP, NULL);
|
diag308(DIAG308_LOAD_NORMAL_DUMP, NULL);
|
||||||
break;
|
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:
|
case IPL_TYPE_FCP:
|
||||||
diag308(DIAG308_SET, reipl_block_fcp);
|
diag308(DIAG308_SET, reipl_block_fcp);
|
||||||
if (reipl_fcp_clear)
|
if (reipl_fcp_clear)
|
||||||
@ -1345,6 +1565,58 @@ out1:
|
|||||||
return rc;
|
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)
|
static int __init reipl_type_init(void)
|
||||||
{
|
{
|
||||||
enum ipl_type reipl_type = ipl_info.type;
|
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) {
|
} else if (reipl_block->pb0_hdr.pbt == IPL_PBT_CCW) {
|
||||||
memcpy(reipl_block_ccw, reipl_block, size);
|
memcpy(reipl_block_ccw, reipl_block, size);
|
||||||
reipl_type = IPL_TYPE_CCW;
|
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:
|
out:
|
||||||
return reipl_set_type(reipl_type);
|
return reipl_set_type(reipl_type);
|
||||||
@ -1384,6 +1659,9 @@ static int __init reipl_init(void)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
rc = reipl_ccw_init();
|
rc = reipl_ccw_init();
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
rc = reipl_eckd_init();
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
rc = reipl_fcp_init();
|
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.ssid = ipl_block.ccw.ssid;
|
||||||
ipl_info.data.ccw.dev_id.devno = ipl_block.ccw.devno;
|
ipl_info.data.ccw.dev_id.devno = ipl_block.ccw.devno;
|
||||||
break;
|
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:
|
||||||
case IPL_TYPE_FCP_DUMP:
|
case IPL_TYPE_FCP_DUMP:
|
||||||
ipl_info.data.fcp.dev_id.ssid = 0;
|
ipl_info.data.fcp.dev_id.ssid = 0;
|
||||||
|
@ -57,8 +57,10 @@ static void __init sclp_early_facilities_detect(void)
|
|||||||
sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
|
sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
|
||||||
sclp.has_iplcc = !!(sccb->byte_134 & 0x02);
|
sclp.has_iplcc = !!(sccb->byte_134 & 0x02);
|
||||||
}
|
}
|
||||||
if (sccb->cpuoff > 137)
|
if (sccb->cpuoff > 137) {
|
||||||
sclp.has_sipl = !!(sccb->cbl & 0x4000);
|
sclp.has_sipl = !!(sccb->cbl & 0x4000);
|
||||||
|
sclp.has_sipl_eckd = !!(sccb->cbl & 0x2000);
|
||||||
|
}
|
||||||
sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
|
sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
|
||||||
sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
|
sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
|
||||||
sclp.rzm <<= 20;
|
sclp.rzm <<= 20;
|
||||||
|
Loading…
Reference in New Issue
Block a user