[S390] boot from NSS support

Add support to boot from a named saved segment (NSS).

Signed-off-by: Hongjie Yang <hongjie@us.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index b1b9a93..2569aaf 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -38,6 +38,7 @@
 #include <linux/device.h>
 #include <linux/notifier.h>
 #include <linux/pfn.h>
+#include <linux/ctype.h>
 #include <linux/reboot.h>
 
 #include <asm/uaccess.h>
@@ -50,6 +51,7 @@
 #include <asm/page.h>
 #include <asm/ptrace.h>
 #include <asm/sections.h>
+#include <asm/ebcdic.h>
 #include <asm/compat.h>
 
 long psw_kernel_bits	= (PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY |
@@ -282,6 +284,140 @@
 	}
 }
 
+/*
+ * Create a Kernel NSS if the SAVESYS= parameter is defined
+*/
+#define DEFSYS_CMD_SIZE	96
+#define SAVESYS_CMD_SIZE	32
+
+extern int _eshared;
+char kernel_nss_name[NSS_NAME_SIZE + 1];
+
+#ifdef CONFIG_SHARED_KERNEL
+static __init void create_kernel_nss(void)
+{
+	unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size;
+#ifdef CONFIG_BLK_DEV_INITRD
+	unsigned int sinitrd_pfn, einitrd_pfn;
+#endif
+	int response;
+	char *savesys_ptr;
+	char upper_command_line[COMMAND_LINE_SIZE];
+	char defsys_cmd[DEFSYS_CMD_SIZE];
+	char savesys_cmd[SAVESYS_CMD_SIZE];
+
+	/* Do nothing if we are not running under VM */
+	if (!MACHINE_IS_VM)
+		return;
+
+	/* Convert COMMAND_LINE to upper case */
+	for (i = 0; i < strlen(COMMAND_LINE); i++)
+		upper_command_line[i] = toupper(COMMAND_LINE[i]);
+
+	savesys_ptr = strstr(upper_command_line, "SAVESYS=");
+
+	if (!savesys_ptr)
+		return;
+
+	savesys_ptr += 8;    /* Point to the beginning of the NSS name */
+	for (i = 0; i < NSS_NAME_SIZE; i++) {
+		if (savesys_ptr[i] == ' ' || savesys_ptr[i] == '\0')
+			break;
+		kernel_nss_name[i] = savesys_ptr[i];
+	}
+
+	stext_pfn = PFN_DOWN(__pa(&_stext));
+	eshared_pfn = PFN_DOWN(__pa(&_eshared));
+	end_pfn = PFN_UP(__pa(&_end));
+	min_size = end_pfn << 2;
+
+	sprintf(defsys_cmd, "DEFSYS %s 00000-%.5X EW %.5X-%.5X SR %.5X-%.5X",
+		kernel_nss_name, stext_pfn - 1, stext_pfn, eshared_pfn - 1,
+		eshared_pfn, end_pfn);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (INITRD_START && INITRD_SIZE) {
+		sinitrd_pfn = PFN_DOWN(__pa(INITRD_START));
+		einitrd_pfn = PFN_UP(__pa(INITRD_START + INITRD_SIZE));
+		min_size = einitrd_pfn << 2;
+		sprintf(defsys_cmd, "%s EW %.5X-%.5X", defsys_cmd,
+		sinitrd_pfn, einitrd_pfn);
+	}
+#endif
+
+	sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK", defsys_cmd, min_size);
+	sprintf(savesys_cmd, "SAVESYS %s \n IPL %s",
+		kernel_nss_name, kernel_nss_name);
+
+	__cpcmd(defsys_cmd, NULL, 0, &response);
+
+	if (response != 0)
+		return;
+
+	__cpcmd(savesys_cmd, NULL, 0, &response);
+
+	if (response != strlen(savesys_cmd))
+		return;
+
+	ipl_flags = IPL_NSS_VALID;
+}
+
+#else /* CONFIG_SHARED_KERNEL */
+
+static inline void create_kernel_nss(void) { }
+
+#endif /* CONFIG_SHARED_KERNEL */
+
+/*
+ * Clear bss memory
+ */
+static __init void clear_bss_section(void)
+{
+	memset(__bss_start, 0, _end - __bss_start);
+}
+
+/*
+ * Initialize storage key for kernel pages
+ */
+static __init void init_kernel_storage_key(void)
+{
+	unsigned long end_pfn, init_pfn;
+
+	end_pfn = PFN_UP(__pa(&_end));
+
+	for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
+		page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY);
+}
+
+static __init void detect_machine_type(void)
+{
+	struct cpuinfo_S390 *cpuinfo = &S390_lowcore.cpu_data;
+
+	asm volatile("stidp %0" : "=m" (S390_lowcore.cpu_data.cpu_id));
+
+	/* Running under z/VM ? */
+	if (cpuinfo->cpu_id.version == 0xff)
+		machine_flags |= 1;
+
+	/* Running on a P/390 ? */
+	if (cpuinfo->cpu_id.machine == 0x7490)
+		machine_flags |= 4;
+}
+
+/*
+ * Save ipl parameters, clear bss memory, initialize storage keys
+ * and create a kernel NSS at startup if the SAVESYS= parm is defined
+ */
+void __init startup_init(void)
+{
+	ipl_save_parameters();
+	clear_bss_section();
+	init_kernel_storage_key();
+	lockdep_init();
+	detect_machine_type();
+	create_kernel_nss();
+}
+
 #ifdef CONFIG_SMP
 void (*_machine_restart)(char *command) = machine_restart_smp;
 void (*_machine_halt)(void) = machine_halt_smp;
