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

sunhv.c (13718B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* sunhv.c: Serial driver for SUN4V hypervisor console.
      3 *
      4 * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
      5 */
      6
      7#include <linux/kernel.h>
      8#include <linux/errno.h>
      9#include <linux/tty.h>
     10#include <linux/tty_flip.h>
     11#include <linux/major.h>
     12#include <linux/circ_buf.h>
     13#include <linux/serial.h>
     14#include <linux/sysrq.h>
     15#include <linux/console.h>
     16#include <linux/spinlock.h>
     17#include <linux/slab.h>
     18#include <linux/delay.h>
     19#include <linux/init.h>
     20#include <linux/of_device.h>
     21
     22#include <asm/hypervisor.h>
     23#include <asm/spitfire.h>
     24#include <asm/prom.h>
     25#include <asm/irq.h>
     26#include <asm/setup.h>
     27
     28#include <linux/serial_core.h>
     29#include <linux/sunserialcore.h>
     30
     31#define CON_BREAK	((long)-1)
     32#define CON_HUP		((long)-2)
     33
     34#define IGNORE_BREAK	0x1
     35#define IGNORE_ALL	0x2
     36
     37static char *con_write_page;
     38static char *con_read_page;
     39
     40static int hung_up = 0;
     41
     42static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit)
     43{
     44	while (!uart_circ_empty(xmit)) {
     45		long status = sun4v_con_putchar(xmit->buf[xmit->tail]);
     46
     47		if (status != HV_EOK)
     48			break;
     49
     50		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
     51		port->icount.tx++;
     52	}
     53}
     54
     55static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit)
     56{
     57	while (!uart_circ_empty(xmit)) {
     58		unsigned long ra = __pa(xmit->buf + xmit->tail);
     59		unsigned long len, status, sent;
     60
     61		len = CIRC_CNT_TO_END(xmit->head, xmit->tail,
     62				      UART_XMIT_SIZE);
     63		status = sun4v_con_write(ra, len, &sent);
     64		if (status != HV_EOK)
     65			break;
     66		xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1);
     67		port->icount.tx += sent;
     68	}
     69}
     70
     71static int receive_chars_getchar(struct uart_port *port)
     72{
     73	int saw_console_brk = 0;
     74	int limit = 10000;
     75
     76	while (limit-- > 0) {
     77		long status;
     78		long c = sun4v_con_getchar(&status);
     79
     80		if (status == HV_EWOULDBLOCK)
     81			break;
     82
     83		if (c == CON_BREAK) {
     84			if (uart_handle_break(port))
     85				continue;
     86			saw_console_brk = 1;
     87			c = 0;
     88		}
     89
     90		if (c == CON_HUP) {
     91			hung_up = 1;
     92			uart_handle_dcd_change(port, 0);
     93		} else if (hung_up) {
     94			hung_up = 0;
     95			uart_handle_dcd_change(port, 1);
     96		}
     97
     98		if (port->state == NULL) {
     99			uart_handle_sysrq_char(port, c);
    100			continue;
    101		}
    102
    103		port->icount.rx++;
    104
    105		if (uart_handle_sysrq_char(port, c))
    106			continue;
    107
    108		tty_insert_flip_char(&port->state->port, c, TTY_NORMAL);
    109	}
    110
    111	return saw_console_brk;
    112}
    113
    114static int receive_chars_read(struct uart_port *port)
    115{
    116	static int saw_console_brk;
    117	int limit = 10000;
    118
    119	while (limit-- > 0) {
    120		unsigned long ra = __pa(con_read_page);
    121		unsigned long bytes_read, i;
    122		long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read);
    123
    124		if (stat != HV_EOK) {
    125			bytes_read = 0;
    126
    127			if (stat == CON_BREAK) {
    128				if (saw_console_brk)
    129					sun_do_break();
    130
    131				if (uart_handle_break(port))
    132					continue;
    133				saw_console_brk = 1;
    134				*con_read_page = 0;
    135				bytes_read = 1;
    136			} else if (stat == CON_HUP) {
    137				hung_up = 1;
    138				uart_handle_dcd_change(port, 0);
    139				continue;
    140			} else {
    141				/* HV_EWOULDBLOCK, etc.  */
    142				break;
    143			}
    144		}
    145
    146		if (hung_up) {
    147			hung_up = 0;
    148			uart_handle_dcd_change(port, 1);
    149		}
    150
    151		if (port->sysrq != 0 &&  *con_read_page) {
    152			for (i = 0; i < bytes_read; i++)
    153				uart_handle_sysrq_char(port, con_read_page[i]);
    154			saw_console_brk = 0;
    155		}
    156
    157		if (port->state == NULL)
    158			continue;
    159
    160		port->icount.rx += bytes_read;
    161
    162		tty_insert_flip_string(&port->state->port, con_read_page,
    163				bytes_read);
    164	}
    165
    166	return saw_console_brk;
    167}
    168
    169struct sunhv_ops {
    170	void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit);
    171	int (*receive_chars)(struct uart_port *port);
    172};
    173
    174static const struct sunhv_ops bychar_ops = {
    175	.transmit_chars = transmit_chars_putchar,
    176	.receive_chars = receive_chars_getchar,
    177};
    178
    179static const struct sunhv_ops bywrite_ops = {
    180	.transmit_chars = transmit_chars_write,
    181	.receive_chars = receive_chars_read,
    182};
    183
    184static const struct sunhv_ops *sunhv_ops = &bychar_ops;
    185
    186static struct tty_port *receive_chars(struct uart_port *port)
    187{
    188	struct tty_port *tport = NULL;
    189
    190	if (port->state != NULL)		/* Unopened serial console */
    191		tport = &port->state->port;
    192
    193	if (sunhv_ops->receive_chars(port))
    194		sun_do_break();
    195
    196	return tport;
    197}
    198
    199static void transmit_chars(struct uart_port *port)
    200{
    201	struct circ_buf *xmit;
    202
    203	if (!port->state)
    204		return;
    205
    206	xmit = &port->state->xmit;
    207	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
    208		return;
    209
    210	sunhv_ops->transmit_chars(port, xmit);
    211
    212	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    213		uart_write_wakeup(port);
    214}
    215
    216static irqreturn_t sunhv_interrupt(int irq, void *dev_id)
    217{
    218	struct uart_port *port = dev_id;
    219	struct tty_port *tport;
    220	unsigned long flags;
    221
    222	spin_lock_irqsave(&port->lock, flags);
    223	tport = receive_chars(port);
    224	transmit_chars(port);
    225	spin_unlock_irqrestore(&port->lock, flags);
    226
    227	if (tport)
    228		tty_flip_buffer_push(tport);
    229
    230	return IRQ_HANDLED;
    231}
    232
    233/* port->lock is not held.  */
    234static unsigned int sunhv_tx_empty(struct uart_port *port)
    235{
    236	/* Transmitter is always empty for us.  If the circ buffer
    237	 * is non-empty or there is an x_char pending, our caller
    238	 * will do the right thing and ignore what we return here.
    239	 */
    240	return TIOCSER_TEMT;
    241}
    242
    243/* port->lock held by caller.  */
    244static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl)
    245{
    246	return;
    247}
    248
    249/* port->lock is held by caller and interrupts are disabled.  */
    250static unsigned int sunhv_get_mctrl(struct uart_port *port)
    251{
    252	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
    253}
    254
    255/* port->lock held by caller.  */
    256static void sunhv_stop_tx(struct uart_port *port)
    257{
    258	return;
    259}
    260
    261/* port->lock held by caller.  */
    262static void sunhv_start_tx(struct uart_port *port)
    263{
    264	transmit_chars(port);
    265}
    266
    267/* port->lock is not held.  */
    268static void sunhv_send_xchar(struct uart_port *port, char ch)
    269{
    270	unsigned long flags;
    271	int limit = 10000;
    272
    273	if (ch == __DISABLED_CHAR)
    274		return;
    275
    276	spin_lock_irqsave(&port->lock, flags);
    277
    278	while (limit-- > 0) {
    279		long status = sun4v_con_putchar(ch);
    280		if (status == HV_EOK)
    281			break;
    282		udelay(1);
    283	}
    284
    285	spin_unlock_irqrestore(&port->lock, flags);
    286}
    287
    288/* port->lock held by caller.  */
    289static void sunhv_stop_rx(struct uart_port *port)
    290{
    291}
    292
    293/* port->lock is not held.  */
    294static void sunhv_break_ctl(struct uart_port *port, int break_state)
    295{
    296	if (break_state) {
    297		unsigned long flags;
    298		int limit = 10000;
    299
    300		spin_lock_irqsave(&port->lock, flags);
    301
    302		while (limit-- > 0) {
    303			long status = sun4v_con_putchar(CON_BREAK);
    304			if (status == HV_EOK)
    305				break;
    306			udelay(1);
    307		}
    308
    309		spin_unlock_irqrestore(&port->lock, flags);
    310	}
    311}
    312
    313/* port->lock is not held.  */
    314static int sunhv_startup(struct uart_port *port)
    315{
    316	return 0;
    317}
    318
    319/* port->lock is not held.  */
    320static void sunhv_shutdown(struct uart_port *port)
    321{
    322}
    323
    324/* port->lock is not held.  */
    325static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios,
    326			      struct ktermios *old)
    327{
    328	unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
    329	unsigned int quot = uart_get_divisor(port, baud);
    330	unsigned int iflag, cflag;
    331	unsigned long flags;
    332
    333	spin_lock_irqsave(&port->lock, flags);
    334
    335	iflag = termios->c_iflag;
    336	cflag = termios->c_cflag;
    337
    338	port->ignore_status_mask = 0;
    339	if (iflag & IGNBRK)
    340		port->ignore_status_mask |= IGNORE_BREAK;
    341	if ((cflag & CREAD) == 0)
    342		port->ignore_status_mask |= IGNORE_ALL;
    343
    344	/* XXX */
    345	uart_update_timeout(port, cflag,
    346			    (port->uartclk / (16 * quot)));
    347
    348	spin_unlock_irqrestore(&port->lock, flags);
    349}
    350
    351static const char *sunhv_type(struct uart_port *port)
    352{
    353	return "SUN4V HCONS";
    354}
    355
    356static void sunhv_release_port(struct uart_port *port)
    357{
    358}
    359
    360static int sunhv_request_port(struct uart_port *port)
    361{
    362	return 0;
    363}
    364
    365static void sunhv_config_port(struct uart_port *port, int flags)
    366{
    367}
    368
    369static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser)
    370{
    371	return -EINVAL;
    372}
    373
    374static const struct uart_ops sunhv_pops = {
    375	.tx_empty	= sunhv_tx_empty,
    376	.set_mctrl	= sunhv_set_mctrl,
    377	.get_mctrl	= sunhv_get_mctrl,
    378	.stop_tx	= sunhv_stop_tx,
    379	.start_tx	= sunhv_start_tx,
    380	.send_xchar	= sunhv_send_xchar,
    381	.stop_rx	= sunhv_stop_rx,
    382	.break_ctl	= sunhv_break_ctl,
    383	.startup	= sunhv_startup,
    384	.shutdown	= sunhv_shutdown,
    385	.set_termios	= sunhv_set_termios,
    386	.type		= sunhv_type,
    387	.release_port	= sunhv_release_port,
    388	.request_port	= sunhv_request_port,
    389	.config_port	= sunhv_config_port,
    390	.verify_port	= sunhv_verify_port,
    391};
    392
    393static struct uart_driver sunhv_reg = {
    394	.owner			= THIS_MODULE,
    395	.driver_name		= "sunhv",
    396	.dev_name		= "ttyHV",
    397	.major			= TTY_MAJOR,
    398};
    399
    400static struct uart_port *sunhv_port;
    401
    402void sunhv_migrate_hvcons_irq(int cpu)
    403{
    404	/* Migrate hvcons irq to param cpu */
    405	irq_force_affinity(sunhv_port->irq, cpumask_of(cpu));
    406}
    407
    408/* Copy 's' into the con_write_page, decoding "\n" into
    409 * "\r\n" along the way.  We have to return two lengths
    410 * because the caller needs to know how much to advance
    411 * 's' and also how many bytes to output via con_write_page.
    412 */
    413static int fill_con_write_page(const char *s, unsigned int n,
    414			       unsigned long *page_bytes)
    415{
    416	const char *orig_s = s;
    417	char *p = con_write_page;
    418	int left = PAGE_SIZE;
    419
    420	while (n--) {
    421		if (*s == '\n') {
    422			if (left < 2)
    423				break;
    424			*p++ = '\r';
    425			left--;
    426		} else if (left < 1)
    427			break;
    428		*p++ = *s++;
    429		left--;
    430	}
    431	*page_bytes = p - con_write_page;
    432	return s - orig_s;
    433}
    434
    435static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n)
    436{
    437	struct uart_port *port = sunhv_port;
    438	unsigned long flags;
    439	int locked = 1;
    440
    441	if (port->sysrq || oops_in_progress)
    442		locked = spin_trylock_irqsave(&port->lock, flags);
    443	else
    444		spin_lock_irqsave(&port->lock, flags);
    445
    446	while (n > 0) {
    447		unsigned long ra = __pa(con_write_page);
    448		unsigned long page_bytes;
    449		unsigned int cpy = fill_con_write_page(s, n,
    450						       &page_bytes);
    451
    452		n -= cpy;
    453		s += cpy;
    454		while (page_bytes > 0) {
    455			unsigned long written;
    456			int limit = 1000000;
    457
    458			while (limit--) {
    459				unsigned long stat;
    460
    461				stat = sun4v_con_write(ra, page_bytes,
    462						       &written);
    463				if (stat == HV_EOK)
    464					break;
    465				udelay(1);
    466			}
    467			if (limit < 0)
    468				break;
    469			page_bytes -= written;
    470			ra += written;
    471		}
    472	}
    473
    474	if (locked)
    475		spin_unlock_irqrestore(&port->lock, flags);
    476}
    477
    478static inline void sunhv_console_putchar(struct uart_port *port, char c)
    479{
    480	int limit = 1000000;
    481
    482	while (limit-- > 0) {
    483		long status = sun4v_con_putchar(c);
    484		if (status == HV_EOK)
    485			break;
    486		udelay(1);
    487	}
    488}
    489
    490static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n)
    491{
    492	struct uart_port *port = sunhv_port;
    493	unsigned long flags;
    494	int i, locked = 1;
    495
    496	if (port->sysrq || oops_in_progress)
    497		locked = spin_trylock_irqsave(&port->lock, flags);
    498	else
    499		spin_lock_irqsave(&port->lock, flags);
    500
    501	for (i = 0; i < n; i++) {
    502		if (*s == '\n')
    503			sunhv_console_putchar(port, '\r');
    504		sunhv_console_putchar(port, *s++);
    505	}
    506
    507	if (locked)
    508		spin_unlock_irqrestore(&port->lock, flags);
    509}
    510
    511static struct console sunhv_console = {
    512	.name	=	"ttyHV",
    513	.write	=	sunhv_console_write_bychar,
    514	.device	=	uart_console_device,
    515	.flags	=	CON_PRINTBUFFER,
    516	.index	=	-1,
    517	.data	=	&sunhv_reg,
    518};
    519
    520static int hv_probe(struct platform_device *op)
    521{
    522	struct uart_port *port;
    523	unsigned long minor;
    524	int err;
    525
    526	if (op->archdata.irqs[0] == 0xffffffff)
    527		return -ENODEV;
    528
    529	port = kzalloc(sizeof(struct uart_port), GFP_KERNEL);
    530	if (unlikely(!port))
    531		return -ENOMEM;
    532
    533	minor = 1;
    534	if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 &&
    535	    minor >= 1) {
    536		err = -ENOMEM;
    537		con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
    538		if (!con_write_page)
    539			goto out_free_port;
    540
    541		con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
    542		if (!con_read_page)
    543			goto out_free_con_write_page;
    544
    545		sunhv_console.write = sunhv_console_write_paged;
    546		sunhv_ops = &bywrite_ops;
    547	}
    548
    549	sunhv_port = port;
    550
    551	port->has_sysrq = 1;
    552	port->line = 0;
    553	port->ops = &sunhv_pops;
    554	port->type = PORT_SUNHV;
    555	port->uartclk = ( 29491200 / 16 ); /* arbitrary */
    556
    557	port->membase = (unsigned char __iomem *) __pa(port);
    558
    559	port->irq = op->archdata.irqs[0];
    560
    561	port->dev = &op->dev;
    562
    563	err = sunserial_register_minors(&sunhv_reg, 1);
    564	if (err)
    565		goto out_free_con_read_page;
    566
    567	sunserial_console_match(&sunhv_console, op->dev.of_node,
    568				&sunhv_reg, port->line, false);
    569
    570	err = uart_add_one_port(&sunhv_reg, port);
    571	if (err)
    572		goto out_unregister_driver;
    573
    574	err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port);
    575	if (err)
    576		goto out_remove_port;
    577
    578	platform_set_drvdata(op, port);
    579
    580	return 0;
    581
    582out_remove_port:
    583	uart_remove_one_port(&sunhv_reg, port);
    584
    585out_unregister_driver:
    586	sunserial_unregister_minors(&sunhv_reg, 1);
    587
    588out_free_con_read_page:
    589	kfree(con_read_page);
    590
    591out_free_con_write_page:
    592	kfree(con_write_page);
    593
    594out_free_port:
    595	kfree(port);
    596	sunhv_port = NULL;
    597	return err;
    598}
    599
    600static int hv_remove(struct platform_device *dev)
    601{
    602	struct uart_port *port = platform_get_drvdata(dev);
    603
    604	free_irq(port->irq, port);
    605
    606	uart_remove_one_port(&sunhv_reg, port);
    607
    608	sunserial_unregister_minors(&sunhv_reg, 1);
    609	kfree(con_read_page);
    610	kfree(con_write_page);
    611	kfree(port);
    612	sunhv_port = NULL;
    613
    614	return 0;
    615}
    616
    617static const struct of_device_id hv_match[] = {
    618	{
    619		.name = "console",
    620		.compatible = "qcn",
    621	},
    622	{
    623		.name = "console",
    624		.compatible = "SUNW,sun4v-console",
    625	},
    626	{},
    627};
    628
    629static struct platform_driver hv_driver = {
    630	.driver = {
    631		.name = "hv",
    632		.of_match_table = hv_match,
    633	},
    634	.probe		= hv_probe,
    635	.remove		= hv_remove,
    636};
    637
    638static int __init sunhv_init(void)
    639{
    640	if (tlb_type != hypervisor)
    641		return -ENODEV;
    642
    643	return platform_driver_register(&hv_driver);
    644}
    645device_initcall(sunhv_init);
    646
    647#if 0 /* ...def MODULE ; never supported as such */
    648MODULE_AUTHOR("David S. Miller");
    649MODULE_DESCRIPTION("SUN4V Hypervisor console driver");
    650MODULE_VERSION("2.0");
    651MODULE_LICENSE("GPL");
    652#endif