2 * wARM - exporting ARM processor specific privileged services to userspace
5 * Author: GraÅžvydas "notaz" Ignotas
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.
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/proc_fs.h>
15 #include <linux/delay.h>
17 #include <linux/seq_file.h>
19 #include <linux/version.h>
20 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
21 #include <linux/uaccess.h>
23 #include <linux/init.h>
24 #include <linux/moduleparam.h>
25 #include <asm/uaccess.h>
27 #define unlocked_ioctl ioctl
34 #ifndef CONFIG_PROC_FS
41 #define WARM_INFO(fmt, ...) \
43 pr_info(PFX fmt, ##__VA_ARGS__)
45 #define SECTION_SIZE 0x100000
46 #define MAX_CACHEOP_RANGE 16384
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 */
53 #if defined(CONFIG_CPU_ARM926T)
54 #define EXPECTED_ID 0x069260
55 #elif defined(CONFIG_CPU_ARM920T)
56 #define EXPECTED_ID 0x029200
58 #error "unsupported CPU"
61 extern unsigned long max_mapnr;
63 /* "upper" physical memory, not seen by Linux and to be mmap'ed */
64 static u32 uppermem_start;
65 static u32 uppermem_end;
69 static u32 *get_pgtable(void)
73 /* get the pointer to the translation table base... */
74 asm ("mrc p15, 0, %0, c2, c0, 0" : "=r"(ttb));
76 return __va((ttb) & 0xffffc000);
79 static int do_set_cb_uppermem(int in_cb, int is_set)
85 if (uppermem_end <= uppermem_start)
88 if (in_cb & WCB_C_BIT)
90 if (in_cb & WCB_B_BIT)
93 pgtable = get_pgtable();
95 for (i = 0; i < 4096; i++)
97 if ((pgtable[i] & 3) != 1)
98 /* must be coarse page table */
101 cpt = __va(pgtable[i] & 0xfffffc00);
103 for (j = 0; j < 256; j++)
112 addr = entry & 0xfffff000;
113 if (uppermem_start <= addr && addr < uppermem_end)
115 pr_debug(PFX "%s C&B bits %08x\n",
116 is_set ? "set" : "clear", entry);
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 */
137 warm_drain_wb_inval_tlb();
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);
147 static int do_set_cb_virt(int in_cb, int is_set, u32 addr, u32 size)
149 int count = 0, bits = 0;
150 u32 desc1, desc2 = 0;
151 u32 *pgtable, *cpt = NULL;
155 if (in_cb & WCB_C_BIT)
157 if (in_cb & WCB_B_BIT)
160 mask = PAGE_SIZE - 1;
162 size = (size + mask) & ~mask;
164 addr &= ~(PAGE_SIZE - 1);
168 pgtable = get_pgtable();
172 desc1 = pgtable[addr >> 20];
176 printk(KERN_WARNING PFX "address %08x not mapped.\n", addr);
180 cpt = __va(desc1 & 0xfffffc00);
181 desc2 = cpt[(addr >> 12) & 0xff];
189 pgtable[addr >> 20] = desc1;
190 addr += SECTION_SIZE;
194 cpt = __va(desc1 & 0xfffff000);
195 desc2 = cpt[(addr >> 10) & 0x3ff];
200 if ((desc2 & 3) == 0) {
201 printk(KERN_WARNING PFX "address %08x not mapped (%08x)\n",
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)
219 cpt[(addr >> 12) & 0xff] = desc2;
222 cpt[(addr >> 10) & 0x3ff] = desc2;
231 warm_drain_wb_inval_tlb();
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);
240 static int do_virt2phys(unsigned long *_addr)
246 pgtable = get_pgtable();
247 desc1 = pgtable[addr >> 20];
250 case 1: /* coarse table */
251 cpt = __va(desc1 & 0xfffffc00);
252 desc2 = cpt[(addr >> 12) & 0xff];
254 case 2: /* 1MB section */
255 *_addr = (desc1 & 0xfff00000) | (addr & 0xfffff);
257 case 3: /* fine table */
258 cpt = __va(desc1 & 0xfffff000);
259 desc2 = cpt[(addr >> 10) & 0x3ff];
266 case 1: /* large page */
267 *_addr = (desc2 & ~0xffff) | (addr & 0xffff);
269 case 2: /* small page */
270 *_addr = (desc2 & ~0x0fff) | (addr & 0x0fff);
272 case 3: /* tiny page */
273 *_addr = (desc2 & ~0x03ff) | (addr & 0x03ff);
282 static int do_cache_ops_whole(int ops)
284 if ((ops & (WOP_D_CLEAN|WOP_D_INVALIDATE)) == (WOP_D_CLEAN|WOP_D_INVALIDATE))
285 warm_cop_clean_inval_d();
287 else if (ops & WOP_D_CLEAN)
290 else if (ops & WOP_D_INVALIDATE) {
291 printk(KERN_WARNING PFX "invalidate without clean is dangerous!\n");
295 if (ops & WOP_I_INVALIDATE)
302 static int do_cache_ops(int ops, u32 addr, u32 size)
310 case WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE:
311 warm_cop_r_clean_d_inval_di(addr, size);
313 case WOP_D_CLEAN|WOP_D_INVALIDATE:
314 warm_cop_r_clean_d_inval_d(addr, size);
316 case WOP_D_CLEAN|WOP_I_INVALIDATE:
317 warm_cop_r_clean_d_inval_i(addr, size);
320 warm_cop_r_clean_d(addr, size);
322 case WOP_D_INVALIDATE|WOP_I_INVALIDATE:
323 warm_cop_r_inval_di(addr, size);
325 case WOP_D_INVALIDATE:
326 warm_cop_r_inval_d(addr, size);
328 case WOP_I_INVALIDATE:
329 warm_cop_r_inval_i(addr, size);
340 static int do_map_op(u32 vaddr, u32 paddr, u32 size, int cb, int is_unmap)
342 int count = 0, retval = 0;
343 u32 pstart, start, end;
344 u32 desc1, apcb_bits;
348 apcb_bits = (3 << 10) | (1 << 5); /* r/w, dom 1 */
355 mask = SECTION_SIZE - 1;
356 size = (size + mask) & ~mask;
362 /* check for overflows */
365 if (pstart + size - 1 < pstart)
368 pgtable = get_pgtable();
370 for (; vaddr < end; vaddr += SECTION_SIZE, paddr += SECTION_SIZE)
372 desc1 = pgtable[vaddr >> 20];
375 if ((desc1 & 3) != 2) {
376 printk(KERN_WARNING PFX "vaddr %08x is not a section? (%08x)\n",
382 if ((desc1 & 3) != 0) {
383 printk(KERN_WARNING PFX "vaddr %08x already mapped? (%08x)\n",
388 v = (paddr & ~mask) | apcb_bits | 0x12;
391 pgtable[vaddr >> 20] = v;
399 for (; vaddr < end && count > 0; vaddr += SECTION_SIZE, count--)
400 pgtable[vaddr >> 20] = 0;
404 warm_drain_wb_inval_tlb();
406 if (retval == 0 && !is_unmap) {
407 WARM_INFO("mapped %08x to %08x with %c%c bit(s) (%d section(s))\n",
408 start, pstart, apcb_bits & 8 ? 'c' : ' ',
409 apcb_bits & 4 ? 'b' : ' ', count);
415 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
416 static long warm_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
418 static int warm_ioctl(struct inode *inode, struct file *file,
419 unsigned int cmd, unsigned long __arg)
422 void __user *arg = (void __user *) __arg;
424 struct warm_cache_op wcop;
425 struct warm_change_cb ccb;
426 struct warm_map_op mop;
433 if (copy_from_user(&u.wcop, arg, sizeof(u.wcop)))
435 if (u.wcop.ops & ~(WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE))
437 if (u.wcop.size == (unsigned long)-1 ||
438 (u.wcop.size > MAX_CACHEOP_RANGE && !(u.wcop.ops & WOP_D_INVALIDATE)))
439 ret = do_cache_ops_whole(u.wcop.ops);
441 ret = do_cache_ops(u.wcop.ops, u.wcop.addr, u.wcop.size);
443 case WARMC_CHANGE_CB:
444 if (copy_from_user(&u.ccb, arg, sizeof(u.ccb)))
446 if (u.ccb.cb & ~(WCB_C_BIT|WCB_B_BIT))
448 if (u.ccb.addr == 0 && u.ccb.size == 0)
449 ret = do_set_cb_uppermem(u.ccb.cb, u.ccb.is_set);
451 ret = do_set_cb_virt(u.ccb.cb, u.ccb.is_set, u.ccb.addr, u.ccb.size);
453 case WARMC_VIRT2PHYS:
454 if (copy_from_user(&u.addr, arg, sizeof(u.addr)))
456 ret = do_virt2phys(&u.addr);
457 if (copy_to_user(arg, &u.addr, sizeof(u.addr)))
461 if (copy_from_user(&u.mop, arg, sizeof(u.mop)))
463 if (u.mop.cb & ~(WCB_C_BIT|WCB_B_BIT))
465 ret = do_map_op(u.mop.virt_addr, u.mop.phys_addr, u.mop.size,
466 u.mop.cb, u.mop.is_unmap);
476 static const char *warm_implementor_name(char code)
486 return "Motorola - Freescale";
493 static const char *warm_arch_name(int code)
514 static int warm_cache_size(int code, int m)
522 static int warm_cache_assoc(int code, int m)
529 return base << (code - 1);
532 static int warm_cache_line(int code)
537 static int warm_seq_show(struct seq_file *seq, void *offset)
541 seq_printf(seq, "wARM: " WARM_VER "\n");
544 asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(tmp));
545 seq_printf(seq, "ID: %08x\n", tmp);
547 /* revised format, not yet documented */
548 } else if ((tmp & 0xf000) == 0) {
550 seq_printf(seq, "Architecture: %d\n",
551 (tmp & 0xf00) == 0x600 ? 3 : 2);
552 seq_printf(seq, "Variant: %d%d0\n", (tmp & 0xf00) >> 8,
555 seq_printf(seq, "Implementor: %c (%s)\n", tmp >> 24,
556 warm_implementor_name(tmp >> 24));
557 if ((tmp & 0xf000) == 7) {
558 seq_printf(seq, "Architecture: %s\n",
559 tmp & 0x800000 ? "4T" : "3");
560 seq_printf(seq, "Variant: 0x%x\n", (tmp & 0x7f0000) >> 16);
562 seq_printf(seq, "Architecture: %s\n",
563 warm_arch_name((tmp & 0x0f0000) >> 16));
564 seq_printf(seq, "Variant: 0x%x\n", (tmp & 0xf00000) >> 20);
566 seq_printf(seq, "Part number: 0x%x\n", (tmp & 0xfff0) >> 4);
568 seq_printf(seq, "Revision: 0x%x\n", tmp & 0xf);
571 asm ("mrc p15, 0, %0, c0, c0, 1" : "=r"(tmp));
572 seq_printf(seq, "Cache ctype: 0x%x\n", (tmp & 0x1e000000) >> 25);
573 seq_printf(seq, "Cache unified: %s\n", (tmp & 0x01000000) ? "no" : "yes");
574 seq_printf(seq, "DCache size: %d\n",
575 warm_cache_size((tmp >> (6+12)) & 0xf, (tmp >> (2+12)) & 1));
576 seq_printf(seq, "DCache associativity: %d\n",
577 warm_cache_assoc((tmp >> (3+12)) & 7, (tmp >> (2+12)) & 1));
578 seq_printf(seq, "DCache line size: %d\n",
579 warm_cache_line((tmp >> (0+12)) & 3));
580 seq_printf(seq, "ICache size: %d\n",
581 warm_cache_size((tmp >> 6) & 0xf, (tmp >> 2) & 1));
582 seq_printf(seq, "ICache associativity: %d\n",
583 warm_cache_assoc((tmp >> 3) & 7, (tmp >> 2) & 1));
584 seq_printf(seq, "ICache line size: %d\n",
585 warm_cache_line((tmp >> 0) & 3));
590 static int warm_open(struct inode *inode, struct file *file)
592 return single_open(file, warm_seq_show, NULL);
595 static int warm_close(struct inode *ino, struct file *file)
597 return single_release(ino, file);
600 static const struct file_operations warm_fops = {
601 .owner = THIS_MODULE,
605 .unlocked_ioctl = warm_ioctl,
606 .release = warm_close,
609 static int __init warm_module_init(void)
611 struct proc_dir_entry *pret;
614 asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(cpuid));
615 if ((cpuid & 0x0ffff0) != EXPECTED_ID) {
616 printk(KERN_ERR PFX "module was compiled for different CPU, aborting\n");
620 pret = create_proc_entry("warm", S_IWUGO | S_IRUGO, NULL);
622 printk(KERN_ERR PFX "can't create proc entry\n");
626 pret->owner = THIS_MODULE;
627 pret->proc_fops = &warm_fops;
629 uppermem_start = RAM_PHYS_START + (max_mapnr << PAGE_SHIFT);
630 uppermem_end = RAM_PHYS_START + RAM_MAX_SIZE;
632 pr_info(PFX WARM_VER " loaded, ");
633 if (uppermem_end <= uppermem_start)
634 printk("no upper mem");
636 printk("upper mem %08x-%08x", uppermem_start, uppermem_end - 1);
639 /* give time for /proc node to appear */
645 static void __exit warm_module_exit(void)
647 remove_proc_entry("warm", NULL);
649 pr_info(PFX "unloaded.\n");
652 module_init(warm_module_init);
653 module_exit(warm_module_exit);
655 module_param(verbose, int, 0644);
657 MODULE_LICENSE("GPL");
658 MODULE_DESCRIPTION("ARM processor services");
659 MODULE_AUTHOR("Grazvydas Ignotas");