From 89175cf766869307c4f57a7a5f63d2819e76c41b Mon Sep 17 00:00:00 2001
From: Heiko Carstens <heiko.carstens@de.ibm.com>
Date: Wed, 11 Jan 2017 09:14:52 +0100
Subject: s390: provide sclp based boot console

Use the early sclp code to provide a boot console. This boot console
is available if the kernel parameter "earlyprintk" has been specified,
just like it works for other architectures that also provide an early
boot console.

This makes debugging of early problems much easier, since now we
finally have working console output even before memory detection is
running.

The boot console will be automatically disabled as soon as another
console will be registered.

Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
---
 arch/s390/include/asm/sclp.h | 1 +
 1 file changed, 1 insertion(+)

(limited to 'arch/s390/include/asm/sclp.h')

diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h
index 8db92a5b3bf1..415eaace3f1b 100644
--- a/arch/s390/include/asm/sclp.h
+++ b/arch/s390/include/asm/sclp.h
@@ -118,6 +118,7 @@ int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count);
 int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count);
 void sclp_early_detect(void);
 void _sclp_print_early(const char *);
+void __sclp_print_early(const char *s, unsigned int len);
 void sclp_ocf_cpc_name_copy(char *dst);
 
 static inline int sclp_get_core_info(struct sclp_core_info *info, int early)
-- 
cgit v1.2.3


From d5ab7a34f9bbad54f89b812e6b0d2d898f9433db Mon Sep 17 00:00:00 2001
From: Heiko Carstens <heiko.carstens@de.ibm.com>
Date: Tue, 24 Jan 2017 15:58:52 +0100
Subject: s390/sclp: make early sclp code readable

This patch

 - unifies the old sclp early code and the sclp early printk code, so
   they can use common functions

 - makes sure all sclp early functions and variables have the same
   "sclp_early" prefix

 - converts the sclp early printk code into readable code by using
   existing data structures instead of hard coded magic arrays

 - splits the early sclp code into two files: sclp_early.c and
   sclp_early_core.c. The core file contains everything that is
   required by the kernel decompressor and may not call functions not
   contained within the core file. Otherwise the result would be a
   link error.

 - changes interrupt handling to be completely synchronous. The old
   early sclp code had a small window which allowed to receive several
   interrupts instead of exactly the single expected interrupt. This
   did hide a subtle potential bug, which is fixed with this large
   rework.

 - contains a couple of small cleanups.

Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
---
 arch/s390/boot/compressed/misc.c    |   2 +-
 arch/s390/include/asm/sclp.h        |  13 +-
 arch/s390/kernel/als.c              |  10 +-
 arch/s390/kernel/early_printk.c     |   2 +-
 arch/s390/kernel/ipl.c              |   2 +-
 arch/s390/kernel/swsusp.S           |   2 +-
 drivers/s390/char/sclp.c            |  25 ----
 drivers/s390/char/sclp.h            |  34 ++++-
 drivers/s390/char/sclp_early.c      | 178 ++++++++---------------
 drivers/s390/char/sclp_early_core.c | 271 ++++++++++++++++++------------------
 10 files changed, 244 insertions(+), 295 deletions(-)

(limited to 'arch/s390/include/asm/sclp.h')

diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c
index 8515dd5a5663..fa95041fa9f6 100644
--- a/arch/s390/boot/compressed/misc.c
+++ b/arch/s390/boot/compressed/misc.c
@@ -66,7 +66,7 @@ static unsigned long free_mem_end_ptr;
 
 static int puts(const char *s)
 {
-	_sclp_print_early(s);
+	sclp_early_printk(s);
 	return 0;
 }
 
diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h
index 415eaace3f1b..ace3bd315438 100644
--- a/arch/s390/include/asm/sclp.h
+++ b/arch/s390/include/asm/sclp.h
@@ -101,7 +101,12 @@ struct zpci_report_error_header {
 	u8 data[0];	/* Subsequent Data passed verbatim to SCLP ET 24 */
 } __packed;
 
