cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ttyprintk.c (5270B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/drivers/char/ttyprintk.c
      4 *
      5 *  Copyright (C) 2010  Samo Pogacnik
      6 */
      7
      8/*
      9 * This pseudo device allows user to make printk messages. It is possible
     10 * to store "console" messages inline with kernel messages for better analyses
     11 * of the boot process, for example.
     12 */
     13
     14#include <linux/console.h>
     15#include <linux/device.h>
     16#include <linux/serial.h>
     17#include <linux/tty.h>
     18#include <linux/module.h>
     19#include <linux/spinlock.h>
     20
     21struct ttyprintk_port {
     22	struct tty_port port;
     23	spinlock_t spinlock;
     24};
     25
     26static struct ttyprintk_port tpk_port;
     27
     28/*
     29 * Our simple preformatting supports transparent output of (time-stamped)
     30 * printk messages (also suitable for logging service):
     31 * - any cr is replaced by nl
     32 * - adds a ttyprintk source tag in front of each line
     33 * - too long message is fragmented, with '\'nl between fragments
     34 * - TPK_STR_SIZE isn't really the write_room limiting factor, because
     35 *   it is emptied on the fly during preformatting.
     36 */
     37#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
     38#define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
     39#define TPK_PREFIX KERN_SOH __stringify(CONFIG_TTY_PRINTK_LEVEL)
     40
     41static int tpk_curr;
     42
     43static char tpk_buffer[TPK_STR_SIZE + 4];
     44
     45static void tpk_flush(void)
     46{
     47	if (tpk_curr > 0) {
     48		tpk_buffer[tpk_curr] = '\0';
     49		printk(TPK_PREFIX "[U] %s\n", tpk_buffer);
     50		tpk_curr = 0;
     51	}
     52}
     53
     54static int tpk_printk(const unsigned char *buf, int count)
     55{
     56	int i;
     57
     58	for (i = 0; i < count; i++) {
     59		if (tpk_curr >= TPK_STR_SIZE) {
     60			/* end of tmp buffer reached: cut the message in two */
     61			tpk_buffer[tpk_curr++] = '\\';
     62			tpk_flush();
     63		}
     64
     65		switch (buf[i]) {
     66		case '\r':
     67			tpk_flush();
     68			if ((i + 1) < count && buf[i + 1] == '\n')
     69				i++;
     70			break;
     71		case '\n':
     72			tpk_flush();
     73			break;
     74		default:
     75			tpk_buffer[tpk_curr++] = buf[i];
     76			break;
     77		}
     78	}
     79
     80	return count;
     81}
     82
     83/*
     84 * TTY operations open function.
     85 */
     86static int tpk_open(struct tty_struct *tty, struct file *filp)
     87{
     88	tty->driver_data = &tpk_port;
     89
     90	return tty_port_open(&tpk_port.port, tty, filp);
     91}
     92
     93/*
     94 * TTY operations close function.
     95 */
     96static void tpk_close(struct tty_struct *tty, struct file *filp)
     97{
     98	struct ttyprintk_port *tpkp = tty->driver_data;
     99
    100	tty_port_close(&tpkp->port, tty, filp);
    101}
    102
    103/*
    104 * TTY operations write function.
    105 */
    106static int tpk_write(struct tty_struct *tty,
    107		const unsigned char *buf, int count)
    108{
    109	struct ttyprintk_port *tpkp = tty->driver_data;
    110	unsigned long flags;
    111	int ret;
    112
    113	/* exclusive use of tpk_printk within this tty */
    114	spin_lock_irqsave(&tpkp->spinlock, flags);
    115	ret = tpk_printk(buf, count);
    116	spin_unlock_irqrestore(&tpkp->spinlock, flags);
    117
    118	return ret;
    119}
    120
    121/*
    122 * TTY operations write_room function.
    123 */
    124static unsigned int tpk_write_room(struct tty_struct *tty)
    125{
    126	return TPK_MAX_ROOM;
    127}
    128
    129/*
    130 * TTY operations hangup function.
    131 */
    132static void tpk_hangup(struct tty_struct *tty)
    133{
    134	struct ttyprintk_port *tpkp = tty->driver_data;
    135
    136	tty_port_hangup(&tpkp->port);
    137}
    138
    139/*
    140 * TTY port operations shutdown function.
    141 */
    142static void tpk_port_shutdown(struct tty_port *tport)
    143{
    144	struct ttyprintk_port *tpkp =
    145		container_of(tport, struct ttyprintk_port, port);
    146	unsigned long flags;
    147
    148	spin_lock_irqsave(&tpkp->spinlock, flags);
    149	tpk_flush();
    150	spin_unlock_irqrestore(&tpkp->spinlock, flags);
    151}
    152
    153static const struct tty_operations ttyprintk_ops = {
    154	.open = tpk_open,
    155	.close = tpk_close,
    156	.write = tpk_write,
    157	.write_room = tpk_write_room,
    158	.hangup = tpk_hangup,
    159};
    160
    161static const struct tty_port_operations tpk_port_ops = {
    162	.shutdown = tpk_port_shutdown,
    163};
    164
    165static struct tty_driver *ttyprintk_driver;
    166
    167static struct tty_driver *ttyprintk_console_device(struct console *c,
    168						   int *index)
    169{
    170	*index = 0;
    171	return ttyprintk_driver;
    172}
    173
    174static struct console ttyprintk_console = {
    175	.name = "ttyprintk",
    176	.device = ttyprintk_console_device,
    177};
    178
    179static int __init ttyprintk_init(void)
    180{
    181	int ret;
    182
    183	spin_lock_init(&tpk_port.spinlock);
    184
    185	ttyprintk_driver = tty_alloc_driver(1,
    186			TTY_DRIVER_RESET_TERMIOS |
    187			TTY_DRIVER_REAL_RAW |
    188			TTY_DRIVER_UNNUMBERED_NODE);
    189	if (IS_ERR(ttyprintk_driver))
    190		return PTR_ERR(ttyprintk_driver);
    191
    192	tty_port_init(&tpk_port.port);
    193	tpk_port.port.ops = &tpk_port_ops;
    194
    195	ttyprintk_driver->driver_name = "ttyprintk";
    196	ttyprintk_driver->name = "ttyprintk";
    197	ttyprintk_driver->major = TTYAUX_MAJOR;
    198	ttyprintk_driver->minor_start = 3;
    199	ttyprintk_driver->type = TTY_DRIVER_TYPE_CONSOLE;
    200	ttyprintk_driver->init_termios = tty_std_termios;
    201	ttyprintk_driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
    202	tty_set_operations(ttyprintk_driver, &ttyprintk_ops);
    203	tty_port_link_device(&tpk_port.port, ttyprintk_driver, 0);
    204
    205	ret = tty_register_driver(ttyprintk_driver);
    206	if (ret < 0) {
    207		printk(KERN_ERR "Couldn't register ttyprintk driver\n");
    208		goto error;
    209	}
    210
    211	register_console(&ttyprintk_console);
    212
    213	return 0;
    214
    215error:
    216	tty_driver_kref_put(ttyprintk_driver);
    217	tty_port_destroy(&tpk_port.port);
    218	return ret;
    219}
    220
    221static void __exit ttyprintk_exit(void)
    222{
    223	unregister_console(&ttyprintk_console);
    224	tty_unregister_driver(ttyprintk_driver);
    225	tty_driver_kref_put(ttyprintk_driver);
    226	tty_port_destroy(&tpk_port.port);
    227}
    228
    229device_initcall(ttyprintk_init);
    230module_exit(ttyprintk_exit);
    231
    232MODULE_LICENSE("GPL");