1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * SCLP line mode console driver
41da177e4SLinus Torvalds *
562b74942SMichael Holzheu * Copyright IBM Corp. 1999, 2009
61da177e4SLinus Torvalds * Author(s): Martin Peschke <mpeschke@de.ibm.com>
71da177e4SLinus Torvalds * Martin Schwidefsky <schwidefsky@de.ibm.com>
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
101da177e4SLinus Torvalds #include <linux/kmod.h>
111da177e4SLinus Torvalds #include <linux/console.h>
121da177e4SLinus Torvalds #include <linux/init.h>
13f39650deSAndy Shevchenko #include <linux/panic_notifier.h>
141da177e4SLinus Torvalds #include <linux/timer.h>
151da177e4SLinus Torvalds #include <linux/jiffies.h>
16095761d2SHeiko Carstens #include <linux/termios.h>
171da177e4SLinus Torvalds #include <linux/err.h>
182332ce1aSHolger Smolinski #include <linux/reboot.h>
195a0e3ad6STejun Heo #include <linux/gfp.h>
201da177e4SLinus Torvalds
211da177e4SLinus Torvalds #include "sclp.h"
221da177e4SLinus Torvalds #include "sclp_rw.h"
231da177e4SLinus Torvalds #include "sclp_tty.h"
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds #define sclp_console_major 4 /* TTYAUX_MAJOR */
261da177e4SLinus Torvalds #define sclp_console_minor 64
271da177e4SLinus Torvalds #define sclp_console_name "ttyS"
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds /* Lock to guard over changes to global variables */
307dd8ed09SVineeth Vijayan static DEFINE_SPINLOCK(sclp_con_lock);
311da177e4SLinus Torvalds /* List of free pages that can be used for console output buffering */
328bc00c04SVineeth Vijayan static LIST_HEAD(sclp_con_pages);
331da177e4SLinus Torvalds /* List of full struct sclp_buffer structures ready for output */
348bc00c04SVineeth Vijayan static LIST_HEAD(sclp_con_outqueue);
351da177e4SLinus Torvalds /* Pointer to current console buffer */
361da177e4SLinus Torvalds static struct sclp_buffer *sclp_conbuf;
371da177e4SLinus Torvalds /* Timer for delayed output of console messages */
381da177e4SLinus Torvalds static struct timer_list sclp_con_timer;
3962b74942SMichael Holzheu /* Flag that output queue is currently running */
4062b74942SMichael Holzheu static int sclp_con_queue_running;
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds /* Output format for console messages */
4398ce70b7SPeter Oberparleiter #define SCLP_CON_COLUMNS 320
4498ce70b7SPeter Oberparleiter #define SPACES_PER_TAB 8
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds static void
sclp_conbuf_callback(struct sclp_buffer * buffer,int rc)471da177e4SLinus Torvalds sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds unsigned long flags;
501da177e4SLinus Torvalds void *page;
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds do {
531da177e4SLinus Torvalds page = sclp_unmake_buffer(buffer);
541da177e4SLinus Torvalds spin_lock_irqsave(&sclp_con_lock, flags);
5562b74942SMichael Holzheu
561da177e4SLinus Torvalds /* Remove buffer from outqueue */
571da177e4SLinus Torvalds list_del(&buffer->list);
581da177e4SLinus Torvalds list_add_tail((struct list_head *) page, &sclp_con_pages);
5962b74942SMichael Holzheu
601da177e4SLinus Torvalds /* Check if there is a pending buffer on the out queue. */
611da177e4SLinus Torvalds buffer = NULL;
621da177e4SLinus Torvalds if (!list_empty(&sclp_con_outqueue))
6362b74942SMichael Holzheu buffer = list_first_entry(&sclp_con_outqueue,
641da177e4SLinus Torvalds struct sclp_buffer, list);
655602bf8aSPeter Oberparleiter if (!buffer) {
6662b74942SMichael Holzheu sclp_con_queue_running = 0;
671da177e4SLinus Torvalds spin_unlock_irqrestore(&sclp_con_lock, flags);
6862b74942SMichael Holzheu break;
6962b74942SMichael Holzheu }
7062b74942SMichael Holzheu spin_unlock_irqrestore(&sclp_con_lock, flags);
7162b74942SMichael Holzheu } while (sclp_emit_buffer(buffer, sclp_conbuf_callback));
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds
7462b74942SMichael Holzheu /*
7562b74942SMichael Holzheu * Finalize and emit first pending buffer.
7662b74942SMichael Holzheu */
sclp_conbuf_emit(void)7762b74942SMichael Holzheu static void sclp_conbuf_emit(void)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds struct sclp_buffer* buffer;
801da177e4SLinus Torvalds unsigned long flags;
811da177e4SLinus Torvalds int rc;
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds spin_lock_irqsave(&sclp_con_lock, flags);
8462b74942SMichael Holzheu if (sclp_conbuf)
8562b74942SMichael Holzheu list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue);
861da177e4SLinus Torvalds sclp_conbuf = NULL;
875602bf8aSPeter Oberparleiter if (sclp_con_queue_running)
8862b74942SMichael Holzheu goto out_unlock;
8962b74942SMichael Holzheu if (list_empty(&sclp_con_outqueue))
9062b74942SMichael Holzheu goto out_unlock;
9162b74942SMichael Holzheu buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer,
9262b74942SMichael Holzheu list);
9362b74942SMichael Holzheu sclp_con_queue_running = 1;
941da177e4SLinus Torvalds spin_unlock_irqrestore(&sclp_con_lock, flags);
9562b74942SMichael Holzheu
961da177e4SLinus Torvalds rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
971da177e4SLinus Torvalds if (rc)
981da177e4SLinus Torvalds sclp_conbuf_callback(buffer, rc);
9962b74942SMichael Holzheu return;
10062b74942SMichael Holzheu out_unlock:
10162b74942SMichael Holzheu spin_unlock_irqrestore(&sclp_con_lock, flags);
10262b74942SMichael Holzheu }
10362b74942SMichael Holzheu
10462b74942SMichael Holzheu /*
10562b74942SMichael Holzheu * Wait until out queue is empty
10662b74942SMichael Holzheu */
sclp_console_sync_queue(void)10762b74942SMichael Holzheu static void sclp_console_sync_queue(void)
10862b74942SMichael Holzheu {
10962b74942SMichael Holzheu unsigned long flags;
11062b74942SMichael Holzheu
11162b74942SMichael Holzheu spin_lock_irqsave(&sclp_con_lock, flags);
112f3dfa86cSMichael Holzheu del_timer(&sclp_con_timer);
11362b74942SMichael Holzheu while (sclp_con_queue_running) {
11462b74942SMichael Holzheu spin_unlock_irqrestore(&sclp_con_lock, flags);
11562b74942SMichael Holzheu sclp_sync_wait();
11662b74942SMichael Holzheu spin_lock_irqsave(&sclp_con_lock, flags);
11762b74942SMichael Holzheu }
11862b74942SMichael Holzheu spin_unlock_irqrestore(&sclp_con_lock, flags);
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds
1211da177e4SLinus Torvalds /*
1221da177e4SLinus Torvalds * When this routine is called from the timer then we flush the
1231da177e4SLinus Torvalds * temporary write buffer without further waiting on a final new line.
1241da177e4SLinus Torvalds */
1251da177e4SLinus Torvalds static void
sclp_console_timeout(struct timer_list * unused)126c9602ee7SKees Cook sclp_console_timeout(struct timer_list *unused)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds sclp_conbuf_emit();
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds /*
13225b41a7bSMartin Schwidefsky * Drop oldest console buffer if sclp_con_drop is set
13325b41a7bSMartin Schwidefsky */
13425b41a7bSMartin Schwidefsky static int
sclp_console_drop_buffer(void)13525b41a7bSMartin Schwidefsky sclp_console_drop_buffer(void)
13625b41a7bSMartin Schwidefsky {
13725b41a7bSMartin Schwidefsky struct list_head *list;
13825b41a7bSMartin Schwidefsky struct sclp_buffer *buffer;
13925b41a7bSMartin Schwidefsky void *page;
14025b41a7bSMartin Schwidefsky
14125b41a7bSMartin Schwidefsky if (!sclp_console_drop)
14225b41a7bSMartin Schwidefsky return 0;
14325b41a7bSMartin Schwidefsky list = sclp_con_outqueue.next;
14425b41a7bSMartin Schwidefsky if (sclp_con_queue_running)
14525b41a7bSMartin Schwidefsky /* The first element is in I/O */
14625b41a7bSMartin Schwidefsky list = list->next;
14725b41a7bSMartin Schwidefsky if (list == &sclp_con_outqueue)
14825b41a7bSMartin Schwidefsky return 0;
14925b41a7bSMartin Schwidefsky list_del(list);
15025b41a7bSMartin Schwidefsky buffer = list_entry(list, struct sclp_buffer, list);
15125b41a7bSMartin Schwidefsky page = sclp_unmake_buffer(buffer);
15225b41a7bSMartin Schwidefsky list_add_tail((struct list_head *) page, &sclp_con_pages);
15325b41a7bSMartin Schwidefsky return 1;
15425b41a7bSMartin Schwidefsky }
15525b41a7bSMartin Schwidefsky
15625b41a7bSMartin Schwidefsky /*
1571da177e4SLinus Torvalds * Writes the given message to S390 system console
1581da177e4SLinus Torvalds */
1591da177e4SLinus Torvalds static void
sclp_console_write(struct console * console,const char * message,unsigned int count)1601da177e4SLinus Torvalds sclp_console_write(struct console *console, const char *message,
1611da177e4SLinus Torvalds unsigned int count)
1621da177e4SLinus Torvalds {
1631da177e4SLinus Torvalds unsigned long flags;
1641da177e4SLinus Torvalds void *page;
1651da177e4SLinus Torvalds int written;
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds if (count == 0)
1681da177e4SLinus Torvalds return;
1691da177e4SLinus Torvalds spin_lock_irqsave(&sclp_con_lock, flags);
1701da177e4SLinus Torvalds /*
1711da177e4SLinus Torvalds * process escape characters, write message into buffer,
1721da177e4SLinus Torvalds * send buffer to SCLP
1731da177e4SLinus Torvalds */
1741da177e4SLinus Torvalds do {
1751da177e4SLinus Torvalds /* make sure we have a console output buffer */
1761da177e4SLinus Torvalds if (sclp_conbuf == NULL) {
17725b41a7bSMartin Schwidefsky if (list_empty(&sclp_con_pages))
17825b41a7bSMartin Schwidefsky sclp_console_full++;
1791da177e4SLinus Torvalds while (list_empty(&sclp_con_pages)) {
18025b41a7bSMartin Schwidefsky if (sclp_console_drop_buffer())
18125b41a7bSMartin Schwidefsky break;
1821da177e4SLinus Torvalds spin_unlock_irqrestore(&sclp_con_lock, flags);
1831da177e4SLinus Torvalds sclp_sync_wait();
1841da177e4SLinus Torvalds spin_lock_irqsave(&sclp_con_lock, flags);
1851da177e4SLinus Torvalds }
1861da177e4SLinus Torvalds page = sclp_con_pages.next;
1871da177e4SLinus Torvalds list_del((struct list_head *) page);
18898ce70b7SPeter Oberparleiter sclp_conbuf = sclp_make_buffer(page, SCLP_CON_COLUMNS,
18998ce70b7SPeter Oberparleiter SPACES_PER_TAB);
1901da177e4SLinus Torvalds }
1911da177e4SLinus Torvalds /* try to write the string to the current output buffer */
1921da177e4SLinus Torvalds written = sclp_write(sclp_conbuf, (const unsigned char *)
1931da177e4SLinus Torvalds message, count);
1941da177e4SLinus Torvalds if (written == count)
1951da177e4SLinus Torvalds break;
1961da177e4SLinus Torvalds /*
1971da177e4SLinus Torvalds * Not all characters could be written to the current
1981da177e4SLinus Torvalds * output buffer. Emit the buffer, create a new buffer
1991da177e4SLinus Torvalds * and then output the rest of the string.
2001da177e4SLinus Torvalds */
2011da177e4SLinus Torvalds spin_unlock_irqrestore(&sclp_con_lock, flags);
2021da177e4SLinus Torvalds sclp_conbuf_emit();
2031da177e4SLinus Torvalds spin_lock_irqsave(&sclp_con_lock, flags);
2041da177e4SLinus Torvalds message += written;
2051da177e4SLinus Torvalds count -= written;
2061da177e4SLinus Torvalds } while (count > 0);
2071da177e4SLinus Torvalds /* Setup timer to output current console buffer after 1/10 second */
2081da177e4SLinus Torvalds if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
2091da177e4SLinus Torvalds !timer_pending(&sclp_con_timer)) {
2108179c7baSHimanshu Jha mod_timer(&sclp_con_timer, jiffies + HZ / 10);
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds spin_unlock_irqrestore(&sclp_con_lock, flags);
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds static struct tty_driver *
sclp_console_device(struct console * c,int * index)2161da177e4SLinus Torvalds sclp_console_device(struct console *c, int *index)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds *index = c->index;
2191da177e4SLinus Torvalds return sclp_tty_driver;
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds
2221da177e4SLinus Torvalds /*
223*4ae46db9SGuilherme G. Piccoli * This panic/reboot notifier makes sure that all buffers
224*4ae46db9SGuilherme G. Piccoli * will be flushed to the SCLP.
2251da177e4SLinus Torvalds */
sclp_console_notify(struct notifier_block * self,unsigned long event,void * data)22662b74942SMichael Holzheu static int sclp_console_notify(struct notifier_block *self,
2272332ce1aSHolger Smolinski unsigned long event, void *data)
2282332ce1aSHolger Smolinski {
229*4ae46db9SGuilherme G. Piccoli /*
230*4ae46db9SGuilherme G. Piccoli * Perform the lock check before effectively getting the
231*4ae46db9SGuilherme G. Piccoli * lock on sclp_conbuf_emit() / sclp_console_sync_queue()
232*4ae46db9SGuilherme G. Piccoli * to prevent potential lockups in atomic context.
233*4ae46db9SGuilherme G. Piccoli */
234*4ae46db9SGuilherme G. Piccoli if (spin_is_locked(&sclp_con_lock))
235*4ae46db9SGuilherme G. Piccoli return NOTIFY_DONE;
236*4ae46db9SGuilherme G. Piccoli
237*4ae46db9SGuilherme G. Piccoli sclp_conbuf_emit();
238*4ae46db9SGuilherme G. Piccoli sclp_console_sync_queue();
239*4ae46db9SGuilherme G. Piccoli
240*4ae46db9SGuilherme G. Piccoli return NOTIFY_DONE;
2412332ce1aSHolger Smolinski }
2422332ce1aSHolger Smolinski
2432332ce1aSHolger Smolinski static struct notifier_block on_panic_nb = {
2442332ce1aSHolger Smolinski .notifier_call = sclp_console_notify,
245*4ae46db9SGuilherme G. Piccoli .priority = INT_MIN + 1, /* run the callback late */
2462332ce1aSHolger Smolinski };
2472332ce1aSHolger Smolinski
2482332ce1aSHolger Smolinski static struct notifier_block on_reboot_nb = {
2492332ce1aSHolger Smolinski .notifier_call = sclp_console_notify,
250*4ae46db9SGuilherme G. Piccoli .priority = INT_MIN + 1, /* run the callback late */
2512332ce1aSHolger Smolinski };
2522332ce1aSHolger Smolinski
2531da177e4SLinus Torvalds /*
2541da177e4SLinus Torvalds * used to register the SCLP console to the kernel and to
2551da177e4SLinus Torvalds * give printk necessary information
2561da177e4SLinus Torvalds */
2571da177e4SLinus Torvalds static struct console sclp_console =
2581da177e4SLinus Torvalds {
2591da177e4SLinus Torvalds .name = sclp_console_name,
2601da177e4SLinus Torvalds .write = sclp_console_write,
2611da177e4SLinus Torvalds .device = sclp_console_device,
2621da177e4SLinus Torvalds .flags = CON_PRINTBUFFER,
2631da177e4SLinus Torvalds .index = 0 /* ttyS0 */
2641da177e4SLinus Torvalds };
2651da177e4SLinus Torvalds
2661da177e4SLinus Torvalds /*
2671da177e4SLinus Torvalds * called by console_init() in drivers/char/tty_io.c at boot-time.
2681da177e4SLinus Torvalds */
2691da177e4SLinus Torvalds static int __init
sclp_console_init(void)2701da177e4SLinus Torvalds sclp_console_init(void)
2711da177e4SLinus Torvalds {
2721da177e4SLinus Torvalds void *page;
2731da177e4SLinus Torvalds int i;
2741da177e4SLinus Torvalds int rc;
2751da177e4SLinus Torvalds
2768f50af49SPeter Oberparleiter /* SCLP consoles are handled together */
2778f50af49SPeter Oberparleiter if (!(CONSOLE_IS_SCLP || CONSOLE_IS_VT220))
2781da177e4SLinus Torvalds return 0;
2791da177e4SLinus Torvalds rc = sclp_rw_init();
2801da177e4SLinus Torvalds if (rc)
2811da177e4SLinus Torvalds return rc;
2821da177e4SLinus Torvalds /* Allocate pages for output buffering */
28325b41a7bSMartin Schwidefsky for (i = 0; i < sclp_console_pages; i++) {
2844c8f4794SHeiko Carstens page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
2854c8f4794SHeiko Carstens list_add_tail(page, &sclp_con_pages);
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds sclp_conbuf = NULL;
288c9602ee7SKees Cook timer_setup(&sclp_con_timer, sclp_console_timeout, 0);
2891da177e4SLinus Torvalds
2901da177e4SLinus Torvalds /* enable printk-access to this driver */
2912332ce1aSHolger Smolinski atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
2922332ce1aSHolger Smolinski register_reboot_notifier(&on_reboot_nb);
2931da177e4SLinus Torvalds register_console(&sclp_console);
2941da177e4SLinus Torvalds return 0;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds
2971da177e4SLinus Torvalds console_initcall(sclp_console_init);
298