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

bt-bmc.c (10741B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (c) 2015-2016, IBM Corporation.
      4 */
      5
      6#include <linux/atomic.h>
      7#include <linux/bt-bmc.h>
      8#include <linux/errno.h>
      9#include <linux/interrupt.h>
     10#include <linux/io.h>
     11#include <linux/miscdevice.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/platform_device.h>
     15#include <linux/poll.h>
     16#include <linux/sched.h>
     17#include <linux/timer.h>
     18
     19/*
     20 * This is a BMC device used to communicate to the host
     21 */
     22#define DEVICE_NAME	"ipmi-bt-host"
     23
     24#define BT_IO_BASE	0xe4
     25#define BT_IRQ		10
     26
     27#define BT_CR0		0x0
     28#define   BT_CR0_IO_BASE		16
     29#define   BT_CR0_IRQ			12
     30#define   BT_CR0_EN_CLR_SLV_RDP		0x8
     31#define   BT_CR0_EN_CLR_SLV_WRP		0x4
     32#define   BT_CR0_ENABLE_IBT		0x1
     33#define BT_CR1		0x4
     34#define   BT_CR1_IRQ_H2B	0x01
     35#define   BT_CR1_IRQ_HBUSY	0x40
     36#define BT_CR2		0x8
     37#define   BT_CR2_IRQ_H2B	0x01
     38#define   BT_CR2_IRQ_HBUSY	0x40
     39#define BT_CR3		0xc
     40#define BT_CTRL		0x10
     41#define   BT_CTRL_B_BUSY		0x80
     42#define   BT_CTRL_H_BUSY		0x40
     43#define   BT_CTRL_OEM0			0x20
     44#define   BT_CTRL_SMS_ATN		0x10
     45#define   BT_CTRL_B2H_ATN		0x08
     46#define   BT_CTRL_H2B_ATN		0x04
     47#define   BT_CTRL_CLR_RD_PTR		0x02
     48#define   BT_CTRL_CLR_WR_PTR		0x01
     49#define BT_BMC2HOST	0x14
     50#define BT_INTMASK	0x18
     51#define   BT_INTMASK_B2H_IRQEN		0x01
     52#define   BT_INTMASK_B2H_IRQ		0x02
     53#define   BT_INTMASK_BMC_HWRST		0x80
     54
     55#define BT_BMC_BUFFER_SIZE 256
     56
     57struct bt_bmc {
     58	struct device		dev;
     59	struct miscdevice	miscdev;
     60	void __iomem		*base;
     61	int			irq;
     62	wait_queue_head_t	queue;
     63	struct timer_list	poll_timer;
     64	struct mutex		mutex;
     65};
     66
     67static atomic_t open_count = ATOMIC_INIT(0);
     68
     69static u8 bt_inb(struct bt_bmc *bt_bmc, int reg)
     70{
     71	return readb(bt_bmc->base + reg);
     72}
     73
     74static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg)
     75{
     76	writeb(data, bt_bmc->base + reg);
     77}
     78
     79static void clr_rd_ptr(struct bt_bmc *bt_bmc)
     80{
     81	bt_outb(bt_bmc, BT_CTRL_CLR_RD_PTR, BT_CTRL);
     82}
     83
     84static void clr_wr_ptr(struct bt_bmc *bt_bmc)
     85{
     86	bt_outb(bt_bmc, BT_CTRL_CLR_WR_PTR, BT_CTRL);
     87}
     88
     89static void clr_h2b_atn(struct bt_bmc *bt_bmc)
     90{
     91	bt_outb(bt_bmc, BT_CTRL_H2B_ATN, BT_CTRL);
     92}
     93
     94static void set_b_busy(struct bt_bmc *bt_bmc)
     95{
     96	if (!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_B_BUSY))
     97		bt_outb(bt_bmc, BT_CTRL_B_BUSY, BT_CTRL);
     98}
     99
    100static void clr_b_busy(struct bt_bmc *bt_bmc)
    101{
    102	if (bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_B_BUSY)
    103		bt_outb(bt_bmc, BT_CTRL_B_BUSY, BT_CTRL);
    104}
    105
    106static void set_b2h_atn(struct bt_bmc *bt_bmc)
    107{
    108	bt_outb(bt_bmc, BT_CTRL_B2H_ATN, BT_CTRL);
    109}
    110
    111static u8 bt_read(struct bt_bmc *bt_bmc)
    112{
    113	return bt_inb(bt_bmc, BT_BMC2HOST);
    114}
    115
    116static ssize_t bt_readn(struct bt_bmc *bt_bmc, u8 *buf, size_t n)
    117{
    118	int i;
    119
    120	for (i = 0; i < n; i++)
    121		buf[i] = bt_read(bt_bmc);
    122	return n;
    123}
    124
    125static void bt_write(struct bt_bmc *bt_bmc, u8 c)
    126{
    127	bt_outb(bt_bmc, c, BT_BMC2HOST);
    128}
    129
    130static ssize_t bt_writen(struct bt_bmc *bt_bmc, u8 *buf, size_t n)
    131{
    132	int i;
    133
    134	for (i = 0; i < n; i++)
    135		bt_write(bt_bmc, buf[i]);
    136	return n;
    137}
    138
    139static void set_sms_atn(struct bt_bmc *bt_bmc)
    140{
    141	bt_outb(bt_bmc, BT_CTRL_SMS_ATN, BT_CTRL);
    142}
    143
    144static struct bt_bmc *file_bt_bmc(struct file *file)
    145{
    146	return container_of(file->private_data, struct bt_bmc, miscdev);
    147}
    148
    149static int bt_bmc_open(struct inode *inode, struct file *file)
    150{
    151	struct bt_bmc *bt_bmc = file_bt_bmc(file);
    152
    153	if (atomic_inc_return(&open_count) == 1) {
    154		clr_b_busy(bt_bmc);
    155		return 0;
    156	}
    157
    158	atomic_dec(&open_count);
    159	return -EBUSY;
    160}
    161
    162/*
    163 * The BT (Block Transfer) interface means that entire messages are
    164 * buffered by the host before a notification is sent to the BMC that
    165 * there is data to be read. The first byte is the length and the
    166 * message data follows. The read operation just tries to capture the
    167 * whole before returning it to userspace.
    168 *
    169 * BT Message format :
    170 *
    171 *    Byte 1  Byte 2     Byte 3  Byte 4  Byte 5:N
    172 *    Length  NetFn/LUN  Seq     Cmd     Data
    173 *
    174 */
    175static ssize_t bt_bmc_read(struct file *file, char __user *buf,
    176			   size_t count, loff_t *ppos)
    177{
    178	struct bt_bmc *bt_bmc = file_bt_bmc(file);
    179	u8 len;
    180	int len_byte = 1;
    181	u8 kbuffer[BT_BMC_BUFFER_SIZE];
    182	ssize_t ret = 0;
    183	ssize_t nread;
    184
    185	WARN_ON(*ppos);
    186
    187	if (wait_event_interruptible(bt_bmc->queue,
    188				     bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN))
    189		return -ERESTARTSYS;
    190
    191	mutex_lock(&bt_bmc->mutex);
    192
    193	if (unlikely(!(bt_inb(bt_bmc, BT_CTRL) & BT_CTRL_H2B_ATN))) {
    194		ret = -EIO;
    195		goto out_unlock;
    196	}
    197
    198	set_b_busy(bt_bmc);
    199	clr_h2b_atn(bt_bmc);
    200	clr_rd_ptr(bt_bmc);
    201
    202	/*
    203	 * The BT frames start with the message length, which does not
    204	 * include the length byte.
    205	 */
    206	kbuffer[0] = bt_read(bt_bmc);
    207	len = kbuffer[0];
    208
    209	/* We pass the length back to userspace as well */
    210	if (len + 1 > count)
    211		len = count - 1;
    212
    213	while (len) {
    214		nread = min_t(ssize_t, len, sizeof(kbuffer) - len_byte);
    215
    216		bt_readn(bt_bmc, kbuffer + len_byte, nread);
    217
    218		if (copy_to_user(buf, kbuffer, nread + len_byte)) {
    219			ret = -EFAULT;
    220			break;
    221		}
    222		len -= nread;
    223		buf += nread + len_byte;
    224		ret += nread + len_byte;
    225		len_byte = 0;
    226	}
    227
    228	clr_b_busy(bt_bmc);
    229
    230out_unlock:
    231	mutex_unlock(&bt_bmc->mutex);
    232	return ret;
    233}
    234
    235/*
    236 * BT Message response format :
    237 *
    238 *    Byte 1  Byte 2     Byte 3  Byte 4  Byte 5  Byte 6:N
    239 *    Length  NetFn/LUN  Seq     Cmd     Code    Data
    240 */
    241static ssize_t bt_bmc_write(struct file *file, const char __user *buf,
    242			    size_t count, loff_t *ppos)
    243{
    244	struct bt_bmc *bt_bmc = file_bt_bmc(file);
    245	u8 kbuffer[BT_BMC_BUFFER_SIZE];
    246	ssize_t ret = 0;
    247	ssize_t nwritten;
    248
    249	/*
    250	 * send a minimum response size
    251	 */
    252	if (count < 5)
    253		return -EINVAL;
    254
    255	WARN_ON(*ppos);
    256
    257	/*
    258	 * There's no interrupt for clearing bmc busy so we have to
    259	 * poll
    260	 */
    261	if (wait_event_interruptible(bt_bmc->queue,
    262				     !(bt_inb(bt_bmc, BT_CTRL) &
    263				       (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))))
    264		return -ERESTARTSYS;
    265
    266	mutex_lock(&bt_bmc->mutex);
    267
    268	if (unlikely(bt_inb(bt_bmc, BT_CTRL) &
    269		     (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN))) {
    270		ret = -EIO;
    271		goto out_unlock;
    272	}
    273
    274	clr_wr_ptr(bt_bmc);
    275
    276	while (count) {
    277		nwritten = min_t(ssize_t, count, sizeof(kbuffer));
    278		if (copy_from_user(&kbuffer, buf, nwritten)) {
    279			ret = -EFAULT;
    280			break;
    281		}
    282
    283		bt_writen(bt_bmc, kbuffer, nwritten);
    284
    285		count -= nwritten;
    286		buf += nwritten;
    287		ret += nwritten;
    288	}
    289
    290	set_b2h_atn(bt_bmc);
    291
    292out_unlock:
    293	mutex_unlock(&bt_bmc->mutex);
    294	return ret;
    295}
    296
    297static long bt_bmc_ioctl(struct file *file, unsigned int cmd,
    298			 unsigned long param)
    299{
    300	struct bt_bmc *bt_bmc = file_bt_bmc(file);
    301
    302	switch (cmd) {
    303	case BT_BMC_IOCTL_SMS_ATN:
    304		set_sms_atn(bt_bmc);
    305		return 0;
    306	}
    307	return -EINVAL;
    308}
    309
    310static int bt_bmc_release(struct inode *inode, struct file *file)
    311{
    312	struct bt_bmc *bt_bmc = file_bt_bmc(file);
    313
    314	atomic_dec(&open_count);
    315	set_b_busy(bt_bmc);
    316	return 0;
    317}
    318
    319static __poll_t bt_bmc_poll(struct file *file, poll_table *wait)
    320{
    321	struct bt_bmc *bt_bmc = file_bt_bmc(file);
    322	__poll_t mask = 0;
    323	u8 ctrl;
    324
    325	poll_wait(file, &bt_bmc->queue, wait);
    326
    327	ctrl = bt_inb(bt_bmc, BT_CTRL);
    328
    329	if (ctrl & BT_CTRL_H2B_ATN)
    330		mask |= EPOLLIN;
    331
    332	if (!(ctrl & (BT_CTRL_H_BUSY | BT_CTRL_B2H_ATN)))
    333		mask |= EPOLLOUT;
    334
    335	return mask;
    336}
    337
    338static const struct file_operations bt_bmc_fops = {
    339	.owner		= THIS_MODULE,
    340	.open		= bt_bmc_open,
    341	.read		= bt_bmc_read,
    342	.write		= bt_bmc_write,
    343	.release	= bt_bmc_release,
    344	.poll		= bt_bmc_poll,
    345	.unlocked_ioctl	= bt_bmc_ioctl,
    346};
    347
    348static void poll_timer(struct timer_list *t)
    349{
    350	struct bt_bmc *bt_bmc = from_timer(bt_bmc, t, poll_timer);
    351
    352	bt_bmc->poll_timer.expires += msecs_to_jiffies(500);
    353	wake_up(&bt_bmc->queue);
    354	add_timer(&bt_bmc->poll_timer);
    355}
    356
    357static irqreturn_t bt_bmc_irq(int irq, void *arg)
    358{
    359	struct bt_bmc *bt_bmc = arg;
    360	u32 reg;
    361
    362	reg = readl(bt_bmc->base + BT_CR2);
    363
    364	reg &= BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY;
    365	if (!reg)
    366		return IRQ_NONE;
    367
    368	/* ack pending IRQs */
    369	writel(reg, bt_bmc->base + BT_CR2);
    370
    371	wake_up(&bt_bmc->queue);
    372	return IRQ_HANDLED;
    373}
    374
    375static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
    376			     struct platform_device *pdev)
    377{
    378	struct device *dev = &pdev->dev;
    379	int rc;
    380	u32 reg;
    381
    382	bt_bmc->irq = platform_get_irq_optional(pdev, 0);
    383	if (bt_bmc->irq < 0)
    384		return bt_bmc->irq;
    385
    386	rc = devm_request_irq(dev, bt_bmc->irq, bt_bmc_irq, IRQF_SHARED,
    387			      DEVICE_NAME, bt_bmc);
    388	if (rc < 0) {
    389		dev_warn(dev, "Unable to request IRQ %d\n", bt_bmc->irq);
    390		bt_bmc->irq = rc;
    391		return rc;
    392	}
    393
    394	/*
    395	 * Configure IRQs on the bmc clearing the H2B and HBUSY bits;
    396	 * H2B will be asserted when the bmc has data for us; HBUSY
    397	 * will be cleared (along with B2H) when we can write the next
    398	 * message to the BT buffer
    399	 */
    400	reg = readl(bt_bmc->base + BT_CR1);
    401	reg |= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY;
    402	writel(reg, bt_bmc->base + BT_CR1);
    403
    404	return 0;
    405}
    406
    407static int bt_bmc_probe(struct platform_device *pdev)
    408{
    409	struct bt_bmc *bt_bmc;
    410	struct device *dev;
    411	int rc;
    412
    413	dev = &pdev->dev;
    414	dev_info(dev, "Found bt bmc device\n");
    415
    416	bt_bmc = devm_kzalloc(dev, sizeof(*bt_bmc), GFP_KERNEL);
    417	if (!bt_bmc)
    418		return -ENOMEM;
    419
    420	dev_set_drvdata(&pdev->dev, bt_bmc);
    421
    422	bt_bmc->base = devm_platform_ioremap_resource(pdev, 0);
    423	if (IS_ERR(bt_bmc->base))
    424		return PTR_ERR(bt_bmc->base);
    425
    426	mutex_init(&bt_bmc->mutex);
    427	init_waitqueue_head(&bt_bmc->queue);
    428
    429	bt_bmc->miscdev.minor	= MISC_DYNAMIC_MINOR;
    430	bt_bmc->miscdev.name	= DEVICE_NAME;
    431	bt_bmc->miscdev.fops	= &bt_bmc_fops;
    432	bt_bmc->miscdev.parent = dev;
    433	rc = misc_register(&bt_bmc->miscdev);
    434	if (rc) {
    435		dev_err(dev, "Unable to register misc device\n");
    436		return rc;
    437	}
    438
    439	bt_bmc_config_irq(bt_bmc, pdev);
    440
    441	if (bt_bmc->irq >= 0) {
    442		dev_info(dev, "Using IRQ %d\n", bt_bmc->irq);
    443	} else {
    444		dev_info(dev, "No IRQ; using timer\n");
    445		timer_setup(&bt_bmc->poll_timer, poll_timer, 0);
    446		bt_bmc->poll_timer.expires = jiffies + msecs_to_jiffies(10);
    447		add_timer(&bt_bmc->poll_timer);
    448	}
    449
    450	writel((BT_IO_BASE << BT_CR0_IO_BASE) |
    451		     (BT_IRQ << BT_CR0_IRQ) |
    452		     BT_CR0_EN_CLR_SLV_RDP |
    453		     BT_CR0_EN_CLR_SLV_WRP |
    454		     BT_CR0_ENABLE_IBT,
    455		bt_bmc->base + BT_CR0);
    456
    457	clr_b_busy(bt_bmc);
    458
    459	return 0;
    460}
    461
    462static int bt_bmc_remove(struct platform_device *pdev)
    463{
    464	struct bt_bmc *bt_bmc = dev_get_drvdata(&pdev->dev);
    465
    466	misc_deregister(&bt_bmc->miscdev);
    467	if (bt_bmc->irq < 0)
    468		del_timer_sync(&bt_bmc->poll_timer);
    469	return 0;
    470}
    471
    472static const struct of_device_id bt_bmc_match[] = {
    473	{ .compatible = "aspeed,ast2400-ibt-bmc" },
    474	{ .compatible = "aspeed,ast2500-ibt-bmc" },
    475	{ .compatible = "aspeed,ast2600-ibt-bmc" },
    476	{ },
    477};
    478
    479static struct platform_driver bt_bmc_driver = {
    480	.driver = {
    481		.name		= DEVICE_NAME,
    482		.of_match_table = bt_bmc_match,
    483	},
    484	.probe = bt_bmc_probe,
    485	.remove = bt_bmc_remove,
    486};
    487
    488module_platform_driver(bt_bmc_driver);
    489
    490MODULE_DEVICE_TABLE(of, bt_bmc_match);
    491MODULE_LICENSE("GPL");
    492MODULE_AUTHOR("Alistair Popple <alistair@popple.id.au>");
    493MODULE_DESCRIPTION("Linux device interface to the IPMI BT interface");