r1 (released with gpSP)
authornotaz <notaz@pixelinis>
Mon, 15 Jun 2009 20:11:28 +0000 (23:11 +0300)
committernotaz <notaz@pixelinis>
Mon, 15 Jun 2009 20:11:28 +0000 (23:11 +0300)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
module/Makefile [new file with mode: 0644]
module/warm_main.c [new file with mode: 0644]
module/warm_ops.S [new file with mode: 0644]
module/warm_ops.h [new file with mode: 0644]
sys_cacheflush.S [new file with mode: 0644]
sys_cacheflush.h [new file with mode: 0644]
test.c [new file with mode: 0644]
warm.c [new file with mode: 0644]
warm.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5761abc
--- /dev/null
@@ -0,0 +1 @@
+*.o
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..4fe3306
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+CC = $(CROSS_COMPILE)gcc
+AR = $(CROSS_COMPILE)ar
+CFLAGS += -Wall
+
+WARM_A = libwarm.a
+WARM_SO = libwarm.so
+OBJS = warm.o sys_cacheflush.o
+
+all: $(WARM_A) $(WARM_SO) test
+
+$(WARM_A): $(OBJS)
+       $(AR) rc $@ $^
+
+$(WARM_SO): $(OBJS)
+       $(CC) $(CFLAGS) $^ -o $@ -shared
+
+test: test.o
+       $(CC) $(CFLAGS) $^ -o $@ libwarm.a
+
+clean:
+       $(RM) $(WARM_A) $(WARM_SO) $(OBJS) test test.o
diff --git a/module/Makefile b/module/Makefile
new file mode 100644 (file)
index 0000000..c5d782f
--- /dev/null
@@ -0,0 +1,21 @@
+ifdef CROSS_COMPILE
+ ifndef KERNEL_DIR
+ $(error specify KERNEL_DIR)
+ endif
+else
+ $(error specify CROSS_COMPILE)
+ RELEASE = $(shell uname -r)
+ KERNEL_DIR = /lib/modules/$(RELEASE)/build
+endif
+
+obj-m += warm.o
+warm-objs += warm_main.o warm_ops.o
+
+
+all:
+       make -C $(KERNEL_DIR) M=$(PWD) modules
+
+clean:
+       make -C $(KERNEL_DIR) M=$(PWD) clean
+       $(RM) modules.order Module.markers
+
diff --git a/module/warm_main.c b/module/warm_main.c
new file mode 100644 (file)
index 0000000..5b8e910
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * wARM - exporting ARM processor specific privileged services to userspace
+ * kernelspace part
+ *
+ * Author: Gražvydas "notaz" Ignotas
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+#define WARM_CODE
+#include "../warm.h"
+#include "warm_ops.h"
+
+#ifndef CONFIG_PROC_FS
+#error need proc_fs
+#endif
+       
+#define WARM_VER "r1"
+#define PFX "wARM: "
+
+#define MAX_CACHEOP_RANGE      16384
+
+/* assume RAM starts at phys addr 0 (this is really machine specific) */
+#define RAM_PHYS_START 0
+#define RAM_MAX_SIZE   0x10000000      /* 256M, try to be future proof */
+
+extern unsigned long max_mapnr;
+
+/* "upper" physical memory, not seen by Linux and to be mmap'ed */
+static u32 uppermem_start;
+static u32 uppermem_end;
+
+static u32 *get_pgtable(void)
+{
+       u32 ttb;
+
+       /* get the pointer to the translation table base... */
+       asm ("mrc p15, 0, %0, c2, c0, 0" : "=r"(ttb));
+
+       return __va((ttb) & 0xffffc000);
+}
+
+static int do_set_cb_uppermem(int in_cb, int is_set)
+{
+       u32 *pgtable, *cpt;
+       int i, j, count = 0;
+       int bits = 0;
+
+       if (uppermem_end <= uppermem_start)
+               return -ENODEV;
+
+       if (in_cb & WCB_C_BIT)
+               bits |= 8;
+       if (in_cb & WCB_B_BIT)
+               bits |= 4;
+
+       pgtable = get_pgtable();
+
+       for (i = 0; i < 4096; i++)
+       {
+               if (!(pgtable[i] & 1))
+                       /* must be course of fine page table */
+                       continue;
+
+               cpt = __va(pgtable[i] & 0xfffffc00);
+
+               for (j = 0; j < 256; j++)
+               {
+                       u32 addr, entry;
+
+                       entry = cpt[j];
+                       if (!(entry & 3))
+                               /* fault */
+                               continue;
+
+                       addr = entry & 0xfffff000;
+                       if (uppermem_start <= addr && addr < uppermem_end)
+                       {
+                               pr_debug(PFX "%s C&B bits %08x\n",
+                                       is_set ? "set" : "clear", entry);
+
+                               if (is_set)
+                                       entry |= bits;
+                               else
+                                       entry &= ~bits;
+
+                               /* need to also set AP bits (so that no faults
+                                * happen and kernel doesn't touch this after us) */
+                               if ((entry & 3) == 3)
+                                       entry |= 0x030; /* tiny page */
+                               else
+                                       entry |= 0xff0;
+
+                               cpt[j] = entry;
+                               count++;
+                       }
+               }
+       }
+
+       warm_cop_clean_d();
+       warm_drain_wb_inval_tlb();
+
+       pr_info(PFX "%c%c bit(s) %s for phys %08x-%08x (%d pages)\n",
+               bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
+               is_set ? "set" : "cleared",
+               uppermem_start, uppermem_end - 1, count);
+
+       return 0;
+}
+
+static int do_set_cb_virt(int in_cb, int is_set, u32 addr, u32 size)
+{
+       int count = 0, bits = 0;
+       u32 desc1, desc2;
+       u32 *pgtable, *cpt;
+       u32 start, end;
+
+       if (in_cb & WCB_C_BIT)
+               bits |= 8;
+       if (in_cb & WCB_B_BIT)
+               bits |= 4;
+
+       size += addr & ~(PAGE_SIZE - 1);
+       size = ALIGN(size, PAGE_SIZE);
+
+       addr &= ~(PAGE_SIZE - 1);
+       start = addr;
+       end = addr + size;
+
+       pgtable = get_pgtable();
+
+       for (; addr < end; addr += PAGE_SIZE)
+       {
+               desc1 = pgtable[addr >> 20];
+
+               if (!(desc1 & 3))
+                       return -EINVAL;
+
+               cpt = __va(desc1 & 0xfffffc00);
+               desc2 = cpt[(addr >> 12) & 0xff];
+               
+               if ((desc2 & 3) != 2) {
+                       printk(KERN_WARNING PFX "not small page? %08x %08x\n", desc2, addr);
+                       return -EINVAL;
+               }
+
+               if (is_set)
+                       desc2 |= bits;
+               else
+                       desc2 &= ~bits;
+               desc2 |= 0xff0;
+
+               cpt[(addr >> 12) & 0xff] = desc2;
+               count++;
+       }
+
+       warm_cop_clean_d();
+       warm_drain_wb_inval_tlb();
+
+       pr_info(PFX "%c%c bit(s) %s virt %08x-%08x (%d pages)\n",
+               bits & 8 ? 'c' : ' ', bits & 4 ? 'b' : ' ',
+               is_set ? "set" : "cleared", start, end - 1, count);
+
+       return 0;
+}
+
+static int do_virt2phys(unsigned long *_addr)
+{
+       u32 addr = *_addr;
+       u32 desc1, desc2;
+       u32 *pgtable, *cpt;
+
+       pgtable = get_pgtable();
+       desc1 = pgtable[addr >> 20];
+
+       if (!(desc1 & 3))
+               return -EINVAL;
+       
+       if ((desc1 & 3) == 2) {
+               /* 1MB section */
+               *_addr = (desc1 & 0xfff00000) | (addr & 0xfffff);
+               return 0;
+       }
+       
+       cpt = __va(desc1 & 0xfffffc00);
+       desc2 = cpt[(addr >> 12) & 0xff];
+       
+       if ((desc2 & 3) != 2) {
+               printk(KERN_WARNING PFX "not small page? %08x %08x\n", desc2, addr);
+               return -EINVAL;
+       }
+
+       *_addr = (desc2 & 0xfffffc00) | (addr & 0x3ff);
+       return 0;
+}
+
+static int do_cache_ops_whole(int ops)
+{
+       if ((ops & (WOP_D_CLEAN|WOP_D_INVALIDATE)) == (WOP_D_CLEAN|WOP_D_INVALIDATE))
+               warm_cop_clean_inval_d();
+
+       else if (ops & WOP_D_CLEAN)
+               warm_cop_clean_d();
+
+       else if (ops & WOP_D_INVALIDATE) {
+               printk(KERN_WARNING PFX "invalidate without clean is dangerous!\n");
+               warm_cop_inval_d();
+       }
+
+       if (ops & WOP_I_INVALIDATE)
+               warm_cop_inval_i();
+       
+       warm_cop_drain_wb();
+       return 0;
+}
+
+static int do_cache_ops(int ops, u32 addr, u32 size)
+{
+       if (addr & 31) {
+               size += addr & 31;
+               addr &= ~31;
+       }
+
+       switch (ops) {
+       case WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE:
+               warm_cop_r_clean_d_inval_di(addr, size);
+               break;
+       case WOP_D_CLEAN|WOP_D_INVALIDATE:
+               warm_cop_r_clean_d_inval_d(addr, size);
+               break;
+       case WOP_D_CLEAN|WOP_I_INVALIDATE:
+               warm_cop_r_clean_d_inval_i(addr, size);
+               break;
+       case WOP_D_CLEAN:
+               warm_cop_r_clean_d(addr, size);
+               break;
+       case WOP_D_INVALIDATE|WOP_I_INVALIDATE:
+               warm_cop_r_inval_di(addr, size);
+               break;
+       case WOP_D_INVALIDATE:
+               warm_cop_r_inval_d(addr, size);
+               break;
+       case WOP_I_INVALIDATE:
+               warm_cop_r_inval_i(addr, size);
+               break;
+       default:
+               /* only drain wb */
+               break;
+       }
+
+       warm_cop_drain_wb();
+       return 0;
+}
+
+static long warm_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
+{
+       void __user *arg = (void __user *) __arg;
+       union {
+               struct warm_cache_op wcop;
+               struct warm_change_cb ccb;
+               unsigned long addr;
+       } u;
+       long ret;
+
+       switch (cmd) {
+       case WARMC_CACHE_OP:
+               if (copy_from_user(&u.wcop, arg, sizeof(u.wcop)))
+                       return -EFAULT;
+               if (u.wcop.ops & ~(WOP_D_CLEAN|WOP_D_INVALIDATE|WOP_I_INVALIDATE))
+                       return -EINVAL;
+               if (u.wcop.size > MAX_CACHEOP_RANGE)
+                       ret = do_cache_ops_whole(u.wcop.ops);
+               else
+                       ret = do_cache_ops(u.wcop.ops, u.wcop.addr, u.wcop.size);
+               break;
+       case WARMC_CHANGE_CB:
+               if (copy_from_user(&u.ccb, arg, sizeof(u.ccb)))
+                       return -EFAULT;
+               if (u.ccb.cb & ~(WCB_C_BIT|WCB_B_BIT))
+                       return -EINVAL;
+               if (u.ccb.addr == 0 && u.ccb.size == 0)
+                       ret = do_set_cb_uppermem(u.ccb.cb, u.ccb.is_set);
+               else
+                       ret = do_set_cb_virt(u.ccb.cb, u.ccb.is_set, u.ccb.addr, u.ccb.size);
+               break;
+       case WARMC_VIRT2PHYS:
+               if (copy_from_user(&u.addr, arg, sizeof(u.addr)))
+                       return -EFAULT;
+               ret = do_virt2phys(&u.addr);
+               if (copy_to_user(arg, &u.addr, sizeof(u.addr)))
+                       return -EFAULT;
+               break;
+       default:
+               ret = -ENOTTY;
+               break;
+       }
+
+       return ret;
+}
+
+static const char *warm_implementor_name(char code)
+{
+       switch (code) {
+       case 'A':
+               return "ARM";
+       case 'D':
+               return "DEC";
+       case 'i':
+               return "Intel";
+       case 'M':
+               return "Motorola - Freescale";
+       case 'V':
+               return "Marvell";
+       }
+       return "???";
+}
+
+static const char *warm_arch_name(int code)
+{
+       switch (code) {
+       case 1:
+               return "4";
+       case 2:
+               return "4T";
+       case 3:
+               return "5";
+       case 4:
+               return "5T";
+       case 5:
+               return "5TE";
+       case 6:
+               return "5TEJ";
+       case 7:
+               return "6";
+       }
+       return "?";
+}
+
+static int warm_cache_size(int code, int m)
+{
+       int base = 512;
+       if (m)
+               base = 768;
+       return base << code;
+}
+
+static int warm_cache_assoc(int code, int m)
+{
+       int base = 2;
+       if (code == 0)
+               return m ? 0 : 1;
+       if (m)
+               base = 3;
+       return base << (code - 1); 
+}
+
+static int warm_cache_line(int code)
+{
+       return 8 << code;
+}
+
+static int warm_seq_show(struct seq_file *seq, void *offset)
+{
+       u32 tmp;
+
+       seq_printf(seq, "wARM: " WARM_VER "\n");
+
+       /* ID codes */
+       asm ("mrc p15, 0, %0, c0, c0, 0" : "=r"(tmp));
+       seq_printf(seq, "ID: %08x\n", tmp);
+       if (tmp & 0x80000) {
+               /* revised format, not yet documented */
+       } else if ((tmp & 0xf000) == 0) {
+               /* pre-ARM7 */
+               seq_printf(seq, "Architecture: %d\n",
+                               (tmp & 0xf00) == 0x600 ? 3 : 2);
+               seq_printf(seq, "Variant: %d%d0\n", (tmp & 0xf00) >> 8,
+                               (tmp & 0xf0) >> 4);
+       } else {
+               seq_printf(seq, "Implementor: %c (%s)\n", tmp >> 24,
+                               warm_implementor_name(tmp >> 24));
+               if ((tmp & 0xf000) == 7) {
+                       seq_printf(seq, "Architecture: %s\n",
+                               tmp & 0x800000 ? "4T" : "3");
+                       seq_printf(seq, "Variant: 0x%x\n", (tmp & 0x7f0000) >> 16);
+               } else {
+                       seq_printf(seq, "Architecture: %s\n",
+                               warm_arch_name((tmp & 0x0f0000) >> 16));
+                       seq_printf(seq, "Variant: 0x%x\n", (tmp & 0xf00000) >> 20);
+               }
+               seq_printf(seq, "Part number: 0x%x\n", (tmp & 0xfff0) >> 4);
+       }
+       seq_printf(seq, "Revision: 0x%x\n", tmp & 0xf);
+
+       /* cache type */
+       asm ("mrc p15, 0, %0, c0, c0, 1" : "=r"(tmp));
+       seq_printf(seq, "Cache ctype: 0x%x\n", (tmp & 0x1e000000) >> 25);
+       seq_printf(seq, "Cache unified: %s\n", (tmp & 0x01000000) ? "no" : "yes");
+       seq_printf(seq, "DCache size: %d\n",
+                       warm_cache_size((tmp >> (6+12)) & 0xf, (tmp >> (2+12)) & 1));
+       seq_printf(seq, "DCache associativity: %d\n",
+                       warm_cache_assoc((tmp >> (3+12)) & 7, (tmp >> (2+12)) & 1));
+       seq_printf(seq, "DCache line size: %d\n",
+                       warm_cache_line((tmp >> (0+12)) & 3));
+       seq_printf(seq, "ICache size: %d\n",
+                       warm_cache_size((tmp >> 6) & 0xf, (tmp >> 2) & 1));
+       seq_printf(seq, "ICache associativity: %d\n",
+                       warm_cache_assoc((tmp >> 3) & 7, (tmp >> 2) & 1));
+       seq_printf(seq, "ICache line size: %d\n",
+                       warm_cache_line((tmp >> 0) & 3));
+
+       return 0;
+}
+
+static int warm_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, warm_seq_show, NULL);
+}
+
+static int warm_close(struct inode *ino, struct file *file)
+{
+       return single_release(ino, file);
+}
+
+static const struct file_operations warm_fops = {
+       .owner  = THIS_MODULE,
+       .open   = warm_open,
+       .read   = seq_read,
+       .llseek = seq_lseek,
+       .unlocked_ioctl = warm_ioctl,
+       .release = warm_close,
+};
+
+static int __init warm_module_init(void)
+{
+       struct proc_dir_entry *pret;
+
+       pret = create_proc_entry("warm", S_IWUGO | S_IRUGO, NULL);
+       if (!pret) {
+               printk(KERN_ERR PFX "can't create proc entry\n");
+               return -1;
+       }
+
+       pret->owner = THIS_MODULE;
+       pret->proc_fops = &warm_fops;
+
+       uppermem_start = RAM_PHYS_START + (max_mapnr << PAGE_SHIFT);
+       uppermem_end = RAM_PHYS_START + RAM_MAX_SIZE;
+
+       pr_info(PFX WARM_VER " loaded, ");
+       if (uppermem_end <= uppermem_start)
+               printk("no upper mem");
+       else
+               printk("upper mem %08x-%08x", uppermem_start, uppermem_end - 1);
+       printk("\n");
+
+       /* give time for /proc node to appear */
+       mdelay(200);
+
+       return 0;
+}
+
+static void __exit warm_module_exit(void)
+{
+       remove_proc_entry("warm", NULL);
+
+       pr_info(PFX "unloaded.\n");
+}
+
+module_init(warm_module_init);
+module_exit(warm_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM processor services");
+MODULE_AUTHOR("Grazvydas Ignotas");
diff --git a/module/warm_ops.S b/module/warm_ops.S
new file mode 100644 (file)
index 0000000..8752e05
--- /dev/null
@@ -0,0 +1,86 @@
+/* vim:filetype=armasm
+*/
+
+#include <linux/init.h>
+
+#ifndef CONFIG_CPU_ARM926T
+#error CPU not supported
+#endif
+
+.text
+.align 2
+
+.global warm_cop_clean_inval_d
+warm_cop_clean_inval_d:
+0:  mrc    p15, 0, r15, c7, c14, 3     @ test, clean and invalidate
+    bne    0b
+    bx     lr
+
+
+.global warm_cop_clean_d
+warm_cop_clean_d:
+0:  mrc    p15, 0, r15, c7, c10, 3     @ test and clean
+    bne    0b
+    bx     lr
+
+
+.global warm_cop_inval_d
+warm_cop_inval_d:
+    mov    r0, #0
+    mcr    p15, 0, r0, c7, c6, 0
+    bx     lr
+
+
+.global warm_cop_inval_i
+warm_cop_inval_i:
+    mov    r0, #0
+    mcr    p15, 0, r0, c7, c5, 0
+    bx     lr
+
+
+.global warm_cop_drain_wb
+warm_cop_drain_wb:
+    mov    r0, #0
+    mcr    p15, 0, r0, c7, c10, 4
+    bx     lr
+
+
+#define R_CLEAN_INVAL_D \
+    mcr    p15, 0, r0, c7, c14, 1
+
+#define R_CLEAN_D \
+    mcr    p15, 0, r0, c7, c10, 1
+
+#define R_INVAL_D \
+    mcr    p15, 0, r0, c7, c6, 1
+
+#define R_INVAL_I \
+    mcr    p15, 0, r0, c7, c5, 1
+
+#define WARM_COP_MK_RANGE_FUNC(name,f1,f2)     \
+.global name                                   ;\
+name:                                          ;\
+    bic    r0, r0, #32-1                       ;\
+0:  f1                                         ;\
+    f2                                         ;\
+    add    r0, r0, #32                         ;\
+    subs   r1, r1, #32                         ;\
+    bgt    0b                                  ;\
+    bx     lr
+
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_clean_d_inval_di, R_CLEAN_INVAL_D, R_INVAL_I)
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_clean_d_inval_d,  R_CLEAN_INVAL_D, )
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_clean_d_inval_i,  R_CLEAN_D,       R_INVAL_I)
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_clean_d,          R_CLEAN_D, )
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_inval_di,         R_INVAL_D, R_INVAL_I)
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_inval_d,          R_INVAL_D, )
+WARM_COP_MK_RANGE_FUNC(warm_cop_r_inval_i,          R_INVAL_I, )
+
+
+.global warm_drain_wb_inval_tlb
+warm_drain_wb_inval_tlb:
+    mov    r0, #0
+    mcr    p15, 0, r0, c7, c10, 4
+    mcr    p15, 0, r0, c8, c7, 0
+    bx     lr
+
diff --git a/module/warm_ops.h b/module/warm_ops.h
new file mode 100644 (file)
index 0000000..fc58071
--- /dev/null
@@ -0,0 +1,17 @@
+#include <linux/kernel.h>
+
+void warm_cop_clean_inval_d(void);
+void warm_cop_clean_d(void);
+void warm_cop_inval_d(void);
+void warm_cop_inval_i(void);
+void warm_cop_drain_wb(void);
+
+void warm_cop_r_clean_d_inval_di(u32 addr, u32 size);
+void warm_cop_r_clean_d_inval_d(u32 addr, u32 size);
+void warm_cop_r_clean_d_inval_i(u32 addr, u32 size);
+void warm_cop_r_clean_d(u32 addr, u32 size);
+void warm_cop_r_inval_di(u32 addr, u32 size);
+void warm_cop_r_inval_d(u32 addr, u32 size);
+void warm_cop_r_inval_i(u32 addr, u32 size);
+
+void warm_drain_wb_inval_tlb(void);
diff --git a/sys_cacheflush.S b/sys_cacheflush.S
new file mode 100644 (file)
index 0000000..61dac35
--- /dev/null
@@ -0,0 +1,22 @@
+@ vim:filetype=armasm
+#include <sys/syscall.h>
+
+
+.global sys_cacheflush @ void *start_addr, void *end_addr
+
+sys_cacheflush:
+    mov     r2, #0
+#ifdef __ARM_EABI__
+    /* EABI version */
+    str     r7, [sp, #-4]!
+    mov     r7, #(__ARM_NR_cacheflush & 0xff)
+    orr     r7, r7, #(__ARM_NR_cacheflush & 0x00ff00)
+    orr     r7, r7, #(__ARM_NR_cacheflush & 0xff0000)
+    swi     0
+    ldr     r7, [sp], #4
+#else
+    /* OABI */
+    swi     __ARM_NR_cacheflush
+#endif
+    bx      lr
+
diff --git a/sys_cacheflush.h b/sys_cacheflush.h
new file mode 100644 (file)
index 0000000..27f3c4c
--- /dev/null
@@ -0,0 +1,3 @@
+
+void sys_cacheflush(void *start_addr, void *end_addr);
+
diff --git a/test.c b/test.c
new file mode 100644 (file)
index 0000000..ced9433
--- /dev/null
+++ b/test.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+#include "warm.h"
+
+int main()
+{
+       warm_init();
+       warm_cache_op_range(0, 0, 0);
+
+       return 0;
+}
+
diff --git a/warm.c b/warm.c
new file mode 100644 (file)
index 0000000..197df0f
--- /dev/null
+++ b/warm.c
@@ -0,0 +1,146 @@
+/*
+ * wARM - exporting ARM processor specific privileged services to userspace
+ * userspace part
+ *
+ * Copyright (c) Gražvydas "notaz" Ignotas, 2009
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the organization nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <errno.h>
+
+#define WARM_CODE
+#include "warm.h"
+#include "sys_cacheflush.h"
+
+static int warm_fd = -1;
+
+int warm_init(void)
+{
+       struct utsname unm;
+       char buff[128];
+
+       warm_fd = open("/proc/warm", O_RDWR);
+       if (warm_fd >= 0)
+               return 0;
+
+       memset(&unm, 0, sizeof(unm));
+       uname(&unm);
+       snprintf(buff, sizeof(buff), "/sbin/insmod warm_%s.ko", unm.release);
+
+       /* try to insmod */
+       system(buff);
+       warm_fd = open("/proc/warm", O_RDWR);
+       if (warm_fd >= 0)
+               return 0;
+
+       fprintf(stderr, "wARM: can't init, acting as sys_cacheflush wrapper\n");
+       return -1;
+}
+
+void warm_finish(void)
+{
+       if (warm_fd >= 0)
+               close(warm_fd);
+       system("rmmod warm");
+}
+
+int warm_cache_op_range(int op, void *addr, unsigned long size)
+{
+       struct warm_cache_op wop;
+       int ret;
+
+       if (warm_fd < 0) {
+               sys_cacheflush(addr, (char *)addr + size);
+               return -1;
+       }
+
+       wop.ops = op;
+       wop.addr = (unsigned long)addr;
+       wop.size = size;
+
+       ret = ioctl(warm_fd, WARMC_CACHE_OP, &wop);
+       if (ret != 0) {
+               perror("WARMC_CACHE_OP failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+int warm_cache_op_all(int op)
+{
+       return warm_cache_op_range(op, NULL, (size_t)-1);
+}
+
+int warm_change_cb_range(int cb, int is_set, void *addr, unsigned long size)
+{
+       struct warm_change_cb ccb;
+       int ret;
+
+       if (warm_fd < 0)
+               return -1;
+       
+       ccb.addr = (unsigned long)addr;
+       ccb.size = size;
+       ccb.cb = cb;
+       ccb.is_set = is_set;
+
+       ret = ioctl(warm_fd, WARMC_CHANGE_CB, &ccb);
+       if (ret != 0) {
+               perror("WARMC_CHANGE_CB failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+int warm_change_cb_upper(int cb, int is_set)
+{
+       return warm_change_cb_range(cb, is_set, 0, 0);
+}
+
+unsigned long warm_virt2phys(const void *ptr)
+{
+       unsigned long ptrio;
+       int ret;
+
+       ptrio = (unsigned long)ptr;
+       ret = ioctl(warm_fd, WARMC_VIRT2PHYS, &ptrio);
+       if (ret != 0) {
+               perror("WARMC_VIRT2PHYS failed");
+               return (unsigned long)-1;
+       }
+
+       return ptrio;
+}
+
diff --git a/warm.h b/warm.h
new file mode 100644 (file)
index 0000000..a3fdd6b
--- /dev/null
+++ b/warm.h
@@ -0,0 +1,100 @@
+/*
+ * wARM - exporting ARM processor specific privileged services to userspace
+ * library functions
+ *
+ * Copyright (c) Gražvydas "notaz" Ignotas, 2009
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the organization nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __WARM_H__
+#define __WARM_H__ 1
+
+/* cache operations (warm_cache_op_*):
+ * o clean - write dirty data to memory, but also leave in cache.
+ * o invalidate - throw away everything in cache, losing dirty data.
+ *
+ * Write buffer is always drained, no ops will only drain WB
+ */
+#define WOP_D_CLEAN            (1 << 0)
+#define WOP_D_INVALIDATE       (1 << 1)
+#define WOP_I_INVALIDATE       (1 << 2)
+
+/* change C and B bits (warm_change_cb_*)
+ * if is_set in not zero, bits are set, else cleared.
+ * the address for range function is virtual address.
+ */
+#define WCB_C_BIT              (1 << 0)
+#define WCB_B_BIT              (1 << 1)
+
+#ifndef __ASSEMBLER__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+int warm_init(void);
+
+int warm_cache_op_range(int ops, void *virt_addr, unsigned long size);
+int warm_cache_op_all(int ops);
+
+int warm_change_cb_upper(int cb, int is_set);
+int warm_change_cb_range(int cb, int is_set, void *virt_addr, unsigned long size);
+
+unsigned long warm_virt2phys(const void *ptr);
+
+void warm_finish(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* internal */
+#ifdef WARM_CODE
+
+#include <linux/ioctl.h>
+
+#define WARM_IOCTL_BASE 'A'
+
+struct warm_cache_op
+{
+       unsigned long addr;
+       unsigned long size;
+       int ops;
+};
+
+struct warm_change_cb
+{
+       unsigned long addr;
+       unsigned long size;
+       int cb;
+       int is_set;
+};
+
+#define WARMC_CACHE_OP _IOW(WARM_IOCTL_BASE,  0, struct warm_cache_op)
+#define WARMC_CHANGE_CB        _IOW(WARM_IOCTL_BASE,  1, struct warm_change_cb)
+#define WARMC_VIRT2PHYS        _IOWR(WARM_IOCTL_BASE, 2, unsigned long)
+
+#endif /* WARM_CODE */
+#endif /* !__ASSEMBLER__ */
+#endif /* __WARM_H__ */