[S390] Extra Kernel Parameters via VMPARM
authorHendrik Brueckner <brueckner@linux.vnet.ibm.com>
Mon, 14 Jul 2008 07:59:09 +0000 (09:59 +0200)
committerHeiko Carstens <heiko.carstens@de.ibm.com>
Mon, 14 Jul 2008 08:02:14 +0000 (10:02 +0200)
Now it is possible to specify additional kernel parameters on the IPL
command line using the IPL PARM option.
If the Linux system is already running, the new reipl sysfs attribute
'parm' can be used to change kernel parameters for the next reboot.
Examples:
  IPL    C PARM dasd=1234 root=/dev/dasda1
  IPL 1234 PARM savesys=mylnxnss
  echo "init=/bin/bash" > /sys/firmware/reipl/ccw/parm

Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
arch/s390/kernel/early.c
arch/s390/kernel/ipl.c
arch/s390/kernel/setup.c
include/asm-s390/ipl.h
include/asm-s390/setup.h

index b2226e41f06788bd688fc6e16ac1fe33cd2a969f..e22473993dc998b6ddb3625ef1dcdd7c5fbd7529 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/pfn.h>
 #include <linux/uaccess.h>
+#include <asm/ebcdic.h>
 #include <asm/ipl.h>
 #include <asm/lowcore.h>
 #include <asm/processor.h>
 /*
  * Create a Kernel NSS if the SAVESYS= parameter is defined
  */
-#define DEFSYS_CMD_SIZE                96
+#define DEFSYS_CMD_SIZE                128
 #define SAVESYS_CMD_SIZE       32
 
 char kernel_nss_name[NSS_NAME_SIZE + 1];
 
+static void __init setup_boot_command_line(void);
+
+
 #ifdef CONFIG_SHARED_KERNEL
+int __init savesys_ipl_nss(char *cmd, const int cmdlen);
+
+asm(
+       "       .section .init.text,\"ax\",@progbits\n"
+       "       .align  4\n"
+       "       .type   savesys_ipl_nss, @function\n"
+       "savesys_ipl_nss:\n"
+#ifdef CONFIG_64BIT
+       "       stmg    6,15,48(15)\n"
+       "       lgr     14,3\n"
+       "       sam31\n"
+       "       diag    2,14,0x8\n"
+       "       sam64\n"
+       "       lgr     2,14\n"
+       "       lmg     6,15,48(15)\n"
+#else
+       "       stm     6,15,24(15)\n"
+       "       lr      14,3\n"
+       "       diag    2,14,0x8\n"
+       "       lr      2,14\n"
+       "       lm      6,15,24(15)\n"
+#endif
+       "       br      14\n"
+       "       .size   savesys_ipl_nss, .-savesys_ipl_nss\n");
+
 static noinline __init void create_kernel_nss(void)
 {
        unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size;
@@ -39,6 +68,7 @@ static noinline __init void create_kernel_nss(void)
        unsigned int sinitrd_pfn, einitrd_pfn;
 #endif
        int response;
+       size_t len;
        char *savesys_ptr;
        char upper_command_line[COMMAND_LINE_SIZE];
        char defsys_cmd[DEFSYS_CMD_SIZE];
@@ -49,8 +79,8 @@ static noinline __init void create_kernel_nss(void)
                return;
 
        /* Convert COMMAND_LINE to upper case */
-       for (i = 0; i < strlen(COMMAND_LINE); i++)
-               upper_command_line[i] = toupper(COMMAND_LINE[i]);
+       for (i = 0; i < strlen(boot_command_line); i++)
+               upper_command_line[i] = toupper(boot_command_line[i]);
 
        savesys_ptr = strstr(upper_command_line, "SAVESYS=");
 
@@ -83,7 +113,8 @@ static noinline __init void create_kernel_nss(void)
        }
 #endif
 
-       sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK", defsys_cmd, min_size);
+       sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK PARMREGS=0-13",
+               defsys_cmd, min_size);
        sprintf(savesys_cmd, "SAVESYS %s \n IPL %s",
                kernel_nss_name, kernel_nss_name);
 
@@ -94,13 +125,24 @@ static noinline __init void create_kernel_nss(void)
                return;
        }
 