-int _sclp_get_core_info_early(struct sclp_core_info *info);
+int sclp_early_get_core_info(struct sclp_core_info *info);
+void sclp_early_get_ipl_info(struct sclp_ipl_info *info);
+void sclp_early_detect(void);
+void sclp_early_printk(const char *s);
+void __sclp_early_printk(const char *s, unsigned int len);
+
 int _sclp_get_core_info(struct sclp_core_info *info);
 int sclp_core_configure(u8 core);
 int sclp_core_deconfigure(u8 core);
@@ -110,21 +115,17 @@ int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
 int sclp_chp_configure(struct chp_id chpid);
 int sclp_chp_deconfigure(struct chp_id chpid);
 int sclp_chp_read_info(struct sclp_chp_info *info);
-void sclp_get_ipl_info(struct sclp_ipl_info *info);
 int sclp_pci_configure(u32 fid);
 int sclp_pci_deconfigure(u32 fid);
 int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid);
 int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count);
 int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count);
-void sclp_early_detect(void);
-void _sclp_print_early(const char *);
-void __sclp_print_early(const char *s, unsigned int len);
 void sclp_ocf_cpc_name_copy(char *dst);
 
 static inline int sclp_get_core_info(struct sclp_core_info *info, int early)
 {
 	if (early)
-		return _sclp_get_core_info_early(info);
+		return sclp_early_get_core_info(info);
 	return _sclp_get_core_info(info);
 }
 
diff --git a/arch/s390/kernel/als.c b/arch/s390/kernel/als.c
index a16e9d1bf9e3..0b3a06f05f90 100644
--- a/arch/s390/kernel/als.c
+++ b/arch/s390/kernel/als.c
@@ -41,7 +41,7 @@ static void __init print_machine_type(void)
 	get_cpu_id(&id);
 	u16_to_hex(type_str, id.machine);
 	strcat(mach_str, type_str);
-	_sclp_print_early(mach_str);
+	sclp_early_printk(mach_str);
 }
 
 static void __init u16_to_decimal(char *str, u16 val)
@@ -79,7 +79,7 @@ static void __init print_missing_facilities(void)
 			 * z/VM adds a four character prefix.
 			 */
 			if (strlen(als_str) > 70) {
-				_sclp_print_early(als_str);
+				sclp_early_printk(als_str);
 				*als_str = '\0';
 			}
 			u16_to_decimal(val_str, i * BITS_PER_LONG + j);
@@ -87,13 +87,13 @@ static void __init print_missing_facilities(void)
 			first = 0;
 		}
 	}
