f491e2232b7c7c14a196da70b4ead7e402f49cb7
[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 static spinlock_t lock;
67
68 static int verbose;
69
70 static u32 *get_pgtable(void)
71 {
72         u32 ttb;
73
74         /* get the pointer to the translation table base... */
75         asm ("mrc p15, 0, %0, c2, c0, 0" : "=r"(ttb));
76
77         return __va((ttb) & 0xffffc000);
78 }
79
80 static int do_set_cb_uppermem(int in_cb, int is_set)
81 {
82         unsigned long flags;
83         u32 *pgtable, *cpt;
84         int i, j, count = 0;
85         int bits = 0;
86
87         if (uppermem_end <= uppermem_start)
88                 return -ENODEV;
89
90         if (in_cb & WCB_C_BIT)
91                 bits |= 8;
92         if (in_cb & WCB_B_BIT)
93                 bits |= 4;
94
95         spin_lock_irqsave(&lock, flags);
96
97         pgtable = get_pgtable();
98
99         for (i = 0; i < 4096; i++)
100         {
101                 if ((pgtable[i] & 3) != 1)
102                         /* must be coarse page table */
103                         continue;
104
105                 cpt = __va(pgtable[i] & 0xfffffc00);
106
107                 for (j = 0; j < 256; j++)
108                 {
109                         u32 addr, entry;
110
111                         entry = cpt[j];
112                         if (!(entry & 3))
113                                 /* fault */
114                                 continue;
115
116                         addr = entry & 0xfffff000;
117                         if (uppermem_start <= addr && addr < uppermem_end)
118                         {
119                                 pr_debug(PFX "%s C&B bits %08x\n",
120                                         is_set ? "set" : "clear", entry);
121
122                                 if (is_set)
123                                         entry |= bits;
124                                 else
125                                         entry &= ~bits;
126
127                                 /* need to also set AP bits (so that no faults
128                                  * happen and kernel doesn't touch this after us) */
129                                 if ((entry & 3) == 3)
130                                         entry |= 0x030; /* tiny page */
131                                 else
132                                         entry |= 0xff0;
133
134                                 cpt[j] = entry;
135                                 count++;
136                         }
137                 }
138         }
139
140         warm_cop_clean_d();
141         warm_drain_wb_inval_tlb();
142
143         spin_unlock_irqrestore(&lock, flags);
144
145         WARM_INFO("%c%c bit(s) %s for phys %08x-%08x (%d pages)\n",
146                 bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
147                 is_set ? "set" : "cleared",
148                 uppermem_start, uppermem_end - 1, count);
149
150         return 0;
151 }
152
153 static int do_set_cb_virt(int in_cb, int is_set, u32 addr, u32 size)
154 {
155         int count = 0, bits = 0;
156         unsigned long flags;
157         u32 desc1, desc2 = 0;
158         u32 *pgtable, *cpt = NULL;
159         u32 start, end;
160         u32 mask;
161
162         if (in_cb & WCB_C_BIT)
163                 bits |= 8;
164         if (in_cb & WCB_B_BIT)
165                 bits |= 4;
166
167         mask = PAGE_SIZE - 1;
168         size += addr & mask;
169         size = (size + mask) & ~mask;
170
171         addr &= ~(PAGE_SIZE - 1);
172         start = addr;
173         end = addr + size;
174
175         spin_lock_irqsave(&lock, flags);
176
177         pgtable = get_pgtable();
178
179         while (addr < end)
180         {
181                 desc1 = pgtable[addr >> 20];
182
183                 switch (desc1 & 3) {
184                 case 0:
185                         spin_unlock_irqrestore(&lock, flags);
186                         printk(KERN_WARNING PFX "address %08x not mapped.\n", addr);
187                         return -EINVAL;
188                 case 1:
189                         /* coarse table */
190                         cpt = __va(desc1 & 0xfffffc00);
191                         desc2 = cpt[(addr >> 12) & 0xff];
192                         break;
193                 case 2:
194                         /* section */
195                         if (is_set)
196                                 desc1 |= bits;
197                         else
198                                 desc1 &= ~bits;
199                         pgtable[addr >> 20] = desc1;
200                         addr += SECTION_SIZE;
201                         count++;
202                         continue;
203                 case 3:
204                         cpt = __va(desc1 & 0xfffff000);
205                         desc2 = cpt[(addr >> 10) & 0x3ff];
206                         break;
207                 }
208
209                 if ((desc2 & 3) == 0) {
210                         spin_unlock_irqrestore(&lock, flags);
211                         printk(KERN_WARNING PFX "address %08x not mapped (%08x)\n",
212                                 addr, desc2);
213                         return -EINVAL;
214                 }
215
216                 if (is_set)
217                         desc2 |= bits;
218                 else
219                         desc2 &= ~bits;
220
221                 /* this might be bad idea, better let it fault so that Linux does
222                  * it's accounting, but that will drop CB bits, so keep this
223                  * for compatibility */
224                 if ((desc2 & 3) == 2)
225                         desc2 |= 0xff0;
226
227                 switch (desc1 & 3) {
228                 case 1:
229                         cpt[(addr >> 12) & 0xff] = desc2;
230                         break;
231                 case 3:
232                         cpt[(addr >> 10) & 0x3ff] = desc2;
233                         break;
234                 }
235
236                 addr += PAGE_SIZE;
237                 count++;
238         }
239
240         warm_cop_clean_d();
241         warm_drain_wb_inval_tlb();
242
243         spin_unlock_irqrestore(&lock, flags);
244
245         WARM_INFO("%c%c bit(s) %s virt %08x-%08x (%d pages)\n",
246                 bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
247                 is_set ? "set" : "cleared", start, end - 1, count);
248
249         return 0;
250 }
251
252 static int do_virt2phys(unsigned long *_addr)
253 {
254         u32 addr = *_addr;
255         u32 desc1, desc2;
256         u32 *pgtable, *cpt;
257
258         pgtable = get_pgtable();
259         desc1 = pgtable[addr >> 20];
260
261         switch (desc1 & 3) {
262         case 1: /* coarse table */
263                 cpt = __va(desc1 & 0xfffffc00);
264                 desc2 = cpt[(addr >> 12) & 0xff];
265                 break;
266         case 2: /* 1MB section */
267                 *_addr = (desc1 & 0xfff00000) | (addr & 0xfffff);
268                 return 0;
269         case 3: /* fine table */
270                 cpt = __va(desc1 & 0xfffff000);
271                 desc2 = cpt[(addr >> 10) & 0x3ff];
272                 break;
273         default:
274                 return -EINVAL;
275         }
276
277         switch (desc2 & 3) {
278         case 1: /* large page */
279                 *_addr = (desc2 & ~0xffff) | (addr & 0xffff);
280                 break;
281         case 2: /* small page */
282                 *_addr = (desc2 & ~0x0fff) | (addr & 0x0fff);
283                 break;
284         case 3: /* tiny page */
285                 *_addr = (desc2 & ~0x03ff) | (addr & 0x03ff);
286                 break;
287         default:
288                 return -EINVAL;
289         }
290
291         return 0;
292 }
293
294 static int do_cache_ops_whole(int ops)
295 {
296         if ((ops & (WOP_D_CLEAN|WOP_D_INVALIDATE)) == (WOP_D_CLEAN|WOP_D_INVALIDATE))
297                 warm_cop_clean_inval_d();
298
299         else if (ops & WOP_D_CLEAN)
300                 warm_cop_clean_d();
301
302         else if (ops & WOP_D_INVALIDATE) {
303                 printk(KERN_WARNING PFX "invalidate without clean is dangerous!\n");
304                 warm_cop_inval_d();
305         }
306
307         if (ops & WOP_I_INVALIDATE)
308                 warm_cop_inval_i();
309         
310         warm_cop_drain_wb();
311         return 0;
312 }
313
314 static int do_cache_ops(int ops, u32 addr, u32 size)
315 {
316         if (addr & 31) {
317                 size += addr & 31;
318                 addr &= ~31;
319         }
320
321         switch (ops) {
322         case WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE:
323                 warm_cop_r_clean_d_inval_di(addr, size);
324                 break;
325         case WOP_D_CLEAN|WOP_D_INVALIDATE:
326                 warm_cop_r_clean_d_inval_d(addr, size);
327                 break;
328         case WOP_D_CLEAN|WOP_I_INVALIDATE:
329                 warm_cop_r_clean_d_inval_i(addr, size);
330                 break;
331         case WOP_D_CLEAN:
332                 warm_cop_r_clean_d(addr, size);
333                 break;
334         case WOP_D_INVALIDATE|WOP_I_INVALIDATE:
335                 warm_cop_r_inval_di(addr, size);
336                 break;
337         case WOP_D_INVALIDATE:
338                 warm_cop_r_inval_d(addr, size);
339                 break;
340         case WOP_I_INVALIDATE:
341                 warm_cop_r_inval_i(addr, size);
342                 break;
343         default:
344                 /* only drain wb */
345                 break;
346         }
347
348         warm_cop_drain_wb();
349         return 0;
350 }
351
352 static int do_map_op(u32 vaddr, u32 paddr, u32 size, int cb, int is_unmap)
353 {
354         int count = 0, retval = 0;
355         unsigned long flags;
356         u32 pstart, start, end;
357         u32 desc1, apcb_bits;
358         u32 *pgtable;
359         u32 v, mask;
360
361         apcb_bits = (3 << 10) | (1 << 5); /* r/w, dom 1 */
362         if (cb & WCB_C_BIT)
363                 apcb_bits |= 8;
364         if (cb & WCB_B_BIT)
365                 apcb_bits |= 4;
366
367         mask = SECTION_SIZE - 1;
368         size = (size + mask) & ~mask;
369
370         pstart = paddr;
371         start = vaddr;
372         end = start + size;
373
374         /* check for overflows */
375         if (end - 1 < start)
376                 return -EINVAL;
377         if (pstart + size - 1 < pstart)
378                 return -EINVAL;
379
380         spin_lock_irqsave(&lock, flags);
381
382         pgtable = get_pgtable();
383
384         for (; vaddr < end; vaddr += SECTION_SIZE, paddr += SECTION_SIZE)
385         {
386                 desc1 = pgtable[vaddr >> 20];
387
388                 if (is_unmap) {
389                         if ((desc1 & 3) != 2) {
390                                 spin_unlock_irqrestore(&lock, flags);
391                                 printk(KERN_WARNING PFX "vaddr %08x is not a section? (%08x)\n",
392                                                 vaddr, desc1);
393                                 return -EINVAL;
394                         }
395                         v = 0;
396                 } else {
397                         if ((desc1 & 3) != 0) {
398                                 printk(KERN_WARNING PFX "vaddr %08x already mapped? (%08x)\n",
399                                                 vaddr, desc1);
400                                 retval = -EINVAL;
401                                 break;
402                         }
403                         v = (paddr & ~mask) | apcb_bits | 0x12;
404                 }
405
406                 pgtable[vaddr >> 20] = v;
407                 count++;
408         }
409
410         if (retval != 0) {
411                 /* undo mappings */
412                 vaddr = start;
413
414                 for (; vaddr < end && count > 0; vaddr += SECTION_SIZE, count--)
415                         pgtable[vaddr >> 20] = 0;
416         }
417
418         warm_cop_clean_d();
419         warm_drain_wb_inval_tlb();
420
421         spin_unlock_irqrestore(&lock, flags);
422
423         if (retval == 0 && !is_unmap) {
424                 WARM_INFO("mapped %08x to %08x with %c%c bit(s) (%d section(s))\n",
425                         start, pstart, apcb_bits & 8 ? 'c' : ' ',
426                         apcb_bits & 4 ? 'b' : ' ', count);
427         }
428
429         return retval;
430 }
431
432 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
433 static long warm_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
434 #else
435 static int warm_ioctl(struct inode *inode, struct file *file,
436                         unsigned int cmd, unsigned long __arg)
437 #endif
438 {
439         void __user *arg = (void __user *) __arg;
440         union {
441                 struct warm_cache_op wcop;
442                 struct warm_change_cb ccb;
443                 struct warm_map_op mop;
444                 unsigned long addr;
445         } u;
446         long ret;
447
448         switch (cmd) {
449         case WARMC_CACHE_OP:
450                 if (copy_from_user(&u.wcop, arg, sizeof(u.wcop)))
451                         return -EFAULT;
452                 if (u.wcop.ops & ~(WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE))
453                         return -EINVAL;
454                 if (u.wcop.size == (unsigned long)-1 ||
455                                 (u.wcop.size > MAX_CACHEOP_RANGE && !(u.wcop.ops & WOP_D_INVALIDATE)))
456                         ret = do_cache_ops_whole(u.wcop.ops);
457                 else
458                         ret = do_cache_ops(u.wcop.ops, u.wcop.addr, u.wcop.size);
459                 break;
460         case WARMC_CHANGE_CB:
461                 if (copy_from_user(&u.ccb, arg, sizeof(u.ccb)))
462                         return -EFAULT;
463                 if (u.ccb.cb & ~(WCB_C_BIT|WCB_B_BIT))
464                         return -EINVAL;
465                 if (u.ccb.addr == 0 && u.ccb.size == 0)
466                         ret = do_set_cb_uppermem(u.ccb.cb, u.ccb.is_set);
467                 else
468                         ret = do_set_cb_virt(u.ccb.cb, u.ccb.is_set, u.ccb.addr, u.ccb.size);
469                 break;
470         case WARMC_VIRT2PHYS:
471                 if (copy_from_user(&u.addr, arg, sizeof(u.addr)))
472                         return -EFAULT;
473                 ret = do_virt2phys(&u.addr);
474                 if (copy_to_user(arg, &u.addr, sizeof(u.addr)))
475                         return -EFAULT;
476                 break;
477         case WARMC_MMAP:
478                 if (copy_from_user(&u.mop, arg, sizeof(u.mop)))
479                         return -EFAULT;
480                 if (u.mop.cb & ~(WCB_C_BIT|WCB_B_BIT))
481                         return -EINVAL;
482                 ret = do_map_op(u.mop.virt_addr, u.mop.phys_addr, u.mop.size,
483                         u.mop.cb, u.mop.is_unmap);
484                 break;
485         default:
486                 ret = -ENOTTY;
487                 break;
488         }
489
490         return ret;
491 }
492
493 static const char *warm_implementor_name(char code)
494 {
495         switch (code) {
496         case 'A':
497                 return "ARM";
498         case 'D':
499                 return "DEC";
500         case 'i':
501                 return "Intel";
502         case 'M':
503                 return "Motorola - Freescale";
504         case 'V':
505                 return "Marvell";
506         }
507         return "???";
508 }
509
510 static const char *warm_arch_name(int code)
511 {
512         switch (code) {
513         case 1:
514                 return "4";
515         case 2:
516                 return "4T";
517         case 3:
518                 return "5";
519         case 4:
520                 return "5T";
521         case 5:
522                 return "5TE";
523         case 6:
524                 return "5TEJ";
525         case 7:
526                 return "6";
527         }
528         return "?";
529 }
530
531 static int warm_cache_size(int code, int m)
532 {
533         int base = 512;
534         if (m)
535                 base = 768;
536         return base << code;
537 }
538
539 static int warm_cache_assoc(int code, int m)
540 {
541         int base = 2;
542         if (code == 0)
543                 return m ? 0 : 1;
544         if (m)
545                 base = 3;
546         return base << (code - 1); 
547 }
548
549 static int warm_cache_line(int code)
550 {
551         return 8 << code;
552 }
553
554 static int warm_seq_show(struct seq_file *seq, void *offset)
555 {
556         u32 tmp;
557
558         seq_printf(seq, "wARM: " WARM_VER "\n");
559
560         /* ID codes */
561         asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(tmp));
562         seq_printf(seq, "ID: %08x\n", tmp);
563         if (tmp & 0x80000) {
564                 /* revised format, not yet documented */
565         } else if ((tmp & 0xf000) == 0) {
566                 /* pre-ARM7 */
567                 seq_printf(seq, "Architecture: %d\n",
568                                 (tmp & 0xf00) == 0x600 ? 3 : 2);
569                 seq_printf(seq, "Variant: %d%d0\n", (tmp & 0xf00) >> 8,
570                                 (tmp & 0xf0) >> 4);
571         } else {
572                 seq_printf(seq, "Implementor: %c (%s)\n", tmp >> 24,
573                                 warm_implementor_name(tmp >> 24));
574                 if ((tmp & 0xf000) == 7) {
575                         seq_printf(seq, "Architecture: %s\n",
576                                 tmp & 0x800000 ? "4T" : "3");
577                         seq_printf(seq, "Variant: 0x%x\n", (tmp & 0x7f0000) >> 16);
578                 } else {
579                         seq_printf(seq, "Architecture: %s\n",
580                                 warm_arch_name((tmp & 0x0f0000) >> 16));
581                         seq_printf(seq, "Variant: 0x%x\n", (tmp & 0xf00000) >> 20);
582                 }
583                 seq_printf(seq, "Part number: 0x%x\n", (tmp & 0xfff0) >> 4);
584         }
585         seq_printf(seq, "Revision: 0x%x\n", tmp & 0xf);
586
587         /* cache type */
588         asm ("mrc p15, 0, %0, c0, c0, 1" : "=r"(tmp));
589         seq_printf(seq, "Cache ctype: 0x%x\n", (tmp & 0x1e000000) >> 25);
590         seq_printf(seq, "Cache unified: %s\n", (tmp & 0x01000000) ? "no" : "yes");
591         seq_printf(seq, "DCache size: %d\n",
592                         warm_cache_size((tmp >> (6+12)) & 0xf, (tmp >> (2+12)) & 1));
593         seq_printf(seq, "DCache associativity: %d\n",
594                         warm_cache_assoc((tmp >> (3+12)) & 7, (tmp >> (2+12)) & 1));
595         seq_printf(seq, "DCache line size: %d\n",
596                         warm_cache_line((tmp >> (0+12)) & 3));
597         seq_printf(seq, "ICache size: %d\n",
598                         warm_cache_size((tmp >> 6) & 0xf, (tmp >> 2) & 1));
599         seq_printf(seq, "ICache associativity: %d\n",
600                         warm_cache_assoc((tmp >> 3) & 7, (tmp >> 2) & 1));
601         seq_printf(seq, "ICache line size: %d\n",
602                         warm_cache_line((tmp >> 0) & 3));
603
604         return 0;
605 }
606
607 static int warm_open(struct inode *inode, struct file *file)
608 {
609         return single_open(file, warm_seq_show, NULL);
610 }
611
612 static int warm_close(struct inode *ino, struct file *file)
613 {
614         return single_release(ino, file);
615 }
616
617 static const struct file_operations warm_fops = {
618         .owner  = THIS_MODULE,
619         .open   = warm_open,
620         .read   = seq_read,
621         .llseek = seq_lseek,
622         .unlocked_ioctl = warm_ioctl,
623         .release = warm_close,
624 };
625
626 static int __init warm_module_init(void)
627 {
628         struct proc_dir_entry *pret;
629         u32 cpuid;
630
631         asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(cpuid));
632         if ((cpuid & 0x0ffff0) != EXPECTED_ID) {
633                 printk(KERN_ERR PFX "module was compiled for different CPU, aborting\n");
634                 return -1;
635         }
636
637         pret = create_proc_entry("warm", S_IWUGO | S_IRUGO, NULL);
638         if (!pret) {
639                 printk(KERN_ERR PFX "can't create proc entry\n");
640                 return -1;
641         }
642
643         pret->owner = THIS_MODULE;
644         pret->proc_fops = &warm_fops;
645
646         spin_lock_init(&lock);
647
648         uppermem_start = RAM_PHYS_START + (max_mapnr << PAGE_SHIFT);
649         uppermem_end = RAM_PHYS_START + RAM_MAX_SIZE;
650
651         pr_info(PFX WARM_VER " loaded, ");
652         if (uppermem_end <= uppermem_start)
653                 printk("no upper mem");
654         else
655                 printk("upper mem %08x-%08x", uppermem_start, uppermem_end - 1);
656         printk("\n");
657
658         /* give time for /proc node to appear */
659         mdelay(200);
660
661         return 0;
662 }
663
664 static void __exit warm_module_exit(void)
665 {
666         remove_proc_entry("warm", NULL);
667
668         pr_info(PFX "unloaded.\n");
669 }
670
671 module_init(warm_module_init);
672 module_exit(warm_module_exit);
673
674 module_param(verbose, int, 0644);
675
676 MODULE_LICENSE("GPL");
677 MODULE_DESCRIPTION("ARM processor services");
678 MODULE_AUTHOR("Grazvydas Ignotas");