-       __cpcmd(savesys_cmd, NULL, 0, &response);
+       len = strlen(savesys_cmd);
+       ASCEBC(savesys_cmd, len);
+       response = savesys_ipl_nss(savesys_cmd, len);
 
-       if (response != strlen(savesys_cmd)) {
+       /* On success: response is equal to the command size,
+        *             max SAVESYS_CMD_SIZE
+        * On error: response contains the numeric portion of cp error message.
+        *           for SAVESYS it will be >= 263
+        */
+       if (response > SAVESYS_CMD_SIZE) {
                kernel_nss_name[0] = '\0';
                return;
        }
 
+       /* re-setup boot command line with new ipl vm parms */
+       ipl_update_parameters();
+       setup_boot_command_line();
+
        ipl_flags = IPL_NSS_VALID;
 }
 
@@ -397,6 +439,26 @@ static __init void rescue_initrd(void)
 #endif
 }
 
+/* Set up boot command line */
+static void __init setup_boot_command_line(void)
+{
+       char *parm = NULL;
+
+       /* copy arch command line */
+       strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
+       boot_command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0;
+
+       /* append IPL PARM data to the boot command line */
+       if (MACHINE_IS_VM) {
+               parm = boot_command_line + strlen(boot_command_line);
+               *parm++ = ' ';
+               get_ipl_vmparm(parm);
+               if (parm[0] == '=')
+                       memmove(boot_command_line, parm + 1, strlen(parm));
+       }
+}
+
+
 /*
  * Save ipl parameters, clear bss memory, initialize storage keys
  * and create a kernel NSS at startup if the SAVESYS= parm is defined
@@ -411,10 +473,12 @@ void __init startup_init(void)
        init_kernel_storage_key();
        lockdep_init();
        lockdep_off();
-       detect_machine_type();
-       create_kernel_nss();
        sort_main_extable();
        setup_lowcore_early();
+       detect_machine_type();
+       ipl_update_parameters();
+       setup_boot_command_line();
+       create_kernel_nss();
        detect_mvpg();
        detect_ieee();
        detect_csp();
index 532542447d661a34537e42ff69571616d8874865..9f6dfd306abb32278a8b716326dfb50dbfca0b66 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/ebcdic.h>
 #include <asm/reset.h>
 #include <asm/sclp.h>
+#include <asm/setup.h>
 
 #define IPL_PARM_BLOCK_VERSION 0
 
@@ -121,6 +122,7 @@ enum ipl_method {
        REIPL_METHOD_FCP_RO_VM,
        REIPL_METHOD_FCP_DUMP,
        REIPL_METHOD_NSS,
+       REIPL_METHOD_NSS_DIAG,
        REIPL_METHOD_DEFAULT,
 };
 
@@ -134,14 +136,15 @@ enum dump_method {
 
 static int diag308_set_works = 0;
 
+static struct ipl_parameter_block ipl_block;
+
 static int reipl_capabilities = IPL_TYPE_UNKNOWN;
 
 static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
 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 char reipl_nss_name[NSS_NAME_SIZE + 1];
+static struct ipl_parameter_block *reipl_block_nss;
 
 static int dump_capabilities = DUMP_TYPE_NONE;
 static enum dump_type dump_type = DUMP_TYPE_NONE;
@@ -263,6 +266,56 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr,
 
 static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
 
+/* VM IPL PARM routines */
+static void reipl_get_ascii_vmparm(char *dest,
+                                  const struct ipl_parameter_block *ipb)
+{
+       int i;
+       int len = 0;
+       char has_lowercase = 0;
+
+       if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
+           (ipb->ipl_info.ccw.vm_parm_len > 0)) {
+
+               len = ipb->ipl_info.ccw.vm_parm_len;
+               memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
+               /* If at least one character is lowercase, we assume mixed
+                * case; otherwise we convert everything to lowercase.
+                */
+               for (i = 0; i < len; i++)
+                       if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */
+                           (dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */
+                           (dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */
+                               has_lowercase = 1;
+                               break;
+                       }
+               if (!has_lowercase)
+                       EBC_TOLOWER(dest, len);
+               EBCASC(dest, len);
+       }
+       dest[len] = 0;
+}
+
+void get_ipl_vmparm(char *dest)
+{
+       if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW))
+               reipl_get_ascii_vmparm(dest, &ipl_block);
+       else
+               dest[0] = 0;
+}
+
+static ssize_t ipl_vm_parm_show(struct kobject *kobj,
+                               struct kobj_attribute *attr, char *page)
+{
+       char parm[DIAG308_VMPARM_SIZE + 1] = {};
+
+       get_ipl_vmparm(parm);
+       return sprintf(page, "%s\n", parm);
+}
+
+static struct kobj_attribute sys_ipl_vm_parm_attr =
+       __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL);
+
 static ssize_t sys_ipl_device_show(struct kobject *kobj,
                                   struct kobj_attribute *attr, char *page)
 {
@@ -370,15 +423,27 @@ static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj,
 static struct kobj_attribute sys_ipl_ccw_loadparm_attr =
        __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL);
 
