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>
12 #include <asm/sections.h>
13 #include <asm/mem_detect.h>
14 #include <asm/facility.h>
18 static struct read_info_sccb
__bootdata(sclp_info_sccb
);
19 static int __bootdata(sclp_info_sccb_valid
);
20 char *sclp_early_sccb
= (char *) EARLY_SCCB_OFFSET
;
21 int sclp_init_state
= sclp_init_state_uninitialized
;
23 * Used to keep track of the size of the event masks. Qemu until version 2.11
24 * only supports 4 and needs a workaround.
26 bool sclp_mask_compat_mode
;
28 void sclp_early_wait_irq(void)
30 unsigned long psw_mask
, addr
;
31 psw_t psw_ext_save
, psw_wait
;
32 union ctlreg0 cr0
, cr0_new
;
34 __ctl_store(cr0
.val
, 0, 0);
35 cr0_new
.val
= cr0
.val
& ~CR0_IRQ_SUBCLASS_MASK
;
38 __ctl_load(cr0_new
.val
, 0, 0);
40 psw_ext_save
= S390_lowcore
.external_new_psw
;
41 psw_mask
= __extract_psw();
42 S390_lowcore
.external_new_psw
.mask
= psw_mask
;
43 psw_wait
.mask
= psw_mask
| PSW_MASK_EXT
| PSW_MASK_WAIT
;
44 S390_lowcore
.ext_int_code
= 0;
49 " stg %[addr],%[psw_wait_addr]\n"
50 " stg %[addr],%[psw_ext_addr]\n"
51 " lpswe %[psw_wait]\n"
53 : [addr
] "=&d" (addr
),
54 [psw_wait_addr
] "=Q" (psw_wait
.addr
),
55 [psw_ext_addr
] "=Q" (S390_lowcore
.external_new_psw
.addr
)
56 : [psw_wait
] "Q" (psw_wait
)
58 } while (S390_lowcore
.ext_int_code
!= EXT_IRQ_SERVICE_SIG
);
60 S390_lowcore
.external_new_psw
= psw_ext_save
;
61 __ctl_load(cr0
.val
, 0, 0);
64 int sclp_early_cmd(sclp_cmdw_t cmd
, void *sccb
)
69 raw_local_irq_save(flags
);
70 rc
= sclp_service_call(cmd
, sccb
);
73 sclp_early_wait_irq();
75 raw_local_irq_restore(flags
);
80 struct sccb_header header
;
84 /* Output multi-line text using SCLP Message interface. */
85 static void sclp_early_print_lm(const char *str
, unsigned int len
)
87 unsigned char *ptr
, *end
, ch
;
88 unsigned int count
, offset
;
89 struct write_sccb
*sccb
;
95 sccb
= (struct write_sccb
*) sclp_early_sccb
;
96 end
= (unsigned char *) sccb
+ EARLY_SCCB_SIZE
- 1;
97 memset(sccb
, 0, sizeof(*sccb
));
98 ptr
= (unsigned char *) &sccb
->msg
.mdb
.mto
;
101 for (count
= sizeof(*mto
); offset
< len
; count
++) {
103 if ((ch
== 0x0a) || (ptr
+ count
> end
))
105 ptr
[count
] = _ascebc
[ch
];
107 mto
= (struct mto
*) ptr
;
108 memset(mto
, 0, sizeof(*mto
));
111 mto
->line_type_flags
= LNTPFLGS_ENDTEXT
;
113 } while ((offset
< len
) && (ptr
+ sizeof(*mto
) <= end
));
114 len
= ptr
- (unsigned char *) sccb
;
115 sccb
->header
.length
= len
- offsetof(struct write_sccb
, header
);
117 msg
->header
.type
= EVTYP_MSG
;
118 msg
->header
.length
= len
- offsetof(struct write_sccb
, msg
.header
);
120 mdb
->header
.type
= 1;
121 mdb
->header
.tag
= 0xD4C4C240;
122 mdb
->header
.revision_code
= 1;
123 mdb
->header
.length
= len
- offsetof(struct write_sccb
, msg
.mdb
.header
);
125 go
->length
= sizeof(*go
);
127 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA
, sccb
);
131 struct sccb_header header
;
133 struct evbuf_header header
;
138 /* Output multi-line text using SCLP VT220 interface. */
139 static void sclp_early_print_vt220(const char *str
, unsigned int len
)
141 struct vt220_sccb
*sccb
;
143 sccb
= (struct vt220_sccb
*) sclp_early_sccb
;
144 if (sizeof(*sccb
) + len
>= EARLY_SCCB_SIZE
)
145 len
= EARLY_SCCB_SIZE
- sizeof(*sccb
);
146 memset(sccb
, 0, sizeof(*sccb
));
147 memcpy(&sccb
->msg
.data
, str
, len
);
148 sccb
->header
.length
= sizeof(*sccb
) + len
;
149 sccb
->msg
.header
.length
= sizeof(sccb
->msg
) + len
;
150 sccb
->msg
.header
.type
= EVTYP_VT220MSG
;
151 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA
, sccb
);
154 int sclp_early_set_event_mask(struct init_sccb
*sccb
,
155 sccb_mask_t receive_mask
,
156 sccb_mask_t send_mask
)
159 memset(sccb
, 0, sizeof(*sccb
));
160 sccb
->header
.length
= sizeof(*sccb
);
161 if (sclp_mask_compat_mode
)
162 sccb
->mask_length
= SCLP_MASK_SIZE_COMPAT
;
164 sccb
->mask_length
= sizeof(sccb_mask_t
);
165 sccb_set_recv_mask(sccb
, receive_mask
);
166 sccb_set_send_mask(sccb
, send_mask
);
167 if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK
, sccb
))
169 if ((sccb
->header
.response_code
== 0x74f0) && !sclp_mask_compat_mode
) {
170 sclp_mask_compat_mode
= true;
173 if (sccb
->header
.response_code
!= 0x20)
178 unsigned int sclp_early_con_check_linemode(struct init_sccb
*sccb
)
180 if (!(sccb_get_sclp_send_mask(sccb
) & EVTYP_OPCMD_MASK
))
182 if (!(sccb_get_sclp_recv_mask(sccb
) & (EVTYP_MSG_MASK
| EVTYP_PMSGCMD_MASK
)))
187 unsigned int sclp_early_con_check_vt220(struct init_sccb
*sccb
)
189 if (sccb_get_sclp_send_mask(sccb
) & EVTYP_VT220MSG_MASK
)
194 static int sclp_early_setup(int disable
, int *have_linemode
, int *have_vt220
)
196 unsigned long receive_mask
, send_mask
;
197 struct init_sccb
*sccb
;
200 BUILD_BUG_ON(sizeof(struct init_sccb
) > PAGE_SIZE
);
202 *have_linemode
= *have_vt220
= 0;
203 sccb
= (struct init_sccb
*) sclp_early_sccb
;
204 receive_mask
= disable
? 0 : EVTYP_OPCMD_MASK
;
205 send_mask
= disable
? 0 : EVTYP_VT220MSG_MASK
| EVTYP_MSG_MASK
;
206 rc
= sclp_early_set_event_mask(sccb
, receive_mask
, send_mask
);
209 *have_linemode
= sclp_early_con_check_linemode(sccb
);
210 *have_vt220
= !!(sccb_get_send_mask(sccb
) & EVTYP_VT220MSG_MASK
);
215 * Output one or more lines of text on the SCLP console (VT220 and /
218 void __sclp_early_printk(const char *str
, unsigned int len
)
220 int have_linemode
, have_vt220
;
222 if (sclp_init_state
!= sclp_init_state_uninitialized
)
224 if (sclp_early_setup(0, &have_linemode
, &have_vt220
) != 0)
227 sclp_early_print_lm(str
, len
);
229 sclp_early_print_vt220(str
, len
);
230 sclp_early_setup(1, &have_linemode
, &have_vt220
);
233 void sclp_early_printk(const char *str
)
235 __sclp_early_printk(str
, strlen(str
));
238 int __init
sclp_early_read_info(void)
241 int length
= test_facility(140) ? EXT_SCCB_READ_SCP
: PAGE_SIZE
;
242 struct read_info_sccb
*sccb
= &sclp_info_sccb
;
243 sclp_cmdw_t commands
[] = {SCLP_CMDW_READ_SCP_INFO_FORCED
,
244 SCLP_CMDW_READ_SCP_INFO
};
246 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
247 memset(sccb
, 0, length
);
248 sccb
->header
.length
= length
;
249 sccb
->header
.function_code
= 0x80;
250 sccb
->header
.control_mask
[2] = 0x80;
251 if (sclp_early_cmd(commands
[i
], sccb
))
253 if (sccb
->header
.response_code
== 0x10) {
254 sclp_info_sccb_valid
= 1;
257 if (sccb
->header
.response_code
!= 0x1f0)
263 struct read_info_sccb
* __init
sclp_early_get_info(void)
265 if (!sclp_info_sccb_valid
)
268 return &sclp_info_sccb
;
271 int __init
sclp_early_get_memsize(unsigned long *mem
)
274 unsigned long rnsize
;
275 struct read_info_sccb
*sccb
= &sclp_info_sccb
;
277 if (!sclp_info_sccb_valid
)
280 rnmax
= sccb
->rnmax
? sccb
->rnmax
: sccb
->rnmax2
;
281 rnsize
= sccb
->rnsize
? sccb
->rnsize
: sccb
->rnsize2
;
283 *mem
= rnsize
* rnmax
;
287 int __init
sclp_early_get_hsa_size(unsigned long *hsa_size
)
289 if (!sclp_info_sccb_valid
)
293 if (sclp_info_sccb
.hsa_size
)
294 *hsa_size
= (sclp_info_sccb
.hsa_size
- 1) * PAGE_SIZE
;
298 #define SCLP_STORAGE_INFO_FACILITY 0x0000400000000000UL
300 void __weak __init
add_mem_detect_block(u64 start
, u64 end
) {}
301 int __init
sclp_early_read_storage_info(void)
303 struct read_storage_sccb
*sccb
= (struct read_storage_sccb
*)sclp_early_sccb
;
304 int rc
, id
, max_id
= 0;
305 unsigned long rn
, rzm
;
309 if (!sclp_info_sccb_valid
)
312 if (!(sclp_info_sccb
.facilities
& SCLP_STORAGE_INFO_FACILITY
))
315 rzm
= sclp_info_sccb
.rnsize
?: sclp_info_sccb
.rnsize2
;
318 for (id
= 0; id
<= max_id
; id
++) {
319 memset(sclp_early_sccb
, 0, EARLY_SCCB_SIZE
);
320 sccb
->header
.length
= EARLY_SCCB_SIZE
;
321 command
= SCLP_CMDW_READ_STORAGE_INFO
| (id
<< 8);
322 rc
= sclp_early_cmd(command
, sccb
);
326 max_id
= sccb
->max_id
;
327 switch (sccb
->header
.response_code
) {
329 for (sn
= 0; sn
< sccb
->assigned
; sn
++) {
330 if (!sccb
->entries
[sn
])
332 rn
= sccb
->entries
[sn
] >> 16;
333 add_mem_detect_block((rn
- 1) * rzm
, rn
* rzm
);
346 mem_detect
.count
= 0;