1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * SCLP line mode console driver |
4 | * |
5 | * Copyright IBM Corp. 1999, 2009 |
6 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
8 | */ |
9 | |
10 | #include <linux/kmod.h> |
11 | #include <linux/console.h> |
12 | #include <linux/init.h> |
13 | #include <linux/panic_notifier.h> |
14 | #include <linux/timer.h> |
15 | #include <linux/jiffies.h> |
16 | #include <linux/termios.h> |
17 | #include <linux/err.h> |
18 | #include <linux/reboot.h> |
19 | #include <linux/gfp.h> |
20 | |
21 | #include "sclp.h" |
22 | #include "sclp_rw.h" |
23 | #include "sclp_tty.h" |
24 | |
25 | #define sclp_console_major 4 /* TTYAUX_MAJOR */ |
26 | #define sclp_console_minor 64 |
27 | #define sclp_console_name "ttyS" |
28 | |
29 | /* Lock to guard over changes to global variables */ |
30 | static DEFINE_SPINLOCK(sclp_con_lock); |
31 | /* List of free pages that can be used for console output buffering */ |
32 | static LIST_HEAD(sclp_con_pages); |
33 | /* List of full struct sclp_buffer structures ready for output */ |
34 | static LIST_HEAD(sclp_con_outqueue); |
35 | /* Pointer to current console buffer */ |
36 | static struct sclp_buffer *sclp_conbuf; |
37 | /* Timer for delayed output of console messages */ |
38 | static struct timer_list sclp_con_timer; |
39 | /* Flag that output queue is currently running */ |
40 | static int sclp_con_queue_running; |
41 | |
42 | /* Output format for console messages */ |
43 | #define SCLP_CON_COLUMNS 320 |
44 | #define SPACES_PER_TAB 8 |
45 | |
46 | static void |
47 | sclp_conbuf_callback(struct sclp_buffer *buffer, int rc) |
48 | { |
49 | unsigned long flags; |
50 | void *page; |
51 | |
52 | do { |
53 | page = sclp_unmake_buffer(buffer); |
54 | spin_lock_irqsave(&sclp_con_lock, flags); |
55 | |
56 | /* Remove buffer from outqueue */ |
57 | list_del(entry: &buffer->list); |
58 | list_add_tail(new: (struct list_head *) page, head: &sclp_con_pages); |
59 | |
60 | /* Check if there is a pending buffer on the out queue. */ |
61 | buffer = NULL; |
62 | if (!list_empty(head: &sclp_con_outqueue)) |
63 | buffer = list_first_entry(&sclp_con_outqueue, |
64 | struct sclp_buffer, list); |
65 | if (!buffer) { |
66 | sclp_con_queue_running = 0; |
67 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
68 | break; |
69 | } |
70 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
71 | } while (sclp_emit_buffer(buffer, sclp_conbuf_callback)); |
72 | } |
73 | |
74 | /* |
75 | * Finalize and emit first pending buffer. |
76 | */ |
77 | static void sclp_conbuf_emit(void) |
78 | { |
79 | struct sclp_buffer* buffer; |
80 | unsigned long flags; |
81 | int rc; |
82 | |
83 | spin_lock_irqsave(&sclp_con_lock, flags); |
84 | if (sclp_conbuf) |
85 | list_add_tail(new: &sclp_conbuf->list, head: &sclp_con_outqueue); |
86 | sclp_conbuf = NULL; |
87 | if (sclp_con_queue_running) |
88 | goto out_unlock; |
89 | if (list_empty(head: &sclp_con_outqueue)) |
90 | goto out_unlock; |
91 | buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer, |
92 | list); |
93 | sclp_con_queue_running = 1; |
94 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
95 | |
96 | rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); |
97 | if (rc) |
98 | sclp_conbuf_callback(buffer, rc); |
99 | return; |
100 | out_unlock: |
101 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
102 | } |
103 | |
104 | /* |
105 | * Wait until out queue is empty |
106 | */ |
107 | static void sclp_console_sync_queue(void) |
108 | { |
109 | unsigned long flags; |
110 | |
111 | spin_lock_irqsave(&sclp_con_lock, flags); |
112 | del_timer(timer: &sclp_con_timer); |
113 | while (sclp_con_queue_running) { |
114 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
115 | sclp_sync_wait(); |
116 | spin_lock_irqsave(&sclp_con_lock, flags); |
117 | } |
118 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
119 | } |
120 | |
121 | /* |
122 | * When this routine is called from the timer then we flush the |
123 | * temporary write buffer without further waiting on a final new line. |
124 | */ |
125 | static void |
126 | sclp_console_timeout(struct timer_list *unused) |
127 | { |
128 | sclp_conbuf_emit(); |
129 | } |
130 | |
131 | /* |
132 | * Drop oldest console buffer if sclp_con_drop is set |
133 | */ |
134 | static int |
135 | sclp_console_drop_buffer(void) |
136 | { |
137 | struct list_head *list; |
138 | struct sclp_buffer *buffer; |
139 | void *page; |
140 | |
141 | if (!sclp_console_drop) |
142 | return 0; |
143 | list = sclp_con_outqueue.next; |
144 | if (sclp_con_queue_running) |
145 | /* The first element is in I/O */ |
146 | list = list->next; |
147 | if (list == &sclp_con_outqueue) |
148 | return 0; |
149 | list_del(entry: list); |
150 | buffer = list_entry(list, struct sclp_buffer, list); |
151 | page = sclp_unmake_buffer(buffer); |
152 | list_add_tail(new: (struct list_head *) page, head: &sclp_con_pages); |
153 | return 1; |
154 | } |
155 | |
156 | /* |
157 | * Writes the given message to S390 system console |
158 | */ |
159 | static void |
160 | sclp_console_write(struct console *console, const char *message, |
161 | unsigned int count) |
162 | { |
163 | unsigned long flags; |
164 | void *page; |
165 | int written; |
166 | |
167 | if (count == 0) |
168 | return; |
169 | spin_lock_irqsave(&sclp_con_lock, flags); |
170 | /* |
171 | * process escape characters, write message into buffer, |
172 | * send buffer to SCLP |
173 | */ |
174 | do { |
175 | /* make sure we have a console output buffer */ |
176 | if (sclp_conbuf == NULL) { |
177 | if (list_empty(head: &sclp_con_pages)) |
178 | sclp_console_full++; |
179 | while (list_empty(head: &sclp_con_pages)) { |
180 | if (sclp_console_drop_buffer()) |
181 | break; |
182 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
183 | sclp_sync_wait(); |
184 | spin_lock_irqsave(&sclp_con_lock, flags); |
185 | } |
186 | page = sclp_con_pages.next; |
187 | list_del(entry: (struct list_head *) page); |
188 | sclp_conbuf = sclp_make_buffer(page, SCLP_CON_COLUMNS, |
189 | SPACES_PER_TAB); |
190 | } |
191 | /* try to write the string to the current output buffer */ |
192 | written = sclp_write(buffer: sclp_conbuf, (const unsigned char *) |
193 | message, count); |
194 | if (written == count) |
195 | break; |
196 | /* |
197 | * Not all characters could be written to the current |
198 | * output buffer. Emit the buffer, create a new buffer |
199 | * and then output the rest of the string. |
200 | */ |
201 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
202 | sclp_conbuf_emit(); |
203 | spin_lock_irqsave(&sclp_con_lock, flags); |
204 | message += written; |
205 | count -= written; |
206 | } while (count > 0); |
207 | /* Setup timer to output current console buffer after 1/10 second */ |
208 | if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 && |
209 | !timer_pending(timer: &sclp_con_timer)) { |
210 | mod_timer(timer: &sclp_con_timer, expires: jiffies + HZ / 10); |
211 | } |
212 | spin_unlock_irqrestore(lock: &sclp_con_lock, flags); |
213 | } |
214 | |
215 | static struct tty_driver * |
216 | sclp_console_device(struct console *c, int *index) |
217 | { |
218 | *index = c->index; |
219 | return sclp_tty_driver; |
220 | } |
221 | |
222 | /* |
223 | * This panic/reboot notifier makes sure that all buffers |
224 | * will be flushed to the SCLP. |
225 | */ |
226 | static int sclp_console_notify(struct notifier_block *self, |
227 | unsigned long event, void *data) |
228 | { |
229 | /* |
230 | * Perform the lock check before effectively getting the |
231 | * lock on sclp_conbuf_emit() / sclp_console_sync_queue() |
232 | * to prevent potential lockups in atomic context. |
233 | */ |
234 | if (spin_is_locked(lock: &sclp_con_lock)) |
235 | return NOTIFY_DONE; |
236 | |
237 | sclp_conbuf_emit(); |
238 | sclp_console_sync_queue(); |
239 | |
240 | return NOTIFY_DONE; |
241 | } |
242 | |
243 | static struct notifier_block on_panic_nb = { |
244 | .notifier_call = sclp_console_notify, |
245 | .priority = INT_MIN + 1, /* run the callback late */ |
246 | }; |
247 | |
248 | static struct notifier_block on_reboot_nb = { |
249 | .notifier_call = sclp_console_notify, |
250 | .priority = INT_MIN + 1, /* run the callback late */ |
251 | }; |
252 | |
253 | /* |
254 | * used to register the SCLP console to the kernel and to |
255 | * give printk necessary information |
256 | */ |
257 | static struct console sclp_console = |
258 | { |
259 | .name = sclp_console_name, |
260 | .write = sclp_console_write, |
261 | .device = sclp_console_device, |
262 | .flags = CON_PRINTBUFFER, |
263 | .index = 0 /* ttyS0 */ |
264 | }; |
265 | |
266 | /* |
267 | * called by console_init() in drivers/char/tty_io.c at boot-time. |
268 | */ |
269 | static int __init |
270 | sclp_console_init(void) |
271 | { |
272 | void *page; |
273 | int i; |
274 | int rc; |
275 | |
276 | /* SCLP consoles are handled together */ |
277 | if (!(CONSOLE_IS_SCLP || CONSOLE_IS_VT220)) |
278 | return 0; |
279 | rc = sclp_rw_init(); |
280 | if (rc) |
281 | return rc; |
282 | /* Allocate pages for output buffering */ |
283 | for (i = 0; i < sclp_console_pages; i++) { |
284 | page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
285 | list_add_tail(new: page, head: &sclp_con_pages); |
286 | } |
287 | sclp_conbuf = NULL; |
288 | timer_setup(&sclp_con_timer, sclp_console_timeout, 0); |
289 | |
290 | /* enable printk-access to this driver */ |
291 | atomic_notifier_chain_register(nh: &panic_notifier_list, nb: &on_panic_nb); |
292 | register_reboot_notifier(&on_reboot_nb); |
293 | register_console(&sclp_console); |
294 | return 0; |
295 | } |
296 | |
297 | console_initcall(sclp_console_init); |
298 | |