-static struct attribute *ipl_ccw_attrs[] = {
+static struct attribute *ipl_ccw_attrs_vm[] = {
+       &sys_ipl_type_attr.attr,
+       &sys_ipl_device_attr.attr,
+       &sys_ipl_ccw_loadparm_attr.attr,
+       &sys_ipl_vm_parm_attr.attr,
+       NULL,
+};
+
+static struct attribute *ipl_ccw_attrs_lpar[] = {
        &sys_ipl_type_attr.attr,
        &sys_ipl_device_attr.attr,
        &sys_ipl_ccw_loadparm_attr.attr,
        NULL,
 };
 
-static struct attribute_group ipl_ccw_attr_group = {
-       .attrs = ipl_ccw_attrs,
+static struct attribute_group ipl_ccw_attr_group_vm = {
+       .attrs = ipl_ccw_attrs_vm,
+};
+
+static struct attribute_group ipl_ccw_attr_group_lpar = {
+       .attrs = ipl_ccw_attrs_lpar
 };
 
 /* NSS ipl device attributes */
@@ -388,6 +453,8 @@ DEFINE_IPL_ATTR_RO(ipl_nss, name, "%s\n", kernel_nss_name);
 static struct attribute *ipl_nss_attrs[] = {
        &sys_ipl_type_attr.attr,
        &sys_ipl_nss_name_attr.attr,
+       &sys_ipl_ccw_loadparm_attr.attr,
+       &sys_ipl_vm_parm_attr.attr,
        NULL,
 };
 
@@ -450,7 +517,12 @@ static int __init ipl_init(void)
        }
        switch (ipl_info.type) {
        case IPL_TYPE_CCW:
-               rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group);
+               if (MACHINE_IS_VM)
+                       rc = sysfs_create_group(&ipl_kset->kobj,
+                                               &ipl_ccw_attr_group_vm);
+               else
+                       rc = sysfs_create_group(&ipl_kset->kobj,
+                                               &ipl_ccw_attr_group_lpar);
                break;
        case IPL_TYPE_FCP:
        case IPL_TYPE_FCP_DUMP:
@@ -481,6 +553,83 @@ static struct shutdown_action __refdata ipl_action = {
  * reipl shutdown action: Reboot Linux on shutdown.
  */
 
+/* VM IPL PARM attributes */
+static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb,
+                                         char *page)
+{
+       char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
+
+       reipl_get_ascii_vmparm(vmparm, ipb);
+       return sprintf(page, "%s\n", vmparm);
+}
+
+static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb,
+                                         size_t vmparm_max,
+                                         const char *buf, size_t len)
+{
+       int i, ip_len;
+
+       /* ignore trailing newline */
+       ip_len = len;
+       if ((len > 0) && (buf[len - 1] == '\n'))
+               ip_len--;
+
+       if (ip_len > vmparm_max)
+               return -EINVAL;
+
+       /* parm is used to store kernel options, check for common chars */
+       for (i = 0; i < ip_len; i++)
+               if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i])))
+                       return -EINVAL;
+
+       memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE);
+       ipb->ipl_info.ccw.vm_parm_len = ip_len;
+       if (ip_len > 0) {
+               ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
+               memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len);
+               ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len);
+       } else {
+               ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID;
+       }
+
+       return len;
+}
+
+/* NSS wrapper */
+static ssize_t reipl_nss_vmparm_show(struct kobject *kobj,
+                                    struct kobj_attribute *attr, char *page)
+{
+       return reipl_generic_vmparm_show(reipl_block_nss, page);
+}
+
+static ssize_t reipl_nss_vmparm_store(struct kobject *kobj,
+                                     struct kobj_attribute *attr,
+                                     const char *buf, size_t len)
+{
+       return reipl_generic_vmparm_store(reipl_block_nss, 56, buf, len);
+}
+
+/* CCW wrapper */
+static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj,
+                                    struct kobj_attribute *attr, char *page)
+{
+       return reipl_generic_vmparm_show(reipl_block_ccw, page);
+}
+
+static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj,
+                                     struct kobj_attribute *attr,
+                                     const char *buf, size_t len)
+{
+       return reipl_generic_vmparm_store(reipl_block_ccw, 64, buf, len);
+}
+
+static struct kobj_attribute sys_reipl_nss_vmparm_attr =
+       __ATTR(parm, S_IRUGO | S_IWUSR, reipl_nss_vmparm_show,
+                                       reipl_nss_vmparm_store);
+static struct kobj_attribute sys_reipl_ccw_vmparm_attr =
+       __ATTR(parm, S_IRUGO | S_IWUSR, reipl_ccw_vmparm_show,
+                                       reipl_ccw_vmparm_store);
+
 /* FCP reipl device attributes */
 
 DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n",
