From: notaz Date: Mon, 15 Jun 2009 20:11:28 +0000 (+0300) Subject: r1 (released with gpSP) X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=198a1649dcb74c973706066d37bb955c139232b1;p=warm.git r1 (released with gpSP) --- 198a1649dcb74c973706066d37bb955c139232b1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5761abc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/Makefile b/Makefile new file mode 100644 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 index 0000000..c5d782f --- /dev/null +++ b/module/Makefile @@ -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 index 0000000..5b8e910 --- /dev/null +++ b/module/warm_main.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 index 0000000..8752e05 --- /dev/null +++ b/module/warm_ops.S @@ -0,0 +1,86 @@ +/* vim:filetype=armasm +*/ + +#include + +#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 index 0000000..fc58071 --- /dev/null +++ b/module/warm_ops.h @@ -0,0 +1,17 @@ +#include + +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 index 0000000..61dac35 --- /dev/null +++ b/sys_cacheflush.S @@ -0,0 +1,22 @@ +@ vim:filetype=armasm +#include + + +.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 index 0000000..27f3c4c --- /dev/null +++ b/sys_cacheflush.h @@ -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 index 0000000..ced9433 --- /dev/null +++ b/test.c @@ -0,0 +1,11 @@ +#include +#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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 + +#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__ */