-	_sclp_print_early(als_str);
-	_sclp_print_early("See Principles of Operations for facility bits");
+	sclp_early_printk(als_str);
+	sclp_early_printk("See Principles of Operations for facility bits");
 }
 
 static void __init facility_mismatch(void)
 {
-	_sclp_print_early("The Linux kernel requires more recent processor hardware");
+	sclp_early_printk("The Linux kernel requires more recent processor hardware");
 	print_machine_type();
 	print_missing_facilities();
 	disabled_wait(0x8badcccc);
diff --git a/arch/s390/kernel/early_printk.c b/arch/s390/kernel/early_printk.c
index 54a4dc582b81..819cb15c67e8 100644
--- a/arch/s390/kernel/early_printk.c
+++ b/arch/s390/kernel/early_printk.c
@@ -9,7 +9,7 @@
 
 static void sclp_early_write(struct console *con, const char *s, unsigned int len)
 {
-	__sclp_print_early(s, len);
+	__sclp_early_printk(s, len);
 }
 
 static struct console sclp_early_console = {
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index 2c6ddced5394..5b91308f6117 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -1864,7 +1864,7 @@ static int __init s390_ipl_init(void)
 {
 	char str[8] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};
 
-	sclp_get_ipl_info(&sclp_ipl_info);
+	sclp_early_get_ipl_info(&sclp_ipl_info);
 	/*
 	 * Fix loadparm: There are systems where the (SCSI) LOADPARM
 	 * returned by read SCP info is invalid (contains EBCDIC blanks)
diff --git a/arch/s390/kernel/swsusp.S b/arch/s390/kernel/swsusp.S
index 1ff21f05d7dd..6c56fb89f553 100644
--- a/arch/s390/kernel/swsusp.S
+++ b/arch/s390/kernel/swsusp.S
@@ -196,7 +196,7 @@ pgm_check_entry:
 	larl	%r15,init_thread_union
 	ahi	%r15,1<<(PAGE_SHIFT+THREAD_SIZE_ORDER)
 	larl	%r2,.Lpanic_string
-	larl	%r3,_sclp_print_early
+	larl	%r3,sclp_early_printk
 	lghi	%r1,0
 	sam31
 	sigp	%r1,%r0,SIGP_SET_ARCHITECTURE
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index befc07acd3e0..9c471ea1b99c 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -140,31 +140,6 @@ static void __sclp_make_read_req(void);
 static int sclp_init_mask(int calculate);
 static int sclp_init(void);
 
-/* Perform service call. Return 0 on success, non-zero otherwise. */
-int
-sclp_service_call(sclp_cmdw_t command, void *sccb)
-{
-	int cc = 4; /* Initialize for program check handling */
-
-	asm volatile(
-		"0:	.insn	rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
-		"1:	ipm	%0\n"
-		"	srl	%0,28\n"
-		"2:\n"
-		EX_TABLE(0b, 2b)
-		EX_TABLE(1b, 2b)
-		: "+&d" (cc) : "d" (command), "a" (__pa(sccb))
-		: "cc", "memory");
-	if (cc == 4)
-		return -EINVAL;
-	if (cc == 3)
-		return -EIO;
-	if (cc == 2)
-		return -EBUSY;
-	return 0;
-}
-
-
 static void
 __sclp_queue_read_req(void)
 {
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 0c1fa376df9e..78d5f542d979 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -204,7 +204,6 @@ void sclp_unregister(struct sclp_register *reg);
 int sclp_remove_processed(struct sccb_header *sccb);
 int sclp_deactivate(void);
 int sclp_reactivate(void);
-int sclp_service_call(sclp_cmdw_t command, void *sccb);
 int sclp_sync_request(sclp_cmdw_t command, void *sccb);
 int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout);
 
@@ -222,8 +221,41 @@ extern int sclp_console_pages;
 extern int sclp_console_drop;
 extern unsigned long sclp_console_full;
 
+extern char sclp_early_sccb[PAGE_SIZE];
+
+void sclp_early_wait_irq(void);
+int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb);
+int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb);
+unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb);
+int sclp_early_set_event_mask(struct init_sccb *sccb,
+			      unsigned long receive_mask,
+			      unsigned long send_mask);
+
 /* useful inlines */
 
+/* Perform service call. Return 0 on success, non-zero otherwise. */
+static inline int sclp_service_call(sclp_cmdw_t command, void *sccb)
+{
+	int cc = 4; /* Initialize for program check handling */
+
+	asm volatile(
+		"0:	.insn	rre,0xb2200000,%1,%2\n"	 /* servc %1,%2 */
+		"1:	ipm	%0\n"
+		"	srl	%0,28\n"
+		"2:\n"
+		EX_TABLE(0b, 2b)
+		EX_TABLE(1b, 2b)
+		: "+&d" (cc) : "d" (command), "a" ((unsigned long)sccb)
+		: "cc", "memory");
+	if (cc == 4)
+		return -EINVAL;
+	if (cc == 3)
+		return -EIO;
+	if (cc == 2)
+		return -EBUSY;
+	return 0;
+}
+
 /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */
 /* translate single character from ASCII to EBCDIC */
 static inline unsigned char
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
index f8e46c22e641..2f9e50379e64 100644
--- a/drivers/s390/char/sclp_early.c
+++ b/drivers/s390/char/sclp_early.c
@@ -55,31 +55,12 @@ struct read_info_sccb {
 	u8	_pad_128[4096 - 128];	/* 128-4095 */
 } __packed __aligned(PAGE_SIZE);
 
-static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata;
 static struct sclp_ipl_info sclp_ipl_info;
 
 struct sclp_info sclp;
 EXPORT_SYMBOL(sclp);
 