@@ -513,27 +662,26 @@ static struct attribute_group reipl_fcp_attr_group = {
 DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
        reipl_block_ccw->ipl_info.ccw.devno);
 
-static void reipl_get_ascii_loadparm(char *loadparm)
+static void reipl_get_ascii_loadparm(char *loadparm,
+                                    struct ipl_parameter_block *ibp)
 {
-       memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param,
-              LOADPARM_LEN);
+       memcpy(loadparm, ibp->ipl_info.ccw.load_parm, LOADPARM_LEN);
        EBCASC(loadparm, LOADPARM_LEN);
        loadparm[LOADPARM_LEN] = 0;
        strstrip(loadparm);
 }
 
-static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj,
-                                      struct kobj_attribute *attr, char *page)
+static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb,
+                                          char *page)
 {
        char buf[LOADPARM_LEN + 1];
 
-       reipl_get_ascii_loadparm(buf);
+       reipl_get_ascii_loadparm(buf, ipb);
        return sprintf(page, "%s\n", buf);
 }
 
-static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj,
-                                       struct kobj_attribute *attr,
-                                       const char *buf, size_t len)
+static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb,
+                                           const char *buf, size_t len)
 {
        int i, lp_len;
 
@@ -552,35 +700,128 @@ static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj,
                return -EINVAL;
        }
        /* initialize loadparm with blanks */
-       memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN);
+       memset(ipb->ipl_info.ccw.load_parm, ' ', LOADPARM_LEN);
        /* copy and convert to ebcdic */
-       memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len);
-       ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN);
+       memcpy(ipb->ipl_info.ccw.load_parm, buf, lp_len);
+       ASCEBC(ipb->ipl_info.ccw.load_parm, LOADPARM_LEN);
        return len;
 }
 
+/* NSS wrapper */
+static ssize_t reipl_nss_loadparm_show(struct kobject *kobj,
+                                      struct kobj_attribute *attr, char *page)
+{
+       return reipl_generic_loadparm_show(reipl_block_nss, page);
+}
+
+static ssize_t reipl_nss_loadparm_store(struct kobject *kobj,
+                                       struct kobj_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return reipl_generic_loadparm_store(reipl_block_nss, buf, len);
+}
+
+/* CCW wrapper */
+static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj,
+                                      struct kobj_attribute *attr, char *page)
+{
+       return reipl_generic_loadparm_show(reipl_block_ccw, page);
+}
+
+static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj,
+                                       struct kobj_attribute *attr,
+                                       const char *buf, size_t len)
+{
+       return reipl_generic_loadparm_store(reipl_block_ccw, buf, len);
+}
+
 static struct kobj_attribute sys_reipl_ccw_loadparm_attr =
