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

adi.c (4564B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Privileged ADI driver for sparc64
      4 *
      5 * Author: Tom Hromatka <tom.hromatka@oracle.com>
      6 */
      7#include <linux/kernel.h>
      8#include <linux/miscdevice.h>
      9#include <linux/module.h>
     10#include <linux/proc_fs.h>
     11#include <linux/slab.h>
     12#include <linux/uaccess.h>
     13#include <asm/asi.h>
     14
     15#define MAX_BUF_SZ	PAGE_SIZE
     16
     17static int adi_open(struct inode *inode, struct file *file)
     18{
     19	file->f_mode |= FMODE_UNSIGNED_OFFSET;
     20	return 0;
     21}
     22
     23static int read_mcd_tag(unsigned long addr)
     24{
     25	long err;
     26	int ver;
     27
     28	__asm__ __volatile__(
     29		"1:	ldxa [%[addr]] %[asi], %[ver]\n"
     30		"	mov 0, %[err]\n"
     31		"2:\n"
     32		"	.section .fixup,#alloc,#execinstr\n"
     33		"	.align 4\n"
     34		"3:	sethi %%hi(2b), %%g1\n"
     35		"	jmpl  %%g1 + %%lo(2b), %%g0\n"
     36		"	mov %[invalid], %[err]\n"
     37		"	.previous\n"
     38		"	.section __ex_table, \"a\"\n"
     39		"	.align 4\n"
     40		"	.word  1b, 3b\n"
     41		"	.previous\n"
     42		: [ver] "=r" (ver), [err] "=r" (err)
     43		: [addr] "r"  (addr), [invalid] "i" (EFAULT),
     44		  [asi] "i" (ASI_MCD_REAL)
     45		: "memory", "g1"
     46		);
     47
     48	if (err)
     49		return -EFAULT;
     50	else
     51		return ver;
     52}
     53
     54static ssize_t adi_read(struct file *file, char __user *buf,
     55			size_t count, loff_t *offp)
     56{
     57	size_t ver_buf_sz, bytes_read = 0;
     58	int ver_buf_idx = 0;
     59	loff_t offset;
     60	u8 *ver_buf;
     61	ssize_t ret;
     62
     63	ver_buf_sz = min_t(size_t, count, MAX_BUF_SZ);
     64	ver_buf = kmalloc(ver_buf_sz, GFP_KERNEL);
     65	if (!ver_buf)
     66		return -ENOMEM;
     67
     68	offset = (*offp) * adi_blksize();
     69
     70	while (bytes_read < count) {
     71		ret = read_mcd_tag(offset);
     72		if (ret < 0)
     73			goto out;
     74
     75		ver_buf[ver_buf_idx] = (u8)ret;
     76		ver_buf_idx++;
     77		offset += adi_blksize();
     78
     79		if (ver_buf_idx >= ver_buf_sz) {
     80			if (copy_to_user(buf + bytes_read, ver_buf,
     81					 ver_buf_sz)) {
     82				ret = -EFAULT;
     83				goto out;
     84			}
     85
     86			bytes_read += ver_buf_sz;
     87			ver_buf_idx = 0;
     88
     89			ver_buf_sz = min(count - bytes_read,
     90					 (size_t)MAX_BUF_SZ);
     91		}
     92	}
     93
     94	(*offp) += bytes_read;
     95	ret = bytes_read;
     96out:
     97	kfree(ver_buf);
     98	return ret;
     99}
    100
    101static int set_mcd_tag(unsigned long addr, u8 ver)
    102{
    103	long err;
    104
    105	__asm__ __volatile__(
    106		"1:	stxa %[ver], [%[addr]] %[asi]\n"
    107		"	mov 0, %[err]\n"
    108		"2:\n"
    109		"	.section .fixup,#alloc,#execinstr\n"
    110		"	.align 4\n"
    111		"3:	sethi %%hi(2b), %%g1\n"
    112		"	jmpl %%g1 + %%lo(2b), %%g0\n"
    113		"	mov %[invalid], %[err]\n"
    114		"	.previous\n"
    115		"	.section __ex_table, \"a\"\n"
    116		"	.align 4\n"
    117		"	.word 1b, 3b\n"
    118		"	.previous\n"
    119		: [err] "=r" (err)
    120		: [ver] "r" (ver), [addr] "r"  (addr),
    121		  [invalid] "i" (EFAULT), [asi] "i" (ASI_MCD_REAL)
    122		: "memory", "g1"
    123		);
    124
    125	if (err)
    126		return -EFAULT;
    127	else
    128		return ver;
    129}
    130
    131static ssize_t adi_write(struct file *file, const char __user *buf,
    132			 size_t count, loff_t *offp)
    133{
    134	size_t ver_buf_sz, bytes_written = 0;
    135	loff_t offset;
    136	u8 *ver_buf;
    137	ssize_t ret;
    138	int i;
    139
    140	if (count <= 0)
    141		return -EINVAL;
    142
    143	ver_buf_sz = min_t(size_t, count, MAX_BUF_SZ);
    144	ver_buf = kmalloc(ver_buf_sz, GFP_KERNEL);
    145	if (!ver_buf)
    146		return -ENOMEM;
    147
    148	offset = (*offp) * adi_blksize();
    149
    150	do {
    151		if (copy_from_user(ver_buf, &buf[bytes_written],
    152				   ver_buf_sz)) {
    153			ret = -EFAULT;
    154			goto out;
    155		}
    156
    157		for (i = 0; i < ver_buf_sz; i++) {
    158			ret = set_mcd_tag(offset, ver_buf[i]);
    159			if (ret < 0)
    160				goto out;
    161
    162			offset += adi_blksize();
    163		}
    164
    165		bytes_written += ver_buf_sz;
    166		ver_buf_sz = min(count - bytes_written, (size_t)MAX_BUF_SZ);
    167	} while (bytes_written < count);
    168
    169	(*offp) += bytes_written;
    170	ret = bytes_written;
    171out:
    172	__asm__ __volatile__("membar #Sync");
    173	kfree(ver_buf);
    174	return ret;
    175}
    176
    177static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
    178{
    179	loff_t ret = -EINVAL;
    180
    181	switch (whence) {
    182	case SEEK_END:
    183	case SEEK_DATA:
    184	case SEEK_HOLE:
    185		/* unsupported */
    186		return -EINVAL;
    187	case SEEK_CUR:
    188		if (offset == 0)
    189			return file->f_pos;
    190
    191		offset += file->f_pos;
    192		break;
    193	case SEEK_SET:
    194		break;
    195	}
    196
    197	if (offset != file->f_pos) {
    198		file->f_pos = offset;
    199		file->f_version = 0;
    200		ret = offset;
    201	}
    202
    203	return ret;
    204}
    205
    206static const struct file_operations adi_fops = {
    207	.owner		= THIS_MODULE,
    208	.llseek		= adi_llseek,
    209	.open		= adi_open,
    210	.read		= adi_read,
    211	.write		= adi_write,
    212};
    213
    214static struct miscdevice adi_miscdev = {
    215	.minor = MISC_DYNAMIC_MINOR,
    216	.name = KBUILD_MODNAME,
    217	.fops = &adi_fops,
    218};
    219
    220static int __init adi_init(void)
    221{
    222	if (!adi_capable())
    223		return -EPERM;
    224
    225	return misc_register(&adi_miscdev);
    226}
    227
    228static void __exit adi_exit(void)
    229{
    230	misc_deregister(&adi_miscdev);
    231}
    232
    233module_init(adi_init);
    234module_exit(adi_exit);
    235
    236MODULE_AUTHOR("Tom Hromatka <tom.hromatka@oracle.com>");
    237MODULE_DESCRIPTION("Privileged interface to ADI");
    238MODULE_VERSION("1.0");
    239MODULE_LICENSE("GPL v2");