]> Git Repo - J-linux.git/blob - drivers/char/adi.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / char / adi.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Privileged ADI driver for sparc64
4  *
5  * Author: Tom Hromatka <[email protected]>
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
17 static 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         else
45                 return ver;
46 }
47
48 static 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;
90 out:
91         kfree(ver_buf);
92         return ret;
93 }
94
95 static 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         else
122                 return ver;
123 }
124
125 static 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;
165 out:
166         __asm__ __volatile__("membar #Sync");
167         kfree(ver_buf);
168         return ret;
169 }
170
171 static 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
199 static 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
207 static struct miscdevice adi_miscdev = {
208         .minor = MISC_DYNAMIC_MINOR,
209         .name = KBUILD_MODNAME,
210         .fops = &adi_fops,
211 };
212
213 static int __init adi_init(void)
214 {
215         if (!adi_capable())
216                 return -EPERM;
217
218         return misc_register(&adi_miscdev);
219 }
220
221 static void __exit adi_exit(void)
222 {
223         misc_deregister(&adi_miscdev);
224 }
225
226 module_init(adi_init);
227 module_exit(adi_exit);
228
229 MODULE_AUTHOR("Tom Hromatka <[email protected]>");
230 MODULE_DESCRIPTION("Privileged interface to ADI");
231 MODULE_VERSION("1.0");
232 MODULE_LICENSE("GPL v2");
This page took 0.038165 seconds and 4 git commands to generate.