-       __ATTR(loadparm, 0644, reipl_ccw_loadparm_show,
-              reipl_ccw_loadparm_store);
+       __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_ccw_loadparm_show,
+                                           reipl_ccw_loadparm_store);
+
+static struct attribute *reipl_ccw_attrs_vm[] = {
+       &sys_reipl_ccw_device_attr.attr,
+       &sys_reipl_ccw_loadparm_attr.attr,
+       &sys_reipl_ccw_vmparm_attr.attr,
+       NULL,
+};
 
-static struct attribute *reipl_ccw_attrs[] = {
+static struct attribute *reipl_ccw_attrs_lpar[] = {
        &sys_reipl_ccw_device_attr.attr,
        &sys_reipl_ccw_loadparm_attr.attr,
        NULL,
 };
 
-static struct attribute_group reipl_ccw_attr_group = {
+static struct attribute_group reipl_ccw_attr_group_vm = {
        .name  = IPL_CCW_STR,
-       .attrs = reipl_ccw_attrs,
+       .attrs = reipl_ccw_attrs_vm,
+};
+
+static struct attribute_group reipl_ccw_attr_group_lpar = {
+       .name  = IPL_CCW_STR,
+       .attrs = reipl_ccw_attrs_lpar,
 };
 
 
 /* NSS reipl device attributes */
+static void reipl_get_ascii_nss_name(char *dst,
+                                    struct ipl_parameter_block *ipb)
+{
+       memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
+       EBCASC(dst, NSS_NAME_SIZE);
+       dst[NSS_NAME_SIZE] = 0;
+}
+
+static ssize_t reipl_nss_name_show(struct kobject *kobj,
+                                  struct kobj_attribute *attr, char *page)
+{
+       char nss_name[NSS_NAME_SIZE + 1] = {};
+
+       reipl_get_ascii_nss_name(nss_name, reipl_block_nss);
+       return sprintf(page, "%s\n", nss_name);
+}
+
+static ssize_t reipl_nss_name_store(struct kobject *kobj,
+                                   struct kobj_attribute *attr,
+                                   const char *buf, size_t len)
+{
+       int nss_len;
+
+       /* ignore trailing newline */
+       nss_len = len;
+       if ((len > 0) && (buf[len - 1] == '\n'))
+               nss_len--;
 
-DEFINE_IPL_ATTR_STR_RW(reipl_nss, name, "%s\n", "%s\n", reipl_nss_name);
+       if (nss_len > NSS_NAME_SIZE)
+               return -EINVAL;
+
+       memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE);
+       if (nss_len > 0) {
+               reipl_block_nss->ipl_info.ccw.vm_flags |=
+                       DIAG308_VM_FLAGS_NSS_VALID;
+               memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len);
+               ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
+               EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
+       } else {
+               reipl_block_nss->ipl_info.ccw.vm_flags &=
+                       ~DIAG308_VM_FLAGS_NSS_VALID;
+       }
+
+       return len;
+}
+
+static struct kobj_attribute sys_reipl_nss_name_attr =
+       __ATTR(name, S_IRUGO | S_IWUSR, reipl_nss_name_show,
+                                       reipl_nss_name_store);
+
+static struct kobj_attribute sys_reipl_nss_loadparm_attr =
+       __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nss_loadparm_show,
+                                           reipl_nss_loadparm_store);
 
 static struct attribute *reipl_nss_attrs[] = {
        &sys_reipl_nss_name_attr.attr,
+       &sys_reipl_nss_loadparm_attr.attr,
+       &sys_reipl_nss_vmparm_attr.attr,
        NULL,
 };
 
@@ -617,7 +858,10 @@ static int reipl_set_type(enum ipl_type type)
                reipl_method = REIPL_METHOD_FCP_DUMP;
                break;
        case IPL_TYPE_NSS:
-               reipl_method = REIPL_METHOD_NSS;
+               if (diag308_set_works)
+                       reipl_method = REIPL_METHOD_NSS_DIAG;
+               else
+                       reipl_method = REIPL_METHOD_NSS;
                break;
        case IPL_TYPE_UNKNOWN:
                reipl_method = REIPL_METHOD_DEFAULT;
