4470
|
1 /*
|
|
2 Direct Hardware Access kernel helper
|
|
3
|
10416
|
4 (C) 2002 Alex Beregszaszi <alex@fsn.hu>
|
4470
|
5
|
|
6 Accessing hardware from userspace as USER (no root needed!)
|
4479
|
7
|
|
8 Tested on 2.2.x (2.2.19) and 2.4.x (2.4.3,2.4.17).
|
4470
|
9
|
4479
|
10 License: GPL
|
|
11
|
|
12 WARNING! THIS MODULE VIOLATES SEVERAL SECURITY LINES! DON'T USE IT
|
|
13 ON PRODUCTION SYSTEMS, ONLY AT HOME, ON A "SINGLE-USER" SYSTEM.
|
|
14 NO WARRANTY!
|
4470
|
15
|
|
16 Tech:
|
4479
|
17 Communication between userspace and kernelspace goes over character
|
|
18 device using ioctl.
|
4470
|
19
|
|
20 Usage:
|
|
21 mknod -m 666 /dev/dhahelper c 180 0
|
|
22
|
|
23 Also you can change the major number, setting the "dhahelper_major"
|
|
24 module parameter, the default is 180, specified in dhahelper.h.
|
|
25
|
|
26 Note: do not use other than minor==0, the module forbids it.
|
|
27
|
|
28 TODO:
|
4479
|
29 * do memory mapping without fops:mmap
|
4470
|
30 * implement unmap memory
|
4479
|
31 * select (request?) a "valid" major number (from Linux project? ;)
|
4470
|
32 * make security
|
|
33 * is pci handling needed? (libdha does this with lowlevel port funcs)
|
4479
|
34 * is mttr handling needed?
|
|
35 * test on older kernels (2.0.x (?))
|
4470
|
36 */
|
|
37
|
|
38 #ifndef MODULE
|
|
39 #define MODULE
|
|
40 #endif
|
|
41
|
|
42 #ifndef __KERNEL__
|
|
43 #define __KERNEL__
|
|
44 #endif
|
|
45
|
|
46 #include <linux/config.h>
|
4479
|
47
|
|
48 #ifdef CONFIG_MODVERSION
|
|
49 #define MODVERSION
|
|
50 #include <linux/modversions.h>
|
|
51 #endif
|
|
52
|
4470
|
53 #include <linux/version.h>
|
|
54 #include <linux/module.h>
|
|
55 #include <linux/types.h>
|
|
56 #include <linux/kernel.h>
|
|
57 #include <linux/sched.h>
|
|
58 #include <linux/mm.h>
|
|
59 #include <linux/string.h>
|
|
60 #include <linux/errno.h>
|
|
61
|
|
62 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
|
|
63 #include <linux/malloc.h>
|
|
64 #else
|
|
65 #include <linux/slab.h>
|
|
66 #endif
|
|
67
|
|
68 #include <linux/pci.h>
|
|
69 #include <linux/ioport.h>
|
|
70 #include <linux/init.h>
|
|
71
|
|
72 #include <asm/uaccess.h>
|
|
73 #include <asm/system.h>
|
|
74 #include <asm/io.h>
|
|
75
|
|
76 #include <linux/mman.h>
|
|
77
|
|
78 #include <linux/fs.h>
|
|
79 #include <linux/unistd.h>
|
|
80
|
|
81 #include "dhahelper.h"
|
|
82
|
10416
|
83 MODULE_AUTHOR("Alex Beregszaszi <alex@fsn.hu>");
|
4479
|
84 MODULE_DESCRIPTION("Provides userspace access to hardware (security violation!)");
|
4470
|
85 #ifdef MODULE_LICENSE
|
4479
|
86 MODULE_LICENSE("GPL");
|
4470
|
87 #endif
|
|
88
|
|
89 static int dhahelper_major = DEFAULT_MAJOR;
|
|
90 MODULE_PARM(dhahelper_major, "i");
|
4479
|
91 MODULE_PARM_DESC(dhahelper_major, "Major number of dhahelper characterdevice");
|
4470
|
92
|
|
93 /* 0 = silent */
|
|
94 /* 1 = report errors (default) */
|
|
95 /* 2 = debug */
|
|
96 static int dhahelper_verbosity = 1;
|
|
97 MODULE_PARM(dhahelper_verbosity, "i");
|
4479
|
98 MODULE_PARM_DESC(dhahelper_verbosity, "Level of verbosity (0 = silent, 1 = only errors, 2 = debug)");
|
4470
|
99
|
|
100 static dhahelper_memory_t last_mem_request;
|
|
101
|
|
102
|
|
103 static int dhahelper_open(struct inode *inode, struct file *file)
|
|
104 {
|
|
105 if (dhahelper_verbosity > 1)
|
|
106 printk(KERN_DEBUG "dhahelper: device opened\n");
|
|
107
|
|
108 if (MINOR(inode->i_rdev) != 0)
|
|
109 return(-ENXIO);
|
|
110
|
|
111 MOD_INC_USE_COUNT;
|
|
112
|
|
113 return(0);
|
|
114 }
|
|
115
|
|
116 static int dhahelper_release(struct inode *inode, struct file *file)
|
|
117 {
|
|
118 if (dhahelper_verbosity > 1)
|
|
119 printk(KERN_DEBUG "dhahelper: device released\n");
|
|
120
|
|
121 if (MINOR(inode->i_rdev) != 0)
|
|
122 return(-ENXIO);
|
|
123
|
|
124 MOD_DEC_USE_COUNT;
|
|
125
|
|
126 return(0);
|
|
127 }
|
|
128
|
|
129 static int dhahelper_ioctl(struct inode *inode, struct file *file,
|
|
130 unsigned int cmd, unsigned long arg)
|
|
131 {
|
|
132 if (dhahelper_verbosity > 1)
|
|
133 printk(KERN_DEBUG "dhahelper: ioctl(cmd=%x, arg=%lx)\n",
|
|
134 cmd, arg);
|
|
135
|
|
136 if (MINOR(inode->i_rdev) != 0)
|
|
137 return(-ENXIO);
|
|
138
|
|
139 switch(cmd)
|
|
140 {
|
|
141 case DHAHELPER_GET_VERSION:
|
|
142 {
|
|
143 int version = API_VERSION;
|
|
144
|
|
145 if (copy_to_user((int *)arg, &version, sizeof(int)))
|
|
146 {
|
|
147 if (dhahelper_verbosity > 0)
|
|
148 printk(KERN_ERR "dhahelper: failed copy to userspace\n");
|
|
149 return(-EFAULT);
|
|
150 }
|
|
151
|
|
152 break;
|
|
153 }
|
|
154 case DHAHELPER_PORT:
|
|
155 {
|
|
156 dhahelper_port_t port;
|
|
157
|
|
158 if (copy_from_user(&port, (dhahelper_port_t *)arg, sizeof(dhahelper_port_t)))
|
|
159 {
|
|
160 if (dhahelper_verbosity > 0)
|
|
161 printk(KERN_ERR "dhahelper: failed copy from userspace\n");
|
|
162 return(-EFAULT);
|
|
163 }
|
|
164
|
|
165 switch(port.operation)
|
|
166 {
|
|
167 case PORT_OP_READ:
|
|
168 {
|
|
169 switch(port.size)
|
|
170 {
|
|
171 case 1:
|
|
172 port.value = inb(port.addr);
|
|
173 break;
|
|
174 case 2:
|
|
175 port.value = inw(port.addr);
|
|
176 break;
|
|
177 case 4:
|
|
178 port.value = inl(port.addr);
|
|
179 break;
|
|
180 default:
|
|
181 if (dhahelper_verbosity > 0)
|
|
182 printk(KERN_ERR "dhahelper: invalid port read size (%d)\n",
|
|
183 port.size);
|
|
184 return(-EINVAL);
|
|
185 }
|
|
186 break;
|
|
187 }
|
|
188 case PORT_OP_WRITE:
|
|
189 {
|
|
190 switch(port.size)
|
|
191 {
|
|
192 case 1:
|
|
193 outb(port.value, port.addr);
|
|
194 break;
|
|
195 case 2:
|
|
196 outw(port.value, port.addr);
|
|
197 break;
|
|
198 case 4:
|
|
199 outl(port.value, port.addr);
|
|
200 break;
|
|
201 default:
|
|
202 if (dhahelper_verbosity > 0)
|
|
203 printk(KERN_ERR "dhahelper: invalid port write size (%d)\n",
|
|
204 port.size);
|
|
205 return(-EINVAL);
|
|
206 }
|
|
207 break;
|
|
208 }
|
|
209 default:
|
|
210 if (dhahelper_verbosity > 0)
|
|
211 printk(KERN_ERR "dhahelper: invalid port operation (%d)\n",
|
|
212 port.operation);
|
|
213 return(-EINVAL);
|
|
214 }
|
|
215
|
|
216 /* copy back only if read was performed */
|
|
217 if (port.operation == PORT_OP_READ)
|
|
218 if (copy_to_user((dhahelper_port_t *)arg, &port, sizeof(dhahelper_port_t)))
|
|
219 {
|
|
220 if (dhahelper_verbosity > 0)
|
|
221 printk(KERN_ERR "dhahelper: failed copy to userspace\n");
|
|
222 return(-EFAULT);
|
|
223 }
|
|
224
|
|
225 break;
|
|
226 }
|
|
227 case DHAHELPER_MEMORY:
|
|
228 {
|
|
229 dhahelper_memory_t mem;
|
|
230
|
|
231 if (copy_from_user(&mem, (dhahelper_memory_t *)arg, sizeof(dhahelper_memory_t)))
|
|
232 {
|
|
233 if (dhahelper_verbosity > 0)
|
|
234 printk(KERN_ERR "dhahelper: failed copy from userspace\n");
|
|
235 return(-EFAULT);
|
|
236 }
|
|
237
|
|
238 switch(mem.operation)
|
|
239 {
|
|
240 case MEMORY_OP_MAP:
|
|
241 {
|
4479
|
242 #if 1
|
4470
|
243 memcpy(&last_mem_request, &mem, sizeof(dhahelper_memory_t));
|
4479
|
244 #else
|
|
245 mem.ret = do_mmap(file, mem.start, mem.size, PROT_READ|PROT_WRITE,
|
|
246 MAP_SHARED, mem.offset);
|
|
247 #endif
|
4470
|
248
|
|
249 break;
|
|
250 }
|
|
251 case MEMORY_OP_UNMAP:
|
|
252 break;
|
|
253 default:
|
|
254 if (dhahelper_verbosity > 0)
|
|
255 printk(KERN_ERR "dhahelper: invalid memory operation (%d)\n",
|
|
256 mem.operation);
|
|
257 return(-EINVAL);
|
|
258 }
|
|
259
|
|
260 if (copy_to_user((dhahelper_memory_t *)arg, &mem, sizeof(dhahelper_memory_t)))
|
|
261 {
|
|
262 if (dhahelper_verbosity > 0)
|
|
263 printk(KERN_ERR "dhahelper: failed copy to userspace\n");
|
|
264 return(-EFAULT);
|
|
265 }
|
|
266
|
|
267 break;
|
|
268 }
|
|
269 default:
|
|
270 if (dhahelper_verbosity > 0)
|
|
271 printk(KERN_ERR "dhahelper: invalid ioctl (%x)\n", cmd);
|
|
272 return(-EINVAL);
|
|
273 }
|
|
274
|
|
275 return(0);
|
|
276 }
|
|
277
|
|
278 static int dhahelper_mmap(struct file *file, struct vm_area_struct *vma)
|
|
279 {
|
|
280 if (last_mem_request.operation != MEMORY_OP_MAP)
|
|
281 {
|
|
282 if (dhahelper_verbosity > 0)
|
|
283 printk(KERN_ERR "dhahelper: mapping not requested before mmap\n");
|
|
284 return(-EFAULT);
|
|
285 }
|
|
286
|
|
287 if (dhahelper_verbosity > 1)
|
|
288 printk(KERN_INFO "dhahelper: mapping %x (size: %x)\n",
|
|
289 last_mem_request.start+last_mem_request.offset, last_mem_request.size);
|
|
290
|
|
291 if (remap_page_range(0, last_mem_request.start + last_mem_request.offset,
|
|
292 last_mem_request.size, vma->vm_page_prot))
|
|
293 {
|
|
294 if (dhahelper_verbosity > 0)
|
|
295 printk(KERN_ERR "dhahelper: error mapping memory\n");
|
|
296 return(-EFAULT);
|
|
297 }
|
|
298
|
|
299 return(0);
|
|
300 }
|
|
301
|
|
302 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
|
|
303 static struct file_operations dhahelper_fops =
|
|
304 {
|
|
305 /*llseek*/ NULL,
|
|
306 /*read*/ NULL,
|
|
307 /*write*/ NULL,
|
|
308 /*readdir*/ NULL,
|
|
309 /*poll*/ NULL,
|
|
310 /*ioctl*/ dhahelper_ioctl,
|
|
311 /*mmap*/ dhahelper_mmap,
|
|
312 /*open*/ dhahelper_open,
|
|
313 /*flush*/ NULL,
|
|
314 /*release*/ dhahelper_release,
|
|
315 /* zero out the last 5 entries too ? */
|
|
316 };
|
|
317 #else
|
|
318 static struct file_operations dhahelper_fops =
|
|
319 {
|
|
320 owner: THIS_MODULE,
|
|
321 ioctl: dhahelper_ioctl,
|
|
322 mmap: dhahelper_mmap,
|
|
323 open: dhahelper_open,
|
|
324 release: dhahelper_release
|
|
325 };
|
|
326 #endif
|
|
327
|
4479
|
328 #if KERNEL_VERSION < KERNEL_VERSION(2,4,0)
|
|
329 int init_module(void)
|
|
330 #else
|
4470
|
331 static int __init init_dhahelper(void)
|
4479
|
332 #endif
|
4470
|
333 {
|
|
334 printk(KERN_INFO "Direct Hardware Access kernel helper (C) Alex Beregszaszi\n");
|
|
335
|
|
336 if(register_chrdev(dhahelper_major, "dhahelper", &dhahelper_fops))
|
|
337 {
|
|
338 if (dhahelper_verbosity > 0)
|
|
339 printk(KERN_ERR "dhahelper: unable to register character device (major: %d)\n",
|
|
340 dhahelper_major);
|
|
341 return(-EIO);
|
|
342 }
|
|
343
|
|
344 return(0);
|
|
345 }
|
|
346
|
4479
|
347 #if KERNEL_VERSION < KERNEL_VERSION(2,4,0)
|
|
348 void cleanup_module(void)
|
|
349 #else
|
4470
|
350 static void __exit exit_dhahelper(void)
|
4479
|
351 #endif
|
4470
|
352 {
|
|
353 unregister_chrdev(dhahelper_major, "dhahelper");
|
|
354 }
|
|
355
|
|
356 EXPORT_NO_SYMBOLS;
|
|
357
|
4479
|
358 #if KERNEL_VERSION >= KERNEL_VERSION(2,4,0)
|
4470
|
359 module_init(init_dhahelper);
|
|
360 module_exit(exit_dhahelper);
|
4479
|
361 #endif
|