|
@@ -2,7 +2,7 @@
|
|
|
* arch/s390/kernel/ipl.c
|
|
|
* ipl/reipl/dump support for Linux on s390.
|
|
|
*
|
|
|
- * Copyright (C) IBM Corp. 2005,2006
|
|
|
+ * Copyright IBM Corp. 2005,2007
|
|
|
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
|
|
|
* Heiko Carstens <heiko.carstens@de.ibm.com>
|
|
|
* Volker Sameske <sameske@de.ibm.com>
|
|
@@ -31,6 +31,43 @@
|
|
|
#define IPL_FCP_DUMP_STR "fcp_dump"
|
|
|
#define IPL_NSS_STR "nss"
|
|
|
|
|
|
+#define DUMP_CCW_STR "ccw"
|
|
|
+#define DUMP_FCP_STR "fcp"
|
|
|
+#define DUMP_NONE_STR "none"
|
|
|
+
|
|
|
+/*
|
|
|
+ * Four shutdown trigger types are supported:
|
|
|
+ * - panic
|
|
|
+ * - halt
|
|
|
+ * - power off
|
|
|
+ * - reipl
|
|
|
+ */
|
|
|
+#define ON_PANIC_STR "on_panic"
|
|
|
+#define ON_HALT_STR "on_halt"
|
|
|
+#define ON_POFF_STR "on_poff"
|
|
|
+#define ON_REIPL_STR "on_reboot"
|
|
|
+
|
|
|
+struct shutdown_action;
|
|
|
+struct shutdown_trigger {
|
|
|
+ char *name;
|
|
|
+ struct shutdown_action *action;
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Five shutdown action types are supported:
|
|
|
+ */
|
|
|
+#define SHUTDOWN_ACTION_IPL_STR "ipl"
|
|
|
+#define SHUTDOWN_ACTION_REIPL_STR "reipl"
|
|
|
+#define SHUTDOWN_ACTION_DUMP_STR "dump"
|
|
|
+#define SHUTDOWN_ACTION_VMCMD_STR "vmcmd"
|
|
|
+#define SHUTDOWN_ACTION_STOP_STR "stop"
|
|
|
+
|
|
|
+struct shutdown_action {
|
|
|
+ char *name;
|
|
|
+ void (*fn) (struct shutdown_trigger *trigger);
|
|
|
+ int (*init) (void);
|
|
|
+};
|
|
|
+
|
|
|
static char *ipl_type_str(enum ipl_type type)
|
|
|
{
|
|
|
switch (type) {
|
|
@@ -54,10 +91,6 @@ enum dump_type {
|
|
|
DUMP_TYPE_FCP = 4,
|
|
|
};
|
|
|
|
|
|
-#define DUMP_NONE_STR "none"
|
|
|
-#define DUMP_CCW_STR "ccw"
|
|
|
-#define DUMP_FCP_STR "fcp"
|
|
|
-
|
|
|
static char *dump_type_str(enum dump_type type)
|
|
|
{
|
|
|
switch (type) {
|
|
@@ -99,30 +132,6 @@ enum dump_method {
|
|
|
DUMP_METHOD_FCP_DIAG,
|
|
|
};
|
|
|
|
|
|
-enum shutdown_action {
|
|
|
- SHUTDOWN_REIPL,
|
|
|
- SHUTDOWN_DUMP,
|
|
|
- SHUTDOWN_STOP,
|
|
|
-};
|
|
|
-
|
|
|
-#define SHUTDOWN_REIPL_STR "reipl"
|
|
|
-#define SHUTDOWN_DUMP_STR "dump"
|
|
|
-#define SHUTDOWN_STOP_STR "stop"
|
|
|
-
|
|
|
-static char *shutdown_action_str(enum shutdown_action action)
|
|
|
-{
|
|
|
- switch (action) {
|
|
|
- case SHUTDOWN_REIPL:
|
|
|
- return SHUTDOWN_REIPL_STR;
|
|
|
- case SHUTDOWN_DUMP:
|
|
|
- return SHUTDOWN_DUMP_STR;
|
|
|
- case SHUTDOWN_STOP:
|
|
|
- return SHUTDOWN_STOP_STR;
|
|
|
- default:
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static int diag308_set_works = 0;
|
|
|
|
|
|
static int reipl_capabilities = IPL_TYPE_UNKNOWN;
|
|
@@ -140,8 +149,6 @@ static enum dump_method dump_method = DUMP_METHOD_NONE;
|
|
|
static struct ipl_parameter_block *dump_block_fcp;
|
|
|
static struct ipl_parameter_block *dump_block_ccw;
|
|
|
|
|
|
-static enum shutdown_action on_panic_action = SHUTDOWN_STOP;
|
|
|
-
|
|
|
static struct sclp_ipl_info sclp_ipl_info;
|
|
|
|
|
|
int diag308(unsigned long subcode, void *addr)
|
|
@@ -205,8 +212,8 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
|
|
|
struct kobj_attribute *attr, \
|
|
|
const char *buf, size_t len) \
|
|
|
{ \
|
|
|
- if (sscanf(buf, _fmt_in, _value) != 1) \
|
|
|
- return -EINVAL; \
|
|
|
+ strncpy(_value, buf, sizeof(_value) - 1); \
|
|
|
+ strstrip(_value); \
|
|
|
return len; \
|
|
|
} \
|
|
|
static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
|
|
@@ -245,33 +252,6 @@ static __init enum ipl_type get_ipl_type(void)
|
|
|
return IPL_TYPE_FCP;
|
|
|
}
|
|
|
|
|
|
-void __init setup_ipl_info(void)
|
|
|
-{
|
|
|
- ipl_info.type = get_ipl_type();
|
|
|
- switch (ipl_info.type) {
|
|
|
- case IPL_TYPE_CCW:
|
|
|
- ipl_info.data.ccw.dev_id.devno = ipl_devno;
|
|
|
- ipl_info.data.ccw.dev_id.ssid = 0;
|
|
|
- break;
|
|
|
- case IPL_TYPE_FCP:
|
|
|
- case IPL_TYPE_FCP_DUMP:
|
|
|
- ipl_info.data.fcp.dev_id.devno =
|
|
|
- IPL_PARMBLOCK_START->ipl_info.fcp.devno;
|
|
|
- ipl_info.data.fcp.dev_id.ssid = 0;
|
|
|
- ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
|
|
|
- ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
|
|
|
- break;
|
|
|
- case IPL_TYPE_NSS:
|
|
|
- strncpy(ipl_info.data.nss.name, kernel_nss_name,
|
|
|
- sizeof(ipl_info.data.nss.name));
|
|
|
- break;
|
|
|
- case IPL_TYPE_UNKNOWN:
|
|
|
- default:
|
|
|
- /* We have no info to copy */
|
|
|
- break;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
struct ipl_info ipl_info;
|
|
|
EXPORT_SYMBOL_GPL(ipl_info);
|
|
|
|
|
@@ -428,8 +408,74 @@ static struct attribute_group ipl_unknown_attr_group = {
|
|
|
|
|
|
static struct kset *ipl_kset;
|
|
|
|
|
|
+static int __init ipl_register_fcp_files(void)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+ rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
|
|
|
+ if (rc)
|
|
|
+ goto out_ipl_parm;
|
|
|
+ rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_scp_data_attr);
|
|
|
+ if (!rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
|
|
|
+
|
|
|
+out_ipl_parm:
|
|
|
+ sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
|
|
|
+out:
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static void ipl_run(struct shutdown_trigger *trigger)
|
|
|
+{
|
|
|
+ diag308(DIAG308_IPL, NULL);
|
|
|
+ if (MACHINE_IS_VM)
|
|
|
+ __cpcmd("IPL", NULL, 0, NULL);
|
|
|
+ else if (ipl_info.type == IPL_TYPE_CCW)
|
|
|
+ reipl_ccw_dev(&ipl_info.data.ccw.dev_id);
|
|
|
+}
|
|
|
+
|
|
|
+static int ipl_init(void)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj);
|
|
|
+ if (!ipl_kset) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ switch (ipl_info.type) {
|
|
|
+ case IPL_TYPE_CCW:
|
|
|
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group);
|
|
|
+ break;
|
|
|
+ case IPL_TYPE_FCP:
|
|
|
+ case IPL_TYPE_FCP_DUMP:
|
|
|
+ rc = ipl_register_fcp_files();
|
|
|
+ break;
|
|
|
+ case IPL_TYPE_NSS:
|
|
|
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_nss_attr_group);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ rc = sysfs_create_group(&ipl_kset->kobj,
|
|
|
+ &ipl_unknown_attr_group);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+out:
|
|
|
+ if (rc)
|
|
|
+ panic("ipl_init failed: rc = %i\n", rc);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct shutdown_action ipl_action = {SHUTDOWN_ACTION_IPL_STR, ipl_run,
|
|
|
+ ipl_init};
|
|
|
+
|
|
|
/*
|
|
|
- * reipl section
|
|
|
+ * reipl shutdown action: Reboot Linux on shutdown.
|
|
|
*/
|
|
|
|
|
|
/* FCP reipl device attributes */
|
|
@@ -600,143 +646,11 @@ static ssize_t reipl_type_store(struct kobject *kobj,
|
|
|
}
|
|
|
|
|
|
static struct kobj_attribute reipl_type_attr =
|
|
|
- __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
|
|
|
+ __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
|
|
|
|
|
|
static struct kset *reipl_kset;
|
|
|
|
|
|
-/*
|
|
|
- * dump section
|
|
|
- */
|
|
|
-
|
|
|
-/* FCP dump device attributes */
|
|
|
-
|
|
|
-DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
|
|
|
- dump_block_fcp->ipl_info.fcp.wwpn);
|
|
|
-DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
|
|
|
- dump_block_fcp->ipl_info.fcp.lun);
|
|
|
-DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
|
|
- dump_block_fcp->ipl_info.fcp.bootprog);
|
|
|
-DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
|
|
|
- dump_block_fcp->ipl_info.fcp.br_lba);
|
|
|
-DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
|
|
- dump_block_fcp->ipl_info.fcp.devno);
|
|
|
-
|
|
|
-static struct attribute *dump_fcp_attrs[] = {
|
|
|
- &sys_dump_fcp_device_attr.attr,
|
|
|
- &sys_dump_fcp_wwpn_attr.attr,
|
|
|
- &sys_dump_fcp_lun_attr.attr,
|
|
|
- &sys_dump_fcp_bootprog_attr.attr,
|
|
|
- &sys_dump_fcp_br_lba_attr.attr,
|
|
|
- NULL,
|
|
|
-};
|
|
|
-
|
|
|
-static struct attribute_group dump_fcp_attr_group = {
|
|
|
- .name = IPL_FCP_STR,
|
|
|
- .attrs = dump_fcp_attrs,
|
|
|
-};
|
|
|
-
|
|
|
-/* CCW dump device attributes */
|
|
|
-
|
|
|
-DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
|
|
|
- dump_block_ccw->ipl_info.ccw.devno);
|
|
|
-
|
|
|
-static struct attribute *dump_ccw_attrs[] = {
|
|
|
- &sys_dump_ccw_device_attr.attr,
|
|
|
- NULL,
|
|
|
-};
|
|
|
-
|
|
|
-static struct attribute_group dump_ccw_attr_group = {
|
|
|
- .name = IPL_CCW_STR,
|
|
|
- .attrs = dump_ccw_attrs,
|
|
|
-};
|
|
|
-
|
|
|
-/* dump type */
|
|
|
-
|
|
|
-static int dump_set_type(enum dump_type type)
|
|
|
-{
|
|
|
- if (!(dump_capabilities & type))
|
|
|
- return -EINVAL;
|
|
|
- switch(type) {
|
|
|
- case DUMP_TYPE_CCW:
|
|
|
- if (MACHINE_IS_VM)
|
|
|
- dump_method = DUMP_METHOD_CCW_VM;
|
|
|
- else if (diag308_set_works)
|
|
|
- dump_method = DUMP_METHOD_CCW_DIAG;
|
|
|
- else
|
|
|
- dump_method = DUMP_METHOD_CCW_CIO;
|
|
|
- break;
|
|
|
- case DUMP_TYPE_FCP:
|
|
|
- dump_method = DUMP_METHOD_FCP_DIAG;
|
|
|
- break;
|
|
|
- default:
|
|
|
- dump_method = DUMP_METHOD_NONE;
|
|
|
- }
|
|
|
- dump_type = type;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t dump_type_show(struct kobject *kobj,
|
|
|
- struct kobj_attribute *attr, char *page)
|
|
|
-{
|
|
|
- return sprintf(page, "%s\n", dump_type_str(dump_type));
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t dump_type_store(struct kobject *kobj,
|
|
|
- struct kobj_attribute *attr,
|
|
|
- const char *buf, size_t len)
|
|
|
-{
|
|
|
- int rc = -EINVAL;
|
|
|
-
|
|
|
- if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
|
|
|
- rc = dump_set_type(DUMP_TYPE_NONE);
|
|
|
- else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
|
|
|
- rc = dump_set_type(DUMP_TYPE_CCW);
|
|
|
- else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
|
|
|
- rc = dump_set_type(DUMP_TYPE_FCP);
|
|
|
- return (rc != 0) ? rc : len;
|
|
|
-}
|
|
|
-
|
|
|
-static struct kobj_attribute dump_type_attr =
|
|
|
- __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
|
|
|
-
|
|
|
-static struct kset *dump_kset;
|
|
|
-
|
|
|
-/*
|
|
|
- * Shutdown actions section
|
|
|
- */
|
|
|
-
|
|
|
-static struct kset *shutdown_actions_kset;
|
|
|
-
|
|
|
-/* on panic */
|
|
|
-
|
|
|
-static ssize_t on_panic_show(struct kobject *kobj,
|
|
|
- struct kobj_attribute *attr, char *page)
|
|
|
-{
|
|
|
- return sprintf(page, "%s\n", shutdown_action_str(on_panic_action));
|
|
|
-}
|
|
|
-
|
|
|
-static ssize_t on_panic_store(struct kobject *kobj,
|
|
|
- struct kobj_attribute *attr,
|
|
|
- const char *buf, size_t len)
|
|
|
-{
|
|
|
- if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0)
|
|
|
- on_panic_action = SHUTDOWN_REIPL;
|
|
|
- else if (strncmp(buf, SHUTDOWN_DUMP_STR,
|
|
|
- strlen(SHUTDOWN_DUMP_STR)) == 0)
|
|
|
- on_panic_action = SHUTDOWN_DUMP;
|
|
|
- else if (strncmp(buf, SHUTDOWN_STOP_STR,
|
|
|
- strlen(SHUTDOWN_STOP_STR)) == 0)
|
|
|
- on_panic_action = SHUTDOWN_STOP;
|
|
|
- else
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- return len;
|
|
|
-}
|
|
|
-
|
|
|
-static struct kobj_attribute on_panic_attr =
|
|
|
- __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
|
|
|
-
|
|
|
-void do_reipl(void)
|
|
|
+void reipl_run(struct shutdown_trigger *trigger)
|
|
|
{
|
|
|
struct ccw_dev_id devid;
|
|
|
static char buf[100];
|
|
@@ -787,119 +701,27 @@ void do_reipl(void)
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
|
|
|
}
|
|
|
|
|
|
-static void do_dump(void)
|
|
|
+static void __init reipl_probe(void)
|
|
|
{
|
|
|
- struct ccw_dev_id devid;
|
|
|
- static char buf[100];
|
|
|
+ void *buffer;
|
|
|
|
|
|
- switch (dump_method) {
|
|
|
- case DUMP_METHOD_CCW_CIO:
|
|
|
- smp_send_stop();
|
|
|
- devid.devno = dump_block_ccw->ipl_info.ccw.devno;
|
|
|
- devid.ssid = 0;
|
|
|
- reipl_ccw_dev(&devid);
|
|
|
- break;
|
|
|
- case DUMP_METHOD_CCW_VM:
|
|
|
- smp_send_stop();
|
|
|
- sprintf(buf, "STORE STATUS");
|
|
|
- __cpcmd(buf, NULL, 0, NULL);
|
|
|
- sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
|
|
|
- __cpcmd(buf, NULL, 0, NULL);
|
|
|
- break;
|
|
|
- case DUMP_METHOD_CCW_DIAG:
|
|
|
- diag308(DIAG308_SET, dump_block_ccw);
|
|
|
- diag308(DIAG308_DUMP, NULL);
|
|
|
- break;
|
|
|
- case DUMP_METHOD_FCP_DIAG:
|
|
|
- diag308(DIAG308_SET, dump_block_fcp);
|
|
|
- diag308(DIAG308_DUMP, NULL);
|
|
|
- break;
|
|
|
- case DUMP_METHOD_NONE:
|
|
|
- default:
|
|
|
+ buffer = (void *) get_zeroed_page(GFP_KERNEL);
|
|
|
+ if (!buffer)
|
|
|
return;
|
|
|
- }
|
|
|
- printk(KERN_EMERG "Dump failed!\n");
|
|
|
+ if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK)
|
|
|
+ diag308_set_works = 1;
|
|
|
+ free_page((unsigned long)buffer);
|
|
|
}
|
|
|
|
|
|
-/* init functions */
|
|
|
-
|
|
|
-static int __init ipl_register_fcp_files(void)
|
|
|
+static int __init reipl_nss_init(void)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
|
- rc = sysfs_create_group(&ipl_kset->kobj,
|
|
|
- &ipl_fcp_attr_group);
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
- rc = sysfs_create_bin_file(&ipl_kset->kobj,
|
|
|
- &ipl_parameter_attr);
|
|
|
- if (rc)
|
|
|
- goto out_ipl_parm;
|
|
|
- rc = sysfs_create_bin_file(&ipl_kset->kobj,
|
|
|
- &ipl_scp_data_attr);
|
|
|
- if (!rc)
|
|
|
- goto out;
|
|
|
-
|
|
|
- sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
|
|
|
-
|
|
|
-out_ipl_parm:
|
|
|
- sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
|
|
|
-out:
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-static int __init ipl_init(void)
|
|
|
-{
|
|
|
- int rc;
|
|
|
-
|
|
|
- ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj);
|
|
|
- if (!ipl_kset)
|
|
|
- return -ENOMEM;
|
|
|
- switch (ipl_info.type) {
|
|
|
- case IPL_TYPE_CCW:
|
|
|
- rc = sysfs_create_group(&ipl_kset->kobj,
|
|
|
- &ipl_ccw_attr_group);
|
|
|
- break;
|
|
|
- case IPL_TYPE_FCP:
|
|
|
- case IPL_TYPE_FCP_DUMP:
|
|
|
- rc = ipl_register_fcp_files();
|
|
|
- break;
|
|
|
- case IPL_TYPE_NSS:
|
|
|
- rc = sysfs_create_group(&ipl_kset->kobj,
|
|
|
- &ipl_nss_attr_group);
|
|
|
- break;
|
|
|
- default:
|
|
|
- rc = sysfs_create_group(&ipl_kset->kobj,
|
|
|
- &ipl_unknown_attr_group);
|
|
|
- break;
|
|
|
- }
|
|
|
- if (rc)
|
|
|
- kset_unregister(ipl_kset);
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-static void __init reipl_probe(void)
|
|
|
-{
|
|
|
- void *buffer;
|
|
|
-
|
|
|
- buffer = (void *) get_zeroed_page(GFP_KERNEL);
|
|
|
- if (!buffer)
|
|
|
- return;
|
|
|
- if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK)
|
|
|
- diag308_set_works = 1;
|
|
|
- free_page((unsigned long)buffer);
|
|
|
-}
|
|
|
-
|
|
|
-static int __init reipl_nss_init(void)
|
|
|
-{
|
|
|
- int rc;
|
|
|
-
|
|
|
- if (!MACHINE_IS_VM)
|
|
|
- return 0;
|
|
|
- rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group);
|
|
|
+ if (!MACHINE_IS_VM)
|
|
|
+ return 0;
|
|
|
+ rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1);
|
|
@@ -970,7 +792,7 @@ static int __init reipl_fcp_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int __init reipl_init(void)
|
|
|
+static int reipl_init(void)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
@@ -997,6 +819,138 @@ static int __init reipl_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static struct shutdown_action reipl_action = {SHUTDOWN_ACTION_REIPL_STR,
|
|
|
+ reipl_run, reipl_init};
|
|
|
+
|
|
|
+/*
|
|
|
+ * dump shutdown action: Dump Linux on shutdown.
|
|
|
+ */
|
|
|
+
|
|
|
+/* FCP dump device attributes */
|
|
|
+
|
|
|
+DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
|
|
|
+ dump_block_fcp->ipl_info.fcp.wwpn);
|
|
|
+DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
|
|
|
+ dump_block_fcp->ipl_info.fcp.lun);
|
|
|
+DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
|
|
+ dump_block_fcp->ipl_info.fcp.bootprog);
|
|
|
+DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
|
|
|
+ dump_block_fcp->ipl_info.fcp.br_lba);
|
|
|
+DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
|
|
+ dump_block_fcp->ipl_info.fcp.devno);
|
|
|
+
|
|
|
+static struct attribute *dump_fcp_attrs[] = {
|
|
|
+ &sys_dump_fcp_device_attr.attr,
|
|
|
+ &sys_dump_fcp_wwpn_attr.attr,
|
|
|
+ &sys_dump_fcp_lun_attr.attr,
|
|
|
+ &sys_dump_fcp_bootprog_attr.attr,
|
|
|
+ &sys_dump_fcp_br_lba_attr.attr,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+static struct attribute_group dump_fcp_attr_group = {
|
|
|
+ .name = IPL_FCP_STR,
|
|
|
+ .attrs = dump_fcp_attrs,
|
|
|
+};
|
|
|
+
|
|
|
+/* CCW dump device attributes */
|
|
|
+
|
|
|
+DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
|
|
|
+ dump_block_ccw->ipl_info.ccw.devno);
|
|
|
+
|
|
|
+static struct attribute *dump_ccw_attrs[] = {
|
|
|
+ &sys_dump_ccw_device_attr.attr,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+static struct attribute_group dump_ccw_attr_group = {
|
|
|
+ .name = IPL_CCW_STR,
|
|
|
+ .attrs = dump_ccw_attrs,
|
|
|
+};
|
|
|
+
|
|
|
+/* dump type */
|
|
|
+
|
|
|
+static int dump_set_type(enum dump_type type)
|
|
|
+{
|
|
|
+ if (!(dump_capabilities & type))
|
|
|
+ return -EINVAL;
|
|
|
+ switch (type) {
|
|
|
+ case DUMP_TYPE_CCW:
|
|
|
+ if (MACHINE_IS_VM)
|
|
|
+ dump_method = DUMP_METHOD_CCW_VM;
|
|
|
+ else
|
|
|
+ dump_method = DUMP_METHOD_CCW_CIO;
|
|
|
+ break;
|
|
|
+ case DUMP_TYPE_FCP:
|
|
|
+ dump_method = DUMP_METHOD_FCP_DIAG;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ dump_method = DUMP_METHOD_NONE;
|
|
|
+ }
|
|
|
+ dump_type = type;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dump_type_show(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr, char *page)
|
|
|
+{
|
|
|
+ return sprintf(page, "%s\n", dump_type_str(dump_type));
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dump_type_store(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr,
|
|
|
+ const char *buf, size_t len)
|
|
|
+{
|
|
|
+ int rc = -EINVAL;
|
|
|
+
|
|
|
+ if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
|
|
|
+ rc = dump_set_type(DUMP_TYPE_NONE);
|
|
|
+ else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
|
|
|
+ rc = dump_set_type(DUMP_TYPE_CCW);
|
|
|
+ else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
|
|
|
+ rc = dump_set_type(DUMP_TYPE_FCP);
|
|
|
+ return (rc != 0) ? rc : len;
|
|
|
+}
|
|
|
+
|
|
|
+static struct kobj_attribute dump_type_attr =
|
|
|
+ __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
|
|
|
+
|
|
|
+static struct kset *dump_kset;
|
|
|
+
|
|
|
+static void dump_run(struct shutdown_trigger *trigger)
|
|
|
+{
|
|
|
+ struct ccw_dev_id devid;
|
|
|
+ static char buf[100];
|
|
|
+
|
|
|
+ switch (dump_method) {
|
|
|
+ case DUMP_METHOD_CCW_CIO:
|
|
|
+ smp_send_stop();
|
|
|
+ devid.devno = dump_block_ccw->ipl_info.ccw.devno;
|
|
|
+ devid.ssid = 0;
|
|
|
+ reipl_ccw_dev(&devid);
|
|
|
+ break;
|
|
|
+ case DUMP_METHOD_CCW_VM:
|
|
|
+ smp_send_stop();
|
|
|
+ sprintf(buf, "STORE STATUS");
|
|
|
+ __cpcmd(buf, NULL, 0, NULL);
|
|
|
+ sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
|
|
|
+ __cpcmd(buf, NULL, 0, NULL);
|
|
|
+ break;
|
|
|
+ case DUMP_METHOD_CCW_DIAG:
|
|
|
+ diag308(DIAG308_SET, dump_block_ccw);
|
|
|
+ diag308(DIAG308_DUMP, NULL);
|
|
|
+ break;
|
|
|
+ case DUMP_METHOD_FCP_DIAG:
|
|
|
+ diag308(DIAG308_SET, dump_block_fcp);
|
|
|
+ diag308(DIAG308_DUMP, NULL);
|
|
|
+ break;
|
|
|
+ case DUMP_METHOD_NONE:
|
|
|
+ default:
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ printk(KERN_EMERG "Dump failed!\n");
|
|
|
+}
|
|
|
+
|
|
|
static int __init dump_ccw_init(void)
|
|
|
{
|
|
|
int rc;
|
|
@@ -1042,31 +996,14 @@ static int __init dump_fcp_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-#define SHUTDOWN_ON_PANIC_PRIO 0
|
|
|
-
|
|
|
-static int shutdown_on_panic_notify(struct notifier_block *self,
|
|
|
- unsigned long event, void *data)
|
|
|
-{
|
|
|
- if (on_panic_action == SHUTDOWN_DUMP)
|
|
|
- do_dump();
|
|
|
- else if (on_panic_action == SHUTDOWN_REIPL)
|
|
|
- do_reipl();
|
|
|
- return NOTIFY_OK;
|
|
|
-}
|
|
|
-
|
|
|
-static struct notifier_block shutdown_on_panic_nb = {
|
|
|
- .notifier_call = shutdown_on_panic_notify,
|
|
|
- .priority = SHUTDOWN_ON_PANIC_PRIO
|
|
|
-};
|
|
|
-
|
|
|
-static int __init dump_init(void)
|
|
|
+static int dump_init(void)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
|
dump_kset = kset_create_and_add("dump", NULL, firmware_kobj);
|
|
|
if (!dump_kset)
|
|
|
return -ENOMEM;
|
|
|
- rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr);
|
|
|
+ rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr);
|
|
|
if (rc) {
|
|
|
kset_unregister(dump_kset);
|
|
|
return rc;
|
|
@@ -1081,47 +1018,376 @@ static int __init dump_init(void)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int __init shutdown_actions_init(void)
|
|
|
+static struct shutdown_action dump_action = {SHUTDOWN_ACTION_DUMP_STR,
|
|
|
+ dump_run, dump_init};
|
|
|
+
|
|
|
+/*
|
|
|
+ * vmcmd shutdown action: Trigger vm command on shutdown.
|
|
|
+ */
|
|
|
+
|
|
|
+static char vmcmd_on_reboot[128];
|
|
|
+static char vmcmd_on_panic[128];
|
|
|
+static char vmcmd_on_halt[128];
|
|
|
+static char vmcmd_on_poff[128];
|
|
|
+
|
|
|
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot);
|
|
|
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic);
|
|
|
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt);
|
|
|
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff);
|
|
|
+
|
|
|
+static struct attribute *vmcmd_attrs[] = {
|
|
|
+ &sys_vmcmd_on_reboot_attr.attr,
|
|
|
+ &sys_vmcmd_on_panic_attr.attr,
|
|
|
+ &sys_vmcmd_on_halt_attr.attr,
|
|
|
+ &sys_vmcmd_on_poff_attr.attr,
|
|
|
+ NULL,
|
|
|
+};
|
|
|
+
|
|
|
+static struct attribute_group vmcmd_attr_group = {
|
|
|
+ .attrs = vmcmd_attrs,
|
|
|
+};
|
|
|
+
|
|
|
+static struct kset *vmcmd_kset;
|
|
|
+
|
|
|
+static void vmcmd_run(struct shutdown_trigger *trigger)
|
|
|
{
|
|
|
- int rc;
|
|
|
+ char *cmd, *next_cmd;
|
|
|
+
|
|
|
+ if (strcmp(trigger->name, ON_REIPL_STR) == 0)
|
|
|
+ cmd = vmcmd_on_reboot;
|
|
|
+ else if (strcmp(trigger->name, ON_PANIC_STR) == 0)
|
|
|
+ cmd = vmcmd_on_panic;
|
|
|
+ else if (strcmp(trigger->name, ON_HALT_STR) == 0)
|
|
|
+ cmd = vmcmd_on_halt;
|
|
|
+ else if (strcmp(trigger->name, ON_POFF_STR) == 0)
|
|
|
+ cmd = vmcmd_on_poff;
|
|
|
+ else
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (strlen(cmd) == 0)
|
|
|
+ return;
|
|
|
+ do {
|
|
|
+ next_cmd = strchr(cmd, '\n');
|
|
|
+ if (next_cmd) {
|
|
|
+ next_cmd[0] = 0;
|
|
|
+ next_cmd += 1;
|
|
|
+ }
|
|
|
+ __cpcmd(cmd, NULL, 0, NULL);
|
|
|
+ cmd = next_cmd;
|
|
|
+ } while (cmd != NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static int vmcmd_init(void)
|
|
|
+{
|
|
|
+ if (!MACHINE_IS_VM)
|
|
|
+ return -ENOTSUPP;
|
|
|
+ vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj);
|
|
|
+ if (!vmcmd_kset)
|
|
|
+ return -ENOMEM;
|
|
|
+ return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group);
|
|
|
+}
|
|
|
+
|
|
|
+static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR,
|
|
|
+ vmcmd_run, vmcmd_init};
|
|
|
+
|
|
|
+/*
|
|
|
+ * stop shutdown action: Stop Linux on shutdown.
|
|
|
+ */
|
|
|
+
|
|
|
+static void stop_run(struct shutdown_trigger *trigger)
|
|
|
+{
|
|
|
+ signal_processor(smp_processor_id(), sigp_stop_and_store_status);
|
|
|
+ for (;;);
|
|
|
+}
|
|
|
+
|
|
|
+static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR,
|
|
|
+ stop_run, NULL};
|
|
|
+
|
|
|
+/* action list */
|
|
|
+
|
|
|
+static struct shutdown_action *shutdown_actions_list[] = {
|
|
|
+ &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action};
|
|
|
+#define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *))
|
|
|
+
|
|
|
+/*
|
|
|
+ * Trigger section
|
|
|
+ */
|
|
|
+
|
|
|
+static struct kset *shutdown_actions_kset;
|
|
|
+
|
|
|
+static int set_trigger(const char *buf, struct shutdown_trigger *trigger,
|
|
|
+ size_t len)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
|
|
|
+ if (!shutdown_actions_list[i])
|
|
|
+ continue;
|
|
|
+ if (strncmp(buf, shutdown_actions_list[i]->name,
|
|
|
+ strlen(shutdown_actions_list[i]->name)) == 0) {
|
|
|
+ trigger->action = shutdown_actions_list[i];
|
|
|
+ return len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
+/* on reipl */
|
|
|
+
|
|
|
+static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR,
|
|
|
+ &reipl_action};
|
|
|
+
|
|
|
+static ssize_t on_reboot_show(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr, char *page)
|
|
|
+{
|
|
|
+ return sprintf(page, "%s\n", on_reboot_trigger.action->name);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t on_reboot_store(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr,
|
|
|
+ const char *buf, size_t len)
|
|
|
+{
|
|
|
+ return set_trigger(buf, &on_reboot_trigger, len);
|
|
|
+}
|
|
|
+
|
|
|
+static struct kobj_attribute on_reboot_attr =
|
|
|
+ __ATTR(on_reboot, 0644, on_reboot_show, on_reboot_store);
|
|
|
+
|
|
|
+static void do_machine_restart(char *__unused)
|
|
|
+{
|
|
|
+ smp_send_stop();
|
|
|
+ on_reboot_trigger.action->fn(&on_reboot_trigger);
|
|
|
+ reipl_run(NULL);
|
|
|
+}
|
|
|
+void (*_machine_restart)(char *command) = do_machine_restart;
|
|
|
+
|
|
|
+/* on panic */
|
|
|
+
|
|
|
+static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action};
|
|
|
+
|
|
|
+static ssize_t on_panic_show(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr, char *page)
|
|
|
+{
|
|
|
+ return sprintf(page, "%s\n", on_panic_trigger.action->name);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t on_panic_store(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr,
|
|
|
+ const char *buf, size_t len)
|
|
|
+{
|
|
|
+ return set_trigger(buf, &on_panic_trigger, len);
|
|
|
+}
|
|
|
+
|
|
|
+static struct kobj_attribute on_panic_attr =
|
|
|
+ __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
|
|
|
+
|
|
|
+static void do_panic(void)
|
|
|
+{
|
|
|
+ on_panic_trigger.action->fn(&on_panic_trigger);
|
|
|
+ stop_run(&on_panic_trigger);
|
|
|
+}
|
|
|
+
|
|
|
+/* on halt */
|
|
|
+
|
|
|
+static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action};
|
|
|
+
|
|
|
+static ssize_t on_halt_show(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr, char *page)
|
|
|
+{
|
|
|
+ return sprintf(page, "%s\n", on_halt_trigger.action->name);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t on_halt_store(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr,
|
|
|
+ const char *buf, size_t len)
|
|
|
+{
|
|
|
+ return set_trigger(buf, &on_halt_trigger, len);
|
|
|
+}
|
|
|
+
|
|
|
+static struct kobj_attribute on_halt_attr =
|
|
|
+ __ATTR(on_halt, 0644, on_halt_show, on_halt_store);
|
|
|
|
|
|
+
|
|
|
+static void do_machine_halt(void)
|
|
|
+{
|
|
|
+ smp_send_stop();
|
|
|
+ on_halt_trigger.action->fn(&on_halt_trigger);
|
|
|
+ stop_run(&on_halt_trigger);
|
|
|
+}
|
|
|
+void (*_machine_halt)(void) = do_machine_halt;
|
|
|
+
|
|
|
+/* on power off */
|
|
|
+
|
|
|
+static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action};
|
|
|
+
|
|
|
+static ssize_t on_poff_show(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr, char *page)
|
|
|
+{
|
|
|
+ return sprintf(page, "%s\n", on_poff_trigger.action->name);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t on_poff_store(struct kobject *kobj,
|
|
|
+ struct kobj_attribute *attr,
|
|
|
+ const char *buf, size_t len)
|
|
|
+{
|
|
|
+ return set_trigger(buf, &on_poff_trigger, len);
|
|
|
+}
|
|
|
+
|
|
|
+static struct kobj_attribute on_poff_attr =
|
|
|
+ __ATTR(on_poff, 0644, on_poff_show, on_poff_store);
|
|
|
+
|
|
|
+
|
|
|
+static void do_machine_power_off(void)
|
|
|
+{
|
|
|
+ smp_send_stop();
|
|
|
+ on_poff_trigger.action->fn(&on_poff_trigger);
|
|
|
+ stop_run(&on_poff_trigger);
|
|
|
+}
|
|
|
+void (*_machine_power_off)(void) = do_machine_power_off;
|
|
|
+
|
|
|
+static void __init shutdown_triggers_init(void)
|
|
|
+{
|
|
|
shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL,
|
|
|
firmware_kobj);
|
|
|
if (!shutdown_actions_kset)
|
|
|
- return -ENOMEM;
|
|
|
- rc = sysfs_create_file(&shutdown_actions_kset->kobj, &on_panic_attr);
|
|
|
- if (rc) {
|
|
|
- kset_unregister(shutdown_actions_kset);
|
|
|
- return rc;
|
|
|
+ goto fail;
|
|
|
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
|
|
|
+ &on_reboot_attr.attr))
|
|
|
+ goto fail;
|
|
|
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
|
|
|
+ &on_panic_attr.attr))
|
|
|
+ goto fail;
|
|
|
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
|
|
|
+ &on_halt_attr.attr))
|
|
|
+ goto fail;
|
|
|
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
|
|
|
+ &on_poff_attr.attr))
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ return;
|
|
|
+fail:
|
|
|
+ panic("shutdown_triggers_init failed\n");
|
|
|
+}
|
|
|
+
|
|
|
+static void __init shutdown_actions_init(void)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
|
|
|
+ if (!shutdown_actions_list[i]->init)
|
|
|
+ continue;
|
|
|
+ if (shutdown_actions_list[i]->init())
|
|
|
+ shutdown_actions_list[i] = NULL;
|
|
|
}
|
|
|
- atomic_notifier_chain_register(&panic_notifier_list,
|
|
|
- &shutdown_on_panic_nb);
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
static int __init s390_ipl_init(void)
|
|
|
{
|
|
|
- int rc;
|
|
|
-
|
|
|
- sclp_get_ipl_info(&sclp_ipl_info);
|
|
|
reipl_probe();
|
|
|
- rc = ipl_init();
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
- rc = reipl_init();
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
- rc = dump_init();
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
- rc = shutdown_actions_init();
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
+ shutdown_actions_init();
|
|
|
+ shutdown_triggers_init();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
__initcall(s390_ipl_init);
|
|
|
|
|
|
+static void __init strncpy_skip_quote(char *dst, char *src, int n)
|
|
|
+{
|
|
|
+ int sx, dx;
|
|
|
+
|
|
|
+ dx = 0;
|
|
|
+ for (sx = 0; src[sx] != 0; sx++) {
|
|
|
+ if (src[sx] == '"')
|
|
|
+ continue;
|
|
|
+ dst[dx++] = src[sx];
|
|
|
+ if (dx >= n)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int __init vmcmd_on_reboot_setup(char *str)
|
|
|
+{
|
|
|
+ if (!MACHINE_IS_VM)
|
|
|
+ return 1;
|
|
|
+ strncpy_skip_quote(vmcmd_on_reboot, str, 127);
|
|
|
+ vmcmd_on_reboot[127] = 0;
|
|
|
+ on_reboot_trigger.action = &vmcmd_action;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+__setup("vmreboot=", vmcmd_on_reboot_setup);
|
|
|
+
|
|
|
+static int __init vmcmd_on_panic_setup(char *str)
|
|
|
+{
|
|
|
+ if (!MACHINE_IS_VM)
|
|
|
+ return 1;
|
|
|
+ strncpy_skip_quote(vmcmd_on_panic, str, 127);
|
|
|
+ vmcmd_on_panic[127] = 0;
|
|
|
+ on_panic_trigger.action = &vmcmd_action;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+__setup("vmpanic=", vmcmd_on_panic_setup);
|
|
|
+
|
|
|
+static int __init vmcmd_on_halt_setup(char *str)
|
|
|
+{
|
|
|
+ if (!MACHINE_IS_VM)
|
|
|
+ return 1;
|
|
|
+ strncpy_skip_quote(vmcmd_on_halt, str, 127);
|
|
|
+ vmcmd_on_halt[127] = 0;
|
|
|
+ on_halt_trigger.action = &vmcmd_action;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+__setup("vmhalt=", vmcmd_on_halt_setup);
|
|
|
+
|
|
|
+static int __init vmcmd_on_poff_setup(char *str)
|
|
|
+{
|
|
|
+ if (!MACHINE_IS_VM)
|
|
|
+ return 1;
|
|
|
+ strncpy_skip_quote(vmcmd_on_poff, str, 127);
|
|
|
+ vmcmd_on_poff[127] = 0;
|
|
|
+ on_poff_trigger.action = &vmcmd_action;
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+__setup("vmpoff=", vmcmd_on_poff_setup);
|
|
|
+
|
|
|
+static int on_panic_notify(struct notifier_block *self,
|
|
|
+ unsigned long event, void *data)
|
|
|
+{
|
|
|
+ do_panic();
|
|
|
+ return NOTIFY_OK;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block on_panic_nb = {
|
|
|
+ .notifier_call = on_panic_notify,
|
|
|
+ .priority = 0,
|
|
|
+};
|
|
|
+
|
|
|
+void __init setup_ipl(void)
|
|
|
+{
|
|
|
+ ipl_info.type = get_ipl_type();
|
|
|
+ switch (ipl_info.type) {
|
|
|
+ case IPL_TYPE_CCW:
|
|
|
+ ipl_info.data.ccw.dev_id.devno = ipl_devno;
|
|
|
+ ipl_info.data.ccw.dev_id.ssid = 0;
|
|
|
+ break;
|
|
|
+ case IPL_TYPE_FCP:
|
|
|
+ case IPL_TYPE_FCP_DUMP:
|
|
|
+ ipl_info.data.fcp.dev_id.devno =
|
|
|
+ IPL_PARMBLOCK_START->ipl_info.fcp.devno;
|
|
|
+ ipl_info.data.fcp.dev_id.ssid = 0;
|
|
|
+ ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
|
|
|
+ ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
|
|
|
+ break;
|
|
|
+ case IPL_TYPE_NSS:
|
|
|
+ strncpy(ipl_info.data.nss.name, kernel_nss_name,
|
|
|
+ sizeof(ipl_info.data.nss.name));
|
|
|
+ break;
|
|
|
+ case IPL_TYPE_UNKNOWN:
|
|
|
+ default:
|
|
|
+ /* We have no info to copy */
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
|
|
|
+}
|
|
|
+
|
|
|
void __init ipl_save_parameters(void)
|
|
|
{
|
|
|
struct cio_iplinfo iplinfo;
|
|
@@ -1202,3 +1468,4 @@ void s390_reset_system(void)
|
|
|
|
|
|
do_reset_calls();
|
|
|
}
|
|
|
+
|