@@ -655,11 +899,38 @@ static struct kobj_attribute reipl_type_attr =
 
 static struct kset *reipl_kset;
 
+static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb,
+                          const enum ipl_method m)
+{
+       char loadparm[LOADPARM_LEN + 1] = {};
+       char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
+       char nss_name[NSS_NAME_SIZE + 1] = {};
+       size_t pos = 0;
+
+       reipl_get_ascii_loadparm(loadparm, ipb);
+       reipl_get_ascii_nss_name(nss_name, ipb);
+       reipl_get_ascii_vmparm(vmparm, ipb);
+
+       switch (m) {
+       case REIPL_METHOD_CCW_VM:
+               pos = sprintf(dst, "IPL %X CLEAR", ipb->ipl_info.ccw.devno);
+               break;
+       case REIPL_METHOD_NSS:
+               pos = sprintf(dst, "IPL %s", nss_name);
+               break;
+       default:
+               break;
+       }
+       if (strlen(loadparm) > 0)
+               pos += sprintf(dst + pos, " LOADPARM '%s'", loadparm);
+       if (strlen(vmparm) > 0)
+               sprintf(dst + pos, " PARM %s", vmparm);
+}
+
 static void reipl_run(struct shutdown_trigger *trigger)
 {
        struct ccw_dev_id devid;
-       static char buf[100];
-       char loadparm[LOADPARM_LEN + 1];
+       static char buf[128];
 
        switch (reipl_method) {
        case REIPL_METHOD_CCW_CIO:
@@ -668,13 +939,7 @@ static void reipl_run(struct shutdown_trigger *trigger)
                reipl_ccw_dev(&devid);
                break;
        case REIPL_METHOD_CCW_VM:
-               reipl_get_ascii_loadparm(loadparm);
-               if (strlen(loadparm) == 0)
-                       sprintf(buf, "IPL %X CLEAR",
-                               reipl_block_ccw->ipl_info.ccw.devno);
-               else
-                       sprintf(buf, "IPL %X CLEAR LOADPARM '%s'",
-                               reipl_block_ccw->ipl_info.ccw.devno, loadparm);
+               get_ipl_string(buf, reipl_block_ccw, REIPL_METHOD_CCW_VM);
                __cpcmd(buf, NULL, 0, NULL);
                break;
        case REIPL_METHOD_CCW_DIAG:
@@ -691,8 +956,12 @@ static void reipl_run(struct shutdown_trigger *trigger)
        case REIPL_METHOD_FCP_RO_VM:
                __cpcmd("IPL", NULL, 0, NULL);
                break;
+       case REIPL_METHOD_NSS_DIAG:
+               diag308(DIAG308_SET, reipl_block_nss);
+               diag308(DIAG308_IPL, NULL);
+               break;
        case REIPL_METHOD_NSS:
-               sprintf(buf, "IPL %s", reipl_nss_name);
+               get_ipl_string(buf, reipl_block_nss, REIPL_METHOD_NSS);
                __cpcmd(buf, NULL, 0, NULL);
                break;
        case REIPL_METHOD_DEFAULT:
@@ -707,16 +976,36 @@ static void reipl_run(struct shutdown_trigger *trigger)
        disabled_wait((unsigned long) __builtin_return_address(0));
 }
 
-static void __init reipl_probe(void)
+static void reipl_block_ccw_init(struct ipl_parameter_block *ipb)
 {
-       void *buffer;
+       ipb->hdr.len = IPL_PARM_BLK_CCW_LEN;
+       ipb->hdr.version = IPL_PARM_BLOCK_VERSION;
+       ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
+       ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+}
 
-       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 void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb)
+{
+       /* LOADPARM */
+       /* check if read scp info worked and set loadparm */
+       if (sclp_ipl_info.is_valid)
+               memcpy(ipb->ipl_info.ccw.load_parm,
+                               &sclp_ipl_info.loadparm, LOADPARM_LEN);
+       else
+               /* read scp info failed: set empty loadparm (EBCDIC blanks) */
+               memset(ipb->ipl_info.ccw.load_parm, 0x40, LOADPARM_LEN);
+       ipb->hdr.flags = DIAG308_FLAGS_LP_VALID;
+
+       /* VM PARM */
+       if (MACHINE_IS_VM && diag308_set_works &&
+           (ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) {
+
+               ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
+               ipb->ipl_info.ccw.vm_parm_len =
+                                       ipl_block.ipl_info.ccw.vm_parm_len;
+               memcpy(ipb->ipl_info.ccw.vm_parm,
+                      ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE);
+       }
 }
 
 static int __init reipl_nss_init(void)
@@ -725,10 +1014,31 @@ static int __init reipl_nss_init(void)
 
        if (!MACHINE_IS_VM)
                return 0;
+
+       reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!reipl_block_nss)
+               return -ENOMEM;
+
+       if (!diag308_set_works)
+               sys_reipl_nss_vmparm_attr.attr.mode = S_IRUGO;
+
        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);
