1 /*
2  *    Copyright IBM Corp. 2015
3  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
4  */
5 #include <linux/kernel.h>
6 #include <asm/ebcdic.h>
7 #include <asm/irq.h>
8 #include <asm/lowcore.h>
9 #include <asm/processor.h>
10 #include <asm/sclp.h>
11 
12 static char _sclp_work_area[4096] __aligned(PAGE_SIZE);
13 
_sclp_wait_int(void)14 static void _sclp_wait_int(void)
15 {
16 	unsigned long cr0, cr0_new, psw_mask, addr;
17 	psw_t psw_ext_save, psw_wait;
18 
19 	__ctl_store(cr0, 0, 0);
20 	cr0_new = cr0 | 0x200;
21 	__ctl_load(cr0_new, 0, 0);
22 
23 	psw_ext_save = S390_lowcore.external_new_psw;
24 	psw_mask = __extract_psw();
25 	S390_lowcore.external_new_psw.mask = psw_mask;
26 	psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
27 	S390_lowcore.ext_int_code = 0;
28 
29 	do {
30 		asm volatile(
31 			"	larl	%[addr],0f\n"
32 			"	stg	%[addr],%[psw_wait_addr]\n"
33 			"	stg	%[addr],%[psw_ext_addr]\n"
34 			"	lpswe	%[psw_wait]\n"
35 			"0:\n"
36 			: [addr] "=&d" (addr),
37 			  [psw_wait_addr] "=Q" (psw_wait.addr),
38 			  [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
39 			: [psw_wait] "Q" (psw_wait)
40 			: "cc", "memory");
41 	} while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
42 
43 	__ctl_load(cr0, 0, 0);
44 	S390_lowcore.external_new_psw = psw_ext_save;
45 }
46 
_sclp_servc(unsigned int cmd,char * sccb)47 static int _sclp_servc(unsigned int cmd, char *sccb)
48 {
49 	unsigned int cc;
50 
51 	do {
52 		asm volatile(
53 			"	.insn	rre,0xb2200000,%1,%2\n"
54 			"	ipm	%0\n"
55 			: "=d" (cc) : "d" (cmd), "a" (sccb)
56 			: "cc", "memory");
57 		cc >>= 28;
58 		if (cc == 3)
59 			return -EINVAL;
60 		_sclp_wait_int();
61 	} while (cc != 0);
62 	return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO;
63 }
64 
_sclp_setup(int disable)65 static int _sclp_setup(int disable)
66 {
67 	static unsigned char init_sccb[] = {
68 		0x00, 0x1c,
69 		0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00,
70 		0x00, 0x04,
71 		0x80, 0x00, 0x00, 0x00,	0x40, 0x00, 0x00, 0x00,
72 		0x00, 0x00, 0x00, 0x00,	0x00, 0x00, 0x00, 0x00
73 	};
74 	unsigned int *masks;
75 	int rc;
76 
77 	memcpy(_sclp_work_area, init_sccb, 28);
78 	masks = (unsigned int *)(_sclp_work_area + 12);
79 	if (disable)
80 		memset(masks, 0, 16);
81 	/* SCLP write mask */
82 	rc = _sclp_servc(0x00780005, _sclp_work_area);
83 	if (rc)
84 		return rc;
85 	if ((masks[0] & masks[3]) != masks[0] ||
86 	    (masks[1] & masks[2]) != masks[1])
87 		return -EIO;
88 	return 0;
89 }
90 
_sclp_print(const char * str)91 static int _sclp_print(const char *str)
92 {
93 	static unsigned char write_head[] = {
94 		/* sccb header */
95 		0x00, 0x52,					/* 0 */
96 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,		/* 2 */
97 		/* evbuf */
98 		0x00, 0x4a,					/* 8 */
99 		0x02, 0x00, 0x00, 0x00,				/* 10 */
100 		/* mdb */
101 		0x00, 0x44,					/* 14 */
102 		0x00, 0x01,					/* 16 */
103 		0xd4, 0xc4, 0xc2, 0x40,				/* 18 */
104 		0x00, 0x00, 0x00, 0x01,				/* 22 */
105 		/* go */
106 		0x00, 0x38,					/* 26 */
107 		0x00, 0x01,					/* 28 */
108 		0x00, 0x00, 0x00, 0x00,				/* 30 */
109 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 34 */
110 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 42 */
111 		0x00, 0x00, 0x00, 0x00,				/* 50 */
112 		0x00, 0x00,					/* 54 */
113 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 56 */
114 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 64 */
115 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 72 */
116 		0x00, 0x00,					/* 80 */
117 	};
118 	static unsigned char write_mto[] = {
119 		/* mto	*/
120 		0x00, 0x0a,					/* 0 */
121 		0x00, 0x04,					/* 2 */
122 		0x10, 0x00,					/* 4 */
123 		0x00, 0x00, 0x00, 0x00				/* 6 */
124 	};
125 	unsigned char *ptr, ch;
126 	unsigned int count;
127 
128 	memcpy(_sclp_work_area, write_head, sizeof(write_head));
129 	ptr = _sclp_work_area + sizeof(write_head);
130 	do {
131 		memcpy(ptr, write_mto, sizeof(write_mto));
132 		for (count = sizeof(write_mto); (ch = *str++) != 0; count++) {
133 			if (ch == 0x0a)
134 				break;
135 			ptr[count] = _ascebc[ch];
136 		}
137 		/* Update length fields in mto, mdb, evbuf and sccb */
138 		*(unsigned short *) ptr = count;
139 		*(unsigned short *)(_sclp_work_area + 14) += count;
140 		*(unsigned short *)(_sclp_work_area + 8) += count;
141 		*(unsigned short *)(_sclp_work_area + 0) += count;
142 		ptr += count;
143 	} while (ch != 0);
144 
145 	/* SCLP write data */
146 	return _sclp_servc(0x00760005, _sclp_work_area);
147 }
148 
_sclp_print_early(const char * str)149 int _sclp_print_early(const char *str)
150 {
151 	int rc;
152 
153 	rc = _sclp_setup(0);
154 	if (rc)
155 		return rc;
156 	rc = _sclp_print(str);
157 	if (rc)
158 		return rc;
159 	return _sclp_setup(1);
160 }
161