brintos

brintos / linux-shallow public Read only

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