+
+       reipl_block_ccw_init(reipl_block_nss);
+       if (ipl_info.type == IPL_TYPE_NSS) {
+               memset(reipl_block_nss->ipl_info.ccw.nss_name,
+                       ' ', NSS_NAME_SIZE);
+               memcpy(reipl_block_nss->ipl_info.ccw.nss_name,
+                       kernel_nss_name, strlen(kernel_nss_name));
+               ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
+               reipl_block_nss->ipl_info.ccw.vm_flags |=
+                       DIAG308_VM_FLAGS_NSS_VALID;
+
+               reipl_block_ccw_fill_parms(reipl_block_nss);
+       }
+
        reipl_capabilities |= IPL_TYPE_NSS;
        return 0;
 }
@@ -740,28 +1050,27 @@ static int __init reipl_ccw_init(void)
        reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
        if (!reipl_block_ccw)
                return -ENOMEM;
-       rc = sysfs_create_group(&reipl_kset->kobj, &reipl_ccw_attr_group);
-       if (rc) {
-               free_page((unsigned long)reipl_block_ccw);
-               return rc;
+
+       if (MACHINE_IS_VM) {
+               if (!diag308_set_works)
+                       sys_reipl_ccw_vmparm_attr.attr.mode = S_IRUGO;
+               rc = sysfs_create_group(&reipl_kset->kobj,
+                                       &reipl_ccw_attr_group_vm);
+       } else {
+               if(!diag308_set_works)
+                       sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
+               rc = sysfs_create_group(&reipl_kset->kobj,
+                                       &reipl_ccw_attr_group_lpar);
        }
-       reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
-       reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
-       reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
-       reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
-       reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID;
-       /* check if read scp info worked and set loadparm */
-       if (sclp_ipl_info.is_valid)
-               memcpy(reipl_block_ccw->ipl_info.ccw.load_param,
-                      &sclp_ipl_info.loadparm, LOADPARM_LEN);
-       else
-               /* read scp info failed: set empty loadparm (EBCDIC blanks) */
-               memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40,
-                      LOADPARM_LEN);
-       if (!MACHINE_IS_VM && !diag308_set_works)
-               sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
-       if (ipl_info.type == IPL_TYPE_CCW)
+       if (rc)
+               return rc;
+
+       reipl_block_ccw_init(reipl_block_ccw);
+       if (ipl_info.type == IPL_TYPE_CCW) {
                reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
+               reipl_block_ccw_fill_parms(reipl_block_ccw);
+       }
+
        reipl_capabilities |= IPL_TYPE_CCW;
        return 0;
 }
@@ -1298,7 +1607,6 @@ static void __init shutdown_actions_init(void)
 
 static int __init s390_ipl_init(void)
 {
-       reipl_probe();
        sclp_get_ipl_info(&sclp_ipl_info);
        shutdown_actions_init();
        shutdown_triggers_init();
@@ -1405,6 +1713,12 @@ void __init setup_ipl(void)
        atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
 }
 