-static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
-{
-	int rc;
-
-	__ctl_set_bit(0, 9);
-	rc = sclp_service_call(cmd, sccb);
-	if (rc)
-		goto out;
-	__load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA |
-			PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT);
-	local_irq_disable();
-out:
-	/* Contents of the sccb might have changed. */
-	barrier();
-	__ctl_clear_bit(0, 9);
-	return rc;
-}
-
-static int __init sclp_read_info_early(struct read_info_sccb *sccb)
+static int __init sclp_early_read_info(struct read_info_sccb *sccb)
 {
 	int rc, i;
 	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
@@ -91,7 +72,7 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb)
 			sccb->header.length = sizeof(*sccb);
 			sccb->header.function_code = 0x80;
 			sccb->header.control_mask[2] = 0x80;
-			rc = sclp_cmd_sync_early(commands[i], sccb);
+			rc = sclp_early_cmd_sync(commands[i], sccb);
 		} while (rc == -EBUSY);
 
 		if (rc)
@@ -104,12 +85,12 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb)
 	return -EIO;
 }
 
-static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
+static void __init sclp_early_facilities_detect(struct read_info_sccb *sccb)
 {
 	struct sclp_core_entry *cpue;
 	u16 boot_cpu_address, cpu;
 
-	if (sclp_read_info_early(sccb))
+	if (sclp_early_read_info(sccb))
 		return;
 
 	sclp.facilities = sccb->facilities;
@@ -172,59 +153,19 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
 }
 
 /*
- * This function will be called after sclp_facilities_detect(), which gets
- * called from early.c code. The sclp_facilities_detect() function retrieves
+ * This function will be called after sclp_early_facilities_detect(), which gets
+ * called from early.c code. The sclp_early_facilities_detect() function retrieves
  * and saves the IPL information.
  */
-void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
+void __init sclp_early_get_ipl_info(struct sclp_ipl_info *info)
 {
 	*info = sclp_ipl_info;
 }
 
-static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb)
-{
-	int rc;
+static struct sclp_core_info sclp_early_core_info __initdata;
+static int sclp_early_core_info_valid __initdata;
 
-	do {
-		rc = sclp_cmd_sync_early(cmd, sccb);
-	} while (rc == -EBUSY);
-
-	if (rc)
-		return -EIO;
-	if (((struct sccb_header *) sccb)->response_code != 0x0020)
-		return -EIO;
-	return 0;
-}
-
-static void __init sccb_init_eq_size(struct sdias_sccb *sccb)
-{
-	memset(sccb, 0, sizeof(*sccb));
-
-	sccb->hdr.length = sizeof(*sccb);
-	sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
-	sccb->evbuf.hdr.type = EVTYP_SDIAS;
-	sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
-	sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
-	sccb->evbuf.event_id = 4712;
-	sccb->evbuf.dbs = 1;
-}
-
-static int __init sclp_set_event_mask(struct init_sccb *sccb,
-				      unsigned long receive_mask,
-				      unsigned long send_mask)
-{
-	memset(sccb, 0, sizeof(*sccb));
-	sccb->header.length = sizeof(*sccb);
-	sccb->mask_length = sizeof(sccb_mask_t);
-	sccb->receive_mask = receive_mask;
-	sccb->send_mask = send_mask;
-	return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
-}
-
-static struct sclp_core_info sclp_core_info_early __initdata;
-static int sclp_core_info_early_valid __initdata;
-
-static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb)
+static void __init sclp_early_init_core_info(struct read_cpu_info_sccb *sccb)
 {
 	int rc;
 
@@ -233,80 +174,76 @@ static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb)
 	memset(sccb, 0, sizeof(*sccb));
 	sccb->header.length = sizeof(*sccb);
 	do {
-		rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb);
+		rc = sclp_early_cmd_sync(SCLP_CMDW_READ_CPU_INFO, sccb);
 	} while (rc == -EBUSY);
 	if (rc)
 		return;
 	if (sccb->header.response_code != 0x0010)
 		return;
-	sclp_fill_core_info(&sclp_core_info_early, sccb);
-	sclp_core_info_early_valid = 1;
+	sclp_fill_core_info(&sclp_early_core_info, sccb);
+	sclp_early_core_info_valid = 1;
 }
 
-int __init _sclp_get_core_info_early(struct sclp_core_info *info)
+int __init sclp_early_get_core_info(struct sclp_core_info *info)
 {
-	if (!sclp_core_info_early_valid)
+	if (!sclp_early_core_info_valid)
 		return -EIO;
-	*info = sclp_core_info_early;
+	*info = sclp_early_core_info;
 	return 0;
 }
 
