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

chan_kern.c (11991B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
      4 */
      5
      6#include <linux/slab.h>
      7#include <linux/tty.h>
      8#include <linux/tty_flip.h>
      9#include "chan.h"
     10#include <os.h>
     11#include <irq_kern.h>
     12
     13#ifdef CONFIG_NOCONFIG_CHAN
     14static void *not_configged_init(char *str, int device,
     15				const struct chan_opts *opts)
     16{
     17	printk(KERN_ERR "Using a channel type which is configured out of "
     18	       "UML\n");
     19	return NULL;
     20}
     21
     22static int not_configged_open(int input, int output, int primary, void *data,
     23			      char **dev_out)
     24{
     25	printk(KERN_ERR "Using a channel type which is configured out of "
     26	       "UML\n");
     27	return -ENODEV;
     28}
     29
     30static void not_configged_close(int fd, void *data)
     31{
     32	printk(KERN_ERR "Using a channel type which is configured out of "
     33	       "UML\n");
     34}
     35
     36static int not_configged_read(int fd, char *c_out, void *data)
     37{
     38	printk(KERN_ERR "Using a channel type which is configured out of "
     39	       "UML\n");
     40	return -EIO;
     41}
     42
     43static int not_configged_write(int fd, const char *buf, int len, void *data)
     44{
     45	printk(KERN_ERR "Using a channel type which is configured out of "
     46	       "UML\n");
     47	return -EIO;
     48}
     49
     50static int not_configged_console_write(int fd, const char *buf, int len)
     51{
     52	printk(KERN_ERR "Using a channel type which is configured out of "
     53	       "UML\n");
     54	return -EIO;
     55}
     56
     57static int not_configged_window_size(int fd, void *data, unsigned short *rows,
     58				     unsigned short *cols)
     59{
     60	printk(KERN_ERR "Using a channel type which is configured out of "
     61	       "UML\n");
     62	return -ENODEV;
     63}
     64
     65static void not_configged_free(void *data)
     66{
     67	printk(KERN_ERR "Using a channel type which is configured out of "
     68	       "UML\n");
     69}
     70
     71static const struct chan_ops not_configged_ops = {
     72	.init		= not_configged_init,
     73	.open		= not_configged_open,
     74	.close		= not_configged_close,
     75	.read		= not_configged_read,
     76	.write		= not_configged_write,
     77	.console_write	= not_configged_console_write,
     78	.window_size	= not_configged_window_size,
     79	.free		= not_configged_free,
     80	.winch		= 0,
     81};
     82#endif /* CONFIG_NOCONFIG_CHAN */
     83
     84static int open_one_chan(struct chan *chan)
     85{
     86	int fd, err;
     87
     88	if (chan->opened)
     89		return 0;
     90
     91	if (chan->ops->open == NULL)
     92		fd = 0;
     93	else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
     94				     chan->data, &chan->dev);
     95	if (fd < 0)
     96		return fd;
     97
     98	err = os_set_fd_block(fd, 0);
     99	if (err) {
    100		(*chan->ops->close)(fd, chan->data);
    101		return err;
    102	}
    103
    104	chan->fd = fd;
    105
    106	chan->opened = 1;
    107	return 0;
    108}
    109
    110static int open_chan(struct list_head *chans)
    111{
    112	struct list_head *ele;
    113	struct chan *chan;
    114	int ret, err = 0;
    115
    116	list_for_each(ele, chans) {
    117		chan = list_entry(ele, struct chan, list);
    118		ret = open_one_chan(chan);
    119		if (chan->primary)
    120			err = ret;
    121	}
    122	return err;
    123}
    124
    125void chan_enable_winch(struct chan *chan, struct tty_port *port)
    126{
    127	if (chan && chan->primary && chan->ops->winch)
    128		register_winch(chan->fd, port);
    129}
    130
    131static void line_timer_cb(struct work_struct *work)
    132{
    133	struct line *line = container_of(work, struct line, task.work);
    134
    135	if (!line->throttled)
    136		chan_interrupt(line, line->read_irq);
    137}
    138
    139int enable_chan(struct line *line)
    140{
    141	struct list_head *ele;
    142	struct chan *chan;
    143	int err;
    144
    145	INIT_DELAYED_WORK(&line->task, line_timer_cb);
    146
    147	list_for_each(ele, &line->chan_list) {
    148		chan = list_entry(ele, struct chan, list);
    149		err = open_one_chan(chan);
    150		if (err) {
    151			if (chan->primary)
    152				goto out_close;
    153
    154			continue;
    155		}
    156
    157		if (chan->enabled)
    158			continue;
    159		err = line_setup_irq(chan->fd, chan->input, chan->output, line,
    160				     chan);
    161		if (err)
    162			goto out_close;
    163
    164		chan->enabled = 1;
    165	}
    166
    167	return 0;
    168
    169 out_close:
    170	close_chan(line);
    171	return err;
    172}
    173
    174/* Items are added in IRQ context, when free_irq can't be called, and
    175 * removed in process context, when it can.
    176 * This handles interrupt sources which disappear, and which need to
    177 * be permanently disabled.  This is discovered in IRQ context, but
    178 * the freeing of the IRQ must be done later.
    179 */
    180static DEFINE_SPINLOCK(irqs_to_free_lock);
    181static LIST_HEAD(irqs_to_free);
    182
    183void free_irqs(void)
    184{
    185	struct chan *chan;
    186	LIST_HEAD(list);
    187	struct list_head *ele;
    188	unsigned long flags;
    189
    190	spin_lock_irqsave(&irqs_to_free_lock, flags);
    191	list_splice_init(&irqs_to_free, &list);
    192	spin_unlock_irqrestore(&irqs_to_free_lock, flags);
    193
    194	list_for_each(ele, &list) {
    195		chan = list_entry(ele, struct chan, free_list);
    196
    197		if (chan->input && chan->enabled)
    198			um_free_irq(chan->line->read_irq, chan);
    199		if (chan->output && chan->enabled)
    200			um_free_irq(chan->line->write_irq, chan);
    201		chan->enabled = 0;
    202	}
    203}
    204
    205static void close_one_chan(struct chan *chan, int delay_free_irq)
    206{
    207	unsigned long flags;
    208
    209	if (!chan->opened)
    210		return;
    211
    212	if (delay_free_irq) {
    213		spin_lock_irqsave(&irqs_to_free_lock, flags);
    214		list_add(&chan->free_list, &irqs_to_free);
    215		spin_unlock_irqrestore(&irqs_to_free_lock, flags);
    216	} else {
    217		if (chan->input && chan->enabled)
    218			um_free_irq(chan->line->read_irq, chan);
    219		if (chan->output && chan->enabled)
    220			um_free_irq(chan->line->write_irq, chan);
    221		chan->enabled = 0;
    222	}
    223	if (chan->ops->close != NULL)
    224		(*chan->ops->close)(chan->fd, chan->data);
    225
    226	chan->opened = 0;
    227	chan->fd = -1;
    228}
    229
    230void close_chan(struct line *line)
    231{
    232	struct chan *chan;
    233
    234	/* Close in reverse order as open in case more than one of them
    235	 * refers to the same device and they save and restore that device's
    236	 * state.  Then, the first one opened will have the original state,
    237	 * so it must be the last closed.
    238	 */
    239	list_for_each_entry_reverse(chan, &line->chan_list, list) {
    240		close_one_chan(chan, 0);
    241	}
    242}
    243
    244void deactivate_chan(struct chan *chan, int irq)
    245{
    246	if (chan && chan->enabled)
    247		deactivate_fd(chan->fd, irq);
    248}
    249
    250int write_chan(struct chan *chan, const char *buf, int len,
    251	       int write_irq)
    252{
    253	int n, ret = 0;
    254
    255	if (len == 0 || !chan || !chan->ops->write)
    256		return 0;
    257
    258	n = chan->ops->write(chan->fd, buf, len, chan->data);
    259	if (chan->primary) {
    260		ret = n;
    261	}
    262	return ret;
    263}
    264
    265int console_write_chan(struct chan *chan, const char *buf, int len)
    266{
    267	int n, ret = 0;
    268
    269	if (!chan || !chan->ops->console_write)
    270		return 0;
    271
    272	n = chan->ops->console_write(chan->fd, buf, len);
    273	if (chan->primary)
    274		ret = n;
    275	return ret;
    276}
    277
    278int console_open_chan(struct line *line, struct console *co)
    279{
    280	int err;
    281
    282	err = open_chan(&line->chan_list);
    283	if (err)
    284		return err;
    285
    286	printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
    287	       co->index);
    288	return 0;
    289}
    290
    291int chan_window_size(struct line *line, unsigned short *rows_out,
    292		      unsigned short *cols_out)
    293{
    294	struct chan *chan;
    295
    296	chan = line->chan_in;
    297	if (chan && chan->primary) {
    298		if (chan->ops->window_size == NULL)
    299			return 0;
    300		return chan->ops->window_size(chan->fd, chan->data,
    301					      rows_out, cols_out);
    302	}
    303	chan = line->chan_out;
    304	if (chan && chan->primary) {
    305		if (chan->ops->window_size == NULL)
    306			return 0;
    307		return chan->ops->window_size(chan->fd, chan->data,
    308					      rows_out, cols_out);
    309	}
    310	return 0;
    311}
    312
    313static void free_one_chan(struct chan *chan)
    314{
    315	list_del(&chan->list);
    316
    317	close_one_chan(chan, 0);
    318
    319	if (chan->ops->free != NULL)
    320		(*chan->ops->free)(chan->data);
    321
    322	if (chan->primary && chan->output)
    323		ignore_sigio_fd(chan->fd);
    324	kfree(chan);
    325}
    326
    327static void free_chan(struct list_head *chans)
    328{
    329	struct list_head *ele, *next;
    330	struct chan *chan;
    331
    332	list_for_each_safe(ele, next, chans) {
    333		chan = list_entry(ele, struct chan, list);
    334		free_one_chan(chan);
    335	}
    336}
    337
    338static int one_chan_config_string(struct chan *chan, char *str, int size,
    339				  char **error_out)
    340{
    341	int n = 0;
    342
    343	if (chan == NULL) {
    344		CONFIG_CHUNK(str, size, n, "none", 1);
    345		return n;
    346	}
    347
    348	CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
    349
    350	if (chan->dev == NULL) {
    351		CONFIG_CHUNK(str, size, n, "", 1);
    352		return n;
    353	}
    354
    355	CONFIG_CHUNK(str, size, n, ":", 0);
    356	CONFIG_CHUNK(str, size, n, chan->dev, 0);
    357
    358	return n;
    359}
    360
    361static int chan_pair_config_string(struct chan *in, struct chan *out,
    362				   char *str, int size, char **error_out)
    363{
    364	int n;
    365
    366	n = one_chan_config_string(in, str, size, error_out);
    367	str += n;
    368	size -= n;
    369
    370	if (in == out) {
    371		CONFIG_CHUNK(str, size, n, "", 1);
    372		return n;
    373	}
    374
    375	CONFIG_CHUNK(str, size, n, ",", 1);
    376	n = one_chan_config_string(out, str, size, error_out);
    377	str += n;
    378	size -= n;
    379	CONFIG_CHUNK(str, size, n, "", 1);
    380
    381	return n;
    382}
    383
    384int chan_config_string(struct line *line, char *str, int size,
    385		       char **error_out)
    386{
    387	struct chan *in = line->chan_in, *out = line->chan_out;
    388
    389	if (in && !in->primary)
    390		in = NULL;
    391	if (out && !out->primary)
    392		out = NULL;
    393
    394	return chan_pair_config_string(in, out, str, size, error_out);
    395}
    396
    397struct chan_type {
    398	char *key;
    399	const struct chan_ops *ops;
    400};
    401
    402static const struct chan_type chan_table[] = {
    403	{ "fd", &fd_ops },
    404
    405#ifdef CONFIG_NULL_CHAN
    406	{ "null", &null_ops },
    407#else
    408	{ "null", &not_configged_ops },
    409#endif
    410
    411#ifdef CONFIG_PORT_CHAN
    412	{ "port", &port_ops },
    413#else
    414	{ "port", &not_configged_ops },
    415#endif
    416
    417#ifdef CONFIG_PTY_CHAN
    418	{ "pty", &pty_ops },
    419	{ "pts", &pts_ops },
    420#else
    421	{ "pty", &not_configged_ops },
    422	{ "pts", &not_configged_ops },
    423#endif
    424
    425#ifdef CONFIG_TTY_CHAN
    426	{ "tty", &tty_ops },
    427#else
    428	{ "tty", &not_configged_ops },
    429#endif
    430
    431#ifdef CONFIG_XTERM_CHAN
    432	{ "xterm", &xterm_ops },
    433#else
    434	{ "xterm", &not_configged_ops },
    435#endif
    436};
    437
    438static struct chan *parse_chan(struct line *line, char *str, int device,
    439			       const struct chan_opts *opts, char **error_out)
    440{
    441	const struct chan_type *entry;
    442	const struct chan_ops *ops;
    443	struct chan *chan;
    444	void *data;
    445	int i;
    446
    447	ops = NULL;
    448	data = NULL;
    449	for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
    450		entry = &chan_table[i];
    451		if (!strncmp(str, entry->key, strlen(entry->key))) {
    452			ops = entry->ops;
    453			str += strlen(entry->key);
    454			break;
    455		}
    456	}
    457	if (ops == NULL) {
    458		*error_out = "No match for configured backends";
    459		return NULL;
    460	}
    461
    462	data = (*ops->init)(str, device, opts);
    463	if (data == NULL) {
    464		*error_out = "Configuration failed";
    465		return NULL;
    466	}
    467
    468	chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
    469	if (chan == NULL) {
    470		*error_out = "Memory allocation failed";
    471		return NULL;
    472	}
    473	*chan = ((struct chan) { .list	 	= LIST_HEAD_INIT(chan->list),
    474				 .free_list 	=
    475				 	LIST_HEAD_INIT(chan->free_list),
    476				 .line		= line,
    477				 .primary	= 1,
    478				 .input		= 0,
    479				 .output 	= 0,
    480				 .opened  	= 0,
    481				 .enabled  	= 0,
    482				 .fd 		= -1,
    483				 .ops 		= ops,
    484				 .data 		= data });
    485	return chan;
    486}
    487
    488int parse_chan_pair(char *str, struct line *line, int device,
    489		    const struct chan_opts *opts, char **error_out)
    490{
    491	struct list_head *chans = &line->chan_list;
    492	struct chan *new;
    493	char *in, *out;
    494
    495	if (!list_empty(chans)) {
    496		line->chan_in = line->chan_out = NULL;
    497		free_chan(chans);
    498		INIT_LIST_HEAD(chans);
    499	}
    500
    501	if (!str)
    502		return 0;
    503
    504	out = strchr(str, ',');
    505	if (out != NULL) {
    506		in = str;
    507		*out = '\0';
    508		out++;
    509		new = parse_chan(line, in, device, opts, error_out);
    510		if (new == NULL)
    511			return -1;
    512
    513		new->input = 1;
    514		list_add(&new->list, chans);
    515		line->chan_in = new;
    516
    517		new = parse_chan(line, out, device, opts, error_out);
    518		if (new == NULL)
    519			return -1;
    520
    521		list_add(&new->list, chans);
    522		new->output = 1;
    523		line->chan_out = new;
    524	}
    525	else {
    526		new = parse_chan(line, str, device, opts, error_out);
    527		if (new == NULL)
    528			return -1;
    529
    530		list_add(&new->list, chans);
    531		new->input = 1;
    532		new->output = 1;
    533		line->chan_in = line->chan_out = new;
    534	}
    535	return 0;
    536}
    537
    538void chan_interrupt(struct line *line, int irq)
    539{
    540	struct tty_port *port = &line->port;
    541	struct chan *chan = line->chan_in;
    542	int err;
    543	char c;
    544
    545	if (!chan || !chan->ops->read)
    546		goto out;
    547
    548	do {
    549		if (!tty_buffer_request_room(port, 1)) {
    550			schedule_delayed_work(&line->task, 1);
    551			goto out;
    552		}
    553		err = chan->ops->read(chan->fd, &c, chan->data);
    554		if (err > 0)
    555			tty_insert_flip_char(port, c, TTY_NORMAL);
    556	} while (err > 0);
    557
    558	if (err == -EIO) {
    559		if (chan->primary) {
    560			tty_port_tty_hangup(&line->port, false);
    561			if (line->chan_out != chan)
    562				close_one_chan(line->chan_out, 1);
    563		}
    564		close_one_chan(chan, 1);
    565		if (chan->primary)
    566			return;
    567	}
    568 out:
    569	tty_flip_buffer_push(port);
    570}