+void __init ipl_update_parameters(void)
+{
+       if (diag308(DIAG308_STORE, &ipl_block) == DIAG308_RC_OK)
+               diag308_set_works = 1;
+}
+
 void __init ipl_save_parameters(void)
 {
        struct cio_iplinfo iplinfo;
index b91caadf974d9b3e861f54a3d82d0e00d49dc9c8..e3b4cdbae34e914d495b799f960a166c40570b9d 100644 (file)
@@ -221,18 +221,17 @@ static void __init conmode_default(void)
 #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
 static void __init setup_zfcpdump(unsigned int console_devno)
 {
-       static char str[64];
+       static char str[41];
 
        if (ipl_info.type != IPL_TYPE_FCP_DUMP)
                return;
        if (console_devno != -1)
-               sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
+               sprintf(str, " cio_ignore=all,!0.0.%04x,!0.0.%04x",
                        ipl_info.data.fcp.dev_id.devno, console_devno);
        else
-               sprintf(str, "cio_ignore=all,!0.0.%04x",
+               sprintf(str, " cio_ignore=all,!0.0.%04x",
                        ipl_info.data.fcp.dev_id.devno);
-       strcat(COMMAND_LINE, " ");
-       strcat(COMMAND_LINE, str);
+       strcat(boot_command_line, str);
        console_loglevel = 2;
 }
 #else
@@ -778,11 +777,9 @@ setup_arch(char **cmdline_p)
                printk("We are running native (64 bit mode)\n");
 #endif /* CONFIG_64BIT */
 
-       /* Save unparsed command line copy for /proc/cmdline */
-       strlcpy(boot_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
-
-       *cmdline_p = COMMAND_LINE;
-       *(*cmdline_p + COMMAND_LINE_SIZE - 1) = '\0';
+       /* Have one command line that is parsed and saved in /proc/cmdline */
+       /* boot_command_line has been already set up in early.c */
+       *cmdline_p = boot_command_line;
 
         ROOT_DEV = Root_RAM0;
 
index c1b2e50392bb095dcba90a0b797133eb4583e2c1..eaca6dff5405f9d9dd6b6b3d8f359c77d2f09890 100644 (file)
@@ -56,15 +56,19 @@ struct ipl_block_fcp {
        u8  scp_data[];
 } __attribute__((packed));
 
+#define DIAG308_VMPARM_SIZE    64
+
 struct ipl_block_ccw {
-       u8  load_param[8];
+       u8  load_parm[8];
        u8  reserved1[84];
        u8  reserved2[2];
        u16 devno;
        u8  vm_flags;
        u8  reserved3[3];
        u32 vm_parm_len;
-       u8  reserved4[80];
+       u8  nss_name[8];
+       u8  vm_parm[DIAG308_VMPARM_SIZE];
+       u8  reserved4[8];
 } __attribute__((packed));
 
 struct ipl_parameter_block {
@@ -73,7 +77,7 @@ struct ipl_parameter_block {
                struct ipl_block_fcp fcp;
                struct ipl_block_ccw ccw;
        } ipl_info;
-} __attribute__((packed));
+} __attribute__((packed,aligned(4096)));
 
 /*
  * IPL validity flags
@@ -86,6 +90,8 @@ extern void do_reipl(void);
 extern void do_halt(void);
 extern void do_poff(void);
 extern void ipl_save_parameters(void);
+extern void ipl_update_parameters(void);
+extern void get_ipl_vmparm(char *);
 
 enum {
        IPL_DEVNO_VALID         = 1,
@@ -147,6 +153,11 @@ enum diag308_flags {
        DIAG308_FLAGS_LP_VALID  = 0x80,
 };
 
+enum diag308_vm_flags {
+       DIAG308_VM_FLAGS_NSS_VALID      = 0x80,
+       DIAG308_VM_FLAGS_VP_VALID       = 0x40,
+};
+
 enum diag308_rc {
        DIAG308_RC_OK   = 1,
 };
index ba69674012a7bf3b622b51d9c873bd6149bab131..f5eebc48d98d2b478e565b5af84a067d75506938 100644 (file)
@@ -8,7 +8,9 @@
 #ifndef _ASM_S390_SETUP_H
 #define _ASM_S390_SETUP_H
 
-#define COMMAND_LINE_SIZE      896
+#define COMMAND_LINE_SIZE      1024
+
+#define ARCH_COMMAND_LINE_SIZE 896
 
 #ifdef __KERNEL__