-static long __init sclp_hsa_size_init(struct sdias_sccb *sccb)
+static long __init sclp_early_hsa_size_init(struct sdias_sccb *sccb)
 {
-	sccb_init_eq_size(sccb);
-	if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
+	memset(sccb, 0, sizeof(*sccb));
+	sccb->hdr.length = sizeof(*sccb);
+	sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
+	sccb->evbuf.hdr.type = EVTYP_SDIAS;
+	sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
+	sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
+	sccb->evbuf.event_id = 4712;
+	sccb->evbuf.dbs = 1;
+	if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
 		return -EIO;
 	if (sccb->evbuf.blk_cnt == 0)
 		return 0;
 	return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
 }
 
-static long __init sclp_hsa_copy_wait(struct sccb_header *sccb)
+static long __init sclp_early_hsa_copy_wait(struct sdias_sccb *sccb)
 {
 	memset(sccb, 0, PAGE_SIZE);
-	sccb->length = PAGE_SIZE;
-	if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb))
+	sccb->hdr.length = PAGE_SIZE;
+	if (sclp_early_cmd(SCLP_CMDW_READ_EVENT_DATA, sccb))
 		return -EIO;
-	if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0)
+	if (sccb->evbuf.blk_cnt == 0)
 		return 0;
-	return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
+	return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
 }
 
-static void __init sclp_hsa_size_detect(void *sccb)
+static void __init sclp_early_hsa_size_detect(void *sccb)
 {
-	long size;
+	unsigned long flags;
+	long size = -EIO;
 
-	/* First try synchronous interface (LPAR) */
-	if (sclp_set_event_mask(sccb, 0, 0x40000010))
-		return;
-	size = sclp_hsa_size_init(sccb);
-	if (size < 0)
-		return;
-	if (size != 0)
+	raw_local_irq_save(flags);
+	if (sclp_early_set_event_mask(sccb, EVTYP_SDIAS_MASK, EVTYP_SDIAS_MASK))
 		goto out;
-	/* Then try asynchronous interface (z/VM) */
-	if (sclp_set_event_mask(sccb, 0x00000010, 0x40000010))
-		return;
-	size = sclp_hsa_size_init(sccb);
-	if (size < 0)
-		return;
-	size = sclp_hsa_copy_wait(sccb);
-	if (size < 0)
-		return;
+	size = sclp_early_hsa_size_init(sccb);
+	/* First check for synchronous response (LPAR) */
+	if (size)
+		goto out_mask;
+	if (!(S390_lowcore.ext_params & 1))
+		sclp_early_wait_irq();
+	size = sclp_early_hsa_copy_wait(sccb);
+out_mask:
+	sclp_early_set_event_mask(sccb, 0, 0);
 out:
-	sclp.hsa_size = size;
-}
-
-static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb)
-{
-	if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK))
-		return 0;
-	if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
-		return 0;
-	return 1;
+	raw_local_irq_restore(flags);
+	if (size > 0)
+		sclp.hsa_size = size;
 }
 
-static void __init sclp_console_detect(struct init_sccb *sccb)
+static void __init sclp_early_console_detect(struct init_sccb *sccb)
 {
 	if (sccb->header.response_code != 0x20)
 		return;
@@ -314,21 +251,22 @@ static void __init sclp_console_detect(struct init_sccb *sccb)
 	if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
 		sclp.has_vt220 = 1;
 
-	if (sclp_con_check_linemode(sccb))
+	if (sclp_early_con_check_linemode(sccb))
 		sclp.has_linemode = 1;
 }
 
 void __init sclp_early_detect(void)
 {
-	void *sccb = &sccb_early;
+	void *sccb = &sclp_early_sccb;
 
-	sclp_facilities_detect(sccb);
-	sclp_init_core_info_early(sccb);
-	sclp_hsa_size_detect(sccb);
+	sclp_early_facilities_detect(sccb);
+	sclp_early_init_core_info(sccb);
+	sclp_early_hsa_size_detect(sccb);
 
-	/* Turn off SCLP event notifications.  Also save remote masks in the
+	/*
+	 * Turn off SCLP event notifications.  Also save remote masks in the
 	 * sccb.  These are sufficient to detect sclp console capabilities.
 	 */
-	sclp_set_event_mask(sccb, 0, 0);
-	sclp_console_detect(sccb);
+	sclp_early_set_event_mask(sccb, 0, 0);
+	sclp_early_console_detect(sccb);
 }
diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c
index 2723ab56fb8f..97f4c84d6635 100644
--- a/drivers/s390/char/sclp_early_core.c
+++ b/drivers/s390/char/sclp_early_core.c
@@ -2,29 +2,24 @@
  *    Copyright IBM Corp. 2015
  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
  */
+
 #include <linux/kernel.h>
+#include <asm/processor.h>
+#include <asm/lowcore.h>
 #include <asm/ebcdic.h>
 #include <asm/irq.h>
-#include <asm/lowcore.h>
-#include <asm/processor.h>
-#include <asm/sclp.h>
-
-#define EVTYP_VT220MSG_MASK	0x00000040
-#define EVTYP_MSG_MASK		0x40000000
-
-static char _sclp_work_area[4096] __aligned(PAGE_SIZE) __section(data);
-static bool have_vt220 __section(data);
-static bool have_linemode __section(data);
+#include "sclp.h"
+#include "sclp_rw.h"
 
+char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(data);
 int sclp_init_state __section(data) = sclp_init_state_uninitialized;
 
-static void _sclp_wait_int(void)
+void sclp_early_wait_irq(void)
 {
-	unsigned long psw_mask, addr, flags;
+	unsigned long psw_mask, addr;
 	psw_t psw_ext_save, psw_wait;
 	union ctlreg0 cr0, cr0_new;
 
-	raw_local_irq_save(flags);
 	__ctl_store(cr0.val, 0, 0);
 	cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
 	cr0_new.lap = 0;
@@ -53,165 +48,173 @@ static void _sclp_wait_int(void)
 
 	S390_lowcore.external_new_psw = psw_ext_save;
 	__ctl_load(cr0.val, 0, 0);
-	raw_local_irq_restore(flags);
 }
 
-static int _sclp_servc(unsigned int cmd, char *sccb)
+int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb)
 {
-	unsigned int cc;
+	unsigned long flags;
+	int rc;
 
-	do {
-		asm volatile(
-			"	.insn	rre,0xb2200000,%1,%2\n"
-			"	ipm	%0\n"
-			: "=d" (cc) : "d" (cmd), "a" (sccb)
-			: "cc", "memory");
-		cc >>= 28;
-		if (cc == 3)
-			return -EINVAL;
-		_sclp_wait_int();
-	} while (cc != 0);
-	return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO;
+	raw_local_irq_save(flags);
+	rc = sclp_service_call(cmd, sccb);
+	if (rc)
+		goto out;
+	sclp_early_wait_irq();
+out:
+	raw_local_irq_restore(flags);
+	return rc;
 }
 
-static int _sclp_setup(int disable)
+int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
 {
-	static unsigned char init_sccb[] = {
-		0x00, 0x1c,
-		0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
-		0x00, 0x04,
-		0x80, 0x00, 0x00, 0x00,	0x40, 0x00, 0x00, 0x40,
-		0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00
-	};
-	unsigned int *masks;
 	int rc;
 
-	memcpy(_sclp_work_area, init_sccb, 28);
-	masks = (unsigned int *)(_sclp_work_area + 12);
-	if (disable)
-		memset(masks, 0, 16);
-	/* SCLP write mask */
-	rc = _sclp_servc(0x00780005, _sclp_work_area);
+	do {
+		rc = sclp_early_cmd_sync(cmd, sccb);
+	} while (rc == -EBUSY);
 	if (rc)
-		return rc;
-	have_vt220 = masks[2] & EVTYP_VT220MSG_MASK;
-	have_linemode = masks[2] & EVTYP_MSG_MASK;
+		return -EIO;
+	if (((struct sccb_header *) sccb)->response_code != 0x0020)
+		return -EIO;
 	return 0;
 }
 
