Subject:  NSS creation with initrd fails
From: Gerald Schaefer <geraldsc@de.ibm.com>
Git-commit: 27e7318c3e47e4fac71fcb472623434063ccc7a5
Patch-mainline: 3.1-rc4
References: bnc#713134

Symptom: When IPLing from a block device in order to create an NSS which
             includes an initrd this might fail with a message that indicates
             that different memory segment overlap.
Problem: By default zipl makes sure that the initrd will be loaded right
             behind the kernel image, if an IPL from a block device is issued.
             If a user wants to create an NSS which includes an initrd this can
             be a problem. When creating an NSS different types of page ranges
             must be in different segments. Since this is not necessarily given
             if the initrd is placed right behind the kernel, the request might
             fail.
Solution: Move the initrd to a higher address so there are no overlapping
             page ranges with different types within any segment.
Problem-id: 74207

Signed-off-by: Gerald Schaefer <geraldsc@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Acked-by: Torsten Duwe <duwe@suse.de>

---

 arch/s390/kernel/early.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -396,17 +396,19 @@ static __init void detect_machine_facili
 static __init void rescue_initrd(void)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
+	unsigned long min_initrd_addr = (unsigned long) _end + (4UL << 20);
 	/*
-	 * Move the initrd right behind the bss section in case it starts
-	 * within the bss section. So we don't overwrite it when the bss
-	 * section gets cleared.
+	 * Just like in case of IPL from VM reader we make sure there is a
+	 * gap of 4MB between end of kernel and start of initrd.
+	 * That way we can also be sure that saving an NSS will succeed,
+	 * which however only requires different segments.
 	 */
 	if (!INITRD_START || !INITRD_SIZE)
 		return;
-	if (INITRD_START >= (unsigned long) __bss_stop)
+	if (INITRD_START >= min_initrd_addr)
 		return;
-	memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE);
-	INITRD_START = (unsigned long) __bss_stop;
+	memmove((void *) min_initrd_addr, (void *) INITRD_START, INITRD_SIZE);
+	INITRD_START = min_initrd_addr;
 #endif
 }
 