@@ -523,7 +659,7 @@
 static void __init
 setup_resources(void)
 {
-	struct resource *res;
+	struct resource *res, *sub_res;
 	int i;
 
 	code_resource.start = (unsigned long) &_text;
@@ -548,8 +684,38 @@
 		res->start = memory_chunk[i].addr;
 		res->end = memory_chunk[i].addr +  memory_chunk[i].size - 1;
 		request_resource(&iomem_resource, res);
-		request_resource(res, &code_resource);
-		request_resource(res, &data_resource);
+
+		if (code_resource.start >= res->start  &&
+			code_resource.start <= res->end &&
+			code_resource.end > res->end) {
+			sub_res = alloc_bootmem_low(sizeof(struct resource));
+			memcpy(sub_res, &code_resource,
+				sizeof(struct resource));
+			sub_res->end = res->end;
+			code_resource.start = res->end + 1;
+			request_resource(res, sub_res);
+		}
+
+		if (code_resource.start >= res->start &&
+			code_resource.start <= res->end &&
+			code_resource.end <= res->end)
+			request_resource(res, &code_resource);
+
+		if (data_resource.start >= res->start &&
+			data_resource.start <= res->end &&
+			data_resource.end > res->end) {
+			sub_res = alloc_bootmem_low(sizeof(struct resource));
+			memcpy(sub_res, &data_resource,
+				sizeof(struct resource));
+			sub_res->end = res->end;
+			data_resource.start = res->end + 1;
+			request_resource(res, sub_res);
+		}
+
+		if (data_resource.start >= res->start &&
+			data_resource.start <= res->end &&
+			data_resource.end <= res->end)
+			request_resource(res, &data_resource);
 	}
 }
 
@@ -585,7 +751,7 @@
 setup_memory(void)
 {
         unsigned long bootmap_size;
-	unsigned long start_pfn, end_pfn, init_pfn;
+	unsigned long start_pfn, end_pfn;
 	int i;
 
 	/*
@@ -595,10 +761,6 @@
 	start_pfn = PFN_UP(__pa(&_end));
 	end_pfn = max_pfn = PFN_DOWN(memory_end);
 
-	/* Initialize storage key for kernel pages */
-	for (init_pfn = 0 ; init_pfn < start_pfn; init_pfn++)
-		page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY);
-
 #ifdef CONFIG_BLK_DEV_INITRD
 	/*
 	 * Move the initrd in case the bitmap of the bootmem allocater