[S390] Automatic IPL after dump
[safe/jmp/linux-2.6] / arch / s390 / kernel / ipl.c
index 2dcf590..5663c1f 100644 (file)
@@ -56,13 +56,14 @@ struct shutdown_trigger {
 };
 
 /*
- * Five shutdown action types are supported:
+ * The following 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"
+#define SHUTDOWN_ACTION_DUMP_REIPL_STR "dump_reipl"
 
 struct shutdown_action {
        char *name;
@@ -146,6 +147,7 @@ static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
 static struct ipl_parameter_block *reipl_block_fcp;
 static struct ipl_parameter_block *reipl_block_ccw;
 static struct ipl_parameter_block *reipl_block_nss;
+static struct ipl_parameter_block *reipl_block_actual;
 
 static int dump_capabilities = DUMP_TYPE_NONE;
 static enum dump_type dump_type = DUMP_TYPE_NONE;
@@ -835,6 +837,7 @@ static int reipl_set_type(enum ipl_type type)
                        reipl_method = REIPL_METHOD_CCW_VM;
                else
                        reipl_method = REIPL_METHOD_CCW_CIO;
+               reipl_block_actual = reipl_block_ccw;
                break;
        case IPL_TYPE_FCP:
                if (diag308_set_works)
@@ -843,6 +846,7 @@ static int reipl_set_type(enum ipl_type type)
                        reipl_method = REIPL_METHOD_FCP_RO_VM;
                else
                        reipl_method = REIPL_METHOD_FCP_RO_DIAG;
+               reipl_block_actual = reipl_block_fcp;
                break;
        case IPL_TYPE_FCP_DUMP:
                reipl_method = REIPL_METHOD_FCP_DUMP;
@@ -852,6 +856,7 @@ static int reipl_set_type(enum ipl_type type)
                        reipl_method = REIPL_METHOD_NSS_DIAG;
                else
                        reipl_method = REIPL_METHOD_NSS;
+               reipl_block_actual = reipl_block_nss;
                break;
        case IPL_TYPE_UNKNOWN:
                reipl_method = REIPL_METHOD_DEFAULT;
@@ -1332,6 +1337,48 @@ static struct shutdown_action __refdata dump_action = {
        .init   = dump_init,
 };
 
+static void dump_reipl_run(struct shutdown_trigger *trigger)
+{
+       preempt_disable();
+       /*
+        * Bypass dynamic address translation (DAT) when storing IPL parameter
+        * information block address and checksum into the prefix area
+        * (corresponding to absolute addresses 0-8191).
+        * When enhanced DAT applies and the STE format control in one,
+        * the absolute address is formed without prefixing. In this case a
+        * normal store (stg/st) into the prefix area would no more match to
+        * absolute addresses 0-8191.
+        */
+#ifdef CONFIG_64BIT
+       asm volatile("sturg %0,%1"
+               :: "a" ((unsigned long) reipl_block_actual),
+               "a" (&lowcore_ptr[smp_processor_id()]->ipib));
+#else
+       asm volatile("stura %0,%1"
+               :: "a" ((unsigned long) reipl_block_actual),
+               "a" (&lowcore_ptr[smp_processor_id()]->ipib));
+#endif
+       asm volatile("stura %0,%1"
+               :: "a" (cksm(reipl_block_actual, reipl_block_actual->hdr.len)),
+               "a" (&lowcore_ptr[smp_processor_id()]->ipib_checksum));
+       preempt_enable();
+       dump_run(trigger);
+}
+
+static int __init dump_reipl_init(void)
+{
+       if (!diag308_set_works)
+               return -EOPNOTSUPP;
+       else
+               return 0;
+}
+
+static struct shutdown_action __refdata dump_reipl_action = {
+       .name   = SHUTDOWN_ACTION_DUMP_REIPL_STR,
+       .fn     = dump_reipl_run,
+       .init   = dump_reipl_init,
+};
+
 /*
  * vmcmd shutdown action: Trigger vm command on shutdown.
  */
@@ -1421,7 +1468,8 @@ static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR,
 /* action list */
 
 static struct shutdown_action *shutdown_actions_list[] = {
-       &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action};
+       &ipl_action, &reipl_action, &dump_reipl_action, &dump_action,
+       &vmcmd_action, &stop_action};
 #define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *))
 
 /*
@@ -1434,11 +1482,11 @@ 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) {
+               if (sysfs_streq(buf, shutdown_actions_list[i]->name)) {
                        trigger->action = shutdown_actions_list[i];
                        return len;
                }