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

bcm_vk_tty.c (8379B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2018-2020 Broadcom.
      4 */
      5
      6#include <linux/tty.h>
      7#include <linux/tty_driver.h>
      8#include <linux/tty_flip.h>
      9
     10#include "bcm_vk.h"
     11
     12/* TTYVK base offset is 0x30000 into BAR1 */
     13#define BAR1_TTYVK_BASE_OFFSET	0x300000
     14/* Each TTYVK channel (TO or FROM) is 0x10000 */
     15#define BAR1_TTYVK_CHAN_OFFSET	0x100000
     16/* Each TTYVK channel has TO and FROM, hence the * 2 */
     17#define BAR1_TTYVK_BASE(index)	(BAR1_TTYVK_BASE_OFFSET + \
     18				 ((index) * BAR1_TTYVK_CHAN_OFFSET * 2))
     19/* TO TTYVK channel base comes before FROM for each index */
     20#define TO_TTYK_BASE(index)	BAR1_TTYVK_BASE(index)
     21#define FROM_TTYK_BASE(index)	(BAR1_TTYVK_BASE(index) + \
     22				 BAR1_TTYVK_CHAN_OFFSET)
     23
     24struct bcm_vk_tty_chan {
     25	u32 reserved;
     26	u32 size;
     27	u32 wr;
     28	u32 rd;
     29	u32 *data;
     30};
     31
     32#define VK_BAR_CHAN(v, DIR, e)	((v)->DIR##_offset \
     33				 + offsetof(struct bcm_vk_tty_chan, e))
     34#define VK_BAR_CHAN_SIZE(v, DIR)	VK_BAR_CHAN(v, DIR, size)
     35#define VK_BAR_CHAN_WR(v, DIR)		VK_BAR_CHAN(v, DIR, wr)
     36#define VK_BAR_CHAN_RD(v, DIR)		VK_BAR_CHAN(v, DIR, rd)
     37#define VK_BAR_CHAN_DATA(v, DIR, off)	(VK_BAR_CHAN(v, DIR, data) + (off))
     38
     39#define VK_BAR0_REGSEG_TTY_DB_OFFSET	0x86c
     40
     41/* Poll every 1/10 of second - temp hack till we use MSI interrupt */
     42#define SERIAL_TIMER_VALUE (HZ / 10)
     43
     44static void bcm_vk_tty_poll(struct timer_list *t)
     45{
     46	struct bcm_vk *vk = from_timer(vk, t, serial_timer);
     47
     48	queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
     49	mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
     50}
     51
     52irqreturn_t bcm_vk_tty_irqhandler(int irq, void *dev_id)
     53{
     54	struct bcm_vk *vk = dev_id;
     55
     56	queue_work(vk->tty_wq_thread, &vk->tty_wq_work);
     57
     58	return IRQ_HANDLED;
     59}
     60
     61static void bcm_vk_tty_wq_handler(struct work_struct *work)
     62{
     63	struct bcm_vk *vk = container_of(work, struct bcm_vk, tty_wq_work);
     64	struct bcm_vk_tty *vktty;
     65	int card_status;
     66	int count;
     67	unsigned char c;
     68	int i;
     69	int wr;
     70
     71	card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
     72	if (BCM_VK_INTF_IS_DOWN(card_status))
     73		return;
     74
     75	for (i = 0; i < BCM_VK_NUM_TTY; i++) {
     76		count = 0;
     77		/* Check the card status that the tty channel is ready */
     78		if ((card_status & BIT(i)) == 0)
     79			continue;
     80
     81		vktty = &vk->tty[i];
     82
     83		/* Don't increment read index if tty app is closed */
     84		if (!vktty->is_opened)
     85			continue;
     86
     87		/* Fetch the wr offset in buffer from VK */
     88		wr = vkread32(vk, BAR_1, VK_BAR_CHAN_WR(vktty, from));
     89
     90		/* safe to ignore until bar read gives proper size */
     91		if (vktty->from_size == 0)
     92			continue;
     93
     94		if (wr >= vktty->from_size) {
     95			dev_err(&vk->pdev->dev,
     96				"ERROR: wq handler ttyVK%d wr:0x%x > 0x%x\n",
     97				i, wr, vktty->from_size);
     98			/* Need to signal and close device in this case */
     99			continue;
    100		}
    101
    102		/*
    103		 * Simple read of circular buffer and
    104		 * insert into tty flip buffer
    105		 */
    106		while (vk->tty[i].rd != wr) {
    107			c = vkread8(vk, BAR_1,
    108				    VK_BAR_CHAN_DATA(vktty, from, vktty->rd));
    109			vktty->rd++;
    110			if (vktty->rd >= vktty->from_size)
    111				vktty->rd = 0;
    112			tty_insert_flip_char(&vktty->port, c, TTY_NORMAL);
    113			count++;
    114		}
    115
    116		if (count) {
    117			tty_flip_buffer_push(&vktty->port);
    118
    119			/* Update read offset from shadow register to card */
    120			vkwrite32(vk, vktty->rd, BAR_1,
    121				  VK_BAR_CHAN_RD(vktty, from));
    122		}
    123	}
    124}
    125
    126static int bcm_vk_tty_open(struct tty_struct *tty, struct file *file)
    127{
    128	int card_status;
    129	struct bcm_vk *vk;
    130	struct bcm_vk_tty *vktty;
    131	int index;
    132
    133	/* initialize the pointer in case something fails */
    134	tty->driver_data = NULL;
    135
    136	vk = (struct bcm_vk *)dev_get_drvdata(tty->dev);
    137	index = tty->index;
    138
    139	if (index >= BCM_VK_NUM_TTY)
    140		return -EINVAL;
    141
    142	vktty = &vk->tty[index];
    143
    144	vktty->pid = task_pid_nr(current);
    145	vktty->to_offset = TO_TTYK_BASE(index);
    146	vktty->from_offset = FROM_TTYK_BASE(index);
    147
    148	/* Do not allow tty device to be opened if tty on card not ready */
    149	card_status = vkread32(vk, BAR_0, BAR_CARD_STATUS);
    150	if (BCM_VK_INTF_IS_DOWN(card_status) || ((card_status & BIT(index)) == 0))
    151		return -EBUSY;
    152
    153	/*
    154	 * Get shadow registers of the buffer sizes and the "to" write offset
    155	 * and "from" read offset
    156	 */
    157	vktty->to_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, to));
    158	vktty->wr = vkread32(vk, BAR_1,  VK_BAR_CHAN_WR(vktty, to));
    159	vktty->from_size = vkread32(vk, BAR_1, VK_BAR_CHAN_SIZE(vktty, from));
    160	vktty->rd = vkread32(vk, BAR_1,  VK_BAR_CHAN_RD(vktty, from));
    161	vktty->is_opened = true;
    162
    163	if (tty->count == 1 && !vktty->irq_enabled) {
    164		timer_setup(&vk->serial_timer, bcm_vk_tty_poll, 0);
    165		mod_timer(&vk->serial_timer, jiffies + SERIAL_TIMER_VALUE);
    166	}
    167	return 0;
    168}
    169
    170static void bcm_vk_tty_close(struct tty_struct *tty, struct file *file)
    171{
    172	struct bcm_vk *vk = dev_get_drvdata(tty->dev);
    173
    174	if (tty->index >= BCM_VK_NUM_TTY)
    175		return;
    176
    177	vk->tty[tty->index].is_opened = false;
    178
    179	if (tty->count == 1)
    180		del_timer_sync(&vk->serial_timer);
    181}
    182
    183static void bcm_vk_tty_doorbell(struct bcm_vk *vk, u32 db_val)
    184{
    185	vkwrite32(vk, db_val, BAR_0,
    186		  VK_BAR0_REGSEG_DB_BASE + VK_BAR0_REGSEG_TTY_DB_OFFSET);
    187}
    188
    189static int bcm_vk_tty_write(struct tty_struct *tty,
    190			    const unsigned char *buffer,
    191			    int count)
    192{
    193	int index;
    194	struct bcm_vk *vk;
    195	struct bcm_vk_tty *vktty;
    196	int i;
    197
    198	index = tty->index;
    199	vk = dev_get_drvdata(tty->dev);
    200	vktty = &vk->tty[index];
    201
    202	/* Simple write each byte to circular buffer */
    203	for (i = 0; i < count; i++) {
    204		vkwrite8(vk, buffer[i], BAR_1,
    205			 VK_BAR_CHAN_DATA(vktty, to, vktty->wr));
    206		vktty->wr++;
    207		if (vktty->wr >= vktty->to_size)
    208			vktty->wr = 0;
    209	}
    210	/* Update write offset from shadow register to card */
    211	vkwrite32(vk, vktty->wr, BAR_1, VK_BAR_CHAN_WR(vktty, to));
    212	bcm_vk_tty_doorbell(vk, 0);
    213
    214	return count;
    215}
    216
    217static unsigned int bcm_vk_tty_write_room(struct tty_struct *tty)
    218{
    219	struct bcm_vk *vk = dev_get_drvdata(tty->dev);
    220
    221	return vk->tty[tty->index].to_size - 1;
    222}
    223
    224static const struct tty_operations serial_ops = {
    225	.open = bcm_vk_tty_open,
    226	.close = bcm_vk_tty_close,
    227	.write = bcm_vk_tty_write,
    228	.write_room = bcm_vk_tty_write_room,
    229};
    230
    231int bcm_vk_tty_init(struct bcm_vk *vk, char *name)
    232{
    233	int i;
    234	int err;
    235	struct tty_driver *tty_drv;
    236	struct device *dev = &vk->pdev->dev;
    237
    238	tty_drv = tty_alloc_driver
    239				(BCM_VK_NUM_TTY,
    240				 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
    241	if (IS_ERR(tty_drv))
    242		return PTR_ERR(tty_drv);
    243
    244	/* Save struct tty_driver for uninstalling the device */
    245	vk->tty_drv = tty_drv;
    246
    247	/* initialize the tty driver */
    248	tty_drv->driver_name = KBUILD_MODNAME;
    249	tty_drv->name = kstrdup(name, GFP_KERNEL);
    250	if (!tty_drv->name) {
    251		err = -ENOMEM;
    252		goto err_tty_driver_kref_put;
    253	}
    254	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
    255	tty_drv->subtype = SERIAL_TYPE_NORMAL;
    256	tty_drv->init_termios = tty_std_termios;
    257	tty_set_operations(tty_drv, &serial_ops);
    258
    259	/* register the tty driver */
    260	err = tty_register_driver(tty_drv);
    261	if (err) {
    262		dev_err(dev, "tty_register_driver failed\n");
    263		goto err_kfree_tty_name;
    264	}
    265
    266	for (i = 0; i < BCM_VK_NUM_TTY; i++) {
    267		struct device *tty_dev;
    268
    269		tty_port_init(&vk->tty[i].port);
    270		tty_dev = tty_port_register_device_attr(&vk->tty[i].port,
    271							tty_drv, i, dev, vk,
    272							NULL);
    273		if (IS_ERR(tty_dev)) {
    274			err = PTR_ERR(tty_dev);
    275			goto unwind;
    276		}
    277		vk->tty[i].is_opened = false;
    278	}
    279
    280	INIT_WORK(&vk->tty_wq_work, bcm_vk_tty_wq_handler);
    281	vk->tty_wq_thread = create_singlethread_workqueue("tty");
    282	if (!vk->tty_wq_thread) {
    283		dev_err(dev, "Fail to create tty workqueue thread\n");
    284		err = -ENOMEM;
    285		goto unwind;
    286	}
    287	return 0;
    288
    289unwind:
    290	while (--i >= 0)
    291		tty_port_unregister_device(&vk->tty[i].port, tty_drv, i);
    292	tty_unregister_driver(tty_drv);
    293
    294err_kfree_tty_name:
    295	kfree(tty_drv->name);
    296	tty_drv->name = NULL;
    297
    298err_tty_driver_kref_put:
    299	tty_driver_kref_put(tty_drv);
    300
    301	return err;
    302}
    303
    304void bcm_vk_tty_exit(struct bcm_vk *vk)
    305{
    306	int i;
    307
    308	del_timer_sync(&vk->serial_timer);
    309	for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
    310		tty_port_unregister_device(&vk->tty[i].port,
    311					   vk->tty_drv,
    312					   i);
    313		tty_port_destroy(&vk->tty[i].port);
    314	}
    315	tty_unregister_driver(vk->tty_drv);
    316
    317	kfree(vk->tty_drv->name);
    318	vk->tty_drv->name = NULL;
    319
    320	tty_driver_kref_put(vk->tty_drv);
    321}
    322
    323void bcm_vk_tty_terminate_tty_user(struct bcm_vk *vk)
    324{
    325	struct bcm_vk_tty *vktty;
    326	int i;
    327
    328	for (i = 0; i < BCM_VK_NUM_TTY; ++i) {
    329		vktty = &vk->tty[i];
    330		if (vktty->pid)
    331			kill_pid(find_vpid(vktty->pid), SIGKILL, 1);
    332	}
    333}
    334
    335void bcm_vk_tty_wq_exit(struct bcm_vk *vk)
    336{
    337	cancel_work_sync(&vk->tty_wq_work);
    338	destroy_workqueue(vk->tty_wq_thread);
    339}