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