1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright IBM Corp. 2015
4 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
7 #include <linux/kernel.h>
8 #include <asm/processor.h>
9 #include <asm/lowcore.h>
10 #include <asm/ebcdic.h>
15 char sclp_early_sccb
[PAGE_SIZE
] __aligned(PAGE_SIZE
) __section(.data
);
16 int sclp_init_state
__section(.data
) = sclp_init_state_uninitialized
;
18 * Used to keep track of the size of the event masks. Qemu until version 2.11
19 * only supports 4 and needs a workaround.
21 bool sclp_mask_compat_mode
__section(.data
);
23 void sclp_early_wait_irq(void)
25 unsigned long psw_mask
, addr
;
26 psw_t psw_ext_save
, psw_wait
;
27 union ctlreg0 cr0
, cr0_new
;
29 __ctl_store(cr0
.val
, 0, 0);
30 cr0_new
.val
= cr0
.val
& ~CR0_IRQ_SUBCLASS_MASK
;
33 __ctl_load(cr0_new
.val
, 0, 0);
35 psw_ext_save
= S390_lowcore
.external_new_psw
;
36 psw_mask
= __extract_psw();
37 S390_lowcore
.external_new_psw
.mask
= psw_mask
;
38 psw_wait
.mask
= psw_mask
| PSW_MASK_EXT
| PSW_MASK_WAIT
;
39 S390_lowcore
.ext_int_code
= 0;
44 " stg %[addr],%[psw_wait_addr]\n"
45 " stg %[addr],%[psw_ext_addr]\n"
46 " lpswe %[psw_wait]\n"
48 : [addr
] "=&d" (addr
),
49 [psw_wait_addr
] "=Q" (psw_wait
.addr
),
50 [psw_ext_addr
] "=Q" (S390_lowcore
.external_new_psw
.addr
)
51 : [psw_wait
] "Q" (psw_wait
)
53 } while (S390_lowcore
.ext_int_code
!= EXT_IRQ_SERVICE_SIG
);
55 S390_lowcore
.external_new_psw
= psw_ext_save
;
56 __ctl_load(cr0
.val
, 0, 0);
59 int sclp_early_cmd(sclp_cmdw_t cmd
, void *sccb
)
64 raw_local_irq_save(flags
);
65 rc
= sclp_service_call(cmd
, sccb
);
68 sclp_early_wait_irq();
70 raw_local_irq_restore(flags
);
75 struct sccb_header header
;
79 /* Output multi-line text using SCLP Message interface. */
80 static void sclp_early_print_lm(const char *str
, unsigned int len
)
82 unsigned char *ptr
, *end
, ch
;
83 unsigned int count
, offset
;
84 struct write_sccb
*sccb
;
90 sccb
= (struct write_sccb
*) &sclp_early_sccb
;
91 end
= (unsigned char *) sccb
+ sizeof(sclp_early_sccb
) - 1;
92 memset(sccb
, 0, sizeof(*sccb
));
93 ptr
= (unsigned char *) &sccb
->msg
.mdb
.mto
;
96 for (count
= sizeof(*mto
); offset
< len
; count
++) {
98 if ((ch
== 0x0a) || (ptr
+ count
> end
))
100 ptr
[count
] = _ascebc
[ch
];
102 mto
= (struct mto
*) ptr
;
103 memset(mto
, 0, sizeof(*mto
));
106 mto
->line_type_flags
= LNTPFLGS_ENDTEXT
;
108 } while ((offset
< len
) && (ptr
+ sizeof(*mto
) <= end
));
109 len
= ptr
- (unsigned char *) sccb
;
110 sccb
->header
.length
= len
- offsetof(struct write_sccb
, header
);
112 msg
->header
.type
= EVTYP_MSG
;
113 msg
->header
.length
= len
- offsetof(struct write_sccb
, msg
.header
);
115 mdb
->header
.type
= 1;
116 mdb
->header
.tag
= 0xD4C4C240;
117 mdb
->header
.revision_code
= 1;
118 mdb
->header
.length
= len
- offsetof(struct write_sccb
, msg
.mdb
.header
);
120 go
->length
= sizeof(*go
);
122 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA
, sccb
);
126 struct sccb_header header
;
128 struct evbuf_header header
;
133 /* Output multi-line text using SCLP VT220 interface. */
134 static void sclp_early_print_vt220(const char *str
, unsigned int len
)
136 struct vt220_sccb
*sccb
;
138 sccb
= (struct vt220_sccb
*) &sclp_early_sccb
;
139 if (sizeof(*sccb
) + len
>= sizeof(sclp_early_sccb
))
140 len
= sizeof(sclp_early_sccb
) - sizeof(*sccb
);
141 memset(sccb
, 0, sizeof(*sccb
));
142 memcpy(&sccb
->msg
.data
, str
, len
);
143 sccb
->header
.length
= sizeof(*sccb
) + len
;
144 sccb
->msg
.header
.length
= sizeof(sccb
->msg
) + len
;
145 sccb
->msg
.header
.type
= EVTYP_VT220MSG
;
146 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA
, sccb
);
149 int sclp_early_set_event_mask(struct init_sccb
*sccb
,
150 sccb_mask_t receive_mask
,
151 sccb_mask_t send_mask
)
154 memset(sccb
, 0, sizeof(*sccb
));
155 sccb
->header
.length
= sizeof(*sccb
);
156 if (sclp_mask_compat_mode
)
157 sccb
->mask_length
= SCLP_MASK_SIZE_COMPAT
;
159 sccb
->mask_length
= sizeof(sccb_mask_t
);
160 sccb_set_recv_mask(sccb
, receive_mask
);
161 sccb_set_send_mask(sccb
, send_mask
);
162 if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK
, sccb
))
164 if ((sccb
->header
.response_code
== 0x74f0) && !sclp_mask_compat_mode
) {
165 sclp_mask_compat_mode
= true;
168 if (sccb
->header
.response_code
!= 0x20)
173 unsigned int sclp_early_con_check_linemode(struct init_sccb
*sccb
)
175 if (!(sccb_get_sclp_send_mask(sccb
) & EVTYP_OPCMD_MASK
))
177 if (!(sccb_get_sclp_recv_mask(sccb
) & (EVTYP_MSG_MASK
| EVTYP_PMSGCMD_MASK
)))
182 unsigned int sclp_early_con_check_vt220(struct init_sccb
*sccb
)
184 if (sccb_get_sclp_send_mask(sccb
) & EVTYP_VT220MSG_MASK
)
189 static int sclp_early_setup(int disable
, int *have_linemode
, int *have_vt220
)
191 unsigned long receive_mask
, send_mask
;
192 struct init_sccb
*sccb
;
195 BUILD_BUG_ON(sizeof(struct init_sccb
) > PAGE_SIZE
);
197 *have_linemode
= *have_vt220
= 0;
198 sccb
= (struct init_sccb
*) &sclp_early_sccb
;
199 receive_mask
= disable
? 0 : EVTYP_OPCMD_MASK
;
200 send_mask
= disable
? 0 : EVTYP_VT220MSG_MASK
| EVTYP_MSG_MASK
;
201 rc
= sclp_early_set_event_mask(sccb
, receive_mask
, send_mask
);
204 *have_linemode
= sclp_early_con_check_linemode(sccb
);
205 *have_vt220
= !!(sccb_get_send_mask(sccb
) & EVTYP_VT220MSG_MASK
);
210 * Output one or more lines of text on the SCLP console (VT220 and /
213 void __sclp_early_printk(const char *str
, unsigned int len
)
215 int have_linemode
, have_vt220
;
217 if (sclp_init_state
!= sclp_init_state_uninitialized
)
219 if (sclp_early_setup(0, &have_linemode
, &have_vt220
) != 0)
222 sclp_early_print_lm(str
, len
);
224 sclp_early_print_vt220(str
, len
);
225 sclp_early_setup(1, &have_linemode
, &have_vt220
);
228 void sclp_early_printk(const char *str
)
230 __sclp_early_printk(str
, strlen(str
));