+struct write_sccb {
+	struct sccb_header header;
+	struct msg_buf msg;
+} __packed;
+
 /* Output multi-line text using SCLP Message interface. */
-static void _sclp_print_lm(const char *str, unsigned int len)
+static void sclp_early_print_lm(const char *str, unsigned int len)
 {
-	static unsigned char write_head[] = {
-		/* sccb header */
-		0x00, 0x52,					/* 0 */
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,		/* 2 */
-		/* evbuf */
-		0x00, 0x4a,					/* 8 */
-		0x02, 0x00, 0x00, 0x00,				/* 10 */
-		/* mdb */
-		0x00, 0x44,					/* 14 */
-		0x00, 0x01,					/* 16 */
-		0xd4, 0xc4, 0xc2, 0x40,				/* 18 */
-		0x00, 0x00, 0x00, 0x01,				/* 22 */
-		/* go */
-		0x00, 0x38,					/* 26 */
-		0x00, 0x01,					/* 28 */
-		0x00, 0x00, 0x00, 0x00,				/* 30 */
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 34 */
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 42 */
-		0x00, 0x00, 0x00, 0x00,				/* 50 */
-		0x00, 0x00,					/* 54 */
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 56 */
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 64 */
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 72 */
-		0x00, 0x00,					/* 80 */
-	};
-	static unsigned char write_mto[] = {
-		/* mto	*/
-		0x00, 0x0a,					/* 0 */
-		0x00, 0x04,					/* 2 */
-		0x10, 0x00,					/* 4 */
-		0x00, 0x00, 0x00, 0x00				/* 6 */
-	};
-	unsigned char *ptr, *end_ptr, ch;
-	unsigned int count, num;
-
-	num = 0;
-	memcpy(_sclp_work_area, write_head, sizeof(write_head));
-	ptr = _sclp_work_area + sizeof(write_head);
-	end_ptr = _sclp_work_area + sizeof(_sclp_work_area) - 1;
+	unsigned char *ptr, *end, ch;
+	unsigned int count, offset;
+	struct write_sccb *sccb;
+	struct msg_buf *msg;
+	struct mdb *mdb;
+	struct mto *mto;
+	struct go *go;
+
+	sccb = (struct write_sccb *) &sclp_early_sccb;
+	end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1;
+	memset(sccb, 0, sizeof(*sccb));
+	ptr = (unsigned char *) &sccb->msg.mdb.mto;
+	offset = 0;
 	do {
-		if (ptr + sizeof(write_mto) > end_ptr)
-			break;
-		memcpy(ptr, write_mto, sizeof(write_mto));
-		for (count = sizeof(write_mto); num < len; count++) {
-			num++;
-			ch = *str++;
-			if (ch == 0x0a)
-				break;
-			if (ptr > end_ptr)
+		for (count = sizeof(*mto); offset < len; count++) {
+			ch = str[offset++];
+			if ((ch == 0x0a) || (ptr + count > end))
 				break;
 			ptr[count] = _ascebc[ch];
 		}
-		/* Update length fields in mto, mdb, evbuf and sccb */
-		*(unsigned short *) ptr = count;
-		*(unsigned short *)(_sclp_work_area + 14) += count;
-		*(unsigned short *)(_sclp_work_area + 8) += count;
-		*(unsigned short *)(_sclp_work_area + 0) += count;
+		mto = (struct mto *) ptr;
+		memset(mto, 0, sizeof(*mto));
+		mto->length = count;
+		mto->type = 4;
+		mto->line_type_flags = LNTPFLGS_ENDTEXT;
 		ptr += count;
-	} while (num < len);
-
-	/* SCLP write data */
-	_sclp_servc(0x00760005, _sclp_work_area);
+	} while ((offset < len) && (ptr + sizeof(*mto) <= end));
+	len = ptr - (unsigned char *) sccb;
+	sccb->header.length = len - offsetof(struct write_sccb, header);
+	msg = &sccb->msg;
+	msg->header.type = EVTYP_MSG;
+	msg->header.length = len - offsetof(struct write_sccb, msg.header);
+	mdb = &msg->mdb;
+	mdb->header.type = 1;
+	mdb->header.tag = 0xD4C4C240;
+	mdb->header.revision_code = 1;
+	mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
+	go = &mdb->go;
+	go->length = sizeof(*go);
+	go->type = 1;
+	sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 }
 
