18e9762b5422731cced148114e481e266c3325a5
[warm.git] / module / warm_main.c
1 /*
2  * wARM - exporting ARM processor specific privileged services to userspace
3  * kernelspace part
4  *
5  * Author: GraÅžvydas "notaz" Ignotas
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/proc_fs.h>
15 #include <linux/delay.h>
16 #include <linux/fs.h>
17 #include <linux/seq_file.h>
18
19 #include <linux/version.h>
20 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
21 #include <linux/uaccess.h>
22 #else
23 #include <linux/init.h>
24 #include <linux/moduleparam.h>
25 #include <asm/uaccess.h>
26 #define __user
27 #define unlocked_ioctl ioctl
28 #endif
29
30 #define WARM_CODE
31 #include "../warm.h"
32 #include "warm_ops.h"
33
34 #ifndef CONFIG_PROC_FS
35 #error need proc_fs
36 #endif
37         
38 #define WARM_VER "r2"
39 #define PFX "wARM: "
40
41 #define WARM_INFO(fmt, ...) \
42         if (verbose) \
43                 pr_info(PFX fmt, ##__VA_ARGS__)
44
45 #define SECTION_SIZE            0x100000
46 #define MAX_CACHEOP_RANGE       16384
47
48 /* assume RAM starts at phys addr 0 (this is really machine specific) */
49 #define RAM_PHYS_START  0
50 #define RAM_MAX_SIZE    0x10000000      /* 256M, try to be future proof */
51
52 /* expected CPU id */
53 #if   defined(CONFIG_CPU_ARM926T)
54 #define EXPECTED_ID     0x069260
55 #elif defined(CONFIG_CPU_ARM920T)
56 #define EXPECTED_ID     0x029200
57 #else
58 #error "unsupported CPU"
59 #endif
60
61 extern unsigned long max_mapnr;
62
63 /* "upper" physical memory, not seen by Linux and to be mmap'ed */
64 static u32 uppermem_start;
65 static u32 uppermem_end;
66
67 static int verbose;
68
69 static u32 *get_pgtable(void)
70 {
71         u32 ttb;
72
73         /* get the pointer to the translation table base... */
74         asm ("mrc p15, 0, %0, c2, c0, 0" : "=r"(ttb));
75
76         return __va((ttb) & 0xffffc000);
77 }
78
79 static int do_set_cb_uppermem(int in_cb, int is_set)
80 {
81         u32 *pgtable, *cpt;
82         int i, j, count = 0;
83         int bits = 0;
84
85         if (uppermem_end <= uppermem_start)
86                 return -ENODEV;
87
88         if (in_cb & WCB_C_BIT)
89                 bits |= 8;
90         if (in_cb & WCB_B_BIT)
91                 bits |= 4;
92
93         pgtable = get_pgtable();
94
95         for (i = 0; i < 4096; i++)
96         {
97                 if ((pgtable[i] & 3) != 1)
98                         /* must be coarse page table */
99                         continue;
100
101                 cpt = __va(pgtable[i] & 0xfffffc00);
102
103                 for (j = 0; j < 256; j++)
104                 {
105                         u32 addr, entry;
106
107                         entry = cpt[j];
108                         if (!(entry & 3))
109                                 /* fault */
110                                 continue;
111
112                         addr = entry & 0xfffff000;
113                         if (uppermem_start <= addr && addr < uppermem_end)
114                         {
115                                 pr_debug(PFX "%s C&B bits %08x\n",
116                                         is_set ? "set" : "clear", entry);
117
118                                 if (is_set)
119                                         entry |= bits;
120                                 else
121                                         entry &= ~bits;
122
123                                 /* need to also set AP bits (so that no faults
124                                  * happen and kernel doesn't touch this after us) */
125                                 if ((entry & 3) == 3)
126                                         entry |= 0x030; /* tiny page */
127                                 else
128                                         entry |= 0xff0;
129
130                                 cpt[j] = entry;
131                                 count++;
132                         }
133                 }
134         }
135
136         warm_cop_clean_d();
137         warm_drain_wb_inval_tlb();
138
139         WARM_INFO("%c%c bit(s) %s for phys %08x-%08x (%d pages)\n",
140                 bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
141                 is_set ? "set" : "cleared",
142                 uppermem_start, uppermem_end - 1, count);
143
144         return 0;
145 }
146
147 static int do_set_cb_virt(int in_cb, int is_set, u32 addr, u32 size)
148 {
149         int count = 0, bits = 0;
150         u32 desc1, desc2 = 0;
151         u32 *pgtable, *cpt = NULL;
152         u32 start, end;
153         u32 mask;
154
155         if (in_cb & WCB_C_BIT)
156                 bits |= 8;
157         if (in_cb & WCB_B_BIT)
158                 bits |= 4;
159
160         mask = PAGE_SIZE - 1;
161         size += addr & mask;
162         size = (size + mask) & ~mask;
163
164         addr &= ~(PAGE_SIZE - 1);
165         start = addr;
166         end = addr + size;
167
168         pgtable = get_pgtable();
169
170         while (addr < end)
171         {
172                 desc1 = pgtable[addr >> 20];
173
174                 switch (desc1 & 3) {
175                 case 0:
176                         printk(KERN_WARNING PFX "address %08x not mapped.\n", addr);
177                         return -EINVAL;
178                 case 1:
179                         /* coarse table */
180                         cpt = __va(desc1 & 0xfffffc00);
181                         desc2 = cpt[(addr >> 12) & 0xff];
182                         break;
183                 case 2:
184                         /* section */
185                         if (is_set)
186                                 desc1 |= bits;
187                         else
188                                 desc1 &= ~bits;
189                         pgtable[addr >> 20] = desc1;
190                         addr += SECTION_SIZE;
191                         count++;
192                         continue;
193                 case 3:
194                         cpt = __va(desc1 & 0xfffff000);
195                         desc2 = cpt[(addr >> 10) & 0x3ff];
196                         break;
197                 }
198
199                 
200                 if ((desc2 & 3) == 0) {
201                         printk(KERN_WARNING PFX "address %08x not mapped (%08x)\n",
202                                 addr, desc2);
203                         return -EINVAL;
204                 }
205
206                 if (is_set)
207                         desc2 |= bits;
208                 else
209                         desc2 &= ~bits;
210
211                 /* this might be bad idea, better let it fault so that Linux does
212                  * it's accounting, but that will drop CB bits, so keep this
213                  * for compatibility */
214                 if ((desc2 & 3) == 2)
215                         desc2 |= 0xff0;
216
217                 switch (desc1 & 3) {
218                 case 1:
219                         cpt[(addr >> 12) & 0xff] = desc2;
220                         break;
221                 case 3:
222                         cpt[(addr >> 10) & 0x3ff] = desc2;
223                         break;
224                 }
225
226                 addr += PAGE_SIZE;
227                 count++;
228         }
229
230         warm_cop_clean_d();
231         warm_drain_wb_inval_tlb();
232
233         WARM_INFO("%c%c bit(s) %s virt %08x-%08x (%d pages)\n",
234                 bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
235                 is_set ? "set" : "cleared", start, end - 1, count);
236
237         return 0;
238 }
239
240 static int do_virt2phys(unsigned long *_addr)
241 {
242         u32 addr = *_addr;
243         u32 desc1, desc2;
244         u32 *pgtable, *cpt;
245
246         pgtable = get_pgtable();
247         desc1 = pgtable[addr >> 20];
248
249         switch (desc1 & 3) {
250         case 1: /* coarse table */
251                 cpt = __va(desc1 & 0xfffffc00);
252                 desc2 = cpt[(addr >> 12) & 0xff];
253                 break;
254         case 2: /* 1MB section */
255                 *_addr = (desc1 & 0xfff00000) | (addr & 0xfffff);
256                 return 0;
257         case 3: /* fine table */
258                 cpt = __va(desc1 & 0xfffff000);
259                 desc2 = cpt[(addr >> 10) & 0x3ff];
260                 break;
261         default:
262                 return -EINVAL;
263         }
264
265         switch (desc2 & 3) {
266         case 1: /* large page */
267                 *_addr = (desc2 & ~0xffff) | (addr & 0xffff);
268                 break;
269         case 2: /* small page */
270                 *_addr = (desc2 & ~0x0fff) | (addr & 0x0fff);
271                 break;
272         case 3: /* tiny page */
273                 *_addr = (desc2 & ~0x03ff) | (addr & 0x03ff);
274                 break;
275         default:
276                 return -EINVAL;
277         }
278
279         return 0;
280 }
281
282 static int do_cache_ops_whole(int ops)
283 {
284         if ((ops & (WOP_D_CLEAN|WOP_D_INVALIDATE)) == (WOP_D_CLEAN|WOP_D_INVALIDATE))
285                 warm_cop_clean_inval_d();
286
287         else if (ops & WOP_D_CLEAN)
288                 warm_cop_clean_d();
289
290         else if (ops & WOP_D_INVALIDATE) {
291                 printk(KERN_WARNING PFX "invalidate without clean is dangerous!\n");
292                 warm_cop_inval_d();
293         }
294
295         if (ops & WOP_I_INVALIDATE)
296                 warm_cop_inval_i();
297         
298         warm_cop_drain_wb();
299         return 0;
300 }
301
302 static int do_cache_ops(int ops, u32 addr, u32 size)
303 {
304         if (addr & 31) {
305                 size += addr & 31;
306                 addr &= ~31;
307         }
308
309         switch (ops) {
310         case WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE:
311                 warm_cop_r_clean_d_inval_di(addr, size);
312                 break;
313         case WOP_D_CLEAN|WOP_D_INVALIDATE:
314                 warm_cop_r_clean_d_inval_d(addr, size);
315                 break;
316         case WOP_D_CLEAN|WOP_I_INVALIDATE:
317                 warm_cop_r_clean_d_inval_i(addr, size);
318                 break;
319         case WOP_D_CLEAN:
320                 warm_cop_r_clean_d(addr, size);
321                 break;
322         case WOP_D_INVALIDATE|WOP_I_INVALIDATE:
323                 warm_cop_r_inval_di(addr, size);
324                 break;
325         case WOP_D_INVALIDATE:
326                 warm_cop_r_inval_d(addr, size);
327                 break;
328         case WOP_I_INVALIDATE:
329                 warm_cop_r_inval_i(addr, size);
330                 break;
331         default:
332                 /* only drain wb */
333                 break;
334         }
335
336         warm_cop_drain_wb();
337         return 0;
338 }
339
340 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
341 static long warm_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
342 #else
343 static int warm_ioctl(struct inode *inode, struct file *file,
344                         unsigned int cmd, unsigned long __arg)
345 #endif
346 {
347         void __user *arg = (void __user *) __arg;
348         union {
349                 struct warm_cache_op wcop;
350                 struct warm_change_cb ccb;
351                 unsigned long addr;
352         } u;
353         long ret;
354
355         switch (cmd) {
356         case WARMC_CACHE_OP:
357                 if (copy_from_user(&u.wcop, arg, sizeof(u.wcop)))
358                         return -EFAULT;
359                 if (u.wcop.ops & ~(WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE))
360                         return -EINVAL;
361                 if (u.wcop.size == (unsigned long)-1 ||
362                                 (u.wcop.size > MAX_CACHEOP_RANGE && !(u.wcop.ops & WOP_D_INVALIDATE)))
363                         ret = do_cache_ops_whole(u.wcop.ops);
364                 else
365                         ret = do_cache_ops(u.wcop.ops, u.wcop.addr, u.wcop.size);
366                 break;
367         case WARMC_CHANGE_CB:
368                 if (copy_from_user(&u.ccb, arg, sizeof(u.ccb)))
369                         return -EFAULT;
370                 if (u.ccb.cb & ~(WCB_C_BIT|WCB_B_BIT))
371                         return -EINVAL;
372                 if (u.ccb.addr == 0 && u.ccb.size == 0)
373                         ret = do_set_cb_uppermem(u.ccb.cb, u.ccb.is_set);
374                 else
375                         ret = do_set_cb_virt(u.ccb.cb, u.ccb.is_set, u.ccb.addr, u.ccb.size);
376                 break;
377         case WARMC_VIRT2PHYS:
378                 if (copy_from_user(&u.addr, arg, sizeof(u.addr)))
379                         return -EFAULT;
380                 ret = do_virt2phys(&u.addr);
381                 if (copy_to_user(arg, &u.addr, sizeof(u.addr)))
382                         return -EFAULT;
383                 break;
384         default:
385                 ret = -ENOTTY;
386                 break;
387         }
388
389         return ret;
390 }
391
392 static const char *warm_implementor_name(char code)
393 {
394         switch (code) {
395         case 'A':
396                 return "ARM";
397         case 'D':
398                 return "DEC";
399         case 'i':
400                 return "Intel";
401         case 'M':
402                 return "Motorola - Freescale";
403         case 'V':
404                 return "Marvell";
405         }
406         return "???";
407 }
408
409 static const char *warm_arch_name(int code)
410 {
411         switch (code) {
412         case 1:
413                 return "4";
414         case 2:
415                 return "4T";
416         case 3:
417                 return "5";
418         case 4:
419                 return "5T";
420         case 5:
421                 return "5TE";
422         case 6:
423                 return "5TEJ";
424         case 7:
425                 return "6";
426         }
427         return "?";
428 }
429
430 static int warm_cache_size(int code, int m)
431 {
432         int base = 512;
433         if (m)
434                 base = 768;
435         return base << code;
436 }
437
438 static int warm_cache_assoc(int code, int m)
439 {
440         int base = 2;
441         if (code == 0)
442                 return m ? 0 : 1;
443         if (m)
444                 base = 3;
445         return base << (code - 1); 
446 }
447
448 static int warm_cache_line(int code)
449 {
450         return 8 << code;
451 }
452
453 static int warm_seq_show(struct seq_file *seq, void *offset)
454 {
455         u32 tmp;
456
457         seq_printf(seq, "wARM: " WARM_VER "\n");
458
459         /* ID codes */
460         asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(tmp));
461         seq_printf(seq, "ID: %08x\n", tmp);
462         if (tmp & 0x80000) {
463                 /* revised format, not yet documented */
464         } else if ((tmp & 0xf000) == 0) {
465                 /* pre-ARM7 */
466                 seq_printf(seq, "Architecture: %d\n",
467                                 (tmp & 0xf00) == 0x600 ? 3 : 2);
468                 seq_printf(seq, "Variant: %d%d0\n", (tmp & 0xf00) >> 8,
469                                 (tmp & 0xf0) >> 4);
470         } else {
471                 seq_printf(seq, "Implementor: %c (%s)\n", tmp >> 24,
472                                 warm_implementor_name(tmp >> 24));
473                 if ((tmp & 0xf000) == 7) {
474                         seq_printf(seq, "Architecture: %s\n",
475                                 tmp & 0x800000 ? "4T" : "3");
476                         seq_printf(seq, "Variant: 0x%x\n", (tmp & 0x7f0000) >> 16);
477                 } else {
478                         seq_printf(seq, "Architecture: %s\n",
479                                 warm_arch_name((tmp & 0x0f0000) >> 16));
480                         seq_printf(seq, "Variant: 0x%x\n", (tmp & 0xf00000) >> 20);
481                 }
482                 seq_printf(seq, "Part number: 0x%x\n", (tmp & 0xfff0) >> 4);
483         }
484         seq_printf(seq, "Revision: 0x%x\n", tmp & 0xf);
485
486         /* cache type */
487         asm ("mrc p15, 0, %0, c0, c0, 1" : "=r"(tmp));
488         seq_printf(seq, "Cache ctype: 0x%x\n", (tmp & 0x1e000000) >> 25);
489         seq_printf(seq, "Cache unified: %s\n", (tmp & 0x01000000) ? "no" : "yes");
490         seq_printf(seq, "DCache size: %d\n",
491                         warm_cache_size((tmp >> (6+12)) & 0xf, (tmp >> (2+12)) & 1));
492         seq_printf(seq, "DCache associativity: %d\n",
493                         warm_cache_assoc((tmp >> (3+12)) & 7, (tmp >> (2+12)) & 1));
494         seq_printf(seq, "DCache line size: %d\n",
495                         warm_cache_line((tmp >> (0+12)) & 3));
496         seq_printf(seq, "ICache size: %d\n",
497                         warm_cache_size((tmp >> 6) & 0xf, (tmp >> 2) & 1));
498         seq_printf(seq, "ICache associativity: %d\n",
499                         warm_cache_assoc((tmp >> 3) & 7, (tmp >> 2) & 1));
500         seq_printf(seq, "ICache line size: %d\n",
501                         warm_cache_line((tmp >> 0) & 3));
502
503         return 0;
504 }
505
506 static int warm_open(struct inode *inode, struct file *file)
507 {
508         return single_open(file, warm_seq_show, NULL);
509 }
510
511 static int warm_close(struct inode *ino, struct file *file)
512 {
513         return single_release(ino, file);
514 }
515
516 static const struct file_operations warm_fops = {
517         .owner  = THIS_MODULE,
518         .open   = warm_open,
519         .read   = seq_read,
520         .llseek = seq_lseek,
521         .unlocked_ioctl = warm_ioctl,
522         .release = warm_close,
523 };
524
525 static int __init warm_module_init(void)
526 {
527         struct proc_dir_entry *pret;
528         u32 cpuid;
529
530         asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(cpuid));
531         if ((cpuid & 0x0ffff0) != EXPECTED_ID) {
532                 printk(KERN_ERR PFX "module was compiled for different CPU, aborting\n");
533                 return -1;
534         }
535
536         pret = create_proc_entry("warm", S_IWUGO | S_IRUGO, NULL);
537         if (!pret) {
538                 printk(KERN_ERR PFX "can't create proc entry\n");
539                 return -1;
540         }
541
542         pret->owner = THIS_MODULE;
543         pret->proc_fops = &warm_fops;
544
545         uppermem_start = RAM_PHYS_START + (max_mapnr << PAGE_SHIFT);
546         uppermem_end = RAM_PHYS_START + RAM_MAX_SIZE;
547
548         pr_info(PFX WARM_VER " loaded, ");
549         if (uppermem_end <= uppermem_start)
550                 printk("no upper mem");
551         else
552                 printk("upper mem %08x-%08x", uppermem_start, uppermem_end - 1);
553         printk("\n");
554
555         /* give time for /proc node to appear */
556         mdelay(200);
557
558         return 0;
559 }
560
561 static void __exit warm_module_exit(void)
562 {
563         remove_proc_entry("warm", NULL);
564
565         pr_info(PFX "unloaded.\n");
566 }
567
568 module_init(warm_module_init);
569 module_exit(warm_module_exit);
570
571 module_param(verbose, int, 0644);
572
573 MODULE_LICENSE("GPL");
574 MODULE_DESCRIPTION("ARM processor services");
575 MODULE_AUTHOR("Grazvydas Ignotas");