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

fsi-scom.c (14600B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * SCOM FSI Client device driver
      4 *
      5 * Copyright (C) IBM Corporation 2016
      6 */
      7
      8#include <linux/fsi.h>
      9#include <linux/module.h>
     10#include <linux/cdev.h>
     11#include <linux/delay.h>
     12#include <linux/fs.h>
     13#include <linux/uaccess.h>
     14#include <linux/slab.h>
     15#include <linux/list.h>
     16
     17#include <uapi/linux/fsi.h>
     18
     19#define FSI_ENGID_SCOM		0x5
     20
     21/* SCOM engine register set */
     22#define SCOM_DATA0_REG		0x00
     23#define SCOM_DATA1_REG		0x04
     24#define SCOM_CMD_REG		0x08
     25#define SCOM_FSI2PIB_RESET_REG	0x18
     26#define SCOM_STATUS_REG		0x1C /* Read */
     27#define SCOM_PIB_RESET_REG	0x1C /* Write */
     28
     29/* Command register */
     30#define SCOM_WRITE_CMD		0x80000000
     31#define SCOM_READ_CMD		0x00000000
     32
     33/* Status register bits */
     34#define SCOM_STATUS_ERR_SUMMARY		0x80000000
     35#define SCOM_STATUS_PROTECTION		0x01000000
     36#define SCOM_STATUS_PARITY		0x04000000
     37#define SCOM_STATUS_PIB_ABORT		0x00100000
     38#define SCOM_STATUS_PIB_RESP_MASK	0x00007000
     39#define SCOM_STATUS_PIB_RESP_SHIFT	12
     40
     41#define SCOM_STATUS_FSI2PIB_ERROR	(SCOM_STATUS_PROTECTION |	\
     42					 SCOM_STATUS_PARITY |		\
     43					 SCOM_STATUS_PIB_ABORT)
     44#define SCOM_STATUS_ANY_ERR		(SCOM_STATUS_FSI2PIB_ERROR |	\
     45					 SCOM_STATUS_PIB_RESP_MASK)
     46/* SCOM address encodings */
     47#define XSCOM_ADDR_IND_FLAG		BIT_ULL(63)
     48#define XSCOM_ADDR_INF_FORM1		BIT_ULL(60)
     49
     50/* SCOM indirect stuff */
     51#define XSCOM_ADDR_DIRECT_PART		0x7fffffffull
     52#define XSCOM_ADDR_INDIRECT_PART	0x000fffff00000000ull
     53#define XSCOM_DATA_IND_READ		BIT_ULL(63)
     54#define XSCOM_DATA_IND_COMPLETE		BIT_ULL(31)
     55#define XSCOM_DATA_IND_ERR_MASK		0x70000000ull
     56#define XSCOM_DATA_IND_ERR_SHIFT	28
     57#define XSCOM_DATA_IND_DATA		0x0000ffffull
     58#define XSCOM_DATA_IND_FORM1_DATA	0x000fffffffffffffull
     59#define XSCOM_ADDR_FORM1_LOW		0x000ffffffffull
     60#define XSCOM_ADDR_FORM1_HI		0xfff00000000ull
     61#define XSCOM_ADDR_FORM1_HI_SHIFT	20
     62
     63/* Retries */
     64#define SCOM_MAX_IND_RETRIES		10	/* Retries indirect not ready */
     65
     66struct scom_device {
     67	struct list_head link;
     68	struct fsi_device *fsi_dev;
     69	struct device dev;
     70	struct cdev cdev;
     71	struct mutex lock;
     72	bool dead;
     73};
     74
     75static int __put_scom(struct scom_device *scom_dev, uint64_t value,
     76		      uint32_t addr, uint32_t *status)
     77{
     78	__be32 data, raw_status;
     79	int rc;
     80
     81	data = cpu_to_be32((value >> 32) & 0xffffffff);
     82	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
     83				sizeof(uint32_t));
     84	if (rc)
     85		return rc;
     86
     87	data = cpu_to_be32(value & 0xffffffff);
     88	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
     89				sizeof(uint32_t));
     90	if (rc)
     91		return rc;
     92
     93	data = cpu_to_be32(SCOM_WRITE_CMD | addr);
     94	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
     95				sizeof(uint32_t));
     96	if (rc)
     97		return rc;
     98	rc = fsi_device_read(scom_dev->fsi_dev, SCOM_STATUS_REG, &raw_status,
     99			     sizeof(uint32_t));
    100	if (rc)
    101		return rc;
    102	*status = be32_to_cpu(raw_status);
    103
    104	return 0;
    105}
    106
    107static int __get_scom(struct scom_device *scom_dev, uint64_t *value,
    108		      uint32_t addr, uint32_t *status)
    109{
    110	__be32 data, raw_status;
    111	int rc;
    112
    113
    114	*value = 0ULL;
    115	data = cpu_to_be32(SCOM_READ_CMD | addr);
    116	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
    117				sizeof(uint32_t));
    118	if (rc)
    119		return rc;
    120	rc = fsi_device_read(scom_dev->fsi_dev, SCOM_STATUS_REG, &raw_status,
    121			     sizeof(uint32_t));
    122	if (rc)
    123		return rc;
    124
    125	/*
    126	 * Read the data registers even on error, so we don't have
    127	 * to interpret the status register here.
    128	 */
    129	rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
    130				sizeof(uint32_t));
    131	if (rc)
    132		return rc;
    133	*value |= (uint64_t)be32_to_cpu(data) << 32;
    134	rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
    135				sizeof(uint32_t));
    136	if (rc)
    137		return rc;
    138	*value |= be32_to_cpu(data);
    139	*status = be32_to_cpu(raw_status);
    140
    141	return rc;
    142}
    143
    144static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value,
    145				   uint64_t addr, uint32_t *status)
    146{
    147	uint64_t ind_data, ind_addr;
    148	int rc, err;
    149
    150	if (value & ~XSCOM_DATA_IND_DATA)
    151		return -EINVAL;
    152
    153	ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
    154	ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | value;
    155	rc = __put_scom(scom, ind_data, ind_addr, status);
    156	if (rc || (*status & SCOM_STATUS_ANY_ERR))
    157		return rc;
    158
    159	rc = __get_scom(scom, &ind_data, addr, status);
    160	if (rc || (*status & SCOM_STATUS_ANY_ERR))
    161		return rc;
    162
    163	err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
    164	*status = err << SCOM_STATUS_PIB_RESP_SHIFT;
    165
    166	return 0;
    167}
    168
    169static int put_indirect_scom_form1(struct scom_device *scom, uint64_t value,
    170				   uint64_t addr, uint32_t *status)
    171{
    172	uint64_t ind_data, ind_addr;
    173
    174	if (value & ~XSCOM_DATA_IND_FORM1_DATA)
    175		return -EINVAL;
    176
    177	ind_addr = addr & XSCOM_ADDR_FORM1_LOW;
    178	ind_data = value | (addr & XSCOM_ADDR_FORM1_HI) << XSCOM_ADDR_FORM1_HI_SHIFT;
    179	return __put_scom(scom, ind_data, ind_addr, status);
    180}
    181
    182static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value,
    183				   uint64_t addr, uint32_t *status)
    184{
    185	uint64_t ind_data, ind_addr;
    186	int rc, err;
    187
    188	ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
    189	ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | XSCOM_DATA_IND_READ;
    190	rc = __put_scom(scom, ind_data, ind_addr, status);
    191	if (rc || (*status & SCOM_STATUS_ANY_ERR))
    192		return rc;
    193
    194	rc = __get_scom(scom, &ind_data, addr, status);
    195	if (rc || (*status & SCOM_STATUS_ANY_ERR))
    196		return rc;
    197
    198	err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
    199	*status = err << SCOM_STATUS_PIB_RESP_SHIFT;
    200	*value = ind_data & XSCOM_DATA_IND_DATA;
    201
    202	return 0;
    203}
    204
    205static int raw_put_scom(struct scom_device *scom, uint64_t value,
    206			uint64_t addr, uint32_t *status)
    207{
    208	if (addr & XSCOM_ADDR_IND_FLAG) {
    209		if (addr & XSCOM_ADDR_INF_FORM1)
    210			return put_indirect_scom_form1(scom, value, addr, status);
    211		else
    212			return put_indirect_scom_form0(scom, value, addr, status);
    213	} else
    214		return __put_scom(scom, value, addr, status);
    215}
    216
    217static int raw_get_scom(struct scom_device *scom, uint64_t *value,
    218			uint64_t addr, uint32_t *status)
    219{
    220	if (addr & XSCOM_ADDR_IND_FLAG) {
    221		if (addr & XSCOM_ADDR_INF_FORM1)
    222			return -ENXIO;
    223		return get_indirect_scom_form0(scom, value, addr, status);
    224	} else
    225		return __get_scom(scom, value, addr, status);
    226}
    227
    228static int handle_fsi2pib_status(struct scom_device *scom, uint32_t status)
    229{
    230	uint32_t dummy = -1;
    231
    232	if (status & SCOM_STATUS_FSI2PIB_ERROR)
    233		fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
    234				 sizeof(uint32_t));
    235
    236	if (status & SCOM_STATUS_PROTECTION)
    237		return -EPERM;
    238	if (status & SCOM_STATUS_PARITY)
    239		return -EIO;
    240
    241	if (status & SCOM_STATUS_PIB_ABORT)
    242		return -EBUSY;
    243	return 0;
    244}
    245
    246static int handle_pib_status(struct scom_device *scom, uint8_t status)
    247{
    248	uint32_t dummy = -1;
    249
    250	if (status == SCOM_PIB_SUCCESS)
    251		return 0;
    252	if (status == SCOM_PIB_BLOCKED)
    253		return -EBUSY;
    254
    255	/* Reset the bridge */
    256	fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
    257			 sizeof(uint32_t));
    258
    259	switch(status) {
    260	case SCOM_PIB_OFFLINE:
    261		return -ENODEV;
    262	case SCOM_PIB_BAD_ADDR:
    263		return -ENXIO;
    264	case SCOM_PIB_TIMEOUT:
    265		return -ETIMEDOUT;
    266	case SCOM_PIB_PARTIAL:
    267	case SCOM_PIB_CLK_ERR:
    268	case SCOM_PIB_PARITY_ERR:
    269	default:
    270		return -EIO;
    271	}
    272}
    273
    274static int put_scom(struct scom_device *scom, uint64_t value,
    275		    uint64_t addr)
    276{
    277	uint32_t status;
    278	int rc;
    279
    280	rc = raw_put_scom(scom, value, addr, &status);
    281	if (rc)
    282		return rc;
    283
    284	rc = handle_fsi2pib_status(scom, status);
    285	if (rc)
    286		return rc;
    287
    288	return handle_pib_status(scom,
    289				 (status & SCOM_STATUS_PIB_RESP_MASK)
    290				 >> SCOM_STATUS_PIB_RESP_SHIFT);
    291}
    292
    293static int get_scom(struct scom_device *scom, uint64_t *value,
    294		    uint64_t addr)
    295{
    296	uint32_t status;
    297	int rc;
    298
    299	rc = raw_get_scom(scom, value, addr, &status);
    300	if (rc)
    301		return rc;
    302
    303	rc = handle_fsi2pib_status(scom, status);
    304	if (rc)
    305		return rc;
    306
    307	return handle_pib_status(scom,
    308				 (status & SCOM_STATUS_PIB_RESP_MASK)
    309				 >> SCOM_STATUS_PIB_RESP_SHIFT);
    310}
    311
    312static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
    313			 loff_t *offset)
    314{
    315	struct scom_device *scom = filep->private_data;
    316	struct device *dev = &scom->fsi_dev->dev;
    317	uint64_t val;
    318	int rc;
    319
    320	if (len != sizeof(uint64_t))
    321		return -EINVAL;
    322
    323	mutex_lock(&scom->lock);
    324	if (scom->dead)
    325		rc = -ENODEV;
    326	else
    327		rc = get_scom(scom, &val, *offset);
    328	mutex_unlock(&scom->lock);
    329	if (rc) {
    330		dev_dbg(dev, "get_scom fail:%d\n", rc);
    331		return rc;
    332	}
    333
    334	rc = copy_to_user(buf, &val, len);
    335	if (rc)
    336		dev_dbg(dev, "copy to user failed:%d\n", rc);
    337
    338	return rc ? rc : len;
    339}
    340
    341static ssize_t scom_write(struct file *filep, const char __user *buf,
    342			  size_t len, loff_t *offset)
    343{
    344	int rc;
    345	struct scom_device *scom = filep->private_data;
    346	struct device *dev = &scom->fsi_dev->dev;
    347	uint64_t val;
    348
    349	if (len != sizeof(uint64_t))
    350		return -EINVAL;
    351
    352	rc = copy_from_user(&val, buf, len);
    353	if (rc) {
    354		dev_dbg(dev, "copy from user failed:%d\n", rc);
    355		return -EINVAL;
    356	}
    357
    358	mutex_lock(&scom->lock);
    359	if (scom->dead)
    360		rc = -ENODEV;
    361	else
    362		rc = put_scom(scom, val, *offset);
    363	mutex_unlock(&scom->lock);
    364	if (rc) {
    365		dev_dbg(dev, "put_scom failed with:%d\n", rc);
    366		return rc;
    367	}
    368
    369	return len;
    370}
    371
    372static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
    373{
    374	switch (whence) {
    375	case SEEK_CUR:
    376		break;
    377	case SEEK_SET:
    378		file->f_pos = offset;
    379		break;
    380	default:
    381		return -EINVAL;
    382	}
    383
    384	return offset;
    385}
    386
    387static void raw_convert_status(struct scom_access *acc, uint32_t status)
    388{
    389	acc->pib_status = (status & SCOM_STATUS_PIB_RESP_MASK) >>
    390		SCOM_STATUS_PIB_RESP_SHIFT;
    391	acc->intf_errors = 0;
    392
    393	if (status & SCOM_STATUS_PROTECTION)
    394		acc->intf_errors |= SCOM_INTF_ERR_PROTECTION;
    395	else if (status & SCOM_STATUS_PARITY)
    396		acc->intf_errors |= SCOM_INTF_ERR_PARITY;
    397	else if (status & SCOM_STATUS_PIB_ABORT)
    398		acc->intf_errors |= SCOM_INTF_ERR_ABORT;
    399	else if (status & SCOM_STATUS_ERR_SUMMARY)
    400		acc->intf_errors |= SCOM_INTF_ERR_UNKNOWN;
    401}
    402
    403static int scom_raw_read(struct scom_device *scom, void __user *argp)
    404{
    405	struct scom_access acc;
    406	uint32_t status;
    407	int rc;
    408
    409	if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
    410		return -EFAULT;
    411
    412	rc = raw_get_scom(scom, &acc.data, acc.addr, &status);
    413	if (rc)
    414		return rc;
    415	raw_convert_status(&acc, status);
    416	if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
    417		return -EFAULT;
    418	return 0;
    419}
    420
    421static int scom_raw_write(struct scom_device *scom, void __user *argp)
    422{
    423	u64 prev_data, mask, data;
    424	struct scom_access acc;
    425	uint32_t status;
    426	int rc;
    427
    428	if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
    429		return -EFAULT;
    430
    431	if (acc.mask) {
    432		rc = raw_get_scom(scom, &prev_data, acc.addr, &status);
    433		if (rc)
    434			return rc;
    435		if (status & SCOM_STATUS_ANY_ERR)
    436			goto fail;
    437		mask = acc.mask;
    438	} else {
    439		prev_data = mask = -1ull;
    440	}
    441	data = (prev_data & ~mask) | (acc.data & mask);
    442	rc = raw_put_scom(scom, data, acc.addr, &status);
    443	if (rc)
    444		return rc;
    445 fail:
    446	raw_convert_status(&acc, status);
    447	if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
    448		return -EFAULT;
    449	return 0;
    450}
    451
    452static int scom_reset(struct scom_device *scom, void __user *argp)
    453{
    454	uint32_t flags, dummy = -1;
    455	int rc = 0;
    456
    457	if (get_user(flags, (__u32 __user *)argp))
    458		return -EFAULT;
    459	if (flags & SCOM_RESET_PIB)
    460		rc = fsi_device_write(scom->fsi_dev, SCOM_PIB_RESET_REG, &dummy,
    461				      sizeof(uint32_t));
    462	if (!rc && (flags & (SCOM_RESET_PIB | SCOM_RESET_INTF)))
    463		rc = fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
    464				      sizeof(uint32_t));
    465	return rc;
    466}
    467
    468static int scom_check(struct scom_device *scom, void __user *argp)
    469{
    470	/* Still need to find out how to get "protected" */
    471	return put_user(SCOM_CHECK_SUPPORTED, (__u32 __user *)argp);
    472}
    473
    474static long scom_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    475{
    476	struct scom_device *scom = file->private_data;
    477	void __user *argp = (void __user *)arg;
    478	int rc = -ENOTTY;
    479
    480	mutex_lock(&scom->lock);
    481	if (scom->dead) {
    482		mutex_unlock(&scom->lock);
    483		return -ENODEV;
    484	}
    485	switch(cmd) {
    486	case FSI_SCOM_CHECK:
    487		rc = scom_check(scom, argp);
    488		break;
    489	case FSI_SCOM_READ:
    490		rc = scom_raw_read(scom, argp);
    491		break;
    492	case FSI_SCOM_WRITE:
    493		rc = scom_raw_write(scom, argp);
    494		break;
    495	case FSI_SCOM_RESET:
    496		rc = scom_reset(scom, argp);
    497		break;
    498	}
    499	mutex_unlock(&scom->lock);
    500	return rc;
    501}
    502
    503static int scom_open(struct inode *inode, struct file *file)
    504{
    505	struct scom_device *scom = container_of(inode->i_cdev, struct scom_device, cdev);
    506
    507	file->private_data = scom;
    508
    509	return 0;
    510}
    511
    512static const struct file_operations scom_fops = {
    513	.owner		= THIS_MODULE,
    514	.open		= scom_open,
    515	.llseek		= scom_llseek,
    516	.read		= scom_read,
    517	.write		= scom_write,
    518	.unlocked_ioctl	= scom_ioctl,
    519};
    520
    521static void scom_free(struct device *dev)
    522{
    523	struct scom_device *scom = container_of(dev, struct scom_device, dev);
    524
    525	put_device(&scom->fsi_dev->dev);
    526	kfree(scom);
    527}
    528
    529static int scom_probe(struct device *dev)
    530{
    531	struct fsi_device *fsi_dev = to_fsi_dev(dev);
    532	struct scom_device *scom;
    533	int rc, didx;
    534
    535	scom = kzalloc(sizeof(*scom), GFP_KERNEL);
    536	if (!scom)
    537		return -ENOMEM;
    538	dev_set_drvdata(dev, scom);
    539	mutex_init(&scom->lock);
    540
    541	/* Grab a reference to the device (parent of our cdev), we'll drop it later */
    542	if (!get_device(dev)) {
    543		kfree(scom);
    544		return -ENODEV;
    545	}
    546	scom->fsi_dev = fsi_dev;
    547
    548	/* Create chardev for userspace access */
    549	scom->dev.type = &fsi_cdev_type;
    550	scom->dev.parent = dev;
    551	scom->dev.release = scom_free;
    552	device_initialize(&scom->dev);
    553
    554	/* Allocate a minor in the FSI space */
    555	rc = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
    556	if (rc)
    557		goto err;
    558
    559	dev_set_name(&scom->dev, "scom%d", didx);
    560	cdev_init(&scom->cdev, &scom_fops);
    561	rc = cdev_device_add(&scom->cdev, &scom->dev);
    562	if (rc) {
    563		dev_err(dev, "Error %d creating char device %s\n",
    564			rc, dev_name(&scom->dev));
    565		goto err_free_minor;
    566	}
    567
    568	return 0;
    569 err_free_minor:
    570	fsi_free_minor(scom->dev.devt);
    571 err:
    572	put_device(&scom->dev);
    573	return rc;
    574}
    575
    576static int scom_remove(struct device *dev)
    577{
    578	struct scom_device *scom = dev_get_drvdata(dev);
    579
    580	mutex_lock(&scom->lock);
    581	scom->dead = true;
    582	mutex_unlock(&scom->lock);
    583	cdev_device_del(&scom->cdev, &scom->dev);
    584	fsi_free_minor(scom->dev.devt);
    585	put_device(&scom->dev);
    586
    587	return 0;
    588}
    589
    590static const struct fsi_device_id scom_ids[] = {
    591	{
    592		.engine_type = FSI_ENGID_SCOM,
    593		.version = FSI_VERSION_ANY,
    594	},
    595	{ 0 }
    596};
    597
    598static struct fsi_driver scom_drv = {
    599	.id_table = scom_ids,
    600	.drv = {
    601		.name = "scom",
    602		.bus = &fsi_bus_type,
    603		.probe = scom_probe,
    604		.remove = scom_remove,
    605	}
    606};
    607
    608static int scom_init(void)
    609{
    610	return fsi_driver_register(&scom_drv);
    611}
    612
    613static void scom_exit(void)
    614{
    615	fsi_driver_unregister(&scom_drv);
    616}
    617
    618module_init(scom_init);
    619module_exit(scom_exit);
    620MODULE_LICENSE("GPL");