+struct vt220_sccb {
+	struct sccb_header header;
+	struct {
+		struct evbuf_header header;
+		char data[];
+	} msg;
+} __packed;
+
 /* Output multi-line text (plus a newline) using SCLP VT220
  * interface.
  */
-static void _sclp_print_vt220(const char *str, unsigned int len)
+static void sclp_early_print_vt220(const char *str, unsigned int len)
+{
+	struct vt220_sccb *sccb;
+
+	sccb = (struct vt220_sccb *) &sclp_early_sccb;
+	if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb))
+		len = sizeof(sclp_early_sccb) - sizeof(*sccb) - 1;
+	memset(sccb, 0, sizeof(*sccb));
+	memcpy(&sccb->msg.data, str, len);
+	sccb->msg.data[len] = '\n';
+	sccb->header.length = sizeof(*sccb) + len + 1;
+	sccb->msg.header.length = sizeof(sccb->msg) + len + 1;
+	sccb->msg.header.type = EVTYP_VT220MSG;
+	sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
+}
+
+int sclp_early_set_event_mask(struct init_sccb *sccb,
+			      unsigned long receive_mask,
+			      unsigned long send_mask)
+{
+	memset(sccb, 0, sizeof(*sccb));
+	sccb->header.length = sizeof(*sccb);
+	sccb->mask_length = sizeof(sccb_mask_t);
+	sccb->receive_mask = receive_mask;
+	sccb->send_mask = send_mask;
+	return sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
+}
+
+unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
 {
-	static unsigned char const write_head[] = {
-		/* sccb header */
-		0x00, 0x0e,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		/* evbuf header */
-		0x00, 0x06,
-		0x1a, 0x00, 0x00, 0x00,
-	};
-
-	if (sizeof(write_head) + len >= sizeof(_sclp_work_area))
-		len = sizeof(_sclp_work_area) - sizeof(write_head) - 1;
-
-	memcpy(_sclp_work_area, write_head, sizeof(write_head));
-	memcpy(_sclp_work_area + sizeof(write_head), str, len);
-	_sclp_work_area[sizeof(write_head) + len] = '\n';
-
-	/* Update length fields in evbuf and sccb headers */
-	*(unsigned short *)(_sclp_work_area + 8) += len + 1;
-	*(unsigned short *)(_sclp_work_area + 0) += len + 1;
-
-	/* SCLP write data */
-	(void)_sclp_servc(0x00760005, _sclp_work_area);
+	if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK))
+		return 0;
+	if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
+		return 0;
+	return 1;
+}
+
+static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
+{
+	unsigned long receive_mask, send_mask;
+	struct init_sccb *sccb;
+	int rc;
+
+	*have_linemode = *have_vt220 = 0;
+	sccb = (struct init_sccb *) &sclp_early_sccb;
+	receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
+	send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
+	rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
+	if (rc)
+		return rc;
+	*have_linemode = sclp_early_con_check_linemode(sccb);
+	*have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK;
+	return rc;
 }
 
 /* Output one or more lines of text on the SCLP console (VT220 and /
  * or line-mode). All lines get terminated; no need for a trailing LF.
  */
-void __sclp_print_early(const char *str, unsigned int len)
+void __sclp_early_printk(const char *str, unsigned int len)
 {
+	int have_linemode, have_vt220;
+
 	if (sclp_init_state != sclp_init_state_uninitialized)
 		return;
-	if (_sclp_setup(0) != 0)
+	if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
 		return;
 	if (have_linemode)
-		_sclp_print_lm(str, len);
+		sclp_early_print_lm(str, len);
 	if (have_vt220)
-		_sclp_print_vt220(str, len);
-	_sclp_setup(1);
+		sclp_early_print_vt220(str, len);
+	sclp_early_setup(1, &have_linemode, &have_vt220);
 }
 
-void _sclp_print_early(const char *str)
+void sclp_early_printk(const char *str)
 {
-	__sclp_print_early(str, strlen(str));
+	__sclp_early_printk(str, strlen(str));
 }
-- 
cgit v1.2.3