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>
17 static struct read_info_sccb
__bootdata(sclp_info_sccb
);
18 static int __bootdata(sclp_info_sccb_valid
);
19 char sclp_early_sccb
[PAGE_SIZE
] __aligned(PAGE_SIZE
) __section(.data
);
20 int sclp_init_state
__section(.data
) = sclp_init_state_uninitialized
;
22 * Used to keep track of the size of the event masks. Qemu until version 2.11
23 * only supports 4 and needs a workaround.
25 bool sclp_mask_compat_mode
__section(.data
);
27 void sclp_early_wait_irq(void)
29 unsigned long psw_mask
, addr
;
30 psw_t psw_ext_save
, psw_wait
;
31 union ctlreg0 cr0
, cr0_new
;
33 __ctl_store(cr0
.val
, 0, 0);
34 cr0_new
.val
= cr0
.val
& ~CR0_IRQ_SUBCLASS_MASK
;
37 __ctl_load(cr0_new
.val
, 0, 0);
39 psw_ext_save
= S390_lowcore
.external_new_psw
;
40 psw_mask
= __extract_psw();
41 S390_lowcore
.external_new_psw
.mask
= psw_mask
;
42 psw_wait
.mask
= psw_mask
| PSW_MASK_EXT
| PSW_MASK_WAIT
;
43 S390_lowcore
.ext_int_code
= 0;
48 " stg %[addr],%[psw_wait_addr]\n"
49 " stg %[addr],%[psw_ext_addr]\n"
50 " lpswe %[psw_wait]\n"
52 : [addr
] "=&d" (addr
),
53 [psw_wait_addr
] "=Q" (psw_wait
.addr
),
54 [psw_ext_addr
] "=Q" (S390_lowcore
.external_new_psw
.addr
)
55 : [psw_wait
] "Q" (psw_wait
)
57 } while (S390_lowcore
.ext_int_code
!= EXT_IRQ_SERVICE_SIG
);
59 S390_lowcore
.external_new_psw
= psw_ext_save
;
60 __ctl_load(cr0
.val
, 0, 0);
63 int sclp_early_cmd(sclp_cmdw_t cmd
, void *sccb
)
68 raw_local_irq_save(flags
);
69 rc
= sclp_service_call(cmd
, sccb
);
72 sclp_early_wait_irq();
74 raw_local_irq_restore(flags
);
79 struct sccb_header header
;
83 /* Output multi-line text using SCLP Message interface. */
84 static void sclp_early_print_lm(const char *str
, unsigned int len
)
86 unsigned char *ptr
, *end
, ch
;
87 unsigned int count
, offset
;
88 struct write_sccb
*sccb
;
94 sccb
= (struct write_sccb
*) &sclp_early_sccb
;
95 end
= (unsigned char *) sccb
+ sizeof(sclp_early_sccb
) - 1;
96 memset(sccb
, 0, sizeof(*sccb
));
97 ptr
= (unsigned char *) &sccb
->msg
.mdb
.mto
;
100 for (count
= sizeof(*mto
); offset
< len
; count
++) {
102 if ((ch
== 0x0a) || (ptr
+ count
> end
))
104 ptr
[count
] = _ascebc
[ch
];
106 mto
= (struct mto
*) ptr
;
107 memset(mto
, 0, sizeof(*mto
));
110 mto
->line_type_flags
= LNTPFLGS_ENDTEXT
;
112 } while ((offset
< len
) && (ptr
+ sizeof(*mto
) <= end
));
113 len
= ptr
- (unsigned char *) sccb
;
114 sccb
->header
.length
= len
- offsetof(struct write_sccb
, header
);
116 msg
->header
.type
= EVTYP_MSG
;
117 msg
->header
.length
= len
- offsetof(struct write_sccb
, msg
.header
);
119 mdb
->header
.type
= 1;
120 mdb
->header
.tag
= 0xD4C4C240;
121 mdb
->header
.revision_code
= 1;
122 mdb
->header
.length
= len
- offsetof(struct write_sccb
, msg
.mdb
.header
);
124 go
->length
= sizeof(*go
);
126 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA
, sccb
);
130 struct sccb_header header
;
132 struct evbuf_header header
;
137 /* Output multi-line text using SCLP VT220 interface. */
138 static void sclp_early_print_vt220(const char *str
, unsigned int len
)
140 struct vt220_sccb
*sccb
;
142 sccb
= (struct vt220_sccb
*) &sclp_early_sccb
;
143 if (sizeof(*sccb
) + len
>= sizeof(sclp_early_sccb
))
144 len
= sizeof(sclp_early_sccb
) - sizeof(*sccb
);
145 memset(sccb
, 0, sizeof(*sccb
));
146 memcpy(&sccb
->msg
.data
, str
, len
);
147 sccb
->header
.length
= sizeof(*sccb
) + len
;
148 sccb
->msg
.header
.length
= sizeof(sccb
->msg
) + len
;
149 sccb
->msg
.header
.type
= EVTYP_VT220MSG
;
150 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA
, sccb
);
153 int sclp_early_set_event_mask(struct init_sccb
*sccb
,
154 sccb_mask_t receive_mask
,
155 sccb_mask_t send_mask
)
158 memset(sccb
, 0, sizeof(*sccb
));
159 sccb
->header
.length
= sizeof(*sccb
);
160 if (sclp_mask_compat_mode
)
161 sccb
->mask_length
= SCLP_MASK_SIZE_COMPAT
;
163 sccb
->mask_length
= sizeof(sccb_mask_t
);
164 sccb_set_recv_mask(sccb
, receive_mask
);
165 sccb_set_send_mask(sccb
, send_mask
);
166 if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK
, sccb
))
168 if ((sccb
->header
.response_code
== 0x74f0) && !sclp_mask_compat_mode
) {
169 sclp_mask_compat_mode
= true;
172 if (sccb
->header
.response_code
!= 0x20)
177 unsigned int sclp_early_con_check_linemode(struct init_sccb
*sccb
)
179 if (!(sccb_get_sclp_send_mask(sccb
) & EVTYP_OPCMD_MASK
))
181 if (!(sccb_get_sclp_recv_mask(sccb
) & (EVTYP_MSG_MASK
| EVTYP_PMSGCMD_MASK
)))
186 unsigned int sclp_early_con_check_vt220(struct init_sccb
*sccb
)
188 if (sccb_get_sclp_send_mask(sccb
) & EVTYP_VT220MSG_MASK
)
193 static int sclp_early_setup(int disable
, int *have_linemode
, int *have_vt220
)
195 unsigned long receive_mask
, send_mask
;
196 struct init_sccb
*sccb
;
199 BUILD_BUG_ON(sizeof(struct init_sccb
) > PAGE_SIZE
);
201 *have_linemode
= *have_vt220
= 0;
202 sccb
= (struct init_sccb
*) &sclp_early_sccb
;
203 receive_mask
= disable
? 0 : EVTYP_OPCMD_MASK
;
204 send_mask
= disable
? 0 : EVTYP_VT220MSG_MASK
| EVTYP_MSG_MASK
;
205 rc
= sclp_early_set_event_mask(sccb
, receive_mask
, send_mask
);
208 *have_linemode
= sclp_early_con_check_linemode(sccb
);
209 *have_vt220
= !!(sccb_get_send_mask(sccb
) & EVTYP_VT220MSG_MASK
);
214 * Output one or more lines of text on the SCLP console (VT220 and /
217 void __sclp_early_printk(const char *str
, unsigned int len
, unsigned int force
)
219 int have_linemode
, have_vt220
;
221 if (!force
&& sclp_init_state
!= sclp_init_state_uninitialized
)
223 if (sclp_early_setup(0, &have_linemode
, &have_vt220
) != 0)
226 sclp_early_print_lm(str
, len
);
228 sclp_early_print_vt220(str
, len
);
229 sclp_early_setup(1, &have_linemode
, &have_vt220
);
232 void sclp_early_printk(const char *str
)
234 __sclp_early_printk(str
, strlen(str
), 0);
237 void sclp_early_printk_force(const char *str
)
239 __sclp_early_printk(str
, strlen(str
), 1);
242 int __init
sclp_early_read_info(void)
245 struct read_info_sccb
*sccb
= &sclp_info_sccb
;
246 sclp_cmdw_t commands
[] = {SCLP_CMDW_READ_SCP_INFO_FORCED
,
247 SCLP_CMDW_READ_SCP_INFO
};
249 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
250 memset(sccb
, 0, sizeof(*sccb
));
251 sccb
->header
.length
= sizeof(*sccb
);
252 sccb
->header
.function_code
= 0x80;
253 sccb
->header
.control_mask
[2] = 0x80;
254 if (sclp_early_cmd(commands
[i
], sccb
))
256 if (sccb
->header
.response_code
== 0x10) {
257 sclp_info_sccb_valid
= 1;
260 if (sccb
->header
.response_code
!= 0x1f0)
266 int __init
sclp_early_get_info(struct read_info_sccb
*info
)
268 if (!sclp_info_sccb_valid
)
271 *info
= sclp_info_sccb
;
275 int __init
sclp_early_get_memsize(unsigned long *mem
)
278 unsigned long rnsize
;
279 struct read_info_sccb
*sccb
= &sclp_info_sccb
;
281 if (!sclp_info_sccb_valid
)
284 rnmax
= sccb
->rnmax
? sccb
->rnmax
: sccb
->rnmax2
;
285 rnsize
= sccb
->rnsize
? sccb
->rnsize
: sccb
->rnsize2
;
287 *mem
= rnsize
* rnmax
;
291 int __init
sclp_early_get_hsa_size(unsigned long *hsa_size
)
293 if (!sclp_info_sccb_valid
)
297 if (sclp_info_sccb
.hsa_size
)
298 *hsa_size
= (sclp_info_sccb
.hsa_size
- 1) * PAGE_SIZE
;
302 #define SCLP_STORAGE_INFO_FACILITY 0x0000400000000000UL
304 void __weak __init
add_mem_detect_block(u64 start
, u64 end
) {}
305 int __init
sclp_early_read_storage_info(void)
307 struct read_storage_sccb
*sccb
= (struct read_storage_sccb
*)&sclp_early_sccb
;
308 int rc
, id
, max_id
= 0;
309 unsigned long rn
, rzm
;
313 if (!sclp_info_sccb_valid
)
316 if (!(sclp_info_sccb
.facilities
& SCLP_STORAGE_INFO_FACILITY
))
319 rzm
= sclp_info_sccb
.rnsize
?: sclp_info_sccb
.rnsize2
;
322 for (id
= 0; id
<= max_id
; id
++) {
323 memset(sclp_early_sccb
, 0, sizeof(sclp_early_sccb
));
324 sccb
->header
.length
= sizeof(sclp_early_sccb
);
325 command
= SCLP_CMDW_READ_STORAGE_INFO
| (id
<< 8);
326 rc
= sclp_early_cmd(command
, sccb
);
330 max_id
= sccb
->max_id
;
331 switch (sccb
->header
.response_code
) {
333 for (sn
= 0; sn
< sccb
->assigned
; sn
++) {
334 if (!sccb
->entries
[sn
])
336 rn
= sccb
->entries
[sn
] >> 16;
337 add_mem_detect_block((rn
- 1) * rzm
, rn
* rzm
);
350 mem_detect
.count
= 0;